Blame view
drivers/leds/leds-pca9532.c
12.2 KB
e14fa8243 leds: Add pca9532... |
1 2 3 |
/* * pca9532.c - 16-bit Led dimmer * |
3dbf622c1 drivers/leds/leds... |
4 |
* Copyright (C) 2011 Jan Weitzel |
b26e0ed49 trivial: Update m... |
5 |
* Copyright (C) 2008 Riku Voipio |
e14fa8243 leds: Add pca9532... |
6 7 8 9 10 |
* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * |
3dbf622c1 drivers/leds/leds... |
11 |
* Datasheet: http://www.nxp.com/documents/data_sheet/PCA9532.pdf |
e14fa8243 leds: Add pca9532... |
12 13 14 15 16 |
* */ #include <linux/module.h> #include <linux/i2c.h> |
5a0e3ad6a include cleanup: ... |
17 |
#include <linux/slab.h> |
e14fa8243 leds: Add pca9532... |
18 19 20 |
#include <linux/leds.h> #include <linux/input.h> #include <linux/mutex.h> |
934cd3f97 leds: leds-pcs953... |
21 |
#include <linux/workqueue.h> |
e14fa8243 leds: Add pca9532... |
22 |
#include <linux/leds-pca9532.h> |
3c1ab50d0 drivers/leds/leds... |
23 |
#include <linux/gpio.h> |
e14fa8243 leds: Add pca9532... |
24 |
|
3dbf622c1 drivers/leds/leds... |
25 26 27 28 29 30 31 |
/* m = num_leds*/ #define PCA9532_REG_INPUT(i) ((i) >> 3) #define PCA9532_REG_OFFSET(m) ((m) >> 4) #define PCA9532_REG_PSC(m, i) (PCA9532_REG_OFFSET(m) + 0x1 + (i) * 2) #define PCA9532_REG_PWM(m, i) (PCA9532_REG_OFFSET(m) + 0x2 + (i) * 2) #define LED_REG(m, led) (PCA9532_REG_OFFSET(m) + 0x5 + (led >> 2)) #define LED_NUM(led) (led & 0x3) |
e14fa8243 leds: Add pca9532... |
32 33 |
#define ldev_to_led(c) container_of(c, struct pca9532_led, ldev) |
3dbf622c1 drivers/leds/leds... |
34 35 36 |
struct pca9532_chip_info { u8 num_leds; }; |
e14fa8243 leds: Add pca9532... |
37 38 39 40 |
struct pca9532_data { struct i2c_client *client; struct pca9532_led leds[16]; struct mutex update_lock; |
85c5204a6 leds: Fix leds-pc... |
41 |
struct input_dev *idev; |
07172d2bf leds: pca9532 - I... |
42 |
struct work_struct work; |
3c1ab50d0 drivers/leds/leds... |
43 44 45 |
#ifdef CONFIG_LEDS_PCA9532_GPIO struct gpio_chip gpio; #endif |
3dbf622c1 drivers/leds/leds... |
46 |
const struct pca9532_chip_info *chip_info; |
e14fa8243 leds: Add pca9532... |
47 48 49 50 51 52 53 |
u8 pwm[2]; u8 psc[2]; }; static int pca9532_probe(struct i2c_client *client, const struct i2c_device_id *id); static int pca9532_remove(struct i2c_client *client); |
3dbf622c1 drivers/leds/leds... |
54 55 56 57 58 59 |
enum { pca9530, pca9531, pca9532, pca9533, }; |
e14fa8243 leds: Add pca9532... |
60 |
static const struct i2c_device_id pca9532_id[] = { |
3dbf622c1 drivers/leds/leds... |
61 62 63 64 |
{ "pca9530", pca9530 }, { "pca9531", pca9531 }, { "pca9532", pca9532 }, { "pca9533", pca9533 }, |
e14fa8243 leds: Add pca9532... |
65 66 67 68 |
{ } }; MODULE_DEVICE_TABLE(i2c, pca9532_id); |
3dbf622c1 drivers/leds/leds... |
69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
static const struct pca9532_chip_info pca9532_chip_info_tbl[] = { [pca9530] = { .num_leds = 2, }, [pca9531] = { .num_leds = 8, }, [pca9532] = { .num_leds = 16, }, [pca9533] = { .num_leds = 4, }, }; |
e14fa8243 leds: Add pca9532... |
83 84 |
static struct i2c_driver pca9532_driver = { .driver = { |
30cb35be6 drivers/leds/leds... |
85 |
.name = "leds-pca953x", |
e14fa8243 leds: Add pca9532... |
86 |
}, |
85c5204a6 leds: Fix leds-pc... |
87 |
.probe = pca9532_probe, |
e14fa8243 leds: Add pca9532... |
88 89 90 |
.remove = pca9532_remove, .id_table = pca9532_id, }; |
25985edce Fix common misspe... |
91 |
/* We have two pwm/blinkers, but 16 possible leds to drive. Additionally, |
e14fa8243 leds: Add pca9532... |
92 93 94 95 |
* the clever Thecus people are using one pwm to drive the beeper. So, * as a compromise we average one pwm to the values requested by all * leds that are not ON/OFF. * */ |
934cd3f97 leds: leds-pcs953... |
96 |
static int pca9532_calcpwm(struct i2c_client *client, int pwm, int blink, |
e14fa8243 leds: Add pca9532... |
97 98 99 100 |
enum led_brightness value) { int a = 0, b = 0, i = 0; struct pca9532_data *data = i2c_get_clientdata(client); |
3dbf622c1 drivers/leds/leds... |
101 |
for (i = 0; i < data->chip_info->num_leds; i++) { |
e14fa8243 leds: Add pca9532... |
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
if (data->leds[i].type == PCA9532_TYPE_LED && data->leds[i].state == PCA9532_PWM0+pwm) { a++; b += data->leds[i].ldev.brightness; } } if (a == 0) { dev_err(&client->dev, "fear of division by zero %d/%d, wanted %d ", b, a, value); return -EINVAL; } b = b/a; if (b > 0xFF) return -EINVAL; |
e14fa8243 leds: Add pca9532... |
118 |
data->pwm[pwm] = b; |
07172d2bf leds: pca9532 - I... |
119 120 |
data->psc[pwm] = blink; return 0; |
934cd3f97 leds: leds-pcs953... |
121 122 123 124 |
} static int pca9532_setpwm(struct i2c_client *client, int pwm) { |
07172d2bf leds: pca9532 - I... |
125 |
struct pca9532_data *data = i2c_get_clientdata(client); |
3dbf622c1 drivers/leds/leds... |
126 |
u8 maxleds = data->chip_info->num_leds; |
07172d2bf leds: pca9532 - I... |
127 |
mutex_lock(&data->update_lock); |
3dbf622c1 drivers/leds/leds... |
128 |
i2c_smbus_write_byte_data(client, PCA9532_REG_PWM(maxleds, pwm), |
e14fa8243 leds: Add pca9532... |
129 |
data->pwm[pwm]); |
3dbf622c1 drivers/leds/leds... |
130 |
i2c_smbus_write_byte_data(client, PCA9532_REG_PSC(maxleds, pwm), |
e14fa8243 leds: Add pca9532... |
131 132 133 134 135 136 137 138 139 140 |
data->psc[pwm]); mutex_unlock(&data->update_lock); return 0; } /* Set LED routing */ static void pca9532_setled(struct pca9532_led *led) { struct i2c_client *client = led->client; struct pca9532_data *data = i2c_get_clientdata(client); |
3dbf622c1 drivers/leds/leds... |
141 |
u8 maxleds = data->chip_info->num_leds; |
e14fa8243 leds: Add pca9532... |
142 143 144 |
char reg; mutex_lock(&data->update_lock); |
3dbf622c1 drivers/leds/leds... |
145 |
reg = i2c_smbus_read_byte_data(client, LED_REG(maxleds, led->id)); |
e14fa8243 leds: Add pca9532... |
146 147 148 149 |
/* zero led bits */ reg = reg & ~(0x3<<LED_NUM(led->id)*2); /* set the new value */ reg = reg | (led->state << LED_NUM(led->id)*2); |
3dbf622c1 drivers/leds/leds... |
150 |
i2c_smbus_write_byte_data(client, LED_REG(maxleds, led->id), reg); |
e14fa8243 leds: Add pca9532... |
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 |
mutex_unlock(&data->update_lock); } static void pca9532_set_brightness(struct led_classdev *led_cdev, enum led_brightness value) { int err = 0; struct pca9532_led *led = ldev_to_led(led_cdev); if (value == LED_OFF) led->state = PCA9532_OFF; else if (value == LED_FULL) led->state = PCA9532_ON; else { led->state = PCA9532_PWM0; /* Thecus: hardcode one pwm */ |
07172d2bf leds: pca9532 - I... |
166 |
err = pca9532_calcpwm(led->client, 0, 0, value); |
e14fa8243 leds: Add pca9532... |
167 168 169 |
if (err) return; /* XXX: led api doesn't allow error code? */ } |
07172d2bf leds: pca9532 - I... |
170 |
schedule_work(&led->work); |
e14fa8243 leds: Add pca9532... |
171 172 173 174 175 176 177 178 |
} static int pca9532_set_blink(struct led_classdev *led_cdev, unsigned long *delay_on, unsigned long *delay_off) { struct pca9532_led *led = ldev_to_led(led_cdev); struct i2c_client *client = led->client; int psc; |
07172d2bf leds: pca9532 - I... |
179 |
int err = 0; |
e14fa8243 leds: Add pca9532... |
180 181 182 |
if (*delay_on == 0 && *delay_off == 0) { /* led subsystem ask us for a blink rate */ |
85c5204a6 leds: Fix leds-pc... |
183 |
*delay_on = 1000; |
e14fa8243 leds: Add pca9532... |
184 185 186 187 188 189 190 |
*delay_off = 1000; } if (*delay_on != *delay_off || *delay_on > 1690 || *delay_on < 6) return -EINVAL; /* Thecus specific: only use PSC/PWM 0 */ psc = (*delay_on * 152-1)/1000; |
07172d2bf leds: pca9532 - I... |
191 192 193 194 195 |
err = pca9532_calcpwm(client, 0, psc, led_cdev->brightness); if (err) return err; schedule_work(&led->work); return 0; |
e14fa8243 leds: Add pca9532... |
196 |
} |
0d7335791 leds: eds-pca9532... |
197 198 |
static int pca9532_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) |
e14fa8243 leds: Add pca9532... |
199 200 |
{ struct pca9532_data *data = input_get_drvdata(dev); |
7fbc3a9b1 leds: Fix &&/|| c... |
201 |
if (!(type == EV_SND && (code == SND_BELL || code == SND_TONE))) |
e14fa8243 leds: Add pca9532... |
202 203 204 205 206 207 208 |
return -1; /* XXX: allow different kind of beeps with psc/pwm modifications */ if (value > 1 && value < 32767) data->pwm[1] = 127; else data->pwm[1] = 0; |
07172d2bf leds: pca9532 - I... |
209 |
schedule_work(&data->work); |
934cd3f97 leds: leds-pcs953... |
210 |
|
07172d2bf leds: pca9532 - I... |
211 |
return 0; |
934cd3f97 leds: leds-pcs953... |
212 213 214 215 |
} static void pca9532_input_work(struct work_struct *work) { |
3dbf622c1 drivers/leds/leds... |
216 217 218 |
struct pca9532_data *data = container_of(work, struct pca9532_data, work); u8 maxleds = data->chip_info->num_leds; |
e14fa8243 leds: Add pca9532... |
219 |
mutex_lock(&data->update_lock); |
3dbf622c1 drivers/leds/leds... |
220 |
i2c_smbus_write_byte_data(data->client, PCA9532_REG_PWM(maxleds, 1), |
e14fa8243 leds: Add pca9532... |
221 222 |
data->pwm[1]); mutex_unlock(&data->update_lock); |
934cd3f97 leds: leds-pcs953... |
223 |
} |
e14fa8243 leds: Add pca9532... |
224 |
|
934cd3f97 leds: leds-pcs953... |
225 226 |
static void pca9532_led_work(struct work_struct *work) { |
07172d2bf leds: pca9532 - I... |
227 228 229 230 231 |
struct pca9532_led *led; led = container_of(work, struct pca9532_led, work); if (led->state == PCA9532_PWM0) pca9532_setpwm(led->client, 0); pca9532_setled(led); |
e14fa8243 leds: Add pca9532... |
232 |
} |
3c1ab50d0 drivers/leds/leds... |
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 |
#ifdef CONFIG_LEDS_PCA9532_GPIO static int pca9532_gpio_request_pin(struct gpio_chip *gc, unsigned offset) { struct pca9532_data *data = container_of(gc, struct pca9532_data, gpio); struct pca9532_led *led = &data->leds[offset]; if (led->type == PCA9532_TYPE_GPIO) return 0; return -EBUSY; } static void pca9532_gpio_set_value(struct gpio_chip *gc, unsigned offset, int val) { struct pca9532_data *data = container_of(gc, struct pca9532_data, gpio); struct pca9532_led *led = &data->leds[offset]; if (val) led->state = PCA9532_ON; else led->state = PCA9532_OFF; pca9532_setled(led); } static int pca9532_gpio_get_value(struct gpio_chip *gc, unsigned offset) { struct pca9532_data *data = container_of(gc, struct pca9532_data, gpio); unsigned char reg; reg = i2c_smbus_read_byte_data(data->client, PCA9532_REG_INPUT(offset)); return !!(reg & (1 << (offset % 8))); } static int pca9532_gpio_direction_input(struct gpio_chip *gc, unsigned offset) { /* To use as input ensure pin is not driven */ pca9532_gpio_set_value(gc, offset, 0); return 0; } static int pca9532_gpio_direction_output(struct gpio_chip *gc, unsigned offset, int val) { pca9532_gpio_set_value(gc, offset, val); return 0; } #endif /* CONFIG_LEDS_PCA9532_GPIO */ static int pca9532_destroy_devices(struct pca9532_data *data, int n_devs) |
125c71352 leds: leds-pca953... |
285 286 287 288 |
{ int i = n_devs; if (!data) |
3c1ab50d0 drivers/leds/leds... |
289 |
return -EINVAL; |
125c71352 leds: leds-pca953... |
290 291 292 293 |
while (--i >= 0) { switch (data->leds[i].type) { case PCA9532_TYPE_NONE: |
3c1ab50d0 drivers/leds/leds... |
294 |
case PCA9532_TYPE_GPIO: |
125c71352 leds: leds-pca953... |
295 296 297 298 299 300 301 302 303 304 305 306 307 308 |
break; case PCA9532_TYPE_LED: led_classdev_unregister(&data->leds[i].ldev); cancel_work_sync(&data->leds[i].work); break; case PCA9532_TYPE_N2100_BEEP: if (data->idev != NULL) { input_unregister_device(data->idev); cancel_work_sync(&data->work); data->idev = NULL; } break; } } |
3c1ab50d0 drivers/leds/leds... |
309 310 311 312 313 314 315 316 317 318 319 320 321 322 |
#ifdef CONFIG_LEDS_PCA9532_GPIO if (data->gpio.dev) { int err = gpiochip_remove(&data->gpio); if (err) { dev_err(&data->client->dev, "%s failed, %d ", "gpiochip_remove()", err); return err; } } #endif return 0; |
125c71352 leds: leds-pca953... |
323 |
} |
e14fa8243 leds: Add pca9532... |
324 325 326 327 |
static int pca9532_configure(struct i2c_client *client, struct pca9532_data *data, struct pca9532_platform_data *pdata) { int i, err = 0; |
3c1ab50d0 drivers/leds/leds... |
328 |
int gpios = 0; |
3dbf622c1 drivers/leds/leds... |
329 |
u8 maxleds = data->chip_info->num_leds; |
e14fa8243 leds: Add pca9532... |
330 331 332 333 |
for (i = 0; i < 2; i++) { data->pwm[i] = pdata->pwm[i]; data->psc[i] = pdata->psc[i]; |
3dbf622c1 drivers/leds/leds... |
334 |
i2c_smbus_write_byte_data(client, PCA9532_REG_PWM(maxleds, i), |
e14fa8243 leds: Add pca9532... |
335 |
data->pwm[i]); |
3dbf622c1 drivers/leds/leds... |
336 |
i2c_smbus_write_byte_data(client, PCA9532_REG_PSC(maxleds, i), |
e14fa8243 leds: Add pca9532... |
337 338 |
data->psc[i]); } |
3dbf622c1 drivers/leds/leds... |
339 |
for (i = 0; i < data->chip_info->num_leds; i++) { |
e14fa8243 leds: Add pca9532... |
340 341 342 343 344 345 346 347 |
struct pca9532_led *led = &data->leds[i]; struct pca9532_led *pled = &pdata->leds[i]; led->client = client; led->id = i; led->type = pled->type; switch (led->type) { case PCA9532_TYPE_NONE: break; |
3c1ab50d0 drivers/leds/leds... |
348 349 350 |
case PCA9532_TYPE_GPIO: gpios++; break; |
e14fa8243 leds: Add pca9532... |
351 352 |
case PCA9532_TYPE_LED: led->state = pled->state; |
85c5204a6 leds: Fix leds-pc... |
353 |
led->name = pled->name; |
e14fa8243 leds: Add pca9532... |
354 355 356 357 |
led->ldev.name = led->name; led->ldev.brightness = LED_OFF; led->ldev.brightness_set = pca9532_set_brightness; led->ldev.blink_set = pca9532_set_blink; |
07172d2bf leds: pca9532 - I... |
358 |
INIT_WORK(&led->work, pca9532_led_work); |
f785d022a leds: leds-pca953... |
359 360 |
err = led_classdev_register(&client->dev, &led->ldev); if (err < 0) { |
e14fa8243 leds: Add pca9532... |
361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 |
dev_err(&client->dev, "couldn't register LED %s ", led->name); goto exit; } pca9532_setled(led); break; case PCA9532_TYPE_N2100_BEEP: BUG_ON(data->idev); led->state = PCA9532_PWM1; pca9532_setled(led); data->idev = input_allocate_device(); if (data->idev == NULL) { err = -ENOMEM; goto exit; } data->idev->name = pled->name; data->idev->phys = "i2c/pca9532"; data->idev->id.bustype = BUS_HOST; |
85c5204a6 leds: Fix leds-pc... |
381 |
data->idev->id.vendor = 0x001f; |
e14fa8243 leds: Add pca9532... |
382 383 384 385 386 387 388 |
data->idev->id.product = 0x0001; data->idev->id.version = 0x0100; data->idev->evbit[0] = BIT_MASK(EV_SND); data->idev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE); data->idev->event = pca9532_event; input_set_drvdata(data->idev, data); |
07172d2bf leds: pca9532 - I... |
389 |
INIT_WORK(&data->work, pca9532_input_work); |
e14fa8243 leds: Add pca9532... |
390 391 392 |
err = input_register_device(data->idev); if (err) { input_free_device(data->idev); |
07172d2bf leds: pca9532 - I... |
393 |
cancel_work_sync(&data->work); |
e14fa8243 leds: Add pca9532... |
394 395 396 397 398 399 |
data->idev = NULL; goto exit; } break; } } |
3c1ab50d0 drivers/leds/leds... |
400 401 402 403 404 405 406 407 408 409 410 |
#ifdef CONFIG_LEDS_PCA9532_GPIO if (gpios) { data->gpio.label = "gpio-pca9532"; data->gpio.direction_input = pca9532_gpio_direction_input; data->gpio.direction_output = pca9532_gpio_direction_output; data->gpio.set = pca9532_gpio_set_value; data->gpio.get = pca9532_gpio_get_value; data->gpio.request = pca9532_gpio_request_pin; data->gpio.can_sleep = 1; data->gpio.base = pdata->gpio_base; |
3dbf622c1 drivers/leds/leds... |
411 |
data->gpio.ngpio = data->chip_info->num_leds; |
3c1ab50d0 drivers/leds/leds... |
412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 |
data->gpio.dev = &client->dev; data->gpio.owner = THIS_MODULE; err = gpiochip_add(&data->gpio); if (err) { /* Use data->gpio.dev as a flag for freeing gpiochip */ data->gpio.dev = NULL; dev_warn(&client->dev, "could not add gpiochip "); } else { dev_info(&client->dev, "gpios %i...%i ", data->gpio.base, data->gpio.base + data->gpio.ngpio - 1); } } #endif |
e14fa8243 leds: Add pca9532... |
429 430 431 |
return 0; exit: |
125c71352 leds: leds-pca953... |
432 |
pca9532_destroy_devices(data, i); |
e14fa8243 leds: Add pca9532... |
433 |
return err; |
e14fa8243 leds: Add pca9532... |
434 435 436 437 438 439 440 |
} static int pca9532_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct pca9532_data *data = i2c_get_clientdata(client); struct pca9532_platform_data *pca9532_pdata = client->dev.platform_data; |
f785d022a leds: leds-pca953... |
441 442 443 444 |
int err; if (!pca9532_pdata) return -EIO; |
e14fa8243 leds: Add pca9532... |
445 446 447 448 |
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -EIO; |
f785d022a leds: leds-pca953... |
449 |
data = kzalloc(sizeof(*data), GFP_KERNEL); |
e14fa8243 leds: Add pca9532... |
450 451 |
if (!data) return -ENOMEM; |
3dbf622c1 drivers/leds/leds... |
452 |
data->chip_info = &pca9532_chip_info_tbl[id->driver_data]; |
e14fa8243 leds: Add pca9532... |
453 454 455 456 457 |
dev_info(&client->dev, "setting platform data "); i2c_set_clientdata(client, data); data->client = client; mutex_init(&data->update_lock); |
f785d022a leds: leds-pca953... |
458 |
err = pca9532_configure(client, data, pca9532_pdata); |
fbae3fb15 i2c: Remove all i... |
459 |
if (err) |
f785d022a leds: leds-pca953... |
460 |
kfree(data); |
e14fa8243 leds: Add pca9532... |
461 |
|
f785d022a leds: leds-pca953... |
462 |
return err; |
e14fa8243 leds: Add pca9532... |
463 464 465 466 467 |
} static int pca9532_remove(struct i2c_client *client) { struct pca9532_data *data = i2c_get_clientdata(client); |
3c1ab50d0 drivers/leds/leds... |
468 |
int err; |
3dbf622c1 drivers/leds/leds... |
469 |
err = pca9532_destroy_devices(data, data->chip_info->num_leds); |
3c1ab50d0 drivers/leds/leds... |
470 471 |
if (err) return err; |
e14fa8243 leds: Add pca9532... |
472 |
kfree(data); |
e14fa8243 leds: Add pca9532... |
473 474 475 476 477 478 479 480 481 482 483 484 |
return 0; } static int __init pca9532_init(void) { return i2c_add_driver(&pca9532_driver); } static void __exit pca9532_exit(void) { i2c_del_driver(&pca9532_driver); } |
b26e0ed49 trivial: Update m... |
485 |
MODULE_AUTHOR("Riku Voipio"); |
e14fa8243 leds: Add pca9532... |
486 487 488 489 490 |
MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("PCA 9532 LED dimmer"); module_init(pca9532_init); module_exit(pca9532_exit); |