123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272 |
- /*
- * External interrupt handling for AT32AP CPUs
- *
- * Copyright (C) 2006 Atmel Corporation
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
- #include <linux/errno.h>
- #include <linux/init.h>
- #include <linux/interrupt.h>
- #include <linux/irq.h>
- #include <linux/platform_device.h>
- #include <linux/random.h>
- #include <linux/slab.h>
- #include <asm/io.h>
- /* EIC register offsets */
- #define EIC_IER 0x0000
- #define EIC_IDR 0x0004
- #define EIC_IMR 0x0008
- #define EIC_ISR 0x000c
- #define EIC_ICR 0x0010
- #define EIC_MODE 0x0014
- #define EIC_EDGE 0x0018
- #define EIC_LEVEL 0x001c
- #define EIC_NMIC 0x0024
- /* Bitfields in NMIC */
- #define EIC_NMIC_ENABLE (1 << 0)
- /* Bit manipulation macros */
- #define EIC_BIT(name) \
- (1 << EIC_##name##_OFFSET)
- #define EIC_BF(name,value) \
- (((value) & ((1 << EIC_##name##_SIZE) - 1)) \
- << EIC_##name##_OFFSET)
- #define EIC_BFEXT(name,value) \
- (((value) >> EIC_##name##_OFFSET) \
- & ((1 << EIC_##name##_SIZE) - 1))
- #define EIC_BFINS(name,value,old) \
- (((old) & ~(((1 << EIC_##name##_SIZE) - 1) \
- << EIC_##name##_OFFSET)) \
- | EIC_BF(name,value))
- /* Register access macros */
- #define eic_readl(port,reg) \
- __raw_readl((port)->regs + EIC_##reg)
- #define eic_writel(port,reg,value) \
- __raw_writel((value), (port)->regs + EIC_##reg)
- struct eic {
- void __iomem *regs;
- struct irq_chip *chip;
- unsigned int first_irq;
- };
- static struct eic *nmi_eic;
- static bool nmi_enabled;
- static void eic_ack_irq(struct irq_data *d)
- {
- struct eic *eic = irq_data_get_irq_chip_data(d);
- eic_writel(eic, ICR, 1 << (d->irq - eic->first_irq));
- }
- static void eic_mask_irq(struct irq_data *d)
- {
- struct eic *eic = irq_data_get_irq_chip_data(d);
- eic_writel(eic, IDR, 1 << (d->irq - eic->first_irq));
- }
- static void eic_mask_ack_irq(struct irq_data *d)
- {
- struct eic *eic = irq_data_get_irq_chip_data(d);
- eic_writel(eic, ICR, 1 << (d->irq - eic->first_irq));
- eic_writel(eic, IDR, 1 << (d->irq - eic->first_irq));
- }
- static void eic_unmask_irq(struct irq_data *d)
- {
- struct eic *eic = irq_data_get_irq_chip_data(d);
- eic_writel(eic, IER, 1 << (d->irq - eic->first_irq));
- }
- static int eic_set_irq_type(struct irq_data *d, unsigned int flow_type)
- {
- struct eic *eic = irq_data_get_irq_chip_data(d);
- unsigned int irq = d->irq;
- unsigned int i = irq - eic->first_irq;
- u32 mode, edge, level;
- flow_type &= IRQ_TYPE_SENSE_MASK;
- if (flow_type == IRQ_TYPE_NONE)
- flow_type = IRQ_TYPE_LEVEL_LOW;
- mode = eic_readl(eic, MODE);
- edge = eic_readl(eic, EDGE);
- level = eic_readl(eic, LEVEL);
- switch (flow_type) {
- case IRQ_TYPE_LEVEL_LOW:
- mode |= 1 << i;
- level &= ~(1 << i);
- break;
- case IRQ_TYPE_LEVEL_HIGH:
- mode |= 1 << i;
- level |= 1 << i;
- break;
- case IRQ_TYPE_EDGE_RISING:
- mode &= ~(1 << i);
- edge |= 1 << i;
- break;
- case IRQ_TYPE_EDGE_FALLING:
- mode &= ~(1 << i);
- edge &= ~(1 << i);
- break;
- default:
- return -EINVAL;
- }
- eic_writel(eic, MODE, mode);
- eic_writel(eic, EDGE, edge);
- eic_writel(eic, LEVEL, level);
- irqd_set_trigger_type(d, flow_type);
- if (flow_type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))
- __irq_set_handler_locked(irq, handle_level_irq);
- else
- __irq_set_handler_locked(irq, handle_edge_irq);
- return IRQ_SET_MASK_OK_NOCOPY;
- }
- static struct irq_chip eic_chip = {
- .name = "eic",
- .irq_ack = eic_ack_irq,
- .irq_mask = eic_mask_irq,
- .irq_mask_ack = eic_mask_ack_irq,
- .irq_unmask = eic_unmask_irq,
- .irq_set_type = eic_set_irq_type,
- };
- static void demux_eic_irq(unsigned int irq, struct irq_desc *desc)
- {
- struct eic *eic = irq_desc_get_handler_data(desc);
- unsigned long status, pending;
- unsigned int i;
- status = eic_readl(eic, ISR);
- pending = status & eic_readl(eic, IMR);
- while (pending) {
- i = fls(pending) - 1;
- pending &= ~(1 << i);
- generic_handle_irq(i + eic->first_irq);
- }
- }
- int nmi_enable(void)
- {
- nmi_enabled = true;
- if (nmi_eic)
- eic_writel(nmi_eic, NMIC, EIC_NMIC_ENABLE);
- return 0;
- }
- void nmi_disable(void)
- {
- if (nmi_eic)
- eic_writel(nmi_eic, NMIC, 0);
- nmi_enabled = false;
- }
- static int __init eic_probe(struct platform_device *pdev)
- {
- struct eic *eic;
- struct resource *regs;
- unsigned int i;
- unsigned int nr_of_irqs;
- unsigned int int_irq;
- int ret;
- u32 pattern;
- regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- int_irq = platform_get_irq(pdev, 0);
- if (!regs || (int)int_irq <= 0) {
- dev_dbg(&pdev->dev, "missing regs and/or irq resource\n");
- return -ENXIO;
- }
- ret = -ENOMEM;
- eic = kzalloc(sizeof(struct eic), GFP_KERNEL);
- if (!eic) {
- dev_dbg(&pdev->dev, "no memory for eic structure\n");
- goto err_kzalloc;
- }
- eic->first_irq = EIM_IRQ_BASE + 32 * pdev->id;
- eic->regs = ioremap(regs->start, resource_size(regs));
- if (!eic->regs) {
- dev_dbg(&pdev->dev, "failed to map regs\n");
- goto err_ioremap;
- }
- /*
- * Find out how many interrupt lines that are actually
- * implemented in hardware.
- */
- eic_writel(eic, IDR, ~0UL);
- eic_writel(eic, MODE, ~0UL);
- pattern = eic_readl(eic, MODE);
- nr_of_irqs = fls(pattern);
- /* Trigger on low level unless overridden by driver */
- eic_writel(eic, EDGE, 0UL);
- eic_writel(eic, LEVEL, 0UL);
- eic->chip = &eic_chip;
- for (i = 0; i < nr_of_irqs; i++) {
- irq_set_chip_and_handler(eic->first_irq + i, &eic_chip,
- handle_level_irq);
- irq_set_chip_data(eic->first_irq + i, eic);
- }
- irq_set_chained_handler_and_data(int_irq, demux_eic_irq, eic);
- if (pdev->id == 0) {
- nmi_eic = eic;
- if (nmi_enabled)
- /*
- * Someone tried to enable NMI before we were
- * ready. Do it now.
- */
- nmi_enable();
- }
- dev_info(&pdev->dev,
- "External Interrupt Controller at 0x%p, IRQ %u\n",
- eic->regs, int_irq);
- dev_info(&pdev->dev,
- "Handling %u external IRQs, starting with IRQ %u\n",
- nr_of_irqs, eic->first_irq);
- return 0;
- err_ioremap:
- kfree(eic);
- err_kzalloc:
- return ret;
- }
- static struct platform_driver eic_driver = {
- .driver = {
- .name = "at32_eic",
- },
- };
- static int __init eic_init(void)
- {
- return platform_driver_probe(&eic_driver, eic_probe);
- }
- arch_initcall(eic_init);
|