/* * 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 * Copyright (c) 2016 Scalys B.V. * * SPDX-License-Identifier: GPL-2.0+ */ #include #include #include #include #include #include #include #include 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,};