diff mbox

[07/10] gpio: ptxpmb-cpld: Add support for PTXPMB CPLD's GPIO

Message ID 1475853451-22121-8-git-send-email-pantelis.antoniou@konsulko.com
State Not Applicable
Headers show

Commit Message

Pantelis Antoniou Oct. 7, 2016, 3:17 p.m. UTC
From: Guenter Roeck <groeck@juniper.net>

Support the GPIO block which is located in PTXPMB CPLDs
on relevant Juniper platforms.

Signed-off-by: Georgi Vlaev <gvlaev@juniper.net>
Signed-off-by: Guenter Roeck <groeck@juniper.net>
Signed-off-by: Rajat Jain <rajatjain@juniper.net>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
---
 drivers/gpio/Kconfig            |  11 +++
 drivers/gpio/Makefile           |   1 +
 drivers/gpio/gpio-ptxpmb-cpld.c | 177 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 189 insertions(+)
 create mode 100644 drivers/gpio/gpio-ptxpmb-cpld.c

Comments

Linus Walleij Oct. 20, 2016, 10:24 p.m. UTC | #1
On Fri, Oct 7, 2016 at 5:17 PM, Pantelis Antoniou
<pantelis.antoniou@konsulko.com> wrote:

> From: Guenter Roeck <groeck@juniper.net>
>
> Support the GPIO block which is located in PTXPMB CPLDs
> on relevant Juniper platforms.
>
> Signed-off-by: Georgi Vlaev <gvlaev@juniper.net>
> Signed-off-by: Guenter Roeck <groeck@juniper.net>
> Signed-off-by: Rajat Jain <rajatjain@juniper.net>
> [Ported from Juniper kernel]
> Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>

Nice with preserved credits.

> +config GPIO_PTXPMB_CPLD
> +       tristate "PTXPMB CPLD GPIO"
> +       depends on MFD_JUNIPER_CPLD
> +       default y if MFD_JUNIPER_CPLD

Can't you do something like

default MFD_JUNIPER_CPLD

so it will become a module if the MFD driver is a module
and compiled-in if the MFD driver is compiled in?

Please check if that works.

> +#include <linux/kernel.h>
> +#include <linux/init.h>
> +#include <linux/pci.h>
> +#include <linux/gpio.h>

Just:
#include <linux/gpio/driver.h>

Should work.

> +#include <linux/errno.h>
> +#include <linux/of_device.h>
> +#include <linux/of_platform.h>
> +#include <linux/of_gpio.h>

Strange that a PCI driver need so much OF stuff.
Really? But OK, do you really use all these includes?

> +#include <linux/io.h>
> +#include <linux/module.h>
> +
> +#include <linux/mfd/ptxpmb_cpld.h>

Is this include deserving a separate whitespace in
front of it?

> +static u8 *ptxpmb_cpld_gpio_get_addr(struct pmb_boot_cpld *cpld,
> +                                    unsigned int nr)
> +{
> +       if (nr < 8)                     /* 0..7: reset                  */
> +               return &cpld->reset;
> +       else if (nr < 16)               /* 8..15: control               */
> +               return &cpld->control;
> +       else if (nr < 24)               /* 16..23: gpio1                */
> +               return &cpld->gpio_1;
> +       else if (nr < 32)               /* 24..31: gpio2                */
> +               return &cpld->gpio_2;
> +       else if (nr < 40)               /* 32..39: gp_reset1            */
> +               return &cpld->gp_reset1;
> +       return &cpld->thermal_status;   /* 40..41: thermal status       */
> +}

Reset, control, gp_reset and *especially* thermal status
does not seem to be GPIO at all. Rather these seem to be
special purpose lines rather than general purpose.

Can you explain why the GPIO driver should even poke at them?

It seems other subdrivers should use these registers...

> +static void ptxpmb_cpld_gpio_set(struct gpio_chip *gpio, unsigned int nr,
> +                                int val)
> +{
> +       u32 reg;
> +       u8 bit = 1 << (nr & 7);

Use this:

#include <linux/bitops.h>

Then inline BIT() instead of making a local variable like this.
See below...

> +       struct ptxpmb_cpld_gpio *chip =
> +         container_of(gpio, struct ptxpmb_cpld_gpio, gpio);

Use:
struct ptxpmb_cpld_gpio *cpldg = gpiochip_get_data(gpio);

Please don't name it "chip" it is confusing with the gpio chip
proper.

> +       u8 *addr = ptxpmb_cpld_gpio_get_addr(chip->base, nr);
> +
> +       mutex_lock(&chip->lock);
> +       reg = ioread8(addr);
> +       if (val)
> +               reg |= bit;
> +       else
> +               reg &= ~bit;

So instead:
if (val)
    reg |= BIT(nr);
else
    reg &= ~BIT(nr);

> +static int ptxpmb_cpld_gpio_get(struct gpio_chip *gpio, unsigned int nr)
> +{
> +       struct ptxpmb_cpld_gpio *chip = container_of(gpio,
> +                                                    struct ptxpmb_cpld_gpio,
> +                                                    gpio);

Same comment as before.

> +       u8 *addr = ptxpmb_cpld_gpio_get_addr(chip->base, nr);
> +       u8 bit = 1 << (nr & 7);
> +
> +       return !!(ioread8(addr) & bit);

Same comment on using BIT()

> +static int ptxpmb_cpld_gpio_direction_output(struct gpio_chip *gpio,
> +                                            unsigned int nr, int val)
> +{
> +       return 0;
> +}
> +
> +static int ptxpmb_cpld_gpio_direction_input(struct gpio_chip *gpio,
> +                                           unsigned int nr)
> +{
> +       return 0;
> +}

Oh yeah? Can you explain how this works electronically?

If these lines are open drain you should also implement
.set_single_ended().

> +static void ptxpmb_cpld_gpio_setup(struct ptxpmb_cpld_gpio *chip)

Again rename from chip.

> +static int ptxpmb_cpld_gpio_probe(struct platform_device *pdev)
> +{
> +       int ret;
> +       struct ptxpmb_cpld_gpio *chip;

Rename from chip.

> +       struct resource *res;
> +       struct device *dev = &pdev->dev;
> +
> +       chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
> +       if (chip == NULL)
> +               return -ENOMEM;
> +
> +       chip->dev = dev;
> +       platform_set_drvdata(pdev, chip);
> +
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       if (!res)
> +               return -ENODEV;
> +
> +       chip->base = devm_ioremap_nocache(dev, res->start, resource_size(res));
> +       if (!chip->base)
> +               return -ENOMEM;
> +
> +       mutex_init(&chip->lock);
> +       ptxpmb_cpld_gpio_setup(chip);
> +       ret = gpiochip_add(&chip->gpio);

Use devm_gpiochip_add_data() so you can use gpiochip_get_data() in the
callbacks and get rid of container_of().

> +       if (ret) {
> +               dev_err(dev, "CPLD gpio: Failed to register GPIO\n");
> +               return ret;
> +       }
> +       return 0;
> +}
> +
> +static int ptxpmb_cpld_gpio_remove(struct platform_device *pdev)
> +{
> +       struct ptxpmb_cpld_gpio *chip = platform_get_drvdata(pdev);
> +
> +       gpiochip_remove(&chip->gpio);
> +
> +       return 0;
> +}

If you use devm_gpiochip_add_data() I don't think this function is
even needed.

Yours,
Linus Walleij
diff mbox

Patch

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 26ee00f..9c91de6 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -360,6 +360,17 @@  config GPIO_PL061
 	help
 	  Say yes here to support the PrimeCell PL061 GPIO device
 
+config GPIO_PTXPMB_CPLD
+	tristate "PTXPMB CPLD GPIO"
+	depends on MFD_JUNIPER_CPLD
+	default y if MFD_JUNIPER_CPLD
+	help
+	  This driver supports the GPIO interfaces on the PTXPMB CPLD which is
+	  present on the relevant Juniper platforms.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called gpio-ptxpmb-cpld.
+
 config GPIO_PXA
 	bool "PXA GPIO support"
 	depends on ARCH_PXA || ARCH_MMP
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index ab28a2d..d397ea5 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -90,6 +90,7 @@  obj-$(CONFIG_GPIO_PCF857X)	+= gpio-pcf857x.o
 obj-$(CONFIG_GPIO_PCH)		+= gpio-pch.o
 obj-$(CONFIG_GPIO_PISOSR)	+= gpio-pisosr.o
 obj-$(CONFIG_GPIO_PL061)	+= gpio-pl061.o
+obj-$(CONFIG_GPIO_PTXPMB_CPLD)	+= gpio-ptxpmb-cpld.o
 obj-$(CONFIG_GPIO_PXA)		+= gpio-pxa.o
 obj-$(CONFIG_GPIO_RC5T583)	+= gpio-rc5t583.o
 obj-$(CONFIG_GPIO_RDC321X)	+= gpio-rdc321x.o
diff --git a/drivers/gpio/gpio-ptxpmb-cpld.c b/drivers/gpio/gpio-ptxpmb-cpld.c
new file mode 100644
index 0000000..e6fab7c
--- /dev/null
+++ b/drivers/gpio/gpio-ptxpmb-cpld.c
@@ -0,0 +1,177 @@ 
+/*
+ * Copyright (C) 2012 Juniper networks
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/gpio.h>
+#include <linux/errno.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
+#include <linux/io.h>
+#include <linux/module.h>
+
+#include <linux/mfd/ptxpmb_cpld.h>
+
+/**
+ * struct ptxpmb_cpld_gpio - GPIO private data structure.
+ * @base:			PCI base address of Memory mapped I/O register.
+ * @dev:			Pointer to device structure.
+ * @gpio:			Data for GPIO infrastructure.
+ */
+struct ptxpmb_cpld_gpio {
+	void __iomem *base;
+	struct device *dev;
+	struct gpio_chip gpio;
+	struct mutex lock;
+};
+
+static u8 *ptxpmb_cpld_gpio_get_addr(struct pmb_boot_cpld *cpld,
+				     unsigned int nr)
+{
+	if (nr < 8)			/* 0..7: reset			*/
+		return &cpld->reset;
+	else if (nr < 16)		/* 8..15: control		*/
+		return &cpld->control;
+	else if (nr < 24)		/* 16..23: gpio1		*/
+		return &cpld->gpio_1;
+	else if (nr < 32)		/* 24..31: gpio2		*/
+		return &cpld->gpio_2;
+	else if (nr < 40)		/* 32..39: gp_reset1		*/
+		return &cpld->gp_reset1;
+	return &cpld->thermal_status;	/* 40..41: thermal status	*/
+}
+
+static void ptxpmb_cpld_gpio_set(struct gpio_chip *gpio, unsigned int nr,
+				 int val)
+{
+	u32 reg;
+	u8 bit = 1 << (nr & 7);
+	struct ptxpmb_cpld_gpio *chip =
+	  container_of(gpio, struct ptxpmb_cpld_gpio, gpio);
+	u8 *addr = ptxpmb_cpld_gpio_get_addr(chip->base, nr);
+
+	mutex_lock(&chip->lock);
+	reg = ioread8(addr);
+	if (val)
+		reg |= bit;
+	else
+		reg &= ~bit;
+
+	iowrite8(reg, addr);
+	mutex_unlock(&chip->lock);
+}
+
+static int ptxpmb_cpld_gpio_get(struct gpio_chip *gpio, unsigned int nr)
+{
+	struct ptxpmb_cpld_gpio *chip = container_of(gpio,
+						     struct ptxpmb_cpld_gpio,
+						     gpio);
+	u8 *addr = ptxpmb_cpld_gpio_get_addr(chip->base, nr);
+	u8 bit = 1 << (nr & 7);
+
+	return !!(ioread8(addr) & bit);
+}
+
+static int ptxpmb_cpld_gpio_direction_output(struct gpio_chip *gpio,
+					     unsigned int nr, int val)
+{
+	return 0;
+}
+
+static int ptxpmb_cpld_gpio_direction_input(struct gpio_chip *gpio,
+					    unsigned int nr)
+{
+	return 0;
+}
+
+static void ptxpmb_cpld_gpio_setup(struct ptxpmb_cpld_gpio *chip)
+{
+	struct gpio_chip *gpio = &chip->gpio;
+
+	gpio->label = dev_name(chip->dev);
+	gpio->owner = THIS_MODULE;
+	gpio->direction_input = ptxpmb_cpld_gpio_direction_input;
+	gpio->get = ptxpmb_cpld_gpio_get;
+	gpio->direction_output = ptxpmb_cpld_gpio_direction_output;
+	gpio->set = ptxpmb_cpld_gpio_set;
+	gpio->dbg_show = NULL;
+	gpio->base = -1;
+	gpio->ngpio = 48;
+	gpio->can_sleep = 0;
+#ifdef CONFIG_OF_GPIO
+	gpio->of_node = chip->dev->of_node;
+#endif
+}
+
+static int ptxpmb_cpld_gpio_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct ptxpmb_cpld_gpio *chip;
+	struct resource *res;
+	struct device *dev = &pdev->dev;
+
+	chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
+	if (chip == NULL)
+		return -ENOMEM;
+
+	chip->dev = dev;
+	platform_set_drvdata(pdev, chip);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -ENODEV;
+
+	chip->base = devm_ioremap_nocache(dev, res->start, resource_size(res));
+	if (!chip->base)
+		return -ENOMEM;
+
+	mutex_init(&chip->lock);
+	ptxpmb_cpld_gpio_setup(chip);
+	ret = gpiochip_add(&chip->gpio);
+	if (ret) {
+		dev_err(dev, "CPLD gpio: Failed to register GPIO\n");
+		return ret;
+	}
+	return 0;
+}
+
+static int ptxpmb_cpld_gpio_remove(struct platform_device *pdev)
+{
+	struct ptxpmb_cpld_gpio *chip = platform_get_drvdata(pdev);
+
+	gpiochip_remove(&chip->gpio);
+
+	return 0;
+}
+
+static const struct of_device_id ptxpmb_cpld_gpio_ids[] = {
+	{ .compatible = "jnx,gpio-ptxpmb-cpld", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, ptxpmb_cpld_gpio_ids);
+
+static struct platform_driver ptxpmb_cpld_gpio_driver = {
+	.driver = {
+		.name = "gpio-ptxpmb-cpld",
+		.owner  = THIS_MODULE,
+		.of_match_table = ptxpmb_cpld_gpio_ids,
+	},
+	.probe = ptxpmb_cpld_gpio_probe,
+	.remove = ptxpmb_cpld_gpio_remove,
+};
+
+module_platform_driver(ptxpmb_cpld_gpio_driver);
+
+MODULE_DESCRIPTION("CPLD FPGA GPIO Driver");
+MODULE_LICENSE("GPL");