19af4d80bSFabian Vogt /* 29af4d80bSFabian Vogt * GPIO controller in LSI ZEVIO SoCs. 39af4d80bSFabian Vogt * 49af4d80bSFabian Vogt * Author: Fabian Vogt <fabian@ritter-vogt.de> 59af4d80bSFabian Vogt * 69af4d80bSFabian Vogt * This program is free software; you can redistribute it and/or modify 79af4d80bSFabian Vogt * it under the terms of the GNU General Public License version 2 as 89af4d80bSFabian Vogt * published by the Free Software Foundation. 99af4d80bSFabian Vogt */ 109af4d80bSFabian Vogt 119af4d80bSFabian Vogt #include <linux/spinlock.h> 129af4d80bSFabian Vogt #include <linux/errno.h> 139af4d80bSFabian Vogt #include <linux/module.h> 149af4d80bSFabian Vogt #include <linux/bitops.h> 159af4d80bSFabian Vogt #include <linux/io.h> 169af4d80bSFabian Vogt #include <linux/of_device.h> 179af4d80bSFabian Vogt #include <linux/of_gpio.h> 189af4d80bSFabian Vogt #include <linux/slab.h> 199af4d80bSFabian Vogt #include <linux/gpio.h> 209af4d80bSFabian Vogt 219af4d80bSFabian Vogt /* 229af4d80bSFabian Vogt * Memory layout: 239af4d80bSFabian Vogt * This chip has four gpio sections, each controls 8 GPIOs. 249af4d80bSFabian Vogt * Bit 0 in section 0 is GPIO 0, bit 2 in section 1 is GPIO 10. 259af4d80bSFabian Vogt * Disclaimer: Reverse engineered! 269af4d80bSFabian Vogt * For more information refer to: 279af4d80bSFabian Vogt * http://hackspire.unsads.com/wiki/index.php/Memory-mapped_I/O_ports#90000000_-_General_Purpose_I.2FO_.28GPIO.29 289af4d80bSFabian Vogt * 299af4d80bSFabian Vogt * 0x00-0x3F: Section 0 309af4d80bSFabian Vogt * +0x00: Masked interrupt status (read-only) 319af4d80bSFabian Vogt * +0x04: R: Interrupt status W: Reset interrupt status 329af4d80bSFabian Vogt * +0x08: R: Interrupt mask W: Mask interrupt 339af4d80bSFabian Vogt * +0x0C: W: Unmask interrupt (write-only) 349af4d80bSFabian Vogt * +0x10: Direction: I/O=1/0 359af4d80bSFabian Vogt * +0x14: Output 369af4d80bSFabian Vogt * +0x18: Input (read-only) 379af4d80bSFabian Vogt * +0x20: R: Level interrupt W: Set as level interrupt 389af4d80bSFabian Vogt * 0x40-0x7F: Section 1 399af4d80bSFabian Vogt * 0x80-0xBF: Section 2 409af4d80bSFabian Vogt * 0xC0-0xFF: Section 3 419af4d80bSFabian Vogt */ 429af4d80bSFabian Vogt 439af4d80bSFabian Vogt #define ZEVIO_GPIO_SECTION_SIZE 0x40 449af4d80bSFabian Vogt 459af4d80bSFabian Vogt /* Offsets to various registers */ 469af4d80bSFabian Vogt #define ZEVIO_GPIO_INT_MASKED_STATUS 0x00 479af4d80bSFabian Vogt #define ZEVIO_GPIO_INT_STATUS 0x04 489af4d80bSFabian Vogt #define ZEVIO_GPIO_INT_UNMASK 0x08 499af4d80bSFabian Vogt #define ZEVIO_GPIO_INT_MASK 0x0C 509af4d80bSFabian Vogt #define ZEVIO_GPIO_DIRECTION 0x10 519af4d80bSFabian Vogt #define ZEVIO_GPIO_OUTPUT 0x14 529af4d80bSFabian Vogt #define ZEVIO_GPIO_INPUT 0x18 539af4d80bSFabian Vogt #define ZEVIO_GPIO_INT_STICKY 0x20 549af4d80bSFabian Vogt 559af4d80bSFabian Vogt /* Bit number of GPIO in its section */ 569af4d80bSFabian Vogt #define ZEVIO_GPIO_BIT(gpio) (gpio&7) 579af4d80bSFabian Vogt 589af4d80bSFabian Vogt struct zevio_gpio { 599af4d80bSFabian Vogt spinlock_t lock; 609af4d80bSFabian Vogt struct of_mm_gpio_chip chip; 619af4d80bSFabian Vogt }; 629af4d80bSFabian Vogt 639af4d80bSFabian Vogt static inline u32 zevio_gpio_port_get(struct zevio_gpio *c, unsigned pin, 649af4d80bSFabian Vogt unsigned port_offset) 659af4d80bSFabian Vogt { 669af4d80bSFabian Vogt unsigned section_offset = ((pin >> 3) & 3)*ZEVIO_GPIO_SECTION_SIZE; 679af4d80bSFabian Vogt return readl(IOMEM(c->chip.regs + section_offset + port_offset)); 689af4d80bSFabian Vogt } 699af4d80bSFabian Vogt 709af4d80bSFabian Vogt static inline void zevio_gpio_port_set(struct zevio_gpio *c, unsigned pin, 719af4d80bSFabian Vogt unsigned port_offset, u32 val) 729af4d80bSFabian Vogt { 739af4d80bSFabian Vogt unsigned section_offset = ((pin >> 3) & 3)*ZEVIO_GPIO_SECTION_SIZE; 749af4d80bSFabian Vogt writel(val, IOMEM(c->chip.regs + section_offset + port_offset)); 759af4d80bSFabian Vogt } 769af4d80bSFabian Vogt 779af4d80bSFabian Vogt /* Functions for struct gpio_chip */ 789af4d80bSFabian Vogt static int zevio_gpio_get(struct gpio_chip *chip, unsigned pin) 799af4d80bSFabian Vogt { 809a3ad668SLinus Walleij struct zevio_gpio *controller = gpiochip_get_data(chip); 81ef7de262SAxel Lin u32 val, dir; 829af4d80bSFabian Vogt 83ef7de262SAxel Lin spin_lock(&controller->lock); 84ef7de262SAxel Lin dir = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_DIRECTION); 85ef7de262SAxel Lin if (dir & BIT(ZEVIO_GPIO_BIT(pin))) 86ef7de262SAxel Lin val = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_INPUT); 87ef7de262SAxel Lin else 88ef7de262SAxel Lin val = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_OUTPUT); 89ef7de262SAxel Lin spin_unlock(&controller->lock); 909af4d80bSFabian Vogt 919af4d80bSFabian Vogt return (val >> ZEVIO_GPIO_BIT(pin)) & 0x1; 929af4d80bSFabian Vogt } 939af4d80bSFabian Vogt 949af4d80bSFabian Vogt static void zevio_gpio_set(struct gpio_chip *chip, unsigned pin, int value) 959af4d80bSFabian Vogt { 969a3ad668SLinus Walleij struct zevio_gpio *controller = gpiochip_get_data(chip); 979af4d80bSFabian Vogt u32 val; 989af4d80bSFabian Vogt 999af4d80bSFabian Vogt spin_lock(&controller->lock); 1009af4d80bSFabian Vogt val = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_OUTPUT); 1019af4d80bSFabian Vogt if (value) 1029af4d80bSFabian Vogt val |= BIT(ZEVIO_GPIO_BIT(pin)); 1039af4d80bSFabian Vogt else 1049af4d80bSFabian Vogt val &= ~BIT(ZEVIO_GPIO_BIT(pin)); 1059af4d80bSFabian Vogt 1069af4d80bSFabian Vogt zevio_gpio_port_set(controller, pin, ZEVIO_GPIO_OUTPUT, val); 1079af4d80bSFabian Vogt spin_unlock(&controller->lock); 1089af4d80bSFabian Vogt } 1099af4d80bSFabian Vogt 1109af4d80bSFabian Vogt static int zevio_gpio_direction_input(struct gpio_chip *chip, unsigned pin) 1119af4d80bSFabian Vogt { 1129a3ad668SLinus Walleij struct zevio_gpio *controller = gpiochip_get_data(chip); 1139af4d80bSFabian Vogt u32 val; 1149af4d80bSFabian Vogt 1159af4d80bSFabian Vogt spin_lock(&controller->lock); 1169af4d80bSFabian Vogt 1179af4d80bSFabian Vogt val = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_DIRECTION); 1189af4d80bSFabian Vogt val |= BIT(ZEVIO_GPIO_BIT(pin)); 1199af4d80bSFabian Vogt zevio_gpio_port_set(controller, pin, ZEVIO_GPIO_DIRECTION, val); 1209af4d80bSFabian Vogt 1219af4d80bSFabian Vogt spin_unlock(&controller->lock); 1229af4d80bSFabian Vogt 1239af4d80bSFabian Vogt return 0; 1249af4d80bSFabian Vogt } 1259af4d80bSFabian Vogt 1269af4d80bSFabian Vogt static int zevio_gpio_direction_output(struct gpio_chip *chip, 1279af4d80bSFabian Vogt unsigned pin, int value) 1289af4d80bSFabian Vogt { 1299a3ad668SLinus Walleij struct zevio_gpio *controller = gpiochip_get_data(chip); 1309af4d80bSFabian Vogt u32 val; 1319af4d80bSFabian Vogt 1329af4d80bSFabian Vogt spin_lock(&controller->lock); 1339af4d80bSFabian Vogt val = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_OUTPUT); 1349af4d80bSFabian Vogt if (value) 1359af4d80bSFabian Vogt val |= BIT(ZEVIO_GPIO_BIT(pin)); 1369af4d80bSFabian Vogt else 1379af4d80bSFabian Vogt val &= ~BIT(ZEVIO_GPIO_BIT(pin)); 1389af4d80bSFabian Vogt 1399af4d80bSFabian Vogt zevio_gpio_port_set(controller, pin, ZEVIO_GPIO_OUTPUT, val); 1409af4d80bSFabian Vogt val = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_DIRECTION); 1419af4d80bSFabian Vogt val &= ~BIT(ZEVIO_GPIO_BIT(pin)); 1429af4d80bSFabian Vogt zevio_gpio_port_set(controller, pin, ZEVIO_GPIO_DIRECTION, val); 1439af4d80bSFabian Vogt 1449af4d80bSFabian Vogt spin_unlock(&controller->lock); 1459af4d80bSFabian Vogt 1469af4d80bSFabian Vogt return 0; 1479af4d80bSFabian Vogt } 1489af4d80bSFabian Vogt 1499af4d80bSFabian Vogt static int zevio_gpio_to_irq(struct gpio_chip *chip, unsigned pin) 1509af4d80bSFabian Vogt { 1519af4d80bSFabian Vogt /* 1529af4d80bSFabian Vogt * TODO: Implement IRQs. 1539af4d80bSFabian Vogt * Not implemented yet due to weird lockups 1549af4d80bSFabian Vogt */ 1559af4d80bSFabian Vogt 1569af4d80bSFabian Vogt return -ENXIO; 1579af4d80bSFabian Vogt } 1589af4d80bSFabian Vogt 1599af4d80bSFabian Vogt static struct gpio_chip zevio_gpio_chip = { 1609af4d80bSFabian Vogt .direction_input = zevio_gpio_direction_input, 1619af4d80bSFabian Vogt .direction_output = zevio_gpio_direction_output, 1629af4d80bSFabian Vogt .set = zevio_gpio_set, 1639af4d80bSFabian Vogt .get = zevio_gpio_get, 1649af4d80bSFabian Vogt .to_irq = zevio_gpio_to_irq, 1659af4d80bSFabian Vogt .base = 0, 1669af4d80bSFabian Vogt .owner = THIS_MODULE, 1679af4d80bSFabian Vogt .ngpio = 32, 1689af4d80bSFabian Vogt .of_gpio_n_cells = 2, 1699af4d80bSFabian Vogt }; 1709af4d80bSFabian Vogt 1719af4d80bSFabian Vogt /* Initialization */ 1729af4d80bSFabian Vogt static int zevio_gpio_probe(struct platform_device *pdev) 1739af4d80bSFabian Vogt { 1749af4d80bSFabian Vogt struct zevio_gpio *controller; 1759af4d80bSFabian Vogt int status, i; 1769af4d80bSFabian Vogt 1779af4d80bSFabian Vogt controller = devm_kzalloc(&pdev->dev, sizeof(*controller), GFP_KERNEL); 17850908d61SJingoo Han if (!controller) 1799af4d80bSFabian Vogt return -ENOMEM; 1809af4d80bSFabian Vogt 181ff00be69SRicardo Ribalda Delgado platform_set_drvdata(pdev, controller); 182ff00be69SRicardo Ribalda Delgado 1839af4d80bSFabian Vogt /* Copy our reference */ 1849af4d80bSFabian Vogt controller->chip.gc = zevio_gpio_chip; 18558383c78SLinus Walleij controller->chip.gc.parent = &pdev->dev; 1869af4d80bSFabian Vogt 1879a3ad668SLinus Walleij status = of_mm_gpiochip_add_data(pdev->dev.of_node, 1889a3ad668SLinus Walleij &(controller->chip), 1899a3ad668SLinus Walleij 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 */ 1989af4d80bSFabian Vogt for (i = 0; i < controller->chip.gc.ngpio; i += 8) 1999af4d80bSFabian Vogt zevio_gpio_port_set(controller, i, ZEVIO_GPIO_INT_MASK, 0xFF); 2009af4d80bSFabian Vogt 20158383c78SLinus Walleij dev_dbg(controller->chip.gc.parent, "ZEVIO GPIO controller set up!\n"); 2029af4d80bSFabian Vogt 2039af4d80bSFabian Vogt return 0; 2049af4d80bSFabian Vogt } 2059af4d80bSFabian Vogt 206ff00be69SRicardo Ribalda Delgado static int zevio_gpio_remove(struct platform_device *pdev) 207ff00be69SRicardo Ribalda Delgado { 208ff00be69SRicardo Ribalda Delgado struct zevio_gpio *controller = platform_get_drvdata(pdev); 209ff00be69SRicardo Ribalda Delgado 210ff00be69SRicardo Ribalda Delgado of_mm_gpiochip_remove(&controller->chip); 211ff00be69SRicardo Ribalda Delgado 212ff00be69SRicardo Ribalda Delgado return 0; 213ff00be69SRicardo Ribalda Delgado } 214ff00be69SRicardo Ribalda Delgado 215a49f2e74SJingoo Han static const struct of_device_id zevio_gpio_of_match[] = { 2169af4d80bSFabian Vogt { .compatible = "lsi,zevio-gpio", }, 2179af4d80bSFabian Vogt { }, 2189af4d80bSFabian Vogt }; 2199af4d80bSFabian Vogt 2209af4d80bSFabian Vogt MODULE_DEVICE_TABLE(of, zevio_gpio_of_match); 2219af4d80bSFabian Vogt 2229af4d80bSFabian Vogt static struct platform_driver zevio_gpio_driver = { 2239af4d80bSFabian Vogt .driver = { 2249af4d80bSFabian Vogt .name = "gpio-zevio", 2259ea8d810SAxel Lin .of_match_table = zevio_gpio_of_match, 2269af4d80bSFabian Vogt }, 2279af4d80bSFabian Vogt .probe = zevio_gpio_probe, 228ff00be69SRicardo Ribalda Delgado .remove = zevio_gpio_remove, 2299af4d80bSFabian Vogt }; 2309af4d80bSFabian Vogt module_platform_driver(zevio_gpio_driver); 2319af4d80bSFabian Vogt 2329af4d80bSFabian Vogt MODULE_LICENSE("GPL"); 2339af4d80bSFabian Vogt MODULE_AUTHOR("Fabian Vogt <fabian@ritter-vogt.de>"); 2349af4d80bSFabian Vogt MODULE_DESCRIPTION("LSI ZEVIO SoC GPIO driver"); 235