1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // Copyright (c) 2019 Crane Merchandising Systems. All rights reserved. |
3 | // Copyright (C) 2019 Oleh Kravchenko <oleg@kaa.org.ua> |
4 | |
5 | #include <linux/delay.h> |
6 | #include <linux/leds.h> |
7 | #include <linux/mod_devicetable.h> |
8 | #include <linux/module.h> |
9 | #include <linux/property.h> |
10 | #include <linux/spi/spi.h> |
11 | |
12 | /* |
13 | * EL15203000 SPI protocol description: |
14 | * +-----+---------+ |
15 | * | LED | COMMAND | |
16 | * +-----+---------+ |
17 | * | 1 | 1 | |
18 | * +-----+---------+ |
19 | * (*) LEDs MCU board expects 20 msec delay per byte. |
20 | * |
21 | * LEDs: |
22 | * +----------+--------------+-------------------------------------------+ |
23 | * | ID | NAME | DESCRIPTION | |
24 | * +----------+--------------+-------------------------------------------+ |
25 | * | 'P' 0x50 | Pipe | Consists from 5 LEDs, controlled by board | |
26 | * +----------+--------------+-------------------------------------------+ |
27 | * | 'S' 0x53 | Screen frame | Light tube around the screen | |
28 | * +----------+--------------+-------------------------------------------+ |
29 | * | 'V' 0x56 | Vending area | Highlights a cup of coffee | |
30 | * +----------+--------------+-------------------------------------------+ |
31 | * |
32 | * COMMAND: |
33 | * +----------+-----------------+--------------+--------------+ |
34 | * | VALUES | PIPE | SCREEN FRAME | VENDING AREA | |
35 | * +----------+-----------------+--------------+--------------+ |
36 | * | '0' 0x30 | Off | |
37 | * +----------+-----------------------------------------------+ |
38 | * | '1' 0x31 | On | |
39 | * +----------+-----------------+--------------+--------------+ |
40 | * | '2' 0x32 | Cascade | Breathing | |
41 | * +----------+-----------------+--------------+ |
42 | * | '3' 0x33 | Inverse cascade | |
43 | * +----------+-----------------+ |
44 | * | '4' 0x34 | Bounce | |
45 | * +----------+-----------------+ |
46 | * | '5' 0x35 | Inverse bounce | |
47 | * +----------+-----------------+ |
48 | */ |
49 | |
50 | /* EL15203000 default settings */ |
51 | #define EL_FW_DELAY_USEC 20000ul |
52 | #define EL_PATTERN_DELAY_MSEC 800u |
53 | #define EL_PATTERN_LEN 10u |
54 | #define EL_PATTERN_HALF_LEN (EL_PATTERN_LEN / 2) |
55 | |
56 | enum el15203000_command { |
57 | /* for all LEDs */ |
58 | EL_OFF = '0', |
59 | EL_ON = '1', |
60 | |
61 | /* for Screen LED */ |
62 | EL_SCREEN_BREATHING = '2', |
63 | |
64 | /* for Pipe LED */ |
65 | EL_PIPE_CASCADE = '2', |
66 | EL_PIPE_INV_CASCADE = '3', |
67 | EL_PIPE_BOUNCE = '4', |
68 | EL_PIPE_INV_BOUNCE = '5', |
69 | }; |
70 | |
71 | struct el15203000_led { |
72 | struct led_classdev ldev; |
73 | struct el15203000 *priv; |
74 | u32 reg; |
75 | }; |
76 | |
77 | struct el15203000 { |
78 | struct device *dev; |
79 | struct mutex lock; |
80 | struct spi_device *spi; |
81 | unsigned long delay; |
82 | size_t count; |
83 | struct el15203000_led leds[] __counted_by(count); |
84 | }; |
85 | |
86 | #define to_el15203000_led(d) container_of(d, struct el15203000_led, ldev) |
87 | |
88 | static int el15203000_cmd(struct el15203000_led *led, u8 brightness) |
89 | { |
90 | int ret; |
91 | u8 cmd[2]; |
92 | size_t i; |
93 | |
94 | mutex_lock(&led->priv->lock); |
95 | |
96 | dev_dbg(led->priv->dev, "Set brightness of 0x%02x(%c) to 0x%02x(%c)" , |
97 | led->reg, led->reg, brightness, brightness); |
98 | |
99 | /* to avoid SPI mistiming with firmware we should wait some time */ |
100 | if (time_after(led->priv->delay, jiffies)) { |
101 | dev_dbg(led->priv->dev, "Wait %luus to sync" , |
102 | EL_FW_DELAY_USEC); |
103 | |
104 | usleep_range(EL_FW_DELAY_USEC, |
105 | EL_FW_DELAY_USEC + 1); |
106 | } |
107 | |
108 | cmd[0] = led->reg; |
109 | cmd[1] = brightness; |
110 | |
111 | for (i = 0; i < ARRAY_SIZE(cmd); i++) { |
112 | if (i) |
113 | usleep_range(EL_FW_DELAY_USEC, |
114 | EL_FW_DELAY_USEC + 1); |
115 | |
116 | ret = spi_write(spi: led->priv->spi, buf: &cmd[i], len: sizeof(cmd[i])); |
117 | if (ret) { |
118 | dev_err(led->priv->dev, |
119 | "spi_write() error %d" , ret); |
120 | break; |
121 | } |
122 | } |
123 | |
124 | led->priv->delay = jiffies + usecs_to_jiffies(EL_FW_DELAY_USEC); |
125 | |
126 | mutex_unlock(lock: &led->priv->lock); |
127 | |
128 | return ret; |
129 | } |
130 | |
131 | static int el15203000_set_blocking(struct led_classdev *ldev, |
132 | enum led_brightness brightness) |
133 | { |
134 | struct el15203000_led *led = to_el15203000_led(ldev); |
135 | |
136 | return el15203000_cmd(led, brightness: brightness == LED_OFF ? EL_OFF : EL_ON); |
137 | } |
138 | |
139 | static int el15203000_pattern_set_S(struct led_classdev *ldev, |
140 | struct led_pattern *pattern, |
141 | u32 len, int repeat) |
142 | { |
143 | struct el15203000_led *led = to_el15203000_led(ldev); |
144 | |
145 | if (repeat > 0 || len != 2 || |
146 | pattern[0].delta_t != 4000 || pattern[0].brightness != 0 || |
147 | pattern[1].delta_t != 4000 || pattern[1].brightness != 1) |
148 | return -EINVAL; |
149 | |
150 | dev_dbg(led->priv->dev, "Breathing mode for 0x%02x(%c)" , |
151 | led->reg, led->reg); |
152 | |
153 | return el15203000_cmd(led, brightness: EL_SCREEN_BREATHING); |
154 | } |
155 | |
156 | static bool is_cascade(const struct led_pattern *pattern, u32 len, |
157 | bool inv, bool right) |
158 | { |
159 | int val, t; |
160 | u32 i; |
161 | |
162 | if (len != EL_PATTERN_HALF_LEN) |
163 | return false; |
164 | |
165 | val = right ? BIT(4) : BIT(0); |
166 | |
167 | for (i = 0; i < len; i++) { |
168 | t = inv ? ~val & GENMASK(4, 0) : val; |
169 | |
170 | if (pattern[i].delta_t != EL_PATTERN_DELAY_MSEC || |
171 | pattern[i].brightness != t) |
172 | return false; |
173 | |
174 | val = right ? val >> 1 : val << 1; |
175 | } |
176 | |
177 | return true; |
178 | } |
179 | |
180 | static bool is_bounce(const struct led_pattern *pattern, u32 len, bool inv) |
181 | { |
182 | if (len != EL_PATTERN_LEN) |
183 | return false; |
184 | |
185 | return is_cascade(pattern, EL_PATTERN_HALF_LEN, inv, right: false) && |
186 | is_cascade(pattern: pattern + EL_PATTERN_HALF_LEN, |
187 | EL_PATTERN_HALF_LEN, inv, right: true); |
188 | } |
189 | |
190 | static int el15203000_pattern_set_P(struct led_classdev *ldev, |
191 | struct led_pattern *pattern, |
192 | u32 len, int repeat) |
193 | { |
194 | struct el15203000_led *led = to_el15203000_led(ldev); |
195 | u8 cmd; |
196 | |
197 | if (repeat > 0) |
198 | return -EINVAL; |
199 | |
200 | if (is_cascade(pattern, len, inv: false, right: false)) { |
201 | dev_dbg(led->priv->dev, "Cascade mode for 0x%02x(%c)" , |
202 | led->reg, led->reg); |
203 | |
204 | cmd = EL_PIPE_CASCADE; |
205 | } else if (is_cascade(pattern, len, inv: true, right: false)) { |
206 | dev_dbg(led->priv->dev, "Inverse cascade mode for 0x%02x(%c)" , |
207 | led->reg, led->reg); |
208 | |
209 | cmd = EL_PIPE_INV_CASCADE; |
210 | } else if (is_bounce(pattern, len, inv: false)) { |
211 | dev_dbg(led->priv->dev, "Bounce mode for 0x%02x(%c)" , |
212 | led->reg, led->reg); |
213 | |
214 | cmd = EL_PIPE_BOUNCE; |
215 | } else if (is_bounce(pattern, len, inv: true)) { |
216 | dev_dbg(led->priv->dev, "Inverse bounce mode for 0x%02x(%c)" , |
217 | led->reg, led->reg); |
218 | |
219 | cmd = EL_PIPE_INV_BOUNCE; |
220 | } else { |
221 | dev_err(led->priv->dev, "Invalid hw_pattern for 0x%02x(%c)!" , |
222 | led->reg, led->reg); |
223 | |
224 | return -EINVAL; |
225 | } |
226 | |
227 | return el15203000_cmd(led, brightness: cmd); |
228 | } |
229 | |
230 | static int el15203000_pattern_clear(struct led_classdev *ldev) |
231 | { |
232 | struct el15203000_led *led = to_el15203000_led(ldev); |
233 | |
234 | return el15203000_cmd(led, brightness: EL_OFF); |
235 | } |
236 | |
237 | static int el15203000_probe_dt(struct el15203000 *priv) |
238 | { |
239 | struct el15203000_led *led = priv->leds; |
240 | struct fwnode_handle *child; |
241 | int ret; |
242 | |
243 | device_for_each_child_node(priv->dev, child) { |
244 | struct led_init_data init_data = {}; |
245 | |
246 | ret = fwnode_property_read_u32(fwnode: child, propname: "reg" , val: &led->reg); |
247 | if (ret) { |
248 | dev_err(priv->dev, "LED without ID number" ); |
249 | goto err_child_out; |
250 | } |
251 | |
252 | if (led->reg > U8_MAX) { |
253 | dev_err(priv->dev, "LED value %d is invalid" , led->reg); |
254 | ret = -EINVAL; |
255 | goto err_child_out; |
256 | } |
257 | |
258 | led->priv = priv; |
259 | led->ldev.max_brightness = LED_ON; |
260 | led->ldev.brightness_set_blocking = el15203000_set_blocking; |
261 | |
262 | if (led->reg == 'S') { |
263 | led->ldev.pattern_set = el15203000_pattern_set_S; |
264 | led->ldev.pattern_clear = el15203000_pattern_clear; |
265 | } else if (led->reg == 'P') { |
266 | led->ldev.pattern_set = el15203000_pattern_set_P; |
267 | led->ldev.pattern_clear = el15203000_pattern_clear; |
268 | } |
269 | |
270 | init_data.fwnode = child; |
271 | ret = devm_led_classdev_register_ext(parent: priv->dev, led_cdev: &led->ldev, |
272 | init_data: &init_data); |
273 | if (ret) { |
274 | dev_err(priv->dev, |
275 | "failed to register LED device %s, err %d" , |
276 | led->ldev.name, ret); |
277 | goto err_child_out; |
278 | } |
279 | |
280 | led++; |
281 | } |
282 | |
283 | return 0; |
284 | |
285 | err_child_out: |
286 | fwnode_handle_put(fwnode: child); |
287 | return ret; |
288 | } |
289 | |
290 | static int el15203000_probe(struct spi_device *spi) |
291 | { |
292 | struct el15203000 *priv; |
293 | size_t count; |
294 | |
295 | count = device_get_child_node_count(dev: &spi->dev); |
296 | if (!count) { |
297 | dev_err(&spi->dev, "LEDs are not defined in device tree!" ); |
298 | return -ENODEV; |
299 | } |
300 | |
301 | priv = devm_kzalloc(dev: &spi->dev, struct_size(priv, leds, count), |
302 | GFP_KERNEL); |
303 | if (!priv) |
304 | return -ENOMEM; |
305 | |
306 | mutex_init(&priv->lock); |
307 | priv->count = count; |
308 | priv->dev = &spi->dev; |
309 | priv->spi = spi; |
310 | priv->delay = jiffies - |
311 | usecs_to_jiffies(EL_FW_DELAY_USEC); |
312 | |
313 | spi_set_drvdata(spi, data: priv); |
314 | |
315 | return el15203000_probe_dt(priv); |
316 | } |
317 | |
318 | static void el15203000_remove(struct spi_device *spi) |
319 | { |
320 | struct el15203000 *priv = spi_get_drvdata(spi); |
321 | |
322 | mutex_destroy(lock: &priv->lock); |
323 | } |
324 | |
325 | static const struct of_device_id el15203000_dt_ids[] = { |
326 | { .compatible = "crane,el15203000" , }, |
327 | {}, |
328 | }; |
329 | |
330 | MODULE_DEVICE_TABLE(of, el15203000_dt_ids); |
331 | |
332 | static struct spi_driver el15203000_driver = { |
333 | .probe = el15203000_probe, |
334 | .remove = el15203000_remove, |
335 | .driver = { |
336 | .name = KBUILD_MODNAME, |
337 | .of_match_table = el15203000_dt_ids, |
338 | }, |
339 | }; |
340 | |
341 | module_spi_driver(el15203000_driver); |
342 | |
343 | MODULE_AUTHOR("Oleh Kravchenko <oleg@kaa.org.ua>" ); |
344 | MODULE_DESCRIPTION("el15203000 LED driver" ); |
345 | MODULE_LICENSE("GPL v2" ); |
346 | MODULE_ALIAS("spi:el15203000" ); |
347 | |