2 * LED driver for Mediatek MT6323 PMIC
4 * Copyright (C) 2017 Sean Wang <sean.wang@mediatek.com>
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of
9 * the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 #include <linux/kernel.h>
17 #include <linux/leds.h>
18 #include <linux/mfd/mt6323/registers.h>
19 #include <linux/mfd/mt6397/core.h>
20 #include <linux/module.h>
22 #include <linux/platform_device.h>
23 #include <linux/regmap.h>
26 * Register field for MT6323_TOP_CKPDN0 to enable
27 * 32K clock common for LED device.
29 #define MT6323_RG_DRV_32K_CK_PDN BIT(11)
30 #define MT6323_RG_DRV_32K_CK_PDN_MASK BIT(11)
33 * Register field for MT6323_TOP_CKPDN2 to enable
34 * individual clock for LED device.
36 #define MT6323_RG_ISINK_CK_PDN(i) BIT(i)
37 #define MT6323_RG_ISINK_CK_PDN_MASK(i) BIT(i)
40 * Register field for MT6323_TOP_CKCON1 to select
43 #define MT6323_RG_ISINK_CK_SEL_MASK(i) (BIT(10) << (i))
46 * Register for MT6323_ISINK_CON0 to setup the
47 * duty cycle of the blink.
49 #define MT6323_ISINK_CON0(i) (MT6323_ISINK0_CON0 + 0x8 * (i))
50 #define MT6323_ISINK_DIM_DUTY_MASK (0x1f << 8)
51 #define MT6323_ISINK_DIM_DUTY(i) (((i) << 8) & \
52 MT6323_ISINK_DIM_DUTY_MASK)
54 /* Register to setup the period of the blink. */
55 #define MT6323_ISINK_CON1(i) (MT6323_ISINK0_CON1 + 0x8 * (i))
56 #define MT6323_ISINK_DIM_FSEL_MASK (0xffff)
57 #define MT6323_ISINK_DIM_FSEL(i) ((i) & MT6323_ISINK_DIM_FSEL_MASK)
59 /* Register to control the brightness. */
60 #define MT6323_ISINK_CON2(i) (MT6323_ISINK0_CON2 + 0x8 * (i))
61 #define MT6323_ISINK_CH_STEP_SHIFT 12
62 #define MT6323_ISINK_CH_STEP_MASK (0x7 << 12)
63 #define MT6323_ISINK_CH_STEP(i) (((i) << 12) & \
64 MT6323_ISINK_CH_STEP_MASK)
65 #define MT6323_ISINK_SFSTR0_TC_MASK (0x3 << 1)
66 #define MT6323_ISINK_SFSTR0_TC(i) (((i) << 1) & \
67 MT6323_ISINK_SFSTR0_TC_MASK)
68 #define MT6323_ISINK_SFSTR0_EN_MASK BIT(0)
69 #define MT6323_ISINK_SFSTR0_EN BIT(0)
71 /* Register to LED channel enablement. */
72 #define MT6323_ISINK_CH_EN_MASK(i) BIT(i)
73 #define MT6323_ISINK_CH_EN(i) BIT(i)
75 #define MT6323_MAX_PERIOD 10000
76 #define MT6323_MAX_LEDS 4
77 #define MT6323_MAX_BRIGHTNESS 6
78 #define MT6323_UNIT_DUTY 3125
79 #define MT6323_CAL_HW_DUTY(o, p) DIV_ROUND_CLOSEST((o) * 100000ul,\
80 (p) * MT6323_UNIT_DUTY)
85 * struct mt6323_led - state container for the LED device
86 * @id: the identifier in MT6323 LED device
87 * @parent: the pointer to MT6323 LED controller
88 * @cdev: LED class device for this LED device
89 * @current_brightness: current state of the LED device
93 struct mt6323_leds
*parent
;
94 struct led_classdev cdev
;
95 enum led_brightness current_brightness
;
99 * struct mt6323_leds - state container for holding LED controller
101 * @dev: the device pointer
102 * @hw: the underlying hardware providing shared
103 * bus for the register operations
104 * @lock: the lock among process context
105 * @led: the array that contains the state of individual
110 struct mt6397_chip
*hw
;
111 /* protect among process context */
113 struct mt6323_led
*led
[MT6323_MAX_LEDS
];
116 static int mt6323_led_hw_brightness(struct led_classdev
*cdev
,
117 enum led_brightness brightness
)
119 struct mt6323_led
*led
= container_of(cdev
, struct mt6323_led
, cdev
);
120 struct mt6323_leds
*leds
= led
->parent
;
121 struct regmap
*regmap
= leds
->hw
->regmap
;
122 u32 con2_mask
= 0, con2_val
= 0;
126 * Setup current output for the corresponding
129 con2_mask
|= MT6323_ISINK_CH_STEP_MASK
|
130 MT6323_ISINK_SFSTR0_TC_MASK
|
131 MT6323_ISINK_SFSTR0_EN_MASK
;
132 con2_val
|= MT6323_ISINK_CH_STEP(brightness
- 1) |
133 MT6323_ISINK_SFSTR0_TC(2) |
134 MT6323_ISINK_SFSTR0_EN
;
136 ret
= regmap_update_bits(regmap
, MT6323_ISINK_CON2(led
->id
),
137 con2_mask
, con2_val
);
141 static int mt6323_led_hw_off(struct led_classdev
*cdev
)
143 struct mt6323_led
*led
= container_of(cdev
, struct mt6323_led
, cdev
);
144 struct mt6323_leds
*leds
= led
->parent
;
145 struct regmap
*regmap
= leds
->hw
->regmap
;
149 status
= MT6323_ISINK_CH_EN(led
->id
);
150 ret
= regmap_update_bits(regmap
, MT6323_ISINK_EN_CTRL
,
151 MT6323_ISINK_CH_EN_MASK(led
->id
), ~status
);
155 usleep_range(100, 300);
156 ret
= regmap_update_bits(regmap
, MT6323_TOP_CKPDN2
,
157 MT6323_RG_ISINK_CK_PDN_MASK(led
->id
),
158 MT6323_RG_ISINK_CK_PDN(led
->id
));
165 static enum led_brightness
166 mt6323_get_led_hw_brightness(struct led_classdev
*cdev
)
168 struct mt6323_led
*led
= container_of(cdev
, struct mt6323_led
, cdev
);
169 struct mt6323_leds
*leds
= led
->parent
;
170 struct regmap
*regmap
= leds
->hw
->regmap
;
174 ret
= regmap_read(regmap
, MT6323_TOP_CKPDN2
, &status
);
178 if (status
& MT6323_RG_ISINK_CK_PDN_MASK(led
->id
))
181 ret
= regmap_read(regmap
, MT6323_ISINK_EN_CTRL
, &status
);
185 if (!(status
& MT6323_ISINK_CH_EN(led
->id
)))
188 ret
= regmap_read(regmap
, MT6323_ISINK_CON2(led
->id
), &status
);
192 return ((status
& MT6323_ISINK_CH_STEP_MASK
)
193 >> MT6323_ISINK_CH_STEP_SHIFT
) + 1;
196 static int mt6323_led_hw_on(struct led_classdev
*cdev
,
197 enum led_brightness brightness
)
199 struct mt6323_led
*led
= container_of(cdev
, struct mt6323_led
, cdev
);
200 struct mt6323_leds
*leds
= led
->parent
;
201 struct regmap
*regmap
= leds
->hw
->regmap
;
206 * Setup required clock source, enable the corresponding
207 * clock and channel and let work with continuous blink as
210 ret
= regmap_update_bits(regmap
, MT6323_TOP_CKCON1
,
211 MT6323_RG_ISINK_CK_SEL_MASK(led
->id
), 0);
215 status
= MT6323_RG_ISINK_CK_PDN(led
->id
);
216 ret
= regmap_update_bits(regmap
, MT6323_TOP_CKPDN2
,
217 MT6323_RG_ISINK_CK_PDN_MASK(led
->id
),
222 usleep_range(100, 300);
224 ret
= regmap_update_bits(regmap
, MT6323_ISINK_EN_CTRL
,
225 MT6323_ISINK_CH_EN_MASK(led
->id
),
226 MT6323_ISINK_CH_EN(led
->id
));
230 ret
= mt6323_led_hw_brightness(cdev
, brightness
);
234 ret
= regmap_update_bits(regmap
, MT6323_ISINK_CON0(led
->id
),
235 MT6323_ISINK_DIM_DUTY_MASK
,
236 MT6323_ISINK_DIM_DUTY(31));
240 ret
= regmap_update_bits(regmap
, MT6323_ISINK_CON1(led
->id
),
241 MT6323_ISINK_DIM_FSEL_MASK
,
242 MT6323_ISINK_DIM_FSEL(1000));
249 static int mt6323_led_set_blink(struct led_classdev
*cdev
,
250 unsigned long *delay_on
,
251 unsigned long *delay_off
)
253 struct mt6323_led
*led
= container_of(cdev
, struct mt6323_led
, cdev
);
254 struct mt6323_leds
*leds
= led
->parent
;
255 struct regmap
*regmap
= leds
->hw
->regmap
;
256 unsigned long period
;
261 * Units are in ms, if over the hardware able
262 * to support, fallback into software blink
264 period
= *delay_on
+ *delay_off
;
266 if (period
> MT6323_MAX_PERIOD
)
270 * LED subsystem requires a default user
271 * friendly blink pattern for the LED so using
272 * 1Hz duty cycle 50% here if without specific
273 * value delay_on and delay off being assigned.
275 if (!*delay_on
&& !*delay_off
) {
281 * Calculate duty_hw based on the percentage of period during
282 * which the led is ON.
284 duty_hw
= MT6323_CAL_HW_DUTY(*delay_on
, period
);
286 /* hardware doesn't support zero duty cycle. */
290 mutex_lock(&leds
->lock
);
292 * Set max_brightness as the software blink behavior
293 * when no blink brightness.
295 if (!led
->current_brightness
) {
296 ret
= mt6323_led_hw_on(cdev
, cdev
->max_brightness
);
299 led
->current_brightness
= cdev
->max_brightness
;
302 ret
= regmap_update_bits(regmap
, MT6323_ISINK_CON0(led
->id
),
303 MT6323_ISINK_DIM_DUTY_MASK
,
304 MT6323_ISINK_DIM_DUTY(duty_hw
- 1));
308 ret
= regmap_update_bits(regmap
, MT6323_ISINK_CON1(led
->id
),
309 MT6323_ISINK_DIM_FSEL_MASK
,
310 MT6323_ISINK_DIM_FSEL(period
- 1));
312 mutex_unlock(&leds
->lock
);
317 static int mt6323_led_set_brightness(struct led_classdev
*cdev
,
318 enum led_brightness brightness
)
320 struct mt6323_led
*led
= container_of(cdev
, struct mt6323_led
, cdev
);
321 struct mt6323_leds
*leds
= led
->parent
;
324 mutex_lock(&leds
->lock
);
326 if (!led
->current_brightness
&& brightness
) {
327 ret
= mt6323_led_hw_on(cdev
, brightness
);
330 } else if (brightness
) {
331 ret
= mt6323_led_hw_brightness(cdev
, brightness
);
335 ret
= mt6323_led_hw_off(cdev
);
340 led
->current_brightness
= brightness
;
342 mutex_unlock(&leds
->lock
);
347 static int mt6323_led_set_dt_default(struct led_classdev
*cdev
,
348 struct device_node
*np
)
350 struct mt6323_led
*led
= container_of(cdev
, struct mt6323_led
, cdev
);
354 led
->cdev
.name
= of_get_property(np
, "label", NULL
) ? : np
->name
;
355 led
->cdev
.default_trigger
= of_get_property(np
,
356 "linux,default-trigger",
359 state
= of_get_property(np
, "default-state", NULL
);
361 if (!strcmp(state
, "keep")) {
362 ret
= mt6323_get_led_hw_brightness(cdev
);
365 led
->current_brightness
= ret
;
367 } else if (!strcmp(state
, "on")) {
369 mt6323_led_set_brightness(cdev
, cdev
->max_brightness
);
371 ret
= mt6323_led_set_brightness(cdev
, LED_OFF
);
378 static int mt6323_led_probe(struct platform_device
*pdev
)
380 struct device
*dev
= &pdev
->dev
;
381 struct device_node
*np
= pdev
->dev
.of_node
;
382 struct device_node
*child
;
383 struct mt6397_chip
*hw
= dev_get_drvdata(pdev
->dev
.parent
);
384 struct mt6323_leds
*leds
;
385 struct mt6323_led
*led
;
390 leds
= devm_kzalloc(dev
, sizeof(*leds
), GFP_KERNEL
);
394 platform_set_drvdata(pdev
, leds
);
398 * leds->hw points to the underlying bus for the register
402 mutex_init(&leds
->lock
);
404 status
= MT6323_RG_DRV_32K_CK_PDN
;
405 ret
= regmap_update_bits(leds
->hw
->regmap
, MT6323_TOP_CKPDN0
,
406 MT6323_RG_DRV_32K_CK_PDN_MASK
, ~status
);
409 "Failed to update MT6323_TOP_CKPDN0 Register\n");
413 for_each_available_child_of_node(np
, child
) {
414 ret
= of_property_read_u32(child
, "reg", ®
);
416 dev_err(dev
, "Failed to read led 'reg' property\n");
420 if (reg
>= MT6323_MAX_LEDS
|| leds
->led
[reg
]) {
421 dev_err(dev
, "Invalid led reg %u\n", reg
);
426 led
= devm_kzalloc(dev
, sizeof(*led
), GFP_KERNEL
);
432 leds
->led
[reg
] = led
;
433 leds
->led
[reg
]->id
= reg
;
434 leds
->led
[reg
]->cdev
.max_brightness
= MT6323_MAX_BRIGHTNESS
;
435 leds
->led
[reg
]->cdev
.brightness_set_blocking
=
436 mt6323_led_set_brightness
;
437 leds
->led
[reg
]->cdev
.blink_set
= mt6323_led_set_blink
;
438 leds
->led
[reg
]->cdev
.brightness_get
=
439 mt6323_get_led_hw_brightness
;
440 leds
->led
[reg
]->parent
= leds
;
442 ret
= mt6323_led_set_dt_default(&leds
->led
[reg
]->cdev
, child
);
445 "Failed to LED set default from devicetree\n");
449 ret
= devm_led_classdev_register(dev
, &leds
->led
[reg
]->cdev
);
451 dev_err(&pdev
->dev
, "Failed to register LED: %d\n",
455 leds
->led
[reg
]->cdev
.dev
->of_node
= child
;
465 static int mt6323_led_remove(struct platform_device
*pdev
)
467 struct mt6323_leds
*leds
= platform_get_drvdata(pdev
);
470 /* Turn the LEDs off on driver removal. */
471 for (i
= 0 ; leds
->led
[i
] ; i
++)
472 mt6323_led_hw_off(&leds
->led
[i
]->cdev
);
474 regmap_update_bits(leds
->hw
->regmap
, MT6323_TOP_CKPDN0
,
475 MT6323_RG_DRV_32K_CK_PDN_MASK
,
476 MT6323_RG_DRV_32K_CK_PDN
);
478 mutex_destroy(&leds
->lock
);
483 static const struct of_device_id mt6323_led_dt_match
[] = {
484 { .compatible
= "mediatek,mt6323-led" },
487 MODULE_DEVICE_TABLE(of
, mt6323_led_dt_match
);
489 static struct platform_driver mt6323_led_driver
= {
490 .probe
= mt6323_led_probe
,
491 .remove
= mt6323_led_remove
,
493 .name
= "mt6323-led",
494 .of_match_table
= mt6323_led_dt_match
,
498 module_platform_driver(mt6323_led_driver
);
500 MODULE_DESCRIPTION("LED driver for Mediatek MT6323 PMIC");
501 MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>");
502 MODULE_LICENSE("GPL");