diff options
Diffstat (limited to 'drivers/gpio/gpio-mpc8xxx.c')
-rw-r--r-- | drivers/gpio/gpio-mpc8xxx.c | 178 |
1 files changed, 178 insertions, 0 deletions
diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c new file mode 100644 index 0000000..c5d72ef --- /dev/null +++ b/drivers/gpio/gpio-mpc8xxx.c @@ -0,0 +1,178 @@ + +/* + * GPIOs on MPC512x/8349/8572/8610/T-series and compatible + * + * Driver ported from the linux kernel 4.5 (b562e44f507e863c6792946e4e1b1449fbbac85d) + * and removed the interrupt functionallity. + * + * Copyright (C) 2008 Peter Korsgaard <jacmet@sunsite.dk> + * Copyright (c) 2016 Scalys B.V. <u-boot@scalys.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <malloc.h> +#include <fdtdec.h> +#include <asm/io.h> +#include <asm/gpio.h> +#include <dm/platform_data/gpio_mpc8xxx.h> + +DECLARE_GLOBAL_DATA_PTR; + +#define MPC8XXX_GPIO_PINS 32 + +static inline u32 mpc8xxx_gpio2mask(unsigned int gpio) +{ + return 1u << (MPC8XXX_GPIO_PINS - 1 - gpio); +} + +static int mpc8xxx_dm_gpio_set(struct udevice *dev, unsigned pin, int val) +{ + struct mpc8xxx_gpio_platdata *plat = dev_get_platdata(dev); + +#if defined(CONFIG_MPC8572) || defined(CONFIG_MPC8536) + + if (val) { + plat->data |= mpc8xxx_gpio2mask(pin); + } else { + plat->data &= ~mpc8xxx_gpio2mask(pin); + } + + out_be32(&(plat->regs->gpdat), plat->data); +#else + if (val) { + setbits_be32(&(plat->regs->gpdat), mpc8xxx_gpio2mask(pin)); + } else { + clrbits_be32(&(plat->regs->gpdat), mpc8xxx_gpio2mask(pin)); + } +#endif + return 0; +} + +static int mpc8xxx_dm_gpio_dir_in(struct udevice *dev, unsigned int gpio) +{ + struct mpc8xxx_gpio_platdata *plat = dev_get_platdata(dev); + + clrbits_be32(&(plat->regs->gpdir), mpc8xxx_gpio2mask(gpio)); + + return 0; +} + +static int mpc8xxx_dm_gpio_dir_out(struct udevice *dev, unsigned int gpio, + int val) +{ + struct mpc8xxx_gpio_platdata *plat = dev_get_platdata(dev); + + mpc8xxx_dm_gpio_set(dev, gpio, val); + + setbits_be32(&(plat->regs->gpdir), mpc8xxx_gpio2mask(gpio)); + + return 0; +} + +static int mpc8xxx_dm_gpio_get(struct udevice *dev, unsigned int gpio) +{ + int ret = 0; + struct mpc8xxx_gpio_platdata *plat = dev_get_platdata(dev); + +#if defined(CONFIG_MPC8572) || defined(CONFIG_MPC8536) + uint32_t data_val, out_mask, out_shadow; + + /* Workaround GPIO 1 errata on MPC8572/MPC8536. The status of GPIOs + * defined as output cannot be determined by reading GPDAT register, + * so we use shadow data register instead. The status of input pins + * is determined by reading GPDAT register. + */ + out_mask = in_be32(&plat->regs->gpdir); + + data_val = in_be32(&plat->regs->gpdat) & ~out_mask; + out_shadow = plat->data & out_mask; + + ret = ! !((data_val | out_shadow) & mpc8xxx_gpio2mask(gpio)); +#else + if (in_be32(&plat->regs->gpdat) & mpc8xxx_gpio2mask(gpio)) { + ret = 1; + } else { + ret = 0; + } +#endif + return ret; +} + +static int mpc8xxx_dm_gpio_get_function(struct udevice *dev, unsigned gpio) +{ + int ret = GPIOF_UNUSED; + struct mpc8xxx_gpio_platdata *plat = dev_get_platdata(dev); + + if (in_be32(&plat->regs->gpdir) & mpc8xxx_gpio2mask(gpio)) { + ret = GPIOF_OUTPUT; + } else { + ret = GPIOF_INPUT; + } + return ret; +} + +static const struct udevice_id mpc8xxx_gpio_ids[] = { + {.compatible = "fsl,mpc8349-gpio",}, + {.compatible = "fsl,mpc8572-gpio",}, + {.compatible = "fsl,mpc8610-gpio",}, + {.compatible = "fsl,mpc5121-gpio",}, + {.compatible = "fsl,mpc5125-gpio",}, + {.compatible = "fsl,pq3-gpio",}, + {.compatible = "fsl,qoriq-gpio",}, + {} +}; + +static const struct dm_gpio_ops mpc8xxx_gpio_ops = { + .direction_input = mpc8xxx_dm_gpio_dir_in, + .direction_output = mpc8xxx_dm_gpio_dir_out, + .get_value = mpc8xxx_dm_gpio_get, + .set_value = mpc8xxx_dm_gpio_set, + .get_function = mpc8xxx_dm_gpio_get_function, +}; + +#ifdef SPL_OF_CONTROL +static int mpc8xxx_gpio_ofdata_to_platdata(struct udevice *dev) +{ + int register_address; + struct mpc8xxx_gpio_platdata *plat = dev_get_platdata(dev); + + register_address = + fdtdec_get_int(gd->fdt_blob, dev->of_offset, "reg", -1); + if (register_address == -1) { + debug("%s: Invalid register offset %d\n", __func__, + register_address); + return -EINVAL; + } + plat->regs = map_physmem(register_address, sizeof(ccsr_gpio_t), + MAP_NOCACHE); + plat->gpio_count = MPC8XXX_GPIO_PINS; + plat->bank_name = fdt_getprop(gd->fdt_blob, dev->of_offset, + "bank-name", NULL); + + return 0; +} +#endif + +static int mpc8xxx_gpio_probe(struct udevice *dev) +{ + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); + struct mpc8xxx_gpio_platdata *plat = dev_get_platdata(dev); + + uc_priv->gpio_count = MPC8XXX_GPIO_PINS; + uc_priv->bank_name = plat->bank_name; + + return 0; +} + +U_BOOT_DRIVER(gpio_mpc8xxx) = { + .name = "gpio-mpc8xxx",.id = UCLASS_GPIO,.of_match = + mpc8xxx_gpio_ids,.ops = &mpc8xxx_gpio_ops, +#ifdef SPL_OF_CONTROL + .ofdata_to_platdata = mpc8xxx_gpio_ofdata_to_platdata, +#endif +.platdata_auto_alloc_size = + sizeof(struct mpc8xxx_gpio_platdata),.probe = mpc8xxx_gpio_probe,}; |