Commit 9263e85a authored by Geoff Levand's avatar Geoff Levand Committed by Paul Mackerras

[POWERPC] PS3: Kexec support

Fixup the core platform parts needed for kexec to work on the PS3.
 - Setup ps3_hpte_clear correctly.
 - Mask interrupts on irq removal.
 - Release all hypervisor resources.
 - Create new routine ps3_shutdown_IRQ()
Signed-off-by: default avatarGeoff Levand <geoffrey.levand@am.sony.com>
Signed-off-by: default avatarPaul Mackerras <paulus@samba.org>
parent 83bb643d
...@@ -234,10 +234,17 @@ static void ps3_hpte_invalidate(unsigned long slot, unsigned long va, ...@@ -234,10 +234,17 @@ static void ps3_hpte_invalidate(unsigned long slot, unsigned long va,
static void ps3_hpte_clear(void) static void ps3_hpte_clear(void)
{ {
/* Make sure to clean up the frame buffer device first */ int result;
ps3fb_cleanup();
lv1_unmap_htab(htab_addr); DBG(" -> %s:%d\n", __func__, __LINE__);
result = lv1_unmap_htab(htab_addr);
BUG_ON(result);
ps3_mm_shutdown();
ps3_mm_vas_destroy();
DBG(" <- %s:%d\n", __func__, __LINE__);
} }
void __init ps3_hpte_init(unsigned long htab_size) void __init ps3_hpte_init(unsigned long htab_size)
......
...@@ -220,6 +220,8 @@ int ps3_virq_setup(enum ps3_cpu_binding cpu, unsigned long outlet, ...@@ -220,6 +220,8 @@ int ps3_virq_setup(enum ps3_cpu_binding cpu, unsigned long outlet,
goto fail_set; goto fail_set;
} }
ps3_chip_mask(*virq);
return result; return result;
fail_set: fail_set:
...@@ -311,6 +313,8 @@ int ps3_irq_plug_destroy(unsigned int virq) ...@@ -311,6 +313,8 @@ int ps3_irq_plug_destroy(unsigned int virq)
pr_debug("%s:%d: node %lu, cpu %d, virq %u\n", __func__, __LINE__, pr_debug("%s:%d: node %lu, cpu %d, virq %u\n", __func__, __LINE__,
pd->node, pd->cpu, virq); pd->node, pd->cpu, virq);
ps3_chip_mask(virq);
result = lv1_disconnect_irq_plug_ext(pd->node, pd->cpu, virq); result = lv1_disconnect_irq_plug_ext(pd->node, pd->cpu, virq);
if (result) if (result)
...@@ -368,7 +372,9 @@ int ps3_event_receive_port_destroy(unsigned int virq) ...@@ -368,7 +372,9 @@ int ps3_event_receive_port_destroy(unsigned int virq)
{ {
int result; int result;
pr_debug(" -> %s:%d virq: %u\n", __func__, __LINE__, virq); pr_debug(" -> %s:%d virq %u\n", __func__, __LINE__, virq);
ps3_chip_mask(virq);
result = lv1_destruct_event_receive_port(virq_to_hw(virq)); result = lv1_destruct_event_receive_port(virq_to_hw(virq));
...@@ -376,17 +382,14 @@ int ps3_event_receive_port_destroy(unsigned int virq) ...@@ -376,17 +382,14 @@ int ps3_event_receive_port_destroy(unsigned int virq)
pr_debug("%s:%d: lv1_destruct_event_receive_port failed: %s\n", pr_debug("%s:%d: lv1_destruct_event_receive_port failed: %s\n",
__func__, __LINE__, ps3_result(result)); __func__, __LINE__, ps3_result(result));
/* lv1_destruct_event_receive_port() destroys the IRQ plug, /*
* so don't call ps3_irq_plug_destroy() here. * Don't call ps3_virq_destroy() here since ps3_smp_cleanup_cpu()
* calls from interrupt context (smp_call_function) when kexecing.
*/ */
result = ps3_virq_destroy(virq);
BUG_ON(result);
pr_debug(" <- %s:%d\n", __func__, __LINE__); pr_debug(" <- %s:%d\n", __func__, __LINE__);
return result; return result;
} }
EXPORT_SYMBOL_GPL(ps3_event_receive_port_destroy);
int ps3_send_event_locally(unsigned int virq) int ps3_send_event_locally(unsigned int virq)
{ {
...@@ -458,6 +461,14 @@ int ps3_sb_event_receive_port_destroy(const struct ps3_device_id *did, ...@@ -458,6 +461,14 @@ int ps3_sb_event_receive_port_destroy(const struct ps3_device_id *did,
result = ps3_event_receive_port_destroy(virq); result = ps3_event_receive_port_destroy(virq);
BUG_ON(result); BUG_ON(result);
/*
* ps3_event_receive_port_destroy() destroys the IRQ plug,
* so don't call ps3_irq_plug_destroy() here.
*/
result = ps3_virq_destroy(virq);
BUG_ON(result);
pr_debug(" <- %s:%d\n", __func__, __LINE__); pr_debug(" <- %s:%d\n", __func__, __LINE__);
return result; return result;
} }
...@@ -498,16 +509,24 @@ EXPORT_SYMBOL_GPL(ps3_io_irq_setup); ...@@ -498,16 +509,24 @@ EXPORT_SYMBOL_GPL(ps3_io_irq_setup);
int ps3_io_irq_destroy(unsigned int virq) int ps3_io_irq_destroy(unsigned int virq)
{ {
int result; int result;
unsigned long outlet = virq_to_hw(virq);
result = lv1_destruct_io_irq_outlet(virq_to_hw(virq)); ps3_chip_mask(virq);
if (result) /*
pr_debug("%s:%d: lv1_destruct_io_irq_outlet failed: %s\n", * lv1_destruct_io_irq_outlet() will destroy the IRQ plug,
__func__, __LINE__, ps3_result(result)); * so call ps3_irq_plug_destroy() first.
*/
result = ps3_irq_plug_destroy(virq); result = ps3_irq_plug_destroy(virq);
BUG_ON(result); BUG_ON(result);
result = lv1_destruct_io_irq_outlet(outlet);
if (result)
pr_debug("%s:%d: lv1_destruct_io_irq_outlet failed: %s\n",
__func__, __LINE__, ps3_result(result));
return result; return result;
} }
EXPORT_SYMBOL_GPL(ps3_io_irq_destroy); EXPORT_SYMBOL_GPL(ps3_io_irq_destroy);
...@@ -552,6 +571,7 @@ int ps3_vuart_irq_destroy(unsigned int virq) ...@@ -552,6 +571,7 @@ int ps3_vuart_irq_destroy(unsigned int virq)
{ {
int result; int result;
ps3_chip_mask(virq);
result = lv1_deconfigure_virtual_uart_irq(); result = lv1_deconfigure_virtual_uart_irq();
if (result) { if (result) {
...@@ -600,9 +620,14 @@ int ps3_spe_irq_setup(enum ps3_cpu_binding cpu, unsigned long spe_id, ...@@ -600,9 +620,14 @@ int ps3_spe_irq_setup(enum ps3_cpu_binding cpu, unsigned long spe_id,
int ps3_spe_irq_destroy(unsigned int virq) int ps3_spe_irq_destroy(unsigned int virq)
{ {
int result = ps3_irq_plug_destroy(virq); int result;
ps3_chip_mask(virq);
result = ps3_irq_plug_destroy(virq);
BUG_ON(result); BUG_ON(result);
return 0;
return result;
} }
...@@ -662,7 +687,7 @@ static int ps3_host_map(struct irq_host *h, unsigned int virq, ...@@ -662,7 +687,7 @@ static int ps3_host_map(struct irq_host *h, unsigned int virq,
pr_debug("%s:%d: hwirq %lu, virq %u\n", __func__, __LINE__, hwirq, pr_debug("%s:%d: hwirq %lu, virq %u\n", __func__, __LINE__, hwirq,
virq); virq);
set_irq_chip_and_handler(virq, &irq_chip, handle_fasteoi_irq); set_irq_chip_and_handler(virq, &ps3_irq_chip, handle_fasteoi_irq);
return 0; return 0;
} }
...@@ -682,7 +707,7 @@ void __init ps3_register_ipi_debug_brk(unsigned int cpu, unsigned int virq) ...@@ -682,7 +707,7 @@ void __init ps3_register_ipi_debug_brk(unsigned int cpu, unsigned int virq)
cpu, virq, pd->bmp.ipi_debug_brk_mask); cpu, virq, pd->bmp.ipi_debug_brk_mask);
} }
unsigned int ps3_get_irq(void) static unsigned int ps3_get_irq(void)
{ {
struct ps3_private *pd = &__get_cpu_var(ps3_private); struct ps3_private *pd = &__get_cpu_var(ps3_private);
u64 x = (pd->bmp.status & pd->bmp.mask); u64 x = (pd->bmp.status & pd->bmp.mask);
...@@ -747,3 +772,16 @@ void __init ps3_init_IRQ(void) ...@@ -747,3 +772,16 @@ void __init ps3_init_IRQ(void)
ppc_md.get_irq = ps3_get_irq; ppc_md.get_irq = ps3_get_irq;
} }
void ps3_shutdown_IRQ(int cpu)
{
int result;
u64 ppe_id;
u64 thread_id = get_hard_smp_processor_id(cpu);
lv1_get_logical_ppe_id(&ppe_id);
result = lv1_configure_irq_state_bitmap(ppe_id, thread_id, 0);
DBG("%s:%d: lv1_configure_irq_state_bitmap (%lu:%lu/%d) %s\n", __func__,
__LINE__, ppe_id, thread_id, cpu, ps3_result(result));
}
...@@ -41,6 +41,7 @@ void ps3_mm_shutdown(void); ...@@ -41,6 +41,7 @@ void ps3_mm_shutdown(void);
/* irq */ /* irq */
void ps3_init_IRQ(void); void ps3_init_IRQ(void);
void ps3_shutdown_IRQ(int cpu);
void __init ps3_register_ipi_debug_brk(unsigned int cpu, unsigned int virq); void __init ps3_register_ipi_debug_brk(unsigned int cpu, unsigned int virq);
/* smp */ /* smp */
......
...@@ -209,31 +209,12 @@ static int __init ps3_probe(void) ...@@ -209,31 +209,12 @@ static int __init ps3_probe(void)
#if defined(CONFIG_KEXEC) #if defined(CONFIG_KEXEC)
static void ps3_kexec_cpu_down(int crash_shutdown, int secondary) static void ps3_kexec_cpu_down(int crash_shutdown, int secondary)
{ {
DBG(" -> %s:%d\n", __func__, __LINE__); int cpu = smp_processor_id();
if (secondary) {
int cpu;
for_each_online_cpu(cpu)
if (cpu)
ps3_smp_cleanup_cpu(cpu);
} else
ps3_smp_cleanup_cpu(0);
DBG(" <- %s:%d\n", __func__, __LINE__); DBG(" -> %s:%d: (%d)\n", __func__, __LINE__, cpu);
}
static void ps3_machine_kexec(struct kimage *image) ps3_smp_cleanup_cpu(cpu);
{ ps3_shutdown_IRQ(cpu);
unsigned long ppe_id;
DBG(" -> %s:%d\n", __func__, __LINE__);
lv1_get_logical_ppe_id(&ppe_id);
lv1_configure_irq_state_bitmap(ppe_id, 0, 0);
ps3_mm_shutdown();
ps3_mm_vas_destroy();
default_machine_kexec(image);
DBG(" <- %s:%d\n", __func__, __LINE__); DBG(" <- %s:%d\n", __func__, __LINE__);
} }
...@@ -255,7 +236,7 @@ define_machine(ps3) { ...@@ -255,7 +236,7 @@ define_machine(ps3) {
.power_off = ps3_power_off, .power_off = ps3_power_off,
#if defined(CONFIG_KEXEC) #if defined(CONFIG_KEXEC)
.kexec_cpu_down = ps3_kexec_cpu_down, .kexec_cpu_down = ps3_kexec_cpu_down,
.machine_kexec = ps3_machine_kexec, .machine_kexec = default_machine_kexec,
.machine_kexec_prepare = default_machine_kexec_prepare, .machine_kexec_prepare = default_machine_kexec_prepare,
.machine_crash_shutdown = default_machine_crash_shutdown, .machine_crash_shutdown = default_machine_crash_shutdown,
#endif #endif
......
...@@ -138,7 +138,7 @@ void ps3_smp_cleanup_cpu(int cpu) ...@@ -138,7 +138,7 @@ void ps3_smp_cleanup_cpu(int cpu)
DBG(" -> %s:%d: (%d)\n", __func__, __LINE__, cpu); DBG(" -> %s:%d: (%d)\n", __func__, __LINE__, cpu);
for (i = 0; i < MSG_COUNT; i++) { for (i = 0; i < MSG_COUNT; i++) {
free_irq(virqs[i], (void*)(long)i); /* Can't call free_irq from interrupt context. */
ps3_event_receive_port_destroy(virqs[i]); ps3_event_receive_port_destroy(virqs[i]);
virqs[i] = NO_IRQ; virqs[i] = NO_IRQ;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment