[PATCH/2.6.17-rc4 4/10]Powerpc: Add tsi108 pic support

Benjamin Herrenschmidt benh at kernel.crashing.org
Thu May 18 10:52:06 EST 2006


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 at tundra.com>
> Signed-off-by: Roy Zang	<tie-fei.zang at 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 at 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 at 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 */




More information about the Linuxppc-dev mailing list