123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327 |
- /*
- * Copyright (C) 2008 Ilya Yanok, Emcraft Systems
- *
- *
- * 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/irq.h>
- #include <linux/of_platform.h>
- #include <linux/io.h>
- /*
- * The FPGA supports 9 interrupt sources, which can be routed to 3
- * interrupt request lines of the MPIC. The line to be used can be
- * specified through the third cell of FDT property "interrupts".
- */
- #define SOCRATES_FPGA_NUM_IRQS 9
- #define FPGA_PIC_IRQCFG (0x0)
- #define FPGA_PIC_IRQMASK(n) (0x4 + 0x4 * (n))
- #define SOCRATES_FPGA_IRQ_MASK ((1 << SOCRATES_FPGA_NUM_IRQS) - 1)
- struct socrates_fpga_irq_info {
- unsigned int irq_line;
- int type;
- };
- /*
- * Interrupt routing and type table
- *
- * IRQ_TYPE_NONE means the interrupt type is configurable,
- * otherwise it's fixed to the specified value.
- */
- static struct socrates_fpga_irq_info fpga_irqs[SOCRATES_FPGA_NUM_IRQS] = {
- [0] = {0, IRQ_TYPE_NONE},
- [1] = {0, IRQ_TYPE_LEVEL_HIGH},
- [2] = {0, IRQ_TYPE_LEVEL_LOW},
- [3] = {0, IRQ_TYPE_NONE},
- [4] = {0, IRQ_TYPE_NONE},
- [5] = {0, IRQ_TYPE_NONE},
- [6] = {0, IRQ_TYPE_NONE},
- [7] = {0, IRQ_TYPE_NONE},
- [8] = {0, IRQ_TYPE_LEVEL_HIGH},
- };
- #define socrates_fpga_irq_to_hw(virq) ((unsigned int)irq_map[virq].hwirq)
- static DEFINE_SPINLOCK(socrates_fpga_pic_lock);
- static void __iomem *socrates_fpga_pic_iobase;
- static struct irq_host *socrates_fpga_pic_irq_host;
- static unsigned int socrates_fpga_irqs[3];
- static inline uint32_t socrates_fpga_pic_read(int reg)
- {
- return in_be32(socrates_fpga_pic_iobase + reg);
- }
- static inline void socrates_fpga_pic_write(int reg, uint32_t val)
- {
- out_be32(socrates_fpga_pic_iobase + reg, val);
- }
- static inline unsigned int socrates_fpga_pic_get_irq(unsigned int irq)
- {
- uint32_t cause;
- unsigned long flags;
- int i;
- /* Check irq line routed to the MPIC */
- for (i = 0; i < 3; i++) {
- if (irq == socrates_fpga_irqs[i])
- break;
- }
- if (i == 3)
- return NO_IRQ;
- spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
- cause = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(i));
- spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
- for (i = SOCRATES_FPGA_NUM_IRQS - 1; i >= 0; i--) {
- if (cause >> (i + 16))
- break;
- }
- return irq_linear_revmap(socrates_fpga_pic_irq_host,
- (irq_hw_number_t)i);
- }
- void socrates_fpga_pic_cascade(unsigned int irq, struct irq_desc *desc)
- {
- unsigned int cascade_irq;
- /*
- * See if we actually have an interrupt, call generic handling code if
- * we do.
- */
- cascade_irq = socrates_fpga_pic_get_irq(irq);
- if (cascade_irq != NO_IRQ)
- generic_handle_irq(cascade_irq);
- desc->chip->eoi(irq);
- }
- static void socrates_fpga_pic_ack(unsigned int virq)
- {
- unsigned long flags;
- unsigned int hwirq, irq_line;
- uint32_t mask;
- hwirq = socrates_fpga_irq_to_hw(virq);
- irq_line = fpga_irqs[hwirq].irq_line;
- spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
- mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line))
- & SOCRATES_FPGA_IRQ_MASK;
- mask |= (1 << (hwirq + 16));
- socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line), mask);
- spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
- }
- static void socrates_fpga_pic_mask(unsigned int virq)
- {
- unsigned long flags;
- unsigned int hwirq;
- int irq_line;
- u32 mask;
- hwirq = socrates_fpga_irq_to_hw(virq);
- irq_line = fpga_irqs[hwirq].irq_line;
- spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
- mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line))
- & SOCRATES_FPGA_IRQ_MASK;
- mask &= ~(1 << hwirq);
- socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line), mask);
- spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
- }
- static void socrates_fpga_pic_mask_ack(unsigned int virq)
- {
- unsigned long flags;
- unsigned int hwirq;
- int irq_line;
- u32 mask;
- hwirq = socrates_fpga_irq_to_hw(virq);
- irq_line = fpga_irqs[hwirq].irq_line;
- spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
- mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line))
- & SOCRATES_FPGA_IRQ_MASK;
- mask &= ~(1 << hwirq);
- mask |= (1 << (hwirq + 16));
- socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line), mask);
- spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
- }
- static void socrates_fpga_pic_unmask(unsigned int virq)
- {
- unsigned long flags;
- unsigned int hwirq;
- int irq_line;
- u32 mask;
- hwirq = socrates_fpga_irq_to_hw(virq);
- irq_line = fpga_irqs[hwirq].irq_line;
- spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
- mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line))
- & SOCRATES_FPGA_IRQ_MASK;
- mask |= (1 << hwirq);
- socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line), mask);
- spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
- }
- static void socrates_fpga_pic_eoi(unsigned int virq)
- {
- unsigned long flags;
- unsigned int hwirq;
- int irq_line;
- u32 mask;
- hwirq = socrates_fpga_irq_to_hw(virq);
- irq_line = fpga_irqs[hwirq].irq_line;
- spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
- mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line))
- & SOCRATES_FPGA_IRQ_MASK;
- mask |= (1 << (hwirq + 16));
- socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line), mask);
- spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
- }
- static int socrates_fpga_pic_set_type(unsigned int virq,
- unsigned int flow_type)
- {
- unsigned long flags;
- unsigned int hwirq;
- int polarity;
- u32 mask;
- hwirq = socrates_fpga_irq_to_hw(virq);
- if (fpga_irqs[hwirq].type != IRQ_TYPE_NONE)
- return -EINVAL;
- switch (flow_type & IRQ_TYPE_SENSE_MASK) {
- case IRQ_TYPE_LEVEL_HIGH:
- polarity = 1;
- break;
- case IRQ_TYPE_LEVEL_LOW:
- polarity = 0;
- break;
- default:
- return -EINVAL;
- }
- spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
- mask = socrates_fpga_pic_read(FPGA_PIC_IRQCFG);
- if (polarity)
- mask |= (1 << hwirq);
- else
- mask &= ~(1 << hwirq);
- socrates_fpga_pic_write(FPGA_PIC_IRQCFG, mask);
- spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
- return 0;
- }
- static struct irq_chip socrates_fpga_pic_chip = {
- .typename = " FPGA-PIC ",
- .ack = socrates_fpga_pic_ack,
- .mask = socrates_fpga_pic_mask,
- .mask_ack = socrates_fpga_pic_mask_ack,
- .unmask = socrates_fpga_pic_unmask,
- .eoi = socrates_fpga_pic_eoi,
- .set_type = socrates_fpga_pic_set_type,
- };
- static int socrates_fpga_pic_host_map(struct irq_host *h, unsigned int virq,
- irq_hw_number_t hwirq)
- {
- /* All interrupts are LEVEL sensitive */
- get_irq_desc(virq)->status |= IRQ_LEVEL;
- set_irq_chip_and_handler(virq, &socrates_fpga_pic_chip,
- handle_fasteoi_irq);
- return 0;
- }
- static int socrates_fpga_pic_host_xlate(struct irq_host *h,
- struct device_node *ct, u32 *intspec, unsigned int intsize,
- irq_hw_number_t *out_hwirq, unsigned int *out_flags)
- {
- struct socrates_fpga_irq_info *fpga_irq = &fpga_irqs[intspec[0]];
- *out_hwirq = intspec[0];
- if (fpga_irq->type == IRQ_TYPE_NONE) {
- /* type is configurable */
- if (intspec[1] != IRQ_TYPE_LEVEL_LOW &&
- intspec[1] != IRQ_TYPE_LEVEL_HIGH) {
- pr_warning("FPGA PIC: invalid irq type, "
- "setting default active low\n");
- *out_flags = IRQ_TYPE_LEVEL_LOW;
- } else {
- *out_flags = intspec[1];
- }
- } else {
- /* type is fixed */
- *out_flags = fpga_irq->type;
- }
- /* Use specified interrupt routing */
- if (intspec[2] <= 2)
- fpga_irq->irq_line = intspec[2];
- else
- pr_warning("FPGA PIC: invalid irq routing\n");
- return 0;
- }
- static struct irq_host_ops socrates_fpga_pic_host_ops = {
- .map = socrates_fpga_pic_host_map,
- .xlate = socrates_fpga_pic_host_xlate,
- };
- void socrates_fpga_pic_init(struct device_node *pic)
- {
- unsigned long flags;
- int i;
- /* Setup an irq_host structure */
- socrates_fpga_pic_irq_host = irq_alloc_host(pic, IRQ_HOST_MAP_LINEAR,
- SOCRATES_FPGA_NUM_IRQS, &socrates_fpga_pic_host_ops,
- SOCRATES_FPGA_NUM_IRQS);
- if (socrates_fpga_pic_irq_host == NULL) {
- pr_err("FPGA PIC: Unable to allocate host\n");
- return;
- }
- for (i = 0; i < 3; i++) {
- socrates_fpga_irqs[i] = irq_of_parse_and_map(pic, i);
- if (socrates_fpga_irqs[i] == NO_IRQ) {
- pr_warning("FPGA PIC: can't get irq%d.\n", i);
- continue;
- }
- set_irq_chained_handler(socrates_fpga_irqs[i],
- socrates_fpga_pic_cascade);
- }
- socrates_fpga_pic_iobase = of_iomap(pic, 0);
- spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
- socrates_fpga_pic_write(FPGA_PIC_IRQMASK(0),
- SOCRATES_FPGA_IRQ_MASK << 16);
- socrates_fpga_pic_write(FPGA_PIC_IRQMASK(1),
- SOCRATES_FPGA_IRQ_MASK << 16);
- socrates_fpga_pic_write(FPGA_PIC_IRQMASK(2),
- SOCRATES_FPGA_IRQ_MASK << 16);
- spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
- pr_info("FPGA PIC: Setting up Socrates FPGA PIC\n");
- }
|