dm: gpio: Add driver for MPC85XX GPIO controller

This patch adds a driver for the built-in GPIO controller of the MPC85XX
SoC (probably supporting other PowerQUICC III SoCs as well).

Each GPIO bank is identified by its own entry in the device tree, i.e.

gpio-controller@fc00 {
      #gpio-cells = <2>;
      compatible = "fsl,pq3-gpio";
      reg = <0xfc00 0x100>
}

By default, each bank is assumed to have 32 GPIOs, but the ngpios
setting is honored, so the number of GPIOs for each bank in configurable
to match the actual GPIO count of the SoC (e.g. the 32/32/23 banks of
the P1022 SoC).

The usual functions of GPIO drivers (setting input/output mode and output
value setting) are supported.

The driver has been tested on MPC85XX, but it is likely that other
PowerQUICC III devices will work as well.

Signed-off-by: Mario Six <mario.six@gdsys.cc>
Reviewed-by: Simon Glass <sjg@chromium.org>
Reviewed-by: York Sun <york.sun@nxp.com>
master
mario.six@gdsys.cc 8 years ago committed by York Sun
parent b7707b043e
commit 07d31f8f98
  1. 2
      arch/powerpc/include/asm/arch-mpc85xx/gpio.h
  2. 2
      arch/powerpc/include/asm/immap_85xx.h
  3. 26
      drivers/gpio/Kconfig
  4. 1
      drivers/gpio/Makefile
  5. 187
      drivers/gpio/mpc85xx_gpio.c

@ -14,6 +14,8 @@
#ifndef __ASM_ARCH_MX85XX_GPIO_H
#define __ASM_ARCH_MX85XX_GPIO_H
#ifndef CONFIG_MPC85XX_GPIO
#include <asm/mpc85xx_gpio.h>
#endif
#endif

@ -265,6 +265,7 @@ typedef struct ccsr_pcix {
#define PIWAR_WRITE_SNOOP 0x00005000
#define PIWAR_MEM_2G 0x0000001e
#ifndef CONFIG_MPC85XX_GPIO
typedef struct ccsr_gpio {
u32 gpdir;
u32 gpodr;
@ -273,6 +274,7 @@ typedef struct ccsr_gpio {
u32 gpimr;
u32 gpicr;
} ccsr_gpio_t;
#endif
/* L2 Cache Registers */
typedef struct ccsr_l2cache {

@ -173,4 +173,30 @@ config DM_PCA953X
Now, max 24 bits chips and PCA953X compatible chips are
supported
config MPC85XX_GPIO
bool "Freescale MPC85XX GPIO driver"
depends on DM_GPIO
help
This driver supports the built-in GPIO controller of MPC85XX CPUs.
Each GPIO bank is identified by its own entry in the device tree,
i.e.
gpio-controller@fc00 {
#gpio-cells = <2>;
compatible = "fsl,pq3-gpio";
reg = <0xfc00 0x100>
}
By default, each bank is assumed to have 32 GPIOs, but the ngpios
setting is honored, so the number of GPIOs for each bank is
configurable to match the actual GPIO count of the SoC (e.g. the
32/32/23 banks of the P1022 SoC).
The standard functions of input/output mode, and output value setting
are supported; the open-drain capability of the controller is not
supported yet.
The driver has been tested on MPC85XX, but it is likely that other
PowerQUICC III devices will work as well.
endmenu

@ -35,6 +35,7 @@ obj-$(CONFIG_DA8XX_GPIO) += da8xx_gpio.o
obj-$(CONFIG_DM644X_GPIO) += da8xx_gpio.o
obj-$(CONFIG_ALTERA_PIO) += altera_pio.o
obj-$(CONFIG_MPC83XX_GPIO) += mpc83xx_gpio.o
obj-$(CONFIG_MPC85XX_GPIO) += mpc85xx_gpio.o
obj-$(CONFIG_SH_GPIO_PFC) += sh_pfc.o
obj-$(CONFIG_OMAP_GPIO) += omap_gpio.o
obj-$(CONFIG_DB8500_GPIO) += db8500_gpio.o

@ -0,0 +1,187 @@
/*
* (C) Copyright 2016
* Mario Six, Guntermann & Drunck GmbH, six@gdsys.de
*
* based on arch/powerpc/include/asm/mpc85xx_gpio.h, which is
*
* Copyright 2010 eXMeritus, A Boeing Company
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <dm.h>
#include <asm/gpio.h>
#include <mapmem.h>
DECLARE_GLOBAL_DATA_PTR;
struct ccsr_gpio {
u32 gpdir;
u32 gpodr;
u32 gpdat;
u32 gpier;
u32 gpimr;
u32 gpicr;
};
struct mpc85xx_gpio_data {
/* The bank's register base in memory */
struct ccsr_gpio __iomem *base;
/* The address of the registers; used to identify the bank */
ulong addr;
/* The GPIO count of the bank */
uint gpio_count;
/* The GPDAT register cannot be used to determine the value of output
* pins on MPC8572/MPC8536, so we shadow it and use the shadowed value
* for output pins */
u32 dat_shadow;
};
inline u32 gpio_mask(unsigned gpio) {
return (1U << (31 - (gpio)));
}
static inline u32 mpc85xx_gpio_get_val(struct ccsr_gpio *base, u32 mask)
{
return in_be32(&base->gpdat) & mask;
}
static inline u32 mpc85xx_gpio_get_dir(struct ccsr_gpio *base, u32 mask)
{
return in_be32(&base->gpdir) & mask;
}
static inline void mpc85xx_gpio_set_in(struct ccsr_gpio *base, u32 gpios)
{
clrbits_be32(&base->gpdat, gpios);
/* GPDIR register 0 -> input */
clrbits_be32(&base->gpdir, gpios);
}
static inline void mpc85xx_gpio_set_low(struct ccsr_gpio *base, u32 gpios)
{
clrbits_be32(&base->gpdat, gpios);
/* GPDIR register 1 -> output */
setbits_be32(&base->gpdir, gpios);
}
static inline void mpc85xx_gpio_set_high(struct ccsr_gpio *base, u32 gpios)
{
setbits_be32(&base->gpdat, gpios);
/* GPDIR register 1 -> output */
setbits_be32(&base->gpdir, gpios);
}
static int mpc85xx_gpio_direction_input(struct udevice *dev, unsigned gpio)
{
struct mpc85xx_gpio_data *data = dev_get_priv(dev);
mpc85xx_gpio_set_in(data->base, gpio_mask(gpio));
return 0;
}
static int mpc85xx_gpio_set_value(struct udevice *dev, unsigned gpio,
int value)
{
struct mpc85xx_gpio_data *data = dev_get_priv(dev);
if (value) {
data->dat_shadow |= gpio_mask(gpio);
mpc85xx_gpio_set_high(data->base, gpio_mask(gpio));
} else {
data->dat_shadow &= ~gpio_mask(gpio);
mpc85xx_gpio_set_low(data->base, gpio_mask(gpio));
}
return 0;
}
static int mpc85xx_gpio_direction_output(struct udevice *dev, unsigned gpio,
int value)
{
return mpc85xx_gpio_set_value(dev, gpio, value);
}
static int mpc85xx_gpio_get_value(struct udevice *dev, unsigned gpio)
{
struct mpc85xx_gpio_data *data = dev_get_priv(dev);
if (!!mpc85xx_gpio_get_dir(data->base, gpio_mask(gpio))) {
/* Output -> use shadowed value */
return !!(data->dat_shadow & gpio_mask(gpio));
} else {
/* Input -> read value from GPDAT register */
return !!mpc85xx_gpio_get_val(data->base, gpio_mask(gpio));
}
}
static int mpc85xx_gpio_get_function(struct udevice *dev, unsigned gpio)
{
struct mpc85xx_gpio_data *data = dev_get_priv(dev);
int dir;
dir = !!mpc85xx_gpio_get_dir(data->base, gpio_mask(gpio));
return dir ? GPIOF_OUTPUT : GPIOF_INPUT;
}
static int mpc85xx_gpio_ofdata_to_platdata(struct udevice *dev) {
struct mpc85xx_gpio_data *data = dev_get_priv(dev);
fdt_addr_t addr;
fdt_size_t size;
addr = fdtdec_get_addr_size_auto_noparent(gd->fdt_blob, dev->of_offset,
"reg", 0, &size);
data->addr = addr;
data->base = map_sysmem(CONFIG_SYS_IMMR + addr, size);
if (!data->base)
return -ENOMEM;
data->gpio_count = fdtdec_get_int(gd->fdt_blob, dev->of_offset,
"ngpios", 32);
data->dat_shadow = 0;
return 0;
}
static int mpc85xx_gpio_probe(struct udevice *dev)
{
struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
struct mpc85xx_gpio_data *data = dev_get_priv(dev);
char name[32], *str;
snprintf(name, sizeof(name), "MPC@%lx_", data->addr);
str = strdup(name);
if (!str)
return -ENOMEM;
uc_priv->bank_name = str;
uc_priv->gpio_count = data->gpio_count;
return 0;
}
static const struct dm_gpio_ops gpio_mpc85xx_ops = {
.direction_input = mpc85xx_gpio_direction_input,
.direction_output = mpc85xx_gpio_direction_output,
.get_value = mpc85xx_gpio_get_value,
.set_value = mpc85xx_gpio_set_value,
.get_function = mpc85xx_gpio_get_function,
};
static const struct udevice_id mpc85xx_gpio_ids[] = {
{ .compatible = "fsl,pq3-gpio" },
{ /* sentinel */ }
};
U_BOOT_DRIVER(gpio_mpc85xx) = {
.name = "gpio_mpc85xx",
.id = UCLASS_GPIO,
.ops = &gpio_mpc85xx_ops,
.ofdata_to_platdata = mpc85xx_gpio_ofdata_to_platdata,
.of_match = mpc85xx_gpio_ids,
.probe = mpc85xx_gpio_probe,
.priv_auto_alloc_size = sizeof(struct mpc85xx_gpio_data),
};
Loading…
Cancel
Save