18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) ST-Ericsson SA 2010 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Author: Hanumath Prasad <hanumath.prasad@stericsson.com> for ST-Ericsson 68c2ecf20Sopenharmony_ci * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/init.h> 108c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 118c2ecf20Sopenharmony_ci#include <linux/slab.h> 128c2ecf20Sopenharmony_ci#include <linux/gpio/driver.h> 138c2ecf20Sopenharmony_ci#include <linux/of.h> 148c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 158c2ecf20Sopenharmony_ci#include <linux/mfd/tc3589x.h> 168c2ecf20Sopenharmony_ci#include <linux/bitops.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci/* 198c2ecf20Sopenharmony_ci * These registers are modified under the irq bus lock and cached to avoid 208c2ecf20Sopenharmony_ci * unnecessary writes in bus_sync_unlock. 218c2ecf20Sopenharmony_ci */ 228c2ecf20Sopenharmony_cienum { REG_IBE, REG_IEV, REG_IS, REG_IE, REG_DIRECT }; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define CACHE_NR_REGS 5 258c2ecf20Sopenharmony_ci#define CACHE_NR_BANKS 3 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistruct tc3589x_gpio { 288c2ecf20Sopenharmony_ci struct gpio_chip chip; 298c2ecf20Sopenharmony_ci struct tc3589x *tc3589x; 308c2ecf20Sopenharmony_ci struct device *dev; 318c2ecf20Sopenharmony_ci struct mutex irq_lock; 328c2ecf20Sopenharmony_ci /* Caches of interrupt control registers for bus_lock */ 338c2ecf20Sopenharmony_ci u8 regs[CACHE_NR_REGS][CACHE_NR_BANKS]; 348c2ecf20Sopenharmony_ci u8 oldregs[CACHE_NR_REGS][CACHE_NR_BANKS]; 358c2ecf20Sopenharmony_ci}; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic int tc3589x_gpio_get(struct gpio_chip *chip, unsigned int offset) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci struct tc3589x_gpio *tc3589x_gpio = gpiochip_get_data(chip); 408c2ecf20Sopenharmony_ci struct tc3589x *tc3589x = tc3589x_gpio->tc3589x; 418c2ecf20Sopenharmony_ci u8 reg = TC3589x_GPIODATA0 + (offset / 8) * 2; 428c2ecf20Sopenharmony_ci u8 mask = BIT(offset % 8); 438c2ecf20Sopenharmony_ci int ret; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci ret = tc3589x_reg_read(tc3589x, reg); 468c2ecf20Sopenharmony_ci if (ret < 0) 478c2ecf20Sopenharmony_ci return ret; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci return !!(ret & mask); 508c2ecf20Sopenharmony_ci} 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic void tc3589x_gpio_set(struct gpio_chip *chip, unsigned int offset, int val) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci struct tc3589x_gpio *tc3589x_gpio = gpiochip_get_data(chip); 558c2ecf20Sopenharmony_ci struct tc3589x *tc3589x = tc3589x_gpio->tc3589x; 568c2ecf20Sopenharmony_ci u8 reg = TC3589x_GPIODATA0 + (offset / 8) * 2; 578c2ecf20Sopenharmony_ci unsigned int pos = offset % 8; 588c2ecf20Sopenharmony_ci u8 data[] = {val ? BIT(pos) : 0, BIT(pos)}; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci tc3589x_block_write(tc3589x, reg, ARRAY_SIZE(data), data); 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic int tc3589x_gpio_direction_output(struct gpio_chip *chip, 648c2ecf20Sopenharmony_ci unsigned int offset, int val) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci struct tc3589x_gpio *tc3589x_gpio = gpiochip_get_data(chip); 678c2ecf20Sopenharmony_ci struct tc3589x *tc3589x = tc3589x_gpio->tc3589x; 688c2ecf20Sopenharmony_ci u8 reg = TC3589x_GPIODIR0 + offset / 8; 698c2ecf20Sopenharmony_ci unsigned int pos = offset % 8; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci tc3589x_gpio_set(chip, offset, val); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci return tc3589x_set_bits(tc3589x, reg, BIT(pos), BIT(pos)); 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic int tc3589x_gpio_direction_input(struct gpio_chip *chip, 778c2ecf20Sopenharmony_ci unsigned int offset) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci struct tc3589x_gpio *tc3589x_gpio = gpiochip_get_data(chip); 808c2ecf20Sopenharmony_ci struct tc3589x *tc3589x = tc3589x_gpio->tc3589x; 818c2ecf20Sopenharmony_ci u8 reg = TC3589x_GPIODIR0 + offset / 8; 828c2ecf20Sopenharmony_ci unsigned int pos = offset % 8; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci return tc3589x_set_bits(tc3589x, reg, BIT(pos), 0); 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic int tc3589x_gpio_get_direction(struct gpio_chip *chip, 888c2ecf20Sopenharmony_ci unsigned int offset) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci struct tc3589x_gpio *tc3589x_gpio = gpiochip_get_data(chip); 918c2ecf20Sopenharmony_ci struct tc3589x *tc3589x = tc3589x_gpio->tc3589x; 928c2ecf20Sopenharmony_ci u8 reg = TC3589x_GPIODIR0 + offset / 8; 938c2ecf20Sopenharmony_ci unsigned int pos = offset % 8; 948c2ecf20Sopenharmony_ci int ret; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci ret = tc3589x_reg_read(tc3589x, reg); 978c2ecf20Sopenharmony_ci if (ret < 0) 988c2ecf20Sopenharmony_ci return ret; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci if (ret & BIT(pos)) 1018c2ecf20Sopenharmony_ci return GPIO_LINE_DIRECTION_OUT; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci return GPIO_LINE_DIRECTION_IN; 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic int tc3589x_gpio_set_config(struct gpio_chip *chip, unsigned int offset, 1078c2ecf20Sopenharmony_ci unsigned long config) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci struct tc3589x_gpio *tc3589x_gpio = gpiochip_get_data(chip); 1108c2ecf20Sopenharmony_ci struct tc3589x *tc3589x = tc3589x_gpio->tc3589x; 1118c2ecf20Sopenharmony_ci /* 1128c2ecf20Sopenharmony_ci * These registers are alterated at each second address 1138c2ecf20Sopenharmony_ci * ODM bit 0 = drive to GND or Hi-Z (open drain) 1148c2ecf20Sopenharmony_ci * ODM bit 1 = drive to VDD or Hi-Z (open source) 1158c2ecf20Sopenharmony_ci */ 1168c2ecf20Sopenharmony_ci u8 odmreg = TC3589x_GPIOODM0 + (offset / 8) * 2; 1178c2ecf20Sopenharmony_ci u8 odereg = TC3589x_GPIOODE0 + (offset / 8) * 2; 1188c2ecf20Sopenharmony_ci unsigned int pos = offset % 8; 1198c2ecf20Sopenharmony_ci int ret; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci switch (pinconf_to_config_param(config)) { 1228c2ecf20Sopenharmony_ci case PIN_CONFIG_DRIVE_OPEN_DRAIN: 1238c2ecf20Sopenharmony_ci /* Set open drain mode */ 1248c2ecf20Sopenharmony_ci ret = tc3589x_set_bits(tc3589x, odmreg, BIT(pos), 0); 1258c2ecf20Sopenharmony_ci if (ret) 1268c2ecf20Sopenharmony_ci return ret; 1278c2ecf20Sopenharmony_ci /* Enable open drain/source mode */ 1288c2ecf20Sopenharmony_ci return tc3589x_set_bits(tc3589x, odereg, BIT(pos), BIT(pos)); 1298c2ecf20Sopenharmony_ci case PIN_CONFIG_DRIVE_OPEN_SOURCE: 1308c2ecf20Sopenharmony_ci /* Set open source mode */ 1318c2ecf20Sopenharmony_ci ret = tc3589x_set_bits(tc3589x, odmreg, BIT(pos), BIT(pos)); 1328c2ecf20Sopenharmony_ci if (ret) 1338c2ecf20Sopenharmony_ci return ret; 1348c2ecf20Sopenharmony_ci /* Enable open drain/source mode */ 1358c2ecf20Sopenharmony_ci return tc3589x_set_bits(tc3589x, odereg, BIT(pos), BIT(pos)); 1368c2ecf20Sopenharmony_ci case PIN_CONFIG_DRIVE_PUSH_PULL: 1378c2ecf20Sopenharmony_ci /* Disable open drain/source mode */ 1388c2ecf20Sopenharmony_ci return tc3589x_set_bits(tc3589x, odereg, BIT(pos), 0); 1398c2ecf20Sopenharmony_ci default: 1408c2ecf20Sopenharmony_ci break; 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci return -ENOTSUPP; 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic const struct gpio_chip template_chip = { 1468c2ecf20Sopenharmony_ci .label = "tc3589x", 1478c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1488c2ecf20Sopenharmony_ci .get = tc3589x_gpio_get, 1498c2ecf20Sopenharmony_ci .set = tc3589x_gpio_set, 1508c2ecf20Sopenharmony_ci .direction_output = tc3589x_gpio_direction_output, 1518c2ecf20Sopenharmony_ci .direction_input = tc3589x_gpio_direction_input, 1528c2ecf20Sopenharmony_ci .get_direction = tc3589x_gpio_get_direction, 1538c2ecf20Sopenharmony_ci .set_config = tc3589x_gpio_set_config, 1548c2ecf20Sopenharmony_ci .can_sleep = true, 1558c2ecf20Sopenharmony_ci}; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic int tc3589x_gpio_irq_set_type(struct irq_data *d, unsigned int type) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 1608c2ecf20Sopenharmony_ci struct tc3589x_gpio *tc3589x_gpio = gpiochip_get_data(gc); 1618c2ecf20Sopenharmony_ci int offset = d->hwirq; 1628c2ecf20Sopenharmony_ci int regoffset = offset / 8; 1638c2ecf20Sopenharmony_ci int mask = BIT(offset % 8); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci if (type == IRQ_TYPE_EDGE_BOTH) { 1668c2ecf20Sopenharmony_ci tc3589x_gpio->regs[REG_IBE][regoffset] |= mask; 1678c2ecf20Sopenharmony_ci return 0; 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci tc3589x_gpio->regs[REG_IBE][regoffset] &= ~mask; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci if (type == IRQ_TYPE_LEVEL_LOW || type == IRQ_TYPE_LEVEL_HIGH) 1738c2ecf20Sopenharmony_ci tc3589x_gpio->regs[REG_IS][regoffset] |= mask; 1748c2ecf20Sopenharmony_ci else 1758c2ecf20Sopenharmony_ci tc3589x_gpio->regs[REG_IS][regoffset] &= ~mask; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci if (type == IRQ_TYPE_EDGE_RISING || type == IRQ_TYPE_LEVEL_HIGH) 1788c2ecf20Sopenharmony_ci tc3589x_gpio->regs[REG_IEV][regoffset] |= mask; 1798c2ecf20Sopenharmony_ci else 1808c2ecf20Sopenharmony_ci tc3589x_gpio->regs[REG_IEV][regoffset] &= ~mask; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci return 0; 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic void tc3589x_gpio_irq_lock(struct irq_data *d) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 1888c2ecf20Sopenharmony_ci struct tc3589x_gpio *tc3589x_gpio = gpiochip_get_data(gc); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci mutex_lock(&tc3589x_gpio->irq_lock); 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_cistatic void tc3589x_gpio_irq_sync_unlock(struct irq_data *d) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 1968c2ecf20Sopenharmony_ci struct tc3589x_gpio *tc3589x_gpio = gpiochip_get_data(gc); 1978c2ecf20Sopenharmony_ci struct tc3589x *tc3589x = tc3589x_gpio->tc3589x; 1988c2ecf20Sopenharmony_ci static const u8 regmap[] = { 1998c2ecf20Sopenharmony_ci [REG_IBE] = TC3589x_GPIOIBE0, 2008c2ecf20Sopenharmony_ci [REG_IEV] = TC3589x_GPIOIEV0, 2018c2ecf20Sopenharmony_ci [REG_IS] = TC3589x_GPIOIS0, 2028c2ecf20Sopenharmony_ci [REG_IE] = TC3589x_GPIOIE0, 2038c2ecf20Sopenharmony_ci [REG_DIRECT] = TC3589x_DIRECT0, 2048c2ecf20Sopenharmony_ci }; 2058c2ecf20Sopenharmony_ci int i, j; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci for (i = 0; i < CACHE_NR_REGS; i++) { 2088c2ecf20Sopenharmony_ci for (j = 0; j < CACHE_NR_BANKS; j++) { 2098c2ecf20Sopenharmony_ci u8 old = tc3589x_gpio->oldregs[i][j]; 2108c2ecf20Sopenharmony_ci u8 new = tc3589x_gpio->regs[i][j]; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci if (new == old) 2138c2ecf20Sopenharmony_ci continue; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci tc3589x_gpio->oldregs[i][j] = new; 2168c2ecf20Sopenharmony_ci tc3589x_reg_write(tc3589x, regmap[i] + j, new); 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci mutex_unlock(&tc3589x_gpio->irq_lock); 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_cistatic void tc3589x_gpio_irq_mask(struct irq_data *d) 2248c2ecf20Sopenharmony_ci{ 2258c2ecf20Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 2268c2ecf20Sopenharmony_ci struct tc3589x_gpio *tc3589x_gpio = gpiochip_get_data(gc); 2278c2ecf20Sopenharmony_ci int offset = d->hwirq; 2288c2ecf20Sopenharmony_ci int regoffset = offset / 8; 2298c2ecf20Sopenharmony_ci int mask = BIT(offset % 8); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci tc3589x_gpio->regs[REG_IE][regoffset] &= ~mask; 2328c2ecf20Sopenharmony_ci tc3589x_gpio->regs[REG_DIRECT][regoffset] |= mask; 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic void tc3589x_gpio_irq_unmask(struct irq_data *d) 2368c2ecf20Sopenharmony_ci{ 2378c2ecf20Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 2388c2ecf20Sopenharmony_ci struct tc3589x_gpio *tc3589x_gpio = gpiochip_get_data(gc); 2398c2ecf20Sopenharmony_ci int offset = d->hwirq; 2408c2ecf20Sopenharmony_ci int regoffset = offset / 8; 2418c2ecf20Sopenharmony_ci int mask = BIT(offset % 8); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci tc3589x_gpio->regs[REG_IE][regoffset] |= mask; 2448c2ecf20Sopenharmony_ci tc3589x_gpio->regs[REG_DIRECT][regoffset] &= ~mask; 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_cistatic struct irq_chip tc3589x_gpio_irq_chip = { 2488c2ecf20Sopenharmony_ci .name = "tc3589x-gpio", 2498c2ecf20Sopenharmony_ci .irq_bus_lock = tc3589x_gpio_irq_lock, 2508c2ecf20Sopenharmony_ci .irq_bus_sync_unlock = tc3589x_gpio_irq_sync_unlock, 2518c2ecf20Sopenharmony_ci .irq_mask = tc3589x_gpio_irq_mask, 2528c2ecf20Sopenharmony_ci .irq_unmask = tc3589x_gpio_irq_unmask, 2538c2ecf20Sopenharmony_ci .irq_set_type = tc3589x_gpio_irq_set_type, 2548c2ecf20Sopenharmony_ci}; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_cistatic irqreturn_t tc3589x_gpio_irq(int irq, void *dev) 2578c2ecf20Sopenharmony_ci{ 2588c2ecf20Sopenharmony_ci struct tc3589x_gpio *tc3589x_gpio = dev; 2598c2ecf20Sopenharmony_ci struct tc3589x *tc3589x = tc3589x_gpio->tc3589x; 2608c2ecf20Sopenharmony_ci u8 status[CACHE_NR_BANKS]; 2618c2ecf20Sopenharmony_ci int ret; 2628c2ecf20Sopenharmony_ci int i; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci ret = tc3589x_block_read(tc3589x, TC3589x_GPIOMIS0, 2658c2ecf20Sopenharmony_ci ARRAY_SIZE(status), status); 2668c2ecf20Sopenharmony_ci if (ret < 0) 2678c2ecf20Sopenharmony_ci return IRQ_NONE; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(status); i++) { 2708c2ecf20Sopenharmony_ci unsigned int stat = status[i]; 2718c2ecf20Sopenharmony_ci if (!stat) 2728c2ecf20Sopenharmony_ci continue; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci while (stat) { 2758c2ecf20Sopenharmony_ci int bit = __ffs(stat); 2768c2ecf20Sopenharmony_ci int line = i * 8 + bit; 2778c2ecf20Sopenharmony_ci int irq = irq_find_mapping(tc3589x_gpio->chip.irq.domain, 2788c2ecf20Sopenharmony_ci line); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci handle_nested_irq(irq); 2818c2ecf20Sopenharmony_ci stat &= ~(1 << bit); 2828c2ecf20Sopenharmony_ci } 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci tc3589x_reg_write(tc3589x, TC3589x_GPIOIC0 + i, status[i]); 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_cistatic int tc3589x_gpio_probe(struct platform_device *pdev) 2918c2ecf20Sopenharmony_ci{ 2928c2ecf20Sopenharmony_ci struct tc3589x *tc3589x = dev_get_drvdata(pdev->dev.parent); 2938c2ecf20Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 2948c2ecf20Sopenharmony_ci struct tc3589x_gpio *tc3589x_gpio; 2958c2ecf20Sopenharmony_ci struct gpio_irq_chip *girq; 2968c2ecf20Sopenharmony_ci int ret; 2978c2ecf20Sopenharmony_ci int irq; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci if (!np) { 3008c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "No Device Tree node found\n"); 3018c2ecf20Sopenharmony_ci return -EINVAL; 3028c2ecf20Sopenharmony_ci } 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 0); 3058c2ecf20Sopenharmony_ci if (irq < 0) 3068c2ecf20Sopenharmony_ci return irq; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci tc3589x_gpio = devm_kzalloc(&pdev->dev, sizeof(struct tc3589x_gpio), 3098c2ecf20Sopenharmony_ci GFP_KERNEL); 3108c2ecf20Sopenharmony_ci if (!tc3589x_gpio) 3118c2ecf20Sopenharmony_ci return -ENOMEM; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci mutex_init(&tc3589x_gpio->irq_lock); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci tc3589x_gpio->dev = &pdev->dev; 3168c2ecf20Sopenharmony_ci tc3589x_gpio->tc3589x = tc3589x; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci tc3589x_gpio->chip = template_chip; 3198c2ecf20Sopenharmony_ci tc3589x_gpio->chip.ngpio = tc3589x->num_gpio; 3208c2ecf20Sopenharmony_ci tc3589x_gpio->chip.parent = &pdev->dev; 3218c2ecf20Sopenharmony_ci tc3589x_gpio->chip.base = -1; 3228c2ecf20Sopenharmony_ci tc3589x_gpio->chip.of_node = np; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci girq = &tc3589x_gpio->chip.irq; 3258c2ecf20Sopenharmony_ci girq->chip = &tc3589x_gpio_irq_chip; 3268c2ecf20Sopenharmony_ci /* This will let us handle the parent IRQ in the driver */ 3278c2ecf20Sopenharmony_ci girq->parent_handler = NULL; 3288c2ecf20Sopenharmony_ci girq->num_parents = 0; 3298c2ecf20Sopenharmony_ci girq->parents = NULL; 3308c2ecf20Sopenharmony_ci girq->default_type = IRQ_TYPE_NONE; 3318c2ecf20Sopenharmony_ci girq->handler = handle_simple_irq; 3328c2ecf20Sopenharmony_ci girq->threaded = true; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci /* Bring the GPIO module out of reset */ 3358c2ecf20Sopenharmony_ci ret = tc3589x_set_bits(tc3589x, TC3589x_RSTCTRL, 3368c2ecf20Sopenharmony_ci TC3589x_RSTCTRL_GPIRST, 0); 3378c2ecf20Sopenharmony_ci if (ret < 0) 3388c2ecf20Sopenharmony_ci return ret; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci /* For tc35894, have to disable Direct KBD interrupts, 3418c2ecf20Sopenharmony_ci * else IRQST will always be 0x20, IRQN low level, can't 3428c2ecf20Sopenharmony_ci * clear the irq status. 3438c2ecf20Sopenharmony_ci * TODO: need more test on other tc3589x chip. 3448c2ecf20Sopenharmony_ci * 3458c2ecf20Sopenharmony_ci */ 3468c2ecf20Sopenharmony_ci ret = tc3589x_reg_write(tc3589x, TC3589x_DKBDMSK, 3478c2ecf20Sopenharmony_ci TC3589x_DKBDMSK_ELINT | TC3589x_DKBDMSK_EINT); 3488c2ecf20Sopenharmony_ci if (ret < 0) 3498c2ecf20Sopenharmony_ci return ret; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci ret = devm_request_threaded_irq(&pdev->dev, 3528c2ecf20Sopenharmony_ci irq, NULL, tc3589x_gpio_irq, 3538c2ecf20Sopenharmony_ci IRQF_ONESHOT, "tc3589x-gpio", 3548c2ecf20Sopenharmony_ci tc3589x_gpio); 3558c2ecf20Sopenharmony_ci if (ret) { 3568c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "unable to get irq: %d\n", ret); 3578c2ecf20Sopenharmony_ci return ret; 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci ret = devm_gpiochip_add_data(&pdev->dev, &tc3589x_gpio->chip, 3618c2ecf20Sopenharmony_ci tc3589x_gpio); 3628c2ecf20Sopenharmony_ci if (ret) { 3638c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "unable to add gpiochip: %d\n", ret); 3648c2ecf20Sopenharmony_ci return ret; 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, tc3589x_gpio); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci return 0; 3708c2ecf20Sopenharmony_ci} 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_cistatic struct platform_driver tc3589x_gpio_driver = { 3738c2ecf20Sopenharmony_ci .driver.name = "tc3589x-gpio", 3748c2ecf20Sopenharmony_ci .probe = tc3589x_gpio_probe, 3758c2ecf20Sopenharmony_ci}; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_cistatic int __init tc3589x_gpio_init(void) 3788c2ecf20Sopenharmony_ci{ 3798c2ecf20Sopenharmony_ci return platform_driver_register(&tc3589x_gpio_driver); 3808c2ecf20Sopenharmony_ci} 3818c2ecf20Sopenharmony_cisubsys_initcall(tc3589x_gpio_init); 382