1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (c) 2018 Crane Merchandising Systems. All rights reserved.
3 // Copyright (C) 2018 Oleh Kravchenko <oleg@kaa.org.ua>
5 #include <linux/delay.h>
6 #include <linux/leds.h>
7 #include <linux/module.h>
8 #include <linux/of_device.h>
9 #include <linux/spi/spi.h>
10 #include <linux/workqueue.h>
11 #include <uapi/linux/uleds.h>
14 * CR0014114 SPI protocol descrtiption:
15 * +----+-----------------------------------+----+
16 * | CMD| BRIGHTNESS |CRC |
17 * +----+-----------------------------------+----+
18 * | | LED0| LED1| LED2| LED3| LED4| LED5| |
19 * | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
20 * | |R|G|B|R|G|B|R|G|B|R|G|B|R|G|B|R|G|B| |
21 * | 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 1 |
22 * | |1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1| |
23 * | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
25 * +----+-----------------------------------+----+
27 * +---------------------------------------------+
29 * PS: Boards can be connected to the chain:
30 * SPI -> board0 -> board1 -> board2 ..
33 /* CR0014114 SPI commands */
34 #define CR_SET_BRIGHTNESS 0x80
35 #define CR_INIT_REENUMERATE 0x81
36 #define CR_NEXT_REENUMERATE 0x82
38 /* CR0014114 default settings */
39 #define CR_MAX_BRIGHTNESS GENMASK(6, 0)
40 #define CR_FW_DELAY_MSEC 10
41 #define CR_RECOUNT_DELAY (HZ * 3600)
43 struct cr0014114_led
{
44 char name
[LED_MAX_NAME_SIZE
];
45 struct cr0014114
*priv
;
46 struct led_classdev ldev
;
53 struct delayed_work work
;
56 struct spi_device
*spi
;
59 struct cr0014114_led leds
[];
62 static void cr0014114_calc_crc(u8
*buf
, const size_t len
)
67 for (i
= 1, crc
= 1; i
< len
- 1; i
++)
71 /* special case when CRC matches the SPI commands */
72 if (crc
== CR_SET_BRIGHTNESS
||
73 crc
== CR_INIT_REENUMERATE
||
74 crc
== CR_NEXT_REENUMERATE
)
80 static int cr0014114_recount(struct cr0014114
*priv
)
86 dev_dbg(priv
->dev
, "LEDs recount is started\n");
88 cmd
= CR_INIT_REENUMERATE
;
89 ret
= spi_write(priv
->spi
, &cmd
, sizeof(cmd
));
93 cmd
= CR_NEXT_REENUMERATE
;
94 for (i
= 0; i
< priv
->count
; i
++) {
95 msleep(CR_FW_DELAY_MSEC
);
97 ret
= spi_write(priv
->spi
, &cmd
, sizeof(cmd
));
103 dev_dbg(priv
->dev
, "LEDs recount is finished\n");
106 dev_err(priv
->dev
, "with error %d", ret
);
111 static int cr0014114_sync(struct cr0014114
*priv
)
115 unsigned long udelay
, now
= jiffies
;
117 /* to avoid SPI mistiming with firmware we should wait some time */
118 if (time_after(priv
->delay
, now
)) {
119 udelay
= jiffies_to_usecs(priv
->delay
- now
);
120 usleep_range(udelay
, udelay
+ 1);
123 if (unlikely(priv
->do_recount
)) {
124 ret
= cr0014114_recount(priv
);
128 priv
->do_recount
= false;
129 msleep(CR_FW_DELAY_MSEC
);
132 priv
->buf
[0] = CR_SET_BRIGHTNESS
;
133 for (i
= 0; i
< priv
->count
; i
++)
134 priv
->buf
[i
+ 1] = priv
->leds
[i
].brightness
;
135 cr0014114_calc_crc(priv
->buf
, priv
->count
+ 2);
136 ret
= spi_write(priv
->spi
, priv
->buf
, priv
->count
+ 2);
139 priv
->delay
= jiffies
+ msecs_to_jiffies(CR_FW_DELAY_MSEC
);
144 static void cr0014114_recount_work(struct work_struct
*work
)
147 struct cr0014114
*priv
= container_of(work
,
151 mutex_lock(&priv
->lock
);
152 priv
->do_recount
= true;
153 ret
= cr0014114_sync(priv
);
154 mutex_unlock(&priv
->lock
);
157 dev_warn(priv
->dev
, "sync of LEDs failed %d\n", ret
);
159 schedule_delayed_work(&priv
->work
, CR_RECOUNT_DELAY
);
162 static int cr0014114_set_sync(struct led_classdev
*ldev
,
163 enum led_brightness brightness
)
166 struct cr0014114_led
*led
= container_of(ldev
,
167 struct cr0014114_led
,
170 dev_dbg(led
->priv
->dev
, "Set brightness of %s to %d\n",
171 led
->name
, brightness
);
173 mutex_lock(&led
->priv
->lock
);
174 led
->brightness
= (u8
)brightness
;
175 ret
= cr0014114_sync(led
->priv
);
176 mutex_unlock(&led
->priv
->lock
);
181 static int cr0014114_probe_dt(struct cr0014114
*priv
)
184 struct cr0014114_led
*led
;
185 struct fwnode_handle
*child
;
186 struct device_node
*np
;
190 device_for_each_child_node(priv
->dev
, child
) {
191 np
= to_of_node(child
);
192 led
= &priv
->leds
[i
];
194 ret
= fwnode_property_read_string(child
, "label", &str
);
196 snprintf(led
->name
, sizeof(led
->name
),
199 snprintf(led
->name
, sizeof(led
->name
),
200 "cr0014114:%s", str
);
202 fwnode_property_read_string(child
, "linux,default-trigger",
203 &led
->ldev
.default_trigger
);
206 led
->ldev
.name
= led
->name
;
207 led
->ldev
.max_brightness
= CR_MAX_BRIGHTNESS
;
208 led
->ldev
.brightness_set_blocking
= cr0014114_set_sync
;
210 ret
= devm_of_led_classdev_register(priv
->dev
, np
,
214 "failed to register LED device %s, err %d",
216 fwnode_handle_put(child
);
220 led
->ldev
.dev
->of_node
= np
;
228 static int cr0014114_probe(struct spi_device
*spi
)
230 struct cr0014114
*priv
;
234 count
= device_get_child_node_count(&spi
->dev
);
236 dev_err(&spi
->dev
, "LEDs are not defined in device tree!");
240 priv
= devm_kzalloc(&spi
->dev
, struct_size(priv
, leds
, count
),
245 priv
->buf
= devm_kzalloc(&spi
->dev
, count
+ 2, GFP_KERNEL
);
249 mutex_init(&priv
->lock
);
250 INIT_DELAYED_WORK(&priv
->work
, cr0014114_recount_work
);
252 priv
->dev
= &spi
->dev
;
254 priv
->delay
= jiffies
-
255 msecs_to_jiffies(CR_FW_DELAY_MSEC
);
257 priv
->do_recount
= true;
258 ret
= cr0014114_sync(priv
);
260 dev_err(priv
->dev
, "first recount failed %d\n", ret
);
264 priv
->do_recount
= true;
265 ret
= cr0014114_sync(priv
);
267 dev_err(priv
->dev
, "second recount failed %d\n", ret
);
271 ret
= cr0014114_probe_dt(priv
);
275 /* setup recount work to workaround buggy firmware */
276 schedule_delayed_work(&priv
->work
, CR_RECOUNT_DELAY
);
278 spi_set_drvdata(spi
, priv
);
283 static int cr0014114_remove(struct spi_device
*spi
)
285 struct cr0014114
*priv
= spi_get_drvdata(spi
);
287 cancel_delayed_work_sync(&priv
->work
);
288 mutex_destroy(&priv
->lock
);
293 static const struct of_device_id cr0014114_dt_ids
[] = {
294 { .compatible
= "crane,cr0014114", },
298 MODULE_DEVICE_TABLE(of
, cr0014114_dt_ids
);
300 static struct spi_driver cr0014114_driver
= {
301 .probe
= cr0014114_probe
,
302 .remove
= cr0014114_remove
,
304 .name
= KBUILD_MODNAME
,
305 .of_match_table
= cr0014114_dt_ids
,
309 module_spi_driver(cr0014114_driver
);
311 MODULE_AUTHOR("Oleh Kravchenko <oleg@kaa.org.ua>");
312 MODULE_DESCRIPTION("cr0014114 LED driver");
313 MODULE_LICENSE("GPL v2");
314 MODULE_ALIAS("spi:cr0014114");