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