1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | // |
3 | // Driver for Panasonic AN30259A 3-channel LED driver |
4 | // |
5 | // Copyright (c) 2018 Simon Shields <simon@lineageos.org> |
6 | // |
7 | // Datasheet: |
8 | // https://www.alliedelec.com/m/d/a9d2b3ee87c2d1a535a41dd747b1c247.pdf |
9 | |
10 | #include <linux/i2c.h> |
11 | #include <linux/leds.h> |
12 | #include <linux/module.h> |
13 | #include <linux/mutex.h> |
14 | #include <linux/of.h> |
15 | #include <linux/regmap.h> |
16 | |
17 | #define AN30259A_MAX_LEDS 3 |
18 | |
19 | #define AN30259A_REG_SRESET 0x00 |
20 | #define AN30259A_LED_SRESET BIT(0) |
21 | |
22 | /* LED power registers */ |
23 | #define AN30259A_REG_LED_ON 0x01 |
24 | #define AN30259A_LED_EN(x) BIT((x) - 1) |
25 | #define AN30259A_LED_SLOPE(x) BIT(((x) - 1) + 4) |
26 | |
27 | #define AN30259A_REG_LEDCC(x) (0x03 + ((x) - 1)) |
28 | |
29 | /* slope control registers */ |
30 | #define AN30259A_REG_SLOPE(x) (0x06 + ((x) - 1)) |
31 | #define AN30259A_LED_SLOPETIME1(x) (x) |
32 | #define AN30259A_LED_SLOPETIME2(x) ((x) << 4) |
33 | |
34 | #define AN30259A_REG_LEDCNT1(x) (0x09 + (4 * ((x) - 1))) |
35 | #define AN30259A_LED_DUTYMAX(x) ((x) << 4) |
36 | #define AN30259A_LED_DUTYMID(x) (x) |
37 | |
38 | #define AN30259A_REG_LEDCNT2(x) (0x0A + (4 * ((x) - 1))) |
39 | #define AN30259A_LED_DELAY(x) ((x) << 4) |
40 | #define AN30259A_LED_DUTYMIN(x) (x) |
41 | |
42 | /* detention time control (length of each slope step) */ |
43 | #define AN30259A_REG_LEDCNT3(x) (0x0B + (4 * ((x) - 1))) |
44 | #define AN30259A_LED_DT1(x) (x) |
45 | #define AN30259A_LED_DT2(x) ((x) << 4) |
46 | |
47 | #define AN30259A_REG_LEDCNT4(x) (0x0C + (4 * ((x) - 1))) |
48 | #define AN30259A_LED_DT3(x) (x) |
49 | #define AN30259A_LED_DT4(x) ((x) << 4) |
50 | |
51 | #define AN30259A_REG_MAX 0x14 |
52 | |
53 | #define AN30259A_BLINK_MAX_TIME 7500 /* ms */ |
54 | #define AN30259A_SLOPE_RESOLUTION 500 /* ms */ |
55 | |
56 | #define AN30259A_NAME "an30259a" |
57 | |
58 | struct an30259a; |
59 | |
60 | struct an30259a_led { |
61 | struct an30259a *chip; |
62 | struct fwnode_handle *fwnode; |
63 | struct led_classdev cdev; |
64 | u32 num; |
65 | enum led_default_state default_state; |
66 | bool sloping; |
67 | }; |
68 | |
69 | struct an30259a { |
70 | struct mutex mutex; /* held when writing to registers */ |
71 | struct i2c_client *client; |
72 | struct an30259a_led leds[AN30259A_MAX_LEDS]; |
73 | struct regmap *regmap; |
74 | int num_leds; |
75 | }; |
76 | |
77 | static int an30259a_brightness_set(struct led_classdev *cdev, |
78 | enum led_brightness brightness) |
79 | { |
80 | struct an30259a_led *led; |
81 | int ret; |
82 | unsigned int led_on; |
83 | |
84 | led = container_of(cdev, struct an30259a_led, cdev); |
85 | mutex_lock(&led->chip->mutex); |
86 | |
87 | ret = regmap_read(map: led->chip->regmap, AN30259A_REG_LED_ON, val: &led_on); |
88 | if (ret) |
89 | goto error; |
90 | |
91 | switch (brightness) { |
92 | case LED_OFF: |
93 | led_on &= ~AN30259A_LED_EN(led->num); |
94 | led_on &= ~AN30259A_LED_SLOPE(led->num); |
95 | led->sloping = false; |
96 | break; |
97 | default: |
98 | led_on |= AN30259A_LED_EN(led->num); |
99 | if (led->sloping) |
100 | led_on |= AN30259A_LED_SLOPE(led->num); |
101 | ret = regmap_write(map: led->chip->regmap, |
102 | AN30259A_REG_LEDCNT1(led->num), |
103 | AN30259A_LED_DUTYMAX(0xf) | |
104 | AN30259A_LED_DUTYMID(0xf)); |
105 | if (ret) |
106 | goto error; |
107 | break; |
108 | } |
109 | |
110 | ret = regmap_write(map: led->chip->regmap, AN30259A_REG_LED_ON, val: led_on); |
111 | if (ret) |
112 | goto error; |
113 | |
114 | ret = regmap_write(map: led->chip->regmap, AN30259A_REG_LEDCC(led->num), |
115 | val: brightness); |
116 | |
117 | error: |
118 | mutex_unlock(lock: &led->chip->mutex); |
119 | |
120 | return ret; |
121 | } |
122 | |
123 | static int an30259a_blink_set(struct led_classdev *cdev, |
124 | unsigned long *delay_off, unsigned long *delay_on) |
125 | { |
126 | struct an30259a_led *led; |
127 | int ret, num; |
128 | unsigned int led_on; |
129 | unsigned long off = *delay_off, on = *delay_on; |
130 | |
131 | led = container_of(cdev, struct an30259a_led, cdev); |
132 | |
133 | mutex_lock(&led->chip->mutex); |
134 | num = led->num; |
135 | |
136 | /* slope time can only be a multiple of 500ms. */ |
137 | if (off % AN30259A_SLOPE_RESOLUTION || on % AN30259A_SLOPE_RESOLUTION) { |
138 | ret = -EINVAL; |
139 | goto error; |
140 | } |
141 | |
142 | /* up to a maximum of 7500ms. */ |
143 | if (off > AN30259A_BLINK_MAX_TIME || on > AN30259A_BLINK_MAX_TIME) { |
144 | ret = -EINVAL; |
145 | goto error; |
146 | } |
147 | |
148 | /* if no blink specified, default to 1 Hz. */ |
149 | if (!off && !on) { |
150 | *delay_off = off = 500; |
151 | *delay_on = on = 500; |
152 | } |
153 | |
154 | /* convert into values the HW will understand. */ |
155 | off /= AN30259A_SLOPE_RESOLUTION; |
156 | on /= AN30259A_SLOPE_RESOLUTION; |
157 | |
158 | /* duty min should be zero (=off), delay should be zero. */ |
159 | ret = regmap_write(map: led->chip->regmap, AN30259A_REG_LEDCNT2(num), |
160 | AN30259A_LED_DELAY(0) | AN30259A_LED_DUTYMIN(0)); |
161 | if (ret) |
162 | goto error; |
163 | |
164 | /* reset detention time (no "breathing" effect). */ |
165 | ret = regmap_write(map: led->chip->regmap, AN30259A_REG_LEDCNT3(num), |
166 | AN30259A_LED_DT1(0) | AN30259A_LED_DT2(0)); |
167 | if (ret) |
168 | goto error; |
169 | ret = regmap_write(map: led->chip->regmap, AN30259A_REG_LEDCNT4(num), |
170 | AN30259A_LED_DT3(0) | AN30259A_LED_DT4(0)); |
171 | if (ret) |
172 | goto error; |
173 | |
174 | /* slope time controls on/off cycle length. */ |
175 | ret = regmap_write(map: led->chip->regmap, AN30259A_REG_SLOPE(num), |
176 | AN30259A_LED_SLOPETIME1(off) | |
177 | AN30259A_LED_SLOPETIME2(on)); |
178 | if (ret) |
179 | goto error; |
180 | |
181 | /* Finally, enable slope mode. */ |
182 | ret = regmap_read(map: led->chip->regmap, AN30259A_REG_LED_ON, val: &led_on); |
183 | if (ret) |
184 | goto error; |
185 | |
186 | led_on |= AN30259A_LED_SLOPE(num) | AN30259A_LED_EN(led->num); |
187 | |
188 | ret = regmap_write(map: led->chip->regmap, AN30259A_REG_LED_ON, val: led_on); |
189 | |
190 | if (!ret) |
191 | led->sloping = true; |
192 | error: |
193 | mutex_unlock(lock: &led->chip->mutex); |
194 | |
195 | return ret; |
196 | } |
197 | |
198 | static int an30259a_dt_init(struct i2c_client *client, |
199 | struct an30259a *chip) |
200 | { |
201 | struct device_node *np = dev_of_node(dev: &client->dev), *child; |
202 | int count, ret; |
203 | int i = 0; |
204 | struct an30259a_led *led; |
205 | |
206 | count = of_get_available_child_count(np); |
207 | if (!count || count > AN30259A_MAX_LEDS) |
208 | return -EINVAL; |
209 | |
210 | for_each_available_child_of_node(np, child) { |
211 | u32 source; |
212 | |
213 | ret = of_property_read_u32(np: child, propname: "reg" , out_value: &source); |
214 | if (ret != 0 || !source || source > AN30259A_MAX_LEDS) { |
215 | dev_err(&client->dev, "Couldn't read LED address: %d\n" , |
216 | ret); |
217 | count--; |
218 | continue; |
219 | } |
220 | |
221 | led = &chip->leds[i]; |
222 | |
223 | led->num = source; |
224 | led->chip = chip; |
225 | led->fwnode = of_fwnode_handle(child); |
226 | led->default_state = led_init_default_state_get(fwnode: led->fwnode); |
227 | |
228 | i++; |
229 | } |
230 | |
231 | if (!count) |
232 | return -EINVAL; |
233 | |
234 | chip->num_leds = i; |
235 | |
236 | return 0; |
237 | } |
238 | |
239 | static const struct regmap_config an30259a_regmap_config = { |
240 | .reg_bits = 8, |
241 | .val_bits = 8, |
242 | .max_register = AN30259A_REG_MAX, |
243 | }; |
244 | |
245 | static void an30259a_init_default_state(struct an30259a_led *led) |
246 | { |
247 | struct an30259a *chip = led->chip; |
248 | int led_on, err; |
249 | |
250 | switch (led->default_state) { |
251 | case LEDS_DEFSTATE_ON: |
252 | led->cdev.brightness = LED_FULL; |
253 | break; |
254 | case LEDS_DEFSTATE_KEEP: |
255 | err = regmap_read(map: chip->regmap, AN30259A_REG_LED_ON, val: &led_on); |
256 | if (err) |
257 | break; |
258 | |
259 | if (!(led_on & AN30259A_LED_EN(led->num))) { |
260 | led->cdev.brightness = LED_OFF; |
261 | break; |
262 | } |
263 | regmap_read(map: chip->regmap, AN30259A_REG_LEDCC(led->num), |
264 | val: &led->cdev.brightness); |
265 | break; |
266 | default: |
267 | led->cdev.brightness = LED_OFF; |
268 | } |
269 | |
270 | an30259a_brightness_set(cdev: &led->cdev, brightness: led->cdev.brightness); |
271 | } |
272 | |
273 | static int an30259a_probe(struct i2c_client *client) |
274 | { |
275 | struct an30259a *chip; |
276 | int i, err; |
277 | |
278 | chip = devm_kzalloc(dev: &client->dev, size: sizeof(*chip), GFP_KERNEL); |
279 | if (!chip) |
280 | return -ENOMEM; |
281 | |
282 | err = an30259a_dt_init(client, chip); |
283 | if (err < 0) |
284 | return err; |
285 | |
286 | mutex_init(&chip->mutex); |
287 | chip->client = client; |
288 | i2c_set_clientdata(client, data: chip); |
289 | |
290 | chip->regmap = devm_regmap_init_i2c(client, &an30259a_regmap_config); |
291 | |
292 | if (IS_ERR(ptr: chip->regmap)) { |
293 | err = PTR_ERR(ptr: chip->regmap); |
294 | dev_err(&client->dev, "Failed to allocate register map: %d\n" , |
295 | err); |
296 | goto exit; |
297 | } |
298 | |
299 | for (i = 0; i < chip->num_leds; i++) { |
300 | struct led_init_data init_data = {}; |
301 | |
302 | an30259a_init_default_state(led: &chip->leds[i]); |
303 | chip->leds[i].cdev.brightness_set_blocking = |
304 | an30259a_brightness_set; |
305 | chip->leds[i].cdev.blink_set = an30259a_blink_set; |
306 | |
307 | init_data.fwnode = chip->leds[i].fwnode; |
308 | init_data.devicename = AN30259A_NAME; |
309 | init_data.default_label = ":" ; |
310 | |
311 | err = devm_led_classdev_register_ext(parent: &client->dev, |
312 | led_cdev: &chip->leds[i].cdev, |
313 | init_data: &init_data); |
314 | if (err < 0) |
315 | goto exit; |
316 | } |
317 | return 0; |
318 | |
319 | exit: |
320 | mutex_destroy(lock: &chip->mutex); |
321 | return err; |
322 | } |
323 | |
324 | static void an30259a_remove(struct i2c_client *client) |
325 | { |
326 | struct an30259a *chip = i2c_get_clientdata(client); |
327 | |
328 | mutex_destroy(lock: &chip->mutex); |
329 | } |
330 | |
331 | static const struct of_device_id an30259a_match_table[] = { |
332 | { .compatible = "panasonic,an30259a" , }, |
333 | { /* sentinel */ }, |
334 | }; |
335 | |
336 | MODULE_DEVICE_TABLE(of, an30259a_match_table); |
337 | |
338 | static const struct i2c_device_id an30259a_id[] = { |
339 | { "an30259a" , 0 }, |
340 | { /* sentinel */ }, |
341 | }; |
342 | MODULE_DEVICE_TABLE(i2c, an30259a_id); |
343 | |
344 | static struct i2c_driver an30259a_driver = { |
345 | .driver = { |
346 | .name = "leds-an30259a" , |
347 | .of_match_table = an30259a_match_table, |
348 | }, |
349 | .probe = an30259a_probe, |
350 | .remove = an30259a_remove, |
351 | .id_table = an30259a_id, |
352 | }; |
353 | |
354 | module_i2c_driver(an30259a_driver); |
355 | |
356 | MODULE_AUTHOR("Simon Shields <simon@lineageos.org>" ); |
357 | MODULE_DESCRIPTION("AN30259A LED driver" ); |
358 | MODULE_LICENSE("GPL v2" ); |
359 | |