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 8*9c8224d0SAndy Shevchenko #include <linux/bitops.h> 99af4d80bSFabian Vogt #include <linux/errno.h> 10a90295b4SPaul Gortmaker #include <linux/init.h> 119af4d80bSFabian Vogt #include <linux/io.h> 12*9c8224d0SAndy Shevchenko #include <linux/mod_devicetable.h> 139af4d80bSFabian Vogt #include <linux/slab.h> 14*9c8224d0SAndy Shevchenko #include <linux/spinlock.h> 15*9c8224d0SAndy Shevchenko 16f0916167SLinus Walleij #include <linux/gpio/driver.h> 179af4d80bSFabian Vogt 189af4d80bSFabian Vogt /* 199af4d80bSFabian Vogt * Memory layout: 209af4d80bSFabian Vogt * This chip has four gpio sections, each controls 8 GPIOs. 219af4d80bSFabian Vogt * Bit 0 in section 0 is GPIO 0, bit 2 in section 1 is GPIO 10. 229af4d80bSFabian Vogt * Disclaimer: Reverse engineered! 239af4d80bSFabian Vogt * For more information refer to: 249af4d80bSFabian Vogt * http://hackspire.unsads.com/wiki/index.php/Memory-mapped_I/O_ports#90000000_-_General_Purpose_I.2FO_.28GPIO.29 259af4d80bSFabian Vogt * 269af4d80bSFabian Vogt * 0x00-0x3F: Section 0 279af4d80bSFabian Vogt * +0x00: Masked interrupt status (read-only) 289af4d80bSFabian Vogt * +0x04: R: Interrupt status W: Reset interrupt status 299af4d80bSFabian Vogt * +0x08: R: Interrupt mask W: Mask interrupt 309af4d80bSFabian Vogt * +0x0C: W: Unmask interrupt (write-only) 319af4d80bSFabian Vogt * +0x10: Direction: I/O=1/0 329af4d80bSFabian Vogt * +0x14: Output 339af4d80bSFabian Vogt * +0x18: Input (read-only) 349af4d80bSFabian Vogt * +0x20: R: Level interrupt W: Set as level interrupt 359af4d80bSFabian Vogt * 0x40-0x7F: Section 1 369af4d80bSFabian Vogt * 0x80-0xBF: Section 2 379af4d80bSFabian Vogt * 0xC0-0xFF: Section 3 389af4d80bSFabian Vogt */ 399af4d80bSFabian Vogt 409af4d80bSFabian Vogt #define ZEVIO_GPIO_SECTION_SIZE 0x40 419af4d80bSFabian Vogt 429af4d80bSFabian Vogt /* Offsets to various registers */ 439af4d80bSFabian Vogt #define ZEVIO_GPIO_INT_MASKED_STATUS 0x00 449af4d80bSFabian Vogt #define ZEVIO_GPIO_INT_STATUS 0x04 459af4d80bSFabian Vogt #define ZEVIO_GPIO_INT_UNMASK 0x08 469af4d80bSFabian Vogt #define ZEVIO_GPIO_INT_MASK 0x0C 479af4d80bSFabian Vogt #define ZEVIO_GPIO_DIRECTION 0x10 489af4d80bSFabian Vogt #define ZEVIO_GPIO_OUTPUT 0x14 499af4d80bSFabian Vogt #define ZEVIO_GPIO_INPUT 0x18 509af4d80bSFabian Vogt #define ZEVIO_GPIO_INT_STICKY 0x20 519af4d80bSFabian Vogt 529af4d80bSFabian Vogt /* Bit number of GPIO in its section */ 539af4d80bSFabian Vogt #define ZEVIO_GPIO_BIT(gpio) (gpio&7) 549af4d80bSFabian Vogt 559af4d80bSFabian Vogt struct zevio_gpio { 56cf8f4462SMoses Christopher Bollavarapu struct gpio_chip chip; 579af4d80bSFabian Vogt spinlock_t lock; 58cf8f4462SMoses Christopher Bollavarapu void __iomem *regs; 599af4d80bSFabian Vogt }; 609af4d80bSFabian Vogt 619af4d80bSFabian Vogt static inline u32 zevio_gpio_port_get(struct zevio_gpio *c, unsigned pin, 629af4d80bSFabian Vogt unsigned port_offset) 639af4d80bSFabian Vogt { 649af4d80bSFabian Vogt unsigned section_offset = ((pin >> 3) & 3)*ZEVIO_GPIO_SECTION_SIZE; 65cf8f4462SMoses Christopher Bollavarapu return readl(IOMEM(c->regs + section_offset + port_offset)); 669af4d80bSFabian Vogt } 679af4d80bSFabian Vogt 689af4d80bSFabian Vogt static inline void zevio_gpio_port_set(struct zevio_gpio *c, unsigned pin, 699af4d80bSFabian Vogt unsigned port_offset, u32 val) 709af4d80bSFabian Vogt { 719af4d80bSFabian Vogt unsigned section_offset = ((pin >> 3) & 3)*ZEVIO_GPIO_SECTION_SIZE; 72cf8f4462SMoses Christopher Bollavarapu writel(val, IOMEM(c->regs + section_offset + port_offset)); 739af4d80bSFabian Vogt } 749af4d80bSFabian Vogt 759af4d80bSFabian Vogt /* Functions for struct gpio_chip */ 769af4d80bSFabian Vogt static int zevio_gpio_get(struct gpio_chip *chip, unsigned pin) 779af4d80bSFabian Vogt { 789a3ad668SLinus Walleij struct zevio_gpio *controller = gpiochip_get_data(chip); 79ef7de262SAxel Lin u32 val, dir; 809af4d80bSFabian Vogt 81ef7de262SAxel Lin spin_lock(&controller->lock); 82ef7de262SAxel Lin dir = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_DIRECTION); 83ef7de262SAxel Lin if (dir & BIT(ZEVIO_GPIO_BIT(pin))) 84ef7de262SAxel Lin val = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_INPUT); 85ef7de262SAxel Lin else 86ef7de262SAxel Lin val = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_OUTPUT); 87ef7de262SAxel Lin spin_unlock(&controller->lock); 889af4d80bSFabian Vogt 899af4d80bSFabian Vogt return (val >> ZEVIO_GPIO_BIT(pin)) & 0x1; 909af4d80bSFabian Vogt } 919af4d80bSFabian Vogt 929af4d80bSFabian Vogt static void zevio_gpio_set(struct gpio_chip *chip, unsigned pin, int value) 939af4d80bSFabian Vogt { 949a3ad668SLinus Walleij struct zevio_gpio *controller = gpiochip_get_data(chip); 959af4d80bSFabian Vogt u32 val; 969af4d80bSFabian Vogt 979af4d80bSFabian Vogt spin_lock(&controller->lock); 989af4d80bSFabian Vogt val = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_OUTPUT); 999af4d80bSFabian Vogt if (value) 1009af4d80bSFabian Vogt val |= BIT(ZEVIO_GPIO_BIT(pin)); 1019af4d80bSFabian Vogt else 1029af4d80bSFabian Vogt val &= ~BIT(ZEVIO_GPIO_BIT(pin)); 1039af4d80bSFabian Vogt 1049af4d80bSFabian Vogt zevio_gpio_port_set(controller, pin, ZEVIO_GPIO_OUTPUT, val); 1059af4d80bSFabian Vogt spin_unlock(&controller->lock); 1069af4d80bSFabian Vogt } 1079af4d80bSFabian Vogt 1089af4d80bSFabian Vogt static int zevio_gpio_direction_input(struct gpio_chip *chip, unsigned pin) 1099af4d80bSFabian Vogt { 1109a3ad668SLinus Walleij struct zevio_gpio *controller = gpiochip_get_data(chip); 1119af4d80bSFabian Vogt u32 val; 1129af4d80bSFabian Vogt 1139af4d80bSFabian Vogt spin_lock(&controller->lock); 1149af4d80bSFabian Vogt 1159af4d80bSFabian Vogt val = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_DIRECTION); 1169af4d80bSFabian Vogt val |= BIT(ZEVIO_GPIO_BIT(pin)); 1179af4d80bSFabian Vogt zevio_gpio_port_set(controller, pin, ZEVIO_GPIO_DIRECTION, val); 1189af4d80bSFabian Vogt 1199af4d80bSFabian Vogt spin_unlock(&controller->lock); 1209af4d80bSFabian Vogt 1219af4d80bSFabian Vogt return 0; 1229af4d80bSFabian Vogt } 1239af4d80bSFabian Vogt 1249af4d80bSFabian Vogt static int zevio_gpio_direction_output(struct gpio_chip *chip, 1259af4d80bSFabian Vogt unsigned pin, int value) 1269af4d80bSFabian Vogt { 1279a3ad668SLinus Walleij struct zevio_gpio *controller = gpiochip_get_data(chip); 1289af4d80bSFabian Vogt u32 val; 1299af4d80bSFabian Vogt 1309af4d80bSFabian Vogt spin_lock(&controller->lock); 1319af4d80bSFabian Vogt val = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_OUTPUT); 1329af4d80bSFabian Vogt if (value) 1339af4d80bSFabian Vogt val |= BIT(ZEVIO_GPIO_BIT(pin)); 1349af4d80bSFabian Vogt else 1359af4d80bSFabian Vogt val &= ~BIT(ZEVIO_GPIO_BIT(pin)); 1369af4d80bSFabian Vogt 1379af4d80bSFabian Vogt zevio_gpio_port_set(controller, pin, ZEVIO_GPIO_OUTPUT, val); 1389af4d80bSFabian Vogt val = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_DIRECTION); 1399af4d80bSFabian Vogt val &= ~BIT(ZEVIO_GPIO_BIT(pin)); 1409af4d80bSFabian Vogt zevio_gpio_port_set(controller, pin, ZEVIO_GPIO_DIRECTION, val); 1419af4d80bSFabian Vogt 1429af4d80bSFabian Vogt spin_unlock(&controller->lock); 1439af4d80bSFabian Vogt 1449af4d80bSFabian Vogt return 0; 1459af4d80bSFabian Vogt } 1469af4d80bSFabian Vogt 1479af4d80bSFabian Vogt static int zevio_gpio_to_irq(struct gpio_chip *chip, unsigned pin) 1489af4d80bSFabian Vogt { 1499af4d80bSFabian Vogt /* 1509af4d80bSFabian Vogt * TODO: Implement IRQs. 1519af4d80bSFabian Vogt * Not implemented yet due to weird lockups 1529af4d80bSFabian Vogt */ 1539af4d80bSFabian Vogt 1549af4d80bSFabian Vogt return -ENXIO; 1559af4d80bSFabian Vogt } 1569af4d80bSFabian Vogt 1574a14af45SBhumika Goyal static const struct gpio_chip zevio_gpio_chip = { 1589af4d80bSFabian Vogt .direction_input = zevio_gpio_direction_input, 1599af4d80bSFabian Vogt .direction_output = zevio_gpio_direction_output, 1609af4d80bSFabian Vogt .set = zevio_gpio_set, 1619af4d80bSFabian Vogt .get = zevio_gpio_get, 1629af4d80bSFabian Vogt .to_irq = zevio_gpio_to_irq, 1639af4d80bSFabian Vogt .base = 0, 1649af4d80bSFabian Vogt .owner = THIS_MODULE, 1659af4d80bSFabian Vogt .ngpio = 32, 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 */ 181cf8f4462SMoses Christopher Bollavarapu controller->chip = zevio_gpio_chip; 182cf8f4462SMoses Christopher Bollavarapu controller->chip.parent = &pdev->dev; 1839af4d80bSFabian Vogt 184cf8f4462SMoses Christopher Bollavarapu controller->regs = devm_platform_ioremap_resource(pdev, 0); 185cf8f4462SMoses Christopher Bollavarapu if (IS_ERR(controller->regs)) 186cf8f4462SMoses Christopher Bollavarapu return dev_err_probe(&pdev->dev, PTR_ERR(controller->regs), 187cf8f4462SMoses Christopher Bollavarapu "failed to ioremap memory resource\n"); 188cf8f4462SMoses Christopher Bollavarapu 189cf8f4462SMoses 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 */ 198cf8f4462SMoses 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 201cf8f4462SMoses 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