// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) ASPEED Technology Inc. * Billy Tsai */ #include #include #include #include #include #include #include #include struct aspeed_gpio_priv { void *regs; }; #define GPIO_G7_IRQ_STS_BASE 0x100 #define GPIO_G7_IRQ_STS_OFFSET(x) (GPIO_G7_IRQ_STS_BASE + (x) * 0x4) #define GPIO_G7_CTRL_REG_BASE 0x180 #define GPIO_G7_CTRL_REG_OFFSET(x) (GPIO_G7_CTRL_REG_BASE + (x) * 0x4) #define GPIO_G7_OUT_DATA BIT(0) #define GPIO_G7_DIR BIT(1) #define GPIO_G7_IRQ_EN BIT(2) #define GPIO_G7_IRQ_TYPE0 BIT(3) #define GPIO_G7_IRQ_TYPE1 BIT(4) #define GPIO_G7_IRQ_TYPE2 BIT(5) #define GPIO_G7_RST_TOLERANCE BIT(6) #define GPIO_G7_DEBOUNCE_SEL GENMASK(8, 7) #define GPIO_G7_INPUT_MASK BIT(9) #define GPIO_G7_IRQ_STS BIT(12) #define GPIO_G7_IN_DATA BIT(13) /* * The configuration of the following registers should be determined * outside of the GPIO driver. */ #define GPIO_G7_PRIVILEGE_W_REG_BASE 0x810 #define GPIO_G7_PRIVILEGE_W_REG_OFFSET(x) (GPIO_G7_PRIVILEGE_W_REG_BASE + ((x) >> 2) * 0x4) #define GPIO_G7_PRIVILEGE_R_REG_BASE 0x910 #define GPIO_G7_PRIVILEGE_R_REG_OFFSET(x) (GPIO_G7_PRIVILEGE_R_REG_BASE + ((x) >> 2) * 0x4) #define GPIO_G7_IRQ_TARGET_REG_BASE 0xA10 #define GPIO_G7_IRQ_TARGET_REG_OFFSET(x) (GPIO_G7_IRQ_TARGET_REG_BASE + ((x) >> 2) * 0x4) #define GPIO_G7_IRQ_TO_INTC2_18 BIT(0) #define GPIO_G7_IRQ_TO_INTC2_19 BIT(1) #define GPIO_G7_IRQ_TO_INTC2_20 BIT(2) #define GPIO_G7_IRQ_TO_SIO BIT(3) #define GPIO_G7_IRQ_TARGET_RESET_TOLERANCE BIT(6) #define GPIO_G7_IRQ_TARGET_W_PROTECT BIT(7) static int aspeed_gpio_direction_input(struct udevice *dev, unsigned int offset) { struct aspeed_gpio_priv *priv = dev_get_priv(dev); void __iomem *addr = priv->regs + GPIO_G7_CTRL_REG_OFFSET(offset); u32 dir = readl(addr); dir &= ~GPIO_G7_DIR; writel(dir, addr); return 0; } static int aspeed_gpio_direction_output(struct udevice *dev, unsigned int offset, int value) { struct aspeed_gpio_priv *priv = dev_get_priv(dev); void __iomem *addr = priv->regs + GPIO_G7_CTRL_REG_OFFSET(offset); u32 data = readl(addr); if (value) data |= GPIO_G7_OUT_DATA; else data &= ~GPIO_G7_OUT_DATA; writel(data, addr); data |= GPIO_G7_DIR; writel(data, addr); return 0; } static int aspeed_gpio_get_value(struct udevice *dev, unsigned int offset) { struct aspeed_gpio_priv *priv = dev_get_priv(dev); void __iomem *addr = priv->regs + GPIO_G7_CTRL_REG_OFFSET(offset); return !!(readl(addr) & GPIO_G7_IN_DATA); } static int aspeed_gpio_set_value(struct udevice *dev, unsigned int offset, int value) { struct aspeed_gpio_priv *priv = dev_get_priv(dev); void __iomem *addr = priv->regs + GPIO_G7_CTRL_REG_OFFSET(offset); u32 data = readl(addr); if (value) data |= GPIO_G7_OUT_DATA; else data &= ~GPIO_G7_OUT_DATA; writel(data, addr); return 0; } static int aspeed_gpio_get_function(struct udevice *dev, unsigned int offset) { struct aspeed_gpio_priv *priv = dev_get_priv(dev); void __iomem *addr = priv->regs + GPIO_G7_CTRL_REG_OFFSET(offset); if (readl(addr) & GPIO_G7_DIR) return GPIOF_OUTPUT; return GPIOF_INPUT; } static const struct dm_gpio_ops aspeed_gpio_ops = { .direction_input = aspeed_gpio_direction_input, .direction_output = aspeed_gpio_direction_output, .get_value = aspeed_gpio_get_value, .set_value = aspeed_gpio_set_value, .get_function = aspeed_gpio_get_function, }; static int aspeed_gpio_probe(struct udevice *dev) { struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); struct aspeed_gpio_priv *priv = dev_get_priv(dev); uc_priv->bank_name = dev->name; ofnode_read_u32(dev_ofnode(dev), "ngpios", &uc_priv->gpio_count); priv->regs = devfdt_get_addr_ptr(dev); return 0; } static const struct udevice_id aspeed_gpio_ids[] = { { .compatible = "aspeed,ast2700-gpio", }, { } }; U_BOOT_DRIVER(gpio_aspeed) = { .name = "gpio-aspeed", .id = UCLASS_GPIO, .of_match = aspeed_gpio_ids, .ops = &aspeed_gpio_ops, .probe = aspeed_gpio_probe, .priv_auto = sizeof(struct aspeed_gpio_priv), };