From: mario.six@gdsys.cc Date: Wed, 25 May 2016 13:15:20 +0000 (+0200) Subject: dm: gpio: Add driver for MPC85XX GPIO controller X-Git-Url: http://git.dujemihanovic.xyz/login.html?a=commitdiff_plain;h=07d31f8f98fb350776c78a681ef27fd8ee288acd;p=u-boot.git 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 Reviewed-by: Simon Glass Reviewed-by: York Sun --- diff --git a/arch/powerpc/include/asm/arch-mpc85xx/gpio.h b/arch/powerpc/include/asm/arch-mpc85xx/gpio.h index da7352abb2..41b6677bba 100644 --- a/arch/powerpc/include/asm/arch-mpc85xx/gpio.h +++ b/arch/powerpc/include/asm/arch-mpc85xx/gpio.h @@ -14,6 +14,8 @@ #ifndef __ASM_ARCH_MX85XX_GPIO_H #define __ASM_ARCH_MX85XX_GPIO_H +#ifndef CONFIG_MPC85XX_GPIO #include +#endif #endif diff --git a/arch/powerpc/include/asm/immap_85xx.h b/arch/powerpc/include/asm/immap_85xx.h index 07d2adf71f..c045a24d1a 100644 --- a/arch/powerpc/include/asm/immap_85xx.h +++ b/arch/powerpc/include/asm/immap_85xx.h @@ -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 { diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 93a7e8c6c2..b674a47824 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -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 diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index ddec1ef8de..21b2cc2677 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -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 diff --git a/drivers/gpio/mpc85xx_gpio.c b/drivers/gpio/mpc85xx_gpio.c new file mode 100644 index 0000000000..17755dfb92 --- /dev/null +++ b/drivers/gpio/mpc85xx_gpio.c @@ -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 +#include +#include +#include + +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), +};