xref: /linux/drivers/gpio/gpio-sifive.c (revision 06d07429858317ded2db7986113a9e0129cd599b)
196868dceSYash Shah // SPDX-License-Identifier: GPL-2.0
296868dceSYash Shah /*
396868dceSYash Shah  * Copyright (C) 2019 SiFive
496868dceSYash Shah  */
596868dceSYash Shah 
696868dceSYash Shah #include <linux/bitops.h>
796868dceSYash Shah #include <linux/device.h>
896868dceSYash Shah #include <linux/errno.h>
996868dceSYash Shah #include <linux/gpio/driver.h>
1096868dceSYash Shah #include <linux/init.h>
1196868dceSYash Shah #include <linux/platform_device.h>
125d472a7eSSamuel Holland #include <linux/property.h>
1396868dceSYash Shah #include <linux/slab.h>
1496868dceSYash Shah #include <linux/spinlock.h>
1596868dceSYash Shah #include <linux/regmap.h>
1696868dceSYash Shah 
1796868dceSYash Shah #define SIFIVE_GPIO_INPUT_VAL	0x00
1896868dceSYash Shah #define SIFIVE_GPIO_INPUT_EN	0x04
1996868dceSYash Shah #define SIFIVE_GPIO_OUTPUT_EN	0x08
2096868dceSYash Shah #define SIFIVE_GPIO_OUTPUT_VAL	0x0C
2196868dceSYash Shah #define SIFIVE_GPIO_RISE_IE	0x18
2296868dceSYash Shah #define SIFIVE_GPIO_RISE_IP	0x1C
2396868dceSYash Shah #define SIFIVE_GPIO_FALL_IE	0x20
2496868dceSYash Shah #define SIFIVE_GPIO_FALL_IP	0x24
2596868dceSYash Shah #define SIFIVE_GPIO_HIGH_IE	0x28
2696868dceSYash Shah #define SIFIVE_GPIO_HIGH_IP	0x2C
2796868dceSYash Shah #define SIFIVE_GPIO_LOW_IE	0x30
2896868dceSYash Shah #define SIFIVE_GPIO_LOW_IP	0x34
2996868dceSYash Shah #define SIFIVE_GPIO_OUTPUT_XOR	0x40
3096868dceSYash Shah 
3196868dceSYash Shah #define SIFIVE_GPIO_MAX		32
3296868dceSYash Shah 
3396868dceSYash Shah struct sifive_gpio {
3496868dceSYash Shah 	void __iomem		*base;
3596868dceSYash Shah 	struct gpio_chip	gc;
3696868dceSYash Shah 	struct regmap		*regs;
37a924eae7SYash Shah 	unsigned long		irq_state;
3896868dceSYash Shah 	unsigned int		trigger[SIFIVE_GPIO_MAX];
39f52d6d8bSGreentime Hu 	unsigned int		irq_number[SIFIVE_GPIO_MAX];
4096868dceSYash Shah };
4196868dceSYash Shah 
sifive_gpio_set_ie(struct sifive_gpio * chip,unsigned int offset)4296868dceSYash Shah static void sifive_gpio_set_ie(struct sifive_gpio *chip, unsigned int offset)
4396868dceSYash Shah {
4496868dceSYash Shah 	unsigned long flags;
4596868dceSYash Shah 	unsigned int trigger;
4696868dceSYash Shah 
473c938cc5SSchspa Shi 	raw_spin_lock_irqsave(&chip->gc.bgpio_lock, flags);
4896868dceSYash Shah 	trigger = (chip->irq_state & BIT(offset)) ? chip->trigger[offset] : 0;
4996868dceSYash Shah 	regmap_update_bits(chip->regs, SIFIVE_GPIO_RISE_IE, BIT(offset),
5096868dceSYash Shah 			   (trigger & IRQ_TYPE_EDGE_RISING) ? BIT(offset) : 0);
5196868dceSYash Shah 	regmap_update_bits(chip->regs, SIFIVE_GPIO_FALL_IE, BIT(offset),
5296868dceSYash Shah 			   (trigger & IRQ_TYPE_EDGE_FALLING) ? BIT(offset) : 0);
5396868dceSYash Shah 	regmap_update_bits(chip->regs, SIFIVE_GPIO_HIGH_IE, BIT(offset),
5496868dceSYash Shah 			   (trigger & IRQ_TYPE_LEVEL_HIGH) ? BIT(offset) : 0);
5596868dceSYash Shah 	regmap_update_bits(chip->regs, SIFIVE_GPIO_LOW_IE, BIT(offset),
5696868dceSYash Shah 			   (trigger & IRQ_TYPE_LEVEL_LOW) ? BIT(offset) : 0);
573c938cc5SSchspa Shi 	raw_spin_unlock_irqrestore(&chip->gc.bgpio_lock, flags);
5896868dceSYash Shah }
5996868dceSYash Shah 
sifive_gpio_irq_set_type(struct irq_data * d,unsigned int trigger)6096868dceSYash Shah static int sifive_gpio_irq_set_type(struct irq_data *d, unsigned int trigger)
6196868dceSYash Shah {
6296868dceSYash Shah 	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
6396868dceSYash Shah 	struct sifive_gpio *chip = gpiochip_get_data(gc);
6496868dceSYash Shah 	int offset = irqd_to_hwirq(d);
6596868dceSYash Shah 
6696868dceSYash Shah 	if (offset < 0 || offset >= gc->ngpio)
6796868dceSYash Shah 		return -EINVAL;
6896868dceSYash Shah 
6996868dceSYash Shah 	chip->trigger[offset] = trigger;
7096868dceSYash Shah 	sifive_gpio_set_ie(chip, offset);
7196868dceSYash Shah 	return 0;
7296868dceSYash Shah }
7396868dceSYash Shah 
sifive_gpio_irq_enable(struct irq_data * d)7496868dceSYash Shah static void sifive_gpio_irq_enable(struct irq_data *d)
7596868dceSYash Shah {
7696868dceSYash Shah 	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
7796868dceSYash Shah 	struct sifive_gpio *chip = gpiochip_get_data(gc);
785a7cb9f3SGeert Uytterhoeven 	irq_hw_number_t hwirq = irqd_to_hwirq(d);
795a7cb9f3SGeert Uytterhoeven 	int offset = hwirq % SIFIVE_GPIO_MAX;
8096868dceSYash Shah 	u32 bit = BIT(offset);
8196868dceSYash Shah 	unsigned long flags;
8296868dceSYash Shah 
835a7cb9f3SGeert Uytterhoeven 	gpiochip_enable_irq(gc, hwirq);
8496868dceSYash Shah 	irq_chip_enable_parent(d);
8596868dceSYash Shah 
8696868dceSYash Shah 	/* Switch to input */
8796868dceSYash Shah 	gc->direction_input(gc, offset);
8896868dceSYash Shah 
893c938cc5SSchspa Shi 	raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
9096868dceSYash Shah 	/* Clear any sticky pending interrupts */
9196868dceSYash Shah 	regmap_write(chip->regs, SIFIVE_GPIO_RISE_IP, bit);
9296868dceSYash Shah 	regmap_write(chip->regs, SIFIVE_GPIO_FALL_IP, bit);
9396868dceSYash Shah 	regmap_write(chip->regs, SIFIVE_GPIO_HIGH_IP, bit);
9496868dceSYash Shah 	regmap_write(chip->regs, SIFIVE_GPIO_LOW_IP, bit);
953c938cc5SSchspa Shi 	raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
9696868dceSYash Shah 
9796868dceSYash Shah 	/* Enable interrupts */
98a924eae7SYash Shah 	assign_bit(offset, &chip->irq_state, 1);
9996868dceSYash Shah 	sifive_gpio_set_ie(chip, offset);
10096868dceSYash Shah }
10196868dceSYash Shah 
sifive_gpio_irq_disable(struct irq_data * d)10296868dceSYash Shah static void sifive_gpio_irq_disable(struct irq_data *d)
10396868dceSYash Shah {
10496868dceSYash Shah 	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
10596868dceSYash Shah 	struct sifive_gpio *chip = gpiochip_get_data(gc);
1065a7cb9f3SGeert Uytterhoeven 	irq_hw_number_t hwirq = irqd_to_hwirq(d);
1075a7cb9f3SGeert Uytterhoeven 	int offset = hwirq % SIFIVE_GPIO_MAX;
10896868dceSYash Shah 
109a924eae7SYash Shah 	assign_bit(offset, &chip->irq_state, 0);
11096868dceSYash Shah 	sifive_gpio_set_ie(chip, offset);
11196868dceSYash Shah 	irq_chip_disable_parent(d);
1125a7cb9f3SGeert Uytterhoeven 	gpiochip_disable_irq(gc, hwirq);
11396868dceSYash Shah }
11496868dceSYash Shah 
sifive_gpio_irq_eoi(struct irq_data * d)11596868dceSYash Shah static void sifive_gpio_irq_eoi(struct irq_data *d)
11696868dceSYash Shah {
11796868dceSYash Shah 	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
11896868dceSYash Shah 	struct sifive_gpio *chip = gpiochip_get_data(gc);
11996868dceSYash Shah 	int offset = irqd_to_hwirq(d) % SIFIVE_GPIO_MAX;
12096868dceSYash Shah 	u32 bit = BIT(offset);
12196868dceSYash Shah 	unsigned long flags;
12296868dceSYash Shah 
1233c938cc5SSchspa Shi 	raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
12496868dceSYash Shah 	/* Clear all pending interrupts */
12596868dceSYash Shah 	regmap_write(chip->regs, SIFIVE_GPIO_RISE_IP, bit);
12696868dceSYash Shah 	regmap_write(chip->regs, SIFIVE_GPIO_FALL_IP, bit);
12796868dceSYash Shah 	regmap_write(chip->regs, SIFIVE_GPIO_HIGH_IP, bit);
12896868dceSYash Shah 	regmap_write(chip->regs, SIFIVE_GPIO_LOW_IP, bit);
1293c938cc5SSchspa Shi 	raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
13096868dceSYash Shah 
13196868dceSYash Shah 	irq_chip_eoi_parent(d);
13296868dceSYash Shah }
13396868dceSYash Shah 
sifive_gpio_irq_set_affinity(struct irq_data * data,const struct cpumask * dest,bool force)134011a78c1SLinus Walleij static int sifive_gpio_irq_set_affinity(struct irq_data *data,
135011a78c1SLinus Walleij 					const struct cpumask *dest,
136011a78c1SLinus Walleij 					bool force)
137011a78c1SLinus Walleij {
138011a78c1SLinus Walleij 	if (data->parent_data)
139011a78c1SLinus Walleij 		return irq_chip_set_affinity_parent(data, dest, force);
140011a78c1SLinus Walleij 
141011a78c1SLinus Walleij 	return -EINVAL;
142011a78c1SLinus Walleij }
143011a78c1SLinus Walleij 
1445a7cb9f3SGeert Uytterhoeven static const struct irq_chip sifive_gpio_irqchip = {
14596868dceSYash Shah 	.name		= "sifive-gpio",
14696868dceSYash Shah 	.irq_set_type	= sifive_gpio_irq_set_type,
14796868dceSYash Shah 	.irq_mask	= irq_chip_mask_parent,
14896868dceSYash Shah 	.irq_unmask	= irq_chip_unmask_parent,
14996868dceSYash Shah 	.irq_enable	= sifive_gpio_irq_enable,
15096868dceSYash Shah 	.irq_disable	= sifive_gpio_irq_disable,
15196868dceSYash Shah 	.irq_eoi	= sifive_gpio_irq_eoi,
152011a78c1SLinus Walleij 	.irq_set_affinity = sifive_gpio_irq_set_affinity,
153d16e0b0eSSamuel Holland 	.irq_set_wake	= irq_chip_set_wake_parent,
1545a7cb9f3SGeert Uytterhoeven 	.flags		= IRQCHIP_IMMUTABLE,
1555a7cb9f3SGeert Uytterhoeven 	GPIOCHIP_IRQ_RESOURCE_HELPERS,
15696868dceSYash Shah };
15796868dceSYash Shah 
sifive_gpio_child_to_parent_hwirq(struct gpio_chip * gc,unsigned int child,unsigned int child_type,unsigned int * parent,unsigned int * parent_type)15896868dceSYash Shah static int sifive_gpio_child_to_parent_hwirq(struct gpio_chip *gc,
15996868dceSYash Shah 					     unsigned int child,
16096868dceSYash Shah 					     unsigned int child_type,
16196868dceSYash Shah 					     unsigned int *parent,
16296868dceSYash Shah 					     unsigned int *parent_type)
16396868dceSYash Shah {
164f52d6d8bSGreentime Hu 	struct sifive_gpio *chip = gpiochip_get_data(gc);
165f52d6d8bSGreentime Hu 	struct irq_data *d = irq_get_irq_data(chip->irq_number[child]);
166f52d6d8bSGreentime Hu 
16796868dceSYash Shah 	*parent_type = IRQ_TYPE_NONE;
168f52d6d8bSGreentime Hu 	*parent = irqd_to_hwirq(d);
169f52d6d8bSGreentime Hu 
17096868dceSYash Shah 	return 0;
17196868dceSYash Shah }
17296868dceSYash Shah 
17396868dceSYash Shah static const struct regmap_config sifive_gpio_regmap_config = {
17496868dceSYash Shah 	.reg_bits = 32,
17596868dceSYash Shah 	.reg_stride = 4,
17696868dceSYash Shah 	.val_bits = 32,
17796868dceSYash Shah 	.fast_io = true,
17896868dceSYash Shah 	.disable_locking = true,
17996868dceSYash Shah };
18096868dceSYash Shah 
sifive_gpio_probe(struct platform_device * pdev)18196868dceSYash Shah static int sifive_gpio_probe(struct platform_device *pdev)
18296868dceSYash Shah {
18396868dceSYash Shah 	struct device *dev = &pdev->dev;
18496868dceSYash Shah 	struct irq_domain *parent;
18596868dceSYash Shah 	struct gpio_irq_chip *girq;
18696868dceSYash Shah 	struct sifive_gpio *chip;
1871cd9cee7SSamuel Holland 	int ret, ngpio;
18896868dceSYash Shah 
18996868dceSYash Shah 	chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
19096868dceSYash Shah 	if (!chip)
19196868dceSYash Shah 		return -ENOMEM;
19296868dceSYash Shah 
19396868dceSYash Shah 	chip->base = devm_platform_ioremap_resource(pdev, 0);
19496868dceSYash Shah 	if (IS_ERR(chip->base)) {
19596868dceSYash Shah 		dev_err(dev, "failed to allocate device memory\n");
19696868dceSYash Shah 		return PTR_ERR(chip->base);
19796868dceSYash Shah 	}
19896868dceSYash Shah 
19996868dceSYash Shah 	chip->regs = devm_regmap_init_mmio(dev, chip->base,
20096868dceSYash Shah 					   &sifive_gpio_regmap_config);
20196868dceSYash Shah 	if (IS_ERR(chip->regs))
20296868dceSYash Shah 		return PTR_ERR(chip->regs);
20396868dceSYash Shah 
2041cd9cee7SSamuel Holland 	for (ngpio = 0; ngpio < SIFIVE_GPIO_MAX; ngpio++) {
2051cd9cee7SSamuel Holland 		ret = platform_get_irq_optional(pdev, ngpio);
206c1bcb976SJiasheng Jiang 		if (ret < 0)
2071cd9cee7SSamuel Holland 			break;
2081cd9cee7SSamuel Holland 		chip->irq_number[ngpio] = ret;
209c1bcb976SJiasheng Jiang 	}
2103b5560c8SSamuel Holland 	if (!ngpio) {
2113b5560c8SSamuel Holland 		dev_err(dev, "no IRQ found\n");
2123b5560c8SSamuel Holland 		return -ENODEV;
2133b5560c8SSamuel Holland 	}
2143b5560c8SSamuel Holland 
2153b5560c8SSamuel Holland 	/*
2163b5560c8SSamuel Holland 	 * The check above ensures at least one parent IRQ is valid.
2173b5560c8SSamuel Holland 	 * Assume all parent IRQs belong to the same domain.
2183b5560c8SSamuel Holland 	 */
2193b5560c8SSamuel Holland 	parent = irq_get_irq_data(chip->irq_number[0])->domain;
220f52d6d8bSGreentime Hu 
22196868dceSYash Shah 	ret = bgpio_init(&chip->gc, dev, 4,
22296868dceSYash Shah 			 chip->base + SIFIVE_GPIO_INPUT_VAL,
22396868dceSYash Shah 			 chip->base + SIFIVE_GPIO_OUTPUT_VAL,
22496868dceSYash Shah 			 NULL,
22596868dceSYash Shah 			 chip->base + SIFIVE_GPIO_OUTPUT_EN,
22696868dceSYash Shah 			 chip->base + SIFIVE_GPIO_INPUT_EN,
227cc38ef93SNiklas Cassel 			 BGPIOF_READ_OUTPUT_REG_SET);
22896868dceSYash Shah 	if (ret) {
22996868dceSYash Shah 		dev_err(dev, "unable to init generic GPIO\n");
23096868dceSYash Shah 		return ret;
23196868dceSYash Shah 	}
23296868dceSYash Shah 
23396868dceSYash Shah 	/* Disable all GPIO interrupts before enabling parent interrupts */
23496868dceSYash Shah 	regmap_write(chip->regs, SIFIVE_GPIO_RISE_IE, 0);
23596868dceSYash Shah 	regmap_write(chip->regs, SIFIVE_GPIO_FALL_IE, 0);
23696868dceSYash Shah 	regmap_write(chip->regs, SIFIVE_GPIO_HIGH_IE, 0);
23796868dceSYash Shah 	regmap_write(chip->regs, SIFIVE_GPIO_LOW_IE, 0);
23896868dceSYash Shah 	chip->irq_state = 0;
23996868dceSYash Shah 
24096868dceSYash Shah 	chip->gc.base = -1;
24196868dceSYash Shah 	chip->gc.ngpio = ngpio;
24296868dceSYash Shah 	chip->gc.label = dev_name(dev);
24396868dceSYash Shah 	chip->gc.parent = dev;
24496868dceSYash Shah 	chip->gc.owner = THIS_MODULE;
24596868dceSYash Shah 	girq = &chip->gc.irq;
2465a7cb9f3SGeert Uytterhoeven 	gpio_irq_chip_set_chip(girq, &sifive_gpio_irqchip);
2475d472a7eSSamuel Holland 	girq->fwnode = dev_fwnode(dev);
24896868dceSYash Shah 	girq->parent_domain = parent;
24996868dceSYash Shah 	girq->child_to_parent_hwirq = sifive_gpio_child_to_parent_hwirq;
25096868dceSYash Shah 	girq->handler = handle_bad_irq;
25196868dceSYash Shah 	girq->default_type = IRQ_TYPE_NONE;
25296868dceSYash Shah 
25396868dceSYash Shah 	return gpiochip_add_data(&chip->gc, chip);
25496868dceSYash Shah }
25596868dceSYash Shah 
25696868dceSYash Shah static const struct of_device_id sifive_gpio_match[] = {
25796868dceSYash Shah 	{ .compatible = "sifive,gpio0" },
25896868dceSYash Shah 	{ .compatible = "sifive,fu540-c000-gpio" },
25996868dceSYash Shah 	{ },
26096868dceSYash Shah };
26196868dceSYash Shah 
26296868dceSYash Shah static struct platform_driver sifive_gpio_driver = {
26396868dceSYash Shah 	.probe		= sifive_gpio_probe,
26496868dceSYash Shah 	.driver = {
26596868dceSYash Shah 		.name	= "sifive_gpio",
2666ae42529SKrzysztof Kozlowski 		.of_match_table = sifive_gpio_match,
26796868dceSYash Shah 	},
26896868dceSYash Shah };
269*6b4c76deSSamuel Holland module_platform_driver(sifive_gpio_driver)
270*6b4c76deSSamuel Holland 
271*6b4c76deSSamuel Holland MODULE_AUTHOR("Yash Shah <yash.shah@sifive.com>");
272*6b4c76deSSamuel Holland MODULE_DESCRIPTION("SiFive GPIO driver");
273*6b4c76deSSamuel Holland MODULE_LICENSE("GPL");
274