14195926aSThomas Bogendoerfer // SPDX-License-Identifier: GPL-2.0
24195926aSThomas Bogendoerfer /* Driver for IDT/Renesas 79RC3243x Interrupt Controller */
34195926aSThomas Bogendoerfer
44195926aSThomas Bogendoerfer #include <linux/bitops.h>
54195926aSThomas Bogendoerfer #include <linux/gpio/driver.h>
64195926aSThomas Bogendoerfer #include <linux/irq.h>
74195926aSThomas Bogendoerfer #include <linux/module.h>
84195926aSThomas Bogendoerfer #include <linux/mod_devicetable.h>
94195926aSThomas Bogendoerfer #include <linux/platform_device.h>
104195926aSThomas Bogendoerfer #include <linux/spinlock.h>
114195926aSThomas Bogendoerfer
124195926aSThomas Bogendoerfer #define IDT_PIC_IRQ_PEND 0x00
134195926aSThomas Bogendoerfer #define IDT_PIC_IRQ_MASK 0x08
144195926aSThomas Bogendoerfer
154195926aSThomas Bogendoerfer #define IDT_GPIO_DIR 0x00
164195926aSThomas Bogendoerfer #define IDT_GPIO_DATA 0x04
174195926aSThomas Bogendoerfer #define IDT_GPIO_ILEVEL 0x08
184195926aSThomas Bogendoerfer #define IDT_GPIO_ISTAT 0x0C
194195926aSThomas Bogendoerfer
204195926aSThomas Bogendoerfer struct idt_gpio_ctrl {
214195926aSThomas Bogendoerfer struct gpio_chip gc;
224195926aSThomas Bogendoerfer void __iomem *pic;
234195926aSThomas Bogendoerfer void __iomem *gpio;
244195926aSThomas Bogendoerfer u32 mask_cache;
254195926aSThomas Bogendoerfer };
264195926aSThomas Bogendoerfer
idt_gpio_dispatch(struct irq_desc * desc)274195926aSThomas Bogendoerfer static void idt_gpio_dispatch(struct irq_desc *desc)
284195926aSThomas Bogendoerfer {
294195926aSThomas Bogendoerfer struct gpio_chip *gc = irq_desc_get_handler_data(desc);
304195926aSThomas Bogendoerfer struct idt_gpio_ctrl *ctrl = gpiochip_get_data(gc);
314195926aSThomas Bogendoerfer struct irq_chip *host_chip = irq_desc_get_chip(desc);
324195926aSThomas Bogendoerfer unsigned int bit, virq;
334195926aSThomas Bogendoerfer unsigned long pending;
344195926aSThomas Bogendoerfer
354195926aSThomas Bogendoerfer chained_irq_enter(host_chip, desc);
364195926aSThomas Bogendoerfer
374195926aSThomas Bogendoerfer pending = readl(ctrl->pic + IDT_PIC_IRQ_PEND);
384195926aSThomas Bogendoerfer pending &= ~ctrl->mask_cache;
394195926aSThomas Bogendoerfer for_each_set_bit(bit, &pending, gc->ngpio) {
404195926aSThomas Bogendoerfer virq = irq_linear_revmap(gc->irq.domain, bit);
414195926aSThomas Bogendoerfer if (virq)
424195926aSThomas Bogendoerfer generic_handle_irq(virq);
434195926aSThomas Bogendoerfer }
444195926aSThomas Bogendoerfer
454195926aSThomas Bogendoerfer chained_irq_exit(host_chip, desc);
464195926aSThomas Bogendoerfer }
474195926aSThomas Bogendoerfer
idt_gpio_irq_set_type(struct irq_data * d,unsigned int flow_type)484195926aSThomas Bogendoerfer static int idt_gpio_irq_set_type(struct irq_data *d, unsigned int flow_type)
494195926aSThomas Bogendoerfer {
504195926aSThomas Bogendoerfer struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
514195926aSThomas Bogendoerfer struct idt_gpio_ctrl *ctrl = gpiochip_get_data(gc);
524195926aSThomas Bogendoerfer unsigned int sense = flow_type & IRQ_TYPE_SENSE_MASK;
534195926aSThomas Bogendoerfer unsigned long flags;
544195926aSThomas Bogendoerfer u32 ilevel;
554195926aSThomas Bogendoerfer
564195926aSThomas Bogendoerfer /* hardware only supports level triggered */
574195926aSThomas Bogendoerfer if (sense == IRQ_TYPE_NONE || (sense & IRQ_TYPE_EDGE_BOTH))
584195926aSThomas Bogendoerfer return -EINVAL;
594195926aSThomas Bogendoerfer
603c938cc5SSchspa Shi raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
614195926aSThomas Bogendoerfer
624195926aSThomas Bogendoerfer ilevel = readl(ctrl->gpio + IDT_GPIO_ILEVEL);
634195926aSThomas Bogendoerfer if (sense & IRQ_TYPE_LEVEL_HIGH)
644195926aSThomas Bogendoerfer ilevel |= BIT(d->hwirq);
654195926aSThomas Bogendoerfer else if (sense & IRQ_TYPE_LEVEL_LOW)
664195926aSThomas Bogendoerfer ilevel &= ~BIT(d->hwirq);
674195926aSThomas Bogendoerfer
684195926aSThomas Bogendoerfer writel(ilevel, ctrl->gpio + IDT_GPIO_ILEVEL);
694195926aSThomas Bogendoerfer irq_set_handler_locked(d, handle_level_irq);
704195926aSThomas Bogendoerfer
713c938cc5SSchspa Shi raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
724195926aSThomas Bogendoerfer return 0;
734195926aSThomas Bogendoerfer }
744195926aSThomas Bogendoerfer
idt_gpio_ack(struct irq_data * d)754195926aSThomas Bogendoerfer static void idt_gpio_ack(struct irq_data *d)
764195926aSThomas Bogendoerfer {
774195926aSThomas Bogendoerfer struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
784195926aSThomas Bogendoerfer struct idt_gpio_ctrl *ctrl = gpiochip_get_data(gc);
794195926aSThomas Bogendoerfer
804195926aSThomas Bogendoerfer writel(~BIT(d->hwirq), ctrl->gpio + IDT_GPIO_ISTAT);
814195926aSThomas Bogendoerfer }
824195926aSThomas Bogendoerfer
idt_gpio_mask(struct irq_data * d)834195926aSThomas Bogendoerfer static void idt_gpio_mask(struct irq_data *d)
844195926aSThomas Bogendoerfer {
854195926aSThomas Bogendoerfer struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
864195926aSThomas Bogendoerfer struct idt_gpio_ctrl *ctrl = gpiochip_get_data(gc);
874195926aSThomas Bogendoerfer unsigned long flags;
884195926aSThomas Bogendoerfer
893c938cc5SSchspa Shi raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
904195926aSThomas Bogendoerfer
914195926aSThomas Bogendoerfer ctrl->mask_cache |= BIT(d->hwirq);
924195926aSThomas Bogendoerfer writel(ctrl->mask_cache, ctrl->pic + IDT_PIC_IRQ_MASK);
934195926aSThomas Bogendoerfer
943c938cc5SSchspa Shi raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
95*9cd9e23aSLinus Walleij
96*9cd9e23aSLinus Walleij gpiochip_disable_irq(gc, irqd_to_hwirq(d));
974195926aSThomas Bogendoerfer }
984195926aSThomas Bogendoerfer
idt_gpio_unmask(struct irq_data * d)994195926aSThomas Bogendoerfer static void idt_gpio_unmask(struct irq_data *d)
1004195926aSThomas Bogendoerfer {
1014195926aSThomas Bogendoerfer struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
1024195926aSThomas Bogendoerfer struct idt_gpio_ctrl *ctrl = gpiochip_get_data(gc);
1034195926aSThomas Bogendoerfer unsigned long flags;
1044195926aSThomas Bogendoerfer
105*9cd9e23aSLinus Walleij gpiochip_enable_irq(gc, irqd_to_hwirq(d));
1063c938cc5SSchspa Shi raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
1074195926aSThomas Bogendoerfer
1084195926aSThomas Bogendoerfer ctrl->mask_cache &= ~BIT(d->hwirq);
1094195926aSThomas Bogendoerfer writel(ctrl->mask_cache, ctrl->pic + IDT_PIC_IRQ_MASK);
1104195926aSThomas Bogendoerfer
1113c938cc5SSchspa Shi raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
1124195926aSThomas Bogendoerfer }
1134195926aSThomas Bogendoerfer
idt_gpio_irq_init_hw(struct gpio_chip * gc)1144195926aSThomas Bogendoerfer static int idt_gpio_irq_init_hw(struct gpio_chip *gc)
1154195926aSThomas Bogendoerfer {
1164195926aSThomas Bogendoerfer struct idt_gpio_ctrl *ctrl = gpiochip_get_data(gc);
1174195926aSThomas Bogendoerfer
1184195926aSThomas Bogendoerfer /* Mask interrupts. */
1194195926aSThomas Bogendoerfer ctrl->mask_cache = 0xffffffff;
1204195926aSThomas Bogendoerfer writel(ctrl->mask_cache, ctrl->pic + IDT_PIC_IRQ_MASK);
1214195926aSThomas Bogendoerfer
1224195926aSThomas Bogendoerfer return 0;
1234195926aSThomas Bogendoerfer }
1244195926aSThomas Bogendoerfer
125*9cd9e23aSLinus Walleij static const struct irq_chip idt_gpio_irqchip = {
1264195926aSThomas Bogendoerfer .name = "IDTGPIO",
1274195926aSThomas Bogendoerfer .irq_mask = idt_gpio_mask,
1284195926aSThomas Bogendoerfer .irq_ack = idt_gpio_ack,
1294195926aSThomas Bogendoerfer .irq_unmask = idt_gpio_unmask,
130*9cd9e23aSLinus Walleij .irq_set_type = idt_gpio_irq_set_type,
131*9cd9e23aSLinus Walleij .flags = IRQCHIP_IMMUTABLE,
132*9cd9e23aSLinus Walleij GPIOCHIP_IRQ_RESOURCE_HELPERS,
1334195926aSThomas Bogendoerfer };
1344195926aSThomas Bogendoerfer
idt_gpio_probe(struct platform_device * pdev)1354195926aSThomas Bogendoerfer static int idt_gpio_probe(struct platform_device *pdev)
1364195926aSThomas Bogendoerfer {
1374195926aSThomas Bogendoerfer struct device *dev = &pdev->dev;
1384195926aSThomas Bogendoerfer struct gpio_irq_chip *girq;
1394195926aSThomas Bogendoerfer struct idt_gpio_ctrl *ctrl;
1407c1cf555SYang Li int parent_irq;
1414195926aSThomas Bogendoerfer int ngpios;
1424195926aSThomas Bogendoerfer int ret;
1434195926aSThomas Bogendoerfer
1444195926aSThomas Bogendoerfer
1454195926aSThomas Bogendoerfer ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
1464195926aSThomas Bogendoerfer if (!ctrl)
1474195926aSThomas Bogendoerfer return -ENOMEM;
1484195926aSThomas Bogendoerfer
1494195926aSThomas Bogendoerfer ctrl->gpio = devm_platform_ioremap_resource_byname(pdev, "gpio");
150622096fdSWei Yongjun if (IS_ERR(ctrl->gpio))
151622096fdSWei Yongjun return PTR_ERR(ctrl->gpio);
1524195926aSThomas Bogendoerfer
1534195926aSThomas Bogendoerfer ctrl->gc.parent = dev;
1544195926aSThomas Bogendoerfer
1554195926aSThomas Bogendoerfer ret = bgpio_init(&ctrl->gc, &pdev->dev, 4, ctrl->gpio + IDT_GPIO_DATA,
1564195926aSThomas Bogendoerfer NULL, NULL, ctrl->gpio + IDT_GPIO_DIR, NULL, 0);
1574195926aSThomas Bogendoerfer if (ret) {
1584195926aSThomas Bogendoerfer dev_err(dev, "bgpio_init failed\n");
1594195926aSThomas Bogendoerfer return ret;
1604195926aSThomas Bogendoerfer }
1614195926aSThomas Bogendoerfer
1624195926aSThomas Bogendoerfer ret = device_property_read_u32(dev, "ngpios", &ngpios);
1634195926aSThomas Bogendoerfer if (!ret)
1644195926aSThomas Bogendoerfer ctrl->gc.ngpio = ngpios;
1654195926aSThomas Bogendoerfer
1664195926aSThomas Bogendoerfer if (device_property_read_bool(dev, "interrupt-controller")) {
1674195926aSThomas Bogendoerfer ctrl->pic = devm_platform_ioremap_resource_byname(pdev, "pic");
168622096fdSWei Yongjun if (IS_ERR(ctrl->pic))
169622096fdSWei Yongjun return PTR_ERR(ctrl->pic);
1704195926aSThomas Bogendoerfer
1714195926aSThomas Bogendoerfer parent_irq = platform_get_irq(pdev, 0);
17230fee1d7SMiaoqian Lin if (parent_irq < 0)
17330fee1d7SMiaoqian Lin return parent_irq;
1744195926aSThomas Bogendoerfer
1754195926aSThomas Bogendoerfer girq = &ctrl->gc.irq;
176*9cd9e23aSLinus Walleij gpio_irq_chip_set_chip(girq, &idt_gpio_irqchip);
1774195926aSThomas Bogendoerfer girq->init_hw = idt_gpio_irq_init_hw;
1784195926aSThomas Bogendoerfer girq->parent_handler = idt_gpio_dispatch;
1794195926aSThomas Bogendoerfer girq->num_parents = 1;
1804195926aSThomas Bogendoerfer girq->parents = devm_kcalloc(dev, girq->num_parents,
1814195926aSThomas Bogendoerfer sizeof(*girq->parents),
1824195926aSThomas Bogendoerfer GFP_KERNEL);
1834195926aSThomas Bogendoerfer if (!girq->parents)
1844195926aSThomas Bogendoerfer return -ENOMEM;
1854195926aSThomas Bogendoerfer
1864195926aSThomas Bogendoerfer girq->parents[0] = parent_irq;
1874195926aSThomas Bogendoerfer girq->default_type = IRQ_TYPE_NONE;
1884195926aSThomas Bogendoerfer girq->handler = handle_bad_irq;
1894195926aSThomas Bogendoerfer }
1904195926aSThomas Bogendoerfer
1914195926aSThomas Bogendoerfer return devm_gpiochip_add_data(&pdev->dev, &ctrl->gc, ctrl);
1924195926aSThomas Bogendoerfer }
1934195926aSThomas Bogendoerfer
1944195926aSThomas Bogendoerfer static const struct of_device_id idt_gpio_of_match[] = {
1954195926aSThomas Bogendoerfer { .compatible = "idt,32434-gpio" },
1964195926aSThomas Bogendoerfer { }
1974195926aSThomas Bogendoerfer };
1984195926aSThomas Bogendoerfer MODULE_DEVICE_TABLE(of, idt_gpio_of_match);
1994195926aSThomas Bogendoerfer
2004195926aSThomas Bogendoerfer static struct platform_driver idt_gpio_driver = {
2014195926aSThomas Bogendoerfer .probe = idt_gpio_probe,
2024195926aSThomas Bogendoerfer .driver = {
2034195926aSThomas Bogendoerfer .name = "idt3243x-gpio",
2044195926aSThomas Bogendoerfer .of_match_table = idt_gpio_of_match,
2054195926aSThomas Bogendoerfer },
2064195926aSThomas Bogendoerfer };
2074195926aSThomas Bogendoerfer module_platform_driver(idt_gpio_driver);
2084195926aSThomas Bogendoerfer
2094195926aSThomas Bogendoerfer MODULE_DESCRIPTION("IDT 79RC3243x GPIO/PIC Driver");
2104195926aSThomas Bogendoerfer MODULE_AUTHOR("Thomas Bogendoerfer <tsbogend@alpha.franken.de>");
2114195926aSThomas Bogendoerfer MODULE_LICENSE("GPL");
212