Re: [PATCH 2/2] leds: add mp3326 driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 31/10/2023 08:01, Yuxi (Yuxi) Wang wrote:
> 
> This patch adds mp3326 led driver.
> 
> Signed-off-by: Yuxi Wang <Yuxi.Wang@xxxxxxxxxxxxxxxxxxx>
> ---
>  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@xxxxxxxxxxxxxxxxxxx>
> + */
> +#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@xxxxxxxxxxxxxxxxxxx>");
> +MODULE_DESCRIPTION("MPS MP3326 LED driver");
> +MODULE_LICENSE("GPL");

Best regards,
Krzysztof




[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux OMAP]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]

  Powered by Linux