Path: blob/master/arch/powerpc/platforms/85xx/socrates_fpga_pic.c
10820 views
/*1* Copyright (C) 2008 Ilya Yanok, Emcraft Systems2*3*4* This program is free software; you can redistribute it and/or modify5* it under the terms of the GNU General Public License version 2 as6* published by the Free Software Foundation.7*8*/910#include <linux/irq.h>11#include <linux/of_platform.h>12#include <linux/io.h>1314/*15* The FPGA supports 9 interrupt sources, which can be routed to 316* interrupt request lines of the MPIC. The line to be used can be17* specified through the third cell of FDT property "interrupts".18*/1920#define SOCRATES_FPGA_NUM_IRQS 92122#define FPGA_PIC_IRQCFG (0x0)23#define FPGA_PIC_IRQMASK(n) (0x4 + 0x4 * (n))2425#define SOCRATES_FPGA_IRQ_MASK ((1 << SOCRATES_FPGA_NUM_IRQS) - 1)2627struct socrates_fpga_irq_info {28unsigned int irq_line;29int type;30};3132/*33* Interrupt routing and type table34*35* IRQ_TYPE_NONE means the interrupt type is configurable,36* otherwise it's fixed to the specified value.37*/38static struct socrates_fpga_irq_info fpga_irqs[SOCRATES_FPGA_NUM_IRQS] = {39[0] = {0, IRQ_TYPE_NONE},40[1] = {0, IRQ_TYPE_LEVEL_HIGH},41[2] = {0, IRQ_TYPE_LEVEL_LOW},42[3] = {0, IRQ_TYPE_NONE},43[4] = {0, IRQ_TYPE_NONE},44[5] = {0, IRQ_TYPE_NONE},45[6] = {0, IRQ_TYPE_NONE},46[7] = {0, IRQ_TYPE_NONE},47[8] = {0, IRQ_TYPE_LEVEL_HIGH},48};4950static DEFINE_RAW_SPINLOCK(socrates_fpga_pic_lock);5152static void __iomem *socrates_fpga_pic_iobase;53static struct irq_host *socrates_fpga_pic_irq_host;54static unsigned int socrates_fpga_irqs[3];5556static inline uint32_t socrates_fpga_pic_read(int reg)57{58return in_be32(socrates_fpga_pic_iobase + reg);59}6061static inline void socrates_fpga_pic_write(int reg, uint32_t val)62{63out_be32(socrates_fpga_pic_iobase + reg, val);64}6566static inline unsigned int socrates_fpga_pic_get_irq(unsigned int irq)67{68uint32_t cause;69unsigned long flags;70int i;7172/* Check irq line routed to the MPIC */73for (i = 0; i < 3; i++) {74if (irq == socrates_fpga_irqs[i])75break;76}77if (i == 3)78return NO_IRQ;7980raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);81cause = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(i));82raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);83for (i = SOCRATES_FPGA_NUM_IRQS - 1; i >= 0; i--) {84if (cause >> (i + 16))85break;86}87return irq_linear_revmap(socrates_fpga_pic_irq_host,88(irq_hw_number_t)i);89}9091void socrates_fpga_pic_cascade(unsigned int irq, struct irq_desc *desc)92{93struct irq_chip *chip = irq_desc_get_chip(desc);94unsigned int cascade_irq;9596/*97* See if we actually have an interrupt, call generic handling code if98* we do.99*/100cascade_irq = socrates_fpga_pic_get_irq(irq);101102if (cascade_irq != NO_IRQ)103generic_handle_irq(cascade_irq);104chip->irq_eoi(&desc->irq_data);105}106107static void socrates_fpga_pic_ack(struct irq_data *d)108{109unsigned long flags;110unsigned int irq_line, hwirq = irqd_to_hwirq(d);111uint32_t mask;112113irq_line = fpga_irqs[hwirq].irq_line;114raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);115mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line))116& SOCRATES_FPGA_IRQ_MASK;117mask |= (1 << (hwirq + 16));118socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line), mask);119raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);120}121122static void socrates_fpga_pic_mask(struct irq_data *d)123{124unsigned long flags;125unsigned int hwirq = irqd_to_hwirq(d);126int irq_line;127u32 mask;128129irq_line = fpga_irqs[hwirq].irq_line;130raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);131mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line))132& SOCRATES_FPGA_IRQ_MASK;133mask &= ~(1 << hwirq);134socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line), mask);135raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);136}137138static void socrates_fpga_pic_mask_ack(struct irq_data *d)139{140unsigned long flags;141unsigned int hwirq = irqd_to_hwirq(d);142int irq_line;143u32 mask;144145irq_line = fpga_irqs[hwirq].irq_line;146raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);147mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line))148& SOCRATES_FPGA_IRQ_MASK;149mask &= ~(1 << hwirq);150mask |= (1 << (hwirq + 16));151socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line), mask);152raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);153}154155static void socrates_fpga_pic_unmask(struct irq_data *d)156{157unsigned long flags;158unsigned int hwirq = irqd_to_hwirq(d);159int irq_line;160u32 mask;161162irq_line = fpga_irqs[hwirq].irq_line;163raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);164mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line))165& SOCRATES_FPGA_IRQ_MASK;166mask |= (1 << hwirq);167socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line), mask);168raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);169}170171static void socrates_fpga_pic_eoi(struct irq_data *d)172{173unsigned long flags;174unsigned int hwirq = irqd_to_hwirq(d);175int irq_line;176u32 mask;177178irq_line = fpga_irqs[hwirq].irq_line;179raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);180mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line))181& SOCRATES_FPGA_IRQ_MASK;182mask |= (1 << (hwirq + 16));183socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line), mask);184raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);185}186187static int socrates_fpga_pic_set_type(struct irq_data *d,188unsigned int flow_type)189{190unsigned long flags;191unsigned int hwirq = irqd_to_hwirq(d);192int polarity;193u32 mask;194195if (fpga_irqs[hwirq].type != IRQ_TYPE_NONE)196return -EINVAL;197198switch (flow_type & IRQ_TYPE_SENSE_MASK) {199case IRQ_TYPE_LEVEL_HIGH:200polarity = 1;201break;202case IRQ_TYPE_LEVEL_LOW:203polarity = 0;204break;205default:206return -EINVAL;207}208raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);209mask = socrates_fpga_pic_read(FPGA_PIC_IRQCFG);210if (polarity)211mask |= (1 << hwirq);212else213mask &= ~(1 << hwirq);214socrates_fpga_pic_write(FPGA_PIC_IRQCFG, mask);215raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);216return 0;217}218219static struct irq_chip socrates_fpga_pic_chip = {220.name = "FPGA-PIC",221.irq_ack = socrates_fpga_pic_ack,222.irq_mask = socrates_fpga_pic_mask,223.irq_mask_ack = socrates_fpga_pic_mask_ack,224.irq_unmask = socrates_fpga_pic_unmask,225.irq_eoi = socrates_fpga_pic_eoi,226.irq_set_type = socrates_fpga_pic_set_type,227};228229static int socrates_fpga_pic_host_map(struct irq_host *h, unsigned int virq,230irq_hw_number_t hwirq)231{232/* All interrupts are LEVEL sensitive */233irq_set_status_flags(virq, IRQ_LEVEL);234irq_set_chip_and_handler(virq, &socrates_fpga_pic_chip,235handle_fasteoi_irq);236237return 0;238}239240static int socrates_fpga_pic_host_xlate(struct irq_host *h,241struct device_node *ct, const u32 *intspec, unsigned int intsize,242irq_hw_number_t *out_hwirq, unsigned int *out_flags)243{244struct socrates_fpga_irq_info *fpga_irq = &fpga_irqs[intspec[0]];245246*out_hwirq = intspec[0];247if (fpga_irq->type == IRQ_TYPE_NONE) {248/* type is configurable */249if (intspec[1] != IRQ_TYPE_LEVEL_LOW &&250intspec[1] != IRQ_TYPE_LEVEL_HIGH) {251pr_warning("FPGA PIC: invalid irq type, "252"setting default active low\n");253*out_flags = IRQ_TYPE_LEVEL_LOW;254} else {255*out_flags = intspec[1];256}257} else {258/* type is fixed */259*out_flags = fpga_irq->type;260}261262/* Use specified interrupt routing */263if (intspec[2] <= 2)264fpga_irq->irq_line = intspec[2];265else266pr_warning("FPGA PIC: invalid irq routing\n");267268return 0;269}270271static struct irq_host_ops socrates_fpga_pic_host_ops = {272.map = socrates_fpga_pic_host_map,273.xlate = socrates_fpga_pic_host_xlate,274};275276void socrates_fpga_pic_init(struct device_node *pic)277{278unsigned long flags;279int i;280281/* Setup an irq_host structure */282socrates_fpga_pic_irq_host = irq_alloc_host(pic, IRQ_HOST_MAP_LINEAR,283SOCRATES_FPGA_NUM_IRQS, &socrates_fpga_pic_host_ops,284SOCRATES_FPGA_NUM_IRQS);285if (socrates_fpga_pic_irq_host == NULL) {286pr_err("FPGA PIC: Unable to allocate host\n");287return;288}289290for (i = 0; i < 3; i++) {291socrates_fpga_irqs[i] = irq_of_parse_and_map(pic, i);292if (socrates_fpga_irqs[i] == NO_IRQ) {293pr_warning("FPGA PIC: can't get irq%d.\n", i);294continue;295}296irq_set_chained_handler(socrates_fpga_irqs[i],297socrates_fpga_pic_cascade);298}299300socrates_fpga_pic_iobase = of_iomap(pic, 0);301302raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);303socrates_fpga_pic_write(FPGA_PIC_IRQMASK(0),304SOCRATES_FPGA_IRQ_MASK << 16);305socrates_fpga_pic_write(FPGA_PIC_IRQMASK(1),306SOCRATES_FPGA_IRQ_MASK << 16);307socrates_fpga_pic_write(FPGA_PIC_IRQMASK(2),308SOCRATES_FPGA_IRQ_MASK << 16);309raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);310311pr_info("FPGA PIC: Setting up Socrates FPGA PIC\n");312}313314315