* [PATCH/2.6.17-rc4 4/10]Powerpc: Add tsi108 pic support
@ 2006-05-17 10:14 Zang Roy-r61911
2006-05-17 16:05 ` Kumar Gala
2006-05-18 0:52 ` Benjamin Herrenschmidt
0 siblings, 2 replies; 16+ messages in thread
From: Zang Roy-r61911 @ 2006-05-17 10:14 UTC (permalink / raw)
To: Paul Mackerras; +Cc: linuxppc-dev list, Alexandre.Bounine, Yang Xin-Xin-r48390
Add Tundra Semiconductor tsi108 host bridge interrupt controller support.
Signed-off-by: Alexandre Bounine <alexandreb@tundra.com>
Signed-off-by: Roy Zang <tie-fei.zang@freescale.com>
---
arch/powerpc/sysdev/tsi108_pic.c | 813 ++++++++++++++++++++++++++++++++++++++
include/asm-powerpc/tsi108_pic.h | 232 +++++++++++
2 files changed, 1045 insertions(+), 0 deletions(-)
create mode 100644 arch/powerpc/sysdev/tsi108_pic.c
create mode 100644 include/asm-powerpc/tsi108_pic.h
7d23f6a0984cd54ca787f880a57067330900abe8
diff --git a/arch/powerpc/sysdev/tsi108_pic.c b/arch/powerpc/sysdev/tsi108_pic.c
new file mode 100644
index 0000000..bbca587
--- /dev/null
+++ b/arch/powerpc/sysdev/tsi108_pic.c
@@ -0,0 +1,813 @@
+/*
+ * (C) Copyright 2005 Tundra Semiconductor Corp.
+ * Alex Bounine, <alexandreb@tundra.com).
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+/*
+ * Tsi108 Interrupt Controller Handling
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/sysdev.h>
+#include <asm/ptrace.h>
+#include <asm/signal.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/prom.h>
+#include <asm/sections.h>
+#include <asm/hardirq.h>
+#include <asm/machdep.h>
+
+#include <asm/tsi108.h>
+#include <asm/tsi108_irq.h>
+#include <asm/tsi108_pic.h>
+#undef DEBUG
+
+#ifdef DEBUG
+#define DBG(fmt...) do { printk(fmt); } while(0)
+#else
+#define DBG(fmt...) do { } while(0)
+#endif
+
+extern u32 get_vir_csrbase(void);
+extern u32 tsi108_read_reg(u32 reg_offset);
+extern void tsi108_write_reg(u32 reg_offset, u32 val);
+
+static phys_addr_t tsi108_pic_phy_addr;
+static u32 tsi108_pic_vir_addr;
+
+static int tsi108_pic_cascade_irq = -1;
+static int (*tsi108_pic_cascade_fn) (struct pt_regs *);
+
+/* Global Operations */
+static void tsi108_pic_set_task_priority(u_int pri);
+static void tsi108_pic_set_spurious(u_int vector);
+void tsi108_pic_mask_all(void);
+
+/* Timer Interrupts */
+static void tsi108_pic_inittimer(u_int timer, u_int pri, u_int vector);
+static void tsi108_pic_maptimer(u_int timer, u_int cpumask);
+
+/* Interrupt Sources */
+static void tsi108_pic_enable_irq(u_int irq);
+static void tsi108_pic_disable_irq(u_int irq);
+static void tsi108_pic_initirq(u_int irq, u_int pri, u_int vector, int polarity,
+ int is_level);
+static void tsi108_pic_mapirq(u_int irq, u_int cpumask, u_int keepmask);
+static void init_pci_source(void);
+static inline int get_pci_source(int vector);
+int tsi108_pic_set_irq_sense(int irq, int pol, int sense);
+
+/*
+ * tsi108_pic interface routines
+ */
+static void tsi108_pic_end_irq(unsigned int irq_nr);
+static void tsi108_pic_ack_irq(unsigned int irq_nr);
+void tsi108_pic_set_affinity(unsigned int irq_nr, unsigned long cpumask);
+
+static struct hw_interrupt_type tsi108_pic_irq = {
+ "tsi108_pic",
+ NULL,
+ NULL,
+ tsi108_pic_enable_irq,
+ tsi108_pic_disable_irq,
+ tsi108_pic_ack_irq,
+ tsi108_pic_end_irq,
+ NULL
+};
+
+static void tsi108_pci_irq_enable(u_int irq);
+static void tsi108_pci_irq_disable(u_int irq);
+static void tsi108_pci_irq_ack(u_int irq);
+static void tsi108_pci_irq_end(u_int irq);
+
+static struct hw_interrupt_type tsi108_pci_irq = {
+ "tsi108_PCI_int",
+ NULL,
+ NULL,
+ tsi108_pci_irq_enable,
+ tsi108_pci_irq_disable,
+ tsi108_pci_irq_ack,
+ tsi108_pci_irq_end,
+ NULL
+};
+
+#ifdef DBG_TSI108_INTERRUPT
+#define ASSERT(expr) if (!(expr)) { \
+ printk("tsi108pic :" \
+ "assertion failed! %s[%d]: %s\n", \
+ __FUNCTION__, __LINE__, #expr); \
+ dump_stack(); \
+ }
+#else
+#define ASSERT(expr) do {} while (0)
+#endif
+
+static inline u_int get_vector_offset(u_int irq)
+{
+ u_int offset;
+
+ if (irq < TSI108_IRQ_BASE || irq >= IRQ_PCI_INTAD_BASE)
+ return 0;
+
+ if (irq < IRQ_TSI108_MBOX0)
+ offset = TSI108_INT_IVPR(irq - TSI108_IRQ_BASE);
+ else if (irq < IRQ_TSI108_DBELL0)
+ offset = TSI108_INT_MBVPR(irq - IRQ_TSI108_MBOX0);
+ else if (irq < IRQ_TSI108_TIMER0)
+ offset = TSI108_INT_DVPR(irq - IRQ_TSI108_DBELL0);
+ else
+ offset = TSI108_INT_GTVPR(irq - IRQ_TSI108_TIMER0);
+
+ return offset;
+}
+
+static inline u_int tsi108_pic_read_reg(u_int reg_offset)
+{
+ return in_be32((volatile u32 *)(tsi108_pic_vir_addr + reg_offset));
+}
+
+static inline void tsi108_pic_write_reg(u_int reg_offset, u_int val)
+{
+ out_be32((volatile u32 *)(tsi108_pic_vir_addr + reg_offset), val);
+}
+
+void tsi108_pic_reset(void)
+{
+ tsi108_pic_write_reg(TSI108_INT_GCR, TSI108PIC_INT_GCR_R);
+ while (tsi108_pic_read_reg(TSI108_INT_GCR) & TSI108PIC_INT_GCR_R)
+ mb();
+}
+
+void tsi108_pic_set_output(int dest_num, u32 sense, u32 polarity)
+{
+ u32 temp = 0;
+ temp |= (IRQ_SENSE_LEVEL == sense) ?
+ (TSI108PIC_INT_CSR_S_LEVEL) : (TSI108PIC_INT_CSR_S_EDGE);
+ temp |= (IRQ_POLARITY_POSITIVE == polarity) ?
+ (TSI108PIC_INT_CSR_P_HIGH) : (TSI108PIC_INT_CSR_P_LOW);
+ tsi108_pic_write_reg(TSI108_INT_CSR(dest_num), temp);
+ mb();
+}
+
+int tsi108_pic_source_cfg(int src_num, /* interrupt source number */
+ u32 sense, /* interrupt source Sense */
+ u32 polarity, /* interrupt source Polarity */
+ TSI108_IRQ_MODE mode /* interrupt delivery Mode */
+ )
+{
+ unsigned temp;
+
+ temp = tsi108_pic_read_reg(TSI108_INT_IVPR(src_num));
+
+ if (temp & TSI108PIC_ACTIVITY) /* error if source is active */
+ return -1;
+
+ if (0 == (temp & TSI108PIC_MASK)) {
+ temp |= TSI108PIC_MASK; /* mask IRQ prior making changes */
+ tsi108_pic_write_reg(TSI108_INT_IVPR(src_num), temp);
+ }
+
+ temp &= ~(TSI108PIC_INT_IVPR_MODE |
+ TSI108PIC_INT_IVPR_S | TSI108PIC_INT_IVPR_P);
+
+ temp |= (IRQ_SENSE_LEVEL == sense) ?
+ (TSI108PIC_INT_CSR_S_LEVEL) : (TSI108PIC_INT_CSR_S_EDGE);
+ temp |= (IRQ_POLARITY_POSITIVE == polarity) ?
+ (TSI108PIC_INT_CSR_P_HIGH) : (TSI108PIC_INT_CSR_P_LOW);
+
+ tsi108_pic_write_reg(TSI108_INT_IVPR(src_num),
+ TSI108PIC_MASK | (mode << 29) | temp);
+ return (0);
+}
+
+int tsi108_pic_set_vector(int src_num, /* source number */
+ int vect, /* vector number */
+ int prio /* interrupt source priority */
+ )
+{
+ unsigned tmp;
+
+ tmp = tsi108_pic_read_reg(TSI108_INT_IVPR(src_num));
+
+ if (tmp & TSI108PIC_ACTIVITY) /* error if source is active */
+ return -1;
+
+ if (0 == (tmp & TSI108PIC_MASK)) {
+ tmp |= TSI108PIC_MASK; /* mask IRQ prior making changes */
+ tsi108_pic_write_reg(TSI108_INT_IVPR(src_num), tmp);
+ }
+
+ /* clear bits to be changed */
+ tmp &= ~(TSI108PIC_VECTOR_MASK | TSI108PIC_PRIORITY_MASK);
+
+ tmp |= (prio << 16) | vect;
+ tsi108_pic_write_reg(TSI108_INT_IVPR(src_num), tmp);
+ return 0;
+}
+
+void tsi108_pic_mask_all()
+{
+ int i;
+ unsigned int vp;
+
+ /* Mask all external and internal interrupt sources */
+ for (i = 0; i < TSI108PIC_MAX_SOURCES; i++) {
+ vp = tsi108_pic_read_reg(TSI108_INT_IVPR(i));
+ tsi108_pic_write_reg(TSI108_INT_IVPR(i), vp | TSI108PIC_MASK);
+ mb();
+
+ /* Make sure that irq is masked */
+ do {
+ vp = tsi108_pic_read_reg(TSI108_INT_IVPR(i));
+ } while ((vp & TSI108PIC_ACTIVITY) && !(vp & TSI108PIC_MASK));
+ }
+
+ /* Mask all timer interrupts */
+ for (i = 0; i < TSI108PIC_NUM_TIMERS; i++) {
+ vp = tsi108_pic_read_reg(TSI108_INT_GTVPR(i));
+ tsi108_pic_write_reg(TSI108_INT_GTVPR(i), vp | TSI108PIC_MASK);
+ mb();
+
+ do {
+ vp = tsi108_pic_read_reg(TSI108_INT_GTVPR(i));
+ } while ((vp & TSI108PIC_ACTIVITY) && !(vp & TSI108PIC_MASK));
+ }
+
+ /* Mask all doorbell interrupts */
+ for (i = 0; i < TSI108PIC_NUM_DBELLS; i++) {
+ vp = tsi108_pic_read_reg(TSI108_INT_DVPR(i));
+ tsi108_pic_write_reg(TSI108_INT_IVPR(i), vp | TSI108PIC_MASK);
+ mb();
+
+ do {
+ vp = tsi108_pic_read_reg(TSI108_INT_DVPR(i));
+ } while ((vp & TSI108PIC_ACTIVITY) && !(vp & TSI108PIC_MASK));
+ }
+
+ /* Mask all mailbox interrupts */
+ for (i = 0; i < 4; i++) {
+ vp = tsi108_pic_read_reg(TSI108_INT_MBVPR(i));
+ tsi108_pic_write_reg(TSI108_INT_MBVPR(i), vp | TSI108PIC_MASK);
+ mb();
+
+ do {
+ vp = tsi108_pic_read_reg(TSI108_INT_MBVPR(i));
+ } while ((vp & TSI108PIC_ACTIVITY) && !(vp & TSI108PIC_MASK));
+ }
+}
+
+/*
+ * The Tsi108 PC initialization routine.
+ * A caller routine (usually from platform-specific code has to provide
+ * sense/polarity configuration information for four external interrupt
+ * sources INT0 - INT3. This should be done in form of four-byte array
+ * (one byte per source ) that contains combination of sensitivity/polarity
+ * flags defined in asm-ppc/irq.h.
+ *
+ * Example of PIC initialization call is shown below:
+ *
+ * u_char your_board_pic_initsenses[] __initdata = {
+ * (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), // INT[0]
+ * (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), // INT[1]
+ * (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), // INT[2]
+ * (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE) // INT[3]
+ * };
+ *
+ * tsi108_pic_init(your_board_pic_initsenses);
+ */
+
+void __init tsi108_pic_init(u_char * board_init_senses)
+{
+ u_int i;
+ u32 sense;
+
+ struct device_node *tsi_pic;
+ tsi_pic = of_find_node_by_type(NULL, "open-pic");
+ if (tsi_pic) {
+ unsigned int size;
+ void *prop = get_property(tsi_pic, "reg", &size);
+ tsi108_pic_phy_addr = of_translate_address(tsi_pic, prop);
+ }
+
+ DBG("%s: Tsi108 pic phy addr = 0x%x\n", __FUNCTION__,
+ (u32) tsi108_pic_phy_addr);
+ if (tsi108_pic_phy_addr == 0) {
+ printk("No tsi108 PIC found !\n");
+ return;
+ }
+
+ tsi108_pic_vir_addr = (u32) ioremap(tsi108_pic_phy_addr, 0x400);
+
+ tsi108_pic_reset();
+
+ if (ppc_md.progress)
+ ppc_md.progress("tsi108_pic_init: enter", 0x122);
+
+ /* Initialize timer interrupts */
+ for (i = 0; i < TSI108PIC_NUM_TIMERS; i++) {
+ /* Disabled, Priority 0 */
+ tsi108_pic_inittimer(i, 0, IRQ_TSI108_TIMER0 + i);
+ /* No processor */
+ tsi108_pic_maptimer(i, 0);
+ }
+
+ /* Init board-specific external sources */
+ for (i = 0; i < 4; i++) {
+ sense = board_init_senses[i];
+
+ if (sense & IRQ_SENSE_MASK)
+ irq_desc[TSI108_IRQ(i)].status |= IRQ_LEVEL;
+
+ /* Enabled, Priority 8 */
+ tsi108_pic_initirq(i, 8, TSI108_IRQ(i),
+ (sense & IRQ_POLARITY_MASK),
+ (sense & IRQ_SENSE_MASK));
+ /* Map to CPU #0 */
+ tsi108_pic_mapirq(TSI108_IRQ(i), 1 << 0, 0);
+ }
+
+ /* Init remaining internal sources. */
+ for (; i < TSI108PIC_MAX_SOURCES; i++) {
+ /* Disabled, Priority 8, by default - Positive Edge */
+ tsi108_pic_initirq(i, 8, TSI108_IRQ(i),
+ IRQ_POLARITY_POSITIVE, IRQ_SENSE_EDGE);
+ /* Map to CPU #0 */
+ tsi108_pic_mapirq(TSI108_IRQ(i), (1 << 0), 0);
+ }
+
+ /*
+ * Change sensitivity to level for sources that require it.
+ */
+
+ irq_desc[IRQ_TSI108_GIGE0].status |= IRQ_LEVEL;
+ irq_desc[IRQ_TSI108_GIGE1].status |= IRQ_LEVEL;
+ irq_desc[IRQ_TSI108_PCI].status |= IRQ_LEVEL;
+
+ /* Init descriptors */
+ for (i = 0; i < TSI108PIC_MAX_SOURCES; i++)
+ irq_desc[i + TSI108_IRQ_BASE].handler = &tsi108_pic_irq;
+
+ for (i = 0; i < NUM_PCI_IRQS; i++) {
+ irq_desc[i + IRQ_PCI_INTAD_BASE].handler = &tsi108_pci_irq;
+ irq_desc[i + IRQ_PCI_INTAD_BASE].status |= IRQ_LEVEL;
+ }
+
+ /* Initialize the spurious interrupt */
+ tsi108_pic_set_spurious(TSI108_IRQ_SPURIOUS);
+ tsi108_pic_set_task_priority(0);
+
+ init_pci_source();
+ tsi108_pic_enable_irq(IRQ_TSI108_PCI);
+
+ i = tsi108_pic_read_reg(TSI108_INT_VECTOR(0));
+ tsi108_pic_write_reg(TSI108_INT_EOI(0), 0);
+
+ if (ppc_md.progress)
+ ppc_md.progress("tsi108_pic_init: exit", 0x222);
+}
+
+/*
+ * Find out the current interrupt
+ */
+static u_int tsi108_pic_get_vect(void)
+{
+ u_int vec;
+
+ vec = tsi108_pic_read_reg(TSI108_INT_VECTOR(0)) & TSI108PIC_VECTOR_MASK;
+
+#ifdef DBG_TSI108_INTERRUPT
+ if (vec == TSI108_IRQ_SPURIOUS)
+ printk("TSI108: SPURIOUS vec=0x%08x\n", vec);
+ else
+ printk("TSI108: read vec=0x%08x\n", vec);
+#endif
+ return (vec);
+}
+
+static inline void tsi108_pic_eoi(void)
+{
+ tsi108_pic_write_reg(TSI108_INT_EOI(0), 0);
+ mb();
+}
+
+static void __init tsi108_pic_set_task_priority(u_int pri)
+{
+ ASSERT(pri >= 0 && pri < TSI108PIC_NUM_PRI);
+
+ tsi108_pic_write_reg(TSI108_INT_TASKP(0),
+ pri & TSI108PIC_INT_TASKP_TASKP);
+ mb();
+}
+
+static void tsi108_pic_set_spurious(u_int vec)
+{
+ ASSERT(vec == TSI108_IRQ_SPURIOUS);
+ tsi108_pic_write_reg(TSI108_INT_SVR, vec);
+ mb();
+}
+
+#ifdef CONFIG_SMP
+/*
+ * Convert a cpu mask from logical to physical cpu numbers.
+ */
+static inline u32 physmask(u32 cpumask)
+{
+ int i;
+ u32 mask = 0;
+
+ for (i = 0; i < NR_CPUS; ++i, cpumask >>= 1)
+ if (cpu_online(i))
+ mask |= (cpumask & 1) << smp_hw_index[i];
+ return mask;
+}
+#else
+#define physmask(cpumask) (cpumask)
+#endif
+
+/*
+ * Initialize a timer interrupt (and disable it)
+ *
+ * timer: timer number
+ * pri: interrupt source priority
+ * vec: the vector it will produce
+ */
+static void __init tsi108_pic_inittimer(u_int timer, u_int pri, u_int vec)
+{
+ unsigned int gtvpr;
+
+ ASSERT(timer >= 0 && timer < TSI108PIC_NUM_TIMERS);
+ ASSERT(pri >= 0 && pri < TSI108PIC_NUM_PRI);
+ ASSERT(vec >= 0 && vec < TSI108PIC_NUM_VECTORS);
+
+ gtvpr = tsi108_pic_read_reg(TSI108_INT_GTVPR(timer));
+ gtvpr &= ~(TSI108PIC_PRIORITY_MASK | TSI108PIC_VECTOR_MASK);
+ gtvpr |= (pri << 16) | vec;
+ tsi108_pic_write_reg(TSI108_INT_GTVPR(timer), gtvpr | TSI108PIC_MASK);
+ mb();
+}
+
+/*
+ * Map a timer interrupt to one or more CPUs
+ */
+static void __init tsi108_pic_maptimer(u_int timer, u_int cpumask)
+{
+ ASSERT(timer >= 0 && timer < TSI108PIC_NUM_TIMERS);
+
+ tsi108_pic_write_reg(TSI108_INT_GTDR(timer), physmask(cpumask));
+ mb();
+}
+
+/*
+ * Initalize the interrupt source which will generate an NMI.
+ * This raises the interrupt's priority from 8 to 9.
+ *
+ * irq: The logical IRQ which generates an NMI.
+ */
+void __init tsi108_pic_init_nmi_irq(u_int irq)
+{
+ u_int offset = get_vector_offset(irq);
+ u_int vpr = tsi108_pic_read_reg(offset);
+ vpr &= ~TSI108PIC_PRIORITY_MASK;
+ tsi108_pic_write_reg(offset, vpr | (9 << 16));
+ mb();
+}
+
+/*
+ *
+ * All functions below take an offset'ed irq argument
+ *
+ */
+
+/*
+ * Hookup a cascade to the tsi108 PIC.
+ */
+void __init
+tsi108_pic_hookup_cascade(u_int irq, char *name,
+ int (*cascade_fn) (struct pt_regs *))
+{
+ tsi108_pic_cascade_irq = irq;
+ tsi108_pic_cascade_fn = cascade_fn;
+ if (request_irq(irq, no_action, SA_INTERRUPT, name, NULL))
+ printk("Unable to get Tsi108 PIC IRQ %d for cascade\n",
+ irq - TSI108_IRQ_BASE);
+}
+
+/*
+ * Enable/disable an external interrupt source
+ *
+ * Externally called, irq is an offseted system-wide interrupt number
+ */
+static void tsi108_pic_enable_irq(u_int irq)
+{
+ u32 offset = get_vector_offset(irq);
+ u32 vpr = tsi108_pic_read_reg(offset);
+
+ /*
+ * Undo sensitivity change (see tsi108_pic_disable_irq())
+ */
+ if (irq_desc[irq].status & IRQ_LEVEL)
+ vpr |= TSI108PIC_INT_IVPR_S;
+
+ tsi108_pic_write_reg(offset, vpr & ~TSI108PIC_MASK);
+ mb();
+}
+
+static void tsi108_pic_disable_irq(u_int irq)
+{
+ u32 offset = get_vector_offset(irq);
+ u32 vpr = tsi108_pic_read_reg(offset);
+
+ /*
+ * Switch level interrupt to edge sensitivity to avoid generation
+ * of spurious interrupt request. See design note in Tsi108 PIC
+ * section of Tsi108 manual.
+ */
+ if (irq_desc[irq].status & IRQ_LEVEL)
+ vpr &= ~TSI108PIC_INT_IVPR_S;
+
+ tsi108_pic_write_reg(offset, vpr | TSI108PIC_MASK);
+ mb();
+ vpr = tsi108_pic_read_reg(offset);
+ if (!(vpr & TSI108PIC_MASK))
+ printk("TSI108_PIC: Error - Unable disable IRQ %d\n", irq);
+}
+
+/*
+ * Initialize an interrupt source (and disable it!)
+ *
+ * irq: Tsi108 PIC interrupt source number
+ * pri: interrupt source priority
+ * vec: the vector it will produce
+ * pol: polarity (1 for positive, 0 for negative)
+ * sense: 1 for level, 0 for edge
+ */
+static void __init
+tsi108_pic_initirq(u_int irq, u_int pri, u_int vec, int pol, int sense)
+{
+ unsigned int ivpr;
+
+ ivpr = TSI108PIC_MASK | (pri << 16) | vec;
+ ivpr |= (IRQ_SENSE_LEVEL == sense) ?
+ TSI108PIC_INT_IVPR_S_LEVEL : TSI108PIC_INT_IVPR_S_EDGE;
+ ivpr |= (IRQ_POLARITY_POSITIVE == pol) ?
+ TSI108PIC_INT_IVPR_P_HIGH : TSI108PIC_INT_IVPR_P_LOW;
+ tsi108_pic_write_reg(TSI108_INT_IVPR(irq), ivpr);
+ mb();
+}
+
+int tsi108_pic_set_irq_sense(int irq, /* PIC source number */
+ int pol, /* interrupt source polarity */
+ int sense /* interrupt source sense */
+ )
+{
+ unsigned int ivpr;
+
+ ivpr = tsi108_pic_read_reg(TSI108_INT_IVPR(irq));
+
+ if (ivpr & TSI108PIC_ACTIVITY) /* error if source is active */
+ return -1;
+
+ if (0 == (ivpr & TSI108PIC_MASK)) {
+ ivpr |= TSI108PIC_MASK; /* mask IRQ prior making changes */
+ tsi108_pic_write_reg(TSI108_INT_IVPR(irq), ivpr);
+ }
+
+ /* clear bits to be changed */
+ ivpr &= ~(TSI108PIC_INT_IVPR_P | TSI108PIC_INT_IVPR_S);
+
+ ivpr |= (IRQ_SENSE_LEVEL == sense) ?
+ TSI108PIC_INT_IVPR_S_LEVEL : TSI108PIC_INT_IVPR_S_EDGE;
+ ivpr |= (IRQ_POLARITY_POSITIVE == pol) ?
+ TSI108PIC_INT_IVPR_P_HIGH : TSI108PIC_INT_IVPR_P_LOW;
+
+ tsi108_pic_write_reg(TSI108_INT_IVPR(irq), ivpr);
+ return 0;
+}
+
+/*
+ * Map an interrupt source to one or more CPUs
+ */
+static void tsi108_pic_mapirq(u_int irq, u_int physmask, u_int keepmask)
+{
+ u_int offset = get_vector_offset(irq);
+
+ if (0 == offset)
+ return;
+ if (keepmask != 0)
+ physmask |= tsi108_pic_read_reg(offset + 4);
+ tsi108_pic_write_reg(offset + 4, physmask);
+ mb();
+}
+
+/* No spinlocks, should not be necessary with the Tsi108 PIC
+ * (1 register = 1 interrupt and we have the desc lock).
+ */
+static void tsi108_pic_ack_irq(unsigned int irq_nr)
+{
+ if ((irq_desc[irq_nr].status & IRQ_LEVEL) == 0)
+ tsi108_pic_eoi();
+}
+
+static void tsi108_pic_end_irq(unsigned int irq_nr)
+{
+ if ((irq_desc[irq_nr].status & IRQ_LEVEL) != 0)
+ tsi108_pic_eoi();
+}
+
+void tsi108_pic_set_affinity(unsigned int irq_nr, unsigned long cpumask)
+{
+ tsi108_pic_mapirq(irq_nr, physmask(cpumask), 0);
+}
+
+int tsi108_pic_get_irq(struct pt_regs *regs)
+{
+ int vector = tsi108_pic_get_vect();
+
+ if (vector == TSI108_IRQ_SPURIOUS) {
+ vector = -1;
+ }
+
+ if (vector == IRQ_TSI108_PCI) {
+ vector = get_pci_source(vector);
+ }
+
+ if (vector == -1) {
+ tsi108_pic_write_reg(TSI108_INT_EOI(0), 0);
+ }
+
+ return vector;
+}
+
+static void tsi108_pci_int_mask(u_int irq)
+{
+ u_int irp_cfg;
+ int int_line = (irq - IRQ_PCI_INTAD_BASE);
+
+ irp_cfg = tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL);
+ mb();
+ irp_cfg |= (1 << int_line); /* INTx_DIR = output */
+ irp_cfg &= ~(3 << (8 + (int_line * 2))); /* INTx_TYPE = unused */
+ tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL, irp_cfg);
+ mb();
+ irp_cfg = tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL);
+}
+
+static void tsi108_pci_int_unmask(u_int irq)
+{
+ u_int irp_cfg;
+ int int_line = (irq - IRQ_PCI_INTAD_BASE);
+
+ irp_cfg = tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL);
+ mb();
+ irp_cfg &= ~(1 << int_line);
+ irp_cfg |= (3 << (8 + (int_line * 2)));
+ tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL, irp_cfg);
+ mb();
+}
+
+static void tsi108_pci_irq_enable(u_int irq)
+{
+ tsi108_pci_int_unmask(irq);
+}
+
+static void tsi108_pci_irq_disable(u_int irq)
+{
+ tsi108_pci_int_mask(irq);
+}
+
+static void tsi108_pci_irq_ack(u_int irq)
+{
+ tsi108_pci_int_mask(irq);
+}
+
+static void tsi108_pci_irq_end(u_int irq)
+{
+ tsi108_pic_eoi(); /* eoi IRQ_TSI108_PCI */
+ tsi108_pci_int_unmask(irq);
+}
+
+static inline int get_pci_source(int vector)
+{
+ u_int temp = 0;
+ int irq = -1;
+ int i;
+ u_int pci_irp_stat;
+ static int mask = 0;
+
+ /* Read PCI/X block interrupt status register */
+ pci_irp_stat = tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_STAT);
+ mb();
+
+ if (pci_irp_stat & TSI108_PCI_IRP_STAT_P_INT) {
+ /* Process Interrupt from PCI bus INTA# - INTD# lines */
+ temp =
+ tsi108_read_reg(TSI108_PCI_OFFSET +
+ TSI108_PCI_IRP_INTAD) & 0xf;
+ mb();
+ for (i = 0; i < 4; i++, mask++) {
+ if (temp & (1 << mask % 4)) {
+ irq = IRQ_PCI_INTA + mask % 4;
+ mask++;
+ break;
+ }
+ }
+ }
+#ifdef DBG_TSI108_INTERRUPT
+ else {
+ printk("TSI108_PIC: error in TSI108_PCI_IRP_STAT\n");
+ pci_irp_stat =
+ tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_STAT);
+ temp =
+ tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_INTAD);
+ mb();
+ printk(">> stat=0x%08x intad=0x%08x ", pci_irp_stat, temp);
+ temp =
+ tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL);
+ mb();
+ printk("cfg_ctl=0x%08x ", temp);
+ temp =
+ tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_ENABLE);
+ mb();
+ printk("irp_enable=0x%08x\n", temp);
+ }
+#endif /* DBG_TSI108_INTERRUPT */
+
+ return irq;
+}
+
+static void init_pci_source(void)
+{
+ tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL,
+ 0x0000ff00);
+ tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_ENABLE,
+ 0x00400000);
+ mb();
+}
+
+static struct sysdev_class tsi108_pic_sysclass = {
+ set_kset_name("tsi108_pic"),
+};
+
+static struct sys_device device_tsi108_pic = {
+ .id = 0,
+ .cls = &tsi108_pic_sysclass,
+};
+
+static struct sysdev_driver driver_tsi108_pic = {
+#ifdef CONFIG_PM /* FIXME: placeholder for future development */
+ .suspend = &tsi108_pic_suspend,
+ .resume = &tsi108_pic_resume,
+#endif /* CONFIG_PM */
+};
+
+static int __init init_tsi108_pic_sysfs(void)
+{
+ int rc;
+
+ if (!get_csrbase())
+ return -ENODEV;
+ printk(KERN_DEBUG "Registering tsi108_pic with sysfs...\n");
+ rc = sysdev_class_register(&tsi108_pic_sysclass);
+ if (rc) {
+ printk(KERN_ERR "Failed registering tsi108_pic sys class\n");
+ return -ENODEV;
+ }
+ rc = sysdev_register(&device_tsi108_pic);
+ if (rc) {
+ printk(KERN_ERR "Failed registering tsi108_pic sys device\n");
+ return -ENODEV;
+ }
+ rc = sysdev_driver_register(&tsi108_pic_sysclass, &driver_tsi108_pic);
+ if (rc) {
+ printk(KERN_ERR "Failed registering tsi108_pic sys driver\n");
+ return -ENODEV;
+ }
+ return 0;
+}
+
+subsys_initcall(init_tsi108_pic_sysfs);
diff --git a/include/asm-powerpc/tsi108_pic.h b/include/asm-powerpc/tsi108_pic.h
new file mode 100644
index 0000000..7b23352
--- /dev/null
+++ b/include/asm-powerpc/tsi108_pic.h
@@ -0,0 +1,232 @@
+/*
+ * (C) Copyright 2005 Tundra Semiconductor Corp.
+ * Alex Bounine, <alexandreb@tundra.com).
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+/*
+ * arch/ppc/syslib/tsi108_pic.h - Tsi108 Interrupt Controller definitions
+ */
+
+#ifndef _LINUX_TSI108_PIC_H
+#define _LINUX_TSI108_PIC_H
+
+#include <asm/tsi108_irq.h>
+
+#ifdef __KERNEL__
+
+/*
+ * Tsi108 PIC supports up to 24 interrupt sources and up to 4 processors
+ */
+
+#define TSI108PIC_MAX_SOURCES 24
+#define TSI108PIC_MAX_PROCESSORS 4
+
+#define TSI108PIC_NUM_TIMERS 4
+#define TSI108PIC_NUM_DBELLS 4
+#define TSI108PIC_NUM_PROC 4
+#define TSI108PIC_NUM_PRI 16
+#define TSI108PIC_NUM_VECTORS 256
+
+/*
+ * Tsi108 PIC Register offsets within block.
+ */
+
+/* Registers controlling sources */
+#define TSI108_INT_FRR (0x000)
+#define TSI108_INT_GCR (0x004)
+#define TSI108_INT_SVR (0x010)
+#define TSI108_INT_GTVPR(n) (0x38 + 0x10*(n))
+#define TSI108_INT_GTDR(n) (0x3C + 0x10*(n))
+#define TSI108_INT_IVPR(n) (0x100 + 0x8*(n))
+#define TSI108_INT_IDR(n) (0x104 + 0x8*(n))
+#define TSI108_INT_DVPR(n) (0x204 + 0xC*(n))
+#define TSI108_INT_DDR(n) (0x208 + 0xC*(n))
+#define TSI108_INT_MBVPR(n) (0x284 + 0x10*(n))
+#define TSI108_INT_MBDR(n) (0x288 + 0x10*(n))
+
+/* Registers controlling destinations */
+#define TSI108_INT_TASKP(n) (0x300 + 0x40*(n))
+#define TSI108_INT_VECTOR(n) (0x304 + 0x40*(n))
+#define TSI108_INT_EOI(n) (0x308 + 0x40*(n))
+#define TSI108_INT_CSR(n) (0x30C + 0x40*(n))
+
+/*
+ * Generic definitions common for different types of interrupt
+ * sources.
+ */
+
+#define TSI108PIC_MASK (0x80000000)
+#define TSI108PIC_ACTIVITY (0x40000000)
+#define TSI108PIC_PRIORITY_MASK (0x000f0000)
+#define TSI108PIC_VECTOR_MASK (0x000000ff)
+
+/**********************************************************
+ * Register Bit Masks definitions for every register
+ */
+
+/* TSI108PIC_INT_FRR : Register Bits Masks Definitions */
+#define TSI108PIC_INT_FRR_VID (0x000000ff)
+#define TSI108PIC_INT_FRR_NCPU (0x00001f00)
+#define TSI108PIC_INT_FRR_NITM (0x0000e000)
+#define TSI108PIC_INT_FRR_NIRQ (0x07ff0000)
+#define TSI108PIC_INT_FRR_NIDOOR (0xe0000000)
+#define TSI108PIC_INT_FRR_RESERVED (0x18000000)
+
+/* TSI108PIC_INT_GCR : Register Bits Masks Definitions */
+#define TSI108PIC_INT_GCR_R (0x80000000)
+#define TSI108PIC_INT_GCR_RESERVED (0x7fffffff)
+
+/* TSI108PIC_INT_ICR : Register Bits Masks Definitions */
+#define TSI108PIC_INT_ICR_R (0x0000000f)
+#define TSI108PIC_INT_ICR_RESERVED (0xfffffff0)
+
+/* TSI108PIC_INT_MVI : Register Bits Masks Definitions */
+#define TSI108PIC_INT_MVI_VID (0x000000ff)
+#define TSI108PIC_INT_MVI_DID (0x0000ff00)
+#define TSI108PIC_INT_MVI_STEP (0x00ff0000)
+#define TSI108PIC_INT_MVI_RESERVED (0xff000000)
+
+/* TSI108PIC_INT_SVR : Register Bits Masks Definitions */
+#define TSI108PIC_INT_SVR_VECTOR (0x000000ff)
+#define TSI108PIC_INT_SVR_RESERVED (0xffffff00)
+
+/* TSI108PIC_INT_TFRR : Register Bits Masks Definitions */
+#define TSI108PIC_INT_TFRR_TIME_FREQ (0xffffffff)
+
+/* TSI108PIC_INT_SOFT_SET : Register Bits Masks Definitions */
+#define TSI108PIC_INT_SOFT_SET_S (0x00ffffff)
+#define TSI108PIC_INT_SOFT_SET_RESERVED (0xff000000)
+
+/* TSI108PIC_INT_SOFT_ENABLE : Register Bits Masks Definitions */
+#define TSI108PIC_INT_SOFT_ENABLE_EN (0x00ffffff)
+#define TSI108PIC_INT_SOFT_ENABLE_RESERVED (0xff000000)
+
+/* TSI108PIC_INT_GTCCR(X) : Register Bits Masks Definitions */
+#define TSI108PIC_INT_GTCCR_COUNT (0x7fffffff)
+#define TSI108PIC_INT_GTCCR_T (0x80000000)
+
+/* TSI108PIC_INT_GTBCR(X) : Register Bits Masks Definitions */
+#define TSI108PIC_INT_GTBCR_B_COUNT (0x7fffffff)
+#define TSI108PIC_INT_GTBCR_CI (0x80000000)
+
+/* TSI108PIC_INT_GTVPR(X) : Register Bits Masks Definitions */
+#define TSI108PIC_INT_GTVPR_VECTOR (0x000000ff)
+#define TSI108PIC_INT_GTVPR_PRIORITY (0x000f0000)
+#define TSI108PIC_INT_GTVPR_PRESCALE (0x00f00000)
+#define TSI108PIC_INT_GTVPR_A (0x40000000)
+#define TSI108PIC_INT_GTVPR_M (0x80000000)
+#define TSI108PIC_INT_GTVPR_RESERVED (0x3f00ff00)
+
+/* TSI108PIC_INT_GTDR(X) : Register Bits Masks Definitions */
+#define TSI108PIC_INT_GTDR_SEL_OUT (0x0000000f)
+#define TSI108PIC_INT_GTDR_RESERVED (0xfffffff0)
+
+/* TSI108PIC_INT_IVPR(X) : Register Bits Masks Definitions */
+#define TSI108PIC_INT_IVPR_VECTOR (0x000000ff)
+#define TSI108PIC_INT_IVPR_PRIORITY (0x000f0000)
+
+#define TSI108PIC_INT_IVPR_P (0x01000000)
+#define TSI108PIC_INT_IVPR_P_LOW (0 << 24)
+#define TSI108PIC_INT_IVPR_P_HIGH (1 << 24)
+
+#define TSI108PIC_INT_IVPR_S (0x02000000)
+#define TSI108PIC_INT_IVPR_S_EDGE (0 << 25)
+#define TSI108PIC_INT_IVPR_S_LEVEL (1 << 25)
+
+#define TSI108PIC_INT_IVPR_MODE (0x20000000)
+#define TSI108PIC_INT_IVPR_A (0x40000000)
+#define TSI108PIC_INT_IVPR_M (0x80000000)
+#define TSI108PIC_INT_IVPR_RESERVED (0x1cf0ff00)
+
+/* TSI108PIC_INT_IDR(X) : Register Bits Masks Definitions */
+#define TSI108PIC_INT_IDR_SEL_OUT (0x0000000f)
+#define TSI108PIC_INT_IDR_RESERVED (0xfffffff0)
+
+/* TSI108PIC_INT_DAR : Register Bits Masks Definitions */
+#define TSI108PIC_INT_DAR_A (0x0000000f)
+#define TSI108PIC_INT_DAR_RESERVED (0xfffffff0)
+
+/* TSI108PIC_INT_DVPR(X) : Register Bits Masks Definitions */
+#define TSI108PIC_INT_DVPR_VECTOR (0x000000ff)
+#define TSI108PIC_INT_DVPR_PRIORITY (0x000f0000)
+#define TSI108PIC_INT_DVPR_A (0x40000000)
+#define TSI108PIC_INT_DVPR_M (0x80000000)
+#define TSI108PIC_INT_DVPR_RESERVED (0x3ff0ff00)
+
+/* TSI108PIC_INT_DDR(X) : Register Bits Masks Definitions */
+#define TSI108PIC_INT_DDR_SEL_OUT (0x0000000f)
+#define TSI108PIC_INT_DDR_RESERVED (0xfffffff0)
+
+/* TSI108PIC_INT_DMR(X) : Register Bits Masks Definitions */
+#define TSI108PIC_INT_DMR_M (0xffffffff)
+
+/* TSI108PIC_INT_MBR(X) : Register Bits Masks Definitions */
+#define TSI108PIC_INT_MBR_M (0xffffffff)
+
+/* TSI108PIC_INT_MBVPR(X) : Register Bits Masks Definitions */
+#define TSI108PIC_INT_MBVPR_VECTOR (0x000000ff)
+#define TSI108PIC_INT_MBVPR_PRIORITY (0x000f0000)
+#define TSI108PIC_INT_MBVPR_A (0x40000000)
+#define TSI108PIC_INT_MBVPR_M (0x80000000)
+#define TSI108PIC_INT_MBVPR_RESERVED (0x3ff0ff00)
+
+/* TSI108PIC_INT_MBDR(X) : Register Bits Masks Definitions */
+#define TSI108PIC_INT_MBDR_SEL_OUT (0x0000000f)
+#define TSI108PIC_INT_MBDR_RESERVED (0xfffffff0)
+
+/* TSI108PIC_INT_TASKP(X) : Register Bits Masks Definitions */
+#define TSI108PIC_INT_TASKP_TASKP (0x0000000f)
+#define TSI108PIC_INT_TASKP_RESERVED (0xfffffff0)
+
+/* TSI108PIC_INT_VECTOR(X) : Register Bits Masks Definitions */
+#define TSI108PIC_INT_VECTOR_VECTOR (0x000000ff)
+#define TSI108PIC_INT_VECTOR_LS_VECTOR (0xff000000)
+#define TSI108PIC_INT_VECTOR_RESERVED (0x00ffff00)
+
+/* TSI108PIC_INT_EOI(X) : Register Bits Masks Definitions */
+#define TSI108PIC_INT_EOI_EOI (0x000000ff)
+#define TSI108PIC_INT_EOI_RESERVED (0xffffff00)
+
+/* TSI108PIC_INT_CSR(X) : Register Bits Masks Definitions */
+#define TSI108PIC_INT_CSR_RESERVED (0xfffffffc)
+
+#define TSI108PIC_INT_CSR_P (1 << 0)
+#define TSI108PIC_INT_CSR_P_LOW (0 << 0)
+#define TSI108PIC_INT_CSR_P_HIGH (1 << 0)
+
+#define TSI108PIC_INT_CSR_S (1 << 1)
+#define TSI108PIC_INT_CSR_S_EDGE (0 << 1)
+#define TSI108PIC_INT_CSR_S_LEVEL (1 << 1)
+
+extern void tsi108_pic_init(u_char * board_init_senses);
+extern void tsi108_pic_reset(void);
+extern void tsi108_pic_set_output(int dest_num, u32 sense, u32 polarity);
+extern int tsi108_pic_source_cfg(int src_num, u32 sense,
+ u32 polarity, TSI108_IRQ_MODE mode);
+extern int tsi108_pic_set_vector(int src_num, int vect, int prio);
+extern void tsi108_pic_init_nmi_irq(u_int irq);
+extern void tsi108_pic_hookup_cascade(u_int irq, char *name,
+ int (*cascade_fn) (struct pt_regs *));
+extern int tsi108_pic_get_irq(struct pt_regs *regs);
+
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_TSI108_PIC_H */
--
1.3.0
^ permalink raw reply related [flat|nested] 16+ messages in thread
* Re: [PATCH/2.6.17-rc4 4/10]Powerpc: Add tsi108 pic support
2006-05-17 10:14 Zang Roy-r61911
@ 2006-05-17 16:05 ` Kumar Gala
2006-05-18 0:52 ` Benjamin Herrenschmidt
1 sibling, 0 replies; 16+ messages in thread
From: Kumar Gala @ 2006-05-17 16:05 UTC (permalink / raw)
To: Zang Roy-r61911
Cc: linuxppc-dev list, Yang Xin-Xin-r48390, Paul Mackerras,
Alexandre.Bounine
On May 17, 2006, at 5:14 AM, Zang Roy-r61911 wrote:
> Add Tundra Semiconductor tsi108 host bridge interrupt controller
> support.
>
> Signed-off-by: Alexandre Bounine <alexandreb@tundra.com>
> Signed-off-by: Roy Zang <tie-fei.zang@freescale.com>
>
> ---
The TSI 108 looks like an OpenPIC derivative you should look at using
mpic for this instead of introducing new pic code.
- k
>
> arch/powerpc/sysdev/tsi108_pic.c | 813 +++++++++++++++++++++++++++
> +++++++++++
> include/asm-powerpc/tsi108_pic.h | 232 +++++++++++
> 2 files changed, 1045 insertions(+), 0 deletions(-)
> create mode 100644 arch/powerpc/sysdev/tsi108_pic.c
> create mode 100644 include/asm-powerpc/tsi108_pic.h
>
> 7d23f6a0984cd54ca787f880a57067330900abe8
> diff --git a/arch/powerpc/sysdev/tsi108_pic.c b/arch/powerpc/sysdev/
> tsi108_pic.c
> new file mode 100644
> index 0000000..bbca587
> --- /dev/null
> +++ b/arch/powerpc/sysdev/tsi108_pic.c
> @@ -0,0 +1,813 @@
> +/*
> + * (C) Copyright 2005 Tundra Semiconductor Corp.
> + * Alex Bounine, <alexandreb@tundra.com).
> + *
> + * See file CREDITS for list of people who contributed to this
> + * project.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 of
> + * the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
> + * MA 02111-1307 USA
> + */
> +
> +/*
> + * Tsi108 Interrupt Controller Handling
> + */
> +
> +#include <linux/config.h>
> +#include <linux/types.h>
> +#include <linux/kernel.h>
> +#include <linux/sched.h>
> +#include <linux/init.h>
> +#include <linux/irq.h>
> +#include <linux/interrupt.h>
> +#include <linux/sysdev.h>
> +#include <asm/ptrace.h>
> +#include <asm/signal.h>
> +#include <asm/io.h>
> +#include <asm/irq.h>
> +#include <asm/prom.h>
> +#include <asm/sections.h>
> +#include <asm/hardirq.h>
> +#include <asm/machdep.h>
> +
> +#include <asm/tsi108.h>
> +#include <asm/tsi108_irq.h>
> +#include <asm/tsi108_pic.h>
> +#undef DEBUG
> +
> +#ifdef DEBUG
> +#define DBG(fmt...) do { printk(fmt); } while(0)
> +#else
> +#define DBG(fmt...) do { } while(0)
> +#endif
> +
> +extern u32 get_vir_csrbase(void);
> +extern u32 tsi108_read_reg(u32 reg_offset);
> +extern void tsi108_write_reg(u32 reg_offset, u32 val);
> +
> +static phys_addr_t tsi108_pic_phy_addr;
> +static u32 tsi108_pic_vir_addr;
> +
> +static int tsi108_pic_cascade_irq = -1;
> +static int (*tsi108_pic_cascade_fn) (struct pt_regs *);
> +
> +/* Global Operations */
> +static void tsi108_pic_set_task_priority(u_int pri);
> +static void tsi108_pic_set_spurious(u_int vector);
> +void tsi108_pic_mask_all(void);
> +
> +/* Timer Interrupts */
> +static void tsi108_pic_inittimer(u_int timer, u_int pri, u_int
> vector);
> +static void tsi108_pic_maptimer(u_int timer, u_int cpumask);
> +
> +/* Interrupt Sources */
> +static void tsi108_pic_enable_irq(u_int irq);
> +static void tsi108_pic_disable_irq(u_int irq);
> +static void tsi108_pic_initirq(u_int irq, u_int pri, u_int vector,
> int polarity,
> + int is_level);
> +static void tsi108_pic_mapirq(u_int irq, u_int cpumask, u_int
> keepmask);
> +static void init_pci_source(void);
> +static inline int get_pci_source(int vector);
> +int tsi108_pic_set_irq_sense(int irq, int pol, int sense);
> +
> +/*
> + * tsi108_pic interface routines
> + */
> +static void tsi108_pic_end_irq(unsigned int irq_nr);
> +static void tsi108_pic_ack_irq(unsigned int irq_nr);
> +void tsi108_pic_set_affinity(unsigned int irq_nr, unsigned long
> cpumask);
> +
> +static struct hw_interrupt_type tsi108_pic_irq = {
> + "tsi108_pic",
> + NULL,
> + NULL,
> + tsi108_pic_enable_irq,
> + tsi108_pic_disable_irq,
> + tsi108_pic_ack_irq,
> + tsi108_pic_end_irq,
> + NULL
> +};
> +
> +static void tsi108_pci_irq_enable(u_int irq);
> +static void tsi108_pci_irq_disable(u_int irq);
> +static void tsi108_pci_irq_ack(u_int irq);
> +static void tsi108_pci_irq_end(u_int irq);
> +
> +static struct hw_interrupt_type tsi108_pci_irq = {
> + "tsi108_PCI_int",
> + NULL,
> + NULL,
> + tsi108_pci_irq_enable,
> + tsi108_pci_irq_disable,
> + tsi108_pci_irq_ack,
> + tsi108_pci_irq_end,
> + NULL
> +};
> +
> +#ifdef DBG_TSI108_INTERRUPT
> +#define ASSERT(expr) if (!(expr)) { \
> + printk("tsi108pic :" \
> + "assertion failed! %s[%d]: %s\n", \
> + __FUNCTION__, __LINE__, #expr); \
> + dump_stack(); \
> + }
> +#else
> +#define ASSERT(expr) do {} while (0)
> +#endif
> +
> +static inline u_int get_vector_offset(u_int irq)
> +{
> + u_int offset;
> +
> + if (irq < TSI108_IRQ_BASE || irq >= IRQ_PCI_INTAD_BASE)
> + return 0;
> +
> + if (irq < IRQ_TSI108_MBOX0)
> + offset = TSI108_INT_IVPR(irq - TSI108_IRQ_BASE);
> + else if (irq < IRQ_TSI108_DBELL0)
> + offset = TSI108_INT_MBVPR(irq - IRQ_TSI108_MBOX0);
> + else if (irq < IRQ_TSI108_TIMER0)
> + offset = TSI108_INT_DVPR(irq - IRQ_TSI108_DBELL0);
> + else
> + offset = TSI108_INT_GTVPR(irq - IRQ_TSI108_TIMER0);
> +
> + return offset;
> +}
> +
> +static inline u_int tsi108_pic_read_reg(u_int reg_offset)
> +{
> + return in_be32((volatile u32 *)(tsi108_pic_vir_addr + reg_offset));
> +}
> +
> +static inline void tsi108_pic_write_reg(u_int reg_offset, u_int val)
> +{
> + out_be32((volatile u32 *)(tsi108_pic_vir_addr + reg_offset), val);
> +}
> +
> +void tsi108_pic_reset(void)
> +{
> + tsi108_pic_write_reg(TSI108_INT_GCR, TSI108PIC_INT_GCR_R);
> + while (tsi108_pic_read_reg(TSI108_INT_GCR) & TSI108PIC_INT_GCR_R)
> + mb();
> +}
> +
> +void tsi108_pic_set_output(int dest_num, u32 sense, u32 polarity)
> +{
> + u32 temp = 0;
> + temp |= (IRQ_SENSE_LEVEL == sense) ?
> + (TSI108PIC_INT_CSR_S_LEVEL) : (TSI108PIC_INT_CSR_S_EDGE);
> + temp |= (IRQ_POLARITY_POSITIVE == polarity) ?
> + (TSI108PIC_INT_CSR_P_HIGH) : (TSI108PIC_INT_CSR_P_LOW);
> + tsi108_pic_write_reg(TSI108_INT_CSR(dest_num), temp);
> + mb();
> +}
> +
> +int tsi108_pic_source_cfg(int src_num, /* interrupt source number */
> + u32 sense, /* interrupt source Sense */
> + u32 polarity, /* interrupt source Polarity */
> + TSI108_IRQ_MODE mode /* interrupt delivery Mode */
> + )
> +{
> + unsigned temp;
> +
> + temp = tsi108_pic_read_reg(TSI108_INT_IVPR(src_num));
> +
> + if (temp & TSI108PIC_ACTIVITY) /* error if source is active */
> + return -1;
> +
> + if (0 == (temp & TSI108PIC_MASK)) {
> + temp |= TSI108PIC_MASK; /* mask IRQ prior making changes */
> + tsi108_pic_write_reg(TSI108_INT_IVPR(src_num), temp);
> + }
> +
> + temp &= ~(TSI108PIC_INT_IVPR_MODE |
> + TSI108PIC_INT_IVPR_S | TSI108PIC_INT_IVPR_P);
> +
> + temp |= (IRQ_SENSE_LEVEL == sense) ?
> + (TSI108PIC_INT_CSR_S_LEVEL) : (TSI108PIC_INT_CSR_S_EDGE);
> + temp |= (IRQ_POLARITY_POSITIVE == polarity) ?
> + (TSI108PIC_INT_CSR_P_HIGH) : (TSI108PIC_INT_CSR_P_LOW);
> +
> + tsi108_pic_write_reg(TSI108_INT_IVPR(src_num),
> + TSI108PIC_MASK | (mode << 29) | temp);
> + return (0);
> +}
> +
> +int tsi108_pic_set_vector(int src_num, /* source number */
> + int vect, /* vector number */
> + int prio /* interrupt source priority */
> + )
> +{
> + unsigned tmp;
> +
> + tmp = tsi108_pic_read_reg(TSI108_INT_IVPR(src_num));
> +
> + if (tmp & TSI108PIC_ACTIVITY) /* error if source is active */
> + return -1;
> +
> + if (0 == (tmp & TSI108PIC_MASK)) {
> + tmp |= TSI108PIC_MASK; /* mask IRQ prior making changes */
> + tsi108_pic_write_reg(TSI108_INT_IVPR(src_num), tmp);
> + }
> +
> + /* clear bits to be changed */
> + tmp &= ~(TSI108PIC_VECTOR_MASK | TSI108PIC_PRIORITY_MASK);
> +
> + tmp |= (prio << 16) | vect;
> + tsi108_pic_write_reg(TSI108_INT_IVPR(src_num), tmp);
> + return 0;
> +}
> +
> +void tsi108_pic_mask_all()
> +{
> + int i;
> + unsigned int vp;
> +
> + /* Mask all external and internal interrupt sources */
> + for (i = 0; i < TSI108PIC_MAX_SOURCES; i++) {
> + vp = tsi108_pic_read_reg(TSI108_INT_IVPR(i));
> + tsi108_pic_write_reg(TSI108_INT_IVPR(i), vp | TSI108PIC_MASK);
> + mb();
> +
> + /* Make sure that irq is masked */
> + do {
> + vp = tsi108_pic_read_reg(TSI108_INT_IVPR(i));
> + } while ((vp & TSI108PIC_ACTIVITY) && !(vp & TSI108PIC_MASK));
> + }
> +
> + /* Mask all timer interrupts */
> + for (i = 0; i < TSI108PIC_NUM_TIMERS; i++) {
> + vp = tsi108_pic_read_reg(TSI108_INT_GTVPR(i));
> + tsi108_pic_write_reg(TSI108_INT_GTVPR(i), vp | TSI108PIC_MASK);
> + mb();
> +
> + do {
> + vp = tsi108_pic_read_reg(TSI108_INT_GTVPR(i));
> + } while ((vp & TSI108PIC_ACTIVITY) && !(vp & TSI108PIC_MASK));
> + }
> +
> + /* Mask all doorbell interrupts */
> + for (i = 0; i < TSI108PIC_NUM_DBELLS; i++) {
> + vp = tsi108_pic_read_reg(TSI108_INT_DVPR(i));
> + tsi108_pic_write_reg(TSI108_INT_IVPR(i), vp | TSI108PIC_MASK);
> + mb();
> +
> + do {
> + vp = tsi108_pic_read_reg(TSI108_INT_DVPR(i));
> + } while ((vp & TSI108PIC_ACTIVITY) && !(vp & TSI108PIC_MASK));
> + }
> +
> + /* Mask all mailbox interrupts */
> + for (i = 0; i < 4; i++) {
> + vp = tsi108_pic_read_reg(TSI108_INT_MBVPR(i));
> + tsi108_pic_write_reg(TSI108_INT_MBVPR(i), vp | TSI108PIC_MASK);
> + mb();
> +
> + do {
> + vp = tsi108_pic_read_reg(TSI108_INT_MBVPR(i));
> + } while ((vp & TSI108PIC_ACTIVITY) && !(vp & TSI108PIC_MASK));
> + }
> +}
> +
> +/*
> + * The Tsi108 PC initialization routine.
> + * A caller routine (usually from platform-specific code has to
> provide
> + * sense/polarity configuration information for four external
> interrupt
> + * sources INT0 - INT3. This should be done in form of four-byte
> array
> + * (one byte per source ) that contains combination of sensitivity/
> polarity
> + * flags defined in asm-ppc/irq.h.
> + *
> + * Example of PIC initialization call is shown below:
> + *
> + * u_char your_board_pic_initsenses[] __initdata = {
> + * (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), // INT[0]
> + * (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), // INT[1]
> + * (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), // INT[2]
> + * (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE) // INT[3]
> + * };
> + *
> + * tsi108_pic_init(your_board_pic_initsenses);
> + */
> +
> +void __init tsi108_pic_init(u_char * board_init_senses)
> +{
> + u_int i;
> + u32 sense;
> +
> + struct device_node *tsi_pic;
> + tsi_pic = of_find_node_by_type(NULL, "open-pic");
> + if (tsi_pic) {
> + unsigned int size;
> + void *prop = get_property(tsi_pic, "reg", &size);
> + tsi108_pic_phy_addr = of_translate_address(tsi_pic, prop);
> + }
> +
> + DBG("%s: Tsi108 pic phy addr = 0x%x\n", __FUNCTION__,
> + (u32) tsi108_pic_phy_addr);
> + if (tsi108_pic_phy_addr == 0) {
> + printk("No tsi108 PIC found !\n");
> + return;
> + }
> +
> + tsi108_pic_vir_addr = (u32) ioremap(tsi108_pic_phy_addr, 0x400);
> +
> + tsi108_pic_reset();
> +
> + if (ppc_md.progress)
> + ppc_md.progress("tsi108_pic_init: enter", 0x122);
> +
> + /* Initialize timer interrupts */
> + for (i = 0; i < TSI108PIC_NUM_TIMERS; i++) {
> + /* Disabled, Priority 0 */
> + tsi108_pic_inittimer(i, 0, IRQ_TSI108_TIMER0 + i);
> + /* No processor */
> + tsi108_pic_maptimer(i, 0);
> + }
> +
> + /* Init board-specific external sources */
> + for (i = 0; i < 4; i++) {
> + sense = board_init_senses[i];
> +
> + if (sense & IRQ_SENSE_MASK)
> + irq_desc[TSI108_IRQ(i)].status |= IRQ_LEVEL;
> +
> + /* Enabled, Priority 8 */
> + tsi108_pic_initirq(i, 8, TSI108_IRQ(i),
> + (sense & IRQ_POLARITY_MASK),
> + (sense & IRQ_SENSE_MASK));
> + /* Map to CPU #0 */
> + tsi108_pic_mapirq(TSI108_IRQ(i), 1 << 0, 0);
> + }
> +
> + /* Init remaining internal sources. */
> + for (; i < TSI108PIC_MAX_SOURCES; i++) {
> + /* Disabled, Priority 8, by default - Positive Edge */
> + tsi108_pic_initirq(i, 8, TSI108_IRQ(i),
> + IRQ_POLARITY_POSITIVE, IRQ_SENSE_EDGE);
> + /* Map to CPU #0 */
> + tsi108_pic_mapirq(TSI108_IRQ(i), (1 << 0), 0);
> + }
> +
> + /*
> + * Change sensitivity to level for sources that require it.
> + */
> +
> + irq_desc[IRQ_TSI108_GIGE0].status |= IRQ_LEVEL;
> + irq_desc[IRQ_TSI108_GIGE1].status |= IRQ_LEVEL;
> + irq_desc[IRQ_TSI108_PCI].status |= IRQ_LEVEL;
> +
> + /* Init descriptors */
> + for (i = 0; i < TSI108PIC_MAX_SOURCES; i++)
> + irq_desc[i + TSI108_IRQ_BASE].handler = &tsi108_pic_irq;
> +
> + for (i = 0; i < NUM_PCI_IRQS; i++) {
> + irq_desc[i + IRQ_PCI_INTAD_BASE].handler = &tsi108_pci_irq;
> + irq_desc[i + IRQ_PCI_INTAD_BASE].status |= IRQ_LEVEL;
> + }
> +
> + /* Initialize the spurious interrupt */
> + tsi108_pic_set_spurious(TSI108_IRQ_SPURIOUS);
> + tsi108_pic_set_task_priority(0);
> +
> + init_pci_source();
> + tsi108_pic_enable_irq(IRQ_TSI108_PCI);
> +
> + i = tsi108_pic_read_reg(TSI108_INT_VECTOR(0));
> + tsi108_pic_write_reg(TSI108_INT_EOI(0), 0);
> +
> + if (ppc_md.progress)
> + ppc_md.progress("tsi108_pic_init: exit", 0x222);
> +}
> +
> +/*
> + * Find out the current interrupt
> + */
> +static u_int tsi108_pic_get_vect(void)
> +{
> + u_int vec;
> +
> + vec = tsi108_pic_read_reg(TSI108_INT_VECTOR(0)) &
> TSI108PIC_VECTOR_MASK;
> +
> +#ifdef DBG_TSI108_INTERRUPT
> + if (vec == TSI108_IRQ_SPURIOUS)
> + printk("TSI108: SPURIOUS vec=0x%08x\n", vec);
> + else
> + printk("TSI108: read vec=0x%08x\n", vec);
> +#endif
> + return (vec);
> +}
> +
> +static inline void tsi108_pic_eoi(void)
> +{
> + tsi108_pic_write_reg(TSI108_INT_EOI(0), 0);
> + mb();
> +}
> +
> +static void __init tsi108_pic_set_task_priority(u_int pri)
> +{
> + ASSERT(pri >= 0 && pri < TSI108PIC_NUM_PRI);
> +
> + tsi108_pic_write_reg(TSI108_INT_TASKP(0),
> + pri & TSI108PIC_INT_TASKP_TASKP);
> + mb();
> +}
> +
> +static void tsi108_pic_set_spurious(u_int vec)
> +{
> + ASSERT(vec == TSI108_IRQ_SPURIOUS);
> + tsi108_pic_write_reg(TSI108_INT_SVR, vec);
> + mb();
> +}
> +
> +#ifdef CONFIG_SMP
> +/*
> + * Convert a cpu mask from logical to physical cpu numbers.
> + */
> +static inline u32 physmask(u32 cpumask)
> +{
> + int i;
> + u32 mask = 0;
> +
> + for (i = 0; i < NR_CPUS; ++i, cpumask >>= 1)
> + if (cpu_online(i))
> + mask |= (cpumask & 1) << smp_hw_index[i];
> + return mask;
> +}
> +#else
> +#define physmask(cpumask) (cpumask)
> +#endif
> +
> +/*
> + * Initialize a timer interrupt (and disable it)
> + *
> + * timer: timer number
> + * pri: interrupt source priority
> + * vec: the vector it will produce
> + */
> +static void __init tsi108_pic_inittimer(u_int timer, u_int pri,
> u_int vec)
> +{
> + unsigned int gtvpr;
> +
> + ASSERT(timer >= 0 && timer < TSI108PIC_NUM_TIMERS);
> + ASSERT(pri >= 0 && pri < TSI108PIC_NUM_PRI);
> + ASSERT(vec >= 0 && vec < TSI108PIC_NUM_VECTORS);
> +
> + gtvpr = tsi108_pic_read_reg(TSI108_INT_GTVPR(timer));
> + gtvpr &= ~(TSI108PIC_PRIORITY_MASK | TSI108PIC_VECTOR_MASK);
> + gtvpr |= (pri << 16) | vec;
> + tsi108_pic_write_reg(TSI108_INT_GTVPR(timer), gtvpr |
> TSI108PIC_MASK);
> + mb();
> +}
> +
> +/*
> + * Map a timer interrupt to one or more CPUs
> + */
> +static void __init tsi108_pic_maptimer(u_int timer, u_int cpumask)
> +{
> + ASSERT(timer >= 0 && timer < TSI108PIC_NUM_TIMERS);
> +
> + tsi108_pic_write_reg(TSI108_INT_GTDR(timer), physmask(cpumask));
> + mb();
> +}
> +
> +/*
> + * Initalize the interrupt source which will generate an NMI.
> + * This raises the interrupt's priority from 8 to 9.
> + *
> + * irq: The logical IRQ which generates an NMI.
> + */
> +void __init tsi108_pic_init_nmi_irq(u_int irq)
> +{
> + u_int offset = get_vector_offset(irq);
> + u_int vpr = tsi108_pic_read_reg(offset);
> + vpr &= ~TSI108PIC_PRIORITY_MASK;
> + tsi108_pic_write_reg(offset, vpr | (9 << 16));
> + mb();
> +}
> +
> +/*
> + *
> + * All functions below take an offset'ed irq argument
> + *
> + */
> +
> +/*
> + * Hookup a cascade to the tsi108 PIC.
> + */
> +void __init
> +tsi108_pic_hookup_cascade(u_int irq, char *name,
> + int (*cascade_fn) (struct pt_regs *))
> +{
> + tsi108_pic_cascade_irq = irq;
> + tsi108_pic_cascade_fn = cascade_fn;
> + if (request_irq(irq, no_action, SA_INTERRUPT, name, NULL))
> + printk("Unable to get Tsi108 PIC IRQ %d for cascade\n",
> + irq - TSI108_IRQ_BASE);
> +}
> +
> +/*
> + * Enable/disable an external interrupt source
> + *
> + * Externally called, irq is an offseted system-wide interrupt
> number
> + */
> +static void tsi108_pic_enable_irq(u_int irq)
> +{
> + u32 offset = get_vector_offset(irq);
> + u32 vpr = tsi108_pic_read_reg(offset);
> +
> + /*
> + * Undo sensitivity change (see tsi108_pic_disable_irq())
> + */
> + if (irq_desc[irq].status & IRQ_LEVEL)
> + vpr |= TSI108PIC_INT_IVPR_S;
> +
> + tsi108_pic_write_reg(offset, vpr & ~TSI108PIC_MASK);
> + mb();
> +}
> +
> +static void tsi108_pic_disable_irq(u_int irq)
> +{
> + u32 offset = get_vector_offset(irq);
> + u32 vpr = tsi108_pic_read_reg(offset);
> +
> + /*
> + * Switch level interrupt to edge sensitivity to avoid generation
> + * of spurious interrupt request. See design note in Tsi108 PIC
> + * section of Tsi108 manual.
> + */
> + if (irq_desc[irq].status & IRQ_LEVEL)
> + vpr &= ~TSI108PIC_INT_IVPR_S;
> +
> + tsi108_pic_write_reg(offset, vpr | TSI108PIC_MASK);
> + mb();
> + vpr = tsi108_pic_read_reg(offset);
> + if (!(vpr & TSI108PIC_MASK))
> + printk("TSI108_PIC: Error - Unable disable IRQ %d\n", irq);
> +}
> +
> +/*
> + * Initialize an interrupt source (and disable it!)
> + *
> + * irq: Tsi108 PIC interrupt source number
> + * pri: interrupt source priority
> + * vec: the vector it will produce
> + * pol: polarity (1 for positive, 0 for negative)
> + * sense: 1 for level, 0 for edge
> + */
> +static void __init
> +tsi108_pic_initirq(u_int irq, u_int pri, u_int vec, int pol, int
> sense)
> +{
> + unsigned int ivpr;
> +
> + ivpr = TSI108PIC_MASK | (pri << 16) | vec;
> + ivpr |= (IRQ_SENSE_LEVEL == sense) ?
> + TSI108PIC_INT_IVPR_S_LEVEL : TSI108PIC_INT_IVPR_S_EDGE;
> + ivpr |= (IRQ_POLARITY_POSITIVE == pol) ?
> + TSI108PIC_INT_IVPR_P_HIGH : TSI108PIC_INT_IVPR_P_LOW;
> + tsi108_pic_write_reg(TSI108_INT_IVPR(irq), ivpr);
> + mb();
> +}
> +
> +int tsi108_pic_set_irq_sense(int irq, /* PIC source number */
> + int pol, /* interrupt source polarity */
> + int sense /* interrupt source sense */
> + )
> +{
> + unsigned int ivpr;
> +
> + ivpr = tsi108_pic_read_reg(TSI108_INT_IVPR(irq));
> +
> + if (ivpr & TSI108PIC_ACTIVITY) /* error if source is active */
> + return -1;
> +
> + if (0 == (ivpr & TSI108PIC_MASK)) {
> + ivpr |= TSI108PIC_MASK; /* mask IRQ prior making changes */
> + tsi108_pic_write_reg(TSI108_INT_IVPR(irq), ivpr);
> + }
> +
> + /* clear bits to be changed */
> + ivpr &= ~(TSI108PIC_INT_IVPR_P | TSI108PIC_INT_IVPR_S);
> +
> + ivpr |= (IRQ_SENSE_LEVEL == sense) ?
> + TSI108PIC_INT_IVPR_S_LEVEL : TSI108PIC_INT_IVPR_S_EDGE;
> + ivpr |= (IRQ_POLARITY_POSITIVE == pol) ?
> + TSI108PIC_INT_IVPR_P_HIGH : TSI108PIC_INT_IVPR_P_LOW;
> +
> + tsi108_pic_write_reg(TSI108_INT_IVPR(irq), ivpr);
> + return 0;
> +}
> +
> +/*
> + * Map an interrupt source to one or more CPUs
> + */
> +static void tsi108_pic_mapirq(u_int irq, u_int physmask, u_int
> keepmask)
> +{
> + u_int offset = get_vector_offset(irq);
> +
> + if (0 == offset)
> + return;
> + if (keepmask != 0)
> + physmask |= tsi108_pic_read_reg(offset + 4);
> + tsi108_pic_write_reg(offset + 4, physmask);
> + mb();
> +}
> +
> +/* No spinlocks, should not be necessary with the Tsi108 PIC
> + * (1 register = 1 interrupt and we have the desc lock).
> + */
> +static void tsi108_pic_ack_irq(unsigned int irq_nr)
> +{
> + if ((irq_desc[irq_nr].status & IRQ_LEVEL) == 0)
> + tsi108_pic_eoi();
> +}
> +
> +static void tsi108_pic_end_irq(unsigned int irq_nr)
> +{
> + if ((irq_desc[irq_nr].status & IRQ_LEVEL) != 0)
> + tsi108_pic_eoi();
> +}
> +
> +void tsi108_pic_set_affinity(unsigned int irq_nr, unsigned long
> cpumask)
> +{
> + tsi108_pic_mapirq(irq_nr, physmask(cpumask), 0);
> +}
> +
> +int tsi108_pic_get_irq(struct pt_regs *regs)
> +{
> + int vector = tsi108_pic_get_vect();
> +
> + if (vector == TSI108_IRQ_SPURIOUS) {
> + vector = -1;
> + }
> +
> + if (vector == IRQ_TSI108_PCI) {
> + vector = get_pci_source(vector);
> + }
> +
> + if (vector == -1) {
> + tsi108_pic_write_reg(TSI108_INT_EOI(0), 0);
> + }
> +
> + return vector;
> +}
> +
> +static void tsi108_pci_int_mask(u_int irq)
> +{
> + u_int irp_cfg;
> + int int_line = (irq - IRQ_PCI_INTAD_BASE);
> +
> + irp_cfg = tsi108_read_reg(TSI108_PCI_OFFSET +
> TSI108_PCI_IRP_CFG_CTL);
> + mb();
> + irp_cfg |= (1 << int_line); /* INTx_DIR = output */
> + irp_cfg &= ~(3 << (8 + (int_line * 2))); /* INTx_TYPE = unused */
> + tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL,
> irp_cfg);
> + mb();
> + irp_cfg = tsi108_read_reg(TSI108_PCI_OFFSET +
> TSI108_PCI_IRP_CFG_CTL);
> +}
> +
> +static void tsi108_pci_int_unmask(u_int irq)
> +{
> + u_int irp_cfg;
> + int int_line = (irq - IRQ_PCI_INTAD_BASE);
> +
> + irp_cfg = tsi108_read_reg(TSI108_PCI_OFFSET +
> TSI108_PCI_IRP_CFG_CTL);
> + mb();
> + irp_cfg &= ~(1 << int_line);
> + irp_cfg |= (3 << (8 + (int_line * 2)));
> + tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL,
> irp_cfg);
> + mb();
> +}
> +
> +static void tsi108_pci_irq_enable(u_int irq)
> +{
> + tsi108_pci_int_unmask(irq);
> +}
> +
> +static void tsi108_pci_irq_disable(u_int irq)
> +{
> + tsi108_pci_int_mask(irq);
> +}
> +
> +static void tsi108_pci_irq_ack(u_int irq)
> +{
> + tsi108_pci_int_mask(irq);
> +}
> +
> +static void tsi108_pci_irq_end(u_int irq)
> +{
> + tsi108_pic_eoi(); /* eoi IRQ_TSI108_PCI */
> + tsi108_pci_int_unmask(irq);
> +}
> +
> +static inline int get_pci_source(int vector)
> +{
> + u_int temp = 0;
> + int irq = -1;
> + int i;
> + u_int pci_irp_stat;
> + static int mask = 0;
> +
> + /* Read PCI/X block interrupt status register */
> + pci_irp_stat = tsi108_read_reg(TSI108_PCI_OFFSET +
> TSI108_PCI_IRP_STAT);
> + mb();
> +
> + if (pci_irp_stat & TSI108_PCI_IRP_STAT_P_INT) {
> + /* Process Interrupt from PCI bus INTA# - INTD# lines */
> + temp =
> + tsi108_read_reg(TSI108_PCI_OFFSET +
> + TSI108_PCI_IRP_INTAD) & 0xf;
> + mb();
> + for (i = 0; i < 4; i++, mask++) {
> + if (temp & (1 << mask % 4)) {
> + irq = IRQ_PCI_INTA + mask % 4;
> + mask++;
> + break;
> + }
> + }
> + }
> +#ifdef DBG_TSI108_INTERRUPT
> + else {
> + printk("TSI108_PIC: error in TSI108_PCI_IRP_STAT\n");
> + pci_irp_stat =
> + tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_STAT);
> + temp =
> + tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_INTAD);
> + mb();
> + printk(">> stat=0x%08x intad=0x%08x ", pci_irp_stat, temp);
> + temp =
> + tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL);
> + mb();
> + printk("cfg_ctl=0x%08x ", temp);
> + temp =
> + tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_ENABLE);
> + mb();
> + printk("irp_enable=0x%08x\n", temp);
> + }
> +#endif /* DBG_TSI108_INTERRUPT */
> +
> + return irq;
> +}
> +
> +static void init_pci_source(void)
> +{
> + tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL,
> + 0x0000ff00);
> + tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_ENABLE,
> + 0x00400000);
> + mb();
> +}
> +
> +static struct sysdev_class tsi108_pic_sysclass = {
> + set_kset_name("tsi108_pic"),
> +};
> +
> +static struct sys_device device_tsi108_pic = {
> + .id = 0,
> + .cls = &tsi108_pic_sysclass,
> +};
> +
> +static struct sysdev_driver driver_tsi108_pic = {
> +#ifdef CONFIG_PM /* FIXME: placeholder for future development */
> + .suspend = &tsi108_pic_suspend,
> + .resume = &tsi108_pic_resume,
> +#endif /* CONFIG_PM */
> +};
> +
> +static int __init init_tsi108_pic_sysfs(void)
> +{
> + int rc;
> +
> + if (!get_csrbase())
> + return -ENODEV;
> + printk(KERN_DEBUG "Registering tsi108_pic with sysfs...\n");
> + rc = sysdev_class_register(&tsi108_pic_sysclass);
> + if (rc) {
> + printk(KERN_ERR "Failed registering tsi108_pic sys class\n");
> + return -ENODEV;
> + }
> + rc = sysdev_register(&device_tsi108_pic);
> + if (rc) {
> + printk(KERN_ERR "Failed registering tsi108_pic sys device\n");
> + return -ENODEV;
> + }
> + rc = sysdev_driver_register(&tsi108_pic_sysclass,
> &driver_tsi108_pic);
> + if (rc) {
> + printk(KERN_ERR "Failed registering tsi108_pic sys driver\n");
> + return -ENODEV;
> + }
> + return 0;
> +}
> +
> +subsys_initcall(init_tsi108_pic_sysfs);
> diff --git a/include/asm-powerpc/tsi108_pic.h b/include/asm-powerpc/
> tsi108_pic.h
> new file mode 100644
> index 0000000..7b23352
> --- /dev/null
> +++ b/include/asm-powerpc/tsi108_pic.h
> @@ -0,0 +1,232 @@
> +/*
> + * (C) Copyright 2005 Tundra Semiconductor Corp.
> + * Alex Bounine, <alexandreb@tundra.com).
> + *
> + * See file CREDITS for list of people who contributed to this
> + * project.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 of
> + * the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
> + * MA 02111-1307 USA
> + */
> +
> +/*
> + * arch/ppc/syslib/tsi108_pic.h - Tsi108 Interrupt Controller
> definitions
> + */
> +
> +#ifndef _LINUX_TSI108_PIC_H
> +#define _LINUX_TSI108_PIC_H
> +
> +#include <asm/tsi108_irq.h>
> +
> +#ifdef __KERNEL__
> +
> +/*
> + * Tsi108 PIC supports up to 24 interrupt sources and up to 4
> processors
> + */
> +
> +#define TSI108PIC_MAX_SOURCES 24
> +#define TSI108PIC_MAX_PROCESSORS 4
> +
> +#define TSI108PIC_NUM_TIMERS 4
> +#define TSI108PIC_NUM_DBELLS 4
> +#define TSI108PIC_NUM_PROC 4
> +#define TSI108PIC_NUM_PRI 16
> +#define TSI108PIC_NUM_VECTORS 256
> +
> +/*
> + * Tsi108 PIC Register offsets within block.
> + */
> +
> +/* Registers controlling sources */
> +#define TSI108_INT_FRR (0x000)
> +#define TSI108_INT_GCR (0x004)
> +#define TSI108_INT_SVR (0x010)
> +#define TSI108_INT_GTVPR(n) (0x38 + 0x10*(n))
> +#define TSI108_INT_GTDR(n) (0x3C + 0x10*(n))
> +#define TSI108_INT_IVPR(n) (0x100 + 0x8*(n))
> +#define TSI108_INT_IDR(n) (0x104 + 0x8*(n))
> +#define TSI108_INT_DVPR(n) (0x204 + 0xC*(n))
> +#define TSI108_INT_DDR(n) (0x208 + 0xC*(n))
> +#define TSI108_INT_MBVPR(n) (0x284 + 0x10*(n))
> +#define TSI108_INT_MBDR(n) (0x288 + 0x10*(n))
> +
> +/* Registers controlling destinations */
> +#define TSI108_INT_TASKP(n) (0x300 + 0x40*(n))
> +#define TSI108_INT_VECTOR(n) (0x304 + 0x40*(n))
> +#define TSI108_INT_EOI(n) (0x308 + 0x40*(n))
> +#define TSI108_INT_CSR(n) (0x30C + 0x40*(n))
> +
> +/*
> + * Generic definitions common for different types of interrupt
> + * sources.
> + */
> +
> +#define TSI108PIC_MASK (0x80000000)
> +#define TSI108PIC_ACTIVITY (0x40000000)
> +#define TSI108PIC_PRIORITY_MASK (0x000f0000)
> +#define TSI108PIC_VECTOR_MASK (0x000000ff)
> +
> +/**********************************************************
> + * Register Bit Masks definitions for every register
> + */
> +
> +/* TSI108PIC_INT_FRR : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_FRR_VID (0x000000ff)
> +#define TSI108PIC_INT_FRR_NCPU (0x00001f00)
> +#define TSI108PIC_INT_FRR_NITM (0x0000e000)
> +#define TSI108PIC_INT_FRR_NIRQ (0x07ff0000)
> +#define TSI108PIC_INT_FRR_NIDOOR (0xe0000000)
> +#define TSI108PIC_INT_FRR_RESERVED (0x18000000)
> +
> +/* TSI108PIC_INT_GCR : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_GCR_R (0x80000000)
> +#define TSI108PIC_INT_GCR_RESERVED (0x7fffffff)
> +
> +/* TSI108PIC_INT_ICR : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_ICR_R (0x0000000f)
> +#define TSI108PIC_INT_ICR_RESERVED (0xfffffff0)
> +
> +/* TSI108PIC_INT_MVI : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_MVI_VID (0x000000ff)
> +#define TSI108PIC_INT_MVI_DID (0x0000ff00)
> +#define TSI108PIC_INT_MVI_STEP (0x00ff0000)
> +#define TSI108PIC_INT_MVI_RESERVED (0xff000000)
> +
> +/* TSI108PIC_INT_SVR : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_SVR_VECTOR (0x000000ff)
> +#define TSI108PIC_INT_SVR_RESERVED (0xffffff00)
> +
> +/* TSI108PIC_INT_TFRR : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_TFRR_TIME_FREQ (0xffffffff)
> +
> +/* TSI108PIC_INT_SOFT_SET : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_SOFT_SET_S (0x00ffffff)
> +#define TSI108PIC_INT_SOFT_SET_RESERVED (0xff000000)
> +
> +/* TSI108PIC_INT_SOFT_ENABLE : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_SOFT_ENABLE_EN (0x00ffffff)
> +#define TSI108PIC_INT_SOFT_ENABLE_RESERVED (0xff000000)
> +
> +/* TSI108PIC_INT_GTCCR(X) : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_GTCCR_COUNT (0x7fffffff)
> +#define TSI108PIC_INT_GTCCR_T (0x80000000)
> +
> +/* TSI108PIC_INT_GTBCR(X) : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_GTBCR_B_COUNT (0x7fffffff)
> +#define TSI108PIC_INT_GTBCR_CI (0x80000000)
> +
> +/* TSI108PIC_INT_GTVPR(X) : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_GTVPR_VECTOR (0x000000ff)
> +#define TSI108PIC_INT_GTVPR_PRIORITY (0x000f0000)
> +#define TSI108PIC_INT_GTVPR_PRESCALE (0x00f00000)
> +#define TSI108PIC_INT_GTVPR_A (0x40000000)
> +#define TSI108PIC_INT_GTVPR_M (0x80000000)
> +#define TSI108PIC_INT_GTVPR_RESERVED (0x3f00ff00)
> +
> +/* TSI108PIC_INT_GTDR(X) : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_GTDR_SEL_OUT (0x0000000f)
> +#define TSI108PIC_INT_GTDR_RESERVED (0xfffffff0)
> +
> +/* TSI108PIC_INT_IVPR(X) : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_IVPR_VECTOR (0x000000ff)
> +#define TSI108PIC_INT_IVPR_PRIORITY (0x000f0000)
> +
> +#define TSI108PIC_INT_IVPR_P (0x01000000)
> +#define TSI108PIC_INT_IVPR_P_LOW (0 << 24)
> +#define TSI108PIC_INT_IVPR_P_HIGH (1 << 24)
> +
> +#define TSI108PIC_INT_IVPR_S (0x02000000)
> +#define TSI108PIC_INT_IVPR_S_EDGE (0 << 25)
> +#define TSI108PIC_INT_IVPR_S_LEVEL (1 << 25)
> +
> +#define TSI108PIC_INT_IVPR_MODE (0x20000000)
> +#define TSI108PIC_INT_IVPR_A (0x40000000)
> +#define TSI108PIC_INT_IVPR_M (0x80000000)
> +#define TSI108PIC_INT_IVPR_RESERVED (0x1cf0ff00)
> +
> +/* TSI108PIC_INT_IDR(X) : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_IDR_SEL_OUT (0x0000000f)
> +#define TSI108PIC_INT_IDR_RESERVED (0xfffffff0)
> +
> +/* TSI108PIC_INT_DAR : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_DAR_A (0x0000000f)
> +#define TSI108PIC_INT_DAR_RESERVED (0xfffffff0)
> +
> +/* TSI108PIC_INT_DVPR(X) : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_DVPR_VECTOR (0x000000ff)
> +#define TSI108PIC_INT_DVPR_PRIORITY (0x000f0000)
> +#define TSI108PIC_INT_DVPR_A (0x40000000)
> +#define TSI108PIC_INT_DVPR_M (0x80000000)
> +#define TSI108PIC_INT_DVPR_RESERVED (0x3ff0ff00)
> +
> +/* TSI108PIC_INT_DDR(X) : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_DDR_SEL_OUT (0x0000000f)
> +#define TSI108PIC_INT_DDR_RESERVED (0xfffffff0)
> +
> +/* TSI108PIC_INT_DMR(X) : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_DMR_M (0xffffffff)
> +
> +/* TSI108PIC_INT_MBR(X) : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_MBR_M (0xffffffff)
> +
> +/* TSI108PIC_INT_MBVPR(X) : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_MBVPR_VECTOR (0x000000ff)
> +#define TSI108PIC_INT_MBVPR_PRIORITY (0x000f0000)
> +#define TSI108PIC_INT_MBVPR_A (0x40000000)
> +#define TSI108PIC_INT_MBVPR_M (0x80000000)
> +#define TSI108PIC_INT_MBVPR_RESERVED (0x3ff0ff00)
> +
> +/* TSI108PIC_INT_MBDR(X) : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_MBDR_SEL_OUT (0x0000000f)
> +#define TSI108PIC_INT_MBDR_RESERVED (0xfffffff0)
> +
> +/* TSI108PIC_INT_TASKP(X) : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_TASKP_TASKP (0x0000000f)
> +#define TSI108PIC_INT_TASKP_RESERVED (0xfffffff0)
> +
> +/* TSI108PIC_INT_VECTOR(X) : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_VECTOR_VECTOR (0x000000ff)
> +#define TSI108PIC_INT_VECTOR_LS_VECTOR (0xff000000)
> +#define TSI108PIC_INT_VECTOR_RESERVED (0x00ffff00)
> +
> +/* TSI108PIC_INT_EOI(X) : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_EOI_EOI (0x000000ff)
> +#define TSI108PIC_INT_EOI_RESERVED (0xffffff00)
> +
> +/* TSI108PIC_INT_CSR(X) : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_CSR_RESERVED (0xfffffffc)
> +
> +#define TSI108PIC_INT_CSR_P (1 << 0)
> +#define TSI108PIC_INT_CSR_P_LOW (0 << 0)
> +#define TSI108PIC_INT_CSR_P_HIGH (1 << 0)
> +
> +#define TSI108PIC_INT_CSR_S (1 << 1)
> +#define TSI108PIC_INT_CSR_S_EDGE (0 << 1)
> +#define TSI108PIC_INT_CSR_S_LEVEL (1 << 1)
> +
> +extern void tsi108_pic_init(u_char * board_init_senses);
> +extern void tsi108_pic_reset(void);
> +extern void tsi108_pic_set_output(int dest_num, u32 sense, u32
> polarity);
> +extern int tsi108_pic_source_cfg(int src_num, u32 sense,
> + u32 polarity, TSI108_IRQ_MODE mode);
> +extern int tsi108_pic_set_vector(int src_num, int vect, int prio);
> +extern void tsi108_pic_init_nmi_irq(u_int irq);
> +extern void tsi108_pic_hookup_cascade(u_int irq, char *name,
> + int (*cascade_fn) (struct pt_regs *));
> +extern int tsi108_pic_get_irq(struct pt_regs *regs);
> +
> +#endif /* __KERNEL__ */
> +
> +#endif /* _LINUX_TSI108_PIC_H */
> --
> 1.3.0
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@ozlabs.org
> https://ozlabs.org/mailman/listinfo/linuxppc-dev
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH/2.6.17-rc4 4/10]Powerpc: Add tsi108 pic support
2006-05-17 10:14 Zang Roy-r61911
2006-05-17 16:05 ` Kumar Gala
@ 2006-05-18 0:52 ` Benjamin Herrenschmidt
1 sibling, 0 replies; 16+ messages in thread
From: Benjamin Herrenschmidt @ 2006-05-18 0:52 UTC (permalink / raw)
To: Zang Roy-r61911
Cc: linuxppc-dev list, Yang Xin-Xin-r48390, Paul Mackerras,
Alexandre.Bounine
On Wed, 2006-05-17 at 18:14 +0800, Zang Roy-r61911 wrote:
> Add Tundra Semiconductor tsi108 host bridge interrupt controller support.
It looks a bit like an hacked up MPIC... Is it different enough to
justify a separate driver ? Or would it be possible to define a TSI108
flag to pass the current mpic driver and add the necessary bits to it ?
> Signed-off-by: Alexandre Bounine <alexandreb@tundra.com>
> Signed-off-by: Roy Zang <tie-fei.zang@freescale.com>
>
> ---
>
> arch/powerpc/sysdev/tsi108_pic.c | 813 ++++++++++++++++++++++++++++++++++++++
> include/asm-powerpc/tsi108_pic.h | 232 +++++++++++
> 2 files changed, 1045 insertions(+), 0 deletions(-)
> create mode 100644 arch/powerpc/sysdev/tsi108_pic.c
> create mode 100644 include/asm-powerpc/tsi108_pic.h
>
> 7d23f6a0984cd54ca787f880a57067330900abe8
> diff --git a/arch/powerpc/sysdev/tsi108_pic.c b/arch/powerpc/sysdev/tsi108_pic.c
> new file mode 100644
> index 0000000..bbca587
> --- /dev/null
> +++ b/arch/powerpc/sysdev/tsi108_pic.c
> @@ -0,0 +1,813 @@
> +/*
> + * (C) Copyright 2005 Tundra Semiconductor Corp.
> + * Alex Bounine, <alexandreb@tundra.com).
> + *
> + * See file CREDITS for list of people who contributed to this
> + * project.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 of
> + * the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
> + * MA 02111-1307 USA
> + */
> +
> +/*
> + * Tsi108 Interrupt Controller Handling
> + */
> +
> +#include <linux/config.h>
> +#include <linux/types.h>
> +#include <linux/kernel.h>
> +#include <linux/sched.h>
> +#include <linux/init.h>
> +#include <linux/irq.h>
> +#include <linux/interrupt.h>
> +#include <linux/sysdev.h>
> +#include <asm/ptrace.h>
> +#include <asm/signal.h>
> +#include <asm/io.h>
> +#include <asm/irq.h>
> +#include <asm/prom.h>
> +#include <asm/sections.h>
> +#include <asm/hardirq.h>
> +#include <asm/machdep.h>
> +
> +#include <asm/tsi108.h>
> +#include <asm/tsi108_irq.h>
> +#include <asm/tsi108_pic.h>
> +#undef DEBUG
> +
> +#ifdef DEBUG
> +#define DBG(fmt...) do { printk(fmt); } while(0)
> +#else
> +#define DBG(fmt...) do { } while(0)
> +#endif
> +
> +extern u32 get_vir_csrbase(void);
> +extern u32 tsi108_read_reg(u32 reg_offset);
> +extern void tsi108_write_reg(u32 reg_offset, u32 val);
> +
> +static phys_addr_t tsi108_pic_phy_addr;
> +static u32 tsi108_pic_vir_addr;
> +
> +static int tsi108_pic_cascade_irq = -1;
> +static int (*tsi108_pic_cascade_fn) (struct pt_regs *);
> +
> +/* Global Operations */
> +static void tsi108_pic_set_task_priority(u_int pri);
> +static void tsi108_pic_set_spurious(u_int vector);
> +void tsi108_pic_mask_all(void);
> +
> +/* Timer Interrupts */
> +static void tsi108_pic_inittimer(u_int timer, u_int pri, u_int vector);
> +static void tsi108_pic_maptimer(u_int timer, u_int cpumask);
> +
> +/* Interrupt Sources */
> +static void tsi108_pic_enable_irq(u_int irq);
> +static void tsi108_pic_disable_irq(u_int irq);
> +static void tsi108_pic_initirq(u_int irq, u_int pri, u_int vector, int polarity,
> + int is_level);
> +static void tsi108_pic_mapirq(u_int irq, u_int cpumask, u_int keepmask);
> +static void init_pci_source(void);
> +static inline int get_pci_source(int vector);
> +int tsi108_pic_set_irq_sense(int irq, int pol, int sense);
> +
> +/*
> + * tsi108_pic interface routines
> + */
> +static void tsi108_pic_end_irq(unsigned int irq_nr);
> +static void tsi108_pic_ack_irq(unsigned int irq_nr);
> +void tsi108_pic_set_affinity(unsigned int irq_nr, unsigned long cpumask);
> +
> +static struct hw_interrupt_type tsi108_pic_irq = {
> + "tsi108_pic",
> + NULL,
> + NULL,
> + tsi108_pic_enable_irq,
> + tsi108_pic_disable_irq,
> + tsi108_pic_ack_irq,
> + tsi108_pic_end_irq,
> + NULL
> +};
> +
> +static void tsi108_pci_irq_enable(u_int irq);
> +static void tsi108_pci_irq_disable(u_int irq);
> +static void tsi108_pci_irq_ack(u_int irq);
> +static void tsi108_pci_irq_end(u_int irq);
> +
> +static struct hw_interrupt_type tsi108_pci_irq = {
> + "tsi108_PCI_int",
> + NULL,
> + NULL,
> + tsi108_pci_irq_enable,
> + tsi108_pci_irq_disable,
> + tsi108_pci_irq_ack,
> + tsi108_pci_irq_end,
> + NULL
> +};
> +
> +#ifdef DBG_TSI108_INTERRUPT
> +#define ASSERT(expr) if (!(expr)) { \
> + printk("tsi108pic :" \
> + "assertion failed! %s[%d]: %s\n", \
> + __FUNCTION__, __LINE__, #expr); \
> + dump_stack(); \
> + }
> +#else
> +#define ASSERT(expr) do {} while (0)
> +#endif
> +
> +static inline u_int get_vector_offset(u_int irq)
> +{
> + u_int offset;
> +
> + if (irq < TSI108_IRQ_BASE || irq >= IRQ_PCI_INTAD_BASE)
> + return 0;
> +
> + if (irq < IRQ_TSI108_MBOX0)
> + offset = TSI108_INT_IVPR(irq - TSI108_IRQ_BASE);
> + else if (irq < IRQ_TSI108_DBELL0)
> + offset = TSI108_INT_MBVPR(irq - IRQ_TSI108_MBOX0);
> + else if (irq < IRQ_TSI108_TIMER0)
> + offset = TSI108_INT_DVPR(irq - IRQ_TSI108_DBELL0);
> + else
> + offset = TSI108_INT_GTVPR(irq - IRQ_TSI108_TIMER0);
> +
> + return offset;
> +}
> +
> +static inline u_int tsi108_pic_read_reg(u_int reg_offset)
> +{
> + return in_be32((volatile u32 *)(tsi108_pic_vir_addr + reg_offset));
> +}
> +
> +static inline void tsi108_pic_write_reg(u_int reg_offset, u_int val)
> +{
> + out_be32((volatile u32 *)(tsi108_pic_vir_addr + reg_offset), val);
> +}
> +
> +void tsi108_pic_reset(void)
> +{
> + tsi108_pic_write_reg(TSI108_INT_GCR, TSI108PIC_INT_GCR_R);
> + while (tsi108_pic_read_reg(TSI108_INT_GCR) & TSI108PIC_INT_GCR_R)
> + mb();
> +}
> +
> +void tsi108_pic_set_output(int dest_num, u32 sense, u32 polarity)
> +{
> + u32 temp = 0;
> + temp |= (IRQ_SENSE_LEVEL == sense) ?
> + (TSI108PIC_INT_CSR_S_LEVEL) : (TSI108PIC_INT_CSR_S_EDGE);
> + temp |= (IRQ_POLARITY_POSITIVE == polarity) ?
> + (TSI108PIC_INT_CSR_P_HIGH) : (TSI108PIC_INT_CSR_P_LOW);
> + tsi108_pic_write_reg(TSI108_INT_CSR(dest_num), temp);
> + mb();
> +}
> +
> +int tsi108_pic_source_cfg(int src_num, /* interrupt source number */
> + u32 sense, /* interrupt source Sense */
> + u32 polarity, /* interrupt source Polarity */
> + TSI108_IRQ_MODE mode /* interrupt delivery Mode */
> + )
> +{
> + unsigned temp;
> +
> + temp = tsi108_pic_read_reg(TSI108_INT_IVPR(src_num));
> +
> + if (temp & TSI108PIC_ACTIVITY) /* error if source is active */
> + return -1;
> +
> + if (0 == (temp & TSI108PIC_MASK)) {
> + temp |= TSI108PIC_MASK; /* mask IRQ prior making changes */
> + tsi108_pic_write_reg(TSI108_INT_IVPR(src_num), temp);
> + }
> +
> + temp &= ~(TSI108PIC_INT_IVPR_MODE |
> + TSI108PIC_INT_IVPR_S | TSI108PIC_INT_IVPR_P);
> +
> + temp |= (IRQ_SENSE_LEVEL == sense) ?
> + (TSI108PIC_INT_CSR_S_LEVEL) : (TSI108PIC_INT_CSR_S_EDGE);
> + temp |= (IRQ_POLARITY_POSITIVE == polarity) ?
> + (TSI108PIC_INT_CSR_P_HIGH) : (TSI108PIC_INT_CSR_P_LOW);
> +
> + tsi108_pic_write_reg(TSI108_INT_IVPR(src_num),
> + TSI108PIC_MASK | (mode << 29) | temp);
> + return (0);
> +}
> +
> +int tsi108_pic_set_vector(int src_num, /* source number */
> + int vect, /* vector number */
> + int prio /* interrupt source priority */
> + )
> +{
> + unsigned tmp;
> +
> + tmp = tsi108_pic_read_reg(TSI108_INT_IVPR(src_num));
> +
> + if (tmp & TSI108PIC_ACTIVITY) /* error if source is active */
> + return -1;
> +
> + if (0 == (tmp & TSI108PIC_MASK)) {
> + tmp |= TSI108PIC_MASK; /* mask IRQ prior making changes */
> + tsi108_pic_write_reg(TSI108_INT_IVPR(src_num), tmp);
> + }
> +
> + /* clear bits to be changed */
> + tmp &= ~(TSI108PIC_VECTOR_MASK | TSI108PIC_PRIORITY_MASK);
> +
> + tmp |= (prio << 16) | vect;
> + tsi108_pic_write_reg(TSI108_INT_IVPR(src_num), tmp);
> + return 0;
> +}
> +
> +void tsi108_pic_mask_all()
> +{
> + int i;
> + unsigned int vp;
> +
> + /* Mask all external and internal interrupt sources */
> + for (i = 0; i < TSI108PIC_MAX_SOURCES; i++) {
> + vp = tsi108_pic_read_reg(TSI108_INT_IVPR(i));
> + tsi108_pic_write_reg(TSI108_INT_IVPR(i), vp | TSI108PIC_MASK);
> + mb();
> +
> + /* Make sure that irq is masked */
> + do {
> + vp = tsi108_pic_read_reg(TSI108_INT_IVPR(i));
> + } while ((vp & TSI108PIC_ACTIVITY) && !(vp & TSI108PIC_MASK));
> + }
> +
> + /* Mask all timer interrupts */
> + for (i = 0; i < TSI108PIC_NUM_TIMERS; i++) {
> + vp = tsi108_pic_read_reg(TSI108_INT_GTVPR(i));
> + tsi108_pic_write_reg(TSI108_INT_GTVPR(i), vp | TSI108PIC_MASK);
> + mb();
> +
> + do {
> + vp = tsi108_pic_read_reg(TSI108_INT_GTVPR(i));
> + } while ((vp & TSI108PIC_ACTIVITY) && !(vp & TSI108PIC_MASK));
> + }
> +
> + /* Mask all doorbell interrupts */
> + for (i = 0; i < TSI108PIC_NUM_DBELLS; i++) {
> + vp = tsi108_pic_read_reg(TSI108_INT_DVPR(i));
> + tsi108_pic_write_reg(TSI108_INT_IVPR(i), vp | TSI108PIC_MASK);
> + mb();
> +
> + do {
> + vp = tsi108_pic_read_reg(TSI108_INT_DVPR(i));
> + } while ((vp & TSI108PIC_ACTIVITY) && !(vp & TSI108PIC_MASK));
> + }
> +
> + /* Mask all mailbox interrupts */
> + for (i = 0; i < 4; i++) {
> + vp = tsi108_pic_read_reg(TSI108_INT_MBVPR(i));
> + tsi108_pic_write_reg(TSI108_INT_MBVPR(i), vp | TSI108PIC_MASK);
> + mb();
> +
> + do {
> + vp = tsi108_pic_read_reg(TSI108_INT_MBVPR(i));
> + } while ((vp & TSI108PIC_ACTIVITY) && !(vp & TSI108PIC_MASK));
> + }
> +}
> +
> +/*
> + * The Tsi108 PC initialization routine.
> + * A caller routine (usually from platform-specific code has to provide
> + * sense/polarity configuration information for four external interrupt
> + * sources INT0 - INT3. This should be done in form of four-byte array
> + * (one byte per source ) that contains combination of sensitivity/polarity
> + * flags defined in asm-ppc/irq.h.
> + *
> + * Example of PIC initialization call is shown below:
> + *
> + * u_char your_board_pic_initsenses[] __initdata = {
> + * (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), // INT[0]
> + * (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), // INT[1]
> + * (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), // INT[2]
> + * (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE) // INT[3]
> + * };
> + *
> + * tsi108_pic_init(your_board_pic_initsenses);
> + */
> +
> +void __init tsi108_pic_init(u_char * board_init_senses)
> +{
> + u_int i;
> + u32 sense;
> +
> + struct device_node *tsi_pic;
> + tsi_pic = of_find_node_by_type(NULL, "open-pic");
> + if (tsi_pic) {
> + unsigned int size;
> + void *prop = get_property(tsi_pic, "reg", &size);
> + tsi108_pic_phy_addr = of_translate_address(tsi_pic, prop);
> + }
> +
> + DBG("%s: Tsi108 pic phy addr = 0x%x\n", __FUNCTION__,
> + (u32) tsi108_pic_phy_addr);
> + if (tsi108_pic_phy_addr == 0) {
> + printk("No tsi108 PIC found !\n");
> + return;
> + }
> +
> + tsi108_pic_vir_addr = (u32) ioremap(tsi108_pic_phy_addr, 0x400);
> +
> + tsi108_pic_reset();
> +
> + if (ppc_md.progress)
> + ppc_md.progress("tsi108_pic_init: enter", 0x122);
> +
> + /* Initialize timer interrupts */
> + for (i = 0; i < TSI108PIC_NUM_TIMERS; i++) {
> + /* Disabled, Priority 0 */
> + tsi108_pic_inittimer(i, 0, IRQ_TSI108_TIMER0 + i);
> + /* No processor */
> + tsi108_pic_maptimer(i, 0);
> + }
> +
> + /* Init board-specific external sources */
> + for (i = 0; i < 4; i++) {
> + sense = board_init_senses[i];
> +
> + if (sense & IRQ_SENSE_MASK)
> + irq_desc[TSI108_IRQ(i)].status |= IRQ_LEVEL;
> +
> + /* Enabled, Priority 8 */
> + tsi108_pic_initirq(i, 8, TSI108_IRQ(i),
> + (sense & IRQ_POLARITY_MASK),
> + (sense & IRQ_SENSE_MASK));
> + /* Map to CPU #0 */
> + tsi108_pic_mapirq(TSI108_IRQ(i), 1 << 0, 0);
> + }
> +
> + /* Init remaining internal sources. */
> + for (; i < TSI108PIC_MAX_SOURCES; i++) {
> + /* Disabled, Priority 8, by default - Positive Edge */
> + tsi108_pic_initirq(i, 8, TSI108_IRQ(i),
> + IRQ_POLARITY_POSITIVE, IRQ_SENSE_EDGE);
> + /* Map to CPU #0 */
> + tsi108_pic_mapirq(TSI108_IRQ(i), (1 << 0), 0);
> + }
> +
> + /*
> + * Change sensitivity to level for sources that require it.
> + */
> +
> + irq_desc[IRQ_TSI108_GIGE0].status |= IRQ_LEVEL;
> + irq_desc[IRQ_TSI108_GIGE1].status |= IRQ_LEVEL;
> + irq_desc[IRQ_TSI108_PCI].status |= IRQ_LEVEL;
> +
> + /* Init descriptors */
> + for (i = 0; i < TSI108PIC_MAX_SOURCES; i++)
> + irq_desc[i + TSI108_IRQ_BASE].handler = &tsi108_pic_irq;
> +
> + for (i = 0; i < NUM_PCI_IRQS; i++) {
> + irq_desc[i + IRQ_PCI_INTAD_BASE].handler = &tsi108_pci_irq;
> + irq_desc[i + IRQ_PCI_INTAD_BASE].status |= IRQ_LEVEL;
> + }
> +
> + /* Initialize the spurious interrupt */
> + tsi108_pic_set_spurious(TSI108_IRQ_SPURIOUS);
> + tsi108_pic_set_task_priority(0);
> +
> + init_pci_source();
> + tsi108_pic_enable_irq(IRQ_TSI108_PCI);
> +
> + i = tsi108_pic_read_reg(TSI108_INT_VECTOR(0));
> + tsi108_pic_write_reg(TSI108_INT_EOI(0), 0);
> +
> + if (ppc_md.progress)
> + ppc_md.progress("tsi108_pic_init: exit", 0x222);
> +}
> +
> +/*
> + * Find out the current interrupt
> + */
> +static u_int tsi108_pic_get_vect(void)
> +{
> + u_int vec;
> +
> + vec = tsi108_pic_read_reg(TSI108_INT_VECTOR(0)) & TSI108PIC_VECTOR_MASK;
> +
> +#ifdef DBG_TSI108_INTERRUPT
> + if (vec == TSI108_IRQ_SPURIOUS)
> + printk("TSI108: SPURIOUS vec=0x%08x\n", vec);
> + else
> + printk("TSI108: read vec=0x%08x\n", vec);
> +#endif
> + return (vec);
> +}
> +
> +static inline void tsi108_pic_eoi(void)
> +{
> + tsi108_pic_write_reg(TSI108_INT_EOI(0), 0);
> + mb();
> +}
> +
> +static void __init tsi108_pic_set_task_priority(u_int pri)
> +{
> + ASSERT(pri >= 0 && pri < TSI108PIC_NUM_PRI);
> +
> + tsi108_pic_write_reg(TSI108_INT_TASKP(0),
> + pri & TSI108PIC_INT_TASKP_TASKP);
> + mb();
> +}
> +
> +static void tsi108_pic_set_spurious(u_int vec)
> +{
> + ASSERT(vec == TSI108_IRQ_SPURIOUS);
> + tsi108_pic_write_reg(TSI108_INT_SVR, vec);
> + mb();
> +}
> +
> +#ifdef CONFIG_SMP
> +/*
> + * Convert a cpu mask from logical to physical cpu numbers.
> + */
> +static inline u32 physmask(u32 cpumask)
> +{
> + int i;
> + u32 mask = 0;
> +
> + for (i = 0; i < NR_CPUS; ++i, cpumask >>= 1)
> + if (cpu_online(i))
> + mask |= (cpumask & 1) << smp_hw_index[i];
> + return mask;
> +}
> +#else
> +#define physmask(cpumask) (cpumask)
> +#endif
> +
> +/*
> + * Initialize a timer interrupt (and disable it)
> + *
> + * timer: timer number
> + * pri: interrupt source priority
> + * vec: the vector it will produce
> + */
> +static void __init tsi108_pic_inittimer(u_int timer, u_int pri, u_int vec)
> +{
> + unsigned int gtvpr;
> +
> + ASSERT(timer >= 0 && timer < TSI108PIC_NUM_TIMERS);
> + ASSERT(pri >= 0 && pri < TSI108PIC_NUM_PRI);
> + ASSERT(vec >= 0 && vec < TSI108PIC_NUM_VECTORS);
> +
> + gtvpr = tsi108_pic_read_reg(TSI108_INT_GTVPR(timer));
> + gtvpr &= ~(TSI108PIC_PRIORITY_MASK | TSI108PIC_VECTOR_MASK);
> + gtvpr |= (pri << 16) | vec;
> + tsi108_pic_write_reg(TSI108_INT_GTVPR(timer), gtvpr | TSI108PIC_MASK);
> + mb();
> +}
> +
> +/*
> + * Map a timer interrupt to one or more CPUs
> + */
> +static void __init tsi108_pic_maptimer(u_int timer, u_int cpumask)
> +{
> + ASSERT(timer >= 0 && timer < TSI108PIC_NUM_TIMERS);
> +
> + tsi108_pic_write_reg(TSI108_INT_GTDR(timer), physmask(cpumask));
> + mb();
> +}
> +
> +/*
> + * Initalize the interrupt source which will generate an NMI.
> + * This raises the interrupt's priority from 8 to 9.
> + *
> + * irq: The logical IRQ which generates an NMI.
> + */
> +void __init tsi108_pic_init_nmi_irq(u_int irq)
> +{
> + u_int offset = get_vector_offset(irq);
> + u_int vpr = tsi108_pic_read_reg(offset);
> + vpr &= ~TSI108PIC_PRIORITY_MASK;
> + tsi108_pic_write_reg(offset, vpr | (9 << 16));
> + mb();
> +}
> +
> +/*
> + *
> + * All functions below take an offset'ed irq argument
> + *
> + */
> +
> +/*
> + * Hookup a cascade to the tsi108 PIC.
> + */
> +void __init
> +tsi108_pic_hookup_cascade(u_int irq, char *name,
> + int (*cascade_fn) (struct pt_regs *))
> +{
> + tsi108_pic_cascade_irq = irq;
> + tsi108_pic_cascade_fn = cascade_fn;
> + if (request_irq(irq, no_action, SA_INTERRUPT, name, NULL))
> + printk("Unable to get Tsi108 PIC IRQ %d for cascade\n",
> + irq - TSI108_IRQ_BASE);
> +}
> +
> +/*
> + * Enable/disable an external interrupt source
> + *
> + * Externally called, irq is an offseted system-wide interrupt number
> + */
> +static void tsi108_pic_enable_irq(u_int irq)
> +{
> + u32 offset = get_vector_offset(irq);
> + u32 vpr = tsi108_pic_read_reg(offset);
> +
> + /*
> + * Undo sensitivity change (see tsi108_pic_disable_irq())
> + */
> + if (irq_desc[irq].status & IRQ_LEVEL)
> + vpr |= TSI108PIC_INT_IVPR_S;
> +
> + tsi108_pic_write_reg(offset, vpr & ~TSI108PIC_MASK);
> + mb();
> +}
> +
> +static void tsi108_pic_disable_irq(u_int irq)
> +{
> + u32 offset = get_vector_offset(irq);
> + u32 vpr = tsi108_pic_read_reg(offset);
> +
> + /*
> + * Switch level interrupt to edge sensitivity to avoid generation
> + * of spurious interrupt request. See design note in Tsi108 PIC
> + * section of Tsi108 manual.
> + */
> + if (irq_desc[irq].status & IRQ_LEVEL)
> + vpr &= ~TSI108PIC_INT_IVPR_S;
> +
> + tsi108_pic_write_reg(offset, vpr | TSI108PIC_MASK);
> + mb();
> + vpr = tsi108_pic_read_reg(offset);
> + if (!(vpr & TSI108PIC_MASK))
> + printk("TSI108_PIC: Error - Unable disable IRQ %d\n", irq);
> +}
> +
> +/*
> + * Initialize an interrupt source (and disable it!)
> + *
> + * irq: Tsi108 PIC interrupt source number
> + * pri: interrupt source priority
> + * vec: the vector it will produce
> + * pol: polarity (1 for positive, 0 for negative)
> + * sense: 1 for level, 0 for edge
> + */
> +static void __init
> +tsi108_pic_initirq(u_int irq, u_int pri, u_int vec, int pol, int sense)
> +{
> + unsigned int ivpr;
> +
> + ivpr = TSI108PIC_MASK | (pri << 16) | vec;
> + ivpr |= (IRQ_SENSE_LEVEL == sense) ?
> + TSI108PIC_INT_IVPR_S_LEVEL : TSI108PIC_INT_IVPR_S_EDGE;
> + ivpr |= (IRQ_POLARITY_POSITIVE == pol) ?
> + TSI108PIC_INT_IVPR_P_HIGH : TSI108PIC_INT_IVPR_P_LOW;
> + tsi108_pic_write_reg(TSI108_INT_IVPR(irq), ivpr);
> + mb();
> +}
> +
> +int tsi108_pic_set_irq_sense(int irq, /* PIC source number */
> + int pol, /* interrupt source polarity */
> + int sense /* interrupt source sense */
> + )
> +{
> + unsigned int ivpr;
> +
> + ivpr = tsi108_pic_read_reg(TSI108_INT_IVPR(irq));
> +
> + if (ivpr & TSI108PIC_ACTIVITY) /* error if source is active */
> + return -1;
> +
> + if (0 == (ivpr & TSI108PIC_MASK)) {
> + ivpr |= TSI108PIC_MASK; /* mask IRQ prior making changes */
> + tsi108_pic_write_reg(TSI108_INT_IVPR(irq), ivpr);
> + }
> +
> + /* clear bits to be changed */
> + ivpr &= ~(TSI108PIC_INT_IVPR_P | TSI108PIC_INT_IVPR_S);
> +
> + ivpr |= (IRQ_SENSE_LEVEL == sense) ?
> + TSI108PIC_INT_IVPR_S_LEVEL : TSI108PIC_INT_IVPR_S_EDGE;
> + ivpr |= (IRQ_POLARITY_POSITIVE == pol) ?
> + TSI108PIC_INT_IVPR_P_HIGH : TSI108PIC_INT_IVPR_P_LOW;
> +
> + tsi108_pic_write_reg(TSI108_INT_IVPR(irq), ivpr);
> + return 0;
> +}
> +
> +/*
> + * Map an interrupt source to one or more CPUs
> + */
> +static void tsi108_pic_mapirq(u_int irq, u_int physmask, u_int keepmask)
> +{
> + u_int offset = get_vector_offset(irq);
> +
> + if (0 == offset)
> + return;
> + if (keepmask != 0)
> + physmask |= tsi108_pic_read_reg(offset + 4);
> + tsi108_pic_write_reg(offset + 4, physmask);
> + mb();
> +}
> +
> +/* No spinlocks, should not be necessary with the Tsi108 PIC
> + * (1 register = 1 interrupt and we have the desc lock).
> + */
> +static void tsi108_pic_ack_irq(unsigned int irq_nr)
> +{
> + if ((irq_desc[irq_nr].status & IRQ_LEVEL) == 0)
> + tsi108_pic_eoi();
> +}
> +
> +static void tsi108_pic_end_irq(unsigned int irq_nr)
> +{
> + if ((irq_desc[irq_nr].status & IRQ_LEVEL) != 0)
> + tsi108_pic_eoi();
> +}
> +
> +void tsi108_pic_set_affinity(unsigned int irq_nr, unsigned long cpumask)
> +{
> + tsi108_pic_mapirq(irq_nr, physmask(cpumask), 0);
> +}
> +
> +int tsi108_pic_get_irq(struct pt_regs *regs)
> +{
> + int vector = tsi108_pic_get_vect();
> +
> + if (vector == TSI108_IRQ_SPURIOUS) {
> + vector = -1;
> + }
> +
> + if (vector == IRQ_TSI108_PCI) {
> + vector = get_pci_source(vector);
> + }
> +
> + if (vector == -1) {
> + tsi108_pic_write_reg(TSI108_INT_EOI(0), 0);
> + }
> +
> + return vector;
> +}
> +
> +static void tsi108_pci_int_mask(u_int irq)
> +{
> + u_int irp_cfg;
> + int int_line = (irq - IRQ_PCI_INTAD_BASE);
> +
> + irp_cfg = tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL);
> + mb();
> + irp_cfg |= (1 << int_line); /* INTx_DIR = output */
> + irp_cfg &= ~(3 << (8 + (int_line * 2))); /* INTx_TYPE = unused */
> + tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL, irp_cfg);
> + mb();
> + irp_cfg = tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL);
> +}
> +
> +static void tsi108_pci_int_unmask(u_int irq)
> +{
> + u_int irp_cfg;
> + int int_line = (irq - IRQ_PCI_INTAD_BASE);
> +
> + irp_cfg = tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL);
> + mb();
> + irp_cfg &= ~(1 << int_line);
> + irp_cfg |= (3 << (8 + (int_line * 2)));
> + tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL, irp_cfg);
> + mb();
> +}
> +
> +static void tsi108_pci_irq_enable(u_int irq)
> +{
> + tsi108_pci_int_unmask(irq);
> +}
> +
> +static void tsi108_pci_irq_disable(u_int irq)
> +{
> + tsi108_pci_int_mask(irq);
> +}
> +
> +static void tsi108_pci_irq_ack(u_int irq)
> +{
> + tsi108_pci_int_mask(irq);
> +}
> +
> +static void tsi108_pci_irq_end(u_int irq)
> +{
> + tsi108_pic_eoi(); /* eoi IRQ_TSI108_PCI */
> + tsi108_pci_int_unmask(irq);
> +}
> +
> +static inline int get_pci_source(int vector)
> +{
> + u_int temp = 0;
> + int irq = -1;
> + int i;
> + u_int pci_irp_stat;
> + static int mask = 0;
> +
> + /* Read PCI/X block interrupt status register */
> + pci_irp_stat = tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_STAT);
> + mb();
> +
> + if (pci_irp_stat & TSI108_PCI_IRP_STAT_P_INT) {
> + /* Process Interrupt from PCI bus INTA# - INTD# lines */
> + temp =
> + tsi108_read_reg(TSI108_PCI_OFFSET +
> + TSI108_PCI_IRP_INTAD) & 0xf;
> + mb();
> + for (i = 0; i < 4; i++, mask++) {
> + if (temp & (1 << mask % 4)) {
> + irq = IRQ_PCI_INTA + mask % 4;
> + mask++;
> + break;
> + }
> + }
> + }
> +#ifdef DBG_TSI108_INTERRUPT
> + else {
> + printk("TSI108_PIC: error in TSI108_PCI_IRP_STAT\n");
> + pci_irp_stat =
> + tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_STAT);
> + temp =
> + tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_INTAD);
> + mb();
> + printk(">> stat=0x%08x intad=0x%08x ", pci_irp_stat, temp);
> + temp =
> + tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL);
> + mb();
> + printk("cfg_ctl=0x%08x ", temp);
> + temp =
> + tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_ENABLE);
> + mb();
> + printk("irp_enable=0x%08x\n", temp);
> + }
> +#endif /* DBG_TSI108_INTERRUPT */
> +
> + return irq;
> +}
> +
> +static void init_pci_source(void)
> +{
> + tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL,
> + 0x0000ff00);
> + tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_ENABLE,
> + 0x00400000);
> + mb();
> +}
> +
> +static struct sysdev_class tsi108_pic_sysclass = {
> + set_kset_name("tsi108_pic"),
> +};
> +
> +static struct sys_device device_tsi108_pic = {
> + .id = 0,
> + .cls = &tsi108_pic_sysclass,
> +};
> +
> +static struct sysdev_driver driver_tsi108_pic = {
> +#ifdef CONFIG_PM /* FIXME: placeholder for future development */
> + .suspend = &tsi108_pic_suspend,
> + .resume = &tsi108_pic_resume,
> +#endif /* CONFIG_PM */
> +};
> +
> +static int __init init_tsi108_pic_sysfs(void)
> +{
> + int rc;
> +
> + if (!get_csrbase())
> + return -ENODEV;
> + printk(KERN_DEBUG "Registering tsi108_pic with sysfs...\n");
> + rc = sysdev_class_register(&tsi108_pic_sysclass);
> + if (rc) {
> + printk(KERN_ERR "Failed registering tsi108_pic sys class\n");
> + return -ENODEV;
> + }
> + rc = sysdev_register(&device_tsi108_pic);
> + if (rc) {
> + printk(KERN_ERR "Failed registering tsi108_pic sys device\n");
> + return -ENODEV;
> + }
> + rc = sysdev_driver_register(&tsi108_pic_sysclass, &driver_tsi108_pic);
> + if (rc) {
> + printk(KERN_ERR "Failed registering tsi108_pic sys driver\n");
> + return -ENODEV;
> + }
> + return 0;
> +}
> +
> +subsys_initcall(init_tsi108_pic_sysfs);
> diff --git a/include/asm-powerpc/tsi108_pic.h b/include/asm-powerpc/tsi108_pic.h
> new file mode 100644
> index 0000000..7b23352
> --- /dev/null
> +++ b/include/asm-powerpc/tsi108_pic.h
> @@ -0,0 +1,232 @@
> +/*
> + * (C) Copyright 2005 Tundra Semiconductor Corp.
> + * Alex Bounine, <alexandreb@tundra.com).
> + *
> + * See file CREDITS for list of people who contributed to this
> + * project.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 of
> + * the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
> + * MA 02111-1307 USA
> + */
> +
> +/*
> + * arch/ppc/syslib/tsi108_pic.h - Tsi108 Interrupt Controller definitions
> + */
> +
> +#ifndef _LINUX_TSI108_PIC_H
> +#define _LINUX_TSI108_PIC_H
> +
> +#include <asm/tsi108_irq.h>
> +
> +#ifdef __KERNEL__
> +
> +/*
> + * Tsi108 PIC supports up to 24 interrupt sources and up to 4 processors
> + */
> +
> +#define TSI108PIC_MAX_SOURCES 24
> +#define TSI108PIC_MAX_PROCESSORS 4
> +
> +#define TSI108PIC_NUM_TIMERS 4
> +#define TSI108PIC_NUM_DBELLS 4
> +#define TSI108PIC_NUM_PROC 4
> +#define TSI108PIC_NUM_PRI 16
> +#define TSI108PIC_NUM_VECTORS 256
> +
> +/*
> + * Tsi108 PIC Register offsets within block.
> + */
> +
> +/* Registers controlling sources */
> +#define TSI108_INT_FRR (0x000)
> +#define TSI108_INT_GCR (0x004)
> +#define TSI108_INT_SVR (0x010)
> +#define TSI108_INT_GTVPR(n) (0x38 + 0x10*(n))
> +#define TSI108_INT_GTDR(n) (0x3C + 0x10*(n))
> +#define TSI108_INT_IVPR(n) (0x100 + 0x8*(n))
> +#define TSI108_INT_IDR(n) (0x104 + 0x8*(n))
> +#define TSI108_INT_DVPR(n) (0x204 + 0xC*(n))
> +#define TSI108_INT_DDR(n) (0x208 + 0xC*(n))
> +#define TSI108_INT_MBVPR(n) (0x284 + 0x10*(n))
> +#define TSI108_INT_MBDR(n) (0x288 + 0x10*(n))
> +
> +/* Registers controlling destinations */
> +#define TSI108_INT_TASKP(n) (0x300 + 0x40*(n))
> +#define TSI108_INT_VECTOR(n) (0x304 + 0x40*(n))
> +#define TSI108_INT_EOI(n) (0x308 + 0x40*(n))
> +#define TSI108_INT_CSR(n) (0x30C + 0x40*(n))
> +
> +/*
> + * Generic definitions common for different types of interrupt
> + * sources.
> + */
> +
> +#define TSI108PIC_MASK (0x80000000)
> +#define TSI108PIC_ACTIVITY (0x40000000)
> +#define TSI108PIC_PRIORITY_MASK (0x000f0000)
> +#define TSI108PIC_VECTOR_MASK (0x000000ff)
> +
> +/**********************************************************
> + * Register Bit Masks definitions for every register
> + */
> +
> +/* TSI108PIC_INT_FRR : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_FRR_VID (0x000000ff)
> +#define TSI108PIC_INT_FRR_NCPU (0x00001f00)
> +#define TSI108PIC_INT_FRR_NITM (0x0000e000)
> +#define TSI108PIC_INT_FRR_NIRQ (0x07ff0000)
> +#define TSI108PIC_INT_FRR_NIDOOR (0xe0000000)
> +#define TSI108PIC_INT_FRR_RESERVED (0x18000000)
> +
> +/* TSI108PIC_INT_GCR : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_GCR_R (0x80000000)
> +#define TSI108PIC_INT_GCR_RESERVED (0x7fffffff)
> +
> +/* TSI108PIC_INT_ICR : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_ICR_R (0x0000000f)
> +#define TSI108PIC_INT_ICR_RESERVED (0xfffffff0)
> +
> +/* TSI108PIC_INT_MVI : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_MVI_VID (0x000000ff)
> +#define TSI108PIC_INT_MVI_DID (0x0000ff00)
> +#define TSI108PIC_INT_MVI_STEP (0x00ff0000)
> +#define TSI108PIC_INT_MVI_RESERVED (0xff000000)
> +
> +/* TSI108PIC_INT_SVR : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_SVR_VECTOR (0x000000ff)
> +#define TSI108PIC_INT_SVR_RESERVED (0xffffff00)
> +
> +/* TSI108PIC_INT_TFRR : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_TFRR_TIME_FREQ (0xffffffff)
> +
> +/* TSI108PIC_INT_SOFT_SET : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_SOFT_SET_S (0x00ffffff)
> +#define TSI108PIC_INT_SOFT_SET_RESERVED (0xff000000)
> +
> +/* TSI108PIC_INT_SOFT_ENABLE : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_SOFT_ENABLE_EN (0x00ffffff)
> +#define TSI108PIC_INT_SOFT_ENABLE_RESERVED (0xff000000)
> +
> +/* TSI108PIC_INT_GTCCR(X) : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_GTCCR_COUNT (0x7fffffff)
> +#define TSI108PIC_INT_GTCCR_T (0x80000000)
> +
> +/* TSI108PIC_INT_GTBCR(X) : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_GTBCR_B_COUNT (0x7fffffff)
> +#define TSI108PIC_INT_GTBCR_CI (0x80000000)
> +
> +/* TSI108PIC_INT_GTVPR(X) : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_GTVPR_VECTOR (0x000000ff)
> +#define TSI108PIC_INT_GTVPR_PRIORITY (0x000f0000)
> +#define TSI108PIC_INT_GTVPR_PRESCALE (0x00f00000)
> +#define TSI108PIC_INT_GTVPR_A (0x40000000)
> +#define TSI108PIC_INT_GTVPR_M (0x80000000)
> +#define TSI108PIC_INT_GTVPR_RESERVED (0x3f00ff00)
> +
> +/* TSI108PIC_INT_GTDR(X) : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_GTDR_SEL_OUT (0x0000000f)
> +#define TSI108PIC_INT_GTDR_RESERVED (0xfffffff0)
> +
> +/* TSI108PIC_INT_IVPR(X) : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_IVPR_VECTOR (0x000000ff)
> +#define TSI108PIC_INT_IVPR_PRIORITY (0x000f0000)
> +
> +#define TSI108PIC_INT_IVPR_P (0x01000000)
> +#define TSI108PIC_INT_IVPR_P_LOW (0 << 24)
> +#define TSI108PIC_INT_IVPR_P_HIGH (1 << 24)
> +
> +#define TSI108PIC_INT_IVPR_S (0x02000000)
> +#define TSI108PIC_INT_IVPR_S_EDGE (0 << 25)
> +#define TSI108PIC_INT_IVPR_S_LEVEL (1 << 25)
> +
> +#define TSI108PIC_INT_IVPR_MODE (0x20000000)
> +#define TSI108PIC_INT_IVPR_A (0x40000000)
> +#define TSI108PIC_INT_IVPR_M (0x80000000)
> +#define TSI108PIC_INT_IVPR_RESERVED (0x1cf0ff00)
> +
> +/* TSI108PIC_INT_IDR(X) : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_IDR_SEL_OUT (0x0000000f)
> +#define TSI108PIC_INT_IDR_RESERVED (0xfffffff0)
> +
> +/* TSI108PIC_INT_DAR : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_DAR_A (0x0000000f)
> +#define TSI108PIC_INT_DAR_RESERVED (0xfffffff0)
> +
> +/* TSI108PIC_INT_DVPR(X) : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_DVPR_VECTOR (0x000000ff)
> +#define TSI108PIC_INT_DVPR_PRIORITY (0x000f0000)
> +#define TSI108PIC_INT_DVPR_A (0x40000000)
> +#define TSI108PIC_INT_DVPR_M (0x80000000)
> +#define TSI108PIC_INT_DVPR_RESERVED (0x3ff0ff00)
> +
> +/* TSI108PIC_INT_DDR(X) : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_DDR_SEL_OUT (0x0000000f)
> +#define TSI108PIC_INT_DDR_RESERVED (0xfffffff0)
> +
> +/* TSI108PIC_INT_DMR(X) : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_DMR_M (0xffffffff)
> +
> +/* TSI108PIC_INT_MBR(X) : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_MBR_M (0xffffffff)
> +
> +/* TSI108PIC_INT_MBVPR(X) : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_MBVPR_VECTOR (0x000000ff)
> +#define TSI108PIC_INT_MBVPR_PRIORITY (0x000f0000)
> +#define TSI108PIC_INT_MBVPR_A (0x40000000)
> +#define TSI108PIC_INT_MBVPR_M (0x80000000)
> +#define TSI108PIC_INT_MBVPR_RESERVED (0x3ff0ff00)
> +
> +/* TSI108PIC_INT_MBDR(X) : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_MBDR_SEL_OUT (0x0000000f)
> +#define TSI108PIC_INT_MBDR_RESERVED (0xfffffff0)
> +
> +/* TSI108PIC_INT_TASKP(X) : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_TASKP_TASKP (0x0000000f)
> +#define TSI108PIC_INT_TASKP_RESERVED (0xfffffff0)
> +
> +/* TSI108PIC_INT_VECTOR(X) : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_VECTOR_VECTOR (0x000000ff)
> +#define TSI108PIC_INT_VECTOR_LS_VECTOR (0xff000000)
> +#define TSI108PIC_INT_VECTOR_RESERVED (0x00ffff00)
> +
> +/* TSI108PIC_INT_EOI(X) : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_EOI_EOI (0x000000ff)
> +#define TSI108PIC_INT_EOI_RESERVED (0xffffff00)
> +
> +/* TSI108PIC_INT_CSR(X) : Register Bits Masks Definitions */
> +#define TSI108PIC_INT_CSR_RESERVED (0xfffffffc)
> +
> +#define TSI108PIC_INT_CSR_P (1 << 0)
> +#define TSI108PIC_INT_CSR_P_LOW (0 << 0)
> +#define TSI108PIC_INT_CSR_P_HIGH (1 << 0)
> +
> +#define TSI108PIC_INT_CSR_S (1 << 1)
> +#define TSI108PIC_INT_CSR_S_EDGE (0 << 1)
> +#define TSI108PIC_INT_CSR_S_LEVEL (1 << 1)
> +
> +extern void tsi108_pic_init(u_char * board_init_senses);
> +extern void tsi108_pic_reset(void);
> +extern void tsi108_pic_set_output(int dest_num, u32 sense, u32 polarity);
> +extern int tsi108_pic_source_cfg(int src_num, u32 sense,
> + u32 polarity, TSI108_IRQ_MODE mode);
> +extern int tsi108_pic_set_vector(int src_num, int vect, int prio);
> +extern void tsi108_pic_init_nmi_irq(u_int irq);
> +extern void tsi108_pic_hookup_cascade(u_int irq, char *name,
> + int (*cascade_fn) (struct pt_regs *));
> +extern int tsi108_pic_get_irq(struct pt_regs *regs);
> +
> +#endif /* __KERNEL__ */
> +
> +#endif /* _LINUX_TSI108_PIC_H */
^ permalink raw reply [flat|nested] 16+ messages in thread
* RE: [PATCH/2.6.17-rc4 4/10]Powerpc: Add tsi108 pic support
@ 2006-05-30 3:28 Zang Roy-r61911
2006-05-30 4:17 ` Benjamin Herrenschmidt
2006-05-30 19:18 ` Kumar Gala
0 siblings, 2 replies; 16+ messages in thread
From: Zang Roy-r61911 @ 2006-05-30 3:28 UTC (permalink / raw)
To: Benjamin Herrenschmidt
Cc: linuxppc-dev list, Yang Xin-Xin-r48390, Paul Mackerras,
Alexandre.Bounine
>
> On Wed, 2006-05-17 at 18:14 +0800, Zang Roy-r61911 wrote:
> > Add Tundra Semiconductor tsi108 host bridge interrupt
> controller support.
>
> It looks a bit like an hacked up MPIC... Is it different
> enough to justify a separate driver ? Or would it be possible
> to define a TSI108 flag to pass the current mpic driver and
> add the necessary bits to it ?
>
Tsi108 implementation of MPIC has many differences form the original one, the following
code implements it with mpic. Any comment? The following patch is just based on
my previous send out patches.
Integrate Tundra Semiconductor tsi108 host bridge interrupt controller
to mpic arch.
Signed-off-by: Alexandre Bounine <alexandreb@tundra.com>
diff -pNur -x project.pj linux-2.6.17-rc4-tun/arch/powerpc/Kconfig linux-2.6.17-rc4-hpc2/arch/powerpc/Kconfig
--- linux-2.6.17-rc4-tun/arch/powerpc/Kconfig 2006-05-28 20:15:21.000000000 -0400
+++ linux-2.6.17-rc4-hpc2/arch/powerpc/Kconfig 2006-05-29 16:04:30.000000000 -0400
@@ -408,7 +408,8 @@ config U3_DART
default n
config MPIC
- depends on PPC_PSERIES || PPC_PMAC || PPC_MAPLE || PPC_CHRP
+ depends on PPC_PSERIES || PPC_PMAC || PPC_MAPLE || PPC_CHRP \
+ || MPC7448HPC2
bool
default y
diff -pNur -x project.pj linux-2.6.17-rc4-tun/arch/powerpc/configs/mpc7448_hpc2_defconfig linux-2.6.17-rc4-hpc2/arch/powerpc/configs/mpc7448_hpc2_defconfig
--- linux-2.6.17-rc4-tun/arch/powerpc/configs/mpc7448_hpc2_defconfig 2006-05-28 20:15:36.000000000 -0400
+++ linux-2.6.17-rc4-hpc2/arch/powerpc/configs/mpc7448_hpc2_defconfig 2006-05-27 19:05:42.000000000 -0400
@@ -1,7 +1,7 @@
#
# Automatically generated make config: don't edit
# Linux kernel version: 2.6.17-rc4
-# Tue May 23 11:29:48 2006
+# Sat May 27 18:45:55 2006
#
# CONFIG_PPC64 is not set
CONFIG_PPC32=y
@@ -110,6 +110,7 @@ CONFIG_DEFAULT_IOSCHED="anticipatory"
# CONFIG_PPC_ISERIES is not set
CONFIG_EMBEDDED6xx=y
# CONFIG_APUS is not set
+CONFIG_MPIC=y
# CONFIG_PPC_RTAS is not set
# CONFIG_MMIO_NVRAM is not set
# CONFIG_PPC_MPC106 is not set
@@ -899,6 +900,7 @@ CONFIG_LOG_BUF_SHIFT=14
# CONFIG_DEBUG_FS is not set
# CONFIG_UNWIND_INFO is not set
# CONFIG_BOOTX_TEXT is not set
+# CONFIG_SERIAL_TEXT_DEBUG is not set
# CONFIG_PPC_EARLY_DEBUG_LPAR is not set
# CONFIG_PPC_EARLY_DEBUG_G5 is not set
# CONFIG_PPC_EARLY_DEBUG_RTAS is not set
diff -pNur -x project.pj linux-2.6.17-rc4-tun/arch/powerpc/platforms/embedded6xx/Kconfig linux-2.6.17-rc4-hpc2/arch/powerpc/platforms/embedded6xx/Kconfig
--- linux-2.6.17-rc4-tun/arch/powerpc/platforms/embedded6xx/Kconfig 2006-05-28 20:15:36.000000000 -0400
+++ linux-2.6.17-rc4-hpc2/arch/powerpc/platforms/embedded6xx/Kconfig 2006-05-29 15:55:13.000000000 -0400
@@ -79,6 +79,7 @@ config MPC7448HPC2
select TSI108_BRIDGE
select DEFAULT_UIMAGE
select PPC_UDBG_16550
+ select MPIC
help
Select MPC7448HPC2 if configuring for Freescale MPC7448HPC2 (Taiga)
platform
diff -pNur -x project.pj linux-2.6.17-rc4-tun/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c linux-2.6.17-rc4-hpc2/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c
--- linux-2.6.17-rc4-tun/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c 2006-05-28 20:15:36.000000000 -0400
+++ linux-2.6.17-rc4-hpc2/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c 2006-05-29 15:52:59.000000000 -0400
@@ -43,7 +43,16 @@
#include <asm/reg.h>
#include <mm/mmu_decl.h>
#include "mpc7448_hpc2.h"
-#include <asm/tsi108_pic.h>
+#include <asm/tsi108_irq.h>
+#include <asm/mpic.h>
+
+#undef DEBUG
+
+#ifdef DEBUG
+#define DBG(fmt...) do { printk(fmt); } while(0)
+#else
+#define DBG(fmt...) do { } while(0)
+#endif
#ifndef CONFIG_PCI
isa_io_base = MPC7448_HPC2_ISA_IO_BASE;
@@ -53,6 +62,8 @@ pci_dram_offset = MPC7448_HPC2_PCI_MEM_O
extern int add_bridge(struct device_node *dev);
extern void _nmask_and_or_msr(unsigned long nmask, unsigned long or_val);
+extern void tsi108_pci_int_init(void);
+extern int tsi108_irq_cascade(struct pt_regs *regs, void *unused);
#ifdef TSI108_ETH
hw_info hw_info_table[TSI108_ETH_MAX_PORTS + 1] = {
@@ -76,10 +87,32 @@ hw_info hw_info_table[TSI108_ETH_MAX_POR
*/
static u_char mpc7448_hpc2_pic_initsenses[] __initdata = {
+ /* External on-board sources */
(IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* INT[0] XINT0 from FPGA */
(IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* INT[1] XINT1 from FPGA */
(IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* INT[2] PHY_INT from both GIGE */
(IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* INT[3] RESERVED */
+ /* Internal Tsi108/109 interrupt sources */
+ (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* Reserved IRQ */
+ (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* Reserved IRQ */
+ (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* Reserved IRQ */
+ (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* Reserved IRQ */
+ (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* DMA0 */
+ (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* DMA1 */
+ (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* DMA2 */
+ (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* DMA3 */
+ (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* UART0 */
+ (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* UART1 */
+ (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* I2C */
+ (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* GPIO */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* GIGE0 */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* GIGE1 */
+ (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* Reserved IRQ */
+ (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* HLP */
+ (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* SDC */
+ (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* Processor IF */
+ (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* Reserved IRQ */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* PCI/X block */
};
/*
@@ -196,11 +229,42 @@ static void __init mpc7448_hpc2_setup_ar
*/
static void __init mpc7448_hpc2_init_IRQ(void)
{
+ struct mpic *mpic;
+ phys_addr_t mpic_paddr = 0;
+ struct device_node *tsi_pic;
+
+ tsi_pic = of_find_node_by_type(NULL, "open-pic");
+ if (tsi_pic) {
+ unsigned int size;
+ void *prop = get_property(tsi_pic, "reg", &size);
+ mpic_paddr = of_translate_address(tsi_pic, prop);
+ }
+
+ if (mpic_paddr == 0) {
+ printk("%s: No tsi108 PIC found !\n", __FUNCTION__);
+ return;
+ }
+
+ DBG("%s: tsi108pic phys_addr = 0x%x\n", __FUNCTION__,
+ (u32) mpic_paddr);
- tsi108_pic_init(mpc7448_hpc2_pic_initsenses);
+ mpic = mpic_alloc(mpic_paddr,
+ MPIC_PRIMARY | MPIC_BIG_ENDIAN | MPIC_WANTS_RESET,
+ 0, /* num_sources used */
+ TSI108_IRQ_BASE,
+ 0, /* num_sources used */
+ NR_IRQS - 4 /* XXXX */,
+ mpc7448_hpc2_pic_initsenses,
+ sizeof(mpc7448_hpc2_pic_initsenses), "Tsi108_PIC");
+
+ BUG_ON(mpic == NULL); /* XXXX */
+
+ mpic_init(mpic);
+ mpic_setup_cascade(IRQ_TSI108_PCI, tsi108_irq_cascade, mpic);
+ tsi108_pci_int_init();
/* Configure MPIC outputs to CPU0 */
- tsi108_pic_set_output(0, IRQ_SENSE_EDGE, IRQ_POLARITY_NEGATIVE);
+ tsi108_write_reg(TSI108_MPIC_OFFSET + 0x30c, 0);
}
static void __init mpc7448_hpc2_map_io(void)
@@ -269,7 +333,7 @@ define_machine(mpc7448_hpc2){
.setup_arch = mpc7448_hpc2_setup_arch,
.init_IRQ = mpc7448_hpc2_init_IRQ,
.show_cpuinfo = mpc7448_hpc2_show_cpuinfo,
- .get_irq = tsi108_pic_get_irq,
+ .get_irq = mpic_get_irq,
.restart = mpc7448_hpc2_restart,
.calibrate_decr = generic_calibrate_decr,
.setup_io_mappings = mpc7448_hpc2_map_io,
diff -pNur -x project.pj linux-2.6.17-rc4-tun/arch/powerpc/sysdev/Makefile linux-2.6.17-rc4-hpc2/arch/powerpc/sysdev/Makefile
--- linux-2.6.17-rc4-tun/arch/powerpc/sysdev/Makefile 2006-05-28 20:12:58.000000000 -0400
+++ linux-2.6.17-rc4-hpc2/arch/powerpc/sysdev/Makefile 2006-05-29 16:06:23.000000000 -0400
@@ -8,4 +8,4 @@ obj-$(CONFIG_U3_DART) += dart_iommu.o
obj-$(CONFIG_MMIO_NVRAM) += mmio_nvram.o
obj-$(CONFIG_PPC_83xx) += ipic.o
obj-$(CONFIG_FSL_SOC) += fsl_soc.o
-obj-$(CONFIG_TSI108_BRIDGE) += tsi108_common.o tsi108_pic.o
+obj-$(CONFIG_TSI108_BRIDGE) += tsi108_common.o tsi108_pci_int.o
diff -pNur -x project.pj linux-2.6.17-rc4-tun/arch/powerpc/sysdev/mpic.c linux-2.6.17-rc4-hpc2/arch/powerpc/sysdev/mpic.c
--- linux-2.6.17-rc4-tun/arch/powerpc/sysdev/mpic.c 2006-03-20 00:53:29.000000000 -0500
+++ linux-2.6.17-rc4-hpc2/arch/powerpc/sysdev/mpic.c 2006-05-29 16:07:03.000000000 -0400
@@ -81,7 +81,7 @@ static inline void _mpic_write(unsigned
static inline u32 _mpic_ipi_read(struct mpic *mpic, unsigned int ipi)
{
unsigned int be = (mpic->flags & MPIC_BIG_ENDIAN) != 0;
- unsigned int offset = MPIC_GREG_IPI_VECTOR_PRI_0 + (ipi * 0x10);
+ unsigned int offset = MPIC_GREG_IPI_VECTOR_PRI_0 + (ipi * MPIC_GREG_IPI_STRIDE);
if (mpic->flags & MPIC_BROKEN_IPI)
be = !be;
@@ -90,7 +90,7 @@ static inline u32 _mpic_ipi_read(struct
static inline void _mpic_ipi_write(struct mpic *mpic, unsigned int ipi, u32 value)
{
- unsigned int offset = MPIC_GREG_IPI_VECTOR_PRI_0 + (ipi * 0x10);
+ unsigned int offset = MPIC_GREG_IPI_VECTOR_PRI_0 + (ipi * MPIC_GREG_IPI_STRIDE);
_mpic_write(mpic->flags & MPIC_BIG_ENDIAN, mpic->gregs, offset, value);
}
@@ -393,7 +393,11 @@ static inline struct mpic * mpic_from_ir
static inline void mpic_eoi(struct mpic *mpic)
{
mpic_cpu_write(MPIC_CPU_EOI, 0);
+#ifndef CONFIG_TSI108_BRIDGE
(void)mpic_cpu_read(MPIC_CPU_WHOAMI);
+#else
+ (void)mpic_cpu_read(MPIC_CPU_OUTPUT);
+#endif
}
#ifdef CONFIG_SMP
@@ -514,9 +518,26 @@ static void mpic_end_irq(unsigned int ir
}
#endif /* CONFIG_MPIC_BROKEN_U3 */
+#ifdef CONFIG_TSI108_BRIDGE
+ if ((irq_desc[irq].status & IRQ_LEVEL) != 0)
+#endif
mpic_eoi(mpic);
}
+#ifdef CONFIG_TSI108_BRIDGE
+static void mpic_ack_irq(unsigned int irq)
+{
+ struct mpic *mpic = mpic_from_irq(irq);
+
+#ifdef DEBUG_IRQ
+ DBG("%s: ack_irq: %d\n", mpic->name, irq);
+#endif
+
+ if ((irq_desc[irq].status & IRQ_LEVEL) == 0)
+ mpic_eoi(mpic);
+}
+#endif /* CONFIG_TSI108_BRIDGE */
+
#ifdef CONFIG_SMP
static void mpic_enable_ipi(unsigned int irq)
@@ -596,6 +617,9 @@ struct mpic * __init mpic_alloc(unsigned
mpic->hc_irq.enable = mpic_enable_irq;
mpic->hc_irq.disable = mpic_disable_irq;
mpic->hc_irq.end = mpic_end_irq;
+#ifdef CONFIG_TSI108_BRIDGE
+ mpic->hc_irq.ack = mpic_ack_irq;
+#endif
if (flags & MPIC_PRIMARY)
mpic->hc_irq.set_affinity = mpic_set_affinity;
#ifdef CONFIG_SMP
@@ -955,8 +979,13 @@ void mpic_send_ipi(unsigned int ipi_no,
DBG("%s: send_ipi(ipi_no: %d)\n", mpic->name, ipi_no);
#endif
+#ifndef CONFIG_TSI108_BRIDGE
mpic_cpu_write(MPIC_CPU_IPI_DISPATCH_0 + ipi_no * 0x10,
mpic_physmask(cpu_mask & cpus_addr(cpu_online_map)[0]));
+#else /* CONFIG_TSI108_BRIDGE */
+ mpic_write(mpic->gregs, MPIC_CPU_IPI_DISPATCH,
+ mpic_physmask(cpu_mask & cpus_addr(cpu_online_map)[0]));
+#endif /* !CONFIG_TSI108_BRIDGE */
}
int mpic_get_one_irq(struct mpic *mpic, struct pt_regs *regs)
@@ -972,11 +1001,20 @@ int mpic_get_one_irq(struct mpic *mpic,
DBG("%s: cascading ...\n", mpic->name);
#endif
irq = mpic->cascade(regs, mpic->cascade_data);
+#ifdef DEBUG_LOW
+ DBG("%s: cascaded irq: %d\n", mpic->name, irq);
+#endif
+#ifndef CONFIG_TSI108_BRIDGE
mpic_eoi(mpic);
+#endif
return irq;
}
- if (unlikely(irq == MPIC_VEC_SPURRIOUS))
+ if (unlikely(irq == MPIC_VEC_SPURRIOUS)) {
+#ifdef CONFIG_TSI108_BRIDGE
+ mpic_eoi(mpic);
+#endif
return -1;
+ }
if (irq < MPIC_VEC_IPI_0) {
#ifdef DEBUG_IRQ
DBG("%s: irq %d\n", mpic->name, irq + mpic->irq_offset);
diff -pNur -x project.pj linux-2.6.17-rc4-tun/arch/powerpc/sysdev/tsi108_common.c linux-2.6.17-rc4-hpc2/arch/powerpc/sysdev/tsi108_common.c
--- linux-2.6.17-rc4-tun/arch/powerpc/sysdev/tsi108_common.c 2006-05-28 20:12:58.000000000 -0400
+++ linux-2.6.17-rc4-hpc2/arch/powerpc/sysdev/tsi108_common.c 2006-05-29 16:08:52.000000000 -0400
@@ -90,9 +90,13 @@ tsi108_direct_write_config(struct pci_bu
{
volatile unsigned char *cfg_addr;
+ if (ppc_md.pci_exclude_device)
+ if (ppc_md.pci_exclude_device(bus->number, devfunc))
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
cfg_addr = (unsigned char *)(tsi_mk_config_addr(bus->number,
devfunc, offset) |
- (offset & 0x03));
+ (offset & 0x03));
#ifdef TSI108_PCI_DEBUG
printk("PCI CFG write : ");
@@ -172,6 +176,10 @@ tsi108_direct_read_config(struct pci_bus
volatile unsigned char *cfg_addr;
u32 temp;
+ if (ppc_md.pci_exclude_device)
+ if (ppc_md.pci_exclude_device(bus->number, devfn))
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
cfg_addr = (unsigned char *)(tsi_mk_config_addr(bus->number,
devfn,
offset) | (offset &
diff -pNur -x project.pj linux-2.6.17-rc4-tun/arch/powerpc/sysdev/tsi108_pci_int.c linux-2.6.17-rc4-hpc2/arch/powerpc/sysdev/tsi108_pci_int.c
--- linux-2.6.17-rc4-tun/arch/powerpc/sysdev/tsi108_pci_int.c 1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.17-rc4-hpc2/arch/powerpc/sysdev/tsi108_pci_int.c 2006-05-29 16:09:04.000000000 -0400
@@ -0,0 +1,219 @@
+/*
+ * (C) Copyright 2005 Tundra Semiconductor Corp.
+ * Alex Bounine, <alexandreb@tundra.com).
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+/*
+ * Tsi108 PCI Interrupt Handling (cascaded to MPIC)
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/sysdev.h>
+#include <asm/ptrace.h>
+#include <asm/signal.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/prom.h>
+#include <asm/sections.h>
+#include <asm/hardirq.h>
+#include <asm/machdep.h>
+
+#include <asm/tsi108.h>
+#include <asm/tsi108_irq.h>
+
+#undef DEBUG
+#undef DBG_TSI108_INTERRUPT
+
+#ifdef DEBUG
+#define DBG(fmt...) do { printk(fmt); } while(0)
+#else
+#define DBG(fmt...) do { } while(0)
+#endif
+
+extern u32 get_vir_csrbase(void);
+extern u32 tsi108_read_reg(u32 reg_offset);
+extern void tsi108_write_reg(u32 reg_offset, u32 val);
+
+/*
+ * Low level utility functions
+ */
+
+static void tsi108_pci_int_mask(u_int irq)
+{
+ u_int irp_cfg;
+ int int_line = (irq - IRQ_PCI_INTAD_BASE);
+
+ irp_cfg = tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL);
+ mb();
+ irp_cfg |= (1 << int_line); /* INTx_DIR = output */
+ irp_cfg &= ~(3 << (8 + (int_line * 2))); /* INTx_TYPE = unused */
+ tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL, irp_cfg);
+ mb();
+ irp_cfg = tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL);
+}
+
+static void tsi108_pci_int_unmask(u_int irq)
+{
+ u_int irp_cfg;
+ int int_line = (irq - IRQ_PCI_INTAD_BASE);
+
+ irp_cfg = tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL);
+ mb();
+ irp_cfg &= ~(1 << int_line);
+ irp_cfg |= (3 << (8 + (int_line * 2)));
+ tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL, irp_cfg);
+ mb();
+}
+
+static void init_pci_source(void)
+{
+ tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL,
+ 0x0000ff00);
+ tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_ENABLE,
+ 0x00400000);
+ mb();
+}
+
+static inline int get_pci_source(void)
+{
+ u_int temp = 0;
+ int irq = -1;
+ int i;
+ u_int pci_irp_stat;
+ static int mask = 0;
+
+ /* Read PCI/X block interrupt status register */
+ pci_irp_stat = tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_STAT);
+ mb();
+
+ if (pci_irp_stat & TSI108_PCI_IRP_STAT_P_INT) {
+ /* Process Interrupt from PCI bus INTA# - INTD# lines */
+ temp =
+ tsi108_read_reg(TSI108_PCI_OFFSET +
+ TSI108_PCI_IRP_INTAD) & 0xf;
+ mb();
+ for (i = 0; i < 4; i++, mask++) {
+ if (temp & (1 << mask % 4)) {
+ irq = IRQ_PCI_INTA + mask % 4;
+ mask++;
+ break;
+ }
+ }
+ }
+#ifdef DBG_TSI108_INTERRUPT
+ else {
+ printk("TSI108_PIC: error in TSI108_PCI_IRP_STAT\n");
+ pci_irp_stat =
+ tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_STAT);
+ temp =
+ tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_INTAD);
+ mb();
+ printk(">> stat=0x%08x intad=0x%08x ", pci_irp_stat, temp);
+ temp =
+ tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL);
+ mb();
+ printk("cfg_ctl=0x%08x ", temp);
+ temp =
+ tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_ENABLE);
+ mb();
+ printk("irp_enable=0x%08x\n", temp);
+ }
+#endif /* DBG_TSI108_INTERRUPT */
+
+ return irq;
+}
+
+
+/*
+ * Linux descriptor level callbacks
+ */
+
+static void tsi108_pci_irq_enable(u_int irq)
+{
+ tsi108_pci_int_unmask(irq);
+}
+
+static void tsi108_pci_irq_disable(u_int irq)
+{
+ tsi108_pci_int_mask(irq);
+}
+
+static void tsi108_pci_irq_ack(u_int irq)
+{
+ tsi108_pci_int_mask(irq);
+}
+
+static void tsi108_pci_irq_end(u_int irq)
+{
+ /* Signal EOI to the Tsi108 interrupt controller. */
+ irq_desc[IRQ_TSI108_PCI].handler->end(IRQ_TSI108_PCI);
+ tsi108_pci_int_unmask(irq);
+}
+
+/*
+ * Interrupt controller descriptor for cascaded PCI interrupt controller.
+ */
+
+struct hw_interrupt_type tsi108_pci_irq = {
+ .typename = "tsi108_PCI_int",
+ .enable = tsi108_pci_irq_enable,
+ .disable = tsi108_pci_irq_disable,
+ .ack = tsi108_pci_irq_ack,
+ .end = tsi108_pci_irq_end,
+};
+
+/*
+ * Exported functions
+ */
+
+/*
+ * The Tsi108 PCI interrupts initialization routine.
+ *
+ * The INTA# - INTD# interrupts on the PCI bus are reported by the PCI block
+ * to the MPIC using single interrupt source (IRQ_TSI108_PCI). Therefore the
+ * PCI block has to be treated as a cascaded interrupt controller connected
+ * to the MPIC.
+ */
+
+void __init tsi108_pci_int_init(void)
+{
+ u_int i;
+
+ DBG("Tsi108_pci_int_init: initializing PCI interrupts\n");
+
+ for (i = 0; i < NUM_PCI_IRQS; i++) {
+ irq_desc[i + IRQ_PCI_INTAD_BASE].handler = &tsi108_pci_irq;
+ irq_desc[i + IRQ_PCI_INTAD_BASE].status |= IRQ_LEVEL;
+ }
+
+ init_pci_source();
+}
+
+int tsi108_irq_cascade(struct pt_regs *regs, void *unused)
+{
+ return get_pci_source();
+}
diff -pNur -x project.pj linux-2.6.17-rc4-tun/arch/powerpc/sysdev/tsi108_pic.c linux-2.6.17-rc4-hpc2/arch/powerpc/sysdev/tsi108_pic.c
--- linux-2.6.17-rc4-tun/arch/powerpc/sysdev/tsi108_pic.c 2006-05-28 20:13:07.000000000 -0400
+++ linux-2.6.17-rc4-hpc2/arch/powerpc/sysdev/tsi108_pic.c 1969-12-31 19:00:00.000000000 -0500
@@ -1,813 +0,0 @@
-/*
- * (C) Copyright 2005 Tundra Semiconductor Corp.
- * Alex Bounine, <alexandreb@tundra.com).
- *
- * See file CREDITS for list of people who contributed to this
- * project.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
- */
-
-/*
- * Tsi108 Interrupt Controller Handling
- */
-
-#include <linux/config.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/init.h>
-#include <linux/irq.h>
-#include <linux/interrupt.h>
-#include <linux/sysdev.h>
-#include <asm/ptrace.h>
-#include <asm/signal.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/prom.h>
-#include <asm/sections.h>
-#include <asm/hardirq.h>
-#include <asm/machdep.h>
-
-#include <asm/tsi108.h>
-#include <asm/tsi108_irq.h>
-#include <asm/tsi108_pic.h>
-#undef DEBUG
-
-#ifdef DEBUG
-#define DBG(fmt...) do { printk(fmt); } while(0)
-#else
-#define DBG(fmt...) do { } while(0)
-#endif
-
-extern u32 get_vir_csrbase(void);
-extern u32 tsi108_read_reg(u32 reg_offset);
-extern void tsi108_write_reg(u32 reg_offset, u32 val);
-
-static phys_addr_t tsi108_pic_phy_addr;
-static u32 tsi108_pic_vir_addr;
-
-static int tsi108_pic_cascade_irq = -1;
-static int (*tsi108_pic_cascade_fn) (struct pt_regs *);
-
-/* Global Operations */
-static void tsi108_pic_set_task_priority(u_int pri);
-static void tsi108_pic_set_spurious(u_int vector);
-void tsi108_pic_mask_all(void);
-
-/* Timer Interrupts */
-static void tsi108_pic_inittimer(u_int timer, u_int pri, u_int vector);
-static void tsi108_pic_maptimer(u_int timer, u_int cpumask);
-
-/* Interrupt Sources */
-static void tsi108_pic_enable_irq(u_int irq);
-static void tsi108_pic_disable_irq(u_int irq);
-static void tsi108_pic_initirq(u_int irq, u_int pri, u_int vector, int polarity,
- int is_level);
-static void tsi108_pic_mapirq(u_int irq, u_int cpumask, u_int keepmask);
-static void init_pci_source(void);
-static inline int get_pci_source(int vector);
-int tsi108_pic_set_irq_sense(int irq, int pol, int sense);
-
-/*
- * tsi108_pic interface routines
- */
-static void tsi108_pic_end_irq(unsigned int irq_nr);
-static void tsi108_pic_ack_irq(unsigned int irq_nr);
-void tsi108_pic_set_affinity(unsigned int irq_nr, unsigned long cpumask);
-
-static struct hw_interrupt_type tsi108_pic_irq = {
- "tsi108_pic",
- NULL,
- NULL,
- tsi108_pic_enable_irq,
- tsi108_pic_disable_irq,
- tsi108_pic_ack_irq,
- tsi108_pic_end_irq,
- NULL
-};
-
-static void tsi108_pci_irq_enable(u_int irq);
-static void tsi108_pci_irq_disable(u_int irq);
-static void tsi108_pci_irq_ack(u_int irq);
-static void tsi108_pci_irq_end(u_int irq);
-
-static struct hw_interrupt_type tsi108_pci_irq = {
- "tsi108_PCI_int",
- NULL,
- NULL,
- tsi108_pci_irq_enable,
- tsi108_pci_irq_disable,
- tsi108_pci_irq_ack,
- tsi108_pci_irq_end,
- NULL
-};
-
-#ifdef DBG_TSI108_INTERRUPT
-#define ASSERT(expr) if (!(expr)) { \
- printk("tsi108pic :" \
- "assertion failed! %s[%d]: %s\n", \
- __FUNCTION__, __LINE__, #expr); \
- dump_stack(); \
- }
-#else
-#define ASSERT(expr) do {} while (0)
-#endif
-
-static inline u_int get_vector_offset(u_int irq)
-{
- u_int offset;
-
- if (irq < TSI108_IRQ_BASE || irq >= IRQ_PCI_INTAD_BASE)
- return 0;
-
- if (irq < IRQ_TSI108_MBOX0)
- offset = TSI108_INT_IVPR(irq - TSI108_IRQ_BASE);
- else if (irq < IRQ_TSI108_DBELL0)
- offset = TSI108_INT_MBVPR(irq - IRQ_TSI108_MBOX0);
- else if (irq < IRQ_TSI108_TIMER0)
- offset = TSI108_INT_DVPR(irq - IRQ_TSI108_DBELL0);
- else
- offset = TSI108_INT_GTVPR(irq - IRQ_TSI108_TIMER0);
-
- return offset;
-}
-
-static inline u_int tsi108_pic_read_reg(u_int reg_offset)
-{
- return in_be32((volatile u32 *)(tsi108_pic_vir_addr + reg_offset));
-}
-
-static inline void tsi108_pic_write_reg(u_int reg_offset, u_int val)
-{
- out_be32((volatile u32 *)(tsi108_pic_vir_addr + reg_offset), val);
-}
-
-void tsi108_pic_reset(void)
-{
- tsi108_pic_write_reg(TSI108_INT_GCR, TSI108PIC_INT_GCR_R);
- while (tsi108_pic_read_reg(TSI108_INT_GCR) & TSI108PIC_INT_GCR_R)
- mb();
-}
-
-void tsi108_pic_set_output(int dest_num, u32 sense, u32 polarity)
-{
- u32 temp = 0;
- temp |= (IRQ_SENSE_LEVEL == sense) ?
- (TSI108PIC_INT_CSR_S_LEVEL) : (TSI108PIC_INT_CSR_S_EDGE);
- temp |= (IRQ_POLARITY_POSITIVE == polarity) ?
- (TSI108PIC_INT_CSR_P_HIGH) : (TSI108PIC_INT_CSR_P_LOW);
- tsi108_pic_write_reg(TSI108_INT_CSR(dest_num), temp);
- mb();
-}
-
-int tsi108_pic_source_cfg(int src_num, /* interrupt source number */
- u32 sense, /* interrupt source Sense */
- u32 polarity, /* interrupt source Polarity */
- TSI108_IRQ_MODE mode /* interrupt delivery Mode */
- )
-{
- unsigned temp;
-
- temp = tsi108_pic_read_reg(TSI108_INT_IVPR(src_num));
-
- if (temp & TSI108PIC_ACTIVITY) /* error if source is active */
- return -1;
-
- if (0 == (temp & TSI108PIC_MASK)) {
- temp |= TSI108PIC_MASK; /* mask IRQ prior making changes */
- tsi108_pic_write_reg(TSI108_INT_IVPR(src_num), temp);
- }
-
- temp &= ~(TSI108PIC_INT_IVPR_MODE |
- TSI108PIC_INT_IVPR_S | TSI108PIC_INT_IVPR_P);
-
- temp |= (IRQ_SENSE_LEVEL == sense) ?
- (TSI108PIC_INT_CSR_S_LEVEL) : (TSI108PIC_INT_CSR_S_EDGE);
- temp |= (IRQ_POLARITY_POSITIVE == polarity) ?
- (TSI108PIC_INT_CSR_P_HIGH) : (TSI108PIC_INT_CSR_P_LOW);
-
- tsi108_pic_write_reg(TSI108_INT_IVPR(src_num),
- TSI108PIC_MASK | (mode << 29) | temp);
- return (0);
-}
-
-int tsi108_pic_set_vector(int src_num, /* source number */
- int vect, /* vector number */
- int prio /* interrupt source priority */
- )
-{
- unsigned tmp;
-
- tmp = tsi108_pic_read_reg(TSI108_INT_IVPR(src_num));
-
- if (tmp & TSI108PIC_ACTIVITY) /* error if source is active */
- return -1;
-
- if (0 == (tmp & TSI108PIC_MASK)) {
- tmp |= TSI108PIC_MASK; /* mask IRQ prior making changes */
- tsi108_pic_write_reg(TSI108_INT_IVPR(src_num), tmp);
- }
-
- /* clear bits to be changed */
- tmp &= ~(TSI108PIC_VECTOR_MASK | TSI108PIC_PRIORITY_MASK);
-
- tmp |= (prio << 16) | vect;
- tsi108_pic_write_reg(TSI108_INT_IVPR(src_num), tmp);
- return 0;
-}
-
-void tsi108_pic_mask_all()
-{
- int i;
- unsigned int vp;
-
- /* Mask all external and internal interrupt sources */
- for (i = 0; i < TSI108PIC_MAX_SOURCES; i++) {
- vp = tsi108_pic_read_reg(TSI108_INT_IVPR(i));
- tsi108_pic_write_reg(TSI108_INT_IVPR(i), vp | TSI108PIC_MASK);
- mb();
-
- /* Make sure that irq is masked */
- do {
- vp = tsi108_pic_read_reg(TSI108_INT_IVPR(i));
- } while ((vp & TSI108PIC_ACTIVITY) && !(vp & TSI108PIC_MASK));
- }
-
- /* Mask all timer interrupts */
- for (i = 0; i < TSI108PIC_NUM_TIMERS; i++) {
- vp = tsi108_pic_read_reg(TSI108_INT_GTVPR(i));
- tsi108_pic_write_reg(TSI108_INT_GTVPR(i), vp | TSI108PIC_MASK);
- mb();
-
- do {
- vp = tsi108_pic_read_reg(TSI108_INT_GTVPR(i));
- } while ((vp & TSI108PIC_ACTIVITY) && !(vp & TSI108PIC_MASK));
- }
-
- /* Mask all doorbell interrupts */
- for (i = 0; i < TSI108PIC_NUM_DBELLS; i++) {
- vp = tsi108_pic_read_reg(TSI108_INT_DVPR(i));
- tsi108_pic_write_reg(TSI108_INT_IVPR(i), vp | TSI108PIC_MASK);
- mb();
-
- do {
- vp = tsi108_pic_read_reg(TSI108_INT_DVPR(i));
- } while ((vp & TSI108PIC_ACTIVITY) && !(vp & TSI108PIC_MASK));
- }
-
- /* Mask all mailbox interrupts */
- for (i = 0; i < 4; i++) {
- vp = tsi108_pic_read_reg(TSI108_INT_MBVPR(i));
- tsi108_pic_write_reg(TSI108_INT_MBVPR(i), vp | TSI108PIC_MASK);
- mb();
-
- do {
- vp = tsi108_pic_read_reg(TSI108_INT_MBVPR(i));
- } while ((vp & TSI108PIC_ACTIVITY) && !(vp & TSI108PIC_MASK));
- }
-}
-
-/*
- * The Tsi108 PC initialization routine.
- * A caller routine (usually from platform-specific code has to provide
- * sense/polarity configuration information for four external interrupt
- * sources INT0 - INT3. This should be done in form of four-byte array
- * (one byte per source ) that contains combination of sensitivity/polarity
- * flags defined in asm-ppc/irq.h.
- *
- * Example of PIC initialization call is shown below:
- *
- * u_char your_board_pic_initsenses[] __initdata = {
- * (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), // INT[0]
- * (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), // INT[1]
- * (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), // INT[2]
- * (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE) // INT[3]
- * };
- *
- * tsi108_pic_init(your_board_pic_initsenses);
- */
-
-void __init tsi108_pic_init(u_char * board_init_senses)
-{
- u_int i;
- u32 sense;
-
- struct device_node *tsi_pic;
- tsi_pic = of_find_node_by_type(NULL, "open-pic");
- if (tsi_pic) {
- unsigned int size;
- void *prop = get_property(tsi_pic, "reg", &size);
- tsi108_pic_phy_addr = of_translate_address(tsi_pic, prop);
- }
-
- DBG("%s: Tsi108 pic phy addr = 0x%x\n", __FUNCTION__,
- (u32) tsi108_pic_phy_addr);
- if (tsi108_pic_phy_addr == 0) {
- printk("No tsi108 PIC found !\n");
- return;
- }
-
- tsi108_pic_vir_addr = (u32) ioremap(tsi108_pic_phy_addr, 0x400);
-
- tsi108_pic_reset();
-
- if (ppc_md.progress)
- ppc_md.progress("tsi108_pic_init: enter", 0x122);
-
- /* Initialize timer interrupts */
- for (i = 0; i < TSI108PIC_NUM_TIMERS; i++) {
- /* Disabled, Priority 0 */
- tsi108_pic_inittimer(i, 0, IRQ_TSI108_TIMER0 + i);
- /* No processor */
- tsi108_pic_maptimer(i, 0);
- }
-
- /* Init board-specific external sources */
- for (i = 0; i < 4; i++) {
- sense = board_init_senses[i];
-
- if (sense & IRQ_SENSE_MASK)
- irq_desc[TSI108_IRQ(i)].status |= IRQ_LEVEL;
-
- /* Enabled, Priority 8 */
- tsi108_pic_initirq(i, 8, TSI108_IRQ(i),
- (sense & IRQ_POLARITY_MASK),
- (sense & IRQ_SENSE_MASK));
- /* Map to CPU #0 */
- tsi108_pic_mapirq(TSI108_IRQ(i), 1 << 0, 0);
- }
-
- /* Init remaining internal sources. */
- for (; i < TSI108PIC_MAX_SOURCES; i++) {
- /* Disabled, Priority 8, by default - Positive Edge */
- tsi108_pic_initirq(i, 8, TSI108_IRQ(i),
- IRQ_POLARITY_POSITIVE, IRQ_SENSE_EDGE);
- /* Map to CPU #0 */
- tsi108_pic_mapirq(TSI108_IRQ(i), (1 << 0), 0);
- }
-
- /*
- * Change sensitivity to level for sources that require it.
- */
-
- irq_desc[IRQ_TSI108_GIGE0].status |= IRQ_LEVEL;
- irq_desc[IRQ_TSI108_GIGE1].status |= IRQ_LEVEL;
- irq_desc[IRQ_TSI108_PCI].status |= IRQ_LEVEL;
-
- /* Init descriptors */
- for (i = 0; i < TSI108PIC_MAX_SOURCES; i++)
- irq_desc[i + TSI108_IRQ_BASE].handler = &tsi108_pic_irq;
-
- for (i = 0; i < NUM_PCI_IRQS; i++) {
- irq_desc[i + IRQ_PCI_INTAD_BASE].handler = &tsi108_pci_irq;
- irq_desc[i + IRQ_PCI_INTAD_BASE].status |= IRQ_LEVEL;
- }
-
- /* Initialize the spurious interrupt */
- tsi108_pic_set_spurious(TSI108_IRQ_SPURIOUS);
- tsi108_pic_set_task_priority(0);
-
- init_pci_source();
- tsi108_pic_enable_irq(IRQ_TSI108_PCI);
-
- i = tsi108_pic_read_reg(TSI108_INT_VECTOR(0));
- tsi108_pic_write_reg(TSI108_INT_EOI(0), 0);
-
- if (ppc_md.progress)
- ppc_md.progress("tsi108_pic_init: exit", 0x222);
-}
-
-/*
- * Find out the current interrupt
- */
-static u_int tsi108_pic_get_vect(void)
-{
- u_int vec;
-
- vec = tsi108_pic_read_reg(TSI108_INT_VECTOR(0)) & TSI108PIC_VECTOR_MASK;
-
-#ifdef DBG_TSI108_INTERRUPT
- if (vec == TSI108_IRQ_SPURIOUS)
- printk("TSI108: SPURIOUS vec=0x%08x\n", vec);
- else
- printk("TSI108: read vec=0x%08x\n", vec);
-#endif
- return (vec);
-}
-
-static inline void tsi108_pic_eoi(void)
-{
- tsi108_pic_write_reg(TSI108_INT_EOI(0), 0);
- mb();
-}
-
-static void __init tsi108_pic_set_task_priority(u_int pri)
-{
- ASSERT(pri >= 0 && pri < TSI108PIC_NUM_PRI);
-
- tsi108_pic_write_reg(TSI108_INT_TASKP(0),
- pri & TSI108PIC_INT_TASKP_TASKP);
- mb();
-}
-
-static void tsi108_pic_set_spurious(u_int vec)
-{
- ASSERT(vec == TSI108_IRQ_SPURIOUS);
- tsi108_pic_write_reg(TSI108_INT_SVR, vec);
- mb();
-}
-
-#ifdef CONFIG_SMP
-/*
- * Convert a cpu mask from logical to physical cpu numbers.
- */
-static inline u32 physmask(u32 cpumask)
-{
- int i;
- u32 mask = 0;
-
- for (i = 0; i < NR_CPUS; ++i, cpumask >>= 1)
- if (cpu_online(i))
- mask |= (cpumask & 1) << smp_hw_index[i];
- return mask;
-}
-#else
-#define physmask(cpumask) (cpumask)
-#endif
-
-/*
- * Initialize a timer interrupt (and disable it)
- *
- * timer: timer number
- * pri: interrupt source priority
- * vec: the vector it will produce
- */
-static void __init tsi108_pic_inittimer(u_int timer, u_int pri, u_int vec)
-{
- unsigned int gtvpr;
-
- ASSERT(timer >= 0 && timer < TSI108PIC_NUM_TIMERS);
- ASSERT(pri >= 0 && pri < TSI108PIC_NUM_PRI);
- ASSERT(vec >= 0 && vec < TSI108PIC_NUM_VECTORS);
-
- gtvpr = tsi108_pic_read_reg(TSI108_INT_GTVPR(timer));
- gtvpr &= ~(TSI108PIC_PRIORITY_MASK | TSI108PIC_VECTOR_MASK);
- gtvpr |= (pri << 16) | vec;
- tsi108_pic_write_reg(TSI108_INT_GTVPR(timer), gtvpr | TSI108PIC_MASK);
- mb();
-}
-
-/*
- * Map a timer interrupt to one or more CPUs
- */
-static void __init tsi108_pic_maptimer(u_int timer, u_int cpumask)
-{
- ASSERT(timer >= 0 && timer < TSI108PIC_NUM_TIMERS);
-
- tsi108_pic_write_reg(TSI108_INT_GTDR(timer), physmask(cpumask));
- mb();
-}
-
-/*
- * Initalize the interrupt source which will generate an NMI.
- * This raises the interrupt's priority from 8 to 9.
- *
- * irq: The logical IRQ which generates an NMI.
- */
-void __init tsi108_pic_init_nmi_irq(u_int irq)
-{
- u_int offset = get_vector_offset(irq);
- u_int vpr = tsi108_pic_read_reg(offset);
- vpr &= ~TSI108PIC_PRIORITY_MASK;
- tsi108_pic_write_reg(offset, vpr | (9 << 16));
- mb();
-}
-
-/*
- *
- * All functions below take an offset'ed irq argument
- *
- */
-
-/*
- * Hookup a cascade to the tsi108 PIC.
- */
-void __init
-tsi108_pic_hookup_cascade(u_int irq, char *name,
- int (*cascade_fn) (struct pt_regs *))
-{
- tsi108_pic_cascade_irq = irq;
- tsi108_pic_cascade_fn = cascade_fn;
- if (request_irq(irq, no_action, SA_INTERRUPT, name, NULL))
- printk("Unable to get Tsi108 PIC IRQ %d for cascade\n",
- irq - TSI108_IRQ_BASE);
-}
-
-/*
- * Enable/disable an external interrupt source
- *
- * Externally called, irq is an offseted system-wide interrupt number
- */
-static void tsi108_pic_enable_irq(u_int irq)
-{
- u32 offset = get_vector_offset(irq);
- u32 vpr = tsi108_pic_read_reg(offset);
-
- /*
- * Undo sensitivity change (see tsi108_pic_disable_irq())
- */
- if (irq_desc[irq].status & IRQ_LEVEL)
- vpr |= TSI108PIC_INT_IVPR_S;
-
- tsi108_pic_write_reg(offset, vpr & ~TSI108PIC_MASK);
- mb();
-}
-
-static void tsi108_pic_disable_irq(u_int irq)
-{
- u32 offset = get_vector_offset(irq);
- u32 vpr = tsi108_pic_read_reg(offset);
-
- /*
- * Switch level interrupt to edge sensitivity to avoid generation
- * of spurious interrupt request. See design note in Tsi108 PIC
- * section of Tsi108 manual.
- */
- if (irq_desc[irq].status & IRQ_LEVEL)
- vpr &= ~TSI108PIC_INT_IVPR_S;
-
- tsi108_pic_write_reg(offset, vpr | TSI108PIC_MASK);
- mb();
- vpr = tsi108_pic_read_reg(offset);
- if (!(vpr & TSI108PIC_MASK))
- printk("TSI108_PIC: Error - Unable disable IRQ %d\n", irq);
-}
-
-/*
- * Initialize an interrupt source (and disable it!)
- *
- * irq: Tsi108 PIC interrupt source number
- * pri: interrupt source priority
- * vec: the vector it will produce
- * pol: polarity (1 for positive, 0 for negative)
- * sense: 1 for level, 0 for edge
- */
-static void __init
-tsi108_pic_initirq(u_int irq, u_int pri, u_int vec, int pol, int sense)
-{
- unsigned int ivpr;
-
- ivpr = TSI108PIC_MASK | (pri << 16) | vec;
- ivpr |= (IRQ_SENSE_LEVEL == sense) ?
- TSI108PIC_INT_IVPR_S_LEVEL : TSI108PIC_INT_IVPR_S_EDGE;
- ivpr |= (IRQ_POLARITY_POSITIVE == pol) ?
- TSI108PIC_INT_IVPR_P_HIGH : TSI108PIC_INT_IVPR_P_LOW;
- tsi108_pic_write_reg(TSI108_INT_IVPR(irq), ivpr);
- mb();
-}
-
-int tsi108_pic_set_irq_sense(int irq, /* PIC source number */
- int pol, /* interrupt source polarity */
- int sense /* interrupt source sense */
- )
-{
- unsigned int ivpr;
-
- ivpr = tsi108_pic_read_reg(TSI108_INT_IVPR(irq));
-
- if (ivpr & TSI108PIC_ACTIVITY) /* error if source is active */
- return -1;
-
- if (0 == (ivpr & TSI108PIC_MASK)) {
- ivpr |= TSI108PIC_MASK; /* mask IRQ prior making changes */
- tsi108_pic_write_reg(TSI108_INT_IVPR(irq), ivpr);
- }
-
- /* clear bits to be changed */
- ivpr &= ~(TSI108PIC_INT_IVPR_P | TSI108PIC_INT_IVPR_S);
-
- ivpr |= (IRQ_SENSE_LEVEL == sense) ?
- TSI108PIC_INT_IVPR_S_LEVEL : TSI108PIC_INT_IVPR_S_EDGE;
- ivpr |= (IRQ_POLARITY_POSITIVE == pol) ?
- TSI108PIC_INT_IVPR_P_HIGH : TSI108PIC_INT_IVPR_P_LOW;
-
- tsi108_pic_write_reg(TSI108_INT_IVPR(irq), ivpr);
- return 0;
-}
-
-/*
- * Map an interrupt source to one or more CPUs
- */
-static void tsi108_pic_mapirq(u_int irq, u_int physmask, u_int keepmask)
-{
- u_int offset = get_vector_offset(irq);
-
- if (0 == offset)
- return;
- if (keepmask != 0)
- physmask |= tsi108_pic_read_reg(offset + 4);
- tsi108_pic_write_reg(offset + 4, physmask);
- mb();
-}
-
-/* No spinlocks, should not be necessary with the Tsi108 PIC
- * (1 register = 1 interrupt and we have the desc lock).
- */
-static void tsi108_pic_ack_irq(unsigned int irq_nr)
-{
- if ((irq_desc[irq_nr].status & IRQ_LEVEL) == 0)
- tsi108_pic_eoi();
-}
-
-static void tsi108_pic_end_irq(unsigned int irq_nr)
-{
- if ((irq_desc[irq_nr].status & IRQ_LEVEL) != 0)
- tsi108_pic_eoi();
-}
-
-void tsi108_pic_set_affinity(unsigned int irq_nr, unsigned long cpumask)
-{
- tsi108_pic_mapirq(irq_nr, physmask(cpumask), 0);
-}
-
-int tsi108_pic_get_irq(struct pt_regs *regs)
-{
- int vector = tsi108_pic_get_vect();
-
- if (vector == TSI108_IRQ_SPURIOUS) {
- vector = -1;
- }
-
- if (vector == IRQ_TSI108_PCI) {
- vector = get_pci_source(vector);
- }
-
- if (vector == -1) {
- tsi108_pic_write_reg(TSI108_INT_EOI(0), 0);
- }
-
- return vector;
-}
-
-static void tsi108_pci_int_mask(u_int irq)
-{
- u_int irp_cfg;
- int int_line = (irq - IRQ_PCI_INTAD_BASE);
-
- irp_cfg = tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL);
- mb();
- irp_cfg |= (1 << int_line); /* INTx_DIR = output */
- irp_cfg &= ~(3 << (8 + (int_line * 2))); /* INTx_TYPE = unused */
- tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL, irp_cfg);
- mb();
- irp_cfg = tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL);
-}
-
-static void tsi108_pci_int_unmask(u_int irq)
-{
- u_int irp_cfg;
- int int_line = (irq - IRQ_PCI_INTAD_BASE);
-
- irp_cfg = tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL);
- mb();
- irp_cfg &= ~(1 << int_line);
- irp_cfg |= (3 << (8 + (int_line * 2)));
- tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL, irp_cfg);
- mb();
-}
-
-static void tsi108_pci_irq_enable(u_int irq)
-{
- tsi108_pci_int_unmask(irq);
-}
-
-static void tsi108_pci_irq_disable(u_int irq)
-{
- tsi108_pci_int_mask(irq);
-}
-
-static void tsi108_pci_irq_ack(u_int irq)
-{
- tsi108_pci_int_mask(irq);
-}
-
-static void tsi108_pci_irq_end(u_int irq)
-{
- tsi108_pic_eoi(); /* eoi IRQ_TSI108_PCI */
- tsi108_pci_int_unmask(irq);
-}
-
-static inline int get_pci_source(int vector)
-{
- u_int temp = 0;
- int irq = -1;
- int i;
- u_int pci_irp_stat;
- static int mask = 0;
-
- /* Read PCI/X block interrupt status register */
- pci_irp_stat = tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_STAT);
- mb();
-
- if (pci_irp_stat & TSI108_PCI_IRP_STAT_P_INT) {
- /* Process Interrupt from PCI bus INTA# - INTD# lines */
- temp =
- tsi108_read_reg(TSI108_PCI_OFFSET +
- TSI108_PCI_IRP_INTAD) & 0xf;
- mb();
- for (i = 0; i < 4; i++, mask++) {
- if (temp & (1 << mask % 4)) {
- irq = IRQ_PCI_INTA + mask % 4;
- mask++;
- break;
- }
- }
- }
-#ifdef DBG_TSI108_INTERRUPT
- else {
- printk("TSI108_PIC: error in TSI108_PCI_IRP_STAT\n");
- pci_irp_stat =
- tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_STAT);
- temp =
- tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_INTAD);
- mb();
- printk(">> stat=0x%08x intad=0x%08x ", pci_irp_stat, temp);
- temp =
- tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL);
- mb();
- printk("cfg_ctl=0x%08x ", temp);
- temp =
- tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_ENABLE);
- mb();
- printk("irp_enable=0x%08x\n", temp);
- }
-#endif /* DBG_TSI108_INTERRUPT */
-
- return irq;
-}
-
-static void init_pci_source(void)
-{
- tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL,
- 0x0000ff00);
- tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_ENABLE,
- 0x00400000);
- mb();
-}
-
-static struct sysdev_class tsi108_pic_sysclass = {
- set_kset_name("tsi108_pic"),
-};
-
-static struct sys_device device_tsi108_pic = {
- .id = 0,
- .cls = &tsi108_pic_sysclass,
-};
-
-static struct sysdev_driver driver_tsi108_pic = {
-#ifdef CONFIG_PM /* FIXME: placeholder for future development */
- .suspend = &tsi108_pic_suspend,
- .resume = &tsi108_pic_resume,
-#endif /* CONFIG_PM */
-};
-
-static int __init init_tsi108_pic_sysfs(void)
-{
- int rc;
-
- if (!get_csrbase())
- return -ENODEV;
- printk(KERN_DEBUG "Registering tsi108_pic with sysfs...\n");
- rc = sysdev_class_register(&tsi108_pic_sysclass);
- if (rc) {
- printk(KERN_ERR "Failed registering tsi108_pic sys class\n");
- return -ENODEV;
- }
- rc = sysdev_register(&device_tsi108_pic);
- if (rc) {
- printk(KERN_ERR "Failed registering tsi108_pic sys device\n");
- return -ENODEV;
- }
- rc = sysdev_driver_register(&tsi108_pic_sysclass, &driver_tsi108_pic);
- if (rc) {
- printk(KERN_ERR "Failed registering tsi108_pic sys driver\n");
- return -ENODEV;
- }
- return 0;
-}
-
-subsys_initcall(init_tsi108_pic_sysfs);
diff -pNur -x project.pj linux-2.6.17-rc4-tun/include/asm-powerpc/mpic.h linux-2.6.17-rc4-hpc2/include/asm-powerpc/mpic.h
--- linux-2.6.17-rc4-tun/include/asm-powerpc/mpic.h 2006-03-20 00:53:29.000000000 -0500
+++ linux-2.6.17-rc4-hpc2/include/asm-powerpc/mpic.h 2006-05-29 16:11:03.000000000 -0400
@@ -4,6 +4,7 @@
#include <linux/irq.h>
+#ifndef CONFIG_TSI108_BRIDGE
/*
* Global registers
*/
@@ -37,6 +38,7 @@
#define MPIC_GREG_IPI_VECTOR_PRI_1 0x000b0
#define MPIC_GREG_IPI_VECTOR_PRI_2 0x000c0
#define MPIC_GREG_IPI_VECTOR_PRI_3 0x000d0
+#define MPIC_GREG_IPI_STRIDE 0x10
#define MPIC_GREG_SPURIOUS 0x000e0
#define MPIC_GREG_TIMER_FREQ 0x000f0
@@ -91,6 +93,87 @@
#define MPIC_VECPRI_SENSE_MASK 0x00400000
#define MPIC_IRQ_DESTINATION 0x00010
+#else /* CONFIG_TSI108_BRIDGE */
+
+/* Tsi108 implementation of MPIC has many differences form the original one */
+
+/*
+ * Global registers
+ */
+
+#define MPIC_GREG_BASE 0x00000
+
+#define MPIC_GREG_FEATURE_0 0x00000
+#define MPIC_GREG_FEATURE_LAST_SRC_MASK 0x07ff0000
+#define MPIC_GREG_FEATURE_LAST_SRC_SHIFT 16
+#define MPIC_GREG_FEATURE_LAST_CPU_MASK 0x00001f00
+#define MPIC_GREG_FEATURE_LAST_CPU_SHIFT 8
+#define MPIC_GREG_FEATURE_VERSION_MASK 0xff
+#define MPIC_GREG_GLOBAL_CONF_0 0x00004
+#define MPIC_GREG_GCONF_RESET 0x80000000
+#define MPIC_GREG_GCONF_8259_PTHROU_DIS 0x00000000
+#define MPIC_GREG_IPI_VECTOR_PRI_0 0x00204 /* Doorbell 0 */
+#define MPIC_GREG_IPI_VECTOR_PRI_1 0x00210 /* Doorbell 1 */
+#define MPIC_GREG_IPI_VECTOR_PRI_2 0x0021c /* Doorbell 2 */
+#define MPIC_GREG_IPI_VECTOR_PRI_3 0x00228 /* Doorbell 3 */
+#define MPIC_GREG_IPI_STRIDE 0x0c
+#define MPIC_GREG_SPURIOUS 0x00010
+#define MPIC_GREG_TIMER_FREQ 0x00014
+
+/*
+ * Timer registers
+ */
+#define MPIC_TIMER_BASE 0x0030
+#define MPIC_TIMER_STRIDE 0x10
+
+#define MPIC_TIMER_CURRENT_CNT 0x00000
+#define MPIC_TIMER_BASE_CNT 0x00004
+#define MPIC_TIMER_VECTOR_PRI 0x00008
+#define MPIC_TIMER_DESTINATION 0x0000c
+
+/*
+ * Per-Processor registers
+ */
+
+#define MPIC_CPU_THISBASE 0x00000
+#define MPIC_CPU_BASE 0x00300
+#define MPIC_CPU_STRIDE 0x00040
+
+#define MPIC_CPU_IPI_DISPATCH 0x00200
+#define MPIC_CPU_CURRENT_TASK_PRI 0x00000
+#define MPIC_CPU_TASKPRI_MASK 0x0000000f
+#define MPIC_CPU_INTACK 0x00004
+#define MPIC_CPU_EOI 0x00008
+#define MPIC_CPU_OUTPUT 0x0000c
+#define MPIC_OUTPUT_POLARITY_POSITIVE 0x00000001
+#define MPIC_OUTPUT_POLARITY_NEGATIVE 0x00000000
+#define MPIC_OUTPUT_POLARITY_MASK 0x00000001
+#define MPIC_OUTPUT_SENSE_LEVEL 0x00000002
+#define MPIC_OUTPUT_SENSE_EDGE 0x00000000
+#define MPIC_OUTPUT_SENSE_MASK 0x00000002
+
+/*
+ * Per-source registers
+ */
+
+#define MPIC_IRQ_BASE 0x00100
+#define MPIC_IRQ_STRIDE 0x00008
+#define MPIC_IRQ_VECTOR_PRI 0x00000
+#define MPIC_VECPRI_MASK 0x80000000
+#define MPIC_VECPRI_ACTIVITY 0x40000000 /* Read Only */
+#define MPIC_VECPRI_PRIORITY_MASK 0x000f0000
+#define MPIC_VECPRI_PRIORITY_SHIFT 16
+#define MPIC_VECPRI_VECTOR_MASK 0x000000ff
+#define MPIC_VECPRI_POLARITY_POSITIVE 0x01000000
+#define MPIC_VECPRI_POLARITY_NEGATIVE 0x00000000
+#define MPIC_VECPRI_POLARITY_MASK 0x01000000
+#define MPIC_VECPRI_SENSE_LEVEL 0x02000000
+#define MPIC_VECPRI_SENSE_EDGE 0x00000000
+#define MPIC_VECPRI_SENSE_MASK 0x02000000
+#define MPIC_IRQ_DESTINATION 0x00004
+
+#endif /* CONFIG_TSI108_BRIDGE */
+
#define MPIC_MAX_IRQ_SOURCES 2048
#define MPIC_MAX_CPUS 32
#define MPIC_MAX_ISU 32
diff -pNur -x project.pj linux-2.6.17-rc4-tun/include/asm-powerpc/tsi108_pic.h linux-2.6.17-rc4-hpc2/include/asm-powerpc/tsi108_pic.h
--- linux-2.6.17-rc4-tun/include/asm-powerpc/tsi108_pic.h 2006-05-28 20:13:07.000000000 -0400
+++ linux-2.6.17-rc4-hpc2/include/asm-powerpc/tsi108_pic.h 1969-12-31 19:00:00.000000000 -0500
@@ -1,232 +0,0 @@
-/*
- * (C) Copyright 2005 Tundra Semiconductor Corp.
- * Alex Bounine, <alexandreb@tundra.com).
- *
- * See file CREDITS for list of people who contributed to this
- * project.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
- */
-
-/*
- * arch/ppc/syslib/tsi108_pic.h - Tsi108 Interrupt Controller definitions
- */
-
-#ifndef _LINUX_TSI108_PIC_H
-#define _LINUX_TSI108_PIC_H
-
-#include <asm/tsi108_irq.h>
-
-#ifdef __KERNEL__
-
-/*
- * Tsi108 PIC supports up to 24 interrupt sources and up to 4 processors
- */
-
-#define TSI108PIC_MAX_SOURCES 24
-#define TSI108PIC_MAX_PROCESSORS 4
-
-#define TSI108PIC_NUM_TIMERS 4
-#define TSI108PIC_NUM_DBELLS 4
-#define TSI108PIC_NUM_PROC 4
-#define TSI108PIC_NUM_PRI 16
-#define TSI108PIC_NUM_VECTORS 256
-
-/*
- * Tsi108 PIC Register offsets within block.
- */
-
-/* Registers controlling sources */
-#define TSI108_INT_FRR (0x000)
-#define TSI108_INT_GCR (0x004)
-#define TSI108_INT_SVR (0x010)
-#define TSI108_INT_GTVPR(n) (0x38 + 0x10*(n))
-#define TSI108_INT_GTDR(n) (0x3C + 0x10*(n))
-#define TSI108_INT_IVPR(n) (0x100 + 0x8*(n))
-#define TSI108_INT_IDR(n) (0x104 + 0x8*(n))
-#define TSI108_INT_DVPR(n) (0x204 + 0xC*(n))
-#define TSI108_INT_DDR(n) (0x208 + 0xC*(n))
-#define TSI108_INT_MBVPR(n) (0x284 + 0x10*(n))
-#define TSI108_INT_MBDR(n) (0x288 + 0x10*(n))
-
-/* Registers controlling destinations */
-#define TSI108_INT_TASKP(n) (0x300 + 0x40*(n))
-#define TSI108_INT_VECTOR(n) (0x304 + 0x40*(n))
-#define TSI108_INT_EOI(n) (0x308 + 0x40*(n))
-#define TSI108_INT_CSR(n) (0x30C + 0x40*(n))
-
-/*
- * Generic definitions common for different types of interrupt
- * sources.
- */
-
-#define TSI108PIC_MASK (0x80000000)
-#define TSI108PIC_ACTIVITY (0x40000000)
-#define TSI108PIC_PRIORITY_MASK (0x000f0000)
-#define TSI108PIC_VECTOR_MASK (0x000000ff)
-
-/**********************************************************
- * Register Bit Masks definitions for every register
- */
-
-/* TSI108PIC_INT_FRR : Register Bits Masks Definitions */
-#define TSI108PIC_INT_FRR_VID (0x000000ff)
-#define TSI108PIC_INT_FRR_NCPU (0x00001f00)
-#define TSI108PIC_INT_FRR_NITM (0x0000e000)
-#define TSI108PIC_INT_FRR_NIRQ (0x07ff0000)
-#define TSI108PIC_INT_FRR_NIDOOR (0xe0000000)
-#define TSI108PIC_INT_FRR_RESERVED (0x18000000)
-
-/* TSI108PIC_INT_GCR : Register Bits Masks Definitions */
-#define TSI108PIC_INT_GCR_R (0x80000000)
-#define TSI108PIC_INT_GCR_RESERVED (0x7fffffff)
-
-/* TSI108PIC_INT_ICR : Register Bits Masks Definitions */
-#define TSI108PIC_INT_ICR_R (0x0000000f)
-#define TSI108PIC_INT_ICR_RESERVED (0xfffffff0)
-
-/* TSI108PIC_INT_MVI : Register Bits Masks Definitions */
-#define TSI108PIC_INT_MVI_VID (0x000000ff)
-#define TSI108PIC_INT_MVI_DID (0x0000ff00)
-#define TSI108PIC_INT_MVI_STEP (0x00ff0000)
-#define TSI108PIC_INT_MVI_RESERVED (0xff000000)
-
-/* TSI108PIC_INT_SVR : Register Bits Masks Definitions */
-#define TSI108PIC_INT_SVR_VECTOR (0x000000ff)
-#define TSI108PIC_INT_SVR_RESERVED (0xffffff00)
-
-/* TSI108PIC_INT_TFRR : Register Bits Masks Definitions */
-#define TSI108PIC_INT_TFRR_TIME_FREQ (0xffffffff)
-
-/* TSI108PIC_INT_SOFT_SET : Register Bits Masks Definitions */
-#define TSI108PIC_INT_SOFT_SET_S (0x00ffffff)
-#define TSI108PIC_INT_SOFT_SET_RESERVED (0xff000000)
-
-/* TSI108PIC_INT_SOFT_ENABLE : Register Bits Masks Definitions */
-#define TSI108PIC_INT_SOFT_ENABLE_EN (0x00ffffff)
-#define TSI108PIC_INT_SOFT_ENABLE_RESERVED (0xff000000)
-
-/* TSI108PIC_INT_GTCCR(X) : Register Bits Masks Definitions */
-#define TSI108PIC_INT_GTCCR_COUNT (0x7fffffff)
-#define TSI108PIC_INT_GTCCR_T (0x80000000)
-
-/* TSI108PIC_INT_GTBCR(X) : Register Bits Masks Definitions */
-#define TSI108PIC_INT_GTBCR_B_COUNT (0x7fffffff)
-#define TSI108PIC_INT_GTBCR_CI (0x80000000)
-
-/* TSI108PIC_INT_GTVPR(X) : Register Bits Masks Definitions */
-#define TSI108PIC_INT_GTVPR_VECTOR (0x000000ff)
-#define TSI108PIC_INT_GTVPR_PRIORITY (0x000f0000)
-#define TSI108PIC_INT_GTVPR_PRESCALE (0x00f00000)
-#define TSI108PIC_INT_GTVPR_A (0x40000000)
-#define TSI108PIC_INT_GTVPR_M (0x80000000)
-#define TSI108PIC_INT_GTVPR_RESERVED (0x3f00ff00)
-
-/* TSI108PIC_INT_GTDR(X) : Register Bits Masks Definitions */
-#define TSI108PIC_INT_GTDR_SEL_OUT (0x0000000f)
-#define TSI108PIC_INT_GTDR_RESERVED (0xfffffff0)
-
-/* TSI108PIC_INT_IVPR(X) : Register Bits Masks Definitions */
-#define TSI108PIC_INT_IVPR_VECTOR (0x000000ff)
-#define TSI108PIC_INT_IVPR_PRIORITY (0x000f0000)
-
-#define TSI108PIC_INT_IVPR_P (0x01000000)
-#define TSI108PIC_INT_IVPR_P_LOW (0 << 24)
-#define TSI108PIC_INT_IVPR_P_HIGH (1 << 24)
-
-#define TSI108PIC_INT_IVPR_S (0x02000000)
-#define TSI108PIC_INT_IVPR_S_EDGE (0 << 25)
-#define TSI108PIC_INT_IVPR_S_LEVEL (1 << 25)
-
-#define TSI108PIC_INT_IVPR_MODE (0x20000000)
-#define TSI108PIC_INT_IVPR_A (0x40000000)
-#define TSI108PIC_INT_IVPR_M (0x80000000)
-#define TSI108PIC_INT_IVPR_RESERVED (0x1cf0ff00)
-
-/* TSI108PIC_INT_IDR(X) : Register Bits Masks Definitions */
-#define TSI108PIC_INT_IDR_SEL_OUT (0x0000000f)
-#define TSI108PIC_INT_IDR_RESERVED (0xfffffff0)
-
-/* TSI108PIC_INT_DAR : Register Bits Masks Definitions */
-#define TSI108PIC_INT_DAR_A (0x0000000f)
-#define TSI108PIC_INT_DAR_RESERVED (0xfffffff0)
-
-/* TSI108PIC_INT_DVPR(X) : Register Bits Masks Definitions */
-#define TSI108PIC_INT_DVPR_VECTOR (0x000000ff)
-#define TSI108PIC_INT_DVPR_PRIORITY (0x000f0000)
-#define TSI108PIC_INT_DVPR_A (0x40000000)
-#define TSI108PIC_INT_DVPR_M (0x80000000)
-#define TSI108PIC_INT_DVPR_RESERVED (0x3ff0ff00)
-
-/* TSI108PIC_INT_DDR(X) : Register Bits Masks Definitions */
-#define TSI108PIC_INT_DDR_SEL_OUT (0x0000000f)
-#define TSI108PIC_INT_DDR_RESERVED (0xfffffff0)
-
-/* TSI108PIC_INT_DMR(X) : Register Bits Masks Definitions */
-#define TSI108PIC_INT_DMR_M (0xffffffff)
-
-/* TSI108PIC_INT_MBR(X) : Register Bits Masks Definitions */
-#define TSI108PIC_INT_MBR_M (0xffffffff)
-
-/* TSI108PIC_INT_MBVPR(X) : Register Bits Masks Definitions */
-#define TSI108PIC_INT_MBVPR_VECTOR (0x000000ff)
-#define TSI108PIC_INT_MBVPR_PRIORITY (0x000f0000)
-#define TSI108PIC_INT_MBVPR_A (0x40000000)
-#define TSI108PIC_INT_MBVPR_M (0x80000000)
-#define TSI108PIC_INT_MBVPR_RESERVED (0x3ff0ff00)
-
-/* TSI108PIC_INT_MBDR(X) : Register Bits Masks Definitions */
-#define TSI108PIC_INT_MBDR_SEL_OUT (0x0000000f)
-#define TSI108PIC_INT_MBDR_RESERVED (0xfffffff0)
-
-/* TSI108PIC_INT_TASKP(X) : Register Bits Masks Definitions */
-#define TSI108PIC_INT_TASKP_TASKP (0x0000000f)
-#define TSI108PIC_INT_TASKP_RESERVED (0xfffffff0)
-
-/* TSI108PIC_INT_VECTOR(X) : Register Bits Masks Definitions */
-#define TSI108PIC_INT_VECTOR_VECTOR (0x000000ff)
-#define TSI108PIC_INT_VECTOR_LS_VECTOR (0xff000000)
-#define TSI108PIC_INT_VECTOR_RESERVED (0x00ffff00)
-
-/* TSI108PIC_INT_EOI(X) : Register Bits Masks Definitions */
-#define TSI108PIC_INT_EOI_EOI (0x000000ff)
-#define TSI108PIC_INT_EOI_RESERVED (0xffffff00)
-
-/* TSI108PIC_INT_CSR(X) : Register Bits Masks Definitions */
-#define TSI108PIC_INT_CSR_RESERVED (0xfffffffc)
-
-#define TSI108PIC_INT_CSR_P (1 << 0)
-#define TSI108PIC_INT_CSR_P_LOW (0 << 0)
-#define TSI108PIC_INT_CSR_P_HIGH (1 << 0)
-
-#define TSI108PIC_INT_CSR_S (1 << 1)
-#define TSI108PIC_INT_CSR_S_EDGE (0 << 1)
-#define TSI108PIC_INT_CSR_S_LEVEL (1 << 1)
-
-extern void tsi108_pic_init(u_char * board_init_senses);
-extern void tsi108_pic_reset(void);
-extern void tsi108_pic_set_output(int dest_num, u32 sense, u32 polarity);
-extern int tsi108_pic_source_cfg(int src_num, u32 sense,
- u32 polarity, TSI108_IRQ_MODE mode);
-extern int tsi108_pic_set_vector(int src_num, int vect, int prio);
-extern void tsi108_pic_init_nmi_irq(u_int irq);
-extern void tsi108_pic_hookup_cascade(u_int irq, char *name,
- int (*cascade_fn) (struct pt_regs *));
-extern int tsi108_pic_get_irq(struct pt_regs *regs);
-
-#endif /* __KERNEL__ */
-
-#endif /* _LINUX_TSI108_PIC_H */
^ permalink raw reply [flat|nested] 16+ messages in thread
* RE: [PATCH/2.6.17-rc4 4/10]Powerpc: Add tsi108 pic support
2006-05-30 3:28 [PATCH/2.6.17-rc4 4/10]Powerpc: Add tsi108 pic support Zang Roy-r61911
@ 2006-05-30 4:17 ` Benjamin Herrenschmidt
2006-05-30 19:18 ` Kumar Gala
1 sibling, 0 replies; 16+ messages in thread
From: Benjamin Herrenschmidt @ 2006-05-30 4:17 UTC (permalink / raw)
To: Zang Roy-r61911
Cc: linuxppc-dev list, Yang Xin-Xin-r48390, Paul Mackerras,
Alexandre.Bounine
On Tue, 2006-05-30 at 11:28 +0800, Zang Roy-r61911 wrote:
> Tsi108 implementation of MPIC has many differences form the original one, the following
> code implements it with mpic. Any comment? The following patch is just based on
> my previous send out patches.
>
> Integrate Tundra Semiconductor tsi108 host bridge interrupt controller
> to mpic arch.
As usual... #ifdef's are evil. Only use them to avoid building bits that
are not useful for a given platform, not to change the behaviour of a
driver. By changing the MPIC driver with #ifdef's, you once again
prevent building a generic kernel image that can boot both your board
and pSeries, powermac, etc...
Please add an MPIC flag instead (as there is already for specifying, for
example, big endian MPICs) and add the necessary abstractions (for
example, put some register stride in the mpic instance structure and
initialize them based on the flags etc...)
Cheers,
Ben.
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH/2.6.17-rc4 4/10]Powerpc: Add tsi108 pic support
2006-05-30 3:28 [PATCH/2.6.17-rc4 4/10]Powerpc: Add tsi108 pic support Zang Roy-r61911
2006-05-30 4:17 ` Benjamin Herrenschmidt
@ 2006-05-30 19:18 ` Kumar Gala
1 sibling, 0 replies; 16+ messages in thread
From: Kumar Gala @ 2006-05-30 19:18 UTC (permalink / raw)
To: Zang Roy-r61911
Cc: Yang Xin-Xin-r48390, Paul Mackerras, Alexandre.Bounine,
linuxppc-dev list
On May 29, 2006, at 10:28 PM, Zang Roy-r61911 wrote:
>>
>> On Wed, 2006-05-17 at 18:14 +0800, Zang Roy-r61911 wrote:
>>> Add Tundra Semiconductor tsi108 host bridge interrupt
>> controller support.
>>
>> It looks a bit like an hacked up MPIC... Is it different
>> enough to justify a separate driver ? Or would it be possible
>> to define a TSI108 flag to pass the current mpic driver and
>> add the necessary bits to it ?
>>
>
> Tsi108 implementation of MPIC has many differences form the
> original one, the following
> code implements it with mpic. Any comment? The following patch is
> just based on
> my previous send out patches.
In the future please provide it against the base, much easier to read.
> Integrate Tundra Semiconductor tsi108 host bridge interrupt controller
> to mpic arch.
>
> Signed-off-by: Alexandre Bounine <alexandreb@tundra.com>
>
[snip]
>
> --- linux-2.6.17-rc4-tun/arch/powerpc/sysdev/mpic.c 2006-03-20
> 00:53:29.000000000 -0500
> +++ linux-2.6.17-rc4-hpc2/arch/powerpc/sysdev/mpic.c 2006-05-29
> 16:07:03.000000000 -0400
> @@ -81,7 +81,7 @@ static inline void _mpic_write(unsigned
> static inline u32 _mpic_ipi_read(struct mpic *mpic, unsigned int ipi)
> {
> unsigned int be = (mpic->flags & MPIC_BIG_ENDIAN) != 0;
> - unsigned int offset = MPIC_GREG_IPI_VECTOR_PRI_0 + (ipi * 0x10);
> + unsigned int offset = MPIC_GREG_IPI_VECTOR_PRI_0 + (ipi *
> MPIC_GREG_IPI_STRIDE);
>
> if (mpic->flags & MPIC_BROKEN_IPI)
> be = !be;
> @@ -90,7 +90,7 @@ static inline u32 _mpic_ipi_read(struct
>
> static inline void _mpic_ipi_write(struct mpic *mpic, unsigned int
> ipi, u32 value)
> {
> - unsigned int offset = MPIC_GREG_IPI_VECTOR_PRI_0 + (ipi * 0x10);
> + unsigned int offset = MPIC_GREG_IPI_VECTOR_PRI_0 + (ipi *
> MPIC_GREG_IPI_STRIDE);
>
> _mpic_write(mpic->flags & MPIC_BIG_ENDIAN, mpic->gregs, offset,
> value);
> }
> @@ -393,7 +393,11 @@ static inline struct mpic * mpic_from_ir
> static inline void mpic_eoi(struct mpic *mpic)
> {
> mpic_cpu_write(MPIC_CPU_EOI, 0);
> +#ifndef CONFIG_TSI108_BRIDGE
> (void)mpic_cpu_read(MPIC_CPU_WHOAMI);
> +#else
> + (void)mpic_cpu_read(MPIC_CPU_OUTPUT);
> +#endif
> }
>
> #ifdef CONFIG_SMP
> @@ -514,9 +518,26 @@ static void mpic_end_irq(unsigned int ir
> }
> #endif /* CONFIG_MPIC_BROKEN_U3 */
>
> +#ifdef CONFIG_TSI108_BRIDGE
> + if ((irq_desc[irq].status & IRQ_LEVEL) != 0)
> +#endif
Why do you have to check if its a LEVEL irq?
> mpic_eoi(mpic);
> }
>
> +#ifdef CONFIG_TSI108_BRIDGE
> +static void mpic_ack_irq(unsigned int irq)
> +{
> + struct mpic *mpic = mpic_from_irq(irq);
> +
> +#ifdef DEBUG_IRQ
> + DBG("%s: ack_irq: %d\n", mpic->name, irq);
> +#endif
> +
> + if ((irq_desc[irq].status & IRQ_LEVEL) == 0)
> + mpic_eoi(mpic);
> +}
> +#endif /* CONFIG_TSI108_BRIDGE */
> +
if the PIC works like other openpic's you dont need an 'ack' we
handle it via 'end'
> #ifdef CONFIG_SMP
>
> static void mpic_enable_ipi(unsigned int irq)
> @@ -596,6 +617,9 @@ struct mpic * __init mpic_alloc(unsigned
> mpic->hc_irq.enable = mpic_enable_irq;
> mpic->hc_irq.disable = mpic_disable_irq;
> mpic->hc_irq.end = mpic_end_irq;
> +#ifdef CONFIG_TSI108_BRIDGE
> + mpic->hc_irq.ack = mpic_ack_irq;
> +#endif
> if (flags & MPIC_PRIMARY)
> mpic->hc_irq.set_affinity = mpic_set_affinity;
> #ifdef CONFIG_SMP
> @@ -955,8 +979,13 @@ void mpic_send_ipi(unsigned int ipi_no,
> DBG("%s: send_ipi(ipi_no: %d)\n", mpic->name, ipi_no);
> #endif
>
> +#ifndef CONFIG_TSI108_BRIDGE
> mpic_cpu_write(MPIC_CPU_IPI_DISPATCH_0 + ipi_no * 0x10,
> mpic_physmask(cpu_mask & cpus_addr(cpu_online_map)[0]));
> +#else /* CONFIG_TSI108_BRIDGE */
> + mpic_write(mpic->gregs, MPIC_CPU_IPI_DISPATCH,
> + mpic_physmask(cpu_mask & cpus_addr(cpu_online_map)[0]));
> +#endif /* !CONFIG_TSI108_BRIDGE */
> }
>
> int mpic_get_one_irq(struct mpic *mpic, struct pt_regs *regs)
> @@ -972,11 +1001,20 @@ int mpic_get_one_irq(struct mpic *mpic,
> DBG("%s: cascading ...\n", mpic->name);
> #endif
> irq = mpic->cascade(regs, mpic->cascade_data);
> +#ifdef DEBUG_LOW
> + DBG("%s: cascaded irq: %d\n", mpic->name, irq);
> +#endif
> +#ifndef CONFIG_TSI108_BRIDGE
> mpic_eoi(mpic);
> +#endif
> return irq;
> }
> - if (unlikely(irq == MPIC_VEC_SPURRIOUS))
> + if (unlikely(irq == MPIC_VEC_SPURRIOUS)) {
> +#ifdef CONFIG_TSI108_BRIDGE
> + mpic_eoi(mpic);
> +#endif
> return -1;
> + }
why the changes to where we do mpic_eoi for TSI108?
> if (irq < MPIC_VEC_IPI_0) {
> #ifdef DEBUG_IRQ
> DBG("%s: irq %d\n", mpic->name, irq + mpic->irq_offset);
[snip]
^ permalink raw reply [flat|nested] 16+ messages in thread
* RE: [PATCH/2.6.17-rc4 4/10]Powerpc: Add tsi108 pic support
@ 2006-06-01 20:45 Alexandre Bounine
2006-06-01 22:06 ` Benjamin Herrenschmidt
0 siblings, 1 reply; 16+ messages in thread
From: Alexandre Bounine @ 2006-06-01 20:45 UTC (permalink / raw)
To: Kumar Gala, Zang Roy-r61911
Cc: Yang Xin-Xin-r48390, Paul Mackerras, linuxppc-dev list
All differences in the Tsi108/109 PIC code from the standard MPIC are =
caused by the HW behavior. The Tsi108/109 PIC looks like standard =
OpenPIC but, in fact, is different in registers mapping and behavior. =
Its logic is close but not exactly as MPIC. =20
Here are replies on comments to the code:
1.Why do you have to check if its a LEVEL irq?
Check for LEVEL irqs is required in the ack/end pair to enable nested =
interrupt servicing and
does not hang when core (local) interrupts are re-enabled in the ISR. =
Otherwise we have to use=20
SA_INTERRUPT flag for all level signaled interrupts.
2. if the PIC works like other openpic's you dont need an 'ack' we =20
handle it via 'end'
Tsi108/109 needs it.
3. why the changes to where we do mpic_eoi for TSI108?
The Tsi108 PIC requires EOI for spurious interrupt (as all other =
interrupt sources).
The do_IRQ() does not call end routine for spurious interrupts. =20
The MPIC code patch for Tsi108/109 demonstrates HW differences and why =
we originally considered having separate code for Tsi108 pic.
Regards,
Alex.
=20
-----Original Message-----
From: Kumar Gala [mailto:galak@kernel.crashing.org]
Sent: Tuesday, May 30, 2006 3:18 PM
To: Zang Roy-r61911
Cc: Benjamin Herrenschmidt; linuxppc-dev list; Yang Xin-Xin-r48390; Paul
Mackerras; Alexandre Bounine
Subject: Re: [PATCH/2.6.17-rc4 4/10]Powerpc: Add tsi108 pic support
On May 29, 2006, at 10:28 PM, Zang Roy-r61911 wrote:
>>
>> On Wed, 2006-05-17 at 18:14 +0800, Zang Roy-r61911 wrote:
>>> Add Tundra Semiconductor tsi108 host bridge interrupt
>> controller support.
>>
>> It looks a bit like an hacked up MPIC... Is it different
>> enough to justify a separate driver ? Or would it be possible
>> to define a TSI108 flag to pass the current mpic driver and
>> add the necessary bits to it ?
>>
>
> Tsi108 implementation of MPIC has many differences form the =20
> original one, the following
> code implements it with mpic. Any comment? The following patch is =20
> just based on
> my previous send out patches.
In the future please provide it against the base, much easier to read.
> Integrate Tundra Semiconductor tsi108 host bridge interrupt controller
> to mpic arch.
>
> Signed-off-by: Alexandre Bounine <alexandreb@tundra.com>
>
[snip]
>
> --- linux-2.6.17-rc4-tun/arch/powerpc/sysdev/mpic.c 2006-03-20 =20
> 00:53:29.000000000 -0500
> +++ linux-2.6.17-rc4-hpc2/arch/powerpc/sysdev/mpic.c 2006-05-29 =20
> 16:07:03.000000000 -0400
> @@ -81,7 +81,7 @@ static inline void _mpic_write(unsigned
> static inline u32 _mpic_ipi_read(struct mpic *mpic, unsigned int ipi)
> {
> unsigned int be =3D (mpic->flags & MPIC_BIG_ENDIAN) !=3D 0;
> - unsigned int offset =3D MPIC_GREG_IPI_VECTOR_PRI_0 + (ipi * 0x10);
> + unsigned int offset =3D MPIC_GREG_IPI_VECTOR_PRI_0 + (ipi * =20
> MPIC_GREG_IPI_STRIDE);
>
> if (mpic->flags & MPIC_BROKEN_IPI)
> be =3D !be;
> @@ -90,7 +90,7 @@ static inline u32 _mpic_ipi_read(struct
>
> static inline void _mpic_ipi_write(struct mpic *mpic, unsigned int =20
> ipi, u32 value)
> {
> - unsigned int offset =3D MPIC_GREG_IPI_VECTOR_PRI_0 + (ipi * 0x10);
> + unsigned int offset =3D MPIC_GREG_IPI_VECTOR_PRI_0 + (ipi * =20
> MPIC_GREG_IPI_STRIDE);
>
> _mpic_write(mpic->flags & MPIC_BIG_ENDIAN, mpic->gregs, offset, =20
> value);
> }
> @@ -393,7 +393,11 @@ static inline struct mpic * mpic_from_ir
> static inline void mpic_eoi(struct mpic *mpic)
> {
> mpic_cpu_write(MPIC_CPU_EOI, 0);
> +#ifndef CONFIG_TSI108_BRIDGE
> (void)mpic_cpu_read(MPIC_CPU_WHOAMI);
> +#else
> + (void)mpic_cpu_read(MPIC_CPU_OUTPUT);
> +#endif
> }
>
> #ifdef CONFIG_SMP
> @@ -514,9 +518,26 @@ static void mpic_end_irq(unsigned int ir
> }
> #endif /* CONFIG_MPIC_BROKEN_U3 */
>
> +#ifdef CONFIG_TSI108_BRIDGE
> + if ((irq_desc[irq].status & IRQ_LEVEL) !=3D 0)
> +#endif
Why do you have to check if its a LEVEL irq?
> mpic_eoi(mpic);
> }
>
> +#ifdef CONFIG_TSI108_BRIDGE
> +static void mpic_ack_irq(unsigned int irq)
> +{
> + struct mpic *mpic =3D mpic_from_irq(irq);
> +
> +#ifdef DEBUG_IRQ
> + DBG("%s: ack_irq: %d\n", mpic->name, irq);
> +#endif
> +
> + if ((irq_desc[irq].status & IRQ_LEVEL) =3D=3D 0)
> + mpic_eoi(mpic);
> +}
> +#endif /* CONFIG_TSI108_BRIDGE */
> +
if the PIC works like other openpic's you dont need an 'ack' we =20
handle it via 'end'
> #ifdef CONFIG_SMP
>
> static void mpic_enable_ipi(unsigned int irq)
> @@ -596,6 +617,9 @@ struct mpic * __init mpic_alloc(unsigned
> mpic->hc_irq.enable =3D mpic_enable_irq;
> mpic->hc_irq.disable =3D mpic_disable_irq;
> mpic->hc_irq.end =3D mpic_end_irq;
> +#ifdef CONFIG_TSI108_BRIDGE
> + mpic->hc_irq.ack =3D mpic_ack_irq;
> +#endif
> if (flags & MPIC_PRIMARY)
> mpic->hc_irq.set_affinity =3D mpic_set_affinity;
> #ifdef CONFIG_SMP
> @@ -955,8 +979,13 @@ void mpic_send_ipi(unsigned int ipi_no,
> DBG("%s: send_ipi(ipi_no: %d)\n", mpic->name, ipi_no);
> #endif
>
> +#ifndef CONFIG_TSI108_BRIDGE
> mpic_cpu_write(MPIC_CPU_IPI_DISPATCH_0 + ipi_no * 0x10,
> mpic_physmask(cpu_mask & cpus_addr(cpu_online_map)[0]));
> +#else /* CONFIG_TSI108_BRIDGE */
> + mpic_write(mpic->gregs, MPIC_CPU_IPI_DISPATCH,
> + mpic_physmask(cpu_mask & cpus_addr(cpu_online_map)[0]));
> +#endif /* !CONFIG_TSI108_BRIDGE */
> }
>
> int mpic_get_one_irq(struct mpic *mpic, struct pt_regs *regs)
> @@ -972,11 +1001,20 @@ int mpic_get_one_irq(struct mpic *mpic,
> DBG("%s: cascading ...\n", mpic->name);
> #endif
> irq =3D mpic->cascade(regs, mpic->cascade_data);
> +#ifdef DEBUG_LOW
> + DBG("%s: cascaded irq: %d\n", mpic->name, irq);
> +#endif
> +#ifndef CONFIG_TSI108_BRIDGE
> mpic_eoi(mpic);
> +#endif
> return irq;
> }
> - if (unlikely(irq =3D=3D MPIC_VEC_SPURRIOUS))
> + if (unlikely(irq =3D=3D MPIC_VEC_SPURRIOUS)) {
> +#ifdef CONFIG_TSI108_BRIDGE
> + mpic_eoi(mpic);
> +#endif
> return -1;
> + }
why the changes to where we do mpic_eoi for TSI108?
> if (irq < MPIC_VEC_IPI_0) {
> #ifdef DEBUG_IRQ
> DBG("%s: irq %d\n", mpic->name, irq + mpic->irq_offset);
[snip]
^ permalink raw reply [flat|nested] 16+ messages in thread
* RE: [PATCH/2.6.17-rc4 4/10]Powerpc: Add tsi108 pic support
2006-06-01 20:45 Alexandre Bounine
@ 2006-06-01 22:06 ` Benjamin Herrenschmidt
0 siblings, 0 replies; 16+ messages in thread
From: Benjamin Herrenschmidt @ 2006-06-01 22:06 UTC (permalink / raw)
To: Alexandre Bounine; +Cc: linuxppc-dev list, Paul Mackerras, Yang Xin-Xin-r48390
On Thu, 2006-06-01 at 16:45 -0400, Alexandre Bounine wrote:
> All differences in the Tsi108/109 PIC code from the standard MPIC are caused by the HW behavior. The Tsi108/109 PIC looks like standard OpenPIC but, in fact, is different in registers mapping and behavior. Its logic is close but not exactly as MPIC.
>
> Here are replies on comments to the code:
>
> 1.Why do you have to check if its a LEVEL irq?
>
> Check for LEVEL irqs is required in the ack/end pair to enable nested interrupt servicing and
> does not hang when core (local) interrupts are re-enabled in the ISR. Otherwise we have to use
> SA_INTERRUPT flag for all level signaled interrupts.
Can you be more precise about what exactly happens and why ? Unless you
EOI handling is broken of course, there should be no need to do anything
other than a single eoi in end(), period.
> 2. if the PIC works like other openpic's you dont need an 'ack' we
> handle it via 'end'
>
> Tsi108/109 needs it.
What for ? Please, give the low level details.
> 3. why the changes to where we do mpic_eoi for TSI108?
> The Tsi108 PIC requires EOI for spurious interrupt (as all other interrupt sources).
Ok, that is acceptable.
> The do_IRQ() does not call end routine for spurious interrupts.
>
> The MPIC code patch for Tsi108/109 demonstrates HW differences and why we originally considered having separate code for Tsi108 pic.
Please tell me more about the HW differences :)
Ben.
>
>
> -----Original Message-----
> From: Kumar Gala [mailto:galak@kernel.crashing.org]
> Sent: Tuesday, May 30, 2006 3:18 PM
> To: Zang Roy-r61911
> Cc: Benjamin Herrenschmidt; linuxppc-dev list; Yang Xin-Xin-r48390; Paul
> Mackerras; Alexandre Bounine
> Subject: Re: [PATCH/2.6.17-rc4 4/10]Powerpc: Add tsi108 pic support
>
>
>
> On May 29, 2006, at 10:28 PM, Zang Roy-r61911 wrote:
>
> >>
> >> On Wed, 2006-05-17 at 18:14 +0800, Zang Roy-r61911 wrote:
> >>> Add Tundra Semiconductor tsi108 host bridge interrupt
> >> controller support.
> >>
> >> It looks a bit like an hacked up MPIC... Is it different
> >> enough to justify a separate driver ? Or would it be possible
> >> to define a TSI108 flag to pass the current mpic driver and
> >> add the necessary bits to it ?
> >>
> >
> > Tsi108 implementation of MPIC has many differences form the
> > original one, the following
> > code implements it with mpic. Any comment? The following patch is
> > just based on
> > my previous send out patches.
>
> In the future please provide it against the base, much easier to read.
>
> > Integrate Tundra Semiconductor tsi108 host bridge interrupt controller
> > to mpic arch.
> >
> > Signed-off-by: Alexandre Bounine <alexandreb@tundra.com>
> >
>
> [snip]
>
> >
> > --- linux-2.6.17-rc4-tun/arch/powerpc/sysdev/mpic.c 2006-03-20
> > 00:53:29.000000000 -0500
> > +++ linux-2.6.17-rc4-hpc2/arch/powerpc/sysdev/mpic.c 2006-05-29
> > 16:07:03.000000000 -0400
> > @@ -81,7 +81,7 @@ static inline void _mpic_write(unsigned
> > static inline u32 _mpic_ipi_read(struct mpic *mpic, unsigned int ipi)
> > {
> > unsigned int be = (mpic->flags & MPIC_BIG_ENDIAN) != 0;
> > - unsigned int offset = MPIC_GREG_IPI_VECTOR_PRI_0 + (ipi * 0x10);
> > + unsigned int offset = MPIC_GREG_IPI_VECTOR_PRI_0 + (ipi *
> > MPIC_GREG_IPI_STRIDE);
> >
> > if (mpic->flags & MPIC_BROKEN_IPI)
> > be = !be;
> > @@ -90,7 +90,7 @@ static inline u32 _mpic_ipi_read(struct
> >
> > static inline void _mpic_ipi_write(struct mpic *mpic, unsigned int
> > ipi, u32 value)
> > {
> > - unsigned int offset = MPIC_GREG_IPI_VECTOR_PRI_0 + (ipi * 0x10);
> > + unsigned int offset = MPIC_GREG_IPI_VECTOR_PRI_0 + (ipi *
> > MPIC_GREG_IPI_STRIDE);
> >
> > _mpic_write(mpic->flags & MPIC_BIG_ENDIAN, mpic->gregs, offset,
> > value);
> > }
> > @@ -393,7 +393,11 @@ static inline struct mpic * mpic_from_ir
> > static inline void mpic_eoi(struct mpic *mpic)
> > {
> > mpic_cpu_write(MPIC_CPU_EOI, 0);
> > +#ifndef CONFIG_TSI108_BRIDGE
> > (void)mpic_cpu_read(MPIC_CPU_WHOAMI);
> > +#else
> > + (void)mpic_cpu_read(MPIC_CPU_OUTPUT);
> > +#endif
> > }
> >
> > #ifdef CONFIG_SMP
> > @@ -514,9 +518,26 @@ static void mpic_end_irq(unsigned int ir
> > }
> > #endif /* CONFIG_MPIC_BROKEN_U3 */
> >
> > +#ifdef CONFIG_TSI108_BRIDGE
> > + if ((irq_desc[irq].status & IRQ_LEVEL) != 0)
> > +#endif
>
> Why do you have to check if its a LEVEL irq?
>
> > mpic_eoi(mpic);
> > }
> >
> > +#ifdef CONFIG_TSI108_BRIDGE
> > +static void mpic_ack_irq(unsigned int irq)
> > +{
> > + struct mpic *mpic = mpic_from_irq(irq);
> > +
> > +#ifdef DEBUG_IRQ
> > + DBG("%s: ack_irq: %d\n", mpic->name, irq);
> > +#endif
> > +
> > + if ((irq_desc[irq].status & IRQ_LEVEL) == 0)
> > + mpic_eoi(mpic);
> > +}
> > +#endif /* CONFIG_TSI108_BRIDGE */
> > +
>
> if the PIC works like other openpic's you dont need an 'ack' we
> handle it via 'end'
>
> > #ifdef CONFIG_SMP
> >
> > static void mpic_enable_ipi(unsigned int irq)
> > @@ -596,6 +617,9 @@ struct mpic * __init mpic_alloc(unsigned
> > mpic->hc_irq.enable = mpic_enable_irq;
> > mpic->hc_irq.disable = mpic_disable_irq;
> > mpic->hc_irq.end = mpic_end_irq;
> > +#ifdef CONFIG_TSI108_BRIDGE
> > + mpic->hc_irq.ack = mpic_ack_irq;
> > +#endif
> > if (flags & MPIC_PRIMARY)
> > mpic->hc_irq.set_affinity = mpic_set_affinity;
> > #ifdef CONFIG_SMP
> > @@ -955,8 +979,13 @@ void mpic_send_ipi(unsigned int ipi_no,
> > DBG("%s: send_ipi(ipi_no: %d)\n", mpic->name, ipi_no);
> > #endif
> >
> > +#ifndef CONFIG_TSI108_BRIDGE
> > mpic_cpu_write(MPIC_CPU_IPI_DISPATCH_0 + ipi_no * 0x10,
> > mpic_physmask(cpu_mask & cpus_addr(cpu_online_map)[0]));
> > +#else /* CONFIG_TSI108_BRIDGE */
> > + mpic_write(mpic->gregs, MPIC_CPU_IPI_DISPATCH,
> > + mpic_physmask(cpu_mask & cpus_addr(cpu_online_map)[0]));
> > +#endif /* !CONFIG_TSI108_BRIDGE */
> > }
> >
> > int mpic_get_one_irq(struct mpic *mpic, struct pt_regs *regs)
> > @@ -972,11 +1001,20 @@ int mpic_get_one_irq(struct mpic *mpic,
> > DBG("%s: cascading ...\n", mpic->name);
> > #endif
> > irq = mpic->cascade(regs, mpic->cascade_data);
> > +#ifdef DEBUG_LOW
> > + DBG("%s: cascaded irq: %d\n", mpic->name, irq);
> > +#endif
> > +#ifndef CONFIG_TSI108_BRIDGE
> > mpic_eoi(mpic);
> > +#endif
> > return irq;
> > }
> > - if (unlikely(irq == MPIC_VEC_SPURRIOUS))
> > + if (unlikely(irq == MPIC_VEC_SPURRIOUS)) {
> > +#ifdef CONFIG_TSI108_BRIDGE
> > + mpic_eoi(mpic);
> > +#endif
> > return -1;
> > + }
>
> why the changes to where we do mpic_eoi for TSI108?
>
> > if (irq < MPIC_VEC_IPI_0) {
> > #ifdef DEBUG_IRQ
> > DBG("%s: irq %d\n", mpic->name, irq + mpic->irq_offset);
>
>
>
> [snip]
^ permalink raw reply [flat|nested] 16+ messages in thread
* RE: [PATCH/2.6.17-rc4 4/10]Powerpc: Add tsi108 pic support
@ 2006-06-02 21:30 Alexandre Bounine
0 siblings, 0 replies; 16+ messages in thread
From: Alexandre Bounine @ 2006-06-02 21:30 UTC (permalink / raw)
To: Benjamin Herrenschmidt
Cc: linuxppc-dev list, Paul Mackerras, Yang Xin-Xin-r48390
The items 1 and 2 not needed. I moved them from the tsi108_pic.c but =
really they have been leftovers from the old code there. Originally code =
ppc/syslib/open_pic.c was used as a template, which was good fit for the =
blocking output mode of the tsi108 PIC (plus some extra tweaks). In that =
mode EOI has to be signaled to PIC to deactivate interrupt line to the =
CPU before it re-enables local interrupts. This is why we have got ack =
routine. We changed mode later and removed most of workarounds except =
these two.
I removed code for 1 and 2 and will send a patch to Roy after retesting =
(probably this weekend
with some other stuff).
Alex.
=20
-----Original Message-----
From: Benjamin Herrenschmidt [mailto:benh@kernel.crashing.org]
Sent: Thursday, June 01, 2006 6:06 PM
To: Alexandre Bounine
Cc: Kumar Gala; Zang Roy-r61911; linuxppc-dev list; Yang Xin-Xin-r48390;
Paul Mackerras
Subject: RE: [PATCH/2.6.17-rc4 4/10]Powerpc: Add tsi108 pic support
On Thu, 2006-06-01 at 16:45 -0400, Alexandre Bounine wrote:
> All differences in the Tsi108/109 PIC code from the standard MPIC are =
caused by the HW behavior. The Tsi108/109 PIC looks like standard =
OpenPIC but, in fact, is different in registers mapping and behavior. =
Its logic is close but not exactly as MPIC. =20
>=20
> Here are replies on comments to the code:
>=20
> 1.Why do you have to check if its a LEVEL irq?
>=20
> Check for LEVEL irqs is required in the ack/end pair to enable nested =
interrupt servicing and
> does not hang when core (local) interrupts are re-enabled in the ISR. =
Otherwise we have to use=20
> SA_INTERRUPT flag for all level signaled interrupts.
Can you be more precise about what exactly happens and why ? Unless you
EOI handling is broken of course, there should be no need to do anything
other than a single eoi in end(), period.
> 2. if the PIC works like other openpic's you dont need an 'ack' we =20
> handle it via 'end'
>=20
> Tsi108/109 needs it.
What for ? Please, give the low level details.
> 3. why the changes to where we do mpic_eoi for TSI108?
> The Tsi108 PIC requires EOI for spurious interrupt (as all other =
interrupt sources).
Ok, that is acceptable.
> The do_IRQ() does not call end routine for spurious interrupts. =20
>=20
> The MPIC code patch for Tsi108/109 demonstrates HW differences and why =
we originally considered having separate code for Tsi108 pic.
Please tell me more about the HW differences :)
Ben.
=20
>=20
>=20
> -----Original Message-----
> From: Kumar Gala [mailto:galak@kernel.crashing.org]
> Sent: Tuesday, May 30, 2006 3:18 PM
> To: Zang Roy-r61911
> Cc: Benjamin Herrenschmidt; linuxppc-dev list; Yang Xin-Xin-r48390; =
Paul
> Mackerras; Alexandre Bounine
> Subject: Re: [PATCH/2.6.17-rc4 4/10]Powerpc: Add tsi108 pic support
>=20
>=20
>=20
> On May 29, 2006, at 10:28 PM, Zang Roy-r61911 wrote:
>=20
> >>
> >> On Wed, 2006-05-17 at 18:14 +0800, Zang Roy-r61911 wrote:
> >>> Add Tundra Semiconductor tsi108 host bridge interrupt
> >> controller support.
> >>
> >> It looks a bit like an hacked up MPIC... Is it different
> >> enough to justify a separate driver ? Or would it be possible
> >> to define a TSI108 flag to pass the current mpic driver and
> >> add the necessary bits to it ?
> >>
> >
> > Tsi108 implementation of MPIC has many differences form the =20
> > original one, the following
> > code implements it with mpic. Any comment? The following patch is =20
> > just based on
> > my previous send out patches.
>=20
> In the future please provide it against the base, much easier to read.
>=20
> > Integrate Tundra Semiconductor tsi108 host bridge interrupt =
controller
> > to mpic arch.
> >
> > Signed-off-by: Alexandre Bounine <alexandreb@tundra.com>
> >
>=20
> [snip]
>=20
> >
> > --- linux-2.6.17-rc4-tun/arch/powerpc/sysdev/mpic.c 2006-03-20 =20
> > 00:53:29.000000000 -0500
> > +++ linux-2.6.17-rc4-hpc2/arch/powerpc/sysdev/mpic.c 2006-05-29 =20
> > 16:07:03.000000000 -0400
> > @@ -81,7 +81,7 @@ static inline void _mpic_write(unsigned
> > static inline u32 _mpic_ipi_read(struct mpic *mpic, unsigned int =
ipi)
> > {
> > unsigned int be =3D (mpic->flags & MPIC_BIG_ENDIAN) !=3D 0;
> > - unsigned int offset =3D MPIC_GREG_IPI_VECTOR_PRI_0 + (ipi * 0x10);
> > + unsigned int offset =3D MPIC_GREG_IPI_VECTOR_PRI_0 + (ipi * =20
> > MPIC_GREG_IPI_STRIDE);
> >
> > if (mpic->flags & MPIC_BROKEN_IPI)
> > be =3D !be;
> > @@ -90,7 +90,7 @@ static inline u32 _mpic_ipi_read(struct
> >
> > static inline void _mpic_ipi_write(struct mpic *mpic, unsigned int =
> > ipi, u32 value)
> > {
> > - unsigned int offset =3D MPIC_GREG_IPI_VECTOR_PRI_0 + (ipi * 0x10);
> > + unsigned int offset =3D MPIC_GREG_IPI_VECTOR_PRI_0 + (ipi * =20
> > MPIC_GREG_IPI_STRIDE);
> >
> > _mpic_write(mpic->flags & MPIC_BIG_ENDIAN, mpic->gregs, offset, =20
> > value);
> > }
> > @@ -393,7 +393,11 @@ static inline struct mpic * mpic_from_ir
> > static inline void mpic_eoi(struct mpic *mpic)
> > {
> > mpic_cpu_write(MPIC_CPU_EOI, 0);
> > +#ifndef CONFIG_TSI108_BRIDGE
> > (void)mpic_cpu_read(MPIC_CPU_WHOAMI);
> > +#else
> > + (void)mpic_cpu_read(MPIC_CPU_OUTPUT);
> > +#endif
> > }
> >
> > #ifdef CONFIG_SMP
> > @@ -514,9 +518,26 @@ static void mpic_end_irq(unsigned int ir
> > }
> > #endif /* CONFIG_MPIC_BROKEN_U3 */
> >
> > +#ifdef CONFIG_TSI108_BRIDGE
> > + if ((irq_desc[irq].status & IRQ_LEVEL) !=3D 0)
> > +#endif
>=20
> Why do you have to check if its a LEVEL irq?
>=20
> > mpic_eoi(mpic);
> > }
> >
> > +#ifdef CONFIG_TSI108_BRIDGE
> > +static void mpic_ack_irq(unsigned int irq)
> > +{
> > + struct mpic *mpic =3D mpic_from_irq(irq);
> > +
> > +#ifdef DEBUG_IRQ
> > + DBG("%s: ack_irq: %d\n", mpic->name, irq);
> > +#endif
> > +
> > + if ((irq_desc[irq].status & IRQ_LEVEL) =3D=3D 0)
> > + mpic_eoi(mpic);
> > +}
> > +#endif /* CONFIG_TSI108_BRIDGE */
> > +
>=20
> if the PIC works like other openpic's you dont need an 'ack' we =20
> handle it via 'end'
>=20
> > #ifdef CONFIG_SMP
> >
> > static void mpic_enable_ipi(unsigned int irq)
> > @@ -596,6 +617,9 @@ struct mpic * __init mpic_alloc(unsigned
> > mpic->hc_irq.enable =3D mpic_enable_irq;
> > mpic->hc_irq.disable =3D mpic_disable_irq;
> > mpic->hc_irq.end =3D mpic_end_irq;
> > +#ifdef CONFIG_TSI108_BRIDGE
> > + mpic->hc_irq.ack =3D mpic_ack_irq;
> > +#endif
> > if (flags & MPIC_PRIMARY)
> > mpic->hc_irq.set_affinity =3D mpic_set_affinity;
> > #ifdef CONFIG_SMP
> > @@ -955,8 +979,13 @@ void mpic_send_ipi(unsigned int ipi_no,
> > DBG("%s: send_ipi(ipi_no: %d)\n", mpic->name, ipi_no);
> > #endif
> >
> > +#ifndef CONFIG_TSI108_BRIDGE
> > mpic_cpu_write(MPIC_CPU_IPI_DISPATCH_0 + ipi_no * 0x10,
> > mpic_physmask(cpu_mask & cpus_addr(cpu_online_map)[0]));
> > +#else /* CONFIG_TSI108_BRIDGE */
> > + mpic_write(mpic->gregs, MPIC_CPU_IPI_DISPATCH,
> > + mpic_physmask(cpu_mask & cpus_addr(cpu_online_map)[0]));
> > +#endif /* !CONFIG_TSI108_BRIDGE */
> > }
> >
> > int mpic_get_one_irq(struct mpic *mpic, struct pt_regs *regs)
> > @@ -972,11 +1001,20 @@ int mpic_get_one_irq(struct mpic *mpic,
> > DBG("%s: cascading ...\n", mpic->name);
> > #endif
> > irq =3D mpic->cascade(regs, mpic->cascade_data);
> > +#ifdef DEBUG_LOW
> > + DBG("%s: cascaded irq: %d\n", mpic->name, irq);
> > +#endif
> > +#ifndef CONFIG_TSI108_BRIDGE
> > mpic_eoi(mpic);
> > +#endif
> > return irq;
> > }
> > - if (unlikely(irq =3D=3D MPIC_VEC_SPURRIOUS))
> > + if (unlikely(irq =3D=3D MPIC_VEC_SPURRIOUS)) {
> > +#ifdef CONFIG_TSI108_BRIDGE
> > + mpic_eoi(mpic);
> > +#endif
> > return -1;
> > + }
>=20
> why the changes to where we do mpic_eoi for TSI108?
>=20
> > if (irq < MPIC_VEC_IPI_0) {
> > #ifdef DEBUG_IRQ
> > DBG("%s: irq %d\n", mpic->name, irq + mpic->irq_offset);
>=20
>=20
>=20
> [snip]
^ permalink raw reply [flat|nested] 16+ messages in thread
* RE: [PATCH/2.6.17-rc4 4/10]Powerpc: Add tsi108 pic support
@ 2006-06-06 9:43 Zang Roy-r61911
2006-06-06 10:17 ` Benjamin Herrenschmidt
0 siblings, 1 reply; 16+ messages in thread
From: Zang Roy-r61911 @ 2006-06-06 9:43 UTC (permalink / raw)
To: Alexandre Bounine, Benjamin Herrenschmidt
Cc: linuxppc-dev list, Paul Mackerras, Yang Xin-Xin-r48390
> The items 1 and 2 not needed. I moved them from the
> tsi108_pic.c but really they have been leftovers from the old
> code there. Originally code ppc/syslib/open_pic.c was used as
> a template, which was good fit for the blocking output mode
> of the tsi108 PIC (plus some extra tweaks). In that mode EOI
> has to be signaled to PIC to deactivate interrupt line to the
> CPU before it re-enables local interrupts. This is why we
> have got ack routine. We changed mode later and removed most
> of workarounds except these two.
>
> I removed code for 1 and 2 and will send a patch to Roy after
> retesting (probably this weekend with some other stuff).
>
> Alex.
>
>
> On Thu, 2006-06-01 at 16:45 -0400, Alexandre Bounine wrote:
> > All differences in the Tsi108/109 PIC code from the
> standard MPIC are caused by the HW behavior. The Tsi108/109
> PIC looks like standard OpenPIC but, in fact, is different in
> registers mapping and behavior. Its logic is close but not
> exactly as MPIC.
> >
> > Here are replies on comments to the code:
> >
> > 1.Why do you have to check if its a LEVEL irq?
> >
> > Check for LEVEL irqs is required in the ack/end pair to
> enable nested
> > interrupt servicing and does not hang when core (local)
> interrupts are
> > re-enabled in the ISR. Otherwise we have to use
> SA_INTERRUPT flag for all level signaled interrupts.
>
> Can you be more precise about what exactly happens and why ?
> Unless you EOI handling is broken of course, there should be
> no need to do anything other than a single eoi in end(), period.
>
> > 2. if the PIC works like other openpic's you dont need an 'ack' we
> > handle it via 'end'
> >
> > Tsi108/109 needs it.
>
> What for ? Please, give the low level details.
>
> > 3. why the changes to where we do mpic_eoi for TSI108?
> > The Tsi108 PIC requires EOI for spurious interrupt (as all
> other interrupt sources).
>
> Ok, that is acceptable.
>
> > The do_IRQ() does not call end routine for spurious interrupts.
> >
> > The MPIC code patch for Tsi108/109 demonstrates HW
> differences and why we originally considered having separate
> code for Tsi108 pic.
>
> Please tell me more about the HW differences :)
>
> Ben.
Update Tsi108 implementation of MPIC.
Any comment?
Integrate Tundra Semiconductor tsi108 host bridge interrupt controller
to mpic arch.
Signed-off-by: Alexandre Bounine <alexandreb@tundra.com>
Signed-off-by: Roy Zang <tie-fei.zang@freescale.com>
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index 7e4d38e..d8a9add 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -408,7 +408,8 @@ config U3_DART
default n
config MPIC
- depends on PPC_PSERIES || PPC_PMAC || PPC_MAPLE || PPC_CHRP
+ depends on PPC_PSERIES || PPC_PMAC || PPC_MAPLE || PPC_CHRP \
+ || MPC7448HPC2
bool
default y
diff --git a/arch/powerpc/configs/mpc7448_hpc2_defconfig b/arch/powerpc/configs/mpc7448_hpc2_defconfig
index 28c7c8b..15a50f4 100644
--- a/arch/powerpc/configs/mpc7448_hpc2_defconfig
+++ b/arch/powerpc/configs/mpc7448_hpc2_defconfig
@@ -1,7 +1,7 @@
#
# Automatically generated make config: don't edit
# Linux kernel version: 2.6.17-rc4
-# Tue May 23 11:29:48 2006
+# Sat May 27 18:45:55 2006
#
# CONFIG_PPC64 is not set
CONFIG_PPC32=y
@@ -110,6 +110,7 @@ # CONFIG_PPC_MULTIPLATFORM is not set
# CONFIG_PPC_ISERIES is not set
CONFIG_EMBEDDED6xx=y
# CONFIG_APUS is not set
+CONFIG_MPIC=y
# CONFIG_PPC_RTAS is not set
# CONFIG_MMIO_NVRAM is not set
# CONFIG_PPC_MPC106 is not set
@@ -899,6 +900,7 @@ CONFIG_LOG_BUF_SHIFT=14
# CONFIG_DEBUG_FS is not set
# CONFIG_UNWIND_INFO is not set
# CONFIG_BOOTX_TEXT is not set
+# CONFIG_SERIAL_TEXT_DEBUG is not set
# CONFIG_PPC_EARLY_DEBUG_LPAR is not set
# CONFIG_PPC_EARLY_DEBUG_G5 is not set
# CONFIG_PPC_EARLY_DEBUG_RTAS is not set
diff --git a/arch/powerpc/platforms/embedded6xx/Kconfig b/arch/powerpc/platforms/embedded6xx/Kconfig
index e125ec6..2b78196 100644
--- a/arch/powerpc/platforms/embedded6xx/Kconfig
+++ b/arch/powerpc/platforms/embedded6xx/Kconfig
@@ -79,6 +79,7 @@ config MPC7448HPC2
select TSI108_BRIDGE
select DEFAULT_UIMAGE
select PPC_UDBG_16550
+ select MPIC
help
Select MPC7448HPC2 if configuring for Freescale MPC7448HPC2 (Taiga)
platform
diff --git a/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c b/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c
index db5dc10..458f70d 100644
--- a/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c
+++ b/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c
@@ -43,7 +43,16 @@ #include <asm/pci-bridge.h>
#include <asm/reg.h>
#include <mm/mmu_decl.h>
#include "mpc7448_hpc2.h"
-#include <asm/tsi108_pic.h>
+#include <asm/tsi108_irq.h>
+#include <asm/mpic.h>
+
+#undef DEBUG
+
+#ifdef DEBUG
+#define DBG(fmt...) do { printk(fmt); } while(0)
+#else
+#define DBG(fmt...) do { } while(0)
+#endif
#ifndef CONFIG_PCI
isa_io_base = MPC7448_HPC2_ISA_IO_BASE;
@@ -53,20 +62,8 @@ #endif
extern int add_bridge(struct device_node *dev);
extern void _nmask_and_or_msr(unsigned long nmask, unsigned long or_val);
-
-#ifdef TSI108_ETH
-hw_info hw_info_table[TSI108_ETH_MAX_PORTS + 1] = {
- {TSI108_CSR_ADDR_PHYS + TSI108_ETH_OFFSET,
- TSI108_CSR_ADDR_PHYS + TSI108_ETH_OFFSET,
- TSI108_PHY0_ADDR, IRQ_TSI108_GIGE0},
-
- {TSI108_CSR_ADDR_PHYS + TSI108_ETH_OFFSET + 0x400,
- TSI108_CSR_ADDR_PHYS + TSI108_ETH_OFFSET,
- TSI108_PHY1_ADDR, IRQ_TSI108_GIGE1},
-
- {TBL_END, TBL_END, TBL_END, TBL_END}
-};
-#endif
+extern void tsi108_pci_int_init(void);
+extern int tsi108_irq_cascade(struct pt_regs *regs, void *unused);
/*
* Define all of the IRQ senses and polarities. Taken from the
@@ -76,10 +73,32 @@ #endif
*/
static u_char mpc7448_hpc2_pic_initsenses[] __initdata = {
+ /* External on-board sources */
(IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* INT[0] XINT0 from FPGA */
(IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* INT[1] XINT1 from FPGA */
(IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* INT[2] PHY_INT from both GIGE */
(IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* INT[3] RESERVED */
+ /* Internal Tsi108/109 interrupt sources */
+ (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* Reserved IRQ */
+ (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* Reserved IRQ */
+ (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* Reserved IRQ */
+ (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* Reserved IRQ */
+ (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* DMA0 */
+ (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* DMA1 */
+ (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* DMA2 */
+ (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* DMA3 */
+ (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* UART0 */
+ (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* UART1 */
+ (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* I2C */
+ (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* GPIO */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* GIGE0 */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* GIGE1 */
+ (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* Reserved IRQ */
+ (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* HLP */
+ (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* SDC */
+ (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* Processor IF */
+ (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* Reserved IRQ */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* PCI/X block */
};
/*
@@ -196,18 +215,44 @@ #endif
*/
static void __init mpc7448_hpc2_init_IRQ(void)
{
+ struct mpic *mpic;
+ phys_addr_t mpic_paddr = 0;
+ struct device_node *tsi_pic;
+
+ tsi_pic = of_find_node_by_type(NULL, "open-pic");
+ if (tsi_pic) {
+ unsigned int size;
+ void *prop = get_property(tsi_pic, "reg", &size);
+ mpic_paddr = of_translate_address(tsi_pic, prop);
+ }
- tsi108_pic_init(mpc7448_hpc2_pic_initsenses);
+ if (mpic_paddr == 0) {
+ printk("%s: No tsi108 PIC found !\n", __FUNCTION__);
+ return;
+ }
- /* Configure MPIC outputs to CPU0 */
- tsi108_pic_set_output(0, IRQ_SENSE_EDGE, IRQ_POLARITY_NEGATIVE);
-}
+ DBG("%s: tsi108pic phys_addr = 0x%x\n", __FUNCTION__,
+ (u32) mpic_paddr);
-static void __init mpc7448_hpc2_map_io(void)
-{
- /* Tsi108 CSR mapping */
- io_block_mapping(TSI108_CSR_ADDR_VIRT, TSI108_CSR_ADDR_PHYS,
- 0x100000, _PAGE_IO);
+ mpic = mpic_alloc(mpic_paddr,
+ MPIC_PRIMARY | MPIC_BIG_ENDIAN | MPIC_WANTS_RESET |
+ MPIC_SPV_EOI | MPIC_CASC_NOEOI |
+ MPIC_MOD_ID(MPIC_ID_TSI108),
+ 0, /* num_sources used */
+ TSI108_IRQ_BASE,
+ 0, /* num_sources used */
+ NR_IRQS - 4 /* XXXX */,
+ mpc7448_hpc2_pic_initsenses,
+ sizeof(mpc7448_hpc2_pic_initsenses), "Tsi108_PIC");
+
+ BUG_ON(mpic == NULL); /* XXXX */
+
+ mpic_init(mpic);
+ mpic_setup_cascade(IRQ_TSI108_PCI, tsi108_irq_cascade, mpic);
+ tsi108_pci_int_init();
+
+ /* Configure MPIC outputs to CPU0 */
+ tsi108_write_reg(TSI108_MPIC_OFFSET + 0x30c, 0);
}
void mpc7448_hpc2_show_cpuinfo(struct seq_file *m)
@@ -269,10 +314,9 @@ define_machine(mpc7448_hpc2){
.setup_arch = mpc7448_hpc2_setup_arch,
.init_IRQ = mpc7448_hpc2_init_IRQ,
.show_cpuinfo = mpc7448_hpc2_show_cpuinfo,
- .get_irq = tsi108_pic_get_irq,
+ .get_irq = mpic_get_irq,
.restart = mpc7448_hpc2_restart,
.calibrate_decr = generic_calibrate_decr,
- .setup_io_mappings = mpc7448_hpc2_map_io,
.machine_check_exception= mpc7448_machine_check_exception,
.progress = udbg_progress,
};
diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile
index 8c0afb7..048e1f6 100644
--- a/arch/powerpc/sysdev/Makefile
+++ b/arch/powerpc/sysdev/Makefile
@@ -8,4 +8,4 @@ obj-$(CONFIG_U3_DART) += dart_iommu.o
obj-$(CONFIG_MMIO_NVRAM) += mmio_nvram.o
obj-$(CONFIG_PPC_83xx) += ipic.o
obj-$(CONFIG_FSL_SOC) += fsl_soc.o
-obj-$(CONFIG_TSI108_BRIDGE) += tsi108_common.o tsi108_pic.o
+obj-$(CONFIG_TSI108_BRIDGE) += tsi108_common.o tsi108_pci_int.o
diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c
index 7dcdfcb..09cb0eb 100644
--- a/arch/powerpc/sysdev/mpic.c
+++ b/arch/powerpc/sysdev/mpic.c
@@ -55,6 +55,78 @@ #define distribute_irqs (0)
#endif
#endif
+static struct mpic_info mpic_infos[] = {
+ [0] = { /* Original OpenPIC compatible MPIC */
+ .greg_base = MPIC_GREG_BASE,
+ .greg_frr0 = MPIC_GREG_FEATURE_0,
+ .greg_config0 = MPIC_GREG_GLOBAL_CONF_0,
+ .greg_vendor_id = MPIC_GREG_VENDOR_ID,
+ .greg_ipi_vp0 = MPIC_GREG_IPI_VECTOR_PRI_0,
+ .greg_ipi_stride = MPIC_GREG_IPI_STRIDE,
+ .greg_spurious = MPIC_GREG_SPURIOUS,
+ .greg_tfrr = MPIC_GREG_TIMER_FREQ,
+
+ .timer_base = MPIC_TIMER_BASE,
+ .timer_stride = MPIC_TIMER_STRIDE,
+ .timer_ccr = MPIC_TIMER_CURRENT_CNT,
+ .timer_bcr = MPIC_TIMER_BASE_CNT,
+ .timer_vpr = MPIC_TIMER_VECTOR_PRI,
+ .timer_dest = MPIC_TIMER_DESTINATION,
+
+ .cpu_base = MPIC_CPU_BASE,
+ .cpu_stride = MPIC_CPU_STRIDE,
+ .cpu_ipi_disp0 = MPIC_CPU_IPI_DISPATCH_0,
+ .cpu_ipi_disp_stride = MPIC_CPU_IPI_DISPATCH_STRIDE,
+ .cpu_task_pri = MPIC_CPU_CURRENT_TASK_PRI,
+ .cpu_whoami = MPIC_CPU_WHOAMI,
+ .cpu_intack = MPIC_CPU_INTACK,
+ .cpu_eoi = MPIC_CPU_EOI,
+
+ .irq_base = MPIC_IRQ_BASE,
+ .irq_stride = MPIC_IRQ_STRIDE,
+ .irq_vpr = MPIC_IRQ_VECTOR_PRI,
+ .irq_vpr_vector = MPIC_VECPRI_VECTOR_MASK,
+ .irq_vpr_polpos = MPIC_VECPRI_POLARITY_POSITIVE,
+ .irq_vpr_senlvl = MPIC_VECPRI_SENSE_LEVEL,
+ .irq_dest = MPIC_IRQ_DESTINATION,
+ },
+
+ [1] = { /* Tsi108/109 PIC */
+ .greg_base = TSI108_GREG_BASE,
+ .greg_frr0 = TSI108_GREG_FEATURE_0,
+ .greg_config0 = TSI108_GREG_GLOBAL_CONF_0,
+ .greg_vendor_id = TSI108_GREG_VENDOR_ID,
+ .greg_ipi_vp0 = TSI108_GREG_IPI_VECTOR_PRI_0,
+ .greg_ipi_stride = TSI108_GREG_IPI_STRIDE,
+ .greg_spurious = TSI108_GREG_SPURIOUS,
+ .greg_tfrr = TSI108_GREG_TIMER_FREQ,
+
+ .timer_base = TSI108_TIMER_BASE,
+ .timer_stride = TSI108_TIMER_STRIDE,
+ .timer_ccr = TSI108_TIMER_CURRENT_CNT,
+ .timer_bcr = TSI108_TIMER_BASE_CNT,
+ .timer_vpr = TSI108_TIMER_VECTOR_PRI,
+ .timer_dest = TSI108_TIMER_DESTINATION,
+
+ .cpu_base = TSI108_CPU_BASE,
+ .cpu_stride = TSI108_CPU_STRIDE,
+ .cpu_ipi_disp0 = TSI108_CPU_IPI_DISPATCH_0,
+ .cpu_ipi_disp_stride = TSI108_CPU_IPI_DISPATCH_STRIDE,
+ .cpu_task_pri = TSI108_CPU_CURRENT_TASK_PRI,
+ .cpu_whoami = 0xFFFFFFFF,
+ .cpu_intack = TSI108_CPU_INTACK,
+ .cpu_eoi = TSI108_CPU_EOI,
+
+ .irq_base = TSI108_IRQ_REG_BASE,
+ .irq_stride = TSI108_IRQ_STRIDE,
+ .irq_vpr = TSI108_IRQ_VECTOR_PRI,
+ .irq_vpr_vector = TSI108_VECPRI_VECTOR_MASK,
+ .irq_vpr_polpos = TSI108_VECPRI_POLARITY_POSITIVE,
+ .irq_vpr_senlvl = TSI108_VECPRI_SENSE_LEVEL,
+ .irq_dest = TSI108_IRQ_DESTINATION,
+ },
+};
+
/*
* Register accessor functions
*/
@@ -81,7 +153,8 @@ static inline void _mpic_write(unsigned
static inline u32 _mpic_ipi_read(struct mpic *mpic, unsigned int ipi)
{
unsigned int be = (mpic->flags & MPIC_BIG_ENDIAN) != 0;
- unsigned int offset = MPIC_GREG_IPI_VECTOR_PRI_0 + (ipi * 0x10);
+ unsigned int offset = mpic->hw_set->greg_ipi_vp0 +
+ (ipi * mpic->hw_set->greg_ipi_stride);
if (mpic->flags & MPIC_BROKEN_IPI)
be = !be;
@@ -90,7 +163,8 @@ static inline u32 _mpic_ipi_read(struct
static inline void _mpic_ipi_write(struct mpic *mpic, unsigned int ipi, u32 value)
{
- unsigned int offset = MPIC_GREG_IPI_VECTOR_PRI_0 + (ipi * 0x10);
+ unsigned int offset = mpic->hw_set->greg_ipi_vp0 +
+ (ipi * mpic->hw_set->greg_ipi_stride);
_mpic_write(mpic->flags & MPIC_BIG_ENDIAN, mpic->gregs, offset, value);
}
@@ -121,7 +195,7 @@ static inline u32 _mpic_irq_read(struct
unsigned int idx = src_no & mpic->isu_mask;
return _mpic_read(mpic->flags & MPIC_BIG_ENDIAN, mpic->isus[isu],
- reg + (idx * MPIC_IRQ_STRIDE));
+ reg + (idx * mpic->hw_set->irq_stride));
}
static inline void _mpic_irq_write(struct mpic *mpic, unsigned int src_no,
@@ -131,7 +205,7 @@ static inline void _mpic_irq_write(struc
unsigned int idx = src_no & mpic->isu_mask;
_mpic_write(mpic->flags & MPIC_BIG_ENDIAN, mpic->isus[isu],
- reg + (idx * MPIC_IRQ_STRIDE), value);
+ reg + (idx * mpic->hw_set->irq_stride), value);
}
#define mpic_read(b,r) _mpic_read(mpic->flags & MPIC_BIG_ENDIAN,(b),(r))
@@ -157,8 +231,8 @@ static void __init mpic_test_broken_ipi(
{
u32 r;
- mpic_write(mpic->gregs, MPIC_GREG_IPI_VECTOR_PRI_0, MPIC_VECPRI_MASK);
- r = mpic_read(mpic->gregs, MPIC_GREG_IPI_VECTOR_PRI_0);
+ mpic_write(mpic->gregs, mpic->hw_set->greg_ipi_vp0, MPIC_VECPRI_MASK);
+ r = mpic_read(mpic->gregs, mpic->hw_set->greg_ipi_vp0);
if (r == le32_to_cpu(MPIC_VECPRI_MASK)) {
printk(KERN_INFO "mpic: Detected reversed IPI registers\n");
@@ -392,8 +466,8 @@ static inline struct mpic * mpic_from_ir
/* Send an EOI */
static inline void mpic_eoi(struct mpic *mpic)
{
- mpic_cpu_write(MPIC_CPU_EOI, 0);
- (void)mpic_cpu_read(MPIC_CPU_WHOAMI);
+ mpic_cpu_write(mpic->hw_set->cpu_eoi, 0);
+ (void)mpic_cpu_read(mpic->hw_set->cpu_task_pri);
}
#ifdef CONFIG_SMP
@@ -419,8 +493,8 @@ static void mpic_enable_irq(unsigned int
DBG("%p: %s: enable_irq: %d (src %d)\n", mpic, mpic->name, irq, src);
- mpic_irq_write(src, MPIC_IRQ_VECTOR_PRI,
- mpic_irq_read(src, MPIC_IRQ_VECTOR_PRI) &
+ mpic_irq_write(src, mpic->hw_set->irq_vpr,
+ mpic_irq_read(src, mpic->hw_set->irq_vpr) &
~MPIC_VECPRI_MASK);
/* make sure mask gets to controller before we return to user */
@@ -429,7 +503,7 @@ static void mpic_enable_irq(unsigned int
printk(KERN_ERR "mpic_enable_irq timeout\n");
break;
}
- } while(mpic_irq_read(src, MPIC_IRQ_VECTOR_PRI) & MPIC_VECPRI_MASK);
+ } while(mpic_irq_read(src, mpic->hw_set->irq_vpr) & MPIC_VECPRI_MASK);
#ifdef CONFIG_MPIC_BROKEN_U3
if (mpic->flags & MPIC_BROKEN_U3) {
@@ -466,8 +540,8 @@ static void mpic_disable_irq(unsigned in
DBG("%s: disable_irq: %d (src %d)\n", mpic->name, irq, src);
- mpic_irq_write(src, MPIC_IRQ_VECTOR_PRI,
- mpic_irq_read(src, MPIC_IRQ_VECTOR_PRI) |
+ mpic_irq_write(src, mpic->hw_set->irq_vpr,
+ mpic_irq_read(src, mpic->hw_set->irq_vpr) |
MPIC_VECPRI_MASK);
/* make sure mask gets to controller before we return to user */
@@ -476,7 +550,7 @@ static void mpic_disable_irq(unsigned in
printk(KERN_ERR "mpic_enable_irq timeout\n");
break;
}
- } while(!(mpic_irq_read(src, MPIC_IRQ_VECTOR_PRI) & MPIC_VECPRI_MASK));
+ } while(!(mpic_irq_read(src, mpic->hw_set->irq_vpr) & MPIC_VECPRI_MASK));
}
static void mpic_shutdown_irq(unsigned int irq)
@@ -557,7 +631,7 @@ static void mpic_set_affinity(unsigned i
cpus_and(tmp, cpumask, cpu_online_map);
- mpic_irq_write(irq - mpic->irq_offset, MPIC_IRQ_DESTINATION,
+ mpic_irq_write(irq - mpic->irq_offset, mpic->hw_set->irq_dest,
mpic_physmask(cpus_addr(tmp)[0]));
}
@@ -613,18 +687,20 @@ #endif /* CONFIG_SMP */
mpic->num_sources = 0; /* so far */
mpic->senses = senses;
mpic->senses_count = senses_count;
+ mpic->hw_set = &mpic_infos[MPIC_GET_MOD_ID(flags)];
/* Map the global registers */
- mpic->gregs = ioremap(phys_addr + MPIC_GREG_BASE, 0x1000);
- mpic->tmregs = mpic->gregs + ((MPIC_TIMER_BASE - MPIC_GREG_BASE) >> 2);
+ mpic->gregs = ioremap(phys_addr + mpic->hw_set->greg_base, 0x1000);
+ mpic->tmregs = mpic->gregs +
+ ((mpic->hw_set->timer_base - mpic->hw_set->greg_base) >> 2);
BUG_ON(mpic->gregs == NULL);
/* Reset */
if (flags & MPIC_WANTS_RESET) {
- mpic_write(mpic->gregs, MPIC_GREG_GLOBAL_CONF_0,
- mpic_read(mpic->gregs, MPIC_GREG_GLOBAL_CONF_0)
+ mpic_write(mpic->gregs, mpic->hw_set->greg_config0,
+ mpic_read(mpic->gregs, mpic->hw_set->greg_config0)
| MPIC_GREG_GCONF_RESET);
- while( mpic_read(mpic->gregs, MPIC_GREG_GLOBAL_CONF_0)
+ while( mpic_read(mpic->gregs, mpic->hw_set->greg_config0)
& MPIC_GREG_GCONF_RESET)
mb();
}
@@ -633,7 +709,7 @@ #endif /* CONFIG_SMP */
* MPICs, num sources as well. On ISU MPICs, sources are counted
* as ISUs are added
*/
- reg = mpic_read(mpic->gregs, MPIC_GREG_FEATURE_0);
+ reg = mpic_read(mpic->gregs, mpic->hw_set->greg_frr0);
mpic->num_cpus = ((reg & MPIC_GREG_FEATURE_LAST_CPU_MASK)
>> MPIC_GREG_FEATURE_LAST_CPU_SHIFT) + 1;
if (isu_size == 0)
@@ -642,16 +718,16 @@ #endif /* CONFIG_SMP */
/* Map the per-CPU registers */
for (i = 0; i < mpic->num_cpus; i++) {
- mpic->cpuregs[i] = ioremap(phys_addr + MPIC_CPU_BASE +
- i * MPIC_CPU_STRIDE, 0x1000);
+ mpic->cpuregs[i] = ioremap(phys_addr + mpic->hw_set->cpu_base +
+ i * mpic->hw_set->cpu_stride, 0x1000);
BUG_ON(mpic->cpuregs[i] == NULL);
}
/* Initialize main ISU if none provided */
if (mpic->isu_size == 0) {
mpic->isu_size = mpic->num_sources;
- mpic->isus[0] = ioremap(phys_addr + MPIC_IRQ_BASE,
- MPIC_IRQ_STRIDE * mpic->isu_size);
+ mpic->isus[0] = ioremap(phys_addr + mpic->hw_set->irq_base,
+ mpic->hw_set->irq_stride * mpic->isu_size);
BUG_ON(mpic->isus[0] == NULL);
}
mpic->isu_shift = 1 + __ilog2(mpic->isu_size - 1);
@@ -693,7 +769,8 @@ void __init mpic_assign_isu(struct mpic
BUG_ON(isu_num >= MPIC_MAX_ISU);
- mpic->isus[isu_num] = ioremap(phys_addr, MPIC_IRQ_STRIDE * mpic->isu_size);
+ mpic->isus[isu_num] = ioremap(phys_addr,
+ mpic->hw_set->irq_stride * mpic->isu_size);
if ((isu_first + mpic->isu_size) > mpic->num_sources)
mpic->num_sources = isu_first + mpic->isu_size;
}
@@ -729,14 +806,15 @@ void __init mpic_init(struct mpic *mpic)
printk(KERN_INFO "mpic: Initializing for %d sources\n", mpic->num_sources);
/* Set current processor priority to max */
- mpic_cpu_write(MPIC_CPU_CURRENT_TASK_PRI, 0xf);
+ mpic_cpu_write(mpic->hw_set->cpu_task_pri, 0xf);
/* Initialize timers: just disable them all */
for (i = 0; i < 4; i++) {
mpic_write(mpic->tmregs,
- i * MPIC_TIMER_STRIDE + MPIC_TIMER_DESTINATION, 0);
+ i * mpic->hw_set->timer_stride +
+ mpic->hw_set->timer_dest, 0);
mpic_write(mpic->tmregs,
- i * MPIC_TIMER_STRIDE + MPIC_TIMER_VECTOR_PRI,
+ i * mpic->hw_set->timer_stride + mpic->hw_set->timer_vpr,
MPIC_VECPRI_MASK |
(MPIC_VEC_TIMER_0 + i));
}
@@ -780,14 +858,14 @@ #endif /* CONFIG_MPIC_BROKEN_U3 */
/* do senses munging */
if (mpic->senses && i < mpic->senses_count) {
if (mpic->senses[i] & IRQ_SENSE_LEVEL)
- vecpri |= MPIC_VECPRI_SENSE_LEVEL;
+ vecpri |= mpic->hw_set->irq_vpr_senlvl;
if (mpic->senses[i] & IRQ_POLARITY_POSITIVE)
- vecpri |= MPIC_VECPRI_POLARITY_POSITIVE;
+ vecpri |= mpic->hw_set->irq_vpr_polpos;
} else
- vecpri |= MPIC_VECPRI_SENSE_LEVEL;
+ vecpri |= mpic->hw_set->irq_vpr_senlvl;
/* remember if it was a level interrupts */
- level = (vecpri & MPIC_VECPRI_SENSE_LEVEL);
+ level = (vecpri & mpic->hw_set->irq_vpr_senlvl);
/* deal with broken U3 */
if (mpic->flags & MPIC_BROKEN_U3) {
@@ -795,7 +873,7 @@ #ifdef CONFIG_MPIC_BROKEN_U3
if (mpic_is_ht_interrupt(mpic, i)) {
vecpri &= ~(MPIC_VECPRI_SENSE_MASK |
MPIC_VECPRI_POLARITY_MASK);
- vecpri |= MPIC_VECPRI_POLARITY_POSITIVE;
+ vecpri |= mpic->hw_set->irq_vpr_polpos;
}
#else
printk(KERN_ERR "mpic: BROKEN_U3 set, but CONFIG doesn't match\n");
@@ -806,8 +884,8 @@ #endif
(level != 0));
/* init hw */
- mpic_irq_write(i, MPIC_IRQ_VECTOR_PRI, vecpri);
- mpic_irq_write(i, MPIC_IRQ_DESTINATION,
+ mpic_irq_write(i, mpic->hw_set->irq_vpr, vecpri);
+ mpic_irq_write(i, mpic->hw_set->irq_dest,
1 << hard_smp_processor_id());
/* init linux descriptors */
@@ -818,15 +896,16 @@ #endif
}
/* Init spurrious vector */
- mpic_write(mpic->gregs, MPIC_GREG_SPURIOUS, MPIC_VEC_SPURRIOUS);
+ mpic_write(mpic->gregs, mpic->hw_set->greg_spurious, MPIC_VEC_SPURRIOUS);
- /* Disable 8259 passthrough */
- mpic_write(mpic->gregs, MPIC_GREG_GLOBAL_CONF_0,
- mpic_read(mpic->gregs, MPIC_GREG_GLOBAL_CONF_0)
- | MPIC_GREG_GCONF_8259_PTHROU_DIS);
+ /* Disable 8259 passthrough, if supported */
+ if (MPIC_GET_MOD_ID(mpic->flags) != MPIC_ID_TSI108)
+ mpic_write(mpic->gregs, mpic->hw_set->greg_config0,
+ mpic_read(mpic->gregs, mpic->hw_set->greg_config0)
+ | MPIC_GREG_GCONF_8259_PTHROU_DIS);
/* Set current processor priority to 0 */
- mpic_cpu_write(MPIC_CPU_CURRENT_TASK_PRI, 0);
+ mpic_cpu_write(mpic->hw_set->cpu_task_pri, 0);
}
@@ -845,9 +924,9 @@ void mpic_irq_set_priority(unsigned int
mpic_ipi_write(irq - mpic->ipi_offset,
reg | (pri << MPIC_VECPRI_PRIORITY_SHIFT));
} else {
- reg = mpic_irq_read(irq - mpic->irq_offset,MPIC_IRQ_VECTOR_PRI)
+ reg = mpic_irq_read(irq - mpic->irq_offset,mpic->hw_set->irq_vpr)
& ~MPIC_VECPRI_PRIORITY_MASK;
- mpic_irq_write(irq - mpic->irq_offset, MPIC_IRQ_VECTOR_PRI,
+ mpic_irq_write(irq - mpic->irq_offset, mpic->hw_set->irq_vpr,
reg | (pri << MPIC_VECPRI_PRIORITY_SHIFT));
}
spin_unlock_irqrestore(&mpic_lock, flags);
@@ -864,7 +943,7 @@ unsigned int mpic_irq_get_priority(unsig
if (is_ipi)
reg = mpic_ipi_read(irq - mpic->ipi_offset);
else
- reg = mpic_irq_read(irq - mpic->irq_offset, MPIC_IRQ_VECTOR_PRI);
+ reg = mpic_irq_read(irq - mpic->irq_offset, mpic->hw_set->irq_vpr);
spin_unlock_irqrestore(&mpic_lock, flags);
return (reg & MPIC_VECPRI_PRIORITY_MASK) >> MPIC_VECPRI_PRIORITY_SHIFT;
}
@@ -890,12 +969,12 @@ #ifdef CONFIG_SMP
*/
if (distribute_irqs) {
for (i = 0; i < mpic->num_sources ; i++)
- mpic_irq_write(i, MPIC_IRQ_DESTINATION,
- mpic_irq_read(i, MPIC_IRQ_DESTINATION) | msk);
+ mpic_irq_write(i, mpic->hw_set->irq_dest,
+ mpic_irq_read(i, mpic->hw_set->irq_dest) | msk);
}
/* Set current processor priority to 0 */
- mpic_cpu_write(MPIC_CPU_CURRENT_TASK_PRI, 0);
+ mpic_cpu_write(mpic->hw_set->cpu_task_pri, 0);
spin_unlock_irqrestore(&mpic_lock, flags);
#endif /* CONFIG_SMP */
@@ -905,7 +984,7 @@ int mpic_cpu_get_priority(void)
{
struct mpic *mpic = mpic_primary;
- return mpic_cpu_read(MPIC_CPU_CURRENT_TASK_PRI);
+ return mpic_cpu_read(mpic->hw_set->cpu_task_pri);
}
void mpic_cpu_set_priority(int prio)
@@ -913,7 +992,7 @@ void mpic_cpu_set_priority(int prio)
struct mpic *mpic = mpic_primary;
prio &= MPIC_CPU_TASKPRI_MASK;
- mpic_cpu_write(MPIC_CPU_CURRENT_TASK_PRI, prio);
+ mpic_cpu_write(mpic->hw_set->cpu_task_pri, prio);
}
/*
@@ -935,11 +1014,11 @@ void mpic_teardown_this_cpu(int secondar
/* let the mpic know we don't want intrs. */
for (i = 0; i < mpic->num_sources ; i++)
- mpic_irq_write(i, MPIC_IRQ_DESTINATION,
- mpic_irq_read(i, MPIC_IRQ_DESTINATION) & ~msk);
+ mpic_irq_write(i, mpic->hw_set->irq_dest,
+ mpic_irq_read(i, mpic->hw_set->irq_dest) & ~msk);
/* Set current processor priority to max */
- mpic_cpu_write(MPIC_CPU_CURRENT_TASK_PRI, 0xf);
+ mpic_cpu_write(mpic->hw_set->cpu_task_pri, 0xf);
spin_unlock_irqrestore(&mpic_lock, flags);
}
@@ -955,7 +1034,8 @@ #ifdef DEBUG_IPI
DBG("%s: send_ipi(ipi_no: %d)\n", mpic->name, ipi_no);
#endif
- mpic_cpu_write(MPIC_CPU_IPI_DISPATCH_0 + ipi_no * 0x10,
+ mpic_cpu_write(mpic->hw_set->cpu_ipi_disp0 +
+ ipi_no * mpic->hw_set->cpu_ipi_disp_stride,
mpic_physmask(cpu_mask & cpus_addr(cpu_online_map)[0]));
}
@@ -963,7 +1043,7 @@ int mpic_get_one_irq(struct mpic *mpic,
{
u32 irq;
- irq = mpic_cpu_read(MPIC_CPU_INTACK) & MPIC_VECPRI_VECTOR_MASK;
+ irq = mpic_cpu_read(mpic->hw_set->cpu_intack) & mpic->hw_set->irq_vpr_vector;
#ifdef DEBUG_LOW
DBG("%s: get_one_irq(): %d\n", mpic->name, irq);
#endif
@@ -972,11 +1052,18 @@ #ifdef DEBUG_LOW
DBG("%s: cascading ...\n", mpic->name);
#endif
irq = mpic->cascade(regs, mpic->cascade_data);
- mpic_eoi(mpic);
+#ifdef DEBUG_LOW
+ DBG("%s: cascaded irq: %d\n", mpic->name, irq);
+#endif
+ if (!(mpic->flags & MPIC_CASC_NOEOI))
+ mpic_eoi(mpic);
return irq;
}
- if (unlikely(irq == MPIC_VEC_SPURRIOUS))
+ if (unlikely(irq == MPIC_VEC_SPURRIOUS)) {
+ if (mpic->flags & MPIC_SPV_EOI)
+ mpic_eoi(mpic);
return -1;
+ }
if (irq < MPIC_VEC_IPI_0) {
#ifdef DEBUG_IRQ
DBG("%s: irq %d\n", mpic->name, irq + mpic->irq_offset);
diff --git a/include/asm-powerpc/mpic.h b/include/asm-powerpc/mpic.h
index 6b9e781..6fa3427 100644
--- a/include/asm-powerpc/mpic.h
+++ b/include/asm-powerpc/mpic.h
@@ -37,6 +37,7 @@ #define MPIC_GREG_IPI_VECTOR_PRI_0 0x000
#define MPIC_GREG_IPI_VECTOR_PRI_1 0x000b0
#define MPIC_GREG_IPI_VECTOR_PRI_2 0x000c0
#define MPIC_GREG_IPI_VECTOR_PRI_3 0x000d0
+#define MPIC_GREG_IPI_STRIDE 0x10
#define MPIC_GREG_SPURIOUS 0x000e0
#define MPIC_GREG_TIMER_FREQ 0x000f0
@@ -64,6 +65,7 @@ #define MPIC_CPU_IPI_DISPATCH_0 0x00040
#define MPIC_CPU_IPI_DISPATCH_1 0x00050
#define MPIC_CPU_IPI_DISPATCH_2 0x00060
#define MPIC_CPU_IPI_DISPATCH_3 0x00070
+#define MPIC_CPU_IPI_DISPATCH_STRIDE 0x00010
#define MPIC_CPU_CURRENT_TASK_PRI 0x00080
#define MPIC_CPU_TASKPRI_MASK 0x0000000f
#define MPIC_CPU_WHOAMI 0x00090
@@ -91,6 +93,55 @@ #define MPIC_VECPRI_SENSE_EDGE 0x0000
#define MPIC_VECPRI_SENSE_MASK 0x00400000
#define MPIC_IRQ_DESTINATION 0x00010
+/******************************************************************************
+ * Tsi108 implementation of MPIC has many differences form the original one
+ */
+
+/*
+ * Global registers
+ */
+
+#define TSI108_GREG_BASE 0x00000
+#define TSI108_GREG_FEATURE_0 0x00000
+#define TSI108_GREG_GLOBAL_CONF_0 0x00004
+#define TSI108_GREG_VENDOR_ID 0x0000c
+#define TSI108_GREG_IPI_VECTOR_PRI_0 0x00204 /* Doorbell 0 */
+#define TSI108_GREG_IPI_STRIDE 0x0c
+#define TSI108_GREG_SPURIOUS 0x00010
+#define TSI108_GREG_TIMER_FREQ 0x00014
+
+/*
+ * Timer registers
+ */
+#define TSI108_TIMER_BASE 0x0030
+#define TSI108_TIMER_STRIDE 0x10
+#define TSI108_TIMER_CURRENT_CNT 0x00000
+#define TSI108_TIMER_BASE_CNT 0x00004
+#define TSI108_TIMER_VECTOR_PRI 0x00008
+#define TSI108_TIMER_DESTINATION 0x0000c
+
+/*
+ * Per-Processor registers
+ */
+#define TSI108_CPU_BASE 0x00300
+#define TSI108_CPU_STRIDE 0x00040
+#define TSI108_CPU_IPI_DISPATCH_0 0x00200
+#define TSI108_CPU_IPI_DISPATCH_STRIDE 0x00000
+#define TSI108_CPU_CURRENT_TASK_PRI 0x00000
+#define TSI108_CPU_INTACK 0x00004
+#define TSI108_CPU_EOI 0x00008
+
+/*
+ * Per-source registers
+ */
+#define TSI108_IRQ_REG_BASE 0x00100
+#define TSI108_IRQ_STRIDE 0x00008
+#define TSI108_IRQ_VECTOR_PRI 0x00000
+#define TSI108_VECPRI_VECTOR_MASK 0x000000ff
+#define TSI108_VECPRI_POLARITY_POSITIVE 0x01000000
+#define TSI108_VECPRI_SENSE_LEVEL 0x02000000
+#define TSI108_IRQ_DESTINATION 0x00004
+
#define MPIC_MAX_IRQ_SOURCES 2048
#define MPIC_MAX_CPUS 32
#define MPIC_MAX_ISU 32
@@ -124,6 +175,40 @@ struct mpic_irq_fixup
};
#endif /* CONFIG_MPIC_BROKEN_U3 */
+struct mpic_info {
+ u32 greg_base; /* offset of global registers from MPIC base */
+ u32 greg_frr0; /* FRR0 offset from base */
+ u32 greg_config0; /* Global Config register offset from base */
+ u32 greg_vendor_id; /* VID register offset from base */
+ u32 greg_ipi_vp0; /* IPI Vector/Priority Registers */
+ u32 greg_ipi_stride; /* IPI Vector/Priority Registers spacing */
+ u32 greg_spurious; /* Spurious Vector Register */
+ u32 greg_tfrr; /* Global Timer Frequency Reporting Register */
+
+ u32 timer_base; /* Global Timer Registers base */
+ u32 timer_stride; /* Global Timer Registers spacing */
+ u32 timer_ccr; /* Global Timer Current Count Register */
+ u32 timer_bcr; /* Global Timer Base Count Register */
+ u32 timer_vpr; /* Global Timer Vector/Priority Register */
+ u32 timer_dest; /* Global Timer Destination Register */
+
+ u32 cpu_base; /* Global Timer Destination Register */
+ u32 cpu_stride; /* Global Timer Destination Register */
+ u32 cpu_ipi_disp0; /* IPI 0 Dispatch Command Register */
+ u32 cpu_ipi_disp_stride; /* IPI Dispatch spacing */
+ u32 cpu_task_pri; /* Processor Current Task Priority Register */
+ u32 cpu_whoami; /* Who Am I Register */
+ u32 cpu_intack; /* Interrupt Acknowledge Register */
+ u32 cpu_eoi; /* End of Interrupt Register */
+
+ u32 irq_base; /* Interrupt registers base */
+ u32 irq_stride; /* Interrupt registers spacing */
+ u32 irq_vpr; /* Interrupt Vector/Priority Register */
+ u32 irq_vpr_vector; /* Interrupt Vector Mask */
+ u32 irq_vpr_polpos; /* Interrupt Positive Polarity bit */
+ u32 irq_vpr_senlvl; /* Interrupt Level Sense bit */
+ u32 irq_dest; /* Interrupt Destination Register */
+};
/* The instance data of a given MPIC */
struct mpic
@@ -168,6 +253,8 @@ #endif
volatile u32 __iomem *tmregs;
volatile u32 __iomem *cpuregs[MPIC_MAX_CPUS];
volatile u32 __iomem *isus[MPIC_MAX_ISU];
+ /* Pointer to HW info structure */
+ struct mpic_info *hw_set;
/* link */
struct mpic *next;
@@ -186,6 +273,16 @@ #define MPIC_BROKEN_U3 0x00000004
#define MPIC_BROKEN_IPI 0x00000008
/* MPIC wants a reset */
#define MPIC_WANTS_RESET 0x00000010
+/* Spurious vector requires EOI */
+#define MPIC_SPV_EOI 0x00000020
+/* No EOI for cascaded interrupt */
+#define MPIC_CASC_NOEOI 0x00000040
+/* MPIC HW modification ID */
+#define MPIC_MOD_ID_MASK 0x00000f00
+#define MPIC_MOD_ID(val) (((val) << 8) & MPIC_MOD_ID_MASK)
+#define MPIC_GET_MOD_ID(flags) (((flags) & MPIC_MOD_ID_MASK) >> 8)
+#define MPIC_ID_MPIC 0 /* Original MPIC */
+#define MPIC_ID_TSI108 1 /* Tsi108/109 PIC */
/* Allocate the controller structure and setup the linux irq descs
* for the range if interrupts passed in. No HW initialization is
^ permalink raw reply related [flat|nested] 16+ messages in thread
* RE: [PATCH/2.6.17-rc4 4/10]Powerpc: Add tsi108 pic support
2006-06-06 9:43 Zang Roy-r61911
@ 2006-06-06 10:17 ` Benjamin Herrenschmidt
0 siblings, 0 replies; 16+ messages in thread
From: Benjamin Herrenschmidt @ 2006-06-06 10:17 UTC (permalink / raw)
To: Zang Roy-r61911
Cc: Alexandre Bounine, Yang Xin-Xin-r48390, Paul Mackerras,
linuxppc-dev list
On Tue, 2006-06-06 at 17:43 +0800, Zang Roy-r61911 wrote:
> Update Tsi108 implementation of MPIC.
> Any comment?
>
> Integrate Tundra Semiconductor tsi108 host bridge interrupt controller
> to mpic arch.
Looks much better :) Still a few things...
> + mpic = mpic_alloc(mpic_paddr,
> + MPIC_PRIMARY | MPIC_BIG_ENDIAN | MPIC_WANTS_RESET |
> + MPIC_SPV_EOI | MPIC_CASC_NOEOI |
> + MPIC_MOD_ID(MPIC_ID_TSI108),
> + 0, /* num_sources used */
> + TSI108_IRQ_BASE,
> + 0, /* num_sources used */
> + NR_IRQS - 4 /* XXXX */,
> + mpc7448_hpc2_pic_initsenses,
> + sizeof(mpc7448_hpc2_pic_initsenses), "Tsi108_PIC");
That's a hell lot of new flags... I'm not sure we need that many or a
single TSI108 one that encloses all the new ones. Also, I'm not sure we
need that model ID encoding thing. Let's do things simple, besides, I
don't want to encourage HW folks into doing the same kind of contraption
in the future (btw, tell the TSI folks for me that they had a BAD BAD
BAD idea to muck around with the base design that way, especially
changing the register map in incompatible ways for no good reason).
> + /* Configure MPIC outputs to CPU0 */
> + tsi108_write_reg(TSI108_MPIC_OFFSET + 0x30c, 0);
> }
It doesn't use the standard multiple processor outputs mecanism of
MPIC ?
> +static struct mpic_info mpic_infos[] = {
> + [0] = { /* Original OpenPIC compatible MPIC */
> + .greg_base = MPIC_GREG_BASE,
> + .greg_frr0 = MPIC_GREG_FEATURE_0,
> + .greg_config0 = MPIC_GREG_GLOBAL_CONF_0,
> + .greg_vendor_id = MPIC_GREG_VENDOR_ID,
> + .greg_ipi_vp0 = MPIC_GREG_IPI_VECTOR_PRI_0,
> + .greg_ipi_stride = MPIC_GREG_IPI_STRIDE,
> + .greg_spurious = MPIC_GREG_SPURIOUS,
> + .greg_tfrr = MPIC_GREG_TIMER_FREQ,
> +
.../...
It's a bit sad to have to go all the way to doing such tables, but I
suspect it's probably the best way to handle it at this point. Send more
nastygrams to the HW folks for me.
> mpic->num_sources = 0; /* so far */
> mpic->senses = senses;
> mpic->senses_count = senses_count;
> + mpic->hw_set = &mpic_infos[MPIC_GET_MOD_ID(flags)];
Well... the model ID thing might not be that a bad idea in the end :) I
need to think about it. I might have to deal with yet another MPIC that
has another regiser map (yeah yeah, TSI aren't the only ones to not get
it)...
.../...
> @@ -963,7 +1043,7 @@ int mpic_get_one_irq(struct mpic *mpic,
> {
> u32 irq;
>
> - irq = mpic_cpu_read(MPIC_CPU_INTACK) & MPIC_VECPRI_VECTOR_MASK;
> + irq = mpic_cpu_read(mpic->hw_set->cpu_intack) & mpic->hw_set->irq_vpr_vector;
> #ifdef DEBUG_LOW
> DBG("%s: get_one_irq(): %d\n", mpic->name, irq);
> #endif
> @@ -972,11 +1052,18 @@ #ifdef DEBUG_LOW
> DBG("%s: cascading ...\n", mpic->name);
> #endif
> irq = mpic->cascade(regs, mpic->cascade_data);
> - mpic_eoi(mpic);
> +#ifdef DEBUG_LOW
> + DBG("%s: cascaded irq: %d\n", mpic->name, irq);
> +#endif
> + if (!(mpic->flags & MPIC_CASC_NOEOI))
> + mpic_eoi(mpic);
> return irq;
> }
Can you tell me why you need the above ? (Why you aren't EOI'ing the
cascade ?) Note that the cascade handling is going away from mpic anyway
with the port to genirq that I'll publish later this week for 2.6.18 and
it will almost be handled as a normal interrupt...
> - if (unlikely(irq == MPIC_VEC_SPURRIOUS))
> + if (unlikely(irq == MPIC_VEC_SPURRIOUS)) {
> + if (mpic->flags & MPIC_SPV_EOI)
> + mpic_eoi(mpic);
> return -1;
> + }
I think the above thing could just test the model ID. It's unlikely that
another implementation need the same "feature", so just test the model
ID rather than adding a flag and if we ever have another model with the
same "feature", then we'll go back to adding a flag :)
Cheers,
Ben.
^ permalink raw reply [flat|nested] 16+ messages in thread
* RE: [PATCH/2.6.17-rc4 4/10]Powerpc: Add tsi108 pic support
@ 2006-06-06 14:45 Alexandre Bounine
2006-06-06 23:08 ` Benjamin Herrenschmidt
0 siblings, 1 reply; 16+ messages in thread
From: Alexandre Bounine @ 2006-06-06 14:45 UTC (permalink / raw)
To: Benjamin Herrenschmidt, Zang Roy-r61911
Cc: linuxppc-dev list, Paul Mackerras, Yang Xin-Xin-r48390
> -----Original Message-----
> From: Benjamin Herrenschmidt [mailto:benh@kernel.crashing.org]
> Sent: Tuesday, June 06, 2006 6:17 AM
> To: Zang Roy-r61911
> Cc: Alexandre Bounine; Kumar Gala; linuxppc-dev list; Yang
> Xin-Xin-r48390; Paul Mackerras
> Subject: RE: [PATCH/2.6.17-rc4 4/10]Powerpc: Add tsi108 pic support
>=20
>=20
> On Tue, 2006-06-06 at 17:43 +0800, Zang Roy-r61911 wrote:
>=20
> > Update Tsi108 implementation of MPIC.
> > Any comment?=20
> >=20
> > Integrate Tundra Semiconductor tsi108 host bridge interrupt=20
> controller=20
> > to mpic arch.
>=20
> Looks much better :) Still a few things...=20
>
Sounds good. We are moving in right direction :)
=20
> > + mpic =3D mpic_alloc(mpic_paddr,
> > + MPIC_PRIMARY | MPIC_BIG_ENDIAN |=20
> MPIC_WANTS_RESET |
> > + MPIC_SPV_EOI | MPIC_CASC_NOEOI |=20
> > + MPIC_MOD_ID(MPIC_ID_TSI108),
> > + 0, /* num_sources used */
> > + TSI108_IRQ_BASE,
> > + 0, /* num_sources used */
> > + NR_IRQS - 4 /* XXXX */,
> > + mpc7448_hpc2_pic_initsenses,
> > + sizeof(mpc7448_hpc2_pic_initsenses),=20
> "Tsi108_PIC");
>=20
> That's a hell lot of new flags... I'm not sure we need that many or a
> single TSI108 one that encloses all the new ones. Also, I'm=20
> not sure we
> need that model ID encoding thing. Let's do things simple, besides, I
> don't want to encourage HW folks into doing the same kind of=20
> contraption
> in the future
More details in comments below.
>(btw, tell the TSI folks for me that they had a BAD BAD
> BAD idea to muck around with the base design that way, especially
> changing the register map in incompatible ways for no good reason).
>=20
Done!
> > + /* Configure MPIC outputs to CPU0 */
> > + tsi108_write_reg(TSI108_MPIC_OFFSET + 0x30c, 0);
> > }
>=20
> It doesn't use the standard multiple processor outputs mecanism of
> MPIC ?
> =20
> > +static struct mpic_info mpic_infos[] =3D {
> > + [0] =3D { /* Original OpenPIC compatible MPIC */
> > + .greg_base =3D MPIC_GREG_BASE,
> > + .greg_frr0 =3D MPIC_GREG_FEATURE_0,
> > + .greg_config0 =3D MPIC_GREG_GLOBAL_CONF_0,
> > + .greg_vendor_id =3D MPIC_GREG_VENDOR_ID,
> > + .greg_ipi_vp0 =3D MPIC_GREG_IPI_VECTOR_PRI_0,
> > + .greg_ipi_stride =3D MPIC_GREG_IPI_STRIDE,
> > + .greg_spurious =3D MPIC_GREG_SPURIOUS,
> > + .greg_tfrr =3D MPIC_GREG_TIMER_FREQ,
> > +
>=20
> .../...
>=20
> It's a bit sad to have to go all the way to doing such tables, but I
> suspect it's probably the best way to handle it at this=20
> point.
> Send more
> nastygrams to the HW folks for me.
>=20
Done:)
> > mpic->num_sources =3D 0; /* so far */
> > mpic->senses =3D senses;
> > mpic->senses_count =3D senses_count;
> > + mpic->hw_set =3D &mpic_infos[MPIC_GET_MOD_ID(flags)];
>=20
> Well... the model ID thing might not be that a bad idea in=20
> the end :) I
> need to think about it. I might have to deal with yet another=20
> MPIC that
> has another regiser map (yeah yeah, TSI aren't the only ones=20
> to not get
> it)...=20
>
I'll tell this to HW guys as well :)=20
> .../...
>=20
> > @@ -963,7 +1043,7 @@ int mpic_get_one_irq(struct mpic *mpic,=20
> > {
> > u32 irq;
> > =20
> > - irq =3D mpic_cpu_read(MPIC_CPU_INTACK) & MPIC_VECPRI_VECTOR_MASK;
> > + irq =3D mpic_cpu_read(mpic->hw_set->cpu_intack) &=20
> mpic->hw_set->irq_vpr_vector;
> > #ifdef DEBUG_LOW
> > DBG("%s: get_one_irq(): %d\n", mpic->name, irq);
> > #endif
> > @@ -972,11 +1052,18 @@ #ifdef DEBUG_LOW
> > DBG("%s: cascading ...\n", mpic->name);
> > #endif
> > irq =3D mpic->cascade(regs, mpic->cascade_data);
> > - mpic_eoi(mpic);
> > +#ifdef DEBUG_LOW
> > + DBG("%s: cascaded irq: %d\n", mpic->name, irq);
> > +#endif
> > + if (!(mpic->flags & MPIC_CASC_NOEOI))
> > + mpic_eoi(mpic);
> > return irq;
> > }
>=20
> Can you tell me why you need the above ? (Why you aren't EOI'ing the
> cascade ?) Note that the cascade handling is going away from=20
> mpic anyway
> with the port to genirq that I'll publish later this week for=20
> 2.6.18 and
> it will almost be handled as a normal interrupt...
>=20
We have a level-signalled irq from the cascaded PCI interrupt =
controller. If I do EOI at=20
this time, level request will not have chance to be cleared (unless all =
PCI interrupts have
an SA_INTERRUPT flag) and result in recurring interrupts.=20
I chose to have an individual flag instead of checking model ID to avoid =
multiple checks within ISR (in case if we have more that one mpic =
version requiring this option). I also expect that it may be useful for =
any external level-signalling cascades connected to MPIC. =20
> > - if (unlikely(irq =3D=3D MPIC_VEC_SPURRIOUS))
> > + if (unlikely(irq =3D=3D MPIC_VEC_SPURRIOUS)) {
> > + if (mpic->flags & MPIC_SPV_EOI)
> > + mpic_eoi(mpic);
> > return -1;
> > + }
>=20
> I think the above thing could just test the model ID. It's=20
> unlikely that
> another implementation need the same "feature", so just test the model
> ID rather than adding a flag and if we ever have another=20
> model with the
> same "feature", then we'll go back to adding a flag :)
>=20
Motivation is the same as above - I just do not want to have multiple ID =
checks here. I agree that it is driven by mpic type (model ID) only. I =
can remove this one if you do not expect any
new "broken" MPICs on horizon. =20
> Cheers,
> Ben.
>=20
Thanks for your feedback,
Alex.
>=20
>=20
^ permalink raw reply [flat|nested] 16+ messages in thread
* RE: [PATCH/2.6.17-rc4 4/10]Powerpc: Add tsi108 pic support
@ 2006-06-06 18:58 Alexandre Bounine
0 siblings, 0 replies; 16+ messages in thread
From: Alexandre Bounine @ 2006-06-06 18:58 UTC (permalink / raw)
To: Benjamin Herrenschmidt, Zang Roy-r61911
Cc: linuxppc-dev list, Paul Mackerras, Yang Xin-Xin-r48390
I forgot to mention another argument in favor of adding separate =
MPIC_SPV_EOI and MPIC_CASC_NOEOI flags:
If we have MPIC with "broken" logic but standard register map we can use =
model ID =3D 0 for
the standard MPIC without creating additional data structure.
Regards,
Alex.
-----Original Message-----
From: Alexandre Bounine=20
Sent: Tuesday, June 06, 2006 10:46 AM
To: 'Benjamin Herrenschmidt'; Zang Roy-r61911
Cc: Kumar Gala; linuxppc-dev list; Yang Xin-Xin-r48390; Paul Mackerras
Subject: RE: [PATCH/2.6.17-rc4 4/10]Powerpc: Add tsi108 pic support
> -----Original Message-----
> From: Benjamin Herrenschmidt [mailto:benh@kernel.crashing.org]
> Sent: Tuesday, June 06, 2006 6:17 AM
> To: Zang Roy-r61911
> Cc: Alexandre Bounine; Kumar Gala; linuxppc-dev list; Yang
> Xin-Xin-r48390; Paul Mackerras
> Subject: RE: [PATCH/2.6.17-rc4 4/10]Powerpc: Add tsi108 pic support
>=20
>=20
> On Tue, 2006-06-06 at 17:43 +0800, Zang Roy-r61911 wrote:
>=20
> > Update Tsi108 implementation of MPIC.
> > Any comment?=20
> >=20
> > Integrate Tundra Semiconductor tsi108 host bridge interrupt=20
> controller=20
> > to mpic arch.
>=20
> Looks much better :) Still a few things...=20
>
Sounds good. We are moving in right direction :)
=20
> > + mpic =3D mpic_alloc(mpic_paddr,
> > + MPIC_PRIMARY | MPIC_BIG_ENDIAN |=20
> MPIC_WANTS_RESET |
> > + MPIC_SPV_EOI | MPIC_CASC_NOEOI |=20
> > + MPIC_MOD_ID(MPIC_ID_TSI108),
> > + 0, /* num_sources used */
> > + TSI108_IRQ_BASE,
> > + 0, /* num_sources used */
> > + NR_IRQS - 4 /* XXXX */,
> > + mpc7448_hpc2_pic_initsenses,
> > + sizeof(mpc7448_hpc2_pic_initsenses),=20
> "Tsi108_PIC");
>=20
> That's a hell lot of new flags... I'm not sure we need that many or a
> single TSI108 one that encloses all the new ones. Also, I'm=20
> not sure we
> need that model ID encoding thing. Let's do things simple, besides, I
> don't want to encourage HW folks into doing the same kind of=20
> contraption
> in the future
More details in comments below.
>(btw, tell the TSI folks for me that they had a BAD BAD
> BAD idea to muck around with the base design that way, especially
> changing the register map in incompatible ways for no good reason).
>=20
Done!
> > + /* Configure MPIC outputs to CPU0 */
> > + tsi108_write_reg(TSI108_MPIC_OFFSET + 0x30c, 0);
> > }
>=20
> It doesn't use the standard multiple processor outputs mecanism of
> MPIC ?
> =20
> > +static struct mpic_info mpic_infos[] =3D {
> > + [0] =3D { /* Original OpenPIC compatible MPIC */
> > + .greg_base =3D MPIC_GREG_BASE,
> > + .greg_frr0 =3D MPIC_GREG_FEATURE_0,
> > + .greg_config0 =3D MPIC_GREG_GLOBAL_CONF_0,
> > + .greg_vendor_id =3D MPIC_GREG_VENDOR_ID,
> > + .greg_ipi_vp0 =3D MPIC_GREG_IPI_VECTOR_PRI_0,
> > + .greg_ipi_stride =3D MPIC_GREG_IPI_STRIDE,
> > + .greg_spurious =3D MPIC_GREG_SPURIOUS,
> > + .greg_tfrr =3D MPIC_GREG_TIMER_FREQ,
> > +
>=20
> .../...
>=20
> It's a bit sad to have to go all the way to doing such tables, but I
> suspect it's probably the best way to handle it at this=20
> point.
> Send more
> nastygrams to the HW folks for me.
>=20
Done:)
> > mpic->num_sources =3D 0; /* so far */
> > mpic->senses =3D senses;
> > mpic->senses_count =3D senses_count;
> > + mpic->hw_set =3D &mpic_infos[MPIC_GET_MOD_ID(flags)];
>=20
> Well... the model ID thing might not be that a bad idea in=20
> the end :) I
> need to think about it. I might have to deal with yet another=20
> MPIC that
> has another regiser map (yeah yeah, TSI aren't the only ones=20
> to not get
> it)...=20
>
I'll tell this to HW guys as well :)=20
> .../...
>=20
> > @@ -963,7 +1043,7 @@ int mpic_get_one_irq(struct mpic *mpic,=20
> > {
> > u32 irq;
> > =20
> > - irq =3D mpic_cpu_read(MPIC_CPU_INTACK) & MPIC_VECPRI_VECTOR_MASK;
> > + irq =3D mpic_cpu_read(mpic->hw_set->cpu_intack) &=20
> mpic->hw_set->irq_vpr_vector;
> > #ifdef DEBUG_LOW
> > DBG("%s: get_one_irq(): %d\n", mpic->name, irq);
> > #endif
> > @@ -972,11 +1052,18 @@ #ifdef DEBUG_LOW
> > DBG("%s: cascading ...\n", mpic->name);
> > #endif
> > irq =3D mpic->cascade(regs, mpic->cascade_data);
> > - mpic_eoi(mpic);
> > +#ifdef DEBUG_LOW
> > + DBG("%s: cascaded irq: %d\n", mpic->name, irq);
> > +#endif
> > + if (!(mpic->flags & MPIC_CASC_NOEOI))
> > + mpic_eoi(mpic);
> > return irq;
> > }
>=20
> Can you tell me why you need the above ? (Why you aren't EOI'ing the
> cascade ?) Note that the cascade handling is going away from=20
> mpic anyway
> with the port to genirq that I'll publish later this week for=20
> 2.6.18 and
> it will almost be handled as a normal interrupt...
>=20
We have a level-signalled irq from the cascaded PCI interrupt =
controller. If I do EOI at=20
this time, level request will not have chance to be cleared (unless all =
PCI interrupts have
an SA_INTERRUPT flag) and result in recurring interrupts.=20
I chose to have an individual flag instead of checking model ID to avoid =
multiple checks within ISR (in case if we have more that one mpic =
version requiring this option). I also expect that it may be useful for =
any external level-signalling cascades connected to MPIC. =20
> > - if (unlikely(irq =3D=3D MPIC_VEC_SPURRIOUS))
> > + if (unlikely(irq =3D=3D MPIC_VEC_SPURRIOUS)) {
> > + if (mpic->flags & MPIC_SPV_EOI)
> > + mpic_eoi(mpic);
> > return -1;
> > + }
>=20
> I think the above thing could just test the model ID. It's=20
> unlikely that
> another implementation need the same "feature", so just test the model
> ID rather than adding a flag and if we ever have another=20
> model with the
> same "feature", then we'll go back to adding a flag :)
>=20
Motivation is the same as above - I just do not want to have multiple ID =
checks here. I agree that it is driven by mpic type (model ID) only. I =
can remove this one if you do not expect any
new "broken" MPICs on horizon. =20
> Cheers,
> Ben.
>=20
Thanks for your feedback,
Alex.
>=20
>=20
^ permalink raw reply [flat|nested] 16+ messages in thread
* RE: [PATCH/2.6.17-rc4 4/10]Powerpc: Add tsi108 pic support
2006-06-06 14:45 Alexandre Bounine
@ 2006-06-06 23:08 ` Benjamin Herrenschmidt
0 siblings, 0 replies; 16+ messages in thread
From: Benjamin Herrenschmidt @ 2006-06-06 23:08 UTC (permalink / raw)
To: Alexandre Bounine; +Cc: linuxppc-dev list, Paul Mackerras, Yang Xin-Xin-r48390
On Tue, 2006-06-06 at 10:45 -0400, Alexandre Bounine wrote:
> We have a level-signalled irq from the cascaded PCI interrupt controller. If I do EOI at
> this time, level request will not have chance to be cleared (unless all PCI interrupts have
> an SA_INTERRUPT flag) and result in recurring interrupts.
Hrm... Ok, when the cascade is a 8259 or an MPIC, we don't have that
problem despite the output also being level... I think that's because
the cascade handler itself will mask the cascade interrupt (on MPIC,
reading the irq does an ack which will mask that priority level). If
your cascaded controller doesn't act this way, you may need something a
bit different in your cascade handler rather than changing mpic.
However, I wouldn't bother too much. As I said, this is all changing a
lot at the moment as I'm porting powerpc to Ingo Molnar and Thomas
Gleixner's new "genirq" layer. Cascade handling will be different and
taken out of MPIC, so you'll be able to implement it the way your want
(with much greater control on what happens) without changing the MPIC
driver.
I'll have patches posted on the list in a few days hopefully.
> I chose to have an individual flag instead of checking model ID to avoid multiple checks within ISR
> (in case if we have more that one mpic version requiring this option). I also expect that it may be
> useful for any external level-signalling cascades connected to MPIC.
As I said above, I think it can just go away with the port to genirq.
> Motivation is the same as above - I just do not want to have multiple ID checks here. I agree that it is
> driven by mpic type (model ID) only. I can remove this one if you do not expect any
> new "broken" MPICs on horizon.
Well, I do expect broken ones but not with that specific issue :)
Cheers,
Ben.
^ permalink raw reply [flat|nested] 16+ messages in thread
* RE: [PATCH/2.6.17-rc4 4/10]Powerpc: Add tsi108 pic support
@ 2006-06-09 9:25 Zang Roy-r61911
2006-06-13 2:09 ` Benjamin Herrenschmidt
0 siblings, 1 reply; 16+ messages in thread
From: Zang Roy-r61911 @ 2006-06-09 9:25 UTC (permalink / raw)
To: Benjamin Herrenschmidt, Alexandre Bounine
Cc: linuxppc-dev list, Paul Mackerras, Yang Xin-Xin-r48390
>
> On Tue, 2006-06-06 at 10:45 -0400, Alexandre Bounine wrote:
>
> > We have a level-signalled irq from the cascaded PCI interrupt
> > controller. If I do EOI at this time, level request will not have
> > chance to be cleared (unless all PCI interrupts have an
> SA_INTERRUPT flag) and result in recurring interrupts.
>
> Hrm... Ok, when the cascade is a 8259 or an MPIC, we don't
> have that problem despite the output also being level... I
> think that's because the cascade handler itself will mask the
> cascade interrupt (on MPIC, reading the irq does an ack which
> will mask that priority level). If your cascaded controller
> doesn't act this way, you may need something a bit different
> in your cascade handler rather than changing mpic.
>
> However, I wouldn't bother too much. As I said, this is all
> changing a lot at the moment as I'm porting powerpc to Ingo
> Molnar and Thomas Gleixner's new "genirq" layer. Cascade
> handling will be different and taken out of MPIC, so you'll
> be able to implement it the way your want (with much greater
> control on what happens) without changing the MPIC driver.
>
> I'll have patches posted on the list in a few days hopefully.
>
> > I chose to have an individual flag instead of checking model ID to
> > avoid multiple checks within ISR (in case if we have more
> that one mpic version requiring this option). I also expect
> that it may be
> > useful for any external level-signalling cascades connected
> to MPIC.
>
> As I said above, I think it can just go away with the port to genirq.
>
> > Motivation is the same as above - I just do not want to
> have multiple
> > ID checks here. I agree that it is driven by mpic type (model ID)
> > only. I can remove this one if you do not expect any new
> "broken" MPICs on horizon.
>
> Well, I do expect broken ones but not with that specific issue :)
>
> Cheers,
> Ben.
>
update mpic support for tsi108 .
Any comment?
diff --git a/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c b/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c
index db5dc10..efee7da 100644
--- a/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c
+++ b/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c
@@ -43,7 +43,16 @@ #include <asm/pci-bridge.h> #include <asm/reg.h> #include <mm/mmu_decl.h> #include "mpc7448_hpc2.h"
-#include <asm/tsi108_pic.h>
+#include <asm/tsi108_irq.h>
+#include <asm/mpic.h>
+
+#undef DEBUG
+
+#ifdef DEBUG
+#define DBG(fmt...) do { printk(fmt); } while(0) #else #define
+DBG(fmt...) do { } while(0) #endif
#ifndef CONFIG_PCI
isa_io_base = MPC7448_HPC2_ISA_IO_BASE; @@ -53,20 +62,8 @@ #endif
extern int add_bridge(struct device_node *dev); extern void _nmask_and_or_msr(unsigned long nmask, unsigned long or_val);
-
-#ifdef TSI108_ETH
-hw_info hw_info_table[TSI108_ETH_MAX_PORTS + 1] = {
- {TSI108_CSR_ADDR_PHYS + TSI108_ETH_OFFSET,
- TSI108_CSR_ADDR_PHYS + TSI108_ETH_OFFSET,
- TSI108_PHY0_ADDR, IRQ_TSI108_GIGE0},
-
- {TSI108_CSR_ADDR_PHYS + TSI108_ETH_OFFSET + 0x400,
- TSI108_CSR_ADDR_PHYS + TSI108_ETH_OFFSET,
- TSI108_PHY1_ADDR, IRQ_TSI108_GIGE1},
-
- {TBL_END, TBL_END, TBL_END, TBL_END}
-};
-#endif
+extern void tsi108_pci_int_init(void);
+extern int tsi108_irq_cascade(struct pt_regs *regs, void *unused);
/*
* Define all of the IRQ senses and polarities. Taken from the @@ -76,10 +73,32 @@ #endif
*/
static u_char mpc7448_hpc2_pic_initsenses[] __initdata = {
+ /* External on-board sources */
(IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* INT[0] XINT0 from FPGA */
(IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* INT[1] XINT1 from FPGA */
(IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* INT[2] PHY_INT from both GIGE */
(IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* INT[3] RESERVED */
+ /* Internal Tsi108/109 interrupt sources */
+ (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* Reserved IRQ */
+ (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* Reserved IRQ */
+ (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* Reserved IRQ */
+ (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* Reserved IRQ */
+ (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* DMA0 */
+ (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* DMA1 */
+ (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* DMA2 */
+ (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* DMA3 */
+ (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* UART0 */
+ (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* UART1 */
+ (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* I2C */
+ (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* GPIO */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* GIGE0 */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* GIGE1 */
+ (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* Reserved IRQ */
+ (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* HLP */
+ (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* SDC */
+ (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* Processor IF */
+ (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* Reserved IRQ */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* PCI/X block */
};
/*
@@ -196,18 +215,43 @@ #endif
*/
static void __init mpc7448_hpc2_init_IRQ(void) {
+ struct mpic *mpic;
+ phys_addr_t mpic_paddr = 0;
+ struct device_node *tsi_pic;
+
+ tsi_pic = of_find_node_by_type(NULL, "open-pic");
+ if (tsi_pic) {
+ unsigned int size;
+ void *prop = get_property(tsi_pic, "reg", &size);
+ mpic_paddr = of_translate_address(tsi_pic, prop);
+ }
- tsi108_pic_init(mpc7448_hpc2_pic_initsenses);
+ if (mpic_paddr == 0) {
+ printk("%s: No tsi108 PIC found !\n", __FUNCTION__);
+ return;
+ }
- /* Configure MPIC outputs to CPU0 */
- tsi108_pic_set_output(0, IRQ_SENSE_EDGE, IRQ_POLARITY_NEGATIVE);
-}
+ DBG("%s: tsi108pic phys_addr = 0x%x\n", __FUNCTION__,
+ (u32) mpic_paddr);
-static void __init mpc7448_hpc2_map_io(void) -{
- /* Tsi108 CSR mapping */
- io_block_mapping(TSI108_CSR_ADDR_VIRT, TSI108_CSR_ADDR_PHYS,
- 0x100000, _PAGE_IO);
+ mpic = mpic_alloc(mpic_paddr,
+ MPIC_PRIMARY | MPIC_BIG_ENDIAN | MPIC_WANTS_RESET |
+ MPIC_SPV_EOI | MPIC_MOD_ID(MPIC_ID_TSI108),
+ 0, /* num_sources used */
+ TSI108_IRQ_BASE,
+ 0, /* num_sources used */
+ NR_IRQS - 4 /* XXXX */,
+ mpc7448_hpc2_pic_initsenses,
+ sizeof(mpc7448_hpc2_pic_initsenses), "Tsi108_PIC");
+
+ BUG_ON(mpic == NULL); /* XXXX */
+
+ mpic_init(mpic);
+ mpic_setup_cascade(IRQ_TSI108_PCI, tsi108_irq_cascade, mpic);
+ tsi108_pci_int_init();
+
+ /* Configure MPIC outputs to CPU0 */
+ tsi108_write_reg(TSI108_MPIC_OFFSET + 0x30c, 0);
}
void mpc7448_hpc2_show_cpuinfo(struct seq_file *m) @@ -269,10 +313,9 @@ define_machine(mpc7448_hpc2){
.setup_arch = mpc7448_hpc2_setup_arch,
.init_IRQ = mpc7448_hpc2_init_IRQ,
.show_cpuinfo = mpc7448_hpc2_show_cpuinfo,
- .get_irq = tsi108_pic_get_irq,
+ .get_irq = mpic_get_irq,
.restart = mpc7448_hpc2_restart,
.calibrate_decr = generic_calibrate_decr,
- .setup_io_mappings = mpc7448_hpc2_map_io,
.machine_check_exception= mpc7448_machine_check_exception,
.progress = udbg_progress,
};
diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile index 8c0afb7..048e1f6 100644
--- a/arch/powerpc/sysdev/Makefile
+++ b/arch/powerpc/sysdev/Makefile
@@ -8,4 +8,4 @@ obj-$(CONFIG_U3_DART) += dart_iommu.o
obj-$(CONFIG_MMIO_NVRAM) += mmio_nvram.o
obj-$(CONFIG_PPC_83xx) += ipic.o
obj-$(CONFIG_FSL_SOC) += fsl_soc.o
-obj-$(CONFIG_TSI108_BRIDGE) += tsi108_common.o tsi108_pic.o
+obj-$(CONFIG_TSI108_BRIDGE) += tsi108_common.o tsi108_pci_int.o
diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c index 7dcdfcb..fc21e47 100644
--- a/arch/powerpc/sysdev/mpic.c
+++ b/arch/powerpc/sysdev/mpic.c
@@ -55,6 +55,78 @@ #define distribute_irqs (0)
#endif
#endif
+static struct mpic_info mpic_infos[] = {
+ [0] = { /* Original OpenPIC compatible MPIC */
+ .greg_base = MPIC_GREG_BASE,
+ .greg_frr0 = MPIC_GREG_FEATURE_0,
+ .greg_config0 = MPIC_GREG_GLOBAL_CONF_0,
+ .greg_vendor_id = MPIC_GREG_VENDOR_ID,
+ .greg_ipi_vp0 = MPIC_GREG_IPI_VECTOR_PRI_0,
+ .greg_ipi_stride = MPIC_GREG_IPI_STRIDE,
+ .greg_spurious = MPIC_GREG_SPURIOUS,
+ .greg_tfrr = MPIC_GREG_TIMER_FREQ,
+
+ .timer_base = MPIC_TIMER_BASE,
+ .timer_stride = MPIC_TIMER_STRIDE,
+ .timer_ccr = MPIC_TIMER_CURRENT_CNT,
+ .timer_bcr = MPIC_TIMER_BASE_CNT,
+ .timer_vpr = MPIC_TIMER_VECTOR_PRI,
+ .timer_dest = MPIC_TIMER_DESTINATION,
+
+ .cpu_base = MPIC_CPU_BASE,
+ .cpu_stride = MPIC_CPU_STRIDE,
+ .cpu_ipi_disp0 = MPIC_CPU_IPI_DISPATCH_0,
+ .cpu_ipi_disp_stride = MPIC_CPU_IPI_DISPATCH_STRIDE,
+ .cpu_task_pri = MPIC_CPU_CURRENT_TASK_PRI,
+ .cpu_whoami = MPIC_CPU_WHOAMI,
+ .cpu_intack = MPIC_CPU_INTACK,
+ .cpu_eoi = MPIC_CPU_EOI,
+
+ .irq_base = MPIC_IRQ_BASE,
+ .irq_stride = MPIC_IRQ_STRIDE,
+ .irq_vpr = MPIC_IRQ_VECTOR_PRI,
+ .irq_vpr_vector = MPIC_VECPRI_VECTOR_MASK,
+ .irq_vpr_polpos = MPIC_VECPRI_POLARITY_POSITIVE,
+ .irq_vpr_senlvl = MPIC_VECPRI_SENSE_LEVEL,
+ .irq_dest = MPIC_IRQ_DESTINATION,
+ },
+
+ [1] = { /* Tsi108/109 PIC */
+ .greg_base = TSI108_GREG_BASE,
+ .greg_frr0 = TSI108_GREG_FEATURE_0,
+ .greg_config0 = TSI108_GREG_GLOBAL_CONF_0,
+ .greg_vendor_id = TSI108_GREG_VENDOR_ID,
+ .greg_ipi_vp0 = TSI108_GREG_IPI_VECTOR_PRI_0,
+ .greg_ipi_stride = TSI108_GREG_IPI_STRIDE,
+ .greg_spurious = TSI108_GREG_SPURIOUS,
+ .greg_tfrr = TSI108_GREG_TIMER_FREQ,
+
+ .timer_base = TSI108_TIMER_BASE,
+ .timer_stride = TSI108_TIMER_STRIDE,
+ .timer_ccr = TSI108_TIMER_CURRENT_CNT,
+ .timer_bcr = TSI108_TIMER_BASE_CNT,
+ .timer_vpr = TSI108_TIMER_VECTOR_PRI,
+ .timer_dest = TSI108_TIMER_DESTINATION,
+
+ .cpu_base = TSI108_CPU_BASE,
+ .cpu_stride = TSI108_CPU_STRIDE,
+ .cpu_ipi_disp0 = TSI108_CPU_IPI_DISPATCH_0,
+ .cpu_ipi_disp_stride = TSI108_CPU_IPI_DISPATCH_STRIDE,
+ .cpu_task_pri = TSI108_CPU_CURRENT_TASK_PRI,
+ .cpu_whoami = 0xFFFFFFFF,
+ .cpu_intack = TSI108_CPU_INTACK,
+ .cpu_eoi = TSI108_CPU_EOI,
+
+ .irq_base = TSI108_IRQ_REG_BASE,
+ .irq_stride = TSI108_IRQ_STRIDE,
+ .irq_vpr = TSI108_IRQ_VECTOR_PRI,
+ .irq_vpr_vector = TSI108_VECPRI_VECTOR_MASK,
+ .irq_vpr_polpos = TSI108_VECPRI_POLARITY_POSITIVE,
+ .irq_vpr_senlvl = TSI108_VECPRI_SENSE_LEVEL,
+ .irq_dest = TSI108_IRQ_DESTINATION,
+ },
+};
+
/*
* Register accessor functions
*/
@@ -81,7 +153,8 @@ static inline void _mpic_write(unsigned static inline u32 _mpic_ipi_read(struct mpic *mpic, unsigned int ipi) {
unsigned int be = (mpic->flags & MPIC_BIG_ENDIAN) != 0;
- unsigned int offset = MPIC_GREG_IPI_VECTOR_PRI_0 + (ipi * 0x10);
+ unsigned int offset = mpic->hw_set->greg_ipi_vp0 +
+ (ipi * mpic->hw_set->greg_ipi_stride);
if (mpic->flags & MPIC_BROKEN_IPI)
be = !be;
@@ -90,7 +163,8 @@ static inline u32 _mpic_ipi_read(struct
static inline void _mpic_ipi_write(struct mpic *mpic, unsigned int ipi, u32 value) {
- unsigned int offset = MPIC_GREG_IPI_VECTOR_PRI_0 + (ipi * 0x10);
+ unsigned int offset = mpic->hw_set->greg_ipi_vp0 +
+ (ipi * mpic->hw_set->greg_ipi_stride);
_mpic_write(mpic->flags & MPIC_BIG_ENDIAN, mpic->gregs, offset, value); } @@ -121,7 +195,7 @@ static inline u32 _mpic_irq_read(struct
unsigned int idx = src_no & mpic->isu_mask;
return _mpic_read(mpic->flags & MPIC_BIG_ENDIAN, mpic->isus[isu],
- reg + (idx * MPIC_IRQ_STRIDE));
+ reg + (idx * mpic->hw_set->irq_stride));
}
static inline void _mpic_irq_write(struct mpic *mpic, unsigned int src_no, @@ -131,7 +205,7 @@ static inline void _mpic_irq_write(struc
unsigned int idx = src_no & mpic->isu_mask;
_mpic_write(mpic->flags & MPIC_BIG_ENDIAN, mpic->isus[isu],
- reg + (idx * MPIC_IRQ_STRIDE), value);
+ reg + (idx * mpic->hw_set->irq_stride), value);
}
#define mpic_read(b,r) _mpic_read(mpic->flags & MPIC_BIG_ENDIAN,(b),(r))
@@ -157,8 +231,8 @@ static void __init mpic_test_broken_ipi( {
u32 r;
- mpic_write(mpic->gregs, MPIC_GREG_IPI_VECTOR_PRI_0, MPIC_VECPRI_MASK);
- r = mpic_read(mpic->gregs, MPIC_GREG_IPI_VECTOR_PRI_0);
+ mpic_write(mpic->gregs, mpic->hw_set->greg_ipi_vp0, MPIC_VECPRI_MASK);
+ r = mpic_read(mpic->gregs, mpic->hw_set->greg_ipi_vp0);
if (r == le32_to_cpu(MPIC_VECPRI_MASK)) {
printk(KERN_INFO "mpic: Detected reversed IPI registers\n"); @@ -392,8 +466,8 @@ static inline struct mpic * mpic_from_ir
/* Send an EOI */
static inline void mpic_eoi(struct mpic *mpic) {
- mpic_cpu_write(MPIC_CPU_EOI, 0);
- (void)mpic_cpu_read(MPIC_CPU_WHOAMI);
+ mpic_cpu_write(mpic->hw_set->cpu_eoi, 0);
+ (void)mpic_cpu_read(mpic->hw_set->cpu_task_pri);
}
#ifdef CONFIG_SMP
@@ -419,8 +493,8 @@ static void mpic_enable_irq(unsigned int
DBG("%p: %s: enable_irq: %d (src %d)\n", mpic, mpic->name, irq, src);
- mpic_irq_write(src, MPIC_IRQ_VECTOR_PRI,
- mpic_irq_read(src, MPIC_IRQ_VECTOR_PRI) &
+ mpic_irq_write(src, mpic->hw_set->irq_vpr,
+ mpic_irq_read(src, mpic->hw_set->irq_vpr) &
~MPIC_VECPRI_MASK);
/* make sure mask gets to controller before we return to user */ @@ -429,7 +503,7 @@ static void mpic_enable_irq(unsigned int
printk(KERN_ERR "mpic_enable_irq timeout\n");
break;
}
- } while(mpic_irq_read(src, MPIC_IRQ_VECTOR_PRI) & MPIC_VECPRI_MASK);
+ } while(mpic_irq_read(src, mpic->hw_set->irq_vpr) & MPIC_VECPRI_MASK);
#ifdef CONFIG_MPIC_BROKEN_U3
if (mpic->flags & MPIC_BROKEN_U3) {
@@ -466,8 +540,8 @@ static void mpic_disable_irq(unsigned in
DBG("%s: disable_irq: %d (src %d)\n", mpic->name, irq, src);
- mpic_irq_write(src, MPIC_IRQ_VECTOR_PRI,
- mpic_irq_read(src, MPIC_IRQ_VECTOR_PRI) |
+ mpic_irq_write(src, mpic->hw_set->irq_vpr,
+ mpic_irq_read(src, mpic->hw_set->irq_vpr) |
MPIC_VECPRI_MASK);
/* make sure mask gets to controller before we return to user */ @@ -476,7 +550,7 @@ static void mpic_disable_irq(unsigned in
printk(KERN_ERR "mpic_enable_irq timeout\n");
break;
}
- } while(!(mpic_irq_read(src, MPIC_IRQ_VECTOR_PRI) & MPIC_VECPRI_MASK));
+ } while(!(mpic_irq_read(src, mpic->hw_set->irq_vpr) &
+MPIC_VECPRI_MASK));
}
static void mpic_shutdown_irq(unsigned int irq) @@ -557,7 +631,7 @@ static void mpic_set_affinity(unsigned i
cpus_and(tmp, cpumask, cpu_online_map);
- mpic_irq_write(irq - mpic->irq_offset, MPIC_IRQ_DESTINATION,
+ mpic_irq_write(irq - mpic->irq_offset, mpic->hw_set->irq_dest,
mpic_physmask(cpus_addr(tmp)[0]));
}
@@ -613,18 +687,20 @@ #endif /* CONFIG_SMP */
mpic->num_sources = 0; /* so far */
mpic->senses = senses;
mpic->senses_count = senses_count;
+ mpic->hw_set = &mpic_infos[MPIC_GET_MOD_ID(flags)];
/* Map the global registers */
- mpic->gregs = ioremap(phys_addr + MPIC_GREG_BASE, 0x1000);
- mpic->tmregs = mpic->gregs + ((MPIC_TIMER_BASE - MPIC_GREG_BASE) >> 2);
+ mpic->gregs = ioremap(phys_addr + mpic->hw_set->greg_base, 0x1000);
+ mpic->tmregs = mpic->gregs +
+ ((mpic->hw_set->timer_base - mpic->hw_set->greg_base) >> 2);
BUG_ON(mpic->gregs == NULL);
/* Reset */
if (flags & MPIC_WANTS_RESET) {
- mpic_write(mpic->gregs, MPIC_GREG_GLOBAL_CONF_0,
- mpic_read(mpic->gregs, MPIC_GREG_GLOBAL_CONF_0)
+ mpic_write(mpic->gregs, mpic->hw_set->greg_config0,
+ mpic_read(mpic->gregs, mpic->hw_set->greg_config0)
| MPIC_GREG_GCONF_RESET);
- while( mpic_read(mpic->gregs, MPIC_GREG_GLOBAL_CONF_0)
+ while( mpic_read(mpic->gregs, mpic->hw_set->greg_config0)
& MPIC_GREG_GCONF_RESET)
mb();
}
@@ -633,7 +709,7 @@ #endif /* CONFIG_SMP */
* MPICs, num sources as well. On ISU MPICs, sources are counted
* as ISUs are added
*/
- reg = mpic_read(mpic->gregs, MPIC_GREG_FEATURE_0);
+ reg = mpic_read(mpic->gregs, mpic->hw_set->greg_frr0);
mpic->num_cpus = ((reg & MPIC_GREG_FEATURE_LAST_CPU_MASK)
>> MPIC_GREG_FEATURE_LAST_CPU_SHIFT) + 1;
if (isu_size == 0)
@@ -642,16 +718,16 @@ #endif /* CONFIG_SMP */
/* Map the per-CPU registers */
for (i = 0; i < mpic->num_cpus; i++) {
- mpic->cpuregs[i] = ioremap(phys_addr + MPIC_CPU_BASE +
- i * MPIC_CPU_STRIDE, 0x1000);
+ mpic->cpuregs[i] = ioremap(phys_addr + mpic->hw_set->cpu_base +
+ i * mpic->hw_set->cpu_stride, 0x1000);
BUG_ON(mpic->cpuregs[i] == NULL);
}
/* Initialize main ISU if none provided */
if (mpic->isu_size == 0) {
mpic->isu_size = mpic->num_sources;
- mpic->isus[0] = ioremap(phys_addr + MPIC_IRQ_BASE,
- MPIC_IRQ_STRIDE * mpic->isu_size);
+ mpic->isus[0] = ioremap(phys_addr + mpic->hw_set->irq_base,
+ mpic->hw_set->irq_stride * mpic->isu_size);
BUG_ON(mpic->isus[0] == NULL);
}
mpic->isu_shift = 1 + __ilog2(mpic->isu_size - 1); @@ -693,7 +769,8 @@ void __init mpic_assign_isu(struct mpic
BUG_ON(isu_num >= MPIC_MAX_ISU);
- mpic->isus[isu_num] = ioremap(phys_addr, MPIC_IRQ_STRIDE * mpic->isu_size);
+ mpic->isus[isu_num] = ioremap(phys_addr,
+ mpic->hw_set->irq_stride * mpic->isu_size);
if ((isu_first + mpic->isu_size) > mpic->num_sources)
mpic->num_sources = isu_first + mpic->isu_size; } @@ -729,14 +806,15 @@ void __init mpic_init(struct mpic *mpic)
printk(KERN_INFO "mpic: Initializing for %d sources\n", mpic->num_sources);
/* Set current processor priority to max */
- mpic_cpu_write(MPIC_CPU_CURRENT_TASK_PRI, 0xf);
+ mpic_cpu_write(mpic->hw_set->cpu_task_pri, 0xf);
/* Initialize timers: just disable them all */
for (i = 0; i < 4; i++) {
mpic_write(mpic->tmregs,
- i * MPIC_TIMER_STRIDE + MPIC_TIMER_DESTINATION, 0);
+ i * mpic->hw_set->timer_stride +
+ mpic->hw_set->timer_dest, 0);
mpic_write(mpic->tmregs,
- i * MPIC_TIMER_STRIDE + MPIC_TIMER_VECTOR_PRI,
+ i * mpic->hw_set->timer_stride + mpic->hw_set->timer_vpr,
MPIC_VECPRI_MASK |
(MPIC_VEC_TIMER_0 + i));
}
@@ -780,14 +858,14 @@ #endif /* CONFIG_MPIC_BROKEN_U3 */
/* do senses munging */
if (mpic->senses && i < mpic->senses_count) {
if (mpic->senses[i] & IRQ_SENSE_LEVEL)
- vecpri |= MPIC_VECPRI_SENSE_LEVEL;
+ vecpri |= mpic->hw_set->irq_vpr_senlvl;
if (mpic->senses[i] & IRQ_POLARITY_POSITIVE)
- vecpri |= MPIC_VECPRI_POLARITY_POSITIVE;
+ vecpri |= mpic->hw_set->irq_vpr_polpos;
} else
- vecpri |= MPIC_VECPRI_SENSE_LEVEL;
+ vecpri |= mpic->hw_set->irq_vpr_senlvl;
/* remember if it was a level interrupts */
- level = (vecpri & MPIC_VECPRI_SENSE_LEVEL);
+ level = (vecpri & mpic->hw_set->irq_vpr_senlvl);
/* deal with broken U3 */
if (mpic->flags & MPIC_BROKEN_U3) {
@@ -795,7 +873,7 @@ #ifdef CONFIG_MPIC_BROKEN_U3
if (mpic_is_ht_interrupt(mpic, i)) {
vecpri &= ~(MPIC_VECPRI_SENSE_MASK |
MPIC_VECPRI_POLARITY_MASK);
- vecpri |= MPIC_VECPRI_POLARITY_POSITIVE;
+ vecpri |= mpic->hw_set->irq_vpr_polpos;
}
#else
printk(KERN_ERR "mpic: BROKEN_U3 set, but CONFIG doesn't match\n"); @@ -806,8 +884,8 @@ #endif
(level != 0));
/* init hw */
- mpic_irq_write(i, MPIC_IRQ_VECTOR_PRI, vecpri);
- mpic_irq_write(i, MPIC_IRQ_DESTINATION,
+ mpic_irq_write(i, mpic->hw_set->irq_vpr, vecpri);
+ mpic_irq_write(i, mpic->hw_set->irq_dest,
1 << hard_smp_processor_id());
/* init linux descriptors */
@@ -818,15 +896,16 @@ #endif
}
/* Init spurrious vector */
- mpic_write(mpic->gregs, MPIC_GREG_SPURIOUS, MPIC_VEC_SPURRIOUS);
+ mpic_write(mpic->gregs, mpic->hw_set->greg_spurious,
+MPIC_VEC_SPURRIOUS);
- /* Disable 8259 passthrough */
- mpic_write(mpic->gregs, MPIC_GREG_GLOBAL_CONF_0,
- mpic_read(mpic->gregs, MPIC_GREG_GLOBAL_CONF_0)
- | MPIC_GREG_GCONF_8259_PTHROU_DIS);
+ /* Disable 8259 passthrough, if supported */
+ if (MPIC_GET_MOD_ID(mpic->flags) != MPIC_ID_TSI108)
+ mpic_write(mpic->gregs, mpic->hw_set->greg_config0,
+ mpic_read(mpic->gregs, mpic->hw_set->greg_config0)
+ | MPIC_GREG_GCONF_8259_PTHROU_DIS);
/* Set current processor priority to 0 */
- mpic_cpu_write(MPIC_CPU_CURRENT_TASK_PRI, 0);
+ mpic_cpu_write(mpic->hw_set->cpu_task_pri, 0);
}
@@ -845,9 +924,9 @@ void mpic_irq_set_priority(unsigned int
mpic_ipi_write(irq - mpic->ipi_offset,
reg | (pri << MPIC_VECPRI_PRIORITY_SHIFT));
} else {
- reg = mpic_irq_read(irq - mpic->irq_offset,MPIC_IRQ_VECTOR_PRI)
+ reg = mpic_irq_read(irq - mpic->irq_offset,mpic->hw_set->irq_vpr)
& ~MPIC_VECPRI_PRIORITY_MASK;
- mpic_irq_write(irq - mpic->irq_offset, MPIC_IRQ_VECTOR_PRI,
+ mpic_irq_write(irq - mpic->irq_offset, mpic->hw_set->irq_vpr,
reg | (pri << MPIC_VECPRI_PRIORITY_SHIFT));
}
spin_unlock_irqrestore(&mpic_lock, flags); @@ -864,7 +943,7 @@ unsigned int mpic_irq_get_priority(unsig
if (is_ipi)
reg = mpic_ipi_read(irq - mpic->ipi_offset);
else
- reg = mpic_irq_read(irq - mpic->irq_offset, MPIC_IRQ_VECTOR_PRI);
+ reg = mpic_irq_read(irq - mpic->irq_offset, mpic->hw_set->irq_vpr);
spin_unlock_irqrestore(&mpic_lock, flags);
return (reg & MPIC_VECPRI_PRIORITY_MASK) >> MPIC_VECPRI_PRIORITY_SHIFT; } @@ -890,12 +969,12 @@ #ifdef CONFIG_SMP
*/
if (distribute_irqs) {
for (i = 0; i < mpic->num_sources ; i++)
- mpic_irq_write(i, MPIC_IRQ_DESTINATION,
- mpic_irq_read(i, MPIC_IRQ_DESTINATION) | msk);
+ mpic_irq_write(i, mpic->hw_set->irq_dest,
+ mpic_irq_read(i, mpic->hw_set->irq_dest) | msk);
}
/* Set current processor priority to 0 */
- mpic_cpu_write(MPIC_CPU_CURRENT_TASK_PRI, 0);
+ mpic_cpu_write(mpic->hw_set->cpu_task_pri, 0);
spin_unlock_irqrestore(&mpic_lock, flags); #endif /* CONFIG_SMP */ @@ -905,7 +984,7 @@ int mpic_cpu_get_priority(void) {
struct mpic *mpic = mpic_primary;
- return mpic_cpu_read(MPIC_CPU_CURRENT_TASK_PRI);
+ return mpic_cpu_read(mpic->hw_set->cpu_task_pri);
}
void mpic_cpu_set_priority(int prio)
@@ -913,7 +992,7 @@ void mpic_cpu_set_priority(int prio)
struct mpic *mpic = mpic_primary;
prio &= MPIC_CPU_TASKPRI_MASK;
- mpic_cpu_write(MPIC_CPU_CURRENT_TASK_PRI, prio);
+ mpic_cpu_write(mpic->hw_set->cpu_task_pri, prio);
}
/*
@@ -935,11 +1014,11 @@ void mpic_teardown_this_cpu(int secondar
/* let the mpic know we don't want intrs. */
for (i = 0; i < mpic->num_sources ; i++)
- mpic_irq_write(i, MPIC_IRQ_DESTINATION,
- mpic_irq_read(i, MPIC_IRQ_DESTINATION) & ~msk);
+ mpic_irq_write(i, mpic->hw_set->irq_dest,
+ mpic_irq_read(i, mpic->hw_set->irq_dest) & ~msk);
/* Set current processor priority to max */
- mpic_cpu_write(MPIC_CPU_CURRENT_TASK_PRI, 0xf);
+ mpic_cpu_write(mpic->hw_set->cpu_task_pri, 0xf);
spin_unlock_irqrestore(&mpic_lock, flags); } @@ -955,7 +1034,8 @@ #ifdef DEBUG_IPI
DBG("%s: send_ipi(ipi_no: %d)\n", mpic->name, ipi_no); #endif
- mpic_cpu_write(MPIC_CPU_IPI_DISPATCH_0 + ipi_no * 0x10,
+ mpic_cpu_write(mpic->hw_set->cpu_ipi_disp0 +
+ ipi_no * mpic->hw_set->cpu_ipi_disp_stride,
mpic_physmask(cpu_mask & cpus_addr(cpu_online_map)[0])); }
@@ -963,7 +1043,7 @@ int mpic_get_one_irq(struct mpic *mpic, {
u32 irq;
- irq = mpic_cpu_read(MPIC_CPU_INTACK) & MPIC_VECPRI_VECTOR_MASK;
+ irq = mpic_cpu_read(mpic->hw_set->cpu_intack) &
+mpic->hw_set->irq_vpr_vector;
#ifdef DEBUG_LOW
DBG("%s: get_one_irq(): %d\n", mpic->name, irq); #endif @@ -972,11 +1052,17 @@ #ifdef DEBUG_LOW
DBG("%s: cascading ...\n", mpic->name); #endif
irq = mpic->cascade(regs, mpic->cascade_data);
+#ifdef DEBUG_LOW
+ DBG("%s: cascaded irq: %d\n", mpic->name, irq); #endif
mpic_eoi(mpic);
return irq;
}
- if (unlikely(irq == MPIC_VEC_SPURRIOUS))
+ if (unlikely(irq == MPIC_VEC_SPURRIOUS)) {
+ if (mpic->flags & MPIC_SPV_EOI)
+ mpic_eoi(mpic);
return -1;
+ }
if (irq < MPIC_VEC_IPI_0) {
#ifdef DEBUG_IRQ
DBG("%s: irq %d\n", mpic->name, irq + mpic->irq_offset); diff --git a/arch/powerpc/sysdev/tsi108_common.c b/arch/powerpc/sysdev/tsi108_common.c
index 3c55f99..03b5d8f 100644
--- a/arch/powerpc/sysdev/tsi108_common.c
+++ b/arch/powerpc/sysdev/tsi108_common.c
@@ -90,9 +90,13 @@ tsi108_direct_write_config(struct pci_bu {
volatile unsigned char *cfg_addr;
+ if (ppc_md.pci_exclude_device)
+ if (ppc_md.pci_exclude_device(bus->number, devfunc))
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
cfg_addr = (unsigned char *)(tsi_mk_config_addr(bus->number,
devfunc, offset) |
- (offset & 0x03));
+ (offset & 0x03));
#ifdef TSI108_PCI_DEBUG
printk("PCI CFG write : ");
@@ -172,6 +176,10 @@ tsi108_direct_read_config(struct pci_bus
volatile unsigned char *cfg_addr;
u32 temp;
+ if (ppc_md.pci_exclude_device)
+ if (ppc_md.pci_exclude_device(bus->number, devfn))
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
cfg_addr = (unsigned char *)(tsi_mk_config_addr(bus->number,
devfn,
offset) | (offset &
diff --git a/include/asm-powerpc/mpic.h b/include/asm-powerpc/mpic.h index 6b9e781..72131a4 100644
--- a/include/asm-powerpc/mpic.h
+++ b/include/asm-powerpc/mpic.h
@@ -37,6 +37,7 @@ #define MPIC_GREG_IPI_VECTOR_PRI_0 0x000
#define MPIC_GREG_IPI_VECTOR_PRI_1 0x000b0
#define MPIC_GREG_IPI_VECTOR_PRI_2 0x000c0
#define MPIC_GREG_IPI_VECTOR_PRI_3 0x000d0
+#define MPIC_GREG_IPI_STRIDE 0x10
#define MPIC_GREG_SPURIOUS 0x000e0
#define MPIC_GREG_TIMER_FREQ 0x000f0
@@ -64,6 +65,7 @@ #define MPIC_CPU_IPI_DISPATCH_0 0x00040
#define MPIC_CPU_IPI_DISPATCH_1 0x00050
#define MPIC_CPU_IPI_DISPATCH_2 0x00060
#define MPIC_CPU_IPI_DISPATCH_3 0x00070
+#define MPIC_CPU_IPI_DISPATCH_STRIDE 0x00010
#define MPIC_CPU_CURRENT_TASK_PRI 0x00080
#define MPIC_CPU_TASKPRI_MASK 0x0000000f
#define MPIC_CPU_WHOAMI 0x00090
@@ -91,6 +93,55 @@ #define MPIC_VECPRI_SENSE_EDGE 0x0000
#define MPIC_VECPRI_SENSE_MASK 0x00400000
#define MPIC_IRQ_DESTINATION 0x00010
+/**********************************************************************
+********
+ * Tsi108 implementation of MPIC has many differences form the original
+one */
+
+/*
+ * Global registers
+ */
+
+#define TSI108_GREG_BASE 0x00000
+#define TSI108_GREG_FEATURE_0 0x00000
+#define TSI108_GREG_GLOBAL_CONF_0 0x00004
+#define TSI108_GREG_VENDOR_ID 0x0000c
+#define TSI108_GREG_IPI_VECTOR_PRI_0 0x00204 /* Doorbell 0 */
+#define TSI108_GREG_IPI_STRIDE 0x0c
+#define TSI108_GREG_SPURIOUS 0x00010
+#define TSI108_GREG_TIMER_FREQ 0x00014
+
+/*
+ * Timer registers
+ */
+#define TSI108_TIMER_BASE 0x0030
+#define TSI108_TIMER_STRIDE 0x10
+#define TSI108_TIMER_CURRENT_CNT 0x00000
+#define TSI108_TIMER_BASE_CNT 0x00004
+#define TSI108_TIMER_VECTOR_PRI 0x00008
+#define TSI108_TIMER_DESTINATION 0x0000c
+
+/*
+ * Per-Processor registers
+ */
+#define TSI108_CPU_BASE 0x00300
+#define TSI108_CPU_STRIDE 0x00040
+#define TSI108_CPU_IPI_DISPATCH_0 0x00200
+#define TSI108_CPU_IPI_DISPATCH_STRIDE 0x00000
+#define TSI108_CPU_CURRENT_TASK_PRI 0x00000
+#define TSI108_CPU_INTACK 0x00004
+#define TSI108_CPU_EOI 0x00008
+
+/*
+ * Per-source registers
+ */
+#define TSI108_IRQ_REG_BASE 0x00100
+#define TSI108_IRQ_STRIDE 0x00008
+#define TSI108_IRQ_VECTOR_PRI 0x00000
+#define TSI108_VECPRI_VECTOR_MASK 0x000000ff
+#define TSI108_VECPRI_POLARITY_POSITIVE 0x01000000
+#define TSI108_VECPRI_SENSE_LEVEL 0x02000000
+#define TSI108_IRQ_DESTINATION 0x00004
+
#define MPIC_MAX_IRQ_SOURCES 2048
#define MPIC_MAX_CPUS 32
#define MPIC_MAX_ISU 32
@@ -124,6 +175,40 @@ struct mpic_irq_fixup }; #endif /* CONFIG_MPIC_BROKEN_U3 */
+struct mpic_info {
+ u32 greg_base; /* offset of global registers from MPIC base */
+ u32 greg_frr0; /* FRR0 offset from base */
+ u32 greg_config0; /* Global Config register offset from base */
+ u32 greg_vendor_id; /* VID register offset from base */
+ u32 greg_ipi_vp0; /* IPI Vector/Priority Registers */
+ u32 greg_ipi_stride; /* IPI Vector/Priority Registers spacing */
+ u32 greg_spurious; /* Spurious Vector Register */
+ u32 greg_tfrr; /* Global Timer Frequency Reporting Register */
+
+ u32 timer_base; /* Global Timer Registers base */
+ u32 timer_stride; /* Global Timer Registers spacing */
+ u32 timer_ccr; /* Global Timer Current Count Register */
+ u32 timer_bcr; /* Global Timer Base Count Register */
+ u32 timer_vpr; /* Global Timer Vector/Priority Register */
+ u32 timer_dest; /* Global Timer Destination Register */
+
+ u32 cpu_base; /* Global Timer Destination Register */
+ u32 cpu_stride; /* Global Timer Destination Register */
+ u32 cpu_ipi_disp0; /* IPI 0 Dispatch Command Register */
+ u32 cpu_ipi_disp_stride; /* IPI Dispatch spacing */
+ u32 cpu_task_pri; /* Processor Current Task Priority Register */
+ u32 cpu_whoami; /* Who Am I Register */
+ u32 cpu_intack; /* Interrupt Acknowledge Register */
+ u32 cpu_eoi; /* End of Interrupt Register */
+
+ u32 irq_base; /* Interrupt registers base */
+ u32 irq_stride; /* Interrupt registers spacing */
+ u32 irq_vpr; /* Interrupt Vector/Priority Register */
+ u32 irq_vpr_vector; /* Interrupt Vector Mask */
+ u32 irq_vpr_polpos; /* Interrupt Positive Polarity bit */
+ u32 irq_vpr_senlvl; /* Interrupt Level Sense bit */
+ u32 irq_dest; /* Interrupt Destination Register */
+};
/* The instance data of a given MPIC */ struct mpic @@ -168,6 +253,8 @@ #endif
volatile u32 __iomem *tmregs;
volatile u32 __iomem *cpuregs[MPIC_MAX_CPUS];
volatile u32 __iomem *isus[MPIC_MAX_ISU];
+ /* Pointer to HW info structure */
+ struct mpic_info *hw_set;
/* link */
struct mpic *next;
@@ -186,6 +273,14 @@ #define MPIC_BROKEN_U3 0x00000004
#define MPIC_BROKEN_IPI 0x00000008
/* MPIC wants a reset */
#define MPIC_WANTS_RESET 0x00000010
+/* Spurious vector requires EOI */
+#define MPIC_SPV_EOI 0x00000020
+/* MPIC HW modification ID */
+#define MPIC_MOD_ID_MASK 0x00000f00
+#define MPIC_MOD_ID(val) (((val) << 8) & MPIC_MOD_ID_MASK)
+#define MPIC_GET_MOD_ID(flags) (((flags) & MPIC_MOD_ID_MASK) >> 8)
+#define MPIC_ID_MPIC 0 /* Original MPIC */
+#define MPIC_ID_TSI108 1 /* Tsi108/109 PIC */
/* Allocate the controller structure and setup the linux irq descs
* for the range if interrupts passed in. No HW initialization is diff --git a/include/asm-powerpc/tsi108.h b/include/asm-powerpc/tsi108.h index ed9ec36..850c56d 100644
--- a/include/asm-powerpc/tsi108.h
+++ b/include/asm-powerpc/tsi108.h
@@ -55,6 +55,7 @@ #define TSI108_PCI_IRP_ENABLE (0x188)
#define TSI108_PCI_IRP_INTAD (0x18C)
#define TSI108_PCI_IRP_STAT_P_INT (0x00400000)
+#define TSI108_PCI_IRP_ENABLE_P_INT (0x00400000)
#define TSI108_CG_PWRUP_STATUS (0x234)
diff --git a/arch/powerpc/sysdev/tsi108_pci_int.c b/arch/powerpc/sysdev/tsi108_pci_int.c
index e69de29..c1ca187 100644
--- a/arch/powerpc/sysdev/tsi108_pci_int.c
+++ b/arch/powerpc/sysdev/tsi108_pci_int.c
@@ -0,0 +1,232 @@
+/*
+ * (C) Copyright 2005 Tundra Semiconductor Corp.
+ * Alex Bounine, <alexandreb@tundra.com).
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+/*
+ * Tsi108 PCI Interrupt Handling (cascaded to MPIC) */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/sysdev.h>
+#include <asm/ptrace.h>
+#include <asm/signal.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/prom.h>
+#include <asm/sections.h>
+#include <asm/hardirq.h>
+#include <asm/machdep.h>
+
+#include <asm/tsi108.h>
+#include <asm/tsi108_irq.h>
+
+#undef DEBUG
+#undef DBG_TSI108_INTERRUPT
+
+#ifdef DEBUG
+#define DBG(fmt...) do { printk(fmt); } while(0) #else #define
+DBG(fmt...) do { } while(0) #endif
+
+extern u32 get_vir_csrbase(void);
+extern u32 tsi108_read_reg(u32 reg_offset); extern void
+tsi108_write_reg(u32 reg_offset, u32 val);
+
+/*
+ * Low level utility functions
+ */
+
+static void tsi108_pci_int_mask(u_int irq) {
+ u_int irp_cfg;
+ int int_line = (irq - IRQ_PCI_INTAD_BASE);
+
+ irp_cfg = tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL);
+ mb();
+ irp_cfg |= (1 << int_line); /* INTx_DIR = output */
+ irp_cfg &= ~(3 << (8 + (int_line * 2))); /* INTx_TYPE = unused */
+ tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL, irp_cfg);
+ mb();
+ irp_cfg = tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL);
+}
+
+static void tsi108_pci_int_unmask(u_int irq) {
+ u_int irp_cfg;
+ int int_line = (irq - IRQ_PCI_INTAD_BASE);
+
+ irp_cfg = tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL);
+ mb();
+ irp_cfg &= ~(1 << int_line);
+ irp_cfg |= (3 << (8 + (int_line * 2)));
+ tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL, irp_cfg);
+ mb();
+}
+
+static void init_pci_source(void)
+{
+ tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL,
+ 0x0000ff00);
+ tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_ENABLE,
+ TSI108_PCI_IRP_ENABLE_P_INT);
+ mb();
+}
+
+static inline int get_pci_source(void)
+{
+ u_int temp = 0;
+ int irq = -1;
+ int i;
+ u_int pci_irp_stat;
+ static int mask = 0;
+
+ /* Read PCI/X block interrupt status register */
+ pci_irp_stat = tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_STAT);
+ mb();
+
+ if (pci_irp_stat & TSI108_PCI_IRP_STAT_P_INT) {
+ /* Process Interrupt from PCI bus INTA# - INTD# lines */
+ temp =
+ tsi108_read_reg(TSI108_PCI_OFFSET +
+ TSI108_PCI_IRP_INTAD) & 0xf;
+ mb();
+ for (i = 0; i < 4; i++, mask++) {
+ if (temp & (1 << mask % 4)) {
+ irq = IRQ_PCI_INTA + mask % 4;
+ mask++;
+ break;
+ }
+ }
+
+ /* Disable interrupts from PCI block */
+ temp = tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_ENABLE);
+ tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_ENABLE,
+ temp & ~TSI108_PCI_IRP_ENABLE_P_INT);
+ mb();
+ (void)tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_ENABLE);
+ mb();
+ }
+#ifdef DBG_TSI108_INTERRUPT
+ else {
+ printk("TSI108_PIC: error in TSI108_PCI_IRP_STAT\n");
+ pci_irp_stat =
+ tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_STAT);
+ temp =
+ tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_INTAD);
+ mb();
+ printk(">> stat=0x%08x intad=0x%08x ", pci_irp_stat, temp);
+ temp =
+ tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL);
+ mb();
+ printk("cfg_ctl=0x%08x ", temp);
+ temp =
+ tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_ENABLE);
+ mb();
+ printk("irp_enable=0x%08x\n", temp);
+ }
+#endif /* DBG_TSI108_INTERRUPT */
+
+ return irq;
+}
+
+
+/*
+ * Linux descriptor level callbacks
+ */
+
+static void tsi108_pci_irq_enable(u_int irq) {
+ tsi108_pci_int_unmask(irq);
+}
+
+static void tsi108_pci_irq_disable(u_int irq) {
+ tsi108_pci_int_mask(irq);
+}
+
+static void tsi108_pci_irq_ack(u_int irq) {
+ tsi108_pci_int_mask(irq);
+}
+
+static void tsi108_pci_irq_end(u_int irq) {
+ tsi108_pci_int_unmask(irq);
+
+ /* Enable interrupts from PCI block */
+ tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_ENABLE,
+ tsi108_read_reg(TSI108_PCI_OFFSET +
+ TSI108_PCI_IRP_ENABLE) |
+ TSI108_PCI_IRP_ENABLE_P_INT);
+ mb();
+}
+
+/*
+ * Interrupt controller descriptor for cascaded PCI interrupt controller.
+ */
+
+struct hw_interrupt_type tsi108_pci_irq = {
+ .typename = "tsi108_PCI_int",
+ .enable = tsi108_pci_irq_enable,
+ .disable = tsi108_pci_irq_disable,
+ .ack = tsi108_pci_irq_ack,
+ .end = tsi108_pci_irq_end,
+};
+
+/*
+ * Exported functions
+ */
+
+/*
+ * The Tsi108 PCI interrupts initialization routine.
+ *
+ * The INTA# - INTD# interrupts on the PCI bus are reported by the PCI
+block
+ * to the MPIC using single interrupt source (IRQ_TSI108_PCI).
+Therefore the
+ * PCI block has to be treated as a cascaded interrupt controller
+connected
+ * to the MPIC.
+ */
+
+void __init tsi108_pci_int_init(void)
+{
+ u_int i;
+
+ DBG("Tsi108_pci_int_init: initializing PCI interrupts\n");
+
+ for (i = 0; i < NUM_PCI_IRQS; i++) {
+ irq_desc[i + IRQ_PCI_INTAD_BASE].handler = &tsi108_pci_irq;
+ irq_desc[i + IRQ_PCI_INTAD_BASE].status |= IRQ_LEVEL;
+ }
+
+ init_pci_source();
+}
+
+int tsi108_irq_cascade(struct pt_regs *regs, void *unused) {
+ return get_pci_source();
+}
^ permalink raw reply related [flat|nested] 16+ messages in thread
* RE: [PATCH/2.6.17-rc4 4/10]Powerpc: Add tsi108 pic support
2006-06-09 9:25 Zang Roy-r61911
@ 2006-06-13 2:09 ` Benjamin Herrenschmidt
0 siblings, 0 replies; 16+ messages in thread
From: Benjamin Herrenschmidt @ 2006-06-13 2:09 UTC (permalink / raw)
To: Zang Roy-r61911
Cc: Alexandre Bounine, Yang Xin-Xin-r48390, Paul Mackerras,
linuxppc-dev list
On Fri, 2006-06-09 at 17:25 +0800, Zang Roy-r61911 wrote:
> Any comment?
Not right now. We'll have to rework this a bit on top of my current
changes to the powerpc interrupt stuff. I'll post patches soon.
Ben.
^ permalink raw reply [flat|nested] 16+ messages in thread
end of thread, other threads:[~2006-06-13 2:09 UTC | newest]
Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-05-30 3:28 [PATCH/2.6.17-rc4 4/10]Powerpc: Add tsi108 pic support Zang Roy-r61911
2006-05-30 4:17 ` Benjamin Herrenschmidt
2006-05-30 19:18 ` Kumar Gala
-- strict thread matches above, loose matches on Subject: below --
2006-06-09 9:25 Zang Roy-r61911
2006-06-13 2:09 ` Benjamin Herrenschmidt
2006-06-06 18:58 Alexandre Bounine
2006-06-06 14:45 Alexandre Bounine
2006-06-06 23:08 ` Benjamin Herrenschmidt
2006-06-06 9:43 Zang Roy-r61911
2006-06-06 10:17 ` Benjamin Herrenschmidt
2006-06-02 21:30 Alexandre Bounine
2006-06-01 20:45 Alexandre Bounine
2006-06-01 22:06 ` Benjamin Herrenschmidt
2006-05-17 10:14 Zang Roy-r61911
2006-05-17 16:05 ` Kumar Gala
2006-05-18 0:52 ` Benjamin Herrenschmidt
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).