summaryrefslogtreecommitdiffstats
path: root/drivers/leds
diff options
context:
space:
mode:
authorCédric Le Goater <clg@kaod.org>2017-08-08 09:42:39 -0400
committerJacek Anaszewski <jacek.anaszewski@gmail.com>2017-08-14 16:22:37 -0400
commit561099a1a2e992a482a8318c0c9c5af26222e5cd (patch)
tree1fab407e2140c2efdb5e005a4302d576d6613d33 /drivers/leds
parent91940bb4ca7b7f1b5426cc14bdbd0c7f8347683f (diff)
leds: pca955x: add GPIO support
The PCA955x family of chips are I2C LED blinkers whose pins not used to control LEDs can be used as general purpose I/Os (GPIOs). The following adds such a support by defining different operation modes for the pins (See bindings documentation for more details). The pca955x driver is then extended with a gpio_chip when some of pins are operating as GPIOs. The default operating mode is to behave as a LED. The GPIO support is conditioned by CONFIG_LEDS_PCA955X_GPIO. Signed-off-by: Cédric Le Goater <clg@kaod.org> Acked-by: Pavel Machek <pavel@ucw.cz> Signed-off-by: Jacek Anaszewski <jacek.anaszewski@gmail.com>
Diffstat (limited to 'drivers/leds')
-rw-r--r--drivers/leds/Kconfig11
-rw-r--r--drivers/leds/leds-pca955x.c137
2 files changed, 131 insertions, 17 deletions
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 594b24d410c3..3013cd35c65e 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -377,6 +377,17 @@ config LEDS_PCA955X
377 LED driver chips accessed via the I2C bus. Supported 377 LED driver chips accessed via the I2C bus. Supported
378 devices include PCA9550, PCA9551, PCA9552, and PCA9553. 378 devices include PCA9550, PCA9551, PCA9552, and PCA9553.
379 379
380config LEDS_PCA955X_GPIO
381 bool "Enable GPIO support for PCA955X"
382 depends on LEDS_PCA955X
383 depends on GPIOLIB
384 help
385 Allow unused pins on PCA955X to be used as gpio.
386
387 To use a pin as gpio the pin type should be set to
388 PCA955X_TYPE_GPIO in the device tree.
389
390
380config LEDS_PCA963X 391config LEDS_PCA963X
381 tristate "LED support for PCA963x I2C chip" 392 tristate "LED support for PCA963x I2C chip"
382 depends on LEDS_CLASS 393 depends on LEDS_CLASS
diff --git a/drivers/leds/leds-pca955x.c b/drivers/leds/leds-pca955x.c
index 5a5d3765cfd3..f062d1e7640f 100644
--- a/drivers/leds/leds-pca955x.c
+++ b/drivers/leds/leds-pca955x.c
@@ -53,6 +53,8 @@
53#include <linux/slab.h> 53#include <linux/slab.h>
54#include <linux/string.h> 54#include <linux/string.h>
55 55
56#include <dt-bindings/leds/leds-pca955x.h>
57
56/* LED select registers determine the source that drives LED outputs */ 58/* LED select registers determine the source that drives LED outputs */
57#define PCA955X_LS_LED_ON 0x0 /* Output LOW */ 59#define PCA955X_LS_LED_ON 0x0 /* Output LOW */
58#define PCA955X_LS_LED_OFF 0x1 /* Output HI-Z */ 60#define PCA955X_LS_LED_OFF 0x1 /* Output HI-Z */
@@ -118,6 +120,9 @@ struct pca955x {
118 struct pca955x_led *leds; 120 struct pca955x_led *leds;
119 struct pca955x_chipdef *chipdef; 121 struct pca955x_chipdef *chipdef;
120 struct i2c_client *client; 122 struct i2c_client *client;
123#ifdef CONFIG_LEDS_PCA955X_GPIO
124 struct gpio_chip gpio;
125#endif
121}; 126};
122 127
123struct pca955x_led { 128struct pca955x_led {
@@ -125,6 +130,7 @@ struct pca955x_led {
125 struct led_classdev led_cdev; 130 struct led_classdev led_cdev;
126 int led_num; /* 0 .. 15 potentially */ 131 int led_num; /* 0 .. 15 potentially */
127 char name[32]; 132 char name[32];
133 u32 type;
128 const char *default_trigger; 134 const char *default_trigger;
129}; 135};
130 136
@@ -259,6 +265,65 @@ static int pca955x_led_set(struct led_classdev *led_cdev,
259 return 0; 265 return 0;
260} 266}
261 267
268#ifdef CONFIG_LEDS_PCA955X_GPIO
269/*
270 * Read the INPUT register, which contains the state of LEDs.
271 */
272static u8 pca955x_read_input(struct i2c_client *client, int n)
273{
274 return (u8)i2c_smbus_read_byte_data(client, n);
275}
276
277static int pca955x_gpio_request_pin(struct gpio_chip *gc, unsigned int offset)
278{
279 struct pca955x *pca955x = gpiochip_get_data(gc);
280 struct pca955x_led *led = &pca955x->leds[offset];
281
282 if (led->type == PCA955X_TYPE_GPIO)
283 return 0;
284
285 return -EBUSY;
286}
287
288static void pca955x_gpio_set_value(struct gpio_chip *gc, unsigned int offset,
289 int val)
290{
291 struct pca955x *pca955x = gpiochip_get_data(gc);
292 struct pca955x_led *led = &pca955x->leds[offset];
293
294 if (val)
295 pca955x_led_set(&led->led_cdev, LED_FULL);
296 else
297 pca955x_led_set(&led->led_cdev, LED_OFF);
298}
299
300static int pca955x_gpio_get_value(struct gpio_chip *gc, unsigned int offset)
301{
302 struct pca955x *pca955x = gpiochip_get_data(gc);
303 struct pca955x_led *led = &pca955x->leds[offset];
304 u8 reg = pca955x_read_input(pca955x->client, led->led_num / 8);
305
306 return !!(reg & (1 << (led->led_num % 8)));
307}
308
309static int pca955x_gpio_direction_input(struct gpio_chip *gc,
310 unsigned int offset)
311{
312 /* To use as input ensure pin is not driven */
313 pca955x_gpio_set_value(gc, offset, 0);
314
315 return 0;
316}
317
318static int pca955x_gpio_direction_output(struct gpio_chip *gc,
319 unsigned int offset, int val)
320{
321 pca955x_gpio_set_value(gc, offset, val);
322
323 return 0;
324}
325#endif /* CONFIG_LEDS_PCA955X_GPIO */
326
262#if IS_ENABLED(CONFIG_OF) 327#if IS_ENABLED(CONFIG_OF)
263static struct pca955x_platform_data * 328static struct pca955x_platform_data *
264pca955x_pdata_of_init(struct i2c_client *client, struct pca955x_chipdef *chip) 329pca955x_pdata_of_init(struct i2c_client *client, struct pca955x_chipdef *chip)
@@ -297,6 +362,8 @@ pca955x_pdata_of_init(struct i2c_client *client, struct pca955x_chipdef *chip)
297 snprintf(pdata->leds[reg].name, sizeof(pdata->leds[reg].name), 362 snprintf(pdata->leds[reg].name, sizeof(pdata->leds[reg].name),
298 "%s", name); 363 "%s", name);
299 364
365 pdata->leds[reg].type = PCA955X_TYPE_LED;
366 of_property_read_u32(child, "type", &pdata->leds[reg].type);
300 of_property_read_string(child, "linux,default-trigger", 367 of_property_read_string(child, "linux,default-trigger",
301 &pdata->leds[reg].default_trigger); 368 &pdata->leds[reg].default_trigger);
302 } 369 }
@@ -332,6 +399,7 @@ static int pca955x_probe(struct i2c_client *client,
332 struct i2c_adapter *adapter; 399 struct i2c_adapter *adapter;
333 int i, err; 400 int i, err;
334 struct pca955x_platform_data *pdata; 401 struct pca955x_platform_data *pdata;
402 int ngpios = 0;
335 403
336 if (id) { 404 if (id) {
337 chip = &pca955x_chipdefs[id->driver_data]; 405 chip = &pca955x_chipdefs[id->driver_data];
@@ -392,9 +460,19 @@ static int pca955x_probe(struct i2c_client *client,
392 pca955x_led = &pca955x->leds[i]; 460 pca955x_led = &pca955x->leds[i];
393 pca955x_led->led_num = i; 461 pca955x_led->led_num = i;
394 pca955x_led->pca955x = pca955x; 462 pca955x_led->pca955x = pca955x;
395 463 pca955x_led->type = pdata->leds[i].type;
396 /* Platform data can specify LED names and default triggers */ 464
397 if (pdata) { 465 switch (pca955x_led->type) {
466 case PCA955X_TYPE_NONE:
467 break;
468 case PCA955X_TYPE_GPIO:
469 ngpios++;
470 break;
471 case PCA955X_TYPE_LED:
472 /*
473 * Platform data can specify LED names and
474 * default triggers
475 */
398 if (pdata->leds[i].name) 476 if (pdata->leds[i].name)
399 snprintf(pca955x_led->name, 477 snprintf(pca955x_led->name,
400 sizeof(pca955x_led->name), "pca955x:%s", 478 sizeof(pca955x_led->name), "pca955x:%s",
@@ -402,23 +480,20 @@ static int pca955x_probe(struct i2c_client *client,
402 if (pdata->leds[i].default_trigger) 480 if (pdata->leds[i].default_trigger)
403 pca955x_led->led_cdev.default_trigger = 481 pca955x_led->led_cdev.default_trigger =
404 pdata->leds[i].default_trigger; 482 pdata->leds[i].default_trigger;
405 } else {
406 snprintf(pca955x_led->name, sizeof(pca955x_led->name),
407 "pca955x:%d", i);
408 }
409 483
410 pca955x_led->led_cdev.name = pca955x_led->name; 484 pca955x_led->led_cdev.name = pca955x_led->name;
411 pca955x_led->led_cdev.brightness_set_blocking = pca955x_led_set; 485 pca955x_led->led_cdev.brightness_set_blocking =
486 pca955x_led_set;
412 487
413 err = devm_led_classdev_register(&client->dev, 488 err = devm_led_classdev_register(&client->dev,
414 &pca955x_led->led_cdev); 489 &pca955x_led->led_cdev);
415 if (err) 490 if (err)
416 return err; 491 return err;
417 }
418 492
419 /* Turn off LEDs */ 493 /* Turn off LED */
420 for (i = 0; i < pca95xx_num_led_regs(chip->bits); i++) 494 pca955x_led_set(&pca955x_led->led_cdev, LED_OFF);
421 pca955x_write_ls(client, i, 0x55); 495 }
496 }
422 497
423 /* PWM0 is used for half brightness or 50% duty cycle */ 498 /* PWM0 is used for half brightness or 50% duty cycle */
424 pca955x_write_pwm(client, 0, 255-LED_HALF); 499 pca955x_write_pwm(client, 0, 255-LED_HALF);
@@ -430,6 +505,34 @@ static int pca955x_probe(struct i2c_client *client,
430 pca955x_write_psc(client, 0, 0); 505 pca955x_write_psc(client, 0, 0);
431 pca955x_write_psc(client, 1, 0); 506 pca955x_write_psc(client, 1, 0);
432 507
508#ifdef CONFIG_LEDS_PCA955X_GPIO
509 if (ngpios) {
510 pca955x->gpio.label = "gpio-pca955x";
511 pca955x->gpio.direction_input = pca955x_gpio_direction_input;
512 pca955x->gpio.direction_output = pca955x_gpio_direction_output;
513 pca955x->gpio.set = pca955x_gpio_set_value;
514 pca955x->gpio.get = pca955x_gpio_get_value;
515 pca955x->gpio.request = pca955x_gpio_request_pin;
516 pca955x->gpio.can_sleep = 1;
517 pca955x->gpio.base = -1;
518 pca955x->gpio.ngpio = ngpios;
519 pca955x->gpio.parent = &client->dev;
520 pca955x->gpio.owner = THIS_MODULE;
521
522 err = devm_gpiochip_add_data(&client->dev, &pca955x->gpio,
523 pca955x);
524 if (err) {
525 /* Use data->gpio.dev as a flag for freeing gpiochip */
526 pca955x->gpio.parent = NULL;
527 dev_warn(&client->dev, "could not add gpiochip\n");
528 return err;
529 }
530 dev_info(&client->dev, "gpios %i...%i\n",
531 pca955x->gpio.base, pca955x->gpio.base +
532 pca955x->gpio.ngpio - 1);
533 }
534#endif
535
433 return 0; 536 return 0;
434} 537}
435 538