diff mbox

[U-Boot,15/23] gpio: eg20t: Add driver for Intel EG20T GPIO controllers

Message ID 20160926182917.27531-16-paul.burton@imgtec.com
State Deferred
Delegated to: Daniel Schwierzeck
Headers show

Commit Message

Paul Burton Sept. 26, 2016, 6:29 p.m. UTC
Add a driver for the GPIO controller found in the Intel EG20T Platform
Controller Hub. This is used on the MIPS Boston development board to
provide GPIOs including ethernet PHY reset.

Signed-off-by: Paul Burton <paul.burton@imgtec.com>

---

 drivers/gpio/Kconfig      |   8 +++
 drivers/gpio/Makefile     |   1 +
 drivers/gpio/eg20t-gpio.c | 133 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 142 insertions(+)
 create mode 100644 drivers/gpio/eg20t-gpio.c

Comments

Simon Glass Sept. 27, 2016, 12:35 a.m. UTC | #1
Hi Paul,

On 26 September 2016 at 12:29, Paul Burton <paul.burton@imgtec.com> wrote:
> Add a driver for the GPIO controller found in the Intel EG20T Platform
> Controller Hub. This is used on the MIPS Boston development board to
> provide GPIOs including ethernet PHY reset.
>
> Signed-off-by: Paul Burton <paul.burton@imgtec.com>
>
> ---
>
>  drivers/gpio/Kconfig      |   8 +++
>  drivers/gpio/Makefile     |   1 +
>  drivers/gpio/eg20t-gpio.c | 133 ++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 142 insertions(+)
>  create mode 100644 drivers/gpio/eg20t-gpio.c
>
> diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
> index 8d9ab52..4a6a22f 100644
> --- a/drivers/gpio/Kconfig
> +++ b/drivers/gpio/Kconfig
> @@ -221,4 +221,12 @@ config MPC85XX_GPIO
>
>           The driver has been tested on MPC85XX, but it is likely that other
>           PowerQUICC III devices will work as well.
> +
> +config EG20T_GPIO
> +       bool "Intel EG20T GPIO driver"
> +       depends on DM_GPIO && DM_PCI
> +       help
> +         Enable this to support the GPIO controller found in the Intel EG20T
> +         Platform Controller Hub.

Can you add a few more details? How many GPIOs? Bank organisation?
Pull-up/down support?

> +
>  endmenu
> diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
> index 8939226..a94aeb1 100644
> --- a/drivers/gpio/Makefile
> +++ b/drivers/gpio/Makefile
> @@ -58,3 +58,4 @@ obj-$(CONFIG_MVEBU_GPIO)      += mvebu_gpio.o
>  obj-$(CONFIG_MSM_GPIO)         += msm_gpio.o
>  obj-$(CONFIG_$(SPL_)PCF8575_GPIO)      += pcf8575_gpio.o
>  obj-$(CONFIG_PM8916_GPIO)      += pm8916_gpio.o
> +obj-$(CONFIG_EG20T_GPIO)       += eg20t-gpio.o
> diff --git a/drivers/gpio/eg20t-gpio.c b/drivers/gpio/eg20t-gpio.c
> new file mode 100644
> index 0000000..05db771
> --- /dev/null
> +++ b/drivers/gpio/eg20t-gpio.c
> @@ -0,0 +1,133 @@
> +/*
> + * Copyright (C) 2016 Imagination Technologies
> + *
> + * SPDX-License-Identifier:    GPL-2.0
> + */
> +
> +#include <common.h>
> +#include <dm.h>
> +#include <errno.h>
> +#include <pci.h>
> +#include <asm/io.h>
> +#include <asm/gpio.h>
> +
> +enum {
> +       REG_IEN         = 0x00,
> +       REG_ISTATUS     = 0x04,
> +       REG_IDISP       = 0x08,
> +       REG_ICLR        = 0x0c,
> +       REG_IMASK       = 0x10,
> +       REG_IMASKCLR    = 0x14,
> +       REG_PO          = 0x18,
> +       REG_PI          = 0x1c,
> +       REG_PM          = 0x20,

How about using a struct?

> +};
> +
> +struct eg20t_gpio_priv {
> +       void *base;
> +};
> +
> +static int eg20t_gpio_get_value(struct udevice *dev, unsigned int offset)
> +{
> +       struct eg20t_gpio_priv *priv = dev_get_priv(dev);
> +       uint32_t pm, pval;
> +
> +       pm = readl(priv->base + REG_PM);
> +       if ((pm >> offset) & 0x1)
> +               pval = readl(priv->base + REG_PO);
> +       else
> +               pval = readl(priv->base + REG_PI);
> +
> +       return (pval >> offset) & 0x1;
> +}
> +
> +static int eg20t_gpio_set_value(struct udevice *dev, unsigned int offset,
> +                               int value)
> +{
> +       struct eg20t_gpio_priv *priv = dev_get_priv(dev);
> +       uint32_t po;
> +
> +       po = readl(priv->base + REG_PO);
> +       if (value)
> +               po |= 1 << offset;
> +       else
> +               po &= ~(1 << offset);
> +       writel(po, priv->base + REG_PO);
> +       return 0;
> +}
> +
> +static int eg20t_gpio_direction_input(struct udevice *dev, unsigned int offset)
> +{
> +       struct eg20t_gpio_priv *priv = dev_get_priv(dev);
> +       uint32_t pm;
> +
> +       pm = readl(priv->base + REG_PM);
> +       pm &= ~(1 << offset);
> +       writel(pm, priv->base + REG_PM);

clrbits_le32(ADDR, 1 << OFFSET);

> +       return 0;
> +}
> +
> +static int eg20t_gpio_direction_output(struct udevice *dev, unsigned int offset,
> +                                      int value)
> +{
> +       struct eg20t_gpio_priv *priv = dev_get_priv(dev);
> +       uint32_t pm;
> +
> +       pm = readl(priv->base + REG_PM);
> +       pm |= 1 << offset;
> +       writel(pm, priv->base + REG_PM);

setbits_le32()

> +
> +       return eg20t_gpio_set_value(dev, offset, value);
> +}
> +
> +static int eg20t_gpio_get_function(struct udevice *dev, unsigned int offset)
> +{
> +       struct eg20t_gpio_priv *priv = dev_get_priv(dev);
> +       uint32_t pm;
> +
> +       pm = readl(priv->base + REG_PM);
> +
> +       if ((pm >> offset) & 0x1)
> +               return GPIOF_OUTPUT;
> +
> +       return GPIOF_INPUT;

Can it have pins which are GPIOF_FUNC?

> +}
> +
> +static const struct dm_gpio_ops eg20t_gpio_ops = {
> +       .direction_input        = eg20t_gpio_direction_input,
> +       .direction_output       = eg20t_gpio_direction_output,
> +       .get_value              = eg20t_gpio_get_value,
> +       .set_value              = eg20t_gpio_set_value,
> +       .get_function           = eg20t_gpio_get_function,
> +};
> +
> +static int eg20t_gpio_probe(struct udevice *dev)
> +{
> +       struct eg20t_gpio_priv *priv = dev_get_priv(dev);
> +       struct gpio_dev_priv *uc_priv = dev->uclass_priv;
> +
> +       priv->base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_1, PCI_REGION_MEM);
> +       if (!priv->base) {
> +               debug("failed to map GPIO registers\n");
> +               return -EINVAL;

How about -ENOMEM or -ENXIO

> +       }
> +
> +       uc_priv->gpio_count = 12;
> +       uc_priv->bank_name = "eg20t";
> +       return 0;
> +}
> +
> +U_BOOT_DRIVER(eg20t_gpio) = {
> +       .name   = "eg20t-gpio",
> +       .id     = UCLASS_GPIO,
> +       .probe  = eg20t_gpio_probe,
> +       .priv_auto_alloc_size = sizeof(struct eg20t_gpio_priv),
> +       .ops    = &eg20t_gpio_ops,
> +};
> +
> +static struct pci_device_id eg20t_gpio_supported[] = {
> +       { PCI_VENDOR_ID_INTEL, 0x8803 },
> +       { },
> +};
> +
> +U_BOOT_PCI_DEVICE(eg20t_gpio, eg20t_gpio_supported);
> --
> 2.10.0
>

Regards,
Simon
Bin Meng Sept. 27, 2016, 6:22 a.m. UTC | #2
Hi Paul,

On Tue, Sep 27, 2016 at 2:29 AM, Paul Burton <paul.burton@imgtec.com> wrote:
> Add a driver for the GPIO controller found in the Intel EG20T Platform
> Controller Hub. This is used on the MIPS Boston development board to
> provide GPIOs including ethernet PHY reset.
>
> Signed-off-by: Paul Burton <paul.burton@imgtec.com>
>
> ---
>
>  drivers/gpio/Kconfig      |   8 +++
>  drivers/gpio/Makefile     |   1 +
>  drivers/gpio/eg20t-gpio.c | 133 ++++++++++++++++++++++++++++++++++++++++++++++

Please use eg20t_gpio.c as the filename, like others do.

>  3 files changed, 142 insertions(+)
>  create mode 100644 drivers/gpio/eg20t-gpio.c
>
> diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
> index 8d9ab52..4a6a22f 100644
> --- a/drivers/gpio/Kconfig
> +++ b/drivers/gpio/Kconfig
> @@ -221,4 +221,12 @@ config MPC85XX_GPIO
>
>           The driver has been tested on MPC85XX, but it is likely that other
>           PowerQUICC III devices will work as well.
> +
> +config EG20T_GPIO
> +       bool "Intel EG20T GPIO driver"
> +       depends on DM_GPIO && DM_PCI
> +       help
> +         Enable this to support the GPIO controller found in the Intel EG20T
> +         Platform Controller Hub.
> +
>  endmenu
> diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
> index 8939226..a94aeb1 100644
> --- a/drivers/gpio/Makefile
> +++ b/drivers/gpio/Makefile
> @@ -58,3 +58,4 @@ obj-$(CONFIG_MVEBU_GPIO)      += mvebu_gpio.o
>  obj-$(CONFIG_MSM_GPIO)         += msm_gpio.o
>  obj-$(CONFIG_$(SPL_)PCF8575_GPIO)      += pcf8575_gpio.o
>  obj-$(CONFIG_PM8916_GPIO)      += pm8916_gpio.o
> +obj-$(CONFIG_EG20T_GPIO)       += eg20t-gpio.o
> diff --git a/drivers/gpio/eg20t-gpio.c b/drivers/gpio/eg20t-gpio.c
> new file mode 100644
> index 0000000..05db771
> --- /dev/null
> +++ b/drivers/gpio/eg20t-gpio.c
> @@ -0,0 +1,133 @@
> +/*
> + * Copyright (C) 2016 Imagination Technologies
> + *
> + * SPDX-License-Identifier:    GPL-2.0
> + */
> +
> +#include <common.h>
> +#include <dm.h>
> +#include <errno.h>
> +#include <pci.h>
> +#include <asm/io.h>
> +#include <asm/gpio.h>
> +
> +enum {
> +       REG_IEN         = 0x00,
> +       REG_ISTATUS     = 0x04,
> +       REG_IDISP       = 0x08,
> +       REG_ICLR        = 0x0c,
> +       REG_IMASK       = 0x10,
> +       REG_IMASKCLR    = 0x14,
> +       REG_PO          = 0x18,
> +       REG_PI          = 0x1c,
> +       REG_PM          = 0x20,
> +};
> +

Please use a struct.

> +struct eg20t_gpio_priv {
> +       void *base;
> +};
> +
> +static int eg20t_gpio_get_value(struct udevice *dev, unsigned int offset)
> +{
> +       struct eg20t_gpio_priv *priv = dev_get_priv(dev);
> +       uint32_t pm, pval;
> +
> +       pm = readl(priv->base + REG_PM);
> +       if ((pm >> offset) & 0x1)
> +               pval = readl(priv->base + REG_PO);
> +       else
> +               pval = readl(priv->base + REG_PI);
> +
> +       return (pval >> offset) & 0x1;
> +}
> +
> +static int eg20t_gpio_set_value(struct udevice *dev, unsigned int offset,
> +                               int value)
> +{
> +       struct eg20t_gpio_priv *priv = dev_get_priv(dev);
> +       uint32_t po;
> +
> +       po = readl(priv->base + REG_PO);
> +       if (value)
> +               po |= 1 << offset;
> +       else
> +               po &= ~(1 << offset);
> +       writel(po, priv->base + REG_PO);

nits: need a blank line.

> +       return 0;
> +}
> +
> +static int eg20t_gpio_direction_input(struct udevice *dev, unsigned int offset)
> +{
> +       struct eg20t_gpio_priv *priv = dev_get_priv(dev);
> +       uint32_t pm;
> +
> +       pm = readl(priv->base + REG_PM);
> +       pm &= ~(1 << offset);
> +       writel(pm, priv->base + REG_PM);

nits: need a blank line.

> +       return 0;
> +}
> +
> +static int eg20t_gpio_direction_output(struct udevice *dev, unsigned int offset,
> +                                      int value)
> +{
> +       struct eg20t_gpio_priv *priv = dev_get_priv(dev);
> +       uint32_t pm;
> +
> +       pm = readl(priv->base + REG_PM);
> +       pm |= 1 << offset;
> +       writel(pm, priv->base + REG_PM);
> +
> +       return eg20t_gpio_set_value(dev, offset, value);
> +}
> +
> +static int eg20t_gpio_get_function(struct udevice *dev, unsigned int offset)
> +{
> +       struct eg20t_gpio_priv *priv = dev_get_priv(dev);
> +       uint32_t pm;
> +
> +       pm = readl(priv->base + REG_PM);
> +
> +       if ((pm >> offset) & 0x1)
> +               return GPIOF_OUTPUT;
> +
> +       return GPIOF_INPUT;
> +}
> +
> +static const struct dm_gpio_ops eg20t_gpio_ops = {
> +       .direction_input        = eg20t_gpio_direction_input,
> +       .direction_output       = eg20t_gpio_direction_output,
> +       .get_value              = eg20t_gpio_get_value,
> +       .set_value              = eg20t_gpio_set_value,
> +       .get_function           = eg20t_gpio_get_function,
> +};
> +
> +static int eg20t_gpio_probe(struct udevice *dev)
> +{
> +       struct eg20t_gpio_priv *priv = dev_get_priv(dev);
> +       struct gpio_dev_priv *uc_priv = dev->uclass_priv;
> +
> +       priv->base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_1, PCI_REGION_MEM);
> +       if (!priv->base) {
> +               debug("failed to map GPIO registers\n");
> +               return -EINVAL;
> +       }
> +
> +       uc_priv->gpio_count = 12;
> +       uc_priv->bank_name = "eg20t";

nits: need a blank line.

> +       return 0;
> +}
> +
> +U_BOOT_DRIVER(eg20t_gpio) = {
> +       .name   = "eg20t-gpio",
> +       .id     = UCLASS_GPIO,
> +       .probe  = eg20t_gpio_probe,
> +       .priv_auto_alloc_size = sizeof(struct eg20t_gpio_priv),
> +       .ops    = &eg20t_gpio_ops,
> +};
> +
> +static struct pci_device_id eg20t_gpio_supported[] = {
> +       { PCI_VENDOR_ID_INTEL, 0x8803 },
> +       { },
> +};
> +
> +U_BOOT_PCI_DEVICE(eg20t_gpio, eg20t_gpio_supported);
> --

Regards,
Bin
Paul Burton Sept. 27, 2016, 8:21 a.m. UTC | #3
On Monday, 26 September 2016 18:35:32 BST Simon Glass wrote:
> > +enum {
> > +       REG_IEN         = 0x00,
> > +       REG_ISTATUS     = 0x04,
> > +       REG_IDISP       = 0x08,
> > +       REG_ICLR        = 0x0c,
> > +       REG_IMASK       = 0x10,
> > +       REG_IMASKCLR    = 0x14,
> > +       REG_PO          = 0x18,
> > +       REG_PI          = 0x1c,
> > +       REG_PM          = 0x20,
> 
> How about using a struct?

Hi Simon,

Interesting - I was told over here[1] that using structs for register access was "deprecated".

[1] https://www.mail-archive.com/u-boot@lists.denx.de/msg220500.html[1]

...so I actually converted the driver away from that style before submitting. Yet both 
yourself & Bin have suggested it - shall I take it that it's not deprecated after all?

Thanks,
    Paul

--------
[1] https://www.mail-archive.com/u-boot@lists.denx.de/msg220500.html
Simon Glass Sept. 27, 2016, 10:17 p.m. UTC | #4
Hi Paul,

On 27 September 2016 at 02:21, Paul Burton <paul.burton@imgtec.com> wrote:
> On Monday, 26 September 2016 18:35:32 BST Simon Glass wrote:
>
>> > +enum {
>
>> > + REG_IEN = 0x00,
>
>> > + REG_ISTATUS = 0x04,
>
>> > + REG_IDISP = 0x08,
>
>> > + REG_ICLR = 0x0c,
>
>> > + REG_IMASK = 0x10,
>
>> > + REG_IMASKCLR = 0x14,
>
>> > + REG_PO = 0x18,
>
>> > + REG_PI = 0x1c,
>
>> > + REG_PM = 0x20,
>
>>
>
>> How about using a struct?
>
>
>
> Hi Simon,
>
>
>
> Interesting - I was told over here[1] that using structs for register access
> was "deprecated".
>
>
>
> [1] https://www.mail-archive.com/u-boot@lists.denx.de/msg220500.html
>
>
>
> ...so I actually converted the driver away from that style before
> submitting. Yet both yourself & Bin have suggested it - shall I take it that
> it's not deprecated after all?

Well I try to use it when possible, as does Bin. It reads better and
we may as well use the features of C. It can't be used if register
offsets differ (e.g. a driver needs to deal with registers being 4
bytes apart on one machine and 8 on another). But in general I think
it is preferable to offsets.

Regards,
Simon
Bin Meng Sept. 28, 2016, 9 a.m. UTC | #5
On Tue, Sep 27, 2016 at 2:22 PM, Bin Meng <bmeng.cn@gmail.com> wrote:
> Hi Paul,
>
> On Tue, Sep 27, 2016 at 2:29 AM, Paul Burton <paul.burton@imgtec.com> wrote:
>> Add a driver for the GPIO controller found in the Intel EG20T Platform
>> Controller Hub. This is used on the MIPS Boston development board to
>> provide GPIOs including ethernet PHY reset.
>>
>> Signed-off-by: Paul Burton <paul.burton@imgtec.com>
>>
>> ---
>>
>>  drivers/gpio/Kconfig      |   8 +++
>>  drivers/gpio/Makefile     |   1 +
>>  drivers/gpio/eg20t-gpio.c | 133 ++++++++++++++++++++++++++++++++++++++++++++++
>
> Please use eg20t_gpio.c as the filename, like others do.
>
>>  3 files changed, 142 insertions(+)
>>  create mode 100644 drivers/gpio/eg20t-gpio.c
>>
>> diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
>> index 8d9ab52..4a6a22f 100644
>> --- a/drivers/gpio/Kconfig
>> +++ b/drivers/gpio/Kconfig
>> @@ -221,4 +221,12 @@ config MPC85XX_GPIO
>>
>>           The driver has been tested on MPC85XX, but it is likely that other
>>           PowerQUICC III devices will work as well.
>> +
>> +config EG20T_GPIO
>> +       bool "Intel EG20T GPIO driver"
>> +       depends on DM_GPIO && DM_PCI
>> +       help
>> +         Enable this to support the GPIO controller found in the Intel EG20T
>> +         Platform Controller Hub.
>> +
>>  endmenu
>> diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
>> index 8939226..a94aeb1 100644
>> --- a/drivers/gpio/Makefile
>> +++ b/drivers/gpio/Makefile
>> @@ -58,3 +58,4 @@ obj-$(CONFIG_MVEBU_GPIO)      += mvebu_gpio.o
>>  obj-$(CONFIG_MSM_GPIO)         += msm_gpio.o
>>  obj-$(CONFIG_$(SPL_)PCF8575_GPIO)      += pcf8575_gpio.o
>>  obj-$(CONFIG_PM8916_GPIO)      += pm8916_gpio.o
>> +obj-$(CONFIG_EG20T_GPIO)       += eg20t-gpio.o
>> diff --git a/drivers/gpio/eg20t-gpio.c b/drivers/gpio/eg20t-gpio.c
>> new file mode 100644
>> index 0000000..05db771
>> --- /dev/null
>> +++ b/drivers/gpio/eg20t-gpio.c
>> @@ -0,0 +1,133 @@
>> +/*
>> + * Copyright (C) 2016 Imagination Technologies
>> + *
>> + * SPDX-License-Identifier:    GPL-2.0
>> + */
>> +
>> +#include <common.h>
>> +#include <dm.h>
>> +#include <errno.h>
>> +#include <pci.h>
>> +#include <asm/io.h>
>> +#include <asm/gpio.h>
>> +
>> +enum {
>> +       REG_IEN         = 0x00,
>> +       REG_ISTATUS     = 0x04,
>> +       REG_IDISP       = 0x08,
>> +       REG_ICLR        = 0x0c,
>> +       REG_IMASK       = 0x10,
>> +       REG_IMASKCLR    = 0x14,
>> +       REG_PO          = 0x18,
>> +       REG_PI          = 0x1c,
>> +       REG_PM          = 0x20,
>> +};
>> +
>
> Please use a struct.
>
>> +struct eg20t_gpio_priv {
>> +       void *base;
>> +};
>> +
>> +static int eg20t_gpio_get_value(struct udevice *dev, unsigned int offset)
>> +{
>> +       struct eg20t_gpio_priv *priv = dev_get_priv(dev);
>> +       uint32_t pm, pval;
>> +
>> +       pm = readl(priv->base + REG_PM);
>> +       if ((pm >> offset) & 0x1)
>> +               pval = readl(priv->base + REG_PO);
>> +       else
>> +               pval = readl(priv->base + REG_PI);
>> +
>> +       return (pval >> offset) & 0x1;
>> +}
>> +
>> +static int eg20t_gpio_set_value(struct udevice *dev, unsigned int offset,
>> +                               int value)
>> +{
>> +       struct eg20t_gpio_priv *priv = dev_get_priv(dev);
>> +       uint32_t po;
>> +
>> +       po = readl(priv->base + REG_PO);
>> +       if (value)
>> +               po |= 1 << offset;
>> +       else
>> +               po &= ~(1 << offset);
>> +       writel(po, priv->base + REG_PO);
>
> nits: need a blank line.
>
>> +       return 0;
>> +}
>> +
>> +static int eg20t_gpio_direction_input(struct udevice *dev, unsigned int offset)
>> +{
>> +       struct eg20t_gpio_priv *priv = dev_get_priv(dev);
>> +       uint32_t pm;
>> +
>> +       pm = readl(priv->base + REG_PM);
>> +       pm &= ~(1 << offset);
>> +       writel(pm, priv->base + REG_PM);
>
> nits: need a blank line.
>
>> +       return 0;
>> +}
>> +
>> +static int eg20t_gpio_direction_output(struct udevice *dev, unsigned int offset,
>> +                                      int value)
>> +{
>> +       struct eg20t_gpio_priv *priv = dev_get_priv(dev);
>> +       uint32_t pm;
>> +
>> +       pm = readl(priv->base + REG_PM);
>> +       pm |= 1 << offset;
>> +       writel(pm, priv->base + REG_PM);
>> +
>> +       return eg20t_gpio_set_value(dev, offset, value);
>> +}
>> +
>> +static int eg20t_gpio_get_function(struct udevice *dev, unsigned int offset)
>> +{
>> +       struct eg20t_gpio_priv *priv = dev_get_priv(dev);
>> +       uint32_t pm;
>> +
>> +       pm = readl(priv->base + REG_PM);
>> +
>> +       if ((pm >> offset) & 0x1)
>> +               return GPIOF_OUTPUT;
>> +
>> +       return GPIOF_INPUT;
>> +}
>> +
>> +static const struct dm_gpio_ops eg20t_gpio_ops = {
>> +       .direction_input        = eg20t_gpio_direction_input,
>> +       .direction_output       = eg20t_gpio_direction_output,
>> +       .get_value              = eg20t_gpio_get_value,
>> +       .set_value              = eg20t_gpio_set_value,
>> +       .get_function           = eg20t_gpio_get_function,
>> +};
>> +
>> +static int eg20t_gpio_probe(struct udevice *dev)
>> +{
>> +       struct eg20t_gpio_priv *priv = dev_get_priv(dev);
>> +       struct gpio_dev_priv *uc_priv = dev->uclass_priv;
>> +
>> +       priv->base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_1, PCI_REGION_MEM);
>> +       if (!priv->base) {
>> +               debug("failed to map GPIO registers\n");
>> +               return -EINVAL;
>> +       }
>> +
>> +       uc_priv->gpio_count = 12;
>> +       uc_priv->bank_name = "eg20t";
>
> nits: need a blank line.
>
>> +       return 0;
>> +}
>> +
>> +U_BOOT_DRIVER(eg20t_gpio) = {
>> +       .name   = "eg20t-gpio",
>> +       .id     = UCLASS_GPIO,
>> +       .probe  = eg20t_gpio_probe,
>> +       .priv_auto_alloc_size = sizeof(struct eg20t_gpio_priv),
>> +       .ops    = &eg20t_gpio_ops,
>> +};
>> +
>> +static struct pci_device_id eg20t_gpio_supported[] = {
>> +       { PCI_VENDOR_ID_INTEL, 0x8803 },
>> +       { },
>> +};
>> +
>> +U_BOOT_PCI_DEVICE(eg20t_gpio, eg20t_gpio_supported);
>> --

Other than the above comments,

Reviewed-by: Bin Meng <bmeng.cn@gmail.com>

Tested on Crown Bay
Tested-by: Bin Meng <bmeng.cn@gmail.com>
diff mbox

Patch

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 8d9ab52..4a6a22f 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -221,4 +221,12 @@  config MPC85XX_GPIO
 
 	  The driver has been tested on MPC85XX, but it is likely that other
 	  PowerQUICC III devices will work as well.
+
+config EG20T_GPIO
+	bool "Intel EG20T GPIO driver"
+	depends on DM_GPIO && DM_PCI
+	help
+	  Enable this to support the GPIO controller found in the Intel EG20T
+	  Platform Controller Hub.
+
 endmenu
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 8939226..a94aeb1 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -58,3 +58,4 @@  obj-$(CONFIG_MVEBU_GPIO)	+= mvebu_gpio.o
 obj-$(CONFIG_MSM_GPIO)		+= msm_gpio.o
 obj-$(CONFIG_$(SPL_)PCF8575_GPIO)	+= pcf8575_gpio.o
 obj-$(CONFIG_PM8916_GPIO)	+= pm8916_gpio.o
+obj-$(CONFIG_EG20T_GPIO)	+= eg20t-gpio.o
diff --git a/drivers/gpio/eg20t-gpio.c b/drivers/gpio/eg20t-gpio.c
new file mode 100644
index 0000000..05db771
--- /dev/null
+++ b/drivers/gpio/eg20t-gpio.c
@@ -0,0 +1,133 @@ 
+/*
+ * Copyright (C) 2016 Imagination Technologies
+ *
+ * SPDX-License-Identifier:	GPL-2.0
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <pci.h>
+#include <asm/io.h>
+#include <asm/gpio.h>
+
+enum {
+	REG_IEN		= 0x00,
+	REG_ISTATUS	= 0x04,
+	REG_IDISP	= 0x08,
+	REG_ICLR	= 0x0c,
+	REG_IMASK	= 0x10,
+	REG_IMASKCLR	= 0x14,
+	REG_PO		= 0x18,
+	REG_PI		= 0x1c,
+	REG_PM		= 0x20,
+};
+
+struct eg20t_gpio_priv {
+	void *base;
+};
+
+static int eg20t_gpio_get_value(struct udevice *dev, unsigned int offset)
+{
+	struct eg20t_gpio_priv *priv = dev_get_priv(dev);
+	uint32_t pm, pval;
+
+	pm = readl(priv->base + REG_PM);
+	if ((pm >> offset) & 0x1)
+		pval = readl(priv->base + REG_PO);
+	else
+		pval = readl(priv->base + REG_PI);
+
+	return (pval >> offset) & 0x1;
+}
+
+static int eg20t_gpio_set_value(struct udevice *dev, unsigned int offset,
+				int value)
+{
+	struct eg20t_gpio_priv *priv = dev_get_priv(dev);
+	uint32_t po;
+
+	po = readl(priv->base + REG_PO);
+	if (value)
+		po |= 1 << offset;
+	else
+		po &= ~(1 << offset);
+	writel(po, priv->base + REG_PO);
+	return 0;
+}
+
+static int eg20t_gpio_direction_input(struct udevice *dev, unsigned int offset)
+{
+	struct eg20t_gpio_priv *priv = dev_get_priv(dev);
+	uint32_t pm;
+
+	pm = readl(priv->base + REG_PM);
+	pm &= ~(1 << offset);
+	writel(pm, priv->base + REG_PM);
+	return 0;
+}
+
+static int eg20t_gpio_direction_output(struct udevice *dev, unsigned int offset,
+				       int value)
+{
+	struct eg20t_gpio_priv *priv = dev_get_priv(dev);
+	uint32_t pm;
+
+	pm = readl(priv->base + REG_PM);
+	pm |= 1 << offset;
+	writel(pm, priv->base + REG_PM);
+
+	return eg20t_gpio_set_value(dev, offset, value);
+}
+
+static int eg20t_gpio_get_function(struct udevice *dev, unsigned int offset)
+{
+	struct eg20t_gpio_priv *priv = dev_get_priv(dev);
+	uint32_t pm;
+
+	pm = readl(priv->base + REG_PM);
+
+	if ((pm >> offset) & 0x1)
+		return GPIOF_OUTPUT;
+
+	return GPIOF_INPUT;
+}
+
+static const struct dm_gpio_ops eg20t_gpio_ops = {
+	.direction_input	= eg20t_gpio_direction_input,
+	.direction_output	= eg20t_gpio_direction_output,
+	.get_value		= eg20t_gpio_get_value,
+	.set_value		= eg20t_gpio_set_value,
+	.get_function		= eg20t_gpio_get_function,
+};
+
+static int eg20t_gpio_probe(struct udevice *dev)
+{
+	struct eg20t_gpio_priv *priv = dev_get_priv(dev);
+	struct gpio_dev_priv *uc_priv = dev->uclass_priv;
+
+	priv->base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_1, PCI_REGION_MEM);
+	if (!priv->base) {
+		debug("failed to map GPIO registers\n");
+		return -EINVAL;
+	}
+
+	uc_priv->gpio_count = 12;
+	uc_priv->bank_name = "eg20t";
+	return 0;
+}
+
+U_BOOT_DRIVER(eg20t_gpio) = {
+	.name	= "eg20t-gpio",
+	.id	= UCLASS_GPIO,
+	.probe	= eg20t_gpio_probe,
+	.priv_auto_alloc_size = sizeof(struct eg20t_gpio_priv),
+	.ops	= &eg20t_gpio_ops,
+};
+
+static struct pci_device_id eg20t_gpio_supported[] = {
+	{ PCI_VENDOR_ID_INTEL, 0x8803 },
+	{ },
+};
+
+U_BOOT_PCI_DEVICE(eg20t_gpio, eg20t_gpio_supported);