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>
9 #include <uapi/linux/uleds.h>
11 /* PMIC global control register definition */
12 #define SC27XX_MODULE_EN0 0xc08
13 #define SC27XX_CLK_EN0 0xc18
14 #define SC27XX_RGB_CTRL 0xebc
16 #define SC27XX_BLTC_EN BIT(9)
17 #define SC27XX_RTC_EN BIT(7)
18 #define SC27XX_RGB_PD BIT(0)
20 /* Breathing light controller register definition */
21 #define SC27XX_LEDS_CTRL 0x00
22 #define SC27XX_LEDS_PRESCALE 0x04
23 #define SC27XX_LEDS_DUTY 0x08
24 #define SC27XX_LEDS_CURVE0 0x0c
25 #define SC27XX_LEDS_CURVE1 0x10
27 #define SC27XX_CTRL_SHIFT 4
28 #define SC27XX_LED_RUN BIT(0)
29 #define SC27XX_LED_TYPE BIT(1)
31 #define SC27XX_DUTY_SHIFT 8
32 #define SC27XX_DUTY_MASK GENMASK(15, 0)
33 #define SC27XX_MOD_MASK GENMASK(7, 0)
35 #define SC27XX_LEDS_OFFSET 0x10
36 #define SC27XX_LEDS_MAX 3
39 char name
[LED_MAX_NAME_SIZE
];
40 struct led_classdev ldev
;
41 struct sc27xx_led_priv
*priv
;
46 struct sc27xx_led_priv
{
47 struct sc27xx_led leds
[SC27XX_LEDS_MAX
];
48 struct regmap
*regmap
;
53 #define to_sc27xx_led(ldev) \
54 container_of(ldev, struct sc27xx_led, ldev)
56 static int sc27xx_led_init(struct regmap
*regmap
)
60 err
= regmap_update_bits(regmap
, SC27XX_MODULE_EN0
, SC27XX_BLTC_EN
,
65 err
= regmap_update_bits(regmap
, SC27XX_CLK_EN0
, SC27XX_RTC_EN
,
70 return regmap_update_bits(regmap
, SC27XX_RGB_CTRL
, SC27XX_RGB_PD
, 0);
73 static u32
sc27xx_led_get_offset(struct sc27xx_led
*leds
)
75 return leds
->priv
->base
+ SC27XX_LEDS_OFFSET
* leds
->line
;
78 static int sc27xx_led_enable(struct sc27xx_led
*leds
, enum led_brightness value
)
80 u32 base
= sc27xx_led_get_offset(leds
);
81 u32 ctrl_base
= leds
->priv
->base
+ SC27XX_LEDS_CTRL
;
82 u8 ctrl_shift
= SC27XX_CTRL_SHIFT
* leds
->line
;
83 struct regmap
*regmap
= leds
->priv
->regmap
;
86 err
= regmap_update_bits(regmap
, base
+ SC27XX_LEDS_DUTY
,
88 (value
<< SC27XX_DUTY_SHIFT
) |
93 return regmap_update_bits(regmap
, ctrl_base
,
94 (SC27XX_LED_RUN
| SC27XX_LED_TYPE
) << ctrl_shift
,
95 (SC27XX_LED_RUN
| SC27XX_LED_TYPE
) << ctrl_shift
);
98 static int sc27xx_led_disable(struct sc27xx_led
*leds
)
100 struct regmap
*regmap
= leds
->priv
->regmap
;
101 u32 ctrl_base
= leds
->priv
->base
+ SC27XX_LEDS_CTRL
;
102 u8 ctrl_shift
= SC27XX_CTRL_SHIFT
* leds
->line
;
104 return regmap_update_bits(regmap
, ctrl_base
,
105 (SC27XX_LED_RUN
| SC27XX_LED_TYPE
) << ctrl_shift
, 0);
108 static int sc27xx_led_set(struct led_classdev
*ldev
, enum led_brightness value
)
110 struct sc27xx_led
*leds
= to_sc27xx_led(ldev
);
113 mutex_lock(&leds
->priv
->lock
);
115 if (value
== LED_OFF
)
116 err
= sc27xx_led_disable(leds
);
118 err
= sc27xx_led_enable(leds
, value
);
120 mutex_unlock(&leds
->priv
->lock
);
125 static int sc27xx_led_register(struct device
*dev
, struct sc27xx_led_priv
*priv
)
129 err
= sc27xx_led_init(priv
->regmap
);
133 for (i
= 0; i
< SC27XX_LEDS_MAX
; i
++) {
134 struct sc27xx_led
*led
= &priv
->leds
[i
];
141 led
->ldev
.name
= led
->name
;
142 led
->ldev
.brightness_set_blocking
= sc27xx_led_set
;
144 err
= devm_led_classdev_register(dev
, &led
->ldev
);
152 static int sc27xx_led_probe(struct platform_device
*pdev
)
154 struct device
*dev
= &pdev
->dev
;
155 struct device_node
*np
= dev
->of_node
, *child
;
156 struct sc27xx_led_priv
*priv
;
158 u32 base
, count
, reg
;
161 count
= of_get_child_count(np
);
162 if (!count
|| count
> SC27XX_LEDS_MAX
)
165 err
= of_property_read_u32(np
, "reg", &base
);
167 dev_err(dev
, "fail to get reg of property\n");
171 priv
= devm_kzalloc(dev
, sizeof(*priv
), GFP_KERNEL
);
175 platform_set_drvdata(pdev
, priv
);
176 mutex_init(&priv
->lock
);
178 priv
->regmap
= dev_get_regmap(dev
->parent
, NULL
);
181 dev_err(dev
, "failed to get regmap: %d\n", err
);
185 for_each_child_of_node(np
, child
) {
186 err
= of_property_read_u32(child
, "reg", ®
);
189 mutex_destroy(&priv
->lock
);
193 if (reg
>= SC27XX_LEDS_MAX
|| priv
->leds
[reg
].active
) {
195 mutex_destroy(&priv
->lock
);
199 priv
->leds
[reg
].active
= true;
201 err
= of_property_read_string(child
, "label", &str
);
203 snprintf(priv
->leds
[reg
].name
, LED_MAX_NAME_SIZE
,
206 snprintf(priv
->leds
[reg
].name
, LED_MAX_NAME_SIZE
,
210 err
= sc27xx_led_register(dev
, priv
);
212 mutex_destroy(&priv
->lock
);
217 static int sc27xx_led_remove(struct platform_device
*pdev
)
219 struct sc27xx_led_priv
*priv
= platform_get_drvdata(pdev
);
221 mutex_destroy(&priv
->lock
);
225 static const struct of_device_id sc27xx_led_of_match
[] = {
226 { .compatible
= "sprd,sc2731-bltc", },
229 MODULE_DEVICE_TABLE(of
, sc27xx_led_of_match
);
231 static struct platform_driver sc27xx_led_driver
= {
234 .of_match_table
= sc27xx_led_of_match
,
236 .probe
= sc27xx_led_probe
,
237 .remove
= sc27xx_led_remove
,
240 module_platform_driver(sc27xx_led_driver
);
242 MODULE_DESCRIPTION("Spreadtrum SC27xx breathing light controller driver");
243 MODULE_AUTHOR("Xiaotong Lu <xiaotong.lu@spreadtrum.com>");
244 MODULE_LICENSE("GPL v2");