162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci//
362306a36Sopenharmony_ci// IXP4 GPIO driver
462306a36Sopenharmony_ci// Copyright (C) 2019 Linus Walleij <linus.walleij@linaro.org>
562306a36Sopenharmony_ci//
662306a36Sopenharmony_ci// based on previous work and know-how from:
762306a36Sopenharmony_ci// Deepak Saxena <dsaxena@plexity.net>
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/gpio/driver.h>
1062306a36Sopenharmony_ci#include <linux/io.h>
1162306a36Sopenharmony_ci#include <linux/irq.h>
1262306a36Sopenharmony_ci#include <linux/irqdomain.h>
1362306a36Sopenharmony_ci#include <linux/irqchip.h>
1462306a36Sopenharmony_ci#include <linux/of_irq.h>
1562306a36Sopenharmony_ci#include <linux/platform_device.h>
1662306a36Sopenharmony_ci#include <linux/bitops.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#define IXP4XX_REG_GPOUT	0x00
1962306a36Sopenharmony_ci#define IXP4XX_REG_GPOE		0x04
2062306a36Sopenharmony_ci#define IXP4XX_REG_GPIN		0x08
2162306a36Sopenharmony_ci#define IXP4XX_REG_GPIS		0x0C
2262306a36Sopenharmony_ci#define IXP4XX_REG_GPIT1	0x10
2362306a36Sopenharmony_ci#define IXP4XX_REG_GPIT2	0x14
2462306a36Sopenharmony_ci#define IXP4XX_REG_GPCLK	0x18
2562306a36Sopenharmony_ci#define IXP4XX_REG_GPDBSEL	0x1C
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci/*
2862306a36Sopenharmony_ci * The hardware uses 3 bits to indicate interrupt "style".
2962306a36Sopenharmony_ci * we clear and set these three bits accordingly. The lower 24
3062306a36Sopenharmony_ci * bits in two registers (GPIT1 and GPIT2) are used to set up
3162306a36Sopenharmony_ci * the style for 8 lines each for a total of 16 GPIO lines.
3262306a36Sopenharmony_ci */
3362306a36Sopenharmony_ci#define IXP4XX_GPIO_STYLE_ACTIVE_HIGH	0x0
3462306a36Sopenharmony_ci#define IXP4XX_GPIO_STYLE_ACTIVE_LOW	0x1
3562306a36Sopenharmony_ci#define IXP4XX_GPIO_STYLE_RISING_EDGE	0x2
3662306a36Sopenharmony_ci#define IXP4XX_GPIO_STYLE_FALLING_EDGE	0x3
3762306a36Sopenharmony_ci#define IXP4XX_GPIO_STYLE_TRANSITIONAL	0x4
3862306a36Sopenharmony_ci#define IXP4XX_GPIO_STYLE_MASK		GENMASK(2, 0)
3962306a36Sopenharmony_ci#define IXP4XX_GPIO_STYLE_SIZE		3
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci/**
4262306a36Sopenharmony_ci * struct ixp4xx_gpio - IXP4 GPIO state container
4362306a36Sopenharmony_ci * @dev: containing device for this instance
4462306a36Sopenharmony_ci * @fwnode: the fwnode for this GPIO chip
4562306a36Sopenharmony_ci * @gc: gpiochip for this instance
4662306a36Sopenharmony_ci * @base: remapped I/O-memory base
4762306a36Sopenharmony_ci * @irq_edge: Each bit represents an IRQ: 1: edge-triggered,
4862306a36Sopenharmony_ci * 0: level triggered
4962306a36Sopenharmony_ci */
5062306a36Sopenharmony_cistruct ixp4xx_gpio {
5162306a36Sopenharmony_ci	struct device *dev;
5262306a36Sopenharmony_ci	struct fwnode_handle *fwnode;
5362306a36Sopenharmony_ci	struct gpio_chip gc;
5462306a36Sopenharmony_ci	void __iomem *base;
5562306a36Sopenharmony_ci	unsigned long long irq_edge;
5662306a36Sopenharmony_ci};
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistatic void ixp4xx_gpio_irq_ack(struct irq_data *d)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
6162306a36Sopenharmony_ci	struct ixp4xx_gpio *g = gpiochip_get_data(gc);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	__raw_writel(BIT(d->hwirq), g->base + IXP4XX_REG_GPIS);
6462306a36Sopenharmony_ci}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistatic void ixp4xx_gpio_mask_irq(struct irq_data *d)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	irq_chip_mask_parent(d);
7162306a36Sopenharmony_ci	gpiochip_disable_irq(gc, d->hwirq);
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic void ixp4xx_gpio_irq_unmask(struct irq_data *d)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
7762306a36Sopenharmony_ci	struct ixp4xx_gpio *g = gpiochip_get_data(gc);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	/* ACK when unmasking if not edge-triggered */
8062306a36Sopenharmony_ci	if (!(g->irq_edge & BIT(d->hwirq)))
8162306a36Sopenharmony_ci		ixp4xx_gpio_irq_ack(d);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	gpiochip_enable_irq(gc, d->hwirq);
8462306a36Sopenharmony_ci	irq_chip_unmask_parent(d);
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic int ixp4xx_gpio_irq_set_type(struct irq_data *d, unsigned int type)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
9062306a36Sopenharmony_ci	struct ixp4xx_gpio *g = gpiochip_get_data(gc);
9162306a36Sopenharmony_ci	int line = d->hwirq;
9262306a36Sopenharmony_ci	unsigned long flags;
9362306a36Sopenharmony_ci	u32 int_style;
9462306a36Sopenharmony_ci	u32 int_reg;
9562306a36Sopenharmony_ci	u32 val;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	switch (type) {
9862306a36Sopenharmony_ci	case IRQ_TYPE_EDGE_BOTH:
9962306a36Sopenharmony_ci		irq_set_handler_locked(d, handle_edge_irq);
10062306a36Sopenharmony_ci		int_style = IXP4XX_GPIO_STYLE_TRANSITIONAL;
10162306a36Sopenharmony_ci		g->irq_edge |= BIT(d->hwirq);
10262306a36Sopenharmony_ci		break;
10362306a36Sopenharmony_ci	case IRQ_TYPE_EDGE_RISING:
10462306a36Sopenharmony_ci		irq_set_handler_locked(d, handle_edge_irq);
10562306a36Sopenharmony_ci		int_style = IXP4XX_GPIO_STYLE_RISING_EDGE;
10662306a36Sopenharmony_ci		g->irq_edge |= BIT(d->hwirq);
10762306a36Sopenharmony_ci		break;
10862306a36Sopenharmony_ci	case IRQ_TYPE_EDGE_FALLING:
10962306a36Sopenharmony_ci		irq_set_handler_locked(d, handle_edge_irq);
11062306a36Sopenharmony_ci		int_style = IXP4XX_GPIO_STYLE_FALLING_EDGE;
11162306a36Sopenharmony_ci		g->irq_edge |= BIT(d->hwirq);
11262306a36Sopenharmony_ci		break;
11362306a36Sopenharmony_ci	case IRQ_TYPE_LEVEL_HIGH:
11462306a36Sopenharmony_ci		irq_set_handler_locked(d, handle_level_irq);
11562306a36Sopenharmony_ci		int_style = IXP4XX_GPIO_STYLE_ACTIVE_HIGH;
11662306a36Sopenharmony_ci		g->irq_edge &= ~BIT(d->hwirq);
11762306a36Sopenharmony_ci		break;
11862306a36Sopenharmony_ci	case IRQ_TYPE_LEVEL_LOW:
11962306a36Sopenharmony_ci		irq_set_handler_locked(d, handle_level_irq);
12062306a36Sopenharmony_ci		int_style = IXP4XX_GPIO_STYLE_ACTIVE_LOW;
12162306a36Sopenharmony_ci		g->irq_edge &= ~BIT(d->hwirq);
12262306a36Sopenharmony_ci		break;
12362306a36Sopenharmony_ci	default:
12462306a36Sopenharmony_ci		return -EINVAL;
12562306a36Sopenharmony_ci	}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	if (line >= 8) {
12862306a36Sopenharmony_ci		/* pins 8-15 */
12962306a36Sopenharmony_ci		line -= 8;
13062306a36Sopenharmony_ci		int_reg = IXP4XX_REG_GPIT2;
13162306a36Sopenharmony_ci	} else {
13262306a36Sopenharmony_ci		/* pins 0-7 */
13362306a36Sopenharmony_ci		int_reg = IXP4XX_REG_GPIT1;
13462306a36Sopenharmony_ci	}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	raw_spin_lock_irqsave(&g->gc.bgpio_lock, flags);
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	/* Clear the style for the appropriate pin */
13962306a36Sopenharmony_ci	val = __raw_readl(g->base + int_reg);
14062306a36Sopenharmony_ci	val &= ~(IXP4XX_GPIO_STYLE_MASK << (line * IXP4XX_GPIO_STYLE_SIZE));
14162306a36Sopenharmony_ci	__raw_writel(val, g->base + int_reg);
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	__raw_writel(BIT(line), g->base + IXP4XX_REG_GPIS);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	/* Set the new style */
14662306a36Sopenharmony_ci	val = __raw_readl(g->base + int_reg);
14762306a36Sopenharmony_ci	val |= (int_style << (line * IXP4XX_GPIO_STYLE_SIZE));
14862306a36Sopenharmony_ci	__raw_writel(val, g->base + int_reg);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	/* Force-configure this line as an input */
15162306a36Sopenharmony_ci	val = __raw_readl(g->base + IXP4XX_REG_GPOE);
15262306a36Sopenharmony_ci	val |= BIT(d->hwirq);
15362306a36Sopenharmony_ci	__raw_writel(val, g->base + IXP4XX_REG_GPOE);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&g->gc.bgpio_lock, flags);
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	/* This parent only accept level high (asserted) */
15862306a36Sopenharmony_ci	return irq_chip_set_type_parent(d, IRQ_TYPE_LEVEL_HIGH);
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_cistatic const struct irq_chip ixp4xx_gpio_irqchip = {
16262306a36Sopenharmony_ci	.name = "IXP4GPIO",
16362306a36Sopenharmony_ci	.irq_ack = ixp4xx_gpio_irq_ack,
16462306a36Sopenharmony_ci	.irq_mask = ixp4xx_gpio_mask_irq,
16562306a36Sopenharmony_ci	.irq_unmask = ixp4xx_gpio_irq_unmask,
16662306a36Sopenharmony_ci	.irq_set_type = ixp4xx_gpio_irq_set_type,
16762306a36Sopenharmony_ci	.flags = IRQCHIP_IMMUTABLE,
16862306a36Sopenharmony_ci	GPIOCHIP_IRQ_RESOURCE_HELPERS,
16962306a36Sopenharmony_ci};
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_cistatic int ixp4xx_gpio_child_to_parent_hwirq(struct gpio_chip *gc,
17262306a36Sopenharmony_ci					     unsigned int child,
17362306a36Sopenharmony_ci					     unsigned int child_type,
17462306a36Sopenharmony_ci					     unsigned int *parent,
17562306a36Sopenharmony_ci					     unsigned int *parent_type)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	/* All these interrupts are level high in the CPU */
17862306a36Sopenharmony_ci	*parent_type = IRQ_TYPE_LEVEL_HIGH;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	/* GPIO lines 0..12 have dedicated IRQs */
18162306a36Sopenharmony_ci	if (child == 0) {
18262306a36Sopenharmony_ci		*parent = 6;
18362306a36Sopenharmony_ci		return 0;
18462306a36Sopenharmony_ci	}
18562306a36Sopenharmony_ci	if (child == 1) {
18662306a36Sopenharmony_ci		*parent = 7;
18762306a36Sopenharmony_ci		return 0;
18862306a36Sopenharmony_ci	}
18962306a36Sopenharmony_ci	if (child >= 2 && child <= 12) {
19062306a36Sopenharmony_ci		*parent = child + 17;
19162306a36Sopenharmony_ci		return 0;
19262306a36Sopenharmony_ci	}
19362306a36Sopenharmony_ci	return -EINVAL;
19462306a36Sopenharmony_ci}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_cistatic int ixp4xx_gpio_probe(struct platform_device *pdev)
19762306a36Sopenharmony_ci{
19862306a36Sopenharmony_ci	unsigned long flags;
19962306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
20062306a36Sopenharmony_ci	struct device_node *np = dev->of_node;
20162306a36Sopenharmony_ci	struct irq_domain *parent;
20262306a36Sopenharmony_ci	struct ixp4xx_gpio *g;
20362306a36Sopenharmony_ci	struct gpio_irq_chip *girq;
20462306a36Sopenharmony_ci	struct device_node *irq_parent;
20562306a36Sopenharmony_ci	int ret;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	g = devm_kzalloc(dev, sizeof(*g), GFP_KERNEL);
20862306a36Sopenharmony_ci	if (!g)
20962306a36Sopenharmony_ci		return -ENOMEM;
21062306a36Sopenharmony_ci	g->dev = dev;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	g->base = devm_platform_ioremap_resource(pdev, 0);
21362306a36Sopenharmony_ci	if (IS_ERR(g->base))
21462306a36Sopenharmony_ci		return PTR_ERR(g->base);
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	irq_parent = of_irq_find_parent(np);
21762306a36Sopenharmony_ci	if (!irq_parent) {
21862306a36Sopenharmony_ci		dev_err(dev, "no IRQ parent node\n");
21962306a36Sopenharmony_ci		return -ENODEV;
22062306a36Sopenharmony_ci	}
22162306a36Sopenharmony_ci	parent = irq_find_host(irq_parent);
22262306a36Sopenharmony_ci	if (!parent) {
22362306a36Sopenharmony_ci		dev_err(dev, "no IRQ parent domain\n");
22462306a36Sopenharmony_ci		return -ENODEV;
22562306a36Sopenharmony_ci	}
22662306a36Sopenharmony_ci	g->fwnode = of_node_to_fwnode(np);
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	/*
22962306a36Sopenharmony_ci	 * Make sure GPIO 14 and 15 are NOT used as clocks but GPIO on
23062306a36Sopenharmony_ci	 * specific machines.
23162306a36Sopenharmony_ci	 */
23262306a36Sopenharmony_ci	if (of_machine_is_compatible("dlink,dsm-g600-a") ||
23362306a36Sopenharmony_ci	    of_machine_is_compatible("iom,nas-100d"))
23462306a36Sopenharmony_ci		__raw_writel(0x0, g->base + IXP4XX_REG_GPCLK);
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	/*
23762306a36Sopenharmony_ci	 * This is a very special big-endian ARM issue: when the IXP4xx is
23862306a36Sopenharmony_ci	 * run in big endian mode, all registers in the machine are switched
23962306a36Sopenharmony_ci	 * around to the CPU-native endianness. As you see mostly in the
24062306a36Sopenharmony_ci	 * driver we use __raw_readl()/__raw_writel() to access the registers
24162306a36Sopenharmony_ci	 * in the appropriate order. With the GPIO library we need to specify
24262306a36Sopenharmony_ci	 * byte order explicitly, so this flag needs to be set when compiling
24362306a36Sopenharmony_ci	 * for big endian.
24462306a36Sopenharmony_ci	 */
24562306a36Sopenharmony_ci#if defined(CONFIG_CPU_BIG_ENDIAN)
24662306a36Sopenharmony_ci	flags = BGPIOF_BIG_ENDIAN_BYTE_ORDER;
24762306a36Sopenharmony_ci#else
24862306a36Sopenharmony_ci	flags = 0;
24962306a36Sopenharmony_ci#endif
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	/* Populate and register gpio chip */
25262306a36Sopenharmony_ci	ret = bgpio_init(&g->gc, dev, 4,
25362306a36Sopenharmony_ci			 g->base + IXP4XX_REG_GPIN,
25462306a36Sopenharmony_ci			 g->base + IXP4XX_REG_GPOUT,
25562306a36Sopenharmony_ci			 NULL,
25662306a36Sopenharmony_ci			 NULL,
25762306a36Sopenharmony_ci			 g->base + IXP4XX_REG_GPOE,
25862306a36Sopenharmony_ci			 flags);
25962306a36Sopenharmony_ci	if (ret) {
26062306a36Sopenharmony_ci		dev_err(dev, "unable to init generic GPIO\n");
26162306a36Sopenharmony_ci		return ret;
26262306a36Sopenharmony_ci	}
26362306a36Sopenharmony_ci	g->gc.ngpio = 16;
26462306a36Sopenharmony_ci	g->gc.label = "IXP4XX_GPIO_CHIP";
26562306a36Sopenharmony_ci	/*
26662306a36Sopenharmony_ci	 * TODO: when we have migrated to device tree and all GPIOs
26762306a36Sopenharmony_ci	 * are fetched using phandles, set this to -1 to get rid of
26862306a36Sopenharmony_ci	 * the fixed gpiochip base.
26962306a36Sopenharmony_ci	 */
27062306a36Sopenharmony_ci	g->gc.base = 0;
27162306a36Sopenharmony_ci	g->gc.parent = &pdev->dev;
27262306a36Sopenharmony_ci	g->gc.owner = THIS_MODULE;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	girq = &g->gc.irq;
27562306a36Sopenharmony_ci	gpio_irq_chip_set_chip(girq, &ixp4xx_gpio_irqchip);
27662306a36Sopenharmony_ci	girq->fwnode = g->fwnode;
27762306a36Sopenharmony_ci	girq->parent_domain = parent;
27862306a36Sopenharmony_ci	girq->child_to_parent_hwirq = ixp4xx_gpio_child_to_parent_hwirq;
27962306a36Sopenharmony_ci	girq->handler = handle_bad_irq;
28062306a36Sopenharmony_ci	girq->default_type = IRQ_TYPE_NONE;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	ret = devm_gpiochip_add_data(dev, &g->gc, g);
28362306a36Sopenharmony_ci	if (ret) {
28462306a36Sopenharmony_ci		dev_err(dev, "failed to add SoC gpiochip\n");
28562306a36Sopenharmony_ci		return ret;
28662306a36Sopenharmony_ci	}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	platform_set_drvdata(pdev, g);
28962306a36Sopenharmony_ci	dev_info(dev, "IXP4 GPIO registered\n");
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	return 0;
29262306a36Sopenharmony_ci}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_cistatic const struct of_device_id ixp4xx_gpio_of_match[] = {
29562306a36Sopenharmony_ci	{
29662306a36Sopenharmony_ci		.compatible = "intel,ixp4xx-gpio",
29762306a36Sopenharmony_ci	},
29862306a36Sopenharmony_ci	{},
29962306a36Sopenharmony_ci};
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_cistatic struct platform_driver ixp4xx_gpio_driver = {
30362306a36Sopenharmony_ci	.driver = {
30462306a36Sopenharmony_ci		.name		= "ixp4xx-gpio",
30562306a36Sopenharmony_ci		.of_match_table = ixp4xx_gpio_of_match,
30662306a36Sopenharmony_ci	},
30762306a36Sopenharmony_ci	.probe = ixp4xx_gpio_probe,
30862306a36Sopenharmony_ci};
30962306a36Sopenharmony_cibuiltin_platform_driver(ixp4xx_gpio_driver);
310