aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/mvebu/patches-5.15/910-drivers-leds-wt61p803-puzzle-improvements.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/mvebu/patches-5.15/910-drivers-leds-wt61p803-puzzle-improvements.patch')
-rw-r--r--target/linux/mvebu/patches-5.15/910-drivers-leds-wt61p803-puzzle-improvements.patch271
1 files changed, 271 insertions, 0 deletions
diff --git a/target/linux/mvebu/patches-5.15/910-drivers-leds-wt61p803-puzzle-improvements.patch b/target/linux/mvebu/patches-5.15/910-drivers-leds-wt61p803-puzzle-improvements.patch
new file mode 100644
index 0000000000..150a65498c
--- /dev/null
+++ b/target/linux/mvebu/patches-5.15/910-drivers-leds-wt61p803-puzzle-improvements.patch
@@ -0,0 +1,271 @@
+--- a/drivers/leds/leds-iei-wt61p803-puzzle.c
++++ b/drivers/leds/leds-iei-wt61p803-puzzle.c
+@@ -9,9 +9,13 @@
+ #include <linux/mfd/iei-wt61p803-puzzle.h>
+ #include <linux/mod_devicetable.h>
+ #include <linux/module.h>
++#include <linux/of.h>
+ #include <linux/platform_device.h>
+ #include <linux/property.h>
+ #include <linux/slab.h>
++#include <linux/workqueue.h>
++
++#define IEI_LEDS_MAX 4
+
+ enum iei_wt61p803_puzzle_led_state {
+ IEI_LED_OFF = 0x30,
+@@ -33,7 +37,11 @@ struct iei_wt61p803_puzzle_led {
+ struct iei_wt61p803_puzzle *mcu;
+ unsigned char response_buffer[IEI_WT61P803_PUZZLE_BUF_SIZE];
+ struct mutex lock; /* mutex to protect led_power_state */
++ struct work_struct work;
+ int led_power_state;
++ int id;
++ u8 blinking;
++ bool active_low;
+ };
+
+ static inline struct iei_wt61p803_puzzle_led *cdev_to_iei_wt61p803_puzzle_led
+@@ -51,10 +59,18 @@ static int iei_wt61p803_puzzle_led_brigh
+ size_t reply_size;
+ int ret;
+
++ if (priv->blinking) {
++ if (brightness == LED_OFF)
++ priv->blinking = 0;
++ else
++ return 0;
++ }
++
+ led_power_cmd[0] = IEI_WT61P803_PUZZLE_CMD_HEADER_START;
+ led_power_cmd[1] = IEI_WT61P803_PUZZLE_CMD_LED;
+- led_power_cmd[2] = IEI_WT61P803_PUZZLE_CMD_LED_POWER;
+- led_power_cmd[3] = brightness == LED_OFF ? IEI_LED_OFF : IEI_LED_ON;
++ led_power_cmd[2] = IEI_WT61P803_PUZZLE_CMD_LED_SET(priv->id);
++ led_power_cmd[3] = ((brightness == LED_OFF) ^ priv->active_low) ?
++ IEI_LED_OFF : priv->blinking?priv->blinking:IEI_LED_ON;
+
+ ret = iei_wt61p803_puzzle_write_command(priv->mcu, led_power_cmd,
+ sizeof(led_power_cmd),
+@@ -90,39 +106,166 @@ static enum led_brightness iei_wt61p803_
+ return led_state;
+ }
+
++static void iei_wt61p803_puzzle_led_apply_blink(struct work_struct *work)
++{
++ struct iei_wt61p803_puzzle_led *priv = container_of(work, struct iei_wt61p803_puzzle_led, work);
++ unsigned char led_blink_cmd[5] = {};
++ unsigned char resp_buf[IEI_WT61P803_PUZZLE_BUF_SIZE];
++ size_t reply_size;
++
++ led_blink_cmd[0] = IEI_WT61P803_PUZZLE_CMD_HEADER_START;
++ led_blink_cmd[1] = IEI_WT61P803_PUZZLE_CMD_LED;
++ led_blink_cmd[2] = IEI_WT61P803_PUZZLE_CMD_LED_SET(priv->id);
++ led_blink_cmd[3] = priv->blinking;
++
++ iei_wt61p803_puzzle_write_command(priv->mcu, led_blink_cmd,
++ sizeof(led_blink_cmd),
++ resp_buf,
++ &reply_size);
++
++ return;
++}
++
++static int iei_wt61p803_puzzle_led_set_blink(struct led_classdev *cdev,
++ unsigned long *delay_on,
++ unsigned long *delay_off)
++{
++ struct iei_wt61p803_puzzle_led *priv = cdev_to_iei_wt61p803_puzzle_led(cdev);
++ u8 blink_mode = 0;
++ int ret = 0;
++
++ /* set defaults */
++ if (!*delay_on && !*delay_off) {
++ *delay_on = 500;
++ *delay_off = 500;
++ }
++
++ /* minimum delay for soft-driven blinking is 100ms to keep load low */
++ if (*delay_on < 100)
++ *delay_on = 100;
++
++ if (*delay_off < 100)
++ *delay_off = 100;
++
++ /* offload blinking to hardware, if possible */
++ if (*delay_on != *delay_off) {
++ ret = -EINVAL;
++ } else if (*delay_on == 100) {
++ blink_mode = IEI_LED_BLINK_5HZ;
++ *delay_on = 100;
++ *delay_off = 100;
++ } else if (*delay_on <= 500) {
++ blink_mode = IEI_LED_BLINK_1HZ;
++ *delay_on = 500;
++ *delay_off = 500;
++ } else {
++ ret = -EINVAL;
++ }
++
++ mutex_lock(&priv->lock);
++ priv->blinking = blink_mode;
++ mutex_unlock(&priv->lock);
++
++ if (blink_mode)
++ schedule_work(&priv->work);
++
++ return ret;
++}
++
++
++static int iei_wt61p803_puzzle_led_set_dt_default(struct led_classdev *cdev,
++ struct device_node *np)
++{
++ const char *state;
++ int ret = 0;
++
++ state = of_get_property(np, "default-state", NULL);
++ if (state) {
++ if (!strcmp(state, "on")) {
++ ret =
++ iei_wt61p803_puzzle_led_brightness_set_blocking(
++ cdev, cdev->max_brightness);
++ } else {
++ ret = iei_wt61p803_puzzle_led_brightness_set_blocking(
++ cdev, LED_OFF);
++ }
++ }
++
++ return ret;
++}
++
+ static int iei_wt61p803_puzzle_led_probe(struct platform_device *pdev)
+ {
+ struct device *dev = &pdev->dev;
++ struct device_node *np = dev_of_node(dev);
++ struct device_node *child;
+ struct iei_wt61p803_puzzle *mcu = dev_get_drvdata(dev->parent);
+ struct iei_wt61p803_puzzle_led *priv;
+- struct led_init_data init_data = {};
+- struct fwnode_handle *child;
+ int ret;
++ u32 reg;
+
+- if (device_get_child_node_count(dev) != 1)
++ if (device_get_child_node_count(dev) > IEI_LEDS_MAX)
+ return -EINVAL;
+
+- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+- if (!priv)
+- return -ENOMEM;
+-
+- priv->mcu = mcu;
+- priv->led_power_state = 1;
+- mutex_init(&priv->lock);
+- dev_set_drvdata(dev, priv);
+-
+- child = device_get_next_child_node(dev, NULL);
+- init_data.fwnode = child;
+-
+- priv->cdev.brightness_set_blocking = iei_wt61p803_puzzle_led_brightness_set_blocking;
+- priv->cdev.brightness_get = iei_wt61p803_puzzle_led_brightness_get;
+- priv->cdev.max_brightness = 1;
++ for_each_available_child_of_node(np, child) {
++ struct led_init_data init_data = {};
+
+- ret = devm_led_classdev_register_ext(dev, &priv->cdev, &init_data);
+- if (ret)
+- dev_err(dev, "Could not register LED\n");
++ ret = of_property_read_u32(child, "reg", &reg);
++ if (ret) {
++ dev_err(dev, "Failed to read led 'reg' property\n");
++ goto put_child_node;
++ }
++
++ if (reg > IEI_LEDS_MAX) {
++ dev_err(dev, "Invalid led reg %u\n", reg);
++ ret = -EINVAL;
++ goto put_child_node;
++ }
++
++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
++ if (!priv) {
++ ret = -ENOMEM;
++ goto put_child_node;
++ }
++
++ mutex_init(&priv->lock);
++
++ dev_set_drvdata(dev, priv);
++
++ if (of_property_read_bool(child, "active-low"))
++ priv->active_low = true;
++
++ priv->mcu = mcu;
++ priv->id = reg;
++ priv->led_power_state = 1;
++ priv->blinking = 0;
++ init_data.fwnode = of_fwnode_handle(child);
++
++ priv->cdev.brightness_set_blocking = iei_wt61p803_puzzle_led_brightness_set_blocking;
++ priv->cdev.brightness_get = iei_wt61p803_puzzle_led_brightness_get;
++ priv->cdev.blink_set = iei_wt61p803_puzzle_led_set_blink;
++
++ priv->cdev.max_brightness = 1;
++
++ INIT_WORK(&priv->work, iei_wt61p803_puzzle_led_apply_blink);
++
++ ret = iei_wt61p803_puzzle_led_set_dt_default(&priv->cdev, child);
++ if (ret) {
++ dev_err(dev, "Could apply default from DT\n");
++ goto put_child_node;
++ }
++
++ ret = devm_led_classdev_register_ext(dev, &priv->cdev, &init_data);
++ if (ret) {
++ dev_err(dev, "Could not register LED\n");
++ goto put_child_node;
++ }
++ }
++
++ return ret;
+
+- fwnode_handle_put(child);
++put_child_node:
++ of_node_put(child);
+ return ret;
+ }
+
+--- a/include/linux/mfd/iei-wt61p803-puzzle.h
++++ b/include/linux/mfd/iei-wt61p803-puzzle.h
+@@ -36,7 +36,7 @@
+ #define IEI_WT61P803_PUZZLE_CMD_FUNCTION_OTHER_POWER_LOSS 0x41 /* A */
+
+ #define IEI_WT61P803_PUZZLE_CMD_LED 0x52 /* R */
+-#define IEI_WT61P803_PUZZLE_CMD_LED_POWER 0x31 /* 1 */
++#define IEI_WT61P803_PUZZLE_CMD_LED_SET(n) (0x30 | (n))
+
+ #define IEI_WT61P803_PUZZLE_CMD_TEMP 0x54 /* T */
+ #define IEI_WT61P803_PUZZLE_CMD_TEMP_ALL 0x41 /* A */
+--- a/drivers/mfd/iei-wt61p803-puzzle.c
++++ b/drivers/mfd/iei-wt61p803-puzzle.c
+@@ -176,6 +176,9 @@ static int iei_wt61p803_puzzle_recv_buf(
+ struct iei_wt61p803_puzzle *mcu = serdev_device_get_drvdata(serdev);
+ int ret;
+
++ print_hex_dump_debug("puzzle-mcu rx: ", DUMP_PREFIX_NONE,
++ 16, 1, data, size, false);
++
+ ret = iei_wt61p803_puzzle_process_resp(mcu, data, size);
+ /* Return the number of processed bytes if function returns error,
+ * discard the remaining incoming data, since the frame this data
+@@ -246,6 +249,9 @@ int iei_wt61p803_puzzle_write_command(st
+
+ cmd[size - 1] = iei_wt61p803_puzzle_checksum(cmd, size - 1);
+
++ print_hex_dump_debug("puzzle-mcu tx: ", DUMP_PREFIX_NONE,
++ 16, 1, cmd, size, false);
++
+ /* Initialize reply struct */
+ reinit_completion(&mcu->reply->received);
+ mcu->reply->size = 0;