All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH linux dev-4.10 0/5] leds: pca955x: add GPIO support
@ 2017-06-09 13:12 Cédric Le Goater
  2017-06-09 13:12 ` [PATCH 1/5] leds: pca955x: add device tree support Cédric Le Goater
                   ` (4 more replies)
  0 siblings, 5 replies; 7+ messages in thread
From: Cédric Le Goater @ 2017-06-09 13:12 UTC (permalink / raw)
  To: openbmc; +Cc: Joel Stanley, Cédric Le Goater

Hello,

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 last patch adds the pca9552 bindings on the witherspoon.

I should send to mainline after some review.

Thanks,


Cédric Le Goater (5):
  leds: pca955x: add device tree support
  leds: pca955x: use devm_led_classdev_register
  leds: pca955x: add GPIO support
  dt-bindings leds: add pca955x
  ARM: dts: aspeed: witherspoon: Add pca9552 device

 .../devicetree/bindings/leds/leds-pca955x.txt      |  88 +++++++
 arch/arm/boot/dts/aspeed-bmc-opp-witherspoon.dts   |  85 +++++++
 drivers/leds/Kconfig                               |  11 +
 drivers/leds/leds-pca955x.c                        | 263 +++++++++++++++++----
 include/dt-bindings/leds/leds-pca955x.h            |  16 ++
 5 files changed, 418 insertions(+), 45 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/leds/leds-pca955x.txt
 create mode 100644 include/dt-bindings/leds/leds-pca955x.h

-- 
2.7.4

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH 1/5] leds: pca955x: add device tree support
  2017-06-09 13:12 [PATCH linux dev-4.10 0/5] leds: pca955x: add GPIO support Cédric Le Goater
@ 2017-06-09 13:12 ` Cédric Le Goater
  2017-06-16  6:46   ` Joel Stanley
  2017-06-09 13:12 ` [PATCH 2/5] leds: pca955x: use devm_led_classdev_register Cédric Le Goater
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 7+ messages in thread
From: Cédric Le Goater @ 2017-06-09 13:12 UTC (permalink / raw)
  To: openbmc; +Cc: Joel Stanley, Cédric Le Goater

It will be used in a following patch to define different operation
modes for each pin.

Signed-off-by: Cédric Le Goater <clg@kaod.org>
---
 drivers/leds/leds-pca955x.c | 88 ++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 83 insertions(+), 5 deletions(-)

diff --git a/drivers/leds/leds-pca955x.c b/drivers/leds/leds-pca955x.c
index 78a7ce816a47..a3e61a57d574 100644
--- a/drivers/leds/leds-pca955x.c
+++ b/drivers/leds/leds-pca955x.c
@@ -41,14 +41,17 @@
  */
 
 #include <linux/acpi.h>
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/string.h>
 #include <linux/ctype.h>
-#include <linux/leds.h>
+#include <linux/delay.h>
 #include <linux/err.h>
+#include <linux/gpio.h>
 #include <linux/i2c.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of.h>
 #include <linux/slab.h>
+#include <linux/string.h>
 
 /* LED select registers determine the source that drives LED outputs */
 #define PCA955X_LS_LED_ON	0x0	/* Output LOW */
@@ -122,6 +125,12 @@ struct pca955x_led {
 	struct led_classdev	led_cdev;
 	int			led_num;	/* 0 .. 15 potentially */
 	char			name[32];
+	const char		*default_trigger;
+};
+
+struct pca955x_platform_data {
+	struct pca955x_led	*leds;
+	int			num_leds;
 };
 
 /* 8 bits per input register */
@@ -250,6 +259,69 @@ static int pca955x_led_set(struct led_classdev *led_cdev,
 	return 0;
 }
 
+#if IS_ENABLED(CONFIG_OF)
+static struct pca955x_platform_data *
+pca955x_pdata_of_init(struct i2c_client *client, struct pca955x_chipdef *chip)
+{
+	struct device_node *np = client->dev.of_node;
+	struct device_node *child;
+	struct pca955x_platform_data *pdata;
+	int count;
+
+	count = of_get_child_count(np);
+	if (!count || count > chip->bits)
+		return ERR_PTR(-ENODEV);
+
+	pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
+	if (!pdata)
+		return ERR_PTR(-ENOMEM);
+
+	pdata->leds = devm_kzalloc(&client->dev,
+			   sizeof(struct pca955x_led) * chip->bits, GFP_KERNEL);
+	if (!pdata->leds)
+		return ERR_PTR(-ENOMEM);
+
+	for_each_child_of_node(np, child) {
+		const char *name;
+		u32 reg;
+		int res;
+
+		res = of_property_read_u32(child, "reg", &reg);
+		if ((res != 0) || (reg >= chip->bits))
+			continue;
+
+		if (of_property_read_string(child, "label", &name))
+			name = child->name;
+
+		snprintf(pdata->leds[reg].name, sizeof(pdata->leds[reg].name),
+			 "%s", name);
+
+		of_property_read_string(child, "linux,default-trigger",
+					&pdata->leds[reg].default_trigger);
+	}
+
+	pdata->num_leds = chip->bits;
+
+	return pdata;
+}
+
+static const struct of_device_id of_pca955x_match[] = {
+	{ .compatible = "nxp,pca9550", .data = (void *)pca9550 },
+	{ .compatible = "nxp,pca9551", .data = (void *)pca9551 },
+	{ .compatible = "nxp,pca9552", .data = (void *)pca9552 },
+	{ .compatible = "nxp,pca9553", .data = (void *)pca9553 },
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, of_pca955x_match);
+#else
+static struct pca955x_platform_data *
+pca955x_pdata_of_init(struct i2c_client *client, struct pca963x_chipdef *chip)
+{
+	return ERR_PTR(-ENODEV);
+}
+#endif
+
 static int pca955x_probe(struct i2c_client *client,
 					const struct i2c_device_id *id)
 {
@@ -257,8 +329,8 @@ static int pca955x_probe(struct i2c_client *client,
 	struct pca955x_led *pca955x_led;
 	struct pca955x_chipdef *chip;
 	struct i2c_adapter *adapter;
-	struct led_platform_data *pdata;
 	int i, err;
+	struct pca955x_platform_data *pdata;
 
 	if (id) {
 		chip = &pca955x_chipdefs[id->driver_data];
@@ -272,6 +344,11 @@ static int pca955x_probe(struct i2c_client *client,
 	}
 	adapter = to_i2c_adapter(client->dev.parent);
 	pdata = dev_get_platdata(&client->dev);
+	if (!pdata) {
+		pdata =	pca955x_pdata_of_init(client, chip);
+		if (IS_ERR(pdata))
+			return PTR_ERR(pdata);
+	}
 
 	/* Make sure the slave address / chip type combo given is possible */
 	if ((client->addr & ~((1 << chip->slv_addr_shift) - 1)) !=
@@ -378,6 +455,7 @@ static struct i2c_driver pca955x_driver = {
 	.driver = {
 		.name	= "leds-pca955x",
 		.acpi_match_table = ACPI_PTR(pca955x_acpi_ids),
+		.of_match_table = of_match_ptr(of_pca955x_match),
 	},
 	.probe	= pca955x_probe,
 	.remove	= pca955x_remove,
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 2/5] leds: pca955x: use devm_led_classdev_register
  2017-06-09 13:12 [PATCH linux dev-4.10 0/5] leds: pca955x: add GPIO support Cédric Le Goater
  2017-06-09 13:12 ` [PATCH 1/5] leds: pca955x: add device tree support Cédric Le Goater
@ 2017-06-09 13:12 ` Cédric Le Goater
  2017-06-09 13:12 ` [PATCH 3/5] leds: pca955x: add GPIO support Cédric Le Goater
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 7+ messages in thread
From: Cédric Le Goater @ 2017-06-09 13:12 UTC (permalink / raw)
  To: openbmc; +Cc: Joel Stanley, Cédric Le Goater

This lets us remove the loop doing the cleanup in case of failure and
also the remove handler of the i2c_driver.

Signed-off-by: Cédric Le Goater <clg@kaod.org>
---
 drivers/leds/leds-pca955x.c | 24 +++---------------------
 1 file changed, 3 insertions(+), 21 deletions(-)

diff --git a/drivers/leds/leds-pca955x.c b/drivers/leds/leds-pca955x.c
index a3e61a57d574..13634a0f6238 100644
--- a/drivers/leds/leds-pca955x.c
+++ b/drivers/leds/leds-pca955x.c
@@ -411,10 +411,10 @@ static int pca955x_probe(struct i2c_client *client,
 		pca955x_led->led_cdev.name = pca955x_led->name;
 		pca955x_led->led_cdev.brightness_set_blocking = pca955x_led_set;
 
-		err = led_classdev_register(&client->dev,
+		err = devm_led_classdev_register(&client->dev,
 					&pca955x_led->led_cdev);
-		if (err < 0)
-			goto exit;
+		if (err)
+			return err;
 	}
 
 	/* Turn off LEDs */
@@ -432,23 +432,6 @@ static int pca955x_probe(struct i2c_client *client,
 	pca955x_write_psc(client, 1, 0);
 
 	return 0;
-
-exit:
-	while (i--)
-		led_classdev_unregister(&pca955x->leds[i].led_cdev);
-
-	return err;
-}
-
-static int pca955x_remove(struct i2c_client *client)
-{
-	struct pca955x *pca955x = i2c_get_clientdata(client);
-	int i;
-
-	for (i = 0; i < pca955x->chipdef->bits; i++)
-		led_classdev_unregister(&pca955x->leds[i].led_cdev);
-
-	return 0;
 }
 
 static struct i2c_driver pca955x_driver = {
@@ -458,7 +441,6 @@ static struct i2c_driver pca955x_driver = {
 		.of_match_table = of_match_ptr(of_pca955x_match),
 	},
 	.probe	= pca955x_probe,
-	.remove	= pca955x_remove,
 	.id_table = pca955x_id,
 };
 
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 3/5] leds: pca955x: add GPIO support
  2017-06-09 13:12 [PATCH linux dev-4.10 0/5] leds: pca955x: add GPIO support Cédric Le Goater
  2017-06-09 13:12 ` [PATCH 1/5] leds: pca955x: add device tree support Cédric Le Goater
  2017-06-09 13:12 ` [PATCH 2/5] leds: pca955x: use devm_led_classdev_register Cédric Le Goater
@ 2017-06-09 13:12 ` Cédric Le Goater
  2017-06-09 13:12 ` [PATCH 4/5] dt-bindings leds: add pca955x Cédric Le Goater
  2017-06-09 13:12 ` [PATCH 5/5] ARM: dts: aspeed: witherspoon: Add pca9552 device Cédric Le Goater
  4 siblings, 0 replies; 7+ messages in thread
From: Cédric Le Goater @ 2017-06-09 13:12 UTC (permalink / raw)
  To: openbmc; +Cc: Joel Stanley, Cédric Le Goater

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>
---
 drivers/leds/Kconfig                    |  11 +++
 drivers/leds/leds-pca955x.c             | 159 +++++++++++++++++++++++++++-----
 include/dt-bindings/leds/leds-pca955x.h |  16 ++++
 3 files changed, 163 insertions(+), 23 deletions(-)
 create mode 100644 include/dt-bindings/leds/leds-pca955x.h

diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index c621cbbb5768..0177a36dab3d 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -352,6 +352,17 @@ config LEDS_PCA955X
 	  LED driver chips accessed via the I2C bus.  Supported
 	  devices include PCA9550, PCA9551, PCA9552, and PCA9553.
 
+config LEDS_PCA955X_GPIO
+	bool "Enable GPIO support for PCA955X"
+	depends on LEDS_PCA955X
+	depends on GPIOLIB
+	help
+	  Allow unused pins on PCA955X to be used as gpio.
+
+	  To use a pin as gpio the pin type should be set to
+	  PCA955X_TYPE_GPIO in the device tree.
+
+
 config LEDS_PCA963X
 	tristate "LED support for PCA963x I2C chip"
 	depends on LEDS_CLASS
diff --git a/drivers/leds/leds-pca955x.c b/drivers/leds/leds-pca955x.c
index 13634a0f6238..92167425192f 100644
--- a/drivers/leds/leds-pca955x.c
+++ b/drivers/leds/leds-pca955x.c
@@ -53,6 +53,8 @@
 #include <linux/slab.h>
 #include <linux/string.h>
 
+#include <dt-bindings/leds/leds-pca955x.h>
+
 /* LED select registers determine the source that drives LED outputs */
 #define PCA955X_LS_LED_ON	0x0	/* Output LOW */
 #define PCA955X_LS_LED_OFF	0x1	/* Output HI-Z */
@@ -118,6 +120,9 @@ struct pca955x {
 	struct pca955x_led *leds;
 	struct pca955x_chipdef	*chipdef;
 	struct i2c_client	*client;
+#ifdef CONFIG_LEDS_PCA955X_GPIO
+	struct gpio_chip gpio;
+#endif
 };
 
 struct pca955x_led {
@@ -125,6 +130,7 @@ struct pca955x_led {
 	struct led_classdev	led_cdev;
 	int			led_num;	/* 0 .. 15 potentially */
 	char			name[32];
+	u32			type;
 	const char		*default_trigger;
 };
 
@@ -259,6 +265,65 @@ static int pca955x_led_set(struct led_classdev *led_cdev,
 	return 0;
 }
 
+#ifdef CONFIG_LEDS_PCA955X_GPIO
+/*
+ * Read the INPUT register, which contains the state of LEDs.
+ */
+static u8 pca955x_read_input(struct i2c_client *client, int n)
+{
+	return (u8)i2c_smbus_read_byte_data(client, n);
+}
+
+static int pca955x_gpio_request_pin(struct gpio_chip *gc, unsigned int offset)
+{
+	struct pca955x *pca955x = gpiochip_get_data(gc);
+	struct pca955x_led *led = &pca955x->leds[offset];
+
+	if (led->type == PCA955X_TYPE_GPIO)
+		return 0;
+
+	return -EBUSY;
+}
+
+static void pca955x_gpio_set_value(struct gpio_chip *gc, unsigned int offset,
+				   int val)
+{
+	struct pca955x *pca955x = gpiochip_get_data(gc);
+	struct pca955x_led *led = &pca955x->leds[offset];
+
+	if (val)
+		pca955x_led_set(&led->led_cdev, LED_FULL);
+	else
+		pca955x_led_set(&led->led_cdev, LED_OFF);
+}
+
+static int pca955x_gpio_get_value(struct gpio_chip *gc, unsigned int offset)
+{
+	struct pca955x *pca955x = gpiochip_get_data(gc);
+	struct pca955x_led *led = &pca955x->leds[offset];
+	u8 reg = pca955x_read_input(pca955x->client, led->led_num / 8);
+
+	return !!(reg & (1 << (led->led_num % 8)));
+}
+
+static int pca955x_gpio_direction_input(struct gpio_chip *gc,
+					unsigned int offset)
+{
+	/* To use as input ensure pin is not driven */
+	pca955x_gpio_set_value(gc, offset, 0);
+
+	return 0;
+}
+
+static int pca955x_gpio_direction_output(struct gpio_chip *gc,
+					 unsigned int offset, int val)
+{
+	pca955x_gpio_set_value(gc, offset, val);
+
+	return 0;
+}
+#endif /* CONFIG_LEDS_PCA955X_GPIO */
+
 #if IS_ENABLED(CONFIG_OF)
 static struct pca955x_platform_data *
 pca955x_pdata_of_init(struct i2c_client *client, struct pca955x_chipdef *chip)
@@ -296,6 +361,8 @@ pca955x_pdata_of_init(struct i2c_client *client, struct pca955x_chipdef *chip)
 		snprintf(pdata->leds[reg].name, sizeof(pdata->leds[reg].name),
 			 "%s", name);
 
+		pdata->leds[reg].type = PCA955X_TYPE_LED;
+		of_property_read_u32(child, "type", &pdata->leds[reg].type);
 		of_property_read_string(child, "linux,default-trigger",
 					&pdata->leds[reg].default_trigger);
 	}
@@ -331,6 +398,7 @@ static int pca955x_probe(struct i2c_client *client,
 	struct i2c_adapter *adapter;
 	int i, err;
 	struct pca955x_platform_data *pdata;
+	int ngpios = 0;
 
 	if (id) {
 		chip = &pca955x_chipdefs[id->driver_data];
@@ -390,36 +458,52 @@ static int pca955x_probe(struct i2c_client *client,
 	pca955x->chipdef = chip;
 
 	for (i = 0; i < chip->bits; i++) {
+		struct pca955x_led *pdata_led = &pdata->leds[i];
+
 		pca955x_led = &pca955x->leds[i];
 		pca955x_led->led_num = i;
 		pca955x_led->pca955x = pca955x;
-
-		/* Platform data can specify LED names and default triggers */
-		if (pdata) {
-			if (pdata->leds[i].name)
+		pca955x_led->type = pdata_led->type;
+
+		switch (pca955x_led->type) {
+		case PCA955X_TYPE_NONE:
+			break;
+		case PCA955X_TYPE_GPIO:
+			ngpios++;
+			break;
+		case PCA955X_TYPE_LED:
+			/*
+			 * Platform data can specify LED names and
+			 * default triggers
+			 */
+			if (pdata) {
+				if (pdata->leds[i].name)
+					snprintf(pca955x_led->name,
+						 sizeof(pca955x_led->name),
+						 "pca955x:%s",
+						 pdata->leds[i].name);
+				if (pdata->leds[i].default_trigger)
+					pca955x_led->led_cdev.default_trigger =
+						pdata->leds[i].default_trigger;
+			} else {
 				snprintf(pca955x_led->name,
-					sizeof(pca955x_led->name), "pca955x:%s",
-					pdata->leds[i].name);
-			if (pdata->leds[i].default_trigger)
-				pca955x_led->led_cdev.default_trigger =
-					pdata->leds[i].default_trigger;
-		} else {
-			snprintf(pca955x_led->name, sizeof(pca955x_led->name),
-				 "pca955x:%d", i);
-		}
+					 sizeof(pca955x_led->name),
+					 "pca955x:%d", i);
+			}
 
-		pca955x_led->led_cdev.name = pca955x_led->name;
-		pca955x_led->led_cdev.brightness_set_blocking = pca955x_led_set;
+			pca955x_led->led_cdev.name = pca955x_led->name;
+			pca955x_led->led_cdev.brightness_set_blocking =
+				pca955x_led_set;
 
-		err = devm_led_classdev_register(&client->dev,
-					&pca955x_led->led_cdev);
-		if (err)
-			return err;
-	}
+			err = devm_led_classdev_register(&client->dev,
+						 &pca955x_led->led_cdev);
+			if (err)
+				return err;
 
-	/* Turn off LEDs */
-	for (i = 0; i < pca95xx_num_led_regs(chip->bits); i++)
-		pca955x_write_ls(client, i, 0x55);
+			/* Turn off LED */
+			pca955x_led_set(&pca955x_led->led_cdev, LED_OFF);
+		}
+	}
 
 	/* PWM0 is used for half brightness or 50% duty cycle */
 	pca955x_write_pwm(client, 0, 255-LED_HALF);
@@ -431,6 +515,35 @@ static int pca955x_probe(struct i2c_client *client,
 	pca955x_write_psc(client, 0, 0);
 	pca955x_write_psc(client, 1, 0);
 
+#ifdef CONFIG_LEDS_PCA955X_GPIO
+	if (ngpios) {
+		pca955x->gpio.label = "gpio-pca955x";
+		pca955x->gpio.direction_input = pca955x_gpio_direction_input;
+		pca955x->gpio.direction_output = pca955x_gpio_direction_output;
+		pca955x->gpio.set = pca955x_gpio_set_value;
+		pca955x->gpio.get = pca955x_gpio_get_value;
+		pca955x->gpio.request = pca955x_gpio_request_pin;
+		pca955x->gpio.can_sleep = 1;
+		pca955x->gpio.base = -1;
+		pca955x->gpio.ngpio = ngpios;
+		pca955x->gpio.parent = &client->dev;
+		pca955x->gpio.owner = THIS_MODULE;
+
+		err = devm_gpiochip_add_data(&client->dev, &pca955x->gpio,
+					     pca955x);
+		if (err) {
+			/* Use data->gpio.dev as a flag for freeing gpiochip */
+			pca955x->gpio.parent = NULL;
+			dev_warn(&client->dev, "could not add gpiochip\n");
+			return err;
+		} else {
+			dev_info(&client->dev, "gpios %i...%i\n",
+				 pca955x->gpio.base, pca955x->gpio.base +
+				 pca955x->gpio.ngpio - 1);
+		}
+	}
+#endif
+
 	return 0;
 }
 
diff --git a/include/dt-bindings/leds/leds-pca955x.h b/include/dt-bindings/leds/leds-pca955x.h
new file mode 100644
index 000000000000..78cb7e979de7
--- /dev/null
+++ b/include/dt-bindings/leds/leds-pca955x.h
@@ -0,0 +1,16 @@
+/*
+ * This header provides constants for pca955x LED bindings.
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2.  This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef _DT_BINDINGS_LEDS_PCA955X_H
+#define _DT_BINDINGS_LEDS_PCA955X_H
+
+#define PCA955X_TYPE_NONE         0
+#define PCA955X_TYPE_LED          1
+#define PCA955X_TYPE_GPIO         2
+
+#endif /* _DT_BINDINGS_LEDS_PCA955X_H */
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 4/5] dt-bindings leds: add pca955x
  2017-06-09 13:12 [PATCH linux dev-4.10 0/5] leds: pca955x: add GPIO support Cédric Le Goater
                   ` (2 preceding siblings ...)
  2017-06-09 13:12 ` [PATCH 3/5] leds: pca955x: add GPIO support Cédric Le Goater
@ 2017-06-09 13:12 ` Cédric Le Goater
  2017-06-09 13:12 ` [PATCH 5/5] ARM: dts: aspeed: witherspoon: Add pca9552 device Cédric Le Goater
  4 siblings, 0 replies; 7+ messages in thread
From: Cédric Le Goater @ 2017-06-09 13:12 UTC (permalink / raw)
  To: openbmc; +Cc: Joel Stanley, Cédric Le Goater

This adds the devicetree bindings for the PCA955x I2C LED blinkers.

Signed-off-by: Cédric Le Goater <clg@kaod.org>
---
 .../devicetree/bindings/leds/leds-pca955x.txt      | 88 ++++++++++++++++++++++
 1 file changed, 88 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/leds/leds-pca955x.txt

diff --git a/Documentation/devicetree/bindings/leds/leds-pca955x.txt b/Documentation/devicetree/bindings/leds/leds-pca955x.txt
new file mode 100644
index 000000000000..7984efb767b4
--- /dev/null
+++ b/Documentation/devicetree/bindings/leds/leds-pca955x.txt
@@ -0,0 +1,88 @@
+* NXP - pca955x LED driver
+
+The PCA955x family of chips are I2C LED blinkers whose pins not used
+to control LEDs can be used as general purpose I/Os. The GPIO pins can
+be input or output, and output pins can also be pulse-width controlled.
+
+Required properties:
+- compatible : should be one of :
+	"nxp,pca9550"
+	"nxp,pca9551"
+	"nxp,pca9552"
+	"nxp,pca9553"
+- #address-cells: must be 1
+- #size-cells: must be 0
+- reg: I2C slave address. depends on the model.
+
+Optional properties:
+- gpio-controller: allows pins to be used as GPIOs.
+- #gpio-cells: must be 2.
+- gpio-line-names: define the names of the GPIO lines
+
+LED sub-node properties:
+- reg : number of LED line.
+		from 0 to  1 for the pca9550
+		from 0 to  7 for the pca9551
+		from 0 to 15 for the pca9552
+		from 0 to  3 for the pca9553
+- type: (optional) either
+	PCA9532_TYPE_NONE
+	PCA9532_TYPE_LED
+	PCA9532_TYPE_GPIO
+	see dt-bindings/leds/leds-pca955x.h (default to LED)
+- label : (optional)
+	see Documentation/devicetree/bindings/leds/common.txt
+- linux,default-trigger : (optional)
+	see Documentation/devicetree/bindings/leds/common.txt
+
+Examples:
+
+pca9552: pca9552@60 {
+	compatible = "nxp,pca9552";
+	#address-cells = <1>;
+        #size-cells = <0>;
+	reg = <0x60>;
+
+	gpio-controller;
+	#gpio-cells = <2>;
+	gpio-line-names = "GPIO12", "GPIO13", "GPIO14", "GPIO15";
+
+	gpio@12 {
+		reg = <12>;
+		type = <PCA955X_TYPE_GPIO>;
+	};
+	gpio@13 {
+		reg = <13>;
+		type = <PCA955X_TYPE_GPIO>;
+	};
+	gpio@14 {
+		reg = <14>;
+		type = <PCA955X_TYPE_GPIO>;
+	};
+	gpio@15 {
+		reg = <15>;
+		type = <PCA955X_TYPE_GPIO>;
+	};
+
+	led@0 {
+		label = "red:power";
+		linux,default-trigger = "default-on";
+		reg = <0>;
+		type = <PCA955X_TYPE_LED>;
+	};
+	led@1 {
+		label = "green:power";
+		reg = <1>;
+		type = <PCA955X_TYPE_LED>;
+	};
+	led@2 {
+		label = "pca9552:yellow";
+		reg = <2>;
+		type = <PCA955X_TYPE_LED>;
+	};
+	led@3 {
+		label = "pca9552:white";
+		reg = <3>;
+		type = <PCA955X_TYPE_LED>;
+	};
+};
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 5/5] ARM: dts: aspeed: witherspoon: Add pca9552 device
  2017-06-09 13:12 [PATCH linux dev-4.10 0/5] leds: pca955x: add GPIO support Cédric Le Goater
                   ` (3 preceding siblings ...)
  2017-06-09 13:12 ` [PATCH 4/5] dt-bindings leds: add pca955x Cédric Le Goater
@ 2017-06-09 13:12 ` Cédric Le Goater
  4 siblings, 0 replies; 7+ messages in thread
From: Cédric Le Goater @ 2017-06-09 13:12 UTC (permalink / raw)
  To: openbmc; +Cc: Joel Stanley, Cédric Le Goater

All pins of the pca9552 are used as GPIOs.

Signed-off-by: Cédric Le Goater <clg@kaod.org>
---
 arch/arm/boot/dts/aspeed-bmc-opp-witherspoon.dts | 85 ++++++++++++++++++++++++
 1 file changed, 85 insertions(+)

diff --git a/arch/arm/boot/dts/aspeed-bmc-opp-witherspoon.dts b/arch/arm/boot/dts/aspeed-bmc-opp-witherspoon.dts
index 3001597d42d9..b80bf4706b66 100644
--- a/arch/arm/boot/dts/aspeed-bmc-opp-witherspoon.dts
+++ b/arch/arm/boot/dts/aspeed-bmc-opp-witherspoon.dts
@@ -2,6 +2,7 @@
 
 #include "aspeed-g5.dtsi"
 #include <dt-bindings/gpio/aspeed-gpio.h>
+#include <dt-bindings/leds/leds-pca955x.h>
 
 / {
 	model = "Witherspoon BMC";
@@ -290,6 +291,90 @@
 &i2c11 {
 	status = "okay";
 
+	pca9552: pca9552@60 {
+		compatible = "nxp,pca9552";
+		reg = <0x60>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		gpio-controller;
+		#gpio-cells = <2>;
+
+		gpio-line-names = "PS_SMBUS_RESET_N", "APSS_RESET_N",
+		"GPU0_TH_OVERT_N_BUFF",	"GPU1_TH_OVERT_N_BUFF",
+		"GPU2_TH_OVERT_N_BUFF", "GPU3_TH_OVERT_N_BUFF",
+		"GPU4_TH_OVERT_N_BUFF",	"GPU5_TH_OVERT_N_BUFF",
+		"GPU0_PWR_GOOD_BUFF", "GPU1_PWR_GOOD_BUFF",
+		"GPU2_PWR_GOOD_BUFF", "GPU3_PWR_GOOD_BUFF",
+		"GPU4_PWR_GOOD_BUFF", "GPU5_PWR_GOOD_BUFF",
+		"12V_BREAKER_FLT_N", "THROTTLE_UNLATCHED_N";
+
+		gpio@0 {
+			reg = <0>;
+			type = <PCA955X_TYPE_GPIO>;
+		};
+		gpio@1 {
+			reg = <1>;
+			type = <PCA955X_TYPE_GPIO>;
+		};
+		gpio@2 {
+			reg = <2>;
+			type = <PCA955X_TYPE_GPIO>;
+		};
+		gpio@3 {
+			reg = <3>;
+			type = <PCA955X_TYPE_GPIO>;
+		};
+		gpio@4 {
+			reg = <4>;
+			type = <PCA955X_TYPE_GPIO>;
+		};
+		gpio@5 {
+			reg = <5>;
+			type = <PCA955X_TYPE_GPIO>;
+		};
+		gpio@6 {
+			reg = <6>;
+			type = <PCA955X_TYPE_GPIO>;
+		};
+		gpio@7 {
+			reg = <7>;
+			type = <PCA955X_TYPE_GPIO>;
+		};
+		gpio@8 {
+			reg = <8>;
+			type = <PCA955X_TYPE_GPIO>;
+		};
+		gpio@9 {
+			reg = <9>;
+			type = <PCA955X_TYPE_GPIO>;
+		};
+		gpio@10 {
+			reg = <10>;
+			type = <PCA955X_TYPE_GPIO>;
+		};
+		gpio@11 {
+			reg = <11>;
+			type = <PCA955X_TYPE_GPIO>;
+		};
+		gpio@12 {
+			reg = <12>;
+			type = <PCA955X_TYPE_GPIO>;
+		};
+		gpio@13 {
+			reg = <13>;
+			type = <PCA955X_TYPE_GPIO>;
+		};
+		gpio@14 {
+			reg = <14>;
+			type = <PCA955X_TYPE_GPIO>;
+		};
+		gpio@15 {
+			reg = <15>;
+			type = <PCA955X_TYPE_GPIO>;
+		};
+
+	};
+
 	/* TPM */
 	/* 9552 */
 
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH 1/5] leds: pca955x: add device tree support
  2017-06-09 13:12 ` [PATCH 1/5] leds: pca955x: add device tree support Cédric Le Goater
@ 2017-06-16  6:46   ` Joel Stanley
  0 siblings, 0 replies; 7+ messages in thread
From: Joel Stanley @ 2017-06-16  6:46 UTC (permalink / raw)
  To: Cédric Le Goater; +Cc: OpenBMC Maillist

On Fri, Jun 9, 2017 at 10:42 PM, Cédric Le Goater <clg@kaod.org> wrote:
> It will be used in a following patch to define different operation
> modes for each pin.
>
> Signed-off-by: Cédric Le Goater <clg@kaod.org>

I've applied this series to dev-4.10 make it easier for us to get some testing.

Thanks for sorting this out.

Cheers,

Joel
> ---
>  drivers/leds/leds-pca955x.c | 88 ++++++++++++++++++++++++++++++++++++++++++---
>  1 file changed, 83 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/leds/leds-pca955x.c b/drivers/leds/leds-pca955x.c
> index 78a7ce816a47..a3e61a57d574 100644
> --- a/drivers/leds/leds-pca955x.c
> +++ b/drivers/leds/leds-pca955x.c
> @@ -41,14 +41,17 @@
>   */
>
>  #include <linux/acpi.h>
> -#include <linux/module.h>
> -#include <linux/delay.h>
> -#include <linux/string.h>
>  #include <linux/ctype.h>
> -#include <linux/leds.h>
> +#include <linux/delay.h>
>  #include <linux/err.h>
> +#include <linux/gpio.h>
>  #include <linux/i2c.h>
> +#include <linux/leds.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/of.h>
>  #include <linux/slab.h>
> +#include <linux/string.h>
>
>  /* LED select registers determine the source that drives LED outputs */
>  #define PCA955X_LS_LED_ON      0x0     /* Output LOW */
> @@ -122,6 +125,12 @@ struct pca955x_led {
>         struct led_classdev     led_cdev;
>         int                     led_num;        /* 0 .. 15 potentially */
>         char                    name[32];
> +       const char              *default_trigger;
> +};
> +
> +struct pca955x_platform_data {
> +       struct pca955x_led      *leds;
> +       int                     num_leds;
>  };
>
>  /* 8 bits per input register */
> @@ -250,6 +259,69 @@ static int pca955x_led_set(struct led_classdev *led_cdev,
>         return 0;
>  }
>
> +#if IS_ENABLED(CONFIG_OF)
> +static struct pca955x_platform_data *
> +pca955x_pdata_of_init(struct i2c_client *client, struct pca955x_chipdef *chip)
> +{
> +       struct device_node *np = client->dev.of_node;
> +       struct device_node *child;
> +       struct pca955x_platform_data *pdata;
> +       int count;
> +
> +       count = of_get_child_count(np);
> +       if (!count || count > chip->bits)
> +               return ERR_PTR(-ENODEV);
> +
> +       pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
> +       if (!pdata)
> +               return ERR_PTR(-ENOMEM);
> +
> +       pdata->leds = devm_kzalloc(&client->dev,
> +                          sizeof(struct pca955x_led) * chip->bits, GFP_KERNEL);
> +       if (!pdata->leds)
> +               return ERR_PTR(-ENOMEM);
> +
> +       for_each_child_of_node(np, child) {
> +               const char *name;
> +               u32 reg;
> +               int res;
> +
> +               res = of_property_read_u32(child, "reg", &reg);
> +               if ((res != 0) || (reg >= chip->bits))
> +                       continue;
> +
> +               if (of_property_read_string(child, "label", &name))
> +                       name = child->name;
> +
> +               snprintf(pdata->leds[reg].name, sizeof(pdata->leds[reg].name),
> +                        "%s", name);
> +
> +               of_property_read_string(child, "linux,default-trigger",
> +                                       &pdata->leds[reg].default_trigger);
> +       }
> +
> +       pdata->num_leds = chip->bits;
> +
> +       return pdata;
> +}
> +
> +static const struct of_device_id of_pca955x_match[] = {
> +       { .compatible = "nxp,pca9550", .data = (void *)pca9550 },
> +       { .compatible = "nxp,pca9551", .data = (void *)pca9551 },
> +       { .compatible = "nxp,pca9552", .data = (void *)pca9552 },
> +       { .compatible = "nxp,pca9553", .data = (void *)pca9553 },
> +       {},
> +};
> +
> +MODULE_DEVICE_TABLE(of, of_pca955x_match);
> +#else
> +static struct pca955x_platform_data *
> +pca955x_pdata_of_init(struct i2c_client *client, struct pca963x_chipdef *chip)
> +{
> +       return ERR_PTR(-ENODEV);
> +}
> +#endif
> +
>  static int pca955x_probe(struct i2c_client *client,
>                                         const struct i2c_device_id *id)
>  {
> @@ -257,8 +329,8 @@ static int pca955x_probe(struct i2c_client *client,
>         struct pca955x_led *pca955x_led;
>         struct pca955x_chipdef *chip;
>         struct i2c_adapter *adapter;
> -       struct led_platform_data *pdata;
>         int i, err;
> +       struct pca955x_platform_data *pdata;
>
>         if (id) {
>                 chip = &pca955x_chipdefs[id->driver_data];
> @@ -272,6 +344,11 @@ static int pca955x_probe(struct i2c_client *client,
>         }
>         adapter = to_i2c_adapter(client->dev.parent);
>         pdata = dev_get_platdata(&client->dev);
> +       if (!pdata) {
> +               pdata = pca955x_pdata_of_init(client, chip);
> +               if (IS_ERR(pdata))
> +                       return PTR_ERR(pdata);
> +       }
>
>         /* Make sure the slave address / chip type combo given is possible */
>         if ((client->addr & ~((1 << chip->slv_addr_shift) - 1)) !=
> @@ -378,6 +455,7 @@ static struct i2c_driver pca955x_driver = {
>         .driver = {
>                 .name   = "leds-pca955x",
>                 .acpi_match_table = ACPI_PTR(pca955x_acpi_ids),
> +               .of_match_table = of_match_ptr(of_pca955x_match),
>         },
>         .probe  = pca955x_probe,
>         .remove = pca955x_remove,
> --
> 2.7.4
>

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2017-06-16  6:46 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-06-09 13:12 [PATCH linux dev-4.10 0/5] leds: pca955x: add GPIO support Cédric Le Goater
2017-06-09 13:12 ` [PATCH 1/5] leds: pca955x: add device tree support Cédric Le Goater
2017-06-16  6:46   ` Joel Stanley
2017-06-09 13:12 ` [PATCH 2/5] leds: pca955x: use devm_led_classdev_register Cédric Le Goater
2017-06-09 13:12 ` [PATCH 3/5] leds: pca955x: add GPIO support Cédric Le Goater
2017-06-09 13:12 ` [PATCH 4/5] dt-bindings leds: add pca955x Cédric Le Goater
2017-06-09 13:12 ` [PATCH 5/5] ARM: dts: aspeed: witherspoon: Add pca9552 device Cédric Le Goater

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.