Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 69c010b2 authored by David S. Miller's avatar David S. Miller
Browse files

sparc32: Use PROM device probing for sun4m irq registers.

parent 2e57572a
Loading
Loading
Loading
Loading
+20 −20
Original line number Diff line number Diff line
@@ -272,17 +272,18 @@ smp4m_ticker:
	 */
maybe_smp4m_msg:
	GET_PROCESSOR4M_ID(o3)
	set	sun4m_interrupts, %l5
	ld	[%l5], %o5
	sethi	%hi(sun4m_irq_percpu), %l5
	sll	%o3, 2, %o3
	or	%l5, %lo(sun4m_irq_percpu), %o5
	sethi	%hi(0x40000000), %o2
	sll	%o3, 12, %o3
	ld	[%o5 + %o3], %o1
	andcc	%o1, %o2, %g0
	ld	[%o1 + 0x00], %o3	! sun4m_irq_percpu[cpu]->pending
	andcc	%o3, %o2, %g0
	be,a	smp4m_ticker
	 cmp	%l7, 14
	st	%o2, [%o5 + 0x4]
	st	%o2, [%o1 + 0x04]	! sun4m_irq_percpu[cpu]->clear=0x40000000
	WRITE_PAUSE
	ld	[%o5], %g0
	ld	[%o1 + 0x00], %g0	! sun4m_irq_percpu[cpu]->pending
	WRITE_PAUSE
	or	%l0, PSR_PIL, %l4
	wr	%l4, 0x0, %psr
@@ -300,16 +301,16 @@ linux_trap_ipi15_sun4m:
	SAVE_ALL
	sethi	%hi(0x80000000), %o2
	GET_PROCESSOR4M_ID(o0)
	set	sun4m_interrupts, %l5
	ld	[%l5], %o5
	sll	%o0, 12, %o0
	add	%o5, %o0, %o5
	ld	[%o5], %o3
	sethi	%hi(sun4m_irq_percpu), %l5
	or	%l5, %lo(sun4m_irq_percpu), %o5
	sll	%o0, 2, %o0
	ld	[%o5 + %o0], %o5
	ld	[%o5 + 0x00], %o3	! sun4m_irq_percpu[cpu]->pending
	andcc	%o3, %o2, %g0
	be	1f			! Must be an NMI async memory error
	 st	%o2, [%o5 + 4]
	 st	%o2, [%o5 + 0x04]	! sun4m_irq_percpu[cpu]->clear=0x80000000
	WRITE_PAUSE
	ld	[%o5], %g0
	ld	[%o5 + 0x00], %g0	! sun4m_irq_percpu[cpu]->pending
	WRITE_PAUSE
	or	%l0, PSR_PIL, %l4
	wr	%l4, 0x0, %psr
@@ -323,12 +324,11 @@ linux_trap_ipi15_sun4m:
1:
	/* NMI async memory error handling. */
	sethi	%hi(0x80000000), %l4
	sethi	%hi(0x4000), %o3
	sub	%o5, %o0, %o5
	add	%o5, %o3, %l5
	st	%l4, [%l5 + 0xc]
	sethi	%hi(sun4m_irq_global), %o5
	ld	[%o5 + %lo(sun4m_irq_global)], %l5
	st	%l4, [%l5 + 0x0c]	! sun4m_irq_global->mask_set=0x80000000
	WRITE_PAUSE
	ld	[%l5], %g0
	ld	[%l5 + 0x00], %g0	! sun4m_irq_global->pending
	WRITE_PAUSE
	or	%l0, PSR_PIL, %l4
	wr	%l4, 0x0, %psr
@@ -337,9 +337,9 @@ linux_trap_ipi15_sun4m:
	WRITE_PAUSE
	call	sun4m_nmi
	 nop
	st	%l4, [%l5 + 0x8]
	st	%l4, [%l5 + 0x08]	! sun4m_irq_global->mask_clear=0x80000000
	WRITE_PAUSE
	ld	[%l5], %g0
	ld	[%l5 + 0x00], %g0	! sun4m_irq_global->pending
	WRITE_PAUSE
	RESTORE_ALL

+54 −109
Original line number Diff line number Diff line
@@ -41,53 +41,25 @@

#include "irq.h"

/* On the sun4m, just like the timers, we have both per-cpu and master
 * interrupt registers.
 */

/* These registers are used for sending/receiving irqs from/to
 * different cpu's.
 */
struct sun4m_intreg_percpu {
	unsigned int tbt;        /* Interrupts still pending for this cpu. */

	/* These next two registers are WRITE-ONLY and are only
	 * "on bit" sensitive, "off bits" written have NO affect.
	 */
	unsigned int clear;  /* Clear this cpus irqs here. */
	unsigned int set;    /* Set this cpus irqs here. */
	unsigned char space[PAGE_SIZE - 12];
struct sun4m_irq_percpu {
	u32		pending;
	u32		clear;
	u32		set;
};

/*
 * djhr
 * Actually the clear and set fields in this struct are misleading..
 * according to the SLAVIO manual (and the same applies for the SEC)
 * the clear field clears bits in the mask which will ENABLE that IRQ
 * the set field sets bits in the mask to DISABLE the IRQ.
 *
 * Also the undirected_xx address in the SLAVIO is defined as
 * RESERVED and write only..
 *
 * DAVEM_NOTE: The SLAVIO only specifies behavior on uniprocessor
 *             sun4m machines, for MP the layout makes more sense.
 */
struct sun4m_intregs {
	struct sun4m_intreg_percpu cpu_intregs[SUN4M_NCPUS];
	unsigned int tbt;                /* IRQ's that are still pending. */
	unsigned int irqs;               /* Master IRQ bits. */

	/* Again, like the above, two these registers are WRITE-ONLY. */
	unsigned int clear;              /* Clear master IRQ's by setting bits here. */
	unsigned int set;                /* Set master IRQ's by setting bits here. */

	/* This register is both READ and WRITE. */
	unsigned int undirected_target;  /* Which cpu gets undirected irqs. */
struct sun4m_irq_global {
	u32		pending;
	u32		mask;
	u32		mask_clear;
	u32		mask_set;
	u32		interrupt_target;
};

static unsigned long dummy;
/* Code in entry.S needs to get at these register mappings.  */
struct sun4m_irq_percpu __iomem *sun4m_irq_percpu[SUN4M_NCPUS];
struct sun4m_irq_global __iomem *sun4m_irq_global;

struct sun4m_intregs *sun4m_interrupts;
static unsigned long dummy;
unsigned long *irq_rcvreg = &dummy;

/* Dave Redman (djhr@tadpole.co.uk)
@@ -182,9 +154,9 @@ static void sun4m_disable_irq(unsigned int irq_nr)
	mask = sun4m_get_irqmask(irq_nr);
	local_irq_save(flags);
	if (irq_nr > 15)
		sun4m_interrupts->set = mask;
		sbus_writel(mask, &sun4m_irq_global->mask_set);
	else
		sun4m_interrupts->cpu_intregs[cpu].set = mask;
		sbus_writel(mask, &sun4m_irq_percpu[cpu]->set);
	local_irq_restore(flags);    
}

@@ -201,13 +173,13 @@ static void sun4m_enable_irq(unsigned int irq_nr)
		mask = sun4m_get_irqmask(irq_nr);
		local_irq_save(flags);
		if (irq_nr > 15)
			sun4m_interrupts->clear = mask;
			sbus_writel(mask, &sun4m_irq_global->mask_clear);
		else
			sun4m_interrupts->cpu_intregs[cpu].clear = mask;
			sbus_writel(mask, &sun4m_irq_percpu[cpu]->clear);
		local_irq_restore(flags);    
	} else {
		local_irq_save(flags);
		sun4m_interrupts->clear = SUN4M_INT_FLOPPY;
		sbus_writel(SUN4M_INT_FLOPPY, &sun4m_irq_global->mask_clear);
		local_irq_restore(flags);
	}
}
@@ -236,34 +208,30 @@ static unsigned long cpu_pil_to_imask[16] = {
 */
static void sun4m_disable_pil_irq(unsigned int pil)
{
	sun4m_interrupts->set = cpu_pil_to_imask[pil];
	sbus_writel(cpu_pil_to_imask[pil], &sun4m_irq_global->mask_set);
}

static void sun4m_enable_pil_irq(unsigned int pil)
{
	sun4m_interrupts->clear = cpu_pil_to_imask[pil];
	sbus_writel(cpu_pil_to_imask[pil], &sun4m_irq_global->mask_clear);
}

#ifdef CONFIG_SMP
static void sun4m_send_ipi(int cpu, int level)
{
	unsigned long mask;

	mask = sun4m_get_irqmask(level);
	sun4m_interrupts->cpu_intregs[cpu].set = mask;
	unsigned long mask = sun4m_get_irqmask(level);
	sbus_writel(mask, &sun4m_irq_percpu[cpu]->set);
}

static void sun4m_clear_ipi(int cpu, int level)
{
	unsigned long mask;

	mask = sun4m_get_irqmask(level);
	sun4m_interrupts->cpu_intregs[cpu].clear = mask;
	unsigned long mask = sun4m_get_irqmask(level);
	sbus_writel(mask, &sun4m_irq_percpu[cpu]->clear);
}

static void sun4m_set_udt(int cpu)
{
	sun4m_interrupts->undirected_target = cpu;
	sbus_writel(cpu, &sun4m_irq_global->interrupt_target);
}
#endif

@@ -347,7 +315,7 @@ static void __init sun4m_init_timers(irq_handler_t counter_fn)
	for (i = 0; i < num_cpu_timers; i++)
		sbus_writel(0, &timers_percpu[i]->l14_limit);
	if (num_cpu_timers == 4)
		sbus_writel(SUN4M_INT_E14, &sun4m_interrupts->set);
		sbus_writel(SUN4M_INT_E14, &sun4m_irq_global->mask_set);

#ifdef CONFIG_SMP
	{
@@ -372,62 +340,38 @@ static void __init sun4m_init_timers(irq_handler_t counter_fn)

void __init sun4m_init_IRQ(void)
{
	int ie_node,i;
	struct linux_prom_registers int_regs[PROMREG_MAX];
	int num_regs;
	struct resource r;
	int mid;
	struct device_node *dp = of_find_node_by_name(NULL, "interrupt");
	int len, i, mid, num_cpu_iregs;
	const u32 *addr;

	local_irq_disable();
	if((ie_node = prom_searchsiblings(prom_getchild(prom_root_node), "obio")) == 0 ||
	   (ie_node = prom_getchild (ie_node)) == 0 ||
	   (ie_node = prom_searchsiblings (ie_node, "interrupt")) == 0) {
		prom_printf("Cannot find /obio/interrupt node\n");
		prom_halt();
	if (!dp) {
		printk(KERN_ERR "sun4m_init_IRQ: No 'interrupt' node.\n");
		return;
	}
	num_regs = prom_getproperty(ie_node, "reg", (char *) int_regs,
				    sizeof(int_regs));
	num_regs = (num_regs/sizeof(struct linux_prom_registers));
    
	/* Apply the obio ranges to these registers. */
	prom_apply_obio_ranges(int_regs, num_regs);
    
	int_regs[4].phys_addr = int_regs[num_regs-1].phys_addr;
	int_regs[4].reg_size = int_regs[num_regs-1].reg_size;
	int_regs[4].which_io = int_regs[num_regs-1].which_io;
	for(ie_node = 1; ie_node < 4; ie_node++) {
		int_regs[ie_node].phys_addr = int_regs[ie_node-1].phys_addr + PAGE_SIZE;
		int_regs[ie_node].reg_size = int_regs[ie_node-1].reg_size;
		int_regs[ie_node].which_io = int_regs[ie_node-1].which_io;

	addr = of_get_property(dp, "address", &len);
	if (!addr) {
		printk(KERN_ERR "sun4m_init_IRQ: No 'address' prop.\n");
		return;
	}

	memset((char *)&r, 0, sizeof(struct resource));
	/* Map the interrupt registers for all possible cpus. */
	r.flags = int_regs[0].which_io;
	r.start = int_regs[0].phys_addr;
	sun4m_interrupts = (struct sun4m_intregs *) of_ioremap(&r, 0,
	    PAGE_SIZE*SUN4M_NCPUS, "interrupts_percpu");
	num_cpu_iregs = (len / sizeof(u32)) - 1;
	for (i = 0; i < num_cpu_iregs; i++) {
		sun4m_irq_percpu[i] = (void __iomem *)
			(unsigned long) addr[i];
	}
	sun4m_irq_global = (void __iomem *)
		(unsigned long) addr[num_cpu_iregs];

	/* Map the system interrupt control registers. */
	r.flags = int_regs[4].which_io;
	r.start = int_regs[4].phys_addr;
	of_ioremap(&r, 0, int_regs[4].reg_size, "interrupts_system");
	local_irq_disable();

	sun4m_interrupts->set = ~SUN4M_INT_MASKALL;
	sbus_writel(~SUN4M_INT_MASKALL, &sun4m_irq_global->mask_set);
	for (i = 0; !cpu_find_by_instance(i, NULL, &mid); i++)
		sun4m_interrupts->cpu_intregs[mid].clear = ~0x17fff;
		sbus_writel(~0x17fff, &sun4m_irq_percpu[mid]->clear);

	if (!cpu_find_by_instance(1, NULL, NULL)) {
		/* system wide interrupts go to cpu 0, this should always
		 * be safe because it is guaranteed to be fitted or OBP doesn't
		 * come up
		 *
		 * Not sure, but writing here on SLAVIO systems may puke
		 * so I don't do it unless there is more than 1 cpu.
		 */
		irq_rcvreg = (unsigned long *)
				&sun4m_interrupts->undirected_target;
		sun4m_interrupts->undirected_target = 0;
	if (num_cpu_iregs == 4) {
		irq_rcvreg = (unsigned long *) &sun4m_irq_global->interrupt_target;
		sbus_writel(0, &sun4m_irq_global->interrupt_target);
	}
	BTFIXUPSET_CALL(enable_irq, sun4m_enable_irq, BTFIXUPCALL_NORM);
	BTFIXUPSET_CALL(disable_irq, sun4m_disable_irq, BTFIXUPCALL_NORM);
@@ -442,5 +386,6 @@ void __init sun4m_init_IRQ(void)
	BTFIXUPSET_CALL(clear_cpu_int, sun4m_clear_ipi, BTFIXUPCALL_NORM);
	BTFIXUPSET_CALL(set_irq_udt, sun4m_set_udt, BTFIXUPCALL_NORM);
#endif

	/* Cannot enable interrupts until OBP ticker is disabled. */
}