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