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