18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * ZTE ZX296702 GPIO driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Author: Jun Nie <jun.nie@linaro.org> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (C) 2015 Linaro Ltd. 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci#include <linux/bitops.h> 108c2ecf20Sopenharmony_ci#include <linux/device.h> 118c2ecf20Sopenharmony_ci#include <linux/errno.h> 128c2ecf20Sopenharmony_ci#include <linux/gpio/driver.h> 138c2ecf20Sopenharmony_ci#include <linux/irqchip/chained_irq.h> 148c2ecf20Sopenharmony_ci#include <linux/init.h> 158c2ecf20Sopenharmony_ci#include <linux/of.h> 168c2ecf20Sopenharmony_ci#include <linux/pinctrl/consumer.h> 178c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 188c2ecf20Sopenharmony_ci#include <linux/pm.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define ZX_GPIO_DIR 0x00 238c2ecf20Sopenharmony_ci#define ZX_GPIO_IVE 0x04 248c2ecf20Sopenharmony_ci#define ZX_GPIO_IV 0x08 258c2ecf20Sopenharmony_ci#define ZX_GPIO_IEP 0x0C 268c2ecf20Sopenharmony_ci#define ZX_GPIO_IEN 0x10 278c2ecf20Sopenharmony_ci#define ZX_GPIO_DI 0x14 288c2ecf20Sopenharmony_ci#define ZX_GPIO_DO1 0x18 298c2ecf20Sopenharmony_ci#define ZX_GPIO_DO0 0x1C 308c2ecf20Sopenharmony_ci#define ZX_GPIO_DO 0x20 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define ZX_GPIO_IM 0x28 338c2ecf20Sopenharmony_ci#define ZX_GPIO_IE 0x2C 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define ZX_GPIO_MIS 0x30 368c2ecf20Sopenharmony_ci#define ZX_GPIO_IC 0x34 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define ZX_GPIO_NR 16 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistruct zx_gpio { 418c2ecf20Sopenharmony_ci raw_spinlock_t lock; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci void __iomem *base; 448c2ecf20Sopenharmony_ci struct gpio_chip gc; 458c2ecf20Sopenharmony_ci}; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic int zx_direction_input(struct gpio_chip *gc, unsigned offset) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci struct zx_gpio *chip = gpiochip_get_data(gc); 508c2ecf20Sopenharmony_ci unsigned long flags; 518c2ecf20Sopenharmony_ci u16 gpiodir; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci if (offset >= gc->ngpio) 548c2ecf20Sopenharmony_ci return -EINVAL; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&chip->lock, flags); 578c2ecf20Sopenharmony_ci gpiodir = readw_relaxed(chip->base + ZX_GPIO_DIR); 588c2ecf20Sopenharmony_ci gpiodir &= ~BIT(offset); 598c2ecf20Sopenharmony_ci writew_relaxed(gpiodir, chip->base + ZX_GPIO_DIR); 608c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&chip->lock, flags); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci return 0; 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic int zx_direction_output(struct gpio_chip *gc, unsigned offset, 668c2ecf20Sopenharmony_ci int value) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci struct zx_gpio *chip = gpiochip_get_data(gc); 698c2ecf20Sopenharmony_ci unsigned long flags; 708c2ecf20Sopenharmony_ci u16 gpiodir; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci if (offset >= gc->ngpio) 738c2ecf20Sopenharmony_ci return -EINVAL; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&chip->lock, flags); 768c2ecf20Sopenharmony_ci gpiodir = readw_relaxed(chip->base + ZX_GPIO_DIR); 778c2ecf20Sopenharmony_ci gpiodir |= BIT(offset); 788c2ecf20Sopenharmony_ci writew_relaxed(gpiodir, chip->base + ZX_GPIO_DIR); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci if (value) 818c2ecf20Sopenharmony_ci writew_relaxed(BIT(offset), chip->base + ZX_GPIO_DO1); 828c2ecf20Sopenharmony_ci else 838c2ecf20Sopenharmony_ci writew_relaxed(BIT(offset), chip->base + ZX_GPIO_DO0); 848c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&chip->lock, flags); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci return 0; 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic int zx_get_value(struct gpio_chip *gc, unsigned offset) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci struct zx_gpio *chip = gpiochip_get_data(gc); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci return !!(readw_relaxed(chip->base + ZX_GPIO_DI) & BIT(offset)); 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic void zx_set_value(struct gpio_chip *gc, unsigned offset, int value) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci struct zx_gpio *chip = gpiochip_get_data(gc); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci if (value) 1018c2ecf20Sopenharmony_ci writew_relaxed(BIT(offset), chip->base + ZX_GPIO_DO1); 1028c2ecf20Sopenharmony_ci else 1038c2ecf20Sopenharmony_ci writew_relaxed(BIT(offset), chip->base + ZX_GPIO_DO0); 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic int zx_irq_type(struct irq_data *d, unsigned trigger) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 1098c2ecf20Sopenharmony_ci struct zx_gpio *chip = gpiochip_get_data(gc); 1108c2ecf20Sopenharmony_ci int offset = irqd_to_hwirq(d); 1118c2ecf20Sopenharmony_ci unsigned long flags; 1128c2ecf20Sopenharmony_ci u16 gpiois, gpioi_epos, gpioi_eneg, gpioiev; 1138c2ecf20Sopenharmony_ci u16 bit = BIT(offset); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci if (offset < 0 || offset >= ZX_GPIO_NR) 1168c2ecf20Sopenharmony_ci return -EINVAL; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&chip->lock, flags); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci gpioiev = readw_relaxed(chip->base + ZX_GPIO_IV); 1218c2ecf20Sopenharmony_ci gpiois = readw_relaxed(chip->base + ZX_GPIO_IVE); 1228c2ecf20Sopenharmony_ci gpioi_epos = readw_relaxed(chip->base + ZX_GPIO_IEP); 1238c2ecf20Sopenharmony_ci gpioi_eneg = readw_relaxed(chip->base + ZX_GPIO_IEN); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci if (trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) { 1268c2ecf20Sopenharmony_ci gpiois |= bit; 1278c2ecf20Sopenharmony_ci if (trigger & IRQ_TYPE_LEVEL_HIGH) 1288c2ecf20Sopenharmony_ci gpioiev |= bit; 1298c2ecf20Sopenharmony_ci else 1308c2ecf20Sopenharmony_ci gpioiev &= ~bit; 1318c2ecf20Sopenharmony_ci } else 1328c2ecf20Sopenharmony_ci gpiois &= ~bit; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci if ((trigger & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) { 1358c2ecf20Sopenharmony_ci gpioi_epos |= bit; 1368c2ecf20Sopenharmony_ci gpioi_eneg |= bit; 1378c2ecf20Sopenharmony_ci } else { 1388c2ecf20Sopenharmony_ci if (trigger & IRQ_TYPE_EDGE_RISING) { 1398c2ecf20Sopenharmony_ci gpioi_epos |= bit; 1408c2ecf20Sopenharmony_ci gpioi_eneg &= ~bit; 1418c2ecf20Sopenharmony_ci } else if (trigger & IRQ_TYPE_EDGE_FALLING) { 1428c2ecf20Sopenharmony_ci gpioi_eneg |= bit; 1438c2ecf20Sopenharmony_ci gpioi_epos &= ~bit; 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci writew_relaxed(gpiois, chip->base + ZX_GPIO_IVE); 1488c2ecf20Sopenharmony_ci writew_relaxed(gpioi_epos, chip->base + ZX_GPIO_IEP); 1498c2ecf20Sopenharmony_ci writew_relaxed(gpioi_eneg, chip->base + ZX_GPIO_IEN); 1508c2ecf20Sopenharmony_ci writew_relaxed(gpioiev, chip->base + ZX_GPIO_IV); 1518c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&chip->lock, flags); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci return 0; 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic void zx_irq_handler(struct irq_desc *desc) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci unsigned long pending; 1598c2ecf20Sopenharmony_ci int offset; 1608c2ecf20Sopenharmony_ci struct gpio_chip *gc = irq_desc_get_handler_data(desc); 1618c2ecf20Sopenharmony_ci struct zx_gpio *chip = gpiochip_get_data(gc); 1628c2ecf20Sopenharmony_ci struct irq_chip *irqchip = irq_desc_get_chip(desc); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci chained_irq_enter(irqchip, desc); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci pending = readw_relaxed(chip->base + ZX_GPIO_MIS); 1678c2ecf20Sopenharmony_ci writew_relaxed(pending, chip->base + ZX_GPIO_IC); 1688c2ecf20Sopenharmony_ci if (pending) { 1698c2ecf20Sopenharmony_ci for_each_set_bit(offset, &pending, ZX_GPIO_NR) 1708c2ecf20Sopenharmony_ci generic_handle_irq(irq_find_mapping(gc->irq.domain, 1718c2ecf20Sopenharmony_ci offset)); 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci chained_irq_exit(irqchip, desc); 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic void zx_irq_mask(struct irq_data *d) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 1808c2ecf20Sopenharmony_ci struct zx_gpio *chip = gpiochip_get_data(gc); 1818c2ecf20Sopenharmony_ci u16 mask = BIT(irqd_to_hwirq(d) % ZX_GPIO_NR); 1828c2ecf20Sopenharmony_ci u16 gpioie; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci raw_spin_lock(&chip->lock); 1858c2ecf20Sopenharmony_ci gpioie = readw_relaxed(chip->base + ZX_GPIO_IM) | mask; 1868c2ecf20Sopenharmony_ci writew_relaxed(gpioie, chip->base + ZX_GPIO_IM); 1878c2ecf20Sopenharmony_ci gpioie = readw_relaxed(chip->base + ZX_GPIO_IE) & ~mask; 1888c2ecf20Sopenharmony_ci writew_relaxed(gpioie, chip->base + ZX_GPIO_IE); 1898c2ecf20Sopenharmony_ci raw_spin_unlock(&chip->lock); 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic void zx_irq_unmask(struct irq_data *d) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 1958c2ecf20Sopenharmony_ci struct zx_gpio *chip = gpiochip_get_data(gc); 1968c2ecf20Sopenharmony_ci u16 mask = BIT(irqd_to_hwirq(d) % ZX_GPIO_NR); 1978c2ecf20Sopenharmony_ci u16 gpioie; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci raw_spin_lock(&chip->lock); 2008c2ecf20Sopenharmony_ci gpioie = readw_relaxed(chip->base + ZX_GPIO_IM) & ~mask; 2018c2ecf20Sopenharmony_ci writew_relaxed(gpioie, chip->base + ZX_GPIO_IM); 2028c2ecf20Sopenharmony_ci gpioie = readw_relaxed(chip->base + ZX_GPIO_IE) | mask; 2038c2ecf20Sopenharmony_ci writew_relaxed(gpioie, chip->base + ZX_GPIO_IE); 2048c2ecf20Sopenharmony_ci raw_spin_unlock(&chip->lock); 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic struct irq_chip zx_irqchip = { 2088c2ecf20Sopenharmony_ci .name = "zx-gpio", 2098c2ecf20Sopenharmony_ci .irq_mask = zx_irq_mask, 2108c2ecf20Sopenharmony_ci .irq_unmask = zx_irq_unmask, 2118c2ecf20Sopenharmony_ci .irq_set_type = zx_irq_type, 2128c2ecf20Sopenharmony_ci}; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_cistatic int zx_gpio_probe(struct platform_device *pdev) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 2178c2ecf20Sopenharmony_ci struct zx_gpio *chip; 2188c2ecf20Sopenharmony_ci struct gpio_irq_chip *girq; 2198c2ecf20Sopenharmony_ci int irq, id, ret; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); 2228c2ecf20Sopenharmony_ci if (!chip) 2238c2ecf20Sopenharmony_ci return -ENOMEM; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci chip->base = devm_platform_ioremap_resource(pdev, 0); 2268c2ecf20Sopenharmony_ci if (IS_ERR(chip->base)) 2278c2ecf20Sopenharmony_ci return PTR_ERR(chip->base); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci id = of_alias_get_id(dev->of_node, "gpio"); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci raw_spin_lock_init(&chip->lock); 2328c2ecf20Sopenharmony_ci chip->gc.request = gpiochip_generic_request; 2338c2ecf20Sopenharmony_ci chip->gc.free = gpiochip_generic_free; 2348c2ecf20Sopenharmony_ci chip->gc.direction_input = zx_direction_input; 2358c2ecf20Sopenharmony_ci chip->gc.direction_output = zx_direction_output; 2368c2ecf20Sopenharmony_ci chip->gc.get = zx_get_value; 2378c2ecf20Sopenharmony_ci chip->gc.set = zx_set_value; 2388c2ecf20Sopenharmony_ci chip->gc.base = ZX_GPIO_NR * id; 2398c2ecf20Sopenharmony_ci chip->gc.ngpio = ZX_GPIO_NR; 2408c2ecf20Sopenharmony_ci chip->gc.label = dev_name(dev); 2418c2ecf20Sopenharmony_ci chip->gc.parent = dev; 2428c2ecf20Sopenharmony_ci chip->gc.owner = THIS_MODULE; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci /* 2458c2ecf20Sopenharmony_ci * irq_chip support 2468c2ecf20Sopenharmony_ci */ 2478c2ecf20Sopenharmony_ci writew_relaxed(0xffff, chip->base + ZX_GPIO_IM); 2488c2ecf20Sopenharmony_ci writew_relaxed(0, chip->base + ZX_GPIO_IE); 2498c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 0); 2508c2ecf20Sopenharmony_ci if (irq < 0) 2518c2ecf20Sopenharmony_ci return irq; 2528c2ecf20Sopenharmony_ci girq = &chip->gc.irq; 2538c2ecf20Sopenharmony_ci girq->chip = &zx_irqchip; 2548c2ecf20Sopenharmony_ci girq->parent_handler = zx_irq_handler; 2558c2ecf20Sopenharmony_ci girq->num_parents = 1; 2568c2ecf20Sopenharmony_ci girq->parents = devm_kcalloc(&pdev->dev, 1, 2578c2ecf20Sopenharmony_ci sizeof(*girq->parents), 2588c2ecf20Sopenharmony_ci GFP_KERNEL); 2598c2ecf20Sopenharmony_ci if (!girq->parents) 2608c2ecf20Sopenharmony_ci return -ENOMEM; 2618c2ecf20Sopenharmony_ci girq->parents[0] = irq; 2628c2ecf20Sopenharmony_ci girq->default_type = IRQ_TYPE_NONE; 2638c2ecf20Sopenharmony_ci girq->handler = handle_simple_irq; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci ret = gpiochip_add_data(&chip->gc, chip); 2668c2ecf20Sopenharmony_ci if (ret) 2678c2ecf20Sopenharmony_ci return ret; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, chip); 2708c2ecf20Sopenharmony_ci dev_info(dev, "ZX GPIO chip registered\n"); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci return 0; 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cistatic const struct of_device_id zx_gpio_match[] = { 2768c2ecf20Sopenharmony_ci { 2778c2ecf20Sopenharmony_ci .compatible = "zte,zx296702-gpio", 2788c2ecf20Sopenharmony_ci }, 2798c2ecf20Sopenharmony_ci { }, 2808c2ecf20Sopenharmony_ci}; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_cistatic struct platform_driver zx_gpio_driver = { 2838c2ecf20Sopenharmony_ci .probe = zx_gpio_probe, 2848c2ecf20Sopenharmony_ci .driver = { 2858c2ecf20Sopenharmony_ci .name = "zx_gpio", 2868c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(zx_gpio_match), 2878c2ecf20Sopenharmony_ci }, 2888c2ecf20Sopenharmony_ci}; 2898c2ecf20Sopenharmony_cibuiltin_platform_driver(zx_gpio_driver) 290