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 | struct an30259a; |
59 | ||
60 | struct an30259a_led { | |
61 | struct an30259a *chip; | |
1817208e | 62 | struct fwnode_handle *fwnode; |
2d00f35c SS |
63 | struct led_classdev cdev; |
64 | u32 num; | |
5ff422a7 | 65 | enum led_default_state default_state; |
2d00f35c | 66 | bool sloping; |
2d00f35c SS |
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(led->chip->regmap, AN30259A_REG_LED_ON, &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(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(led->chip->regmap, AN30259A_REG_LED_ON, led_on); | |
111 | if (ret) | |
112 | goto error; | |
113 | ||
114 | ret = regmap_write(led->chip->regmap, AN30259A_REG_LEDCC(led->num), | |
115 | brightness); | |
116 | ||
117 | error: | |
118 | mutex_unlock(&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(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(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(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(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(led->chip->regmap, AN30259A_REG_LED_ON, &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(led->chip->regmap, AN30259A_REG_LED_ON, led_on); | |
189 | ||
190 | if (!ret) | |
191 | led->sloping = true; | |
192 | error: | |
193 | mutex_unlock(&led->chip->mutex); | |
194 | ||
195 | return ret; | |
196 | } | |
197 | ||
198 | static int an30259a_dt_init(struct i2c_client *client, | |
199 | struct an30259a *chip) | |
200 | { | |
8853c95e | 201 | struct device_node *np = dev_of_node(&client->dev), *child; |
2d00f35c SS |
202 | int count, ret; |
203 | int i = 0; | |
2d00f35c SS |
204 | struct an30259a_led *led; |
205 | ||
99a013c8 | 206 | count = of_get_available_child_count(np); |
2d00f35c SS |
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(child, "reg", &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; | |
1817208e | 225 | led->fwnode = of_fwnode_handle(child); |
5ff422a7 | 226 | led->default_state = led_init_default_state_get(led->fwnode); |
2d00f35c | 227 | |
2d00f35c SS |
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) { | |
5ff422a7 | 251 | case LEDS_DEFSTATE_ON: |
2d00f35c SS |
252 | led->cdev.brightness = LED_FULL; |
253 | break; | |
5ff422a7 | 254 | case LEDS_DEFSTATE_KEEP: |
2d00f35c SS |
255 | err = regmap_read(chip->regmap, AN30259A_REG_LED_ON, &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(chip->regmap, AN30259A_REG_LEDCC(led->num), | |
264 | &led->cdev.brightness); | |
265 | break; | |
266 | default: | |
267 | led->cdev.brightness = LED_OFF; | |
268 | } | |
269 | ||
270 | an30259a_brightness_set(&led->cdev, 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(&client->dev, 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, chip); | |
289 | ||
290 | chip->regmap = devm_regmap_init_i2c(client, &an30259a_regmap_config); | |
291 | ||
fc7b5028 CY |
292 | if (IS_ERR(chip->regmap)) { |
293 | err = PTR_ERR(chip->regmap); | |
294 | dev_err(&client->dev, "Failed to allocate register map: %d\n", | |
295 | err); | |
296 | goto exit; | |
297 | } | |
298 | ||
2d00f35c | 299 | for (i = 0; i < chip->num_leds; i++) { |
1817208e JA |
300 | struct led_init_data init_data = {}; |
301 | ||
2d00f35c SS |
302 | an30259a_init_default_state(&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 | ||
1817208e JA |
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(&client->dev, | |
312 | &chip->leds[i].cdev, | |
313 | &init_data); | |
2d00f35c SS |
314 | if (err < 0) |
315 | goto exit; | |
316 | } | |
317 | return 0; | |
318 | ||
319 | exit: | |
320 | mutex_destroy(&chip->mutex); | |
321 | return err; | |
322 | } | |
323 | ||
ed5c2f5f | 324 | static void an30259a_remove(struct i2c_client *client) |
2d00f35c SS |
325 | { |
326 | struct an30259a *chip = i2c_get_clientdata(client); | |
327 | ||
328 | mutex_destroy(&chip->mutex); | |
2d00f35c SS |
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 = { | |
f3b357c2 | 346 | .name = "leds-an30259a", |
2d00f35c SS |
347 | .of_match_table = of_match_ptr(an30259a_match_table), |
348 | }, | |
d9ff8a8e | 349 | .probe = an30259a_probe, |
2d00f35c SS |
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>"); | |
f3b357c2 | 357 | MODULE_DESCRIPTION("AN30259A LED driver"); |
2d00f35c | 358 | MODULE_LICENSE("GPL v2"); |