1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (C) 2018 Spreadtrum Communications Inc.
4 #include <linux/leds.h>
5 #include <linux/module.h>
7 #include <linux/platform_device.h>
8 #include <linux/regmap.h>
10 /* PMIC global control register definition */
11 #define SC27XX_MODULE_EN0 0xc08
12 #define SC27XX_CLK_EN0 0xc18
13 #define SC27XX_RGB_CTRL 0xebc
15 #define SC27XX_BLTC_EN BIT(9)
16 #define SC27XX_RTC_EN BIT(7)
17 #define SC27XX_RGB_PD BIT(0)
19 /* Breathing light controller register definition */
20 #define SC27XX_LEDS_CTRL 0x00
21 #define SC27XX_LEDS_PRESCALE 0x04
22 #define SC27XX_LEDS_DUTY 0x08
23 #define SC27XX_LEDS_CURVE0 0x0c
24 #define SC27XX_LEDS_CURVE1 0x10
26 #define SC27XX_CTRL_SHIFT 4
27 #define SC27XX_LED_RUN BIT(0)
28 #define SC27XX_LED_TYPE BIT(1)
30 #define SC27XX_DUTY_SHIFT 8
31 #define SC27XX_DUTY_MASK GENMASK(15, 0)
32 #define SC27XX_MOD_MASK GENMASK(7, 0)
34 #define SC27XX_CURVE_SHIFT 8
35 #define SC27XX_CURVE_L_MASK GENMASK(7, 0)
36 #define SC27XX_CURVE_H_MASK GENMASK(15, 8)
38 #define SC27XX_LEDS_OFFSET 0x10
39 #define SC27XX_LEDS_MAX 3
40 #define SC27XX_LEDS_PATTERN_CNT 4
41 /* Stage duration step, in milliseconds */
42 #define SC27XX_LEDS_STEP 125
43 /* Minimum and maximum duration, in milliseconds */
44 #define SC27XX_DELTA_T_MIN SC27XX_LEDS_STEP
45 #define SC27XX_DELTA_T_MAX (SC27XX_LEDS_STEP * 255)
48 struct fwnode_handle
*fwnode
;
49 struct led_classdev ldev
;
50 struct sc27xx_led_priv
*priv
;
55 struct sc27xx_led_priv
{
56 struct sc27xx_led leds
[SC27XX_LEDS_MAX
];
57 struct regmap
*regmap
;
62 #define to_sc27xx_led(ldev) \
63 container_of(ldev, struct sc27xx_led, ldev)
65 static int sc27xx_led_init(struct regmap
*regmap
)
69 err
= regmap_update_bits(regmap
, SC27XX_MODULE_EN0
, SC27XX_BLTC_EN
,
74 err
= regmap_update_bits(regmap
, SC27XX_CLK_EN0
, SC27XX_RTC_EN
,
79 return regmap_update_bits(regmap
, SC27XX_RGB_CTRL
, SC27XX_RGB_PD
, 0);
82 static u32
sc27xx_led_get_offset(struct sc27xx_led
*leds
)
84 return leds
->priv
->base
+ SC27XX_LEDS_OFFSET
* leds
->line
;
87 static int sc27xx_led_enable(struct sc27xx_led
*leds
, enum led_brightness value
)
89 u32 base
= sc27xx_led_get_offset(leds
);
90 u32 ctrl_base
= leds
->priv
->base
+ SC27XX_LEDS_CTRL
;
91 u8 ctrl_shift
= SC27XX_CTRL_SHIFT
* leds
->line
;
92 struct regmap
*regmap
= leds
->priv
->regmap
;
95 err
= regmap_update_bits(regmap
, base
+ SC27XX_LEDS_DUTY
,
97 (value
<< SC27XX_DUTY_SHIFT
) |
102 return regmap_update_bits(regmap
, ctrl_base
,
103 (SC27XX_LED_RUN
| SC27XX_LED_TYPE
) << ctrl_shift
,
104 (SC27XX_LED_RUN
| SC27XX_LED_TYPE
) << ctrl_shift
);
107 static int sc27xx_led_disable(struct sc27xx_led
*leds
)
109 struct regmap
*regmap
= leds
->priv
->regmap
;
110 u32 ctrl_base
= leds
->priv
->base
+ SC27XX_LEDS_CTRL
;
111 u8 ctrl_shift
= SC27XX_CTRL_SHIFT
* leds
->line
;
113 return regmap_update_bits(regmap
, ctrl_base
,
114 (SC27XX_LED_RUN
| SC27XX_LED_TYPE
) << ctrl_shift
, 0);
117 static int sc27xx_led_set(struct led_classdev
*ldev
, enum led_brightness value
)
119 struct sc27xx_led
*leds
= to_sc27xx_led(ldev
);
122 mutex_lock(&leds
->priv
->lock
);
124 if (value
== LED_OFF
)
125 err
= sc27xx_led_disable(leds
);
127 err
= sc27xx_led_enable(leds
, value
);
129 mutex_unlock(&leds
->priv
->lock
);
134 static void sc27xx_led_clamp_align_delta_t(u32
*delta_t
)
136 u32 v
, offset
, t
= *delta_t
;
138 v
= t
+ SC27XX_LEDS_STEP
/ 2;
139 v
= clamp_t(u32
, v
, SC27XX_DELTA_T_MIN
, SC27XX_DELTA_T_MAX
);
140 offset
= v
- SC27XX_DELTA_T_MIN
;
141 offset
= SC27XX_LEDS_STEP
* (offset
/ SC27XX_LEDS_STEP
);
143 *delta_t
= SC27XX_DELTA_T_MIN
+ offset
;
146 static int sc27xx_led_pattern_clear(struct led_classdev
*ldev
)
148 struct sc27xx_led
*leds
= to_sc27xx_led(ldev
);
149 struct regmap
*regmap
= leds
->priv
->regmap
;
150 u32 base
= sc27xx_led_get_offset(leds
);
151 u32 ctrl_base
= leds
->priv
->base
+ SC27XX_LEDS_CTRL
;
152 u8 ctrl_shift
= SC27XX_CTRL_SHIFT
* leds
->line
;
155 mutex_lock(&leds
->priv
->lock
);
157 /* Reset the rise, high, fall and low time to zero. */
158 regmap_write(regmap
, base
+ SC27XX_LEDS_CURVE0
, 0);
159 regmap_write(regmap
, base
+ SC27XX_LEDS_CURVE1
, 0);
161 err
= regmap_update_bits(regmap
, ctrl_base
,
162 (SC27XX_LED_RUN
| SC27XX_LED_TYPE
) << ctrl_shift
, 0);
164 ldev
->brightness
= LED_OFF
;
166 mutex_unlock(&leds
->priv
->lock
);
171 static int sc27xx_led_pattern_set(struct led_classdev
*ldev
,
172 struct led_pattern
*pattern
,
175 struct sc27xx_led
*leds
= to_sc27xx_led(ldev
);
176 u32 base
= sc27xx_led_get_offset(leds
);
177 u32 ctrl_base
= leds
->priv
->base
+ SC27XX_LEDS_CTRL
;
178 u8 ctrl_shift
= SC27XX_CTRL_SHIFT
* leds
->line
;
179 struct regmap
*regmap
= leds
->priv
->regmap
;
183 * Must contain 4 tuples to configure the rise time, high time, fall
184 * time and low time to enable the breathing mode.
186 if (len
!= SC27XX_LEDS_PATTERN_CNT
)
189 mutex_lock(&leds
->priv
->lock
);
191 sc27xx_led_clamp_align_delta_t(&pattern
[0].delta_t
);
192 err
= regmap_update_bits(regmap
, base
+ SC27XX_LEDS_CURVE0
,
194 pattern
[0].delta_t
/ SC27XX_LEDS_STEP
);
198 sc27xx_led_clamp_align_delta_t(&pattern
[1].delta_t
);
199 err
= regmap_update_bits(regmap
, base
+ SC27XX_LEDS_CURVE1
,
201 pattern
[1].delta_t
/ SC27XX_LEDS_STEP
);
205 sc27xx_led_clamp_align_delta_t(&pattern
[2].delta_t
);
206 err
= regmap_update_bits(regmap
, base
+ SC27XX_LEDS_CURVE0
,
208 (pattern
[2].delta_t
/ SC27XX_LEDS_STEP
) <<
213 sc27xx_led_clamp_align_delta_t(&pattern
[3].delta_t
);
214 err
= regmap_update_bits(regmap
, base
+ SC27XX_LEDS_CURVE1
,
216 (pattern
[3].delta_t
/ SC27XX_LEDS_STEP
) <<
221 err
= regmap_update_bits(regmap
, base
+ SC27XX_LEDS_DUTY
,
223 (pattern
[1].brightness
<< SC27XX_DUTY_SHIFT
) |
228 /* Enable the LED breathing mode */
229 err
= regmap_update_bits(regmap
, ctrl_base
,
230 SC27XX_LED_RUN
<< ctrl_shift
,
231 SC27XX_LED_RUN
<< ctrl_shift
);
233 ldev
->brightness
= pattern
[1].brightness
;
236 mutex_unlock(&leds
->priv
->lock
);
241 static int sc27xx_led_register(struct device
*dev
, struct sc27xx_led_priv
*priv
)
245 err
= sc27xx_led_init(priv
->regmap
);
249 for (i
= 0; i
< SC27XX_LEDS_MAX
; i
++) {
250 struct sc27xx_led
*led
= &priv
->leds
[i
];
251 struct led_init_data init_data
= {};
258 led
->ldev
.brightness_set_blocking
= sc27xx_led_set
;
259 led
->ldev
.pattern_set
= sc27xx_led_pattern_set
;
260 led
->ldev
.pattern_clear
= sc27xx_led_pattern_clear
;
261 led
->ldev
.default_trigger
= "pattern";
263 init_data
.fwnode
= led
->fwnode
;
264 init_data
.devicename
= "sc27xx";
265 init_data
.default_label
= ":";
267 err
= devm_led_classdev_register_ext(dev
, &led
->ldev
,
276 static int sc27xx_led_probe(struct platform_device
*pdev
)
278 struct device
*dev
= &pdev
->dev
;
279 struct device_node
*np
= dev_of_node(dev
);
280 struct sc27xx_led_priv
*priv
;
281 u32 base
, count
, reg
;
284 count
= of_get_available_child_count(np
);
285 if (!count
|| count
> SC27XX_LEDS_MAX
)
288 err
= of_property_read_u32(np
, "reg", &base
);
290 dev_err(dev
, "fail to get reg of property\n");
294 priv
= devm_kzalloc(dev
, sizeof(*priv
), GFP_KERNEL
);
298 platform_set_drvdata(pdev
, priv
);
300 priv
->regmap
= dev_get_regmap(dev
->parent
, NULL
);
303 dev_err(dev
, "failed to get regmap: %d\n", err
);
307 for_each_available_child_of_node_scoped(np
, child
) {
308 err
= of_property_read_u32(child
, "reg", ®
);
312 if (reg
>= SC27XX_LEDS_MAX
|| priv
->leds
[reg
].active
)
315 priv
->leds
[reg
].fwnode
= of_fwnode_handle(child
);
316 priv
->leds
[reg
].active
= true;
319 mutex_init(&priv
->lock
);
321 err
= sc27xx_led_register(dev
, priv
);
323 mutex_destroy(&priv
->lock
);
328 static void sc27xx_led_remove(struct platform_device
*pdev
)
330 struct sc27xx_led_priv
*priv
= platform_get_drvdata(pdev
);
332 mutex_destroy(&priv
->lock
);
335 static const struct of_device_id sc27xx_led_of_match
[] = {
336 { .compatible
= "sprd,sc2731-bltc", },
339 MODULE_DEVICE_TABLE(of
, sc27xx_led_of_match
);
341 static struct platform_driver sc27xx_led_driver
= {
344 .of_match_table
= sc27xx_led_of_match
,
346 .probe
= sc27xx_led_probe
,
347 .remove_new
= sc27xx_led_remove
,
350 module_platform_driver(sc27xx_led_driver
);
352 MODULE_DESCRIPTION("Spreadtrum SC27xx breathing light controller driver");
353 MODULE_AUTHOR("Xiaotong Lu <xiaotong.lu@spreadtrum.com>");
354 MODULE_AUTHOR("Baolin Wang <baolin.wang@linaro.org>");
355 MODULE_LICENSE("GPL v2");