From a09af0551f5cfa850ca4d39be4395bb3526c8bdf Mon Sep 17 00:00:00 2001 From: Justin Stitt Date: Wed, 16 Aug 2023 19:37:52 +0000 Subject: leds: pca955x: Fix -Wvoid-pointer-to-enum-cast warning When building with clang 18 I see the following warning: | drivers/leds/leds-pca955x.c:487:15: warning: cast to smaller integer | type 'enum pca955x_type' from 'const void *' [-Wvoid-pointer-to-enum-cast] | 487 | chip_type = (enum pca955x_type)md; This is due to the fact that `md` is a void* while `enum pca995x_type` has the size of an int. Add uintptr_t cast to silence clang warning while also keeping enum cast for readability and consistency with other `chip_type` assignment just a few lines below: | chip_type = (enum pca955x_type)id->driver_data; Reported-by: Nathan Chancellor Closes: https://github.com/ClangBuiltLinux/linux/issues/1910 Signed-off-by: Justin Stitt Reviewed-by: Nathan Chancellor Link: https://lore.kernel.org/r/20230816-void-drivers-leds-leds-pca955x-v1-1-2967e4c1bdcc@google.com Signed-off-by: Lee Jones --- drivers/leds/leds-pca955x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/leds') diff --git a/drivers/leds/leds-pca955x.c b/drivers/leds/leds-pca955x.c index b10e1ef38db0..1d7fa0cd97bf 100644 --- a/drivers/leds/leds-pca955x.c +++ b/drivers/leds/leds-pca955x.c @@ -484,7 +484,7 @@ static int pca955x_probe(struct i2c_client *client) const void *md = device_get_match_data(&client->dev); if (md) { - chip_type = (enum pca955x_type)md; + chip_type = (enum pca955x_type)(uintptr_t)md; } else { const struct i2c_device_id *id = i2c_match_id(pca955x_id, client); -- cgit From ff861ca9f2d54a81d10cd3e8d2384fe17c10fdc4 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Fri, 15 Sep 2023 13:09:39 -0700 Subject: leds: aw200xx: Annotate struct aw200xx with __counted_by Prepare for the coming implementation by GCC and Clang of the __counted_by attribute. Flexible array members annotated with __counted_by can have their accesses bounds-checked at run-time checking via CONFIG_UBSAN_BOUNDS (for array indexing) and CONFIG_FORTIFY_SOURCE (for strcpy/memcpy-family functions). As found with Coccinelle[1], add __counted_by for struct aw200xx. [1] https://github.com/kees/kernel-tools/blob/trunk/coccinelle/examples/counted_by.cocci Signed-off-by: Kees Cook Reviewed-by: Gustavo A. R. Silva Link: https://lore.kernel.org/r/20230915200938.never.767-kees@kernel.org Signed-off-by: Lee Jones --- drivers/leds/leds-aw200xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/leds') diff --git a/drivers/leds/leds-aw200xx.c b/drivers/leds/leds-aw200xx.c index 691a743cc9b0..4d517cace3e7 100644 --- a/drivers/leds/leds-aw200xx.c +++ b/drivers/leds/leds-aw200xx.c @@ -112,7 +112,7 @@ struct aw200xx { struct mutex mutex; u32 num_leds; u32 display_rows; - struct aw200xx_led leds[]; + struct aw200xx_led leds[] __counted_by(num_leds); }; static ssize_t dim_show(struct device *dev, struct device_attribute *devattr, -- cgit From 679cec1809b4140add538f28638546ea2f5c8959 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Fri, 15 Sep 2023 13:09:48 -0700 Subject: leds: cr0014114: Annotate struct cr0014114 with __counted_by Prepare for the coming implementation by GCC and Clang of the __counted_by attribute. Flexible array members annotated with __counted_by can have their accesses bounds-checked at run-time checking via CONFIG_UBSAN_BOUNDS (for array indexing) and CONFIG_FORTIFY_SOURCE (for strcpy/memcpy-family functions). As found with Coccinelle[1], add __counted_by for struct cr0014114. [1] https://github.com/kees/kernel-tools/blob/trunk/coccinelle/examples/counted_by.cocci Signed-off-by: Kees Cook Reviewed-by: Gustavo A. R. Silva Link: https://lore.kernel.org/r/20230915200948.never.728-kees@kernel.org Signed-off-by: Lee Jones --- drivers/leds/leds-cr0014114.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/leds') diff --git a/drivers/leds/leds-cr0014114.c b/drivers/leds/leds-cr0014114.c index b33bca397ea6..c9914fc51f20 100644 --- a/drivers/leds/leds-cr0014114.c +++ b/drivers/leds/leds-cr0014114.c @@ -56,7 +56,7 @@ struct cr0014114 { struct spi_device *spi; u8 *buf; unsigned long delay; - struct cr0014114_led leds[]; + struct cr0014114_led leds[] __counted_by(count); }; static void cr0014114_calc_crc(u8 *buf, const size_t len) -- cgit From a29feca113687cc691fe9f0f23652fafbafddc90 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Fri, 15 Sep 2023 13:09:56 -0700 Subject: leds: el15203000: Annotate struct el15203000 with __counted_by Prepare for the coming implementation by GCC and Clang of the __counted_by attribute. Flexible array members annotated with __counted_by can have their accesses bounds-checked at run-time checking via CONFIG_UBSAN_BOUNDS (for array indexing) and CONFIG_FORTIFY_SOURCE (for strcpy/memcpy-family functions). As found with Coccinelle[1], add __counted_by for struct el15203000. [1] https://github.com/kees/kernel-tools/blob/trunk/coccinelle/examples/counted_by.cocci Signed-off-by: Kees Cook Reviewed-by: Gustavo A. R. Silva Link: https://lore.kernel.org/r/20230915200955.never.871-kees@kernel.org Signed-off-by: Lee Jones --- drivers/leds/leds-el15203000.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/leds') diff --git a/drivers/leds/leds-el15203000.c b/drivers/leds/leds-el15203000.c index 7e7b617bcd56..d40194a3029f 100644 --- a/drivers/leds/leds-el15203000.c +++ b/drivers/leds/leds-el15203000.c @@ -80,7 +80,7 @@ struct el15203000 { struct spi_device *spi; unsigned long delay; size_t count; - struct el15203000_led leds[]; + struct el15203000_led leds[] __counted_by(count); }; #define to_el15203000_led(d) container_of(d, struct el15203000_led, ldev) -- cgit From 52cd75108a426bdd43161d0e73e7bee95d103ed1 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Fri, 15 Sep 2023 13:10:04 -0700 Subject: leds: gpio: Annotate struct gpio_leds_priv with __counted_by Prepare for the coming implementation by GCC and Clang of the __counted_by attribute. Flexible array members annotated with __counted_by can have their accesses bounds-checked at run-time checking via CONFIG_UBSAN_BOUNDS (for array indexing) and CONFIG_FORTIFY_SOURCE (for strcpy/memcpy-family functions). As found with Coccinelle[1], add __counted_by for struct gpio_leds_priv. [1] https://github.com/kees/kernel-tools/blob/trunk/coccinelle/examples/counted_by.cocci Signed-off-by: Kees Cook Reviewed-by: Gustavo A. R. Silva Link: https://lore.kernel.org/r/20230915201003.never.148-kees@kernel.org Signed-off-by: Lee Jones --- drivers/leds/leds-gpio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/leds') diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index 7bfe40a6bfdd..a6597f0f3eb4 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -142,7 +142,7 @@ static int create_gpio_led(const struct gpio_led *template, struct gpio_leds_priv { int num_leds; - struct gpio_led_data leds[]; + struct gpio_led_data leds[] __counted_by(num_leds); }; static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev) -- cgit From bcbadbb29cb6aa6f51505514ae635fd467ebca43 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Fri, 15 Sep 2023 13:10:10 -0700 Subject: leds: lm3697: Annotate struct lm3697 with __counted_by Prepare for the coming implementation by GCC and Clang of the __counted_by attribute. Flexible array members annotated with __counted_by can have their accesses bounds-checked at run-time checking via CONFIG_UBSAN_BOUNDS (for array indexing) and CONFIG_FORTIFY_SOURCE (for strcpy/memcpy-family functions). As found with Coccinelle[1], add __counted_by for struct lm3697. [1] https://github.com/kees/kernel-tools/blob/trunk/coccinelle/examples/counted_by.cocci Signed-off-by: Kees Cook Reviewed-by: Gustavo A. R. Silva Link: https://lore.kernel.org/r/20230915201010.never.399-kees@kernel.org Signed-off-by: Lee Jones --- drivers/leds/leds-lm3697.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/leds') diff --git a/drivers/leds/leds-lm3697.c b/drivers/leds/leds-lm3697.c index cfb8ac220db6..380d17a58fe9 100644 --- a/drivers/leds/leds-lm3697.c +++ b/drivers/leds/leds-lm3697.c @@ -89,7 +89,7 @@ struct lm3697 { int bank_cfg; int num_banks; - struct lm3697_led leds[]; + struct lm3697_led leds[] __counted_by(num_banks); }; static const struct reg_default lm3697_reg_defs[] = { -- cgit From 0847c33bafe5b58f2f223153c007c1e185f84f48 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Fri, 15 Sep 2023 13:11:00 -0700 Subject: leds: qcom-lpg: Annotate struct lpg_led with __counted_by Prepare for the coming implementation by GCC and Clang of the __counted_by attribute. Flexible array members annotated with __counted_by can have their accesses bounds-checked at run-time checking via CONFIG_UBSAN_BOUNDS (for array indexing) and CONFIG_FORTIFY_SOURCE (for strcpy/memcpy-family functions). As found with Coccinelle[1], add __counted_by for struct lpg_led. [1] https://github.com/kees/kernel-tools/blob/trunk/coccinelle/examples/counted_by.cocci Signed-off-by: Kees Cook Reviewed-by: Gustavo A. R. Silva Link: https://lore.kernel.org/r/20230915201059.never.086-kees@kernel.org Signed-off-by: Lee Jones --- drivers/leds/rgb/leds-qcom-lpg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/leds') diff --git a/drivers/leds/rgb/leds-qcom-lpg.c b/drivers/leds/rgb/leds-qcom-lpg.c index df469aaa7e6e..7d93e02a030a 100644 --- a/drivers/leds/rgb/leds-qcom-lpg.c +++ b/drivers/leds/rgb/leds-qcom-lpg.c @@ -173,7 +173,7 @@ struct lpg_led { struct led_classdev_mc mcdev; unsigned int num_channels; - struct lpg_channel *channels[]; + struct lpg_channel *channels[] __counted_by(num_channels); }; /** -- cgit From eccc489ef68d70cfdd850ba24933f1febbf2893e Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Sat, 16 Sep 2023 18:45:16 +0200 Subject: leds: simatic-ipc-leds-gpio: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Make simatic_ipc_leds_gpio_remove() return void instead of returning zero unconditionally. After that the three remove callbacks that use this function were trivial to convert to return void, too. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230916164516.1063380-1-u.kleine-koenig@pengutronix.de Signed-off-by: Lee Jones --- drivers/leds/simple/simatic-ipc-leds-gpio-apollolake.c | 8 ++++---- drivers/leds/simple/simatic-ipc-leds-gpio-core.c | 4 +--- drivers/leds/simple/simatic-ipc-leds-gpio-elkhartlake.c | 7 +++---- drivers/leds/simple/simatic-ipc-leds-gpio-f7188x.c | 8 ++++---- drivers/leds/simple/simatic-ipc-leds-gpio.h | 6 +++--- 5 files changed, 15 insertions(+), 18 deletions(-) (limited to 'drivers/leds') diff --git a/drivers/leds/simple/simatic-ipc-leds-gpio-apollolake.c b/drivers/leds/simple/simatic-ipc-leds-gpio-apollolake.c index e1c712729dcf..4183ee71fcce 100644 --- a/drivers/leds/simple/simatic-ipc-leds-gpio-apollolake.c +++ b/drivers/leds/simple/simatic-ipc-leds-gpio-apollolake.c @@ -45,15 +45,15 @@ static int simatic_ipc_leds_gpio_apollolake_probe(struct platform_device *pdev) &simatic_ipc_led_gpio_table_extra); } -static int simatic_ipc_leds_gpio_apollolake_remove(struct platform_device *pdev) +static void simatic_ipc_leds_gpio_apollolake_remove(struct platform_device *pdev) { - return simatic_ipc_leds_gpio_remove(pdev, &simatic_ipc_led_gpio_table, - &simatic_ipc_led_gpio_table_extra); + simatic_ipc_leds_gpio_remove(pdev, &simatic_ipc_led_gpio_table, + &simatic_ipc_led_gpio_table_extra); } static struct platform_driver simatic_ipc_led_gpio_apollolake_driver = { .probe = simatic_ipc_leds_gpio_apollolake_probe, - .remove = simatic_ipc_leds_gpio_apollolake_remove, + .remove_new = simatic_ipc_leds_gpio_apollolake_remove, .driver = { .name = KBUILD_MODNAME, }, diff --git a/drivers/leds/simple/simatic-ipc-leds-gpio-core.c b/drivers/leds/simple/simatic-ipc-leds-gpio-core.c index c552ea73ed9d..667ba1bc3a30 100644 --- a/drivers/leds/simple/simatic-ipc-leds-gpio-core.c +++ b/drivers/leds/simple/simatic-ipc-leds-gpio-core.c @@ -33,15 +33,13 @@ static const struct gpio_led_platform_data simatic_ipc_gpio_leds_pdata = { .leds = simatic_ipc_gpio_leds, }; -int simatic_ipc_leds_gpio_remove(struct platform_device *pdev, +void simatic_ipc_leds_gpio_remove(struct platform_device *pdev, struct gpiod_lookup_table *table, struct gpiod_lookup_table *table_extra) { gpiod_remove_lookup_table(table); gpiod_remove_lookup_table(table_extra); platform_device_unregister(simatic_leds_pdev); - - return 0; } EXPORT_SYMBOL_GPL(simatic_ipc_leds_gpio_remove); diff --git a/drivers/leds/simple/simatic-ipc-leds-gpio-elkhartlake.c b/drivers/leds/simple/simatic-ipc-leds-gpio-elkhartlake.c index 6ba21dbb3ba0..4a53d4dbf52f 100644 --- a/drivers/leds/simple/simatic-ipc-leds-gpio-elkhartlake.c +++ b/drivers/leds/simple/simatic-ipc-leds-gpio-elkhartlake.c @@ -36,15 +36,14 @@ static int simatic_ipc_leds_gpio_elkhartlake_probe(struct platform_device *pdev) NULL); } -static int simatic_ipc_leds_gpio_elkhartlake_remove(struct platform_device *pdev) +static void simatic_ipc_leds_gpio_elkhartlake_remove(struct platform_device *pdev) { - return simatic_ipc_leds_gpio_remove(pdev, &simatic_ipc_led_gpio_table, - NULL); + simatic_ipc_leds_gpio_remove(pdev, &simatic_ipc_led_gpio_table, NULL); } static struct platform_driver simatic_ipc_led_gpio_elkhartlake_driver = { .probe = simatic_ipc_leds_gpio_elkhartlake_probe, - .remove = simatic_ipc_leds_gpio_elkhartlake_remove, + .remove_new = simatic_ipc_leds_gpio_elkhartlake_remove, .driver = { .name = KBUILD_MODNAME, }, diff --git a/drivers/leds/simple/simatic-ipc-leds-gpio-f7188x.c b/drivers/leds/simple/simatic-ipc-leds-gpio-f7188x.c index 583a6b6c7c22..c7c3a1f986e6 100644 --- a/drivers/leds/simple/simatic-ipc-leds-gpio-f7188x.c +++ b/drivers/leds/simple/simatic-ipc-leds-gpio-f7188x.c @@ -45,15 +45,15 @@ static int simatic_ipc_leds_gpio_f7188x_probe(struct platform_device *pdev) &simatic_ipc_led_gpio_table_extra); } -static int simatic_ipc_leds_gpio_f7188x_remove(struct platform_device *pdev) +static void simatic_ipc_leds_gpio_f7188x_remove(struct platform_device *pdev) { - return simatic_ipc_leds_gpio_remove(pdev, &simatic_ipc_led_gpio_table, - &simatic_ipc_led_gpio_table_extra); + simatic_ipc_leds_gpio_remove(pdev, &simatic_ipc_led_gpio_table, + &simatic_ipc_led_gpio_table_extra); } static struct platform_driver simatic_ipc_led_gpio_driver = { .probe = simatic_ipc_leds_gpio_f7188x_probe, - .remove = simatic_ipc_leds_gpio_f7188x_remove, + .remove_new = simatic_ipc_leds_gpio_f7188x_remove, .driver = { .name = KBUILD_MODNAME, }, diff --git a/drivers/leds/simple/simatic-ipc-leds-gpio.h b/drivers/leds/simple/simatic-ipc-leds-gpio.h index 3d4877aa4e0c..6b2519809cee 100644 --- a/drivers/leds/simple/simatic-ipc-leds-gpio.h +++ b/drivers/leds/simple/simatic-ipc-leds-gpio.h @@ -15,8 +15,8 @@ int simatic_ipc_leds_gpio_probe(struct platform_device *pdev, struct gpiod_lookup_table *table, struct gpiod_lookup_table *table_extra); -int simatic_ipc_leds_gpio_remove(struct platform_device *pdev, - struct gpiod_lookup_table *table, - struct gpiod_lookup_table *table_extra); +void simatic_ipc_leds_gpio_remove(struct platform_device *pdev, + struct gpiod_lookup_table *table, + struct gpiod_lookup_table *table_extra); #endif /* _SIMATIC_IPC_LEDS_GPIO_H */ -- cgit From 6061302092305a0584aacf0d82a9d5e66653884c Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Sun, 17 Sep 2023 15:09:47 +0200 Subject: leds: Convert all platform drivers to return void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). All platform drivers below drivers/leds/ unconditionally return zero in their remove callback and so can be converted trivially to the variant returning void. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230917130947.1122198-1-u.kleine-koenig@pengutronix.de Signed-off-by: Lee Jones --- drivers/leds/blink/leds-lgm-sso.c | 6 ++---- drivers/leds/flash/leds-aat1290.c | 6 ++---- drivers/leds/flash/leds-ktd2692.c | 6 ++---- drivers/leds/flash/leds-max77693.c | 6 ++---- drivers/leds/flash/leds-mt6360.c | 5 ++--- drivers/leds/flash/leds-qcom-flash.c | 5 ++--- drivers/leds/flash/leds-rt8515.c | 6 ++---- drivers/leds/flash/leds-sgm3140.c | 6 ++---- drivers/leds/leds-88pm860x.c | 6 ++---- drivers/leds/leds-adp5520.c | 6 ++---- drivers/leds/leds-clevo-mail.c | 5 ++--- drivers/leds/leds-da903x.c | 6 ++---- drivers/leds/leds-da9052.c | 6 ++---- drivers/leds/leds-lm3533.c | 6 ++---- drivers/leds/leds-mc13783.c | 6 ++---- drivers/leds/leds-mlxreg.c | 6 ++---- drivers/leds/leds-mt6323.c | 6 ++---- drivers/leds/leds-nic78bx.c | 6 ++---- drivers/leds/leds-powernv.c | 5 ++--- drivers/leds/leds-rb532.c | 5 ++--- drivers/leds/leds-regulator.c | 5 ++--- drivers/leds/leds-sc27xx-bltc.c | 5 ++--- drivers/leds/leds-sunfire.c | 8 +++----- drivers/leds/leds-wm831x-status.c | 6 ++---- drivers/leds/leds-wm8350.c | 5 ++--- drivers/leds/rgb/leds-qcom-lpg.c | 6 ++---- 26 files changed, 53 insertions(+), 97 deletions(-) (limited to 'drivers/leds') diff --git a/drivers/leds/blink/leds-lgm-sso.c b/drivers/leds/blink/leds-lgm-sso.c index 35c61311e7fd..7b04ea146260 100644 --- a/drivers/leds/blink/leds-lgm-sso.c +++ b/drivers/leds/blink/leds-lgm-sso.c @@ -837,7 +837,7 @@ static int intel_sso_led_probe(struct platform_device *pdev) return 0; } -static int intel_sso_led_remove(struct platform_device *pdev) +static void intel_sso_led_remove(struct platform_device *pdev) { struct sso_led_priv *priv; struct sso_led *led, *n; @@ -850,8 +850,6 @@ static int intel_sso_led_remove(struct platform_device *pdev) } regmap_exit(priv->mmap); - - return 0; } static const struct of_device_id of_sso_led_match[] = { @@ -863,7 +861,7 @@ MODULE_DEVICE_TABLE(of, of_sso_led_match); static struct platform_driver intel_sso_led_driver = { .probe = intel_sso_led_probe, - .remove = intel_sso_led_remove, + .remove_new = intel_sso_led_remove, .driver = { .name = "lgm-ssoled", .of_match_table = of_sso_led_match, diff --git a/drivers/leds/flash/leds-aat1290.c b/drivers/leds/flash/leds-aat1290.c index f12ecb2c6580..0195935a7c99 100644 --- a/drivers/leds/flash/leds-aat1290.c +++ b/drivers/leds/flash/leds-aat1290.c @@ -522,7 +522,7 @@ err_flash_register: return ret; } -static int aat1290_led_remove(struct platform_device *pdev) +static void aat1290_led_remove(struct platform_device *pdev) { struct aat1290_led *led = platform_get_drvdata(pdev); @@ -530,8 +530,6 @@ static int aat1290_led_remove(struct platform_device *pdev) led_classdev_flash_unregister(&led->fled_cdev); mutex_destroy(&led->lock); - - return 0; } static const struct of_device_id aat1290_led_dt_match[] = { @@ -542,7 +540,7 @@ MODULE_DEVICE_TABLE(of, aat1290_led_dt_match); static struct platform_driver aat1290_led_driver = { .probe = aat1290_led_probe, - .remove = aat1290_led_remove, + .remove_new = aat1290_led_remove, .driver = { .name = "aat1290", .of_match_table = aat1290_led_dt_match, diff --git a/drivers/leds/flash/leds-ktd2692.c b/drivers/leds/flash/leds-ktd2692.c index 670f3bf2e906..598eee5daa52 100644 --- a/drivers/leds/flash/leds-ktd2692.c +++ b/drivers/leds/flash/leds-ktd2692.c @@ -386,15 +386,13 @@ static int ktd2692_probe(struct platform_device *pdev) return 0; } -static int ktd2692_remove(struct platform_device *pdev) +static void ktd2692_remove(struct platform_device *pdev) { struct ktd2692_context *led = platform_get_drvdata(pdev); led_classdev_flash_unregister(&led->fled_cdev); mutex_destroy(&led->lock); - - return 0; } static const struct of_device_id ktd2692_match[] = { @@ -409,7 +407,7 @@ static struct platform_driver ktd2692_driver = { .of_match_table = ktd2692_match, }, .probe = ktd2692_probe, - .remove = ktd2692_remove, + .remove_new = ktd2692_remove, }; module_platform_driver(ktd2692_driver); diff --git a/drivers/leds/flash/leds-max77693.c b/drivers/leds/flash/leds-max77693.c index 5c1faeb55a31..9f016b851193 100644 --- a/drivers/leds/flash/leds-max77693.c +++ b/drivers/leds/flash/leds-max77693.c @@ -1016,7 +1016,7 @@ err_register_led1: return ret; } -static int max77693_led_remove(struct platform_device *pdev) +static void max77693_led_remove(struct platform_device *pdev) { struct max77693_led_device *led = platform_get_drvdata(pdev); struct max77693_sub_led *sub_leds = led->sub_leds; @@ -1032,8 +1032,6 @@ static int max77693_led_remove(struct platform_device *pdev) } mutex_destroy(&led->lock); - - return 0; } static const struct of_device_id max77693_led_dt_match[] = { @@ -1044,7 +1042,7 @@ MODULE_DEVICE_TABLE(of, max77693_led_dt_match); static struct platform_driver max77693_led_driver = { .probe = max77693_led_probe, - .remove = max77693_led_remove, + .remove_new = max77693_led_remove, .driver = { .name = "max77693-led", .of_match_table = max77693_led_dt_match, diff --git a/drivers/leds/flash/leds-mt6360.c b/drivers/leds/flash/leds-mt6360.c index 1af6c5898343..81401c481e2b 100644 --- a/drivers/leds/flash/leds-mt6360.c +++ b/drivers/leds/flash/leds-mt6360.c @@ -855,12 +855,11 @@ out_flash_release: return ret; } -static int mt6360_led_remove(struct platform_device *pdev) +static void mt6360_led_remove(struct platform_device *pdev) { struct mt6360_priv *priv = platform_get_drvdata(pdev); mt6360_v4l2_flash_release(priv); - return 0; } static const struct of_device_id __maybe_unused mt6360_led_of_id[] = { @@ -875,7 +874,7 @@ static struct platform_driver mt6360_led_driver = { .of_match_table = mt6360_led_of_id, }, .probe = mt6360_led_probe, - .remove = mt6360_led_remove, + .remove_new = mt6360_led_remove, }; module_platform_driver(mt6360_led_driver); diff --git a/drivers/leds/flash/leds-qcom-flash.c b/drivers/leds/flash/leds-qcom-flash.c index a73d3ea5c97a..7c99a3039171 100644 --- a/drivers/leds/flash/leds-qcom-flash.c +++ b/drivers/leds/flash/leds-qcom-flash.c @@ -755,7 +755,7 @@ release: return rc; } -static int qcom_flash_led_remove(struct platform_device *pdev) +static void qcom_flash_led_remove(struct platform_device *pdev) { struct qcom_flash_data *flash_data = platform_get_drvdata(pdev); @@ -763,7 +763,6 @@ static int qcom_flash_led_remove(struct platform_device *pdev) v4l2_flash_release(flash_data->v4l2_flash[flash_data->leds_count--]); mutex_destroy(&flash_data->lock); - return 0; } static const struct of_device_id qcom_flash_led_match_table[] = { @@ -778,7 +777,7 @@ static struct platform_driver qcom_flash_led_driver = { .of_match_table = qcom_flash_led_match_table, }, .probe = qcom_flash_led_probe, - .remove = qcom_flash_led_remove, + .remove_new = qcom_flash_led_remove, }; module_platform_driver(qcom_flash_led_driver); diff --git a/drivers/leds/flash/leds-rt8515.c b/drivers/leds/flash/leds-rt8515.c index 44904fdee3cc..eef426924eaf 100644 --- a/drivers/leds/flash/leds-rt8515.c +++ b/drivers/leds/flash/leds-rt8515.c @@ -367,15 +367,13 @@ static int rt8515_probe(struct platform_device *pdev) return 0; } -static int rt8515_remove(struct platform_device *pdev) +static void rt8515_remove(struct platform_device *pdev) { struct rt8515 *rt = platform_get_drvdata(pdev); rt8515_v4l2_flash_release(rt); del_timer_sync(&rt->powerdown_timer); mutex_destroy(&rt->lock); - - return 0; } static const struct of_device_id rt8515_match[] = { @@ -390,7 +388,7 @@ static struct platform_driver rt8515_driver = { .of_match_table = rt8515_match, }, .probe = rt8515_probe, - .remove = rt8515_remove, + .remove_new = rt8515_remove, }; module_platform_driver(rt8515_driver); diff --git a/drivers/leds/flash/leds-sgm3140.c b/drivers/leds/flash/leds-sgm3140.c index d3f50dca5136..eb648ff54b4e 100644 --- a/drivers/leds/flash/leds-sgm3140.c +++ b/drivers/leds/flash/leds-sgm3140.c @@ -278,15 +278,13 @@ err: return ret; } -static int sgm3140_remove(struct platform_device *pdev) +static void sgm3140_remove(struct platform_device *pdev) { struct sgm3140 *priv = platform_get_drvdata(pdev); del_timer_sync(&priv->powerdown_timer); v4l2_flash_release(priv->v4l2_flash); - - return 0; } static const struct of_device_id sgm3140_dt_match[] = { @@ -299,7 +297,7 @@ MODULE_DEVICE_TABLE(of, sgm3140_dt_match); static struct platform_driver sgm3140_driver = { .probe = sgm3140_probe, - .remove = sgm3140_remove, + .remove_new = sgm3140_remove, .driver = { .name = "sgm3140", .of_match_table = sgm3140_dt_match, diff --git a/drivers/leds/leds-88pm860x.c b/drivers/leds/leds-88pm860x.c index 508d0d859f2e..033ab5fed38a 100644 --- a/drivers/leds/leds-88pm860x.c +++ b/drivers/leds/leds-88pm860x.c @@ -215,13 +215,11 @@ static int pm860x_led_probe(struct platform_device *pdev) return 0; } -static int pm860x_led_remove(struct platform_device *pdev) +static void pm860x_led_remove(struct platform_device *pdev) { struct pm860x_led *data = platform_get_drvdata(pdev); led_classdev_unregister(&data->cdev); - - return 0; } static struct platform_driver pm860x_led_driver = { @@ -229,7 +227,7 @@ static struct platform_driver pm860x_led_driver = { .name = "88pm860x-led", }, .probe = pm860x_led_probe, - .remove = pm860x_led_remove, + .remove_new = pm860x_led_remove, }; module_platform_driver(pm860x_led_driver); diff --git a/drivers/leds/leds-adp5520.c b/drivers/leds/leds-adp5520.c index 5a0cc7af2df8..d89a4dca50ae 100644 --- a/drivers/leds/leds-adp5520.c +++ b/drivers/leds/leds-adp5520.c @@ -163,7 +163,7 @@ err: return ret; } -static int adp5520_led_remove(struct platform_device *pdev) +static void adp5520_led_remove(struct platform_device *pdev) { struct adp5520_leds_platform_data *pdata = dev_get_platdata(&pdev->dev); struct adp5520_led *led; @@ -177,8 +177,6 @@ static int adp5520_led_remove(struct platform_device *pdev) for (i = 0; i < pdata->num_leds; i++) { led_classdev_unregister(&led[i].cdev); } - - return 0; } static struct platform_driver adp5520_led_driver = { @@ -186,7 +184,7 @@ static struct platform_driver adp5520_led_driver = { .name = "adp5520-led", }, .probe = adp5520_led_probe, - .remove = adp5520_led_remove, + .remove_new = adp5520_led_remove, }; module_platform_driver(adp5520_led_driver); diff --git a/drivers/leds/leds-clevo-mail.c b/drivers/leds/leds-clevo-mail.c index f512e99b976b..82da0fe688ad 100644 --- a/drivers/leds/leds-clevo-mail.c +++ b/drivers/leds/leds-clevo-mail.c @@ -159,14 +159,13 @@ static int __init clevo_mail_led_probe(struct platform_device *pdev) return led_classdev_register(&pdev->dev, &clevo_mail_led); } -static int clevo_mail_led_remove(struct platform_device *pdev) +static void clevo_mail_led_remove(struct platform_device *pdev) { led_classdev_unregister(&clevo_mail_led); - return 0; } static struct platform_driver clevo_mail_led_driver = { - .remove = clevo_mail_led_remove, + .remove_new = clevo_mail_led_remove, .driver = { .name = KBUILD_MODNAME, }, diff --git a/drivers/leds/leds-da903x.c b/drivers/leds/leds-da903x.c index 2b5fb00438a2..f067a5f4d3c4 100644 --- a/drivers/leds/leds-da903x.c +++ b/drivers/leds/leds-da903x.c @@ -121,13 +121,11 @@ static int da903x_led_probe(struct platform_device *pdev) return 0; } -static int da903x_led_remove(struct platform_device *pdev) +static void da903x_led_remove(struct platform_device *pdev) { struct da903x_led *led = platform_get_drvdata(pdev); led_classdev_unregister(&led->cdev); - - return 0; } static struct platform_driver da903x_led_driver = { @@ -135,7 +133,7 @@ static struct platform_driver da903x_led_driver = { .name = "da903x-led", }, .probe = da903x_led_probe, - .remove = da903x_led_remove, + .remove_new = da903x_led_remove, }; module_platform_driver(da903x_led_driver); diff --git a/drivers/leds/leds-da9052.c b/drivers/leds/leds-da9052.c index 04060c862bf9..64679d62076b 100644 --- a/drivers/leds/leds-da9052.c +++ b/drivers/leds/leds-da9052.c @@ -156,7 +156,7 @@ err: return error; } -static int da9052_led_remove(struct platform_device *pdev) +static void da9052_led_remove(struct platform_device *pdev) { struct da9052_led *led = platform_get_drvdata(pdev); struct da9052_pdata *pdata; @@ -172,8 +172,6 @@ static int da9052_led_remove(struct platform_device *pdev) da9052_set_led_brightness(&led[i], LED_OFF); led_classdev_unregister(&led[i].cdev); } - - return 0; } static struct platform_driver da9052_led_driver = { @@ -181,7 +179,7 @@ static struct platform_driver da9052_led_driver = { .name = "da9052-leds", }, .probe = da9052_led_probe, - .remove = da9052_led_remove, + .remove_new = da9052_led_remove, }; module_platform_driver(da9052_led_driver); diff --git a/drivers/leds/leds-lm3533.c b/drivers/leds/leds-lm3533.c index bcd414eb4724..a3d33165d262 100644 --- a/drivers/leds/leds-lm3533.c +++ b/drivers/leds/leds-lm3533.c @@ -718,7 +718,7 @@ err_deregister: return ret; } -static int lm3533_led_remove(struct platform_device *pdev) +static void lm3533_led_remove(struct platform_device *pdev) { struct lm3533_led *led = platform_get_drvdata(pdev); @@ -726,8 +726,6 @@ static int lm3533_led_remove(struct platform_device *pdev) lm3533_ctrlbank_disable(&led->cb); led_classdev_unregister(&led->cdev); - - return 0; } static void lm3533_led_shutdown(struct platform_device *pdev) @@ -746,7 +744,7 @@ static struct platform_driver lm3533_led_driver = { .name = "lm3533-leds", }, .probe = lm3533_led_probe, - .remove = lm3533_led_remove, + .remove_new = lm3533_led_remove, .shutdown = lm3533_led_shutdown, }; module_platform_driver(lm3533_led_driver); diff --git a/drivers/leds/leds-mc13783.c b/drivers/leds/leds-mc13783.c index 675502c15c2b..bbd1d359bba4 100644 --- a/drivers/leds/leds-mc13783.c +++ b/drivers/leds/leds-mc13783.c @@ -261,15 +261,13 @@ static int __init mc13xxx_led_probe(struct platform_device *pdev) return ret; } -static int mc13xxx_led_remove(struct platform_device *pdev) +static void mc13xxx_led_remove(struct platform_device *pdev) { struct mc13xxx_leds *leds = platform_get_drvdata(pdev); int i; for (i = 0; i < leds->num_leds; i++) led_classdev_unregister(&leds->led[i].cdev); - - return 0; } static const struct mc13xxx_led_devtype mc13783_led_devtype = { @@ -305,7 +303,7 @@ static struct platform_driver mc13xxx_led_driver = { .driver = { .name = "mc13xxx-led", }, - .remove = mc13xxx_led_remove, + .remove_new = mc13xxx_led_remove, .id_table = mc13xxx_led_id_table, }; module_platform_driver_probe(mc13xxx_led_driver, mc13xxx_led_probe); diff --git a/drivers/leds/leds-mlxreg.c b/drivers/leds/leds-mlxreg.c index 39210653acf7..d8e3d5d8d2d0 100644 --- a/drivers/leds/leds-mlxreg.c +++ b/drivers/leds/leds-mlxreg.c @@ -275,13 +275,11 @@ static int mlxreg_led_probe(struct platform_device *pdev) return mlxreg_led_config(priv); } -static int mlxreg_led_remove(struct platform_device *pdev) +static void mlxreg_led_remove(struct platform_device *pdev) { struct mlxreg_led_priv_data *priv = dev_get_drvdata(&pdev->dev); mutex_destroy(&priv->access_lock); - - return 0; } static struct platform_driver mlxreg_led_driver = { @@ -289,7 +287,7 @@ static struct platform_driver mlxreg_led_driver = { .name = "leds-mlxreg", }, .probe = mlxreg_led_probe, - .remove = mlxreg_led_remove, + .remove_new = mlxreg_led_remove, }; module_platform_driver(mlxreg_led_driver); diff --git a/drivers/leds/leds-mt6323.c b/drivers/leds/leds-mt6323.c index 24f35bdb55fb..40d508510823 100644 --- a/drivers/leds/leds-mt6323.c +++ b/drivers/leds/leds-mt6323.c @@ -632,7 +632,7 @@ put_child_node: return ret; } -static int mt6323_led_remove(struct platform_device *pdev) +static void mt6323_led_remove(struct platform_device *pdev) { struct mt6323_leds *leds = platform_get_drvdata(pdev); const struct mt6323_regs *regs = leds->pdata->regs; @@ -647,8 +647,6 @@ static int mt6323_led_remove(struct platform_device *pdev) RG_DRV_32K_CK_PDN); mutex_destroy(&leds->lock); - - return 0; } static const struct mt6323_regs mt6323_registers = { @@ -723,7 +721,7 @@ MODULE_DEVICE_TABLE(of, mt6323_led_dt_match); static struct platform_driver mt6323_led_driver = { .probe = mt6323_led_probe, - .remove = mt6323_led_remove, + .remove_new = mt6323_led_remove, .driver = { .name = "mt6323-led", .of_match_table = mt6323_led_dt_match, diff --git a/drivers/leds/leds-nic78bx.c b/drivers/leds/leds-nic78bx.c index f196f52eec1e..a86b43dd995e 100644 --- a/drivers/leds/leds-nic78bx.c +++ b/drivers/leds/leds-nic78bx.c @@ -167,15 +167,13 @@ static int nic78bx_probe(struct platform_device *pdev) return ret; } -static int nic78bx_remove(struct platform_device *pdev) +static void nic78bx_remove(struct platform_device *pdev) { struct nic78bx_led_data *led_data = platform_get_drvdata(pdev); /* Lock LED register */ outb(NIC78BX_LOCK_VALUE, led_data->io_base + NIC78BX_LOCK_REG_OFFSET); - - return 0; } static const struct acpi_device_id led_device_ids[] = { @@ -186,7 +184,7 @@ MODULE_DEVICE_TABLE(acpi, led_device_ids); static struct platform_driver led_driver = { .probe = nic78bx_probe, - .remove = nic78bx_remove, + .remove_new = nic78bx_remove, .driver = { .name = KBUILD_MODNAME, .acpi_match_table = ACPI_PTR(led_device_ids), diff --git a/drivers/leds/leds-powernv.c b/drivers/leds/leds-powernv.c index 743e2cdd0891..4f01acb75727 100644 --- a/drivers/leds/leds-powernv.c +++ b/drivers/leds/leds-powernv.c @@ -309,7 +309,7 @@ out: } /* Platform driver remove */ -static int powernv_led_remove(struct platform_device *pdev) +static void powernv_led_remove(struct platform_device *pdev) { struct powernv_led_common *powernv_led_common; @@ -321,7 +321,6 @@ static int powernv_led_remove(struct platform_device *pdev) mutex_destroy(&powernv_led_common->lock); dev_info(&pdev->dev, "PowerNV led module unregistered\n"); - return 0; } /* Platform driver property match */ @@ -335,7 +334,7 @@ MODULE_DEVICE_TABLE(of, powernv_led_match); static struct platform_driver powernv_led_driver = { .probe = powernv_led_probe, - .remove = powernv_led_remove, + .remove_new = powernv_led_remove, .driver = { .name = "powernv-led-driver", .of_match_table = powernv_led_match, diff --git a/drivers/leds/leds-rb532.c b/drivers/leds/leds-rb532.c index b6447c1721b4..e66f73879c8e 100644 --- a/drivers/leds/leds-rb532.c +++ b/drivers/leds/leds-rb532.c @@ -42,15 +42,14 @@ static int rb532_led_probe(struct platform_device *pdev) return led_classdev_register(&pdev->dev, &rb532_uled); } -static int rb532_led_remove(struct platform_device *pdev) +static void rb532_led_remove(struct platform_device *pdev) { led_classdev_unregister(&rb532_uled); - return 0; } static struct platform_driver rb532_led_driver = { .probe = rb532_led_probe, - .remove = rb532_led_remove, + .remove_new = rb532_led_remove, .driver = { .name = "rb532-led", }, diff --git a/drivers/leds/leds-regulator.c b/drivers/leds/leds-regulator.c index 8a8b73b4e358..848e929c4a61 100644 --- a/drivers/leds/leds-regulator.c +++ b/drivers/leds/leds-regulator.c @@ -173,13 +173,12 @@ static int regulator_led_probe(struct platform_device *pdev) return 0; } -static int regulator_led_remove(struct platform_device *pdev) +static void regulator_led_remove(struct platform_device *pdev) { struct regulator_led *led = platform_get_drvdata(pdev); led_classdev_unregister(&led->cdev); regulator_led_disable(led); - return 0; } static const struct of_device_id regulator_led_of_match[] = { @@ -194,7 +193,7 @@ static struct platform_driver regulator_led_driver = { .of_match_table = regulator_led_of_match, }, .probe = regulator_led_probe, - .remove = regulator_led_remove, + .remove_new = regulator_led_remove, }; module_platform_driver(regulator_led_driver); diff --git a/drivers/leds/leds-sc27xx-bltc.c b/drivers/leds/leds-sc27xx-bltc.c index e199ea15e406..af1f00a2f328 100644 --- a/drivers/leds/leds-sc27xx-bltc.c +++ b/drivers/leds/leds-sc27xx-bltc.c @@ -330,12 +330,11 @@ static int sc27xx_led_probe(struct platform_device *pdev) return err; } -static int sc27xx_led_remove(struct platform_device *pdev) +static void sc27xx_led_remove(struct platform_device *pdev) { struct sc27xx_led_priv *priv = platform_get_drvdata(pdev); mutex_destroy(&priv->lock); - return 0; } static const struct of_device_id sc27xx_led_of_match[] = { @@ -350,7 +349,7 @@ static struct platform_driver sc27xx_led_driver = { .of_match_table = sc27xx_led_of_match, }, .probe = sc27xx_led_probe, - .remove = sc27xx_led_remove, + .remove_new = sc27xx_led_remove, }; module_platform_driver(sc27xx_led_driver); diff --git a/drivers/leds/leds-sunfire.c b/drivers/leds/leds-sunfire.c index eba7313719bf..6fd89efb420a 100644 --- a/drivers/leds/leds-sunfire.c +++ b/drivers/leds/leds-sunfire.c @@ -163,15 +163,13 @@ static int sunfire_led_generic_probe(struct platform_device *pdev, return 0; } -static int sunfire_led_generic_remove(struct platform_device *pdev) +static void sunfire_led_generic_remove(struct platform_device *pdev) { struct sunfire_drvdata *p = platform_get_drvdata(pdev); int i; for (i = 0; i < NUM_LEDS_PER_BOARD; i++) led_classdev_unregister(&p->leds[i].led_cdev); - - return 0; } static struct led_type clockboard_led_types[NUM_LEDS_PER_BOARD] = { @@ -221,7 +219,7 @@ MODULE_ALIAS("platform:sunfire-fhc-leds"); static struct platform_driver sunfire_clockboard_led_driver = { .probe = sunfire_clockboard_led_probe, - .remove = sunfire_led_generic_remove, + .remove_new = sunfire_led_generic_remove, .driver = { .name = "sunfire-clockboard-leds", }, @@ -229,7 +227,7 @@ static struct platform_driver sunfire_clockboard_led_driver = { static struct platform_driver sunfire_fhc_led_driver = { .probe = sunfire_fhc_led_probe, - .remove = sunfire_led_generic_remove, + .remove_new = sunfire_led_generic_remove, .driver = { .name = "sunfire-fhc-leds", }, diff --git a/drivers/leds/leds-wm831x-status.c b/drivers/leds/leds-wm831x-status.c index c48b80574f02..70b32d80f960 100644 --- a/drivers/leds/leds-wm831x-status.c +++ b/drivers/leds/leds-wm831x-status.c @@ -280,13 +280,11 @@ static int wm831x_status_probe(struct platform_device *pdev) return 0; } -static int wm831x_status_remove(struct platform_device *pdev) +static void wm831x_status_remove(struct platform_device *pdev) { struct wm831x_status *drvdata = platform_get_drvdata(pdev); led_classdev_unregister(&drvdata->cdev); - - return 0; } static struct platform_driver wm831x_status_driver = { @@ -294,7 +292,7 @@ static struct platform_driver wm831x_status_driver = { .name = "wm831x-status", }, .probe = wm831x_status_probe, - .remove = wm831x_status_remove, + .remove_new = wm831x_status_remove, }; module_platform_driver(wm831x_status_driver); diff --git a/drivers/leds/leds-wm8350.c b/drivers/leds/leds-wm8350.c index 8f243c413723..61cbefa05710 100644 --- a/drivers/leds/leds-wm8350.c +++ b/drivers/leds/leds-wm8350.c @@ -242,13 +242,12 @@ static int wm8350_led_probe(struct platform_device *pdev) return led_classdev_register(&pdev->dev, &led->cdev); } -static int wm8350_led_remove(struct platform_device *pdev) +static void wm8350_led_remove(struct platform_device *pdev) { struct wm8350_led *led = platform_get_drvdata(pdev); led_classdev_unregister(&led->cdev); wm8350_led_disable(led); - return 0; } static struct platform_driver wm8350_led_driver = { @@ -256,7 +255,7 @@ static struct platform_driver wm8350_led_driver = { .name = "wm8350-led", }, .probe = wm8350_led_probe, - .remove = wm8350_led_remove, + .remove_new = wm8350_led_remove, .shutdown = wm8350_led_shutdown, }; diff --git a/drivers/leds/rgb/leds-qcom-lpg.c b/drivers/leds/rgb/leds-qcom-lpg.c index 7d93e02a030a..bf03abb94e68 100644 --- a/drivers/leds/rgb/leds-qcom-lpg.c +++ b/drivers/leds/rgb/leds-qcom-lpg.c @@ -1364,13 +1364,11 @@ static int lpg_probe(struct platform_device *pdev) return lpg_add_pwm(lpg); } -static int lpg_remove(struct platform_device *pdev) +static void lpg_remove(struct platform_device *pdev) { struct lpg *lpg = platform_get_drvdata(pdev); pwmchip_remove(&lpg->pwm); - - return 0; } static const struct lpg_data pm8916_pwm_data = { @@ -1532,7 +1530,7 @@ MODULE_DEVICE_TABLE(of, lpg_of_table); static struct platform_driver lpg_driver = { .probe = lpg_probe, - .remove = lpg_remove, + .remove_new = lpg_remove, .driver = { .name = "qcom-spmi-lpg", .of_match_table = lpg_of_table, -- cgit From 476301c15d44831413b94d54f248233b14c0ad85 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Fri, 15 Sep 2023 13:10:20 -0700 Subject: leds: mt6360: Annotate struct mt6360_priv with __counted_by Prepare for the coming implementation by GCC and Clang of the __counted_by attribute. Flexible array members annotated with __counted_by can have their accesses bounds-checked at run-time checking via CONFIG_UBSAN_BOUNDS (for array indexing) and CONFIG_FORTIFY_SOURCE (for strcpy/memcpy-family functions). As found with Coccinelle[1], add __counted_by for struct mt6360_priv. [1] https://github.com/kees/kernel-tools/blob/trunk/coccinelle/examples/counted_by.cocci Signed-off-by: Kees Cook Reviewed-by: Gustavo A. R. Silva Reviewed-by: AngeloGioacchino Del Regno Link: https://lore.kernel.org/r/20230915201020.never.433-kees@kernel.org Signed-off-by: Lee Jones --- drivers/leds/flash/leds-mt6360.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/leds') diff --git a/drivers/leds/flash/leds-mt6360.c b/drivers/leds/flash/leds-mt6360.c index 81401c481e2b..a90de82f4568 100644 --- a/drivers/leds/flash/leds-mt6360.c +++ b/drivers/leds/flash/leds-mt6360.c @@ -91,7 +91,7 @@ struct mt6360_priv { unsigned int fled_torch_used; unsigned int leds_active; unsigned int leds_count; - struct mt6360_led leds[]; + struct mt6360_led leds[] __counted_by(leds_count); }; static int mt6360_mc_brightness_set(struct led_classdev *lcdev, -- cgit From e3c9d952139c1eb42f35bdcdcb85a66a72587b34 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Fri, 15 Sep 2023 13:10:52 -0700 Subject: leds: mt6370: Annotate struct mt6370_priv with __counted_by Prepare for the coming implementation by GCC and Clang of the __counted_by attribute. Flexible array members annotated with __counted_by can have their accesses bounds-checked at run-time checking via CONFIG_UBSAN_BOUNDS (for array indexing) and CONFIG_FORTIFY_SOURCE (for strcpy/memcpy-family functions). As found with Coccinelle[1], add __counted_by for struct mt6370_priv. [1] https://github.com/kees/kernel-tools/blob/trunk/coccinelle/examples/counted_by.cocci Signed-off-by: Kees Cook Reviewed-by: Gustavo A. R. Silva Reviewed-by: AngeloGioacchino Del Regno Link: https://lore.kernel.org/r/20230915201051.never.429-kees@kernel.org Signed-off-by: Lee Jones --- drivers/leds/flash/leds-mt6370-flash.c | 2 +- drivers/leds/rgb/leds-mt6370-rgb.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/leds') diff --git a/drivers/leds/flash/leds-mt6370-flash.c b/drivers/leds/flash/leds-mt6370-flash.c index 931067c8a75f..912d9d622320 100644 --- a/drivers/leds/flash/leds-mt6370-flash.c +++ b/drivers/leds/flash/leds-mt6370-flash.c @@ -81,7 +81,7 @@ struct mt6370_priv { unsigned int fled_torch_used; unsigned int leds_active; unsigned int leds_count; - struct mt6370_led leds[]; + struct mt6370_led leds[] __counted_by(leds_count); }; static int mt6370_torch_brightness_set(struct led_classdev *lcdev, enum led_brightness level) diff --git a/drivers/leds/rgb/leds-mt6370-rgb.c b/drivers/leds/rgb/leds-mt6370-rgb.c index bb62431efe83..448d0da11848 100644 --- a/drivers/leds/rgb/leds-mt6370-rgb.c +++ b/drivers/leds/rgb/leds-mt6370-rgb.c @@ -153,7 +153,7 @@ struct mt6370_priv { const struct mt6370_pdata *pdata; unsigned int leds_count; unsigned int leds_active; - struct mt6370_led leds[]; + struct mt6370_led leds[] __counted_by(leds_count); }; static const struct reg_field common_reg_fields[F_MAX_FIELDS] = { -- cgit From 7c977019c53ed2689e5509ebd1d89a424cf3d313 Mon Sep 17 00:00:00 2001 From: Stefan Eichenberger Date: Mon, 18 Sep 2023 16:32:38 +0200 Subject: leds: lp55xx: Use gpiod_set_value_cansleep() Use gpiod_set_value_cansleep in the init_device function. Without this change, the driver may print a warning if the LP55xx enable pin is connected to a GPIO chip which can sleep (e.g. a GPIO expander): WARNING: CPU: 0 PID: 2719 at drivers/gpio/gpiolib.c:3051 gpiod_set_value+0x64/0xbc Signed-off-by: Stefan Eichenberger Link: https://lore.kernel.org/r/20230918143238.75600-1-eichest@gmail.com Signed-off-by: Lee Jones --- drivers/leds/leds-lp55xx-common.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/leds') diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index 77bb26906ea6..8e7074f0fee0 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -442,9 +442,9 @@ int lp55xx_init_device(struct lp55xx_chip *chip) gpiod_direction_output(pdata->enable_gpiod, 0); gpiod_set_consumer_name(pdata->enable_gpiod, "LP55xx enable"); - gpiod_set_value(pdata->enable_gpiod, 0); + gpiod_set_value_cansleep(pdata->enable_gpiod, 0); usleep_range(1000, 2000); /* Keep enable down at least 1ms */ - gpiod_set_value(pdata->enable_gpiod, 1); + gpiod_set_value_cansleep(pdata->enable_gpiod, 1); usleep_range(1000, 2000); /* 500us abs min. */ } -- cgit From 6de283b96b31b4890e3ee8c86caca2a3a30d1011 Mon Sep 17 00:00:00 2001 From: Marek Behún Date: Mon, 18 Sep 2023 18:11:01 +0200 Subject: leds: turris-omnia: Do not use SMBUS calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The leds-turris-omnia driver uses three function for I2C access: - i2c_smbus_write_byte_data() and i2c_smbus_read_byte_data(), which cause an emulated SMBUS transfer, - i2c_master_send(), which causes an ordinary I2C transfer. The Turris Omnia MCU LED controller is not semantically SMBUS, it operates as a simple I2C bus. It does not implement any of the SMBUS specific features, like PEC, or procedure calls, or anything. Moreover the I2C controller driver also does not implement SMBUS, and so the emulated SMBUS procedure from drivers/i2c/i2c-core-smbus.c is used for the SMBUS calls, which gives an unnecessary overhead. When I first wrote the driver, I was unaware of these facts, and I simply used the first function that worked. Drop the I2C SMBUS calls and instead use simple I2C transfers. Fixes: 089381b27abe ("leds: initial support for Turris Omnia LEDs") Signed-off-by: Marek Behún Link: https://lore.kernel.org/r/20230918161104.20860-2-kabel@kernel.org Signed-off-by: Lee Jones --- drivers/leds/leds-turris-omnia.c | 54 +++++++++++++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 12 deletions(-) (limited to 'drivers/leds') diff --git a/drivers/leds/leds-turris-omnia.c b/drivers/leds/leds-turris-omnia.c index b8a95a917cfa..b13a547e72c4 100644 --- a/drivers/leds/leds-turris-omnia.c +++ b/drivers/leds/leds-turris-omnia.c @@ -2,7 +2,7 @@ /* * CZ.NIC's Turris Omnia LEDs driver * - * 2020 by Marek Behún + * 2020, 2023 by Marek Behún */ #include @@ -41,6 +41,37 @@ struct omnia_leds { struct omnia_led leds[]; }; +static int omnia_cmd_write_u8(const struct i2c_client *client, u8 cmd, u8 val) +{ + u8 buf[2] = { cmd, val }; + + return i2c_master_send(client, buf, sizeof(buf)); +} + +static int omnia_cmd_read_u8(const struct i2c_client *client, u8 cmd) +{ + struct i2c_msg msgs[2]; + u8 reply; + int ret; + + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = 1; + msgs[0].buf = &cmd; + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = 1; + msgs[1].buf = &reply; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (likely(ret == ARRAY_SIZE(msgs))) + return reply; + else if (ret < 0) + return ret; + else + return -EIO; +} + static int omnia_led_brightness_set_blocking(struct led_classdev *cdev, enum led_brightness brightness) { @@ -64,7 +95,7 @@ static int omnia_led_brightness_set_blocking(struct led_classdev *cdev, if (buf[2] || buf[3] || buf[4]) state |= CMD_LED_STATE_ON; - ret = i2c_smbus_write_byte_data(leds->client, CMD_LED_STATE, state); + ret = omnia_cmd_write_u8(leds->client, CMD_LED_STATE, state); if (ret >= 0 && (state & CMD_LED_STATE_ON)) ret = i2c_master_send(leds->client, buf, 5); @@ -114,9 +145,9 @@ static int omnia_led_register(struct i2c_client *client, struct omnia_led *led, cdev->brightness_set_blocking = omnia_led_brightness_set_blocking; /* put the LED into software mode */ - ret = i2c_smbus_write_byte_data(client, CMD_LED_MODE, - CMD_LED_MODE_LED(led->reg) | - CMD_LED_MODE_USER); + ret = omnia_cmd_write_u8(client, CMD_LED_MODE, + CMD_LED_MODE_LED(led->reg) | + CMD_LED_MODE_USER); if (ret < 0) { dev_err(dev, "Cannot set LED %pOF to software mode: %i\n", np, ret); @@ -124,8 +155,8 @@ static int omnia_led_register(struct i2c_client *client, struct omnia_led *led, } /* disable the LED */ - ret = i2c_smbus_write_byte_data(client, CMD_LED_STATE, - CMD_LED_STATE_LED(led->reg)); + ret = omnia_cmd_write_u8(client, CMD_LED_STATE, + CMD_LED_STATE_LED(led->reg)); if (ret < 0) { dev_err(dev, "Cannot set LED %pOF brightness: %i\n", np, ret); return ret; @@ -158,7 +189,7 @@ static ssize_t brightness_show(struct device *dev, struct device_attribute *a, struct i2c_client *client = to_i2c_client(dev); int ret; - ret = i2c_smbus_read_byte_data(client, CMD_LED_GET_BRIGHTNESS); + ret = omnia_cmd_read_u8(client, CMD_LED_GET_BRIGHTNESS); if (ret < 0) return ret; @@ -179,8 +210,7 @@ static ssize_t brightness_store(struct device *dev, struct device_attribute *a, if (brightness > 100) return -EINVAL; - ret = i2c_smbus_write_byte_data(client, CMD_LED_SET_BRIGHTNESS, - (u8)brightness); + ret = omnia_cmd_write_u8(client, CMD_LED_SET_BRIGHTNESS, brightness); return ret < 0 ? ret : count; } @@ -237,8 +267,8 @@ static void omnia_leds_remove(struct i2c_client *client) u8 buf[5]; /* put all LEDs into default (HW triggered) mode */ - i2c_smbus_write_byte_data(client, CMD_LED_MODE, - CMD_LED_MODE_LED(OMNIA_BOARD_LEDS)); + omnia_cmd_write_u8(client, CMD_LED_MODE, + CMD_LED_MODE_LED(OMNIA_BOARD_LEDS)); /* set all LEDs color to [255, 255, 255] */ buf[0] = CMD_LED_COLOR; -- cgit From 9f028c9e1c32249cb2487d2103512d6b9597b932 Mon Sep 17 00:00:00 2001 From: Marek Behún Date: Mon, 18 Sep 2023 18:11:02 +0200 Subject: leds: turris-omnia: Make set_brightness() more efficient MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement caching of the LED color and state values that are sent to MCU in order to make the set_brightness() operation more efficient by avoiding I2C transactions which are not needed. On Turris Omnia's MCU, which acts as the RGB LED controller, each LED has a RGB color, and a ON/OFF state, which are configurable via I2C commands CMD_LED_COLOR and CMD_LED_STATE. The CMD_LED_COLOR command sends 5 bytes and the CMD_LED_STATE command 2 bytes over the I2C bus, which operates at 100 kHz. With I2C overhead this allows ~1670 color changing commands and ~3200 state changing commands per second (or around 1000 color + state changes per second). This may seem more than enough, but the issue is that the I2C bus is shared with another peripheral, the MCU. The MCU exposes an interrupt interface, and it can trigger hundreds of interrupts per second. Each time, we need to read the interrupt state register over this I2C bus. Whenever we are sending a LED color/state changing command, the interrupt reading is waiting. Currently, every time LED brightness or LED multi intensity is changed, we send a CMD_LED_STATE command, and if the computed color (brightness adjusted multi_intensity) is non-zero, we also send a CMD_LED_COLOR command. Consider for example the situation when we have a netdev trigger enabled for a LED. The netdev trigger does not change the LED color, only the brightness (either to 0 or to currently configured brightness), and so there is no need to send the CMD_LED_COLOR command. But each change of brightness to 0 sends one CMD_LED_STATE command, and each change of brightness to max_brightness sends one CMD_LED_STATE command and one CMD_LED_COLOR command: set_brightness(0) -> CMD_LED_STATE set_brightness(255) -> CMD_LED_STATE + CMD_LED_COLOR (unnecessary) We can avoid the unnecessary I2C transactions if we cache the values of state and color that are sent to the controller. If the color does not change from the one previously sent, there is no need to do the CMD_LED_COLOR I2C transaction, and if the state does not change, there is no need to do the CMD_LED_STATE transaction. Because we need to make sure that our cached values are consistent with the controller state, add explicit setting of the LED color to white at probe time (this is the default setting when MCU resets, but does not necessarily need to be the case, for example if U-Boot played with the LED colors). Signed-off-by: Marek Behún Link: https://lore.kernel.org/r/20230918161104.20860-3-kabel@kernel.org Signed-off-by: Lee Jones --- drivers/leds/leds-turris-omnia.c | 96 ++++++++++++++++++++++++++++++++-------- 1 file changed, 78 insertions(+), 18 deletions(-) (limited to 'drivers/leds') diff --git a/drivers/leds/leds-turris-omnia.c b/drivers/leds/leds-turris-omnia.c index b13a547e72c4..16ea9d56062a 100644 --- a/drivers/leds/leds-turris-omnia.c +++ b/drivers/leds/leds-turris-omnia.c @@ -30,6 +30,8 @@ struct omnia_led { struct led_classdev_mc mc_cdev; struct mc_subled subled_info[OMNIA_LED_NUM_CHANNELS]; + u8 cached_channels[OMNIA_LED_NUM_CHANNELS]; + bool on; int reg; }; @@ -72,36 +74,82 @@ static int omnia_cmd_read_u8(const struct i2c_client *client, u8 cmd) return -EIO; } +static int omnia_led_send_color_cmd(const struct i2c_client *client, + struct omnia_led *led) +{ + char cmd[5]; + int ret; + + cmd[0] = CMD_LED_COLOR; + cmd[1] = led->reg; + cmd[2] = led->subled_info[0].brightness; + cmd[3] = led->subled_info[1].brightness; + cmd[4] = led->subled_info[2].brightness; + + /* Send the color change command */ + ret = i2c_master_send(client, cmd, 5); + if (ret < 0) + return ret; + + /* Cache the RGB channel brightnesses */ + for (int i = 0; i < OMNIA_LED_NUM_CHANNELS; ++i) + led->cached_channels[i] = led->subled_info[i].brightness; + + return 0; +} + +/* Determine if the computed RGB channels are different from the cached ones */ +static bool omnia_led_channels_changed(struct omnia_led *led) +{ + for (int i = 0; i < OMNIA_LED_NUM_CHANNELS; ++i) + if (led->subled_info[i].brightness != led->cached_channels[i]) + return true; + + return false; +} + static int omnia_led_brightness_set_blocking(struct led_classdev *cdev, enum led_brightness brightness) { struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(cdev); struct omnia_leds *leds = dev_get_drvdata(cdev->dev->parent); struct omnia_led *led = to_omnia_led(mc_cdev); - u8 buf[5], state; - int ret; + int err = 0; mutex_lock(&leds->lock); - led_mc_calc_color_components(&led->mc_cdev, brightness); + /* + * Only recalculate RGB brightnesses from intensities if brightness is + * non-zero. Otherwise we won't be using them and we can save ourselves + * some software divisions (Omnia's CPU does not implement the division + * instruction). + */ + if (brightness) { + led_mc_calc_color_components(mc_cdev, brightness); + + /* + * Send color command only if brightness is non-zero and the RGB + * channel brightnesses changed. + */ + if (omnia_led_channels_changed(led)) + err = omnia_led_send_color_cmd(leds->client, led); + } - buf[0] = CMD_LED_COLOR; - buf[1] = led->reg; - buf[2] = mc_cdev->subled_info[0].brightness; - buf[3] = mc_cdev->subled_info[1].brightness; - buf[4] = mc_cdev->subled_info[2].brightness; + /* Send on/off state change only if (bool)brightness changed */ + if (!err && !brightness != !led->on) { + u8 state = CMD_LED_STATE_LED(led->reg); - state = CMD_LED_STATE_LED(led->reg); - if (buf[2] || buf[3] || buf[4]) - state |= CMD_LED_STATE_ON; + if (brightness) + state |= CMD_LED_STATE_ON; - ret = omnia_cmd_write_u8(leds->client, CMD_LED_STATE, state); - if (ret >= 0 && (state & CMD_LED_STATE_ON)) - ret = i2c_master_send(leds->client, buf, 5); + err = omnia_cmd_write_u8(leds->client, CMD_LED_STATE, state); + if (!err) + led->on = !!brightness; + } mutex_unlock(&leds->lock); - return ret; + return err; } static int omnia_led_register(struct i2c_client *client, struct omnia_led *led, @@ -129,11 +177,15 @@ static int omnia_led_register(struct i2c_client *client, struct omnia_led *led, } led->subled_info[0].color_index = LED_COLOR_ID_RED; - led->subled_info[0].channel = 0; led->subled_info[1].color_index = LED_COLOR_ID_GREEN; - led->subled_info[1].channel = 1; led->subled_info[2].color_index = LED_COLOR_ID_BLUE; - led->subled_info[2].channel = 2; + + /* Initial color is white */ + for (int i = 0; i < OMNIA_LED_NUM_CHANNELS; ++i) { + led->subled_info[i].intensity = 255; + led->subled_info[i].brightness = 255; + led->subled_info[i].channel = i; + } led->mc_cdev.subled_info = led->subled_info; led->mc_cdev.num_colors = OMNIA_LED_NUM_CHANNELS; @@ -162,6 +214,14 @@ static int omnia_led_register(struct i2c_client *client, struct omnia_led *led, return ret; } + /* Set initial color and cache it */ + ret = omnia_led_send_color_cmd(client, led); + if (ret < 0) { + dev_err(dev, "Cannot set LED %pOF initial color: %i\n", np, + ret); + return ret; + } + ret = devm_led_classdev_multicolor_register_ext(dev, &led->mc_cdev, &init_data); if (ret < 0) { -- cgit From cbd6954fecbeb822cf380fa2573ccb4a3a457e88 Mon Sep 17 00:00:00 2001 From: Marek Behún Date: Mon, 18 Sep 2023 18:11:03 +0200 Subject: leds: turris-omnia: Support HW controlled mode via private trigger MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for enabling MCU controlled mode of the Turris Omnia LEDs via a LED private trigger called "omnia-mcu". Recall that private LED triggers will only be listed in the sysfs trigger file for LEDs that support them (currently there is no user of this mechanism). When in MCU controlled mode, the user can still set LED color, but the blinking is done by MCU, which does different things for different LEDs: - WAN LED is blinked according to the LED[0] pin of the WAN PHY - LAN LEDs are blinked according to the LED[0] output of the corresponding port of the LAN switch - PCIe LEDs are blinked according to the logical OR of the MiniPCIe port LED pins In the future I want to make the netdev trigger to transparently offload the blinking to the HW if user sets compatible settings for the netdev trigger (for LEDs associated with network devices). There was some work on this already, and hopefully we will be able to complete it sometime, but for now there are still multiple blockers for this, and even if there weren't, we still would not be able to configure HW controlled mode for the LEDs associated with MiniPCIe ports. In the meantime let's support HW controlled mode via the private LED trigger mechanism. If, in the future, we manage to complete the netdev trigger offloading, we can still keep this private trigger for backwards compatibility, if needed. We also set "omnia-mcu" to cdev->default_trigger, so that the MCU keeps control until the user first wants to take over it. If a different default trigger is specified in device-tree via the 'linux,default-trigger' property, LED class will overwrite cdev->default_trigger, and so the DT property will be respected. Signed-off-by: Marek Behún Link: https://lore.kernel.org/r/20230918161104.20860-4-kabel@kernel.org Signed-off-by: Lee Jones --- drivers/leds/Kconfig | 1 + drivers/leds/leds-turris-omnia.c | 98 ++++++++++++++++++++++++++++++++++++---- 2 files changed, 91 insertions(+), 8 deletions(-) (limited to 'drivers/leds') diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index b92208eccdea..6292fddcc55c 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -187,6 +187,7 @@ config LEDS_TURRIS_OMNIA depends on I2C depends on MACH_ARMADA_38X || COMPILE_TEST depends on OF + select LEDS_TRIGGERS help This option enables basic support for the LEDs found on the front side of CZ.NIC's Turris Omnia router. There are 12 RGB LEDs on the diff --git a/drivers/leds/leds-turris-omnia.c b/drivers/leds/leds-turris-omnia.c index 16ea9d56062a..ef2844d35168 100644 --- a/drivers/leds/leds-turris-omnia.c +++ b/drivers/leds/leds-turris-omnia.c @@ -31,7 +31,7 @@ struct omnia_led { struct led_classdev_mc mc_cdev; struct mc_subled subled_info[OMNIA_LED_NUM_CHANNELS]; u8 cached_channels[OMNIA_LED_NUM_CHANNELS]; - bool on; + bool on, hwtrig; int reg; }; @@ -120,12 +120,14 @@ static int omnia_led_brightness_set_blocking(struct led_classdev *cdev, /* * Only recalculate RGB brightnesses from intensities if brightness is - * non-zero. Otherwise we won't be using them and we can save ourselves - * some software divisions (Omnia's CPU does not implement the division - * instruction). + * non-zero (if it is zero and the LED is in HW blinking mode, we use + * max_brightness as brightness). Otherwise we won't be using them and + * we can save ourselves some software divisions (Omnia's CPU does not + * implement the division instruction). */ - if (brightness) { - led_mc_calc_color_components(mc_cdev, brightness); + if (brightness || led->hwtrig) { + led_mc_calc_color_components(mc_cdev, brightness ?: + cdev->max_brightness); /* * Send color command only if brightness is non-zero and the RGB @@ -135,8 +137,11 @@ static int omnia_led_brightness_set_blocking(struct led_classdev *cdev, err = omnia_led_send_color_cmd(leds->client, led); } - /* Send on/off state change only if (bool)brightness changed */ - if (!err && !brightness != !led->on) { + /* + * Send on/off state change only if (bool)brightness changed and the LED + * is not being blinked by HW. + */ + if (!err && !led->hwtrig && !brightness != !led->on) { u8 state = CMD_LED_STATE_LED(led->reg); if (brightness) @@ -152,6 +157,71 @@ static int omnia_led_brightness_set_blocking(struct led_classdev *cdev, return err; } +static struct led_hw_trigger_type omnia_hw_trigger_type; + +static int omnia_hwtrig_activate(struct led_classdev *cdev) +{ + struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(cdev); + struct omnia_leds *leds = dev_get_drvdata(cdev->dev->parent); + struct omnia_led *led = to_omnia_led(mc_cdev); + int err = 0; + + mutex_lock(&leds->lock); + + if (!led->on) { + /* + * If the LED is off (brightness was set to 0), the last + * configured color was not necessarily sent to the MCU. + * Recompute with max_brightness and send if needed. + */ + led_mc_calc_color_components(mc_cdev, cdev->max_brightness); + + if (omnia_led_channels_changed(led)) + err = omnia_led_send_color_cmd(leds->client, led); + } + + if (!err) { + /* Put the LED into MCU controlled mode */ + err = omnia_cmd_write_u8(leds->client, CMD_LED_MODE, + CMD_LED_MODE_LED(led->reg)); + if (!err) + led->hwtrig = true; + } + + mutex_unlock(&leds->lock); + + return err; +} + +static void omnia_hwtrig_deactivate(struct led_classdev *cdev) +{ + struct omnia_leds *leds = dev_get_drvdata(cdev->dev->parent); + struct omnia_led *led = to_omnia_led(lcdev_to_mccdev(cdev)); + int err; + + mutex_lock(&leds->lock); + + led->hwtrig = false; + + /* Put the LED into software mode */ + err = omnia_cmd_write_u8(leds->client, CMD_LED_MODE, + CMD_LED_MODE_LED(led->reg) | + CMD_LED_MODE_USER); + + mutex_unlock(&leds->lock); + + if (err < 0) + dev_err(cdev->dev, "Cannot put LED to software mode: %i\n", + err); +} + +static struct led_trigger omnia_hw_trigger = { + .name = "omnia-mcu", + .activate = omnia_hwtrig_activate, + .deactivate = omnia_hwtrig_deactivate, + .trigger_type = &omnia_hw_trigger_type, +}; + static int omnia_led_register(struct i2c_client *client, struct omnia_led *led, struct device_node *np) { @@ -195,6 +265,12 @@ static int omnia_led_register(struct i2c_client *client, struct omnia_led *led, cdev = &led->mc_cdev.led_cdev; cdev->max_brightness = 255; cdev->brightness_set_blocking = omnia_led_brightness_set_blocking; + cdev->trigger_type = &omnia_hw_trigger_type; + /* + * Use the omnia-mcu trigger as the default trigger. It may be rewritten + * by LED class from the linux,default-trigger property. + */ + cdev->default_trigger = omnia_hw_trigger.name; /* put the LED into software mode */ ret = omnia_cmd_write_u8(client, CMD_LED_MODE, @@ -308,6 +384,12 @@ static int omnia_leds_probe(struct i2c_client *client) mutex_init(&leds->lock); + ret = devm_led_trigger_register(dev, &omnia_hw_trigger); + if (ret < 0) { + dev_err(dev, "Cannot register private LED trigger: %d\n", ret); + return ret; + } + led = &leds->leds[0]; for_each_available_child_of_node(np, child) { ret = omnia_led_register(client, led, child); -- cgit From 43e9082fbccc7df8b2028c1ba040c58cefda703f Mon Sep 17 00:00:00 2001 From: Marek Behún Date: Mon, 18 Sep 2023 18:11:04 +0200 Subject: leds: turris-omnia: Add support for enabling/disabling HW gamma correction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the MCU on Turris Omnia is running newer firmware versions, the LED controller supports RGB gamma correction (and enables it by default for newer boards). Determine whether the gamma correction setting feature is supported and add the ability to set it via sysfs attribute file. Signed-off-by: Marek Behún Link: https://lore.kernel.org/r/20230918161104.20860-5-kabel@kernel.org Signed-off-by: Lee Jones --- drivers/leds/leds-turris-omnia.c | 137 ++++++++++++++++++++++++++++++++++----- 1 file changed, 120 insertions(+), 17 deletions(-) (limited to 'drivers/leds') diff --git a/drivers/leds/leds-turris-omnia.c b/drivers/leds/leds-turris-omnia.c index ef2844d35168..f27241896970 100644 --- a/drivers/leds/leds-turris-omnia.c +++ b/drivers/leds/leds-turris-omnia.c @@ -15,17 +15,30 @@ #define OMNIA_BOARD_LEDS 12 #define OMNIA_LED_NUM_CHANNELS 3 -#define CMD_LED_MODE 3 -#define CMD_LED_MODE_LED(l) ((l) & 0x0f) -#define CMD_LED_MODE_USER 0x10 +/* MCU controller commands at I2C address 0x2a */ +#define OMNIA_MCU_I2C_ADDR 0x2a -#define CMD_LED_STATE 4 -#define CMD_LED_STATE_LED(l) ((l) & 0x0f) -#define CMD_LED_STATE_ON 0x10 +#define CMD_GET_STATUS_WORD 0x01 +#define STS_FEATURES_SUPPORTED BIT(2) -#define CMD_LED_COLOR 5 -#define CMD_LED_SET_BRIGHTNESS 7 -#define CMD_LED_GET_BRIGHTNESS 8 +#define CMD_GET_FEATURES 0x10 +#define FEAT_LED_GAMMA_CORRECTION BIT(5) + +/* LED controller commands at I2C address 0x2b */ +#define CMD_LED_MODE 0x03 +#define CMD_LED_MODE_LED(l) ((l) & 0x0f) +#define CMD_LED_MODE_USER 0x10 + +#define CMD_LED_STATE 0x04 +#define CMD_LED_STATE_LED(l) ((l) & 0x0f) +#define CMD_LED_STATE_ON 0x10 + +#define CMD_LED_COLOR 0x05 +#define CMD_LED_SET_BRIGHTNESS 0x07 +#define CMD_LED_GET_BRIGHTNESS 0x08 + +#define CMD_SET_GAMMA_CORRECTION 0x30 +#define CMD_GET_GAMMA_CORRECTION 0x31 struct omnia_led { struct led_classdev_mc mc_cdev; @@ -40,6 +53,7 @@ struct omnia_led { struct omnia_leds { struct i2c_client *client; struct mutex lock; + bool has_gamma_correction; struct omnia_led leds[]; }; @@ -50,30 +64,42 @@ static int omnia_cmd_write_u8(const struct i2c_client *client, u8 cmd, u8 val) return i2c_master_send(client, buf, sizeof(buf)); } -static int omnia_cmd_read_u8(const struct i2c_client *client, u8 cmd) +static int omnia_cmd_read_raw(struct i2c_adapter *adapter, u8 addr, u8 cmd, + void *reply, size_t len) { struct i2c_msg msgs[2]; - u8 reply; int ret; - msgs[0].addr = client->addr; + msgs[0].addr = addr; msgs[0].flags = 0; msgs[0].len = 1; msgs[0].buf = &cmd; - msgs[1].addr = client->addr; + msgs[1].addr = addr; msgs[1].flags = I2C_M_RD; - msgs[1].len = 1; - msgs[1].buf = &reply; + msgs[1].len = len; + msgs[1].buf = reply; - ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + ret = i2c_transfer(adapter, msgs, ARRAY_SIZE(msgs)); if (likely(ret == ARRAY_SIZE(msgs))) - return reply; + return len; else if (ret < 0) return ret; else return -EIO; } +static int omnia_cmd_read_u8(const struct i2c_client *client, u8 cmd) +{ + u8 reply; + int ret; + + ret = omnia_cmd_read_raw(client->adapter, client->addr, cmd, &reply, 1); + if (ret < 0) + return ret; + + return reply; +} + static int omnia_led_send_color_cmd(const struct i2c_client *client, struct omnia_led *led) { @@ -352,12 +378,74 @@ static ssize_t brightness_store(struct device *dev, struct device_attribute *a, } static DEVICE_ATTR_RW(brightness); +static ssize_t gamma_correction_show(struct device *dev, + struct device_attribute *a, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct omnia_leds *leds = i2c_get_clientdata(client); + int ret; + + if (leds->has_gamma_correction) { + ret = omnia_cmd_read_u8(client, CMD_GET_GAMMA_CORRECTION); + if (ret < 0) + return ret; + } else { + ret = 0; + } + + return sysfs_emit(buf, "%d\n", !!ret); +} + +static ssize_t gamma_correction_store(struct device *dev, + struct device_attribute *a, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct omnia_leds *leds = i2c_get_clientdata(client); + bool val; + int ret; + + if (!leds->has_gamma_correction) + return -EOPNOTSUPP; + + if (kstrtobool(buf, &val) < 0) + return -EINVAL; + + ret = omnia_cmd_write_u8(client, CMD_SET_GAMMA_CORRECTION, val); + + return ret < 0 ? ret : count; +} +static DEVICE_ATTR_RW(gamma_correction); + static struct attribute *omnia_led_controller_attrs[] = { &dev_attr_brightness.attr, + &dev_attr_gamma_correction.attr, NULL, }; ATTRIBUTE_GROUPS(omnia_led_controller); +static int omnia_mcu_get_features(const struct i2c_client *client) +{ + u16 reply; + int err; + + err = omnia_cmd_read_raw(client->adapter, OMNIA_MCU_I2C_ADDR, + CMD_GET_STATUS_WORD, &reply, sizeof(reply)); + if (err < 0) + return err; + + /* Check whether MCU firmware supports the CMD_GET_FEAUTRES command */ + if (!(le16_to_cpu(reply) & STS_FEATURES_SUPPORTED)) + return 0; + + err = omnia_cmd_read_raw(client->adapter, OMNIA_MCU_I2C_ADDR, + CMD_GET_FEATURES, &reply, sizeof(reply)); + if (err < 0) + return err; + + return le16_to_cpu(reply); +} + static int omnia_leds_probe(struct i2c_client *client) { struct device *dev = &client->dev; @@ -382,6 +470,21 @@ static int omnia_leds_probe(struct i2c_client *client) leds->client = client; i2c_set_clientdata(client, leds); + ret = omnia_mcu_get_features(client); + if (ret < 0) { + dev_err(dev, "Cannot determine MCU supported features: %d\n", + ret); + return ret; + } + + leds->has_gamma_correction = ret & FEAT_LED_GAMMA_CORRECTION; + if (!leds->has_gamma_correction) { + dev_info(dev, + "Your board's MCU firmware does not support the LED gamma correction feature.\n"); + dev_info(dev, + "Consider upgrading MCU firmware with the omnia-mcutool utility.\n"); + } + mutex_init(&leds->lock); ret = devm_led_trigger_register(dev, &omnia_hw_trigger); -- cgit From 76fe464c8e64e71b2e4af11edeef0e5d85eeb6aa Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 22 Sep 2023 21:28:34 +0200 Subject: leds: pwm: Don't disable the PWM when the LED should be off MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Disabling a PWM (i.e. calling pwm_apply_state with .enabled = false) gives no guarantees what the PWM output does. It might freeze where it currently is, or go in a High-Z state or drive the active or inactive state, it might even continue to toggle. To ensure that the LED gets really disabled, don't disable the PWM even when .duty_cycle is zero. This fixes disabling a leds-pwm LED on i.MX28. The PWM on this SoC is one of those that freezes its output on disable, so if you disable an LED that is full on, it stays on. If you disable a LED with half brightness it goes off in 50% of the cases and full on in the other 50%. Fixes: 41c42ff5dbe2 ("leds: simple driver for pwm driven LEDs") Reported-by: Rogan Dawes Reported-by: Fabio Estevam Signed-off-by: Uwe Kleine-König Reviewed-by: Fabio Estevam Link: https://lore.kernel.org/r/20230922192834.1695727-1-u.kleine-koenig@pengutronix.de Signed-off-by: Lee Jones --- drivers/leds/leds-pwm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/leds') diff --git a/drivers/leds/leds-pwm.c b/drivers/leds/leds-pwm.c index 419b710984ab..2b3bf1353b70 100644 --- a/drivers/leds/leds-pwm.c +++ b/drivers/leds/leds-pwm.c @@ -53,7 +53,7 @@ static int led_pwm_set(struct led_classdev *led_cdev, duty = led_dat->pwmstate.period - duty; led_dat->pwmstate.duty_cycle = duty; - led_dat->pwmstate.enabled = duty > 0; + led_dat->pwmstate.enabled = true; return pwm_apply_state(led_dat->pwm, &led_dat->pwmstate); } -- cgit From ff50f53276131a3059e8307d11293af388ed2bcd Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 23 Sep 2023 09:15:38 +0200 Subject: leds: trigger: ledtrig-cpu:: Fix 'output may be truncated' issue for 'cpu' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to teach the compiler that 'trig->name' will never be truncated, we need to tell it that 'cpu' is not negative. When building with W=1, this fixes the following warnings: drivers/leds/trigger/ledtrig-cpu.c: In function ‘ledtrig_cpu_init’: drivers/leds/trigger/ledtrig-cpu.c:155:56: error: ‘%d’ directive output may be truncated writing between 1 and 11 bytes into a region of size 5 [-Werror=format-truncation=] 155 | snprintf(trig->name, MAX_NAME_LEN, "cpu%d", cpu); | ^~ drivers/leds/trigger/ledtrig-cpu.c:155:52: note: directive argument in the range [-2147483648, 7] 155 | snprintf(trig->name, MAX_NAME_LEN, "cpu%d", cpu); | ^~~~~~~ drivers/leds/trigger/ledtrig-cpu.c:155:17: note: ‘snprintf’ output between 5 and 15 bytes into a destination of size 8 155 | snprintf(trig->name, MAX_NAME_LEN, "cpu%d", cpu); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Fixes: 8f88731d052d ("led-triggers: create a trigger for CPU activity") Signed-off-by: Christophe JAILLET Link: https://lore.kernel.org/r/3f4be7a99933cf8566e630da54f6ab913caac432.1695453322.git.christophe.jaillet@wanadoo.fr Signed-off-by: Lee Jones --- drivers/leds/trigger/ledtrig-cpu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/leds') diff --git a/drivers/leds/trigger/ledtrig-cpu.c b/drivers/leds/trigger/ledtrig-cpu.c index 8af4f9bb9cde..05848a2fecff 100644 --- a/drivers/leds/trigger/ledtrig-cpu.c +++ b/drivers/leds/trigger/ledtrig-cpu.c @@ -130,7 +130,7 @@ static int ledtrig_prepare_down_cpu(unsigned int cpu) static int __init ledtrig_cpu_init(void) { - int cpu; + unsigned int cpu; int ret; /* Supports up to 9999 cpu cores */ @@ -152,7 +152,7 @@ static int __init ledtrig_cpu_init(void) if (cpu >= 8) continue; - snprintf(trig->name, MAX_NAME_LEN, "cpu%d", cpu); + snprintf(trig->name, MAX_NAME_LEN, "cpu%u", cpu); led_trigger_register_simple(trig->name, &trig->_trig); } -- cgit From a337ee0d25ba24212269c2442981dc4fb5232a8c Mon Sep 17 00:00:00 2001 From: Justin Stitt Date: Fri, 22 Sep 2023 15:27:17 +0000 Subject: leds: lp3952: Replace deprecated strncpy with strscpy `strncpy` is deprecated for use on NUL-terminated destination strings [1] and as such we should prefer more robust and less ambiguous string interfaces. We expect `dest` to be NUL-terminated due to its use with dev_err. lp3952_get_label()'s dest argument is priv->leds[i].name: | acpi_ret = lp3952_get_label(&priv->client->dev, led_name_hdl[i], | priv->leds[i].name); ... which is then assigned to: | priv->leds[i].cdev.name = priv->leds[i].name; ... which is used with a format string | dev_err(&priv->client->dev, | "couldn't register LED %s\n", | priv->leds[i].cdev.name); There is no indication that NUL-padding is required but if it is let's opt for strscpy_pad. Considering the above, a suitable replacement is `strscpy` [2] due to the fact that it guarantees NUL-termination on the destination buffer without unnecessarily NUL-padding. Link: https://www.kernel.org/doc/html/latest/process/deprecated.html#strncpy-on-nul-terminated-strings [1] Link: https://manpages.debian.org/testing/linux-manual-4.8/strscpy.9.en.html [2] Link: https://github.com/KSPP/linux/issues/90 Signed-off-by: Justin Stitt Reviewed-by: Kees Cook Link: https://lore.kernel.org/r/20230922-strncpy-drivers-leds-leds-lp3952-c-v1-1-4941d6f60ca4@google.com Signed-off-by: Lee Jones --- drivers/leds/leds-lp3952.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/leds') diff --git a/drivers/leds/leds-lp3952.c b/drivers/leds/leds-lp3952.c index 3bd55652a706..62ade3f05a87 100644 --- a/drivers/leds/leds-lp3952.c +++ b/drivers/leds/leds-lp3952.c @@ -101,7 +101,7 @@ static int lp3952_get_label(struct device *dev, const char *label, char *dest) if (ret) return ret; - strncpy(dest, str, LP3952_LABEL_MAX_LEN); + strscpy(dest, str, LP3952_LABEL_MAX_LEN); return 0; } -- cgit From 3b581cb58816d07b033c800d42a040d7ba566fb6 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Sat, 23 Sep 2023 18:19:20 +0100 Subject: leds: pca955x: Convert enum->pointer for data in the match tables Convert enum->pointer for data in the match tables, so that device_get_match_data() can do match against OF/ACPI/I2C tables, once i2c bus type match support added to it. Replace enum->struct *pca955x_chipdefs for data in the match table. Simplify the probe() by replacing device_get_match_data() and ID lookup for retrieving data by i2c_get_match_data(). While at it, add const definition to pca955x_chipdefs[]. Signed-off-by: Biju Das Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230923171921.53503-2-biju.das.jz@bp.renesas.com Signed-off-by: Lee Jones --- drivers/leds/leds-pca955x.c | 49 +++++++++++++++++---------------------------- 1 file changed, 18 insertions(+), 31 deletions(-) (limited to 'drivers/leds') diff --git a/drivers/leds/leds-pca955x.c b/drivers/leds/leds-pca955x.c index 1d7fa0cd97bf..891cfc05301d 100644 --- a/drivers/leds/leds-pca955x.c +++ b/drivers/leds/leds-pca955x.c @@ -76,7 +76,7 @@ struct pca955x_chipdef { int slv_addr_shift; /* Number of bits to ignore */ }; -static struct pca955x_chipdef pca955x_chipdefs[] = { +static const struct pca955x_chipdef pca955x_chipdefs[] = { [pca9550] = { .bits = 2, .slv_addr = /* 110000x */ 0x60, @@ -105,11 +105,11 @@ static struct pca955x_chipdef pca955x_chipdefs[] = { }; static const struct i2c_device_id pca955x_id[] = { - { "pca9550", pca9550 }, - { "pca9551", pca9551 }, - { "pca9552", pca9552 }, - { "ibm-pca9552", ibm_pca9552 }, - { "pca9553", pca9553 }, + { "pca9550", (kernel_ulong_t)&pca955x_chipdefs[pca9550] }, + { "pca9551", (kernel_ulong_t)&pca955x_chipdefs[pca9551] }, + { "pca9552", (kernel_ulong_t)&pca955x_chipdefs[pca9552] }, + { "ibm-pca9552", (kernel_ulong_t)&pca955x_chipdefs[ibm_pca9552] }, + { "pca9553", (kernel_ulong_t)&pca955x_chipdefs[pca9553] }, { } }; MODULE_DEVICE_TABLE(i2c, pca955x_id); @@ -117,7 +117,7 @@ MODULE_DEVICE_TABLE(i2c, pca955x_id); struct pca955x { struct mutex lock; struct pca955x_led *leds; - struct pca955x_chipdef *chipdef; + const struct pca955x_chipdef *chipdef; struct i2c_client *client; unsigned long active_pins; #ifdef CONFIG_LEDS_PCA955X_GPIO @@ -415,7 +415,7 @@ static int pca955x_gpio_direction_output(struct gpio_chip *gc, #endif /* CONFIG_LEDS_PCA955X_GPIO */ static struct pca955x_platform_data * -pca955x_get_pdata(struct i2c_client *client, struct pca955x_chipdef *chip) +pca955x_get_pdata(struct i2c_client *client, const struct pca955x_chipdef *chip) { struct pca955x_platform_data *pdata; struct pca955x_led *led; @@ -458,11 +458,11 @@ pca955x_get_pdata(struct i2c_client *client, struct pca955x_chipdef *chip) } static const struct of_device_id of_pca955x_match[] = { - { .compatible = "nxp,pca9550", .data = (void *)pca9550 }, - { .compatible = "nxp,pca9551", .data = (void *)pca9551 }, - { .compatible = "nxp,pca9552", .data = (void *)pca9552 }, - { .compatible = "ibm,pca9552", .data = (void *)ibm_pca9552 }, - { .compatible = "nxp,pca9553", .data = (void *)pca9553 }, + { .compatible = "nxp,pca9550", .data = &pca955x_chipdefs[pca9550] }, + { .compatible = "nxp,pca9551", .data = &pca955x_chipdefs[pca9551] }, + { .compatible = "nxp,pca9552", .data = &pca955x_chipdefs[pca9552] }, + { .compatible = "ibm,pca9552", .data = &pca955x_chipdefs[ibm_pca9552] }, + { .compatible = "nxp,pca9553", .data = &pca955x_chipdefs[pca9553] }, {}, }; MODULE_DEVICE_TABLE(of, of_pca955x_match); @@ -471,7 +471,7 @@ static int pca955x_probe(struct i2c_client *client) { struct pca955x *pca955x; struct pca955x_led *pca955x_led; - struct pca955x_chipdef *chip; + const struct pca955x_chipdef *chip; struct led_classdev *led; struct led_init_data init_data; struct i2c_adapter *adapter; @@ -480,24 +480,11 @@ static int pca955x_probe(struct i2c_client *client) bool set_default_label = false; bool keep_pwm = false; char default_label[8]; - enum pca955x_type chip_type; - const void *md = device_get_match_data(&client->dev); - - if (md) { - chip_type = (enum pca955x_type)(uintptr_t)md; - } else { - const struct i2c_device_id *id = i2c_match_id(pca955x_id, - client); - - if (id) { - chip_type = (enum pca955x_type)id->driver_data; - } else { - dev_err(&client->dev, "unknown chip\n"); - return -ENODEV; - } - } - chip = &pca955x_chipdefs[chip_type]; + chip = i2c_get_match_data(client); + if (!chip) + return dev_err_probe(&client->dev, -ENODEV, "unknown chip\n"); + adapter = client->adapter; pdata = dev_get_platdata(&client->dev); if (!pdata) { -- cgit From 8d3fd7edd5f70fb814c07a321f347b5fe21d3fac Mon Sep 17 00:00:00 2001 From: Biju Das Date: Sat, 23 Sep 2023 18:19:21 +0100 Subject: leds: pca955x: Cleanup OF/ID table terminators Some cleanups: * Remove the trailing comma in the terminator entry for the OF table making code robust against (theoretical) misrebases or other similar things where the new entry goes _after_ the termination without the compiler noticing. * Drop a space from terminator entry for ID table. While at it, move OF/ID table near to the user. Signed-off-by: Biju Das Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230923171921.53503-3-biju.das.jz@bp.renesas.com Signed-off-by: Lee Jones --- drivers/leds/leds-pca955x.c | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) (limited to 'drivers/leds') diff --git a/drivers/leds/leds-pca955x.c b/drivers/leds/leds-pca955x.c index 891cfc05301d..94a9f8a54b35 100644 --- a/drivers/leds/leds-pca955x.c +++ b/drivers/leds/leds-pca955x.c @@ -104,16 +104,6 @@ static const struct pca955x_chipdef pca955x_chipdefs[] = { }, }; -static const struct i2c_device_id pca955x_id[] = { - { "pca9550", (kernel_ulong_t)&pca955x_chipdefs[pca9550] }, - { "pca9551", (kernel_ulong_t)&pca955x_chipdefs[pca9551] }, - { "pca9552", (kernel_ulong_t)&pca955x_chipdefs[pca9552] }, - { "ibm-pca9552", (kernel_ulong_t)&pca955x_chipdefs[ibm_pca9552] }, - { "pca9553", (kernel_ulong_t)&pca955x_chipdefs[pca9553] }, - { } -}; -MODULE_DEVICE_TABLE(i2c, pca955x_id); - struct pca955x { struct mutex lock; struct pca955x_led *leds; @@ -457,16 +447,6 @@ pca955x_get_pdata(struct i2c_client *client, const struct pca955x_chipdef *chip) return pdata; } -static const struct of_device_id of_pca955x_match[] = { - { .compatible = "nxp,pca9550", .data = &pca955x_chipdefs[pca9550] }, - { .compatible = "nxp,pca9551", .data = &pca955x_chipdefs[pca9551] }, - { .compatible = "nxp,pca9552", .data = &pca955x_chipdefs[pca9552] }, - { .compatible = "ibm,pca9552", .data = &pca955x_chipdefs[ibm_pca9552] }, - { .compatible = "nxp,pca9553", .data = &pca955x_chipdefs[pca9553] }, - {}, -}; -MODULE_DEVICE_TABLE(of, of_pca955x_match); - static int pca955x_probe(struct i2c_client *client) { struct pca955x *pca955x; @@ -650,6 +630,26 @@ static int pca955x_probe(struct i2c_client *client) return 0; } +static const struct i2c_device_id pca955x_id[] = { + { "pca9550", (kernel_ulong_t)&pca955x_chipdefs[pca9550] }, + { "pca9551", (kernel_ulong_t)&pca955x_chipdefs[pca9551] }, + { "pca9552", (kernel_ulong_t)&pca955x_chipdefs[pca9552] }, + { "ibm-pca9552", (kernel_ulong_t)&pca955x_chipdefs[ibm_pca9552] }, + { "pca9553", (kernel_ulong_t)&pca955x_chipdefs[pca9553] }, + {} +}; +MODULE_DEVICE_TABLE(i2c, pca955x_id); + +static const struct of_device_id of_pca955x_match[] = { + { .compatible = "nxp,pca9550", .data = &pca955x_chipdefs[pca9550] }, + { .compatible = "nxp,pca9551", .data = &pca955x_chipdefs[pca9551] }, + { .compatible = "nxp,pca9552", .data = &pca955x_chipdefs[pca9552] }, + { .compatible = "ibm,pca9552", .data = &pca955x_chipdefs[ibm_pca9552] }, + { .compatible = "nxp,pca9553", .data = &pca955x_chipdefs[pca9553] }, + {} +}; +MODULE_DEVICE_TABLE(of, of_pca955x_match); + static struct i2c_driver pca955x_driver = { .driver = { .name = "leds-pca955x", -- cgit From 4a11dbf04f31c71eb458c062129e95b7aa308464 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 26 Sep 2023 23:48:13 +0200 Subject: leds: triggers: gpio: Rewrite to use trigger-sources By providing a GPIO line as "trigger-sources" in the FWNODE (such as from the device tree) and combining with the GPIO trigger, we can support a GPIO LED trigger in a natural way from the hardware description instead of using the custom sysfs and deprecated global GPIO numberspace. Example: gpio: gpio@0 { compatible "my-gpio"; gpio-controller; #gpio-cells = <2>; interrupt-controller; #interrupt-cells = <2>; #trigger-source-cells = <2>; }; leds { compatible = "gpio-leds"; led-my-gpio { label = "device:blue:myled"; gpios = <&gpio 0 GPIO_ACTIVE_HIGH>; default-state = "off"; linux,default-trigger = "gpio"; trigger-sources = <&gpio 1 GPIO_ACTIVE_HIGH>; }; }; Make this the norm, unmark the driver as broken. Delete the sysfs handling of GPIOs. Since GPIO descriptors inherently can describe inversion, the inversion handling can just be deleted. Signed-off-by: Linus Walleij Link: https://lore.kernel.org/r/20230926-gpio-led-trigger-dt-v2-3-e06e458b788e@linaro.org Signed-off-by: Lee Jones --- drivers/leds/trigger/Kconfig | 5 +- drivers/leds/trigger/ledtrig-gpio.c | 137 +++++++++++------------------------- 2 files changed, 41 insertions(+), 101 deletions(-) (limited to 'drivers/leds') diff --git a/drivers/leds/trigger/Kconfig b/drivers/leds/trigger/Kconfig index 2a57328eca20..d11d80176fc0 100644 --- a/drivers/leds/trigger/Kconfig +++ b/drivers/leds/trigger/Kconfig @@ -83,13 +83,10 @@ config LEDS_TRIGGER_ACTIVITY config LEDS_TRIGGER_GPIO tristate "LED GPIO Trigger" depends on GPIOLIB || COMPILE_TEST - depends on BROKEN help This allows LEDs to be controlled by gpio events. It's good when using gpios as switches and triggering the needed LEDs - from there. One use case is n810's keypad LEDs that could - be triggered by this trigger when user slides up to show - keypad. + from there. Triggers are defined as device properties. If unsure, say N. diff --git a/drivers/leds/trigger/ledtrig-gpio.c b/drivers/leds/trigger/ledtrig-gpio.c index 0120faa3dafa..9b7fe5dd5208 100644 --- a/drivers/leds/trigger/ledtrig-gpio.c +++ b/drivers/leds/trigger/ledtrig-gpio.c @@ -3,12 +3,13 @@ * ledtrig-gio.c - LED Trigger Based on GPIO events * * Copyright 2009 Felipe Balbi + * Copyright 2023 Linus Walleij */ #include #include #include -#include +#include #include #include #include @@ -16,10 +17,8 @@ struct gpio_trig_data { struct led_classdev *led; - unsigned desired_brightness; /* desired brightness when led is on */ - unsigned inverted; /* true when gpio is inverted */ - unsigned gpio; /* gpio that triggers the leds */ + struct gpio_desc *gpiod; /* gpio that triggers the led */ }; static irqreturn_t gpio_trig_irq(int irq, void *_led) @@ -28,10 +27,7 @@ static irqreturn_t gpio_trig_irq(int irq, void *_led) struct gpio_trig_data *gpio_data = led_get_trigger_data(led); int tmp; - tmp = gpio_get_value_cansleep(gpio_data->gpio); - if (gpio_data->inverted) - tmp = !tmp; - + tmp = gpiod_get_value_cansleep(gpio_data->gpiod); if (tmp) { if (gpio_data->desired_brightness) led_set_brightness_nosleep(gpio_data->led, @@ -73,93 +69,8 @@ static ssize_t gpio_trig_brightness_store(struct device *dev, static DEVICE_ATTR(desired_brightness, 0644, gpio_trig_brightness_show, gpio_trig_brightness_store); -static ssize_t gpio_trig_inverted_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct gpio_trig_data *gpio_data = led_trigger_get_drvdata(dev); - - return sprintf(buf, "%u\n", gpio_data->inverted); -} - -static ssize_t gpio_trig_inverted_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t n) -{ - struct led_classdev *led = led_trigger_get_led(dev); - struct gpio_trig_data *gpio_data = led_trigger_get_drvdata(dev); - unsigned long inverted; - int ret; - - ret = kstrtoul(buf, 10, &inverted); - if (ret < 0) - return ret; - - if (inverted > 1) - return -EINVAL; - - gpio_data->inverted = inverted; - - /* After inverting, we need to update the LED. */ - if (gpio_is_valid(gpio_data->gpio)) - gpio_trig_irq(0, led); - - return n; -} -static DEVICE_ATTR(inverted, 0644, gpio_trig_inverted_show, - gpio_trig_inverted_store); - -static ssize_t gpio_trig_gpio_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct gpio_trig_data *gpio_data = led_trigger_get_drvdata(dev); - - return sprintf(buf, "%u\n", gpio_data->gpio); -} - -static ssize_t gpio_trig_gpio_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t n) -{ - struct led_classdev *led = led_trigger_get_led(dev); - struct gpio_trig_data *gpio_data = led_trigger_get_drvdata(dev); - unsigned gpio; - int ret; - - ret = sscanf(buf, "%u", &gpio); - if (ret < 1) { - dev_err(dev, "couldn't read gpio number\n"); - return -EINVAL; - } - - if (gpio_data->gpio == gpio) - return n; - - if (!gpio_is_valid(gpio)) { - if (gpio_is_valid(gpio_data->gpio)) - free_irq(gpio_to_irq(gpio_data->gpio), led); - gpio_data->gpio = gpio; - return n; - } - - ret = request_threaded_irq(gpio_to_irq(gpio), NULL, gpio_trig_irq, - IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_RISING - | IRQF_TRIGGER_FALLING, "ledtrig-gpio", led); - if (ret) { - dev_err(dev, "request_irq failed with error %d\n", ret); - } else { - if (gpio_is_valid(gpio_data->gpio)) - free_irq(gpio_to_irq(gpio_data->gpio), led); - gpio_data->gpio = gpio; - /* After changing the GPIO, we need to update the LED. */ - gpio_trig_irq(0, led); - } - - return ret ? ret : n; -} -static DEVICE_ATTR(gpio, 0644, gpio_trig_gpio_show, gpio_trig_gpio_store); - static struct attribute *gpio_trig_attrs[] = { &dev_attr_desired_brightness.attr, - &dev_attr_inverted.attr, - &dev_attr_gpio.attr, NULL }; ATTRIBUTE_GROUPS(gpio_trig); @@ -167,16 +78,48 @@ ATTRIBUTE_GROUPS(gpio_trig); static int gpio_trig_activate(struct led_classdev *led) { struct gpio_trig_data *gpio_data; + struct device *dev = led->dev; + int ret; gpio_data = kzalloc(sizeof(*gpio_data), GFP_KERNEL); if (!gpio_data) return -ENOMEM; - gpio_data->led = led; - gpio_data->gpio = -ENOENT; + /* + * The generic property "trigger-sources" is followed, + * and we hope that this is a GPIO. + */ + gpio_data->gpiod = fwnode_gpiod_get_index(dev->fwnode, + "trigger-sources", + 0, GPIOD_IN, + "led-trigger"); + if (IS_ERR(gpio_data->gpiod)) { + ret = PTR_ERR(gpio_data->gpiod); + kfree(gpio_data); + return ret; + } + if (!gpio_data->gpiod) { + dev_err(dev, "no valid GPIO for the trigger\n"); + kfree(gpio_data); + return -EINVAL; + } + gpio_data->led = led; led_set_trigger_data(led, gpio_data); + ret = request_threaded_irq(gpiod_to_irq(gpio_data->gpiod), NULL, gpio_trig_irq, + IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_RISING + | IRQF_TRIGGER_FALLING, "ledtrig-gpio", led); + if (ret) { + dev_err(dev, "request_irq failed with error %d\n", ret); + gpiod_put(gpio_data->gpiod); + kfree(gpio_data); + return ret; + } + + /* Finally update the LED to initial status */ + gpio_trig_irq(0, led); + return 0; } @@ -184,8 +127,8 @@ static void gpio_trig_deactivate(struct led_classdev *led) { struct gpio_trig_data *gpio_data = led_get_trigger_data(led); - if (gpio_is_valid(gpio_data->gpio)) - free_irq(gpio_to_irq(gpio_data->gpio), led); + free_irq(gpiod_to_irq(gpio_data->gpiod), led); + gpiod_put(gpio_data->gpiod); kfree(gpio_data); } -- cgit From 8e31906c75bcb8d36b51992fea318c879f2ae82b Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 29 Sep 2023 17:23:35 +0200 Subject: leds: lm3601x: Convert to use maple tree register cache The maple tree register cache is based on a much more modern data structure than the rbtree cache and makes optimisation choices which are probably more appropriate for modern systems than those made by the rbtree cache. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20230929-leds-maple-v1-1-ba5f9dcb1e75@kernel.org Signed-off-by: Lee Jones --- drivers/leds/flash/leds-lm3601x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/leds') diff --git a/drivers/leds/flash/leds-lm3601x.c b/drivers/leds/flash/leds-lm3601x.c index b6c524facf49..8191be0ef0c6 100644 --- a/drivers/leds/flash/leds-lm3601x.c +++ b/drivers/leds/flash/leds-lm3601x.c @@ -123,7 +123,7 @@ static const struct regmap_config lm3601x_regmap = { .max_register = LM3601X_DEV_ID_REG, .reg_defaults = lm3601x_regmap_defs, .num_reg_defaults = ARRAY_SIZE(lm3601x_regmap_defs), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .volatile_reg = lm3601x_volatile_reg, }; -- cgit From 65e9b51344cbd5a71fce160ae3142fdc70e43989 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 29 Sep 2023 17:23:36 +0200 Subject: leds: aw200xx: Convert to use maple tree register cache The maple tree register cache is based on a much more modern data structure than the rbtree cache and makes optimisation choices which are probably more appropriate for modern systems than those made by the rbtree cache. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20230929-leds-maple-v1-2-ba5f9dcb1e75@kernel.org Signed-off-by: Lee Jones --- drivers/leds/leds-aw200xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/leds') diff --git a/drivers/leds/leds-aw200xx.c b/drivers/leds/leds-aw200xx.c index 4d517cace3e7..14ca236ce29e 100644 --- a/drivers/leds/leds-aw200xx.c +++ b/drivers/leds/leds-aw200xx.c @@ -479,7 +479,7 @@ static const struct regmap_config aw200xx_regmap_config = { .num_ranges = ARRAY_SIZE(aw200xx_ranges), .rd_table = &aw200xx_readable_table, .wr_table = &aw200xx_writeable_table, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; static int aw200xx_probe(struct i2c_client *client) -- cgit From 5d97716f3fd2bb0f4462df23e4e1088dfcd85e5d Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 29 Sep 2023 17:23:37 +0200 Subject: leds: lm392x: Convert to use maple tree register cache The maple tree register cache is based on a much more modern data structure than the rbtree cache and makes optimisation choices which are probably more appropriate for modern systems than those made by the rbtree cache. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20230929-leds-maple-v1-3-ba5f9dcb1e75@kernel.org Signed-off-by: Lee Jones --- drivers/leds/leds-lm3692x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/leds') diff --git a/drivers/leds/leds-lm3692x.c b/drivers/leds/leds-lm3692x.c index f8ad61e47a19..c319ff4d70b2 100644 --- a/drivers/leds/leds-lm3692x.c +++ b/drivers/leds/leds-lm3692x.c @@ -139,7 +139,7 @@ static const struct regmap_config lm3692x_regmap_config = { .max_register = LM3692X_FAULT_FLAGS, .reg_defaults = lm3692x_reg_defs, .num_reg_defaults = ARRAY_SIZE(lm3692x_reg_defs), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; static int lm3692x_fault_check(struct lm3692x_led *led) -- cgit From fc8e107e7b15906a92afbeed9b289cc695984d5e Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 29 Sep 2023 17:23:38 +0200 Subject: leds: lp3952: Convert to use maple tree register cache The maple tree register cache is based on a much more modern data structure than the rbtree cache and makes optimisation choices which are probably more appropriate for modern systems than those made by the rbtree cache. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20230929-leds-maple-v1-4-ba5f9dcb1e75@kernel.org Signed-off-by: Lee Jones --- drivers/leds/leds-lp3952.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/leds') diff --git a/drivers/leds/leds-lp3952.c b/drivers/leds/leds-lp3952.c index 62ade3f05a87..5d18bbfd1f23 100644 --- a/drivers/leds/leds-lp3952.c +++ b/drivers/leds/leds-lp3952.c @@ -204,7 +204,7 @@ static const struct regmap_config lp3952_regmap = { .reg_bits = 8, .val_bits = 8, .max_register = REG_MAX, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; static int lp3952_probe(struct i2c_client *client) -- cgit From 9ddf40434ee4e1aac6ce2535fa852fbbad6f6d68 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 2 Oct 2023 16:56:29 +0300 Subject: leds: tca6507: Don't use fixed GPIO base First of all, the fixed GPIO base is source of troubles and it doesn't scale. Second, there is no in-kernel user of this base, so drop it. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20231002135629.2605462-1-andriy.shevchenko@linux.intel.com Signed-off-by: Lee Jones --- drivers/leds/leds-tca6507.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'drivers/leds') diff --git a/drivers/leds/leds-tca6507.c b/drivers/leds/leds-tca6507.c index aab861771210..e19074614095 100644 --- a/drivers/leds/leds-tca6507.c +++ b/drivers/leds/leds-tca6507.c @@ -92,9 +92,6 @@ struct tca6507_platform_data { struct led_platform_data leds; -#ifdef CONFIG_GPIOLIB - int gpio_base; -#endif }; #define TCA6507_MAKE_GPIO 1 @@ -636,7 +633,7 @@ static int tca6507_probe_gpios(struct device *dev, tca->gpio.label = "gpio-tca6507"; tca->gpio.ngpio = gpios; - tca->gpio.base = pdata->gpio_base; + tca->gpio.base = -1; tca->gpio.owner = THIS_MODULE; tca->gpio.direction_output = tca6507_gpio_direction_output; tca->gpio.set = tca6507_gpio_set_value; @@ -715,9 +712,6 @@ tca6507_led_dt_init(struct device *dev) pdata->leds.leds = tca_leds; pdata->leds.num_leds = NUM_LEDS; -#ifdef CONFIG_GPIOLIB - pdata->gpio_base = -1; -#endif return pdata; } -- cgit From a067943129b4ec6b835e02cfd5fbef01093c1471 Mon Sep 17 00:00:00 2001 From: Ondrej Jirman Date: Sun, 8 Oct 2023 16:40:13 +0200 Subject: leds: core: Add more colors from DT bindings to led_colors The colors are already part of DT bindings. Make sure the kernel is able to convert them to strings. Signed-off-by: Ondrej Jirman Link: https://lore.kernel.org/r/20231008144014.1180334-1-megi@xff.cz Signed-off-by: Lee Jones --- drivers/leds/led-core.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/leds') diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c index 04f9ea675f2c..742595d923ee 100644 --- a/drivers/leds/led-core.c +++ b/drivers/leds/led-core.c @@ -36,6 +36,11 @@ const char * const led_colors[LED_COLOR_ID_MAX] = { [LED_COLOR_ID_IR] = "ir", [LED_COLOR_ID_MULTI] = "multicolor", [LED_COLOR_ID_RGB] = "rgb", + [LED_COLOR_ID_PURPLE] = "purple", + [LED_COLOR_ID_ORANGE] = "orange", + [LED_COLOR_ID_PINK] = "pink", + [LED_COLOR_ID_CYAN] = "cyan", + [LED_COLOR_ID_LIME] = "lime", }; EXPORT_SYMBOL_GPL(led_colors); -- cgit From 0ebdb7210943eb345992bea9892adbd15a206193 Mon Sep 17 00:00:00 2001 From: André Apitzsch Date: Mon, 2 Oct 2023 18:48:28 +0200 Subject: leds: Add ktd202x driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit adds support for Kinetic KTD2026/7 RGB/White LED driver. Signed-off-by: André Apitzsch Link: https://lore.kernel.org/r/20231002-ktd202x-v6-2-26be8eefeb88@apitzsch.eu Signed-off-by: Lee Jones --- drivers/leds/rgb/Kconfig | 13 + drivers/leds/rgb/Makefile | 1 + drivers/leds/rgb/leds-ktd202x.c | 625 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 639 insertions(+) create mode 100644 drivers/leds/rgb/leds-ktd202x.c (limited to 'drivers/leds') diff --git a/drivers/leds/rgb/Kconfig b/drivers/leds/rgb/Kconfig index 183bccc06cf3..a6a21f564673 100644 --- a/drivers/leds/rgb/Kconfig +++ b/drivers/leds/rgb/Kconfig @@ -14,6 +14,19 @@ config LEDS_GROUP_MULTICOLOR To compile this driver as a module, choose M here: the module will be called leds-group-multicolor. +config LEDS_KTD202X + tristate "LED support for KTD202x Chips" + depends on I2C + depends on OF + select REGMAP_I2C + help + This option enables support for the Kinetic KTD2026/KTD2027 + RGB/White LED driver found in different BQ mobile phones. + It is a 3 or 4 channel LED driver programmed via an I2C interface. + + To compile this driver as a module, choose M here: the module + will be called leds-ktd202x. + config LEDS_PWM_MULTICOLOR tristate "PWM driven multi-color LED Support" depends on PWM diff --git a/drivers/leds/rgb/Makefile b/drivers/leds/rgb/Makefile index c11cc56384e7..243f31e4d70d 100644 --- a/drivers/leds/rgb/Makefile +++ b/drivers/leds/rgb/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_LEDS_GROUP_MULTICOLOR) += leds-group-multicolor.o +obj-$(CONFIG_LEDS_KTD202X) += leds-ktd202x.o obj-$(CONFIG_LEDS_PWM_MULTICOLOR) += leds-pwm-multicolor.o obj-$(CONFIG_LEDS_QCOM_LPG) += leds-qcom-lpg.o obj-$(CONFIG_LEDS_MT6370_RGB) += leds-mt6370-rgb.o diff --git a/drivers/leds/rgb/leds-ktd202x.c b/drivers/leds/rgb/leds-ktd202x.c new file mode 100644 index 000000000000..514965795a10 --- /dev/null +++ b/drivers/leds/rgb/leds-ktd202x.c @@ -0,0 +1,625 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Kinetic KTD2026/7 RGB/White LED driver with I2C interface + * + * Copyright 2023 André Apitzsch + * + * Datasheet: https://www.kinet-ic.com/uploads/KTD2026-7-04h.pdf + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define KTD2026_NUM_LEDS 3 +#define KTD2027_NUM_LEDS 4 +#define KTD202X_MAX_LEDS 4 + +/* Register bank */ +#define KTD202X_REG_RESET_CONTROL 0x00 +#define KTD202X_REG_FLASH_PERIOD 0x01 +#define KTD202X_REG_PWM1_TIMER 0x02 +#define KTD202X_REG_PWM2_TIMER 0x03 +#define KTD202X_REG_CHANNEL_CTRL 0x04 +#define KTD202X_REG_TRISE_FALL 0x05 +#define KTD202X_REG_LED_IOUT(x) (0x06 + (x)) + +/* Register 0 */ +#define KTD202X_TIMER_SLOT_CONTROL_TSLOT1 0x00 +#define KTD202X_TIMER_SLOT_CONTROL_TSLOT2 0x01 +#define KTD202X_TIMER_SLOT_CONTROL_TSLOT3 0x02 +#define KTD202X_TIMER_SLOT_CONTROL_TSLOT4 0x03 +#define KTD202X_RSTR_RESET 0x07 + +#define KTD202X_ENABLE_CTRL_WAKE 0x00 /* SCL High & SDA High */ +#define KTD202X_ENABLE_CTRL_SLEEP 0x08 /* SCL High & SDA Toggling */ + +#define KTD202X_TRISE_FALL_SCALE_NORMAL 0x00 +#define KTD202X_TRISE_FALL_SCALE_SLOW_X2 0x20 +#define KTD202X_TRISE_FALL_SCALE_SLOW_X4 0x40 +#define KTD202X_TRISE_FALL_SCALE_FAST_X8 0x60 + +/* Register 1 */ +#define KTD202X_FLASH_PERIOD_256_MS_LOG_RAMP 0x00 + +/* Register 2-3 */ +#define KTD202X_FLASH_ON_TIME_0_4_PERCENT 0x01 + +/* Register 4 */ +#define KTD202X_CHANNEL_CTRL_MASK(x) (BIT(2 * (x)) | BIT(2 * (x) + 1)) +#define KTD202X_CHANNEL_CTRL_OFF 0x00 +#define KTD202X_CHANNEL_CTRL_ON(x) BIT(2 * (x)) +#define KTD202X_CHANNEL_CTRL_PWM1(x) BIT(2 * (x) + 1) +#define KTD202X_CHANNEL_CTRL_PWM2(x) (BIT(2 * (x)) | BIT(2 * (x) + 1)) + +/* Register 5 */ +#define KTD202X_RAMP_TIMES_2_MS 0x00 + +/* Register 6-9 */ +#define KTD202X_LED_CURRENT_10_mA 0x4f + +#define KTD202X_FLASH_PERIOD_MIN_MS 256 +#define KTD202X_FLASH_PERIOD_STEP_MS 128 +#define KTD202X_FLASH_PERIOD_MAX_STEPS 126 +#define KTD202X_FLASH_ON_MAX 256 + +#define KTD202X_MAX_BRIGHTNESS 192 + +static const struct reg_default ktd202x_reg_defaults[] = { + { KTD202X_REG_RESET_CONTROL, KTD202X_TIMER_SLOT_CONTROL_TSLOT1 | + KTD202X_ENABLE_CTRL_WAKE | KTD202X_TRISE_FALL_SCALE_NORMAL }, + { KTD202X_REG_FLASH_PERIOD, KTD202X_FLASH_PERIOD_256_MS_LOG_RAMP }, + { KTD202X_REG_PWM1_TIMER, KTD202X_FLASH_ON_TIME_0_4_PERCENT }, + { KTD202X_REG_PWM2_TIMER, KTD202X_FLASH_ON_TIME_0_4_PERCENT }, + { KTD202X_REG_CHANNEL_CTRL, KTD202X_CHANNEL_CTRL_OFF }, + { KTD202X_REG_TRISE_FALL, KTD202X_RAMP_TIMES_2_MS }, + { KTD202X_REG_LED_IOUT(0), KTD202X_LED_CURRENT_10_mA }, + { KTD202X_REG_LED_IOUT(1), KTD202X_LED_CURRENT_10_mA }, + { KTD202X_REG_LED_IOUT(2), KTD202X_LED_CURRENT_10_mA }, + { KTD202X_REG_LED_IOUT(3), KTD202X_LED_CURRENT_10_mA }, +}; + +struct ktd202x_led { + struct ktd202x *chip; + union { + struct led_classdev cdev; + struct led_classdev_mc mcdev; + }; + u32 index; +}; + +struct ktd202x { + struct mutex mutex; + struct regulator_bulk_data regulators[2]; + struct device *dev; + struct regmap *regmap; + bool enabled; + int num_leds; + struct ktd202x_led leds[] __counted_by(num_leds); +}; + +static int ktd202x_chip_disable(struct ktd202x *chip) +{ + int ret; + + if (!chip->enabled) + return 0; + + regmap_write(chip->regmap, KTD202X_REG_RESET_CONTROL, KTD202X_ENABLE_CTRL_SLEEP); + + ret = regulator_bulk_disable(ARRAY_SIZE(chip->regulators), chip->regulators); + if (ret) { + dev_err(chip->dev, "Failed to disable regulators: %d\n", ret); + return ret; + } + + chip->enabled = false; + return 0; +} + +static int ktd202x_chip_enable(struct ktd202x *chip) +{ + int ret; + + if (chip->enabled) + return 0; + + ret = regulator_bulk_enable(ARRAY_SIZE(chip->regulators), chip->regulators); + if (ret) { + dev_err(chip->dev, "Failed to enable regulators: %d\n", ret); + return ret; + } + chip->enabled = true; + + ret = regmap_write(chip->regmap, KTD202X_REG_RESET_CONTROL, KTD202X_ENABLE_CTRL_WAKE); + + if (ret) { + dev_err(chip->dev, "Failed to enable the chip: %d\n", ret); + ktd202x_chip_disable(chip); + } + + return ret; +} + +static bool ktd202x_chip_in_use(struct ktd202x *chip) +{ + int i; + + for (i = 0; i < chip->num_leds; i++) { + if (chip->leds[i].cdev.brightness) + return true; + } + + return false; +} + +static int ktd202x_brightness_set(struct ktd202x_led *led, + struct mc_subled *subleds, + unsigned int num_channels) +{ + bool mode_blink = false; + int channel; + int state; + int ret; + int i; + + if (ktd202x_chip_in_use(led->chip)) { + ret = ktd202x_chip_enable(led->chip); + if (ret) + return ret; + } + + ret = regmap_read(led->chip->regmap, KTD202X_REG_CHANNEL_CTRL, &state); + if (ret) + return ret; + + /* + * In multicolor case, assume blink mode if PWM is set for at least one + * channel because another channel cannot be in state ON at the same time + */ + for (i = 0; i < num_channels; i++) { + int channel_state; + + channel = subleds[i].channel; + channel_state = (state >> 2 * channel) & KTD202X_CHANNEL_CTRL_MASK(0); + if (channel_state == KTD202X_CHANNEL_CTRL_OFF) + continue; + mode_blink = channel_state == KTD202X_CHANNEL_CTRL_PWM1(0); + break; + } + + for (i = 0; i < num_channels; i++) { + enum led_brightness brightness; + int mode; + + brightness = subleds[i].brightness; + channel = subleds[i].channel; + + if (brightness) { + /* Register expects brightness between 0 and MAX_BRIGHTNESS - 1 */ + ret = regmap_write(led->chip->regmap, KTD202X_REG_LED_IOUT(channel), + brightness - 1); + if (ret) + return ret; + + if (mode_blink) + mode = KTD202X_CHANNEL_CTRL_PWM1(channel); + else + mode = KTD202X_CHANNEL_CTRL_ON(channel); + } else { + mode = KTD202X_CHANNEL_CTRL_OFF; + } + ret = regmap_update_bits(led->chip->regmap, KTD202X_REG_CHANNEL_CTRL, + KTD202X_CHANNEL_CTRL_MASK(channel), mode); + if (ret) + return ret; + } + + if (!ktd202x_chip_in_use(led->chip)) + return ktd202x_chip_disable(led->chip); + + return 0; +} + +static int ktd202x_brightness_single_set(struct led_classdev *cdev, + enum led_brightness value) +{ + struct ktd202x_led *led = container_of(cdev, struct ktd202x_led, cdev); + struct mc_subled info; + int ret; + + cdev->brightness = value; + + mutex_lock(&led->chip->mutex); + + info.brightness = value; + info.channel = led->index; + ret = ktd202x_brightness_set(led, &info, 1); + + mutex_unlock(&led->chip->mutex); + + return ret; +} + +static int ktd202x_brightness_mc_set(struct led_classdev *cdev, + enum led_brightness value) +{ + struct led_classdev_mc *mc = lcdev_to_mccdev(cdev); + struct ktd202x_led *led = container_of(mc, struct ktd202x_led, mcdev); + int ret; + + cdev->brightness = value; + + mutex_lock(&led->chip->mutex); + + led_mc_calc_color_components(mc, value); + ret = ktd202x_brightness_set(led, mc->subled_info, mc->num_colors); + + mutex_unlock(&led->chip->mutex); + + return ret; +} + +static int ktd202x_blink_set(struct ktd202x_led *led, unsigned long *delay_on, + unsigned long *delay_off, struct mc_subled *subleds, + unsigned int num_channels) +{ + unsigned long delay_total_ms; + int ret, num_steps, on; + u8 ctrl_mask = 0; + u8 ctrl_pwm1 = 0; + u8 ctrl_on = 0; + int i; + + mutex_lock(&led->chip->mutex); + + for (i = 0; i < num_channels; i++) { + int channel = subleds[i].channel; + + ctrl_mask |= KTD202X_CHANNEL_CTRL_MASK(channel); + ctrl_on |= KTD202X_CHANNEL_CTRL_ON(channel); + ctrl_pwm1 |= KTD202X_CHANNEL_CTRL_PWM1(channel); + } + + /* Never off - brightness is already set, disable blinking */ + if (!*delay_off) { + ret = regmap_update_bits(led->chip->regmap, KTD202X_REG_CHANNEL_CTRL, + ctrl_mask, ctrl_on); + goto out; + } + + /* Convert into values the HW will understand. */ + + /* Integer representation of time of flash period */ + num_steps = (*delay_on + *delay_off - KTD202X_FLASH_PERIOD_MIN_MS) / + KTD202X_FLASH_PERIOD_STEP_MS; + num_steps = clamp(num_steps, 0, KTD202X_FLASH_PERIOD_MAX_STEPS); + + /* Integer representation of percentage of LED ON time */ + on = (*delay_on * KTD202X_FLASH_ON_MAX) / (*delay_on + *delay_off); + + /* Actually used delay_{on,off} values */ + delay_total_ms = num_steps * KTD202X_FLASH_PERIOD_STEP_MS + KTD202X_FLASH_PERIOD_MIN_MS; + *delay_on = (delay_total_ms * on) / KTD202X_FLASH_ON_MAX; + *delay_off = delay_total_ms - *delay_on; + + /* Set timings */ + ret = regmap_write(led->chip->regmap, KTD202X_REG_FLASH_PERIOD, num_steps); + if (ret) + goto out; + + ret = regmap_write(led->chip->regmap, KTD202X_REG_PWM1_TIMER, on); + if (ret) + goto out; + + ret = regmap_update_bits(led->chip->regmap, KTD202X_REG_CHANNEL_CTRL, + ctrl_mask, ctrl_pwm1); +out: + mutex_unlock(&led->chip->mutex); + return ret; +} + +static int ktd202x_blink_single_set(struct led_classdev *cdev, + unsigned long *delay_on, + unsigned long *delay_off) +{ + struct ktd202x_led *led = container_of(cdev, struct ktd202x_led, cdev); + struct mc_subled info; + int ret; + + if (!cdev->brightness) { + ret = ktd202x_brightness_single_set(cdev, KTD202X_MAX_BRIGHTNESS); + if (ret) + return ret; + } + + /* If no blink specified, default to 1 Hz. */ + if (!*delay_off && !*delay_on) { + *delay_off = 500; + *delay_on = 500; + } + + /* Never on - just set to off */ + if (!*delay_on) + return ktd202x_brightness_single_set(cdev, LED_OFF); + + info.channel = led->index; + + return ktd202x_blink_set(led, delay_on, delay_off, &info, 1); +} + +static int ktd202x_blink_mc_set(struct led_classdev *cdev, + unsigned long *delay_on, + unsigned long *delay_off) +{ + struct led_classdev_mc *mc = lcdev_to_mccdev(cdev); + struct ktd202x_led *led = container_of(mc, struct ktd202x_led, mcdev); + int ret; + + if (!cdev->brightness) { + ret = ktd202x_brightness_mc_set(cdev, KTD202X_MAX_BRIGHTNESS); + if (ret) + return ret; + } + + /* If no blink specified, default to 1 Hz. */ + if (!*delay_off && !*delay_on) { + *delay_off = 500; + *delay_on = 500; + } + + /* Never on - just set to off */ + if (!*delay_on) + return ktd202x_brightness_mc_set(cdev, LED_OFF); + + return ktd202x_blink_set(led, delay_on, delay_off, mc->subled_info, + mc->num_colors); +} + +static int ktd202x_setup_led_rgb(struct ktd202x *chip, struct device_node *np, + struct ktd202x_led *led, struct led_init_data *init_data) +{ + struct led_classdev *cdev; + struct device_node *child; + struct mc_subled *info; + int num_channels; + int i = 0; + + num_channels = of_get_available_child_count(np); + if (!num_channels || num_channels > chip->num_leds) + return -EINVAL; + + info = devm_kcalloc(chip->dev, num_channels, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + for_each_available_child_of_node(np, child) { + u32 mono_color; + u32 reg; + int ret; + + ret = of_property_read_u32(child, "reg", ®); + if (ret != 0 || reg >= chip->num_leds) { + dev_err(chip->dev, "invalid 'reg' of %pOFn\n", child); + of_node_put(child); + return -EINVAL; + } + + ret = of_property_read_u32(child, "color", &mono_color); + if (ret < 0 && ret != -EINVAL) { + dev_err(chip->dev, "failed to parse 'color' of %pOF\n", child); + of_node_put(child); + return ret; + } + + info[i].color_index = mono_color; + info[i].channel = reg; + info[i].intensity = KTD202X_MAX_BRIGHTNESS; + i++; + } + + led->mcdev.subled_info = info; + led->mcdev.num_colors = num_channels; + + cdev = &led->mcdev.led_cdev; + cdev->brightness_set_blocking = ktd202x_brightness_mc_set; + cdev->blink_set = ktd202x_blink_mc_set; + + return devm_led_classdev_multicolor_register_ext(chip->dev, &led->mcdev, init_data); +} + +static int ktd202x_setup_led_single(struct ktd202x *chip, struct device_node *np, + struct ktd202x_led *led, struct led_init_data *init_data) +{ + struct led_classdev *cdev; + u32 reg; + int ret; + + ret = of_property_read_u32(np, "reg", ®); + if (ret != 0 || reg >= chip->num_leds) { + dev_err(chip->dev, "invalid 'reg' of %pOFn\n", np); + return -EINVAL; + } + led->index = reg; + + cdev = &led->cdev; + cdev->brightness_set_blocking = ktd202x_brightness_single_set; + cdev->blink_set = ktd202x_blink_single_set; + + return devm_led_classdev_register_ext(chip->dev, &led->cdev, init_data); +} + +static int ktd202x_add_led(struct ktd202x *chip, struct device_node *np, unsigned int index) +{ + struct ktd202x_led *led = &chip->leds[index]; + struct led_init_data init_data = {}; + struct led_classdev *cdev; + u32 color; + int ret; + + /* Color property is optional in single color case */ + ret = of_property_read_u32(np, "color", &color); + if (ret < 0 && ret != -EINVAL) { + dev_err(chip->dev, "failed to parse 'color' of %pOF\n", np); + return ret; + } + + led->chip = chip; + init_data.fwnode = of_fwnode_handle(np); + + if (color == LED_COLOR_ID_RGB) { + cdev = &led->mcdev.led_cdev; + ret = ktd202x_setup_led_rgb(chip, np, led, &init_data); + } else { + cdev = &led->cdev; + ret = ktd202x_setup_led_single(chip, np, led, &init_data); + } + + if (ret) { + dev_err(chip->dev, "unable to register %s\n", cdev->name); + return ret; + } + + cdev->max_brightness = KTD202X_MAX_BRIGHTNESS; + + return 0; +} + +static int ktd202x_probe_dt(struct ktd202x *chip) +{ + struct device_node *np = dev_of_node(chip->dev), *child; + int count; + int i = 0; + + chip->num_leds = (int)(unsigned long)of_device_get_match_data(chip->dev); + + count = of_get_available_child_count(np); + if (!count || count > chip->num_leds) + return -EINVAL; + + regmap_write(chip->regmap, KTD202X_REG_RESET_CONTROL, KTD202X_RSTR_RESET); + + /* Allow the device to execute the complete reset */ + usleep_range(200, 300); + + for_each_available_child_of_node(np, child) { + int ret = ktd202x_add_led(chip, child, i); + + if (ret) { + of_node_put(child); + return ret; + } + i++; + } + + return 0; +} + +static const struct regmap_config ktd202x_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x09, + .cache_type = REGCACHE_FLAT, + .reg_defaults = ktd202x_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(ktd202x_reg_defaults), +}; + +static int ktd202x_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct ktd202x *chip; + int count; + int ret; + + count = device_get_child_node_count(dev); + if (!count || count > KTD202X_MAX_LEDS) + return dev_err_probe(dev, -EINVAL, "Incorrect number of leds (%d)", count); + + chip = devm_kzalloc(dev, struct_size(chip, leds, count), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->dev = dev; + i2c_set_clientdata(client, chip); + + chip->regmap = devm_regmap_init_i2c(client, &ktd202x_regmap_config); + if (IS_ERR(chip->regmap)) { + ret = dev_err_probe(dev, PTR_ERR(chip->regmap), + "Failed to allocate register map.\n"); + return ret; + } + + chip->regulators[0].supply = "vin"; + chip->regulators[1].supply = "vio"; + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(chip->regulators), chip->regulators); + if (ret < 0) { + dev_err_probe(dev, ret, "Failed to request regulators.\n"); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(chip->regulators), chip->regulators); + if (ret) { + dev_err_probe(dev, ret, "Failed to enable regulators.\n"); + return ret; + } + + ret = ktd202x_probe_dt(chip); + if (ret < 0) { + regulator_bulk_disable(ARRAY_SIZE(chip->regulators), chip->regulators); + return ret; + } + + ret = regulator_bulk_disable(ARRAY_SIZE(chip->regulators), chip->regulators); + if (ret) { + dev_err_probe(dev, ret, "Failed to disable regulators.\n"); + return ret; + } + + mutex_init(&chip->mutex); + + return 0; +} + +static void ktd202x_remove(struct i2c_client *client) +{ + struct ktd202x *chip = i2c_get_clientdata(client); + + ktd202x_chip_disable(chip); + + mutex_destroy(&chip->mutex); +} + +static void ktd202x_shutdown(struct i2c_client *client) +{ + struct ktd202x *chip = i2c_get_clientdata(client); + + /* Reset registers to make sure all LEDs are off before shutdown */ + regmap_write(chip->regmap, KTD202X_REG_RESET_CONTROL, KTD202X_RSTR_RESET); +} + +static const struct of_device_id ktd202x_match_table[] = { + { .compatible = "kinetic,ktd2026", .data = (void *)KTD2026_NUM_LEDS }, + { .compatible = "kinetic,ktd2027", .data = (void *)KTD2027_NUM_LEDS }, + {}, +}; +MODULE_DEVICE_TABLE(of, ktd202x_match_table); + +static struct i2c_driver ktd202x_driver = { + .driver = { + .name = "leds-ktd202x", + .of_match_table = ktd202x_match_table, + }, + .probe = ktd202x_probe, + .remove = ktd202x_remove, + .shutdown = ktd202x_shutdown, +}; +module_i2c_driver(ktd202x_driver); + +MODULE_AUTHOR("André Apitzsch "); +MODULE_DESCRIPTION("Kinetic KTD2026/7 LED driver"); +MODULE_LICENSE("GPL"); -- cgit From 259e33cbb1712a7dd844fc9757661cc47cb0e39b Mon Sep 17 00:00:00 2001 From: Christian Marangi Date: Sat, 7 Oct 2023 15:10:42 +0200 Subject: leds: trigger: netdev: Move size check in set_device_name GCC 13.2 complains about array subscript 17 is above array bounds of 'char[16]' with IFNAMSIZ set to 16. The warning is correct but this scenario is impossible. set_device_name is called by device_name_store (store sysfs entry) and netdev_trig_activate. device_name_store already check if size is >= of IFNAMSIZ and return -EINVAL. (making the warning scenario impossible) netdev_trig_activate works on already defined interface, where the name has already been checked and should already follow the condition of strlen() < IFNAMSIZ. Aside from the scenario being impossible, set_device_name can be improved to both mute the warning and make the function safer. To make it safer, move size check from device_name_store directly to set_device_name and prevent any out of bounds scenario. Cc: stable@vger.kernel.org Fixes: 28a6a2ef18ad ("leds: trigger: netdev: refactor code setting device name") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202309192035.GTJEEbem-lkp@intel.com/ Signed-off-by: Christian Marangi Link: https://lore.kernel.org/r/20231007131042.15032-1-ansuelsmth@gmail.com Signed-off-by: Lee Jones --- drivers/leds/trigger/ledtrig-netdev.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/leds') diff --git a/drivers/leds/trigger/ledtrig-netdev.c b/drivers/leds/trigger/ledtrig-netdev.c index 58f3352539e8..e358e77e4b38 100644 --- a/drivers/leds/trigger/ledtrig-netdev.c +++ b/drivers/leds/trigger/ledtrig-netdev.c @@ -221,6 +221,9 @@ static ssize_t device_name_show(struct device *dev, static int set_device_name(struct led_netdev_data *trigger_data, const char *name, size_t size) { + if (size >= IFNAMSIZ) + return -EINVAL; + cancel_delayed_work_sync(&trigger_data->work); mutex_lock(&trigger_data->lock); @@ -263,9 +266,6 @@ static ssize_t device_name_store(struct device *dev, struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev); int ret; - if (size >= IFNAMSIZ) - return -EINVAL; - ret = set_device_name(trigger_data, buf, size); if (ret < 0) -- cgit From 50be9e029b3ac72848685d7a74d1bd32b36309bf Mon Sep 17 00:00:00 2001 From: Chunyan Zhang Date: Fri, 13 Oct 2023 10:20:10 +0800 Subject: leds: sc27xx: Move mutex_init() down Move the mutex_init() to avoid redundant mutex_destroy() calls after that for each time the probe fails. Signed-off-by: Chunyan Zhang Reviewed-by: Baolin Wang Link: https://lore.kernel.org/r/20231013022010.854367-1-chunyan.zhang@unisoc.com Signed-off-by: Lee Jones --- drivers/leds/leds-sc27xx-bltc.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/leds') diff --git a/drivers/leds/leds-sc27xx-bltc.c b/drivers/leds/leds-sc27xx-bltc.c index af1f00a2f328..f04db793e8d6 100644 --- a/drivers/leds/leds-sc27xx-bltc.c +++ b/drivers/leds/leds-sc27xx-bltc.c @@ -296,7 +296,6 @@ static int sc27xx_led_probe(struct platform_device *pdev) return -ENOMEM; platform_set_drvdata(pdev, priv); - mutex_init(&priv->lock); priv->base = base; priv->regmap = dev_get_regmap(dev->parent, NULL); if (!priv->regmap) { @@ -309,13 +308,11 @@ static int sc27xx_led_probe(struct platform_device *pdev) err = of_property_read_u32(child, "reg", ®); if (err) { of_node_put(child); - mutex_destroy(&priv->lock); return err; } if (reg >= SC27XX_LEDS_MAX || priv->leds[reg].active) { of_node_put(child); - mutex_destroy(&priv->lock); return -EINVAL; } @@ -323,6 +320,8 @@ static int sc27xx_led_probe(struct platform_device *pdev) priv->leds[reg].active = true; } + mutex_init(&priv->lock); + err = sc27xx_led_register(dev, priv); if (err) mutex_destroy(&priv->lock); -- cgit From 78cbcfd8b13cefe089296d053f222934a53e153d Mon Sep 17 00:00:00 2001 From: Marek Behún Date: Mon, 16 Oct 2023 16:15:38 +0200 Subject: leds: turris-omnia: Fix brightness setting and trigger activating MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I have improperly refactored commits 4d5ed2621c24 ("leds: turris-omnia: Make set_brightness() more efficient") and aaf38273cf76 ("leds: turris-omnia: Support HW controlled mode via private trigger") after Lee requested a change in API semantics of the new functions I introduced in commit 28350bc0ac77 ("leds: turris-omnia: Do not use SMBUS calls"). Before the change, the function omnia_cmd_write_u8() returned 0 on success, and afterwards it returned a positive value (number of bytes written). The latter version was applied, but the following commits did not properly account for this change. This results in non-functional LED's .brightness_set_blocking() and trigger's .activate() methods. The main reasoning behind the semantics change was that read/write methods should return the number of read/written bytes on success. It was pointed to me [1] that this is not always true (for example the regmap API does not do so), and since the driver never uses this number of read/written bytes information, I decided to fix this issue by changing the functions to the original semantics (return 0 on success). [1] https://lore.kernel.org/linux-gpio/ZQnn+Gi0xVlsGCYA@smile.fi.intel.com/ Fixes: 28350bc0ac77 ("leds: turris-omnia: Do not use SMBUS calls") Signed-off-by: Marek Behún Link: https://lore.kernel.org/r/20231016141538.30037-1-kabel@kernel.org Signed-off-by: Lee Jones --- drivers/leds/leds-turris-omnia.c | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) (limited to 'drivers/leds') diff --git a/drivers/leds/leds-turris-omnia.c b/drivers/leds/leds-turris-omnia.c index f27241896970..b443f8c989fa 100644 --- a/drivers/leds/leds-turris-omnia.c +++ b/drivers/leds/leds-turris-omnia.c @@ -60,8 +60,11 @@ struct omnia_leds { static int omnia_cmd_write_u8(const struct i2c_client *client, u8 cmd, u8 val) { u8 buf[2] = { cmd, val }; + int ret; + + ret = i2c_master_send(client, buf, sizeof(buf)); - return i2c_master_send(client, buf, sizeof(buf)); + return ret < 0 ? ret : 0; } static int omnia_cmd_read_raw(struct i2c_adapter *adapter, u8 addr, u8 cmd, @@ -81,7 +84,7 @@ static int omnia_cmd_read_raw(struct i2c_adapter *adapter, u8 addr, u8 cmd, ret = i2c_transfer(adapter, msgs, ARRAY_SIZE(msgs)); if (likely(ret == ARRAY_SIZE(msgs))) - return len; + return 0; else if (ret < 0) return ret; else @@ -91,11 +94,11 @@ static int omnia_cmd_read_raw(struct i2c_adapter *adapter, u8 addr, u8 cmd, static int omnia_cmd_read_u8(const struct i2c_client *client, u8 cmd) { u8 reply; - int ret; + int err; - ret = omnia_cmd_read_raw(client->adapter, client->addr, cmd, &reply, 1); - if (ret < 0) - return ret; + err = omnia_cmd_read_raw(client->adapter, client->addr, cmd, &reply, 1); + if (err) + return err; return reply; } @@ -236,7 +239,7 @@ static void omnia_hwtrig_deactivate(struct led_classdev *cdev) mutex_unlock(&leds->lock); - if (err < 0) + if (err) dev_err(cdev->dev, "Cannot put LED to software mode: %i\n", err); } @@ -302,7 +305,7 @@ static int omnia_led_register(struct i2c_client *client, struct omnia_led *led, ret = omnia_cmd_write_u8(client, CMD_LED_MODE, CMD_LED_MODE_LED(led->reg) | CMD_LED_MODE_USER); - if (ret < 0) { + if (ret) { dev_err(dev, "Cannot set LED %pOF to software mode: %i\n", np, ret); return ret; @@ -311,7 +314,7 @@ static int omnia_led_register(struct i2c_client *client, struct omnia_led *led, /* disable the LED */ ret = omnia_cmd_write_u8(client, CMD_LED_STATE, CMD_LED_STATE_LED(led->reg)); - if (ret < 0) { + if (ret) { dev_err(dev, "Cannot set LED %pOF brightness: %i\n", np, ret); return ret; } @@ -364,7 +367,7 @@ static ssize_t brightness_store(struct device *dev, struct device_attribute *a, { struct i2c_client *client = to_i2c_client(dev); unsigned long brightness; - int ret; + int err; if (kstrtoul(buf, 10, &brightness)) return -EINVAL; @@ -372,9 +375,9 @@ static ssize_t brightness_store(struct device *dev, struct device_attribute *a, if (brightness > 100) return -EINVAL; - ret = omnia_cmd_write_u8(client, CMD_LED_SET_BRIGHTNESS, brightness); + err = omnia_cmd_write_u8(client, CMD_LED_SET_BRIGHTNESS, brightness); - return ret < 0 ? ret : count; + return err ?: count; } static DEVICE_ATTR_RW(brightness); @@ -403,7 +406,7 @@ static ssize_t gamma_correction_store(struct device *dev, struct i2c_client *client = to_i2c_client(dev); struct omnia_leds *leds = i2c_get_clientdata(client); bool val; - int ret; + int err; if (!leds->has_gamma_correction) return -EOPNOTSUPP; @@ -411,9 +414,9 @@ static ssize_t gamma_correction_store(struct device *dev, if (kstrtobool(buf, &val) < 0) return -EINVAL; - ret = omnia_cmd_write_u8(client, CMD_SET_GAMMA_CORRECTION, val); + err = omnia_cmd_write_u8(client, CMD_SET_GAMMA_CORRECTION, val); - return ret < 0 ? ret : count; + return err ?: count; } static DEVICE_ATTR_RW(gamma_correction); @@ -431,7 +434,7 @@ static int omnia_mcu_get_features(const struct i2c_client *client) err = omnia_cmd_read_raw(client->adapter, OMNIA_MCU_I2C_ADDR, CMD_GET_STATUS_WORD, &reply, sizeof(reply)); - if (err < 0) + if (err) return err; /* Check whether MCU firmware supports the CMD_GET_FEAUTRES command */ @@ -440,7 +443,7 @@ static int omnia_mcu_get_features(const struct i2c_client *client) err = omnia_cmd_read_raw(client->adapter, OMNIA_MCU_I2C_ADDR, CMD_GET_FEATURES, &reply, sizeof(reply)); - if (err < 0) + if (err) return err; return le16_to_cpu(reply); -- cgit From 49e50aad22aebaaca3ff7abbdd462deaf16c5f35 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 16 Oct 2023 18:30:51 +0300 Subject: leds: core: Refactor led_update_brightness() to use standard pattern The standard conditional pattern is to check for errors first and bail out if any. Refactor led_update_brightness() accordingly. While at it, drop unneeded assignment and return 0 unconditionally on success. Signed-off-by: Andy Shevchenko Acked-by: Denis Osterland-Heim Reviewed-by: Hans de Goede Link: https://lore.kernel.org/r/20231016153051.1409074-1-andriy.shevchenko@linux.intel.com Signed-off-by: Lee Jones --- drivers/leds/led-core.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/leds') diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c index 742595d923ee..cedda8204c57 100644 --- a/drivers/leds/led-core.c +++ b/drivers/leds/led-core.c @@ -364,17 +364,17 @@ EXPORT_SYMBOL_GPL(led_set_brightness_sync); int led_update_brightness(struct led_classdev *led_cdev) { - int ret = 0; + int ret; if (led_cdev->brightness_get) { ret = led_cdev->brightness_get(led_cdev); - if (ret >= 0) { - led_cdev->brightness = ret; - return 0; - } + if (ret < 0) + return ret; + + led_cdev->brightness = ret; } - return ret; + return 0; } EXPORT_SYMBOL_GPL(led_update_brightness); -- cgit From e80fc4bfc820aa44c500cf4c61e08765f36d3c63 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 16 Oct 2023 19:10:00 +0300 Subject: leds: gpio: Keep driver firmware interface agnostic The of.h is used as a proxy to mod_devicetable, replace former by latter. The commit 2d6180147e92 ("leds: gpio: Configure per-LED pin control") added yet another unneeded OF APIs. Replace with direct use of fwnode. Altogether this makes driver agnostic to the firmware interface in use. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20231016161005.1471768-1-andriy.shevchenko@linux.intel.com Signed-off-by: Lee Jones --- drivers/leds/leds-gpio.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/leds') diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index a6597f0f3eb4..debadb48ceda 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -11,8 +11,8 @@ #include #include #include +#include #include -#include #include #include #include @@ -129,8 +129,8 @@ static int create_gpio_led(const struct gpio_led *template, ret = PTR_ERR(pinctrl); if (ret != -ENODEV) { dev_warn(led_dat->cdev.dev, - "Failed to select %pOF pinctrl: %d\n", - to_of_node(fwnode), ret); + "Failed to select %pfw pinctrl: %d\n", + fwnode, ret); } else { /* pinctrl-%d not present, not an error */ ret = 0; -- cgit From f5ad594e389c57dfe19059ce868392809f9b1a71 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 16 Oct 2023 19:10:01 +0300 Subject: leds: gpio: Utilise PTR_ERR_OR_ZERO() Avoid a boilerplate code by using PTR_ERR_OR_ZERO() in create_gpio_led(). Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20231016161005.1471768-2-andriy.shevchenko@linux.intel.com Signed-off-by: Lee Jones --- drivers/leds/leds-gpio.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) (limited to 'drivers/leds') diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index debadb48ceda..4071cb9eefec 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -125,16 +125,13 @@ static int create_gpio_led(const struct gpio_led *template, return ret; pinctrl = devm_pinctrl_get_select_default(led_dat->cdev.dev); - if (IS_ERR(pinctrl)) { - ret = PTR_ERR(pinctrl); - if (ret != -ENODEV) { - dev_warn(led_dat->cdev.dev, - "Failed to select %pfw pinctrl: %d\n", - fwnode, ret); - } else { - /* pinctrl-%d not present, not an error */ - ret = 0; - } + ret = PTR_ERR_OR_ZERO(pinctrl); + /* pinctrl-%d not present, not an error */ + if (ret == -ENODEV) + ret = 0; + if (ret) { + dev_warn(led_dat->cdev.dev, "Failed to select %pfw pinctrl: %d\n", + fwnode, ret); } return ret; -- cgit From 5ac50ec712921f6250188732387bf5dac33736ae Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 16 Oct 2023 19:10:02 +0300 Subject: leds: gpio: Refactor code to use devm_gpiod_get_index_optional() Instead of checking for the specific error codes, replace devm_gpiod_get_index() with devm_gpiod_get_index_optional(). In this case we just return all errors to the caller and simply check for NULL in case if legacy GPIO is being used. As the result the code is easier to read and maintain. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20231016161005.1471768-3-andriy.shevchenko@linux.intel.com Signed-off-by: Lee Jones --- drivers/leds/leds-gpio.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/leds') diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index 4071cb9eefec..7c9c6a93dfd7 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -218,13 +218,13 @@ static struct gpio_desc *gpio_led_get_gpiod(struct device *dev, int idx, * device, this will hit the board file, if any and get * the GPIO from there. */ - gpiod = devm_gpiod_get_index(dev, NULL, idx, GPIOD_OUT_LOW); - if (!IS_ERR(gpiod)) { + gpiod = devm_gpiod_get_index_optional(dev, NULL, idx, GPIOD_OUT_LOW); + if (IS_ERR(gpiod)) + return gpiod; + if (gpiod) { gpiod_set_consumer_name(gpiod, template->name); return gpiod; } - if (PTR_ERR(gpiod) != -ENOENT) - return gpiod; /* * This is the legacy code path for platform code that -- cgit From 54e657d604ae27a30ce4f84c243661d9b744bbce Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 16 Oct 2023 19:10:03 +0300 Subject: leds: gpio: Move temporary variable for struct device to gpio_led_probe() Use temporary variable for struct device in gpio_led_probe() in order to make code neater. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20231016161005.1471768-4-andriy.shevchenko@linux.intel.com Signed-off-by: Lee Jones --- drivers/leds/leds-gpio.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) (limited to 'drivers/leds') diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index 7c9c6a93dfd7..fd3f90f95fa2 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -142,9 +142,8 @@ struct gpio_leds_priv { struct gpio_led_data leds[] __counted_by(num_leds); }; -static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev) +static struct gpio_leds_priv *gpio_leds_create(struct device *dev) { - struct device *dev = &pdev->dev; struct fwnode_handle *child; struct gpio_leds_priv *priv; int count, ret; @@ -253,13 +252,13 @@ static struct gpio_desc *gpio_led_get_gpiod(struct device *dev, int idx, static int gpio_led_probe(struct platform_device *pdev) { - struct gpio_led_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct device *dev = &pdev->dev; + struct gpio_led_platform_data *pdata = dev_get_platdata(dev); struct gpio_leds_priv *priv; int i, ret = 0; if (pdata && pdata->num_leds) { - priv = devm_kzalloc(&pdev->dev, struct_size(priv, leds, pdata->num_leds), - GFP_KERNEL); + priv = devm_kzalloc(dev, struct_size(priv, leds, pdata->num_leds), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -272,22 +271,20 @@ static int gpio_led_probe(struct platform_device *pdev) led_dat->gpiod = template->gpiod; else led_dat->gpiod = - gpio_led_get_gpiod(&pdev->dev, - i, template); + gpio_led_get_gpiod(dev, i, template); if (IS_ERR(led_dat->gpiod)) { - dev_info(&pdev->dev, "Skipping unavailable LED gpio %d (%s)\n", + dev_info(dev, "Skipping unavailable LED gpio %d (%s)\n", template->gpio, template->name); continue; } - ret = create_gpio_led(template, led_dat, - &pdev->dev, NULL, + ret = create_gpio_led(template, led_dat, dev, NULL, pdata->gpio_blink_set); if (ret < 0) return ret; } } else { - priv = gpio_leds_create(pdev); + priv = gpio_leds_create(dev); if (IS_ERR(priv)) return PTR_ERR(priv); } -- cgit From 7b2d8a059c77344eca0f5281f97224d1accfb88a Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 16 Oct 2023 19:10:04 +0300 Subject: leds: gpio: Remove unneeded assignment The initial ret is not used anywhere, drop the unneeded assignment. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20231016161005.1471768-5-andriy.shevchenko@linux.intel.com Signed-off-by: Lee Jones --- drivers/leds/leds-gpio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/leds') diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index fd3f90f95fa2..d6e8298ffb3e 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -255,7 +255,7 @@ static int gpio_led_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct gpio_led_platform_data *pdata = dev_get_platdata(dev); struct gpio_leds_priv *priv; - int i, ret = 0; + int i, ret; if (pdata && pdata->num_leds) { priv = devm_kzalloc(dev, struct_size(priv, leds, pdata->num_leds), GFP_KERNEL); -- cgit From 2038d3fdc7434d522369c58cb7a4acd5315eebec Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 16 Oct 2023 19:10:05 +0300 Subject: leds: gpio: Update headers Include headers which we are direct users of, no need to have proxies. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20231016161005.1471768-6-andriy.shevchenko@linux.intel.com Signed-off-by: Lee Jones --- drivers/leds/leds-gpio.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/leds') diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index d6e8298ffb3e..710c319ad312 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -6,17 +6,21 @@ * Raphael Assenat * Copyright (C) 2008 Freescale Semiconductor, Inc. */ +#include +#include #include #include #include -#include #include #include #include +#include #include #include #include #include +#include + #include "leds.h" struct gpio_led_data { -- cgit From b9604be241587fb29c0f40450e53d0a37dc611b5 Mon Sep 17 00:00:00 2001 From: Su Hui Date: Fri, 20 Oct 2023 17:19:31 +0800 Subject: leds: lp5521: Add an error check in lp5521_post_init_device lp55xx_write() can return an error code, add a check for this. Signed-off-by: Su Hui Link: https://lore.kernel.org/r/20231020091930.207870-1-suhui@nfschina.com Signed-off-by: Lee Jones --- drivers/leds/leds-lp5521.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/leds') diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 2ef19ad23b1d..f9c8b568b652 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -301,6 +301,8 @@ static int lp5521_post_init_device(struct lp55xx_chip *chip) /* Set all PWMs to direct control mode */ ret = lp55xx_write(chip, LP5521_REG_OP_MODE, LP5521_CMD_DIRECT); + if (ret) + return ret; /* Update configuration for the clock setting */ val = LP5521_DEFAULT_CFG; -- cgit