Merge tag 'for-linus-20180929' of git://git.kernel.dk/linux-block
[linux-2.6/btrfs-unstable.git] / drivers / leds / leds-sc27xx-bltc.c
blob9d9b7aab843f188bb51b06b4a9aab3e933f344d1
1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (C) 2018 Spreadtrum Communications Inc.
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>
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
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;
46 struct sc27xx_led_priv {
47 struct sc27xx_led leds[SC27XX_LEDS_MAX];
48 struct regmap *regmap;
49 struct mutex lock;
50 u32 base;
53 #define to_sc27xx_led(ldev) \
54 container_of(ldev, struct sc27xx_led, ldev)
56 static int sc27xx_led_init(struct regmap *regmap)
58 int err;
60 err = regmap_update_bits(regmap, SC27XX_MODULE_EN0, SC27XX_BLTC_EN,
61 SC27XX_BLTC_EN);
62 if (err)
63 return err;
65 err = regmap_update_bits(regmap, SC27XX_CLK_EN0, SC27XX_RTC_EN,
66 SC27XX_RTC_EN);
67 if (err)
68 return err;
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;
84 int err;
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;
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);
111 int err;
113 mutex_lock(&leds->priv->lock);
115 if (value == LED_OFF)
116 err = sc27xx_led_disable(leds);
117 else
118 err = sc27xx_led_enable(leds, value);
120 mutex_unlock(&leds->priv->lock);
122 return err;
125 static int sc27xx_led_register(struct device *dev, struct sc27xx_led_priv *priv)
127 int i, err;
129 err = sc27xx_led_init(priv->regmap);
130 if (err)
131 return err;
133 for (i = 0; i < SC27XX_LEDS_MAX; i++) {
134 struct sc27xx_led *led = &priv->leds[i];
136 if (!led->active)
137 continue;
139 led->line = i;
140 led->priv = priv;
141 led->ldev.name = led->name;
142 led->ldev.brightness_set_blocking = sc27xx_led_set;
144 err = devm_led_classdev_register(dev, &led->ldev);
145 if (err)
146 return err;
149 return 0;
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;
157 const char *str;
158 u32 base, count, reg;
159 int err;
161 count = of_get_child_count(np);
162 if (!count || count > SC27XX_LEDS_MAX)
163 return -EINVAL;
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;
171 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
172 if (!priv)
173 return -ENOMEM;
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;
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;
193 if (reg >= SC27XX_LEDS_MAX || priv->leds[reg].active) {
194 of_node_put(child);
195 mutex_destroy(&priv->lock);
196 return -EINVAL;
199 priv->leds[reg].active = true;
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);
210 err = sc27xx_led_register(dev, priv);
211 if (err)
212 mutex_destroy(&priv->lock);
214 return err;
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);
222 return 0;
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 = {
232 .driver = {
233 .name = "sprd-bltc",
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");