Blame view

drivers/leds/leds-lp55xx-common.c 13.6 KB
c93d08fa7   Milo(Woogyom) Kim   leds-lp55xx: add ...
1
  /*
ff45262a8   Kim, Milo   leds: add new LP5...
2
   * LP5521/LP5523/LP55231/LP5562 Common Driver
c93d08fa7   Milo(Woogyom) Kim   leds-lp55xx: add ...
3
4
5
6
7
8
9
10
11
12
13
   *
   * Copyright 2012 Texas Instruments
   *
   * Author: Milo(Woogyom) Kim <milo.kim@ti.com>
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License version 2 as
   * published by the Free Software Foundation.
   *
   * Derived from leds-lp5521.c, leds-lp5523.c
   */
53b419226   Kim, Milo   leds: lp55xx: use...
14
  #include <linux/clk.h>
a85908dd7   Milo(Woogyom) Kim   leds-lp55xx: use ...
15
  #include <linux/delay.h>
10c06d178   Milo(Woogyom) Kim   leds-lp55xx: supp...
16
  #include <linux/firmware.h>
c93d08fa7   Milo(Woogyom) Kim   leds-lp55xx: add ...
17
18
19
20
  #include <linux/i2c.h>
  #include <linux/leds.h>
  #include <linux/module.h>
  #include <linux/platform_data/leds-lp55xx.h>
7542a04b1   Linus Walleij   leds: lp55xx: add...
21
  #include <linux/slab.h>
30dae2f98   Sebastian Reichel   leds: lp55xx: han...
22
23
  #include <linux/gpio.h>
  #include <linux/of_gpio.h>
c93d08fa7   Milo(Woogyom) Kim   leds-lp55xx: add ...
24
25
  
  #include "leds-lp55xx-common.h"
53b419226   Kim, Milo   leds: lp55xx: use...
26
27
  /* External clock rate */
  #define LP55XX_CLK_32K			32768
a6e4679a0   Milo(Woogyom) Kim   leds-lp55xx: use ...
28
29
30
31
  static struct lp55xx_led *cdev_to_lp55xx_led(struct led_classdev *cdev)
  {
  	return container_of(cdev, struct lp55xx_led, cdev);
  }
a96bfa135   Milo(Woogyom) Kim   leds-lp55xx: prov...
32
33
34
35
  static struct lp55xx_led *dev_to_lp55xx_led(struct device *dev)
  {
  	return cdev_to_lp55xx_led(dev_get_drvdata(dev));
  }
48068d5de   Milo(Woogyom) Kim   leds-lp55xx: use ...
36
37
38
39
40
41
42
43
44
  static void lp55xx_reset_device(struct lp55xx_chip *chip)
  {
  	struct lp55xx_device_config *cfg = chip->cfg;
  	u8 addr = cfg->reset.addr;
  	u8 val  = cfg->reset.val;
  
  	/* no error checking here because no ACK from the device after reset */
  	lp55xx_write(chip, addr, val);
  }
e3a700d8a   Milo(Woogyom) Kim   leds-lp55xx: use ...
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
  static int lp55xx_detect_device(struct lp55xx_chip *chip)
  {
  	struct lp55xx_device_config *cfg = chip->cfg;
  	u8 addr = cfg->enable.addr;
  	u8 val  = cfg->enable.val;
  	int ret;
  
  	ret = lp55xx_write(chip, addr, val);
  	if (ret)
  		return ret;
  
  	usleep_range(1000, 2000);
  
  	ret = lp55xx_read(chip, addr, &val);
  	if (ret)
  		return ret;
  
  	if (val != cfg->enable.val)
  		return -ENODEV;
  
  	return 0;
  }
ffbdccdbb   Milo(Woogyom) Kim   leds-lp55xx: use ...
67
68
69
70
71
72
73
74
75
  static int lp55xx_post_init_device(struct lp55xx_chip *chip)
  {
  	struct lp55xx_device_config *cfg = chip->cfg;
  
  	if (!cfg->post_init_device)
  		return 0;
  
  	return cfg->post_init_device(chip);
  }
a96bfa135   Milo(Woogyom) Kim   leds-lp55xx: prov...
76
77
78
79
80
  static ssize_t lp55xx_show_current(struct device *dev,
  			    struct device_attribute *attr,
  			    char *buf)
  {
  	struct lp55xx_led *led = dev_to_lp55xx_led(dev);
24d321284   Kim, Milo   leds: lp55xx: fix...
81
82
  	return scnprintf(buf, PAGE_SIZE, "%d
  ", led->led_current);
a96bfa135   Milo(Woogyom) Kim   leds-lp55xx: prov...
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
  }
  
  static ssize_t lp55xx_store_current(struct device *dev,
  			     struct device_attribute *attr,
  			     const char *buf, size_t len)
  {
  	struct lp55xx_led *led = dev_to_lp55xx_led(dev);
  	struct lp55xx_chip *chip = led->chip;
  	unsigned long curr;
  
  	if (kstrtoul(buf, 0, &curr))
  		return -EINVAL;
  
  	if (curr > led->max_current)
  		return -EINVAL;
  
  	if (!chip->cfg->set_led_current)
  		return len;
  
  	mutex_lock(&chip->lock);
  	chip->cfg->set_led_current(led, (u8)curr);
  	mutex_unlock(&chip->lock);
  
  	return len;
  }
  
  static ssize_t lp55xx_show_max_current(struct device *dev,
  			    struct device_attribute *attr,
  			    char *buf)
  {
  	struct lp55xx_led *led = dev_to_lp55xx_led(dev);
24d321284   Kim, Milo   leds: lp55xx: fix...
114
115
  	return scnprintf(buf, PAGE_SIZE, "%d
  ", led->max_current);
a96bfa135   Milo(Woogyom) Kim   leds-lp55xx: prov...
116
117
118
119
120
  }
  
  static DEVICE_ATTR(led_current, S_IRUGO | S_IWUSR, lp55xx_show_current,
  		lp55xx_store_current);
  static DEVICE_ATTR(max_current, S_IRUGO , lp55xx_show_max_current, NULL);
44a1255b0   Johan Hovold   leds: lp55xx-comm...
121
  static struct attribute *lp55xx_led_attrs[] = {
a96bfa135   Milo(Woogyom) Kim   leds-lp55xx: prov...
122
123
  	&dev_attr_led_current.attr,
  	&dev_attr_max_current.attr,
0e2023463   Milo(Woogyom) Kim   leds-lp55xx: use ...
124
125
  	NULL,
  };
44a1255b0   Johan Hovold   leds: lp55xx-comm...
126
  ATTRIBUTE_GROUPS(lp55xx_led);
0e2023463   Milo(Woogyom) Kim   leds-lp55xx: use ...
127
128
129
130
  
  static void lp55xx_set_brightness(struct led_classdev *cdev,
  			     enum led_brightness brightness)
  {
a6e4679a0   Milo(Woogyom) Kim   leds-lp55xx: use ...
131
132
133
134
  	struct lp55xx_led *led = cdev_to_lp55xx_led(cdev);
  
  	led->brightness = (u8)brightness;
  	schedule_work(&led->brightness_work);
0e2023463   Milo(Woogyom) Kim   leds-lp55xx: use ...
135
  }
9e9b3db1b   Milo(Woogyom) Kim   leds-lp55xx: use ...
136
137
138
  static int lp55xx_init_led(struct lp55xx_led *led,
  			struct lp55xx_chip *chip, int chan)
  {
0e2023463   Milo(Woogyom) Kim   leds-lp55xx: use ...
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
  	struct lp55xx_platform_data *pdata = chip->pdata;
  	struct lp55xx_device_config *cfg = chip->cfg;
  	struct device *dev = &chip->cl->dev;
  	char name[32];
  	int ret;
  	int max_channel = cfg->max_channel;
  
  	if (chan >= max_channel) {
  		dev_err(dev, "invalid channel: %d / %d
  ", chan, max_channel);
  		return -EINVAL;
  	}
  
  	if (pdata->led_config[chan].led_current == 0)
  		return 0;
  
  	led->led_current = pdata->led_config[chan].led_current;
  	led->max_current = pdata->led_config[chan].max_current;
  	led->chan_nr = pdata->led_config[chan].chan_nr;
f65f0a1a9   Linus Walleij   leds: lp55xx: ena...
158
  	led->cdev.default_trigger = pdata->led_config[chan].default_trigger;
0e2023463   Milo(Woogyom) Kim   leds-lp55xx: use ...
159
160
161
162
163
164
165
166
167
  
  	if (led->chan_nr >= max_channel) {
  		dev_err(dev, "Use channel numbers between 0 and %d
  ",
  			max_channel - 1);
  		return -EINVAL;
  	}
  
  	led->cdev.brightness_set = lp55xx_set_brightness;
44a1255b0   Johan Hovold   leds: lp55xx-comm...
168
  	led->cdev.groups = lp55xx_led_groups;
0e2023463   Milo(Woogyom) Kim   leds-lp55xx: use ...
169
170
171
172
173
174
175
176
  
  	if (pdata->led_config[chan].name) {
  		led->cdev.name = pdata->led_config[chan].name;
  	} else {
  		snprintf(name, sizeof(name), "%s:channel%d",
  			pdata->label ? : chip->cl->name, chan);
  		led->cdev.name = name;
  	}
0e2023463   Milo(Woogyom) Kim   leds-lp55xx: use ...
177
178
179
180
181
182
  	ret = led_classdev_register(dev, &led->cdev);
  	if (ret) {
  		dev_err(dev, "led register err: %d
  ", ret);
  		return ret;
  	}
9e9b3db1b   Milo(Woogyom) Kim   leds-lp55xx: use ...
183
184
  	return 0;
  }
10c06d178   Milo(Woogyom) Kim   leds-lp55xx: supp...
185
186
187
188
  static void lp55xx_firmware_loaded(const struct firmware *fw, void *context)
  {
  	struct lp55xx_chip *chip = context;
  	struct device *dev = &chip->cl->dev;
93ad8a1d5   Milo Kim   leds: lp5523: Sup...
189
  	enum lp55xx_engine_index idx = chip->engine_idx;
10c06d178   Milo(Woogyom) Kim   leds-lp55xx: supp...
190
191
192
193
194
195
196
197
198
  
  	if (!fw) {
  		dev_err(dev, "firmware request failed
  ");
  		goto out;
  	}
  
  	/* handling firmware data is chip dependent */
  	mutex_lock(&chip->lock);
93ad8a1d5   Milo Kim   leds: lp5523: Sup...
199
  	chip->engines[idx - 1].mode = LP55XX_ENGINE_LOAD;
10c06d178   Milo(Woogyom) Kim   leds-lp55xx: supp...
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
  	chip->fw = fw;
  	if (chip->cfg->firmware_cb)
  		chip->cfg->firmware_cb(chip);
  
  	mutex_unlock(&chip->lock);
  
  out:
  	/* firmware should be released for other channel use */
  	release_firmware(chip->fw);
  }
  
  static int lp55xx_request_firmware(struct lp55xx_chip *chip)
  {
  	const char *name = chip->cl->name;
  	struct device *dev = &chip->cl->dev;
  
  	return request_firmware_nowait(THIS_MODULE, true, name, dev,
  				GFP_KERNEL, chip, lp55xx_firmware_loaded);
  }
  
  static ssize_t lp55xx_show_engine_select(struct device *dev,
  			    struct device_attribute *attr,
  			    char *buf)
  {
  	struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
  	struct lp55xx_chip *chip = led->chip;
  
  	return sprintf(buf, "%d
  ", chip->engine_idx);
  }
  
  static ssize_t lp55xx_store_engine_select(struct device *dev,
  			     struct device_attribute *attr,
  			     const char *buf, size_t len)
  {
  	struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
  	struct lp55xx_chip *chip = led->chip;
  	unsigned long val;
  	int ret;
  
  	if (kstrtoul(buf, 0, &val))
  		return -EINVAL;
  
  	/* select the engine to be run */
  
  	switch (val) {
  	case LP55XX_ENGINE_1:
  	case LP55XX_ENGINE_2:
  	case LP55XX_ENGINE_3:
  		mutex_lock(&chip->lock);
  		chip->engine_idx = val;
  		ret = lp55xx_request_firmware(chip);
  		mutex_unlock(&chip->lock);
  		break;
  	default:
  		dev_err(dev, "%lu: invalid engine index. (1, 2, 3)
  ", val);
  		return -EINVAL;
  	}
  
  	if (ret) {
  		dev_err(dev, "request firmware err: %d
  ", ret);
  		return ret;
  	}
  
  	return len;
  }
  
  static inline void lp55xx_run_engine(struct lp55xx_chip *chip, bool start)
  {
  	if (chip->cfg->run_engine)
  		chip->cfg->run_engine(chip, start);
  }
  
  static ssize_t lp55xx_store_engine_run(struct device *dev,
  			     struct device_attribute *attr,
  			     const char *buf, size_t len)
  {
  	struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
  	struct lp55xx_chip *chip = led->chip;
  	unsigned long val;
  
  	if (kstrtoul(buf, 0, &val))
  		return -EINVAL;
  
  	/* run or stop the selected engine */
  
  	if (val <= 0) {
  		lp55xx_run_engine(chip, false);
  		return len;
  	}
  
  	mutex_lock(&chip->lock);
  	lp55xx_run_engine(chip, true);
  	mutex_unlock(&chip->lock);
  
  	return len;
  }
  
  static DEVICE_ATTR(select_engine, S_IRUGO | S_IWUSR,
  		   lp55xx_show_engine_select, lp55xx_store_engine_select);
  static DEVICE_ATTR(run_engine, S_IWUSR, NULL, lp55xx_store_engine_run);
b3b6f8119   Milo(Woogyom) Kim   leds-lp55xx: add ...
303
  static struct attribute *lp55xx_engine_attributes[] = {
10c06d178   Milo(Woogyom) Kim   leds-lp55xx: supp...
304
305
  	&dev_attr_select_engine.attr,
  	&dev_attr_run_engine.attr,
b3b6f8119   Milo(Woogyom) Kim   leds-lp55xx: add ...
306
307
308
309
310
311
  	NULL,
  };
  
  static const struct attribute_group lp55xx_engine_attr_group = {
  	.attrs = lp55xx_engine_attributes,
  };
c93d08fa7   Milo(Woogyom) Kim   leds-lp55xx: add ...
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
  int lp55xx_write(struct lp55xx_chip *chip, u8 reg, u8 val)
  {
  	return i2c_smbus_write_byte_data(chip->cl, reg, val);
  }
  EXPORT_SYMBOL_GPL(lp55xx_write);
  
  int lp55xx_read(struct lp55xx_chip *chip, u8 reg, u8 *val)
  {
  	s32 ret;
  
  	ret = i2c_smbus_read_byte_data(chip->cl, reg);
  	if (ret < 0)
  		return ret;
  
  	*val = ret;
  	return 0;
  }
  EXPORT_SYMBOL_GPL(lp55xx_read);
  
  int lp55xx_update_bits(struct lp55xx_chip *chip, u8 reg, u8 mask, u8 val)
  {
  	int ret;
  	u8 tmp;
  
  	ret = lp55xx_read(chip, reg, &tmp);
  	if (ret)
  		return ret;
  
  	tmp &= ~mask;
  	tmp |= val & mask;
  
  	return lp55xx_write(chip, reg, tmp);
  }
  EXPORT_SYMBOL_GPL(lp55xx_update_bits);
53b419226   Kim, Milo   leds: lp55xx: use...
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
  bool lp55xx_is_extclk_used(struct lp55xx_chip *chip)
  {
  	struct clk *clk;
  	int err;
  
  	clk = devm_clk_get(&chip->cl->dev, "32k_clk");
  	if (IS_ERR(clk))
  		goto use_internal_clk;
  
  	err = clk_prepare_enable(clk);
  	if (err)
  		goto use_internal_clk;
  
  	if (clk_get_rate(clk) != LP55XX_CLK_32K) {
  		clk_disable_unprepare(clk);
  		goto use_internal_clk;
  	}
  
  	dev_info(&chip->cl->dev, "%dHz external clock used
  ",	LP55XX_CLK_32K);
  
  	chip->clk = clk;
  	return true;
  
  use_internal_clk:
  	dev_info(&chip->cl->dev, "internal clock used
  ");
  	return false;
  }
  EXPORT_SYMBOL_GPL(lp55xx_is_extclk_used);
a85908dd7   Milo(Woogyom) Kim   leds-lp55xx: use ...
376
377
378
  int lp55xx_init_device(struct lp55xx_chip *chip)
  {
  	struct lp55xx_platform_data *pdata;
48068d5de   Milo(Woogyom) Kim   leds-lp55xx: use ...
379
  	struct lp55xx_device_config *cfg;
a85908dd7   Milo(Woogyom) Kim   leds-lp55xx: use ...
380
381
382
383
384
385
  	struct device *dev = &chip->cl->dev;
  	int ret = 0;
  
  	WARN_ON(!chip);
  
  	pdata = chip->pdata;
48068d5de   Milo(Woogyom) Kim   leds-lp55xx: use ...
386
  	cfg = chip->cfg;
a85908dd7   Milo(Woogyom) Kim   leds-lp55xx: use ...
387

48068d5de   Milo(Woogyom) Kim   leds-lp55xx: use ...
388
  	if (!pdata || !cfg)
a85908dd7   Milo(Woogyom) Kim   leds-lp55xx: use ...
389
  		return -EINVAL;
30dae2f98   Sebastian Reichel   leds: lp55xx: han...
390
391
392
  	if (gpio_is_valid(pdata->enable_gpio)) {
  		ret = devm_gpio_request_one(dev, pdata->enable_gpio,
  					    GPIOF_DIR_OUT, "lp5523_enable");
a85908dd7   Milo(Woogyom) Kim   leds-lp55xx: use ...
393
  		if (ret < 0) {
30dae2f98   Sebastian Reichel   leds: lp55xx: han...
394
395
396
  			dev_err(dev, "could not acquire enable gpio (err=%d)
  ",
  				ret);
a85908dd7   Milo(Woogyom) Kim   leds-lp55xx: use ...
397
398
  			goto err;
  		}
a85908dd7   Milo(Woogyom) Kim   leds-lp55xx: use ...
399

30dae2f98   Sebastian Reichel   leds: lp55xx: han...
400
  		gpio_set_value(pdata->enable_gpio, 0);
a85908dd7   Milo(Woogyom) Kim   leds-lp55xx: use ...
401
  		usleep_range(1000, 2000); /* Keep enable down at least 1ms */
30dae2f98   Sebastian Reichel   leds: lp55xx: han...
402
  		gpio_set_value(pdata->enable_gpio, 1);
a85908dd7   Milo(Woogyom) Kim   leds-lp55xx: use ...
403
404
  		usleep_range(1000, 2000); /* 500us abs min. */
  	}
48068d5de   Milo(Woogyom) Kim   leds-lp55xx: use ...
405
406
407
408
409
410
411
  	lp55xx_reset_device(chip);
  
  	/*
  	 * Exact value is not available. 10 - 20ms
  	 * appears to be enough for reset.
  	 */
  	usleep_range(10000, 20000);
e3a700d8a   Milo(Woogyom) Kim   leds-lp55xx: use ...
412
413
414
415
416
417
  	ret = lp55xx_detect_device(chip);
  	if (ret) {
  		dev_err(dev, "device detection err: %d
  ", ret);
  		goto err;
  	}
ffbdccdbb   Milo(Woogyom) Kim   leds-lp55xx: use ...
418
419
  	/* chip specific initialization */
  	ret = lp55xx_post_init_device(chip);
22ebeb488   Milo(Woogyom) Kim   leds-lp55xx: clea...
420
421
422
423
424
  	if (ret) {
  		dev_err(dev, "post init device err: %d
  ", ret);
  		goto err_post_init;
  	}
ffbdccdbb   Milo(Woogyom) Kim   leds-lp55xx: use ...
425
426
  
  	return 0;
22ebeb488   Milo(Woogyom) Kim   leds-lp55xx: clea...
427
  err_post_init:
6ce617626   Milo(Woogyom) Kim   leds-lp55xx: use ...
428
429
430
431
432
433
434
435
436
  	lp55xx_deinit_device(chip);
  err:
  	return ret;
  }
  EXPORT_SYMBOL_GPL(lp55xx_init_device);
  
  void lp55xx_deinit_device(struct lp55xx_chip *chip)
  {
  	struct lp55xx_platform_data *pdata = chip->pdata;
53b419226   Kim, Milo   leds: lp55xx: use...
437
438
  	if (chip->clk)
  		clk_disable_unprepare(chip->clk);
30dae2f98   Sebastian Reichel   leds: lp55xx: han...
439
440
  	if (gpio_is_valid(pdata->enable_gpio))
  		gpio_set_value(pdata->enable_gpio, 0);
a85908dd7   Milo(Woogyom) Kim   leds-lp55xx: use ...
441
  }
6ce617626   Milo(Woogyom) Kim   leds-lp55xx: use ...
442
  EXPORT_SYMBOL_GPL(lp55xx_deinit_device);
a85908dd7   Milo(Woogyom) Kim   leds-lp55xx: use ...
443

9e9b3db1b   Milo(Woogyom) Kim   leds-lp55xx: use ...
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
  int lp55xx_register_leds(struct lp55xx_led *led, struct lp55xx_chip *chip)
  {
  	struct lp55xx_platform_data *pdata = chip->pdata;
  	struct lp55xx_device_config *cfg = chip->cfg;
  	int num_channels = pdata->num_channels;
  	struct lp55xx_led *each;
  	u8 led_current;
  	int ret;
  	int i;
  
  	if (!cfg->brightness_work_fn) {
  		dev_err(&chip->cl->dev, "empty brightness configuration
  ");
  		return -EINVAL;
  	}
  
  	for (i = 0; i < num_channels; i++) {
  
  		/* do not initialize channels that are not connected */
  		if (pdata->led_config[i].led_current == 0)
  			continue;
  
  		led_current = pdata->led_config[i].led_current;
  		each = led + i;
  		ret = lp55xx_init_led(each, chip, i);
  		if (ret)
  			goto err_init_led;
  
  		INIT_WORK(&each->brightness_work, cfg->brightness_work_fn);
  
  		chip->num_leds++;
  		each->chip = chip;
  
  		/* setting led current at each channel */
  		if (cfg->set_led_current)
  			cfg->set_led_current(each, led_current);
  	}
  
  	return 0;
  
  err_init_led:
d9067d284   Milo(Woogyom) Kim   leds-lp55xx: fix ...
485
  	lp55xx_unregister_leds(led, chip);
9e9b3db1b   Milo(Woogyom) Kim   leds-lp55xx: use ...
486
487
488
  	return ret;
  }
  EXPORT_SYMBOL_GPL(lp55xx_register_leds);
c3a68ebfc   Milo(Woogyom) Kim   leds-lp55xx: use ...
489
490
491
492
  void lp55xx_unregister_leds(struct lp55xx_led *led, struct lp55xx_chip *chip)
  {
  	int i;
  	struct lp55xx_led *each;
c3a68ebfc   Milo(Woogyom) Kim   leds-lp55xx: use ...
493
494
495
  
  	for (i = 0; i < chip->num_leds; i++) {
  		each = led + i;
c3a68ebfc   Milo(Woogyom) Kim   leds-lp55xx: use ...
496
497
498
499
500
  		led_classdev_unregister(&each->cdev);
  		flush_work(&each->brightness_work);
  	}
  }
  EXPORT_SYMBOL_GPL(lp55xx_unregister_leds);
b3b6f8119   Milo(Woogyom) Kim   leds-lp55xx: add ...
501
502
503
  int lp55xx_register_sysfs(struct lp55xx_chip *chip)
  {
  	struct device *dev = &chip->cl->dev;
240085e25   Milo(Woogyom) Kim   leds-lp55xx: supp...
504
505
506
507
508
509
510
511
512
  	struct lp55xx_device_config *cfg = chip->cfg;
  	int ret;
  
  	if (!cfg->run_engine || !cfg->firmware_cb)
  		goto dev_specific_attrs;
  
  	ret = sysfs_create_group(&dev->kobj, &lp55xx_engine_attr_group);
  	if (ret)
  		return ret;
b3b6f8119   Milo(Woogyom) Kim   leds-lp55xx: add ...
513

240085e25   Milo(Woogyom) Kim   leds-lp55xx: supp...
514
515
516
  dev_specific_attrs:
  	return cfg->dev_attr_group ?
  		sysfs_create_group(&dev->kobj, cfg->dev_attr_group) : 0;
b3b6f8119   Milo(Woogyom) Kim   leds-lp55xx: add ...
517
518
  }
  EXPORT_SYMBOL_GPL(lp55xx_register_sysfs);
ba6fa8465   Milo(Woogyom) Kim   leds-lp55xx: add ...
519
520
521
522
523
524
525
526
527
528
529
  void lp55xx_unregister_sysfs(struct lp55xx_chip *chip)
  {
  	struct device *dev = &chip->cl->dev;
  	struct lp55xx_device_config *cfg = chip->cfg;
  
  	if (cfg->dev_attr_group)
  		sysfs_remove_group(&dev->kobj, cfg->dev_attr_group);
  
  	sysfs_remove_group(&dev->kobj, &lp55xx_engine_attr_group);
  }
  EXPORT_SYMBOL_GPL(lp55xx_unregister_sysfs);
7542a04b1   Linus Walleij   leds: lp55xx: add...
530
531
  int lp55xx_of_populate_pdata(struct device *dev, struct device_node *np)
  {
2dac91280   Kim, Milo   leds: lp55xx: sup...
532
  	struct device_node *child;
7542a04b1   Linus Walleij   leds: lp55xx: add...
533
  	struct lp55xx_platform_data *pdata;
2dac91280   Kim, Milo   leds: lp55xx: sup...
534
535
536
  	struct lp55xx_led_config *cfg;
  	int num_channels;
  	int i = 0;
7542a04b1   Linus Walleij   leds: lp55xx: add...
537
538
539
540
  
  	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
  	if (!pdata)
  		return -ENOMEM;
2dac91280   Kim, Milo   leds: lp55xx: sup...
541
542
543
544
545
546
  	num_channels = of_get_child_count(np);
  	if (num_channels == 0) {
  		dev_err(dev, "no LED channels
  ");
  		return -EINVAL;
  	}
7542a04b1   Linus Walleij   leds: lp55xx: add...
547

2dac91280   Kim, Milo   leds: lp55xx: sup...
548
549
  	cfg = devm_kzalloc(dev, sizeof(*cfg) * num_channels, GFP_KERNEL);
  	if (!cfg)
7542a04b1   Linus Walleij   leds: lp55xx: add...
550
  		return -ENOMEM;
2dac91280   Kim, Milo   leds: lp55xx: sup...
551
552
553
554
555
556
557
558
559
  	pdata->led_config = &cfg[0];
  	pdata->num_channels = num_channels;
  
  	for_each_child_of_node(np, child) {
  		cfg[i].chan_nr = i;
  
  		of_property_read_string(child, "chan-name", &cfg[i].name);
  		of_property_read_u8(child, "led-cur", &cfg[i].led_current);
  		of_property_read_u8(child, "max-cur", &cfg[i].max_current);
f65f0a1a9   Linus Walleij   leds: lp55xx: ena...
560
561
  		cfg[i].default_trigger =
  			of_get_property(child, "linux,default-trigger", NULL);
2dac91280   Kim, Milo   leds: lp55xx: sup...
562
563
  
  		i++;
7542a04b1   Linus Walleij   leds: lp55xx: add...
564
  	}
2dac91280   Kim, Milo   leds: lp55xx: sup...
565
566
567
  
  	of_property_read_string(np, "label", &pdata->label);
  	of_property_read_u8(np, "clock-mode", &pdata->clock_mode);
30dae2f98   Sebastian Reichel   leds: lp55xx: han...
568
  	pdata->enable_gpio = of_get_named_gpio(np, "enable-gpio", 0);
33b3a561f   Kim, Milo   leds: support new...
569
570
  	/* LP8501 specific */
  	of_property_read_u8(np, "pwr-sel", (u8 *)&pdata->pwr_sel);
7542a04b1   Linus Walleij   leds: lp55xx: add...
571
572
573
574
575
  	dev->platform_data = pdata;
  
  	return 0;
  }
  EXPORT_SYMBOL_GPL(lp55xx_of_populate_pdata);
c93d08fa7   Milo(Woogyom) Kim   leds-lp55xx: add ...
576
577
578
  MODULE_AUTHOR("Milo Kim <milo.kim@ti.com>");
  MODULE_DESCRIPTION("LP55xx Common Driver");
  MODULE_LICENSE("GPL");