summaryrefslogtreecommitdiff
path: root/drivers/gpio/gpio-mpc8xxx.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpio/gpio-mpc8xxx.c')
-rw-r--r--drivers/gpio/gpio-mpc8xxx.c178
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,};