devicetree.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [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; 14+ 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", &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", &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] 14+ messages in thread

* Re: [PATCH 2/2] leds: add mp3326 driver
  2023-10-31  7:01 Yuxi (Yuxi) Wang
@ 2023-10-31  8:16 ` Krzysztof Kozlowski
  0 siblings, 0 replies; 14+ 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", &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", &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] 14+ messages in thread

* [PATCH 0/2] leds: add mp3326 driver
@ 2023-11-08  3:29 Yuxi Wang
  2023-11-08  3:29 ` [PATCH 1/2] dt-bindings: leds: add mps mp3326 LED Yuxi Wang
  2023-11-08  3:29 ` [PATCH 2/2] leds: add mp3326 driver Yuxi Wang
  0 siblings, 2 replies; 14+ 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

Add the binding description and the corresponding driver for
the mps mp3326.

Signed-off-by: Yuxi Wang <wyx137120466@gmail.com>

Yuxi Wang (2):
  dt-bindings: leds: add mps mp3326 LED
  leds: add mp3326 driver

 .../devicetree/bindings/leds/leds-mp3326.yaml | 184 +++++
 drivers/leds/Kconfig                          |   7 +
 drivers/leds/Makefile                         |   1 +
 drivers/leds/leds-mp3326.c                    | 627 ++++++++++++++++++
 4 files changed, 819 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/leds/leds-mp3326.yaml
 create mode 100644 drivers/leds/leds-mp3326.c

-- 
2.25.1


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH 1/2] dt-bindings: leds: add mps mp3326 LED
  2023-11-08  3:29 [PATCH 0/2] leds: add mp3326 driver Yuxi Wang
@ 2023-11-08  3:29 ` Yuxi Wang
  2023-11-08  4:15   ` Rob Herring
  2023-11-08  8:20   ` Krzysztof Kozlowski
  2023-11-08  3:29 ` [PATCH 2/2] leds: add mp3326 driver Yuxi Wang
  1 sibling, 2 replies; 14+ 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

Document mps mp3326 LED driver devicetree bindings.

Signed-off-by: Yuxi Wang <wyx137120466@gmail.com>
---
 .../devicetree/bindings/leds/leds-mp3326.yaml | 184 ++++++++++++++++++
 1 file changed, 184 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/leds/leds-mp3326.yaml

diff --git a/Documentation/devicetree/bindings/leds/leds-mp3326.yaml b/Documentation/devicetree/bindings/leds/leds-mp3326.yaml
new file mode 100644
index 000000000000..899cf568f647
--- /dev/null
+++ b/Documentation/devicetree/bindings/leds/leds-mp3326.yaml
@@ -0,0 +1,184 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/mps,mp3326.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: MPS MP3326 RGB/White LED Driver
+
+maintainers:
+  - Yuxi Wang <wyx137120466@gmail.com>
+
+description: |
+  The MP3326 is a RGB/White LED driver with I2C interface.
+
+  For more product information please see the link below:
+  https://www.monolithicpower.com/en/products/mp3326.html
+
+properties:
+  compatible:
+    - const: mps,mp3326
+
+  "#address-cells":
+    const: 1
+
+  "#size-cells":
+    const: 0
+
+  reg:
+    maxItems: 1
+
+  mps,led-protect:
+    description: |
+      LED short protection threshold.
+    enum: [0, 1, 2, 3]
+
+  multi-led:
+    type: object
+
+    properties:
+      "#address-cells":
+        const: 1
+      "#size-cells":
+        const: 0
+
+      color:
+        description: RGB module
+        const: LED_COLOR_ID_RGB
+
+      led_r:
+        type: object
+
+        properties:
+          "#address-cells":
+            const: 1
+          "#size-cells":
+            const: 0
+          reg:
+            description: Index of the LED.
+            minimum: 1
+            maximum: 16
+          color:
+            description: Red.
+            const: LED_COLOR_ID_RED
+          required:
+            - reg
+            - color
+
+      led_g:
+        type: object
+
+        properties:
+          "#address-cells":
+            const: 1
+          "#size-cells":
+            const: 0
+        reg:
+          description: Index of the LED.
+          minimum: 1
+          maximum: 16
+        color:
+          description: Green.
+          const: LED_COLOR_ID_GREEN
+        required:
+          - reg
+          - color
+
+      led_b:
+        type: object
+
+        properties:
+          "#address-cells":
+            const: 1
+          "#size-cells":
+            const: 0
+        reg:
+          description: Index of the LED.
+          minimum: 1
+          maximum: 16
+        color:
+          description: Blue.
+          const: LED_COLOR_ID_BLUE
+        required:
+          - reg
+          - color
+
+  patternProperties:
+    "^led@[0-3]$":
+      type: object
+
+      properties:
+        reg:
+          description: Index of the LED.
+          minimum: 1
+          maximum: 16
+
+        required:
+          - reg
+          - color
+
+examples:
+  - |
+    #include <dt-bindings/leds/common.h>
+
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        mp3326@30 {
+            compatible = "mps,mp3326";
+            reg = <0x30>;
+            led-protect =<3>;
+            #address-cells = <1>;
+            #size-cells = <0>;
+
+            multi-led {
+              color = <LED_COLOR_ID_RGB>;
+              #address-cells = <1>;
+              #size-cells = <0>;
+
+              rgb_r@1 {
+                  #address-cells = <1>;
+                  #size-cells = <0>;
+                  reg  = <1>;
+                  color = <LED_COLOR_ID_RED>;
+              };
+              rgb_g@2 {
+                  #address-cells = <1>;
+                  #size-cells = <0>;
+                  reg = <2>;
+                  color = <LED_COLOR_ID_GREEN>;
+              };
+              rgb_b@3 {
+                  #address-cells = <1>;
+                  #size-cells = <0>;
+                  reg = <3>;
+                  color = <LED_COLOR_ID_BLUE>;
+              };
+            };
+        };
+    };
+
+  - |
+    #include <dt-bindings/leds/common.h>
+
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        mp3326@30 {
+            compatible = "mps,mp3326";
+            reg = <0x30>;
+            led-protect =<3>;
+            #address-cells = <1>;
+            #size-cells = <0>;
+            led0@0 {
+                #address-cells = <1>;
+                #size-cells = <0>;
+                reg= <5>;
+                color = <LED_COLOR_ID_WHITE>;
+            };
+        };
+    };
+
+...
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH 2/2] leds: add mp3326 driver
  2023-11-08  3:29 [PATCH 0/2] leds: add mp3326 driver Yuxi Wang
  2023-11-08  3:29 ` [PATCH 1/2] dt-bindings: leds: add mps mp3326 LED Yuxi Wang
@ 2023-11-08  3:29 ` Yuxi Wang
  2023-11-12 11:21   ` kernel test robot
  1 sibling, 1 reply; 14+ 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", &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", &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] 14+ messages in thread

* Re: [PATCH 1/2] dt-bindings: leds: add mps mp3326 LED
  2023-11-08  3:29 ` [PATCH 1/2] dt-bindings: leds: add mps mp3326 LED Yuxi Wang
@ 2023-11-08  4:15   ` Rob Herring
  2023-11-08  8:20   ` Krzysztof Kozlowski
  1 sibling, 0 replies; 14+ messages in thread
From: Rob Herring @ 2023-11-08  4:15 UTC (permalink / raw)
  To: Yuxi Wang
  Cc: linux-leds, Yuxi.Wang, krzysztof.kozlowski+dt, lee, conor+dt,
	linux-kernel, devicetree, robh+dt, pavel


On Wed, 08 Nov 2023 11:29:20 +0800, Yuxi Wang wrote:
> Document mps mp3326 LED driver devicetree bindings.
> 
> Signed-off-by: Yuxi Wang <wyx137120466@gmail.com>
> ---
>  .../devicetree/bindings/leds/leds-mp3326.yaml | 184 ++++++++++++++++++
>  1 file changed, 184 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/leds/leds-mp3326.yaml
> 

My bot found errors running 'make DT_CHECKER_FLAGS=-m dt_binding_check'
on your patch (DT_CHECKER_FLAGS is new in v5.13):

yamllint warnings/errors:

dtschema/dtc warnings/errors:
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/leds/leds-mp3326.yaml: properties:multi-led:properties:led_r:properties:required: ['reg', 'color'] is not of type 'object', 'boolean'
	from schema $id: http://json-schema.org/draft-07/schema#
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/leds/leds-mp3326.yaml: properties:compatible: [{'const': 'mps,mp3326'}] is not of type 'object', 'boolean'
	from schema $id: http://json-schema.org/draft-07/schema#
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/leds/leds-mp3326.yaml: properties: 'patternProperties' should not be valid under {'$ref': '#/definitions/json-schema-prop-names'}
	hint: A json-schema keyword was found instead of a DT property name.
	from schema $id: http://devicetree.org/meta-schemas/keywords.yaml#
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/leds/leds-mp3326.yaml: properties:multi-led:properties:led_r:properties: 'required' should not be valid under {'$ref': '#/definitions/json-schema-prop-names'}
	hint: A json-schema keyword was found instead of a DT property name.
	from schema $id: http://devicetree.org/meta-schemas/keywords.yaml#
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/leds/leds-mp3326.yaml: properties:multi-led:properties:led_r:properties:required: ['reg', 'color'] is not of type 'object', 'boolean'
	from schema $id: http://devicetree.org/meta-schemas/keywords.yaml#
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/leds/leds-mp3326.yaml: properties:multi-led:properties:led_g: 'anyOf' conditional failed, one must be fixed:
	'reg' is not one of ['$ref', 'additionalItems', 'additionalProperties', 'allOf', 'anyOf', 'const', 'contains', 'default', 'dependencies', 'dependentRequired', 'dependentSchemas', 'deprecated', 'description', 'else', 'enum', 'exclusiveMaximum', 'exclusiveMinimum', 'items', 'if', 'minItems', 'minimum', 'maxItems', 'maximum', 'multipleOf', 'not', 'oneOf', 'pattern', 'patternProperties', 'properties', 'required', 'then', 'typeSize', 'unevaluatedProperties', 'uniqueItems']
	'type' was expected
	from schema $id: http://devicetree.org/meta-schemas/keywords.yaml#
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/leds/leds-mp3326.yaml: properties:multi-led:properties:led_g: 'anyOf' conditional failed, one must be fixed:
	'color' is not one of ['$ref', 'additionalItems', 'additionalProperties', 'allOf', 'anyOf', 'const', 'contains', 'default', 'dependencies', 'dependentRequired', 'dependentSchemas', 'deprecated', 'description', 'else', 'enum', 'exclusiveMaximum', 'exclusiveMinimum', 'items', 'if', 'minItems', 'minimum', 'maxItems', 'maximum', 'multipleOf', 'not', 'oneOf', 'pattern', 'patternProperties', 'properties', 'required', 'then', 'typeSize', 'unevaluatedProperties', 'uniqueItems']
	'type' was expected
	from schema $id: http://devicetree.org/meta-schemas/keywords.yaml#
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/leds/leds-mp3326.yaml: properties:multi-led:properties:led_b: 'anyOf' conditional failed, one must be fixed:
	'reg' is not one of ['$ref', 'additionalItems', 'additionalProperties', 'allOf', 'anyOf', 'const', 'contains', 'default', 'dependencies', 'dependentRequired', 'dependentSchemas', 'deprecated', 'description', 'else', 'enum', 'exclusiveMaximum', 'exclusiveMinimum', 'items', 'if', 'minItems', 'minimum', 'maxItems', 'maximum', 'multipleOf', 'not', 'oneOf', 'pattern', 'patternProperties', 'properties', 'required', 'then', 'typeSize', 'unevaluatedProperties', 'uniqueItems']
	'type' was expected
	from schema $id: http://devicetree.org/meta-schemas/keywords.yaml#
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/leds/leds-mp3326.yaml: properties:multi-led:properties:led_b: 'anyOf' conditional failed, one must be fixed:
	'color' is not one of ['$ref', 'additionalItems', 'additionalProperties', 'allOf', 'anyOf', 'const', 'contains', 'default', 'dependencies', 'dependentRequired', 'dependentSchemas', 'deprecated', 'description', 'else', 'enum', 'exclusiveMaximum', 'exclusiveMinimum', 'items', 'if', 'minItems', 'minimum', 'maxItems', 'maximum', 'multipleOf', 'not', 'oneOf', 'pattern', 'patternProperties', 'properties', 'required', 'then', 'typeSize', 'unevaluatedProperties', 'uniqueItems']
	'type' was expected
	from schema $id: http://devicetree.org/meta-schemas/keywords.yaml#
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/leds/leds-mp3326.yaml: properties:compatible: [{'const': 'mps,mp3326'}] is not of type 'object', 'boolean'
	from schema $id: http://devicetree.org/meta-schemas/keywords.yaml#
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/leds/leds-mp3326.yaml: properties:patternProperties: 'anyOf' conditional failed, one must be fixed:
	'^led@[0-3]$' is not one of ['$ref', 'additionalItems', 'additionalProperties', 'allOf', 'anyOf', 'const', 'contains', 'default', 'dependencies', 'dependentRequired', 'dependentSchemas', 'deprecated', 'description', 'else', 'enum', 'exclusiveMaximum', 'exclusiveMinimum', 'items', 'if', 'minItems', 'minimum', 'maxItems', 'maximum', 'multipleOf', 'not', 'oneOf', 'pattern', 'patternProperties', 'properties', 'required', 'then', 'typeSize', 'unevaluatedProperties', 'uniqueItems']
	'type' was expected
	from schema $id: http://devicetree.org/meta-schemas/keywords.yaml#
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/leds/leds-mp3326.yaml: 'oneOf' conditional failed, one must be fixed:
	'unevaluatedProperties' is a required property
	'additionalProperties' is a required property
	hint: Either unevaluatedProperties or additionalProperties must be present
	from schema $id: http://devicetree.org/meta-schemas/core.yaml#
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/leds/leds-mp3326.yaml: properties:multi-led:properties:led_g: 'reg' is not one of ['type', 'description', 'dependencies', 'dependentRequired', 'dependentSchemas', 'properties', 'patternProperties', 'additionalProperties', 'unevaluatedProperties', 'deprecated', 'required', 'not', 'allOf', 'anyOf', 'oneOf', '$ref']
	from schema $id: http://devicetree.org/meta-schemas/nodes.yaml#
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/leds/leds-mp3326.yaml: properties:multi-led:properties:led_g: 'color' is not one of ['type', 'description', 'dependencies', 'dependentRequired', 'dependentSchemas', 'properties', 'patternProperties', 'additionalProperties', 'unevaluatedProperties', 'deprecated', 'required', 'not', 'allOf', 'anyOf', 'oneOf', '$ref']
	from schema $id: http://devicetree.org/meta-schemas/nodes.yaml#
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/leds/leds-mp3326.yaml: properties:multi-led:properties:led_b: 'reg' is not one of ['type', 'description', 'dependencies', 'dependentRequired', 'dependentSchemas', 'properties', 'patternProperties', 'additionalProperties', 'unevaluatedProperties', 'deprecated', 'required', 'not', 'allOf', 'anyOf', 'oneOf', '$ref']
	from schema $id: http://devicetree.org/meta-schemas/nodes.yaml#
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/leds/leds-mp3326.yaml: properties:multi-led:properties:led_b: 'color' is not one of ['type', 'description', 'dependencies', 'dependentRequired', 'dependentSchemas', 'properties', 'patternProperties', 'additionalProperties', 'unevaluatedProperties', 'deprecated', 'required', 'not', 'allOf', 'anyOf', 'oneOf', '$ref']
	from schema $id: http://devicetree.org/meta-schemas/nodes.yaml#
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/leds/leds-mp3326.yaml: $id: Cannot determine base path from $id, relative path/filename doesn't match actual path or filename
 	 $id: http://devicetree.org/schemas/mps,mp3326.yaml
 	file: /builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/leds/leds-mp3326.yaml
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/leds/leds-mp3326.yaml: mps,led-protect: missing type definition
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/leds/leds-mp3326.yaml: patternProperties: missing type definition
Documentation/devicetree/bindings/leds/leds-mp3326.example.dtb: /example-0/i2c/mp3326@30: failed to match any schema with compatible: ['mps,mp3326']
Documentation/devicetree/bindings/leds/leds-mp3326.example.dtb: /example-1/i2c/mp3326@30: failed to match any schema with compatible: ['mps,mp3326']

doc reference errors (make refcheckdocs):

See https://patchwork.ozlabs.org/project/devicetree-bindings/patch/20231108032921.3134115-2-wyx137120466@gmail.com

The base for the series is generally the latest rc1. A different dependency
should be noted in *this* patch.

If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure 'yamllint' is installed and dt-schema is up to
date:

pip3 install dtschema --upgrade

Please check and re-submit after running the above command yourself. Note
that DT_SCHEMA_FILES can be set to your schema file to speed up checking
your schema. However, it must be unset to test all examples with your schema.


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 1/2] dt-bindings: leds: add mps mp3326 LED
  2023-11-08  3:29 ` [PATCH 1/2] dt-bindings: leds: add mps mp3326 LED Yuxi Wang
  2023-11-08  4:15   ` Rob Herring
@ 2023-11-08  8:20   ` Krzysztof Kozlowski
  2023-11-08  9:30     ` Yuxi (Yuxi) Wang
  1 sibling, 1 reply; 14+ messages in thread
From: Krzysztof Kozlowski @ 2023-11-08  8:20 UTC (permalink / raw)
  To: Yuxi Wang, pavel, lee, robh+dt, krzysztof.kozlowski+dt, conor+dt,
	Yuxi.Wang
  Cc: linux-leds, devicetree, linux-kernel

On 08/11/2023 04:29, Yuxi Wang wrote:
> Document mps mp3326 LED driver devicetree bindings.
> 
> Signed-off-by: Yuxi Wang <wyx137120466@gmail.com>
> ---
>  .../devicetree/bindings/leds/leds-mp3326.yaml | 184 ++++++++++++++++++
>  1 file changed, 184 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/leds/leds-mp3326.yaml

Except that this was not tested :(, few more comments.

Filename like compatible.

> 
> diff --git a/Documentation/devicetree/bindings/leds/leds-mp3326.yaml b/Documentation/devicetree/bindings/leds/leds-mp3326.yaml
> new file mode 100644
> index 000000000000..899cf568f647
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/leds/leds-mp3326.yaml
> @@ -0,0 +1,184 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/mps,mp3326.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: MPS MP3326 RGB/White LED Driver
> +
> +maintainers:
> +  - Yuxi Wang <wyx137120466@gmail.com>
> +
> +description: |
> +  The MP3326 is a RGB/White LED driver with I2C interface.
> +
> +  For more product information please see the link below:
> +  https://www.monolithicpower.com/en/products/mp3326.html
> +
> +properties:
> +  compatible:
> +    - const: mps,mp3326
> +
> +  "#address-cells":
> +    const: 1
> +
> +  "#size-cells":
> +    const: 0
> +
> +  reg:
> +    maxItems: 1
> +
> +  mps,led-protect:
> +    description: |
> +      LED short protection threshold.

threshold? So in some units? What does it mean? What do the values mean?

> +    enum: [0, 1, 2, 3]
> +
> +  multi-led:
> +    type: object
> +
> +    properties:
> +      "#address-cells":
> +        const: 1
> +      "#size-cells":
> +        const: 0
> +
> +      color:
> +        description: RGB module
> +        const: LED_COLOR_ID_RGB
> +
> +      led_r:

Nope. First, no underscores in names. Second, please open existing
bindings and look how it is done there.

> +        type: object
> +
> +        properties:
> +          "#address-cells":
> +            const: 1
> +          "#size-cells":
> +            const: 0

Why do you have the,?

> +          reg:
> +            description: Index of the LED.
> +            minimum: 1
> +            maximum: 16

Please format it properly. You miss blank lines between each property
description.

> +          color:
> +            description: Red.
> +            const: LED_COLOR_ID_RED
> +          required:
> +            - reg
> +            - color
> +
> +      led_g:
> +        type: object
> +
> +        properties:
> +          "#address-cells":
> +            const: 1
> +          "#size-cells":
> +            const: 0
> +        reg:
> +          description: Index of the LED.
> +          minimum: 1
> +          maximum: 16
> +        color:
> +          description: Green.
> +          const: LED_COLOR_ID_GREEN
> +        required:
> +          - reg
> +          - color
> +
> +      led_b:
> +        type: object
> +
> +        properties:
> +          "#address-cells":
> +            const: 1
> +          "#size-cells":
> +            const: 0
> +        reg:
> +          description: Index of the LED.
> +          minimum: 1
> +          maximum: 16
> +        color:
> +          description: Blue.
> +          const: LED_COLOR_ID_BLUE
> +        required:
> +          - reg
> +          - color
> +
> +  patternProperties:
> +    "^led@[0-3]$":
> +      type: object
> +
> +      properties:
> +        reg:
> +          description: Index of the LED.
> +          minimum: 1
> +          maximum: 16
> +
> +        required:
> +          - reg
> +          - color

Missing required, additionalProperties.

Open existing binding and use it as example.

> +
> +examples:
> +  - |
> +    #include <dt-bindings/leds/common.h>
> +
> +    i2c {
> +        #address-cells = <1>;
> +        #size-cells = <0>;
> +
> +        mp3326@30 {

Node names should be generic. See also an explanation and list of
examples (not exhaustive) in DT specification:
https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html#generic-names-recommendation


> +            compatible = "mps,mp3326";
> +            reg = <0x30>;
> +            led-protect =<3>;
> +            #address-cells = <1>;
> +            #size-cells = <0>;
> +
> +            multi-led {
> +              color = <LED_COLOR_ID_RGB>;
> +              #address-cells = <1>;
> +              #size-cells = <0>;
> +
> +              rgb_r@1 {
> +                  #address-cells = <1>;
> +                  #size-cells = <0>;
> +                  reg  = <1>;
> +                  color = <LED_COLOR_ID_RED>;
> +              };
> +              rgb_g@2 {
> +                  #address-cells = <1>;
> +                  #size-cells = <0>;
> +                  reg = <2>;
> +                  color = <LED_COLOR_ID_GREEN>;
> +              };
> +              rgb_b@3 {
> +                  #address-cells = <1>;
> +                  #size-cells = <0>;
> +                  reg = <3>;
> +                  color = <LED_COLOR_ID_BLUE>;
> +              };
> +            };
> +        };
> +    };
> +
> +  - |
> +    #include <dt-bindings/leds/common.h>
> +
> +    i2c {
> +        #address-cells = <1>;
> +        #size-cells = <0>;
> +
> +        mp3326@30 {

Node names should be generic. See also an explanation and list of
examples (not exhaustive) in DT specification:
https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html#generic-names-recommendation


> +            compatible = "mps,mp3326";
> +            reg = <0x30>;
> +            led-protect =<3>;
> +            #address-cells = <1>;
> +            #size-cells = <0>;
> +            led0@0 {
> +                #address-cells = <1>;
> +                #size-cells = <0>;
> +                reg= <5>;
> +                color = <LED_COLOR_ID_WHITE>;
> +            };
> +        };
> +    };
> +
> +...

Best regards,
Krzysztof


^ permalink raw reply	[flat|nested] 14+ messages in thread

* RE: [PATCH 1/2] dt-bindings: leds: add mps mp3326 LED
  2023-11-08  8:20   ` Krzysztof Kozlowski
@ 2023-11-08  9:30     ` Yuxi (Yuxi) Wang
  2023-11-08 10:42       ` Krzysztof Kozlowski
  0 siblings, 1 reply; 14+ messages in thread
From: Yuxi (Yuxi) Wang @ 2023-11-08  9:30 UTC (permalink / raw)
  To: Krzysztof Kozlowski, 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, Yuxi (Yuxi) Wang

> On 08/11/2023 04:29, Yuxi Wang wrote:
> > Document mps mp3326 LED driver devicetree bindings.
> >
> > Signed-off-by: Yuxi Wang <wyx137120466@gmail.com>
> > ---
> >  .../devicetree/bindings/leds/leds-mp3326.yaml | 184 ++++++++++++++++++
> >  1 file changed, 184 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/leds/leds-mp3326.yaml
> 
> Except that this was not tested :(, few more comments.
> 
> Filename like compatible.
> 
Thank you for pointing this out. I will use mps,mp3326.yaml in the next version.

> >
> > diff --git a/Documentation/devicetree/bindings/leds/leds-mp3326.yaml b/Documentation/devicetree/bindings/leds/leds-
> mp3326.yaml
> > new file mode 100644
> > index 000000000000..899cf568f647
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/leds/leds-mp3326.yaml
> > @@ -0,0 +1,184 @@
> > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > +%YAML 1.2
> > +---
> > +$id:
> https://urldefense.com/v3/__http://devicetree.org/schemas/mps,mp3326.yaml*__;Iw!!FIHMVlGrYVGa5kwGHCY!X_Ejb8ueivebvMv2
> RooVy9MVgqWRElf75NTk8JPqFXaYwWNyzupK7XKN5UKAvJRo-WMQmvxIuJPq5isKHeoqFbKZ4_cH81h24frs$
> > +$schema: https://urldefense.com/v3/__http://devicetree.org/meta-
> schemas/core.yaml*__;Iw!!FIHMVlGrYVGa5kwGHCY!X_Ejb8ueivebvMv2RooVy9MVgqWRElf75NTk8JPqFXaYwWNyzupK7XKN5UKAvJR
> o-WMQmvxIuJPq5isKHeoqFbKZ4_cH88smmVyA$
> > +
> > +title: MPS MP3326 RGB/White LED Driver
> > +
> > +maintainers:
> > +  - Yuxi Wang <wyx137120466@gmail.com>
> > +
> > +description: |
> > +  The MP3326 is a RGB/White LED driver with I2C interface.
> > +
> > +  For more product information please see the link below:
> > +  https://www.monolithicpower.com/en/products/mp3326.html
> > +
> > +properties:
> > +  compatible:
> > +    - const: mps,mp3326
> > +
> > +  "#address-cells":
> > +    const: 1
> > +
> > +  "#size-cells":
> > +    const: 0
> > +
> > +  reg:
> > +    maxItems: 1
> > +
> > +  mps,led-protect:
> > +    description: |
> > +      LED short protection threshold.
> 
> threshold? So in some units? What does it mean? What do the values mean?
> 
Yes, it indicates short protection threshold in mp3326.
But they do not have the units. They just indicate the corresponding bits value of register which can configure short protection value.

> > +    enum: [0, 1, 2, 3]
> > +
> > +  multi-led:
> > +    type: object
> > +
> > +    properties:
> > +      "#address-cells":
> > +        const: 1
> > +      "#size-cells":
> > +        const: 0
> > +
> > +      color:
> > +        description: RGB module
> > +        const: LED_COLOR_ID_RGB
> > +
> > +      led_r:
> 
> Nope. First, no underscores in names. Second, please open existing
> bindings and look how it is done there.
> 
Thank you for pointing out this, I will Fixed it in the next version.
> > +        type: object
> > +
> > +        properties:
> > +          "#address-cells":
> > +            const: 1
> > +          "#size-cells":
> > +            const: 0
> 
> Why do you have the,?
> 

Sorry,  here in no , .
what do you mean?

> > +          reg:
> > +            description: Index of the LED.
> > +            minimum: 1
> > +            maximum: 16
> 
> Please format it properly. You miss blank lines between each property
> description.
> 
Thank you for pointing this out. I will add the blank lines in the next version.
> > +          color:
> > +            description: Red.
> > +            const: LED_COLOR_ID_RED
> > +          required:
> > +            - reg
> > +            - color
> > +
> > +      led_g:
> > +        type: object
> > +
> > +        properties:
> > +          "#address-cells":
> > +            const: 1
> > +          "#size-cells":
> > +            const: 0
> > +        reg:
> > +          description: Index of the LED.
> > +          minimum: 1
> > +          maximum: 16
> > +        color:
> > +          description: Green.
> > +          const: LED_COLOR_ID_GREEN
> > +        required:
> > +          - reg
> > +          - color
> > +
> > +      led_b:
> > +        type: object
> > +
> > +        properties:
> > +          "#address-cells":
> > +            const: 1
> > +          "#size-cells":
> > +            const: 0
> > +        reg:
> > +          description: Index of the LED.
> > +          minimum: 1
> > +          maximum: 16
> > +        color:
> > +          description: Blue.
> > +          const: LED_COLOR_ID_BLUE
> > +        required:
> > +          - reg
> > +          - color
> > +
> > +  patternProperties:
> > +    "^led@[0-3]$":
> > +      type: object
> > +
> > +      properties:
> > +        reg:
> > +          description: Index of the LED.
> > +          minimum: 1
> > +          maximum: 16
> > +
> > +        required:
> > +          - reg
> > +          - color
> 
> Missing required, additionalProperties.
> 
> Open existing binding and use it as example.
> 
Thank you for pointing this out. I will add the additional Properties in the next version.
> > +
> > +examples:
> > +  - |
> > +    #include <dt-bindings/leds/common.h>
> > +
> > +    i2c {
> > +        #address-cells = <1>;
> > +        #size-cells = <0>;
> > +
> > +        mp3326@30 {
> 
> Node names should be generic. See also an explanation and list of
> examples (not exhaustive) in DT specification:
> https://urldefense.com/v3/__https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html*generic-
> names-
> recommendation__;Iw!!FIHMVlGrYVGa5kwGHCY!X_Ejb8ueivebvMv2RooVy9MVgqWRElf75NTk8JPqFXaYwWNyzupK7XKN5UKAvJRo-
> WMQmvxIuJPq5isKHeoqFbKZ4_cH8-K2cXo8$
> 
Thank you for sharing this, I will check it and change it in the next version.
> 
> > +            compatible = "mps,mp3326";
> > +            reg = <0x30>;
> > +            led-protect =<3>;
> > +            #address-cells = <1>;
> > +            #size-cells = <0>;
> > +
> > +            multi-led {
> > +              color = <LED_COLOR_ID_RGB>;
> > +              #address-cells = <1>;
> > +              #size-cells = <0>;
> > +
> > +              rgb_r@1 {
> > +                  #address-cells = <1>;
> > +                  #size-cells = <0>;
> > +                  reg  = <1>;
> > +                  color = <LED_COLOR_ID_RED>;
> > +              };
> > +              rgb_g@2 {
> > +                  #address-cells = <1>;
> > +                  #size-cells = <0>;
> > +                  reg = <2>;
> > +                  color = <LED_COLOR_ID_GREEN>;
> > +              };
> > +              rgb_b@3 {
> > +                  #address-cells = <1>;
> > +                  #size-cells = <0>;
> > +                  reg = <3>;
> > +                  color = <LED_COLOR_ID_BLUE>;
> > +              };
> > +            };
> > +        };
> > +    };
> > +
> > +  - |
> > +    #include <dt-bindings/leds/common.h>
> > +
> > +    i2c {
> > +        #address-cells = <1>;
> > +        #size-cells = <0>;
> > +
> > +        mp3326@30 {
> 
> Node names should be generic. See also an explanation and list of
> examples (not exhaustive) in DT specification:
> https://urldefense.com/v3/__https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html*generic-
> names-
> recommendation__;Iw!!FIHMVlGrYVGa5kwGHCY!X_Ejb8ueivebvMv2RooVy9MVgqWRElf75NTk8JPqFXaYwWNyzupK7XKN5UKAvJRo-
> WMQmvxIuJPq5isKHeoqFbKZ4_cH8-K2cXo8$
> 
Thank you for sharing this, I will check it and change it in the next version.
> 
> > +            compatible = "mps,mp3326";
> > +            reg = <0x30>;
> > +            led-protect =<3>;
> > +            #address-cells = <1>;
> > +            #size-cells = <0>;
> > +            led0@0 {
> > +                #address-cells = <1>;
> > +                #size-cells = <0>;
> > +                reg= <5>;
> > +                color = <LED_COLOR_ID_WHITE>;
> > +            };
> > +        };
> > +    };
> > +
> > +...
> 
> Best regards,
> Krzysztof


Best Regards.

Yuxi Wang 




^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 1/2] dt-bindings: leds: add mps mp3326 LED
  2023-11-08  9:30     ` Yuxi (Yuxi) Wang
@ 2023-11-08 10:42       ` Krzysztof Kozlowski
  0 siblings, 0 replies; 14+ messages in thread
From: Krzysztof Kozlowski @ 2023-11-08 10:42 UTC (permalink / raw)
  To: Yuxi (Yuxi) Wang, 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

On 08/11/2023 10:30, Yuxi (Yuxi) Wang wrote:
>>> +  reg:
>>> +    maxItems: 1
>>> +
>>> +  mps,led-protect:
>>> +    description: |
>>> +      LED short protection threshold.
>>
>> threshold? So in some units? What does it mean? What do the values mean?
>>
> Yes, it indicates short protection threshold in mp3326.
> But they do not have the units. They just indicate the corresponding bits value of register which can configure short protection value.

I meant, what are the real, not how you wrote it here, values?

What do the values mean? Description should state that and according to
this, you might have to use standard unit suffixes so these will be real
values.

> 
>>> +    enum: [0, 1, 2, 3]
>>> +
>>> +  multi-led:
>>> +    type: object
>>> +
>>> +    properties:
>>> +      "#address-cells":
>>> +        const: 1
>>> +      "#size-cells":
>>> +        const: 0
>>> +
>>> +      color:
>>> +        description: RGB module
>>> +        const: LED_COLOR_ID_RGB
>>> +
>>> +      led_r:
>>
>> Nope. First, no underscores in names. Second, please open existing
>> bindings and look how it is done there.
>>
> Thank you for pointing out this, I will Fixed it in the next version.
>>> +        type: object
>>> +
>>> +        properties:
>>> +          "#address-cells":
>>> +            const: 1
>>> +          "#size-cells":
>>> +            const: 0
>>
>> Why do you have the,?
>>
> 
> Sorry,  here in no , .
> what do you mean?

s/the,/them/

Why do you need these?

> 
>>> +          reg:
>>> +            description: Index of the LED.
>>> +            minimum: 1
>>> +            maximum: 16
>>


Best regards,
Krzysztof


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 2/2] leds: add mp3326 driver
  2023-11-08  3:29 ` [PATCH 2/2] leds: add mp3326 driver Yuxi Wang
@ 2023-11-12 11:21   ` kernel test robot
  0 siblings, 0 replies; 14+ 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", &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", &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] 14+ 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; 14+ 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", &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", &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] 14+ 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; 14+ 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] 14+ 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; 14+ 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] 14+ 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; 14+ 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] 14+ messages in thread

end of thread, other threads:[~2023-11-25 21:05 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-11-08  3:29 [PATCH 0/2] leds: add mp3326 driver Yuxi Wang
2023-11-08  3:29 ` [PATCH 1/2] dt-bindings: leds: add mps mp3326 LED Yuxi Wang
2023-11-08  4:15   ` Rob Herring
2023-11-08  8:20   ` Krzysztof Kozlowski
2023-11-08  9:30     ` Yuxi (Yuxi) Wang
2023-11-08 10:42       ` Krzysztof Kozlowski
2023-11-08  3:29 ` [PATCH 2/2] leds: add mp3326 driver Yuxi Wang
2023-11-12 11:21   ` kernel test robot
  -- strict thread matches above, loose matches on Subject: below --
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
2023-10-31  7:01 Yuxi (Yuxi) Wang
2023-10-31  8:16 ` Krzysztof Kozlowski

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).