mediatek: import driver for Adtran SmartRG RGBW LED
authorDaniel Golle <daniel@makrotopia.org>
Fri, 17 Nov 2023 02:01:34 +0000 (02:01 +0000)
committerDaniel Golle <daniel@makrotopia.org>
Sun, 10 Dec 2023 06:22:51 +0000 (06:22 +0000)
Import driver for I2C-connected HolTek MCU controlling the RGBW LED
found in Adtran SmartRG devices.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
target/linux/mediatek/files/drivers/leds/leds-smartrg-system.c [new file with mode: 0644]
target/linux/mediatek/filogic/config-5.15
target/linux/mediatek/filogic/config-6.1
target/linux/mediatek/mt7622/config-5.15
target/linux/mediatek/mt7622/config-6.1
target/linux/mediatek/mt7623/config-5.15
target/linux/mediatek/mt7623/config-6.1
target/linux/mediatek/mt7629/config-5.15
target/linux/mediatek/mt7629/config-6.1
target/linux/mediatek/patches-5.15/950-smartrg-i2c-led-driver.patch [new file with mode: 0644]
target/linux/mediatek/patches-6.1/950-smartrg-i2c-led-driver.patch [new file with mode: 0644]

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 (file)
index 0000000..1679555
--- /dev/null
@@ -0,0 +1,239 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/version.h>
+
+/**
+ * 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 <shen.loh@adtran.com>");
+MODULE_AUTHOR("Daniel Golle <daniel@makrotopia.org>");
+MODULE_LICENSE("GPL v2");
index 8c46b9ad2c5c60e5557f762ae0fbeec4b3ab47b6..1499f79b9a11555eca0ad988bc017760d22668ea 100644 (file)
@@ -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
index 0bd5e9cb708f09baa882fcf11e846fbefd00267b..9473967d00fca9a8aa032d7088615c2b1785e038 100644 (file)
@@ -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
index 643eaa45b6d23bfedd44a3b44ae00ca78128589a..18f972e949f09c6ae17cba60b95cba6dff9bd315 100644 (file)
@@ -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
index 7055e4d7285b3f5d0c12bdaa880f8b37d78ab0ca..6272a3bac66319e898a8dd9d9bd476518a3653ec 100644 (file)
@@ -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
index 039a904f197000faee2fca02e0b882f2f0f8a582..2f12488327e8ad785f60aa634e74814cab567e53 100644 (file)
@@ -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
index b6a75ab80a63e8f35ea7ad995495adc5daf712be..218016b200c3dfdc7ee4b5c0a2f1f661e71e09f5 100644 (file)
@@ -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
index 08089dde2a321126917ad5ba7c699025b814a566..8cddb04d6677b7fa425f400f73a4b0abb1b66309 100644 (file)
@@ -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
index c0c501e59e149c4b58fa5494a5540dad1c4cd5cb..ad79b5ba9ecc7453e10c856a573f06222e7b9fb6 100644 (file)
@@ -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 (file)
index 0000000..9f7b4ef
--- /dev/null
@@ -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 (file)
index 0000000..a98cca1
--- /dev/null
@@ -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