* [PATCH 2/2] leds: add mp3326 driver
@ 2023-10-31 7:01 Yuxi (Yuxi) Wang
2023-10-31 8:16 ` Krzysztof Kozlowski
0 siblings, 1 reply; 8+ messages in thread
From: Yuxi (Yuxi) Wang @ 2023-10-31 7:01 UTC (permalink / raw)
To: pavel@ucw.cz, lee@kernel.org, robh+dt@kernel.org,
krzysztof.kozlowski+dt@linaro.org, conor+dt@kernel.org
Cc: linux-leds@vger.kernel.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org, wyx137120466@gmail.com
This patch adds mp3326 led driver.
Signed-off-by: Yuxi Wang <Yuxi.Wang@monolithicpower.com>
---
drivers/leds/Kconfig | 7 +
drivers/leds/Makefile | 1 +
drivers/leds/leds-mp3326.c | 632 +++++++++++++++++++++++++++++++++++++
3 files changed, 640 insertions(+)
create mode 100644 drivers/leds/leds-mp3326.c
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index b92208eccdea..ac8115bffc2e 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -260,6 +260,13 @@ config LEDS_MIKROTIK_RB532
This option enables support for the so called "User LED" of
Mikrotik's Routerboard 532.
+config LEDS_MP3326
+ tristate "LED Support for MPS MP3326"
+ depends on LEDS_CLASS
+ help
+ This option enables support for on-chip LED drivers found on
+ MPS MP3326.
+
config LEDS_MT6323
tristate "LED Support for Mediatek MT6323 PMIC"
depends on LEDS_CLASS
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index d7348e8bc019..196befb56278 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -63,6 +63,7 @@ obj-$(CONFIG_LEDS_MENF21BMC) += leds-menf21bmc.o
obj-$(CONFIG_LEDS_MIKROTIK_RB532) += leds-rb532.o
obj-$(CONFIG_LEDS_MLXCPLD) += leds-mlxcpld.o
obj-$(CONFIG_LEDS_MLXREG) += leds-mlxreg.o
+obj-$(CONFIG_LEDS_MP3326) += leds-mp3326.o
obj-$(CONFIG_LEDS_MT6323) += leds-mt6323.o
obj-$(CONFIG_LEDS_NET48XX) += leds-net48xx.o
obj-$(CONFIG_LEDS_NETXBIG) += leds-netxbig.o
diff --git a/drivers/leds/leds-mp3326.c b/drivers/leds/leds-mp3326.c
new file mode 100644
index 000000000000..140c71b334f7
--- /dev/null
+++ b/drivers/leds/leds-mp3326.c
@@ -0,0 +1,632 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * MP3326 Led driver
+ *
+ * Copyright 2023 Monolithic Power Systems, Inc
+ *
+ * Author: Yuxi Wang <Yuxi.Wang@monolithicpower.com>
+ */
+#include <linux/bits.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/i2c.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/leds.h>
+#include <linux/device.h>
+#include <linux/led-class-multicolor.h>
+
+#define MP3326_PWM_DIM_FREQUENCY_CONFIG 0x00
+#define MP3326_PWM_CTRL 0x01
+#define MP3326_PWM_DIM_FREQUENCY_CONFIG 0x00
+#define MP3326_PWM_CTRL_CHANNEL_9_16 0x04
+#define MP3326_PWM_CTRL_CHANNEL_1_8 0x05
+#define MP3326_PWM_OPEN_FAULT_CHANNEL_9_16 0x06
+#define MP3326_PWM_OPEN_FAULT_CHANNEL_1_8 0x07
+#define MP3326_PWM_SHORT_FAULT_CHANNEL_9_16 0x08
+#define MP3326_PWM_SHORT_FAULT_CHANNEL_1_8 0x09
+#define MP3326_PWM_CURRENT_SET_CHANNEL1 0x0A
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL1 0x0B
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL1 0x0C
+#define MP3326_PWM_CURRENT_SET_CHANNEL2 0x0D
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL2 0x0E
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL2 0x0F
+#define MP3326_PWM_CURRENT_SET_CHANNEL3 0x10
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL3 0x11
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL3 0x12
+#define MP3326_PWM_CURRENT_SET_CHANNEL4 0x13
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL4 0x14
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL4 0x15
+#define MP3326_PWM_CURRENT_SET_CHANNEL5 0x16
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL5 0x17
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL5 0x18
+#define MP3326_PWM_CURRENT_SET_CHANNEL6 0x19
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL6 0x1A
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL6 0x1B
+#define MP3326_PWM_CURRENT_SET_CHANNEL7 0x1C
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL7 0x1D
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL7 0x1E
+#define MP3326_PWM_CURRENT_SET_CHANNEL8 0x1F
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL8 0x20
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL8 0x21
+#define MP3326_PWM_CURRENT_SET_CHANNEL9 0x22
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL9 0x23
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL9 0x24
+#define MP3326_PWM_CURRENT_SET_CHANNEL10 0x25
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL10 0x26
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL10 0x27
+#define MP3326_PWM_CURRENT_SET_CHANNEL11 0x28
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL11 0x29
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL11 0x2A
+#define MP3326_PWM_CURRENT_SET_CHANNEL12 0x2B
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL12 0x2C
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL12 0x2D
+#define MP3326_PWM_CURRENT_SET_CHANNEL13 0x2E
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL13 0x2F
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL13 0x30
+#define MP3326_PWM_CURRENT_SET_CHANNEL14 0x31
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL14 0x32
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL14 0x33
+#define MP3326_PWM_CURRENT_SET_CHANNEL15 0x34
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL15 0x35
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL15 0x36
+#define MP3326_PWM_CURRENT_SET_CHANNEL16 0x37
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL16 0x38
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL16 0x39
+#define MAX_BRIGHTNESS 63
+
+enum led_ctrl {
+ ENABLE = 0,
+ BRIGHTNESS,
+ COLOR_L4,
+ COLOR_H8,
+ OPEN_FAULT,
+ SHORT_FAULT,
+ Max_CTRL,
+};
+
+enum mp3326_channel {
+ Channel1,
+ Channel2,
+ Channel3,
+ Channel4,
+ Channel5,
+ Channel6,
+ Channel7,
+ Channel8,
+ Channel9,
+ Channel10,
+ Channel11,
+ Channel12,
+ Channel13,
+ Channel14,
+ Channel15,
+ Channel16,
+ Max_Channel,
+};
+
+#define MP3326_Reg_Connect_Inner(prefix, range) prefix##range
+#define MP3326_Reg_Connect(prefix, range) MP3326_Reg_Connect_Inner(prefix, range)
+#define MP3326_Reg_Field(reg, minbit, maxbit) REG_FIELD(reg, minbit, maxbit)
+#define Range1(a, b) MP3326_Reg_Connect_Inner(a, b)
+#define Range2(a, b) MP3326_Reg_Connect_Inner(a, b)
+
+#define MP3326_Channel_FIELD(bit, num, range) { \
+ MP3326_Reg_Field(MP3326_Reg_Connect(MP3326_PWM_CTRL_CHANNEL_, range), bit, bit), \
+ MP3326_Reg_Field(MP3326_Reg_Connect(MP3326_PWM_CURRENT_SET_CHANNEL, num), 0, 5), \
+ MP3326_Reg_Field(MP3326_Reg_Connect(MP3326_PWM_DUTY_LSB_SET_CHANNEL, num), 0, 3), \
+ MP3326_Reg_Field(MP3326_Reg_Connect(MP3326_PWM_DUTY_MSB_SET_CHANNEL, num), 0, 7), \
+ MP3326_Reg_Field(MP3326_Reg_Connect(MP3326_PWM_OPEN_FAULT_CHANNEL_, range), bit, bit), \
+ MP3326_Reg_Field(MP3326_Reg_Connect(MP3326_PWM_SHORT_FAULT_CHANNEL_, range), bit, bit), \
+ }
+struct mp3326_led {
+ struct mp3326 *private_data;
+ struct led_classdev cdev;
+ struct mc_subled *subled_info;
+ int num_colors;
+};
+
+struct mp3326 {
+ struct i2c_client *client;
+ struct regmap *regmap;
+ struct regmap_field *regmap_fields[Max_Channel][Max_CTRL];
+ int num_of_leds;
+ struct mp3326_led leds[];
+};
+
+static const struct regmap_config MP3326_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+static const struct reg_field channels_reg_fields[Max_Channel][Max_CTRL] = {
+ [Channel1] = MP3326_Channel_FIELD(0, 1, Range1(1_, 8)),
+ [Channel2] = MP3326_Channel_FIELD(1, 2, Range1(1_, 8)),
+ [Channel3] = MP3326_Channel_FIELD(2, 3, Range1(1_, 8)),
+ [Channel4] = MP3326_Channel_FIELD(3, 4, Range1(1_, 8)),
+ [Channel5] = MP3326_Channel_FIELD(4, 5, Range1(1_, 8)),
+ [Channel6] = MP3326_Channel_FIELD(5, 6, Range1(1_, 8)),
+ [Channel7] = MP3326_Channel_FIELD(6, 7, Range1(1_, 8)),
+ [Channel8] = MP3326_Channel_FIELD(7, 8, Range1(1_, 8)),
+ [Channel9] = MP3326_Channel_FIELD(0, 9, Range2(9_, 16)),
+ [Channel10] = MP3326_Channel_FIELD(1, 10, Range2(9_, 16)),
+ [Channel11] = MP3326_Channel_FIELD(2, 11, Range2(9_, 16)),
+ [Channel12] = MP3326_Channel_FIELD(3, 12, Range2(9_, 16)),
+ [Channel13] = MP3326_Channel_FIELD(4, 13, Range2(9_, 16)),
+ [Channel14] = MP3326_Channel_FIELD(5, 14, Range2(9_, 16)),
+ [Channel15] = MP3326_Channel_FIELD(6, 15, Range2(9_, 16)),
+ [Channel16] = MP3326_Channel_FIELD(7, 16, Range2(9_, 16)),
+};
+
+static int led_brightness_set(struct led_classdev *led_cdev, enum led_brightness brightness)
+{
+ struct mp3326_led *led = container_of(led_cdev, struct mp3326_led, cdev);
+ struct mp3326 *chip = led->private_data;
+ int ret;
+ int i;
+
+ if (brightness > led_cdev->max_brightness)
+ brightness = led_cdev->max_brightness;
+ if (brightness < 0)
+ brightness = 0;
+ for (i = 0; i < led->num_colors; i++) {
+ ret = regmap_field_write(
+ chip->regmap_fields[led->subled_info[i].channel][BRIGHTNESS],
+ brightness);
+ if (ret)
+ return ret;
+ led->subled_info[i].brightness = brightness;
+ }
+ led_cdev->brightness = brightness;
+ return 0;
+}
+
+static ssize_t led_pwm_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct led_classdev *lcdev = dev_get_drvdata(dev);
+ struct mp3326_led *led = container_of(lcdev, struct mp3326_led, cdev);
+ struct mp3326 *chip = led->private_data;
+ ssize_t ret;
+ int r_val, g_val, b_val;
+ int i;
+
+ ret = sscanf(buf, "%i %i %i", &r_val, &g_val, &b_val);
+ if (ret != 3 && ret != 1)
+ return ret;
+ r_val = r_val * 4095 / 255 + (r_val * 4095 % 255) / (255 / 2);
+ g_val = g_val * 4095 / 255 + (g_val * 4095 % 255) / (255 / 2);
+ b_val = b_val * 4095 / 255 + (b_val * 4095 % 255) / (255 / 2);
+ for (i = 0; i < led->num_colors; i++) {
+ switch (led->subled_info[i].color_index) {
+ case LED_COLOR_ID_RED:
+ ret = regmap_field_write(
+ chip->regmap_fields[led->subled_info[i].channel][COLOR_L4],
+ r_val & 0x0f);
+ if (ret)
+ return ret;
+ ret = regmap_field_write(
+ chip->regmap_fields[led->subled_info[i].channel][COLOR_H8],
+ r_val >> 4);
+ if (ret)
+ return ret;
+ break;
+ case LED_COLOR_ID_GREEN:
+ ret = regmap_field_write(
+ chip->regmap_fields[led->subled_info[i].channel][COLOR_L4],
+ g_val & 0x0f);
+ if (ret)
+ return ret;
+ ret = regmap_field_write(
+ chip->regmap_fields[led->subled_info[i].channel][COLOR_H8],
+ g_val >> 4);
+ if (ret)
+ return ret;
+ break;
+ case LED_COLOR_ID_BLUE:
+ ret = regmap_field_write(
+ chip->regmap_fields[led->subled_info[i].channel][COLOR_L4],
+ b_val & 0x0f);
+ if (ret)
+ return ret;
+ ret = regmap_field_write(
+ chip->regmap_fields[led->subled_info[i].channel][COLOR_H8],
+ b_val >> 4);
+ if (ret)
+ return ret;
+ break;
+ default:
+ ret = regmap_field_write(
+ chip->regmap_fields[led->subled_info[i].channel][COLOR_L4],
+ r_val & 0x0f);
+ if (ret)
+ return ret;
+ ret = regmap_field_write(
+ chip->regmap_fields[led->subled_info[i].channel][COLOR_H8],
+ r_val >> 4);
+ if (ret)
+ return ret;
+ break;
+ }
+ }
+ return count;
+}
+
+static ssize_t led_pwm_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct led_classdev *lcdev = dev_get_drvdata(dev);
+ struct mp3326_led *led = container_of(lcdev, struct mp3326_led, cdev);
+ struct mp3326 *chip = led->private_data;
+ ssize_t ret;
+ int r_val = 0, g_val = 0, b_val = 0, val;
+ int i;
+
+ for (i = 0; i < led->num_colors; i++) {
+ switch (led->subled_info[i].color_index) {
+ case LED_COLOR_ID_RED:
+ ret = regmap_field_read(
+ chip->regmap_fields[led->subled_info[i].channel][COLOR_L4], &val);
+ if (ret)
+ return ret;
+ r_val |= val;
+ ret = regmap_field_read(
+ chip->regmap_fields[led->subled_info[i].channel][COLOR_H8], &val);
+ if (ret)
+ return ret;
+ r_val |= val << 4;
+ break;
+ case LED_COLOR_ID_GREEN:
+ ret = regmap_field_read(
+ chip->regmap_fields[led->subled_info[i].channel][COLOR_L4], &val);
+ if (ret)
+ return ret;
+ g_val |= val;
+ ret = regmap_field_read(
+ chip->regmap_fields[led->subled_info[i].channel][COLOR_H8], &val);
+ if (ret)
+ return ret;
+ g_val |= val << 4;
+ break;
+ case LED_COLOR_ID_BLUE:
+ ret = regmap_field_read(
+ chip->regmap_fields[led->subled_info[i].channel][COLOR_L4], &val);
+ if (ret)
+ return ret;
+ b_val |= val;
+ ret = regmap_field_read(
+ chip->regmap_fields[led->subled_info[i].channel][COLOR_H8], &val);
+ if (ret)
+ return ret;
+ b_val |= val << 4;
+ break;
+ default:
+ ret = regmap_field_read(
+ chip->regmap_fields[led->subled_info[i].channel][COLOR_L4], &val);
+ if (ret)
+ return ret;
+ r_val |= val;
+ ret = regmap_field_read(
+ chip->regmap_fields[led->subled_info[i].channel][COLOR_H8], &val);
+ if (ret)
+ return ret;
+ r_val |= val << 4;
+ break;
+ }
+ }
+ r_val = r_val * 255 / 4095 + (r_val * 255 % 4095) / (4095 / 2);
+ g_val = g_val * 255 / 4095 + (g_val * 255 % 4095) / (4095 / 2);
+ b_val = b_val * 255 / 4095 + (b_val * 255 % 4095) / (4095 / 2);
+ if (led->num_colors == 1)
+ return sysfs_emit(buf, "0x%x\n", r_val);
+ else
+ return sysfs_emit(buf, "0x%x 0x%x 0x%x\n", r_val, g_val, b_val);
+}
+
+static ssize_t led_enable_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct led_classdev *lcdev = dev_get_drvdata(dev);
+ struct mp3326_led *led = container_of(lcdev, struct mp3326_led, cdev);
+ struct mp3326 *chip = led->private_data;
+ ssize_t ret;
+ uint val, i;
+
+ ret = kstrtouint(buf, 0, &val);
+ if (ret)
+ return ret;
+ for (i = 0; i < led->num_colors; i++) {
+ ret = regmap_field_write(
+ chip->regmap_fields[led->subled_info[i].channel][BRIGHTNESS],
+ led->subled_info[i].brightness);
+ if (ret)
+ return ret;
+ ret = regmap_field_write(
+ chip->regmap_fields[led->subled_info[i].channel][ENABLE], !!val);
+ if (ret)
+ return ret;
+ }
+
+ return count;
+}
+
+static ssize_t led_enable_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct led_classdev *lcdev = dev_get_drvdata(dev);
+ struct mp3326_led *led = container_of(lcdev, struct mp3326_led, cdev);
+ struct mp3326 *chip = led->private_data;
+ uint val, rval = 0;
+ int i, ret;
+
+ for (i = 0; i < led->num_colors; i++) {
+ ret = regmap_field_read(
+ chip->regmap_fields[led->subled_info[i].channel][ENABLE], &val);
+
+ rval |= val << i;
+ if (ret)
+ return ret;
+ }
+
+ if (rval)
+ return sysfs_emit(buf, "%s\n", "True");
+ else
+ return sysfs_emit(buf, "%s\n", "False");
+}
+
+static ssize_t led_short_fault_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct led_classdev *lcdev = dev_get_drvdata(dev);
+ struct mp3326_led *led = container_of(lcdev, struct mp3326_led, cdev);
+ struct mp3326 *chip = led->private_data;
+ uint val, rval = 0, i;
+ int ret;
+
+ for (i = 0; i < led->num_colors; i++) {
+ ret = regmap_field_read(
+ chip->regmap_fields[led->subled_info[i].channel][SHORT_FAULT], &val);
+ rval |= val << i;
+ if (ret)
+ return ret;
+ }
+
+ if (rval)
+ return sysfs_emit(buf, "%s\n", "Occur");
+ else
+ return sysfs_emit(buf, "%s\n", "None");
+}
+
+static ssize_t led_open_fault_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct led_classdev *lcdev = dev_get_drvdata(dev);
+ struct mp3326_led *led = container_of(lcdev, struct mp3326_led, cdev);
+ struct mp3326 *chip = led->private_data;
+ uint val, rval = 0, i;
+ int ret;
+
+ for (i = 0; i < led->num_colors; i++) {
+ ret = regmap_field_read(
+ chip->regmap_fields[led->subled_info[i].channel][OPEN_FAULT], &val);
+ rval |= val << i;
+ if (ret)
+ return ret;
+ }
+
+ if (rval)
+ return sysfs_emit(buf, "%s\n", "Occur");
+ else
+ return sysfs_emit(buf, "%s\n", "None");
+}
+
+static DEVICE_ATTR_RW(led_pwm);
+static DEVICE_ATTR_RW(led_enable);
+static DEVICE_ATTR_RO(led_short_fault);
+static DEVICE_ATTR_RO(led_open_fault);
+
+static struct attribute *led_sysfs_attrs[] = {
+ &dev_attr_led_pwm.attr,
+ &dev_attr_led_enable.attr,
+ &dev_attr_led_short_fault.attr,
+ &dev_attr_led_open_fault.attr,
+ NULL,
+};
+
+ATTRIBUTE_GROUPS(led_sysfs);
+
+static int mp3326_add_led(struct mp3326 *chip, struct device_node *np, int index)
+{
+ struct mp3326_led *led = &chip->leds[index];
+ struct mc_subled *info;
+ struct device_node *child;
+ struct led_classdev *cdev;
+ struct led_init_data init_data = {};
+ int ret;
+ int i;
+ int count;
+ u32 color = 0;
+ u32 reg = 0;
+
+ ret = of_property_read_u32(np, "color", &color);
+ if (ret) {
+ dev_err(&chip->client->dev, "Miss color in the node\n");
+ return ret;
+ }
+ led->private_data = chip;
+ if (color == LED_COLOR_ID_RGB) {
+ count = of_get_child_count(np);
+ if (count != 3) {
+ dev_err(&chip->client->dev, "RGB must have three node.\n");
+ return -EINVAL;
+ }
+
+ info = devm_kcalloc(&chip->client->dev, 3, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ for_each_available_child_of_node(np, child) {
+ ret = of_property_read_u32(child, "reg", ®);
+ if (ret || reg > Max_Channel) {
+ dev_err(&chip->client->dev,
+ "reg must less or equal than %d\n", Max_Channel);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32(child, "color", &color);
+ if (ret) {
+ dev_err(&chip->client->dev, "color must have value\n");
+ return ret;
+ }
+
+ if (color > 3 || !color) {
+ dev_err(&chip->client->dev,
+ "color must be Red, Green and Blue. The color is %d\n", color);
+ return ret;
+ }
+ info[i].color_index = color;
+ info[i].channel = reg - 1;
+ info[i].brightness = 0;
+ i++;
+ }
+
+ led->subled_info = info;
+ led->num_colors = 3;
+ cdev = &led->cdev;
+ cdev->max_brightness = MAX_BRIGHTNESS;
+ cdev->brightness_set_blocking = led_brightness_set;
+ cdev->groups = led_sysfs_groups;
+ init_data.fwnode = &np->fwnode;
+
+ ret = devm_led_classdev_register_ext(&chip->client->dev, &led->cdev, &init_data);
+
+ if (ret) {
+ dev_err(&chip->client->dev, "Unable register multicolor:%s\n", cdev->name);
+ return ret;
+ }
+ } else {
+ ret = of_property_read_u32(np, "reg", ®);
+ if (ret || reg > Max_Channel) {
+ dev_err(&chip->client->dev,
+ "reg must less or equal than %d\n", Max_Channel);
+ return -EINVAL;
+ }
+ info = devm_kcalloc(&chip->client->dev, 1, sizeof(*info), GFP_KERNEL);
+ led->num_colors = 1;
+ info[i].color_index = LED_COLOR_ID_WHITE;
+ info[i].channel = reg - 1;
+ info[i].brightness = 0;
+ led->subled_info = info;
+ cdev = &led->cdev;
+ cdev->max_brightness = MAX_BRIGHTNESS;
+ cdev->brightness_set_blocking = led_brightness_set;
+ cdev->groups = led_sysfs_groups;
+ init_data.fwnode = &np->fwnode;
+ ret = devm_led_classdev_register_ext(&chip->client->dev, &led->cdev, &init_data);
+ if (ret) {
+ dev_err(&chip->client->dev, "Unable register led:%s\n", cdev->name);
+ return ret;
+ }
+ }
+ return ret;
+}
+
+static int mp3326_parse_dt(struct mp3326 *chip)
+{
+ struct device_node *np = dev_of_node(&chip->client->dev);
+ struct device_node *child;
+ int ret;
+ int index;
+ int val;
+
+ for_each_available_child_of_node(np, child) {
+ ret = mp3326_add_led(chip, child, index);
+ if (ret)
+ return ret;
+ index++;
+ }
+ ret = of_property_read_u32(np, "led-protect", &val);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(chip->regmap, 0x01, BIT(4) | BIT(5), val << 4);
+
+ ret = regmap_write(chip->regmap, MP3326_PWM_CTRL_CHANNEL_9_16, 0);
+ if (ret)
+ return ret;
+ ret = regmap_write(chip->regmap, MP3326_PWM_CTRL_CHANNEL_1_8, 0);
+ if (ret)
+ return ret;
+ return 0;
+}
+
+static int mp3326_leds_probe(struct i2c_client *client)
+{
+ struct mp3326 *chip;
+ const struct reg_field *reg_fields;
+ int count, i, j;
+
+ count = device_get_child_node_count(&client->dev);
+ if (!count) {
+ return dev_err_probe(&client->dev, -EINVAL,
+ "Incorrect number of leds (%d)", count);
+ }
+ chip = devm_kzalloc(&client->dev, struct_size(chip, leds, count), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ chip->client = client;
+ chip->num_of_leds = count;
+ i2c_set_clientdata(client, chip);
+ chip->regmap = devm_regmap_init_i2c(client, &MP3326_regmap_config);
+ if (IS_ERR(chip->regmap))
+ return PTR_ERR(chip->regmap);
+
+ for (i = 0; i < Max_Channel; i++) {
+ reg_fields = channels_reg_fields[i];
+ for (j = 0; j < Max_CTRL; j++) {
+ chip->regmap_fields[i][j] = devm_regmap_field_alloc(&client->dev,
+ chip->regmap, reg_fields[j]);
+ if (IS_ERR(chip->regmap_fields[i][j])) {
+ dev_err(&client->dev,
+ "regmap field alloc fail, channel:%d, item: %d\n", i, j);
+ return PTR_ERR(chip->regmap_fields[i][j]);
+ }
+ }
+ }
+ if (mp3326_parse_dt(chip))
+ return 1;
+ else
+ return 0;
+}
+
+static const struct i2c_device_id mp3326_id[] = {
+ {"mp3326", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, mp3326_id);
+
+static const struct of_device_id mp3326_of_match[] = {
+ { .compatible = "mps,mp3326" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, mp3326_of_match);
+
+static struct i2c_driver mp3326_driver = {
+ .probe_new = mp3326_leds_probe,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "MP3326_led",
+ .of_match_table = mp3326_of_match,
+ },
+ .id_table = mp3326_id,
+};
+
+module_i2c_driver(mp3326_driver);
+MODULE_AUTHOR("Yuxi Wang <Yuxi.Wang@monolithicpower.com>");
+MODULE_DESCRIPTION("MPS MP3326 LED driver");
+MODULE_LICENSE("GPL");
--
2.25.1
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH 2/2] leds: add mp3326 driver
2023-10-31 7:01 [PATCH 2/2] leds: add mp3326 driver Yuxi (Yuxi) Wang
@ 2023-10-31 8:16 ` Krzysztof Kozlowski
0 siblings, 0 replies; 8+ messages in thread
From: Krzysztof Kozlowski @ 2023-10-31 8:16 UTC (permalink / raw)
To: Yuxi (Yuxi) Wang, pavel@ucw.cz, lee@kernel.org,
robh+dt@kernel.org, krzysztof.kozlowski+dt@linaro.org,
conor+dt@kernel.org
Cc: linux-leds@vger.kernel.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org, wyx137120466@gmail.com
On 31/10/2023 08:01, Yuxi (Yuxi) Wang wrote:
>
> This patch adds mp3326 led driver.
>
> Signed-off-by: Yuxi Wang <Yuxi.Wang@monolithicpower.com>
> ---
> drivers/leds/Kconfig | 7 +
> drivers/leds/Makefile | 1 +
> drivers/leds/leds-mp3326.c | 632 +++++++++++++++++++++++++++++++++++++
> 3 files changed, 640 insertions(+)
> create mode 100644 drivers/leds/leds-mp3326.c
>
> diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
> index b92208eccdea..ac8115bffc2e 100644
> --- a/drivers/leds/Kconfig
> +++ b/drivers/leds/Kconfig
> @@ -260,6 +260,13 @@ config LEDS_MIKROTIK_RB532
> This option enables support for the so called "User LED" of
> Mikrotik's Routerboard 532.
>
> +config LEDS_MP3326
> + tristate "LED Support for MPS MP3326"
> + depends on LEDS_CLASS
> + help
> + This option enables support for on-chip LED drivers found on
> + MPS MP3326.
> +
> config LEDS_MT6323
> tristate "LED Support for Mediatek MT6323 PMIC"
> depends on LEDS_CLASS
> diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
> index d7348e8bc019..196befb56278 100644
> --- a/drivers/leds/Makefile
> +++ b/drivers/leds/Makefile
> @@ -63,6 +63,7 @@ obj-$(CONFIG_LEDS_MENF21BMC) += leds-menf21bmc.o
> obj-$(CONFIG_LEDS_MIKROTIK_RB532) += leds-rb532.o
> obj-$(CONFIG_LEDS_MLXCPLD) += leds-mlxcpld.o
> obj-$(CONFIG_LEDS_MLXREG) += leds-mlxreg.o
> +obj-$(CONFIG_LEDS_MP3326) += leds-mp3326.o
> obj-$(CONFIG_LEDS_MT6323) += leds-mt6323.o
> obj-$(CONFIG_LEDS_NET48XX) += leds-net48xx.o
> obj-$(CONFIG_LEDS_NETXBIG) += leds-netxbig.o
> diff --git a/drivers/leds/leds-mp3326.c b/drivers/leds/leds-mp3326.c
> new file mode 100644
> index 000000000000..140c71b334f7
> --- /dev/null
> +++ b/drivers/leds/leds-mp3326.c
> @@ -0,0 +1,632 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * MP3326 Led driver
> + *
> + * Copyright 2023 Monolithic Power Systems, Inc
> + *
> + * Author: Yuxi Wang <Yuxi.Wang@monolithicpower.com>
> + */
> +#include <linux/bits.h>
> +#include <linux/module.h>
> +#include <linux/mod_devicetable.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/i2c.h>
> +#include <linux/of.h>
> +#include <linux/slab.h>
> +#include <linux/interrupt.h>
> +#include <linux/mutex.h>
> +#include <linux/leds.h>
> +#include <linux/device.h>
> +#include <linux/led-class-multicolor.h>
> +
> +#define MP3326_PWM_DIM_FREQUENCY_CONFIG 0x00
> +#define MP3326_PWM_CTRL 0x01
> +#define MP3326_PWM_DIM_FREQUENCY_CONFIG 0x00
> +#define MP3326_PWM_CTRL_CHANNEL_9_16 0x04
> +#define MP3326_PWM_CTRL_CHANNEL_1_8 0x05
> +#define MP3326_PWM_OPEN_FAULT_CHANNEL_9_16 0x06
> +#define MP3326_PWM_OPEN_FAULT_CHANNEL_1_8 0x07
> +#define MP3326_PWM_SHORT_FAULT_CHANNEL_9_16 0x08
> +#define MP3326_PWM_SHORT_FAULT_CHANNEL_1_8 0x09
> +#define MP3326_PWM_CURRENT_SET_CHANNEL1 0x0A
> +#define MP3326_PWM_DUTY_LSB_SET_CHANNEL1 0x0B
> +#define MP3326_PWM_DUTY_MSB_SET_CHANNEL1 0x0C
> +#define MP3326_PWM_CURRENT_SET_CHANNEL2 0x0D
> +#define MP3326_PWM_DUTY_LSB_SET_CHANNEL2 0x0E
> +#define MP3326_PWM_DUTY_MSB_SET_CHANNEL2 0x0F
> +#define MP3326_PWM_CURRENT_SET_CHANNEL3 0x10
> +#define MP3326_PWM_DUTY_LSB_SET_CHANNEL3 0x11
> +#define MP3326_PWM_DUTY_MSB_SET_CHANNEL3 0x12
> +#define MP3326_PWM_CURRENT_SET_CHANNEL4 0x13
> +#define MP3326_PWM_DUTY_LSB_SET_CHANNEL4 0x14
> +#define MP3326_PWM_DUTY_MSB_SET_CHANNEL4 0x15
> +#define MP3326_PWM_CURRENT_SET_CHANNEL5 0x16
> +#define MP3326_PWM_DUTY_LSB_SET_CHANNEL5 0x17
> +#define MP3326_PWM_DUTY_MSB_SET_CHANNEL5 0x18
> +#define MP3326_PWM_CURRENT_SET_CHANNEL6 0x19
> +#define MP3326_PWM_DUTY_LSB_SET_CHANNEL6 0x1A
> +#define MP3326_PWM_DUTY_MSB_SET_CHANNEL6 0x1B
> +#define MP3326_PWM_CURRENT_SET_CHANNEL7 0x1C
> +#define MP3326_PWM_DUTY_LSB_SET_CHANNEL7 0x1D
> +#define MP3326_PWM_DUTY_MSB_SET_CHANNEL7 0x1E
> +#define MP3326_PWM_CURRENT_SET_CHANNEL8 0x1F
> +#define MP3326_PWM_DUTY_LSB_SET_CHANNEL8 0x20
> +#define MP3326_PWM_DUTY_MSB_SET_CHANNEL8 0x21
> +#define MP3326_PWM_CURRENT_SET_CHANNEL9 0x22
> +#define MP3326_PWM_DUTY_LSB_SET_CHANNEL9 0x23
> +#define MP3326_PWM_DUTY_MSB_SET_CHANNEL9 0x24
> +#define MP3326_PWM_CURRENT_SET_CHANNEL10 0x25
> +#define MP3326_PWM_DUTY_LSB_SET_CHANNEL10 0x26
> +#define MP3326_PWM_DUTY_MSB_SET_CHANNEL10 0x27
> +#define MP3326_PWM_CURRENT_SET_CHANNEL11 0x28
> +#define MP3326_PWM_DUTY_LSB_SET_CHANNEL11 0x29
> +#define MP3326_PWM_DUTY_MSB_SET_CHANNEL11 0x2A
> +#define MP3326_PWM_CURRENT_SET_CHANNEL12 0x2B
> +#define MP3326_PWM_DUTY_LSB_SET_CHANNEL12 0x2C
> +#define MP3326_PWM_DUTY_MSB_SET_CHANNEL12 0x2D
> +#define MP3326_PWM_CURRENT_SET_CHANNEL13 0x2E
> +#define MP3326_PWM_DUTY_LSB_SET_CHANNEL13 0x2F
> +#define MP3326_PWM_DUTY_MSB_SET_CHANNEL13 0x30
> +#define MP3326_PWM_CURRENT_SET_CHANNEL14 0x31
> +#define MP3326_PWM_DUTY_LSB_SET_CHANNEL14 0x32
> +#define MP3326_PWM_DUTY_MSB_SET_CHANNEL14 0x33
> +#define MP3326_PWM_CURRENT_SET_CHANNEL15 0x34
> +#define MP3326_PWM_DUTY_LSB_SET_CHANNEL15 0x35
> +#define MP3326_PWM_DUTY_MSB_SET_CHANNEL15 0x36
> +#define MP3326_PWM_CURRENT_SET_CHANNEL16 0x37
> +#define MP3326_PWM_DUTY_LSB_SET_CHANNEL16 0x38
> +#define MP3326_PWM_DUTY_MSB_SET_CHANNEL16 0x39
> +#define MAX_BRIGHTNESS 63
> +
> +enum led_ctrl {
> + ENABLE = 0,
> + BRIGHTNESS,
> + COLOR_L4,
> + COLOR_H8,
> + OPEN_FAULT,
> + SHORT_FAULT,
> + Max_CTRL,
Dpn't use CamelCase
> +};
> +
> +enum mp3326_channel {
> + Channel1,
> + Channel2,
> + Channel3,
> + Channel4,
> + Channel5,
> + Channel6,
> + Channel7,
> + Channel8,
> + Channel9,
> + Channel10,
> + Channel11,
> + Channel12,
> + Channel13,
> + Channel14,
> + Channel15,
> + Channel16,
Ditto
> + Max_Channel,> +};
> +
> +#define MP3326_Reg_Connect_Inner(prefix, range) prefix##range
> +#define MP3326_Reg_Connect(prefix, range) MP3326_Reg_Connect_Inner(prefix, range)
> +#define MP3326_Reg_Field(reg, minbit, maxbit) REG_FIELD(reg, minbit, maxbit)
> +#define Range1(a, b) MP3326_Reg_Connect_Inner(a, b)
> +#define Range2(a, b) MP3326_Reg_Connect_Inner(a, b)
OK, so this driver was copied from some out-of-tree, poor quality old
code not even using Linux coding style. Please rewrite everything to
match Linux coding style.
> +
> +#define MP3326_Channel_FIELD(bit, num, range) { \
> + MP3326_Reg_Field(MP3326_Reg_Connect(MP3326_PWM_CTRL_CHANNEL_, range), bit, bit), \
> + MP3326_Reg_Field(MP3326_Reg_Connect(MP3326_PWM_CURRENT_SET_CHANNEL, num), 0, 5), \
> + MP3326_Reg_Field(MP3326_Reg_Connect(MP3326_PWM_DUTY_LSB_SET_CHANNEL, num), 0, 3), \
> + MP3326_Reg_Field(MP3326_Reg_Connect(MP3326_PWM_DUTY_MSB_SET_CHANNEL, num), 0, 7), \
> + MP3326_Reg_Field(MP3326_Reg_Connect(MP3326_PWM_OPEN_FAULT_CHANNEL_, range), bit, bit), \
> + MP3326_Reg_Field(MP3326_Reg_Connect(MP3326_PWM_SHORT_FAULT_CHANNEL_, range), bit, bit), \
> + }
> +struct mp3326_led {
> + struct mp3326 *private_data;
> + struct led_classdev cdev;
> + struct mc_subled *subled_info;
> + int num_colors;
> +};
...
> +
> + for_each_available_child_of_node(np, child) {
> + ret = of_property_read_u32(child, "reg", ®);
> + if (ret || reg > Max_Channel) {
> + dev_err(&chip->client->dev,
> + "reg must less or equal than %d\n", Max_Channel);
> + return -EINVAL;
> + }
> +
> + ret = of_property_read_u32(child, "color", &color);
> + if (ret) {
> + dev_err(&chip->client->dev, "color must have value\n");
> + return ret;
> + }
> +
> + if (color > 3 || !color) {
> + dev_err(&chip->client->dev,
> + "color must be Red, Green and Blue. The color is %d\n", color);
Broken indentation. Everywhere. Be sure that checkpatch --strict does
not print any (any!) warnings.
> + return ret;
> + }
> + info[i].color_index = color;
> + info[i].channel = reg - 1;
> + info[i].brightness = 0;
> + i++;
> + }
> +
> + led->subled_info = info;
> + led->num_colors = 3;
> + cdev = &led->cdev;
> + cdev->max_brightness = MAX_BRIGHTNESS;
> + cdev->brightness_set_blocking = led_brightness_set;
> + cdev->groups = led_sysfs_groups;
> + init_data.fwnode = &np->fwnode;
> +
> + ret = devm_led_classdev_register_ext(&chip->client->dev, &led->cdev, &init_data);
> +
> + if (ret) {
> + dev_err(&chip->client->dev, "Unable register multicolor:%s\n", cdev->name);
> + return ret;
> + }
> + } else {
> + ret = of_property_read_u32(np, "reg", ®);
> + if (ret || reg > Max_Channel) {
> + dev_err(&chip->client->dev,
> + "reg must less or equal than %d\n", Max_Channel);
> + return -EINVAL;
> + }
> + info = devm_kcalloc(&chip->client->dev, 1, sizeof(*info), GFP_KERNEL);
> + led->num_colors = 1;
> + info[i].color_index = LED_COLOR_ID_WHITE;
> + info[i].channel = reg - 1;
> + info[i].brightness = 0;
> + led->subled_info = info;
> + cdev = &led->cdev;
> + cdev->max_brightness = MAX_BRIGHTNESS;
> + cdev->brightness_set_blocking = led_brightness_set;
> + cdev->groups = led_sysfs_groups;
> + init_data.fwnode = &np->fwnode;
> + ret = devm_led_classdev_register_ext(&chip->client->dev, &led->cdev, &init_data);
> + if (ret) {
> + dev_err(&chip->client->dev, "Unable register led:%s\n", cdev->name);
> + return ret;
> + }
> + }
> + return ret;
> +}
> +
> +static int mp3326_parse_dt(struct mp3326 *chip)
> +{
> + struct device_node *np = dev_of_node(&chip->client->dev);
> + struct device_node *child;
> + int ret;
> + int index;
iteration variables have name 'i'. Not index.
> + int val;
> +
> + for_each_available_child_of_node(np, child) {
> + ret = mp3326_add_led(chip, child, index);
> + if (ret)
> + return ret;
> + index++;
> + }
> + ret = of_property_read_u32(np, "led-protect", &val);
> + if (ret)
> + return ret;
> +
> + ret = regmap_update_bits(chip->regmap, 0x01, BIT(4) | BIT(5), val << 4);
> +
> + ret = regmap_write(chip->regmap, MP3326_PWM_CTRL_CHANNEL_9_16, 0);
> + if (ret)
> + return ret;
> + ret = regmap_write(chip->regmap, MP3326_PWM_CTRL_CHANNEL_1_8, 0);
> + if (ret)
> + return ret;
> + return 0;
> +}
> +
> +static int mp3326_leds_probe(struct i2c_client *client)
> +{
> + struct mp3326 *chip;
> + const struct reg_field *reg_fields;
> + int count, i, j;
> +
> + count = device_get_child_node_count(&client->dev);
> + if (!count) {
> + return dev_err_probe(&client->dev, -EINVAL,
> + "Incorrect number of leds (%d)", count);
> + }
> + chip = devm_kzalloc(&client->dev, struct_size(chip, leds, count), GFP_KERNEL);
> + if (!chip)
> + return -ENOMEM;
> +
> + chip->client = client;
> + chip->num_of_leds = count;
> + i2c_set_clientdata(client, chip);
> + chip->regmap = devm_regmap_init_i2c(client, &MP3326_regmap_config);
> + if (IS_ERR(chip->regmap))
> + return PTR_ERR(chip->regmap);
> +
> + for (i = 0; i < Max_Channel; i++) {
> + reg_fields = channels_reg_fields[i];
> + for (j = 0; j < Max_CTRL; j++) {
> + chip->regmap_fields[i][j] = devm_regmap_field_alloc(&client->dev,
> + chip->regmap, reg_fields[j]);
> + if (IS_ERR(chip->regmap_fields[i][j])) {
> + dev_err(&client->dev,
> + "regmap field alloc fail, channel:%d, item: %d\n", i, j);
> + return PTR_ERR(chip->regmap_fields[i][j]);
Messed indentation. Anyway, memory allocation errors are not usually
printed. Why should they be printed here?
> + }
> + }
> + }
> + if (mp3326_parse_dt(chip))
> + return 1;
> + else
> + return 0;
> +}
> +
> +static const struct i2c_device_id mp3326_id[] = {
> + {"mp3326", 0},
> + {}
> +};
> +MODULE_DEVICE_TABLE(i2c, mp3326_id);
> +
> +static const struct of_device_id mp3326_of_match[] = {
> + { .compatible = "mps,mp3326" },
> + {}
> +};
> +MODULE_DEVICE_TABLE(of, mp3326_of_match);
> +
> +static struct i2c_driver mp3326_driver = {
> + .probe_new = mp3326_leds_probe,
> + .driver = {
> + .owner = THIS_MODULE,
Drop. Kernel does not have it since few years.
> + .name = "MP3326_led",
One less indent.
> + .of_match_table = mp3326_of_match,
> + },
> + .id_table = mp3326_id,
> +};
> +
> +module_i2c_driver(mp3326_driver);
> +MODULE_AUTHOR("Yuxi Wang <Yuxi.Wang@monolithicpower.com>");
> +MODULE_DESCRIPTION("MPS MP3326 LED driver");
> +MODULE_LICENSE("GPL");
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 2/2] leds: add mp3326 driver
2023-11-08 3:29 [PATCH 0/2] " Yuxi Wang
@ 2023-11-08 3:29 ` Yuxi Wang
2023-11-12 11:21 ` kernel test robot
0 siblings, 1 reply; 8+ messages in thread
From: Yuxi Wang @ 2023-11-08 3:29 UTC (permalink / raw)
To: pavel, lee, robh+dt, krzysztof.kozlowski+dt, conor+dt,
wyx137120466, Yuxi.Wang
Cc: linux-leds, devicetree, linux-kernel
This patch adds mp3326 led driver.
Signed-off-by: Yuxi Wang <wyx137120466@gmail.com>
---
drivers/leds/Kconfig | 7 +
drivers/leds/Makefile | 1 +
drivers/leds/leds-mp3326.c | 627 +++++++++++++++++++++++++++++++++++++
3 files changed, 635 insertions(+)
create mode 100644 drivers/leds/leds-mp3326.c
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index b92208eccdea..ac8115bffc2e 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -260,6 +260,13 @@ config LEDS_MIKROTIK_RB532
This option enables support for the so called "User LED" of
Mikrotik's Routerboard 532.
+config LEDS_MP3326
+ tristate "LED Support for MPS MP3326"
+ depends on LEDS_CLASS
+ help
+ This option enables support for on-chip LED drivers found on
+ MPS MP3326.
+
config LEDS_MT6323
tristate "LED Support for Mediatek MT6323 PMIC"
depends on LEDS_CLASS
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index d7348e8bc019..196befb56278 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -63,6 +63,7 @@ obj-$(CONFIG_LEDS_MENF21BMC) += leds-menf21bmc.o
obj-$(CONFIG_LEDS_MIKROTIK_RB532) += leds-rb532.o
obj-$(CONFIG_LEDS_MLXCPLD) += leds-mlxcpld.o
obj-$(CONFIG_LEDS_MLXREG) += leds-mlxreg.o
+obj-$(CONFIG_LEDS_MP3326) += leds-mp3326.o
obj-$(CONFIG_LEDS_MT6323) += leds-mt6323.o
obj-$(CONFIG_LEDS_NET48XX) += leds-net48xx.o
obj-$(CONFIG_LEDS_NETXBIG) += leds-netxbig.o
diff --git a/drivers/leds/leds-mp3326.c b/drivers/leds/leds-mp3326.c
new file mode 100644
index 000000000000..c6b6398d9dc1
--- /dev/null
+++ b/drivers/leds/leds-mp3326.c
@@ -0,0 +1,627 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * MP3326 Led driver
+ *
+ * Copyright 2023 Monolithic Power Systems, Inc
+ *
+ * Author: Yuxi Wang <wyx137120466@gmail.com>
+ */
+#include <linux/bits.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/i2c.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/leds.h>
+#include <linux/device.h>
+#include <linux/led-class-multicolor.h>
+
+#define MP3326_PWM_DIM_FREQUENCY_CONFIG 0x00
+#define MP3326_PWM_CTRL 0x01
+#define MP3326_PWM_DIM_FREQUENCY_CONFIG 0x00
+#define MP3326_PWM_CTRL_CHANNEL_9_16 0x04
+#define MP3326_PWM_CTRL_CHANNEL_1_8 0x05
+#define MP3326_PWM_OPEN_FAULT_CHANNEL_9_16 0x06
+#define MP3326_PWM_OPEN_FAULT_CHANNEL_1_8 0x07
+#define MP3326_PWM_SHORT_FAULT_CHANNEL_9_16 0x08
+#define MP3326_PWM_SHORT_FAULT_CHANNEL_1_8 0x09
+#define MP3326_PWM_CURRENT_SET_CHANNEL1 0x0A
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL1 0x0B
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL1 0x0C
+#define MP3326_PWM_CURRENT_SET_CHANNEL2 0x0D
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL2 0x0E
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL2 0x0F
+#define MP3326_PWM_CURRENT_SET_CHANNEL3 0x10
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL3 0x11
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL3 0x12
+#define MP3326_PWM_CURRENT_SET_CHANNEL4 0x13
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL4 0x14
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL4 0x15
+#define MP3326_PWM_CURRENT_SET_CHANNEL5 0x16
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL5 0x17
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL5 0x18
+#define MP3326_PWM_CURRENT_SET_CHANNEL6 0x19
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL6 0x1A
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL6 0x1B
+#define MP3326_PWM_CURRENT_SET_CHANNEL7 0x1C
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL7 0x1D
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL7 0x1E
+#define MP3326_PWM_CURRENT_SET_CHANNEL8 0x1F
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL8 0x20
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL8 0x21
+#define MP3326_PWM_CURRENT_SET_CHANNEL9 0x22
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL9 0x23
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL9 0x24
+#define MP3326_PWM_CURRENT_SET_CHANNEL10 0x25
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL10 0x26
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL10 0x27
+#define MP3326_PWM_CURRENT_SET_CHANNEL11 0x28
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL11 0x29
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL11 0x2A
+#define MP3326_PWM_CURRENT_SET_CHANNEL12 0x2B
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL12 0x2C
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL12 0x2D
+#define MP3326_PWM_CURRENT_SET_CHANNEL13 0x2E
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL13 0x2F
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL13 0x30
+#define MP3326_PWM_CURRENT_SET_CHANNEL14 0x31
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL14 0x32
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL14 0x33
+#define MP3326_PWM_CURRENT_SET_CHANNEL15 0x34
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL15 0x35
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL15 0x36
+#define MP3326_PWM_CURRENT_SET_CHANNEL16 0x37
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL16 0x38
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL16 0x39
+#define MAX_BRIGHTNESS 63
+
+enum led_ctrl {
+ ENABLE = 0,
+ BRIGHTNESS,
+ COLOR_L4,
+ COLOR_H8,
+ OPEN_FAULT,
+ SHORT_FAULT,
+ MAX_CTRL,
+};
+
+enum mp3326_channel {
+ CHANNEL1,
+ CHANNEL2,
+ CHANNEL3,
+ CHANNEL4,
+ CHANNEL5,
+ CHANNEL6,
+ CHANNEL7,
+ CHANNEL8,
+ CHANNEL9,
+ CHANNEL10,
+ CHANNEL11,
+ CHANNEL12,
+ CHANNEL13,
+ CHANNEL14,
+ CHANNEL15,
+ CHANNEL16,
+ MAX_CHANNEL,
+};
+
+#define MP3326_REG_CONNECT_INNER(prefix, range) prefix##range
+#define MP3326_REG_CONNECT(prefix, range) MP3326_REG_CONNECT_INNER(prefix, range)
+#define MP3326_REG_FIELD(reg, minbit, maxbit) REG_FIELD(reg, minbit, maxbit)
+#define RANGE(a, b) MP3326_REG_CONNECT_INNER(a, b)
+
+#define MP3326_CHANNEL_FIELD(bit, num, range) { \
+ MP3326_REG_FIELD(MP3326_REG_CONNECT(MP3326_PWM_CTRL_CHANNEL, range), bit, bit), \
+ MP3326_REG_FIELD(MP3326_REG_CONNECT(MP3326_PWM_CURRENT_SET_CHANNEL, num), 0, 5), \
+ MP3326_REG_FIELD(MP3326_REG_CONNECT(MP3326_PWM_DUTY_LSB_SET_CHANNEL, num), 0, 3), \
+ MP3326_REG_FIELD(MP3326_REG_CONNECT(MP3326_PWM_DUTY_MSB_SET_CHANNEL, num), 0, 7), \
+ MP3326_REG_FIELD(MP3326_REG_CONNECT(MP3326_PWM_OPEN_FAULT_CHANNEL, range), bit, bit), \
+ MP3326_REG_FIELD(MP3326_REG_CONNECT(MP3326_PWM_SHORT_FAULT_CHANNEL, range), bit, bit), \
+ }
+struct mp3326_led {
+ struct mp3326 *private_data;
+ struct led_classdev cdev;
+ struct mc_subled *subled_info;
+ int num_colors;
+};
+
+struct mp3326 {
+ struct i2c_client *client;
+ struct regmap *regmap;
+ struct regmap_field *regmap_fields[MAX_CHANNEL][MAX_CTRL];
+ int num_of_leds;
+ struct mp3326_led leds[];
+};
+
+static const struct regmap_config MP3326_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+static const struct reg_field channels_reg_fields[MAX_CHANNEL][MAX_CTRL] = {
+ [CHANNEL1] = MP3326_CHANNEL_FIELD(0, 1, RANGE(_1, _8)),
+ [CHANNEL2] = MP3326_CHANNEL_FIELD(1, 2, RANGE(_1, _8)),
+ [CHANNEL3] = MP3326_CHANNEL_FIELD(2, 3, RANGE(_1, _8)),
+ [CHANNEL4] = MP3326_CHANNEL_FIELD(3, 4, RANGE(_1, _8)),
+ [CHANNEL5] = MP3326_CHANNEL_FIELD(4, 5, RANGE(_1, _8)),
+ [CHANNEL6] = MP3326_CHANNEL_FIELD(5, 6, RANGE(_1, _8)),
+ [CHANNEL7] = MP3326_CHANNEL_FIELD(6, 7, RANGE(_1, _8)),
+ [CHANNEL8] = MP3326_CHANNEL_FIELD(7, 8, RANGE(_1, _8)),
+ [CHANNEL9] = MP3326_CHANNEL_FIELD(0, 9, RANGE(_9, _16)),
+ [CHANNEL10] = MP3326_CHANNEL_FIELD(1, 10, RANGE(_9, _16)),
+ [CHANNEL11] = MP3326_CHANNEL_FIELD(2, 11, RANGE(_9, _16)),
+ [CHANNEL12] = MP3326_CHANNEL_FIELD(3, 12, RANGE(_9, _16)),
+ [CHANNEL13] = MP3326_CHANNEL_FIELD(4, 13, RANGE(_9, _16)),
+ [CHANNEL14] = MP3326_CHANNEL_FIELD(5, 14, RANGE(_9, _16)),
+ [CHANNEL15] = MP3326_CHANNEL_FIELD(6, 15, RANGE(_9, _16)),
+ [CHANNEL16] = MP3326_CHANNEL_FIELD(7, 16, RANGE(_9, _16)),
+};
+
+static int led_brightness_set(struct led_classdev *led_cdev, enum led_brightness brightness)
+{
+ struct mp3326_led *led = container_of(led_cdev, struct mp3326_led, cdev);
+ struct mp3326 *chip = led->private_data;
+ int ret;
+ int i;
+
+ if (brightness > led_cdev->max_brightness)
+ brightness = led_cdev->max_brightness;
+ if (brightness < 0)
+ brightness = 0;
+ for (i = 0; i < led->num_colors; i++) {
+ ret = regmap_field_write(
+ chip->regmap_fields[led->subled_info[i].channel][BRIGHTNESS],
+ brightness);
+ if (ret)
+ return ret;
+ led->subled_info[i].brightness = brightness;
+ }
+ led_cdev->brightness = brightness;
+ return 0;
+}
+
+static ssize_t led_pwm_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct led_classdev *lcdev = dev_get_drvdata(dev);
+ struct mp3326_led *led = container_of(lcdev, struct mp3326_led, cdev);
+ struct mp3326 *chip = led->private_data;
+ ssize_t ret;
+ int r_val, g_val, b_val;
+ int i;
+
+ ret = sscanf(buf, "%i %i %i", &r_val, &g_val, &b_val);
+ if (ret != 3 && ret != 1)
+ return ret;
+ r_val = r_val * 4095 / 255 + (r_val * 4095 % 255) / (255 / 2);
+ g_val = g_val * 4095 / 255 + (g_val * 4095 % 255) / (255 / 2);
+ b_val = b_val * 4095 / 255 + (b_val * 4095 % 255) / (255 / 2);
+ for (i = 0; i < led->num_colors; i++) {
+ switch (led->subled_info[i].color_index) {
+ case LED_COLOR_ID_RED:
+ ret = regmap_field_write(
+ chip->regmap_fields[led->subled_info[i].channel][COLOR_L4],
+ r_val & 0x0f);
+ if (ret)
+ return ret;
+ ret = regmap_field_write(
+ chip->regmap_fields[led->subled_info[i].channel][COLOR_H8],
+ r_val >> 4);
+ if (ret)
+ return ret;
+ break;
+ case LED_COLOR_ID_GREEN:
+ ret = regmap_field_write(
+ chip->regmap_fields[led->subled_info[i].channel][COLOR_L4],
+ g_val & 0x0f);
+ if (ret)
+ return ret;
+ ret = regmap_field_write(
+ chip->regmap_fields[led->subled_info[i].channel][COLOR_H8],
+ g_val >> 4);
+ if (ret)
+ return ret;
+ break;
+ case LED_COLOR_ID_BLUE:
+ ret = regmap_field_write(
+ chip->regmap_fields[led->subled_info[i].channel][COLOR_L4],
+ b_val & 0x0f);
+ if (ret)
+ return ret;
+ ret = regmap_field_write(
+ chip->regmap_fields[led->subled_info[i].channel][COLOR_H8],
+ b_val >> 4);
+ if (ret)
+ return ret;
+ break;
+ default:
+ ret = regmap_field_write(
+ chip->regmap_fields[led->subled_info[i].channel][COLOR_L4],
+ r_val & 0x0f);
+ if (ret)
+ return ret;
+ ret = regmap_field_write(
+ chip->regmap_fields[led->subled_info[i].channel][COLOR_H8],
+ r_val >> 4);
+ if (ret)
+ return ret;
+ break;
+ }
+ }
+ return count;
+}
+
+static ssize_t led_pwm_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct led_classdev *lcdev = dev_get_drvdata(dev);
+ struct mp3326_led *led = container_of(lcdev, struct mp3326_led, cdev);
+ struct mp3326 *chip = led->private_data;
+ ssize_t ret;
+ int r_val = 0, g_val = 0, b_val = 0, val;
+ int i;
+
+ for (i = 0; i < led->num_colors; i++) {
+ switch (led->subled_info[i].color_index) {
+ case LED_COLOR_ID_RED:
+ ret = regmap_field_read(
+ chip->regmap_fields[led->subled_info[i].channel][COLOR_L4], &val);
+ if (ret)
+ return ret;
+ r_val |= val;
+ ret = regmap_field_read(
+ chip->regmap_fields[led->subled_info[i].channel][COLOR_H8], &val);
+ if (ret)
+ return ret;
+ r_val |= val << 4;
+ break;
+ case LED_COLOR_ID_GREEN:
+ ret = regmap_field_read(
+ chip->regmap_fields[led->subled_info[i].channel][COLOR_L4], &val);
+ if (ret)
+ return ret;
+ g_val |= val;
+ ret = regmap_field_read(
+ chip->regmap_fields[led->subled_info[i].channel][COLOR_H8], &val);
+ if (ret)
+ return ret;
+ g_val |= val << 4;
+ break;
+ case LED_COLOR_ID_BLUE:
+ ret = regmap_field_read(
+ chip->regmap_fields[led->subled_info[i].channel][COLOR_L4], &val);
+ if (ret)
+ return ret;
+ b_val |= val;
+ ret = regmap_field_read(
+ chip->regmap_fields[led->subled_info[i].channel][COLOR_H8], &val);
+ if (ret)
+ return ret;
+ b_val |= val << 4;
+ break;
+ default:
+ ret = regmap_field_read(
+ chip->regmap_fields[led->subled_info[i].channel][COLOR_L4], &val);
+ if (ret)
+ return ret;
+ r_val |= val;
+ ret = regmap_field_read(
+ chip->regmap_fields[led->subled_info[i].channel][COLOR_H8], &val);
+ if (ret)
+ return ret;
+ r_val |= val << 4;
+ break;
+ }
+ }
+ r_val = r_val * 255 / 4095 + (r_val * 255 % 4095) / (4095 / 2);
+ g_val = g_val * 255 / 4095 + (g_val * 255 % 4095) / (4095 / 2);
+ b_val = b_val * 255 / 4095 + (b_val * 255 % 4095) / (4095 / 2);
+ if (led->num_colors == 1)
+ return sysfs_emit(buf, "0x%x\n", r_val);
+ else
+ return sysfs_emit(buf, "0x%x 0x%x 0x%x\n", r_val, g_val, b_val);
+}
+
+static ssize_t led_enable_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct led_classdev *lcdev = dev_get_drvdata(dev);
+ struct mp3326_led *led = container_of(lcdev, struct mp3326_led, cdev);
+ struct mp3326 *chip = led->private_data;
+ ssize_t ret;
+ uint val, i;
+
+ ret = kstrtouint(buf, 0, &val);
+ if (ret)
+ return ret;
+ for (i = 0; i < led->num_colors; i++) {
+ ret = regmap_field_write(
+ chip->regmap_fields[led->subled_info[i].channel][BRIGHTNESS],
+ led->subled_info[i].brightness);
+ if (ret)
+ return ret;
+ ret = regmap_field_write(
+ chip->regmap_fields[led->subled_info[i].channel][ENABLE], !!val);
+ if (ret)
+ return ret;
+ }
+
+ return count;
+}
+
+static ssize_t led_enable_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct led_classdev *lcdev = dev_get_drvdata(dev);
+ struct mp3326_led *led = container_of(lcdev, struct mp3326_led, cdev);
+ struct mp3326 *chip = led->private_data;
+ uint val, rval = 0;
+ int i, ret;
+
+ for (i = 0; i < led->num_colors; i++) {
+ ret = regmap_field_read(
+ chip->regmap_fields[led->subled_info[i].channel][ENABLE], &val);
+
+ rval |= val << i;
+ if (ret)
+ return ret;
+ }
+
+ if (rval)
+ return sysfs_emit(buf, "%s\n", "True");
+ else
+ return sysfs_emit(buf, "%s\n", "False");
+}
+
+static ssize_t led_short_fault_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct led_classdev *lcdev = dev_get_drvdata(dev);
+ struct mp3326_led *led = container_of(lcdev, struct mp3326_led, cdev);
+ struct mp3326 *chip = led->private_data;
+ uint val, rval = 0, i;
+ int ret;
+
+ for (i = 0; i < led->num_colors; i++) {
+ ret = regmap_field_read(
+ chip->regmap_fields[led->subled_info[i].channel][SHORT_FAULT], &val);
+ rval |= val << i;
+ if (ret)
+ return ret;
+ }
+
+ if (rval)
+ return sysfs_emit(buf, "%s\n", "Occur");
+ else
+ return sysfs_emit(buf, "%s\n", "None");
+}
+
+static ssize_t led_open_fault_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct led_classdev *lcdev = dev_get_drvdata(dev);
+ struct mp3326_led *led = container_of(lcdev, struct mp3326_led, cdev);
+ struct mp3326 *chip = led->private_data;
+ uint val, rval = 0, i;
+ int ret;
+
+ for (i = 0; i < led->num_colors; i++) {
+ ret = regmap_field_read(
+ chip->regmap_fields[led->subled_info[i].channel][OPEN_FAULT], &val);
+ rval |= val << i;
+ if (ret)
+ return ret;
+ }
+
+ if (rval)
+ return sysfs_emit(buf, "%s\n", "Occur");
+ else
+ return sysfs_emit(buf, "%s\n", "None");
+}
+
+static DEVICE_ATTR_RW(led_pwm);
+static DEVICE_ATTR_RW(led_enable);
+static DEVICE_ATTR_RO(led_short_fault);
+static DEVICE_ATTR_RO(led_open_fault);
+
+static struct attribute *led_sysfs_attrs[] = {
+ &dev_attr_led_pwm.attr,
+ &dev_attr_led_enable.attr,
+ &dev_attr_led_short_fault.attr,
+ &dev_attr_led_open_fault.attr,
+ NULL,
+};
+
+ATTRIBUTE_GROUPS(led_sysfs);
+
+static int mp3326_add_led(struct mp3326 *chip, struct device_node *np, int index)
+{
+ struct mp3326_led *led = &chip->leds[index];
+ struct mc_subled *info;
+ struct device_node *child;
+ struct led_classdev *cdev;
+ struct led_init_data init_data = {};
+ int ret;
+ int i;
+ int count;
+ u32 color = 0;
+ u32 reg = 0;
+
+ ret = of_property_read_u32(np, "color", &color);
+ if (ret) {
+ dev_err(&chip->client->dev, "Miss color in the node\n");
+ return ret;
+ }
+ led->private_data = chip;
+ if (color == LED_COLOR_ID_RGB) {
+ count = of_get_child_count(np);
+ if (count != 3) {
+ dev_err(&chip->client->dev, "RGB must have three node.\n");
+ return -EINVAL;
+ }
+
+ info = devm_kcalloc(&chip->client->dev, 3, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ for_each_available_child_of_node(np, child) {
+ ret = of_property_read_u32(child, "reg", ®);
+ if (ret || reg > MAX_CHANNEL) {
+ dev_err(&chip->client->dev,
+ "reg must less or equal than %d\n", MAX_CHANNEL);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32(child, "color", &color);
+ if (ret) {
+ dev_err(&chip->client->dev, "color must have value\n");
+ return ret;
+ }
+
+ if (color > 3 || !color) {
+ dev_err(&chip->client->dev,
+ "color must be Red, Green and Blue. The color is %d\n", color);
+ return ret;
+ }
+ info[i].color_index = color;
+ info[i].channel = reg - 1;
+ info[i].brightness = 0;
+ i++;
+ }
+
+ led->subled_info = info;
+ led->num_colors = 3;
+ cdev = &led->cdev;
+ cdev->max_brightness = MAX_BRIGHTNESS;
+ cdev->brightness_set_blocking = led_brightness_set;
+ cdev->groups = led_sysfs_groups;
+ init_data.fwnode = &np->fwnode;
+
+ ret = devm_led_classdev_register_ext(&chip->client->dev, &led->cdev, &init_data);
+
+ if (ret) {
+ dev_err(&chip->client->dev, "Unable register multicolor:%s\n", cdev->name);
+ return ret;
+ }
+ } else {
+ ret = of_property_read_u32(np, "reg", ®);
+ if (ret || reg > MAX_CHANNEL) {
+ dev_err(&chip->client->dev,
+ "reg must less or equal than %d\n", MAX_CHANNEL);
+ return -EINVAL;
+ }
+ info = devm_kcalloc(&chip->client->dev, 1, sizeof(*info), GFP_KERNEL);
+ led->num_colors = 1;
+ info[i].color_index = LED_COLOR_ID_WHITE;
+ info[i].channel = reg - 1;
+ info[i].brightness = 0;
+ led->subled_info = info;
+ cdev = &led->cdev;
+ cdev->max_brightness = MAX_BRIGHTNESS;
+ cdev->brightness_set_blocking = led_brightness_set;
+ cdev->groups = led_sysfs_groups;
+ init_data.fwnode = &np->fwnode;
+ ret = devm_led_classdev_register_ext(&chip->client->dev, &led->cdev, &init_data);
+ if (ret) {
+ dev_err(&chip->client->dev, "Unable register led:%s\n", cdev->name);
+ return ret;
+ }
+ }
+ return ret;
+}
+
+static int mp3326_parse_dt(struct mp3326 *chip)
+{
+ struct device_node *np = dev_of_node(&chip->client->dev);
+ struct device_node *child;
+ int ret;
+ int i;
+ int val;
+
+ for_each_available_child_of_node(np, child) {
+ ret = mp3326_add_led(chip, child, i);
+ if (ret)
+ return ret;
+ i++;
+ }
+ ret = of_property_read_u32(np, "led-protect", &val);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(chip->regmap, 0x01, BIT(4) | BIT(5), val << 4);
+
+ ret = regmap_write(chip->regmap, MP3326_PWM_CTRL_CHANNEL_9_16, 0);
+ if (ret)
+ return ret;
+ ret = regmap_write(chip->regmap, MP3326_PWM_CTRL_CHANNEL_1_8, 0);
+ if (ret)
+ return ret;
+ return 0;
+}
+
+static int mp3326_leds_probe(struct i2c_client *client)
+{
+ struct mp3326 *chip;
+ const struct reg_field *reg_fields;
+ int count, i, j;
+
+ count = device_get_child_node_count(&client->dev);
+ if (!count) {
+ return dev_err_probe(&client->dev, -EINVAL,
+ "Incorrect number of leds (%d)", count);
+ }
+ chip = devm_kzalloc(&client->dev, struct_size(chip, leds, count), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ chip->client = client;
+ chip->num_of_leds = count;
+ i2c_set_clientdata(client, chip);
+ chip->regmap = devm_regmap_init_i2c(client, &MP3326_regmap_config);
+ if (IS_ERR(chip->regmap))
+ return PTR_ERR(chip->regmap);
+
+ for (i = 0; i < MAX_CHANNEL; i++) {
+ reg_fields = channels_reg_fields[i];
+ for (j = 0; j < MAX_CTRL; j++) {
+ chip->regmap_fields[i][j] = devm_regmap_field_alloc(&client->dev,
+ chip->regmap, reg_fields[j]);
+ if (IS_ERR(chip->regmap_fields[i][j]))
+ return PTR_ERR(chip->regmap_fields[i][j]);
+ }
+ }
+ if (mp3326_parse_dt(chip))
+ return 1;
+ else
+ return 0;
+}
+
+static const struct i2c_device_id mp3326_id[] = {
+ {"mp3326", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, mp3326_id);
+
+static const struct of_device_id mp3326_of_match[] = {
+ { .compatible = "mps,mp3326" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, mp3326_of_match);
+
+static struct i2c_driver mp3326_driver = {
+ .probe_new = mp3326_leds_probe,
+ .driver = {
+ .name = "mp3326_led",
+ .of_match_table = mp3326_of_match,
+ },
+ .id_table = mp3326_id,
+};
+
+module_i2c_driver(mp3326_driver);
+MODULE_AUTHOR("Yuxi Wang <wyx137120466@gmail.com>");
+MODULE_DESCRIPTION("MPS MP3326 LED driver");
+MODULE_LICENSE("GPL");
--
2.25.1
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH 2/2] leds: add mp3326 driver
2023-11-08 3:29 ` [PATCH 2/2] " Yuxi Wang
@ 2023-11-12 11:21 ` kernel test robot
0 siblings, 0 replies; 8+ messages in thread
From: kernel test robot @ 2023-11-12 11:21 UTC (permalink / raw)
To: Yuxi Wang, pavel, lee, robh+dt, krzysztof.kozlowski+dt, conor+dt,
Yuxi.Wang
Cc: llvm, oe-kbuild-all, linux-leds, devicetree, linux-kernel
Hi Yuxi,
kernel test robot noticed the following build warnings:
[auto build test WARNING on lee-leds/for-leds-next]
[also build test WARNING on robh/for-next pavel-leds/for-next linus/master v6.6 next-20231110]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Yuxi-Wang/dt-bindings-leds-add-mps-mp3326-LED/20231108-113235
base: https://git.kernel.org/pub/scm/linux/kernel/git/lee/leds.git for-leds-next
patch link: https://lore.kernel.org/r/20231108032921.3134115-3-wyx137120466%40gmail.com
patch subject: [PATCH 2/2] leds: add mp3326 driver
config: arm64-allmodconfig (https://download.01.org/0day-ci/archive/20231112/202311121908.Kyj8FdWx-lkp@intel.com/config)
compiler: clang version 17.0.0 (https://github.com/llvm/llvm-project.git 4a5ac14ee968ff0ad5d2cc1ffa0299048db4c88a)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20231112/202311121908.Kyj8FdWx-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202311121908.Kyj8FdWx-lkp@intel.com/
All warnings (new ones prefixed by >>):
>> drivers/leds/leds-mp3326.c:490:9: warning: variable 'i' is uninitialized when used here [-Wuninitialized]
490 | info[i].color_index = color;
| ^
drivers/leds/leds-mp3326.c:449:7: note: initialize the variable 'i' to silence this warning
449 | int i;
| ^
| = 0
drivers/leds/leds-mp3326.c:546:37: warning: variable 'i' is uninitialized when used here [-Wuninitialized]
546 | ret = mp3326_add_led(chip, child, i);
| ^
drivers/leds/leds-mp3326.c:542:7: note: initialize the variable 'i' to silence this warning
542 | int i;
| ^
| = 0
2 warnings generated.
vim +/i +490 drivers/leds/leds-mp3326.c
440
441 static int mp3326_add_led(struct mp3326 *chip, struct device_node *np, int index)
442 {
443 struct mp3326_led *led = &chip->leds[index];
444 struct mc_subled *info;
445 struct device_node *child;
446 struct led_classdev *cdev;
447 struct led_init_data init_data = {};
448 int ret;
449 int i;
450 int count;
451 u32 color = 0;
452 u32 reg = 0;
453
454 ret = of_property_read_u32(np, "color", &color);
455 if (ret) {
456 dev_err(&chip->client->dev, "Miss color in the node\n");
457 return ret;
458 }
459 led->private_data = chip;
460 if (color == LED_COLOR_ID_RGB) {
461 count = of_get_child_count(np);
462 if (count != 3) {
463 dev_err(&chip->client->dev, "RGB must have three node.\n");
464 return -EINVAL;
465 }
466
467 info = devm_kcalloc(&chip->client->dev, 3, sizeof(*info), GFP_KERNEL);
468 if (!info)
469 return -ENOMEM;
470
471 for_each_available_child_of_node(np, child) {
472 ret = of_property_read_u32(child, "reg", ®);
473 if (ret || reg > MAX_CHANNEL) {
474 dev_err(&chip->client->dev,
475 "reg must less or equal than %d\n", MAX_CHANNEL);
476 return -EINVAL;
477 }
478
479 ret = of_property_read_u32(child, "color", &color);
480 if (ret) {
481 dev_err(&chip->client->dev, "color must have value\n");
482 return ret;
483 }
484
485 if (color > 3 || !color) {
486 dev_err(&chip->client->dev,
487 "color must be Red, Green and Blue. The color is %d\n", color);
488 return ret;
489 }
> 490 info[i].color_index = color;
491 info[i].channel = reg - 1;
492 info[i].brightness = 0;
493 i++;
494 }
495
496 led->subled_info = info;
497 led->num_colors = 3;
498 cdev = &led->cdev;
499 cdev->max_brightness = MAX_BRIGHTNESS;
500 cdev->brightness_set_blocking = led_brightness_set;
501 cdev->groups = led_sysfs_groups;
502 init_data.fwnode = &np->fwnode;
503
504 ret = devm_led_classdev_register_ext(&chip->client->dev, &led->cdev, &init_data);
505
506 if (ret) {
507 dev_err(&chip->client->dev, "Unable register multicolor:%s\n", cdev->name);
508 return ret;
509 }
510 } else {
511 ret = of_property_read_u32(np, "reg", ®);
512 if (ret || reg > MAX_CHANNEL) {
513 dev_err(&chip->client->dev,
514 "reg must less or equal than %d\n", MAX_CHANNEL);
515 return -EINVAL;
516 }
517 info = devm_kcalloc(&chip->client->dev, 1, sizeof(*info), GFP_KERNEL);
518 led->num_colors = 1;
519 info[i].color_index = LED_COLOR_ID_WHITE;
520 info[i].channel = reg - 1;
521 info[i].brightness = 0;
522 led->subled_info = info;
523 cdev = &led->cdev;
524 cdev->max_brightness = MAX_BRIGHTNESS;
525 cdev->brightness_set_blocking = led_brightness_set;
526 cdev->groups = led_sysfs_groups;
527 init_data.fwnode = &np->fwnode;
528 ret = devm_led_classdev_register_ext(&chip->client->dev, &led->cdev, &init_data);
529 if (ret) {
530 dev_err(&chip->client->dev, "Unable register led:%s\n", cdev->name);
531 return ret;
532 }
533 }
534 return ret;
535 }
536
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 2/2] leds: add mp3326 driver
2023-11-24 9:30 [PATCH 0/2] leds: Add a driver for MP3326 Yuxi Wang
@ 2023-11-24 9:30 ` Yuxi Wang
2023-11-24 15:42 ` kernel test robot
` (2 more replies)
0 siblings, 3 replies; 8+ messages in thread
From: Yuxi Wang @ 2023-11-24 9:30 UTC (permalink / raw)
To: pavel, lee, robh+dt, krzysztof.kozlowski+dt, conor+dt, Yuxi.Wang
Cc: linux-kernel, linux-leds, devicetree, wyx137120466
This commit adds support for MPS MP3326 LED driver.
Signed-off-by: Yuxi Wang <Yuxi.Wang@monolithicpower.com>
---
drivers/leds/Kconfig | 7 +
drivers/leds/Makefile | 1 +
drivers/leds/leds-mp3326.c | 622 +++++++++++++++++++++++++++++++++++++
3 files changed, 630 insertions(+)
create mode 100644 drivers/leds/leds-mp3326.c
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index bfa11e7b157f..f9da210c89a2 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -262,6 +262,13 @@ config LEDS_MIKROTIK_RB532
This option enables support for the so called "User LED" of
Mikrotik's Routerboard 532.
+config LEDS_MP3326
+ tristate "LED Support for MPS MP3326"
+ depends on LEDS_CLASS
+ help
+ This option enables support for on-chip LED drivers found on
+ MPS MP3326.
+
config LEDS_MT6323
tristate "LED Support for Mediatek MT6323 PMIC"
depends on LEDS_CLASS
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index ce07dc295ff0..c0a8b4b3e7c8 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -64,6 +64,7 @@ obj-$(CONFIG_LEDS_MENF21BMC) += leds-menf21bmc.o
obj-$(CONFIG_LEDS_MIKROTIK_RB532) += leds-rb532.o
obj-$(CONFIG_LEDS_MLXCPLD) += leds-mlxcpld.o
obj-$(CONFIG_LEDS_MLXREG) += leds-mlxreg.o
+obj-$(CONFIG_LEDS_MP3326) += leds-mp3326.o
obj-$(CONFIG_LEDS_MT6323) += leds-mt6323.o
obj-$(CONFIG_LEDS_NET48XX) += leds-net48xx.o
obj-$(CONFIG_LEDS_NETXBIG) += leds-netxbig.o
diff --git a/drivers/leds/leds-mp3326.c b/drivers/leds/leds-mp3326.c
new file mode 100644
index 000000000000..aca95da8c839
--- /dev/null
+++ b/drivers/leds/leds-mp3326.c
@@ -0,0 +1,622 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * MP3326 Led driver
+ *
+ * Copyright 2023 Monolithic Power Systems, Inc
+ *
+ * Author: Yuxi Wang <wyx137120466@gmail.com>
+ */
+#include <linux/bits.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/i2c.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/leds.h>
+#include <linux/device.h>
+#include <linux/led-class-multicolor.h>
+
+#define MP3326_PWM_DIM_FREQUENCY_CONFIG 0x00
+#define MP3326_PWM_CTRL 0x01
+#define MP3326_PWM_DIM_FREQUENCY_CONFIG 0x00
+#define MP3326_PWM_CTRL_CHANNEL_9_16 0x04
+#define MP3326_PWM_CTRL_CHANNEL_1_8 0x05
+#define MP3326_PWM_OPEN_FAULT_CHANNEL_9_16 0x06
+#define MP3326_PWM_OPEN_FAULT_CHANNEL_1_8 0x07
+#define MP3326_PWM_SHORT_FAULT_CHANNEL_9_16 0x08
+#define MP3326_PWM_SHORT_FAULT_CHANNEL_1_8 0x09
+#define MP3326_PWM_CURRENT_SET_CHANNEL1 0x0A
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL1 0x0B
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL1 0x0C
+#define MP3326_PWM_CURRENT_SET_CHANNEL2 0x0D
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL2 0x0E
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL2 0x0F
+#define MP3326_PWM_CURRENT_SET_CHANNEL3 0x10
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL3 0x11
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL3 0x12
+#define MP3326_PWM_CURRENT_SET_CHANNEL4 0x13
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL4 0x14
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL4 0x15
+#define MP3326_PWM_CURRENT_SET_CHANNEL5 0x16
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL5 0x17
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL5 0x18
+#define MP3326_PWM_CURRENT_SET_CHANNEL6 0x19
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL6 0x1A
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL6 0x1B
+#define MP3326_PWM_CURRENT_SET_CHANNEL7 0x1C
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL7 0x1D
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL7 0x1E
+#define MP3326_PWM_CURRENT_SET_CHANNEL8 0x1F
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL8 0x20
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL8 0x21
+#define MP3326_PWM_CURRENT_SET_CHANNEL9 0x22
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL9 0x23
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL9 0x24
+#define MP3326_PWM_CURRENT_SET_CHANNEL10 0x25
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL10 0x26
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL10 0x27
+#define MP3326_PWM_CURRENT_SET_CHANNEL11 0x28
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL11 0x29
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL11 0x2A
+#define MP3326_PWM_CURRENT_SET_CHANNEL12 0x2B
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL12 0x2C
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL12 0x2D
+#define MP3326_PWM_CURRENT_SET_CHANNEL13 0x2E
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL13 0x2F
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL13 0x30
+#define MP3326_PWM_CURRENT_SET_CHANNEL14 0x31
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL14 0x32
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL14 0x33
+#define MP3326_PWM_CURRENT_SET_CHANNEL15 0x34
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL15 0x35
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL15 0x36
+#define MP3326_PWM_CURRENT_SET_CHANNEL16 0x37
+#define MP3326_PWM_DUTY_LSB_SET_CHANNEL16 0x38
+#define MP3326_PWM_DUTY_MSB_SET_CHANNEL16 0x39
+#define MAX_BRIGHTNESS 63
+
+enum led_ctrl {
+ ENABLE = 0,
+ BRIGHTNESS,
+ COLOR_L4,
+ COLOR_H8,
+ OPEN_FAULT,
+ SHORT_FAULT,
+ MAX_CTRL,
+};
+
+enum mp3326_channel {
+ CHANNEL1,
+ CHANNEL2,
+ CHANNEL3,
+ CHANNEL4,
+ CHANNEL5,
+ CHANNEL6,
+ CHANNEL7,
+ CHANNEL8,
+ CHANNEL9,
+ CHANNEL10,
+ CHANNEL11,
+ CHANNEL12,
+ CHANNEL13,
+ CHANNEL14,
+ CHANNEL15,
+ CHANNEL16,
+ MAX_CHANNEL,
+};
+
+#define MP3326_REG_CONNECT_INNER(prefix, range) prefix##range
+#define MP3326_REG_CONNECT(prefix, range) MP3326_REG_CONNECT_INNER(prefix, range)
+#define MP3326_REG_FIELD(reg, minbit, maxbit) REG_FIELD(reg, minbit, maxbit)
+#define RANGE(a, b) MP3326_REG_CONNECT_INNER(a, b)
+
+#define MP3326_CHANNEL_FIELD(bit, num, range) { \
+ MP3326_REG_FIELD(MP3326_REG_CONNECT(MP3326_PWM_CTRL_CHANNEL, range), bit, bit), \
+ MP3326_REG_FIELD(MP3326_REG_CONNECT(MP3326_PWM_CURRENT_SET_CHANNEL, num), 0, 5), \
+ MP3326_REG_FIELD(MP3326_REG_CONNECT(MP3326_PWM_DUTY_LSB_SET_CHANNEL, num), 0, 3), \
+ MP3326_REG_FIELD(MP3326_REG_CONNECT(MP3326_PWM_DUTY_MSB_SET_CHANNEL, num), 0, 7), \
+ MP3326_REG_FIELD(MP3326_REG_CONNECT(MP3326_PWM_OPEN_FAULT_CHANNEL, range), bit, bit), \
+ MP3326_REG_FIELD(MP3326_REG_CONNECT(MP3326_PWM_SHORT_FAULT_CHANNEL, range), bit, bit), \
+ }
+struct mp3326_led {
+ struct mp3326 *private_data;
+ struct led_classdev cdev;
+ struct mc_subled *subled_info;
+ int num_colors;
+};
+
+struct mp3326 {
+ struct i2c_client *client;
+ struct regmap *regmap;
+ struct regmap_field *regmap_fields[MAX_CHANNEL][MAX_CTRL];
+ int num_of_leds;
+ struct mp3326_led leds[];
+};
+
+static const struct regmap_config MP3326_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+static const struct reg_field channels_reg_fields[MAX_CHANNEL][MAX_CTRL] = {
+ [CHANNEL1] = MP3326_CHANNEL_FIELD(0, 1, RANGE(_1, _8)),
+ [CHANNEL2] = MP3326_CHANNEL_FIELD(1, 2, RANGE(_1, _8)),
+ [CHANNEL3] = MP3326_CHANNEL_FIELD(2, 3, RANGE(_1, _8)),
+ [CHANNEL4] = MP3326_CHANNEL_FIELD(3, 4, RANGE(_1, _8)),
+ [CHANNEL5] = MP3326_CHANNEL_FIELD(4, 5, RANGE(_1, _8)),
+ [CHANNEL6] = MP3326_CHANNEL_FIELD(5, 6, RANGE(_1, _8)),
+ [CHANNEL7] = MP3326_CHANNEL_FIELD(6, 7, RANGE(_1, _8)),
+ [CHANNEL8] = MP3326_CHANNEL_FIELD(7, 8, RANGE(_1, _8)),
+ [CHANNEL9] = MP3326_CHANNEL_FIELD(0, 9, RANGE(_9, _16)),
+ [CHANNEL10] = MP3326_CHANNEL_FIELD(1, 10, RANGE(_9, _16)),
+ [CHANNEL11] = MP3326_CHANNEL_FIELD(2, 11, RANGE(_9, _16)),
+ [CHANNEL12] = MP3326_CHANNEL_FIELD(3, 12, RANGE(_9, _16)),
+ [CHANNEL13] = MP3326_CHANNEL_FIELD(4, 13, RANGE(_9, _16)),
+ [CHANNEL14] = MP3326_CHANNEL_FIELD(5, 14, RANGE(_9, _16)),
+ [CHANNEL15] = MP3326_CHANNEL_FIELD(6, 15, RANGE(_9, _16)),
+ [CHANNEL16] = MP3326_CHANNEL_FIELD(7, 16, RANGE(_9, _16)),
+};
+
+static int led_brightness_set(struct led_classdev *led_cdev, enum led_brightness brightness)
+{
+ struct mp3326_led *led = container_of(led_cdev, struct mp3326_led, cdev);
+ struct mp3326 *chip = led->private_data;
+ int ret;
+ int i;
+
+ if (brightness > led_cdev->max_brightness)
+ brightness = led_cdev->max_brightness;
+ if (brightness < 0)
+ brightness = 0;
+ for (i = 0; i < led->num_colors; i++) {
+ ret = regmap_field_write(
+ chip->regmap_fields[led->subled_info[i].channel][BRIGHTNESS],
+ brightness);
+ if (ret)
+ return ret;
+ led->subled_info[i].brightness = brightness;
+ }
+ led_cdev->brightness = brightness;
+ return 0;
+}
+
+static ssize_t led_pwm_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct led_classdev *lcdev = dev_get_drvdata(dev);
+ struct mp3326_led *led = container_of(lcdev, struct mp3326_led, cdev);
+ struct mp3326 *chip = led->private_data;
+ ssize_t ret;
+ int r_val, g_val, b_val;
+ int i;
+
+ ret = sscanf(buf, "%i %i %i", &r_val, &g_val, &b_val);
+ if (ret != 3 && ret != 1)
+ return ret;
+ r_val = r_val * 4095 / 255 + (r_val * 4095 % 255) / (255 / 2);
+ g_val = g_val * 4095 / 255 + (g_val * 4095 % 255) / (255 / 2);
+ b_val = b_val * 4095 / 255 + (b_val * 4095 % 255) / (255 / 2);
+ for (i = 0; i < led->num_colors; i++) {
+ switch (led->subled_info[i].color_index) {
+ case LED_COLOR_ID_RED:
+ ret = regmap_field_write(
+ chip->regmap_fields[led->subled_info[i].channel][COLOR_L4],
+ r_val & 0x0f);
+ if (ret)
+ return ret;
+ ret = regmap_field_write(
+ chip->regmap_fields[led->subled_info[i].channel][COLOR_H8],
+ r_val >> 4);
+ if (ret)
+ return ret;
+ break;
+ case LED_COLOR_ID_GREEN:
+ ret = regmap_field_write(
+ chip->regmap_fields[led->subled_info[i].channel][COLOR_L4],
+ g_val & 0x0f);
+ if (ret)
+ return ret;
+ ret = regmap_field_write(
+ chip->regmap_fields[led->subled_info[i].channel][COLOR_H8],
+ g_val >> 4);
+ if (ret)
+ return ret;
+ break;
+ case LED_COLOR_ID_BLUE:
+ ret = regmap_field_write(
+ chip->regmap_fields[led->subled_info[i].channel][COLOR_L4],
+ b_val & 0x0f);
+ if (ret)
+ return ret;
+ ret = regmap_field_write(
+ chip->regmap_fields[led->subled_info[i].channel][COLOR_H8],
+ b_val >> 4);
+ if (ret)
+ return ret;
+ break;
+ default:
+ ret = regmap_field_write(
+ chip->regmap_fields[led->subled_info[i].channel][COLOR_L4],
+ r_val & 0x0f);
+ if (ret)
+ return ret;
+ ret = regmap_field_write(
+ chip->regmap_fields[led->subled_info[i].channel][COLOR_H8],
+ r_val >> 4);
+ if (ret)
+ return ret;
+ break;
+ }
+ }
+ return count;
+}
+
+static ssize_t led_pwm_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct led_classdev *lcdev = dev_get_drvdata(dev);
+ struct mp3326_led *led = container_of(lcdev, struct mp3326_led, cdev);
+ struct mp3326 *chip = led->private_data;
+ ssize_t ret;
+ int r_val = 0, g_val = 0, b_val = 0, val;
+ int i;
+
+ for (i = 0; i < led->num_colors; i++) {
+ switch (led->subled_info[i].color_index) {
+ case LED_COLOR_ID_RED:
+ ret = regmap_field_read(
+ chip->regmap_fields[led->subled_info[i].channel][COLOR_L4], &val);
+ if (ret)
+ return ret;
+ r_val |= val;
+ ret = regmap_field_read(
+ chip->regmap_fields[led->subled_info[i].channel][COLOR_H8], &val);
+ if (ret)
+ return ret;
+ r_val |= val << 4;
+ break;
+ case LED_COLOR_ID_GREEN:
+ ret = regmap_field_read(
+ chip->regmap_fields[led->subled_info[i].channel][COLOR_L4], &val);
+ if (ret)
+ return ret;
+ g_val |= val;
+ ret = regmap_field_read(
+ chip->regmap_fields[led->subled_info[i].channel][COLOR_H8], &val);
+ if (ret)
+ return ret;
+ g_val |= val << 4;
+ break;
+ case LED_COLOR_ID_BLUE:
+ ret = regmap_field_read(
+ chip->regmap_fields[led->subled_info[i].channel][COLOR_L4], &val);
+ if (ret)
+ return ret;
+ b_val |= val;
+ ret = regmap_field_read(
+ chip->regmap_fields[led->subled_info[i].channel][COLOR_H8], &val);
+ if (ret)
+ return ret;
+ b_val |= val << 4;
+ break;
+ default:
+ ret = regmap_field_read(
+ chip->regmap_fields[led->subled_info[i].channel][COLOR_L4], &val);
+ if (ret)
+ return ret;
+ r_val |= val;
+ ret = regmap_field_read(
+ chip->regmap_fields[led->subled_info[i].channel][COLOR_H8], &val);
+ if (ret)
+ return ret;
+ r_val |= val << 4;
+ break;
+ }
+ }
+ r_val = r_val * 255 / 4095 + (r_val * 255 % 4095) / (4095 / 2);
+ g_val = g_val * 255 / 4095 + (g_val * 255 % 4095) / (4095 / 2);
+ b_val = b_val * 255 / 4095 + (b_val * 255 % 4095) / (4095 / 2);
+ if (led->num_colors == 1)
+ return sysfs_emit(buf, "0x%x\n", r_val);
+ else
+ return sysfs_emit(buf, "0x%x 0x%x 0x%x\n", r_val, g_val, b_val);
+}
+
+static ssize_t led_enable_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct led_classdev *lcdev = dev_get_drvdata(dev);
+ struct mp3326_led *led = container_of(lcdev, struct mp3326_led, cdev);
+ struct mp3326 *chip = led->private_data;
+ ssize_t ret;
+ uint val, i;
+
+ ret = kstrtouint(buf, 0, &val);
+ if (ret)
+ return ret;
+ for (i = 0; i < led->num_colors; i++) {
+ ret = regmap_field_write(
+ chip->regmap_fields[led->subled_info[i].channel][BRIGHTNESS],
+ led->subled_info[i].brightness);
+ if (ret)
+ return ret;
+ ret = regmap_field_write(
+ chip->regmap_fields[led->subled_info[i].channel][ENABLE], !!val);
+ if (ret)
+ return ret;
+ }
+
+ return count;
+}
+
+static ssize_t led_enable_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct led_classdev *lcdev = dev_get_drvdata(dev);
+ struct mp3326_led *led = container_of(lcdev, struct mp3326_led, cdev);
+ struct mp3326 *chip = led->private_data;
+ uint val, rval = 0;
+ int i, ret;
+
+ for (i = 0; i < led->num_colors; i++) {
+ ret = regmap_field_read(
+ chip->regmap_fields[led->subled_info[i].channel][ENABLE], &val);
+
+ rval |= val << i;
+ if (ret)
+ return ret;
+ }
+
+ if (rval)
+ return sysfs_emit(buf, "%s\n", "True");
+ else
+ return sysfs_emit(buf, "%s\n", "False");
+}
+
+static ssize_t led_short_fault_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct led_classdev *lcdev = dev_get_drvdata(dev);
+ struct mp3326_led *led = container_of(lcdev, struct mp3326_led, cdev);
+ struct mp3326 *chip = led->private_data;
+ uint val, rval = 0, i;
+ int ret;
+
+ for (i = 0; i < led->num_colors; i++) {
+ ret = regmap_field_read(
+ chip->regmap_fields[led->subled_info[i].channel][SHORT_FAULT], &val);
+ rval |= val << i;
+ if (ret)
+ return ret;
+ }
+
+ if (rval)
+ return sysfs_emit(buf, "%s\n", "Occur");
+ else
+ return sysfs_emit(buf, "%s\n", "None");
+}
+
+static ssize_t led_open_fault_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct led_classdev *lcdev = dev_get_drvdata(dev);
+ struct mp3326_led *led = container_of(lcdev, struct mp3326_led, cdev);
+ struct mp3326 *chip = led->private_data;
+ uint val, rval = 0, i;
+ int ret;
+
+ for (i = 0; i < led->num_colors; i++) {
+ ret = regmap_field_read(
+ chip->regmap_fields[led->subled_info[i].channel][OPEN_FAULT], &val);
+ rval |= val << i;
+ if (ret)
+ return ret;
+ }
+
+ if (rval)
+ return sysfs_emit(buf, "%s\n", "Occur");
+ else
+ return sysfs_emit(buf, "%s\n", "None");
+}
+
+static DEVICE_ATTR_RW(led_pwm);
+static DEVICE_ATTR_RW(led_enable);
+static DEVICE_ATTR_RO(led_short_fault);
+static DEVICE_ATTR_RO(led_open_fault);
+
+static struct attribute *led_sysfs_attrs[] = {
+ &dev_attr_led_pwm.attr,
+ &dev_attr_led_enable.attr,
+ &dev_attr_led_short_fault.attr,
+ &dev_attr_led_open_fault.attr,
+ NULL,
+};
+
+ATTRIBUTE_GROUPS(led_sysfs);
+
+static int mp3326_add_led(struct mp3326 *chip, struct device_node *np, int index)
+{
+ struct mp3326_led *led = &chip->leds[index];
+ struct mc_subled *info;
+ struct device_node *child;
+ struct led_classdev *cdev;
+ struct led_init_data init_data = {};
+ int ret;
+ int i = 0;
+ int count;
+ u32 color = 0;
+ u32 reg = 0;
+
+ ret = of_property_read_u32(np, "color", &color);
+ if (ret) {
+ dev_err(&chip->client->dev, "Miss color in the node\n");
+ return ret;
+ }
+ led->private_data = chip;
+ if (color == LED_COLOR_ID_RGB) {
+ count = of_get_child_count(np);
+ if (count != 3) {
+ dev_err(&chip->client->dev, "RGB must have three node.\n");
+ return -EINVAL;
+ }
+
+ info = devm_kcalloc(&chip->client->dev, 3, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ for_each_available_child_of_node(np, child) {
+ ret = of_property_read_u32(child, "reg", ®);
+ if (ret || reg > MAX_CHANNEL) {
+ dev_err(&chip->client->dev,
+ "reg must less or equal than %d\n", MAX_CHANNEL);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32(child, "color", &color);
+ if (ret) {
+ dev_err(&chip->client->dev, "color must have value\n");
+ return ret;
+ }
+
+ if (color > 3 || !color) {
+ dev_err(&chip->client->dev,
+ "color must be Red, Green and Blue. The color is %d\n", color);
+ return ret;
+ }
+ info[i].color_index = color;
+ info[i].channel = reg - 1;
+ info[i].brightness = 0;
+ i++;
+ }
+
+ led->subled_info = info;
+ led->num_colors = 3;
+ cdev = &led->cdev;
+ cdev->max_brightness = MAX_BRIGHTNESS;
+ cdev->brightness_set_blocking = led_brightness_set;
+ cdev->groups = led_sysfs_groups;
+ init_data.fwnode = &np->fwnode;
+
+ ret = devm_led_classdev_register_ext(&chip->client->dev, &led->cdev, &init_data);
+
+ if (ret) {
+ dev_err(&chip->client->dev, "Unable register multicolor:%s\n", cdev->name);
+ return ret;
+ }
+ } else {
+ ret = of_property_read_u32(np, "reg", ®);
+ if (ret || reg > MAX_CHANNEL) {
+ dev_err(&chip->client->dev,
+ "reg must less or equal than %d\n", MAX_CHANNEL);
+ return -EINVAL;
+ }
+ info = devm_kcalloc(&chip->client->dev, 1, sizeof(*info), GFP_KERNEL);
+ led->num_colors = 1;
+ info[i].color_index = LED_COLOR_ID_WHITE;
+ info[i].channel = reg - 1;
+ info[i].brightness = 0;
+ led->subled_info = info;
+ cdev = &led->cdev;
+ cdev->max_brightness = MAX_BRIGHTNESS;
+ cdev->brightness_set_blocking = led_brightness_set;
+ cdev->groups = led_sysfs_groups;
+ init_data.fwnode = &np->fwnode;
+ ret = devm_led_classdev_register_ext(&chip->client->dev, &led->cdev, &init_data);
+ if (ret) {
+ dev_err(&chip->client->dev, "Unable register led:%s\n", cdev->name);
+ return ret;
+ }
+ }
+ return ret;
+}
+
+static int mp3326_parse_dt(struct mp3326 *chip)
+{
+ struct device_node *np = dev_of_node(&chip->client->dev);
+ struct device_node *child;
+ int ret;
+ int i = 0;
+ int val;
+
+ for_each_available_child_of_node(np, child) {
+ ret = mp3326_add_led(chip, child, i);
+ if (ret)
+ return ret;
+ i++;
+ }
+
+ ret = regmap_write(chip->regmap, MP3326_PWM_CTRL_CHANNEL_9_16, 0);
+ if (ret)
+ return ret;
+ ret = regmap_write(chip->regmap, MP3326_PWM_CTRL_CHANNEL_1_8, 0);
+ if (ret)
+ return ret;
+ return 0;
+}
+
+static int mp3326_leds_probe(struct i2c_client *client)
+{
+ struct mp3326 *chip;
+ const struct reg_field *reg_fields;
+ int count, i, j;
+
+ count = device_get_child_node_count(&client->dev);
+ if (!count) {
+ return dev_err_probe(&client->dev, -EINVAL,
+ "Incorrect number of leds (%d)", count);
+ }
+ chip = devm_kzalloc(&client->dev, struct_size(chip, leds, count), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ chip->client = client;
+ chip->num_of_leds = count;
+ i2c_set_clientdata(client, chip);
+ chip->regmap = devm_regmap_init_i2c(client, &MP3326_regmap_config);
+ if (IS_ERR(chip->regmap))
+ return PTR_ERR(chip->regmap);
+
+ for (i = 0; i < MAX_CHANNEL; i++) {
+ reg_fields = channels_reg_fields[i];
+ for (j = 0; j < MAX_CTRL; j++) {
+ chip->regmap_fields[i][j] = devm_regmap_field_alloc(&client->dev,
+ chip->regmap, reg_fields[j]);
+ if (IS_ERR(chip->regmap_fields[i][j]))
+ return PTR_ERR(chip->regmap_fields[i][j]);
+ }
+ }
+ if (mp3326_parse_dt(chip))
+ return 1;
+ else
+ return 0;
+}
+
+static const struct i2c_device_id mp3326_id[] = {
+ {"mp3326", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, mp3326_id);
+
+static const struct of_device_id mp3326_of_match[] = {
+ { .compatible = "mps,mp3326" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, mp3326_of_match);
+
+static struct i2c_driver mp3326_driver = {
+ .probe_new = mp3326_leds_probe,
+ .driver = {
+ .name = "mp3326_led",
+ .of_match_table = mp3326_of_match,
+ },
+ .id_table = mp3326_id,
+};
+
+module_i2c_driver(mp3326_driver);
+MODULE_AUTHOR("Yuxi Wang <wyx137120466@gmail.com>");
+MODULE_DESCRIPTION("MPS MP3326 LED driver");
+MODULE_LICENSE("GPL");
--
2.25.1
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH 2/2] leds: add mp3326 driver
2023-11-24 9:30 ` [PATCH 2/2] leds: add mp3326 driver Yuxi Wang
@ 2023-11-24 15:42 ` kernel test robot
2023-11-25 20:00 ` kernel test robot
2023-11-25 21:05 ` kernel test robot
2 siblings, 0 replies; 8+ messages in thread
From: kernel test robot @ 2023-11-24 15:42 UTC (permalink / raw)
To: Yuxi Wang, pavel, lee, robh+dt, krzysztof.kozlowski+dt, conor+dt
Cc: oe-kbuild-all, linux-kernel, linux-leds, devicetree, wyx137120466
Hi Yuxi,
kernel test robot noticed the following build warnings:
[auto build test WARNING on lee-leds/for-leds-next]
[also build test WARNING on robh/for-next linus/master v6.7-rc2 next-20231124]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Yuxi-Wang/dt-bindings-leds-add-mps-mp3326-LED/20231124-173610
base: https://git.kernel.org/pub/scm/linux/kernel/git/lee/leds.git for-leds-next
patch link: https://lore.kernel.org/r/20231124093034.951-3-Yuxi.Wang%40monolithicpower.com
patch subject: [PATCH 2/2] leds: add mp3326 driver
config: openrisc-allyesconfig (https://download.01.org/0day-ci/archive/20231124/202311242100.FGwL3wE0-lkp@intel.com/config)
compiler: or1k-linux-gcc (GCC) 13.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20231124/202311242100.FGwL3wE0-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202311242100.FGwL3wE0-lkp@intel.com/
All warnings (new ones prefixed by >>):
drivers/leds/leds-mp3326.c: In function 'mp3326_parse_dt':
>> drivers/leds/leds-mp3326.c:543:13: warning: unused variable 'val' [-Wunused-variable]
543 | int val;
| ^~~
drivers/leds/leds-mp3326.c: At top level:
drivers/leds/leds-mp3326.c:611:10: error: 'struct i2c_driver' has no member named 'probe_new'
611 | .probe_new = mp3326_leds_probe,
| ^~~~~~~~~
drivers/leds/leds-mp3326.c:611:22: warning: initialization of 'unsigned int' from 'int (*)(struct i2c_client *)' makes integer from pointer without a cast [-Wint-conversion]
611 | .probe_new = mp3326_leds_probe,
| ^~~~~~~~~~~~~~~~~
drivers/leds/leds-mp3326.c:611:22: note: (near initialization for 'mp3326_driver.class')
vim +/val +543 drivers/leds/leds-mp3326.c
536
537 static int mp3326_parse_dt(struct mp3326 *chip)
538 {
539 struct device_node *np = dev_of_node(&chip->client->dev);
540 struct device_node *child;
541 int ret;
542 int i = 0;
> 543 int val;
544
545 for_each_available_child_of_node(np, child) {
546 ret = mp3326_add_led(chip, child, i);
547 if (ret)
548 return ret;
549 i++;
550 }
551
552 ret = regmap_write(chip->regmap, MP3326_PWM_CTRL_CHANNEL_9_16, 0);
553 if (ret)
554 return ret;
555 ret = regmap_write(chip->regmap, MP3326_PWM_CTRL_CHANNEL_1_8, 0);
556 if (ret)
557 return ret;
558 return 0;
559 }
560
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 2/2] leds: add mp3326 driver
2023-11-24 9:30 ` [PATCH 2/2] leds: add mp3326 driver Yuxi Wang
2023-11-24 15:42 ` kernel test robot
@ 2023-11-25 20:00 ` kernel test robot
2023-11-25 21:05 ` kernel test robot
2 siblings, 0 replies; 8+ messages in thread
From: kernel test robot @ 2023-11-25 20:00 UTC (permalink / raw)
To: Yuxi Wang, pavel, lee, robh+dt, krzysztof.kozlowski+dt, conor+dt
Cc: llvm, oe-kbuild-all, linux-kernel, linux-leds, devicetree,
wyx137120466
Hi Yuxi,
kernel test robot noticed the following build errors:
[auto build test ERROR on lee-leds/for-leds-next]
[also build test ERROR on robh/for-next linus/master v6.7-rc2 next-20231124]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Yuxi-Wang/dt-bindings-leds-add-mps-mp3326-LED/20231124-173610
base: https://git.kernel.org/pub/scm/linux/kernel/git/lee/leds.git for-leds-next
patch link: https://lore.kernel.org/r/20231124093034.951-3-Yuxi.Wang%40monolithicpower.com
patch subject: [PATCH 2/2] leds: add mp3326 driver
config: i386-buildonly-randconfig-005-20231126 (https://download.01.org/0day-ci/archive/20231126/202311260229.JhaLyLj7-lkp@intel.com/config)
compiler: clang version 16.0.4 (https://github.com/llvm/llvm-project.git ae42196bc493ffe877a7e3dff8be32035dea4d07)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20231126/202311260229.JhaLyLj7-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202311260229.JhaLyLj7-lkp@intel.com/
All errors (new ones prefixed by >>):
drivers/leds/leds-mp3326.c:543:6: warning: unused variable 'val' [-Wunused-variable]
int val;
^
drivers/leds/leds-mp3326.c:611:3: error: field designator 'probe_new' does not refer to any field in type 'struct i2c_driver'
.probe_new = mp3326_leds_probe,
^
>> drivers/leds/leds-mp3326.c:619:1: error: type specifier missing, defaults to 'int'; ISO C99 and later do not support implicit int [-Wimplicit-int]
module_i2c_driver(mp3326_driver);
^
int
>> drivers/leds/leds-mp3326.c:619:19: error: a parameter list without types is only allowed in a function definition
module_i2c_driver(mp3326_driver);
^
1 warning and 3 errors generated.
vim +/int +619 drivers/leds/leds-mp3326.c
618
> 619 module_i2c_driver(mp3326_driver);
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 2/2] leds: add mp3326 driver
2023-11-24 9:30 ` [PATCH 2/2] leds: add mp3326 driver Yuxi Wang
2023-11-24 15:42 ` kernel test robot
2023-11-25 20:00 ` kernel test robot
@ 2023-11-25 21:05 ` kernel test robot
2 siblings, 0 replies; 8+ messages in thread
From: kernel test robot @ 2023-11-25 21:05 UTC (permalink / raw)
To: Yuxi Wang, pavel, lee, robh+dt, krzysztof.kozlowski+dt, conor+dt
Cc: oe-kbuild-all, linux-kernel, linux-leds, devicetree, wyx137120466
Hi Yuxi,
kernel test robot noticed the following build warnings:
[auto build test WARNING on lee-leds/for-leds-next]
[also build test WARNING on robh/for-next linus/master v6.7-rc2 next-20231124]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Yuxi-Wang/dt-bindings-leds-add-mps-mp3326-LED/20231124-173610
base: https://git.kernel.org/pub/scm/linux/kernel/git/lee/leds.git for-leds-next
patch link: https://lore.kernel.org/r/20231124093034.951-3-Yuxi.Wang%40monolithicpower.com
patch subject: [PATCH 2/2] leds: add mp3326 driver
config: x86_64-randconfig-123-20231126 (https://download.01.org/0day-ci/archive/20231126/202311260403.PyY8h5Dj-lkp@intel.com/config)
compiler: gcc-7 (Ubuntu 7.5.0-6ubuntu2) 7.5.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20231126/202311260403.PyY8h5Dj-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202311260403.PyY8h5Dj-lkp@intel.com/
All warnings (new ones prefixed by >>):
drivers/leds/leds-mp3326.c: In function 'mp3326_parse_dt':
drivers/leds/leds-mp3326.c:543:6: warning: unused variable 'val' [-Wunused-variable]
int val;
^~~
drivers/leds/leds-mp3326.c: At top level:
drivers/leds/leds-mp3326.c:611:3: error: 'struct i2c_driver' has no member named 'probe_new'; did you mean 'probe'?
.probe_new = mp3326_leds_probe,
^~~~~~~~~
probe
>> drivers/leds/leds-mp3326.c:611:15: warning: initialization makes integer from pointer without a cast [-Wint-conversion]
.probe_new = mp3326_leds_probe,
^~~~~~~~~~~~~~~~~
drivers/leds/leds-mp3326.c:611:15: note: (near initialization for 'mp3326_driver.class')
drivers/leds/leds-mp3326.c:611:15: error: initializer element is not computable at load time
drivers/leds/leds-mp3326.c:611:15: note: (near initialization for 'mp3326_driver.class')
vim +611 drivers/leds/leds-mp3326.c
609
610 static struct i2c_driver mp3326_driver = {
> 611 .probe_new = mp3326_leds_probe,
612 .driver = {
613 .name = "mp3326_led",
614 .of_match_table = mp3326_of_match,
615 },
616 .id_table = mp3326_id,
617 };
618
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2023-11-25 21:05 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-10-31 7:01 [PATCH 2/2] leds: add mp3326 driver Yuxi (Yuxi) Wang
2023-10-31 8:16 ` Krzysztof Kozlowski
-- strict thread matches above, loose matches on Subject: below --
2023-11-08 3:29 [PATCH 0/2] " Yuxi Wang
2023-11-08 3:29 ` [PATCH 2/2] " Yuxi Wang
2023-11-12 11:21 ` kernel test robot
2023-11-24 9:30 [PATCH 0/2] leds: Add a driver for MP3326 Yuxi Wang
2023-11-24 9:30 ` [PATCH 2/2] leds: add mp3326 driver Yuxi Wang
2023-11-24 15:42 ` kernel test robot
2023-11-25 20:00 ` kernel test robot
2023-11-25 21:05 ` kernel test robot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).