540914298993d0ed2bbca7c41bc6058473eefe77
[openwrt/staging/jow.git] / target / linux / bmips / files / drivers / leds / leds-sercomm-msp430.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Driver for Sercomm MSP430G2513 LEDs.
4 *
5 * Copyright 2023 Álvaro Fernández Rojas <noltari@gmail.com>
6 */
7
8 #include <linux/delay.h>
9 #include <linux/leds.h>
10 #include <linux/module.h>
11 #include <linux/of_device.h>
12 #include <linux/spi/spi.h>
13 #include "leds.h"
14
15 /*
16 * MSP430G2513 SPI protocol description:
17 * +----+----+----+----+----+----+
18 * | b1 | b2 | b3 | b4 | b5 | b6 |
19 * +----+----+----+----+----+----+
20 * 6 bytes TX & RX per transaction.
21 *
22 * LEDs:
23 * MSP430G2513 can control up to 9 LEDs.
24 * b1: LED ID [1,9]
25 * b2: LED function
26 * b3-b6: LED function parameters
27 *
28 * LED functions:
29 * [0] Off
30 * [1] On
31 * [2] Flash
32 * - b4: delay (x 6ms)
33 * - b5: repeat (0 = infinite)
34 * [3] Pulse
35 * - b3: delay (x 6ms)
36 * - b4: blink while pulsing? (unknown)
37 * - b5: repeat (0 = infinite)
38 * [4] Pulse On
39 * - b3: delay (x 6ms)
40 * - b4: blink while pulsing? (unknown)
41 * - b5: repeat (0 = infinite)
42 * [5] Pulse Off
43 * - b3: delay (x 6ms)
44 * - b4: blink while pulsing? (unknown)
45 * - b5: repeat (0 = infinite)
46 * [6] Level
47 * - b3: brightness [0,4]
48 *
49 * MCU Commands (b1 = 0x55):
50 * [0x0a] FW upgrade data
51 * - b3: Data size (usually 0x40), which is appended to TX & RX.
52 * [0x31] Get MCU version? (unknown)
53 * [0x68] Get MCU work mode
54 * [0xa5] Start FW upgrade
55 * [0xf0] End FW upgrade
56 */
57
58 #define MSP430_CMD_BYTES 6
59 #define MSP430_CMD_MCU 0x55
60 #define MSP430_MCU_WM 0x68
61
62 #define MSP430_LED_MIN_ID 1
63 #define MSP430_LED_MAX_ID 9
64
65 #define MSP430_LED_OFF 0
66 #define MSP430_LED_ON 1
67 #define MSP430_LED_FLASH 2
68 #define MSP430_LED_PULSE 3
69 #define MSP430_LED_PULSE_ON 4
70 #define MSP430_LED_PULSE_OFF 5
71 #define MSP430_LED_LEVEL 6
72
73 #define MSP430_LED_BLINK_DEF 500
74 #define MSP430_LED_BLINK_MASK 0xff
75 #define MSP430_LED_BLINK_MS 6
76 #define MSP430_LED_BLINK_MAX (MSP430_LED_BLINK_MS * \
77 MSP430_LED_BLINK_MASK)
78
79 #define MSP430_LED_BRIGHTNESS_MAX 5
80 #define MSP430_LED_REPEAT_MAX 0xff
81
82 /**
83 * struct msp430_led - state container for Sercomm MSP430 based LEDs
84 * @cdev: LED class device for this LED
85 * @spi: spi resource
86 * @id: LED ID
87 */
88 struct msp430_led {
89 struct led_classdev cdev;
90 struct spi_device *spi;
91 u8 id;
92 };
93
94 static inline int msp430_cmd(struct spi_device *spi, u8 tx[MSP430_CMD_BYTES],
95 u8 rx[MSP430_CMD_BYTES])
96 {
97 struct device *dev = &spi->dev;
98 int rc;
99
100 memset(rx, 0, MSP430_CMD_BYTES);
101
102 rc = spi_write_then_read(spi, tx, MSP430_CMD_BYTES,
103 rx, MSP430_CMD_BYTES);
104 if (rc)
105 dev_err(dev, "spi error\n");
106
107 dev_dbg(dev, "msp430_cmd: [%02x %02x %02x %02x %02x %02x]"
108 " -> [%02x %02x %02x %02x %02x %02x]",
109 tx[0], tx[1], tx[2], tx[3], tx[4], tx[5],
110 rx[0], rx[1], rx[2], rx[3], rx[4], rx[5]);
111
112 return rc;
113 }
114
115 static unsigned long msp430_blink_delay(unsigned long delay)
116 {
117 unsigned long msp430_delay;
118
119 msp430_delay = delay + MSP430_LED_BLINK_MS / 2;
120 msp430_delay = msp430_delay / MSP430_LED_BLINK_MS;
121 if (msp430_delay == 0)
122 msp430_delay = 1;
123
124 return msp430_delay;
125 }
126
127 static int msp430_blink_set(struct led_classdev *led_cdev,
128 unsigned long *delay_on,
129 unsigned long *delay_off)
130 {
131 struct msp430_led *led =
132 container_of(led_cdev, struct msp430_led, cdev);
133 u8 tx[MSP430_CMD_BYTES] = {led->id, MSP430_LED_FLASH, 0, 0, 0, 0};
134 u8 rx[MSP430_CMD_BYTES];
135 unsigned long delay;
136
137 if (!*delay_on)
138 *delay_on = MSP430_LED_BLINK_DEF;
139 if (!*delay_off)
140 *delay_off = MSP430_LED_BLINK_DEF;
141
142 delay = msp430_blink_delay(*delay_on);
143 if (delay != msp430_blink_delay(*delay_off)) {
144 dev_dbg(led_cdev->dev,
145 "fallback to soft blinking (delay_on != delay_off)\n");
146 return -EINVAL;
147 }
148
149 if (delay > MSP430_LED_BLINK_MASK) {
150 dev_dbg(led_cdev->dev,
151 "fallback to soft blinking (delay > %ums)\n",
152 MSP430_LED_BLINK_MAX);
153 return -EINVAL;
154 }
155
156 tx[3] = delay;
157
158 return msp430_cmd(led->spi, tx, rx);
159 }
160
161 static int msp430_brightness_set(struct led_classdev *led_cdev,
162 enum led_brightness brightness)
163 {
164 struct msp430_led *led =
165 container_of(led_cdev, struct msp430_led, cdev);
166 u8 tx[MSP430_CMD_BYTES] = {led->id, 0, 0, 0, 0, 0};
167 u8 rx[MSP430_CMD_BYTES];
168 u8 val = (u8) brightness;
169
170 switch (val)
171 {
172 case LED_OFF:
173 tx[1] = MSP430_LED_OFF;
174 break;
175 case MSP430_LED_BRIGHTNESS_MAX:
176 tx[1] = MSP430_LED_ON;
177 break;
178 default:
179 tx[1] = MSP430_LED_LEVEL;
180 tx[2] = val - 1;
181 break;
182 }
183
184 return msp430_cmd(led->spi, tx, rx);
185 }
186
187 static int msp430_pattern_clear(struct led_classdev *ldev)
188 {
189 msp430_brightness_set(ldev, LED_OFF);
190
191 return 0;
192 }
193
194 static int msp430_pattern_set(struct led_classdev *led_cdev,
195 struct led_pattern *pattern,
196 u32 len, int repeat)
197 {
198 struct msp430_led *led =
199 container_of(led_cdev, struct msp430_led, cdev);
200 u8 tx[MSP430_CMD_BYTES] = {led->id, 0, 0, 0, 0, 0};
201 u8 rx[MSP430_CMD_BYTES];
202 unsigned long delay0;
203 unsigned long delay1;
204 int rc;
205
206 if (len != 2 ||
207 repeat > MSP430_LED_REPEAT_MAX ||
208 pattern[0].delta_t > MSP430_LED_BLINK_MAX ||
209 pattern[1].delta_t > MSP430_LED_BLINK_MAX)
210 return -EINVAL;
211
212 delay0 = msp430_blink_delay(pattern[0].delta_t);
213 delay1 = msp430_blink_delay(pattern[1].delta_t);
214
215 /* Pulse: <off> <delay> <max> <delay> */
216 if (delay0 == delay1 &&
217 pattern[0].brightness == LED_OFF &&
218 pattern[1].brightness == MSP430_LED_BRIGHTNESS_MAX)
219 {
220 tx[1] = MSP430_LED_PULSE;
221 tx[2] = delay0;
222 tx[4] = (u8) repeat;
223 }
224
225 /* Pulse On: <off> <delay> <max> <0ms> */
226 if (pattern[0].delta_t != 0 &&
227 pattern[1].delta_t == 0 &&
228 pattern[0].brightness == LED_OFF &&
229 pattern[1].brightness == MSP430_LED_BRIGHTNESS_MAX) {
230 tx[1] = MSP430_LED_PULSE_ON;
231 tx[2] = delay0;
232 tx[4] = (u8) repeat;
233 }
234
235 /* Pulse Off: <max> <delay> <off> <0ms> */
236 if (pattern[0].delta_t != 0 &&
237 pattern[1].delta_t == 0 &&
238 pattern[0].brightness == MSP430_LED_BRIGHTNESS_MAX &&
239 pattern[1].brightness == LED_OFF) {
240 tx[1] = MSP430_LED_PULSE_OFF;
241 tx[2] = delay0;
242 tx[4] = (u8) repeat;
243 }
244
245 if (!tx[1])
246 return -EINVAL;
247
248 rc = msp430_cmd(led->spi, tx, rx);
249 if (rc)
250 return rc;
251
252 return 0;
253 }
254
255 static int msp430_led(struct spi_device *spi, struct device_node *nc, u8 id)
256 {
257 struct device *dev = &spi->dev;
258 struct led_init_data init_data = {};
259 struct msp430_led *led;
260 enum led_default_state state;
261 int rc;
262
263 led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
264 if (!led)
265 return -ENOMEM;
266
267 led->id = id;
268 led->spi = spi;
269
270 init_data.fwnode = of_fwnode_handle(nc);
271
272 state = led_init_default_state_get(init_data.fwnode);
273 switch (state) {
274 case LEDS_DEFSTATE_ON:
275 led->cdev.brightness = MSP430_LED_BRIGHTNESS_MAX;
276 break;
277 default:
278 led->cdev.brightness = LED_OFF;
279 break;
280 }
281
282 msp430_brightness_set(&led->cdev, led->cdev.brightness);
283
284 led->cdev.blink_set = msp430_blink_set;
285 led->cdev.brightness_set_blocking = msp430_brightness_set;
286 led->cdev.max_brightness = MSP430_LED_BRIGHTNESS_MAX;
287 led->cdev.pattern_clear = msp430_pattern_clear;
288 led->cdev.pattern_set = msp430_pattern_set;
289
290 rc = devm_led_classdev_register_ext(dev, &led->cdev, &init_data);
291 if (rc < 0)
292 return rc;
293
294 dev_dbg(dev, "registered LED %s\n", led->cdev.name);
295
296 return 0;
297 }
298
299 static inline int msp430_check_workmode(struct spi_device *spi)
300 {
301 struct device *dev = &spi->dev;
302 u8 tx[MSP430_CMD_BYTES] = {MSP430_CMD_MCU, MSP430_MCU_WM, 0, 0, 0, 0};
303 u8 rx[MSP430_CMD_BYTES];
304 int rc;
305
306 rc = msp430_cmd(spi, tx, rx);
307 if (rc)
308 return rc;
309
310 if ((rx[3] == 0xA5 && rx[4] == 'Z') ||
311 (rx[4] == 0xA5 && rx[5] == 'Z') ||
312 (rx[4] == '\b' && rx[5] == '\n')) {
313 dev_err(dev, "invalid workmode: "
314 "[%02x %02x %02x %02x %02x %02x]\n",
315 rx[0], rx[1], rx[2], rx[3], rx[4], rx[5]);
316 return -EINVAL;
317 }
318
319 return 0;
320 }
321
322 static int msp430_leds_probe(struct spi_device *spi)
323 {
324 struct device *dev = &spi->dev;
325 struct device_node *np = dev_of_node(dev);
326 struct device_node *child;
327 int rc;
328
329 rc = msp430_check_workmode(spi);
330 if (rc)
331 return rc;
332
333 for_each_available_child_of_node(np, child) {
334 u32 reg;
335
336 if (of_property_read_u32(child, "reg", &reg))
337 continue;
338
339 if (reg < MSP430_LED_MIN_ID || reg > MSP430_LED_MAX_ID) {
340 dev_err(dev, "invalid LED (%u) [%d, %d]\n", reg,
341 MSP430_LED_MIN_ID, MSP430_LED_MAX_ID);
342 continue;
343 }
344
345 rc = msp430_led(spi, child, reg);
346 if (rc < 0) {
347 of_node_put(child);
348 return rc;
349 }
350 }
351
352 return 0;
353 }
354
355 static const struct of_device_id msp430_leds_of_match[] = {
356 { .compatible = "sercomm,msp430-leds", },
357 { },
358 };
359 MODULE_DEVICE_TABLE(of, msp430_leds_of_match);
360
361 static const struct spi_device_id msp430_leds_id_table[] = {
362 { "msp430-leds", 0 },
363 { }
364 };
365
366 static struct spi_driver msp430_leds_driver = {
367 .driver = {
368 .name = KBUILD_MODNAME,
369 .of_match_table = msp430_leds_of_match,
370 },
371 .id_table = msp430_leds_id_table,
372 .probe = msp430_leds_probe,
373 };
374
375 module_spi_driver(msp430_leds_driver);
376
377 MODULE_AUTHOR("Álvaro Fernández Rojas <noltari@gmail.com>");
378 MODULE_DESCRIPTION("LED driver for Sercomm MSP430 controllers");
379 MODULE_LICENSE("GPL v2");
380 MODULE_ALIAS("platform:leds-sercomm-msp430");