summaryrefslogtreecommitdiff
path: root/arch/arm/mach-sa1100/neponset.c
diff options
context:
space:
mode:
authorRussell King <rmk+kernel@armlinux.org.uk>2016-08-26 23:52:42 +0100
committerRussell King (Oracle) <rmk+kernel@armlinux.org.uk>2022-04-02 11:11:39 +0100
commit44df9479dfcc44b3b367c041d8b78c23244c1658 (patch)
tree53c7d4a080e322a2766f863d6eefce75e9447323 /arch/arm/mach-sa1100/neponset.c
parent525d0eb0b3e584d308cf3272ddb4158f9a55e77c (diff)
ARM: sa1100/neponset: convert to irq domain
Convert Neponset to use an irq domain rather than statically allocated interrupt numbers, and switch to using the IRQ domain to lookup the Linux interrupt number when decoding an interrupt. Since interrupts remain disabled across an interrupt handler, we can simplify the handler code somewhat. Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
Diffstat (limited to 'arch/arm/mach-sa1100/neponset.c')
-rw-r--r--arch/arm/mach-sa1100/neponset.c113
1 files changed, 62 insertions, 51 deletions
diff --git a/arch/arm/mach-sa1100/neponset.c b/arch/arm/mach-sa1100/neponset.c
index 9d2ae7febca3..0718c9c4fb0a 100644
--- a/arch/arm/mach-sa1100/neponset.c
+++ b/arch/arm/mach-sa1100/neponset.c
@@ -78,9 +78,11 @@ static const char *neponset_aud_names[] = {
struct neponset_drvdata {
void __iomem *base;
+ struct device *dev;
struct platform_device *sa1111;
struct platform_device *smc91x;
unsigned irq_base;
+ struct irq_domain *irq_domain;
struct gpio_chip *gpio[4];
struct clk_hw *enet_osc_ck;
struct clk_hw *enet_osc_gate;
@@ -126,6 +128,18 @@ static struct gpiod_lookup_table neponset_pcmcia_table = {
static struct neponset_drvdata *nep;
+static void neponset_handle_irq(struct neponset_drvdata *d, int irq)
+{
+ int virq = irq_find_mapping(d->irq_domain, irq);
+
+ if (!virq) {
+ dev_warn(d->dev, "spurious irq detected, hwirq %d irq %d\n",
+ irq, virq);
+ return;
+ }
+ generic_handle_irq(virq);
+}
+
/*
* Install handler for Neponset IRQ. Note that we have to loop here
* since the ETHERNET and USAR IRQs are level based, and we need to
@@ -135,12 +149,11 @@ static struct neponset_drvdata *nep;
static void neponset_irq_handler(struct irq_desc *desc)
{
struct neponset_drvdata *d = irq_desc_get_handler_data(desc);
- unsigned int irr;
+ unsigned long irr;
+ int irq;
while (1) {
- /*
- * Acknowledge the parent IRQ.
- */
+ // Acknowledge the parent IRQ before reading the IRR.
desc->irq_data.chip->irq_ack(&desc->irq_data);
/*
@@ -151,36 +164,12 @@ static void neponset_irq_handler(struct irq_desc *desc)
irr = readb_relaxed(d->base + IRR);
irr ^= IRR_ETHERNET | IRR_USAR;
+ // If no pending interrupts, then we're done.
if ((irr & (IRR_ETHERNET | IRR_USAR | IRR_SA1111)) == 0)
break;
- /*
- * Since there is no individual mask, we have to
- * mask the parent IRQ. This is safe, since we'll
- * recheck the register for any pending IRQs.
- */
- if (irr & (IRR_ETHERNET | IRR_USAR)) {
- desc->irq_data.chip->irq_mask(&desc->irq_data);
-
- /*
- * Ack the interrupt now to prevent re-entering
- * this neponset handler. Again, this is safe
- * since we'll check the IRR register prior to
- * leaving.
- */
- desc->irq_data.chip->irq_ack(&desc->irq_data);
-
- if (irr & IRR_ETHERNET)
- generic_handle_irq(d->irq_base + NEP_IRQ_SMC91X);
-
- if (irr & IRR_USAR)
- generic_handle_irq(d->irq_base + NEP_IRQ_USAR);
-
- desc->irq_data.chip->irq_unmask(&desc->irq_data);
- }
-
- if (irr & IRR_SA1111)
- generic_handle_irq(d->irq_base + NEP_IRQ_SA1111);
+ for_each_set_bit(irq, &irr, 8)
+ neponset_handle_irq(d, irq);
}
}
@@ -196,6 +185,44 @@ static struct irq_chip nochip = {
.irq_unmask = nochip_noop,
};
+static int neponset_irqdomain_map(struct irq_domain *d, unsigned int irq,
+ irq_hw_number_t hwirq)
+{
+ irq_set_chip_and_handler(irq, &nochip, handle_simple_irq);
+ irq_clear_status_flags(irq, IRQ_NOREQUEST);
+
+ return 0;
+}
+
+static const struct irq_domain_ops nep_irqdomain_ops = {
+ .map = neponset_irqdomain_map,
+ .xlate = irq_domain_xlate_onetwocell,
+};
+
+static int neponset_init_irr(struct device *dev, struct neponset_drvdata *d, int irq)
+{
+ int ret;
+
+ ret = irq_alloc_descs(-1, IRQ_BOARD_START, NEP_IRQ_NR, -1);
+ if (ret <= 0) {
+ dev_err(dev, "unable to allocate %u irqs: %d\n",
+ NEP_IRQ_NR, ret);
+ if (ret == 0)
+ ret = -ENOMEM;
+ return ret;
+ }
+
+ d->irq_base = ret;
+
+ d->irq_domain = irq_domain_add_simple(NULL, NEP_IRQ_NR, ret,
+ &nep_irqdomain_ops, d);
+
+ irq_set_irq_type(irq, IRQ_TYPE_EDGE_RISING);
+ irq_set_chained_handler_and_data(irq, neponset_irq_handler, d);
+
+ return 0;
+}
+
static int neponset_init_gpio(struct gpio_chip **gcp,
struct device *dev, const char *label, void __iomem *reg,
unsigned num, bool in, const char *const * names)
@@ -308,6 +335,8 @@ static int neponset_probe(struct platform_device *dev)
goto err_alloc;
}
+ d->dev = &dev->dev;
+
d->base = ioremap(nep_res->start, SZ_4K);
if (!d->base) {
ret = -ENOMEM;
@@ -321,27 +350,9 @@ static int neponset_probe(struct platform_device *dev)
goto err_id;
}
- ret = irq_alloc_descs(-1, IRQ_BOARD_START, NEP_IRQ_NR, -1);
- if (ret <= 0) {
- dev_err(&dev->dev, "unable to allocate %u irqs: %d\n",
- NEP_IRQ_NR, ret);
- if (ret == 0)
- ret = -ENOMEM;
+ ret = neponset_init_irr(&dev->dev, d, irq);
+ if (ret < 0)
goto err_irq_alloc;
- }
-
- d->irq_base = ret;
-
- irq_set_chip_and_handler(d->irq_base + NEP_IRQ_SMC91X, &nochip,
- handle_simple_irq);
- irq_clear_status_flags(d->irq_base + NEP_IRQ_SMC91X, IRQ_NOREQUEST | IRQ_NOPROBE);
- irq_set_chip_and_handler(d->irq_base + NEP_IRQ_USAR, &nochip,
- handle_simple_irq);
- irq_clear_status_flags(d->irq_base + NEP_IRQ_USAR, IRQ_NOREQUEST | IRQ_NOPROBE);
- irq_set_chip(d->irq_base + NEP_IRQ_SA1111, &nochip);
-
- irq_set_irq_type(irq, IRQ_TYPE_EDGE_RISING);
- irq_set_chained_handler_and_data(irq, neponset_irq_handler, d);
/* Disable GPIO 0/1 drivers so the buttons work on the Assabet */
writeb_relaxed(NCR_GP01_OFF, d->base + NCR_0);