150a65498cd3d407dd6e29acb411a08d66d695a1
[openwrt/staging/jow.git] / target / linux / mvebu / patches-5.15 / 910-drivers-leds-wt61p803-puzzle-improvements.patch
1 --- a/drivers/leds/leds-iei-wt61p803-puzzle.c
2 +++ b/drivers/leds/leds-iei-wt61p803-puzzle.c
3 @@ -9,9 +9,13 @@
4 #include <linux/mfd/iei-wt61p803-puzzle.h>
5 #include <linux/mod_devicetable.h>
6 #include <linux/module.h>
7 +#include <linux/of.h>
8 #include <linux/platform_device.h>
9 #include <linux/property.h>
10 #include <linux/slab.h>
11 +#include <linux/workqueue.h>
12 +
13 +#define IEI_LEDS_MAX 4
14
15 enum iei_wt61p803_puzzle_led_state {
16 IEI_LED_OFF = 0x30,
17 @@ -33,7 +37,11 @@ struct iei_wt61p803_puzzle_led {
18 struct iei_wt61p803_puzzle *mcu;
19 unsigned char response_buffer[IEI_WT61P803_PUZZLE_BUF_SIZE];
20 struct mutex lock; /* mutex to protect led_power_state */
21 + struct work_struct work;
22 int led_power_state;
23 + int id;
24 + u8 blinking;
25 + bool active_low;
26 };
27
28 static inline struct iei_wt61p803_puzzle_led *cdev_to_iei_wt61p803_puzzle_led
29 @@ -51,10 +59,18 @@ static int iei_wt61p803_puzzle_led_brigh
30 size_t reply_size;
31 int ret;
32
33 + if (priv->blinking) {
34 + if (brightness == LED_OFF)
35 + priv->blinking = 0;
36 + else
37 + return 0;
38 + }
39 +
40 led_power_cmd[0] = IEI_WT61P803_PUZZLE_CMD_HEADER_START;
41 led_power_cmd[1] = IEI_WT61P803_PUZZLE_CMD_LED;
42 - led_power_cmd[2] = IEI_WT61P803_PUZZLE_CMD_LED_POWER;
43 - led_power_cmd[3] = brightness == LED_OFF ? IEI_LED_OFF : IEI_LED_ON;
44 + led_power_cmd[2] = IEI_WT61P803_PUZZLE_CMD_LED_SET(priv->id);
45 + led_power_cmd[3] = ((brightness == LED_OFF) ^ priv->active_low) ?
46 + IEI_LED_OFF : priv->blinking?priv->blinking:IEI_LED_ON;
47
48 ret = iei_wt61p803_puzzle_write_command(priv->mcu, led_power_cmd,
49 sizeof(led_power_cmd),
50 @@ -90,39 +106,166 @@ static enum led_brightness iei_wt61p803_
51 return led_state;
52 }
53
54 +static void iei_wt61p803_puzzle_led_apply_blink(struct work_struct *work)
55 +{
56 + struct iei_wt61p803_puzzle_led *priv = container_of(work, struct iei_wt61p803_puzzle_led, work);
57 + unsigned char led_blink_cmd[5] = {};
58 + unsigned char resp_buf[IEI_WT61P803_PUZZLE_BUF_SIZE];
59 + size_t reply_size;
60 +
61 + led_blink_cmd[0] = IEI_WT61P803_PUZZLE_CMD_HEADER_START;
62 + led_blink_cmd[1] = IEI_WT61P803_PUZZLE_CMD_LED;
63 + led_blink_cmd[2] = IEI_WT61P803_PUZZLE_CMD_LED_SET(priv->id);
64 + led_blink_cmd[3] = priv->blinking;
65 +
66 + iei_wt61p803_puzzle_write_command(priv->mcu, led_blink_cmd,
67 + sizeof(led_blink_cmd),
68 + resp_buf,
69 + &reply_size);
70 +
71 + return;
72 +}
73 +
74 +static int iei_wt61p803_puzzle_led_set_blink(struct led_classdev *cdev,
75 + unsigned long *delay_on,
76 + unsigned long *delay_off)
77 +{
78 + struct iei_wt61p803_puzzle_led *priv = cdev_to_iei_wt61p803_puzzle_led(cdev);
79 + u8 blink_mode = 0;
80 + int ret = 0;
81 +
82 + /* set defaults */
83 + if (!*delay_on && !*delay_off) {
84 + *delay_on = 500;
85 + *delay_off = 500;
86 + }
87 +
88 + /* minimum delay for soft-driven blinking is 100ms to keep load low */
89 + if (*delay_on < 100)
90 + *delay_on = 100;
91 +
92 + if (*delay_off < 100)
93 + *delay_off = 100;
94 +
95 + /* offload blinking to hardware, if possible */
96 + if (*delay_on != *delay_off) {
97 + ret = -EINVAL;
98 + } else if (*delay_on == 100) {
99 + blink_mode = IEI_LED_BLINK_5HZ;
100 + *delay_on = 100;
101 + *delay_off = 100;
102 + } else if (*delay_on <= 500) {
103 + blink_mode = IEI_LED_BLINK_1HZ;
104 + *delay_on = 500;
105 + *delay_off = 500;
106 + } else {
107 + ret = -EINVAL;
108 + }
109 +
110 + mutex_lock(&priv->lock);
111 + priv->blinking = blink_mode;
112 + mutex_unlock(&priv->lock);
113 +
114 + if (blink_mode)
115 + schedule_work(&priv->work);
116 +
117 + return ret;
118 +}
119 +
120 +
121 +static int iei_wt61p803_puzzle_led_set_dt_default(struct led_classdev *cdev,
122 + struct device_node *np)
123 +{
124 + const char *state;
125 + int ret = 0;
126 +
127 + state = of_get_property(np, "default-state", NULL);
128 + if (state) {
129 + if (!strcmp(state, "on")) {
130 + ret =
131 + iei_wt61p803_puzzle_led_brightness_set_blocking(
132 + cdev, cdev->max_brightness);
133 + } else {
134 + ret = iei_wt61p803_puzzle_led_brightness_set_blocking(
135 + cdev, LED_OFF);
136 + }
137 + }
138 +
139 + return ret;
140 +}
141 +
142 static int iei_wt61p803_puzzle_led_probe(struct platform_device *pdev)
143 {
144 struct device *dev = &pdev->dev;
145 + struct device_node *np = dev_of_node(dev);
146 + struct device_node *child;
147 struct iei_wt61p803_puzzle *mcu = dev_get_drvdata(dev->parent);
148 struct iei_wt61p803_puzzle_led *priv;
149 - struct led_init_data init_data = {};
150 - struct fwnode_handle *child;
151 int ret;
152 + u32 reg;
153
154 - if (device_get_child_node_count(dev) != 1)
155 + if (device_get_child_node_count(dev) > IEI_LEDS_MAX)
156 return -EINVAL;
157
158 - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
159 - if (!priv)
160 - return -ENOMEM;
161 -
162 - priv->mcu = mcu;
163 - priv->led_power_state = 1;
164 - mutex_init(&priv->lock);
165 - dev_set_drvdata(dev, priv);
166 -
167 - child = device_get_next_child_node(dev, NULL);
168 - init_data.fwnode = child;
169 -
170 - priv->cdev.brightness_set_blocking = iei_wt61p803_puzzle_led_brightness_set_blocking;
171 - priv->cdev.brightness_get = iei_wt61p803_puzzle_led_brightness_get;
172 - priv->cdev.max_brightness = 1;
173 + for_each_available_child_of_node(np, child) {
174 + struct led_init_data init_data = {};
175
176 - ret = devm_led_classdev_register_ext(dev, &priv->cdev, &init_data);
177 - if (ret)
178 - dev_err(dev, "Could not register LED\n");
179 + ret = of_property_read_u32(child, "reg", &reg);
180 + if (ret) {
181 + dev_err(dev, "Failed to read led 'reg' property\n");
182 + goto put_child_node;
183 + }
184 +
185 + if (reg > IEI_LEDS_MAX) {
186 + dev_err(dev, "Invalid led reg %u\n", reg);
187 + ret = -EINVAL;
188 + goto put_child_node;
189 + }
190 +
191 + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
192 + if (!priv) {
193 + ret = -ENOMEM;
194 + goto put_child_node;
195 + }
196 +
197 + mutex_init(&priv->lock);
198 +
199 + dev_set_drvdata(dev, priv);
200 +
201 + if (of_property_read_bool(child, "active-low"))
202 + priv->active_low = true;
203 +
204 + priv->mcu = mcu;
205 + priv->id = reg;
206 + priv->led_power_state = 1;
207 + priv->blinking = 0;
208 + init_data.fwnode = of_fwnode_handle(child);
209 +
210 + priv->cdev.brightness_set_blocking = iei_wt61p803_puzzle_led_brightness_set_blocking;
211 + priv->cdev.brightness_get = iei_wt61p803_puzzle_led_brightness_get;
212 + priv->cdev.blink_set = iei_wt61p803_puzzle_led_set_blink;
213 +
214 + priv->cdev.max_brightness = 1;
215 +
216 + INIT_WORK(&priv->work, iei_wt61p803_puzzle_led_apply_blink);
217 +
218 + ret = iei_wt61p803_puzzle_led_set_dt_default(&priv->cdev, child);
219 + if (ret) {
220 + dev_err(dev, "Could apply default from DT\n");
221 + goto put_child_node;
222 + }
223 +
224 + ret = devm_led_classdev_register_ext(dev, &priv->cdev, &init_data);
225 + if (ret) {
226 + dev_err(dev, "Could not register LED\n");
227 + goto put_child_node;
228 + }
229 + }
230 +
231 + return ret;
232
233 - fwnode_handle_put(child);
234 +put_child_node:
235 + of_node_put(child);
236 return ret;
237 }
238
239 --- a/include/linux/mfd/iei-wt61p803-puzzle.h
240 +++ b/include/linux/mfd/iei-wt61p803-puzzle.h
241 @@ -36,7 +36,7 @@
242 #define IEI_WT61P803_PUZZLE_CMD_FUNCTION_OTHER_POWER_LOSS 0x41 /* A */
243
244 #define IEI_WT61P803_PUZZLE_CMD_LED 0x52 /* R */
245 -#define IEI_WT61P803_PUZZLE_CMD_LED_POWER 0x31 /* 1 */
246 +#define IEI_WT61P803_PUZZLE_CMD_LED_SET(n) (0x30 | (n))
247
248 #define IEI_WT61P803_PUZZLE_CMD_TEMP 0x54 /* T */
249 #define IEI_WT61P803_PUZZLE_CMD_TEMP_ALL 0x41 /* A */
250 --- a/drivers/mfd/iei-wt61p803-puzzle.c
251 +++ b/drivers/mfd/iei-wt61p803-puzzle.c
252 @@ -176,6 +176,9 @@ static int iei_wt61p803_puzzle_recv_buf(
253 struct iei_wt61p803_puzzle *mcu = serdev_device_get_drvdata(serdev);
254 int ret;
255
256 + print_hex_dump_debug("puzzle-mcu rx: ", DUMP_PREFIX_NONE,
257 + 16, 1, data, size, false);
258 +
259 ret = iei_wt61p803_puzzle_process_resp(mcu, data, size);
260 /* Return the number of processed bytes if function returns error,
261 * discard the remaining incoming data, since the frame this data
262 @@ -246,6 +249,9 @@ int iei_wt61p803_puzzle_write_command(st
263
264 cmd[size - 1] = iei_wt61p803_puzzle_checksum(cmd, size - 1);
265
266 + print_hex_dump_debug("puzzle-mcu tx: ", DUMP_PREFIX_NONE,
267 + 16, 1, cmd, size, false);
268 +
269 /* Initialize reply struct */
270 reinit_completion(&mcu->reply->received);
271 mcu->reply->size = 0;