Drop upstreamed patches: 0001-v6.16-pinctrl-armada-37xx-use-correct-OUTPUT_VAL-register-.patch 0002-v6.16-pinctrl-armada-37xx-set-GPIO-output-value-before-set.patch 820-v6.11-01-dt-bindings-firmware-add-cznic-turris-omnia-mcu-bind.patch 820-v6.11-02-platform-cznic-Add-preliminary-support-for-Turris-Om.patch 820-v6.11-03-platform-cznic-turris-omnia-mcu-Add-support-for-MCU-.patch 820-v6.11-04-platform-cznic-turris-omnia-mcu-Add-support-for-powe.patch 820-v6.11-05-platform-cznic-turris-omnia-mcu-Add-support-for-MCU-.patch 820-v6.11-06-platform-cznic-turris-omnia-mcu-Add-support-for-MCU-.patch 820-v6.11-07-ARM-dts-turris-omnia-Add-MCU-system-controller-node.patch 820-v6.11-08-ARM-dts-turris-omnia-Add-GPIO-key-node-for-front-but.patch 820-v6.11-09-platform-cznic-turris-omnia-mcu-Depend-on-OF.patch 820-v6.11-10-platform-cznic-turris-omnia-mcu-Depend-on-WATCHDOG.patch 820-v6.11-11-platform-cznic-turris-omnia-mcu-fix-Kconfig-dependen.patch Manually refreshed: 310-linksys-use-eth0-as-cpu-port.patch 350-drivers-thermal-step_wise-add-support-for-hysteresis.patch 901-dt-bindings-Add-IEI-vendor-prefix-and-IEI-WT61P803-P.patch 902-drivers-mfd-Add-a-driver-for-IEI-WT61P803-PUZZLE-MCU.patch 910-drivers-leds-wt61p803-puzzle-improvements.patch All other patches automatically refreshed. Signed-off-by: Stefan Kalscheuer <stefan@stklcode.de> Link: https://github.com/openwrt/openwrt/pull/18975 Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
273 lines
7.7 KiB
Diff
273 lines
7.7 KiB
Diff
--- 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,168 @@ 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", ®);
|
|
+ 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;
|
|
+ }
|
|
+
|
|
+ ret = devm_mutex_init(dev, &priv->lock);
|
|
+ if (ret)
|
|
+ goto put_child_node;
|
|
+
|
|
+ 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 size_t iei_wt61p803_puzzle_recv_b
|
|
struct iei_wt61p803_puzzle *mcu = serdev_device_get_drvdata(serdev);
|
|
size_t 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;
|