[SPARC64]: Only use bypass accesses to INO buckets.
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
f14356de1c
commit
42d5f99b1d
|
@ -42,6 +42,7 @@
|
||||||
#include <asm/auxio.h>
|
#include <asm/auxio.h>
|
||||||
#include <asm/head.h>
|
#include <asm/head.h>
|
||||||
#include <asm/hypervisor.h>
|
#include <asm/hypervisor.h>
|
||||||
|
#include <asm/cacheflush.h>
|
||||||
|
|
||||||
/* UPA nodes send interrupt packet to UltraSparc with first data reg
|
/* UPA nodes send interrupt packet to UltraSparc with first data reg
|
||||||
* value low 5 (7 on Starfire) bits holding the IRQ identifier being
|
* value low 5 (7 on Starfire) bits holding the IRQ identifier being
|
||||||
|
@ -56,10 +57,10 @@
|
||||||
* of the vectored interrupt trap handler(s) in entry.S and sun4v_ivec.S
|
* of the vectored interrupt trap handler(s) in entry.S and sun4v_ivec.S
|
||||||
*/
|
*/
|
||||||
struct ino_bucket {
|
struct ino_bucket {
|
||||||
/*0x00*/unsigned long irq_chain_pa;
|
/*0x00*/unsigned long __irq_chain_pa;
|
||||||
|
|
||||||
/* Virtual interrupt number assigned to this INO. */
|
/* Virtual interrupt number assigned to this INO. */
|
||||||
/*0x08*/unsigned int virt_irq;
|
/*0x08*/unsigned int __virt_irq;
|
||||||
/*0x0c*/unsigned int __pad;
|
/*0x0c*/unsigned int __pad;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -67,6 +68,60 @@ struct ino_bucket {
|
||||||
struct ino_bucket *ivector_table;
|
struct ino_bucket *ivector_table;
|
||||||
unsigned long ivector_table_pa;
|
unsigned long ivector_table_pa;
|
||||||
|
|
||||||
|
/* On several sun4u processors, it is illegal to mix bypass and
|
||||||
|
* non-bypass accesses. Therefore we access all INO buckets
|
||||||
|
* using bypass accesses only.
|
||||||
|
*/
|
||||||
|
static unsigned long bucket_get_chain_pa(unsigned long bucket_pa)
|
||||||
|
{
|
||||||
|
unsigned long ret;
|
||||||
|
|
||||||
|
__asm__ __volatile__("ldxa [%1] %2, %0"
|
||||||
|
: "=&r" (ret)
|
||||||
|
: "r" (bucket_pa +
|
||||||
|
offsetof(struct ino_bucket,
|
||||||
|
__irq_chain_pa)),
|
||||||
|
"i" (ASI_PHYS_USE_EC));
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bucket_clear_chain_pa(unsigned long bucket_pa)
|
||||||
|
{
|
||||||
|
__asm__ __volatile__("stxa %%g0, [%0] %1"
|
||||||
|
: /* no outputs */
|
||||||
|
: "r" (bucket_pa +
|
||||||
|
offsetof(struct ino_bucket,
|
||||||
|
__irq_chain_pa)),
|
||||||
|
"i" (ASI_PHYS_USE_EC));
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int bucket_get_virt_irq(unsigned long bucket_pa)
|
||||||
|
{
|
||||||
|
unsigned int ret;
|
||||||
|
|
||||||
|
__asm__ __volatile__("lduwa [%1] %2, %0"
|
||||||
|
: "=&r" (ret)
|
||||||
|
: "r" (bucket_pa +
|
||||||
|
offsetof(struct ino_bucket,
|
||||||
|
__virt_irq)),
|
||||||
|
"i" (ASI_PHYS_USE_EC));
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bucket_set_virt_irq(unsigned long bucket_pa,
|
||||||
|
unsigned int virt_irq)
|
||||||
|
{
|
||||||
|
__asm__ __volatile__("stwa %0, [%1] %2"
|
||||||
|
: /* no outputs */
|
||||||
|
: "r" (virt_irq),
|
||||||
|
"r" (bucket_pa +
|
||||||
|
offsetof(struct ino_bucket,
|
||||||
|
__virt_irq)),
|
||||||
|
"i" (ASI_PHYS_USE_EC));
|
||||||
|
}
|
||||||
|
|
||||||
#define __irq_ino(irq) \
|
#define __irq_ino(irq) \
|
||||||
(((struct ino_bucket *)(irq)) - &ivector_table[0])
|
(((struct ino_bucket *)(irq)) - &ivector_table[0])
|
||||||
#define __bucket(irq) ((struct ino_bucket *)(irq))
|
#define __bucket(irq) ((struct ino_bucket *)(irq))
|
||||||
|
@ -569,18 +624,21 @@ unsigned int build_irq(int inofixup, unsigned long iclr, unsigned long imap)
|
||||||
{
|
{
|
||||||
struct ino_bucket *bucket;
|
struct ino_bucket *bucket;
|
||||||
struct irq_handler_data *data;
|
struct irq_handler_data *data;
|
||||||
|
unsigned int virt_irq;
|
||||||
int ino;
|
int ino;
|
||||||
|
|
||||||
BUG_ON(tlb_type == hypervisor);
|
BUG_ON(tlb_type == hypervisor);
|
||||||
|
|
||||||
ino = (upa_readq(imap) & (IMAP_IGN | IMAP_INO)) + inofixup;
|
ino = (upa_readq(imap) & (IMAP_IGN | IMAP_INO)) + inofixup;
|
||||||
bucket = &ivector_table[ino];
|
bucket = &ivector_table[ino];
|
||||||
if (!bucket->virt_irq) {
|
virt_irq = bucket_get_virt_irq(__pa(bucket));
|
||||||
bucket->virt_irq = virt_irq_alloc(__irq(bucket));
|
if (!virt_irq) {
|
||||||
set_irq_chip(bucket->virt_irq, &sun4u_irq);
|
virt_irq = virt_irq_alloc(__irq(bucket));
|
||||||
|
bucket_set_virt_irq(__pa(bucket), virt_irq);
|
||||||
|
set_irq_chip(virt_irq, &sun4u_irq);
|
||||||
}
|
}
|
||||||
|
|
||||||
data = get_irq_chip_data(bucket->virt_irq);
|
data = get_irq_chip_data(virt_irq);
|
||||||
if (unlikely(data))
|
if (unlikely(data))
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
@ -589,13 +647,13 @@ unsigned int build_irq(int inofixup, unsigned long iclr, unsigned long imap)
|
||||||
prom_printf("IRQ: kzalloc(irq_handler_data) failed.\n");
|
prom_printf("IRQ: kzalloc(irq_handler_data) failed.\n");
|
||||||
prom_halt();
|
prom_halt();
|
||||||
}
|
}
|
||||||
set_irq_chip_data(bucket->virt_irq, data);
|
set_irq_chip_data(virt_irq, data);
|
||||||
|
|
||||||
data->imap = imap;
|
data->imap = imap;
|
||||||
data->iclr = iclr;
|
data->iclr = iclr;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
return bucket->virt_irq;
|
return virt_irq;
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned int sun4v_build_common(unsigned long sysino,
|
static unsigned int sun4v_build_common(unsigned long sysino,
|
||||||
|
@ -603,16 +661,19 @@ static unsigned int sun4v_build_common(unsigned long sysino,
|
||||||
{
|
{
|
||||||
struct ino_bucket *bucket;
|
struct ino_bucket *bucket;
|
||||||
struct irq_handler_data *data;
|
struct irq_handler_data *data;
|
||||||
|
unsigned int virt_irq;
|
||||||
|
|
||||||
BUG_ON(tlb_type != hypervisor);
|
BUG_ON(tlb_type != hypervisor);
|
||||||
|
|
||||||
bucket = &ivector_table[sysino];
|
bucket = &ivector_table[sysino];
|
||||||
if (!bucket->virt_irq) {
|
virt_irq = bucket_get_virt_irq(__pa(bucket));
|
||||||
bucket->virt_irq = virt_irq_alloc(__irq(bucket));
|
if (!virt_irq) {
|
||||||
set_irq_chip(bucket->virt_irq, chip);
|
virt_irq = virt_irq_alloc(__irq(bucket));
|
||||||
|
bucket_set_virt_irq(__pa(bucket), virt_irq);
|
||||||
|
set_irq_chip(virt_irq, chip);
|
||||||
}
|
}
|
||||||
|
|
||||||
data = get_irq_chip_data(bucket->virt_irq);
|
data = get_irq_chip_data(virt_irq);
|
||||||
if (unlikely(data))
|
if (unlikely(data))
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
@ -621,7 +682,7 @@ static unsigned int sun4v_build_common(unsigned long sysino,
|
||||||
prom_printf("IRQ: kzalloc(irq_handler_data) failed.\n");
|
prom_printf("IRQ: kzalloc(irq_handler_data) failed.\n");
|
||||||
prom_halt();
|
prom_halt();
|
||||||
}
|
}
|
||||||
set_irq_chip_data(bucket->virt_irq, data);
|
set_irq_chip_data(virt_irq, data);
|
||||||
|
|
||||||
/* Catch accidental accesses to these things. IMAP/ICLR handling
|
/* Catch accidental accesses to these things. IMAP/ICLR handling
|
||||||
* is done by hypervisor calls on sun4v platforms, not by direct
|
* is done by hypervisor calls on sun4v platforms, not by direct
|
||||||
|
@ -631,7 +692,7 @@ static unsigned int sun4v_build_common(unsigned long sysino,
|
||||||
data->iclr = ~0UL;
|
data->iclr = ~0UL;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
return bucket->virt_irq;
|
return virt_irq;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int sun4v_build_irq(u32 devhandle, unsigned int devino)
|
unsigned int sun4v_build_irq(u32 devhandle, unsigned int devino)
|
||||||
|
@ -646,19 +707,24 @@ unsigned int sun4v_build_virq(u32 devhandle, unsigned int devino)
|
||||||
struct irq_handler_data *data;
|
struct irq_handler_data *data;
|
||||||
struct ino_bucket *bucket;
|
struct ino_bucket *bucket;
|
||||||
unsigned long hv_err, cookie;
|
unsigned long hv_err, cookie;
|
||||||
|
unsigned int virt_irq;
|
||||||
|
|
||||||
bucket = kzalloc(sizeof(struct ino_bucket), GFP_ATOMIC);
|
bucket = kzalloc(sizeof(struct ino_bucket), GFP_ATOMIC);
|
||||||
if (unlikely(!bucket))
|
if (unlikely(!bucket))
|
||||||
return 0;
|
return 0;
|
||||||
|
__flush_dcache_range((unsigned long) bucket,
|
||||||
|
((unsigned long) bucket +
|
||||||
|
sizeof(struct ino_bucket)));
|
||||||
|
|
||||||
bucket->virt_irq = virt_irq_alloc(__irq(bucket));
|
virt_irq = virt_irq_alloc(__irq(bucket));
|
||||||
set_irq_chip(bucket->virt_irq, &sun4v_virq);
|
bucket_set_virt_irq(__pa(bucket), virt_irq);
|
||||||
|
set_irq_chip(virt_irq, &sun4v_virq);
|
||||||
|
|
||||||
data = kzalloc(sizeof(struct irq_handler_data), GFP_ATOMIC);
|
data = kzalloc(sizeof(struct irq_handler_data), GFP_ATOMIC);
|
||||||
if (unlikely(!data))
|
if (unlikely(!data))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
set_irq_chip_data(bucket->virt_irq, data);
|
set_irq_chip_data(virt_irq, data);
|
||||||
|
|
||||||
/* Catch accidental accesses to these things. IMAP/ICLR handling
|
/* Catch accidental accesses to these things. IMAP/ICLR handling
|
||||||
* is done by hypervisor calls on sun4v platforms, not by direct
|
* is done by hypervisor calls on sun4v platforms, not by direct
|
||||||
|
@ -675,10 +741,10 @@ unsigned int sun4v_build_virq(u32 devhandle, unsigned int devino)
|
||||||
prom_halt();
|
prom_halt();
|
||||||
}
|
}
|
||||||
|
|
||||||
virt_to_real_irq_table[bucket->virt_irq].dev_handle = devhandle;
|
virt_to_real_irq_table[virt_irq].dev_handle = devhandle;
|
||||||
virt_to_real_irq_table[bucket->virt_irq].dev_ino = devino;
|
virt_to_real_irq_table[virt_irq].dev_ino = devino;
|
||||||
|
|
||||||
return bucket->virt_irq;
|
return virt_irq;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ack_bad_irq(unsigned int virt_irq)
|
void ack_bad_irq(unsigned int virt_irq)
|
||||||
|
@ -718,17 +784,9 @@ void handler_irq(int irq, struct pt_regs *regs)
|
||||||
unsigned long next_pa;
|
unsigned long next_pa;
|
||||||
unsigned int virt_irq;
|
unsigned int virt_irq;
|
||||||
|
|
||||||
__asm__ __volatile__("ldxa [%2] %4, %0\n\t"
|
next_pa = bucket_get_chain_pa(bucket_pa);
|
||||||
"lduwa [%3] %4, %1\n\t"
|
virt_irq = bucket_get_virt_irq(bucket_pa);
|
||||||
"stxa %%g0, [%2] %4"
|
bucket_clear_chain_pa(bucket_pa);
|
||||||
: "=&r" (next_pa), "=&r" (virt_irq)
|
|
||||||
: "r" (bucket_pa +
|
|
||||||
offsetof(struct ino_bucket,
|
|
||||||
irq_chain_pa)),
|
|
||||||
"r" (bucket_pa +
|
|
||||||
offsetof(struct ino_bucket,
|
|
||||||
virt_irq)),
|
|
||||||
"i" (ASI_PHYS_USE_EC));
|
|
||||||
|
|
||||||
__do_IRQ(virt_irq);
|
__do_IRQ(virt_irq);
|
||||||
|
|
||||||
|
@ -957,6 +1015,8 @@ void __init init_IRQ(void)
|
||||||
prom_printf("Fatal error, cannot allocate ivector_table\n");
|
prom_printf("Fatal error, cannot allocate ivector_table\n");
|
||||||
prom_halt();
|
prom_halt();
|
||||||
}
|
}
|
||||||
|
__flush_dcache_range((unsigned long) ivector_table,
|
||||||
|
((unsigned long) ivector_table) + size);
|
||||||
|
|
||||||
ivector_table_pa = __pa(ivector_table);
|
ivector_table_pa = __pa(ivector_table);
|
||||||
|
|
||||||
|
|
|
@ -631,7 +631,6 @@ void prom_world(int enter)
|
||||||
__asm__ __volatile__("flushw");
|
__asm__ __volatile__("flushw");
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DCACHE_ALIASING_POSSIBLE
|
|
||||||
void __flush_dcache_range(unsigned long start, unsigned long end)
|
void __flush_dcache_range(unsigned long start, unsigned long end)
|
||||||
{
|
{
|
||||||
unsigned long va;
|
unsigned long va;
|
||||||
|
@ -655,7 +654,6 @@ void __flush_dcache_range(unsigned long start, unsigned long end)
|
||||||
"i" (ASI_DCACHE_INVALIDATE));
|
"i" (ASI_DCACHE_INVALIDATE));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif /* DCACHE_ALIASING_POSSIBLE */
|
|
||||||
|
|
||||||
/* get_new_mmu_context() uses "cache + 1". */
|
/* get_new_mmu_context() uses "cache + 1". */
|
||||||
DEFINE_SPINLOCK(ctx_alloc_lock);
|
DEFINE_SPINLOCK(ctx_alloc_lock);
|
||||||
|
|
Loading…
Reference in a new issue