xref: /openbmc/linux/drivers/gpio/gpio-idt3243x.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
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