xref: /openbmc/linux/drivers/leds/leds-sc27xx-bltc.c (revision cbecf716ca618fd44feda6bd9a64a8179d031fc5)
1e081c49eSBaolin Wang // SPDX-License-Identifier: GPL-2.0
2e081c49eSBaolin Wang // Copyright (C) 2018 Spreadtrum Communications Inc.
3e081c49eSBaolin Wang 
4e081c49eSBaolin Wang #include <linux/leds.h>
5e081c49eSBaolin Wang #include <linux/module.h>
6e081c49eSBaolin Wang #include <linux/of.h>
7e081c49eSBaolin Wang #include <linux/platform_device.h>
8e081c49eSBaolin Wang #include <linux/regmap.h>
9e081c49eSBaolin Wang 
10e081c49eSBaolin Wang /* PMIC global control register definition */
11e081c49eSBaolin Wang #define SC27XX_MODULE_EN0	0xc08
12e081c49eSBaolin Wang #define SC27XX_CLK_EN0		0xc18
13e081c49eSBaolin Wang #define SC27XX_RGB_CTRL		0xebc
14e081c49eSBaolin Wang 
15e081c49eSBaolin Wang #define SC27XX_BLTC_EN		BIT(9)
16e081c49eSBaolin Wang #define SC27XX_RTC_EN		BIT(7)
17e081c49eSBaolin Wang #define SC27XX_RGB_PD		BIT(0)
18e081c49eSBaolin Wang 
19e081c49eSBaolin Wang /* Breathing light controller register definition */
20e081c49eSBaolin Wang #define SC27XX_LEDS_CTRL	0x00
21e081c49eSBaolin Wang #define SC27XX_LEDS_PRESCALE	0x04
22e081c49eSBaolin Wang #define SC27XX_LEDS_DUTY	0x08
23e081c49eSBaolin Wang #define SC27XX_LEDS_CURVE0	0x0c
24e081c49eSBaolin Wang #define SC27XX_LEDS_CURVE1	0x10
25e081c49eSBaolin Wang 
26e081c49eSBaolin Wang #define SC27XX_CTRL_SHIFT	4
27e081c49eSBaolin Wang #define SC27XX_LED_RUN		BIT(0)
28e081c49eSBaolin Wang #define SC27XX_LED_TYPE		BIT(1)
29e081c49eSBaolin Wang 
30e081c49eSBaolin Wang #define SC27XX_DUTY_SHIFT	8
31e081c49eSBaolin Wang #define SC27XX_DUTY_MASK	GENMASK(15, 0)
32e081c49eSBaolin Wang #define SC27XX_MOD_MASK		GENMASK(7, 0)
33e081c49eSBaolin Wang 
348dbac65fSBaolin Wang #define SC27XX_CURVE_SHIFT	8
358dbac65fSBaolin Wang #define SC27XX_CURVE_L_MASK	GENMASK(7, 0)
368dbac65fSBaolin Wang #define SC27XX_CURVE_H_MASK	GENMASK(15, 8)
378dbac65fSBaolin Wang 
38e081c49eSBaolin Wang #define SC27XX_LEDS_OFFSET	0x10
39e081c49eSBaolin Wang #define SC27XX_LEDS_MAX		3
408dbac65fSBaolin Wang #define SC27XX_LEDS_PATTERN_CNT	4
418dbac65fSBaolin Wang /* Stage duration step, in milliseconds */
428dbac65fSBaolin Wang #define SC27XX_LEDS_STEP	125
438dbac65fSBaolin Wang /* Minimum and maximum duration, in milliseconds */
448dbac65fSBaolin Wang #define SC27XX_DELTA_T_MIN	SC27XX_LEDS_STEP
458dbac65fSBaolin Wang #define SC27XX_DELTA_T_MAX	(SC27XX_LEDS_STEP * 255)
46e081c49eSBaolin Wang 
47e081c49eSBaolin Wang struct sc27xx_led {
485fdf85a0SJacek Anaszewski 	struct fwnode_handle *fwnode;
49e081c49eSBaolin Wang 	struct led_classdev ldev;
50e081c49eSBaolin Wang 	struct sc27xx_led_priv *priv;
51e081c49eSBaolin Wang 	u8 line;
52e081c49eSBaolin Wang 	bool active;
53e081c49eSBaolin Wang };
54e081c49eSBaolin Wang 
55e081c49eSBaolin Wang struct sc27xx_led_priv {
56e081c49eSBaolin Wang 	struct sc27xx_led leds[SC27XX_LEDS_MAX];
57e081c49eSBaolin Wang 	struct regmap *regmap;
58e081c49eSBaolin Wang 	struct mutex lock;
59e081c49eSBaolin Wang 	u32 base;
60e081c49eSBaolin Wang };
61e081c49eSBaolin Wang 
62e081c49eSBaolin Wang #define to_sc27xx_led(ldev) \
63e081c49eSBaolin Wang 	container_of(ldev, struct sc27xx_led, ldev)
64e081c49eSBaolin Wang 
sc27xx_led_init(struct regmap * regmap)65e081c49eSBaolin Wang static int sc27xx_led_init(struct regmap *regmap)
66e081c49eSBaolin Wang {
67e081c49eSBaolin Wang 	int err;
68e081c49eSBaolin Wang 
69e081c49eSBaolin Wang 	err = regmap_update_bits(regmap, SC27XX_MODULE_EN0, SC27XX_BLTC_EN,
70e081c49eSBaolin Wang 				 SC27XX_BLTC_EN);
71e081c49eSBaolin Wang 	if (err)
72e081c49eSBaolin Wang 		return err;
73e081c49eSBaolin Wang 
74e081c49eSBaolin Wang 	err = regmap_update_bits(regmap, SC27XX_CLK_EN0, SC27XX_RTC_EN,
75e081c49eSBaolin Wang 				 SC27XX_RTC_EN);
76e081c49eSBaolin Wang 	if (err)
77e081c49eSBaolin Wang 		return err;
78e081c49eSBaolin Wang 
79e081c49eSBaolin Wang 	return regmap_update_bits(regmap, SC27XX_RGB_CTRL, SC27XX_RGB_PD, 0);
80e081c49eSBaolin Wang }
81e081c49eSBaolin Wang 
sc27xx_led_get_offset(struct sc27xx_led * leds)82e081c49eSBaolin Wang static u32 sc27xx_led_get_offset(struct sc27xx_led *leds)
83e081c49eSBaolin Wang {
84e081c49eSBaolin Wang 	return leds->priv->base + SC27XX_LEDS_OFFSET * leds->line;
85e081c49eSBaolin Wang }
86e081c49eSBaolin Wang 
sc27xx_led_enable(struct sc27xx_led * leds,enum led_brightness value)87e081c49eSBaolin Wang static int sc27xx_led_enable(struct sc27xx_led *leds, enum led_brightness value)
88e081c49eSBaolin Wang {
89e081c49eSBaolin Wang 	u32 base = sc27xx_led_get_offset(leds);
90e081c49eSBaolin Wang 	u32 ctrl_base = leds->priv->base + SC27XX_LEDS_CTRL;
91e081c49eSBaolin Wang 	u8 ctrl_shift = SC27XX_CTRL_SHIFT * leds->line;
92e081c49eSBaolin Wang 	struct regmap *regmap = leds->priv->regmap;
93e081c49eSBaolin Wang 	int err;
94e081c49eSBaolin Wang 
95e081c49eSBaolin Wang 	err = regmap_update_bits(regmap, base + SC27XX_LEDS_DUTY,
96e081c49eSBaolin Wang 				 SC27XX_DUTY_MASK,
97e081c49eSBaolin Wang 				 (value << SC27XX_DUTY_SHIFT) |
98e081c49eSBaolin Wang 				 SC27XX_MOD_MASK);
99e081c49eSBaolin Wang 	if (err)
100e081c49eSBaolin Wang 		return err;
101e081c49eSBaolin Wang 
102e081c49eSBaolin Wang 	return regmap_update_bits(regmap, ctrl_base,
103e081c49eSBaolin Wang 			(SC27XX_LED_RUN | SC27XX_LED_TYPE) << ctrl_shift,
104e081c49eSBaolin Wang 			(SC27XX_LED_RUN | SC27XX_LED_TYPE) << ctrl_shift);
105e081c49eSBaolin Wang }
106e081c49eSBaolin Wang 
sc27xx_led_disable(struct sc27xx_led * leds)107e081c49eSBaolin Wang static int sc27xx_led_disable(struct sc27xx_led *leds)
108e081c49eSBaolin Wang {
109e081c49eSBaolin Wang 	struct regmap *regmap = leds->priv->regmap;
110e081c49eSBaolin Wang 	u32 ctrl_base = leds->priv->base + SC27XX_LEDS_CTRL;
111e081c49eSBaolin Wang 	u8 ctrl_shift = SC27XX_CTRL_SHIFT * leds->line;
112e081c49eSBaolin Wang 
113e081c49eSBaolin Wang 	return regmap_update_bits(regmap, ctrl_base,
114e081c49eSBaolin Wang 			(SC27XX_LED_RUN | SC27XX_LED_TYPE) << ctrl_shift, 0);
115e081c49eSBaolin Wang }
116e081c49eSBaolin Wang 
sc27xx_led_set(struct led_classdev * ldev,enum led_brightness value)117e081c49eSBaolin Wang static int sc27xx_led_set(struct led_classdev *ldev, enum led_brightness value)
118e081c49eSBaolin Wang {
119e081c49eSBaolin Wang 	struct sc27xx_led *leds = to_sc27xx_led(ldev);
120e081c49eSBaolin Wang 	int err;
121e081c49eSBaolin Wang 
122e081c49eSBaolin Wang 	mutex_lock(&leds->priv->lock);
123e081c49eSBaolin Wang 
124e081c49eSBaolin Wang 	if (value == LED_OFF)
125e081c49eSBaolin Wang 		err = sc27xx_led_disable(leds);
126e081c49eSBaolin Wang 	else
127e081c49eSBaolin Wang 		err = sc27xx_led_enable(leds, value);
128e081c49eSBaolin Wang 
129e081c49eSBaolin Wang 	mutex_unlock(&leds->priv->lock);
130e081c49eSBaolin Wang 
131e081c49eSBaolin Wang 	return err;
132e081c49eSBaolin Wang }
133e081c49eSBaolin Wang 
sc27xx_led_clamp_align_delta_t(u32 * delta_t)1348dbac65fSBaolin Wang static void sc27xx_led_clamp_align_delta_t(u32 *delta_t)
1358dbac65fSBaolin Wang {
1368dbac65fSBaolin Wang 	u32 v, offset, t = *delta_t;
1378dbac65fSBaolin Wang 
1388dbac65fSBaolin Wang 	v = t + SC27XX_LEDS_STEP / 2;
1398dbac65fSBaolin Wang 	v = clamp_t(u32, v, SC27XX_DELTA_T_MIN, SC27XX_DELTA_T_MAX);
1408dbac65fSBaolin Wang 	offset = v - SC27XX_DELTA_T_MIN;
1418dbac65fSBaolin Wang 	offset = SC27XX_LEDS_STEP * (offset / SC27XX_LEDS_STEP);
1428dbac65fSBaolin Wang 
1438dbac65fSBaolin Wang 	*delta_t = SC27XX_DELTA_T_MIN + offset;
1448dbac65fSBaolin Wang }
1458dbac65fSBaolin Wang 
sc27xx_led_pattern_clear(struct led_classdev * ldev)1468dbac65fSBaolin Wang static int sc27xx_led_pattern_clear(struct led_classdev *ldev)
1478dbac65fSBaolin Wang {
1488dbac65fSBaolin Wang 	struct sc27xx_led *leds = to_sc27xx_led(ldev);
1498dbac65fSBaolin Wang 	struct regmap *regmap = leds->priv->regmap;
1508dbac65fSBaolin Wang 	u32 base = sc27xx_led_get_offset(leds);
1518dbac65fSBaolin Wang 	u32 ctrl_base = leds->priv->base + SC27XX_LEDS_CTRL;
1528dbac65fSBaolin Wang 	u8 ctrl_shift = SC27XX_CTRL_SHIFT * leds->line;
1538dbac65fSBaolin Wang 	int err;
1548dbac65fSBaolin Wang 
1558dbac65fSBaolin Wang 	mutex_lock(&leds->priv->lock);
1568dbac65fSBaolin Wang 
1578dbac65fSBaolin Wang 	/* Reset the rise, high, fall and low time to zero. */
1588dbac65fSBaolin Wang 	regmap_write(regmap, base + SC27XX_LEDS_CURVE0, 0);
1598dbac65fSBaolin Wang 	regmap_write(regmap, base + SC27XX_LEDS_CURVE1, 0);
1608dbac65fSBaolin Wang 
1618dbac65fSBaolin Wang 	err = regmap_update_bits(regmap, ctrl_base,
1628dbac65fSBaolin Wang 			(SC27XX_LED_RUN | SC27XX_LED_TYPE) << ctrl_shift, 0);
1638dbac65fSBaolin Wang 
1648dbac65fSBaolin Wang 	ldev->brightness = LED_OFF;
1658dbac65fSBaolin Wang 
1668dbac65fSBaolin Wang 	mutex_unlock(&leds->priv->lock);
1678dbac65fSBaolin Wang 
1688dbac65fSBaolin Wang 	return err;
1698dbac65fSBaolin Wang }
1708dbac65fSBaolin Wang 
sc27xx_led_pattern_set(struct led_classdev * ldev,struct led_pattern * pattern,u32 len,int repeat)1718dbac65fSBaolin Wang static int sc27xx_led_pattern_set(struct led_classdev *ldev,
1728dbac65fSBaolin Wang 				  struct led_pattern *pattern,
1738dbac65fSBaolin Wang 				  u32 len, int repeat)
1748dbac65fSBaolin Wang {
1758dbac65fSBaolin Wang 	struct sc27xx_led *leds = to_sc27xx_led(ldev);
1768dbac65fSBaolin Wang 	u32 base = sc27xx_led_get_offset(leds);
1778dbac65fSBaolin Wang 	u32 ctrl_base = leds->priv->base + SC27XX_LEDS_CTRL;
1788dbac65fSBaolin Wang 	u8 ctrl_shift = SC27XX_CTRL_SHIFT * leds->line;
1798dbac65fSBaolin Wang 	struct regmap *regmap = leds->priv->regmap;
1808dbac65fSBaolin Wang 	int err;
1818dbac65fSBaolin Wang 
1828dbac65fSBaolin Wang 	/*
1838dbac65fSBaolin Wang 	 * Must contain 4 tuples to configure the rise time, high time, fall
1848dbac65fSBaolin Wang 	 * time and low time to enable the breathing mode.
1858dbac65fSBaolin Wang 	 */
1868dbac65fSBaolin Wang 	if (len != SC27XX_LEDS_PATTERN_CNT)
1878dbac65fSBaolin Wang 		return -EINVAL;
1888dbac65fSBaolin Wang 
1898dbac65fSBaolin Wang 	mutex_lock(&leds->priv->lock);
1908dbac65fSBaolin Wang 
1918dbac65fSBaolin Wang 	sc27xx_led_clamp_align_delta_t(&pattern[0].delta_t);
1928dbac65fSBaolin Wang 	err = regmap_update_bits(regmap, base + SC27XX_LEDS_CURVE0,
1938dbac65fSBaolin Wang 				 SC27XX_CURVE_L_MASK,
1948dbac65fSBaolin Wang 				 pattern[0].delta_t / SC27XX_LEDS_STEP);
1958dbac65fSBaolin Wang 	if (err)
1968dbac65fSBaolin Wang 		goto out;
1978dbac65fSBaolin Wang 
1988dbac65fSBaolin Wang 	sc27xx_led_clamp_align_delta_t(&pattern[1].delta_t);
1998dbac65fSBaolin Wang 	err = regmap_update_bits(regmap, base + SC27XX_LEDS_CURVE1,
2008dbac65fSBaolin Wang 				 SC27XX_CURVE_L_MASK,
2018dbac65fSBaolin Wang 				 pattern[1].delta_t / SC27XX_LEDS_STEP);
2028dbac65fSBaolin Wang 	if (err)
2038dbac65fSBaolin Wang 		goto out;
2048dbac65fSBaolin Wang 
2058dbac65fSBaolin Wang 	sc27xx_led_clamp_align_delta_t(&pattern[2].delta_t);
2068dbac65fSBaolin Wang 	err = regmap_update_bits(regmap, base + SC27XX_LEDS_CURVE0,
2078dbac65fSBaolin Wang 				 SC27XX_CURVE_H_MASK,
2088dbac65fSBaolin Wang 				 (pattern[2].delta_t / SC27XX_LEDS_STEP) <<
2098dbac65fSBaolin Wang 				 SC27XX_CURVE_SHIFT);
2108dbac65fSBaolin Wang 	if (err)
2118dbac65fSBaolin Wang 		goto out;
2128dbac65fSBaolin Wang 
2138dbac65fSBaolin Wang 	sc27xx_led_clamp_align_delta_t(&pattern[3].delta_t);
2148dbac65fSBaolin Wang 	err = regmap_update_bits(regmap, base + SC27XX_LEDS_CURVE1,
2158dbac65fSBaolin Wang 				 SC27XX_CURVE_H_MASK,
2168dbac65fSBaolin Wang 				 (pattern[3].delta_t / SC27XX_LEDS_STEP) <<
2178dbac65fSBaolin Wang 				 SC27XX_CURVE_SHIFT);
2188dbac65fSBaolin Wang 	if (err)
2198dbac65fSBaolin Wang 		goto out;
2208dbac65fSBaolin Wang 
2218dbac65fSBaolin Wang 	err = regmap_update_bits(regmap, base + SC27XX_LEDS_DUTY,
2228dbac65fSBaolin Wang 				 SC27XX_DUTY_MASK,
2238dbac65fSBaolin Wang 				 (pattern[1].brightness << SC27XX_DUTY_SHIFT) |
2248dbac65fSBaolin Wang 				 SC27XX_MOD_MASK);
2258dbac65fSBaolin Wang 	if (err)
2268dbac65fSBaolin Wang 		goto out;
2278dbac65fSBaolin Wang 
2288dbac65fSBaolin Wang 	/* Enable the LED breathing mode */
2298dbac65fSBaolin Wang 	err = regmap_update_bits(regmap, ctrl_base,
2308dbac65fSBaolin Wang 				 SC27XX_LED_RUN << ctrl_shift,
2318dbac65fSBaolin Wang 				 SC27XX_LED_RUN << ctrl_shift);
2328dbac65fSBaolin Wang 	if (!err)
2338dbac65fSBaolin Wang 		ldev->brightness = pattern[1].brightness;
2348dbac65fSBaolin Wang 
2358dbac65fSBaolin Wang out:
2368dbac65fSBaolin Wang 	mutex_unlock(&leds->priv->lock);
2378dbac65fSBaolin Wang 
2388dbac65fSBaolin Wang 	return err;
2398dbac65fSBaolin Wang }
2408dbac65fSBaolin Wang 
sc27xx_led_register(struct device * dev,struct sc27xx_led_priv * priv)241e081c49eSBaolin Wang static int sc27xx_led_register(struct device *dev, struct sc27xx_led_priv *priv)
242e081c49eSBaolin Wang {
243e081c49eSBaolin Wang 	int i, err;
244e081c49eSBaolin Wang 
245e081c49eSBaolin Wang 	err = sc27xx_led_init(priv->regmap);
246e081c49eSBaolin Wang 	if (err)
247e081c49eSBaolin Wang 		return err;
248e081c49eSBaolin Wang 
249e081c49eSBaolin Wang 	for (i = 0; i < SC27XX_LEDS_MAX; i++) {
250e081c49eSBaolin Wang 		struct sc27xx_led *led = &priv->leds[i];
2515fdf85a0SJacek Anaszewski 		struct led_init_data init_data = {};
252e081c49eSBaolin Wang 
253e081c49eSBaolin Wang 		if (!led->active)
254e081c49eSBaolin Wang 			continue;
255e081c49eSBaolin Wang 
256e081c49eSBaolin Wang 		led->line = i;
257e081c49eSBaolin Wang 		led->priv = priv;
258e081c49eSBaolin Wang 		led->ldev.brightness_set_blocking = sc27xx_led_set;
2598dbac65fSBaolin Wang 		led->ldev.pattern_set = sc27xx_led_pattern_set;
2608dbac65fSBaolin Wang 		led->ldev.pattern_clear = sc27xx_led_pattern_clear;
2618dbac65fSBaolin Wang 		led->ldev.default_trigger = "pattern";
262e081c49eSBaolin Wang 
2635fdf85a0SJacek Anaszewski 		init_data.fwnode = led->fwnode;
2645fdf85a0SJacek Anaszewski 		init_data.devicename = "sc27xx";
2655fdf85a0SJacek Anaszewski 		init_data.default_label = ":";
2665fdf85a0SJacek Anaszewski 
2675fdf85a0SJacek Anaszewski 		err = devm_led_classdev_register_ext(dev, &led->ldev,
2685fdf85a0SJacek Anaszewski 						     &init_data);
269e081c49eSBaolin Wang 		if (err)
270e081c49eSBaolin Wang 			return err;
271e081c49eSBaolin Wang 	}
272e081c49eSBaolin Wang 
273e081c49eSBaolin Wang 	return 0;
274e081c49eSBaolin Wang }
275e081c49eSBaolin Wang 
sc27xx_led_probe(struct platform_device * pdev)276e081c49eSBaolin Wang static int sc27xx_led_probe(struct platform_device *pdev)
277e081c49eSBaolin Wang {
278e081c49eSBaolin Wang 	struct device *dev = &pdev->dev;
2798853c95eSMarek Behún 	struct device_node *np = dev_of_node(dev), *child;
280e081c49eSBaolin Wang 	struct sc27xx_led_priv *priv;
281e081c49eSBaolin Wang 	u32 base, count, reg;
282e081c49eSBaolin Wang 	int err;
283e081c49eSBaolin Wang 
284*99a013c8SMarek Behún 	count = of_get_available_child_count(np);
285e081c49eSBaolin Wang 	if (!count || count > SC27XX_LEDS_MAX)
286e081c49eSBaolin Wang 		return -EINVAL;
287e081c49eSBaolin Wang 
288e081c49eSBaolin Wang 	err = of_property_read_u32(np, "reg", &base);
289e081c49eSBaolin Wang 	if (err) {
290e081c49eSBaolin Wang 		dev_err(dev, "fail to get reg of property\n");
291e081c49eSBaolin Wang 		return err;
292e081c49eSBaolin Wang 	}
293e081c49eSBaolin Wang 
294e081c49eSBaolin Wang 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
295e081c49eSBaolin Wang 	if (!priv)
296e081c49eSBaolin Wang 		return -ENOMEM;
297e081c49eSBaolin Wang 
298e081c49eSBaolin Wang 	platform_set_drvdata(pdev, priv);
299e081c49eSBaolin Wang 	mutex_init(&priv->lock);
300e081c49eSBaolin Wang 	priv->base = base;
301e081c49eSBaolin Wang 	priv->regmap = dev_get_regmap(dev->parent, NULL);
30243926c27SWei Yongjun 	if (!priv->regmap) {
30343926c27SWei Yongjun 		err = -ENODEV;
304e081c49eSBaolin Wang 		dev_err(dev, "failed to get regmap: %d\n", err);
305e081c49eSBaolin Wang 		return err;
306e081c49eSBaolin Wang 	}
307e081c49eSBaolin Wang 
308*99a013c8SMarek Behún 	for_each_available_child_of_node(np, child) {
309e081c49eSBaolin Wang 		err = of_property_read_u32(child, "reg", &reg);
310e081c49eSBaolin Wang 		if (err) {
311e081c49eSBaolin Wang 			of_node_put(child);
312e081c49eSBaolin Wang 			mutex_destroy(&priv->lock);
313e081c49eSBaolin Wang 			return err;
314e081c49eSBaolin Wang 		}
315e081c49eSBaolin Wang 
316e081c49eSBaolin Wang 		if (reg >= SC27XX_LEDS_MAX || priv->leds[reg].active) {
317e081c49eSBaolin Wang 			of_node_put(child);
318e081c49eSBaolin Wang 			mutex_destroy(&priv->lock);
319e081c49eSBaolin Wang 			return -EINVAL;
320e081c49eSBaolin Wang 		}
321e081c49eSBaolin Wang 
3225fdf85a0SJacek Anaszewski 		priv->leds[reg].fwnode = of_fwnode_handle(child);
323e081c49eSBaolin Wang 		priv->leds[reg].active = true;
324e081c49eSBaolin Wang 	}
325e081c49eSBaolin Wang 
326e081c49eSBaolin Wang 	err = sc27xx_led_register(dev, priv);
327e081c49eSBaolin Wang 	if (err)
328e081c49eSBaolin Wang 		mutex_destroy(&priv->lock);
329e081c49eSBaolin Wang 
330e081c49eSBaolin Wang 	return err;
331e081c49eSBaolin Wang }
332e081c49eSBaolin Wang 
sc27xx_led_remove(struct platform_device * pdev)333e081c49eSBaolin Wang static int sc27xx_led_remove(struct platform_device *pdev)
334e081c49eSBaolin Wang {
335e081c49eSBaolin Wang 	struct sc27xx_led_priv *priv = platform_get_drvdata(pdev);
336e081c49eSBaolin Wang 
337e081c49eSBaolin Wang 	mutex_destroy(&priv->lock);
338e081c49eSBaolin Wang 	return 0;
339e081c49eSBaolin Wang }
340e081c49eSBaolin Wang 
341e081c49eSBaolin Wang static const struct of_device_id sc27xx_led_of_match[] = {
342e081c49eSBaolin Wang 	{ .compatible = "sprd,sc2731-bltc", },
343e081c49eSBaolin Wang 	{ }
344e081c49eSBaolin Wang };
345e081c49eSBaolin Wang MODULE_DEVICE_TABLE(of, sc27xx_led_of_match);
346e081c49eSBaolin Wang 
347e081c49eSBaolin Wang static struct platform_driver sc27xx_led_driver = {
348e081c49eSBaolin Wang 	.driver = {
349e081c49eSBaolin Wang 		.name = "sprd-bltc",
350e081c49eSBaolin Wang 		.of_match_table = sc27xx_led_of_match,
351e081c49eSBaolin Wang 	},
352e081c49eSBaolin Wang 	.probe = sc27xx_led_probe,
353e081c49eSBaolin Wang 	.remove = sc27xx_led_remove,
354e081c49eSBaolin Wang };
355e081c49eSBaolin Wang 
356e081c49eSBaolin Wang module_platform_driver(sc27xx_led_driver);
357e081c49eSBaolin Wang 
358e081c49eSBaolin Wang MODULE_DESCRIPTION("Spreadtrum SC27xx breathing light controller driver");
359e081c49eSBaolin Wang MODULE_AUTHOR("Xiaotong Lu <xiaotong.lu@spreadtrum.com>");
3608dbac65fSBaolin Wang MODULE_AUTHOR("Baolin Wang <baolin.wang@linaro.org>");
361e081c49eSBaolin Wang MODULE_LICENSE("GPL v2");
362