Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
leds-wm8350.c
Go to the documentation of this file.
1 /*
2  * LED driver for WM8350 driven LEDS.
3  *
4  * Copyright(C) 2007, 2008 Wolfson Microelectronics PLC.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  *
10  */
11 
12 #include <linux/kernel.h>
13 #include <linux/init.h>
14 #include <linux/platform_device.h>
15 #include <linux/leds.h>
16 #include <linux/err.h>
17 #include <linux/mfd/wm8350/pmic.h>
19 #include <linux/slab.h>
20 #include <linux/module.h>
21 
22 /* Microamps */
23 static const int isink_cur[] = {
24  4,
25  5,
26  6,
27  7,
28  8,
29  10,
30  11,
31  14,
32  16,
33  19,
34  23,
35  27,
36  32,
37  39,
38  46,
39  54,
40  65,
41  77,
42  92,
43  109,
44  130,
45  154,
46  183,
47  218,
48  259,
49  308,
50  367,
51  436,
52  518,
53  616,
54  733,
55  872,
56  1037,
57  1233,
58  1466,
59  1744,
60  2073,
61  2466,
62  2933,
63  3487,
64  4147,
65  4932,
66  5865,
67  6975,
68  8294,
69  9864,
70  11730,
71  13949,
72  16589,
73  19728,
74  23460,
75  27899,
76  33178,
77  39455,
78  46920,
79  55798,
80  66355,
81  78910,
82  93840,
83  111596,
84  132710,
85  157820,
86  187681,
87  223191
88 };
89 
90 #define to_wm8350_led(led_cdev) \
91  container_of(led_cdev, struct wm8350_led, cdev)
92 
93 static void wm8350_led_enable(struct wm8350_led *led)
94 {
95  int ret;
96 
97  if (led->enabled)
98  return;
99 
100  ret = regulator_enable(led->isink);
101  if (ret != 0) {
102  dev_err(led->cdev.dev, "Failed to enable ISINK: %d\n", ret);
103  return;
104  }
105 
106  ret = regulator_enable(led->dcdc);
107  if (ret != 0) {
108  dev_err(led->cdev.dev, "Failed to enable DCDC: %d\n", ret);
109  regulator_disable(led->isink);
110  return;
111  }
112 
113  led->enabled = 1;
114 }
115 
116 static void wm8350_led_disable(struct wm8350_led *led)
117 {
118  int ret;
119 
120  if (!led->enabled)
121  return;
122 
123  ret = regulator_disable(led->dcdc);
124  if (ret != 0) {
125  dev_err(led->cdev.dev, "Failed to disable DCDC: %d\n", ret);
126  return;
127  }
128 
129  ret = regulator_disable(led->isink);
130  if (ret != 0) {
131  dev_err(led->cdev.dev, "Failed to disable ISINK: %d\n", ret);
132  regulator_enable(led->dcdc);
133  return;
134  }
135 
136  led->enabled = 0;
137 }
138 
139 static void led_work(struct work_struct *work)
140 {
141  struct wm8350_led *led = container_of(work, struct wm8350_led, work);
142  int ret;
143  int uA;
144  unsigned long flags;
145 
146  mutex_lock(&led->mutex);
147 
148  spin_lock_irqsave(&led->value_lock, flags);
149 
150  if (led->value == LED_OFF) {
151  spin_unlock_irqrestore(&led->value_lock, flags);
152  wm8350_led_disable(led);
153  goto out;
154  }
155 
156  /* This scales linearly into the index of valid current
157  * settings which results in a linear scaling of perceived
158  * brightness due to the non-linear current settings provided
159  * by the hardware.
160  */
161  uA = (led->max_uA_index * led->value) / LED_FULL;
162  spin_unlock_irqrestore(&led->value_lock, flags);
163  BUG_ON(uA >= ARRAY_SIZE(isink_cur));
164 
165  ret = regulator_set_current_limit(led->isink, isink_cur[uA],
166  isink_cur[uA]);
167  if (ret != 0)
168  dev_err(led->cdev.dev, "Failed to set %duA: %d\n",
169  isink_cur[uA], ret);
170 
171  wm8350_led_enable(led);
172 
173 out:
174  mutex_unlock(&led->mutex);
175 }
176 
177 static void wm8350_led_set(struct led_classdev *led_cdev,
178  enum led_brightness value)
179 {
180  struct wm8350_led *led = to_wm8350_led(led_cdev);
181  unsigned long flags;
182 
183  spin_lock_irqsave(&led->value_lock, flags);
184  led->value = value;
185  schedule_work(&led->work);
186  spin_unlock_irqrestore(&led->value_lock, flags);
187 }
188 
189 static void wm8350_led_shutdown(struct platform_device *pdev)
190 {
191  struct wm8350_led *led = platform_get_drvdata(pdev);
192 
193  mutex_lock(&led->mutex);
194  led->value = LED_OFF;
195  wm8350_led_disable(led);
196  mutex_unlock(&led->mutex);
197 }
198 
199 static int wm8350_led_probe(struct platform_device *pdev)
200 {
201  struct regulator *isink, *dcdc;
202  struct wm8350_led *led;
203  struct wm8350_led_platform_data *pdata = pdev->dev.platform_data;
204  int i;
205 
206  if (pdata == NULL) {
207  dev_err(&pdev->dev, "no platform data\n");
208  return -ENODEV;
209  }
210 
211  if (pdata->max_uA < isink_cur[0]) {
212  dev_err(&pdev->dev, "Invalid maximum current %duA\n",
213  pdata->max_uA);
214  return -EINVAL;
215  }
216 
217  isink = devm_regulator_get(&pdev->dev, "led_isink");
218  if (IS_ERR(isink)) {
219  printk(KERN_ERR "%s: can't get ISINK\n", __func__);
220  return PTR_ERR(isink);
221  }
222 
223  dcdc = devm_regulator_get(&pdev->dev, "led_vcc");
224  if (IS_ERR(dcdc)) {
225  printk(KERN_ERR "%s: can't get DCDC\n", __func__);
226  return PTR_ERR(dcdc);
227  }
228 
229  led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL);
230  if (led == NULL)
231  return -ENOMEM;
232 
233  led->cdev.brightness_set = wm8350_led_set;
234  led->cdev.default_trigger = pdata->default_trigger;
235  led->cdev.name = pdata->name;
236  led->cdev.flags |= LED_CORE_SUSPENDRESUME;
237  led->enabled = regulator_is_enabled(isink);
238  led->isink = isink;
239  led->dcdc = dcdc;
240 
241  for (i = 0; i < ARRAY_SIZE(isink_cur) - 1; i++)
242  if (isink_cur[i] >= pdata->max_uA)
243  break;
244  led->max_uA_index = i;
245  if (pdata->max_uA != isink_cur[i])
246  dev_warn(&pdev->dev,
247  "Maximum current %duA is not directly supported,"
248  " check platform data\n",
249  pdata->max_uA);
250 
251  spin_lock_init(&led->value_lock);
252  mutex_init(&led->mutex);
253  INIT_WORK(&led->work, led_work);
254  led->value = LED_OFF;
255  platform_set_drvdata(pdev, led);
256 
257  return led_classdev_register(&pdev->dev, &led->cdev);
258 }
259 
260 static int wm8350_led_remove(struct platform_device *pdev)
261 {
262  struct wm8350_led *led = platform_get_drvdata(pdev);
263 
265  flush_work(&led->work);
266  wm8350_led_disable(led);
267  return 0;
268 }
269 
270 static struct platform_driver wm8350_led_driver = {
271  .driver = {
272  .name = "wm8350-led",
273  .owner = THIS_MODULE,
274  },
275  .probe = wm8350_led_probe,
276  .remove = wm8350_led_remove,
277  .shutdown = wm8350_led_shutdown,
278 };
279 
280 module_platform_driver(wm8350_led_driver);
281 
282 MODULE_AUTHOR("Mark Brown");
283 MODULE_DESCRIPTION("WM8350 LED driver");
284 MODULE_LICENSE("GPL");
285 MODULE_ALIAS("platform:wm8350-led");