18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * GPIO driver for NXP LPC18xx/43xx.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2018 Vladimir Zapolskiy <vz@mleia.com>
68c2ecf20Sopenharmony_ci * Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com>
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/clk.h>
118c2ecf20Sopenharmony_ci#include <linux/gpio/driver.h>
128c2ecf20Sopenharmony_ci#include <linux/io.h>
138c2ecf20Sopenharmony_ci#include <linux/irqdomain.h>
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/of.h>
168c2ecf20Sopenharmony_ci#include <linux/of_address.h>
178c2ecf20Sopenharmony_ci#include <linux/of_gpio.h>
188c2ecf20Sopenharmony_ci#include <linux/of_irq.h>
198c2ecf20Sopenharmony_ci#include <linux/pinctrl/consumer.h>
208c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci/* LPC18xx GPIO register offsets */
238c2ecf20Sopenharmony_ci#define LPC18XX_REG_DIR(n)	(0x2000 + n * sizeof(u32))
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#define LPC18XX_MAX_PORTS	8
268c2ecf20Sopenharmony_ci#define LPC18XX_PINS_PER_PORT	32
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci/* LPC18xx GPIO pin interrupt controller register offsets */
298c2ecf20Sopenharmony_ci#define LPC18XX_GPIO_PIN_IC_ISEL	0x00
308c2ecf20Sopenharmony_ci#define LPC18XX_GPIO_PIN_IC_IENR	0x04
318c2ecf20Sopenharmony_ci#define LPC18XX_GPIO_PIN_IC_SIENR	0x08
328c2ecf20Sopenharmony_ci#define LPC18XX_GPIO_PIN_IC_CIENR	0x0c
338c2ecf20Sopenharmony_ci#define LPC18XX_GPIO_PIN_IC_IENF	0x10
348c2ecf20Sopenharmony_ci#define LPC18XX_GPIO_PIN_IC_SIENF	0x14
358c2ecf20Sopenharmony_ci#define LPC18XX_GPIO_PIN_IC_CIENF	0x18
368c2ecf20Sopenharmony_ci#define LPC18XX_GPIO_PIN_IC_RISE	0x1c
378c2ecf20Sopenharmony_ci#define LPC18XX_GPIO_PIN_IC_FALL	0x20
388c2ecf20Sopenharmony_ci#define LPC18XX_GPIO_PIN_IC_IST		0x24
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#define NR_LPC18XX_GPIO_PIN_IC_IRQS	8
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistruct lpc18xx_gpio_pin_ic {
438c2ecf20Sopenharmony_ci	void __iomem *base;
448c2ecf20Sopenharmony_ci	struct irq_domain *domain;
458c2ecf20Sopenharmony_ci	struct raw_spinlock lock;
468c2ecf20Sopenharmony_ci};
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistruct lpc18xx_gpio_chip {
498c2ecf20Sopenharmony_ci	struct gpio_chip gpio;
508c2ecf20Sopenharmony_ci	void __iomem *base;
518c2ecf20Sopenharmony_ci	struct clk *clk;
528c2ecf20Sopenharmony_ci	struct lpc18xx_gpio_pin_ic *pin_ic;
538c2ecf20Sopenharmony_ci	spinlock_t lock;
548c2ecf20Sopenharmony_ci};
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistatic inline void lpc18xx_gpio_pin_ic_isel(struct lpc18xx_gpio_pin_ic *ic,
578c2ecf20Sopenharmony_ci					    u32 pin, bool set)
588c2ecf20Sopenharmony_ci{
598c2ecf20Sopenharmony_ci	u32 val = readl_relaxed(ic->base + LPC18XX_GPIO_PIN_IC_ISEL);
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	if (set)
628c2ecf20Sopenharmony_ci		val &= ~BIT(pin);
638c2ecf20Sopenharmony_ci	else
648c2ecf20Sopenharmony_ci		val |= BIT(pin);
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	writel_relaxed(val, ic->base + LPC18XX_GPIO_PIN_IC_ISEL);
678c2ecf20Sopenharmony_ci}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistatic inline void lpc18xx_gpio_pin_ic_set(struct lpc18xx_gpio_pin_ic *ic,
708c2ecf20Sopenharmony_ci					   u32 pin, u32 reg)
718c2ecf20Sopenharmony_ci{
728c2ecf20Sopenharmony_ci	writel_relaxed(BIT(pin), ic->base + reg);
738c2ecf20Sopenharmony_ci}
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_cistatic void lpc18xx_gpio_pin_ic_mask(struct irq_data *d)
768c2ecf20Sopenharmony_ci{
778c2ecf20Sopenharmony_ci	struct lpc18xx_gpio_pin_ic *ic = d->chip_data;
788c2ecf20Sopenharmony_ci	u32 type = irqd_get_trigger_type(d);
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	raw_spin_lock(&ic->lock);
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	if (type & IRQ_TYPE_LEVEL_MASK || type & IRQ_TYPE_EDGE_RISING)
838c2ecf20Sopenharmony_ci		lpc18xx_gpio_pin_ic_set(ic, d->hwirq,
848c2ecf20Sopenharmony_ci					LPC18XX_GPIO_PIN_IC_CIENR);
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	if (type & IRQ_TYPE_EDGE_FALLING)
878c2ecf20Sopenharmony_ci		lpc18xx_gpio_pin_ic_set(ic, d->hwirq,
888c2ecf20Sopenharmony_ci					LPC18XX_GPIO_PIN_IC_CIENF);
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	raw_spin_unlock(&ic->lock);
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	irq_chip_mask_parent(d);
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistatic void lpc18xx_gpio_pin_ic_unmask(struct irq_data *d)
968c2ecf20Sopenharmony_ci{
978c2ecf20Sopenharmony_ci	struct lpc18xx_gpio_pin_ic *ic = d->chip_data;
988c2ecf20Sopenharmony_ci	u32 type = irqd_get_trigger_type(d);
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	raw_spin_lock(&ic->lock);
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	if (type & IRQ_TYPE_LEVEL_MASK || type & IRQ_TYPE_EDGE_RISING)
1038c2ecf20Sopenharmony_ci		lpc18xx_gpio_pin_ic_set(ic, d->hwirq,
1048c2ecf20Sopenharmony_ci					LPC18XX_GPIO_PIN_IC_SIENR);
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	if (type & IRQ_TYPE_EDGE_FALLING)
1078c2ecf20Sopenharmony_ci		lpc18xx_gpio_pin_ic_set(ic, d->hwirq,
1088c2ecf20Sopenharmony_ci					LPC18XX_GPIO_PIN_IC_SIENF);
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	raw_spin_unlock(&ic->lock);
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	irq_chip_unmask_parent(d);
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cistatic void lpc18xx_gpio_pin_ic_eoi(struct irq_data *d)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	struct lpc18xx_gpio_pin_ic *ic = d->chip_data;
1188c2ecf20Sopenharmony_ci	u32 type = irqd_get_trigger_type(d);
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	raw_spin_lock(&ic->lock);
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	if (type & IRQ_TYPE_EDGE_BOTH)
1238c2ecf20Sopenharmony_ci		lpc18xx_gpio_pin_ic_set(ic, d->hwirq,
1248c2ecf20Sopenharmony_ci					LPC18XX_GPIO_PIN_IC_IST);
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	raw_spin_unlock(&ic->lock);
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	irq_chip_eoi_parent(d);
1298c2ecf20Sopenharmony_ci}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_cistatic int lpc18xx_gpio_pin_ic_set_type(struct irq_data *d, unsigned int type)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	struct lpc18xx_gpio_pin_ic *ic = d->chip_data;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	raw_spin_lock(&ic->lock);
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	if (type & IRQ_TYPE_LEVEL_HIGH) {
1388c2ecf20Sopenharmony_ci		lpc18xx_gpio_pin_ic_isel(ic, d->hwirq, true);
1398c2ecf20Sopenharmony_ci		lpc18xx_gpio_pin_ic_set(ic, d->hwirq,
1408c2ecf20Sopenharmony_ci					LPC18XX_GPIO_PIN_IC_SIENF);
1418c2ecf20Sopenharmony_ci	} else if (type & IRQ_TYPE_LEVEL_LOW) {
1428c2ecf20Sopenharmony_ci		lpc18xx_gpio_pin_ic_isel(ic, d->hwirq, true);
1438c2ecf20Sopenharmony_ci		lpc18xx_gpio_pin_ic_set(ic, d->hwirq,
1448c2ecf20Sopenharmony_ci					LPC18XX_GPIO_PIN_IC_CIENF);
1458c2ecf20Sopenharmony_ci	} else {
1468c2ecf20Sopenharmony_ci		lpc18xx_gpio_pin_ic_isel(ic, d->hwirq, false);
1478c2ecf20Sopenharmony_ci	}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	raw_spin_unlock(&ic->lock);
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	return 0;
1528c2ecf20Sopenharmony_ci}
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_cistatic struct irq_chip lpc18xx_gpio_pin_ic = {
1558c2ecf20Sopenharmony_ci	.name		= "LPC18xx GPIO pin",
1568c2ecf20Sopenharmony_ci	.irq_mask	= lpc18xx_gpio_pin_ic_mask,
1578c2ecf20Sopenharmony_ci	.irq_unmask	= lpc18xx_gpio_pin_ic_unmask,
1588c2ecf20Sopenharmony_ci	.irq_eoi	= lpc18xx_gpio_pin_ic_eoi,
1598c2ecf20Sopenharmony_ci	.irq_set_type	= lpc18xx_gpio_pin_ic_set_type,
1608c2ecf20Sopenharmony_ci	.flags		= IRQCHIP_SET_TYPE_MASKED,
1618c2ecf20Sopenharmony_ci};
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_cistatic int lpc18xx_gpio_pin_ic_domain_alloc(struct irq_domain *domain,
1648c2ecf20Sopenharmony_ci					    unsigned int virq,
1658c2ecf20Sopenharmony_ci					    unsigned int nr_irqs, void *data)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	struct irq_fwspec parent_fwspec, *fwspec = data;
1688c2ecf20Sopenharmony_ci	struct lpc18xx_gpio_pin_ic *ic = domain->host_data;
1698c2ecf20Sopenharmony_ci	irq_hw_number_t hwirq;
1708c2ecf20Sopenharmony_ci	int ret;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	if (nr_irqs != 1)
1738c2ecf20Sopenharmony_ci		return -EINVAL;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	hwirq = fwspec->param[0];
1768c2ecf20Sopenharmony_ci	if (hwirq >= NR_LPC18XX_GPIO_PIN_IC_IRQS)
1778c2ecf20Sopenharmony_ci		return -EINVAL;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	/*
1808c2ecf20Sopenharmony_ci	 * All LPC18xx/LPC43xx GPIO pin hardware interrupts are translated
1818c2ecf20Sopenharmony_ci	 * into edge interrupts 32...39 on parent Cortex-M3/M4 NVIC
1828c2ecf20Sopenharmony_ci	 */
1838c2ecf20Sopenharmony_ci	parent_fwspec.fwnode = domain->parent->fwnode;
1848c2ecf20Sopenharmony_ci	parent_fwspec.param_count = 1;
1858c2ecf20Sopenharmony_ci	parent_fwspec.param[0] = hwirq + 32;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	ret = irq_domain_alloc_irqs_parent(domain, virq, 1, &parent_fwspec);
1888c2ecf20Sopenharmony_ci	if (ret < 0) {
1898c2ecf20Sopenharmony_ci		pr_err("failed to allocate parent irq %u: %d\n",
1908c2ecf20Sopenharmony_ci		       parent_fwspec.param[0], ret);
1918c2ecf20Sopenharmony_ci		return ret;
1928c2ecf20Sopenharmony_ci	}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	return irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
1958c2ecf20Sopenharmony_ci					     &lpc18xx_gpio_pin_ic, ic);
1968c2ecf20Sopenharmony_ci}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_cistatic const struct irq_domain_ops lpc18xx_gpio_pin_ic_domain_ops = {
1998c2ecf20Sopenharmony_ci	.alloc	= lpc18xx_gpio_pin_ic_domain_alloc,
2008c2ecf20Sopenharmony_ci	.xlate	= irq_domain_xlate_twocell,
2018c2ecf20Sopenharmony_ci	.free	= irq_domain_free_irqs_common,
2028c2ecf20Sopenharmony_ci};
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_cistatic int lpc18xx_gpio_pin_ic_probe(struct lpc18xx_gpio_chip *gc)
2058c2ecf20Sopenharmony_ci{
2068c2ecf20Sopenharmony_ci	struct device *dev = gc->gpio.parent;
2078c2ecf20Sopenharmony_ci	struct irq_domain *parent_domain;
2088c2ecf20Sopenharmony_ci	struct device_node *parent_node;
2098c2ecf20Sopenharmony_ci	struct lpc18xx_gpio_pin_ic *ic;
2108c2ecf20Sopenharmony_ci	struct resource res;
2118c2ecf20Sopenharmony_ci	int ret, index;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	parent_node = of_irq_find_parent(dev->of_node);
2148c2ecf20Sopenharmony_ci	if (!parent_node)
2158c2ecf20Sopenharmony_ci		return -ENXIO;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	parent_domain = irq_find_host(parent_node);
2188c2ecf20Sopenharmony_ci	of_node_put(parent_node);
2198c2ecf20Sopenharmony_ci	if (!parent_domain)
2208c2ecf20Sopenharmony_ci		return -ENXIO;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	ic = devm_kzalloc(dev, sizeof(*ic), GFP_KERNEL);
2238c2ecf20Sopenharmony_ci	if (!ic)
2248c2ecf20Sopenharmony_ci		return -ENOMEM;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	index = of_property_match_string(dev->of_node, "reg-names",
2278c2ecf20Sopenharmony_ci					 "gpio-pin-ic");
2288c2ecf20Sopenharmony_ci	if (index < 0) {
2298c2ecf20Sopenharmony_ci		ret = -ENODEV;
2308c2ecf20Sopenharmony_ci		goto free_ic;
2318c2ecf20Sopenharmony_ci	}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	ret = of_address_to_resource(dev->of_node, index, &res);
2348c2ecf20Sopenharmony_ci	if (ret < 0)
2358c2ecf20Sopenharmony_ci		goto free_ic;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	ic->base = devm_ioremap_resource(dev, &res);
2388c2ecf20Sopenharmony_ci	if (IS_ERR(ic->base)) {
2398c2ecf20Sopenharmony_ci		ret = PTR_ERR(ic->base);
2408c2ecf20Sopenharmony_ci		goto free_ic;
2418c2ecf20Sopenharmony_ci	}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	raw_spin_lock_init(&ic->lock);
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	ic->domain = irq_domain_add_hierarchy(parent_domain, 0,
2468c2ecf20Sopenharmony_ci					      NR_LPC18XX_GPIO_PIN_IC_IRQS,
2478c2ecf20Sopenharmony_ci					      dev->of_node,
2488c2ecf20Sopenharmony_ci					      &lpc18xx_gpio_pin_ic_domain_ops,
2498c2ecf20Sopenharmony_ci					      ic);
2508c2ecf20Sopenharmony_ci	if (!ic->domain) {
2518c2ecf20Sopenharmony_ci		pr_err("unable to add irq domain\n");
2528c2ecf20Sopenharmony_ci		ret = -ENODEV;
2538c2ecf20Sopenharmony_ci		goto free_iomap;
2548c2ecf20Sopenharmony_ci	}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	gc->pin_ic = ic;
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	return 0;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_cifree_iomap:
2618c2ecf20Sopenharmony_ci	devm_iounmap(dev, ic->base);
2628c2ecf20Sopenharmony_cifree_ic:
2638c2ecf20Sopenharmony_ci	devm_kfree(dev, ic);
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	return ret;
2668c2ecf20Sopenharmony_ci}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_cistatic void lpc18xx_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
2698c2ecf20Sopenharmony_ci{
2708c2ecf20Sopenharmony_ci	struct lpc18xx_gpio_chip *gc = gpiochip_get_data(chip);
2718c2ecf20Sopenharmony_ci	writeb(value ? 1 : 0, gc->base + offset);
2728c2ecf20Sopenharmony_ci}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_cistatic int lpc18xx_gpio_get(struct gpio_chip *chip, unsigned offset)
2758c2ecf20Sopenharmony_ci{
2768c2ecf20Sopenharmony_ci	struct lpc18xx_gpio_chip *gc = gpiochip_get_data(chip);
2778c2ecf20Sopenharmony_ci	return !!readb(gc->base + offset);
2788c2ecf20Sopenharmony_ci}
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_cistatic int lpc18xx_gpio_direction(struct gpio_chip *chip, unsigned offset,
2818c2ecf20Sopenharmony_ci				  bool out)
2828c2ecf20Sopenharmony_ci{
2838c2ecf20Sopenharmony_ci	struct lpc18xx_gpio_chip *gc = gpiochip_get_data(chip);
2848c2ecf20Sopenharmony_ci	unsigned long flags;
2858c2ecf20Sopenharmony_ci	u32 port, pin, dir;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	port = offset / LPC18XX_PINS_PER_PORT;
2888c2ecf20Sopenharmony_ci	pin  = offset % LPC18XX_PINS_PER_PORT;
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	spin_lock_irqsave(&gc->lock, flags);
2918c2ecf20Sopenharmony_ci	dir = readl(gc->base + LPC18XX_REG_DIR(port));
2928c2ecf20Sopenharmony_ci	if (out)
2938c2ecf20Sopenharmony_ci		dir |= BIT(pin);
2948c2ecf20Sopenharmony_ci	else
2958c2ecf20Sopenharmony_ci		dir &= ~BIT(pin);
2968c2ecf20Sopenharmony_ci	writel(dir, gc->base + LPC18XX_REG_DIR(port));
2978c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&gc->lock, flags);
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	return 0;
3008c2ecf20Sopenharmony_ci}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_cistatic int lpc18xx_gpio_direction_input(struct gpio_chip *chip,
3038c2ecf20Sopenharmony_ci					unsigned offset)
3048c2ecf20Sopenharmony_ci{
3058c2ecf20Sopenharmony_ci	return lpc18xx_gpio_direction(chip, offset, false);
3068c2ecf20Sopenharmony_ci}
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_cistatic int lpc18xx_gpio_direction_output(struct gpio_chip *chip,
3098c2ecf20Sopenharmony_ci					 unsigned offset, int value)
3108c2ecf20Sopenharmony_ci{
3118c2ecf20Sopenharmony_ci	lpc18xx_gpio_set(chip, offset, value);
3128c2ecf20Sopenharmony_ci	return lpc18xx_gpio_direction(chip, offset, true);
3138c2ecf20Sopenharmony_ci}
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_cistatic const struct gpio_chip lpc18xx_chip = {
3168c2ecf20Sopenharmony_ci	.label			= "lpc18xx/43xx-gpio",
3178c2ecf20Sopenharmony_ci	.request		= gpiochip_generic_request,
3188c2ecf20Sopenharmony_ci	.free			= gpiochip_generic_free,
3198c2ecf20Sopenharmony_ci	.direction_input	= lpc18xx_gpio_direction_input,
3208c2ecf20Sopenharmony_ci	.direction_output	= lpc18xx_gpio_direction_output,
3218c2ecf20Sopenharmony_ci	.set			= lpc18xx_gpio_set,
3228c2ecf20Sopenharmony_ci	.get			= lpc18xx_gpio_get,
3238c2ecf20Sopenharmony_ci	.ngpio			= LPC18XX_MAX_PORTS * LPC18XX_PINS_PER_PORT,
3248c2ecf20Sopenharmony_ci	.owner			= THIS_MODULE,
3258c2ecf20Sopenharmony_ci};
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_cistatic int lpc18xx_gpio_probe(struct platform_device *pdev)
3288c2ecf20Sopenharmony_ci{
3298c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
3308c2ecf20Sopenharmony_ci	struct lpc18xx_gpio_chip *gc;
3318c2ecf20Sopenharmony_ci	int index, ret;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	gc = devm_kzalloc(dev, sizeof(*gc), GFP_KERNEL);
3348c2ecf20Sopenharmony_ci	if (!gc)
3358c2ecf20Sopenharmony_ci		return -ENOMEM;
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	gc->gpio = lpc18xx_chip;
3388c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, gc);
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	index = of_property_match_string(dev->of_node, "reg-names", "gpio");
3418c2ecf20Sopenharmony_ci	if (index < 0) {
3428c2ecf20Sopenharmony_ci		/* To support backward compatibility take the first resource */
3438c2ecf20Sopenharmony_ci		gc->base = devm_platform_ioremap_resource(pdev, 0);
3448c2ecf20Sopenharmony_ci	} else {
3458c2ecf20Sopenharmony_ci		struct resource res;
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci		ret = of_address_to_resource(dev->of_node, index, &res);
3488c2ecf20Sopenharmony_ci		if (ret < 0)
3498c2ecf20Sopenharmony_ci			return ret;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci		gc->base = devm_ioremap_resource(dev, &res);
3528c2ecf20Sopenharmony_ci	}
3538c2ecf20Sopenharmony_ci	if (IS_ERR(gc->base))
3548c2ecf20Sopenharmony_ci		return PTR_ERR(gc->base);
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	gc->clk = devm_clk_get(dev, NULL);
3578c2ecf20Sopenharmony_ci	if (IS_ERR(gc->clk)) {
3588c2ecf20Sopenharmony_ci		dev_err(dev, "input clock not found\n");
3598c2ecf20Sopenharmony_ci		return PTR_ERR(gc->clk);
3608c2ecf20Sopenharmony_ci	}
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(gc->clk);
3638c2ecf20Sopenharmony_ci	if (ret) {
3648c2ecf20Sopenharmony_ci		dev_err(dev, "unable to enable clock\n");
3658c2ecf20Sopenharmony_ci		return ret;
3668c2ecf20Sopenharmony_ci	}
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	spin_lock_init(&gc->lock);
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	gc->gpio.parent = dev;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	ret = devm_gpiochip_add_data(dev, &gc->gpio, gc);
3738c2ecf20Sopenharmony_ci	if (ret) {
3748c2ecf20Sopenharmony_ci		dev_err(dev, "failed to add gpio chip\n");
3758c2ecf20Sopenharmony_ci		clk_disable_unprepare(gc->clk);
3768c2ecf20Sopenharmony_ci		return ret;
3778c2ecf20Sopenharmony_ci	}
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	/* On error GPIO pin interrupt controller just won't be registered */
3808c2ecf20Sopenharmony_ci	lpc18xx_gpio_pin_ic_probe(gc);
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	return 0;
3838c2ecf20Sopenharmony_ci}
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_cistatic int lpc18xx_gpio_remove(struct platform_device *pdev)
3868c2ecf20Sopenharmony_ci{
3878c2ecf20Sopenharmony_ci	struct lpc18xx_gpio_chip *gc = platform_get_drvdata(pdev);
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	if (gc->pin_ic)
3908c2ecf20Sopenharmony_ci		irq_domain_remove(gc->pin_ic->domain);
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	clk_disable_unprepare(gc->clk);
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	return 0;
3958c2ecf20Sopenharmony_ci}
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_cistatic const struct of_device_id lpc18xx_gpio_match[] = {
3988c2ecf20Sopenharmony_ci	{ .compatible = "nxp,lpc1850-gpio" },
3998c2ecf20Sopenharmony_ci	{ }
4008c2ecf20Sopenharmony_ci};
4018c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, lpc18xx_gpio_match);
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_cistatic struct platform_driver lpc18xx_gpio_driver = {
4048c2ecf20Sopenharmony_ci	.probe	= lpc18xx_gpio_probe,
4058c2ecf20Sopenharmony_ci	.remove	= lpc18xx_gpio_remove,
4068c2ecf20Sopenharmony_ci	.driver	= {
4078c2ecf20Sopenharmony_ci		.name		= "lpc18xx-gpio",
4088c2ecf20Sopenharmony_ci		.of_match_table	= lpc18xx_gpio_match,
4098c2ecf20Sopenharmony_ci	},
4108c2ecf20Sopenharmony_ci};
4118c2ecf20Sopenharmony_cimodule_platform_driver(lpc18xx_gpio_driver);
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ciMODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>");
4148c2ecf20Sopenharmony_ciMODULE_AUTHOR("Vladimir Zapolskiy <vz@mleia.com>");
4158c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("GPIO driver for LPC18xx/43xx");
4168c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
417