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 platform_set_drvdata(pdev, chip);
25496868dceSYash Shah return gpiochip_add_data(&chip->gc, chip);
25596868dceSYash Shah }
25696868dceSYash Shah
25796868dceSYash Shah static const struct of_device_id sifive_gpio_match[] = {
25896868dceSYash Shah { .compatible = "sifive,gpio0" },
25996868dceSYash Shah { .compatible = "sifive,fu540-c000-gpio" },
26096868dceSYash Shah { },
26196868dceSYash Shah };
26296868dceSYash Shah
26396868dceSYash Shah static struct platform_driver sifive_gpio_driver = {
26496868dceSYash Shah .probe = sifive_gpio_probe,
26596868dceSYash Shah .driver = {
26696868dceSYash Shah .name = "sifive_gpio",
2676ae42529SKrzysztof Kozlowski .of_match_table = sifive_gpio_match,
26896868dceSYash Shah },
26996868dceSYash Shah };
270*6b4c76deSSamuel Holland module_platform_driver(sifive_gpio_driver)
271*6b4c76deSSamuel Holland
272*6b4c76deSSamuel Holland MODULE_AUTHOR("Yash Shah <yash.shah@sifive.com>");
273*6b4c76deSSamuel Holland MODULE_DESCRIPTION("SiFive GPIO driver");
274*6b4c76deSSamuel Holland MODULE_LICENSE("GPL");
275