// SPDX-License-Identifier: GPL-2.0 /* * RZ/G2L Pin Function Controller * * Copyright (C) 2021-2023 Renesas Electronics Corp. */ #include #include #include #include #include static void rzg2l_gpio_set(const struct rzg2l_pfc_data *data, u32 port, u8 pin, bool value) { if (value) setbits_8(data->base + P(port), BIT(pin)); else clrbits_8(data->base + P(port), BIT(pin)); } static int rzg2l_gpio_get_value(struct udevice *dev, unsigned int offset) { const struct rzg2l_pfc_data *data = (const struct rzg2l_pfc_data *)dev_get_driver_data(dev); const u32 port = RZG2L_PINMUX_TO_PORT(offset); const u8 pin = RZG2L_PINMUX_TO_PIN(offset); u16 pm_state; pm_state = (readw(data->base + PM(port)) >> (pin * 2)) & PM_MASK; switch (pm_state) { case PM_INPUT: return !!(readb(data->base + PIN(port)) & BIT(pin)); case PM_OUTPUT: case PM_OUTPUT_IEN: return !!(readb(data->base + P(port)) & BIT(pin)); default: /* PM_HIGH_Z */ return 0; } } static int rzg2l_gpio_set_value(struct udevice *dev, unsigned int offset, int value) { const struct rzg2l_pfc_data *data = (const struct rzg2l_pfc_data *)dev_get_driver_data(dev); const u32 port = RZG2L_PINMUX_TO_PORT(offset); const u8 pin = RZG2L_PINMUX_TO_PIN(offset); rzg2l_gpio_set(data, port, pin, (bool)value); return 0; } static void rzg2l_gpio_set_direction(const struct rzg2l_pfc_data *data, u32 port, u8 pin, bool output) { clrsetbits_le16(data->base + PM(port), PM_MASK << (pin * 2), (output ? PM_OUTPUT : PM_INPUT) << (pin * 2)); } static int rzg2l_gpio_direction_input(struct udevice *dev, unsigned int offset) { const struct rzg2l_pfc_data *data = (const struct rzg2l_pfc_data *)dev_get_driver_data(dev); const u32 port = RZG2L_PINMUX_TO_PORT(offset); const u8 pin = RZG2L_PINMUX_TO_PIN(offset); rzg2l_gpio_set_direction(data, port, pin, false); return 0; } static int rzg2l_gpio_direction_output(struct udevice *dev, unsigned int offset, int value) { const struct rzg2l_pfc_data *data = (const struct rzg2l_pfc_data *)dev_get_driver_data(dev); const u32 port = RZG2L_PINMUX_TO_PORT(offset); const u8 pin = RZG2L_PINMUX_TO_PIN(offset); rzg2l_gpio_set(data, port, pin, (bool)value); rzg2l_gpio_set_direction(data, port, pin, true); return 0; } static int rzg2l_gpio_request(struct udevice *dev, unsigned int offset, const char *label) { const struct rzg2l_pfc_data *data = (const struct rzg2l_pfc_data *)dev_get_driver_data(dev); const u32 port = RZG2L_PINMUX_TO_PORT(offset); const u8 pin = RZG2L_PINMUX_TO_PIN(offset); if (!rzg2l_port_validate(data, port, pin)) { dev_err(dev, "Invalid GPIO %u:%u\n", port, pin); return -EINVAL; } /* Select GPIO mode in PMC Register */ clrbits_8(data->base + PMC(port), BIT(pin)); return 0; } static int rzg2l_gpio_get_function(struct udevice *dev, unsigned int offset) { const struct rzg2l_pfc_data *data = (const struct rzg2l_pfc_data *)dev_get_driver_data(dev); const u32 port = RZG2L_PINMUX_TO_PORT(offset); const u8 pin = RZG2L_PINMUX_TO_PIN(offset); u16 pm_state; u8 pmc_state; if (!rzg2l_port_validate(data, port, pin)) { /* This offset does not correspond to a valid GPIO pin. */ return -ENOENT; } /* Check if the pin is in GPIO or function mode. */ pmc_state = readb(data->base + PMC(port)) & BIT(pin); if (pmc_state) return GPIOF_FUNC; /* Check the pin direction. */ pm_state = (readw(data->base + PM(port)) >> (pin * 2)) & PM_MASK; switch (pm_state) { case PM_INPUT: return GPIOF_INPUT; case PM_OUTPUT: case PM_OUTPUT_IEN: return GPIOF_OUTPUT; default: /* PM_HIGH_Z */ return GPIOF_UNUSED; } } static const struct dm_gpio_ops rzg2l_gpio_ops = { .direction_input = rzg2l_gpio_direction_input, .direction_output = rzg2l_gpio_direction_output, .get_value = rzg2l_gpio_get_value, .set_value = rzg2l_gpio_set_value, .request = rzg2l_gpio_request, .get_function = rzg2l_gpio_get_function, }; static int rzg2l_gpio_probe(struct udevice *dev) { struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); struct ofnode_phandle_args args; int ret; uc_priv->bank_name = "rzg2l-pfc-gpio"; ret = ofnode_parse_phandle_with_args(dev_ofnode(dev), "gpio-ranges", NULL, 3, 0, &args); if (ret < 0) { dev_err(dev, "Failed to parse gpio-ranges: %d\n", ret); return -EINVAL; } uc_priv->gpio_count = args.args[2]; return rzg2l_pfc_enable(dev); } U_BOOT_DRIVER(rzg2l_pfc_gpio) = { .name = "rzg2l-pfc-gpio", .id = UCLASS_GPIO, .ops = &rzg2l_gpio_ops, .probe = rzg2l_gpio_probe, };