// SPDX-License-Identifier: GPL-2.0+ /* * (C) Copyright 2015 Google, Inc * * (C) Copyright 2008-2014 Rockchip Electronics * Peter, Software Engineering, . */ #include #include #include #include #include #include #include #include #include #include #include #include #define SWPORT_DR 0x0000 #define SWPORT_DDR 0x0004 #define EXT_PORT 0x0050 #define SWPORT_DR_L 0x0000 #define SWPORT_DR_H 0x0004 #define SWPORT_DDR_L 0x0008 #define SWPORT_DDR_H 0x000C #define EXT_PORT_V2 0x0070 #define VER_ID_V2 0x0078 enum { ROCKCHIP_GPIOS_PER_BANK = 32, }; struct rockchip_gpio_priv { void __iomem *regs; struct udevice *pinctrl; int bank; char name[2]; u32 version; }; static int rockchip_gpio_get_value(struct udevice *dev, unsigned offset) { struct rockchip_gpio_priv *priv = dev_get_priv(dev); u32 mask = BIT(offset), data; if (priv->version) data = readl(priv->regs + EXT_PORT_V2); else data = readl(priv->regs + EXT_PORT); return (data & mask) ? 1 : 0; } static int rockchip_gpio_set_value(struct udevice *dev, unsigned offset, int value) { struct rockchip_gpio_priv *priv = dev_get_priv(dev); u32 mask = BIT(offset), data = value ? mask : 0; if (priv->version && offset >= 16) rk_clrsetreg(priv->regs + SWPORT_DR_H, mask >> 16, data >> 16); else if (priv->version) rk_clrsetreg(priv->regs + SWPORT_DR_L, mask, data); else clrsetbits_le32(priv->regs + SWPORT_DR, mask, data); return 0; } static int rockchip_gpio_direction_input(struct udevice *dev, unsigned offset) { struct rockchip_gpio_priv *priv = dev_get_priv(dev); u32 mask = BIT(offset); if (priv->version && offset >= 16) rk_clrreg(priv->regs + SWPORT_DDR_H, mask >> 16); else if (priv->version) rk_clrreg(priv->regs + SWPORT_DDR_L, mask); else clrbits_le32(priv->regs + SWPORT_DDR, mask); return 0; } static int rockchip_gpio_direction_output(struct udevice *dev, unsigned offset, int value) { struct rockchip_gpio_priv *priv = dev_get_priv(dev); u32 mask = BIT(offset); rockchip_gpio_set_value(dev, offset, value); if (priv->version && offset >= 16) rk_setreg(priv->regs + SWPORT_DDR_H, mask >> 16); else if (priv->version) rk_setreg(priv->regs + SWPORT_DDR_L, mask); else setbits_le32(priv->regs + SWPORT_DDR, mask); return 0; } static int rockchip_gpio_get_function(struct udevice *dev, unsigned offset) { struct rockchip_gpio_priv *priv = dev_get_priv(dev); u32 mask = BIT(offset), data; int ret; if (CONFIG_IS_ENABLED(PINCTRL)) { ret = pinctrl_get_gpio_mux(priv->pinctrl, priv->bank, offset); if (ret < 0) return ret; else if (ret != RK_FUNC_GPIO) return GPIOF_FUNC; } if (priv->version && offset >= 16) data = readl(priv->regs + SWPORT_DDR_H) << 16; else if (priv->version) data = readl(priv->regs + SWPORT_DDR_L); else data = readl(priv->regs + SWPORT_DDR); return (data & mask) ? GPIOF_OUTPUT : GPIOF_INPUT; } /* Simple SPL interface to GPIOs */ #ifdef CONFIG_SPL_BUILD enum { PULL_NONE_1V8 = 0, PULL_DOWN_1V8 = 1, PULL_UP_1V8 = 3, }; int spl_gpio_set_pull(void *vregs, uint gpio, int pull) { u32 *regs = vregs; uint val; regs += gpio >> GPIO_BANK_SHIFT; gpio &= GPIO_OFFSET_MASK; switch (pull) { case GPIO_PULL_UP: val = PULL_UP_1V8; break; case GPIO_PULL_DOWN: val = PULL_DOWN_1V8; break; case GPIO_PULL_NORMAL: default: val = PULL_NONE_1V8; break; } clrsetbits_le32(regs, 3 << (gpio * 2), val << (gpio * 2)); return 0; } int spl_gpio_output(void *vregs, uint gpio, int value) { struct rockchip_gpio_regs * const regs = vregs; clrsetbits_le32(®s->swport_dr, 1 << gpio, value << gpio); /* Set direction */ clrsetbits_le32(®s->swport_ddr, 1 << gpio, 1 << gpio); return 0; } #endif /* CONFIG_SPL_BUILD */ static int rockchip_gpio_probe(struct udevice *dev) { struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); struct rockchip_gpio_priv *priv = dev_get_priv(dev); struct ofnode_phandle_args args; char *end; int ret; priv->regs = dev_read_addr_ptr(dev); if (CONFIG_IS_ENABLED(PINCTRL)) { ret = uclass_first_device_err(UCLASS_PINCTRL, &priv->pinctrl); if (ret) return ret; } /* * If "gpio-ranges" is present in the devicetree use it to parse * the GPIO bank ID, otherwise use the legacy method. */ ret = ofnode_parse_phandle_with_args(dev_ofnode(dev), "gpio-ranges", NULL, 3, 0, &args); if (!ret || ret != -ENOENT) { uc_priv->gpio_count = args.args[2]; priv->bank = args.args[1] / ROCKCHIP_GPIOS_PER_BANK; } else { uc_priv->gpio_count = ROCKCHIP_GPIOS_PER_BANK; end = strrchr(dev->name, '@'); priv->bank = trailing_strtoln(dev->name, end); } priv->name[0] = 'A' + priv->bank; uc_priv->bank_name = priv->name; priv->version = readl(priv->regs + VER_ID_V2); return 0; } static const struct dm_gpio_ops gpio_rockchip_ops = { .direction_input = rockchip_gpio_direction_input, .direction_output = rockchip_gpio_direction_output, .get_value = rockchip_gpio_get_value, .set_value = rockchip_gpio_set_value, .get_function = rockchip_gpio_get_function, }; static const struct udevice_id rockchip_gpio_ids[] = { { .compatible = "rockchip,gpio-bank" }, { } }; U_BOOT_DRIVER(rockchip_gpio_bank) = { .name = "rockchip_gpio_bank", .id = UCLASS_GPIO, .of_match = rockchip_gpio_ids, .ops = &gpio_rockchip_ops, .priv_auto = sizeof(struct rockchip_gpio_priv), .probe = rockchip_gpio_probe, };