// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2018 Stefan Roese * * Based on the Linux driver version which is: * Copyright (C) 2009-2011 Gabor Juhos * Copyright (C) 2013 John Crispin */ #include #include #include #include #include #include #include #include #include #include #include #define MTK_MAX_BANK 3 #define MTK_BANK_WIDTH 32 enum mediatek_gpio_reg { GPIO_REG_CTRL = 0, GPIO_REG_POL, GPIO_REG_DATA, GPIO_REG_DSET, GPIO_REG_DCLR, GPIO_REG_REDGE, GPIO_REG_FEDGE, GPIO_REG_HLVL, GPIO_REG_LLVL, GPIO_REG_STAT, GPIO_REG_EDGE, }; static void __iomem *mediatek_gpio_membase; struct mediatek_gpio_plat { char bank_name[3]; /* Name of bank, e.g. "PA", "PB" etc */ int gpio_count; int bank; }; static u32 reg_offs(struct mediatek_gpio_plat *plat, int reg) { return (reg * 0x10) + (plat->bank * 0x4); } static int mediatek_gpio_get_value(struct udevice *dev, unsigned int offset) { struct mediatek_gpio_plat *plat = dev_get_plat(dev); return !!(ioread32(mediatek_gpio_membase + reg_offs(plat, GPIO_REG_DATA)) & BIT(offset)); } static int mediatek_gpio_set_value(struct udevice *dev, unsigned int offset, int value) { struct mediatek_gpio_plat *plat = dev_get_plat(dev); iowrite32(BIT(offset), mediatek_gpio_membase + reg_offs(plat, value ? GPIO_REG_DSET : GPIO_REG_DCLR)); return 0; } static int mediatek_gpio_direction_input(struct udevice *dev, unsigned int offset) { struct mediatek_gpio_plat *plat = dev_get_plat(dev); clrbits_le32(mediatek_gpio_membase + reg_offs(plat, GPIO_REG_CTRL), BIT(offset)); return 0; } static int mediatek_gpio_direction_output(struct udevice *dev, unsigned int offset, int value) { struct mediatek_gpio_plat *plat = dev_get_plat(dev); setbits_le32(mediatek_gpio_membase + reg_offs(plat, GPIO_REG_CTRL), BIT(offset)); mediatek_gpio_set_value(dev, offset, value); return 0; } static int mediatek_gpio_get_function(struct udevice *dev, unsigned int offset) { struct mediatek_gpio_plat *plat = dev_get_plat(dev); u32 t; t = ioread32(mediatek_gpio_membase + reg_offs(plat, GPIO_REG_CTRL)); if (t & BIT(offset)) return GPIOF_OUTPUT; return GPIOF_INPUT; } static const struct dm_gpio_ops gpio_mediatek_ops = { .direction_input = mediatek_gpio_direction_input, .direction_output = mediatek_gpio_direction_output, .get_value = mediatek_gpio_get_value, .set_value = mediatek_gpio_set_value, .get_function = mediatek_gpio_get_function, }; static int gpio_mediatek_probe(struct udevice *dev) { struct mediatek_gpio_plat *plat = dev_get_plat(dev); struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); /* Tell the uclass how many GPIOs we have */ if (plat) { uc_priv->gpio_count = plat->gpio_count; uc_priv->bank_name = plat->bank_name; } return 0; } /** * We have a top-level GPIO device with no actual GPIOs. It has a child * device for each Mediatek bank. */ static int gpio_mediatek_bind(struct udevice *parent) { struct mediatek_gpio_plat *plat = dev_get_plat(parent); ofnode node; int bank = 0; int ret; /* If this is a child device, there is nothing to do here */ if (plat) return 0; mediatek_gpio_membase = dev_remap_addr(parent); if (!mediatek_gpio_membase) return -EINVAL; for (node = dev_read_first_subnode(parent); ofnode_valid(node); node = dev_read_next_subnode(node)) { struct mediatek_gpio_plat *plat; struct udevice *dev; plat = calloc(1, sizeof(*plat)); if (!plat) return -ENOMEM; plat->bank_name[0] = 'P'; plat->bank_name[1] = 'A' + bank; plat->bank_name[2] = '\0'; plat->gpio_count = MTK_BANK_WIDTH; plat->bank = bank; ret = device_bind(parent, parent->driver, plat->bank_name, plat, node, &dev); if (ret) return ret; bank++; } return 0; } static const struct udevice_id mediatek_gpio_ids[] = { { .compatible = "mtk,mt7621-gpio" }, { } }; U_BOOT_DRIVER(gpio_mediatek) = { .name = "gpio_mediatek", .id = UCLASS_GPIO, .ops = &gpio_mediatek_ops, .of_match = mediatek_gpio_ids, .bind = gpio_mediatek_bind, .probe = gpio_mediatek_probe, };