mediatek: update to latest kernel patchset from v4.13-rc
[openwrt/openwrt.git] / target / linux / mediatek / patches-4.9 / 0020-leds-Add-LED-support-for-MT6323-PMIC.patch
1 From e482f9590f2e831c68bcf85e3f9f4c88bbd3329f Mon Sep 17 00:00:00 2001
2 From: Sean Wang <sean.wang@mediatek.com>
3 Date: Mon, 20 Mar 2017 14:47:26 +0800
4 Subject: [PATCH 20/57] leds: Add LED support for MT6323 PMIC
5
6 MT6323 PMIC is a multi-function device that includes LED function.
7 It allows attaching up to 4 LEDs which can either be on, off or dimmed
8 and/or blinked with the controller.
9
10 Signed-off-by: Sean Wang <sean.wang@mediatek.com>
11 Reviewed-by: Jacek Anaszewski <jacek.anaszewski@gmail.com>
12 ---
13 drivers/leds/Kconfig | 8 +
14 drivers/leds/leds-mt6323.c | 502 +++++++++++++++++++++++++++++++++++++++++++++
15 2 files changed, 510 insertions(+)
16 create mode 100644 drivers/leds/leds-mt6323.c
17
18 --- a/drivers/leds/Kconfig
19 +++ b/drivers/leds/Kconfig
20 @@ -117,6 +117,14 @@ config LEDS_MIKROTIK_RB532
21 This option enables support for the so called "User LED" of
22 Mikrotik's Routerboard 532.
23
24 +config LEDS_MT6323
25 + tristate "LED Support for Mediatek MT6323 PMIC"
26 + depends on LEDS_CLASS
27 + depends on MFD_MT6397
28 + help
29 + This option enables support for on-chip LED drivers found on
30 + Mediatek MT6323 PMIC.
31 +
32 config LEDS_S3C24XX
33 tristate "LED Support for Samsung S3C24XX GPIO LEDs"
34 depends on LEDS_CLASS
35 --- /dev/null
36 +++ b/drivers/leds/leds-mt6323.c
37 @@ -0,0 +1,502 @@
38 +/*
39 + * LED driver for Mediatek MT6323 PMIC
40 + *
41 + * Copyright (C) 2017 Sean Wang <sean.wang@mediatek.com>
42 + *
43 + * This program is free software; you can redistribute it and/or
44 + * modify it under the terms of the GNU General Public License as
45 + * published by the Free Software Foundation; either version 2 of
46 + * the License, or (at your option) any later version.
47 + *
48 + * This program is distributed in the hope that it will be useful,
49 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
50 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
51 + * GNU General Public License for more details.
52 + */
53 +#include <linux/kernel.h>
54 +#include <linux/leds.h>
55 +#include <linux/mfd/mt6323/registers.h>
56 +#include <linux/mfd/mt6397/core.h>
57 +#include <linux/module.h>
58 +#include <linux/of.h>
59 +#include <linux/platform_device.h>
60 +#include <linux/regmap.h>
61 +
62 +/*
63 + * Register field for MT6323_TOP_CKPDN0 to enable
64 + * 32K clock common for LED device.
65 + */
66 +#define MT6323_RG_DRV_32K_CK_PDN BIT(11)
67 +#define MT6323_RG_DRV_32K_CK_PDN_MASK BIT(11)
68 +
69 +/*
70 + * Register field for MT6323_TOP_CKPDN2 to enable
71 + * individual clock for LED device.
72 + */
73 +#define MT6323_RG_ISINK_CK_PDN(i) BIT(i)
74 +#define MT6323_RG_ISINK_CK_PDN_MASK(i) BIT(i)
75 +
76 +/*
77 + * Register field for MT6323_TOP_CKCON1 to select
78 + * clock source.
79 + */
80 +#define MT6323_RG_ISINK_CK_SEL_MASK(i) (BIT(10) << (i))
81 +
82 +/*
83 + * Register for MT6323_ISINK_CON0 to setup the
84 + * duty cycle of the blink.
85 + */
86 +#define MT6323_ISINK_CON0(i) (MT6323_ISINK0_CON0 + 0x8 * (i))
87 +#define MT6323_ISINK_DIM_DUTY_MASK (0x1f << 8)
88 +#define MT6323_ISINK_DIM_DUTY(i) (((i) << 8) & \
89 + MT6323_ISINK_DIM_DUTY_MASK)
90 +
91 +/* Register to setup the period of the blink. */
92 +#define MT6323_ISINK_CON1(i) (MT6323_ISINK0_CON1 + 0x8 * (i))
93 +#define MT6323_ISINK_DIM_FSEL_MASK (0xffff)
94 +#define MT6323_ISINK_DIM_FSEL(i) ((i) & MT6323_ISINK_DIM_FSEL_MASK)
95 +
96 +/* Register to control the brightness. */
97 +#define MT6323_ISINK_CON2(i) (MT6323_ISINK0_CON2 + 0x8 * (i))
98 +#define MT6323_ISINK_CH_STEP_SHIFT 12
99 +#define MT6323_ISINK_CH_STEP_MASK (0x7 << 12)
100 +#define MT6323_ISINK_CH_STEP(i) (((i) << 12) & \
101 + MT6323_ISINK_CH_STEP_MASK)
102 +#define MT6323_ISINK_SFSTR0_TC_MASK (0x3 << 1)
103 +#define MT6323_ISINK_SFSTR0_TC(i) (((i) << 1) & \
104 + MT6323_ISINK_SFSTR0_TC_MASK)
105 +#define MT6323_ISINK_SFSTR0_EN_MASK BIT(0)
106 +#define MT6323_ISINK_SFSTR0_EN BIT(0)
107 +
108 +/* Register to LED channel enablement. */
109 +#define MT6323_ISINK_CH_EN_MASK(i) BIT(i)
110 +#define MT6323_ISINK_CH_EN(i) BIT(i)
111 +
112 +#define MT6323_MAX_PERIOD 10000
113 +#define MT6323_MAX_LEDS 4
114 +#define MT6323_MAX_BRIGHTNESS 6
115 +#define MT6323_UNIT_DUTY 3125
116 +#define MT6323_CAL_HW_DUTY(o, p) DIV_ROUND_CLOSEST((o) * 100000ul,\
117 + (p) * MT6323_UNIT_DUTY)
118 +
119 +struct mt6323_leds;
120 +
121 +/**
122 + * struct mt6323_led - state container for the LED device
123 + * @id: the identifier in MT6323 LED device
124 + * @parent: the pointer to MT6323 LED controller
125 + * @cdev: LED class device for this LED device
126 + * @current_brightness: current state of the LED device
127 + */
128 +struct mt6323_led {
129 + int id;
130 + struct mt6323_leds *parent;
131 + struct led_classdev cdev;
132 + enum led_brightness current_brightness;
133 +};
134 +
135 +/**
136 + * struct mt6323_leds - state container for holding LED controller
137 + * of the driver
138 + * @dev: the device pointer
139 + * @hw: the underlying hardware providing shared
140 + * bus for the register operations
141 + * @lock: the lock among process context
142 + * @led: the array that contains the state of individual
143 + * LED device
144 + */
145 +struct mt6323_leds {
146 + struct device *dev;
147 + struct mt6397_chip *hw;
148 + /* protect among process context */
149 + struct mutex lock;
150 + struct mt6323_led *led[MT6323_MAX_LEDS];
151 +};
152 +
153 +static int mt6323_led_hw_brightness(struct led_classdev *cdev,
154 + enum led_brightness brightness)
155 +{
156 + struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev);
157 + struct mt6323_leds *leds = led->parent;
158 + struct regmap *regmap = leds->hw->regmap;
159 + u32 con2_mask = 0, con2_val = 0;
160 + int ret;
161 +
162 + /*
163 + * Setup current output for the corresponding
164 + * brightness level.
165 + */
166 + con2_mask |= MT6323_ISINK_CH_STEP_MASK |
167 + MT6323_ISINK_SFSTR0_TC_MASK |
168 + MT6323_ISINK_SFSTR0_EN_MASK;
169 + con2_val |= MT6323_ISINK_CH_STEP(brightness - 1) |
170 + MT6323_ISINK_SFSTR0_TC(2) |
171 + MT6323_ISINK_SFSTR0_EN;
172 +
173 + ret = regmap_update_bits(regmap, MT6323_ISINK_CON2(led->id),
174 + con2_mask, con2_val);
175 + return ret;
176 +}
177 +
178 +static int mt6323_led_hw_off(struct led_classdev *cdev)
179 +{
180 + struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev);
181 + struct mt6323_leds *leds = led->parent;
182 + struct regmap *regmap = leds->hw->regmap;
183 + unsigned int status;
184 + int ret;
185 +
186 + status = MT6323_ISINK_CH_EN(led->id);
187 + ret = regmap_update_bits(regmap, MT6323_ISINK_EN_CTRL,
188 + MT6323_ISINK_CH_EN_MASK(led->id), ~status);
189 + if (ret < 0)
190 + return ret;
191 +
192 + usleep_range(100, 300);
193 + ret = regmap_update_bits(regmap, MT6323_TOP_CKPDN2,
194 + MT6323_RG_ISINK_CK_PDN_MASK(led->id),
195 + MT6323_RG_ISINK_CK_PDN(led->id));
196 + if (ret < 0)
197 + return ret;
198 +
199 + return 0;
200 +}
201 +
202 +static enum led_brightness
203 +mt6323_get_led_hw_brightness(struct led_classdev *cdev)
204 +{
205 + struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev);
206 + struct mt6323_leds *leds = led->parent;
207 + struct regmap *regmap = leds->hw->regmap;
208 + unsigned int status;
209 + int ret;
210 +
211 + ret = regmap_read(regmap, MT6323_TOP_CKPDN2, &status);
212 + if (ret < 0)
213 + return ret;
214 +
215 + if (status & MT6323_RG_ISINK_CK_PDN_MASK(led->id))
216 + return 0;
217 +
218 + ret = regmap_read(regmap, MT6323_ISINK_EN_CTRL, &status);
219 + if (ret < 0)
220 + return ret;
221 +
222 + if (!(status & MT6323_ISINK_CH_EN(led->id)))
223 + return 0;
224 +
225 + ret = regmap_read(regmap, MT6323_ISINK_CON2(led->id), &status);
226 + if (ret < 0)
227 + return ret;
228 +
229 + return ((status & MT6323_ISINK_CH_STEP_MASK)
230 + >> MT6323_ISINK_CH_STEP_SHIFT) + 1;
231 +}
232 +
233 +static int mt6323_led_hw_on(struct led_classdev *cdev,
234 + enum led_brightness brightness)
235 +{
236 + struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev);
237 + struct mt6323_leds *leds = led->parent;
238 + struct regmap *regmap = leds->hw->regmap;
239 + unsigned int status;
240 + int ret;
241 +
242 + /*
243 + * Setup required clock source, enable the corresponding
244 + * clock and channel and let work with continuous blink as
245 + * the default.
246 + */
247 + ret = regmap_update_bits(regmap, MT6323_TOP_CKCON1,
248 + MT6323_RG_ISINK_CK_SEL_MASK(led->id), 0);
249 + if (ret < 0)
250 + return ret;
251 +
252 + status = MT6323_RG_ISINK_CK_PDN(led->id);
253 + ret = regmap_update_bits(regmap, MT6323_TOP_CKPDN2,
254 + MT6323_RG_ISINK_CK_PDN_MASK(led->id),
255 + ~status);
256 + if (ret < 0)
257 + return ret;
258 +
259 + usleep_range(100, 300);
260 +
261 + ret = regmap_update_bits(regmap, MT6323_ISINK_EN_CTRL,
262 + MT6323_ISINK_CH_EN_MASK(led->id),
263 + MT6323_ISINK_CH_EN(led->id));
264 + if (ret < 0)
265 + return ret;
266 +
267 + ret = mt6323_led_hw_brightness(cdev, brightness);
268 + if (ret < 0)
269 + return ret;
270 +
271 + ret = regmap_update_bits(regmap, MT6323_ISINK_CON0(led->id),
272 + MT6323_ISINK_DIM_DUTY_MASK,
273 + MT6323_ISINK_DIM_DUTY(31));
274 + if (ret < 0)
275 + return ret;
276 +
277 + ret = regmap_update_bits(regmap, MT6323_ISINK_CON1(led->id),
278 + MT6323_ISINK_DIM_FSEL_MASK,
279 + MT6323_ISINK_DIM_FSEL(1000));
280 + if (ret < 0)
281 + return ret;
282 +
283 + return 0;
284 +}
285 +
286 +static int mt6323_led_set_blink(struct led_classdev *cdev,
287 + unsigned long *delay_on,
288 + unsigned long *delay_off)
289 +{
290 + struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev);
291 + struct mt6323_leds *leds = led->parent;
292 + struct regmap *regmap = leds->hw->regmap;
293 + unsigned long period;
294 + u8 duty_hw;
295 + int ret;
296 +
297 + /*
298 + * Units are in ms, if over the hardware able
299 + * to support, fallback into software blink
300 + */
301 + period = *delay_on + *delay_off;
302 +
303 + if (period > MT6323_MAX_PERIOD)
304 + return -EINVAL;
305 +
306 + /*
307 + * LED subsystem requires a default user
308 + * friendly blink pattern for the LED so using
309 + * 1Hz duty cycle 50% here if without specific
310 + * value delay_on and delay off being assigned.
311 + */
312 + if (!*delay_on && !*delay_off) {
313 + *delay_on = 500;
314 + *delay_off = 500;
315 + }
316 +
317 + /*
318 + * Calculate duty_hw based on the percentage of period during
319 + * which the led is ON.
320 + */
321 + duty_hw = MT6323_CAL_HW_DUTY(*delay_on, period);
322 +
323 + /* hardware doesn't support zero duty cycle. */
324 + if (!duty_hw)
325 + return -EINVAL;
326 +
327 + mutex_lock(&leds->lock);
328 + /*
329 + * Set max_brightness as the software blink behavior
330 + * when no blink brightness.
331 + */
332 + if (!led->current_brightness) {
333 + ret = mt6323_led_hw_on(cdev, cdev->max_brightness);
334 + if (ret < 0)
335 + goto out;
336 + led->current_brightness = cdev->max_brightness;
337 + }
338 +
339 + ret = regmap_update_bits(regmap, MT6323_ISINK_CON0(led->id),
340 + MT6323_ISINK_DIM_DUTY_MASK,
341 + MT6323_ISINK_DIM_DUTY(duty_hw - 1));
342 + if (ret < 0)
343 + goto out;
344 +
345 + ret = regmap_update_bits(regmap, MT6323_ISINK_CON1(led->id),
346 + MT6323_ISINK_DIM_FSEL_MASK,
347 + MT6323_ISINK_DIM_FSEL(period - 1));
348 +out:
349 + mutex_unlock(&leds->lock);
350 +
351 + return ret;
352 +}
353 +
354 +static int mt6323_led_set_brightness(struct led_classdev *cdev,
355 + enum led_brightness brightness)
356 +{
357 + struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev);
358 + struct mt6323_leds *leds = led->parent;
359 + int ret;
360 +
361 + mutex_lock(&leds->lock);
362 +
363 + if (!led->current_brightness && brightness) {
364 + ret = mt6323_led_hw_on(cdev, brightness);
365 + if (ret < 0)
366 + goto out;
367 + } else if (brightness) {
368 + ret = mt6323_led_hw_brightness(cdev, brightness);
369 + if (ret < 0)
370 + goto out;
371 + } else {
372 + ret = mt6323_led_hw_off(cdev);
373 + if (ret < 0)
374 + goto out;
375 + }
376 +
377 + led->current_brightness = brightness;
378 +out:
379 + mutex_unlock(&leds->lock);
380 +
381 + return ret;
382 +}
383 +
384 +static int mt6323_led_set_dt_default(struct led_classdev *cdev,
385 + struct device_node *np)
386 +{
387 + struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev);
388 + const char *state;
389 + int ret = 0;
390 +
391 + led->cdev.name = of_get_property(np, "label", NULL) ? : np->name;
392 + led->cdev.default_trigger = of_get_property(np,
393 + "linux,default-trigger",
394 + NULL);
395 +
396 + state = of_get_property(np, "default-state", NULL);
397 + if (state) {
398 + if (!strcmp(state, "keep")) {
399 + ret = mt6323_get_led_hw_brightness(cdev);
400 + if (ret < 0)
401 + return ret;
402 + led->current_brightness = ret;
403 + ret = 0;
404 + } else if (!strcmp(state, "on")) {
405 + ret =
406 + mt6323_led_set_brightness(cdev, cdev->max_brightness);
407 + } else {
408 + ret = mt6323_led_set_brightness(cdev, LED_OFF);
409 + }
410 + }
411 +
412 + return ret;
413 +}
414 +
415 +static int mt6323_led_probe(struct platform_device *pdev)
416 +{
417 + struct device *dev = &pdev->dev;
418 + struct device_node *np = pdev->dev.of_node;
419 + struct device_node *child;
420 + struct mt6397_chip *hw = dev_get_drvdata(pdev->dev.parent);
421 + struct mt6323_leds *leds;
422 + struct mt6323_led *led;
423 + int ret;
424 + unsigned int status;
425 + u32 reg;
426 +
427 + leds = devm_kzalloc(dev, sizeof(*leds), GFP_KERNEL);
428 + if (!leds)
429 + return -ENOMEM;
430 +
431 + platform_set_drvdata(pdev, leds);
432 + leds->dev = dev;
433 +
434 + /*
435 + * leds->hw points to the underlying bus for the register
436 + * controlled.
437 + */
438 + leds->hw = hw;
439 + mutex_init(&leds->lock);
440 +
441 + status = MT6323_RG_DRV_32K_CK_PDN;
442 + ret = regmap_update_bits(leds->hw->regmap, MT6323_TOP_CKPDN0,
443 + MT6323_RG_DRV_32K_CK_PDN_MASK, ~status);
444 + if (ret < 0) {
445 + dev_err(leds->dev,
446 + "Failed to update MT6323_TOP_CKPDN0 Register\n");
447 + return ret;
448 + }
449 +
450 + for_each_available_child_of_node(np, child) {
451 + ret = of_property_read_u32(child, "reg", &reg);
452 + if (ret) {
453 + dev_err(dev, "Failed to read led 'reg' property\n");
454 + goto put_child_node;
455 + }
456 +
457 + if (reg < 0 || reg > MT6323_MAX_LEDS || leds->led[reg]) {
458 + dev_err(dev, "Invalid led reg %u\n", reg);
459 + ret = -EINVAL;
460 + goto put_child_node;
461 + }
462 +
463 + led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
464 + if (!led) {
465 + ret = -ENOMEM;
466 + goto put_child_node;
467 + }
468 +
469 + leds->led[reg] = led;
470 + leds->led[reg]->id = reg;
471 + leds->led[reg]->cdev.max_brightness = MT6323_MAX_BRIGHTNESS;
472 + leds->led[reg]->cdev.brightness_set_blocking =
473 + mt6323_led_set_brightness;
474 + leds->led[reg]->cdev.blink_set = mt6323_led_set_blink;
475 + leds->led[reg]->cdev.brightness_get =
476 + mt6323_get_led_hw_brightness;
477 + leds->led[reg]->parent = leds;
478 +
479 + ret = mt6323_led_set_dt_default(&leds->led[reg]->cdev, child);
480 + if (ret < 0) {
481 + dev_err(leds->dev,
482 + "Failed to LED set default from devicetree\n");
483 + goto put_child_node;
484 + }
485 +
486 + ret = devm_led_classdev_register(dev, &leds->led[reg]->cdev);
487 + if (ret) {
488 + dev_err(&pdev->dev, "Failed to register LED: %d\n",
489 + ret);
490 + goto put_child_node;
491 + }
492 + leds->led[reg]->cdev.dev->of_node = child;
493 + }
494 +
495 + return 0;
496 +
497 +put_child_node:
498 + of_node_put(child);
499 + return ret;
500 +}
501 +
502 +static int mt6323_led_remove(struct platform_device *pdev)
503 +{
504 + struct mt6323_leds *leds = platform_get_drvdata(pdev);
505 + int i;
506 +
507 + /* Turn the LEDs off on driver removal. */
508 + for (i = 0 ; leds->led[i] ; i++)
509 + mt6323_led_hw_off(&leds->led[i]->cdev);
510 +
511 + regmap_update_bits(leds->hw->regmap, MT6323_TOP_CKPDN0,
512 + MT6323_RG_DRV_32K_CK_PDN_MASK,
513 + MT6323_RG_DRV_32K_CK_PDN);
514 +
515 + mutex_destroy(&leds->lock);
516 +
517 + return 0;
518 +}
519 +
520 +static const struct of_device_id mt6323_led_dt_match[] = {
521 + { .compatible = "mediatek,mt6323-led" },
522 + {},
523 +};
524 +MODULE_DEVICE_TABLE(of, mt6323_led_dt_match);
525 +
526 +static struct platform_driver mt6323_led_driver = {
527 + .probe = mt6323_led_probe,
528 + .remove = mt6323_led_remove,
529 + .driver = {
530 + .name = "mt6323-led",
531 + .of_match_table = mt6323_led_dt_match,
532 + },
533 +};
534 +
535 +module_platform_driver(mt6323_led_driver);
536 +
537 +MODULE_DESCRIPTION("LED driver for Mediatek MT6323 PMIC");
538 +MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>");
539 +MODULE_LICENSE("GPL");