Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Chris Packham | 1555 | 99.68% | 1 | 20.00% |
Uwe Kleine-König | 2 | 0.13% | 1 | 20.00% |
Jonas Gorski | 1 | 0.06% | 1 | 20.00% |
Marc Zyngier | 1 | 0.06% | 1 | 20.00% |
Linus Walleij | 1 | 0.06% | 1 | 20.00% |
Total | 1560 | 5 |
// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2017 Broadcom */ #include <linux/gpio/driver.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/irq.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/spinlock.h> #define IPROC_CCA_INT_F_GPIOINT BIT(0) #define IPROC_CCA_INT_STS 0x20 #define IPROC_CCA_INT_MASK 0x24 #define IPROC_GPIO_CCA_DIN 0x0 #define IPROC_GPIO_CCA_DOUT 0x4 #define IPROC_GPIO_CCA_OUT_EN 0x8 #define IPROC_GPIO_CCA_INT_LEVEL 0x10 #define IPROC_GPIO_CCA_INT_LEVEL_MASK 0x14 #define IPROC_GPIO_CCA_INT_EVENT 0x18 #define IPROC_GPIO_CCA_INT_EVENT_MASK 0x1C #define IPROC_GPIO_CCA_INT_EDGE 0x24 struct iproc_gpio_chip { struct irq_chip irqchip; struct gpio_chip gc; spinlock_t lock; struct device *dev; void __iomem *base; void __iomem *intr; }; static inline struct iproc_gpio_chip * to_iproc_gpio(struct gpio_chip *gc) { return container_of(gc, struct iproc_gpio_chip, gc); } static void iproc_gpio_irq_ack(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct iproc_gpio_chip *chip = to_iproc_gpio(gc); int pin = d->hwirq; unsigned long flags; u32 irq = d->irq; u32 irq_type, event_status = 0; spin_lock_irqsave(&chip->lock, flags); irq_type = irq_get_trigger_type(irq); if (irq_type & IRQ_TYPE_EDGE_BOTH) { event_status |= BIT(pin); writel_relaxed(event_status, chip->base + IPROC_GPIO_CCA_INT_EVENT); } spin_unlock_irqrestore(&chip->lock, flags); } static void iproc_gpio_irq_unmask(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct iproc_gpio_chip *chip = to_iproc_gpio(gc); int pin = d->hwirq; unsigned long flags; u32 irq = d->irq; u32 int_mask, irq_type, event_mask; spin_lock_irqsave(&chip->lock, flags); irq_type = irq_get_trigger_type(irq); event_mask = readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_EVENT_MASK); int_mask = readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_LEVEL_MASK); if (irq_type & IRQ_TYPE_EDGE_BOTH) { event_mask |= 1 << pin; writel_relaxed(event_mask, chip->base + IPROC_GPIO_CCA_INT_EVENT_MASK); } else { int_mask |= 1 << pin; writel_relaxed(int_mask, chip->base + IPROC_GPIO_CCA_INT_LEVEL_MASK); } spin_unlock_irqrestore(&chip->lock, flags); } static void iproc_gpio_irq_mask(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct iproc_gpio_chip *chip = to_iproc_gpio(gc); int pin = d->hwirq; unsigned long flags; u32 irq = d->irq; u32 irq_type, int_mask, event_mask; spin_lock_irqsave(&chip->lock, flags); irq_type = irq_get_trigger_type(irq); event_mask = readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_EVENT_MASK); int_mask = readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_LEVEL_MASK); if (irq_type & IRQ_TYPE_EDGE_BOTH) { event_mask &= ~BIT(pin); writel_relaxed(event_mask, chip->base + IPROC_GPIO_CCA_INT_EVENT_MASK); } else { int_mask &= ~BIT(pin); writel_relaxed(int_mask, chip->base + IPROC_GPIO_CCA_INT_LEVEL_MASK); } spin_unlock_irqrestore(&chip->lock, flags); } static int iproc_gpio_irq_set_type(struct irq_data *d, u32 type) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct iproc_gpio_chip *chip = to_iproc_gpio(gc); int pin = d->hwirq; unsigned long flags; u32 irq = d->irq; u32 event_pol, int_pol; int ret = 0; spin_lock_irqsave(&chip->lock, flags); switch (type & IRQ_TYPE_SENSE_MASK) { case IRQ_TYPE_EDGE_RISING: event_pol = readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_EDGE); event_pol &= ~BIT(pin); writel_relaxed(event_pol, chip->base + IPROC_GPIO_CCA_INT_EDGE); break; case IRQ_TYPE_EDGE_FALLING: event_pol = readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_EDGE); event_pol |= BIT(pin); writel_relaxed(event_pol, chip->base + IPROC_GPIO_CCA_INT_EDGE); break; case IRQ_TYPE_LEVEL_HIGH: int_pol = readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_LEVEL); int_pol &= ~BIT(pin); writel_relaxed(int_pol, chip->base + IPROC_GPIO_CCA_INT_LEVEL); break; case IRQ_TYPE_LEVEL_LOW: int_pol = readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_LEVEL); int_pol |= BIT(pin); writel_relaxed(int_pol, chip->base + IPROC_GPIO_CCA_INT_LEVEL); break; default: /* should not come here */ ret = -EINVAL; goto out_unlock; } if (type & IRQ_TYPE_LEVEL_MASK) irq_set_handler_locked(irq_get_irq_data(irq), handle_level_irq); else if (type & IRQ_TYPE_EDGE_BOTH) irq_set_handler_locked(irq_get_irq_data(irq), handle_edge_irq); out_unlock: spin_unlock_irqrestore(&chip->lock, flags); return ret; } static irqreturn_t iproc_gpio_irq_handler(int irq, void *data) { struct gpio_chip *gc = (struct gpio_chip *)data; struct iproc_gpio_chip *chip = to_iproc_gpio(gc); int bit; unsigned long int_bits = 0; u32 int_status; /* go through the entire GPIOs and handle all interrupts */ int_status = readl_relaxed(chip->intr + IPROC_CCA_INT_STS); if (int_status & IPROC_CCA_INT_F_GPIOINT) { u32 event, level; /* Get level and edge interrupts */ event = readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_EVENT_MASK); event &= readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_EVENT); level = readl_relaxed(chip->base + IPROC_GPIO_CCA_DIN); level ^= readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_LEVEL); level &= readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_LEVEL_MASK); int_bits = level | event; for_each_set_bit(bit, &int_bits, gc->ngpio) generic_handle_domain_irq(gc->irq.domain, bit); } return int_bits ? IRQ_HANDLED : IRQ_NONE; } static int iproc_gpio_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *dn = pdev->dev.of_node; struct iproc_gpio_chip *chip; u32 num_gpios; int irq, ret; chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); if (!chip) return -ENOMEM; chip->dev = dev; platform_set_drvdata(pdev, chip); spin_lock_init(&chip->lock); chip->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(chip->base)) return PTR_ERR(chip->base); ret = bgpio_init(&chip->gc, dev, 4, chip->base + IPROC_GPIO_CCA_DIN, chip->base + IPROC_GPIO_CCA_DOUT, NULL, chip->base + IPROC_GPIO_CCA_OUT_EN, NULL, 0); if (ret) { dev_err(dev, "unable to init GPIO chip\n"); return ret; } chip->gc.label = dev_name(dev); if (!of_property_read_u32(dn, "ngpios", &num_gpios)) chip->gc.ngpio = num_gpios; irq = platform_get_irq(pdev, 0); if (irq > 0) { struct gpio_irq_chip *girq; struct irq_chip *irqc; u32 val; irqc = &chip->irqchip; irqc->name = dev_name(dev); irqc->irq_ack = iproc_gpio_irq_ack; irqc->irq_mask = iproc_gpio_irq_mask; irqc->irq_unmask = iproc_gpio_irq_unmask; irqc->irq_set_type = iproc_gpio_irq_set_type; chip->intr = devm_platform_ioremap_resource(pdev, 1); if (IS_ERR(chip->intr)) return PTR_ERR(chip->intr); /* Enable GPIO interrupts for CCA GPIO */ val = readl_relaxed(chip->intr + IPROC_CCA_INT_MASK); val |= IPROC_CCA_INT_F_GPIOINT; writel_relaxed(val, chip->intr + IPROC_CCA_INT_MASK); /* * Directly request the irq here instead of passing * a flow-handler because the irq is shared. */ ret = devm_request_irq(dev, irq, iproc_gpio_irq_handler, IRQF_SHARED, chip->gc.label, &chip->gc); if (ret) { dev_err(dev, "Fail to request IRQ%d: %d\n", irq, ret); return ret; } girq = &chip->gc.irq; girq->chip = irqc; /* This will let us handle the parent IRQ in the driver */ girq->parent_handler = NULL; girq->num_parents = 0; girq->parents = NULL; girq->default_type = IRQ_TYPE_NONE; girq->handler = handle_simple_irq; } ret = devm_gpiochip_add_data(dev, &chip->gc, chip); if (ret) { dev_err(dev, "unable to add GPIO chip\n"); return ret; } return 0; } static int iproc_gpio_remove(struct platform_device *pdev) { struct iproc_gpio_chip *chip = platform_get_drvdata(pdev); if (chip->intr) { u32 val; val = readl_relaxed(chip->intr + IPROC_CCA_INT_MASK); val &= ~IPROC_CCA_INT_F_GPIOINT; writel_relaxed(val, chip->intr + IPROC_CCA_INT_MASK); } return 0; } static const struct of_device_id bcm_iproc_gpio_of_match[] = { { .compatible = "brcm,iproc-gpio-cca" }, {} }; MODULE_DEVICE_TABLE(of, bcm_iproc_gpio_of_match); static struct platform_driver bcm_iproc_gpio_driver = { .driver = { .name = "iproc-xgs-gpio", .of_match_table = bcm_iproc_gpio_of_match, }, .probe = iproc_gpio_probe, .remove = iproc_gpio_remove, }; module_platform_driver(bcm_iproc_gpio_driver); MODULE_DESCRIPTION("XGS IPROC GPIO driver"); MODULE_LICENSE("GPL v2");
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1