GNU Linux-libre 4.19.264-gnu1
[releases.git] / drivers / leds / leds-sc27xx-bltc.c
1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (C) 2018 Spreadtrum Communications Inc.
3
4 #include <linux/leds.h>
5 #include <linux/module.h>
6 #include <linux/of.h>
7 #include <linux/platform_device.h>
8 #include <linux/regmap.h>
9 #include <uapi/linux/uleds.h>
10
11 /* PMIC global control register definition */
12 #define SC27XX_MODULE_EN0       0xc08
13 #define SC27XX_CLK_EN0          0xc18
14 #define SC27XX_RGB_CTRL         0xebc
15
16 #define SC27XX_BLTC_EN          BIT(9)
17 #define SC27XX_RTC_EN           BIT(7)
18 #define SC27XX_RGB_PD           BIT(0)
19
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
26
27 #define SC27XX_CTRL_SHIFT       4
28 #define SC27XX_LED_RUN          BIT(0)
29 #define SC27XX_LED_TYPE         BIT(1)
30
31 #define SC27XX_DUTY_SHIFT       8
32 #define SC27XX_DUTY_MASK        GENMASK(15, 0)
33 #define SC27XX_MOD_MASK         GENMASK(7, 0)
34
35 #define SC27XX_LEDS_OFFSET      0x10
36 #define SC27XX_LEDS_MAX         3
37
38 struct sc27xx_led {
39         char name[LED_MAX_NAME_SIZE];
40         struct led_classdev ldev;
41         struct sc27xx_led_priv *priv;
42         u8 line;
43         bool active;
44 };
45
46 struct sc27xx_led_priv {
47         struct sc27xx_led leds[SC27XX_LEDS_MAX];
48         struct regmap *regmap;
49         struct mutex lock;
50         u32 base;
51 };
52
53 #define to_sc27xx_led(ldev) \
54         container_of(ldev, struct sc27xx_led, ldev)
55
56 static int sc27xx_led_init(struct regmap *regmap)
57 {
58         int err;
59
60         err = regmap_update_bits(regmap, SC27XX_MODULE_EN0, SC27XX_BLTC_EN,
61                                  SC27XX_BLTC_EN);
62         if (err)
63                 return err;
64
65         err = regmap_update_bits(regmap, SC27XX_CLK_EN0, SC27XX_RTC_EN,
66                                  SC27XX_RTC_EN);
67         if (err)
68                 return err;
69
70         return regmap_update_bits(regmap, SC27XX_RGB_CTRL, SC27XX_RGB_PD, 0);
71 }
72
73 static u32 sc27xx_led_get_offset(struct sc27xx_led *leds)
74 {
75         return leds->priv->base + SC27XX_LEDS_OFFSET * leds->line;
76 }
77
78 static int sc27xx_led_enable(struct sc27xx_led *leds, enum led_brightness value)
79 {
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;
84         int err;
85
86         err = regmap_update_bits(regmap, base + SC27XX_LEDS_DUTY,
87                                  SC27XX_DUTY_MASK,
88                                  (value << SC27XX_DUTY_SHIFT) |
89                                  SC27XX_MOD_MASK);
90         if (err)
91                 return err;
92
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);
96 }
97
98 static int sc27xx_led_disable(struct sc27xx_led *leds)
99 {
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;
103
104         return regmap_update_bits(regmap, ctrl_base,
105                         (SC27XX_LED_RUN | SC27XX_LED_TYPE) << ctrl_shift, 0);
106 }
107
108 static int sc27xx_led_set(struct led_classdev *ldev, enum led_brightness value)
109 {
110         struct sc27xx_led *leds = to_sc27xx_led(ldev);
111         int err;
112
113         mutex_lock(&leds->priv->lock);
114
115         if (value == LED_OFF)
116                 err = sc27xx_led_disable(leds);
117         else
118                 err = sc27xx_led_enable(leds, value);
119
120         mutex_unlock(&leds->priv->lock);
121
122         return err;
123 }
124
125 static int sc27xx_led_register(struct device *dev, struct sc27xx_led_priv *priv)
126 {
127         int i, err;
128
129         err = sc27xx_led_init(priv->regmap);
130         if (err)
131                 return err;
132
133         for (i = 0; i < SC27XX_LEDS_MAX; i++) {
134                 struct sc27xx_led *led = &priv->leds[i];
135
136                 if (!led->active)
137                         continue;
138
139                 led->line = i;
140                 led->priv = priv;
141                 led->ldev.name = led->name;
142                 led->ldev.brightness_set_blocking = sc27xx_led_set;
143
144                 err = devm_led_classdev_register(dev, &led->ldev);
145                 if (err)
146                         return err;
147         }
148
149         return 0;
150 }
151
152 static int sc27xx_led_probe(struct platform_device *pdev)
153 {
154         struct device *dev = &pdev->dev;
155         struct device_node *np = dev->of_node, *child;
156         struct sc27xx_led_priv *priv;
157         const char *str;
158         u32 base, count, reg;
159         int err;
160
161         count = of_get_child_count(np);
162         if (!count || count > SC27XX_LEDS_MAX)
163                 return -EINVAL;
164
165         err = of_property_read_u32(np, "reg", &base);
166         if (err) {
167                 dev_err(dev, "fail to get reg of property\n");
168                 return err;
169         }
170
171         priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
172         if (!priv)
173                 return -ENOMEM;
174
175         platform_set_drvdata(pdev, priv);
176         mutex_init(&priv->lock);
177         priv->base = base;
178         priv->regmap = dev_get_regmap(dev->parent, NULL);
179         if (!priv->regmap) {
180                 err = -ENODEV;
181                 dev_err(dev, "failed to get regmap: %d\n", err);
182                 return err;
183         }
184
185         for_each_child_of_node(np, child) {
186                 err = of_property_read_u32(child, "reg", &reg);
187                 if (err) {
188                         of_node_put(child);
189                         mutex_destroy(&priv->lock);
190                         return err;
191                 }
192
193                 if (reg >= SC27XX_LEDS_MAX || priv->leds[reg].active) {
194                         of_node_put(child);
195                         mutex_destroy(&priv->lock);
196                         return -EINVAL;
197                 }
198
199                 priv->leds[reg].active = true;
200
201                 err = of_property_read_string(child, "label", &str);
202                 if (err)
203                         snprintf(priv->leds[reg].name, LED_MAX_NAME_SIZE,
204                                  "sc27xx::");
205                 else
206                         snprintf(priv->leds[reg].name, LED_MAX_NAME_SIZE,
207                                  "sc27xx:%s", str);
208         }
209
210         err = sc27xx_led_register(dev, priv);
211         if (err)
212                 mutex_destroy(&priv->lock);
213
214         return err;
215 }
216
217 static int sc27xx_led_remove(struct platform_device *pdev)
218 {
219         struct sc27xx_led_priv *priv = platform_get_drvdata(pdev);
220
221         mutex_destroy(&priv->lock);
222         return 0;
223 }
224
225 static const struct of_device_id sc27xx_led_of_match[] = {
226         { .compatible = "sprd,sc2731-bltc", },
227         { }
228 };
229 MODULE_DEVICE_TABLE(of, sc27xx_led_of_match);
230
231 static struct platform_driver sc27xx_led_driver = {
232         .driver = {
233                 .name = "sprd-bltc",
234                 .of_match_table = sc27xx_led_of_match,
235         },
236         .probe = sc27xx_led_probe,
237         .remove = sc27xx_led_remove,
238 };
239
240 module_platform_driver(sc27xx_led_driver);
241
242 MODULE_DESCRIPTION("Spreadtrum SC27xx breathing light controller driver");
243 MODULE_AUTHOR("Xiaotong Lu <xiaotong.lu@spreadtrum.com>");
244 MODULE_LICENSE("GPL v2");