diff mbox series

[2/2] gpio: Add a Gateworks PLD GPIO driver

Message ID 20190130203156.19774-2-linus.walleij@linaro.org
State Superseded
Headers show
Series None | expand

Commit Message

Linus Walleij Jan. 30, 2019, 8:31 p.m. UTC
This adds a driver for Gateworks PLD GPIO, that exist in
two instances on the Gateworks Cambria GW2358-4 router
platform at least.

Cc: Imre Kaloz <kaloz@openwrt.org>
Cc: Tim Harvey <tharvey@gateworks.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>

---
 drivers/gpio/Kconfig       |   7 ++
 drivers/gpio/Makefile      |   1 +
 drivers/gpio/gpio-gw-pld.c | 137 +++++++++++++++++++++++++++++++++++++
 3 files changed, 145 insertions(+)
 create mode 100644 drivers/gpio/gpio-gw-pld.c

-- 
2.20.1

Comments

Bartosz Golaszewski Jan. 31, 2019, 8:28 a.m. UTC | #1
śr., 30 sty 2019 o 21:32 Linus Walleij <linus.walleij@linaro.org> napisał(a):
>

> This adds a driver for Gateworks PLD GPIO, that exist in

> two instances on the Gateworks Cambria GW2358-4 router

> platform at least.

>


Hi Linus,

a couple remarks below.

> Cc: Imre Kaloz <kaloz@openwrt.org>

> Cc: Tim Harvey <tharvey@gateworks.com>

> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>

> ---

>  drivers/gpio/Kconfig       |   7 ++

>  drivers/gpio/Makefile      |   1 +

>  drivers/gpio/gpio-gw-pld.c | 137 +++++++++++++++++++++++++++++++++++++

>  3 files changed, 145 insertions(+)

>  create mode 100644 drivers/gpio/gpio-gw-pld.c

>

> diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig

> index 77785f4b94bb..fe4a47e49a24 100644

> --- a/drivers/gpio/Kconfig

> +++ b/drivers/gpio/Kconfig

> @@ -842,6 +842,13 @@ config GPIO_ADNP

>           enough to represent all pins, but the driver will assume a

>           register layout for 64 pins (8 registers).

>

> +config GPIO_GW_PLD

> +       tristate "Gateworks PLD GPIO Expander"

> +       depends on OF_GPIO

> +       help

> +         Say yes here to provide access to the Gateworks I2C PLD GPIO

> +         Expander. This is used at least on the Cambria GW2358-4.

> +

>  config GPIO_MAX7300

>         tristate "Maxim MAX7300 GPIO expander"

>         select GPIO_MAX730X

> diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile

> index 289ad2727bef..cf66523b5eec 100644

> --- a/drivers/gpio/Makefile

> +++ b/drivers/gpio/Makefile

> @@ -55,6 +55,7 @@ obj-$(CONFIG_GPIO_FTGPIO010)  += gpio-ftgpio010.o

>  obj-$(CONFIG_GPIO_GE_FPGA)     += gpio-ge.o

>  obj-$(CONFIG_GPIO_GPIO_MM)     += gpio-gpio-mm.o

>  obj-$(CONFIG_GPIO_GRGPIO)      += gpio-grgpio.o

> +obj-$(CONFIG_GPIO_GW_PLD)      += gpio-gw-pld.o

>  obj-$(CONFIG_GPIO_HLWD)                += gpio-hlwd.o

>  obj-$(CONFIG_HTC_EGPIO)                += gpio-htc-egpio.o

>  obj-$(CONFIG_GPIO_ICH)         += gpio-ich.o

> diff --git a/drivers/gpio/gpio-gw-pld.c b/drivers/gpio/gpio-gw-pld.c

> new file mode 100644

> index 000000000000..7ba8dd102f5a

> --- /dev/null

> +++ b/drivers/gpio/gpio-gw-pld.c

> @@ -0,0 +1,137 @@

> +// SPDX-License-Identifier: GPL-2.0+

> +/*

> + * Gateworks I2C PLD GPIO expander

> + *

> + * Copyright (C) 2019 Linus Walleij <linus.walleij@linaro.org>

> + *

> + * Based on code and know-how from the OpenWrt driver:

> + * Copyright (C) 2009 Gateworks Corporation

> + * Authors: Chris Lang, Imre Kaloz

> + */


I was told to use C++-style comments for the entire copyright header
in the reviews for my max77650 submission on two separate occasions.
While I wasn't aware of that policy it seems to be right[1] and I'd
say we should try to keep the code base uniform.

> +#include <linux/kernel.h>

> +#include <linux/slab.h>

> +#include <linux/gpio/driver.h>

> +#include <linux/i2c.h>

> +#include <linux/module.h>

> +

> +/**

> + * struct gw_pld - State container for Gateworks PLD

> + * @chip: GPIO chip instance

> + * @client: I2C client

> + * @out: shadow register for the output bute

> + */

> +struct gw_pld {

> +       struct gpio_chip chip;

> +       struct i2c_client *client;

> +       unsigned out;


Checkpatch should complain about using unsigned instead of unsigned int.

> +};

> +

> +/*

> + * The Gateworks I2C PLD chip only expose one read and one write register.

> + * Writing a "one" bit (to match the reset state) lets that pin be used as an

> + * input. It is an open-drain model.

> + */

> +static int gw_pld_input8(struct gpio_chip *gc, unsigned offset)

> +{

> +       struct gw_pld *gw = gpiochip_get_data(gc);

> +

> +       gw->out |= (1 << offset);

> +

> +       return i2c_smbus_write_byte(gw->client, gw->out);


May I suggest using the i2c regmap for that? You wouldn't even need to
keep track of the out variable - you could use the
regmap_update_bits() helper.

I often advocate the use of regmap whenever possible because it really
is superior to using the low-level primitives for subsystems. Just
take a look at the code shrink of at24 after porting it to regmap!

> +}

> +

> +static int gw_pld_get8(struct gpio_chip *gc, unsigned offset)

> +{

> +       struct gw_pld *gw = gpiochip_get_data(gc);

> +       s32 value;

> +

> +       value = i2c_smbus_read_byte(gw->client);

> +

> +       return (value < 0) ? 0 : (value & (1 << offset));

> +}

> +

> +static int gw_pld_output8(struct gpio_chip *gc, unsigned offset, int value)

> +{

> +       struct gw_pld *gw = gpiochip_get_data(gc);

> +       unsigned bit = 1 << offset;

> +

> +       if (value)

> +               gw->out |= bit;

> +       else

> +               gw->out &= ~bit;

> +

> +       return i2c_smbus_write_byte(gw->client, gw->out);

> +}

> +

> +static void gw_pld_set8(struct gpio_chip *gc, unsigned offset, int value)

> +{

> +       gw_pld_output8(gc, offset, value);

> +}

> +

> +static int gw_pld_probe(struct i2c_client *client,

> +                       const struct i2c_device_id *id)

> +{

> +       struct device *dev = &client->dev;

> +       struct device_node *np = dev->of_node;

> +       struct gw_pld *gw;

> +       int ret;

> +

> +       gw = devm_kzalloc(dev, sizeof(*gw), GFP_KERNEL);

> +       if (!gw)

> +               return -ENOMEM;

> +

> +       gw->chip.base = -1;

> +       gw->chip.can_sleep = true;

> +       gw->chip.parent = dev;

> +       gw->chip.of_node = np;

> +       gw->chip.owner = THIS_MODULE;

> +       gw->chip.label = dev_name(dev);

> +       gw->chip.ngpio = 8;

> +       gw->chip.direction_input = gw_pld_input8;

> +       gw->chip.get = gw_pld_get8;

> +       gw->chip.direction_output = gw_pld_output8;

> +       gw->chip.set = gw_pld_set8;

> +       gw->client = client;

> +

> +       /*

> +        * The Gateworks I2C PLD chip does not properly send the acknowledge

> +        * bit at all times, but we can still use the standard i2c_smbus

> +        * functions by simply ignoring this bit.

> +        */

> +       client->flags |= I2C_M_IGNORE_NAK;

> +       gw->out = 0xFF;

> +

> +       i2c_set_clientdata(client, gw);

> +

> +       ret = devm_gpiochip_add_data(dev, &gw->chip, gw);

> +       if (ret)

> +               return ret;

> +

> +       dev_info(dev, "registered Gateworks PLD GPIO device\n");

> +

> +       return 0;

> +}

> +

> +static const struct i2c_device_id gw_pld_id[] = {

> +       { "gw-pld", },

> +       { }

> +};

> +MODULE_DEVICE_TABLE(i2c, gw_pld_id);

> +

> +static const struct of_device_id gw_pld_dt_ids[] = {

> +       { .compatible = "gateworks,pld-gpio", },

> +};

> +MODULE_DEVICE_TABLE(of, gw_pld_dt_ids);

> +

> +static struct i2c_driver gw_pld_driver = {

> +       .driver = {

> +               .name = "gw_pld",

> +               .of_match_table = gw_pld_dt_ids,

> +       },

> +       .probe = gw_pld_probe,

> +       .id_table = gw_pld_id,

> +};

> +module_i2c_driver(gw_pld_driver);

> +

> +MODULE_LICENSE("GPL");

> +MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");

> --

> 2.20.1

>


Best regards,
Bartosz Golaszewski

[1] https://lkml.org/lkml/2017/11/2/715
Linus Walleij Feb. 1, 2019, 8:37 p.m. UTC | #2
Hi Bartosz,

thanks for your review!

On Thu, Jan 31, 2019 at 9:28 AM Bartosz Golaszewski
<bgolaszewski@baylibre.com> wrote:

> a couple remarks below.


Fixed most! One comment:

> > +static int gw_pld_input8(struct gpio_chip *gc, unsigned offset)

> > +{

> > +       struct gw_pld *gw = gpiochip_get_data(gc);

> > +

> > +       gw->out |= (1 << offset);

> > +

> > +       return i2c_smbus_write_byte(gw->client, gw->out);

>

> May I suggest using the i2c regmap for that? You wouldn't even need to

> keep track of the out variable - you could use the

> regmap_update_bits() helper.


I can't (AFAICT) beacuse, i2c regmap is for registers, which have
register offsets. It accesses the devices with two standard transfers,
one telling the register, and the second doing the actual byte read or
write.

This thing has only one read and one write register so it doesn't
send any register offset, just the value.

So I doubt I can make it any simpler. I realized I forgot to use the
BIT() macros that I usually insist on myself though.

Yours,
Linus Walleij
diff mbox series

Patch

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 77785f4b94bb..fe4a47e49a24 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -842,6 +842,13 @@  config GPIO_ADNP
 	  enough to represent all pins, but the driver will assume a
 	  register layout for 64 pins (8 registers).
 
+config GPIO_GW_PLD
+	tristate "Gateworks PLD GPIO Expander"
+	depends on OF_GPIO
+	help
+	  Say yes here to provide access to the Gateworks I2C PLD GPIO
+	  Expander. This is used at least on the Cambria GW2358-4.
+
 config GPIO_MAX7300
 	tristate "Maxim MAX7300 GPIO expander"
 	select GPIO_MAX730X
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 289ad2727bef..cf66523b5eec 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -55,6 +55,7 @@  obj-$(CONFIG_GPIO_FTGPIO010)	+= gpio-ftgpio010.o
 obj-$(CONFIG_GPIO_GE_FPGA)	+= gpio-ge.o
 obj-$(CONFIG_GPIO_GPIO_MM)	+= gpio-gpio-mm.o
 obj-$(CONFIG_GPIO_GRGPIO)	+= gpio-grgpio.o
+obj-$(CONFIG_GPIO_GW_PLD)	+= gpio-gw-pld.o
 obj-$(CONFIG_GPIO_HLWD)		+= gpio-hlwd.o
 obj-$(CONFIG_HTC_EGPIO)		+= gpio-htc-egpio.o
 obj-$(CONFIG_GPIO_ICH)		+= gpio-ich.o
diff --git a/drivers/gpio/gpio-gw-pld.c b/drivers/gpio/gpio-gw-pld.c
new file mode 100644
index 000000000000..7ba8dd102f5a
--- /dev/null
+++ b/drivers/gpio/gpio-gw-pld.c
@@ -0,0 +1,137 @@ 
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Gateworks I2C PLD GPIO expander
+ *
+ * Copyright (C) 2019 Linus Walleij <linus.walleij@linaro.org>
+ *
+ * Based on code and know-how from the OpenWrt driver:
+ * Copyright (C) 2009 Gateworks Corporation
+ * Authors: Chris Lang, Imre Kaloz
+ */
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/gpio/driver.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+
+/**
+ * struct gw_pld - State container for Gateworks PLD
+ * @chip: GPIO chip instance
+ * @client: I2C client
+ * @out: shadow register for the output bute
+ */
+struct gw_pld {
+	struct gpio_chip chip;
+	struct i2c_client *client;
+	unsigned out;
+};
+
+/*
+ * The Gateworks I2C PLD chip only expose one read and one write register.
+ * Writing a "one" bit (to match the reset state) lets that pin be used as an
+ * input. It is an open-drain model.
+ */
+static int gw_pld_input8(struct gpio_chip *gc, unsigned offset)
+{
+	struct gw_pld *gw = gpiochip_get_data(gc);
+
+	gw->out |= (1 << offset);
+
+	return i2c_smbus_write_byte(gw->client, gw->out);
+}
+
+static int gw_pld_get8(struct gpio_chip *gc, unsigned offset)
+{
+	struct gw_pld *gw = gpiochip_get_data(gc);
+	s32 value;
+
+	value = i2c_smbus_read_byte(gw->client);
+
+	return (value < 0) ? 0 : (value & (1 << offset));
+}
+
+static int gw_pld_output8(struct gpio_chip *gc, unsigned offset, int value)
+{
+	struct gw_pld *gw = gpiochip_get_data(gc);
+	unsigned bit = 1 << offset;
+
+	if (value)
+		gw->out |= bit;
+	else
+		gw->out &= ~bit;
+
+	return i2c_smbus_write_byte(gw->client, gw->out);
+}
+
+static void gw_pld_set8(struct gpio_chip *gc, unsigned offset, int value)
+{
+	gw_pld_output8(gc, offset, value);
+}
+
+static int gw_pld_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct device *dev = &client->dev;
+	struct device_node *np = dev->of_node;
+	struct gw_pld *gw;
+	int ret;
+
+	gw = devm_kzalloc(dev, sizeof(*gw), GFP_KERNEL);
+	if (!gw)
+		return -ENOMEM;
+
+	gw->chip.base = -1;
+	gw->chip.can_sleep = true;
+	gw->chip.parent = dev;
+	gw->chip.of_node = np;
+	gw->chip.owner = THIS_MODULE;
+	gw->chip.label = dev_name(dev);
+	gw->chip.ngpio = 8;
+	gw->chip.direction_input = gw_pld_input8;
+	gw->chip.get = gw_pld_get8;
+	gw->chip.direction_output = gw_pld_output8;
+	gw->chip.set = gw_pld_set8;
+	gw->client = client;
+
+	/*
+	 * The Gateworks I2C PLD chip does not properly send the acknowledge
+	 * bit at all times, but we can still use the standard i2c_smbus
+	 * functions by simply ignoring this bit.
+	 */
+	client->flags |= I2C_M_IGNORE_NAK;
+	gw->out = 0xFF;
+
+	i2c_set_clientdata(client, gw);
+
+	ret = devm_gpiochip_add_data(dev, &gw->chip, gw);
+	if (ret)
+		return ret;
+
+	dev_info(dev, "registered Gateworks PLD GPIO device\n");
+
+	return 0;
+}
+
+static const struct i2c_device_id gw_pld_id[] = {
+	{ "gw-pld", },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, gw_pld_id);
+
+static const struct of_device_id gw_pld_dt_ids[] = {
+	{ .compatible = "gateworks,pld-gpio", },
+};
+MODULE_DEVICE_TABLE(of, gw_pld_dt_ids);
+
+static struct i2c_driver gw_pld_driver = {
+	.driver = {
+		.name = "gw_pld",
+		.of_match_table = gw_pld_dt_ids,
+	},
+	.probe = gw_pld_probe,
+	.id_table = gw_pld_id,
+};
+module_i2c_driver(gw_pld_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");