1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Driver for Sercomm MSP430G2513 LEDs.
5 * Copyright 2023 Álvaro Fernández Rojas <noltari@gmail.com>
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>
16 * MSP430G2513 SPI protocol description:
17 * +----+----+----+----+----+----+
18 * | b1 | b2 | b3 | b4 | b5 | b6 |
19 * +----+----+----+----+----+----+
20 * 6 bytes TX & RX per transaction.
23 * MSP430G2513 can control up to 9 LEDs.
26 * b3-b6: LED function parameters
33 * - b5: repeat (0 = infinite)
36 * - b4: blink while pulsing? (unknown)
37 * - b5: repeat (0 = infinite)
40 * - b4: blink while pulsing? (unknown)
41 * - b5: repeat (0 = infinite)
44 * - b4: blink while pulsing? (unknown)
45 * - b5: repeat (0 = infinite)
47 * - b3: brightness [0,4]
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
58 #define MSP430_CMD_BYTES 6
59 #define MSP430_CMD_MCU 0x55
60 #define MSP430_MCU_WM 0x68
62 #define MSP430_LED_MIN_ID 1
63 #define MSP430_LED_MAX_ID 9
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
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)
79 #define MSP430_LED_BRIGHTNESS_MAX 5
80 #define MSP430_LED_REPEAT_MAX 0xff
83 * struct msp430_led - state container for Sercomm MSP430 based LEDs
84 * @cdev: LED class device for this LED
89 struct led_classdev cdev
;
90 struct spi_device
*spi
;
94 static inline int msp430_cmd(struct spi_device
*spi
, u8 tx
[MSP430_CMD_BYTES
],
95 u8 rx
[MSP430_CMD_BYTES
])
97 struct device
*dev
= &spi
->dev
;
100 memset(rx
, 0, MSP430_CMD_BYTES
);
102 rc
= spi_write_then_read(spi
, tx
, MSP430_CMD_BYTES
,
103 rx
, MSP430_CMD_BYTES
);
105 dev_err(dev
, "spi error\n");
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]);
115 static unsigned long msp430_blink_delay(unsigned long delay
)
117 unsigned long msp430_delay
;
119 msp430_delay
= delay
+ MSP430_LED_BLINK_MS
/ 2;
120 msp430_delay
= msp430_delay
/ MSP430_LED_BLINK_MS
;
121 if (msp430_delay
== 0)
127 static int msp430_blink_set(struct led_classdev
*led_cdev
,
128 unsigned long *delay_on
,
129 unsigned long *delay_off
)
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
];
138 *delay_on
= MSP430_LED_BLINK_DEF
;
140 *delay_off
= MSP430_LED_BLINK_DEF
;
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");
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
);
158 return msp430_cmd(led
->spi
, tx
, rx
);
161 static int msp430_brightness_set(struct led_classdev
*led_cdev
,
162 enum led_brightness brightness
)
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
;
173 tx
[1] = MSP430_LED_OFF
;
175 case MSP430_LED_BRIGHTNESS_MAX
:
176 tx
[1] = MSP430_LED_ON
;
179 tx
[1] = MSP430_LED_LEVEL
;
184 return msp430_cmd(led
->spi
, tx
, rx
);
187 static int msp430_pattern_clear(struct led_classdev
*ldev
)
189 msp430_brightness_set(ldev
, LED_OFF
);
194 static int msp430_pattern_set(struct led_classdev
*led_cdev
,
195 struct led_pattern
*pattern
,
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
;
207 repeat
> MSP430_LED_REPEAT_MAX
||
208 pattern
[0].delta_t
> MSP430_LED_BLINK_MAX
||
209 pattern
[1].delta_t
> MSP430_LED_BLINK_MAX
)
212 delay0
= msp430_blink_delay(pattern
[0].delta_t
);
213 delay1
= msp430_blink_delay(pattern
[1].delta_t
);
215 /* Pulse: <off> <delay> <max> <delay> */
216 if (delay0
== delay1
&&
217 pattern
[0].brightness
== LED_OFF
&&
218 pattern
[1].brightness
== MSP430_LED_BRIGHTNESS_MAX
)
220 tx
[1] = MSP430_LED_PULSE
;
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
;
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
;
248 rc
= msp430_cmd(led
->spi
, tx
, rx
);
255 static int msp430_led(struct spi_device
*spi
, struct device_node
*nc
, u8 id
)
257 struct device
*dev
= &spi
->dev
;
258 struct led_init_data init_data
= {};
259 struct msp430_led
*led
;
260 enum led_default_state state
;
263 led
= devm_kzalloc(dev
, sizeof(*led
), GFP_KERNEL
);
270 init_data
.fwnode
= of_fwnode_handle(nc
);
272 state
= led_init_default_state_get(init_data
.fwnode
);
274 case LEDS_DEFSTATE_ON
:
275 led
->cdev
.brightness
= MSP430_LED_BRIGHTNESS_MAX
;
278 led
->cdev
.brightness
= LED_OFF
;
282 msp430_brightness_set(&led
->cdev
, led
->cdev
.brightness
);
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
;
290 rc
= devm_led_classdev_register_ext(dev
, &led
->cdev
, &init_data
);
294 dev_dbg(dev
, "registered LED %s\n", led
->cdev
.name
);
299 static inline int msp430_check_workmode(struct spi_device
*spi
)
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
];
306 rc
= msp430_cmd(spi
, tx
, rx
);
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]);
322 static int msp430_leds_probe(struct spi_device
*spi
)
324 struct device
*dev
= &spi
->dev
;
325 struct device_node
*np
= dev_of_node(dev
);
326 struct device_node
*child
;
329 rc
= msp430_check_workmode(spi
);
333 for_each_available_child_of_node(np
, child
) {
336 if (of_property_read_u32(child
, "reg", ®
))
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
);
345 rc
= msp430_led(spi
, child
, reg
);
355 static const struct of_device_id msp430_leds_of_match
[] = {
356 { .compatible
= "sercomm,msp430-leds", },
359 MODULE_DEVICE_TABLE(of
, msp430_leds_of_match
);
361 static const struct spi_device_id msp430_leds_id_table
[] = {
362 { "msp430-leds", 0 },
366 static struct spi_driver msp430_leds_driver
= {
368 .name
= KBUILD_MODNAME
,
369 .of_match_table
= msp430_leds_of_match
,
371 .id_table
= msp430_leds_id_table
,
372 .probe
= msp430_leds_probe
,
375 module_spi_driver(msp430_leds_driver
);
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");