// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2017 Marek Vasut */ #include #include #include #include #include #include #include #include #include #include #include "../pinctrl/renesas/sh_pfc.h" #define GPIO_IOINTSEL 0x00 /* General IO/Interrupt Switching Register */ #define GPIO_INOUTSEL 0x04 /* General Input/Output Switching Register */ #define GPIO_OUTDT 0x08 /* General Output Register */ #define GPIO_INDT_G2 0x0c /* General Input Register */ #define GPIO_POSNEG_G2 0x20 /* Positive/Negative Logic Select Register */ #define GPIO_INEN_G4 0x50 /* General Input Enable Register */ #define GPIO_INDT_G5 0x1c /* General Input Register */ #define GPIO_POSNEG_G5 0x90 /* Positive/Negative Logic Select Register */ #define GPIO_INEN_G5 0x18 /* General Input Enable Register */ #define RCAR_MAX_GPIO_PER_BANK 32 #define RCAR_GPIO_HAS_INEN BIT(0) DECLARE_GLOBAL_DATA_PTR; struct rcar_gpio_data { u32 quirks; u32 indt_offset; u32 posneg_offset; u32 inen_offset; }; struct rcar_gpio_priv { void __iomem *regs; const struct rcar_gpio_data *data; }; static int rcar_gpio_get_value(struct udevice *dev, unsigned offset) { struct rcar_gpio_priv *priv = dev_get_priv(dev); const struct rcar_gpio_data *data = priv->data; const u32 bit = BIT(offset); /* * Testing on r8a7790 shows that INDT does not show correct pin state * when configured as output, so use OUTDT in case of output pins. */ if (readl(priv->regs + GPIO_INOUTSEL) & bit) return !!(readl(priv->regs + GPIO_OUTDT) & bit); else return !!(readl(priv->regs + data->indt_offset) & bit); } static int rcar_gpio_set_value(struct udevice *dev, unsigned offset, int value) { struct rcar_gpio_priv *priv = dev_get_priv(dev); if (value) setbits_le32(priv->regs + GPIO_OUTDT, BIT(offset)); else clrbits_le32(priv->regs + GPIO_OUTDT, BIT(offset)); return 0; } static void rcar_gpio_set_direction(struct udevice *dev, unsigned offset, bool output) { struct rcar_gpio_priv *priv = dev_get_priv(dev); const struct rcar_gpio_data *data = priv->data; void __iomem *regs = priv->regs; /* * follow steps in the GPIO documentation for * "Setting General Output Mode" and * "Setting General Input Mode" */ /* Configure postive logic in POSNEG */ clrbits_le32(regs + data->posneg_offset, BIT(offset)); /* Select "Input Enable/Disable" in INEN */ if (data->quirks & RCAR_GPIO_HAS_INEN) { if (output) clrbits_le32(regs + data->inen_offset, BIT(offset)); else setbits_le32(regs + data->inen_offset, BIT(offset)); } /* Select "General Input/Output Mode" in IOINTSEL */ clrbits_le32(regs + GPIO_IOINTSEL, BIT(offset)); /* Select Input Mode or Output Mode in INOUTSEL */ if (output) setbits_le32(regs + GPIO_INOUTSEL, BIT(offset)); else clrbits_le32(regs + GPIO_INOUTSEL, BIT(offset)); } static int rcar_gpio_direction_input(struct udevice *dev, unsigned offset) { rcar_gpio_set_direction(dev, offset, false); return 0; } static int rcar_gpio_direction_output(struct udevice *dev, unsigned offset, int value) { /* write GPIO value to output before selecting output mode of pin */ rcar_gpio_set_value(dev, offset, value); rcar_gpio_set_direction(dev, offset, true); return 0; } static int rcar_gpio_get_function(struct udevice *dev, unsigned offset) { struct rcar_gpio_priv *priv = dev_get_priv(dev); if (readl(priv->regs + GPIO_INOUTSEL) & BIT(offset)) return GPIOF_OUTPUT; else return GPIOF_INPUT; } static const struct dm_gpio_ops rcar_gpio_ops = { .request = pinctrl_gpio_request, .rfree = pinctrl_gpio_free, .direction_input = rcar_gpio_direction_input, .direction_output = rcar_gpio_direction_output, .get_value = rcar_gpio_get_value, .set_value = rcar_gpio_set_value, .get_function = rcar_gpio_get_function, }; static int rcar_gpio_probe(struct udevice *dev) { struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); struct rcar_gpio_priv *priv = dev_get_priv(dev); struct fdtdec_phandle_args args; struct clk clk; int node = dev_of_offset(dev); int ret; priv->regs = dev_read_addr_ptr(dev); priv->data = (const struct rcar_gpio_data *)dev_get_driver_data(dev); uc_priv->bank_name = dev->name; ret = fdtdec_parse_phandle_with_args(gd->fdt_blob, node, "gpio-ranges", NULL, 3, 0, &args); uc_priv->gpio_count = ret == 0 ? args.args[2] : RCAR_MAX_GPIO_PER_BANK; ret = clk_get_by_index(dev, 0, &clk); if (ret < 0) { dev_err(dev, "Failed to get GPIO bank clock\n"); return ret; } ret = clk_enable(&clk); if (ret) { dev_err(dev, "Failed to enable GPIO bank clock\n"); return ret; } return 0; } static const struct rcar_gpio_data rcar_gpio_gen2_data = { .indt_offset = GPIO_INDT_G2, .posneg_offset = GPIO_POSNEG_G2, }; static const struct rcar_gpio_data rcar_gpio_gen3_data = { .quirks = RCAR_GPIO_HAS_INEN, .indt_offset = GPIO_INDT_G2, .posneg_offset = GPIO_POSNEG_G2, .inen_offset = GPIO_INEN_G4, }; static const struct rcar_gpio_data rcar_gpio_gen5_data = { .quirks = RCAR_GPIO_HAS_INEN, .indt_offset = GPIO_INDT_G5, .posneg_offset = GPIO_POSNEG_G5, .inen_offset = GPIO_INEN_G5, }; static const struct udevice_id rcar_gpio_ids[] = { { .compatible = "renesas,gpio-r8a7795", .data = (ulong)&rcar_gpio_gen2_data }, { .compatible = "renesas,gpio-r8a7796", .data = (ulong)&rcar_gpio_gen2_data }, { .compatible = "renesas,gpio-r8a77965", .data = (ulong)&rcar_gpio_gen2_data }, { .compatible = "renesas,gpio-r8a77970", .data = (ulong)&rcar_gpio_gen2_data }, { .compatible = "renesas,gpio-r8a77990", .data = (ulong)&rcar_gpio_gen2_data }, { .compatible = "renesas,gpio-r8a77995", .data = (ulong)&rcar_gpio_gen2_data }, { .compatible = "renesas,gpio-r8a779a0", .data = (ulong)&rcar_gpio_gen3_data }, { .compatible = "renesas,rcar-gen2-gpio", .data = (ulong)&rcar_gpio_gen2_data }, { .compatible = "renesas,rcar-gen3-gpio", .data = (ulong)&rcar_gpio_gen2_data }, { .compatible = "renesas,rcar-gen4-gpio", .data = (ulong)&rcar_gpio_gen3_data }, { .compatible = "renesas,rcar-gen5-gpio", .data = (ulong)&rcar_gpio_gen5_data }, { /* sentinel */ } }; U_BOOT_DRIVER(rcar_gpio) = { .name = "rcar-gpio", .id = UCLASS_GPIO, .of_match = rcar_gpio_ids, .ops = &rcar_gpio_ops, .priv_auto = sizeof(struct rcar_gpio_priv), .probe = rcar_gpio_probe, };