Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Chris Packham | 1573 | 99.94% | 1 | 50.00% |
Linus Walleij | 1 | 0.06% | 1 | 50.00% |
Total | 1574 | 2 |
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
// 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_irq(irq_linear_revmap(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; chip = platform_get_drvdata(pdev); if (!chip) return -ENODEV; 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