1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 29af4d80bSFabian Vogt /* 39af4d80bSFabian Vogt * GPIO controller in LSI ZEVIO SoCs. 49af4d80bSFabian Vogt * 59af4d80bSFabian Vogt * Author: Fabian Vogt <fabian@ritter-vogt.de> 69af4d80bSFabian Vogt */ 79af4d80bSFabian Vogt 89af4d80bSFabian Vogt #include <linux/spinlock.h> 99af4d80bSFabian Vogt #include <linux/errno.h> 10a90295b4SPaul Gortmaker #include <linux/init.h> 119af4d80bSFabian Vogt #include <linux/bitops.h> 129af4d80bSFabian Vogt #include <linux/io.h> 139af4d80bSFabian Vogt #include <linux/of_device.h> 149af4d80bSFabian Vogt #include <linux/slab.h> 15f0916167SLinus Walleij #include <linux/gpio/driver.h> 169af4d80bSFabian Vogt 179af4d80bSFabian Vogt /* 189af4d80bSFabian Vogt * Memory layout: 199af4d80bSFabian Vogt * This chip has four gpio sections, each controls 8 GPIOs. 209af4d80bSFabian Vogt * Bit 0 in section 0 is GPIO 0, bit 2 in section 1 is GPIO 10. 219af4d80bSFabian Vogt * Disclaimer: Reverse engineered! 229af4d80bSFabian Vogt * For more information refer to: 239af4d80bSFabian Vogt * http://hackspire.unsads.com/wiki/index.php/Memory-mapped_I/O_ports#90000000_-_General_Purpose_I.2FO_.28GPIO.29 249af4d80bSFabian Vogt * 259af4d80bSFabian Vogt * 0x00-0x3F: Section 0 269af4d80bSFabian Vogt * +0x00: Masked interrupt status (read-only) 279af4d80bSFabian Vogt * +0x04: R: Interrupt status W: Reset interrupt status 289af4d80bSFabian Vogt * +0x08: R: Interrupt mask W: Mask interrupt 299af4d80bSFabian Vogt * +0x0C: W: Unmask interrupt (write-only) 309af4d80bSFabian Vogt * +0x10: Direction: I/O=1/0 319af4d80bSFabian Vogt * +0x14: Output 329af4d80bSFabian Vogt * +0x18: Input (read-only) 339af4d80bSFabian Vogt * +0x20: R: Level interrupt W: Set as level interrupt 349af4d80bSFabian Vogt * 0x40-0x7F: Section 1 359af4d80bSFabian Vogt * 0x80-0xBF: Section 2 369af4d80bSFabian Vogt * 0xC0-0xFF: Section 3 379af4d80bSFabian Vogt */ 389af4d80bSFabian Vogt 399af4d80bSFabian Vogt #define ZEVIO_GPIO_SECTION_SIZE 0x40 409af4d80bSFabian Vogt 419af4d80bSFabian Vogt /* Offsets to various registers */ 429af4d80bSFabian Vogt #define ZEVIO_GPIO_INT_MASKED_STATUS 0x00 439af4d80bSFabian Vogt #define ZEVIO_GPIO_INT_STATUS 0x04 449af4d80bSFabian Vogt #define ZEVIO_GPIO_INT_UNMASK 0x08 459af4d80bSFabian Vogt #define ZEVIO_GPIO_INT_MASK 0x0C 469af4d80bSFabian Vogt #define ZEVIO_GPIO_DIRECTION 0x10 479af4d80bSFabian Vogt #define ZEVIO_GPIO_OUTPUT 0x14 489af4d80bSFabian Vogt #define ZEVIO_GPIO_INPUT 0x18 499af4d80bSFabian Vogt #define ZEVIO_GPIO_INT_STICKY 0x20 509af4d80bSFabian Vogt 519af4d80bSFabian Vogt /* Bit number of GPIO in its section */ 529af4d80bSFabian Vogt #define ZEVIO_GPIO_BIT(gpio) (gpio&7) 539af4d80bSFabian Vogt 549af4d80bSFabian Vogt struct zevio_gpio { 55*cf8f4462SMoses Christopher Bollavarapu struct gpio_chip chip; 569af4d80bSFabian Vogt spinlock_t lock; 57*cf8f4462SMoses Christopher Bollavarapu void __iomem *regs; 589af4d80bSFabian Vogt }; 599af4d80bSFabian Vogt 609af4d80bSFabian Vogt static inline u32 zevio_gpio_port_get(struct zevio_gpio *c, unsigned pin, 619af4d80bSFabian Vogt unsigned port_offset) 629af4d80bSFabian Vogt { 639af4d80bSFabian Vogt unsigned section_offset = ((pin >> 3) & 3)*ZEVIO_GPIO_SECTION_SIZE; 64*cf8f4462SMoses Christopher Bollavarapu return readl(IOMEM(c->regs + section_offset + port_offset)); 659af4d80bSFabian Vogt } 669af4d80bSFabian Vogt 679af4d80bSFabian Vogt static inline void zevio_gpio_port_set(struct zevio_gpio *c, unsigned pin, 689af4d80bSFabian Vogt unsigned port_offset, u32 val) 699af4d80bSFabian Vogt { 709af4d80bSFabian Vogt unsigned section_offset = ((pin >> 3) & 3)*ZEVIO_GPIO_SECTION_SIZE; 71*cf8f4462SMoses Christopher Bollavarapu writel(val, IOMEM(c->regs + section_offset + port_offset)); 729af4d80bSFabian Vogt } 739af4d80bSFabian Vogt 749af4d80bSFabian Vogt /* Functions for struct gpio_chip */ 759af4d80bSFabian Vogt static int zevio_gpio_get(struct gpio_chip *chip, unsigned pin) 769af4d80bSFabian Vogt { 779a3ad668SLinus Walleij struct zevio_gpio *controller = gpiochip_get_data(chip); 78ef7de262SAxel Lin u32 val, dir; 799af4d80bSFabian Vogt 80ef7de262SAxel Lin spin_lock(&controller->lock); 81ef7de262SAxel Lin dir = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_DIRECTION); 82ef7de262SAxel Lin if (dir & BIT(ZEVIO_GPIO_BIT(pin))) 83ef7de262SAxel Lin val = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_INPUT); 84ef7de262SAxel Lin else 85ef7de262SAxel Lin val = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_OUTPUT); 86ef7de262SAxel Lin spin_unlock(&controller->lock); 879af4d80bSFabian Vogt 889af4d80bSFabian Vogt return (val >> ZEVIO_GPIO_BIT(pin)) & 0x1; 899af4d80bSFabian Vogt } 909af4d80bSFabian Vogt 919af4d80bSFabian Vogt static void zevio_gpio_set(struct gpio_chip *chip, unsigned pin, int value) 929af4d80bSFabian Vogt { 939a3ad668SLinus Walleij struct zevio_gpio *controller = gpiochip_get_data(chip); 949af4d80bSFabian Vogt u32 val; 959af4d80bSFabian Vogt 969af4d80bSFabian Vogt spin_lock(&controller->lock); 979af4d80bSFabian Vogt val = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_OUTPUT); 989af4d80bSFabian Vogt if (value) 999af4d80bSFabian Vogt val |= BIT(ZEVIO_GPIO_BIT(pin)); 1009af4d80bSFabian Vogt else 1019af4d80bSFabian Vogt val &= ~BIT(ZEVIO_GPIO_BIT(pin)); 1029af4d80bSFabian Vogt 1039af4d80bSFabian Vogt zevio_gpio_port_set(controller, pin, ZEVIO_GPIO_OUTPUT, val); 1049af4d80bSFabian Vogt spin_unlock(&controller->lock); 1059af4d80bSFabian Vogt } 1069af4d80bSFabian Vogt 1079af4d80bSFabian Vogt static int zevio_gpio_direction_input(struct gpio_chip *chip, unsigned pin) 1089af4d80bSFabian Vogt { 1099a3ad668SLinus Walleij struct zevio_gpio *controller = gpiochip_get_data(chip); 1109af4d80bSFabian Vogt u32 val; 1119af4d80bSFabian Vogt 1129af4d80bSFabian Vogt spin_lock(&controller->lock); 1139af4d80bSFabian Vogt 1149af4d80bSFabian Vogt val = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_DIRECTION); 1159af4d80bSFabian Vogt val |= BIT(ZEVIO_GPIO_BIT(pin)); 1169af4d80bSFabian Vogt zevio_gpio_port_set(controller, pin, ZEVIO_GPIO_DIRECTION, val); 1179af4d80bSFabian Vogt 1189af4d80bSFabian Vogt spin_unlock(&controller->lock); 1199af4d80bSFabian Vogt 1209af4d80bSFabian Vogt return 0; 1219af4d80bSFabian Vogt } 1229af4d80bSFabian Vogt 1239af4d80bSFabian Vogt static int zevio_gpio_direction_output(struct gpio_chip *chip, 1249af4d80bSFabian Vogt unsigned pin, int value) 1259af4d80bSFabian Vogt { 1269a3ad668SLinus Walleij struct zevio_gpio *controller = gpiochip_get_data(chip); 1279af4d80bSFabian Vogt u32 val; 1289af4d80bSFabian Vogt 1299af4d80bSFabian Vogt spin_lock(&controller->lock); 1309af4d80bSFabian Vogt val = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_OUTPUT); 1319af4d80bSFabian Vogt if (value) 1329af4d80bSFabian Vogt val |= BIT(ZEVIO_GPIO_BIT(pin)); 1339af4d80bSFabian Vogt else 1349af4d80bSFabian Vogt val &= ~BIT(ZEVIO_GPIO_BIT(pin)); 1359af4d80bSFabian Vogt 1369af4d80bSFabian Vogt zevio_gpio_port_set(controller, pin, ZEVIO_GPIO_OUTPUT, val); 1379af4d80bSFabian Vogt val = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_DIRECTION); 1389af4d80bSFabian Vogt val &= ~BIT(ZEVIO_GPIO_BIT(pin)); 1399af4d80bSFabian Vogt zevio_gpio_port_set(controller, pin, ZEVIO_GPIO_DIRECTION, val); 1409af4d80bSFabian Vogt 1419af4d80bSFabian Vogt spin_unlock(&controller->lock); 1429af4d80bSFabian Vogt 1439af4d80bSFabian Vogt return 0; 1449af4d80bSFabian Vogt } 1459af4d80bSFabian Vogt 1469af4d80bSFabian Vogt static int zevio_gpio_to_irq(struct gpio_chip *chip, unsigned pin) 1479af4d80bSFabian Vogt { 1489af4d80bSFabian Vogt /* 1499af4d80bSFabian Vogt * TODO: Implement IRQs. 1509af4d80bSFabian Vogt * Not implemented yet due to weird lockups 1519af4d80bSFabian Vogt */ 1529af4d80bSFabian Vogt 1539af4d80bSFabian Vogt return -ENXIO; 1549af4d80bSFabian Vogt } 1559af4d80bSFabian Vogt 1564a14af45SBhumika Goyal static const struct gpio_chip zevio_gpio_chip = { 1579af4d80bSFabian Vogt .direction_input = zevio_gpio_direction_input, 1589af4d80bSFabian Vogt .direction_output = zevio_gpio_direction_output, 1599af4d80bSFabian Vogt .set = zevio_gpio_set, 1609af4d80bSFabian Vogt .get = zevio_gpio_get, 1619af4d80bSFabian Vogt .to_irq = zevio_gpio_to_irq, 1629af4d80bSFabian Vogt .base = 0, 1639af4d80bSFabian Vogt .owner = THIS_MODULE, 1649af4d80bSFabian Vogt .ngpio = 32, 1659af4d80bSFabian Vogt .of_gpio_n_cells = 2, 1669af4d80bSFabian Vogt }; 1679af4d80bSFabian Vogt 1689af4d80bSFabian Vogt /* Initialization */ 1699af4d80bSFabian Vogt static int zevio_gpio_probe(struct platform_device *pdev) 1709af4d80bSFabian Vogt { 1719af4d80bSFabian Vogt struct zevio_gpio *controller; 1729af4d80bSFabian Vogt int status, i; 1739af4d80bSFabian Vogt 1749af4d80bSFabian Vogt controller = devm_kzalloc(&pdev->dev, sizeof(*controller), GFP_KERNEL); 17550908d61SJingoo Han if (!controller) 1769af4d80bSFabian Vogt return -ENOMEM; 1779af4d80bSFabian Vogt 178ff00be69SRicardo Ribalda Delgado platform_set_drvdata(pdev, controller); 179ff00be69SRicardo Ribalda Delgado 1809af4d80bSFabian Vogt /* Copy our reference */ 181*cf8f4462SMoses Christopher Bollavarapu controller->chip = zevio_gpio_chip; 182*cf8f4462SMoses Christopher Bollavarapu controller->chip.parent = &pdev->dev; 1839af4d80bSFabian Vogt 184*cf8f4462SMoses Christopher Bollavarapu controller->regs = devm_platform_ioremap_resource(pdev, 0); 185*cf8f4462SMoses Christopher Bollavarapu if (IS_ERR(controller->regs)) 186*cf8f4462SMoses Christopher Bollavarapu return dev_err_probe(&pdev->dev, PTR_ERR(controller->regs), 187*cf8f4462SMoses Christopher Bollavarapu "failed to ioremap memory resource\n"); 188*cf8f4462SMoses Christopher Bollavarapu 189*cf8f4462SMoses Christopher Bollavarapu status = devm_gpiochip_add_data(&pdev->dev, &controller->chip, controller); 1909af4d80bSFabian Vogt if (status) { 1919af4d80bSFabian Vogt dev_err(&pdev->dev, "failed to add gpiochip: %d\n", status); 1929af4d80bSFabian Vogt return status; 1939af4d80bSFabian Vogt } 1949af4d80bSFabian Vogt 1959af4d80bSFabian Vogt spin_lock_init(&controller->lock); 1969af4d80bSFabian Vogt 1979af4d80bSFabian Vogt /* Disable interrupts, they only cause errors */ 198*cf8f4462SMoses Christopher Bollavarapu for (i = 0; i < controller->chip.ngpio; i += 8) 1999af4d80bSFabian Vogt zevio_gpio_port_set(controller, i, ZEVIO_GPIO_INT_MASK, 0xFF); 2009af4d80bSFabian Vogt 201*cf8f4462SMoses Christopher Bollavarapu dev_dbg(controller->chip.parent, "ZEVIO GPIO controller set up!\n"); 2029af4d80bSFabian Vogt 2039af4d80bSFabian Vogt return 0; 2049af4d80bSFabian Vogt } 2059af4d80bSFabian Vogt 206a49f2e74SJingoo Han static const struct of_device_id zevio_gpio_of_match[] = { 2079af4d80bSFabian Vogt { .compatible = "lsi,zevio-gpio", }, 2089af4d80bSFabian Vogt { }, 2099af4d80bSFabian Vogt }; 2109af4d80bSFabian Vogt 2119af4d80bSFabian Vogt static struct platform_driver zevio_gpio_driver = { 2129af4d80bSFabian Vogt .driver = { 2139af4d80bSFabian Vogt .name = "gpio-zevio", 2149ea8d810SAxel Lin .of_match_table = zevio_gpio_of_match, 215a90295b4SPaul Gortmaker .suppress_bind_attrs = true, 2169af4d80bSFabian Vogt }, 2179af4d80bSFabian Vogt .probe = zevio_gpio_probe, 2189af4d80bSFabian Vogt }; 219a90295b4SPaul Gortmaker builtin_platform_driver(zevio_gpio_driver); 220