]>
Commit | Line | Data |
---|---|---|
2d00f35c SS |
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> | |
2d00f35c SS |
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 | ||
1817208e JA |
56 | #define AN30259A_NAME "an30259a" |
57 | ||
2d00f35c SS |
58 | #define STATE_OFF 0 |
59 | #define STATE_KEEP 1 | |
60 | #define STATE_ON 2 | |
61 | ||
62 | struct an30259a; | |
63 | ||
64 | struct an30259a_led { | |
65 | struct an30259a *chip; | |
1817208e | 66 | struct fwnode_handle *fwnode; |
2d00f35c SS |
67 | struct led_classdev cdev; |
68 | u32 num; | |
69 | u32 default_state; | |
70 | bool sloping; | |
2d00f35c SS |
71 | }; |
72 | ||
73 | struct an30259a { | |
74 | struct mutex mutex; /* held when writing to registers */ | |
75 | struct i2c_client *client; | |
76 | struct an30259a_led leds[AN30259A_MAX_LEDS]; | |
77 | struct regmap *regmap; | |
78 | int num_leds; | |
79 | }; | |
80 | ||
81 | static int an30259a_brightness_set(struct led_classdev *cdev, | |
82 | enum led_brightness brightness) | |
83 | { | |
84 | struct an30259a_led *led; | |
85 | int ret; | |
86 | unsigned int led_on; | |
87 | ||
88 | led = container_of(cdev, struct an30259a_led, cdev); | |
89 | mutex_lock(&led->chip->mutex); | |
90 | ||
91 | ret = regmap_read(led->chip->regmap, AN30259A_REG_LED_ON, &led_on); | |
92 | if (ret) | |
93 | goto error; | |
94 | ||
95 | switch (brightness) { | |
96 | case LED_OFF: | |
97 | led_on &= ~AN30259A_LED_EN(led->num); | |
98 | led_on &= ~AN30259A_LED_SLOPE(led->num); | |
99 | led->sloping = false; | |
100 | break; | |
101 | default: | |
102 | led_on |= AN30259A_LED_EN(led->num); | |
103 | if (led->sloping) | |
104 | led_on |= AN30259A_LED_SLOPE(led->num); | |
105 | ret = regmap_write(led->chip->regmap, | |
106 | AN30259A_REG_LEDCNT1(led->num), | |
107 | AN30259A_LED_DUTYMAX(0xf) | | |
108 | AN30259A_LED_DUTYMID(0xf)); | |
109 | if (ret) | |
110 | goto error; | |
111 | break; | |
112 | } | |
113 | ||
114 | ret = regmap_write(led->chip->regmap, AN30259A_REG_LED_ON, led_on); | |
115 | if (ret) | |
116 | goto error; | |
117 | ||
118 | ret = regmap_write(led->chip->regmap, AN30259A_REG_LEDCC(led->num), | |
119 | brightness); | |
120 | ||
121 | error: | |
122 | mutex_unlock(&led->chip->mutex); | |
123 | ||
124 | return ret; | |
125 | } | |
126 | ||
127 | static int an30259a_blink_set(struct led_classdev *cdev, | |
128 | unsigned long *delay_off, unsigned long *delay_on) | |
129 | { | |
130 | struct an30259a_led *led; | |
131 | int ret, num; | |
132 | unsigned int led_on; | |
133 | unsigned long off = *delay_off, on = *delay_on; | |
134 | ||
135 | led = container_of(cdev, struct an30259a_led, cdev); | |
136 | ||
137 | mutex_lock(&led->chip->mutex); | |
138 | num = led->num; | |
139 | ||
140 | /* slope time can only be a multiple of 500ms. */ | |
141 | if (off % AN30259A_SLOPE_RESOLUTION || on % AN30259A_SLOPE_RESOLUTION) { | |
142 | ret = -EINVAL; | |
143 | goto error; | |
144 | } | |
145 | ||
146 | /* up to a maximum of 7500ms. */ | |
147 | if (off > AN30259A_BLINK_MAX_TIME || on > AN30259A_BLINK_MAX_TIME) { | |
148 | ret = -EINVAL; | |
149 | goto error; | |
150 | } | |
151 | ||
152 | /* if no blink specified, default to 1 Hz. */ | |
153 | if (!off && !on) { | |
154 | *delay_off = off = 500; | |
155 | *delay_on = on = 500; | |
156 | } | |
157 | ||
158 | /* convert into values the HW will understand. */ | |
159 | off /= AN30259A_SLOPE_RESOLUTION; | |
160 | on /= AN30259A_SLOPE_RESOLUTION; | |
161 | ||
162 | /* duty min should be zero (=off), delay should be zero. */ | |
163 | ret = regmap_write(led->chip->regmap, AN30259A_REG_LEDCNT2(num), | |
164 | AN30259A_LED_DELAY(0) | AN30259A_LED_DUTYMIN(0)); | |
165 | if (ret) | |
166 | goto error; | |
167 | ||
168 | /* reset detention time (no "breathing" effect). */ | |
169 | ret = regmap_write(led->chip->regmap, AN30259A_REG_LEDCNT3(num), | |
170 | AN30259A_LED_DT1(0) | AN30259A_LED_DT2(0)); | |
171 | if (ret) | |
172 | goto error; | |
173 | ret = regmap_write(led->chip->regmap, AN30259A_REG_LEDCNT4(num), | |
174 | AN30259A_LED_DT3(0) | AN30259A_LED_DT4(0)); | |
175 | if (ret) | |
176 | goto error; | |
177 | ||
178 | /* slope time controls on/off cycle length. */ | |
179 | ret = regmap_write(led->chip->regmap, AN30259A_REG_SLOPE(num), | |
180 | AN30259A_LED_SLOPETIME1(off) | | |
181 | AN30259A_LED_SLOPETIME2(on)); | |
182 | if (ret) | |
183 | goto error; | |
184 | ||
185 | /* Finally, enable slope mode. */ | |
186 | ret = regmap_read(led->chip->regmap, AN30259A_REG_LED_ON, &led_on); | |
187 | if (ret) | |
188 | goto error; | |
189 | ||
190 | led_on |= AN30259A_LED_SLOPE(num) | AN30259A_LED_EN(led->num); | |
191 | ||
192 | ret = regmap_write(led->chip->regmap, AN30259A_REG_LED_ON, led_on); | |
193 | ||
194 | if (!ret) | |
195 | led->sloping = true; | |
196 | error: | |
197 | mutex_unlock(&led->chip->mutex); | |
198 | ||
199 | return ret; | |
200 | } | |
201 | ||
202 | static int an30259a_dt_init(struct i2c_client *client, | |
203 | struct an30259a *chip) | |
204 | { | |
8853c95e | 205 | struct device_node *np = dev_of_node(&client->dev), *child; |
2d00f35c SS |
206 | int count, ret; |
207 | int i = 0; | |
208 | const char *str; | |
209 | struct an30259a_led *led; | |
210 | ||
99a013c8 | 211 | count = of_get_available_child_count(np); |
2d00f35c SS |
212 | if (!count || count > AN30259A_MAX_LEDS) |
213 | return -EINVAL; | |
214 | ||
215 | for_each_available_child_of_node(np, child) { | |
216 | u32 source; | |
217 | ||
218 | ret = of_property_read_u32(child, "reg", &source); | |
219 | if (ret != 0 || !source || source > AN30259A_MAX_LEDS) { | |
220 | dev_err(&client->dev, "Couldn't read LED address: %d\n", | |
221 | ret); | |
222 | count--; | |
223 | continue; | |
224 | } | |
225 | ||
226 | led = &chip->leds[i]; | |
227 | ||
228 | led->num = source; | |
229 | led->chip = chip; | |
1817208e | 230 | led->fwnode = of_fwnode_handle(child); |
2d00f35c SS |
231 | |
232 | if (!of_property_read_string(child, "default-state", &str)) { | |
233 | if (!strcmp(str, "on")) | |
234 | led->default_state = STATE_ON; | |
235 | else if (!strcmp(str, "keep")) | |
236 | led->default_state = STATE_KEEP; | |
237 | else | |
238 | led->default_state = STATE_OFF; | |
239 | } | |
240 | ||
2d00f35c SS |
241 | i++; |
242 | } | |
243 | ||
244 | if (!count) | |
245 | return -EINVAL; | |
246 | ||
247 | chip->num_leds = i; | |
248 | ||
249 | return 0; | |
250 | } | |
251 | ||
252 | static const struct regmap_config an30259a_regmap_config = { | |
253 | .reg_bits = 8, | |
254 | .val_bits = 8, | |
255 | .max_register = AN30259A_REG_MAX, | |
256 | }; | |
257 | ||
258 | static void an30259a_init_default_state(struct an30259a_led *led) | |
259 | { | |
260 | struct an30259a *chip = led->chip; | |
261 | int led_on, err; | |
262 | ||
263 | switch (led->default_state) { | |
264 | case STATE_ON: | |
265 | led->cdev.brightness = LED_FULL; | |
266 | break; | |
267 | case STATE_KEEP: | |
268 | err = regmap_read(chip->regmap, AN30259A_REG_LED_ON, &led_on); | |
269 | if (err) | |
270 | break; | |
271 | ||
272 | if (!(led_on & AN30259A_LED_EN(led->num))) { | |
273 | led->cdev.brightness = LED_OFF; | |
274 | break; | |
275 | } | |
276 | regmap_read(chip->regmap, AN30259A_REG_LEDCC(led->num), | |
277 | &led->cdev.brightness); | |
278 | break; | |
279 | default: | |
280 | led->cdev.brightness = LED_OFF; | |
281 | } | |
282 | ||
283 | an30259a_brightness_set(&led->cdev, led->cdev.brightness); | |
284 | } | |
285 | ||
286 | static int an30259a_probe(struct i2c_client *client) | |
287 | { | |
288 | struct an30259a *chip; | |
289 | int i, err; | |
290 | ||
291 | chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); | |
292 | if (!chip) | |
293 | return -ENOMEM; | |
294 | ||
295 | err = an30259a_dt_init(client, chip); | |
296 | if (err < 0) | |
297 | return err; | |
298 | ||
299 | mutex_init(&chip->mutex); | |
300 | chip->client = client; | |
301 | i2c_set_clientdata(client, chip); | |
302 | ||
303 | chip->regmap = devm_regmap_init_i2c(client, &an30259a_regmap_config); | |
304 | ||
fc7b5028 CY |
305 | if (IS_ERR(chip->regmap)) { |
306 | err = PTR_ERR(chip->regmap); | |
307 | dev_err(&client->dev, "Failed to allocate register map: %d\n", | |
308 | err); | |
309 | goto exit; | |
310 | } | |
311 | ||
2d00f35c | 312 | for (i = 0; i < chip->num_leds; i++) { |
1817208e JA |
313 | struct led_init_data init_data = {}; |
314 | ||
2d00f35c SS |
315 | an30259a_init_default_state(&chip->leds[i]); |
316 | chip->leds[i].cdev.brightness_set_blocking = | |
317 | an30259a_brightness_set; | |
318 | chip->leds[i].cdev.blink_set = an30259a_blink_set; | |
319 | ||
1817208e JA |
320 | init_data.fwnode = chip->leds[i].fwnode; |
321 | init_data.devicename = AN30259A_NAME; | |
322 | init_data.default_label = ":"; | |
323 | ||
324 | err = devm_led_classdev_register_ext(&client->dev, | |
325 | &chip->leds[i].cdev, | |
326 | &init_data); | |
2d00f35c SS |
327 | if (err < 0) |
328 | goto exit; | |
329 | } | |
330 | return 0; | |
331 | ||
332 | exit: | |
333 | mutex_destroy(&chip->mutex); | |
334 | return err; | |
335 | } | |
336 | ||
337 | static int an30259a_remove(struct i2c_client *client) | |
338 | { | |
339 | struct an30259a *chip = i2c_get_clientdata(client); | |
340 | ||
341 | mutex_destroy(&chip->mutex); | |
342 | ||
343 | return 0; | |
344 | } | |
345 | ||
346 | static const struct of_device_id an30259a_match_table[] = { | |
347 | { .compatible = "panasonic,an30259a", }, | |
348 | { /* sentinel */ }, | |
349 | }; | |
350 | ||
351 | MODULE_DEVICE_TABLE(of, an30259a_match_table); | |
352 | ||
353 | static const struct i2c_device_id an30259a_id[] = { | |
354 | { "an30259a", 0 }, | |
355 | { /* sentinel */ }, | |
356 | }; | |
357 | MODULE_DEVICE_TABLE(i2c, an30259a_id); | |
358 | ||
359 | static struct i2c_driver an30259a_driver = { | |
360 | .driver = { | |
f3b357c2 | 361 | .name = "leds-an30259a", |
2d00f35c SS |
362 | .of_match_table = of_match_ptr(an30259a_match_table), |
363 | }, | |
364 | .probe_new = an30259a_probe, | |
365 | .remove = an30259a_remove, | |
366 | .id_table = an30259a_id, | |
367 | }; | |
368 | ||
369 | module_i2c_driver(an30259a_driver); | |
370 | ||
371 | MODULE_AUTHOR("Simon Shields <simon@lineageos.org>"); | |
f3b357c2 | 372 | MODULE_DESCRIPTION("AN30259A LED driver"); |
2d00f35c | 373 | MODULE_LICENSE("GPL v2"); |