1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * LED driver for Mediatek MT6323 PMIC
5 * Copyright (C) 2017 Sean Wang <sean.wang@mediatek.com>
7 #include <linux/kernel.h>
8 #include <linux/leds.h>
9 #include <linux/mfd/mt6323/registers.h>
10 #include <linux/mfd/mt6397/core.h>
11 #include <linux/module.h>
13 #include <linux/platform_device.h>
14 #include <linux/regmap.h>
17 * Register field for TOP_CKPDN0 to enable
18 * 32K clock common for LED device.
20 #define RG_DRV_32K_CK_PDN BIT(11)
21 #define RG_DRV_32K_CK_PDN_MASK BIT(11)
23 /* 32K/1M/6M clock common for WLED device */
24 #define RG_VWLED_1M_CK_PDN BIT(0)
25 #define RG_VWLED_32K_CK_PDN BIT(12)
26 #define RG_VWLED_6M_CK_PDN BIT(13)
29 * Register field for TOP_CKPDN2 to enable
30 * individual clock for LED device.
32 #define RG_ISINK_CK_PDN(i) BIT(i)
33 #define RG_ISINK_CK_PDN_MASK(i) BIT(i)
36 * Register field for TOP_CKCON1 to select
39 #define RG_ISINK_CK_SEL_MASK(i) (BIT(10) << (i))
41 #define ISINK_CON(r, i) (r + 0x8 * (i))
43 /* ISINK_CON0: Register to setup the duty cycle of the blink. */
44 #define ISINK_DIM_DUTY_MASK (0x1f << 8)
45 #define ISINK_DIM_DUTY(i) (((i) << 8) & ISINK_DIM_DUTY_MASK)
47 /* ISINK_CON1: Register to setup the period of the blink. */
48 #define ISINK_DIM_FSEL_MASK (0xffff)
49 #define ISINK_DIM_FSEL(i) ((i) & ISINK_DIM_FSEL_MASK)
51 /* ISINK_CON2: Register to control the brightness. */
52 #define ISINK_CH_STEP_SHIFT 12
53 #define ISINK_CH_STEP_MASK (0x7 << 12)
54 #define ISINK_CH_STEP(i) (((i) << 12) & ISINK_CH_STEP_MASK)
55 #define ISINK_SFSTR0_TC_MASK (0x3 << 1)
56 #define ISINK_SFSTR0_TC(i) (((i) << 1) & ISINK_SFSTR0_TC_MASK)
57 #define ISINK_SFSTR0_EN_MASK BIT(0)
58 #define ISINK_SFSTR0_EN BIT(0)
60 /* Register to LED channel enablement. */
61 #define ISINK_CH_EN_MASK(i) BIT(i)
62 #define ISINK_CH_EN(i) BIT(i)
64 #define MAX_SUPPORTED_LEDS 8
69 * struct mt6323_led - state container for the LED device
70 * @id: the identifier in MT6323 LED device
71 * @parent: the pointer to MT6323 LED controller
72 * @cdev: LED class device for this LED device
73 * @current_brightness: current state of the LED device
77 struct mt6323_leds
*parent
;
78 struct led_classdev cdev
;
79 enum led_brightness current_brightness
;
83 * struct mt6323_regs - register spec for the LED device
84 * @top_ckpdn: Offset to ISINK_CKPDN[0..x] registers
85 * @num_top_ckpdn: Number of ISINK_CKPDN registers
86 * @top_ckcon: Offset to ISINK_CKCON[0..x] registers
87 * @num_top_ckcon: Number of ISINK_CKCON registers
88 * @isink_con: Offset to ISINKx_CON[0..x] registers
89 * @num_isink_con: Number of ISINKx_CON registers
90 * @isink_max_regs: Number of ISINK[0..x] registers
91 * @isink_en_ctrl: Offset to ISINK_EN_CTRL register
92 * @iwled_en_ctrl: Offset to IWLED_EN_CTRL register
107 * struct mt6323_hwspec - hardware specific parameters
108 * @max_period: Maximum period for all LEDs
109 * @max_leds: Maximum number of supported LEDs
110 * @max_wleds: Maximum number of WLEDs
111 * @max_brightness: Maximum brightness for all LEDs
112 * @unit_duty: Steps of duty per period
114 struct mt6323_hwspec
{
123 * struct mt6323_data - device specific data
124 * @regs: Register spec for this device
125 * @spec: Hardware specific parameters
128 const struct mt6323_regs
*regs
;
129 const struct mt6323_hwspec
*spec
;
133 * struct mt6323_leds - state container for holding LED controller
135 * @dev: the device pointer
136 * @hw: the underlying hardware providing shared
137 * bus for the register operations
138 * @pdata: device specific data
139 * @lock: the lock among process context
140 * @led: the array that contains the state of individual
145 struct mt6397_chip
*hw
;
146 const struct mt6323_data
*pdata
;
147 /* protect among process context */
149 struct mt6323_led
*led
[MAX_SUPPORTED_LEDS
];
152 static int mt6323_led_hw_brightness(struct led_classdev
*cdev
,
153 enum led_brightness brightness
)
155 struct mt6323_led
*led
= container_of(cdev
, struct mt6323_led
, cdev
);
156 struct mt6323_leds
*leds
= led
->parent
;
157 const struct mt6323_regs
*regs
= leds
->pdata
->regs
;
158 struct regmap
*regmap
= leds
->hw
->regmap
;
159 u32 con2_mask
= 0, con2_val
= 0;
163 * Setup current output for the corresponding
166 con2_mask
|= ISINK_CH_STEP_MASK
|
167 ISINK_SFSTR0_TC_MASK
|
168 ISINK_SFSTR0_EN_MASK
;
169 con2_val
|= ISINK_CH_STEP(brightness
- 1) |
173 ret
= regmap_update_bits(regmap
, ISINK_CON(regs
->isink_con
[2], led
->id
),
174 con2_mask
, con2_val
);
178 static int mt6323_led_hw_off(struct led_classdev
*cdev
)
180 struct mt6323_led
*led
= container_of(cdev
, struct mt6323_led
, cdev
);
181 struct mt6323_leds
*leds
= led
->parent
;
182 const struct mt6323_regs
*regs
= leds
->pdata
->regs
;
183 struct regmap
*regmap
= leds
->hw
->regmap
;
187 status
= ISINK_CH_EN(led
->id
);
188 ret
= regmap_update_bits(regmap
, regs
->isink_en_ctrl
,
189 ISINK_CH_EN_MASK(led
->id
), ~status
);
193 usleep_range(100, 300);
194 ret
= regmap_update_bits(regmap
, regs
->top_ckpdn
[2],
195 RG_ISINK_CK_PDN_MASK(led
->id
),
196 RG_ISINK_CK_PDN(led
->id
));
203 static enum led_brightness
204 mt6323_get_led_hw_brightness(struct led_classdev
*cdev
)
206 struct mt6323_led
*led
= container_of(cdev
, struct mt6323_led
, cdev
);
207 struct mt6323_leds
*leds
= led
->parent
;
208 const struct mt6323_regs
*regs
= leds
->pdata
->regs
;
209 struct regmap
*regmap
= leds
->hw
->regmap
;
213 ret
= regmap_read(regmap
, regs
->top_ckpdn
[2], &status
);
217 if (status
& RG_ISINK_CK_PDN_MASK(led
->id
))
220 ret
= regmap_read(regmap
, regs
->isink_en_ctrl
, &status
);
224 if (!(status
& ISINK_CH_EN(led
->id
)))
227 ret
= regmap_read(regmap
, ISINK_CON(regs
->isink_con
[2], led
->id
), &status
);
231 return ((status
& ISINK_CH_STEP_MASK
)
232 >> ISINK_CH_STEP_SHIFT
) + 1;
235 static int mt6323_led_hw_on(struct led_classdev
*cdev
,
236 enum led_brightness brightness
)
238 struct mt6323_led
*led
= container_of(cdev
, struct mt6323_led
, cdev
);
239 struct mt6323_leds
*leds
= led
->parent
;
240 const struct mt6323_regs
*regs
= leds
->pdata
->regs
;
241 struct regmap
*regmap
= leds
->hw
->regmap
;
246 * Setup required clock source, enable the corresponding
247 * clock and channel and let work with continuous blink as
250 ret
= regmap_update_bits(regmap
, regs
->top_ckcon
[1],
251 RG_ISINK_CK_SEL_MASK(led
->id
), 0);
255 status
= RG_ISINK_CK_PDN(led
->id
);
256 ret
= regmap_update_bits(regmap
, regs
->top_ckpdn
[2],
257 RG_ISINK_CK_PDN_MASK(led
->id
),
262 usleep_range(100, 300);
264 ret
= regmap_update_bits(regmap
, regs
->isink_en_ctrl
,
265 ISINK_CH_EN_MASK(led
->id
),
266 ISINK_CH_EN(led
->id
));
270 ret
= mt6323_led_hw_brightness(cdev
, brightness
);
274 ret
= regmap_update_bits(regmap
, ISINK_CON(regs
->isink_con
[0], led
->id
),
280 ret
= regmap_update_bits(regmap
, ISINK_CON(regs
->isink_con
[1], led
->id
),
282 ISINK_DIM_FSEL(1000));
289 static int mt6323_led_set_blink(struct led_classdev
*cdev
,
290 unsigned long *delay_on
,
291 unsigned long *delay_off
)
293 struct mt6323_led
*led
= container_of(cdev
, struct mt6323_led
, cdev
);
294 struct mt6323_leds
*leds
= led
->parent
;
295 const struct mt6323_regs
*regs
= leds
->pdata
->regs
;
296 const struct mt6323_hwspec
*spec
= leds
->pdata
->spec
;
297 struct regmap
*regmap
= leds
->hw
->regmap
;
298 unsigned long period
;
303 * LED subsystem requires a default user
304 * friendly blink pattern for the LED so using
305 * 1Hz duty cycle 50% here if without specific
306 * value delay_on and delay off being assigned.
308 if (!*delay_on
&& !*delay_off
) {
314 * Units are in ms, if over the hardware able
315 * to support, fallback into software blink
317 period
= *delay_on
+ *delay_off
;
319 if (period
> spec
->max_period
)
323 * Calculate duty_hw based on the percentage of period during
324 * which the led is ON.
326 duty_hw
= DIV_ROUND_CLOSEST(*delay_on
* 100000ul, period
* spec
->unit_duty
);
328 /* hardware doesn't support zero duty cycle. */
332 mutex_lock(&leds
->lock
);
334 * Set max_brightness as the software blink behavior
335 * when no blink brightness.
337 if (!led
->current_brightness
) {
338 ret
= mt6323_led_hw_on(cdev
, cdev
->max_brightness
);
341 led
->current_brightness
= cdev
->max_brightness
;
344 ret
= regmap_update_bits(regmap
, ISINK_CON(regs
->isink_con
[0], led
->id
),
346 ISINK_DIM_DUTY(duty_hw
- 1));
350 ret
= regmap_update_bits(regmap
, ISINK_CON(regs
->isink_con
[1], led
->id
),
352 ISINK_DIM_FSEL(period
- 1));
354 mutex_unlock(&leds
->lock
);
359 static int mt6323_led_set_brightness(struct led_classdev
*cdev
,
360 enum led_brightness brightness
)
362 struct mt6323_led
*led
= container_of(cdev
, struct mt6323_led
, cdev
);
363 struct mt6323_leds
*leds
= led
->parent
;
366 mutex_lock(&leds
->lock
);
368 if (!led
->current_brightness
&& brightness
) {
369 ret
= mt6323_led_hw_on(cdev
, brightness
);
372 } else if (brightness
) {
373 ret
= mt6323_led_hw_brightness(cdev
, brightness
);
377 ret
= mt6323_led_hw_off(cdev
);
382 led
->current_brightness
= brightness
;
384 mutex_unlock(&leds
->lock
);
389 static int mtk_wled_hw_on(struct led_classdev
*cdev
)
391 struct mt6323_led
*led
= container_of(cdev
, struct mt6323_led
, cdev
);
392 struct mt6323_leds
*leds
= led
->parent
;
393 const struct mt6323_regs
*regs
= leds
->pdata
->regs
;
394 struct regmap
*regmap
= leds
->hw
->regmap
;
397 ret
= regmap_clear_bits(regmap
, regs
->top_ckpdn
[0], RG_VWLED_32K_CK_PDN
);
401 ret
= regmap_clear_bits(regmap
, regs
->top_ckpdn
[0], RG_VWLED_6M_CK_PDN
);
405 ret
= regmap_clear_bits(regmap
, regs
->top_ckpdn
[0], RG_VWLED_1M_CK_PDN
);
409 usleep_range(5000, 6000);
411 /* Enable WLED channel pair */
412 ret
= regmap_set_bits(regmap
, regs
->iwled_en_ctrl
, BIT(led
->id
));
416 ret
= regmap_set_bits(regmap
, regs
->iwled_en_ctrl
, BIT(led
->id
+ 1));
423 static int mtk_wled_hw_off(struct led_classdev
*cdev
)
425 struct mt6323_led
*led
= container_of(cdev
, struct mt6323_led
, cdev
);
426 struct mt6323_leds
*leds
= led
->parent
;
427 const struct mt6323_regs
*regs
= leds
->pdata
->regs
;
428 struct regmap
*regmap
= leds
->hw
->regmap
;
431 ret
= regmap_clear_bits(regmap
, regs
->iwled_en_ctrl
, BIT(led
->id
+ 1));
435 ret
= regmap_clear_bits(regmap
, regs
->iwled_en_ctrl
, BIT(led
->id
));
439 ret
= regmap_set_bits(regmap
, regs
->top_ckpdn
[0], RG_VWLED_32K_CK_PDN
);
443 ret
= regmap_set_bits(regmap
, regs
->top_ckpdn
[0], RG_VWLED_6M_CK_PDN
);
447 ret
= regmap_set_bits(regmap
, regs
->top_ckpdn
[0], RG_VWLED_1M_CK_PDN
);
454 static enum led_brightness
mt6323_get_wled_brightness(struct led_classdev
*cdev
)
456 struct mt6323_led
*led
= container_of(cdev
, struct mt6323_led
, cdev
);
457 struct mt6323_leds
*leds
= led
->parent
;
458 const struct mt6323_regs
*regs
= leds
->pdata
->regs
;
459 struct regmap
*regmap
= leds
->hw
->regmap
;
463 ret
= regmap_read(regmap
, regs
->iwled_en_ctrl
, &status
);
467 /* Always two channels per WLED */
468 status
&= BIT(led
->id
) | BIT(led
->id
+ 1);
470 return status
? led
->current_brightness
: 0;
473 static int mt6323_wled_set_brightness(struct led_classdev
*cdev
,
474 enum led_brightness brightness
)
476 struct mt6323_led
*led
= container_of(cdev
, struct mt6323_led
, cdev
);
477 struct mt6323_leds
*leds
= led
->parent
;
480 mutex_lock(&leds
->lock
);
483 if (!led
->current_brightness
)
484 ret
= mtk_wled_hw_on(cdev
);
488 ret
= mtk_wled_hw_off(cdev
);
493 led
->current_brightness
= brightness
;
495 mutex_unlock(&leds
->lock
);
500 static int mt6323_led_set_dt_default(struct led_classdev
*cdev
,
501 struct device_node
*np
)
503 struct mt6323_led
*led
= container_of(cdev
, struct mt6323_led
, cdev
);
504 enum led_default_state state
;
507 state
= led_init_default_state_get(of_fwnode_handle(np
));
509 case LEDS_DEFSTATE_ON
:
510 ret
= mt6323_led_set_brightness(cdev
, cdev
->max_brightness
);
512 case LEDS_DEFSTATE_KEEP
:
513 ret
= mt6323_get_led_hw_brightness(cdev
);
516 led
->current_brightness
= ret
;
520 ret
= mt6323_led_set_brightness(cdev
, LED_OFF
);
526 static int mt6323_led_probe(struct platform_device
*pdev
)
528 struct device
*dev
= &pdev
->dev
;
529 struct device_node
*np
= dev_of_node(dev
);
530 struct mt6397_chip
*hw
= dev_get_drvdata(dev
->parent
);
531 struct mt6323_leds
*leds
;
532 struct mt6323_led
*led
;
533 const struct mt6323_regs
*regs
;
534 const struct mt6323_hwspec
*spec
;
540 leds
= devm_kzalloc(dev
, sizeof(*leds
), GFP_KERNEL
);
544 platform_set_drvdata(pdev
, leds
);
546 leds
->pdata
= device_get_match_data(dev
);
547 regs
= leds
->pdata
->regs
;
548 spec
= leds
->pdata
->spec
;
549 max_leds
= spec
->max_leds
+ spec
->max_wleds
;
552 * leds->hw points to the underlying bus for the register
556 mutex_init(&leds
->lock
);
558 status
= RG_DRV_32K_CK_PDN
;
559 ret
= regmap_update_bits(leds
->hw
->regmap
, regs
->top_ckpdn
[0],
560 RG_DRV_32K_CK_PDN_MASK
, ~status
);
563 "Failed to update TOP_CKPDN0 Register\n");
567 for_each_available_child_of_node_scoped(np
, child
) {
568 struct led_init_data init_data
= {};
571 ret
= of_property_read_u32(child
, "reg", ®
);
573 dev_err(dev
, "Failed to read led 'reg' property\n");
577 if (reg
>= max_leds
|| reg
>= MAX_SUPPORTED_LEDS
||
579 dev_err(dev
, "Invalid led reg %u\n", reg
);
583 led
= devm_kzalloc(dev
, sizeof(*led
), GFP_KERNEL
);
587 is_wled
= of_property_read_bool(child
, "mediatek,is-wled");
589 leds
->led
[reg
] = led
;
590 leds
->led
[reg
]->id
= reg
;
591 leds
->led
[reg
]->cdev
.max_brightness
= spec
->max_brightness
;
594 leds
->led
[reg
]->cdev
.brightness_set_blocking
=
595 mt6323_wled_set_brightness
;
596 leds
->led
[reg
]->cdev
.brightness_get
=
597 mt6323_get_wled_brightness
;
599 leds
->led
[reg
]->cdev
.brightness_set_blocking
=
600 mt6323_led_set_brightness
;
601 leds
->led
[reg
]->cdev
.blink_set
= mt6323_led_set_blink
;
602 leds
->led
[reg
]->cdev
.brightness_get
=
603 mt6323_get_led_hw_brightness
;
605 leds
->led
[reg
]->parent
= leds
;
607 ret
= mt6323_led_set_dt_default(&leds
->led
[reg
]->cdev
, child
);
610 "Failed to LED set default from devicetree\n");
614 init_data
.fwnode
= of_fwnode_handle(child
);
616 ret
= devm_led_classdev_register_ext(dev
, &leds
->led
[reg
]->cdev
,
619 dev_err(dev
, "Failed to register LED: %d\n", ret
);
627 static void mt6323_led_remove(struct platform_device
*pdev
)
629 struct mt6323_leds
*leds
= platform_get_drvdata(pdev
);
630 const struct mt6323_regs
*regs
= leds
->pdata
->regs
;
633 /* Turn the LEDs off on driver removal. */
634 for (i
= 0 ; leds
->led
[i
] ; i
++)
635 mt6323_led_hw_off(&leds
->led
[i
]->cdev
);
637 regmap_update_bits(leds
->hw
->regmap
, regs
->top_ckpdn
[0],
638 RG_DRV_32K_CK_PDN_MASK
,
641 mutex_destroy(&leds
->lock
);
644 static const struct mt6323_regs mt6323_registers
= {
645 .top_ckpdn
= (const u16
[]){ 0x102, 0x106, 0x10e },
647 .top_ckcon
= (const u16
[]){ 0x120, 0x126 },
649 .isink_con
= (const u16
[]){ 0x330, 0x332, 0x334 },
651 .isink_max_regs
= 4, /* ISINK[0..3] */
652 .isink_en_ctrl
= 0x356,
655 static const struct mt6323_regs mt6331_registers
= {
656 .top_ckpdn
= (const u16
[]){ 0x138, 0x13e, 0x144 },
658 .top_ckcon
= (const u16
[]){ 0x14c, 0x14a },
660 .isink_con
= (const u16
[]){ 0x40c, 0x40e, 0x410, 0x412, 0x414 },
662 .isink_max_regs
= 4, /* ISINK[0..3] */
663 .isink_en_ctrl
= 0x43a,
666 static const struct mt6323_regs mt6332_registers
= {
667 .top_ckpdn
= (const u16
[]){ 0x8094, 0x809a, 0x80a0 },
669 .top_ckcon
= (const u16
[]){ 0x80a6, 0x80ac },
671 .isink_con
= (const u16
[]){ 0x8cd4 },
673 .isink_max_regs
= 12, /* IWLED[0..2, 3..9] */
674 .iwled_en_ctrl
= 0x8cda,
677 static const struct mt6323_hwspec mt6323_spec
= {
684 static const struct mt6323_hwspec mt6332_spec
= {
685 /* There are no LEDs in MT6332. Only WLEDs are present. */
688 .max_brightness
= 1024,
691 static const struct mt6323_data mt6323_pdata
= {
692 .regs
= &mt6323_registers
,
693 .spec
= &mt6323_spec
,
696 static const struct mt6323_data mt6331_pdata
= {
697 .regs
= &mt6331_registers
,
698 .spec
= &mt6323_spec
,
701 static const struct mt6323_data mt6332_pdata
= {
702 .regs
= &mt6332_registers
,
703 .spec
= &mt6332_spec
,
706 static const struct of_device_id mt6323_led_dt_match
[] = {
707 { .compatible
= "mediatek,mt6323-led", .data
= &mt6323_pdata
},
708 { .compatible
= "mediatek,mt6331-led", .data
= &mt6331_pdata
},
709 { .compatible
= "mediatek,mt6332-led", .data
= &mt6332_pdata
},
712 MODULE_DEVICE_TABLE(of
, mt6323_led_dt_match
);
714 static struct platform_driver mt6323_led_driver
= {
715 .probe
= mt6323_led_probe
,
716 .remove
= mt6323_led_remove
,
718 .name
= "mt6323-led",
719 .of_match_table
= mt6323_led_dt_match
,
723 module_platform_driver(mt6323_led_driver
);
725 MODULE_DESCRIPTION("LED driver for Mediatek MT6323 PMIC");
726 MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>");
727 MODULE_LICENSE("GPL");