blob: ba122bb72ab72b8997bbba918e31b8bb16c712f3 [file] [log] [blame]
Amy Malochef3d5a062012-08-16 19:14:11 -07001
Amy Malochea5ca5552012-10-23 13:34:46 -07002/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
Amy Malochef3d5a062012-08-16 19:14:11 -07003 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 and
6 * only version 2 as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14#include <linux/kernel.h>
15#include <linux/module.h>
16#include <linux/init.h>
17#include <linux/slab.h>
18#include <linux/leds.h>
19#include <linux/err.h>
Amy Malochedc3e5572012-09-25 16:39:06 -070020#include <linux/spinlock.h>
Amy Malochef3d5a062012-08-16 19:14:11 -070021#include <linux/of_platform.h>
22#include <linux/of_device.h>
23#include <linux/spmi.h>
24
25#define WLED_MOD_EN_REG(base, n) (base + 0x60 + n*0x10)
26#define WLED_IDAC_DLY_REG(base, n) (WLED_MOD_EN_REG(base, n) + 0x01)
27#define WLED_FULL_SCALE_REG(base, n) (WLED_IDAC_DLY_REG(base, n) + 0x01)
28
29/* wled control registers */
30#define WLED_BRIGHTNESS_CNTL_LSB(base, n) (base + 0x40 + 2*n)
31#define WLED_BRIGHTNESS_CNTL_MSB(base, n) (base + 0x41 + 2*n)
32#define WLED_MOD_CTRL_REG(base) (base + 0x46)
33#define WLED_SYNC_REG(base) (base + 0x47)
34#define WLED_FDBCK_CTRL_REG(base) (base + 0x48)
35#define WLED_SWITCHING_FREQ_REG(base) (base + 0x4C)
36#define WLED_OVP_CFG_REG(base) (base + 0x4D)
37#define WLED_BOOST_LIMIT_REG(base) (base + 0x4E)
38#define WLED_CURR_SINK_REG(base) (base + 0x4F)
39#define WLED_HIGH_POLE_CAP_REG(base) (base + 0x58)
40#define WLED_CURR_SINK_MASK 0xE0
41#define WLED_CURR_SINK_SHFT 0x05
42#define WLED_SWITCH_FREQ_MASK 0x02
43#define WLED_OVP_VAL_MASK 0x03
44#define WLED_OVP_VAL_BIT_SHFT 0x00
45#define WLED_BOOST_LIMIT_MASK 0x07
46#define WLED_BOOST_LIMIT_BIT_SHFT 0x00
47#define WLED_BOOST_ON 0x80
48#define WLED_BOOST_OFF 0x00
49#define WLED_EN_MASK 0x80
50#define WLED_NO_MASK 0x00
51#define WLED_CP_SELECT_MAX 0x03
52#define WLED_CP_SELECT_MASK 0x02
53#define WLED_USE_EXT_GEN_MOD_SRC 0x01
54#define WLED_CTL_DLY_STEP 200
55#define WLED_CTL_DLY_MAX 1400
56#define WLED_MAX_CURR 25
57#define WLED_MSB_MASK 0x0F
58#define WLED_MAX_CURR_MASK 0x19
59#define WLED_OP_FDBCK_MASK 0x07
60#define WLED_OP_FDBCK_BIT_SHFT 0x00
61
62#define WLED_MAX_LEVEL 255
63#define WLED_8_BIT_MASK 0xFF
64#define WLED_4_BIT_MASK 0x0F
65#define WLED_8_BIT_SHFT 0x08
66#define WLED_MAX_DUTY_CYCLE 0xFFF
67
68#define WLED_SYNC_VAL 0x07
69#define WLED_SYNC_RESET_VAL 0x00
70
Amy Malochef3d5a062012-08-16 19:14:11 -070071#define WLED_DEFAULT_STRINGS 0x01
72#define WLED_DEFAULT_OVP_VAL 0x02
73#define WLED_BOOST_LIM_DEFAULT 0x03
74#define WLED_CP_SEL_DEFAULT 0x00
75#define WLED_CTRL_DLY_DEFAULT 0x00
76#define WLED_SWITCH_FREQ_DEFAULT 0x02
77
Amy Maloche864a6d52012-10-03 15:58:12 -070078#define FLASH_SAFETY_TIMER(base) (base + 0x40)
79#define FLASH_MAX_CURR(base) (base + 0x41)
80#define FLASH_LED_0_CURR(base) (base + 0x42)
81#define FLASH_LED_1_CURR(base) (base + 0x43)
82#define FLASH_CLAMP_CURR(base) (base + 0x44)
83#define FLASH_LED_TMR_CTRL(base) (base + 0x48)
84#define FLASH_HEADROOM(base) (base + 0x49)
85#define FLASH_STARTUP_DELAY(base) (base + 0x4B)
86#define FLASH_MASK_ENABLE(base) (base + 0x4C)
87#define FLASH_VREG_OK_FORCE(base) (base + 0x4F)
88#define FLASH_ENABLE_CONTROL(base) (base + 0x46)
89#define FLASH_LED_STROBE_CTRL(base) (base + 0x47)
90
91#define FLASH_MAX_LEVEL 0x4F
92#define FLASH_NO_MASK 0x00
93
94#define FLASH_MASK_1 0x20
95#define FLASH_MASK_REG_MASK 0xE0
96#define FLASH_HEADROOM_MASK 0x03
97#define FLASH_SAFETY_TIMER_MASK 0x7F
98#define FLASH_CURRENT_MASK 0xFF
99#define FLASH_TMR_MASK 0x03
100#define FLASH_TMR_WATCHDOG 0x03
101#define FLASH_TMR_SAFETY 0x00
102
103#define FLASH_HW_VREG_OK 0x80
104#define FLASH_VREG_MASK 0xC0
105
106#define FLASH_STARTUP_DLY_MASK 0x02
107
108#define FLASH_ENABLE_ALL 0xE0
109#define FLASH_ENABLE_MODULE 0x80
110#define FLASH_ENABLE_MODULE_MASK 0x80
111#define FLASH_DISABLE_ALL 0x00
112#define FLASH_ENABLE_MASK 0x60
113#define FLASH_ENABLE_LED_0 0x40
114#define FLASH_ENABLE_LED_1 0x20
115#define FLASH_INIT_MASK 0xE0
116
117#define FLASH_STROBE_ALL 0xC0
118#define FLASH_STROBE_MASK 0xC0
119#define FLASH_LED_0_OUTPUT 0x80
120#define FLASH_LED_1_OUTPUT 0x40
121
122#define FLASH_CURRENT_PRGM_MIN 1
123#define FLASH_CURRENT_PRGM_SHIFT 1
124
125#define FLASH_DURATION_200ms 0x13
126#define FLASH_CLAMP_200mA 0x0F
127
Amy Malochea5ca5552012-10-23 13:34:46 -0700128#define LED_TRIGGER_DEFAULT "none"
129
Amy Malochef3d5a062012-08-16 19:14:11 -0700130/**
131 * enum qpnp_leds - QPNP supported led ids
132 * @QPNP_ID_WLED - White led backlight
133 */
134enum qpnp_leds {
Amy Maloche864a6d52012-10-03 15:58:12 -0700135 QPNP_ID_WLED = 0,
136 QPNP_ID_FLASH1_LED0,
137 QPNP_ID_FLASH1_LED1,
138 QPNP_ID_MAX,
Amy Malochef3d5a062012-08-16 19:14:11 -0700139};
140
141/* current boost limit */
142enum wled_current_boost_limit {
143 WLED_CURR_LIMIT_105mA,
144 WLED_CURR_LIMIT_385mA,
145 WLED_CURR_LIMIT_525mA,
146 WLED_CURR_LIMIT_805mA,
147 WLED_CURR_LIMIT_980mA,
148 WLED_CURR_LIMIT_1260mA,
149 WLED_CURR_LIMIT_1400mA,
150 WLED_CURR_LIMIT_1680mA,
151};
152
153/* over voltage protection threshold */
154enum wled_ovp_threshold {
155 WLED_OVP_35V,
156 WLED_OVP_32V,
157 WLED_OVP_29V,
158 WLED_OVP_37V,
159};
160
161/* switch frquency */
162enum wled_switch_freq {
163 WLED_800kHz = 0,
164 WLED_960kHz,
165 WLED_1600kHz,
166 WLED_3200kHz,
167};
168
Amy Maloche864a6d52012-10-03 15:58:12 -0700169enum flash_headroom {
170 HEADROOM_250mV = 0,
171 HEADROOM_300mV,
172 HEADROOM_400mV,
173 HEADROOM_500mV,
174};
175
176enum flash_startup_dly {
177 DELAY_10us = 0,
178 DELAY_32us,
179 DELAY_64us,
180 DELAY_128us,
181};
182
Amy Malochef3d5a062012-08-16 19:14:11 -0700183static u8 wled_debug_regs[] = {
184 /* common registers */
185 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4d, 0x4e, 0x4f,
186 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
187 /* LED1 */
188 0x60, 0x61, 0x62, 0x63, 0x66,
Amy Malochea5ca5552012-10-23 13:34:46 -0700189 /* LED2 */
Amy Malochef3d5a062012-08-16 19:14:11 -0700190 0x70, 0x71, 0x72, 0x73, 0x76,
Amy Malochea5ca5552012-10-23 13:34:46 -0700191 /* LED3 */
Amy Malochef3d5a062012-08-16 19:14:11 -0700192 0x80, 0x81, 0x82, 0x83, 0x86,
193};
194
Amy Maloche864a6d52012-10-03 15:58:12 -0700195static u8 flash_debug_regs[] = {
196 0x40, 0x41, 0x42, 0x43, 0x44, 0x48, 0x49, 0x4b, 0x4c,
197 0x4f, 0x46, 0x47,
198};
199
200
Amy Malochef3d5a062012-08-16 19:14:11 -0700201/**
202 * wled_config_data - wled configuration data
203 * @num_strings - number of wled strings supported
204 * @ovp_val - over voltage protection threshold
205 * @boost_curr_lim - boot current limit
206 * @cp_select - high pole capacitance
207 * @ctrl_delay_us - delay in activation of led
208 * @dig_mod_gen_en - digital module generator
209 * @cs_out_en - current sink output enable
210 * @op_fdbck - selection of output as feedback for the boost
211 */
212struct wled_config_data {
213 u8 num_strings;
214 u8 ovp_val;
215 u8 boost_curr_lim;
216 u8 cp_select;
217 u8 ctrl_delay_us;
218 u8 switch_freq;
219 bool dig_mod_gen_en;
220 bool cs_out_en;
221 bool op_fdbck;
222};
223
224/**
Amy Maloche864a6d52012-10-03 15:58:12 -0700225 * flash_config_data - flash configuration data
226 * @current_prgm - current to be programmed, scaled by max level
227 * @clamp_curr - clamp current to use
228 * @headroom - headroom value to use
229 * @duration - duration of the flash
230 * @enable_module - enable address for particular flash
231 * @trigger_flash - trigger flash
232 * @startup_dly - startup delay for flash
233 * @current_addr - address to write for current
234 * @second_addr - address of secondary flash to be written
235 * @safety_timer - enable safety timer or watchdog timer
236 */
237struct flash_config_data {
238 u8 current_prgm;
239 u8 clamp_curr;
240 u8 headroom;
241 u8 duration;
242 u8 enable_module;
243 u8 trigger_flash;
244 u8 startup_dly;
245 u16 current_addr;
246 u16 second_addr;
247 bool safety_timer;
248};
249
250/**
Amy Malochef3d5a062012-08-16 19:14:11 -0700251 * struct qpnp_led_data - internal led data structure
252 * @led_classdev - led class device
253 * @id - led index
254 * @base_reg - base register given in device tree
255 * @lock - to protect the transactions
256 * @reg - cached value of led register
Amy Malochea5ca5552012-10-23 13:34:46 -0700257 * @num_leds - number of leds in the module
Amy Malochef3d5a062012-08-16 19:14:11 -0700258 * @max_current - maximum current supported by LED
259 * @default_on - true: default state max, false, default state 0
260 */
261struct qpnp_led_data {
262 struct led_classdev cdev;
263 struct spmi_device *spmi_dev;
264 int id;
265 u16 base;
266 u8 reg;
Amy Malochea5ca5552012-10-23 13:34:46 -0700267 u8 num_leds;
Amy Malochedc3e5572012-09-25 16:39:06 -0700268 spinlock_t lock;
Amy Malochef3d5a062012-08-16 19:14:11 -0700269 struct wled_config_data *wled_cfg;
Amy Maloche864a6d52012-10-03 15:58:12 -0700270 struct flash_config_data *flash_cfg;
Amy Malochef3d5a062012-08-16 19:14:11 -0700271 int max_current;
272 bool default_on;
273};
274
275static int
276qpnp_led_masked_write(struct qpnp_led_data *led, u16 addr, u8 mask, u8 val)
277{
278 int rc;
279 u8 reg;
280
281 rc = spmi_ext_register_readl(led->spmi_dev->ctrl, led->spmi_dev->sid,
282 addr, &reg, 1);
283 if (rc) {
284 dev_err(&led->spmi_dev->dev,
285 "Unable to read from addr=%x, rc(%d)\n", addr, rc);
286 }
287
288 reg &= ~mask;
289 reg |= val;
290
291 rc = spmi_ext_register_writel(led->spmi_dev->ctrl, led->spmi_dev->sid,
292 addr, &reg, 1);
293 if (rc)
294 dev_err(&led->spmi_dev->dev,
295 "Unable to write to addr=%x, rc(%d)\n", addr, rc);
296 return rc;
297}
298
Amy Malochea5ca5552012-10-23 13:34:46 -0700299static void qpnp_dump_regs(struct qpnp_led_data *led, u8 regs[], u8 array_size)
300{
301 int i;
302 u8 val;
303
304 pr_debug("===== %s LED register dump start =====\n", led->cdev.name);
305 for (i = 0; i < array_size; i++) {
306 spmi_ext_register_readl(led->spmi_dev->ctrl,
307 led->spmi_dev->sid,
308 led->base + regs[i],
309 &val, sizeof(val));
310 pr_debug("0x%x = 0x%x\n", led->base + regs[i], val);
311 }
312 pr_debug("===== %s LED register dump end =====\n", led->cdev.name);
313}
314
Amy Malochef3d5a062012-08-16 19:14:11 -0700315static int qpnp_wled_set(struct qpnp_led_data *led)
316{
317 int rc, duty;
318 u8 level, val, i, num_wled_strings;
319
320 level = led->cdev.brightness;
321
322 if (level > WLED_MAX_LEVEL)
323 level = WLED_MAX_LEVEL;
324 if (level == 0) {
325 val = WLED_BOOST_OFF;
326 rc = spmi_ext_register_writel(led->spmi_dev->ctrl,
327 led->spmi_dev->sid, WLED_MOD_CTRL_REG(led->base),
328 &val, 1);
329 if (rc) {
330 dev_err(&led->spmi_dev->dev,
331 "WLED write ctrl reg failed(%d)\n", rc);
332 return rc;
333 }
334 } else {
335 val = WLED_BOOST_ON;
336 rc = spmi_ext_register_writel(led->spmi_dev->ctrl,
337 led->spmi_dev->sid, WLED_MOD_CTRL_REG(led->base),
338 &val, 1);
339 if (rc) {
340 dev_err(&led->spmi_dev->dev,
341 "WLED write ctrl reg failed(%d)\n", rc);
342 return rc;
343 }
344 }
345
346 duty = (WLED_MAX_DUTY_CYCLE * level) / WLED_MAX_LEVEL;
347
348 num_wled_strings = led->wled_cfg->num_strings;
349
350 /* program brightness control registers */
351 for (i = 0; i < num_wled_strings; i++) {
352 rc = qpnp_led_masked_write(led,
353 WLED_BRIGHTNESS_CNTL_MSB(led->base, i), WLED_MSB_MASK,
354 (duty >> WLED_8_BIT_SHFT) & WLED_4_BIT_MASK);
355 if (rc) {
356 dev_err(&led->spmi_dev->dev,
357 "WLED set brightness MSB failed(%d)\n", rc);
358 return rc;
359 }
360 val = duty & WLED_8_BIT_MASK;
361 rc = spmi_ext_register_writel(led->spmi_dev->ctrl,
362 led->spmi_dev->sid,
363 WLED_BRIGHTNESS_CNTL_LSB(led->base, i), &val, 1);
364 if (rc) {
365 dev_err(&led->spmi_dev->dev,
366 "WLED set brightness LSB failed(%d)\n", rc);
367 return rc;
368 }
369 }
370
371 /* sync */
372 val = WLED_SYNC_VAL;
373 rc = spmi_ext_register_writel(led->spmi_dev->ctrl, led->spmi_dev->sid,
374 WLED_SYNC_REG(led->base), &val, 1);
375 if (rc) {
376 dev_err(&led->spmi_dev->dev,
377 "WLED set sync reg failed(%d)\n", rc);
378 return rc;
379 }
380
381 val = WLED_SYNC_RESET_VAL;
382 rc = spmi_ext_register_writel(led->spmi_dev->ctrl, led->spmi_dev->sid,
383 WLED_SYNC_REG(led->base), &val, 1);
384 if (rc) {
385 dev_err(&led->spmi_dev->dev,
386 "WLED reset sync reg failed(%d)\n", rc);
387 return rc;
388 }
389 return 0;
390}
391
Amy Maloche864a6d52012-10-03 15:58:12 -0700392static int qpnp_flash_set(struct qpnp_led_data *led)
393{
394 int rc;
395 int val = led->cdev.brightness;
396
397 led->flash_cfg->current_prgm = (val * FLASH_MAX_LEVEL /
398 led->max_current);
399
400 led->flash_cfg->current_prgm =
401 led->flash_cfg->current_prgm >> FLASH_CURRENT_PRGM_SHIFT;
402 if (!led->flash_cfg->current_prgm)
403 led->flash_cfg->current_prgm = FLASH_CURRENT_PRGM_MIN;
404
405 /* Set led current */
406 if (val > 0) {
407 rc = qpnp_led_masked_write(led, led->flash_cfg->current_addr,
408 FLASH_CURRENT_MASK, led->flash_cfg->current_prgm);
409 if (rc) {
410 dev_err(&led->spmi_dev->dev,
411 "Current reg write failed(%d)\n", rc);
412 return rc;
413 }
414
415 rc = qpnp_led_masked_write(led, led->flash_cfg->second_addr,
416 FLASH_CURRENT_MASK, led->flash_cfg->current_prgm);
417 if (rc) {
418 dev_err(&led->spmi_dev->dev,
419 "Current reg write failed(%d)\n", rc);
420 return rc;
421 }
422
423 rc = qpnp_led_masked_write(led, FLASH_ENABLE_CONTROL(led->base),
424 FLASH_ENABLE_MASK,
425 FLASH_ENABLE_ALL);
426 if (rc) {
427 dev_err(&led->spmi_dev->dev,
428 "Enable reg write failed(%d)\n", rc);
429 return rc;
430 }
431 rc = qpnp_led_masked_write(led,
432 FLASH_LED_STROBE_CTRL(led->base),
433 FLASH_STROBE_MASK, FLASH_STROBE_ALL);
434
435 if (rc) {
436 dev_err(&led->spmi_dev->dev,
437 "LED %d flash write failed(%d)\n", led->id, rc);
438 return rc;
439 }
440 } else {
441 rc = qpnp_led_masked_write(led, FLASH_ENABLE_CONTROL(led->base),
442 FLASH_ENABLE_MASK,
443 FLASH_DISABLE_ALL);
444 if (rc) {
445 dev_err(&led->spmi_dev->dev,
446 "Enable reg write failed(%d)\n", rc);
447 return rc;
448 }
449
450 rc = qpnp_led_masked_write(led,
451 FLASH_LED_STROBE_CTRL(led->base),
452 FLASH_STROBE_MASK,
453 FLASH_DISABLE_ALL);
454 if (rc) {
455 dev_err(&led->spmi_dev->dev,
456 "LED %d flash write failed(%d)\n", led->id, rc);
457 return rc;
458 }
459 }
460
461 return 0;
462}
463
Amy Malochef3d5a062012-08-16 19:14:11 -0700464static void qpnp_led_set(struct led_classdev *led_cdev,
465 enum led_brightness value)
466{
467 int rc;
468 struct qpnp_led_data *led;
469
470 led = container_of(led_cdev, struct qpnp_led_data, cdev);
Amy Malochef3d5a062012-08-16 19:14:11 -0700471 if (value < LED_OFF || value > led->cdev.max_brightness) {
Amy Malochea5ca5552012-10-23 13:34:46 -0700472 dev_err(&led->spmi_dev->dev, "Invalid brightness value\n");
Amy Malochef3d5a062012-08-16 19:14:11 -0700473 return;
474 }
475
Amy Malochedc3e5572012-09-25 16:39:06 -0700476 spin_lock(&led->lock);
Amy Malochef3d5a062012-08-16 19:14:11 -0700477 led->cdev.brightness = value;
478
479 switch (led->id) {
480 case QPNP_ID_WLED:
481 rc = qpnp_wled_set(led);
482 if (rc < 0)
Amy Malochea5ca5552012-10-23 13:34:46 -0700483 dev_err(&led->spmi_dev->dev,
Amy Malochef3d5a062012-08-16 19:14:11 -0700484 "WLED set brightness failed (%d)\n", rc);
485 break;
Amy Maloche864a6d52012-10-03 15:58:12 -0700486 case QPNP_ID_FLASH1_LED0:
487 case QPNP_ID_FLASH1_LED1:
488 rc = qpnp_flash_set(led);
489 if (rc < 0)
490 dev_err(&led->spmi_dev->dev,
491 "FLASH set brightness failed (%d)\n", rc);
492 break;
Amy Malochef3d5a062012-08-16 19:14:11 -0700493 default:
Amy Malochea5ca5552012-10-23 13:34:46 -0700494 dev_err(&led->spmi_dev->dev, "Invalid LED(%d)\n", led->id);
Amy Malochef3d5a062012-08-16 19:14:11 -0700495 break;
496 }
Amy Malochedc3e5572012-09-25 16:39:06 -0700497 spin_unlock(&led->lock);
Amy Malochef3d5a062012-08-16 19:14:11 -0700498}
499
500static int __devinit qpnp_led_set_max_brightness(struct qpnp_led_data *led)
501{
502 switch (led->id) {
503 case QPNP_ID_WLED:
504 led->cdev.max_brightness = WLED_MAX_LEVEL;
505 break;
Amy Maloche864a6d52012-10-03 15:58:12 -0700506 case QPNP_ID_FLASH1_LED0:
507 case QPNP_ID_FLASH1_LED1:
508 led->cdev.max_brightness = led->max_current;
509 break;
Amy Malochef3d5a062012-08-16 19:14:11 -0700510 default:
Amy Malochea5ca5552012-10-23 13:34:46 -0700511 dev_err(&led->spmi_dev->dev, "Invalid LED(%d)\n", led->id);
Amy Malochef3d5a062012-08-16 19:14:11 -0700512 return -EINVAL;
513 }
514
515 return 0;
516}
517
518static enum led_brightness qpnp_led_get(struct led_classdev *led_cdev)
519{
520 struct qpnp_led_data *led;
521
522 led = container_of(led_cdev, struct qpnp_led_data, cdev);
523
524 return led->cdev.brightness;
525}
526
527static int __devinit qpnp_wled_init(struct qpnp_led_data *led)
528{
529 int rc, i;
530 u8 num_wled_strings;
531
532 num_wled_strings = led->wled_cfg->num_strings;
533
534 /* verify ranges */
535 if (led->wled_cfg->ovp_val > WLED_OVP_37V) {
536 dev_err(&led->spmi_dev->dev, "Invalid ovp value\n");
537 return -EINVAL;
538 }
539
540 if (led->wled_cfg->boost_curr_lim > WLED_CURR_LIMIT_1680mA) {
541 dev_err(&led->spmi_dev->dev, "Invalid boost current limit\n");
542 return -EINVAL;
543 }
544
545 if (led->wled_cfg->cp_select > WLED_CP_SELECT_MAX) {
546 dev_err(&led->spmi_dev->dev, "Invalid pole capacitance\n");
547 return -EINVAL;
548 }
549
550 if ((led->max_current > WLED_MAX_CURR)) {
551 dev_err(&led->spmi_dev->dev, "Invalid max current\n");
552 return -EINVAL;
553 }
554
555 if ((led->wled_cfg->ctrl_delay_us % WLED_CTL_DLY_STEP) ||
556 (led->wled_cfg->ctrl_delay_us > WLED_CTL_DLY_MAX)) {
557 dev_err(&led->spmi_dev->dev, "Invalid control delay\n");
558 return -EINVAL;
559 }
560
561 /* program over voltage protection threshold */
562 rc = qpnp_led_masked_write(led, WLED_OVP_CFG_REG(led->base),
563 WLED_OVP_VAL_MASK,
564 (led->wled_cfg->ovp_val << WLED_OVP_VAL_BIT_SHFT));
565 if (rc) {
566 dev_err(&led->spmi_dev->dev,
567 "WLED OVP reg write failed(%d)\n", rc);
568 return rc;
569 }
570
571 /* program current boost limit */
572 rc = qpnp_led_masked_write(led, WLED_BOOST_LIMIT_REG(led->base),
573 WLED_BOOST_LIMIT_MASK, led->wled_cfg->boost_curr_lim);
574 if (rc) {
575 dev_err(&led->spmi_dev->dev,
576 "WLED boost limit reg write failed(%d)\n", rc);
577 return rc;
578 }
579
580 /* program output feedback */
581 rc = qpnp_led_masked_write(led, WLED_FDBCK_CTRL_REG(led->base),
582 WLED_OP_FDBCK_MASK,
583 (led->wled_cfg->op_fdbck << WLED_OP_FDBCK_BIT_SHFT));
584 if (rc) {
585 dev_err(&led->spmi_dev->dev,
586 "WLED fdbck ctrl reg write failed(%d)\n", rc);
587 return rc;
588 }
589
590 /* program switch frequency */
591 rc = qpnp_led_masked_write(led, WLED_SWITCHING_FREQ_REG(led->base),
592 WLED_SWITCH_FREQ_MASK, led->wled_cfg->switch_freq);
593 if (rc) {
594 dev_err(&led->spmi_dev->dev,
595 "WLED switch freq reg write failed(%d)\n", rc);
596 return rc;
597 }
598
599 /* program current sink */
600 if (led->wled_cfg->cs_out_en) {
601 rc = qpnp_led_masked_write(led, WLED_CURR_SINK_REG(led->base),
602 WLED_CURR_SINK_MASK,
603 (led->wled_cfg->num_strings << WLED_CURR_SINK_SHFT));
604 if (rc) {
605 dev_err(&led->spmi_dev->dev,
606 "WLED curr sink reg write failed(%d)\n", rc);
607 return rc;
608 }
609 }
610
611 /* program high pole capacitance */
612 rc = qpnp_led_masked_write(led, WLED_HIGH_POLE_CAP_REG(led->base),
613 WLED_CP_SELECT_MASK, led->wled_cfg->cp_select);
614 if (rc) {
615 dev_err(&led->spmi_dev->dev,
616 "WLED pole cap reg write failed(%d)\n", rc);
617 return rc;
618 }
619
620 /* program modulator, current mod src and cabc */
621 for (i = 0; i < num_wled_strings; i++) {
622 rc = qpnp_led_masked_write(led, WLED_MOD_EN_REG(led->base, i),
623 WLED_NO_MASK, WLED_EN_MASK);
624 if (rc) {
625 dev_err(&led->spmi_dev->dev,
626 "WLED mod enable reg write failed(%d)\n", rc);
627 return rc;
628 }
629
630 if (led->wled_cfg->dig_mod_gen_en) {
631 rc = qpnp_led_masked_write(led,
632 WLED_MOD_EN_REG(led->base, i),
633 WLED_NO_MASK, WLED_USE_EXT_GEN_MOD_SRC);
634 if (rc) {
635 dev_err(&led->spmi_dev->dev,
636 "WLED dig mod en reg write failed(%d)\n", rc);
637 }
638 }
639
640 rc = qpnp_led_masked_write(led,
641 WLED_FULL_SCALE_REG(led->base, i), WLED_MAX_CURR_MASK,
642 led->max_current);
643 if (rc) {
644 dev_err(&led->spmi_dev->dev,
645 "WLED max current reg write failed(%d)\n", rc);
646 return rc;
647 }
648
649 }
650
651 /* dump wled registers */
Amy Malochea5ca5552012-10-23 13:34:46 -0700652 qpnp_dump_regs(led, wled_debug_regs, ARRAY_SIZE(wled_debug_regs));
Amy Malochef3d5a062012-08-16 19:14:11 -0700653
654 return 0;
655}
656
Amy Maloche864a6d52012-10-03 15:58:12 -0700657static int __devinit qpnp_flash_init(struct qpnp_led_data *led)
658{
659 int rc;
660
661 rc = qpnp_led_masked_write(led,
662 FLASH_LED_STROBE_CTRL(led->base),
663 FLASH_STROBE_MASK, FLASH_DISABLE_ALL);
664 if (rc) {
665 dev_err(&led->spmi_dev->dev,
666 "LED %d flash write failed(%d)\n", led->id, rc);
667 return rc;
668 }
669 rc = qpnp_led_masked_write(led, FLASH_ENABLE_CONTROL(led->base),
670 FLASH_INIT_MASK, FLASH_ENABLE_MODULE);
671 if (rc) {
672 dev_err(&led->spmi_dev->dev,
673 "Enable reg write failed(%d)\n", rc);
674 return rc;
675 }
676
677 /* Set flash safety timer */
678 rc = qpnp_led_masked_write(led, FLASH_SAFETY_TIMER(led->base),
679 FLASH_SAFETY_TIMER_MASK, led->flash_cfg->duration);
680 if (rc) {
681 dev_err(&led->spmi_dev->dev,
682 "Safety timer reg write failed(%d)\n", rc);
683 return rc;
684 }
685
686 /* Set max current */
687 rc = qpnp_led_masked_write(led, FLASH_MAX_CURR(led->base),
688 FLASH_CURRENT_MASK, FLASH_MAX_LEVEL);
689 if (rc) {
690 dev_err(&led->spmi_dev->dev,
691 "Max current reg write failed(%d)\n", rc);
692 return rc;
693 }
694 /* Set clamp current */
695 rc = qpnp_led_masked_write(led, FLASH_CLAMP_CURR(led->base),
696 FLASH_CURRENT_MASK, led->flash_cfg->clamp_curr);
697 if (rc) {
698 dev_err(&led->spmi_dev->dev,
699 "Clamp current reg write failed(%d)\n", rc);
700 return rc;
701 }
702
703 /* Set timer control - safety or watchdog */
704 if (led->flash_cfg->safety_timer)
705 rc = qpnp_led_masked_write(led, FLASH_LED_TMR_CTRL(led->base),
706 FLASH_TMR_MASK, FLASH_TMR_SAFETY);
707 else
708 rc = qpnp_led_masked_write(led, FLASH_LED_TMR_CTRL(led->base),
709 FLASH_TMR_MASK, FLASH_TMR_WATCHDOG);
710 if (rc) {
711 dev_err(&led->spmi_dev->dev,
712 "LED timer ctrl reg write failed(%d)\n", rc);
713 return rc;
714 }
715 /* Set headroom */
716 rc = qpnp_led_masked_write(led, FLASH_HEADROOM(led->base),
717 FLASH_HEADROOM_MASK, led->flash_cfg->headroom);
718 if (rc) {
719 dev_err(&led->spmi_dev->dev,
720 "Headroom reg write failed(%d)\n", rc);
721 return rc;
722 }
723
724 /* Set mask enable */
725 rc = qpnp_led_masked_write(led, FLASH_MASK_ENABLE(led->base),
726 FLASH_MASK_REG_MASK, FLASH_MASK_1);
727 if (rc) {
728 dev_err(&led->spmi_dev->dev,
729 "Mask enable reg write failed(%d)\n", rc);
730 return rc;
731 }
732
733 /* Set startup delay */
734 rc = qpnp_led_masked_write(led, FLASH_STARTUP_DELAY(led->base),
735 FLASH_STARTUP_DLY_MASK, led->flash_cfg->startup_dly);
736 if (rc) {
737 dev_err(&led->spmi_dev->dev,
738 "Startup delay reg write failed(%d)\n", rc);
739 return rc;
740 }
741
742 rc = qpnp_led_masked_write(led, FLASH_VREG_OK_FORCE(led->base),
743 FLASH_VREG_MASK, FLASH_HW_VREG_OK);
744 if (rc) {
745 dev_err(&led->spmi_dev->dev,
746 "Vreg OK reg write failed(%d)\n", rc);
747 return rc;
748 }
749
750 /* Set led current and enable module */
751 rc = qpnp_led_masked_write(led, led->flash_cfg->current_addr,
752 FLASH_CURRENT_MASK, led->flash_cfg->current_prgm);
753 if (rc) {
754 dev_err(&led->spmi_dev->dev,
755 "Current reg write failed(%d)\n", rc);
756 return rc;
757 }
758
759 rc = qpnp_led_masked_write(led, FLASH_ENABLE_CONTROL(led->base),
760 FLASH_ENABLE_MODULE_MASK, FLASH_ENABLE_MODULE);
761 if (rc) {
762 dev_err(&led->spmi_dev->dev,
763 "Enable reg write failed(%d)\n", rc);
764 return rc;
765 }
766 /* dump flash registers */
767 qpnp_dump_regs(led, flash_debug_regs, ARRAY_SIZE(flash_debug_regs));
768
769 return 0;
770}
771
Amy Malochef3d5a062012-08-16 19:14:11 -0700772static int __devinit qpnp_led_initialize(struct qpnp_led_data *led)
773{
774 int rc;
775
776 switch (led->id) {
777 case QPNP_ID_WLED:
778 rc = qpnp_wled_init(led);
779 if (rc)
Amy Malochea5ca5552012-10-23 13:34:46 -0700780 dev_err(&led->spmi_dev->dev,
Amy Malochef3d5a062012-08-16 19:14:11 -0700781 "WLED initialize failed(%d)\n", rc);
782 break;
Amy Maloche864a6d52012-10-03 15:58:12 -0700783 case QPNP_ID_FLASH1_LED0:
784 case QPNP_ID_FLASH1_LED1:
785 rc = qpnp_flash_init(led);
786 if (rc)
787 dev_err(&led->spmi_dev->dev,
788 "FLASH initialize failed(%d)\n", rc);
789 break;
Amy Malochef3d5a062012-08-16 19:14:11 -0700790 default:
Amy Malochea5ca5552012-10-23 13:34:46 -0700791 dev_err(&led->spmi_dev->dev, "Invalid LED(%d)\n", led->id);
Amy Malochef3d5a062012-08-16 19:14:11 -0700792 rc = -EINVAL;
793 }
794
795 return rc;
796}
797
Amy Malochea5ca5552012-10-23 13:34:46 -0700798static int __devinit qpnp_get_common_configs(struct qpnp_led_data *led,
799 struct device_node *node)
800{
801 int rc;
802 const char *temp_string;
803
804 led->cdev.default_trigger = LED_TRIGGER_DEFAULT;
805 rc = of_property_read_string(node, "linux,default-trigger",
806 &temp_string);
807 if (!rc)
808 led->cdev.default_trigger = temp_string;
809 else if (rc != -EINVAL)
810 return rc;
811
812 led->default_on = false;
813 rc = of_property_read_string(node, "qcom,default-state",
814 &temp_string);
815 if (!rc) {
816 if (strncmp(temp_string, "on", sizeof("on")) == 0)
817 led->default_on = true;
818 } else if (rc != -EINVAL)
819 return rc;
820
821 return 0;
822}
823
Amy Malochef3d5a062012-08-16 19:14:11 -0700824/*
825 * Handlers for alternative sources of platform_data
826 */
827static int __devinit qpnp_get_config_wled(struct qpnp_led_data *led,
828 struct device_node *node)
829{
830 u32 val;
831 int rc;
Amy Malochef3d5a062012-08-16 19:14:11 -0700832
833 led->wled_cfg = devm_kzalloc(&led->spmi_dev->dev,
834 sizeof(struct wled_config_data), GFP_KERNEL);
835 if (!led->wled_cfg) {
836 dev_err(&led->spmi_dev->dev, "Unable to allocate memory\n");
837 return -ENOMEM;
838 }
839
Amy Malochef3d5a062012-08-16 19:14:11 -0700840 led->wled_cfg->num_strings = WLED_DEFAULT_STRINGS;
841 rc = of_property_read_u32(node, "qcom,num-strings", &val);
842 if (!rc)
843 led->wled_cfg->num_strings = (u8) val;
844 else if (rc != -EINVAL)
845 return rc;
846
847 led->wled_cfg->ovp_val = WLED_DEFAULT_OVP_VAL;
848 rc = of_property_read_u32(node, "qcom,ovp-val", &val);
849 if (!rc)
850 led->wled_cfg->ovp_val = (u8) val;
851 else if (rc != -EINVAL)
852 return rc;
853
854 led->wled_cfg->boost_curr_lim = WLED_BOOST_LIM_DEFAULT;
855 rc = of_property_read_u32(node, "qcom,boost-curr-lim", &val);
856 if (!rc)
857 led->wled_cfg->boost_curr_lim = (u8) val;
858 else if (rc != -EINVAL)
859 return rc;
860
861 led->wled_cfg->cp_select = WLED_CP_SEL_DEFAULT;
862 rc = of_property_read_u32(node, "qcom,cp-sel", &val);
863 if (!rc)
864 led->wled_cfg->cp_select = (u8) val;
865 else if (rc != -EINVAL)
866 return rc;
867
868 led->wled_cfg->ctrl_delay_us = WLED_CTRL_DLY_DEFAULT;
869 rc = of_property_read_u32(node, "qcom,ctrl-delay-us", &val);
870 if (!rc)
871 led->wled_cfg->ctrl_delay_us = (u8) val;
872 else if (rc != -EINVAL)
873 return rc;
874
875 led->wled_cfg->switch_freq = WLED_SWITCH_FREQ_DEFAULT;
876 rc = of_property_read_u32(node, "qcom,switch-freq", &val);
877 if (!rc)
878 led->wled_cfg->switch_freq = (u8) val;
879 else if (rc != -EINVAL)
880 return rc;
881
882 led->wled_cfg->dig_mod_gen_en =
883 of_property_read_bool(node, "qcom,dig-mod-gen-en");
884
885 led->wled_cfg->cs_out_en =
886 of_property_read_bool(node, "qcom,cs-out-en");
887
888 led->wled_cfg->op_fdbck =
889 of_property_read_bool(node, "qcom,op-fdbck");
890
891 return 0;
892}
893
Amy Maloche864a6d52012-10-03 15:58:12 -0700894static int __devinit qpnp_get_config_flash(struct qpnp_led_data *led,
895 struct device_node *node)
896{
897 int rc;
898 u32 val;
899
900 led->flash_cfg = devm_kzalloc(&led->spmi_dev->dev,
901 sizeof(struct flash_config_data), GFP_KERNEL);
902 if (!led->flash_cfg) {
903 dev_err(&led->spmi_dev->dev, "Unable to allocate memory\n");
904 return -ENOMEM;
905 }
906
907 if (led->id == QPNP_ID_FLASH1_LED0) {
908 led->flash_cfg->enable_module = FLASH_ENABLE_ALL;
909 led->flash_cfg->current_addr = FLASH_LED_0_CURR(led->base);
910 led->flash_cfg->second_addr = FLASH_LED_1_CURR(led->base);
911 led->flash_cfg->trigger_flash = FLASH_LED_0_OUTPUT;
912 } else if (led->id == QPNP_ID_FLASH1_LED1) {
913 led->flash_cfg->enable_module = FLASH_ENABLE_ALL;
914 led->flash_cfg->current_addr = FLASH_LED_1_CURR(led->base);
915 led->flash_cfg->second_addr = FLASH_LED_0_CURR(led->base);
916 led->flash_cfg->trigger_flash = FLASH_LED_1_OUTPUT;
917 } else {
918 dev_err(&led->spmi_dev->dev, "Unknown flash LED name given\n");
919 return -EINVAL;
920 }
921
922 rc = of_property_read_u32(node, "qcom,current", &val);
923 if (!rc)
924 led->flash_cfg->current_prgm = (val *
925 FLASH_MAX_LEVEL / led->max_current);
926 else
927 return -EINVAL;
928
929 rc = of_property_read_u32(node, "qcom,headroom", &val);
930 if (!rc)
931 led->flash_cfg->headroom = (u8) val;
932 else if (rc == -EINVAL)
933 led->flash_cfg->headroom = HEADROOM_300mV;
934 else
935 return rc;
936
937 rc = of_property_read_u32(node, "qcom,duration", &val);
938 if (!rc)
939 led->flash_cfg->duration = (((u8) val) - 10) / 10;
940 else if (rc == -EINVAL)
941 led->flash_cfg->duration = FLASH_DURATION_200ms;
942 else
943 return rc;
944
945 rc = of_property_read_u32(node, "qcom,clamp-curr", &val);
946 if (!rc)
947 led->flash_cfg->clamp_curr = (val *
948 FLASH_MAX_LEVEL / led->max_current);
949 else if (rc == -EINVAL)
950 led->flash_cfg->clamp_curr = FLASH_CLAMP_200mA;
951 else
952 return rc;
953
954 rc = of_property_read_u32(node, "qcom,startup-dly", &val);
955 if (!rc)
956 led->flash_cfg->startup_dly = (u8) val;
957 else if (rc == -EINVAL)
958 led->flash_cfg->startup_dly = DELAY_32us;
959 else
960 return rc;
961
962 led->flash_cfg->safety_timer =
963 of_property_read_bool(node, "qcom,safety-timer");
964
965 return 0;
966}
967
Amy Malochef3d5a062012-08-16 19:14:11 -0700968static int __devinit qpnp_leds_probe(struct spmi_device *spmi)
969{
970 struct qpnp_led_data *led;
971 struct resource *led_resource;
Amy Malochea5ca5552012-10-23 13:34:46 -0700972 struct device_node *node, *temp;
973 int rc, i, num_leds = 0;
Amy Malochef3d5a062012-08-16 19:14:11 -0700974 const char *led_label;
975
Amy Malochea5ca5552012-10-23 13:34:46 -0700976 node = spmi->dev.of_node;
977 if (node == NULL)
978 return -ENODEV;
979
980 temp = NULL;
981 while ((temp = of_get_next_child(node, temp)))
982 num_leds++;
983
984 led = devm_kzalloc(&spmi->dev,
985 (sizeof(struct qpnp_led_data) * num_leds), GFP_KERNEL);
Amy Malochef3d5a062012-08-16 19:14:11 -0700986 if (!led) {
987 dev_err(&spmi->dev, "Unable to allocate memory\n");
988 return -ENOMEM;
989 }
990
Amy Malochea5ca5552012-10-23 13:34:46 -0700991 led->num_leds = num_leds;
992 num_leds = 0;
Amy Malochef3d5a062012-08-16 19:14:11 -0700993
Amy Malochea5ca5552012-10-23 13:34:46 -0700994 for_each_child_of_node(node, temp) {
995 led->spmi_dev = spmi;
Amy Malochef3d5a062012-08-16 19:14:11 -0700996
Amy Malochea5ca5552012-10-23 13:34:46 -0700997 led_resource = spmi_get_resource(spmi, NULL, IORESOURCE_MEM, 0);
998 if (!led_resource) {
999 dev_err(&spmi->dev, "Unable to get LED base address\n");
1000 return -ENXIO;
1001 }
1002 led->base = led_resource->start;
Amy Malochef3d5a062012-08-16 19:14:11 -07001003
Amy Malochea5ca5552012-10-23 13:34:46 -07001004 dev_set_drvdata(&spmi->dev, led);
Amy Malochef3d5a062012-08-16 19:14:11 -07001005
Amy Malochef3d5a062012-08-16 19:14:11 -07001006
Amy Malochea5ca5552012-10-23 13:34:46 -07001007 rc = of_property_read_string(temp, "label", &led_label);
Amy Malochef3d5a062012-08-16 19:14:11 -07001008 if (rc < 0) {
1009 dev_err(&led->spmi_dev->dev,
Amy Malochea5ca5552012-10-23 13:34:46 -07001010 "Failure reading label, rc = %d\n", rc);
Amy Malochef3d5a062012-08-16 19:14:11 -07001011 return rc;
1012 }
Amy Malochea5ca5552012-10-23 13:34:46 -07001013
1014 rc = of_property_read_string(temp, "linux,name",
1015 &led->cdev.name);
1016 if (rc < 0) {
1017 dev_err(&led->spmi_dev->dev,
1018 "Failure reading led name, rc = %d\n", rc);
1019 return rc;
1020 }
1021
1022 rc = of_property_read_u32(temp, "qcom,max-current",
1023 &led->max_current);
1024 if (rc < 0) {
1025 dev_err(&led->spmi_dev->dev,
1026 "Failure reading max_current, rc = %d\n", rc);
1027 return rc;
1028 }
1029
1030 rc = of_property_read_u32(temp, "qcom,id", &led->id);
1031 if (rc < 0) {
1032 dev_err(&led->spmi_dev->dev,
1033 "Failure reading led id, rc = %d\n", rc);
1034 return rc;
1035 }
1036
1037 rc = qpnp_get_common_configs(led, temp);
1038 if (rc) {
1039 dev_err(&led->spmi_dev->dev,
1040 "Failure reading common led configuration," \
1041 " rc = %d\n", rc);
1042 return rc;
1043 }
1044
1045 led->cdev.brightness_set = qpnp_led_set;
1046 led->cdev.brightness_get = qpnp_led_get;
1047
1048 if (strncmp(led_label, "wled", sizeof("wled")) == 0) {
1049 rc = qpnp_get_config_wled(led, temp);
1050 if (rc < 0) {
1051 dev_err(&led->spmi_dev->dev,
1052 "Unable to read wled config data\n");
1053 return rc;
1054 }
Amy Maloche864a6d52012-10-03 15:58:12 -07001055 } else if (strncmp(led_label, "flash", sizeof("flash"))
1056 == 0) {
1057 rc = qpnp_get_config_flash(led, temp);
1058 if (rc < 0) {
1059 dev_err(&led->spmi_dev->dev,
1060 "Unable to read flash config data\n");
1061 return rc;
1062 }
Amy Malochea5ca5552012-10-23 13:34:46 -07001063 } else {
1064 dev_err(&led->spmi_dev->dev, "No LED matching label\n");
1065 return -EINVAL;
1066 }
1067
1068 spin_lock_init(&led->lock);
1069
1070 rc = qpnp_led_initialize(led);
1071 if (rc < 0)
1072 goto fail_id_check;
1073
1074 rc = qpnp_led_set_max_brightness(led);
1075 if (rc < 0)
1076 goto fail_id_check;
1077
1078 rc = led_classdev_register(&spmi->dev, &led->cdev);
1079 if (rc) {
1080 dev_err(&spmi->dev, "unable to register led %d,rc=%d\n",
1081 led->id, rc);
1082 goto fail_id_check;
1083 }
1084 /* configure default state */
1085 if (led->default_on)
1086 led->cdev.brightness = led->cdev.max_brightness;
1087 else
1088 led->cdev.brightness = LED_OFF;
1089
1090 qpnp_led_set(&led->cdev, led->cdev.brightness);
1091 led++;
1092 num_leds++;
Amy Malochef3d5a062012-08-16 19:14:11 -07001093 }
Amy Malochef3d5a062012-08-16 19:14:11 -07001094 return 0;
1095
1096fail_id_check:
Amy Malochea5ca5552012-10-23 13:34:46 -07001097 for (i = 0; i < num_leds; i++)
1098 led_classdev_unregister(&led[i].cdev);
Amy Malochef3d5a062012-08-16 19:14:11 -07001099 return rc;
1100}
1101
1102static int __devexit qpnp_leds_remove(struct spmi_device *spmi)
1103{
1104 struct qpnp_led_data *led = dev_get_drvdata(&spmi->dev);
Amy Malochea5ca5552012-10-23 13:34:46 -07001105 int i;
Amy Malochef3d5a062012-08-16 19:14:11 -07001106
Amy Malochea5ca5552012-10-23 13:34:46 -07001107 for (i = 0; i < led->num_leds; i++)
1108 led_classdev_unregister(&led[i].cdev);
Amy Malochef3d5a062012-08-16 19:14:11 -07001109
1110 return 0;
1111}
1112static struct of_device_id spmi_match_table[] = {
1113 { .compatible = "qcom,leds-qpnp",
1114 }
1115};
1116
1117static struct spmi_driver qpnp_leds_driver = {
1118 .driver = {
1119 .name = "qcom,leds-qpnp",
1120 .of_match_table = spmi_match_table,
1121 },
1122 .probe = qpnp_leds_probe,
1123 .remove = __devexit_p(qpnp_leds_remove),
1124};
1125
1126static int __init qpnp_led_init(void)
1127{
1128 return spmi_driver_register(&qpnp_leds_driver);
1129}
1130module_init(qpnp_led_init);
1131
1132static void __exit qpnp_led_exit(void)
1133{
1134 spmi_driver_unregister(&qpnp_leds_driver);
1135}
1136module_exit(qpnp_led_exit);
1137
1138MODULE_DESCRIPTION("QPNP LEDs driver");
1139MODULE_LICENSE("GPL v2");
1140MODULE_ALIAS("leds:leds-qpnp");
Amy Maloche864a6d52012-10-03 15:58:12 -07001141