16a41b6c5SChris Packham // SPDX-License-Identifier: GPL-2.0 26a41b6c5SChris Packham /* 36a41b6c5SChris Packham * Copyright (C) 2017 Broadcom 46a41b6c5SChris Packham */ 56a41b6c5SChris Packham 66a41b6c5SChris Packham #include <linux/gpio/driver.h> 76a41b6c5SChris Packham #include <linux/init.h> 86a41b6c5SChris Packham #include <linux/interrupt.h> 96a41b6c5SChris Packham #include <linux/io.h> 106a41b6c5SChris Packham #include <linux/irq.h> 116a41b6c5SChris Packham #include <linux/kernel.h> 126a41b6c5SChris Packham #include <linux/module.h> 136a41b6c5SChris Packham #include <linux/platform_device.h> 146a41b6c5SChris Packham #include <linux/spinlock.h> 156a41b6c5SChris Packham 166a41b6c5SChris Packham #define IPROC_CCA_INT_F_GPIOINT BIT(0) 176a41b6c5SChris Packham #define IPROC_CCA_INT_STS 0x20 186a41b6c5SChris Packham #define IPROC_CCA_INT_MASK 0x24 196a41b6c5SChris Packham 206a41b6c5SChris Packham #define IPROC_GPIO_CCA_DIN 0x0 216a41b6c5SChris Packham #define IPROC_GPIO_CCA_DOUT 0x4 226a41b6c5SChris Packham #define IPROC_GPIO_CCA_OUT_EN 0x8 236a41b6c5SChris Packham #define IPROC_GPIO_CCA_INT_LEVEL 0x10 246a41b6c5SChris Packham #define IPROC_GPIO_CCA_INT_LEVEL_MASK 0x14 256a41b6c5SChris Packham #define IPROC_GPIO_CCA_INT_EVENT 0x18 266a41b6c5SChris Packham #define IPROC_GPIO_CCA_INT_EVENT_MASK 0x1C 276a41b6c5SChris Packham #define IPROC_GPIO_CCA_INT_EDGE 0x24 286a41b6c5SChris Packham 296a41b6c5SChris Packham struct iproc_gpio_chip { 306a41b6c5SChris Packham struct irq_chip irqchip; 316a41b6c5SChris Packham struct gpio_chip gc; 326a41b6c5SChris Packham spinlock_t lock; 336a41b6c5SChris Packham struct device *dev; 346a41b6c5SChris Packham void __iomem *base; 356a41b6c5SChris Packham void __iomem *intr; 366a41b6c5SChris Packham }; 376a41b6c5SChris Packham 386a41b6c5SChris Packham static inline struct iproc_gpio_chip * 396a41b6c5SChris Packham to_iproc_gpio(struct gpio_chip *gc) 406a41b6c5SChris Packham { 416a41b6c5SChris Packham return container_of(gc, struct iproc_gpio_chip, gc); 426a41b6c5SChris Packham } 436a41b6c5SChris Packham 446a41b6c5SChris Packham static void iproc_gpio_irq_ack(struct irq_data *d) 456a41b6c5SChris Packham { 466a41b6c5SChris Packham struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 476a41b6c5SChris Packham struct iproc_gpio_chip *chip = to_iproc_gpio(gc); 486a41b6c5SChris Packham int pin = d->hwirq; 496a41b6c5SChris Packham unsigned long flags; 506a41b6c5SChris Packham u32 irq = d->irq; 516a41b6c5SChris Packham u32 irq_type, event_status = 0; 526a41b6c5SChris Packham 536a41b6c5SChris Packham spin_lock_irqsave(&chip->lock, flags); 546a41b6c5SChris Packham irq_type = irq_get_trigger_type(irq); 556a41b6c5SChris Packham if (irq_type & IRQ_TYPE_EDGE_BOTH) { 566a41b6c5SChris Packham event_status |= BIT(pin); 576a41b6c5SChris Packham writel_relaxed(event_status, 586a41b6c5SChris Packham chip->base + IPROC_GPIO_CCA_INT_EVENT); 596a41b6c5SChris Packham } 606a41b6c5SChris Packham spin_unlock_irqrestore(&chip->lock, flags); 616a41b6c5SChris Packham } 626a41b6c5SChris Packham 636a41b6c5SChris Packham static void iproc_gpio_irq_unmask(struct irq_data *d) 646a41b6c5SChris Packham { 656a41b6c5SChris Packham struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 666a41b6c5SChris Packham struct iproc_gpio_chip *chip = to_iproc_gpio(gc); 676a41b6c5SChris Packham int pin = d->hwirq; 686a41b6c5SChris Packham unsigned long flags; 696a41b6c5SChris Packham u32 irq = d->irq; 706a41b6c5SChris Packham u32 int_mask, irq_type, event_mask; 716a41b6c5SChris Packham 726a41b6c5SChris Packham spin_lock_irqsave(&chip->lock, flags); 736a41b6c5SChris Packham irq_type = irq_get_trigger_type(irq); 746a41b6c5SChris Packham event_mask = readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_EVENT_MASK); 756a41b6c5SChris Packham int_mask = readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_LEVEL_MASK); 766a41b6c5SChris Packham 776a41b6c5SChris Packham if (irq_type & IRQ_TYPE_EDGE_BOTH) { 786a41b6c5SChris Packham event_mask |= 1 << pin; 796a41b6c5SChris Packham writel_relaxed(event_mask, 806a41b6c5SChris Packham chip->base + IPROC_GPIO_CCA_INT_EVENT_MASK); 816a41b6c5SChris Packham } else { 826a41b6c5SChris Packham int_mask |= 1 << pin; 836a41b6c5SChris Packham writel_relaxed(int_mask, 846a41b6c5SChris Packham chip->base + IPROC_GPIO_CCA_INT_LEVEL_MASK); 856a41b6c5SChris Packham } 866a41b6c5SChris Packham spin_unlock_irqrestore(&chip->lock, flags); 876a41b6c5SChris Packham } 886a41b6c5SChris Packham 896a41b6c5SChris Packham static void iproc_gpio_irq_mask(struct irq_data *d) 906a41b6c5SChris Packham { 916a41b6c5SChris Packham struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 926a41b6c5SChris Packham struct iproc_gpio_chip *chip = to_iproc_gpio(gc); 936a41b6c5SChris Packham int pin = d->hwirq; 946a41b6c5SChris Packham unsigned long flags; 956a41b6c5SChris Packham u32 irq = d->irq; 966a41b6c5SChris Packham u32 irq_type, int_mask, event_mask; 976a41b6c5SChris Packham 986a41b6c5SChris Packham spin_lock_irqsave(&chip->lock, flags); 996a41b6c5SChris Packham irq_type = irq_get_trigger_type(irq); 1006a41b6c5SChris Packham event_mask = readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_EVENT_MASK); 1016a41b6c5SChris Packham int_mask = readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_LEVEL_MASK); 1026a41b6c5SChris Packham 1036a41b6c5SChris Packham if (irq_type & IRQ_TYPE_EDGE_BOTH) { 1046a41b6c5SChris Packham event_mask &= ~BIT(pin); 1056a41b6c5SChris Packham writel_relaxed(event_mask, 1066a41b6c5SChris Packham chip->base + IPROC_GPIO_CCA_INT_EVENT_MASK); 1076a41b6c5SChris Packham } else { 1086a41b6c5SChris Packham int_mask &= ~BIT(pin); 1096a41b6c5SChris Packham writel_relaxed(int_mask, 1106a41b6c5SChris Packham chip->base + IPROC_GPIO_CCA_INT_LEVEL_MASK); 1116a41b6c5SChris Packham } 1126a41b6c5SChris Packham spin_unlock_irqrestore(&chip->lock, flags); 1136a41b6c5SChris Packham } 1146a41b6c5SChris Packham 1156a41b6c5SChris Packham static int iproc_gpio_irq_set_type(struct irq_data *d, u32 type) 1166a41b6c5SChris Packham { 1176a41b6c5SChris Packham struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 1186a41b6c5SChris Packham struct iproc_gpio_chip *chip = to_iproc_gpio(gc); 1196a41b6c5SChris Packham int pin = d->hwirq; 1206a41b6c5SChris Packham unsigned long flags; 1216a41b6c5SChris Packham u32 irq = d->irq; 1226a41b6c5SChris Packham u32 event_pol, int_pol; 1236a41b6c5SChris Packham int ret = 0; 1246a41b6c5SChris Packham 1256a41b6c5SChris Packham spin_lock_irqsave(&chip->lock, flags); 1266a41b6c5SChris Packham switch (type & IRQ_TYPE_SENSE_MASK) { 1276a41b6c5SChris Packham case IRQ_TYPE_EDGE_RISING: 1286a41b6c5SChris Packham event_pol = readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_EDGE); 1296a41b6c5SChris Packham event_pol &= ~BIT(pin); 1306a41b6c5SChris Packham writel_relaxed(event_pol, chip->base + IPROC_GPIO_CCA_INT_EDGE); 1316a41b6c5SChris Packham break; 1326a41b6c5SChris Packham case IRQ_TYPE_EDGE_FALLING: 1336a41b6c5SChris Packham event_pol = readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_EDGE); 1346a41b6c5SChris Packham event_pol |= BIT(pin); 1356a41b6c5SChris Packham writel_relaxed(event_pol, chip->base + IPROC_GPIO_CCA_INT_EDGE); 1366a41b6c5SChris Packham break; 1376a41b6c5SChris Packham case IRQ_TYPE_LEVEL_HIGH: 1386a41b6c5SChris Packham int_pol = readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_LEVEL); 1396a41b6c5SChris Packham int_pol &= ~BIT(pin); 1406a41b6c5SChris Packham writel_relaxed(int_pol, chip->base + IPROC_GPIO_CCA_INT_LEVEL); 1416a41b6c5SChris Packham break; 1426a41b6c5SChris Packham case IRQ_TYPE_LEVEL_LOW: 1436a41b6c5SChris Packham int_pol = readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_LEVEL); 1446a41b6c5SChris Packham int_pol |= BIT(pin); 1456a41b6c5SChris Packham writel_relaxed(int_pol, chip->base + IPROC_GPIO_CCA_INT_LEVEL); 1466a41b6c5SChris Packham break; 1476a41b6c5SChris Packham default: 1486a41b6c5SChris Packham /* should not come here */ 1496a41b6c5SChris Packham ret = -EINVAL; 1506a41b6c5SChris Packham goto out_unlock; 1516a41b6c5SChris Packham } 1526a41b6c5SChris Packham 1536a41b6c5SChris Packham if (type & IRQ_TYPE_LEVEL_MASK) 1546a41b6c5SChris Packham irq_set_handler_locked(irq_get_irq_data(irq), handle_level_irq); 1556a41b6c5SChris Packham else if (type & IRQ_TYPE_EDGE_BOTH) 1566a41b6c5SChris Packham irq_set_handler_locked(irq_get_irq_data(irq), handle_edge_irq); 1576a41b6c5SChris Packham 1586a41b6c5SChris Packham out_unlock: 1596a41b6c5SChris Packham spin_unlock_irqrestore(&chip->lock, flags); 1606a41b6c5SChris Packham 1616a41b6c5SChris Packham return ret; 1626a41b6c5SChris Packham } 1636a41b6c5SChris Packham 1646a41b6c5SChris Packham static irqreturn_t iproc_gpio_irq_handler(int irq, void *data) 1656a41b6c5SChris Packham { 1666a41b6c5SChris Packham struct gpio_chip *gc = (struct gpio_chip *)data; 1676a41b6c5SChris Packham struct iproc_gpio_chip *chip = to_iproc_gpio(gc); 1686a41b6c5SChris Packham int bit; 1696a41b6c5SChris Packham unsigned long int_bits = 0; 1706a41b6c5SChris Packham u32 int_status; 1716a41b6c5SChris Packham 1726a41b6c5SChris Packham /* go through the entire GPIOs and handle all interrupts */ 1736a41b6c5SChris Packham int_status = readl_relaxed(chip->intr + IPROC_CCA_INT_STS); 1746a41b6c5SChris Packham if (int_status & IPROC_CCA_INT_F_GPIOINT) { 1756a41b6c5SChris Packham u32 event, level; 1766a41b6c5SChris Packham 1776a41b6c5SChris Packham /* Get level and edge interrupts */ 1786a41b6c5SChris Packham event = 1796a41b6c5SChris Packham readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_EVENT_MASK); 1806a41b6c5SChris Packham event &= readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_EVENT); 1816a41b6c5SChris Packham level = readl_relaxed(chip->base + IPROC_GPIO_CCA_DIN); 1826a41b6c5SChris Packham level ^= readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_LEVEL); 1836a41b6c5SChris Packham level &= 1846a41b6c5SChris Packham readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_LEVEL_MASK); 1856a41b6c5SChris Packham int_bits = level | event; 1866a41b6c5SChris Packham 1876a41b6c5SChris Packham for_each_set_bit(bit, &int_bits, gc->ngpio) 1886a41b6c5SChris Packham generic_handle_irq(irq_linear_revmap(gc->irq.domain, bit)); 1896a41b6c5SChris Packham } 1906a41b6c5SChris Packham 1916a41b6c5SChris Packham return int_bits ? IRQ_HANDLED : IRQ_NONE; 1926a41b6c5SChris Packham } 1936a41b6c5SChris Packham 1946a41b6c5SChris Packham static int iproc_gpio_probe(struct platform_device *pdev) 1956a41b6c5SChris Packham { 1966a41b6c5SChris Packham struct device *dev = &pdev->dev; 1976a41b6c5SChris Packham struct device_node *dn = pdev->dev.of_node; 1986a41b6c5SChris Packham struct iproc_gpio_chip *chip; 1996a41b6c5SChris Packham u32 num_gpios; 2006a41b6c5SChris Packham int irq, ret; 2016a41b6c5SChris Packham 2026a41b6c5SChris Packham chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); 2036a41b6c5SChris Packham if (!chip) 2046a41b6c5SChris Packham return -ENOMEM; 2056a41b6c5SChris Packham 2066a41b6c5SChris Packham chip->dev = dev; 2076a41b6c5SChris Packham platform_set_drvdata(pdev, chip); 2086a41b6c5SChris Packham spin_lock_init(&chip->lock); 2096a41b6c5SChris Packham 2106a41b6c5SChris Packham chip->base = devm_platform_ioremap_resource(pdev, 0); 2116a41b6c5SChris Packham if (IS_ERR(chip->base)) 2126a41b6c5SChris Packham return PTR_ERR(chip->base); 2136a41b6c5SChris Packham 2146a41b6c5SChris Packham ret = bgpio_init(&chip->gc, dev, 4, 2156a41b6c5SChris Packham chip->base + IPROC_GPIO_CCA_DIN, 2166a41b6c5SChris Packham chip->base + IPROC_GPIO_CCA_DOUT, 2176a41b6c5SChris Packham NULL, 2186a41b6c5SChris Packham chip->base + IPROC_GPIO_CCA_OUT_EN, 2196a41b6c5SChris Packham NULL, 2206a41b6c5SChris Packham 0); 2216a41b6c5SChris Packham if (ret) { 2226a41b6c5SChris Packham dev_err(dev, "unable to init GPIO chip\n"); 2236a41b6c5SChris Packham return ret; 2246a41b6c5SChris Packham } 2256a41b6c5SChris Packham 2266a41b6c5SChris Packham chip->gc.label = dev_name(dev); 2276a41b6c5SChris Packham if (of_property_read_u32(dn, "ngpios", &num_gpios)) 2286a41b6c5SChris Packham chip->gc.ngpio = num_gpios; 2296a41b6c5SChris Packham 2306a41b6c5SChris Packham irq = platform_get_irq(pdev, 0); 2316a41b6c5SChris Packham if (irq > 0) { 2326a41b6c5SChris Packham struct gpio_irq_chip *girq; 2336a41b6c5SChris Packham struct irq_chip *irqc; 2346a41b6c5SChris Packham u32 val; 2356a41b6c5SChris Packham 2366a41b6c5SChris Packham irqc = &chip->irqchip; 2376a41b6c5SChris Packham irqc->name = dev_name(dev); 2386a41b6c5SChris Packham irqc->irq_ack = iproc_gpio_irq_ack; 2396a41b6c5SChris Packham irqc->irq_mask = iproc_gpio_irq_mask; 2406a41b6c5SChris Packham irqc->irq_unmask = iproc_gpio_irq_unmask; 2416a41b6c5SChris Packham irqc->irq_set_type = iproc_gpio_irq_set_type; 2426a41b6c5SChris Packham 2436a41b6c5SChris Packham chip->intr = devm_platform_ioremap_resource(pdev, 1); 2446a41b6c5SChris Packham if (IS_ERR(chip->intr)) 2456a41b6c5SChris Packham return PTR_ERR(chip->intr); 2466a41b6c5SChris Packham 2476a41b6c5SChris Packham /* Enable GPIO interrupts for CCA GPIO */ 2486a41b6c5SChris Packham val = readl_relaxed(chip->intr + IPROC_CCA_INT_MASK); 2496a41b6c5SChris Packham val |= IPROC_CCA_INT_F_GPIOINT; 2506a41b6c5SChris Packham writel_relaxed(val, chip->intr + IPROC_CCA_INT_MASK); 2516a41b6c5SChris Packham 2526a41b6c5SChris Packham /* 2536a41b6c5SChris Packham * Directly request the irq here instead of passing 254*72780ce5SLinus Walleij * a flow-handler because the irq is shared. 2556a41b6c5SChris Packham */ 2566a41b6c5SChris Packham ret = devm_request_irq(dev, irq, iproc_gpio_irq_handler, 2576a41b6c5SChris Packham IRQF_SHARED, chip->gc.label, &chip->gc); 2586a41b6c5SChris Packham if (ret) { 2596a41b6c5SChris Packham dev_err(dev, "Fail to request IRQ%d: %d\n", irq, ret); 2606a41b6c5SChris Packham return ret; 2616a41b6c5SChris Packham } 2626a41b6c5SChris Packham 2636a41b6c5SChris Packham girq = &chip->gc.irq; 2646a41b6c5SChris Packham girq->chip = irqc; 2656a41b6c5SChris Packham /* This will let us handle the parent IRQ in the driver */ 2666a41b6c5SChris Packham girq->parent_handler = NULL; 2676a41b6c5SChris Packham girq->num_parents = 0; 2686a41b6c5SChris Packham girq->parents = NULL; 2696a41b6c5SChris Packham girq->default_type = IRQ_TYPE_NONE; 2706a41b6c5SChris Packham girq->handler = handle_simple_irq; 2716a41b6c5SChris Packham } 2726a41b6c5SChris Packham 2736a41b6c5SChris Packham ret = devm_gpiochip_add_data(dev, &chip->gc, chip); 2746a41b6c5SChris Packham if (ret) { 2756a41b6c5SChris Packham dev_err(dev, "unable to add GPIO chip\n"); 2766a41b6c5SChris Packham return ret; 2776a41b6c5SChris Packham } 2786a41b6c5SChris Packham 2796a41b6c5SChris Packham return 0; 2806a41b6c5SChris Packham } 2816a41b6c5SChris Packham 2826a41b6c5SChris Packham static int __exit iproc_gpio_remove(struct platform_device *pdev) 2836a41b6c5SChris Packham { 2846a41b6c5SChris Packham struct iproc_gpio_chip *chip; 2856a41b6c5SChris Packham 2866a41b6c5SChris Packham chip = platform_get_drvdata(pdev); 2876a41b6c5SChris Packham if (!chip) 2886a41b6c5SChris Packham return -ENODEV; 2896a41b6c5SChris Packham 2906a41b6c5SChris Packham if (chip->intr) { 2916a41b6c5SChris Packham u32 val; 2926a41b6c5SChris Packham 2936a41b6c5SChris Packham val = readl_relaxed(chip->intr + IPROC_CCA_INT_MASK); 2946a41b6c5SChris Packham val &= ~IPROC_CCA_INT_F_GPIOINT; 2956a41b6c5SChris Packham writel_relaxed(val, chip->intr + IPROC_CCA_INT_MASK); 2966a41b6c5SChris Packham } 2976a41b6c5SChris Packham 2986a41b6c5SChris Packham return 0; 2996a41b6c5SChris Packham } 3006a41b6c5SChris Packham 3015d682fa3SMark Brown static const struct of_device_id bcm_iproc_gpio_of_match[] = { 3026a41b6c5SChris Packham { .compatible = "brcm,iproc-gpio-cca" }, 3036a41b6c5SChris Packham {} 3046a41b6c5SChris Packham }; 3056a41b6c5SChris Packham MODULE_DEVICE_TABLE(of, bcm_iproc_gpio_of_match); 3066a41b6c5SChris Packham 3076a41b6c5SChris Packham static struct platform_driver bcm_iproc_gpio_driver = { 3086a41b6c5SChris Packham .driver = { 3096a41b6c5SChris Packham .name = "iproc-xgs-gpio", 3106a41b6c5SChris Packham .of_match_table = bcm_iproc_gpio_of_match, 3116a41b6c5SChris Packham }, 3126a41b6c5SChris Packham .probe = iproc_gpio_probe, 3136a41b6c5SChris Packham .remove = iproc_gpio_remove, 3146a41b6c5SChris Packham }; 3156a41b6c5SChris Packham 3166a41b6c5SChris Packham module_platform_driver(bcm_iproc_gpio_driver); 3176a41b6c5SChris Packham 3186a41b6c5SChris Packham MODULE_DESCRIPTION("XGS IPROC GPIO driver"); 3196a41b6c5SChris Packham MODULE_LICENSE("GPL v2"); 320