From a270f61c00c7d5a62664b85baa56a8ab1a242f8d Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Fri, 17 Nov 2023 02:01:34 +0000 Subject: mediatek: import driver for Adtran SmartRG RGBW LED Import driver for I2C-connected HolTek MCU controlling the RGBW LED found in Adtran SmartRG devices. Signed-off-by: Daniel Golle --- .../files/drivers/leds/leds-smartrg-system.c | 239 +++++++++++++++++++++ target/linux/mediatek/filogic/config-5.15 | 1 + target/linux/mediatek/filogic/config-6.1 | 1 + target/linux/mediatek/mt7622/config-5.15 | 1 + target/linux/mediatek/mt7622/config-6.1 | 1 + target/linux/mediatek/mt7623/config-5.15 | 1 + target/linux/mediatek/mt7623/config-6.1 | 1 + target/linux/mediatek/mt7629/config-5.15 | 1 + target/linux/mediatek/mt7629/config-6.1 | 1 + .../patches-5.15/950-smartrg-i2c-led-driver.patch | 34 +++ .../patches-6.1/950-smartrg-i2c-led-driver.patch | 34 +++ 11 files changed, 315 insertions(+) create mode 100644 target/linux/mediatek/files/drivers/leds/leds-smartrg-system.c create mode 100644 target/linux/mediatek/patches-5.15/950-smartrg-i2c-led-driver.patch create mode 100644 target/linux/mediatek/patches-6.1/950-smartrg-i2c-led-driver.patch (limited to 'target/linux/mediatek') diff --git a/target/linux/mediatek/files/drivers/leds/leds-smartrg-system.c b/target/linux/mediatek/files/drivers/leds/leds-smartrg-system.c new file mode 100644 index 0000000000..1679555068 --- /dev/null +++ b/target/linux/mediatek/files/drivers/leds/leds-smartrg-system.c @@ -0,0 +1,239 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * Driver for SmartRG RGBW LED microcontroller. + * RGBW LED is connected to a Holtek HT45F0062 that is on the I2C bus. + * + */ + +struct srg_led_ctrl; +struct srg_led { + u8 index; + struct led_classdev led; + struct srg_led_ctrl *ctrl; +}; + +struct srg_led_ctrl { + struct mutex lock; + struct i2c_client *client; + struct srg_led channel[4]; + u8 control[5]; +}; + +static int +srg_led_i2c_write(struct srg_led_ctrl *sysled_ctrl, u8 reg, u8 value) +{ + return i2c_smbus_write_byte_data(sysled_ctrl->client, reg, value); +} + +/* + * MC LED Command: 0 = OFF, 1 = ON, 2 = Flash, 3 = Pulse, 4 = Blink + * */ +static int +srg_led_control_sync(struct srg_led_ctrl *sysled_ctrl) +{ + int i, ret; + + for (i = 1; i < 5; i++) { + ret = srg_led_i2c_write(sysled_ctrl, i, sysled_ctrl->control[i]); + if (ret) + break; + } + return ret; +} + +/* + * This function overrides the led driver timer trigger to offload + * flashing to the micro-controller. The negative effect of this + * is the inability to configure the delay_on and delay_off periods. + * + * */ +static int +srg_led_set_pulse(struct led_classdev *led_cdev, + unsigned long *delay_on, + unsigned long *delay_off) +{ + struct srg_led *sysled = container_of(led_cdev, struct srg_led, led); + struct srg_led_ctrl *sysled_ctrl = sysled->ctrl; + bool blinking = false, pulsing = false; + u8 cbyte; + int ret; + + if (delay_on && delay_off && (*delay_on > 100) && (*delay_on <= 500)) { + pulsing = true; + *delay_on = 500; + *delay_off = 500; + } else if (delay_on && delay_off && (*delay_on >= 50) && (*delay_on <= 100)) { + blinking = true; + *delay_on = 50; + *delay_off = 50; + } + + cbyte = pulsing ? 3 : blinking ? 2 : 0; + mutex_lock(&sysled_ctrl->lock); + ret = srg_led_i2c_write(sysled_ctrl, sysled->index + 4, + (blinking || pulsing) ? 255 : 0); + if (!ret) { + sysled_ctrl->control[sysled->index] = cbyte; + ret = srg_led_control_sync(sysled_ctrl); + } + mutex_unlock(&sysled_ctrl->lock); + + return !cbyte; +} + +static int +srg_led_set_brightness(struct led_classdev *led_cdev, + enum led_brightness value) +{ + struct srg_led *sysled = container_of(led_cdev, struct srg_led, led); + struct srg_led_ctrl *sysled_ctrl = sysled->ctrl; + int ret; + + mutex_lock(&sysled_ctrl->lock); + ret = srg_led_i2c_write(sysled_ctrl, sysled->index + 4, value); + if (!ret) { + sysled_ctrl->control[sysled->index] = !!value; + ret = srg_led_control_sync(sysled_ctrl); + } + mutex_unlock(&sysled_ctrl->lock); + return ret; +} + +static int +srg_led_init_led(struct srg_led_ctrl *sysled_ctrl, struct device_node *np) +{ + struct led_init_data init_data = {}; + struct led_classdev *led_cdev; + struct srg_led *sysled; + int index, ret; + + if (!np) + return -ENOENT; + + ret = of_property_read_u32(np, "reg", &index); + if (ret) { + dev_err(&sysled_ctrl->client->dev, + "srg_led_init_led: no reg defined in np!\n"); + return ret; + } + + if (index < 1 || index > 4) + return -EINVAL; + + sysled = &sysled_ctrl->channel[index - 1]; + led_cdev = &sysled->led; + + sysled->index = index; + sysled->ctrl = sysled_ctrl; + + init_data.fwnode = of_fwnode_handle(np); + + led_cdev->name = of_get_property(np, "label", NULL) ? : np->name; + led_cdev->brightness = LED_OFF; + led_cdev->max_brightness = LED_FULL; + led_cdev->brightness_set_blocking = srg_led_set_brightness; + led_cdev->blink_set = srg_led_set_pulse; + + srg_led_i2c_write(sysled_ctrl, index + 4, 0); + + ret = devm_led_classdev_register_ext(&sysled_ctrl->client->dev, + led_cdev, &init_data); + if (ret) { + dev_err(&sysled_ctrl->client->dev, + "srg_led_init_led: led register %s error ret %d!n", + led_cdev->name, ret); + return ret; + } + + return 0; +} + +static int +srg_led_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct device_node *np = client->dev.of_node, *child; + struct srg_led_ctrl *sysled_ctrl; + + sysled_ctrl = devm_kzalloc(&client->dev, sizeof(*sysled_ctrl), GFP_KERNEL); + if (!sysled_ctrl) + return -ENOMEM; + + sysled_ctrl->client = client; + + mutex_init(&sysled_ctrl->lock); + + i2c_set_clientdata(client, sysled_ctrl); + + for_each_child_of_node(np, child) { + if (srg_led_init_led(sysled_ctrl, child)) + continue; + + msleep(5); + } + + return srg_led_control_sync(sysled_ctrl);; +} + +static void srg_led_disable(struct i2c_client *client) +{ + struct srg_led_ctrl *sysled_ctrl = i2c_get_clientdata(client); + int i; + + for (i = 1; i < 10; i++) + srg_led_i2c_write(sysled_ctrl, i, 0); +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,16,0) +static void +#else +static int +#endif +srg_led_remove(struct i2c_client *client) +{ + struct srg_led_ctrl *sysled_ctrl = i2c_get_clientdata(client); + + srg_led_disable(client); + mutex_destroy(&sysled_ctrl->lock); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5,16,0) + return 0; +#endif +} + +static const struct i2c_device_id srg_led_id[] = { + { "srg-sysled", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, srg_led_id); + +static const struct of_device_id of_srg_led_match[] = { + { .compatible = "srg,sysled", }, + {}, +}; +MODULE_DEVICE_TABLE(of, of_srg_led_match); + +static struct i2c_driver srg_sysled_driver = { + .driver = { + .name = "srg-sysled", + .of_match_table = of_srg_led_match, + }, + .probe = srg_led_probe, + .remove = srg_led_remove, + .id_table = srg_led_id, +}; +module_i2c_driver(srg_sysled_driver); + +MODULE_DESCRIPTION("SmartRG system LED driver"); +MODULE_AUTHOR("Shen Loh "); +MODULE_AUTHOR("Daniel Golle "); +MODULE_LICENSE("GPL v2"); diff --git a/target/linux/mediatek/filogic/config-5.15 b/target/linux/mediatek/filogic/config-5.15 index 8c46b9ad2c..1499f79b9a 100644 --- a/target/linux/mediatek/filogic/config-5.15 +++ b/target/linux/mediatek/filogic/config-5.15 @@ -195,6 +195,7 @@ CONFIG_IRQ_TIME_ACCOUNTING=y CONFIG_IRQ_WORK=y CONFIG_JBD2=y CONFIG_JUMP_LABEL=y +CONFIG_LEDS_SMARTRG_LED=y CONFIG_LIBFDT=y CONFIG_LOCK_DEBUGGING_SUPPORT=y CONFIG_LOCK_SPIN_ON_OWNER=y diff --git a/target/linux/mediatek/filogic/config-6.1 b/target/linux/mediatek/filogic/config-6.1 index 0bd5e9cb70..9473967d00 100644 --- a/target/linux/mediatek/filogic/config-6.1 +++ b/target/linux/mediatek/filogic/config-6.1 @@ -212,6 +212,7 @@ CONFIG_IRQ_TIME_ACCOUNTING=y CONFIG_IRQ_WORK=y CONFIG_JBD2=y CONFIG_JUMP_LABEL=y +CONFIG_LEDS_SMARTRG_LED=y CONFIG_LIBFDT=y CONFIG_LOCK_DEBUGGING_SUPPORT=y CONFIG_LOCK_SPIN_ON_OWNER=y diff --git a/target/linux/mediatek/mt7622/config-5.15 b/target/linux/mediatek/mt7622/config-5.15 index 643eaa45b6..18f972e949 100644 --- a/target/linux/mediatek/mt7622/config-5.15 +++ b/target/linux/mediatek/mt7622/config-5.15 @@ -215,6 +215,7 @@ CONFIG_IRQ_TIME_ACCOUNTING=y CONFIG_IRQ_WORK=y CONFIG_JBD2=y CONFIG_JUMP_LABEL=y +# CONFIG_LEDS_SMARTRG_LED is not set CONFIG_LIBFDT=y CONFIG_LOCK_DEBUGGING_SUPPORT=y CONFIG_LOCK_SPIN_ON_OWNER=y diff --git a/target/linux/mediatek/mt7622/config-6.1 b/target/linux/mediatek/mt7622/config-6.1 index 7055e4d728..6272a3bac6 100644 --- a/target/linux/mediatek/mt7622/config-6.1 +++ b/target/linux/mediatek/mt7622/config-6.1 @@ -216,6 +216,7 @@ CONFIG_IRQ_TIME_ACCOUNTING=y CONFIG_IRQ_WORK=y CONFIG_JBD2=y CONFIG_JUMP_LABEL=y +# CONFIG_LEDS_SMARTRG_LED is not set CONFIG_LIBFDT=y CONFIG_LOCK_DEBUGGING_SUPPORT=y CONFIG_LOCK_SPIN_ON_OWNER=y diff --git a/target/linux/mediatek/mt7623/config-5.15 b/target/linux/mediatek/mt7623/config-5.15 index 039a904f19..2f12488327 100644 --- a/target/linux/mediatek/mt7623/config-5.15 +++ b/target/linux/mediatek/mt7623/config-5.15 @@ -297,6 +297,7 @@ CONFIG_KMAP_LOCAL_NON_LINEAR_PTE_ARRAY=y CONFIG_LCD_CLASS_DEVICE=y CONFIG_LCD_PLATFORM=y CONFIG_LEDS_MT6323=y +# CONFIG_LEDS_SMARTRG_LED is not set CONFIG_LIBFDT=y CONFIG_LOCK_DEBUGGING_SUPPORT=y CONFIG_LOCK_SPIN_ON_OWNER=y diff --git a/target/linux/mediatek/mt7623/config-6.1 b/target/linux/mediatek/mt7623/config-6.1 index b6a75ab80a..218016b200 100644 --- a/target/linux/mediatek/mt7623/config-6.1 +++ b/target/linux/mediatek/mt7623/config-6.1 @@ -317,6 +317,7 @@ CONFIG_LCD_CLASS_DEVICE=y CONFIG_LCD_PLATFORM=y CONFIG_LEDS_MT6323=y # CONFIG_LEDS_QCOM_LPG is not set +# CONFIG_LEDS_SMARTRG_LED is not set CONFIG_LIBFDT=y CONFIG_LOCK_DEBUGGING_SUPPORT=y CONFIG_LOCK_SPIN_ON_OWNER=y diff --git a/target/linux/mediatek/mt7629/config-5.15 b/target/linux/mediatek/mt7629/config-5.15 index 08089dde2a..8cddb04d66 100644 --- a/target/linux/mediatek/mt7629/config-5.15 +++ b/target/linux/mediatek/mt7629/config-5.15 @@ -149,6 +149,7 @@ CONFIG_IRQ_FORCED_THREADING=y CONFIG_IRQ_TIME_ACCOUNTING=y CONFIG_IRQ_WORK=y # CONFIG_LEDS_BRIGHTNESS_HW_CHANGED is not set +# CONFIG_LEDS_SMARTRG_LED is not set CONFIG_LIBFDT=y CONFIG_LOCK_DEBUGGING_SUPPORT=y CONFIG_LOCK_SPIN_ON_OWNER=y diff --git a/target/linux/mediatek/mt7629/config-6.1 b/target/linux/mediatek/mt7629/config-6.1 index c0c501e59e..ad79b5ba9e 100644 --- a/target/linux/mediatek/mt7629/config-6.1 +++ b/target/linux/mediatek/mt7629/config-6.1 @@ -165,6 +165,7 @@ CONFIG_IRQ_TIME_ACCOUNTING=y CONFIG_IRQ_WORK=y # CONFIG_LEDS_BRIGHTNESS_HW_CHANGED is not set CONFIG_LIBFDT=y +# CONFIG_LEDS_SMARTRG_LED is not set CONFIG_LOCK_DEBUGGING_SUPPORT=y CONFIG_LOCK_SPIN_ON_OWNER=y CONFIG_LZO_COMPRESS=y diff --git a/target/linux/mediatek/patches-5.15/950-smartrg-i2c-led-driver.patch b/target/linux/mediatek/patches-5.15/950-smartrg-i2c-led-driver.patch new file mode 100644 index 0000000000..9f7b4ef338 --- /dev/null +++ b/target/linux/mediatek/patches-5.15/950-smartrg-i2c-led-driver.patch @@ -0,0 +1,34 @@ +--- + drivers/leds/Kconfig | 10 ++++++++++ + drivers/leds/Makefile | 1 + + 2 files changed, 11 insertions(+) + +--- a/drivers/leds/Kconfig ++++ b/drivers/leds/Kconfig +@@ -877,6 +877,16 @@ source "drivers/leds/blink/Kconfig" + comment "Flash and Torch LED drivers" + source "drivers/leds/flash/Kconfig" + ++config LEDS_SMARTRG_LED ++ tristate "LED support for Adtran SmartRG" ++ depends on LEDS_CLASS && I2C && OF ++ help ++ This option enables support for the Adtran SmartRG platform ++ system LED driver. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called leds-smartrg-system. ++ + comment "LED Triggers" + source "drivers/leds/trigger/Kconfig" + +--- a/drivers/leds/Makefile ++++ b/drivers/leds/Makefile +@@ -77,6 +77,7 @@ obj-$(CONFIG_LEDS_PWM) += leds-pwm.o + obj-$(CONFIG_LEDS_REGULATOR) += leds-regulator.o + obj-$(CONFIG_LEDS_S3C24XX) += leds-s3c24xx.o + obj-$(CONFIG_LEDS_SC27XX_BLTC) += leds-sc27xx-bltc.o ++obj-$(CONFIG_LEDS_SMARTRG_LED) += leds-smartrg-system.o + obj-$(CONFIG_LEDS_SUNFIRE) += leds-sunfire.o + obj-$(CONFIG_LEDS_SYSCON) += leds-syscon.o + obj-$(CONFIG_LEDS_TCA6507) += leds-tca6507.o diff --git a/target/linux/mediatek/patches-6.1/950-smartrg-i2c-led-driver.patch b/target/linux/mediatek/patches-6.1/950-smartrg-i2c-led-driver.patch new file mode 100644 index 0000000000..a98cca19a5 --- /dev/null +++ b/target/linux/mediatek/patches-6.1/950-smartrg-i2c-led-driver.patch @@ -0,0 +1,34 @@ +--- + drivers/leds/Kconfig | 10 ++++++++++ + drivers/leds/Makefile | 1 + + 2 files changed, 11 insertions(+) + +--- a/drivers/leds/Kconfig ++++ b/drivers/leds/Kconfig +@@ -873,6 +873,16 @@ source "drivers/leds/flash/Kconfig" + comment "RGB LED drivers" + source "drivers/leds/rgb/Kconfig" + ++config LEDS_SMARTRG_LED ++ tristate "LED support for Adtran SmartRG" ++ depends on LEDS_CLASS && I2C && OF ++ help ++ This option enables support for the Adtran SmartRG platform ++ system LED driver. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called leds-smartrg-system. ++ + comment "LED Triggers" + source "drivers/leds/trigger/Kconfig" + +--- a/drivers/leds/Makefile ++++ b/drivers/leds/Makefile +@@ -76,6 +76,7 @@ obj-$(CONFIG_LEDS_PWM) += leds-pwm.o + obj-$(CONFIG_LEDS_REGULATOR) += leds-regulator.o + obj-$(CONFIG_LEDS_S3C24XX) += leds-s3c24xx.o + obj-$(CONFIG_LEDS_SC27XX_BLTC) += leds-sc27xx-bltc.o ++obj-$(CONFIG_LEDS_SMARTRG_LED) += leds-smartrg-system.o + obj-$(CONFIG_LEDS_SUNFIRE) += leds-sunfire.o + obj-$(CONFIG_LEDS_SYSCON) += leds-syscon.o + obj-$(CONFIG_LEDS_TCA6507) += leds-tca6507.o -- cgit v1.2.3