xref: /openbmc/linux/arch/sparc/kernel/sun4m_irq.c (revision 778b1c65bfa2bfe4018394480f97d387e8f00a91)
11da177e4SLinus Torvalds /*  sun4m_irq.c
21da177e4SLinus Torvalds  *  arch/sparc/kernel/sun4m_irq.c:
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  *  djhr: Hacked out of irq.c into a CPU dependent version.
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  *  Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
71da177e4SLinus Torvalds  *  Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx)
81da177e4SLinus Torvalds  *  Copyright (C) 1995 Pete A. Zaitcev (zaitcev@yahoo.com)
91da177e4SLinus Torvalds  *  Copyright (C) 1996 Dave Redman (djhr@tadpole.co.uk)
101da177e4SLinus Torvalds  */
111da177e4SLinus Torvalds 
121da177e4SLinus Torvalds #include <linux/errno.h>
131da177e4SLinus Torvalds #include <linux/linkage.h>
141da177e4SLinus Torvalds #include <linux/kernel_stat.h>
151da177e4SLinus Torvalds #include <linux/signal.h>
161da177e4SLinus Torvalds #include <linux/sched.h>
171da177e4SLinus Torvalds #include <linux/ptrace.h>
181da177e4SLinus Torvalds #include <linux/smp.h>
191da177e4SLinus Torvalds #include <linux/interrupt.h>
201da177e4SLinus Torvalds #include <linux/slab.h>
211da177e4SLinus Torvalds #include <linux/init.h>
221da177e4SLinus Torvalds #include <linux/ioport.h>
23454eeb2dSDavid S. Miller #include <linux/of.h>
24454eeb2dSDavid S. Miller #include <linux/of_device.h>
251da177e4SLinus Torvalds 
261da177e4SLinus Torvalds #include <asm/ptrace.h>
271da177e4SLinus Torvalds #include <asm/processor.h>
281da177e4SLinus Torvalds #include <asm/system.h>
291da177e4SLinus Torvalds #include <asm/psr.h>
301da177e4SLinus Torvalds #include <asm/vaddrs.h>
311da177e4SLinus Torvalds #include <asm/timer.h>
321da177e4SLinus Torvalds #include <asm/openprom.h>
331da177e4SLinus Torvalds #include <asm/oplib.h>
341da177e4SLinus Torvalds #include <asm/traps.h>
351da177e4SLinus Torvalds #include <asm/pgalloc.h>
361da177e4SLinus Torvalds #include <asm/pgtable.h>
371da177e4SLinus Torvalds #include <asm/smp.h>
381da177e4SLinus Torvalds #include <asm/irq.h>
391da177e4SLinus Torvalds #include <asm/io.h>
401da177e4SLinus Torvalds #include <asm/cacheflush.h>
411da177e4SLinus Torvalds 
4232231a66SAl Viro #include "irq.h"
4332231a66SAl Viro 
4469c010b2SDavid S. Miller struct sun4m_irq_percpu {
4569c010b2SDavid S. Miller 	u32		pending;
4669c010b2SDavid S. Miller 	u32		clear;
4769c010b2SDavid S. Miller 	u32		set;
4832231a66SAl Viro };
4932231a66SAl Viro 
5069c010b2SDavid S. Miller struct sun4m_irq_global {
5169c010b2SDavid S. Miller 	u32		pending;
5269c010b2SDavid S. Miller 	u32		mask;
5369c010b2SDavid S. Miller 	u32		mask_clear;
5469c010b2SDavid S. Miller 	u32		mask_set;
5569c010b2SDavid S. Miller 	u32		interrupt_target;
5632231a66SAl Viro };
5732231a66SAl Viro 
5869c010b2SDavid S. Miller /* Code in entry.S needs to get at these register mappings.  */
5969c010b2SDavid S. Miller struct sun4m_irq_percpu __iomem *sun4m_irq_percpu[SUN4M_NCPUS];
6069c010b2SDavid S. Miller struct sun4m_irq_global __iomem *sun4m_irq_global;
6169c010b2SDavid S. Miller 
6232231a66SAl Viro /* Dave Redman (djhr@tadpole.co.uk)
6332231a66SAl Viro  * The sun4m interrupt registers.
6432231a66SAl Viro  */
6532231a66SAl Viro #define SUN4M_INT_ENABLE  	0x80000000
6632231a66SAl Viro #define SUN4M_INT_E14     	0x00000080
6732231a66SAl Viro #define SUN4M_INT_E10     	0x00080000
6832231a66SAl Viro 
6932231a66SAl Viro #define SUN4M_HARD_INT(x)	(0x000000001 << (x))
7032231a66SAl Viro #define SUN4M_SOFT_INT(x)	(0x000010000 << (x))
7132231a66SAl Viro 
7232231a66SAl Viro #define	SUN4M_INT_MASKALL	0x80000000	  /* mask all interrupts */
7332231a66SAl Viro #define	SUN4M_INT_MODULE_ERR	0x40000000	  /* module error */
7432231a66SAl Viro #define	SUN4M_INT_M2S_WRITE	0x20000000	  /* write buffer error */
7532231a66SAl Viro #define	SUN4M_INT_ECC		0x10000000	  /* ecc memory error */
7632231a66SAl Viro #define	SUN4M_INT_FLOPPY	0x00400000	  /* floppy disk */
7732231a66SAl Viro #define	SUN4M_INT_MODULE	0x00200000	  /* module interrupt */
7832231a66SAl Viro #define	SUN4M_INT_VIDEO		0x00100000	  /* onboard video */
7932231a66SAl Viro #define	SUN4M_INT_REALTIME	0x00080000	  /* system timer */
8032231a66SAl Viro #define	SUN4M_INT_SCSI		0x00040000	  /* onboard scsi */
8132231a66SAl Viro #define	SUN4M_INT_AUDIO		0x00020000	  /* audio/isdn */
8232231a66SAl Viro #define	SUN4M_INT_ETHERNET	0x00010000	  /* onboard ethernet */
8332231a66SAl Viro #define	SUN4M_INT_SERIAL	0x00008000	  /* serial ports */
8432231a66SAl Viro #define	SUN4M_INT_KBDMS		0x00004000	  /* keyboard/mouse */
8532231a66SAl Viro #define	SUN4M_INT_SBUSBITS	0x00003F80	  /* sbus int bits */
8632231a66SAl Viro 
8732231a66SAl Viro #define SUN4M_INT_SBUS(x)	(1 << (x+7))
8832231a66SAl Viro #define SUN4M_INT_VME(x)	(1 << (x))
8932231a66SAl Viro 
90*778b1c65SDavid S. Miller /* Interrupt level assignment on sun4m:
91*778b1c65SDavid S. Miller  *
92*778b1c65SDavid S. Miller  *	level		source
93*778b1c65SDavid S. Miller  * ------------------------------------------------------------
94*778b1c65SDavid S. Miller  *        1		softint-1
95*778b1c65SDavid S. Miller  *	  2		softint-2, VME/SBUS level 1
96*778b1c65SDavid S. Miller  *	  3		softint-3, VME/SBUS level 2
97*778b1c65SDavid S. Miller  *	  4		softint-4, onboard SCSI
98*778b1c65SDavid S. Miller  *	  5		softint-5, VME/SBUS level 3
99*778b1c65SDavid S. Miller  *	  6		softint-6, onboard ETHERNET
100*778b1c65SDavid S. Miller  *	  7		softint-7, VME/SBUS level 4
101*778b1c65SDavid S. Miller  *	  8		softint-8, onboard VIDEO
102*778b1c65SDavid S. Miller  *	  9		softint-9, VME/SBUS level 5, Module Interrupt
103*778b1c65SDavid S. Miller  *	 10		softint-10, system counter/timer
104*778b1c65SDavid S. Miller  *	 11		softint-11, VME/SBUS level 6, Floppy
105*778b1c65SDavid S. Miller  *	 12		softint-12, Keyboard/Mouse, Serial
106*778b1c65SDavid S. Miller  *	 13		softint-13, VME/SBUS level 7, ISDN Audio
107*778b1c65SDavid S. Miller  *	 14		softint-14, per-processor counter/timer
108*778b1c65SDavid S. Miller  *	 15		softint-15, Asynchronous Errors (broadcast)
109*778b1c65SDavid S. Miller  *
110*778b1c65SDavid S. Miller  * Each interrupt source is masked distinctly in the sun4m interrupt
111*778b1c65SDavid S. Miller  * registers.  The PIL level alone is therefore ambiguous, since multiple
112*778b1c65SDavid S. Miller  * interrupt sources map to a single PIL.
113*778b1c65SDavid S. Miller  *
114*778b1c65SDavid S. Miller  * This ambiguity is resolved in the 'intr' property for device nodes
115*778b1c65SDavid S. Miller  * in the OF device tree.  Each 'intr' property entry is composed of
116*778b1c65SDavid S. Miller  * two 32-bit words.  The first word is the IRQ priority value, which
117*778b1c65SDavid S. Miller  * is what we're intersted in.  The second word is the IRQ vector, which
118*778b1c65SDavid S. Miller  * is unused.
119*778b1c65SDavid S. Miller  *
120*778b1c65SDavid S. Miller  * The low 4 bits of the IRQ priority indicate the PIL, and the upper
121*778b1c65SDavid S. Miller  * 4 bits indicate onboard vs. SBUS leveled vs. VME leveled.  0x20
122*778b1c65SDavid S. Miller  * means onboard, 0x30 means SBUS leveled, and 0x40 means VME leveled.
123*778b1c65SDavid S. Miller  *
124*778b1c65SDavid S. Miller  * For example, an 'intr' IRQ priority value of 0x24 is onboard SCSI
125*778b1c65SDavid S. Miller  * whereas a value of 0x33 is SBUS level 2.  Here are some sample
126*778b1c65SDavid S. Miller  * 'intr' property IRQ priority values from ss4, ss5, ss10, ss20, and
127*778b1c65SDavid S. Miller  * Tadpole S3 GX systems.
128*778b1c65SDavid S. Miller  *
129*778b1c65SDavid S. Miller  * esp: 	0x24	onboard ESP SCSI
130*778b1c65SDavid S. Miller  * le:  	0x26	onboard Lance ETHERNET
131*778b1c65SDavid S. Miller  * p9100:	0x32	SBUS level 1 P9100 video
132*778b1c65SDavid S. Miller  * bpp:  	0x33	SBUS level 2 BPP parallel port device
133*778b1c65SDavid S. Miller  * DBRI:	0x39	SBUS level 5 DBRI ISDN audio
134*778b1c65SDavid S. Miller  * SUNW,leo:	0x39	SBUS level 5 LEO video
135*778b1c65SDavid S. Miller  * pcmcia:	0x3b	SBUS level 6 PCMCIA controller
136*778b1c65SDavid S. Miller  * uctrl:	0x3b	SBUS level 6 UCTRL device
137*778b1c65SDavid S. Miller  * modem:	0x3d	SBUS level 7 MODEM
138*778b1c65SDavid S. Miller  * zs:		0x2c	onboard keyboard/mouse/serial
139*778b1c65SDavid S. Miller  * floppy:	0x2b	onboard Floppy
140*778b1c65SDavid S. Miller  * power:	0x22	onboard power device (XXX unknown mask bit XXX)
141*778b1c65SDavid S. Miller  */
142*778b1c65SDavid S. Miller 
1431da177e4SLinus Torvalds /* These tables only apply for interrupts greater than 15..
1441da177e4SLinus Torvalds  *
1451da177e4SLinus Torvalds  * any intr value below 0x10 is considered to be a soft-int
1461da177e4SLinus Torvalds  * this may be useful or it may not.. but that's how I've done it.
1471da177e4SLinus Torvalds  * and it won't clash with what OBP is telling us about devices.
1481da177e4SLinus Torvalds  *
1491da177e4SLinus Torvalds  * take an encoded intr value and lookup if it's valid
1501da177e4SLinus Torvalds  * then get the mask bits that match from irq_mask
1511da177e4SLinus Torvalds  *
1521da177e4SLinus Torvalds  * P3: Translation from irq 0x0d to mask 0x2000 is for MrCoffee.
1531da177e4SLinus Torvalds  */
1541da177e4SLinus Torvalds static unsigned char irq_xlate[32] = {
1551da177e4SLinus Torvalds     /*  0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  a,  b,  c,  d,  e,  f */
1561da177e4SLinus Torvalds 	0,  0,  0,  0,  1,  0,  2,  0,  3,  0,  4,  5,  6, 14,  0,  7,
1571da177e4SLinus Torvalds 	0,  0,  8,  9,  0, 10,  0, 11,  0, 12,  0, 13,  0, 14,  0,  0
1581da177e4SLinus Torvalds };
1591da177e4SLinus Torvalds 
1601da177e4SLinus Torvalds static unsigned long irq_mask[] = {
1611da177e4SLinus Torvalds 	0,						  /* illegal index */
1621da177e4SLinus Torvalds 	SUN4M_INT_SCSI,				  	  /*  1 irq 4 */
1631da177e4SLinus Torvalds 	SUN4M_INT_ETHERNET,				  /*  2 irq 6 */
1641da177e4SLinus Torvalds 	SUN4M_INT_VIDEO,				  /*  3 irq 8 */
1651da177e4SLinus Torvalds 	SUN4M_INT_REALTIME,				  /*  4 irq 10 */
1661da177e4SLinus Torvalds 	SUN4M_INT_FLOPPY,				  /*  5 irq 11 */
1671da177e4SLinus Torvalds 	(SUN4M_INT_SERIAL | SUN4M_INT_KBDMS),	  	  /*  6 irq 12 */
1681da177e4SLinus Torvalds 	SUN4M_INT_MODULE_ERR,			  	  /*  7 irq 15 */
1691da177e4SLinus Torvalds 	SUN4M_INT_SBUS(0),				  /*  8 irq 2 */
1701da177e4SLinus Torvalds 	SUN4M_INT_SBUS(1),				  /*  9 irq 3 */
1711da177e4SLinus Torvalds 	SUN4M_INT_SBUS(2),				  /* 10 irq 5 */
1721da177e4SLinus Torvalds 	SUN4M_INT_SBUS(3),				  /* 11 irq 7 */
1731da177e4SLinus Torvalds 	SUN4M_INT_SBUS(4),				  /* 12 irq 9 */
1741da177e4SLinus Torvalds 	SUN4M_INT_SBUS(5),				  /* 13 irq 11 */
1751da177e4SLinus Torvalds 	SUN4M_INT_SBUS(6)				  /* 14 irq 13 */
1761da177e4SLinus Torvalds };
1771da177e4SLinus Torvalds 
178c61c65cdSAdrian Bunk static unsigned long sun4m_get_irqmask(unsigned int irq)
1791da177e4SLinus Torvalds {
1801da177e4SLinus Torvalds 	unsigned long mask;
1811da177e4SLinus Torvalds 
1821da177e4SLinus Torvalds 	if (irq > 0x20) {
1831da177e4SLinus Torvalds 		/* OBIO/SBUS interrupts */
1841da177e4SLinus Torvalds 		irq &= 0x1f;
1851da177e4SLinus Torvalds 		mask = irq_mask[irq_xlate[irq]];
1861da177e4SLinus Torvalds 		if (!mask)
1871da177e4SLinus Torvalds 			printk("sun4m_get_irqmask: IRQ%d has no valid mask!\n",irq);
1881da177e4SLinus Torvalds 	} else {
1891da177e4SLinus Torvalds 		/* Soft Interrupts will come here.
1901da177e4SLinus Torvalds 		 * Currently there is no way to trigger them but I'm sure
1911da177e4SLinus Torvalds 		 * something could be cooked up.
1921da177e4SLinus Torvalds 		 */
1931da177e4SLinus Torvalds 		irq &= 0xf;
1941da177e4SLinus Torvalds 		mask = SUN4M_SOFT_INT(irq);
1951da177e4SLinus Torvalds 	}
1961da177e4SLinus Torvalds 	return mask;
1971da177e4SLinus Torvalds }
1981da177e4SLinus Torvalds 
1991da177e4SLinus Torvalds static void sun4m_disable_irq(unsigned int irq_nr)
2001da177e4SLinus Torvalds {
2011da177e4SLinus Torvalds 	unsigned long mask, flags;
2021da177e4SLinus Torvalds 	int cpu = smp_processor_id();
2031da177e4SLinus Torvalds 
2041da177e4SLinus Torvalds 	mask = sun4m_get_irqmask(irq_nr);
2051da177e4SLinus Torvalds 	local_irq_save(flags);
2061da177e4SLinus Torvalds 	if (irq_nr > 15)
20769c010b2SDavid S. Miller 		sbus_writel(mask, &sun4m_irq_global->mask_set);
2081da177e4SLinus Torvalds 	else
20969c010b2SDavid S. Miller 		sbus_writel(mask, &sun4m_irq_percpu[cpu]->set);
2101da177e4SLinus Torvalds 	local_irq_restore(flags);
2111da177e4SLinus Torvalds }
2121da177e4SLinus Torvalds 
2131da177e4SLinus Torvalds static void sun4m_enable_irq(unsigned int irq_nr)
2141da177e4SLinus Torvalds {
2151da177e4SLinus Torvalds 	unsigned long mask, flags;
2161da177e4SLinus Torvalds 	int cpu = smp_processor_id();
2171da177e4SLinus Torvalds 
2181da177e4SLinus Torvalds 	/* Dreadful floppy hack. When we use 0x2b instead of
2191da177e4SLinus Torvalds          * 0x0b the system blows (it starts to whistle!).
2201da177e4SLinus Torvalds          * So we continue to use 0x0b. Fixme ASAP. --P3
2211da177e4SLinus Torvalds          */
2221da177e4SLinus Torvalds         if (irq_nr != 0x0b) {
2231da177e4SLinus Torvalds 		mask = sun4m_get_irqmask(irq_nr);
2241da177e4SLinus Torvalds 		local_irq_save(flags);
2251da177e4SLinus Torvalds 		if (irq_nr > 15)
22669c010b2SDavid S. Miller 			sbus_writel(mask, &sun4m_irq_global->mask_clear);
2271da177e4SLinus Torvalds 		else
22869c010b2SDavid S. Miller 			sbus_writel(mask, &sun4m_irq_percpu[cpu]->clear);
2291da177e4SLinus Torvalds 		local_irq_restore(flags);
2301da177e4SLinus Torvalds 	} else {
2311da177e4SLinus Torvalds 		local_irq_save(flags);
23269c010b2SDavid S. Miller 		sbus_writel(SUN4M_INT_FLOPPY, &sun4m_irq_global->mask_clear);
2331da177e4SLinus Torvalds 		local_irq_restore(flags);
2341da177e4SLinus Torvalds 	}
2351da177e4SLinus Torvalds }
2361da177e4SLinus Torvalds 
2371da177e4SLinus Torvalds static unsigned long cpu_pil_to_imask[16] = {
2381da177e4SLinus Torvalds /*0*/	0x00000000,
2391da177e4SLinus Torvalds /*1*/	0x00000000,
2401da177e4SLinus Torvalds /*2*/	SUN4M_INT_SBUS(0) | SUN4M_INT_VME(0),
2411da177e4SLinus Torvalds /*3*/	SUN4M_INT_SBUS(1) | SUN4M_INT_VME(1),
2421da177e4SLinus Torvalds /*4*/	SUN4M_INT_SCSI,
2431da177e4SLinus Torvalds /*5*/	SUN4M_INT_SBUS(2) | SUN4M_INT_VME(2),
2441da177e4SLinus Torvalds /*6*/	SUN4M_INT_ETHERNET,
2451da177e4SLinus Torvalds /*7*/	SUN4M_INT_SBUS(3) | SUN4M_INT_VME(3),
2461da177e4SLinus Torvalds /*8*/	SUN4M_INT_VIDEO,
2471da177e4SLinus Torvalds /*9*/	SUN4M_INT_SBUS(4) | SUN4M_INT_VME(4) | SUN4M_INT_MODULE_ERR,
2481da177e4SLinus Torvalds /*10*/	SUN4M_INT_REALTIME,
2491da177e4SLinus Torvalds /*11*/	SUN4M_INT_SBUS(5) | SUN4M_INT_VME(5) | SUN4M_INT_FLOPPY,
2501da177e4SLinus Torvalds /*12*/	SUN4M_INT_SERIAL | SUN4M_INT_KBDMS,
2511da177e4SLinus Torvalds /*13*/	SUN4M_INT_AUDIO,
2521da177e4SLinus Torvalds /*14*/	SUN4M_INT_E14,
2531da177e4SLinus Torvalds /*15*/	0x00000000
2541da177e4SLinus Torvalds };
2551da177e4SLinus Torvalds 
2561da177e4SLinus Torvalds /* We assume the caller has disabled local interrupts when these are called,
2571da177e4SLinus Torvalds  * or else very bizarre behavior will result.
2581da177e4SLinus Torvalds  */
2591da177e4SLinus Torvalds static void sun4m_disable_pil_irq(unsigned int pil)
2601da177e4SLinus Torvalds {
26169c010b2SDavid S. Miller 	sbus_writel(cpu_pil_to_imask[pil], &sun4m_irq_global->mask_set);
2621da177e4SLinus Torvalds }
2631da177e4SLinus Torvalds 
2641da177e4SLinus Torvalds static void sun4m_enable_pil_irq(unsigned int pil)
2651da177e4SLinus Torvalds {
26669c010b2SDavid S. Miller 	sbus_writel(cpu_pil_to_imask[pil], &sun4m_irq_global->mask_clear);
2671da177e4SLinus Torvalds }
2681da177e4SLinus Torvalds 
2691da177e4SLinus Torvalds #ifdef CONFIG_SMP
2701da177e4SLinus Torvalds static void sun4m_send_ipi(int cpu, int level)
2711da177e4SLinus Torvalds {
27269c010b2SDavid S. Miller 	unsigned long mask = sun4m_get_irqmask(level);
27369c010b2SDavid S. Miller 	sbus_writel(mask, &sun4m_irq_percpu[cpu]->set);
2741da177e4SLinus Torvalds }
2751da177e4SLinus Torvalds 
2761da177e4SLinus Torvalds static void sun4m_clear_ipi(int cpu, int level)
2771da177e4SLinus Torvalds {
27869c010b2SDavid S. Miller 	unsigned long mask = sun4m_get_irqmask(level);
27969c010b2SDavid S. Miller 	sbus_writel(mask, &sun4m_irq_percpu[cpu]->clear);
2801da177e4SLinus Torvalds }
2811da177e4SLinus Torvalds 
2821da177e4SLinus Torvalds static void sun4m_set_udt(int cpu)
2831da177e4SLinus Torvalds {
28469c010b2SDavid S. Miller 	sbus_writel(cpu, &sun4m_irq_global->interrupt_target);
2851da177e4SLinus Torvalds }
2861da177e4SLinus Torvalds #endif
2871da177e4SLinus Torvalds 
2889b2e43aeSDavid S. Miller struct sun4m_timer_percpu {
2899b2e43aeSDavid S. Miller 	u32		l14_limit;
2909b2e43aeSDavid S. Miller 	u32		l14_count;
2919b2e43aeSDavid S. Miller 	u32		l14_limit_noclear;
2929b2e43aeSDavid S. Miller 	u32		user_timer_start_stop;
2939b2e43aeSDavid S. Miller };
2949b2e43aeSDavid S. Miller 
2959b2e43aeSDavid S. Miller static struct sun4m_timer_percpu __iomem *timers_percpu[SUN4M_NCPUS];
2969b2e43aeSDavid S. Miller 
2979b2e43aeSDavid S. Miller struct sun4m_timer_global {
2989b2e43aeSDavid S. Miller 	u32		l10_limit;
2999b2e43aeSDavid S. Miller 	u32		l10_count;
3009b2e43aeSDavid S. Miller 	u32		l10_limit_noclear;
3019b2e43aeSDavid S. Miller 	u32		reserved;
3029b2e43aeSDavid S. Miller 	u32		timer_config;
3039b2e43aeSDavid S. Miller };
3049b2e43aeSDavid S. Miller 
3059b2e43aeSDavid S. Miller static struct sun4m_timer_global __iomem *timers_global;
3069b2e43aeSDavid S. Miller 
3071da177e4SLinus Torvalds #define OBIO_INTR	0x20
3081da177e4SLinus Torvalds #define TIMER_IRQ  	(OBIO_INTR | 10)
3091da177e4SLinus Torvalds 
3101da177e4SLinus Torvalds unsigned int lvl14_resolution = (((1000000/HZ) + 1) << 10);
3111da177e4SLinus Torvalds 
3121da177e4SLinus Torvalds static void sun4m_clear_clock_irq(void)
3131da177e4SLinus Torvalds {
3149b2e43aeSDavid S. Miller 	sbus_readl(&timers_global->l10_limit);
3151da177e4SLinus Torvalds }
3161da177e4SLinus Torvalds 
3171de937a5SDavid S. Miller /* Exported for sun4m_smp.c */
3181de937a5SDavid S. Miller void sun4m_clear_profile_irq(int cpu)
3191da177e4SLinus Torvalds {
3209b2e43aeSDavid S. Miller 	sbus_readl(&timers_percpu[cpu]->l14_limit);
3211da177e4SLinus Torvalds }
3221da177e4SLinus Torvalds 
3231da177e4SLinus Torvalds static void sun4m_load_profile_irq(int cpu, unsigned int limit)
3241da177e4SLinus Torvalds {
3259b2e43aeSDavid S. Miller 	sbus_writel(limit, &timers_percpu[cpu]->l14_limit);
3261da177e4SLinus Torvalds }
3271da177e4SLinus Torvalds 
32840220c1aSDavid Howells static void __init sun4m_init_timers(irq_handler_t counter_fn)
3291da177e4SLinus Torvalds {
3309b2e43aeSDavid S. Miller 	struct device_node *dp = of_find_node_by_name(NULL, "counter");
3319b2e43aeSDavid S. Miller 	int i, err, len, num_cpu_timers;
3329b2e43aeSDavid S. Miller 	const u32 *addr;
3331da177e4SLinus Torvalds 
3349b2e43aeSDavid S. Miller 	if (!dp) {
3359b2e43aeSDavid S. Miller 		printk(KERN_ERR "sun4m_init_timers: No 'counter' node.\n");
3369b2e43aeSDavid S. Miller 		return;
3371da177e4SLinus Torvalds 	}
3381da177e4SLinus Torvalds 
3399b2e43aeSDavid S. Miller 	addr = of_get_property(dp, "address", &len);
3409b2e43aeSDavid S. Miller 	if (!addr) {
3419b2e43aeSDavid S. Miller 		printk(KERN_ERR "sun4m_init_timers: No 'address' prop.\n");
3429b2e43aeSDavid S. Miller 		return;
3431da177e4SLinus Torvalds 	}
3441da177e4SLinus Torvalds 
3459b2e43aeSDavid S. Miller 	num_cpu_timers = (len / sizeof(u32)) - 1;
3469b2e43aeSDavid S. Miller 	for (i = 0; i < num_cpu_timers; i++) {
3479b2e43aeSDavid S. Miller 		timers_percpu[i] = (void __iomem *)
3489b2e43aeSDavid S. Miller 			(unsigned long) addr[i];
3491da177e4SLinus Torvalds 	}
3509b2e43aeSDavid S. Miller 	timers_global = (void __iomem *)
3519b2e43aeSDavid S. Miller 		(unsigned long) addr[num_cpu_timers];
3529b2e43aeSDavid S. Miller 
3539b2e43aeSDavid S. Miller 	sbus_writel((((1000000/HZ) + 1) << 10), &timers_global->l10_limit);
3549b2e43aeSDavid S. Miller 
3559b2e43aeSDavid S. Miller 	master_l10_counter = &timers_global->l10_count;
3569b2e43aeSDavid S. Miller 
3579b2e43aeSDavid S. Miller 	err = request_irq(TIMER_IRQ, counter_fn,
3589b2e43aeSDavid S. Miller 			  (IRQF_DISABLED | SA_STATIC_ALLOC), "timer", NULL);
3599b2e43aeSDavid S. Miller 	if (err) {
3609b2e43aeSDavid S. Miller 		printk(KERN_ERR "sun4m_init_timers: Register IRQ error %d.\n",
3619b2e43aeSDavid S. Miller 			err);
3629b2e43aeSDavid S. Miller 		return;
3639b2e43aeSDavid S. Miller 	}
3649b2e43aeSDavid S. Miller 
3659b2e43aeSDavid S. Miller 	for (i = 0; i < num_cpu_timers; i++)
3669b2e43aeSDavid S. Miller 		sbus_writel(0, &timers_percpu[i]->l14_limit);
3679b2e43aeSDavid S. Miller 	if (num_cpu_timers == 4)
36869c010b2SDavid S. Miller 		sbus_writel(SUN4M_INT_E14, &sun4m_irq_global->mask_set);
3699b2e43aeSDavid S. Miller 
3701da177e4SLinus Torvalds #ifdef CONFIG_SMP
3711da177e4SLinus Torvalds 	{
3721da177e4SLinus Torvalds 		unsigned long flags;
3731da177e4SLinus Torvalds 		extern unsigned long lvl14_save[4];
3741da177e4SLinus Torvalds 		struct tt_entry *trap_table = &sparc_ttable[SP_TRAP_IRQ1 + (14 - 1)];
3751da177e4SLinus Torvalds 
3761da177e4SLinus Torvalds 		/* For SMP we use the level 14 ticker, however the bootup code
377d1a78c32SSimon Arlott 		 * has copied the firmware's level 14 vector into the boot cpu's
3781da177e4SLinus Torvalds 		 * trap table, we must fix this now or we get squashed.
3791da177e4SLinus Torvalds 		 */
3801da177e4SLinus Torvalds 		local_irq_save(flags);
3811da177e4SLinus Torvalds 		trap_table->inst_one = lvl14_save[0];
3821da177e4SLinus Torvalds 		trap_table->inst_two = lvl14_save[1];
3831da177e4SLinus Torvalds 		trap_table->inst_three = lvl14_save[2];
3841da177e4SLinus Torvalds 		trap_table->inst_four = lvl14_save[3];
3851da177e4SLinus Torvalds 		local_flush_cache_all();
3861da177e4SLinus Torvalds 		local_irq_restore(flags);
3871da177e4SLinus Torvalds 	}
3881da177e4SLinus Torvalds #endif
3891da177e4SLinus Torvalds }
3901da177e4SLinus Torvalds 
3911da177e4SLinus Torvalds void __init sun4m_init_IRQ(void)
3921da177e4SLinus Torvalds {
39369c010b2SDavid S. Miller 	struct device_node *dp = of_find_node_by_name(NULL, "interrupt");
39469c010b2SDavid S. Miller 	int len, i, mid, num_cpu_iregs;
39569c010b2SDavid S. Miller 	const u32 *addr;
39669c010b2SDavid S. Miller 
39769c010b2SDavid S. Miller 	if (!dp) {
39869c010b2SDavid S. Miller 		printk(KERN_ERR "sun4m_init_IRQ: No 'interrupt' node.\n");
39969c010b2SDavid S. Miller 		return;
40069c010b2SDavid S. Miller 	}
40169c010b2SDavid S. Miller 
40269c010b2SDavid S. Miller 	addr = of_get_property(dp, "address", &len);
40369c010b2SDavid S. Miller 	if (!addr) {
40469c010b2SDavid S. Miller 		printk(KERN_ERR "sun4m_init_IRQ: No 'address' prop.\n");
40569c010b2SDavid S. Miller 		return;
40669c010b2SDavid S. Miller 	}
40769c010b2SDavid S. Miller 
40869c010b2SDavid S. Miller 	num_cpu_iregs = (len / sizeof(u32)) - 1;
40969c010b2SDavid S. Miller 	for (i = 0; i < num_cpu_iregs; i++) {
41069c010b2SDavid S. Miller 		sun4m_irq_percpu[i] = (void __iomem *)
41169c010b2SDavid S. Miller 			(unsigned long) addr[i];
41269c010b2SDavid S. Miller 	}
41369c010b2SDavid S. Miller 	sun4m_irq_global = (void __iomem *)
41469c010b2SDavid S. Miller 		(unsigned long) addr[num_cpu_iregs];
4151da177e4SLinus Torvalds 
4161da177e4SLinus Torvalds 	local_irq_disable();
4171da177e4SLinus Torvalds 
41869c010b2SDavid S. Miller 	sbus_writel(~SUN4M_INT_MASKALL, &sun4m_irq_global->mask_set);
4191da177e4SLinus Torvalds 	for (i = 0; !cpu_find_by_instance(i, NULL, &mid); i++)
42069c010b2SDavid S. Miller 		sbus_writel(~0x17fff, &sun4m_irq_percpu[mid]->clear);
4211da177e4SLinus Torvalds 
422e7913de9SDavid S. Miller 	if (num_cpu_iregs == 4)
42369c010b2SDavid S. Miller 		sbus_writel(0, &sun4m_irq_global->interrupt_target);
424e7913de9SDavid S. Miller 
4251da177e4SLinus Torvalds 	BTFIXUPSET_CALL(enable_irq, sun4m_enable_irq, BTFIXUPCALL_NORM);
4261da177e4SLinus Torvalds 	BTFIXUPSET_CALL(disable_irq, sun4m_disable_irq, BTFIXUPCALL_NORM);
4271da177e4SLinus Torvalds 	BTFIXUPSET_CALL(enable_pil_irq, sun4m_enable_pil_irq, BTFIXUPCALL_NORM);
4281da177e4SLinus Torvalds 	BTFIXUPSET_CALL(disable_pil_irq, sun4m_disable_pil_irq, BTFIXUPCALL_NORM);
4291da177e4SLinus Torvalds 	BTFIXUPSET_CALL(clear_clock_irq, sun4m_clear_clock_irq, BTFIXUPCALL_NORM);
4301da177e4SLinus Torvalds 	BTFIXUPSET_CALL(load_profile_irq, sun4m_load_profile_irq, BTFIXUPCALL_NORM);
4311da177e4SLinus Torvalds 	sparc_init_timers = sun4m_init_timers;
4321da177e4SLinus Torvalds #ifdef CONFIG_SMP
4331da177e4SLinus Torvalds 	BTFIXUPSET_CALL(set_cpu_int, sun4m_send_ipi, BTFIXUPCALL_NORM);
4341da177e4SLinus Torvalds 	BTFIXUPSET_CALL(clear_cpu_int, sun4m_clear_ipi, BTFIXUPCALL_NORM);
4351da177e4SLinus Torvalds 	BTFIXUPSET_CALL(set_irq_udt, sun4m_set_udt, BTFIXUPCALL_NORM);
4361da177e4SLinus Torvalds #endif
43769c010b2SDavid S. Miller 
4381da177e4SLinus Torvalds 	/* Cannot enable interrupts until OBP ticker is disabled. */
4391da177e4SLinus Torvalds }
440