Path: blob/master/arch/powerpc/platforms/85xx/socrates_fpga_pic.c
26481 views
// SPDX-License-Identifier: GPL-2.0-only1/*2* Copyright (C) 2008 Ilya Yanok, Emcraft Systems3*/45#include <linux/irq.h>6#include <linux/of_address.h>7#include <linux/of_irq.h>8#include <linux/io.h>910#include "socrates_fpga_pic.h"1112/*13* The FPGA supports 9 interrupt sources, which can be routed to 314* interrupt request lines of the MPIC. The line to be used can be15* specified through the third cell of FDT property "interrupts".16*/1718#define SOCRATES_FPGA_NUM_IRQS 91920#define FPGA_PIC_IRQCFG (0x0)21#define FPGA_PIC_IRQMASK(n) (0x4 + 0x4 * (n))2223#define SOCRATES_FPGA_IRQ_MASK ((1 << SOCRATES_FPGA_NUM_IRQS) - 1)2425struct socrates_fpga_irq_info {26unsigned int irq_line;27int type;28};2930/*31* Interrupt routing and type table32*33* IRQ_TYPE_NONE means the interrupt type is configurable,34* otherwise it's fixed to the specified value.35*/36static struct socrates_fpga_irq_info fpga_irqs[SOCRATES_FPGA_NUM_IRQS] = {37[0] = {0, IRQ_TYPE_NONE},38[1] = {0, IRQ_TYPE_LEVEL_HIGH},39[2] = {0, IRQ_TYPE_LEVEL_LOW},40[3] = {0, IRQ_TYPE_NONE},41[4] = {0, IRQ_TYPE_NONE},42[5] = {0, IRQ_TYPE_NONE},43[6] = {0, IRQ_TYPE_NONE},44[7] = {0, IRQ_TYPE_NONE},45[8] = {0, IRQ_TYPE_LEVEL_HIGH},46};4748static DEFINE_RAW_SPINLOCK(socrates_fpga_pic_lock);4950static void __iomem *socrates_fpga_pic_iobase;51static struct irq_domain *socrates_fpga_pic_irq_host;52static unsigned int socrates_fpga_irqs[3];5354static inline uint32_t socrates_fpga_pic_read(int reg)55{56return in_be32(socrates_fpga_pic_iobase + reg);57}5859static inline void socrates_fpga_pic_write(int reg, uint32_t val)60{61out_be32(socrates_fpga_pic_iobase + reg, val);62}6364static inline unsigned int socrates_fpga_pic_get_irq(unsigned int irq)65{66uint32_t cause;67unsigned long flags;68int i;6970/* Check irq line routed to the MPIC */71for (i = 0; i < 3; i++) {72if (irq == socrates_fpga_irqs[i])73break;74}75if (i == 3)76return 0;7778raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);79cause = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(i));80raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);81for (i = SOCRATES_FPGA_NUM_IRQS - 1; i >= 0; i--) {82if (cause >> (i + 16))83break;84}85return irq_find_mapping(socrates_fpga_pic_irq_host,86(irq_hw_number_t)i);87}8889static void socrates_fpga_pic_cascade(struct irq_desc *desc)90{91struct irq_chip *chip = irq_desc_get_chip(desc);92unsigned int irq = irq_desc_get_irq(desc);93unsigned int cascade_irq;9495/*96* See if we actually have an interrupt, call generic handling code if97* we do.98*/99cascade_irq = socrates_fpga_pic_get_irq(irq);100101if (cascade_irq)102generic_handle_irq(cascade_irq);103chip->irq_eoi(&desc->irq_data);104}105106static void socrates_fpga_pic_ack(struct irq_data *d)107{108unsigned long flags;109unsigned int irq_line, hwirq = irqd_to_hwirq(d);110uint32_t mask;111112irq_line = fpga_irqs[hwirq].irq_line;113raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);114mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line))115& SOCRATES_FPGA_IRQ_MASK;116mask |= (1 << (hwirq + 16));117socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line), mask);118raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);119}120121static void socrates_fpga_pic_mask(struct irq_data *d)122{123unsigned long flags;124unsigned int hwirq = irqd_to_hwirq(d);125int irq_line;126u32 mask;127128irq_line = fpga_irqs[hwirq].irq_line;129raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);130mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line))131& SOCRATES_FPGA_IRQ_MASK;132mask &= ~(1 << hwirq);133socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line), mask);134raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);135}136137static void socrates_fpga_pic_mask_ack(struct irq_data *d)138{139unsigned long flags;140unsigned int hwirq = irqd_to_hwirq(d);141int irq_line;142u32 mask;143144irq_line = fpga_irqs[hwirq].irq_line;145raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);146mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line))147& SOCRATES_FPGA_IRQ_MASK;148mask &= ~(1 << hwirq);149mask |= (1 << (hwirq + 16));150socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line), mask);151raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);152}153154static void socrates_fpga_pic_unmask(struct irq_data *d)155{156unsigned long flags;157unsigned int hwirq = irqd_to_hwirq(d);158int irq_line;159u32 mask;160161irq_line = fpga_irqs[hwirq].irq_line;162raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);163mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line))164& SOCRATES_FPGA_IRQ_MASK;165mask |= (1 << hwirq);166socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line), mask);167raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);168}169170static void socrates_fpga_pic_eoi(struct irq_data *d)171{172unsigned long flags;173unsigned int hwirq = irqd_to_hwirq(d);174int irq_line;175u32 mask;176177irq_line = fpga_irqs[hwirq].irq_line;178raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);179mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line))180& SOCRATES_FPGA_IRQ_MASK;181mask |= (1 << (hwirq + 16));182socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line), mask);183raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);184}185186static int socrates_fpga_pic_set_type(struct irq_data *d,187unsigned int flow_type)188{189unsigned long flags;190unsigned int hwirq = irqd_to_hwirq(d);191int polarity;192u32 mask;193194if (fpga_irqs[hwirq].type != IRQ_TYPE_NONE)195return -EINVAL;196197switch (flow_type & IRQ_TYPE_SENSE_MASK) {198case IRQ_TYPE_LEVEL_HIGH:199polarity = 1;200break;201case IRQ_TYPE_LEVEL_LOW:202polarity = 0;203break;204default:205return -EINVAL;206}207raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);208mask = socrates_fpga_pic_read(FPGA_PIC_IRQCFG);209if (polarity)210mask |= (1 << hwirq);211else212mask &= ~(1 << hwirq);213socrates_fpga_pic_write(FPGA_PIC_IRQCFG, mask);214raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);215return 0;216}217218static struct irq_chip socrates_fpga_pic_chip = {219.name = "FPGA-PIC",220.irq_ack = socrates_fpga_pic_ack,221.irq_mask = socrates_fpga_pic_mask,222.irq_mask_ack = socrates_fpga_pic_mask_ack,223.irq_unmask = socrates_fpga_pic_unmask,224.irq_eoi = socrates_fpga_pic_eoi,225.irq_set_type = socrates_fpga_pic_set_type,226};227228static int socrates_fpga_pic_host_map(struct irq_domain *h, unsigned int virq,229irq_hw_number_t hwirq)230{231/* All interrupts are LEVEL sensitive */232irq_set_status_flags(virq, IRQ_LEVEL);233irq_set_chip_and_handler(virq, &socrates_fpga_pic_chip,234handle_fasteoi_irq);235236return 0;237}238239static int socrates_fpga_pic_host_xlate(struct irq_domain *h,240struct device_node *ct, const u32 *intspec, unsigned int intsize,241irq_hw_number_t *out_hwirq, unsigned int *out_flags)242{243struct socrates_fpga_irq_info *fpga_irq = &fpga_irqs[intspec[0]];244245*out_hwirq = intspec[0];246if (fpga_irq->type == IRQ_TYPE_NONE) {247/* type is configurable */248if (intspec[1] != IRQ_TYPE_LEVEL_LOW &&249intspec[1] != IRQ_TYPE_LEVEL_HIGH) {250pr_warn("FPGA PIC: invalid irq type, setting default active low\n");251*out_flags = IRQ_TYPE_LEVEL_LOW;252} else {253*out_flags = intspec[1];254}255} else {256/* type is fixed */257*out_flags = fpga_irq->type;258}259260/* Use specified interrupt routing */261if (intspec[2] <= 2)262fpga_irq->irq_line = intspec[2];263else264pr_warn("FPGA PIC: invalid irq routing\n");265266return 0;267}268269static const struct irq_domain_ops socrates_fpga_pic_host_ops = {270.map = socrates_fpga_pic_host_map,271.xlate = socrates_fpga_pic_host_xlate,272};273274void __init socrates_fpga_pic_init(struct device_node *pic)275{276unsigned long flags;277int i;278279/* Setup an irq_domain structure */280socrates_fpga_pic_irq_host = irq_domain_create_linear(of_fwnode_handle(pic),281SOCRATES_FPGA_NUM_IRQS, &socrates_fpga_pic_host_ops, NULL);282if (socrates_fpga_pic_irq_host == NULL) {283pr_err("FPGA PIC: Unable to allocate host\n");284return;285}286287for (i = 0; i < 3; i++) {288socrates_fpga_irqs[i] = irq_of_parse_and_map(pic, i);289if (!socrates_fpga_irqs[i]) {290pr_warn("FPGA PIC: can't get irq%d\n", i);291continue;292}293irq_set_chained_handler(socrates_fpga_irqs[i],294socrates_fpga_pic_cascade);295}296297socrates_fpga_pic_iobase = of_iomap(pic, 0);298299raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);300socrates_fpga_pic_write(FPGA_PIC_IRQMASK(0),301SOCRATES_FPGA_IRQ_MASK << 16);302socrates_fpga_pic_write(FPGA_PIC_IRQMASK(1),303SOCRATES_FPGA_IRQ_MASK << 16);304socrates_fpga_pic_write(FPGA_PIC_IRQMASK(2),305SOCRATES_FPGA_IRQ_MASK << 16);306raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);307308pr_info("FPGA PIC: Setting up Socrates FPGA PIC\n");309}310311312