From a081e126e12f78651499ba0f9944a7df1e9b06b6 Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Tue, 5 Dec 2006 15:54:14 +1100 Subject: [POWERPC] Fix cell pmu initialisation Make sure that the pmu is not initialised unless we are running on a cell. Also make the init routine static. Signed-off-by: Stephen Rothwell Signed-off-by: Paul Mackerras --- arch/powerpc/platforms/cell/pmu.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'arch/powerpc/platforms') diff --git a/arch/powerpc/platforms/cell/pmu.c b/arch/powerpc/platforms/cell/pmu.c index 99c612025e8f..d04ae1671e6c 100644 --- a/arch/powerpc/platforms/cell/pmu.c +++ b/arch/powerpc/platforms/cell/pmu.c @@ -382,11 +382,14 @@ static irqreturn_t cbe_pm_irq(int irq, void *dev_id) return IRQ_HANDLED; } -int __init cbe_init_pm_irq(void) +static int __init cbe_init_pm_irq(void) { unsigned int irq; int rc, node; + if (!machine_is(cell)) + return 0; + for_each_node(node) { irq = irq_create_mapping(NULL, IIC_IRQ_IOEX_PMI | (node << IIC_IRQ_NODE_SHIFT)); -- cgit v1.2.3 From 0332c2d447a7a20a4d744ba3814a349d0c1c6405 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Tue, 5 Dec 2006 17:52:36 +1100 Subject: [POWERPC] Move rtas_stop_self() into platforms/pseries/hotplug-cpu.c As the first step in consolidating the pseries hotplug cpu code, create platforms/pseries/hotplug-cpu.c and move rtas_stop_self() into it. Do the rtas token initialisation in a new initcall, rather than rtas_initialize(). Signed-off-by: Michael Ellerman Acked-by: Linas Vepstas Signed-off-by: Paul Mackerras --- arch/powerpc/kernel/rtas.c | 29 -------------- arch/powerpc/platforms/pseries/Makefile | 2 + arch/powerpc/platforms/pseries/hotplug-cpu.c | 58 ++++++++++++++++++++++++++++ 3 files changed, 60 insertions(+), 29 deletions(-) create mode 100644 arch/powerpc/platforms/pseries/hotplug-cpu.c (limited to 'arch/powerpc/platforms') diff --git a/arch/powerpc/kernel/rtas.c b/arch/powerpc/kernel/rtas.c index 387ed0d9ad61..952f4c2fc1eb 100644 --- a/arch/powerpc/kernel/rtas.c +++ b/arch/powerpc/kernel/rtas.c @@ -810,32 +810,6 @@ asmlinkage int ppc_rtas(struct rtas_args __user *uargs) return 0; } -#ifdef CONFIG_HOTPLUG_CPU -/* This version can't take the spinlock, because it never returns */ -static struct rtas_args rtas_stop_self_args = { - /* The token is initialized for real in setup_system() */ - .token = RTAS_UNKNOWN_SERVICE, - .nargs = 0, - .nret = 1, - .rets = &rtas_stop_self_args.args[0], -}; - -void rtas_stop_self(void) -{ - struct rtas_args *rtas_args = &rtas_stop_self_args; - - local_irq_disable(); - - BUG_ON(rtas_args->token == RTAS_UNKNOWN_SERVICE); - - printk("cpu %u (hwid %u) Ready to die...\n", - smp_processor_id(), hard_smp_processor_id()); - enter_rtas(__pa(rtas_args)); - - panic("Alas, I survived.\n"); -} -#endif - /* * Call early during boot, before mem init or bootmem, to retrieve the RTAS * informations from the device-tree and allocate the RMO buffer for userland @@ -880,9 +854,6 @@ void __init rtas_initialize(void) #endif rtas_rmo_buf = lmb_alloc_base(RTAS_RMOBUF_MAX, PAGE_SIZE, rtas_region); -#ifdef CONFIG_HOTPLUG_CPU - rtas_stop_self_args.token = rtas_token("stop-self"); -#endif /* CONFIG_HOTPLUG_CPU */ #ifdef CONFIG_RTAS_ERROR_LOGGING rtas_last_error_token = rtas_token("rtas-last-error"); #endif diff --git a/arch/powerpc/platforms/pseries/Makefile b/arch/powerpc/platforms/pseries/Makefile index 997243a91be8..69590fbf83da 100644 --- a/arch/powerpc/platforms/pseries/Makefile +++ b/arch/powerpc/platforms/pseries/Makefile @@ -10,6 +10,8 @@ obj-$(CONFIG_XICS) += xics.o obj-$(CONFIG_SCANLOG) += scanlog.o obj-$(CONFIG_EEH) += eeh.o eeh_cache.o eeh_driver.o eeh_event.o +obj-$(CONFIG_HOTPLUG_CPU) += hotplug-cpu.o + obj-$(CONFIG_HVC_CONSOLE) += hvconsole.o obj-$(CONFIG_HVCS) += hvcserver.o obj-$(CONFIG_HCALL_STATS) += hvCall_inst.o diff --git a/arch/powerpc/platforms/pseries/hotplug-cpu.c b/arch/powerpc/platforms/pseries/hotplug-cpu.c new file mode 100644 index 000000000000..6e21ebdfcef7 --- /dev/null +++ b/arch/powerpc/platforms/pseries/hotplug-cpu.c @@ -0,0 +1,58 @@ +/* + * pseries CPU Hotplug infrastructure. + * + * Split out from arch/powerpc/kernel/rtas.c + * + * Peter Bergner, IBM March 2001. + * Copyright (C) 2001 IBM. + * + * Copyright (C) 2006 Michael Ellerman, IBM Corporation + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "xics.h" + +/* This version can't take the spinlock, because it never returns */ +static struct rtas_args rtas_stop_self_args = { + .token = RTAS_UNKNOWN_SERVICE, + .nargs = 0, + .nret = 1, + .rets = &rtas_stop_self_args.args[0], +}; + +void rtas_stop_self(void) +{ + struct rtas_args *args = &rtas_stop_self_args; + + local_irq_disable(); + + BUG_ON(args->token == RTAS_UNKNOWN_SERVICE); + + printk("cpu %u (hwid %u) Ready to die...\n", + smp_processor_id(), hard_smp_processor_id()); + enter_rtas(__pa(args)); + + panic("Alas, I survived.\n"); +} + +static int __init pseries_cpu_hotplug_init(void) +{ + rtas_stop_self_args.token = rtas_token("stop-self"); + + return 0; +} +arch_initcall(pseries_cpu_hotplug_init); -- cgit v1.2.3 From 04da6af960194ecdee4c29cd3f86e766903418ca Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Tue, 5 Dec 2006 17:52:37 +1100 Subject: [POWERPC] Move pSeries_mach_cpu_die() into platforms/pseries/hotplug-cpu.c Move pSeries_mach_cpu_die() into platforms/pseries/hotplug-cpu.c, this allows rtas_stop_self() to be static so remove the prototype. Wire up pSeries_mach_cpu_die() in the initcall, rather than statically in setup.c, the initcall will still run prior to the cpu hotplug code being callable, so there should be no change in behaviour. Signed-off-by: Michael Ellerman Signed-off-by: Paul Mackerras --- arch/powerpc/platforms/pseries/hotplug-cpu.c | 18 ++++++++++++++++-- arch/powerpc/platforms/pseries/setup.c | 16 ---------------- include/asm-powerpc/rtas.h | 2 -- 3 files changed, 16 insertions(+), 20 deletions(-) (limited to 'arch/powerpc/platforms') diff --git a/arch/powerpc/platforms/pseries/hotplug-cpu.c b/arch/powerpc/platforms/pseries/hotplug-cpu.c index 6e21ebdfcef7..9e9b6b159fab 100644 --- a/arch/powerpc/platforms/pseries/hotplug-cpu.c +++ b/arch/powerpc/platforms/pseries/hotplug-cpu.c @@ -1,7 +1,8 @@ /* * pseries CPU Hotplug infrastructure. * - * Split out from arch/powerpc/kernel/rtas.c + * Split out from arch/powerpc/platforms/pseries/setup.c and + * arch/powerpc/kernel/rtas.c * * Peter Bergner, IBM March 2001. * Copyright (C) 2001 IBM. @@ -34,7 +35,7 @@ static struct rtas_args rtas_stop_self_args = { .rets = &rtas_stop_self_args.args[0], }; -void rtas_stop_self(void) +static void rtas_stop_self(void) { struct rtas_args *args = &rtas_stop_self_args; @@ -49,10 +50,23 @@ void rtas_stop_self(void) panic("Alas, I survived.\n"); } +static void pSeries_mach_cpu_die(void) +{ + local_irq_disable(); + idle_task_exit(); + xics_teardown_cpu(0); + rtas_stop_self(); + /* Should never get here... */ + BUG(); + for(;;); +} + static int __init pseries_cpu_hotplug_init(void) { rtas_stop_self_args.token = rtas_token("stop-self"); + ppc_md.cpu_die = pSeries_mach_cpu_die; + return 0; } arch_initcall(pseries_cpu_hotplug_init); diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c index 0dc2548ca9bc..6090d753c44c 100644 --- a/arch/powerpc/platforms/pseries/setup.c +++ b/arch/powerpc/platforms/pseries/setup.c @@ -347,21 +347,6 @@ static int __init pSeries_init_panel(void) } arch_initcall(pSeries_init_panel); -#ifdef CONFIG_HOTPLUG_CPU -static void pSeries_mach_cpu_die(void) -{ - local_irq_disable(); - idle_task_exit(); - xics_teardown_cpu(0); - rtas_stop_self(); - /* Should never get here... */ - BUG(); - for(;;); -} -#else -#define pSeries_mach_cpu_die NULL -#endif - static int pseries_set_dabr(unsigned long dabr) { return plpar_hcall_norets(H_SET_DABR, dabr); @@ -561,7 +546,6 @@ define_machine(pseries) { .power_off = rtas_power_off, .halt = rtas_halt, .panic = rtas_os_term, - .cpu_die = pSeries_mach_cpu_die, .get_boot_time = rtas_get_boot_time, .get_rtc_time = rtas_get_rtc_time, .set_rtc_time = rtas_set_rtc_time, diff --git a/include/asm-powerpc/rtas.h b/include/asm-powerpc/rtas.h index 5a0c136c0416..031ef57fb195 100644 --- a/include/asm-powerpc/rtas.h +++ b/include/asm-powerpc/rtas.h @@ -221,8 +221,6 @@ extern int rtas_get_error_log_max(void); extern spinlock_t rtas_data_buf_lock; extern char rtas_data_buf[RTAS_DATA_BUF_SIZE]; -extern void rtas_stop_self(void); - /* RMO buffer reserved for user-space RTAS use */ extern unsigned long rtas_rmo_buf; -- cgit v1.2.3 From 413f7c405a342b0b9370ea7a652b9f0270183bf3 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Tue, 5 Dec 2006 17:52:38 +1100 Subject: [POWERPC] Move the rest of the hotplug cpu code into platforms/pseries/hotplug-cpu.c Move the rest of the hotplug cpu code from platforms/pseries/smp.c into platforms/pseries/hotplug-cpu.c. Wire up the smp_ops callbacks and the notifier in the hotplug cpu initcall, rather than in smp_init_pseries(). No change in behaviour. Signed-off-by: Michael Ellerman Signed-off-by: Paul Mackerras --- arch/powerpc/platforms/pseries/hotplug-cpu.c | 201 ++++++++++++++++++++++++++- arch/powerpc/platforms/pseries/smp.c | 200 -------------------------- 2 files changed, 199 insertions(+), 202 deletions(-) (limited to 'arch/powerpc/platforms') diff --git a/arch/powerpc/platforms/pseries/hotplug-cpu.c b/arch/powerpc/platforms/pseries/hotplug-cpu.c index 9e9b6b159fab..12864d75126d 100644 --- a/arch/powerpc/platforms/pseries/hotplug-cpu.c +++ b/arch/powerpc/platforms/pseries/hotplug-cpu.c @@ -1,11 +1,14 @@ /* * pseries CPU Hotplug infrastructure. * - * Split out from arch/powerpc/platforms/pseries/setup.c and - * arch/powerpc/kernel/rtas.c + * Split out from arch/powerpc/platforms/pseries/setup.c + * arch/powerpc/kernel/rtas.c, and arch/powerpc/platforms/pseries/smp.c * * Peter Bergner, IBM March 2001. * Copyright (C) 2001 IBM. + * Dave Engebretsen, Peter Bergner, and + * Mike Corrigan {engebret|bergner|mikec}@us.ibm.com + * Plus various changes from other IBM teams... * * Copyright (C) 2006 Michael Ellerman, IBM Corporation * @@ -61,12 +64,206 @@ static void pSeries_mach_cpu_die(void) for(;;); } +/* Get state of physical CPU. + * Return codes: + * 0 - The processor is in the RTAS stopped state + * 1 - stop-self is in progress + * 2 - The processor is not in the RTAS stopped state + * -1 - Hardware Error + * -2 - Hardware Busy, Try again later. + */ +static int query_cpu_stopped(unsigned int pcpu) +{ + int cpu_status; + int status, qcss_tok; + + qcss_tok = rtas_token("query-cpu-stopped-state"); + if (qcss_tok == RTAS_UNKNOWN_SERVICE) + return -1; + status = rtas_call(qcss_tok, 1, 2, &cpu_status, pcpu); + if (status != 0) { + printk(KERN_ERR + "RTAS query-cpu-stopped-state failed: %i\n", status); + return status; + } + + return cpu_status; +} + +static int pSeries_cpu_disable(void) +{ + int cpu = smp_processor_id(); + + cpu_clear(cpu, cpu_online_map); + vdso_data->processorCount--; + + /*fix boot_cpuid here*/ + if (cpu == boot_cpuid) + boot_cpuid = any_online_cpu(cpu_online_map); + + /* FIXME: abstract this to not be platform specific later on */ + xics_migrate_irqs_away(); + return 0; +} + +static void pSeries_cpu_die(unsigned int cpu) +{ + int tries; + int cpu_status; + unsigned int pcpu = get_hard_smp_processor_id(cpu); + + for (tries = 0; tries < 25; tries++) { + cpu_status = query_cpu_stopped(pcpu); + if (cpu_status == 0 || cpu_status == -1) + break; + msleep(200); + } + if (cpu_status != 0) { + printk("Querying DEAD? cpu %i (%i) shows %i\n", + cpu, pcpu, cpu_status); + } + + /* Isolation and deallocation are definatly done by + * drslot_chrp_cpu. If they were not they would be + * done here. Change isolate state to Isolate and + * change allocation-state to Unusable. + */ + paca[cpu].cpu_start = 0; +} + +/* + * Update cpu_present_map and paca(s) for a new cpu node. The wrinkle + * here is that a cpu device node may represent up to two logical cpus + * in the SMT case. We must honor the assumption in other code that + * the logical ids for sibling SMT threads x and y are adjacent, such + * that x^1 == y and y^1 == x. + */ +static int pSeries_add_processor(struct device_node *np) +{ + unsigned int cpu; + cpumask_t candidate_map, tmp = CPU_MASK_NONE; + int err = -ENOSPC, len, nthreads, i; + const u32 *intserv; + + intserv = get_property(np, "ibm,ppc-interrupt-server#s", &len); + if (!intserv) + return 0; + + nthreads = len / sizeof(u32); + for (i = 0; i < nthreads; i++) + cpu_set(i, tmp); + + lock_cpu_hotplug(); + + BUG_ON(!cpus_subset(cpu_present_map, cpu_possible_map)); + + /* Get a bitmap of unoccupied slots. */ + cpus_xor(candidate_map, cpu_possible_map, cpu_present_map); + if (cpus_empty(candidate_map)) { + /* If we get here, it most likely means that NR_CPUS is + * less than the partition's max processors setting. + */ + printk(KERN_ERR "Cannot add cpu %s; this system configuration" + " supports %d logical cpus.\n", np->full_name, + cpus_weight(cpu_possible_map)); + goto out_unlock; + } + + while (!cpus_empty(tmp)) + if (cpus_subset(tmp, candidate_map)) + /* Found a range where we can insert the new cpu(s) */ + break; + else + cpus_shift_left(tmp, tmp, nthreads); + + if (cpus_empty(tmp)) { + printk(KERN_ERR "Unable to find space in cpu_present_map for" + " processor %s with %d thread(s)\n", np->name, + nthreads); + goto out_unlock; + } + + for_each_cpu_mask(cpu, tmp) { + BUG_ON(cpu_isset(cpu, cpu_present_map)); + cpu_set(cpu, cpu_present_map); + set_hard_smp_processor_id(cpu, *intserv++); + } + err = 0; +out_unlock: + unlock_cpu_hotplug(); + return err; +} + +/* + * Update the present map for a cpu node which is going away, and set + * the hard id in the paca(s) to -1 to be consistent with boot time + * convention for non-present cpus. + */ +static void pSeries_remove_processor(struct device_node *np) +{ + unsigned int cpu; + int len, nthreads, i; + const u32 *intserv; + + intserv = get_property(np, "ibm,ppc-interrupt-server#s", &len); + if (!intserv) + return; + + nthreads = len / sizeof(u32); + + lock_cpu_hotplug(); + for (i = 0; i < nthreads; i++) { + for_each_present_cpu(cpu) { + if (get_hard_smp_processor_id(cpu) != intserv[i]) + continue; + BUG_ON(cpu_online(cpu)); + cpu_clear(cpu, cpu_present_map); + set_hard_smp_processor_id(cpu, -1); + break; + } + if (cpu == NR_CPUS) + printk(KERN_WARNING "Could not find cpu to remove " + "with physical id 0x%x\n", intserv[i]); + } + unlock_cpu_hotplug(); +} + +static int pSeries_smp_notifier(struct notifier_block *nb, unsigned long action, void *node) +{ + int err = NOTIFY_OK; + + switch (action) { + case PSERIES_RECONFIG_ADD: + if (pSeries_add_processor(node)) + err = NOTIFY_BAD; + break; + case PSERIES_RECONFIG_REMOVE: + pSeries_remove_processor(node); + break; + default: + err = NOTIFY_DONE; + break; + } + return err; +} + +static struct notifier_block pSeries_smp_nb = { + .notifier_call = pSeries_smp_notifier, +}; + static int __init pseries_cpu_hotplug_init(void) { rtas_stop_self_args.token = rtas_token("stop-self"); ppc_md.cpu_die = pSeries_mach_cpu_die; + smp_ops->cpu_disable = pSeries_cpu_disable; + smp_ops->cpu_die = pSeries_cpu_die; + + /* Processors can be added/removed only on LPAR */ + if (firmware_has_feature(FW_FEATURE_LPAR)) + pSeries_reconfig_notifier_register(&pSeries_smp_nb); + return 0; } arch_initcall(pseries_cpu_hotplug_init); diff --git a/arch/powerpc/platforms/pseries/smp.c b/arch/powerpc/platforms/pseries/smp.c index c6624b8a0e77..4408518eaebe 100644 --- a/arch/powerpc/platforms/pseries/smp.c +++ b/arch/powerpc/platforms/pseries/smp.c @@ -64,197 +64,6 @@ static cpumask_t of_spin_map; extern void generic_secondary_smp_init(unsigned long); -#ifdef CONFIG_HOTPLUG_CPU - -/* Get state of physical CPU. - * Return codes: - * 0 - The processor is in the RTAS stopped state - * 1 - stop-self is in progress - * 2 - The processor is not in the RTAS stopped state - * -1 - Hardware Error - * -2 - Hardware Busy, Try again later. - */ -static int query_cpu_stopped(unsigned int pcpu) -{ - int cpu_status; - int status, qcss_tok; - - qcss_tok = rtas_token("query-cpu-stopped-state"); - if (qcss_tok == RTAS_UNKNOWN_SERVICE) - return -1; - status = rtas_call(qcss_tok, 1, 2, &cpu_status, pcpu); - if (status != 0) { - printk(KERN_ERR - "RTAS query-cpu-stopped-state failed: %i\n", status); - return status; - } - - return cpu_status; -} - -static int pSeries_cpu_disable(void) -{ - int cpu = smp_processor_id(); - - cpu_clear(cpu, cpu_online_map); - vdso_data->processorCount--; - - /*fix boot_cpuid here*/ - if (cpu == boot_cpuid) - boot_cpuid = any_online_cpu(cpu_online_map); - - /* FIXME: abstract this to not be platform specific later on */ - xics_migrate_irqs_away(); - return 0; -} - -static void pSeries_cpu_die(unsigned int cpu) -{ - int tries; - int cpu_status; - unsigned int pcpu = get_hard_smp_processor_id(cpu); - - for (tries = 0; tries < 25; tries++) { - cpu_status = query_cpu_stopped(pcpu); - if (cpu_status == 0 || cpu_status == -1) - break; - msleep(200); - } - if (cpu_status != 0) { - printk("Querying DEAD? cpu %i (%i) shows %i\n", - cpu, pcpu, cpu_status); - } - - /* Isolation and deallocation are definatly done by - * drslot_chrp_cpu. If they were not they would be - * done here. Change isolate state to Isolate and - * change allocation-state to Unusable. - */ - paca[cpu].cpu_start = 0; -} - -/* - * Update cpu_present_map and paca(s) for a new cpu node. The wrinkle - * here is that a cpu device node may represent up to two logical cpus - * in the SMT case. We must honor the assumption in other code that - * the logical ids for sibling SMT threads x and y are adjacent, such - * that x^1 == y and y^1 == x. - */ -static int pSeries_add_processor(struct device_node *np) -{ - unsigned int cpu; - cpumask_t candidate_map, tmp = CPU_MASK_NONE; - int err = -ENOSPC, len, nthreads, i; - const u32 *intserv; - - intserv = get_property(np, "ibm,ppc-interrupt-server#s", &len); - if (!intserv) - return 0; - - nthreads = len / sizeof(u32); - for (i = 0; i < nthreads; i++) - cpu_set(i, tmp); - - lock_cpu_hotplug(); - - BUG_ON(!cpus_subset(cpu_present_map, cpu_possible_map)); - - /* Get a bitmap of unoccupied slots. */ - cpus_xor(candidate_map, cpu_possible_map, cpu_present_map); - if (cpus_empty(candidate_map)) { - /* If we get here, it most likely means that NR_CPUS is - * less than the partition's max processors setting. - */ - printk(KERN_ERR "Cannot add cpu %s; this system configuration" - " supports %d logical cpus.\n", np->full_name, - cpus_weight(cpu_possible_map)); - goto out_unlock; - } - - while (!cpus_empty(tmp)) - if (cpus_subset(tmp, candidate_map)) - /* Found a range where we can insert the new cpu(s) */ - break; - else - cpus_shift_left(tmp, tmp, nthreads); - - if (cpus_empty(tmp)) { - printk(KERN_ERR "Unable to find space in cpu_present_map for" - " processor %s with %d thread(s)\n", np->name, - nthreads); - goto out_unlock; - } - - for_each_cpu_mask(cpu, tmp) { - BUG_ON(cpu_isset(cpu, cpu_present_map)); - cpu_set(cpu, cpu_present_map); - set_hard_smp_processor_id(cpu, *intserv++); - } - err = 0; -out_unlock: - unlock_cpu_hotplug(); - return err; -} - -/* - * Update the present map for a cpu node which is going away, and set - * the hard id in the paca(s) to -1 to be consistent with boot time - * convention for non-present cpus. - */ -static void pSeries_remove_processor(struct device_node *np) -{ - unsigned int cpu; - int len, nthreads, i; - const u32 *intserv; - - intserv = get_property(np, "ibm,ppc-interrupt-server#s", &len); - if (!intserv) - return; - - nthreads = len / sizeof(u32); - - lock_cpu_hotplug(); - for (i = 0; i < nthreads; i++) { - for_each_present_cpu(cpu) { - if (get_hard_smp_processor_id(cpu) != intserv[i]) - continue; - BUG_ON(cpu_online(cpu)); - cpu_clear(cpu, cpu_present_map); - set_hard_smp_processor_id(cpu, -1); - break; - } - if (cpu == NR_CPUS) - printk(KERN_WARNING "Could not find cpu to remove " - "with physical id 0x%x\n", intserv[i]); - } - unlock_cpu_hotplug(); -} - -static int pSeries_smp_notifier(struct notifier_block *nb, unsigned long action, void *node) -{ - int err = NOTIFY_OK; - - switch (action) { - case PSERIES_RECONFIG_ADD: - if (pSeries_add_processor(node)) - err = NOTIFY_BAD; - break; - case PSERIES_RECONFIG_REMOVE: - pSeries_remove_processor(node); - break; - default: - err = NOTIFY_DONE; - break; - } - return err; -} - -static struct notifier_block pSeries_smp_nb = { - .notifier_call = pSeries_smp_notifier, -}; - -#endif /* CONFIG_HOTPLUG_CPU */ - /** * smp_startup_cpu() - start the given cpu * @@ -422,15 +231,6 @@ static void __init smp_init_pseries(void) DBG(" -> smp_init_pSeries()\n"); -#ifdef CONFIG_HOTPLUG_CPU - smp_ops->cpu_disable = pSeries_cpu_disable; - smp_ops->cpu_die = pSeries_cpu_die; - - /* Processors can be added/removed only on LPAR */ - if (firmware_has_feature(FW_FEATURE_LPAR)) - pSeries_reconfig_notifier_register(&pSeries_smp_nb); -#endif - /* Mark threads which are still spinning in hold loops. */ if (cpu_has_feature(CPU_FTR_SMT)) { for_each_present_cpu(i) { -- cgit v1.2.3 From 674fa677c01ad0b90237f5cddc8d68502fea5156 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Tue, 5 Dec 2006 17:52:38 +1100 Subject: [POWERPC] Only enable cpu hotplug via RTAS if the required firmware support is found To support cpu hotplug on pseries we require two RTAS tokens. The cpu hotplug machinery should only be wired up if these tokens are found in the device tree. Signed-off-by: Michael Ellerman Signed-off-by: Paul Mackerras --- arch/powerpc/platforms/pseries/hotplug-cpu.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'arch/powerpc/platforms') diff --git a/arch/powerpc/platforms/pseries/hotplug-cpu.c b/arch/powerpc/platforms/pseries/hotplug-cpu.c index 12864d75126d..e78e8ac95550 100644 --- a/arch/powerpc/platforms/pseries/hotplug-cpu.c +++ b/arch/powerpc/platforms/pseries/hotplug-cpu.c @@ -64,6 +64,8 @@ static void pSeries_mach_cpu_die(void) for(;;); } +static int qcss_tok; /* query-cpu-stopped-state token */ + /* Get state of physical CPU. * Return codes: * 0 - The processor is in the RTAS stopped state @@ -74,12 +76,8 @@ static void pSeries_mach_cpu_die(void) */ static int query_cpu_stopped(unsigned int pcpu) { - int cpu_status; - int status, qcss_tok; + int cpu_status, status; - qcss_tok = rtas_token("query-cpu-stopped-state"); - if (qcss_tok == RTAS_UNKNOWN_SERVICE) - return -1; status = rtas_call(qcss_tok, 1, 2, &cpu_status, pcpu); if (status != 0) { printk(KERN_ERR @@ -254,9 +252,16 @@ static struct notifier_block pSeries_smp_nb = { static int __init pseries_cpu_hotplug_init(void) { rtas_stop_self_args.token = rtas_token("stop-self"); + qcss_tok = rtas_token("query-cpu-stopped-state"); - ppc_md.cpu_die = pSeries_mach_cpu_die; + if (rtas_stop_self_args.token == RTAS_UNKNOWN_SERVICE || + qcss_tok == RTAS_UNKNOWN_SERVICE) { + printk(KERN_INFO "CPU Hotplug not supported by firmware " + "- disabling.\n"); + return 0; + } + ppc_md.cpu_die = pSeries_mach_cpu_die; smp_ops->cpu_disable = pSeries_cpu_disable; smp_ops->cpu_die = pSeries_cpu_die; -- cgit v1.2.3 From 06ba30b6bfb30c84b70fed681b8c920dbff5d5a4 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Tue, 5 Dec 2006 17:52:39 +1100 Subject: [POWERPC] Cleanup pass over platforms/pseries/hotplug-cpu.c Purely cosmetic. Change pSeries to pseries inline with other parts of the kernel, and fix an overly long line. Signed-off-by: Michael Ellerman Signed-off-by: Paul Mackerras --- arch/powerpc/platforms/pseries/hotplug-cpu.c | 29 ++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) (limited to 'arch/powerpc/platforms') diff --git a/arch/powerpc/platforms/pseries/hotplug-cpu.c b/arch/powerpc/platforms/pseries/hotplug-cpu.c index e78e8ac95550..f460b9cbfd46 100644 --- a/arch/powerpc/platforms/pseries/hotplug-cpu.c +++ b/arch/powerpc/platforms/pseries/hotplug-cpu.c @@ -53,7 +53,7 @@ static void rtas_stop_self(void) panic("Alas, I survived.\n"); } -static void pSeries_mach_cpu_die(void) +static void pseries_mach_cpu_die(void) { local_irq_disable(); idle_task_exit(); @@ -88,7 +88,7 @@ static int query_cpu_stopped(unsigned int pcpu) return cpu_status; } -static int pSeries_cpu_disable(void) +static int pseries_cpu_disable(void) { int cpu = smp_processor_id(); @@ -104,7 +104,7 @@ static int pSeries_cpu_disable(void) return 0; } -static void pSeries_cpu_die(unsigned int cpu) +static void pseries_cpu_die(unsigned int cpu) { int tries; int cpu_status; @@ -136,7 +136,7 @@ static void pSeries_cpu_die(unsigned int cpu) * the logical ids for sibling SMT threads x and y are adjacent, such * that x^1 == y and y^1 == x. */ -static int pSeries_add_processor(struct device_node *np) +static int pseries_add_processor(struct device_node *np) { unsigned int cpu; cpumask_t candidate_map, tmp = CPU_MASK_NONE; @@ -197,7 +197,7 @@ out_unlock: * the hard id in the paca(s) to -1 to be consistent with boot time * convention for non-present cpus. */ -static void pSeries_remove_processor(struct device_node *np) +static void pseries_remove_processor(struct device_node *np) { unsigned int cpu; int len, nthreads, i; @@ -226,17 +226,18 @@ static void pSeries_remove_processor(struct device_node *np) unlock_cpu_hotplug(); } -static int pSeries_smp_notifier(struct notifier_block *nb, unsigned long action, void *node) +static int pseries_smp_notifier(struct notifier_block *nb, + unsigned long action, void *node) { int err = NOTIFY_OK; switch (action) { case PSERIES_RECONFIG_ADD: - if (pSeries_add_processor(node)) + if (pseries_add_processor(node)) err = NOTIFY_BAD; break; case PSERIES_RECONFIG_REMOVE: - pSeries_remove_processor(node); + pseries_remove_processor(node); break; default: err = NOTIFY_DONE; @@ -245,8 +246,8 @@ static int pSeries_smp_notifier(struct notifier_block *nb, unsigned long action, return err; } -static struct notifier_block pSeries_smp_nb = { - .notifier_call = pSeries_smp_notifier, +static struct notifier_block pseries_smp_nb = { + .notifier_call = pseries_smp_notifier, }; static int __init pseries_cpu_hotplug_init(void) @@ -261,13 +262,13 @@ static int __init pseries_cpu_hotplug_init(void) return 0; } - ppc_md.cpu_die = pSeries_mach_cpu_die; - smp_ops->cpu_disable = pSeries_cpu_disable; - smp_ops->cpu_die = pSeries_cpu_die; + ppc_md.cpu_die = pseries_mach_cpu_die; + smp_ops->cpu_disable = pseries_cpu_disable; + smp_ops->cpu_die = pseries_cpu_die; /* Processors can be added/removed only on LPAR */ if (firmware_has_feature(FW_FEATURE_LPAR)) - pSeries_reconfig_notifier_register(&pSeries_smp_nb); + pSeries_reconfig_notifier_register(&pseries_smp_nb); return 0; } -- cgit v1.2.3 From d0e70341c05f6c31375530e0ae29b319153004a7 Mon Sep 17 00:00:00 2001 From: Linas Vepstas Date: Wed, 6 Dec 2006 12:32:20 -0600 Subject: [POWERPC] EEH recovery tweaks If one attempts to create a device driver recovery sequence that does not depend on a hard reset of the device, but simply just attempts to resume processing, then one discovers that the recovery sequence implemented on powerpc is not quite right. This patch fixes this up. Signed-off-by: Linas Vepstas Signed-off-by: Paul Mackerras --- arch/powerpc/platforms/pseries/eeh.c | 1 + arch/powerpc/platforms/pseries/eeh_driver.c | 13 ++++++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) (limited to 'arch/powerpc/platforms') diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c index 3c2d63ebf787..da6e5362e7cd 100644 --- a/arch/powerpc/platforms/pseries/eeh.c +++ b/arch/powerpc/platforms/pseries/eeh.c @@ -337,6 +337,7 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev) printk (KERN_ERR "EEH: Device driver ignored %d bad reads, panicing\n", pdn->eeh_check_count); dump_stack(); + msleep(5000); /* re-read the slot reset state */ if (read_slot_reset_state(pdn, rets) != 0) diff --git a/arch/powerpc/platforms/pseries/eeh_driver.c b/arch/powerpc/platforms/pseries/eeh_driver.c index c2bc9904f1cb..cbd6b0711ab4 100644 --- a/arch/powerpc/platforms/pseries/eeh_driver.c +++ b/arch/powerpc/platforms/pseries/eeh_driver.c @@ -170,14 +170,19 @@ static void eeh_report_reset(struct pci_dev *dev, void *userdata) static void eeh_report_resume(struct pci_dev *dev, void *userdata) { struct pci_driver *driver = dev->driver; + struct device_node *dn = pci_device_to_OF_node(dev); dev->error_state = pci_channel_io_normal; if (!driver) return; - if (!driver->err_handler) - return; - if (!driver->err_handler->resume) + + if ((PCI_DN(dn)->eeh_mode) & EEH_MODE_IRQ_DISABLED) { + PCI_DN(dn)->eeh_mode &= ~EEH_MODE_IRQ_DISABLED; + enable_irq(dev->irq); + } + if (!driver->err_handler || + !driver->err_handler->resume) return; driver->err_handler->resume(dev); @@ -407,6 +412,8 @@ struct pci_dn * handle_eeh_events (struct eeh_event *event) if (rc) result = PCI_ERS_RESULT_NEED_RESET; + else + result = PCI_ERS_RESULT_RECOVERED; } /* If any device has a hard failure, then shut off everything. */ -- cgit v1.2.3 From 17877116c6b0fa78501526e7ec03cabe967a3a72 Mon Sep 17 00:00:00 2001 From: Nathan Lynch Date: Wed, 6 Dec 2006 18:50:43 -0600 Subject: [POWERPC] maple: Match "pcie" name for CPC945 Some firmwares have "pcie" for the "name" property of the CPC945 PCI Express host bridge. Check for "pcie" in addition to "pci" so we don't miss it. Signed-off-by: Nathan Lynch Signed-off-by: Paul Mackerras --- arch/powerpc/platforms/maple/pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arch/powerpc/platforms') diff --git a/arch/powerpc/platforms/maple/pci.c b/arch/powerpc/platforms/maple/pci.c index 3a32deda765d..3f6a69f67195 100644 --- a/arch/powerpc/platforms/maple/pci.c +++ b/arch/powerpc/platforms/maple/pci.c @@ -562,7 +562,7 @@ void __init maple_pci_init(void) for (np = NULL; (np = of_get_next_child(root, np)) != NULL;) { if (np->name == NULL) continue; - if (strcmp(np->name, "pci") == 0) { + if (!strcmp(np->name, "pci") || !strcmp(np->name, "pcie")) { if (add_bridge(np) == 0) of_node_get(np); } -- cgit v1.2.3 From 9e254c45fb0385b411ad93960f3838de80895210 Mon Sep 17 00:00:00 2001 From: Nathan Lynch Date: Wed, 6 Dec 2006 18:50:46 -0600 Subject: [POWERPC] maple: Use RTAS for reboot and halt On maple, use the RTAS "system-reboot" and "power-off" methods if they are available. Signed-off-by: Nathan Lynch Signed-off-by: Paul Mackerras --- arch/powerpc/platforms/maple/setup.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'arch/powerpc/platforms') diff --git a/arch/powerpc/platforms/maple/setup.c b/arch/powerpc/platforms/maple/setup.c index 094989d50bab..f12d5c69e74d 100644 --- a/arch/powerpc/platforms/maple/setup.c +++ b/arch/powerpc/platforms/maple/setup.c @@ -60,6 +60,7 @@ #include #include #include +#include #include #include "maple.h" @@ -166,6 +167,16 @@ struct smp_ops_t maple_smp_ops = { }; #endif /* CONFIG_SMP */ +static void __init maple_use_rtas_reboot_and_halt_if_present(void) +{ + if (rtas_service_present("system-reboot") && + rtas_service_present("power-off")) { + ppc_md.restart = rtas_restart; + ppc_md.power_off = rtas_power_off; + ppc_md.halt = rtas_halt; + } +} + void __init maple_setup_arch(void) { /* init to some ~sane value until calibrate_delay() runs */ @@ -181,6 +192,7 @@ void __init maple_setup_arch(void) #ifdef CONFIG_DUMMY_CONSOLE conswitchp = &dummy_con; #endif + maple_use_rtas_reboot_and_halt_if_present(); printk(KERN_DEBUG "Using native/NAP idle loop\n"); } -- cgit v1.2.3 From 18414ec0b56783f625edb95a9499bcb0ef12d6b8 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Thu, 7 Dec 2006 11:07:32 -0600 Subject: [POWERPC] Remove QE header files from lite5200.c The MPC 5200 does not have a QUICCEngine (QE), so lite5200.c should not include the QE header files. Signed-off-by: Timur Tabi Acked-by: Grant Likely Signed-off-by: Paul Mackerras --- arch/powerpc/platforms/52xx/lite5200.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'arch/powerpc/platforms') diff --git a/arch/powerpc/platforms/52xx/lite5200.c b/arch/powerpc/platforms/52xx/lite5200.c index a375c15b4315..eaff71e74fb0 100644 --- a/arch/powerpc/platforms/52xx/lite5200.c +++ b/arch/powerpc/platforms/52xx/lite5200.c @@ -40,8 +40,6 @@ #include #include #include -#include -#include #include #include -- cgit v1.2.3 From 22b6e590478ae8757f0411cf16a24c25d8dfea86 Mon Sep 17 00:00:00 2001 From: Christian Krafft Date: Thu, 7 Dec 2006 19:01:16 +0100 Subject: [POWERPC] cbe_thermal: Fix initialization of sysfs attribute_group This patch adds NULL to the initialization of the attribute_groups. The spu_attributes and ppe_attributes arrays are arrays of pointers that need to be terminated with a NULL entry. Signed-off-by: Christian Krafft Signed-off-by: Paul Mackerras --- arch/powerpc/platforms/cell/cbe_thermal.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'arch/powerpc/platforms') diff --git a/arch/powerpc/platforms/cell/cbe_thermal.c b/arch/powerpc/platforms/cell/cbe_thermal.c index 616a0a3fd0e2..70e0d968d30f 100644 --- a/arch/powerpc/platforms/cell/cbe_thermal.c +++ b/arch/powerpc/platforms/cell/cbe_thermal.c @@ -115,6 +115,7 @@ static struct sysdev_attribute attr_spu_temperature = { static struct attribute *spu_attributes[] = { &attr_spu_temperature.attr, + NULL, }; static struct attribute_group spu_attribute_group = { @@ -135,6 +136,7 @@ static struct sysdev_attribute attr_ppe_temperature1 = { static struct attribute *ppe_attributes[] = { &attr_ppe_temperature0.attr, &attr_ppe_temperature1.attr, + NULL, }; static struct attribute_group ppe_attribute_group = { -- cgit v1.2.3 From 5773bbcdec54b7258cb9e2aa6f3459b4cbfd9dc5 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Fri, 8 Dec 2006 18:08:37 +1100 Subject: [POWERPC] micro optimise pSeries_probe We find the OF root the line before, we may as well use it. Signed-off-by: Anton Blanchard Signed-off-by: Paul Mackerras --- arch/powerpc/platforms/pseries/setup.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'arch/powerpc/platforms') diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c index 6090d753c44c..3e2f7467057d 100644 --- a/arch/powerpc/platforms/pseries/setup.c +++ b/arch/powerpc/platforms/pseries/setup.c @@ -433,8 +433,8 @@ static int __init pSeries_probe_hypertas(unsigned long node, static int __init pSeries_probe(void) { unsigned long root = of_get_flat_dt_root(); - char *dtype = of_get_flat_dt_prop(of_get_flat_dt_root(), - "device_type", NULL); + char *dtype = of_get_flat_dt_prop(root, "device_type", NULL); + if (dtype == NULL) return 0; if (strcmp(dtype, "chrp")) -- cgit v1.2.3 From a223535425eb28082a0925b0ce2f02f962936cf4 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Fri, 8 Dec 2006 18:22:09 +1100 Subject: [POWERPC] dont allow pSeries_probe to succeed without initialising MMU pSeries_probe can decide that we are a pseries but then fail to initialise the MMU. If an rtas node doesnt exist, we continually fall out of pSeries_probe_hypertas early and never get to the MMU init code. While pseries without RTAS is an illegal combination, the way we currently fail is a pain to track down, and can happen if your flattened device tree code has issues (like mine did :). With the following patch we init the MMU, come up and print some warnings about RTAS not existing, instead of looping on 0x400 exceptions. Signed-off-by: Anton Blanchard Signed-off-by: Paul Mackerras --- arch/powerpc/platforms/pseries/setup.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'arch/powerpc/platforms') diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c index 3e2f7467057d..042ecae107ac 100644 --- a/arch/powerpc/platforms/pseries/setup.c +++ b/arch/powerpc/platforms/pseries/setup.c @@ -422,11 +422,6 @@ static int __init pSeries_probe_hypertas(unsigned long node, if (of_get_flat_dt_prop(node, "ibm,hypertas-functions", NULL) != NULL) powerpc_firmware_features |= FW_FEATURE_LPAR; - if (firmware_has_feature(FW_FEATURE_LPAR)) - hpte_init_lpar(); - else - hpte_init_native(); - return 1; } @@ -452,6 +447,11 @@ static int __init pSeries_probe(void) /* Now try to figure out if we are running on LPAR */ of_scan_flat_dt(pSeries_probe_hypertas, NULL); + if (firmware_has_feature(FW_FEATURE_LPAR)) + hpte_init_lpar(); + else + hpte_init_native(); + DBG("Machine is%s LPAR !\n", (powerpc_firmware_features & FW_FEATURE_LPAR) ? "" : " not"); -- cgit v1.2.3 From 74e95d5de9d8eb243cda68b546bdb29f6ef0f01c Mon Sep 17 00:00:00 2001 From: Geoff Levand Date: Fri, 8 Dec 2006 18:27:47 -0800 Subject: [POWERPC] ps3: Add vuart support Adds support for the PS3 virtual UART (vuart). The vuart provides a bi-directional byte stream data link between logical partitions. This is needed for the ps3 graphics driver and the ps3 power control support to be able to communicate with the lv1 policy module. Signed-off-by: Geoff Levand Acked-by: Arnd Bergmann Signed-off-by: Paul Mackerras --- arch/powerpc/configs/ps3_defconfig | 1 + arch/powerpc/platforms/ps3/Kconfig | 11 + drivers/ps3/Makefile | 1 + drivers/ps3/vuart.c | 965 +++++++++++++++++++++++++++++++++++++ drivers/ps3/vuart.h | 94 ++++ 5 files changed, 1072 insertions(+) create mode 100644 drivers/ps3/vuart.c create mode 100644 drivers/ps3/vuart.h (limited to 'arch/powerpc/platforms') diff --git a/arch/powerpc/configs/ps3_defconfig b/arch/powerpc/configs/ps3_defconfig index f2d888e014a9..70ed61337f5c 100644 --- a/arch/powerpc/configs/ps3_defconfig +++ b/arch/powerpc/configs/ps3_defconfig @@ -157,6 +157,7 @@ CONFIG_SPU_BASE=y CONFIG_PS3_HTAB_SIZE=20 CONFIG_PS3_DYNAMIC_DMA=y CONFIG_PS3_USE_LPAR_ADDR=y +CONFIG_PS3_VUART=y # # Kernel options diff --git a/arch/powerpc/platforms/ps3/Kconfig b/arch/powerpc/platforms/ps3/Kconfig index 451bfcd5502e..de52ec4e9e58 100644 --- a/arch/powerpc/platforms/ps3/Kconfig +++ b/arch/powerpc/platforms/ps3/Kconfig @@ -40,4 +40,15 @@ config PS3_USE_LPAR_ADDR If you have any doubt, choose the default y. +config PS3_VUART + depends on PPC_PS3 + bool "PS3 Virtual UART support" + default y + help + Include support for the PS3 Virtual UART. + + This support is required for several system services + including the System Manager and AV Settings. In + general, all users will say Y. + endmenu diff --git a/drivers/ps3/Makefile b/drivers/ps3/Makefile index b52d547b7a78..8433eb7562cb 100644 --- a/drivers/ps3/Makefile +++ b/drivers/ps3/Makefile @@ -1 +1,2 @@ obj-y += system-bus.o +obj-$(CONFIG_PS3_VUART) += vuart.o diff --git a/drivers/ps3/vuart.c b/drivers/ps3/vuart.c new file mode 100644 index 000000000000..6974f65bcda5 --- /dev/null +++ b/drivers/ps3/vuart.c @@ -0,0 +1,965 @@ +/* + * PS3 virtual uart + * + * Copyright (C) 2006 Sony Computer Entertainment Inc. + * Copyright 2006 Sony Corp. + * + * 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; version 2 of the License. + * + * 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 + */ + +#include +#include +#include +#include + +#include +#include + +#include "vuart.h" + +MODULE_AUTHOR("Sony Corporation"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("ps3 vuart"); + +/** + * vuart - An inter-partition data link service. + * port 0: PS3 AV Settings. + * port 2: PS3 System Manager. + * + * The vuart provides a bi-directional byte stream data link between logical + * partitions. Its primary role is as a communications link between the guest + * OS and the system policy module. The current HV does not support any + * connections other than those listed. + */ + +enum {PORT_COUNT = 3,}; + +enum vuart_param { + PARAM_TX_TRIGGER = 0, + PARAM_RX_TRIGGER = 1, + PARAM_INTERRUPT_MASK = 2, + PARAM_RX_BUF_SIZE = 3, /* read only */ + PARAM_RX_BYTES = 4, /* read only */ + PARAM_TX_BUF_SIZE = 5, /* read only */ + PARAM_TX_BYTES = 6, /* read only */ + PARAM_INTERRUPT_STATUS = 7, /* read only */ +}; + +enum vuart_interrupt_bit { + INTERRUPT_BIT_TX = 0, + INTERRUPT_BIT_RX = 1, + INTERRUPT_BIT_DISCONNECT = 2, +}; + +enum vuart_interrupt_mask { + INTERRUPT_MASK_TX = 1, + INTERRUPT_MASK_RX = 2, + INTERRUPT_MASK_DISCONNECT = 4, +}; + +/** + * struct ports_bmp - bitmap indicating ports needing service. + * + * A 256 bit read only bitmap indicating ports needing service. Do not write + * to these bits. Must not cross a page boundary. + */ + +struct ports_bmp { + u64 status; + u64 unused[3]; +} __attribute__ ((aligned (32))); + +/* redefine dev_dbg to do a syntax check */ + +#if !defined(DEBUG) +#undef dev_dbg +static inline int __attribute__ ((format (printf, 2, 3))) dev_dbg( + const struct device *_dev, const char *fmt, ...) {return 0;} +#endif + +#define dump_ports_bmp(_b) _dump_ports_bmp(_b, __func__, __LINE__) +static void __attribute__ ((unused)) _dump_ports_bmp( + const struct ports_bmp* bmp, const char* func, int line) +{ + pr_debug("%s:%d: ports_bmp: %016lxh\n", func, line, bmp->status); +} + +static int ps3_vuart_match_id_to_port(enum ps3_match_id match_id, + unsigned int *port_number) +{ + switch(match_id) { + case PS3_MATCH_ID_AV_SETTINGS: + *port_number = 0; + return 0; + case PS3_MATCH_ID_SYSTEM_MANAGER: + *port_number = 2; + return 0; + default: + WARN_ON(1); + *port_number = UINT_MAX; + return -EINVAL; + }; +} + +#define dump_port_params(_b) _dump_port_params(_b, __func__, __LINE__) +static void __attribute__ ((unused)) _dump_port_params(unsigned int port_number, + const char* func, int line) +{ +#if defined(DEBUG) + static const char *strings[] = { + "tx_trigger ", + "rx_trigger ", + "interrupt_mask ", + "rx_buf_size ", + "rx_bytes ", + "tx_buf_size ", + "tx_bytes ", + "interrupt_status", + }; + int result; + unsigned int i; + u64 value; + + for (i = 0; i < ARRAY_SIZE(strings); i++) { + result = lv1_get_virtual_uart_param(port_number, i, &value); + + if (result) { + pr_debug("%s:%d: port_%u: %s failed: %s\n", func, line, + port_number, strings[i], ps3_result(result)); + continue; + } + pr_debug("%s:%d: port_%u: %s = %lxh\n", + func, line, port_number, strings[i], value); + } +#endif +} + +struct vuart_triggers { + unsigned long rx; + unsigned long tx; +}; + +int ps3_vuart_get_triggers(struct ps3_vuart_port_device *dev, + struct vuart_triggers *trig) +{ + int result; + unsigned long size; + unsigned long val; + + result = lv1_get_virtual_uart_param(dev->port_number, + PARAM_TX_TRIGGER, &trig->tx); + + if (result) { + dev_dbg(&dev->core, "%s:%d: tx_trigger failed: %s\n", + __func__, __LINE__, ps3_result(result)); + return result; + } + + result = lv1_get_virtual_uart_param(dev->port_number, + PARAM_RX_BUF_SIZE, &size); + + if (result) { + dev_dbg(&dev->core, "%s:%d: tx_buf_size failed: %s\n", + __func__, __LINE__, ps3_result(result)); + return result; + } + + result = lv1_get_virtual_uart_param(dev->port_number, + PARAM_RX_TRIGGER, &val); + + if (result) { + dev_dbg(&dev->core, "%s:%d: rx_trigger failed: %s\n", + __func__, __LINE__, ps3_result(result)); + return result; + } + + trig->rx = size - val; + + dev_dbg(&dev->core, "%s:%d: tx %lxh, rx %lxh\n", __func__, __LINE__, + trig->tx, trig->rx); + + return result; +} + +int ps3_vuart_set_triggers(struct ps3_vuart_port_device *dev, unsigned int tx, + unsigned int rx) +{ + int result; + unsigned long size; + + result = lv1_set_virtual_uart_param(dev->port_number, + PARAM_TX_TRIGGER, tx); + + if (result) { + dev_dbg(&dev->core, "%s:%d: tx_trigger failed: %s\n", + __func__, __LINE__, ps3_result(result)); + return result; + } + + result = lv1_get_virtual_uart_param(dev->port_number, + PARAM_RX_BUF_SIZE, &size); + + if (result) { + dev_dbg(&dev->core, "%s:%d: tx_buf_size failed: %s\n", + __func__, __LINE__, ps3_result(result)); + return result; + } + + result = lv1_set_virtual_uart_param(dev->port_number, + PARAM_RX_TRIGGER, size - rx); + + if (result) { + dev_dbg(&dev->core, "%s:%d: rx_trigger failed: %s\n", + __func__, __LINE__, ps3_result(result)); + return result; + } + + dev_dbg(&dev->core, "%s:%d: tx %xh, rx %xh\n", __func__, __LINE__, + tx, rx); + + return result; +} + +static int ps3_vuart_get_rx_bytes_waiting(struct ps3_vuart_port_device *dev, + unsigned long *bytes_waiting) +{ + int result = lv1_get_virtual_uart_param(dev->port_number, + PARAM_RX_BYTES, bytes_waiting); + + if (result) + dev_dbg(&dev->core, "%s:%d: rx_bytes failed: %s\n", + __func__, __LINE__, ps3_result(result)); + + dev_dbg(&dev->core, "%s:%d: %lxh\n", __func__, __LINE__, + *bytes_waiting); + return result; +} + +static int ps3_vuart_set_interrupt_mask(struct ps3_vuart_port_device *dev, + unsigned long mask) +{ + int result; + + dev_dbg(&dev->core, "%s:%d: %lxh\n", __func__, __LINE__, mask); + + dev->interrupt_mask = mask; + + result = lv1_set_virtual_uart_param(dev->port_number, + PARAM_INTERRUPT_MASK, dev->interrupt_mask); + + if (result) + dev_dbg(&dev->core, "%s:%d: interrupt_mask failed: %s\n", + __func__, __LINE__, ps3_result(result)); + + return result; +} + +static int ps3_vuart_get_interrupt_mask(struct ps3_vuart_port_device *dev, + unsigned long *status) +{ + int result = lv1_get_virtual_uart_param(dev->port_number, + PARAM_INTERRUPT_STATUS, status); + + if (result) + dev_dbg(&dev->core, "%s:%d: interrupt_status failed: %s\n", + __func__, __LINE__, ps3_result(result)); + + dev_dbg(&dev->core, "%s:%d: m %lxh, s %lxh, m&s %lxh\n", + __func__, __LINE__, dev->interrupt_mask, *status, + dev->interrupt_mask & *status); + + return result; +} + +int ps3_vuart_enable_interrupt_tx(struct ps3_vuart_port_device *dev) +{ + return (dev->interrupt_mask & INTERRUPT_MASK_TX) ? 0 + : ps3_vuart_set_interrupt_mask(dev, dev->interrupt_mask + | INTERRUPT_MASK_TX); +} + +int ps3_vuart_enable_interrupt_rx(struct ps3_vuart_port_device *dev) +{ + return (dev->interrupt_mask & INTERRUPT_MASK_RX) ? 0 + : ps3_vuart_set_interrupt_mask(dev, dev->interrupt_mask + | INTERRUPT_MASK_RX); +} + +int ps3_vuart_enable_interrupt_disconnect(struct ps3_vuart_port_device *dev) +{ + return (dev->interrupt_mask & INTERRUPT_MASK_DISCONNECT) ? 0 + : ps3_vuart_set_interrupt_mask(dev, dev->interrupt_mask + | INTERRUPT_MASK_DISCONNECT); +} + +int ps3_vuart_disable_interrupt_tx(struct ps3_vuart_port_device *dev) +{ + return (dev->interrupt_mask & INTERRUPT_MASK_TX) + ? ps3_vuart_set_interrupt_mask(dev, dev->interrupt_mask + & ~INTERRUPT_MASK_TX) : 0; +} + +int ps3_vuart_disable_interrupt_rx(struct ps3_vuart_port_device *dev) +{ + return (dev->interrupt_mask & INTERRUPT_MASK_RX) + ? ps3_vuart_set_interrupt_mask(dev, dev->interrupt_mask + & ~INTERRUPT_MASK_RX) : 0; +} + +int ps3_vuart_disable_interrupt_disconnect(struct ps3_vuart_port_device *dev) +{ + return (dev->interrupt_mask & INTERRUPT_MASK_DISCONNECT) + ? ps3_vuart_set_interrupt_mask(dev, dev->interrupt_mask + & ~INTERRUPT_MASK_DISCONNECT) : 0; +} + +/** + * ps3_vuart_raw_write - Low level write helper. + * + * Do not call ps3_vuart_raw_write directly, use ps3_vuart_write. + */ + +static int ps3_vuart_raw_write(struct ps3_vuart_port_device *dev, + const void* buf, unsigned int bytes, unsigned long *bytes_written) +{ + int result; + + dev_dbg(&dev->core, "%s:%d: %xh\n", __func__, __LINE__, bytes); + + result = lv1_write_virtual_uart(dev->port_number, + ps3_mm_phys_to_lpar(__pa(buf)), bytes, bytes_written); + + if (result) { + dev_dbg(&dev->core, "%s:%d: lv1_write_virtual_uart failed: " + "%s\n", __func__, __LINE__, ps3_result(result)); + return result; + } + + dev->stats.bytes_written += *bytes_written; + + dev_dbg(&dev->core, "%s:%d: wrote %lxh/%xh=>%lxh\n", __func__, + __LINE__, *bytes_written, bytes, dev->stats.bytes_written); + + return result; +} + +/** + * ps3_vuart_raw_read - Low level read helper. + * + * Do not call ps3_vuart_raw_read directly, use ps3_vuart_read. + */ + +static int ps3_vuart_raw_read(struct ps3_vuart_port_device *dev, void* buf, + unsigned int bytes, unsigned long *bytes_read) +{ + int result; + + dev_dbg(&dev->core, "%s:%d: %xh\n", __func__, __LINE__, bytes); + + result = lv1_read_virtual_uart(dev->port_number, + ps3_mm_phys_to_lpar(__pa(buf)), bytes, bytes_read); + + if (result) { + dev_dbg(&dev->core, "%s:%d: lv1_read_virtual_uart failed: %s\n", + __func__, __LINE__, ps3_result(result)); + return result; + } + + dev->stats.bytes_read += *bytes_read; + + dev_dbg(&dev->core, "%s:%d: read %lxh/%xh=>%lxh\n", __func__, __LINE__, + *bytes_read, bytes, dev->stats.bytes_read); + + return result; +} + +/** + * struct list_buffer - An element for a port device fifo buffer list. + */ + +struct list_buffer { + struct list_head link; + const unsigned char *head; + const unsigned char *tail; + unsigned long dbg_number; + unsigned char data[]; +}; + +/** + * ps3_vuart_write - the entry point for writing data to a port + * + * If the port is idle on entry as much of the incoming data is written to + * the port as the port will accept. Otherwise a list buffer is created + * and any remaning incoming data is copied to that buffer. The buffer is + * then enqueued for transmision via the transmit interrupt. + */ + +int ps3_vuart_write(struct ps3_vuart_port_device *dev, const void* buf, + unsigned int bytes) +{ + static unsigned long dbg_number; + int result; + unsigned long flags; + struct list_buffer *lb; + + dev_dbg(&dev->core, "%s:%d: %u(%xh) bytes\n", __func__, __LINE__, + bytes, bytes); + + spin_lock_irqsave(&dev->tx_list.lock, flags); + + if (list_empty(&dev->tx_list.head)) { + unsigned long bytes_written; + + result = ps3_vuart_raw_write(dev, buf, bytes, &bytes_written); + + spin_unlock_irqrestore(&dev->tx_list.lock, flags); + + if (result) { + dev_dbg(&dev->core, + "%s:%d: ps3_vuart_raw_write failed\n", + __func__, __LINE__); + return result; + } + + if (bytes_written == bytes) { + dev_dbg(&dev->core, "%s:%d: wrote %xh bytes\n", + __func__, __LINE__, bytes); + return 0; + } + + bytes -= bytes_written; + buf += bytes_written; + } else + spin_unlock_irqrestore(&dev->tx_list.lock, flags); + + lb = kmalloc(sizeof(struct list_buffer) + bytes, GFP_KERNEL); + + if (!lb) { + return -ENOMEM; + } + + memcpy(lb->data, buf, bytes); + lb->head = lb->data; + lb->tail = lb->data + bytes; + lb->dbg_number = ++dbg_number; + + spin_lock_irqsave(&dev->tx_list.lock, flags); + list_add_tail(&lb->link, &dev->tx_list.head); + ps3_vuart_enable_interrupt_tx(dev); + spin_unlock_irqrestore(&dev->tx_list.lock, flags); + + dev_dbg(&dev->core, "%s:%d: queued buf_%lu, %xh bytes\n", + __func__, __LINE__, lb->dbg_number, bytes); + + return 0; +} + +/** + * ps3_vuart_read - the entry point for reading data from a port + * + * If enough bytes to satisfy the request are held in the buffer list those + * bytes are dequeued and copied to the caller's buffer. Emptied list buffers + * are retiered. If the request cannot be statified by bytes held in the list + * buffers -EAGAIN is returned. + */ + +int ps3_vuart_read(struct ps3_vuart_port_device *dev, void* buf, + unsigned int bytes) +{ + unsigned long flags; + struct list_buffer *lb, *n; + unsigned long bytes_read; + + dev_dbg(&dev->core, "%s:%d: %u(%xh) bytes\n", __func__, __LINE__, + bytes, bytes); + + spin_lock_irqsave(&dev->rx_list.lock, flags); + + if (dev->rx_list.bytes_held < bytes) { + spin_unlock_irqrestore(&dev->rx_list.lock, flags); + dev_dbg(&dev->core, "%s:%d: starved for %lxh bytes\n", + __func__, __LINE__, bytes - dev->rx_list.bytes_held); + return -EAGAIN; + } + + list_for_each_entry_safe(lb, n, &dev->rx_list.head, link) { + bytes_read = min((unsigned int)(lb->tail - lb->head), bytes); + + memcpy(buf, lb->head, bytes_read); + buf += bytes_read; + bytes -= bytes_read; + dev->rx_list.bytes_held -= bytes_read; + + if (bytes_read < lb->tail - lb->head) { + lb->head += bytes_read; + spin_unlock_irqrestore(&dev->rx_list.lock, flags); + + dev_dbg(&dev->core, + "%s:%d: dequeued buf_%lu, %lxh bytes\n", + __func__, __LINE__, lb->dbg_number, bytes_read); + return 0; + } + + dev_dbg(&dev->core, "%s:%d free buf_%lu\n", __func__, __LINE__, + lb->dbg_number); + + list_del(&lb->link); + kfree(lb); + } + spin_unlock_irqrestore(&dev->rx_list.lock, flags); + + dev_dbg(&dev->core, "%s:%d: dequeued buf_%lu, %xh bytes\n", + __func__, __LINE__, lb->dbg_number, bytes); + + return 0; +} + +/** + * ps3_vuart_handle_interrupt_tx - third stage transmit interrupt handler + * + * Services the transmit interrupt for the port. Writes as much data from the + * buffer list as the port will accept. Retires any emptied list buffers and + * adjusts the final list buffer state for a partial write. + */ + +static int ps3_vuart_handle_interrupt_tx(struct ps3_vuart_port_device *dev) +{ + int result = 0; + unsigned long flags; + struct list_buffer *lb, *n; + unsigned long bytes_total = 0; + + dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); + + spin_lock_irqsave(&dev->tx_list.lock, flags); + + list_for_each_entry_safe(lb, n, &dev->tx_list.head, link) { + + unsigned long bytes_written; + + result = ps3_vuart_raw_write(dev, lb->head, lb->tail - lb->head, + &bytes_written); + + if (result) { + dev_dbg(&dev->core, + "%s:%d: ps3_vuart_raw_write failed\n", + __func__, __LINE__); + break; + } + + bytes_total += bytes_written; + + if (bytes_written < lb->tail - lb->head) { + lb->head += bytes_written; + dev_dbg(&dev->core, + "%s:%d cleared buf_%lu, %lxh bytes\n", + __func__, __LINE__, lb->dbg_number, + bytes_written); + goto port_full; + } + + dev_dbg(&dev->core, "%s:%d free buf_%lu\n", __func__, __LINE__, + lb->dbg_number); + + list_del(&lb->link); + kfree(lb); + } + + ps3_vuart_disable_interrupt_tx(dev); +port_full: + spin_unlock_irqrestore(&dev->tx_list.lock, flags); + dev_dbg(&dev->core, "%s:%d wrote %lxh bytes total\n", + __func__, __LINE__, bytes_total); + return result; +} + +/** + * ps3_vuart_handle_interrupt_rx - third stage receive interrupt handler + * + * Services the receive interrupt for the port. Creates a list buffer and + * copies all waiting port data to that buffer and enqueues the buffer in the + * buffer list. Buffer list data is dequeued via ps3_vuart_read. + */ + +static int ps3_vuart_handle_interrupt_rx(struct ps3_vuart_port_device *dev) +{ + static unsigned long dbg_number; + int result = 0; + unsigned long flags; + struct list_buffer *lb; + unsigned long bytes; + + dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); + + result = ps3_vuart_get_rx_bytes_waiting(dev, &bytes); + + if (result) + return -EIO; + + BUG_ON(!bytes); + + /* add some extra space for recently arrived data */ + + bytes += 128; + + lb = kmalloc(sizeof(struct list_buffer) + bytes, GFP_ATOMIC); + + if (!lb) + return -ENOMEM; + + ps3_vuart_raw_read(dev, lb->data, bytes, &bytes); + + lb->head = lb->data; + lb->tail = lb->data + bytes; + lb->dbg_number = ++dbg_number; + + spin_lock_irqsave(&dev->rx_list.lock, flags); + list_add_tail(&lb->link, &dev->rx_list.head); + dev->rx_list.bytes_held += bytes; + spin_unlock_irqrestore(&dev->rx_list.lock, flags); + + dev_dbg(&dev->core, "%s:%d: queued buf_%lu, %lxh bytes\n", + __func__, __LINE__, lb->dbg_number, bytes); + + return 0; +} + +static int ps3_vuart_handle_interrupt_disconnect( + struct ps3_vuart_port_device *dev) +{ + dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); + BUG_ON("no support"); + return -1; +} + +/** + * ps3_vuart_handle_port_interrupt - second stage interrupt handler + * + * Services any pending interrupt types for the port. Passes control to the + * third stage type specific interrupt handler. Returns control to the first + * stage handler after one iteration. + */ + +static int ps3_vuart_handle_port_interrupt(struct ps3_vuart_port_device *dev) +{ + int result; + unsigned long status; + + result = ps3_vuart_get_interrupt_mask(dev, &status); + + if (result) + return result; + + dev_dbg(&dev->core, "%s:%d: status: %lxh\n", __func__, __LINE__, + status); + + if (status & INTERRUPT_MASK_DISCONNECT) { + dev->stats.disconnect_interrupts++; + result = ps3_vuart_handle_interrupt_disconnect(dev); + if (result) + ps3_vuart_disable_interrupt_disconnect(dev); + } + + if (status & INTERRUPT_MASK_TX) { + dev->stats.tx_interrupts++; + result = ps3_vuart_handle_interrupt_tx(dev); + if (result) + ps3_vuart_disable_interrupt_tx(dev); + } + + if (status & INTERRUPT_MASK_RX) { + dev->stats.rx_interrupts++; + result = ps3_vuart_handle_interrupt_rx(dev); + if (result) + ps3_vuart_disable_interrupt_rx(dev); + } + + return 0; +} + +struct vuart_private { + unsigned int in_use; + unsigned int virq; + struct ps3_vuart_port_device *devices[PORT_COUNT]; + const struct ports_bmp bmp; +}; + +/** + * ps3_vuart_irq_handler - first stage interrupt handler + * + * Loops finding any interrupting port and its associated instance data. + * Passes control to the second stage port specific interrupt handler. Loops + * until all outstanding interrupts are serviced. + */ + +static irqreturn_t ps3_vuart_irq_handler(int irq, void *_private) +{ + struct vuart_private *private; + + BUG_ON(!_private); + private = (struct vuart_private *)_private; + + while (1) { + unsigned int port; + + dump_ports_bmp(&private->bmp); + + port = (BITS_PER_LONG - 1) - __ilog2(private->bmp.status); + + if (port == BITS_PER_LONG) + break; + + BUG_ON(port >= PORT_COUNT); + BUG_ON(!private->devices[port]); + + ps3_vuart_handle_port_interrupt(private->devices[port]); + } + + return IRQ_HANDLED; +} + +static int ps3_vuart_match(struct device *_dev, struct device_driver *_drv) +{ + int result; + struct ps3_vuart_port_driver *drv = to_ps3_vuart_port_driver(_drv); + struct ps3_vuart_port_device *dev = to_ps3_vuart_port_device(_dev); + + result = dev->match_id == drv->match_id; + + dev_info(&dev->core, "%s:%d: dev=%u(%s), drv=%u(%s): %s\n", __func__, + __LINE__, dev->match_id, dev->core.bus_id, drv->match_id, + drv->core.name, (result ? "match" : "miss")); + + return result; +} + +static struct vuart_private vuart_private; + +static int ps3_vuart_probe(struct device *_dev) +{ + int result; + unsigned long tmp; + struct ps3_vuart_port_device *dev = to_ps3_vuart_port_device(_dev); + struct ps3_vuart_port_driver *drv = + to_ps3_vuart_port_driver(_dev->driver); + + dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); + + BUG_ON(!drv); + + result = ps3_vuart_match_id_to_port(dev->match_id, &dev->port_number); + + if (result) { + dev_dbg(&dev->core, "%s:%d: unknown match_id (%d)\n", + __func__, __LINE__, dev->match_id); + result = -EINVAL; + goto fail_match; + } + + if (vuart_private.devices[dev->port_number]) { + dev_dbg(&dev->core, "%s:%d: port busy (%d)\n", __func__, + __LINE__, dev->port_number); + result = -EBUSY; + goto fail_match; + } + + vuart_private.devices[dev->port_number] = dev; + + INIT_LIST_HEAD(&dev->tx_list.head); + spin_lock_init(&dev->tx_list.lock); + INIT_LIST_HEAD(&dev->rx_list.head); + spin_lock_init(&dev->rx_list.lock); + + vuart_private.in_use++; + if (vuart_private.in_use == 1) { + result = ps3_alloc_vuart_irq((void*)&vuart_private.bmp.status, + &vuart_private.virq); + + if (result) { + dev_dbg(&dev->core, + "%s:%d: ps3_alloc_vuart_irq failed (%d)\n", + __func__, __LINE__, result); + result = -EPERM; + goto fail_alloc_irq; + } + + result = request_irq(vuart_private.virq, ps3_vuart_irq_handler, + IRQF_DISABLED, "vuart", &vuart_private); + + if (result) { + dev_info(&dev->core, "%s:%d: request_irq failed (%d)\n", + __func__, __LINE__, result); + goto fail_request_irq; + } + } + + ps3_vuart_set_interrupt_mask(dev, INTERRUPT_MASK_RX); + + /* clear stale pending interrupts */ + ps3_vuart_get_interrupt_mask(dev, &tmp); + + ps3_vuart_set_triggers(dev, 1, 1); + + if (drv->probe) + result = drv->probe(dev); + else { + result = 0; + dev_info(&dev->core, "%s:%d: no probe method\n", __func__, + __LINE__); + } + + if (result) { + dev_dbg(&dev->core, "%s:%d: drv->probe failed\n", + __func__, __LINE__); + goto fail_probe; + } + + return result; + +fail_probe: +fail_request_irq: + vuart_private.in_use--; + if (!vuart_private.in_use) { + ps3_free_vuart_irq(vuart_private.virq); + vuart_private.virq = NO_IRQ; + } +fail_alloc_irq: +fail_match: + dev_dbg(&dev->core, "%s:%d failed\n", __func__, __LINE__); + return result; +} + +static int ps3_vuart_remove(struct device *_dev) +{ + struct ps3_vuart_port_device *dev = to_ps3_vuart_port_device(_dev); + struct ps3_vuart_port_driver *drv = + to_ps3_vuart_port_driver(_dev->driver); + + dev_dbg(&dev->core, "%s:%d: %s\n", __func__, __LINE__, + dev->core.bus_id); + + BUG_ON(vuart_private.in_use < 1); + + if (drv->remove) + drv->remove(dev); + else + dev_dbg(&dev->core, "%s:%d: %s no remove method\n", __func__, + __LINE__, dev->core.bus_id); + + vuart_private.in_use--; + + if (!vuart_private.in_use) { + free_irq(vuart_private.virq, &vuart_private); + ps3_free_vuart_irq(vuart_private.virq); + vuart_private.virq = NO_IRQ; + } + return 0; +} + +/** + * ps3_vuart - The vuart instance. + * + * The vuart is managed as a bus that port devices connect to. + */ + +struct bus_type ps3_vuart = { + .name = "ps3_vuart", + .match = ps3_vuart_match, + .probe = ps3_vuart_probe, + .remove = ps3_vuart_remove, +}; + +int __init ps3_vuart_init(void) +{ + int result; + + pr_debug("%s:%d:\n", __func__, __LINE__); + result = bus_register(&ps3_vuart); + BUG_ON(result); + return result; +} + +void __exit ps3_vuart_exit(void) +{ + pr_debug("%s:%d:\n", __func__, __LINE__); + bus_unregister(&ps3_vuart); +} + +core_initcall(ps3_vuart_init); +module_exit(ps3_vuart_exit); + +/** + * ps3_vuart_port_release_device - Remove a vuart port device. + */ + +static void ps3_vuart_port_release_device(struct device *_dev) +{ + struct ps3_vuart_port_device *dev = to_ps3_vuart_port_device(_dev); +#if defined(DEBUG) + memset(dev, 0xad, sizeof(struct ps3_vuart_port_device)); +#endif + kfree(dev); +} + +/** + * ps3_vuart_port_device_register - Add a vuart port device. + */ + +int ps3_vuart_port_device_register(struct ps3_vuart_port_device *dev) +{ + int result; + static unsigned int dev_count = 1; + + dev->core.parent = NULL; + dev->core.bus = &ps3_vuart; + dev->core.release = ps3_vuart_port_release_device; + + snprintf(dev->core.bus_id, sizeof(dev->core.bus_id), "vuart_%02x", + dev_count++); + + dev_dbg(&dev->core, "%s:%d register\n", __func__, __LINE__); + + result = device_register(&dev->core); + + return result; +} + +EXPORT_SYMBOL_GPL(ps3_vuart_port_device_register); + +/** + * ps3_vuart_port_driver_register - Add a vuart port device driver. + */ + +int ps3_vuart_port_driver_register(struct ps3_vuart_port_driver *drv) +{ + int result; + + pr_debug("%s:%d: (%s)\n", __func__, __LINE__, drv->core.name); + drv->core.bus = &ps3_vuart; + result = driver_register(&drv->core); + return result; +} + +EXPORT_SYMBOL_GPL(ps3_vuart_port_driver_register); + +/** + * ps3_vuart_port_driver_unregister - Remove a vuart port device driver. + */ + +void ps3_vuart_port_driver_unregister(struct ps3_vuart_port_driver *drv) +{ + driver_unregister(&drv->core); +} + +EXPORT_SYMBOL_GPL(ps3_vuart_port_driver_unregister); diff --git a/drivers/ps3/vuart.h b/drivers/ps3/vuart.h new file mode 100644 index 000000000000..28fd89f0c8aa --- /dev/null +++ b/drivers/ps3/vuart.h @@ -0,0 +1,94 @@ +/* + * PS3 virtual uart + * + * Copyright (C) 2006 Sony Computer Entertainment Inc. + * Copyright 2006 Sony Corp. + * + * 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; version 2 of the License. + * + * 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 + */ + +#if !defined(_PS3_VUART_H) +#define _PS3_VUART_H + +struct ps3_vuart_stats { + unsigned long bytes_written; + unsigned long bytes_read; + unsigned long tx_interrupts; + unsigned long rx_interrupts; + unsigned long disconnect_interrupts; +}; + +/** + * struct ps3_vuart_port_device - a device on a vuart port + */ + +struct ps3_vuart_port_device { + enum ps3_match_id match_id; + struct device core; + + /* private driver variables */ + unsigned int port_number; + unsigned long interrupt_mask; + struct { + spinlock_t lock; + struct list_head head; + } tx_list; + struct { + unsigned long bytes_held; + spinlock_t lock; + struct list_head head; + } rx_list; + struct ps3_vuart_stats stats; +}; + +/** + * struct ps3_vuart_port_driver - a driver for a device on a vuart port + */ + +struct ps3_vuart_port_driver { + enum ps3_match_id match_id; + struct device_driver core; + int (*probe)(struct ps3_vuart_port_device *); + int (*remove)(struct ps3_vuart_port_device *); + int (*tx_event)(struct ps3_vuart_port_device *dev); + int (*rx_event)(struct ps3_vuart_port_device *dev); + int (*disconnect_event)(struct ps3_vuart_port_device *dev); + /* int (*suspend)(struct ps3_vuart_port_device *, pm_message_t); */ + /* int (*resume)(struct ps3_vuart_port_device *); */ +}; + +int ps3_vuart_port_device_register(struct ps3_vuart_port_device *dev); +int ps3_vuart_port_driver_register(struct ps3_vuart_port_driver *drv); +void ps3_vuart_port_driver_unregister(struct ps3_vuart_port_driver *drv); +int ps3_vuart_write(struct ps3_vuart_port_device *dev, + const void* buf, unsigned int bytes); +int ps3_vuart_read(struct ps3_vuart_port_device *dev, void* buf, + unsigned int bytes); +static inline struct ps3_vuart_port_driver *to_ps3_vuart_port_driver( + struct device_driver *_drv) +{ + return container_of(_drv, struct ps3_vuart_port_driver, core); +} +static inline struct ps3_vuart_port_device *to_ps3_vuart_port_device( + struct device *_dev) +{ + return container_of(_dev, struct ps3_vuart_port_device, core); +} + +int ps3_vuart_write(struct ps3_vuart_port_device *dev, const void* buf, + unsigned int bytes); +int ps3_vuart_read(struct ps3_vuart_port_device *dev, void* buf, + unsigned int bytes); + +#endif -- cgit v1.2.3 From 39f44be375d07a977ba68f900c87cea97cb05f4a Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Mon, 11 Dec 2006 15:13:37 +1100 Subject: [POWERPC] Fix SPU coredump code for max_fdset removal Commit bbea9f69668a3d0cf9feba15a724cd02896f8675 removed the max_fdset element of struct fdtable. It appears that checking max_fds is sufficient now. Signed-off-by: Paul Mackerras --- arch/powerpc/platforms/cell/spufs/coredump.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arch/powerpc/platforms') diff --git a/arch/powerpc/platforms/cell/spufs/coredump.c b/arch/powerpc/platforms/cell/spufs/coredump.c index 26945c491f6b..725e19561159 100644 --- a/arch/powerpc/platforms/cell/spufs/coredump.c +++ b/arch/powerpc/platforms/cell/spufs/coredump.c @@ -147,7 +147,7 @@ static int spufs_arch_notes_size(void) struct fdtable *fdt = files_fdtable(current->files); int size = 0, fd; - for (fd = 0; fd < fdt->max_fdset && fd < fdt->max_fds; fd++) { + for (fd = 0; fd < fdt->max_fds; fd++) { if (FD_ISSET(fd, fdt->open_fds)) { struct file *file = fcheck(fd); -- cgit v1.2.3