diff options
Diffstat (limited to 'arch/powerpc')
60 files changed, 1378 insertions, 208 deletions
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index fe88418167c5..63c9cafda9c4 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -42,6 +42,9 @@ config GENERIC_HARDIRQS bool default y +config HAVE_GET_USER_PAGES_FAST + def_bool PPC64 + config HAVE_SETUP_PER_CPU_AREA def_bool PPC64 @@ -117,6 +120,7 @@ config PPC select HAVE_KPROBES select HAVE_ARCH_KGDB select HAVE_KRETPROBES + select HAVE_ARCH_TRACEHOOK select HAVE_LMB select HAVE_DMA_ATTRS if PPC64 select USE_GENERIC_SMP_HELPERS if SMP diff --git a/arch/powerpc/boot/dts/mpc832x_mds.dts b/arch/powerpc/boot/dts/mpc832x_mds.dts index 7345743d3d96..fbc930410ff6 100644 --- a/arch/powerpc/boot/dts/mpc832x_mds.dts +++ b/arch/powerpc/boot/dts/mpc832x_mds.dts @@ -68,6 +68,7 @@ #address-cells = <1>; #size-cells = <1>; device_type = "soc"; + compatible = "simple-bus"; ranges = <0x0 0xe0000000 0x00100000>; reg = <0xe0000000 0x00000200>; bus-frequency = <132000000>; diff --git a/arch/powerpc/boot/dts/mpc832x_rdb.dts b/arch/powerpc/boot/dts/mpc832x_rdb.dts index e74c045a0f8c..b157d1885a28 100644 --- a/arch/powerpc/boot/dts/mpc832x_rdb.dts +++ b/arch/powerpc/boot/dts/mpc832x_rdb.dts @@ -51,6 +51,7 @@ #address-cells = <1>; #size-cells = <1>; device_type = "soc"; + compatible = "simple-bus"; ranges = <0x0 0xe0000000 0x00100000>; reg = <0xe0000000 0x00000200>; bus-frequency = <0>; diff --git a/arch/powerpc/boot/dts/mpc8349emitx.dts b/arch/powerpc/boot/dts/mpc8349emitx.dts index 8dfab5662585..700e076ef3f5 100644 --- a/arch/powerpc/boot/dts/mpc8349emitx.dts +++ b/arch/powerpc/boot/dts/mpc8349emitx.dts @@ -52,6 +52,7 @@ #address-cells = <1>; #size-cells = <1>; device_type = "soc"; + compatible = "simple-bus"; ranges = <0x0 0xe0000000 0x00100000>; reg = <0xe0000000 0x00000200>; bus-frequency = <0>; // from bootloader diff --git a/arch/powerpc/boot/dts/mpc8349emitxgp.dts b/arch/powerpc/boot/dts/mpc8349emitxgp.dts index 49ca3497eefb..cdd3063258ea 100644 --- a/arch/powerpc/boot/dts/mpc8349emitxgp.dts +++ b/arch/powerpc/boot/dts/mpc8349emitxgp.dts @@ -50,6 +50,7 @@ #address-cells = <1>; #size-cells = <1>; device_type = "soc"; + compatible = "simple-bus"; ranges = <0x0 0xe0000000 0x00100000>; reg = <0xe0000000 0x00000200>; bus-frequency = <0>; // from bootloader diff --git a/arch/powerpc/boot/dts/mpc834x_mds.dts b/arch/powerpc/boot/dts/mpc834x_mds.dts index ba586cb7afbb..783241c00240 100644 --- a/arch/powerpc/boot/dts/mpc834x_mds.dts +++ b/arch/powerpc/boot/dts/mpc834x_mds.dts @@ -57,6 +57,7 @@ #address-cells = <1>; #size-cells = <1>; device_type = "soc"; + compatible = "simple-bus"; ranges = <0x0 0xe0000000 0x00100000>; reg = <0xe0000000 0x00000200>; bus-frequency = <0>; diff --git a/arch/powerpc/boot/dts/mpc836x_mds.dts b/arch/powerpc/boot/dts/mpc836x_mds.dts index 3701dae1ee02..a3b76a709951 100644 --- a/arch/powerpc/boot/dts/mpc836x_mds.dts +++ b/arch/powerpc/boot/dts/mpc836x_mds.dts @@ -61,6 +61,7 @@ #address-cells = <1>; #size-cells = <1>; device_type = "soc"; + compatible = "simple-bus"; ranges = <0x0 0xe0000000 0x00100000>; reg = <0xe0000000 0x00000200>; bus-frequency = <264000000>; diff --git a/arch/powerpc/boot/dts/mpc836x_rdk.dts b/arch/powerpc/boot/dts/mpc836x_rdk.dts index 8acd1d6577f2..89c9202f8bd7 100644 --- a/arch/powerpc/boot/dts/mpc836x_rdk.dts +++ b/arch/powerpc/boot/dts/mpc836x_rdk.dts @@ -149,18 +149,14 @@ }; crypto@30000 { - compatible = "fsl,sec2-crypto"; + compatible = "fsl,sec2.0"; reg = <0x30000 0x10000>; - interrupts = <11 8>; + interrupts = <11 0x8>; interrupt-parent = <&ipic>; - num-channels = <4>; - channel-fifo-len = <24>; - exec-units-mask = <0x7e>; - /* - * desc mask is for rev1.x, we need runtime fixup - * for >=2.x - */ - descriptor-types-mask = <0x1010ebf>; + fsl,num-channels = <4>; + fsl,channel-fifo-len = <24>; + fsl,exec-units-mask = <0x7e>; + fsl,descriptor-types-mask = <0x01010ebf>; }; ipic: interrupt-controller@700 { diff --git a/arch/powerpc/boot/dts/mpc8377_mds.dts b/arch/powerpc/boot/dts/mpc8377_mds.dts index 0a700cb5f611..432782b6d20a 100644 --- a/arch/powerpc/boot/dts/mpc8377_mds.dts +++ b/arch/powerpc/boot/dts/mpc8377_mds.dts @@ -117,6 +117,7 @@ #address-cells = <1>; #size-cells = <1>; device_type = "soc"; + compatible = "simple-bus"; ranges = <0x0 0xe0000000 0x00100000>; reg = <0xe0000000 0x00000200>; bus-frequency = <0>; diff --git a/arch/powerpc/boot/dts/mpc8378_mds.dts b/arch/powerpc/boot/dts/mpc8378_mds.dts index 29c8c76a58f7..ed32c8ddafe3 100644 --- a/arch/powerpc/boot/dts/mpc8378_mds.dts +++ b/arch/powerpc/boot/dts/mpc8378_mds.dts @@ -117,6 +117,7 @@ #address-cells = <1>; #size-cells = <1>; device_type = "soc"; + compatible = "simple-bus"; ranges = <0x0 0xe0000000 0x00100000>; reg = <0xe0000000 0x00000200>; bus-frequency = <0>; diff --git a/arch/powerpc/boot/dts/mpc8379_mds.dts b/arch/powerpc/boot/dts/mpc8379_mds.dts index d641a8985ea3..f4db9ed4a301 100644 --- a/arch/powerpc/boot/dts/mpc8379_mds.dts +++ b/arch/powerpc/boot/dts/mpc8379_mds.dts @@ -117,6 +117,7 @@ #address-cells = <1>; #size-cells = <1>; device_type = "soc"; + compatible = "simple-bus"; ranges = <0x0 0xe0000000 0x00100000>; reg = <0xe0000000 0x00000200>; bus-frequency = <0>; diff --git a/arch/powerpc/boot/dts/mpc8536ds.dts b/arch/powerpc/boot/dts/mpc8536ds.dts index 02cfa24a1695..1505d6855eff 100644 --- a/arch/powerpc/boot/dts/mpc8536ds.dts +++ b/arch/powerpc/boot/dts/mpc8536ds.dts @@ -49,6 +49,7 @@ #address-cells = <1>; #size-cells = <1>; device_type = "soc"; + compatible = "simple-bus"; ranges = <0x0 0xffe00000 0x100000>; reg = <0xffe00000 0x1000>; bus-frequency = <0>; // Filled out by uboot. diff --git a/arch/powerpc/boot/dts/mpc8540ads.dts b/arch/powerpc/boot/dts/mpc8540ads.dts index f2273a872b11..9568bfaff8f7 100644 --- a/arch/powerpc/boot/dts/mpc8540ads.dts +++ b/arch/powerpc/boot/dts/mpc8540ads.dts @@ -53,6 +53,7 @@ #address-cells = <1>; #size-cells = <1>; device_type = "soc"; + compatible = "simple-bus"; ranges = <0x0 0xe0000000 0x100000>; reg = <0xe0000000 0x100000>; // CCSRBAR 1M bus-frequency = <0>; diff --git a/arch/powerpc/boot/dts/mpc8541cds.dts b/arch/powerpc/boot/dts/mpc8541cds.dts index c4469f19ff82..6480f4fd96e0 100644 --- a/arch/powerpc/boot/dts/mpc8541cds.dts +++ b/arch/powerpc/boot/dts/mpc8541cds.dts @@ -53,6 +53,7 @@ #address-cells = <1>; #size-cells = <1>; device_type = "soc"; + compatible = "simple-bus"; ranges = <0x0 0xe0000000 0x100000>; reg = <0xe0000000 0x1000>; // CCSRBAR 1M bus-frequency = <0>; diff --git a/arch/powerpc/boot/dts/mpc8544ds.dts b/arch/powerpc/boot/dts/mpc8544ds.dts index 7d3829d3495e..f1fb20737e3e 100644 --- a/arch/powerpc/boot/dts/mpc8544ds.dts +++ b/arch/powerpc/boot/dts/mpc8544ds.dts @@ -54,6 +54,7 @@ #address-cells = <1>; #size-cells = <1>; device_type = "soc"; + compatible = "simple-bus"; ranges = <0x0 0xe0000000 0x100000>; reg = <0xe0000000 0x1000>; // CCSRBAR 1M diff --git a/arch/powerpc/boot/dts/mpc8548cds.dts b/arch/powerpc/boot/dts/mpc8548cds.dts index d84466bb7eca..431b496270dc 100644 --- a/arch/powerpc/boot/dts/mpc8548cds.dts +++ b/arch/powerpc/boot/dts/mpc8548cds.dts @@ -58,6 +58,7 @@ #address-cells = <1>; #size-cells = <1>; device_type = "soc"; + compatible = "simple-bus"; ranges = <0x0 0xe0000000 0x100000>; reg = <0xe0000000 0x1000>; // CCSRBAR bus-frequency = <0>; diff --git a/arch/powerpc/boot/dts/mpc8555cds.dts b/arch/powerpc/boot/dts/mpc8555cds.dts index e03a78006283..d833a5c4f476 100644 --- a/arch/powerpc/boot/dts/mpc8555cds.dts +++ b/arch/powerpc/boot/dts/mpc8555cds.dts @@ -53,6 +53,7 @@ #address-cells = <1>; #size-cells = <1>; device_type = "soc"; + compatible = "simple-bus"; ranges = <0x0 0xe0000000 0x100000>; reg = <0xe0000000 0x1000>; // CCSRBAR 1M bus-frequency = <0>; diff --git a/arch/powerpc/boot/dts/mpc8560ads.dts b/arch/powerpc/boot/dts/mpc8560ads.dts index ba8159de040b..4d1f2f284094 100644 --- a/arch/powerpc/boot/dts/mpc8560ads.dts +++ b/arch/powerpc/boot/dts/mpc8560ads.dts @@ -53,6 +53,7 @@ #address-cells = <1>; #size-cells = <1>; device_type = "soc"; + compatible = "simple-bus"; ranges = <0x0 0xe0000000 0x100000>; reg = <0xe0000000 0x200>; bus-frequency = <330000000>; diff --git a/arch/powerpc/boot/dts/mpc8568mds.dts b/arch/powerpc/boot/dts/mpc8568mds.dts index 9c30a34821dc..a15f10343f53 100644 --- a/arch/powerpc/boot/dts/mpc8568mds.dts +++ b/arch/powerpc/boot/dts/mpc8568mds.dts @@ -60,6 +60,7 @@ #address-cells = <1>; #size-cells = <1>; device_type = "soc"; + compatible = "simple-bus"; ranges = <0x0 0xe0000000 0x100000>; reg = <0xe0000000 0x1000>; bus-frequency = <0>; diff --git a/arch/powerpc/boot/dts/mpc8572ds.dts b/arch/powerpc/boot/dts/mpc8572ds.dts index 08c61e3daecc..e124dd18fb5a 100644 --- a/arch/powerpc/boot/dts/mpc8572ds.dts +++ b/arch/powerpc/boot/dts/mpc8572ds.dts @@ -68,6 +68,7 @@ #address-cells = <1>; #size-cells = <1>; device_type = "soc"; + compatible = "simple-bus"; ranges = <0x0 0xffe00000 0x100000>; reg = <0xffe00000 0x1000>; // CCSRBAR & soc regs, remove once parse code for immrbase fixed bus-frequency = <0>; // Filled out by uboot. diff --git a/arch/powerpc/kernel/entry_32.S b/arch/powerpc/kernel/entry_32.S index 81c8324a4a3c..1cbbf7033641 100644 --- a/arch/powerpc/kernel/entry_32.S +++ b/arch/powerpc/kernel/entry_32.S @@ -148,7 +148,7 @@ transfer_to_handler: /* Check to see if the dbcr0 register is set up to debug. Use the internal debug mode bit to do this. */ lwz r12,THREAD_DBCR0(r12) - andis. r12,r12,(DBCR0_IDM | DBSR_DAC1R | DBSR_DAC1W)@h + andis. r12,r12,DBCR0_IDM@h beq+ 3f /* From user and task is ptraced - load up global dbcr0 */ li r12,-1 /* clear all pending debug events */ @@ -292,7 +292,7 @@ syscall_exit_cont: /* If the process has its own DBCR0 value, load it up. The internal debug mode bit tells us that dbcr0 should be loaded. */ lwz r0,THREAD+THREAD_DBCR0(r2) - andis. r10,r0,(DBCR0_IDM | DBSR_DAC1R | DBSR_DAC1W)@h + andis. r10,r0,DBCR0_IDM@h bnel- load_dbcr0 #endif #ifdef CONFIG_44x @@ -343,7 +343,12 @@ syscall_dotrace: stw r0,_TRAP(r1) addi r3,r1,STACK_FRAME_OVERHEAD bl do_syscall_trace_enter - lwz r0,GPR0(r1) /* Restore original registers */ + /* + * Restore argument registers possibly just changed. + * We use the return value of do_syscall_trace_enter + * for call number to look up in the table (r0). + */ + mr r0,r3 lwz r3,GPR3(r1) lwz r4,GPR4(r1) lwz r5,GPR5(r1) @@ -720,7 +725,7 @@ restore_user: /* Check whether this process has its own DBCR0 value. The internal debug mode bit tells us that dbcr0 should be loaded. */ lwz r0,THREAD+THREAD_DBCR0(r2) - andis. r10,r0,(DBCR0_IDM | DBSR_DAC1R | DBSR_DAC1W)@h + andis. r10,r0,DBCR0_IDM@h bnel- load_dbcr0 #endif @@ -1055,8 +1060,8 @@ do_user_signal: /* r10 contains MSR_KERNEL here */ SAVE_NVGPRS(r1) rlwinm r3,r3,0,0,30 stw r3,_TRAP(r1) -2: li r3,0 - addi r4,r1,STACK_FRAME_OVERHEAD +2: addi r3,r1,STACK_FRAME_OVERHEAD + mr r4,r9 bl do_signal REST_NVGPRS(r1) b recheck diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S index d7369243ae44..2d802e97097c 100644 --- a/arch/powerpc/kernel/entry_64.S +++ b/arch/powerpc/kernel/entry_64.S @@ -214,7 +214,12 @@ syscall_dotrace: bl .save_nvgprs addi r3,r1,STACK_FRAME_OVERHEAD bl .do_syscall_trace_enter - ld r0,GPR0(r1) /* Restore original registers */ + /* + * Restore argument registers possibly just changed. + * We use the return value of do_syscall_trace_enter + * for the call number to look up in the table (r0). + */ + mr r0,r3 ld r3,GPR3(r1) ld r4,GPR4(r1) ld r5,GPR5(r1) @@ -638,8 +643,7 @@ user_work: b .ret_from_except_lite 1: bl .save_nvgprs - li r3,0 - addi r4,r1,STACK_FRAME_OVERHEAD + addi r3,r1,STACK_FRAME_OVERHEAD bl .do_signal b .ret_from_except diff --git a/arch/powerpc/kernel/legacy_serial.c b/arch/powerpc/kernel/legacy_serial.c index 4d96e1db55ee..9ddfaef1a184 100644 --- a/arch/powerpc/kernel/legacy_serial.c +++ b/arch/powerpc/kernel/legacy_serial.c @@ -493,18 +493,18 @@ static int __init serial_dev_init(void) device_initcall(serial_dev_init); +#ifdef CONFIG_SERIAL_8250_CONSOLE /* * This is called very early, as part of console_init() (typically just after * time_init()). This function is respondible for trying to find a good * default console on serial ports. It tries to match the open firmware - * default output with one of the available serial console drivers, either - * one of the platform serial ports that have been probed earlier by - * find_legacy_serial_ports() or some more platform specific ones. + * default output with one of the available serial console drivers that have + * been probed earlier by find_legacy_serial_ports() */ static int __init check_legacy_serial_console(void) { struct device_node *prom_stdout = NULL; - int speed = 0, offset = 0; + int i, speed = 0, offset = 0; const char *name; const u32 *spd; @@ -548,31 +548,20 @@ static int __init check_legacy_serial_console(void) if (spd) speed = *spd; - if (0) - ; -#ifdef CONFIG_SERIAL_8250_CONSOLE - else if (strcmp(name, "serial") == 0) { - int i; - /* Look for it in probed array */ - for (i = 0; i < legacy_serial_count; i++) { - if (prom_stdout != legacy_serial_infos[i].np) - continue; - offset = i; - speed = legacy_serial_infos[i].speed; - break; - } - if (i >= legacy_serial_count) - goto not_found; + if (strcmp(name, "serial") != 0) + goto not_found; + + /* Look for it in probed array */ + for (i = 0; i < legacy_serial_count; i++) { + if (prom_stdout != legacy_serial_infos[i].np) + continue; + offset = i; + speed = legacy_serial_infos[i].speed; + break; } -#endif /* CONFIG_SERIAL_8250_CONSOLE */ -#ifdef CONFIG_SERIAL_PMACZILOG_CONSOLE - else if (strcmp(name, "ch-a") == 0) - offset = 0; - else if (strcmp(name, "ch-b") == 0) - offset = 1; -#endif /* CONFIG_SERIAL_PMACZILOG_CONSOLE */ - else + if (i >= legacy_serial_count) goto not_found; + of_node_put(prom_stdout); DBG("Found serial console at ttyS%d\n", offset); @@ -591,3 +580,4 @@ static int __init check_legacy_serial_console(void) } console_initcall(check_legacy_serial_console); +#endif /* CONFIG_SERIAL_8250_CONSOLE */ diff --git a/arch/powerpc/kernel/lparcfg.c b/arch/powerpc/kernel/lparcfg.c index 9f856a0c3e38..1a09719c7628 100644 --- a/arch/powerpc/kernel/lparcfg.c +++ b/arch/powerpc/kernel/lparcfg.c @@ -636,10 +636,6 @@ static ssize_t lparcfg_write(struct file *file, const char __user * buf, retval = -EIO; } else if (retval == H_PARAMETER) { retval = -EINVAL; - } else { - printk(KERN_WARNING "%s: received unknown hv return code %ld", - __func__, retval); - retval = -EIO; } return retval; diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c index db2497ccc111..e030f3bd5024 100644 --- a/arch/powerpc/kernel/process.c +++ b/arch/powerpc/kernel/process.c @@ -254,7 +254,7 @@ void do_dabr(struct pt_regs *regs, unsigned long address, return; /* Clear the DAC and struct entries. One shot trigger */ -#if (defined(CONFIG_44x) || defined(CONFIG_BOOKE)) +#if defined(CONFIG_BOOKE) mtspr(SPRN_DBCR0, mfspr(SPRN_DBCR0) & ~(DBSR_DAC1R | DBSR_DAC1W | DBCR0_IDM)); #endif @@ -286,7 +286,7 @@ int set_dabr(unsigned long dabr) mtspr(SPRN_DABR, dabr); #endif -#if defined(CONFIG_44x) || defined(CONFIG_BOOKE) +#if defined(CONFIG_BOOKE) mtspr(SPRN_DAC1, dabr); #endif @@ -373,7 +373,7 @@ struct task_struct *__switch_to(struct task_struct *prev, if (unlikely(__get_cpu_var(current_dabr) != new->thread.dabr)) set_dabr(new->thread.dabr); -#if defined(CONFIG_44x) || defined(CONFIG_BOOKE) +#if defined(CONFIG_BOOKE) /* If new thread DAC (HW breakpoint) is the same then leave it */ if (new->thread.dabr) set_dabr(new->thread.dabr); @@ -568,7 +568,7 @@ void flush_thread(void) current->thread.dabr = 0; set_dabr(0); -#if defined(CONFIG_44x) || defined(CONFIG_BOOKE) +#if defined(CONFIG_BOOKE) current->thread.dbcr0 &= ~(DBSR_DAC1R | DBSR_DAC1W); #endif } diff --git a/arch/powerpc/kernel/prom_init.c b/arch/powerpc/kernel/prom_init.c index c4ab2195b9cb..b72849ac7db3 100644 --- a/arch/powerpc/kernel/prom_init.c +++ b/arch/powerpc/kernel/prom_init.c @@ -205,8 +205,6 @@ static int __initdata mem_reserve_cnt; static cell_t __initdata regbuf[1024]; -#define MAX_CPU_THREADS 2 - /* * Error results ... some OF calls will return "-1" on error, some * will return 0, some will return either. To simplify, here are @@ -1339,10 +1337,6 @@ static void __init prom_hold_cpus(void) unsigned int reg; phandle node; char type[64]; - int cpuid = 0; - unsigned int interrupt_server[MAX_CPU_THREADS]; - unsigned int cpu_threads, hw_cpu_num; - int propsize; struct prom_t *_prom = &RELOC(prom); unsigned long *spinloop = (void *) LOW_ADDR(__secondary_hold_spinloop); @@ -1386,7 +1380,6 @@ static void __init prom_hold_cpus(void) reg = -1; prom_getprop(node, "reg", ®, sizeof(reg)); - prom_debug("\ncpuid = 0x%x\n", cpuid); prom_debug("cpu hw idx = 0x%x\n", reg); /* Init the acknowledge var which will be reset by @@ -1395,28 +1388,9 @@ static void __init prom_hold_cpus(void) */ *acknowledge = (unsigned long)-1; - propsize = prom_getprop(node, "ibm,ppc-interrupt-server#s", - &interrupt_server, - sizeof(interrupt_server)); - if (propsize < 0) { - /* no property. old hardware has no SMT */ - cpu_threads = 1; - interrupt_server[0] = reg; /* fake it with phys id */ - } else { - /* We have a threaded processor */ - cpu_threads = propsize / sizeof(u32); - if (cpu_threads > MAX_CPU_THREADS) { - prom_printf("SMT: too many threads!\n" - "SMT: found %x, max is %x\n", - cpu_threads, MAX_CPU_THREADS); - cpu_threads = 1; /* ToDo: panic? */ - } - } - - hw_cpu_num = interrupt_server[0]; - if (hw_cpu_num != _prom->cpu) { + if (reg != _prom->cpu) { /* Primary Thread of non-boot cpu */ - prom_printf("%x : starting cpu hw idx %x... ", cpuid, reg); + prom_printf("starting cpu hw idx %x... ", reg); call_prom("start-cpu", 3, 0, node, secondary_hold, reg); @@ -1431,17 +1405,10 @@ static void __init prom_hold_cpus(void) } #ifdef CONFIG_SMP else - prom_printf("%x : boot cpu %x\n", cpuid, reg); + prom_printf("boot cpu hw idx %x\n", reg); #endif /* CONFIG_SMP */ - - /* Reserve cpu #s for secondary threads. They start later. */ - cpuid += cpu_threads; } - if (cpuid > NR_CPUS) - prom_printf("WARNING: maximum CPUs (" __stringify(NR_CPUS) - ") exceeded: ignoring extras\n"); - prom_debug("prom_hold_cpus: end...\n"); } diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c index a5d0e78779c8..3635be61f899 100644 --- a/arch/powerpc/kernel/ptrace.c +++ b/arch/powerpc/kernel/ptrace.c @@ -22,6 +22,7 @@ #include <linux/errno.h> #include <linux/ptrace.h> #include <linux/regset.h> +#include <linux/tracehook.h> #include <linux/elf.h> #include <linux/user.h> #include <linux/security.h> @@ -374,7 +375,7 @@ static int vsr_get(struct task_struct *target, const struct user_regset *regset, flush_vsx_to_thread(target); for (i = 0; i < 32 ; i++) - buf[i] = current->thread.fpr[i][TS_VSRLOWOFFSET]; + buf[i] = target->thread.fpr[i][TS_VSRLOWOFFSET]; ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, buf, 0, 32 * sizeof(double)); @@ -393,7 +394,7 @@ static int vsr_set(struct task_struct *target, const struct user_regset *regset, ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, buf, 0, 32 * sizeof(double)); for (i = 0; i < 32 ; i++) - current->thread.fpr[i][TS_VSRLOWOFFSET] = buf[i]; + target->thread.fpr[i][TS_VSRLOWOFFSET] = buf[i]; return ret; @@ -717,7 +718,7 @@ void user_disable_single_step(struct task_struct *task) struct pt_regs *regs = task->thread.regs; -#if defined(CONFIG_44x) || defined(CONFIG_BOOKE) +#if defined(CONFIG_BOOKE) /* If DAC then do not single step, skip */ if (task->thread.dabr) return; @@ -744,10 +745,11 @@ int ptrace_set_debugreg(struct task_struct *task, unsigned long addr, if (addr > 0) return -EINVAL; + /* The bottom 3 bits in dabr are flags */ if ((data & ~0x7UL) >= TASK_SIZE) return -EIO; -#ifdef CONFIG_PPC64 +#ifndef CONFIG_BOOKE /* For processors using DABR (i.e. 970), the bottom 3 bits are flags. * It was assumed, on previous implementations, that 3 bits were @@ -769,7 +771,7 @@ int ptrace_set_debugreg(struct task_struct *task, unsigned long addr, task->thread.dabr = data; #endif -#if defined(CONFIG_44x) || defined(CONFIG_BOOKE) +#if defined(CONFIG_BOOKE) /* As described above, it was assumed 3 bits were passed with the data * address, but we will assume only the mode bits will be passed @@ -973,15 +975,13 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) case PTRACE_GETVSRREGS: return copy_regset_to_user(child, &user_ppc_native_view, REGSET_VSX, - 0, (32 * sizeof(vector128) + - sizeof(u32)), + 0, 32 * sizeof(double), (void __user *) data); case PTRACE_SETVSRREGS: return copy_regset_from_user(child, &user_ppc_native_view, REGSET_VSX, - 0, (32 * sizeof(vector128) + - sizeof(u32)), + 0, 32 * sizeof(double), (const void __user *) data); #endif #ifdef CONFIG_SPE @@ -1013,31 +1013,24 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) return ret; } -static void do_syscall_trace(void) +/* + * We must return the syscall number to actually look up in the table. + * This can be -1L to skip running any syscall at all. + */ +long do_syscall_trace_enter(struct pt_regs *regs) { - /* the 0x80 provides a way for the tracing parent to distinguish - between a syscall stop and SIGTRAP delivery */ - ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) - ? 0x80 : 0)); - - /* - * this isn't the same as continuing with a signal, but it will do - * for normal use. strace only continues with a signal if the - * stopping signal is not SIGTRAP. -brl - */ - if (current->exit_code) { - send_sig(current->exit_code, current, 1); - current->exit_code = 0; - } -} + long ret = 0; -void do_syscall_trace_enter(struct pt_regs *regs) -{ secure_computing(regs->gpr[0]); - if (test_thread_flag(TIF_SYSCALL_TRACE) - && (current->ptrace & PT_PTRACED)) - do_syscall_trace(); + if (test_thread_flag(TIF_SYSCALL_TRACE) && + tracehook_report_syscall_entry(regs)) + /* + * Tracing decided this syscall should not happen. + * We'll return a bogus call number to get an ENOSYS + * error, but leave the original number in regs->gpr[0]. + */ + ret = -1L; if (unlikely(current->audit_context)) { #ifdef CONFIG_PPC64 @@ -1055,16 +1048,19 @@ void do_syscall_trace_enter(struct pt_regs *regs) regs->gpr[5] & 0xffffffff, regs->gpr[6] & 0xffffffff); } + + return ret ?: regs->gpr[0]; } void do_syscall_trace_leave(struct pt_regs *regs) { + int step; + if (unlikely(current->audit_context)) audit_syscall_exit((regs->ccr&0x10000000)?AUDITSC_FAILURE:AUDITSC_SUCCESS, regs->result); - if ((test_thread_flag(TIF_SYSCALL_TRACE) - || test_thread_flag(TIF_SINGLESTEP)) - && (current->ptrace & PT_PTRACED)) - do_syscall_trace(); + step = test_thread_flag(TIF_SINGLESTEP); + if (step || test_thread_flag(TIF_SYSCALL_TRACE)) + tracehook_report_syscall_exit(regs, step); } diff --git a/arch/powerpc/kernel/ptrace32.c b/arch/powerpc/kernel/ptrace32.c index 67bf1a1e7e14..197d49c790ad 100644 --- a/arch/powerpc/kernel/ptrace32.c +++ b/arch/powerpc/kernel/ptrace32.c @@ -294,6 +294,8 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, case PTRACE_SETFPREGS: case PTRACE_GETVRREGS: case PTRACE_SETVRREGS: + case PTRACE_GETVSRREGS: + case PTRACE_SETVSRREGS: case PTRACE_GETREGS64: case PTRACE_SETREGS64: case PPC_PTRACE_GETFPREGS: diff --git a/arch/powerpc/kernel/setup-common.c b/arch/powerpc/kernel/setup-common.c index 61a3f4132087..9cc5a52711e5 100644 --- a/arch/powerpc/kernel/setup-common.c +++ b/arch/powerpc/kernel/setup-common.c @@ -367,7 +367,6 @@ static void __init cpu_init_thread_core_maps(int tpc) * setup_cpu_maps - initialize the following cpu maps: * cpu_possible_map * cpu_present_map - * cpu_sibling_map * * Having the possible map set up early allows us to restrict allocations * of things like irqstacks to num_possible_cpus() rather than NR_CPUS. @@ -475,29 +474,6 @@ void __init smp_setup_cpu_maps(void) */ cpu_init_thread_core_maps(nthreads); } - -/* - * Being that cpu_sibling_map is now a per_cpu array, then it cannot - * be initialized until the per_cpu areas have been created. This - * function is now called from setup_per_cpu_areas(). - */ -void __init smp_setup_cpu_sibling_map(void) -{ -#ifdef CONFIG_PPC64 - int i, cpu, base; - - for_each_possible_cpu(cpu) { - DBG("Sibling map for CPU %d:", cpu); - base = cpu_first_thread_in_core(cpu); - for (i = 0; i < threads_per_core; i++) { - cpu_set(base + i, per_cpu(cpu_sibling_map, cpu)); - DBG(" %d", base + i); - } - DBG("\n"); - } - -#endif /* CONFIG_PPC64 */ -} #endif /* CONFIG_SMP */ #ifdef CONFIG_PCSPKR_PLATFORM diff --git a/arch/powerpc/kernel/setup_64.c b/arch/powerpc/kernel/setup_64.c index 04d8de9f0fc6..8b25f51f03bf 100644 --- a/arch/powerpc/kernel/setup_64.c +++ b/arch/powerpc/kernel/setup_64.c @@ -611,9 +611,6 @@ void __init setup_per_cpu_areas(void) paca[i].data_offset = ptr - __per_cpu_start; memcpy(ptr, __per_cpu_start, __per_cpu_end - __per_cpu_start); } - - /* Now that per_cpu is setup, initialize cpu_sibling_map */ - smp_setup_cpu_sibling_map(); } #endif diff --git a/arch/powerpc/kernel/signal.c b/arch/powerpc/kernel/signal.c index 7aada783ec6a..a54405ebd7b0 100644 --- a/arch/powerpc/kernel/signal.c +++ b/arch/powerpc/kernel/signal.c @@ -9,7 +9,7 @@ * this archive for more details. */ -#include <linux/ptrace.h> +#include <linux/tracehook.h> #include <linux/signal.h> #include <asm/uaccess.h> #include <asm/unistd.h> @@ -112,7 +112,7 @@ static void check_syscall_restart(struct pt_regs *regs, struct k_sigaction *ka, } } -int do_signal(sigset_t *oldset, struct pt_regs *regs) +static int do_signal_pending(sigset_t *oldset, struct pt_regs *regs) { siginfo_t info; int signr; @@ -147,7 +147,7 @@ int do_signal(sigset_t *oldset, struct pt_regs *regs) */ if (current->thread.dabr) { set_dabr(current->thread.dabr); -#if defined(CONFIG_44x) || defined(CONFIG_BOOKE) +#if defined(CONFIG_BOOKE) mtspr(SPRN_DBCR0, current->thread.dbcr0); #endif } @@ -177,11 +177,28 @@ int do_signal(sigset_t *oldset, struct pt_regs *regs) * its frame, and we can clear the TLF_RESTORE_SIGMASK flag. */ current_thread_info()->local_flags &= ~_TLF_RESTORE_SIGMASK; + + /* + * Let tracing know that we've done the handler setup. + */ + tracehook_signal_handler(signr, &info, &ka, regs, + test_thread_flag(TIF_SINGLESTEP)); } return ret; } +void do_signal(struct pt_regs *regs, unsigned long thread_info_flags) +{ + if (thread_info_flags & _TIF_SIGPENDING) + do_signal_pending(NULL, regs); + + if (thread_info_flags & _TIF_NOTIFY_RESUME) { + clear_thread_flag(TIF_NOTIFY_RESUME); + tracehook_notify_resume(regs); + } +} + long sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss, unsigned long r5, unsigned long r6, unsigned long r7, unsigned long r8, struct pt_regs *regs) diff --git a/arch/powerpc/kernel/smp.c b/arch/powerpc/kernel/smp.c index f5ae9fa222ea..5337ca7bb649 100644 --- a/arch/powerpc/kernel/smp.c +++ b/arch/powerpc/kernel/smp.c @@ -41,6 +41,7 @@ #include <asm/smp.h> #include <asm/time.h> #include <asm/machdep.h> +#include <asm/cputhreads.h> #include <asm/cputable.h> #include <asm/system.h> #include <asm/mpic.h> @@ -62,10 +63,12 @@ struct thread_info *secondary_ti; cpumask_t cpu_possible_map = CPU_MASK_NONE; cpumask_t cpu_online_map = CPU_MASK_NONE; DEFINE_PER_CPU(cpumask_t, cpu_sibling_map) = CPU_MASK_NONE; +DEFINE_PER_CPU(cpumask_t, cpu_core_map) = CPU_MASK_NONE; EXPORT_SYMBOL(cpu_online_map); EXPORT_SYMBOL(cpu_possible_map); EXPORT_PER_CPU_SYMBOL(cpu_sibling_map); +EXPORT_PER_CPU_SYMBOL(cpu_core_map); /* SMP operations for this machine */ struct smp_ops_t *smp_ops; @@ -228,6 +231,8 @@ void __devinit smp_prepare_boot_cpu(void) BUG_ON(smp_processor_id() != boot_cpuid); cpu_set(boot_cpuid, cpu_online_map); + cpu_set(boot_cpuid, per_cpu(cpu_sibling_map, boot_cpuid)); + cpu_set(boot_cpuid, per_cpu(cpu_core_map, boot_cpuid)); #ifdef CONFIG_PPC64 paca[boot_cpuid].__current = current; #endif @@ -375,11 +380,60 @@ int __cpuinit __cpu_up(unsigned int cpu) return 0; } +/* Return the value of the reg property corresponding to the given + * logical cpu. + */ +int cpu_to_core_id(int cpu) +{ + struct device_node *np; + const int *reg; + int id = -1; + + np = of_get_cpu_node(cpu, NULL); + if (!np) + goto out; + + reg = of_get_property(np, "reg", NULL); + if (!reg) + goto out; + + id = *reg; +out: + of_node_put(np); + return id; +} + +/* Must be called when no change can occur to cpu_present_map, + * i.e. during cpu online or offline. + */ +static struct device_node *cpu_to_l2cache(int cpu) +{ + struct device_node *np; + const phandle *php; + phandle ph; + + if (!cpu_present(cpu)) + return NULL; + + np = of_get_cpu_node(cpu, NULL); + if (np == NULL) + return NULL; + + php = of_get_property(np, "l2-cache", NULL); + if (php == NULL) + return NULL; + ph = *php; + of_node_put(np); + + return of_find_node_by_phandle(ph); +} /* Activate a secondary processor. */ int __devinit start_secondary(void *unused) { unsigned int cpu = smp_processor_id(); + struct device_node *l2_cache; + int i, base; atomic_inc(&init_mm.mm_count); current->active_mm = &init_mm; @@ -400,6 +454,33 @@ int __devinit start_secondary(void *unused) ipi_call_lock(); cpu_set(cpu, cpu_online_map); + /* Update sibling maps */ + base = cpu_first_thread_in_core(cpu); + for (i = 0; i < threads_per_core; i++) { + if (cpu_is_offline(base + i)) + continue; + cpu_set(cpu, per_cpu(cpu_sibling_map, base + i)); + cpu_set(base + i, per_cpu(cpu_sibling_map, cpu)); + + /* cpu_core_map should be a superset of + * cpu_sibling_map even if we don't have cache + * information, so update the former here, too. + */ + cpu_set(cpu, per_cpu(cpu_core_map, base +i)); + cpu_set(base + i, per_cpu(cpu_core_map, cpu)); + } + l2_cache = cpu_to_l2cache(cpu); + for_each_online_cpu(i) { + struct device_node *np = cpu_to_l2cache(i); + if (!np) + continue; + if (np == l2_cache) { + cpu_set(cpu, per_cpu(cpu_core_map, i)); + cpu_set(i, per_cpu(cpu_core_map, cpu)); + } + of_node_put(np); + } + of_node_put(l2_cache); ipi_call_unlock(); local_irq_enable(); @@ -437,10 +518,42 @@ void __init smp_cpus_done(unsigned int max_cpus) #ifdef CONFIG_HOTPLUG_CPU int __cpu_disable(void) { - if (smp_ops->cpu_disable) - return smp_ops->cpu_disable(); + struct device_node *l2_cache; + int cpu = smp_processor_id(); + int base, i; + int err; - return -ENOSYS; + if (!smp_ops->cpu_disable) + return -ENOSYS; + + err = smp_ops->cpu_disable(); + if (err) + return err; + + /* Update sibling maps */ + base = cpu_first_thread_in_core(cpu); + for (i = 0; i < threads_per_core; i++) { + cpu_clear(cpu, per_cpu(cpu_sibling_map, base + i)); + cpu_clear(base + i, per_cpu(cpu_sibling_map, cpu)); + cpu_clear(cpu, per_cpu(cpu_core_map, base +i)); + cpu_clear(base + i, per_cpu(cpu_core_map, cpu)); + } + + l2_cache = cpu_to_l2cache(cpu); + for_each_present_cpu(i) { + struct device_node *np = cpu_to_l2cache(i); + if (!np) + continue; + if (np == l2_cache) { + cpu_clear(cpu, per_cpu(cpu_core_map, i)); + cpu_clear(i, per_cpu(cpu_core_map, cpu)); + } + of_node_put(np); + } + of_node_put(l2_cache); + + + return 0; } void __cpu_die(unsigned int cpu) diff --git a/arch/powerpc/kernel/stacktrace.c b/arch/powerpc/kernel/stacktrace.c index f2589645870a..b0dbb1daa4df 100644 --- a/arch/powerpc/kernel/stacktrace.c +++ b/arch/powerpc/kernel/stacktrace.c @@ -13,7 +13,6 @@ #include <linux/module.h> #include <linux/sched.h> #include <linux/stacktrace.h> -#include <linux/module.h> #include <asm/ptrace.h> #include <asm/processor.h> diff --git a/arch/powerpc/kernel/sysfs.c b/arch/powerpc/kernel/sysfs.c index 800e5e9a087b..56d172d16e56 100644 --- a/arch/powerpc/kernel/sysfs.c +++ b/arch/powerpc/kernel/sysfs.c @@ -22,6 +22,8 @@ static DEFINE_PER_CPU(struct cpu, cpu_devices); +static DEFINE_PER_CPU(struct kobject *, cache_toplevel); + /* SMT stuff */ #ifdef CONFIG_PPC_MULTIPLATFORM @@ -297,8 +299,289 @@ static struct sysdev_attribute pa6t_attrs[] = { #endif /* CONFIG_DEBUG_KERNEL */ }; +struct cache_desc { + struct kobject kobj; + struct cache_desc *next; + const char *type; /* Instruction, Data, or Unified */ + u32 size; /* total cache size in KB */ + u32 line_size; /* in bytes */ + u32 nr_sets; /* number of sets */ + u32 level; /* e.g. 1, 2, 3... */ + u32 associativity; /* e.g. 8-way... 0 is fully associative */ +}; + +DEFINE_PER_CPU(struct cache_desc *, cache_desc); + +static struct cache_desc *kobj_to_cache_desc(struct kobject *k) +{ + return container_of(k, struct cache_desc, kobj); +} + +static void cache_desc_release(struct kobject *k) +{ + struct cache_desc *desc = kobj_to_cache_desc(k); + + pr_debug("%s: releasing %s\n", __func__, kobject_name(k)); + + if (desc->next) + kobject_put(&desc->next->kobj); + + kfree(kobj_to_cache_desc(k)); +} + +static ssize_t cache_desc_show(struct kobject *k, struct attribute *attr, char *buf) +{ + struct kobj_attribute *kobj_attr; + + kobj_attr = container_of(attr, struct kobj_attribute, attr); + + return kobj_attr->show(k, kobj_attr, buf); +} + +static struct sysfs_ops cache_desc_sysfs_ops = { + .show = cache_desc_show, +}; + +static struct kobj_type cache_desc_type = { + .release = cache_desc_release, + .sysfs_ops = &cache_desc_sysfs_ops, +}; + +static ssize_t cache_size_show(struct kobject *k, struct kobj_attribute *attr, char *buf) +{ + struct cache_desc *cache = kobj_to_cache_desc(k); + + return sprintf(buf, "%uK\n", cache->size); +} + +static struct kobj_attribute cache_size_attr = + __ATTR(size, 0444, cache_size_show, NULL); + +static ssize_t cache_line_size_show(struct kobject *k, struct kobj_attribute *attr, char *buf) +{ + struct cache_desc *cache = kobj_to_cache_desc(k); + + return sprintf(buf, "%u\n", cache->line_size); +} + +static struct kobj_attribute cache_line_size_attr = + __ATTR(coherency_line_size, 0444, cache_line_size_show, NULL); + +static ssize_t cache_nr_sets_show(struct kobject *k, struct kobj_attribute *attr, char *buf) +{ + struct cache_desc *cache = kobj_to_cache_desc(k); + + return sprintf(buf, "%u\n", cache->nr_sets); +} + +static struct kobj_attribute cache_nr_sets_attr = + __ATTR(number_of_sets, 0444, cache_nr_sets_show, NULL); + +static ssize_t cache_type_show(struct kobject *k, struct kobj_attribute *attr, char *buf) +{ + struct cache_desc *cache = kobj_to_cache_desc(k); + + return sprintf(buf, "%s\n", cache->type); +} + +static struct kobj_attribute cache_type_attr = + __ATTR(type, 0444, cache_type_show, NULL); + +static ssize_t cache_level_show(struct kobject *k, struct kobj_attribute *attr, char *buf) +{ + struct cache_desc *cache = kobj_to_cache_desc(k); + + return sprintf(buf, "%u\n", cache->level); +} + +static struct kobj_attribute cache_level_attr = + __ATTR(level, 0444, cache_level_show, NULL); + +static ssize_t cache_assoc_show(struct kobject *k, struct kobj_attribute *attr, char *buf) +{ + struct cache_desc *cache = kobj_to_cache_desc(k); + + return sprintf(buf, "%u\n", cache->associativity); +} + +static struct kobj_attribute cache_assoc_attr = + __ATTR(ways_of_associativity, 0444, cache_assoc_show, NULL); + +struct cache_desc_info { + const char *type; + const char *size_prop; + const char *line_size_prop; + const char *nr_sets_prop; +}; + +/* PowerPC Processor binding says the [di]-cache-* must be equal on + * unified caches, so just use d-cache properties. */ +static struct cache_desc_info ucache_info = { + .type = "Unified", + .size_prop = "d-cache-size", + .line_size_prop = "d-cache-line-size", + .nr_sets_prop = "d-cache-sets", +}; -static void register_cpu_online(unsigned int cpu) +static struct cache_desc_info dcache_info = { + .type = "Data", + .size_prop = "d-cache-size", + .line_size_prop = "d-cache-line-size", + .nr_sets_prop = "d-cache-sets", +}; + +static struct cache_desc_info icache_info = { + .type = "Instruction", + .size_prop = "i-cache-size", + .line_size_prop = "i-cache-line-size", + .nr_sets_prop = "i-cache-sets", +}; + +static struct cache_desc * __cpuinit create_cache_desc(struct device_node *np, struct kobject *parent, int index, int level, struct cache_desc_info *info) +{ + const u32 *cache_line_size; + struct cache_desc *new; + const u32 *cache_size; + const u32 *nr_sets; + int rc; + + new = kzalloc(sizeof(*new), GFP_KERNEL); + if (!new) + return NULL; + + rc = kobject_init_and_add(&new->kobj, &cache_desc_type, parent, + "index%d", index); + if (rc) + goto err; + + /* type */ + new->type = info->type; + rc = sysfs_create_file(&new->kobj, &cache_type_attr.attr); + WARN_ON(rc); + + /* level */ + new->level = level; + rc = sysfs_create_file(&new->kobj, &cache_level_attr.attr); + WARN_ON(rc); + + /* size */ + cache_size = of_get_property(np, info->size_prop, NULL); + if (cache_size) { + new->size = *cache_size / 1024; + rc = sysfs_create_file(&new->kobj, + &cache_size_attr.attr); + WARN_ON(rc); + } + + /* coherency_line_size */ + cache_line_size = of_get_property(np, info->line_size_prop, NULL); + if (cache_line_size) { + new->line_size = *cache_line_size; + rc = sysfs_create_file(&new->kobj, + &cache_line_size_attr.attr); + WARN_ON(rc); + } + + /* number_of_sets */ + nr_sets = of_get_property(np, info->nr_sets_prop, NULL); + if (nr_sets) { + new->nr_sets = *nr_sets; + rc = sysfs_create_file(&new->kobj, + &cache_nr_sets_attr.attr); + WARN_ON(rc); + } + + /* ways_of_associativity */ + if (new->nr_sets == 1) { + /* fully associative */ + new->associativity = 0; + goto create_assoc; + } + + if (new->nr_sets && new->size && new->line_size) { + /* If we have values for all of these we can derive + * the associativity. */ + new->associativity = + ((new->size * 1024) / new->nr_sets) / new->line_size; +create_assoc: + rc = sysfs_create_file(&new->kobj, + &cache_assoc_attr.attr); + WARN_ON(rc); + } + + return new; +err: + kfree(new); + return NULL; +} + +static bool cache_is_unified(struct device_node *np) +{ + return of_get_property(np, "cache-unified", NULL); +} + +static struct cache_desc * __cpuinit create_cache_index_info(struct device_node *np, struct kobject *parent, int index, int level) +{ + const phandle *next_cache_phandle; + struct device_node *next_cache; + struct cache_desc *new, **end; + + pr_debug("%s(node = %s, index = %d)\n", __func__, np->full_name, index); + + if (cache_is_unified(np)) { + new = create_cache_desc(np, parent, index, level, + &ucache_info); + } else { + new = create_cache_desc(np, parent, index, level, + &dcache_info); + if (new) { + index++; + new->next = create_cache_desc(np, parent, index, level, + &icache_info); + } + } + if (!new) + return NULL; + + end = &new->next; + while (*end) + end = &(*end)->next; + + next_cache_phandle = of_get_property(np, "l2-cache", NULL); + if (!next_cache_phandle) + goto out; + + next_cache = of_find_node_by_phandle(*next_cache_phandle); + if (!next_cache) + goto out; + + *end = create_cache_index_info(next_cache, parent, ++index, ++level); + + of_node_put(next_cache); +out: + return new; +} + +static void __cpuinit create_cache_info(struct sys_device *sysdev) +{ + struct kobject *cache_toplevel; + struct device_node *np = NULL; + int cpu = sysdev->id; + + cache_toplevel = kobject_create_and_add("cache", &sysdev->kobj); + if (!cache_toplevel) + return; + per_cpu(cache_toplevel, cpu) = cache_toplevel; + np = of_get_cpu_node(cpu, NULL); + if (np != NULL) { + per_cpu(cache_desc, cpu) = + create_cache_index_info(np, cache_toplevel, 0, 1); + of_node_put(np); + } + return; +} + +static void __cpuinit register_cpu_online(unsigned int cpu) { struct cpu *c = &per_cpu(cpu_devices, cpu); struct sys_device *s = &c->sysdev; @@ -346,9 +629,33 @@ static void register_cpu_online(unsigned int cpu) if (cpu_has_feature(CPU_FTR_DSCR)) sysdev_create_file(s, &attr_dscr); + + create_cache_info(s); } #ifdef CONFIG_HOTPLUG_CPU +static void remove_cache_info(struct sys_device *sysdev) +{ + struct kobject *cache_toplevel; + struct cache_desc *cache_desc; + int cpu = sysdev->id; + + cache_desc = per_cpu(cache_desc, cpu); + if (cache_desc != NULL) { + sysfs_remove_file(&cache_desc->kobj, &cache_size_attr.attr); + sysfs_remove_file(&cache_desc->kobj, &cache_line_size_attr.attr); + sysfs_remove_file(&cache_desc->kobj, &cache_type_attr.attr); + sysfs_remove_file(&cache_desc->kobj, &cache_level_attr.attr); + sysfs_remove_file(&cache_desc->kobj, &cache_nr_sets_attr.attr); + sysfs_remove_file(&cache_desc->kobj, &cache_assoc_attr.attr); + + kobject_put(&cache_desc->kobj); + } + cache_toplevel = per_cpu(cache_toplevel, cpu); + if (cache_toplevel != NULL) + kobject_put(cache_toplevel); +} + static void unregister_cpu_online(unsigned int cpu) { struct cpu *c = &per_cpu(cpu_devices, cpu); @@ -399,6 +706,8 @@ static void unregister_cpu_online(unsigned int cpu) if (cpu_has_feature(CPU_FTR_DSCR)) sysdev_remove_file(s, &attr_dscr); + + remove_cache_info(s); } #endif /* CONFIG_HOTPLUG_CPU */ diff --git a/arch/powerpc/kernel/vio.c b/arch/powerpc/kernel/vio.c index ade8aeaa2e70..22a3c33fd751 100644 --- a/arch/powerpc/kernel/vio.c +++ b/arch/powerpc/kernel/vio.c @@ -530,7 +530,7 @@ static dma_addr_t vio_dma_iommu_map_single(struct device *dev, void *vaddr, } ret = dma_iommu_ops.map_single(dev, vaddr, size, direction, attrs); - if (unlikely(dma_mapping_error(ret))) { + if (unlikely(dma_mapping_error(dev, ret))) { vio_cmo_dealloc(viodev, roundup(size, IOMMU_PAGE_SIZE)); atomic_inc(&viodev->cmo.allocs_failed); } @@ -1031,8 +1031,8 @@ void vio_cmo_set_dev_desired(struct vio_dev *viodev, size_t desired) {} static int vio_cmo_bus_probe(struct vio_dev *viodev) { return 0; } static void vio_cmo_bus_remove(struct vio_dev *viodev) {} static void vio_cmo_set_dma_ops(struct vio_dev *viodev) {} -static void vio_cmo_bus_init() {} -static void vio_cmo_sysfs_init() { } +static void vio_cmo_bus_init(void) {} +static void vio_cmo_sysfs_init(void) { } #endif /* CONFIG_PPC_SMLPAR */ EXPORT_SYMBOL(vio_cmo_entitlement_update); EXPORT_SYMBOL(vio_cmo_set_dev_desired); diff --git a/arch/powerpc/mm/Makefile b/arch/powerpc/mm/Makefile index 1c00e0196f6c..e7392b45a5ef 100644 --- a/arch/powerpc/mm/Makefile +++ b/arch/powerpc/mm/Makefile @@ -12,7 +12,8 @@ obj-y := fault.o mem.o \ mmu_context_$(CONFIG_WORD_SIZE).o hash-$(CONFIG_PPC_NATIVE) := hash_native_64.o obj-$(CONFIG_PPC64) += hash_utils_64.o \ - slb_low.o slb.o stab.o mmap.o $(hash-y) + slb_low.o slb.o stab.o \ + gup.o mmap.o $(hash-y) obj-$(CONFIG_PPC_STD_MMU_32) += ppc_mmu_32.o obj-$(CONFIG_PPC_STD_MMU) += hash_low_$(CONFIG_WORD_SIZE).o \ tlb_$(CONFIG_WORD_SIZE).o diff --git a/arch/powerpc/mm/gup.c b/arch/powerpc/mm/gup.c new file mode 100644 index 000000000000..9fdf4d6335e4 --- /dev/null +++ b/arch/powerpc/mm/gup.c @@ -0,0 +1,280 @@ +/* + * Lockless get_user_pages_fast for powerpc + * + * Copyright (C) 2008 Nick Piggin + * Copyright (C) 2008 Novell Inc. + */ +#undef DEBUG + +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/hugetlb.h> +#include <linux/vmstat.h> +#include <linux/pagemap.h> +#include <linux/rwsem.h> +#include <asm/pgtable.h> + +/* + * The performance critical leaf functions are made noinline otherwise gcc + * inlines everything into a single function which results in too much + * register pressure. + */ +static noinline int gup_pte_range(pmd_t pmd, unsigned long addr, + unsigned long end, int write, struct page **pages, int *nr) +{ + unsigned long mask, result; + pte_t *ptep; + + result = _PAGE_PRESENT|_PAGE_USER; + if (write) + result |= _PAGE_RW; + mask = result | _PAGE_SPECIAL; + + ptep = pte_offset_kernel(&pmd, addr); + do { + pte_t pte = *ptep; + struct page *page; + + if ((pte_val(pte) & mask) != result) + return 0; + VM_BUG_ON(!pfn_valid(pte_pfn(pte))); + page = pte_page(pte); + if (!page_cache_get_speculative(page)) + return 0; + if (unlikely(pte != *ptep)) { + put_page(page); + return 0; + } + pages[*nr] = page; + (*nr)++; + + } while (ptep++, addr += PAGE_SIZE, addr != end); + + return 1; +} + +#ifdef CONFIG_HUGETLB_PAGE +static noinline int gup_huge_pte(pte_t *ptep, struct hstate *hstate, + unsigned long *addr, unsigned long end, + int write, struct page **pages, int *nr) +{ + unsigned long mask; + unsigned long pte_end; + struct page *head, *page; + pte_t pte; + int refs; + + pte_end = (*addr + huge_page_size(hstate)) & huge_page_mask(hstate); + if (pte_end < end) + end = pte_end; + + pte = *ptep; + mask = _PAGE_PRESENT|_PAGE_USER; + if (write) + mask |= _PAGE_RW; + if ((pte_val(pte) & mask) != mask) + return 0; + /* hugepages are never "special" */ + VM_BUG_ON(!pfn_valid(pte_pfn(pte))); + + refs = 0; + head = pte_page(pte); + page = head + ((*addr & ~huge_page_mask(hstate)) >> PAGE_SHIFT); + do { + VM_BUG_ON(compound_head(page) != head); + pages[*nr] = page; + (*nr)++; + page++; + refs++; + } while (*addr += PAGE_SIZE, *addr != end); + + if (!page_cache_add_speculative(head, refs)) { + *nr -= refs; + return 0; + } + if (unlikely(pte != *ptep)) { + /* Could be optimized better */ + while (*nr) { + put_page(page); + (*nr)--; + } + } + + return 1; +} +#endif /* CONFIG_HUGETLB_PAGE */ + +static int gup_pmd_range(pud_t pud, unsigned long addr, unsigned long end, + int write, struct page **pages, int *nr) +{ + unsigned long next; + pmd_t *pmdp; + + pmdp = pmd_offset(&pud, addr); + do { + pmd_t pmd = *pmdp; + + next = pmd_addr_end(addr, end); + if (pmd_none(pmd)) + return 0; + if (!gup_pte_range(pmd, addr, next, write, pages, nr)) + return 0; + } while (pmdp++, addr = next, addr != end); + + return 1; +} + +static int gup_pud_range(pgd_t pgd, unsigned long addr, unsigned long end, + int write, struct page **pages, int *nr) +{ + unsigned long next; + pud_t *pudp; + + pudp = pud_offset(&pgd, addr); + do { + pud_t pud = *pudp; + + next = pud_addr_end(addr, end); + if (pud_none(pud)) + return 0; + if (!gup_pmd_range(pud, addr, next, write, pages, nr)) + return 0; + } while (pudp++, addr = next, addr != end); + + return 1; +} + +int get_user_pages_fast(unsigned long start, int nr_pages, int write, + struct page **pages) +{ + struct mm_struct *mm = current->mm; + unsigned long addr, len, end; + unsigned long next; + pgd_t *pgdp; + int psize, nr = 0; + unsigned int shift; + + pr_debug("%s(%lx,%x,%s)\n", __func__, start, nr_pages, write ? "write" : "read"); + + start &= PAGE_MASK; + addr = start; + len = (unsigned long) nr_pages << PAGE_SHIFT; + end = start + len; + + if (unlikely(!access_ok(write ? VERIFY_WRITE : VERIFY_READ, + start, len))) + goto slow_irqon; + + pr_debug(" aligned: %lx .. %lx\n", start, end); + +#ifdef CONFIG_HUGETLB_PAGE + /* We bail out on slice boundary crossing when hugetlb is + * enabled in order to not have to deal with two different + * page table formats + */ + if (addr < SLICE_LOW_TOP) { + if (end > SLICE_LOW_TOP) + goto slow_irqon; + + if (unlikely(GET_LOW_SLICE_INDEX(addr) != + GET_LOW_SLICE_INDEX(end - 1))) + goto slow_irqon; + } else { + if (unlikely(GET_HIGH_SLICE_INDEX(addr) != + GET_HIGH_SLICE_INDEX(end - 1))) + goto slow_irqon; + } +#endif /* CONFIG_HUGETLB_PAGE */ + + /* + * XXX: batch / limit 'nr', to avoid large irq off latency + * needs some instrumenting to determine the common sizes used by + * important workloads (eg. DB2), and whether limiting the batch size + * will decrease performance. + * + * It seems like we're in the clear for the moment. Direct-IO is + * the main guy that batches up lots of get_user_pages, and even + * they are limited to 64-at-a-time which is not so many. + */ + /* + * This doesn't prevent pagetable teardown, but does prevent + * the pagetables from being freed on powerpc. + * + * So long as we atomically load page table pointers versus teardown, + * we can follow the address down to the the page and take a ref on it. + */ + local_irq_disable(); + + psize = get_slice_psize(mm, addr); + shift = mmu_psize_defs[psize].shift; + +#ifdef CONFIG_HUGETLB_PAGE + if (unlikely(mmu_huge_psizes[psize])) { + pte_t *ptep; + unsigned long a = addr; + unsigned long sz = ((1UL) << shift); + struct hstate *hstate = size_to_hstate(sz); + + BUG_ON(!hstate); + /* + * XXX: could be optimized to avoid hstate + * lookup entirely (just use shift) + */ + + do { + VM_BUG_ON(shift != mmu_psize_defs[get_slice_psize(mm, a)].shift); + ptep = huge_pte_offset(mm, a); + pr_debug(" %016lx: huge ptep %p\n", a, ptep); + if (!ptep || !gup_huge_pte(ptep, hstate, &a, end, write, pages, + &nr)) + goto slow; + } while (a != end); + } else +#endif /* CONFIG_HUGETLB_PAGE */ + { + pgdp = pgd_offset(mm, addr); + do { + pgd_t pgd = *pgdp; + + VM_BUG_ON(shift != mmu_psize_defs[get_slice_psize(mm, addr)].shift); + pr_debug(" %016lx: normal pgd %p\n", addr, (void *)pgd); + next = pgd_addr_end(addr, end); + if (pgd_none(pgd)) + goto slow; + if (!gup_pud_range(pgd, addr, next, write, pages, &nr)) + goto slow; + } while (pgdp++, addr = next, addr != end); + } + local_irq_enable(); + + VM_BUG_ON(nr != (end - start) >> PAGE_SHIFT); + return nr; + + { + int ret; + +slow: + local_irq_enable(); +slow_irqon: + pr_debug(" slow path ! nr = %d\n", nr); + + /* Try to get the remaining pages with get_user_pages */ + start += nr << PAGE_SHIFT; + pages += nr; + + down_read(&mm->mmap_sem); + ret = get_user_pages(current, mm, start, + (end - start) >> PAGE_SHIFT, write, 0, pages, NULL); + up_read(&mm->mmap_sem); + + /* Have to be a bit careful with return values */ + if (nr > 0) { + if (ret < 0) + ret = nr; + else + ret += nr; + } + + return ret; + } +} diff --git a/arch/powerpc/mm/hugetlbpage.c b/arch/powerpc/mm/hugetlbpage.c index ed0aab0208a6..f1c2d55b4377 100644 --- a/arch/powerpc/mm/hugetlbpage.c +++ b/arch/powerpc/mm/hugetlbpage.c @@ -736,14 +736,21 @@ static int __init hugetlbpage_init(void) if (!cpu_has_feature(CPU_FTR_16M_PAGE)) return -ENODEV; + /* Add supported huge page sizes. Need to change HUGE_MAX_HSTATE * and adjust PTE_NONCACHE_NUM if the number of supported huge page * sizes changes. */ set_huge_psize(MMU_PAGE_16M); - set_huge_psize(MMU_PAGE_64K); set_huge_psize(MMU_PAGE_16G); + /* Temporarily disable support for 64K huge pages when 64K SPU local + * store support is enabled as the current implementation conflicts. + */ +#ifndef CONFIG_SPU_FS_64K_LS + set_huge_psize(MMU_PAGE_64K); +#endif + for (psize = 0; psize < MMU_PAGE_COUNT; ++psize) { if (mmu_huge_psizes[psize]) { huge_pgtable_cache(psize) = kmem_cache_create( diff --git a/arch/powerpc/platforms/83xx/mpc832x_mds.c b/arch/powerpc/platforms/83xx/mpc832x_mds.c index dd4be4aee314..ec43477caa63 100644 --- a/arch/powerpc/platforms/83xx/mpc832x_mds.c +++ b/arch/powerpc/platforms/83xx/mpc832x_mds.c @@ -105,6 +105,7 @@ static void __init mpc832x_sys_setup_arch(void) static struct of_device_id mpc832x_ids[] = { { .type = "soc", }, { .compatible = "soc", }, + { .compatible = "simple-bus", }, { .type = "qe", }, { .compatible = "fsl,qe", }, {}, diff --git a/arch/powerpc/platforms/83xx/mpc832x_rdb.c b/arch/powerpc/platforms/83xx/mpc832x_rdb.c index f049d692d4c8..0300268ce5b8 100644 --- a/arch/powerpc/platforms/83xx/mpc832x_rdb.c +++ b/arch/powerpc/platforms/83xx/mpc832x_rdb.c @@ -115,6 +115,7 @@ static void __init mpc832x_rdb_setup_arch(void) static struct of_device_id mpc832x_ids[] = { { .type = "soc", }, { .compatible = "soc", }, + { .compatible = "simple-bus", }, { .type = "qe", }, { .compatible = "fsl,qe", }, {}, diff --git a/arch/powerpc/platforms/83xx/mpc834x_itx.c b/arch/powerpc/platforms/83xx/mpc834x_itx.c index 7301d77a08ee..76092d37c7d9 100644 --- a/arch/powerpc/platforms/83xx/mpc834x_itx.c +++ b/arch/powerpc/platforms/83xx/mpc834x_itx.c @@ -41,6 +41,7 @@ static struct of_device_id __initdata mpc834x_itx_ids[] = { { .compatible = "fsl,pq2pro-localbus", }, + { .compatible = "simple-bus", }, {}, }; diff --git a/arch/powerpc/platforms/83xx/mpc834x_mds.c b/arch/powerpc/platforms/83xx/mpc834x_mds.c index 30d509aa9f08..fc3f2ed1f3e9 100644 --- a/arch/powerpc/platforms/83xx/mpc834x_mds.c +++ b/arch/powerpc/platforms/83xx/mpc834x_mds.c @@ -111,6 +111,7 @@ static void __init mpc834x_mds_init_IRQ(void) static struct of_device_id mpc834x_ids[] = { { .type = "soc", }, { .compatible = "soc", }, + { .compatible = "simple-bus", }, {}, }; diff --git a/arch/powerpc/platforms/83xx/mpc836x_mds.c b/arch/powerpc/platforms/83xx/mpc836x_mds.c index 75b80e836576..9d46e5bdd101 100644 --- a/arch/powerpc/platforms/83xx/mpc836x_mds.c +++ b/arch/powerpc/platforms/83xx/mpc836x_mds.c @@ -136,6 +136,7 @@ static void __init mpc836x_mds_setup_arch(void) static struct of_device_id mpc836x_ids[] = { { .type = "soc", }, { .compatible = "soc", }, + { .compatible = "simple-bus", }, { .type = "qe", }, { .compatible = "fsl,qe", }, {}, diff --git a/arch/powerpc/platforms/83xx/sbc834x.c b/arch/powerpc/platforms/83xx/sbc834x.c index fc21f5c15bab..156c4e218009 100644 --- a/arch/powerpc/platforms/83xx/sbc834x.c +++ b/arch/powerpc/platforms/83xx/sbc834x.c @@ -83,6 +83,7 @@ static void __init sbc834x_init_IRQ(void) static struct __initdata of_device_id sbc834x_ids[] = { { .type = "soc", }, { .compatible = "soc", }, + { .compatible = "simple-bus", }, {}, }; diff --git a/arch/powerpc/platforms/85xx/ksi8560.c b/arch/powerpc/platforms/85xx/ksi8560.c index 2145adeb220c..8a3b117b6ce2 100644 --- a/arch/powerpc/platforms/85xx/ksi8560.c +++ b/arch/powerpc/platforms/85xx/ksi8560.c @@ -222,6 +222,7 @@ static void ksi8560_show_cpuinfo(struct seq_file *m) static struct of_device_id __initdata of_bus_ids[] = { { .type = "soc", }, + { .type = "simple-bus", }, { .name = "cpm", }, { .name = "localbus", }, {}, diff --git a/arch/powerpc/platforms/85xx/mpc8536_ds.c b/arch/powerpc/platforms/85xx/mpc8536_ds.c index 6b846aa1ced9..1bf5aefdfeb1 100644 --- a/arch/powerpc/platforms/85xx/mpc8536_ds.c +++ b/arch/powerpc/platforms/85xx/mpc8536_ds.c @@ -91,6 +91,7 @@ static void __init mpc8536_ds_setup_arch(void) static struct of_device_id __initdata mpc8536_ds_ids[] = { { .type = "soc", }, { .compatible = "soc", }, + { .compatible = "simple-bus", }, {}, }; diff --git a/arch/powerpc/platforms/85xx/mpc85xx_ads.c b/arch/powerpc/platforms/85xx/mpc85xx_ads.c index ba498d6f2d02..d17807a6b89a 100644 --- a/arch/powerpc/platforms/85xx/mpc85xx_ads.c +++ b/arch/powerpc/platforms/85xx/mpc85xx_ads.c @@ -230,6 +230,7 @@ static struct of_device_id __initdata of_bus_ids[] = { { .type = "soc", }, { .name = "cpm", }, { .name = "localbus", }, + { .compatible = "simple-bus", }, {}, }; diff --git a/arch/powerpc/platforms/85xx/mpc85xx_ds.c b/arch/powerpc/platforms/85xx/mpc85xx_ds.c index 00c535806647..483b65cbabae 100644 --- a/arch/powerpc/platforms/85xx/mpc85xx_ds.c +++ b/arch/powerpc/platforms/85xx/mpc85xx_ds.c @@ -186,6 +186,7 @@ static int __init mpc8544_ds_probe(void) static struct of_device_id __initdata mpc85xxds_ids[] = { { .type = "soc", }, { .compatible = "soc", }, + { .compatible = "simple-bus", }, {}, }; diff --git a/arch/powerpc/platforms/85xx/mpc85xx_mds.c b/arch/powerpc/platforms/85xx/mpc85xx_mds.c index 43a459f63e31..2494c5155919 100644 --- a/arch/powerpc/platforms/85xx/mpc85xx_mds.c +++ b/arch/powerpc/platforms/85xx/mpc85xx_mds.c @@ -260,6 +260,7 @@ machine_arch_initcall(mpc85xx_mds, board_fixups); static struct of_device_id mpc85xx_ids[] = { { .type = "soc", }, { .compatible = "soc", }, + { .compatible = "simple-bus", }, { .type = "qe", }, { .compatible = "fsl,qe", }, {}, diff --git a/arch/powerpc/platforms/85xx/sbc8560.c b/arch/powerpc/platforms/85xx/sbc8560.c index 2c580cd24e4f..6509ade71668 100644 --- a/arch/powerpc/platforms/85xx/sbc8560.c +++ b/arch/powerpc/platforms/85xx/sbc8560.c @@ -217,6 +217,7 @@ static struct of_device_id __initdata of_bus_ids[] = { { .type = "soc", }, { .name = "cpm", }, { .name = "localbus", }, + { .compatible = "simple-bus", }, {}, }; diff --git a/arch/powerpc/platforms/8xx/Kconfig b/arch/powerpc/platforms/8xx/Kconfig index 6fc849e51e48..71d7562e190b 100644 --- a/arch/powerpc/platforms/8xx/Kconfig +++ b/arch/powerpc/platforms/8xx/Kconfig @@ -105,6 +105,16 @@ config 8xx_COPYBACK If in doubt, say Y here. +config 8xx_GPIO + bool "GPIO API Support" + select GENERIC_GPIO + select ARCH_REQUIRE_GPIOLIB + help + Saying Y here will cause the ports on an MPC8xx processor to be used + with the GPIO API. If you say N here, the kernel needs less memory. + + If in doubt, say Y here. + config 8xx_CPU6 bool "CPU6 Silicon Errata (860 Pre Rev. C)" help diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig index 1d0968775c0a..4c900efa164e 100644 --- a/arch/powerpc/platforms/Kconfig +++ b/arch/powerpc/platforms/Kconfig @@ -254,6 +254,8 @@ config CPM2 select CPM select PPC_LIB_RHEAP select PPC_PCI_CHOICE + select ARCH_REQUIRE_GPIOLIB + select GENERIC_GPIO help The CPM2 (Communications Processor Module) is a coprocessor on embedded CPUs made by Freescale. Selecting this option means that @@ -281,6 +283,7 @@ config FSL_ULI1575 config CPM bool + select PPC_CLOCK config OF_RTC bool diff --git a/arch/powerpc/platforms/cell/cbe_cpufreq_pmi.c b/arch/powerpc/platforms/cell/cbe_cpufreq_pmi.c index 69288f653144..3233fe84d158 100644 --- a/arch/powerpc/platforms/cell/cbe_cpufreq_pmi.c +++ b/arch/powerpc/platforms/cell/cbe_cpufreq_pmi.c @@ -96,6 +96,12 @@ static int pmi_notifier(struct notifier_block *nb, struct cpufreq_frequency_table *cbe_freqs; u8 node; + /* Should this really be called for CPUFREQ_ADJUST, CPUFREQ_INCOMPATIBLE + * and CPUFREQ_NOTIFY policy events?) + */ + if (event == CPUFREQ_START) + return 0; + cbe_freqs = cpufreq_frequency_get_table(policy->cpu); node = cbe_cpu_to_node(policy->cpu); diff --git a/arch/powerpc/platforms/powermac/setup.c b/arch/powerpc/platforms/powermac/setup.c index 31635446901a..88ccf3a08a9c 100644 --- a/arch/powerpc/platforms/powermac/setup.c +++ b/arch/powerpc/platforms/powermac/setup.c @@ -541,6 +541,78 @@ static int __init pmac_declare_of_platform_devices(void) } machine_device_initcall(powermac, pmac_declare_of_platform_devices); +#ifdef CONFIG_SERIAL_PMACZILOG_CONSOLE +/* + * This is called very early, as part of console_init() (typically just after + * time_init()). This function is respondible for trying to find a good + * default console on serial ports. It tries to match the open firmware + * default output with one of the available serial console drivers. + */ +static int __init check_pmac_serial_console(void) +{ + struct device_node *prom_stdout = NULL; + int offset = 0; + const char *name; +#ifdef CONFIG_SERIAL_PMACZILOG_TTYS + char *devname = "ttyS"; +#else + char *devname = "ttyPZ"; +#endif + + pr_debug(" -> check_pmac_serial_console()\n"); + + /* The user has requested a console so this is already set up. */ + if (strstr(boot_command_line, "console=")) { + pr_debug(" console was specified !\n"); + return -EBUSY; + } + + if (!of_chosen) { + pr_debug(" of_chosen is NULL !\n"); + return -ENODEV; + } + + /* We are getting a weird phandle from OF ... */ + /* ... So use the full path instead */ + name = of_get_property(of_chosen, "linux,stdout-path", NULL); + if (name == NULL) { + pr_debug(" no linux,stdout-path !\n"); + return -ENODEV; + } + prom_stdout = of_find_node_by_path(name); + if (!prom_stdout) { + pr_debug(" can't find stdout package %s !\n", name); + return -ENODEV; + } + pr_debug("stdout is %s\n", prom_stdout->full_name); + + name = of_get_property(prom_stdout, "name", NULL); + if (!name) { + pr_debug(" stdout package has no name !\n"); + goto not_found; + } + + if (strcmp(name, "ch-a") == 0) + offset = 0; + else if (strcmp(name, "ch-b") == 0) + offset = 1; + else + goto not_found; + of_node_put(prom_stdout); + + pr_debug("Found serial console at %s%d\n", devname, offset); + + return add_preferred_console(devname, offset, NULL); + + not_found: + pr_debug("No preferred console found !\n"); + of_node_put(prom_stdout); + return -ENODEV; +} +console_initcall(check_pmac_serial_console); + +#endif /* CONFIG_SERIAL_PMACZILOG_CONSOLE */ + /* * Called very early, MMU is off, device-tree isn't unflattened */ diff --git a/arch/powerpc/platforms/powermac/udbg_scc.c b/arch/powerpc/platforms/powermac/udbg_scc.c index 47de4d3fc167..572771fd8463 100644 --- a/arch/powerpc/platforms/powermac/udbg_scc.c +++ b/arch/powerpc/platforms/powermac/udbg_scc.c @@ -125,13 +125,23 @@ void udbg_scc_init(int force_scc) out_8(sccc, 0xc0); /* If SCC was the OF output port, read the BRG value, else - * Setup for 57600 8N1 + * Setup for 38400 or 57600 8N1 depending on the machine */ if (ch_def != NULL) { out_8(sccc, 13); scc_inittab[1] = in_8(sccc); out_8(sccc, 12); scc_inittab[3] = in_8(sccc); + } else if (machine_is_compatible("RackMac1,1") + || machine_is_compatible("RackMac1,2") + || machine_is_compatible("MacRISC4")) { + /* Xserves and G5s default to 57600 */ + scc_inittab[1] = 0; + scc_inittab[3] = 0; + } else { + /* Others default to 38400 */ + scc_inittab[1] = 0; + scc_inittab[3] = 1; } for (i = 0; i < sizeof(scc_inittab); ++i) diff --git a/arch/powerpc/platforms/pseries/cmm.c b/arch/powerpc/platforms/pseries/cmm.c index c6b3be03168b..38fe32a7cc70 100644 --- a/arch/powerpc/platforms/pseries/cmm.c +++ b/arch/powerpc/platforms/pseries/cmm.c @@ -289,7 +289,9 @@ static int cmm_thread(void *dummy) } #define CMM_SHOW(name, format, args...) \ - static ssize_t show_##name(struct sys_device *dev, char *buf) \ + static ssize_t show_##name(struct sys_device *dev, \ + struct sysdev_attribute *attr, \ + char *buf) \ { \ return sprintf(buf, format, ##args); \ } \ @@ -298,12 +300,14 @@ static int cmm_thread(void *dummy) CMM_SHOW(loaned_kb, "%lu\n", PAGES2KB(loaned_pages)); CMM_SHOW(loaned_target_kb, "%lu\n", PAGES2KB(loaned_pages_target)); -static ssize_t show_oom_pages(struct sys_device *dev, char *buf) +static ssize_t show_oom_pages(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { return sprintf(buf, "%lu\n", PAGES2KB(oom_freed_pages)); } static ssize_t store_oom_pages(struct sys_device *dev, + struct sysdev_attribute *attr, const char *buf, size_t count) { unsigned long val = simple_strtoul (buf, NULL, 10); diff --git a/arch/powerpc/sysdev/cpm1.c b/arch/powerpc/sysdev/cpm1.c index 661df42830b9..4a04823e8423 100644 --- a/arch/powerpc/sysdev/cpm1.c +++ b/arch/powerpc/sysdev/cpm1.c @@ -30,6 +30,7 @@ #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/module.h> +#include <linux/spinlock.h> #include <asm/page.h> #include <asm/pgtable.h> #include <asm/8xx_immap.h> @@ -42,6 +43,10 @@ #include <asm/fs_pd.h> +#ifdef CONFIG_8xx_GPIO +#include <linux/of_gpio.h> +#endif + #define CPM_MAP_SIZE (0x4000) cpm8xx_t __iomem *cpmp; /* Pointer to comm processor space */ @@ -290,20 +295,24 @@ struct cpm_ioport16 { __be16 res[3]; }; -struct cpm_ioport32 { - __be32 dir, par, sor; +struct cpm_ioport32b { + __be32 dir, par, odr, dat; +}; + +struct cpm_ioport32e { + __be32 dir, par, sor, odr, dat; }; static void cpm1_set_pin32(int port, int pin, int flags) { - struct cpm_ioport32 __iomem *iop; + struct cpm_ioport32e __iomem *iop; pin = 1 << (31 - pin); if (port == CPM_PORTB) - iop = (struct cpm_ioport32 __iomem *) + iop = (struct cpm_ioport32e __iomem *) &mpc8xx_immr->im_cpm.cp_pbdir; else - iop = (struct cpm_ioport32 __iomem *) + iop = (struct cpm_ioport32e __iomem *) &mpc8xx_immr->im_cpm.cp_pedir; if (flags & CPM_PIN_OUTPUT) @@ -498,3 +507,251 @@ int cpm1_clk_setup(enum cpm_clk_target target, int clock, int mode) return 0; } + +/* + * GPIO LIB API implementation + */ +#ifdef CONFIG_8xx_GPIO + +struct cpm1_gpio16_chip { + struct of_mm_gpio_chip mm_gc; + spinlock_t lock; + + /* shadowed data register to clear/set bits safely */ + u16 cpdata; +}; + +static inline struct cpm1_gpio16_chip * +to_cpm1_gpio16_chip(struct of_mm_gpio_chip *mm_gc) +{ + return container_of(mm_gc, struct cpm1_gpio16_chip, mm_gc); +} + +static void cpm1_gpio16_save_regs(struct of_mm_gpio_chip *mm_gc) +{ + struct cpm1_gpio16_chip *cpm1_gc = to_cpm1_gpio16_chip(mm_gc); + struct cpm_ioport16 __iomem *iop = mm_gc->regs; + + cpm1_gc->cpdata = in_be16(&iop->dat); +} + +static int cpm1_gpio16_get(struct gpio_chip *gc, unsigned int gpio) +{ + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + struct cpm_ioport16 __iomem *iop = mm_gc->regs; + u16 pin_mask; + + pin_mask = 1 << (15 - gpio); + + return !!(in_be16(&iop->dat) & pin_mask); +} + +static void cpm1_gpio16_set(struct gpio_chip *gc, unsigned int gpio, int value) +{ + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + struct cpm1_gpio16_chip *cpm1_gc = to_cpm1_gpio16_chip(mm_gc); + struct cpm_ioport16 __iomem *iop = mm_gc->regs; + unsigned long flags; + u16 pin_mask = 1 << (15 - gpio); + + spin_lock_irqsave(&cpm1_gc->lock, flags); + + if (value) + cpm1_gc->cpdata |= pin_mask; + else + cpm1_gc->cpdata &= ~pin_mask; + + out_be16(&iop->dat, cpm1_gc->cpdata); + + spin_unlock_irqrestore(&cpm1_gc->lock, flags); +} + +static int cpm1_gpio16_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) +{ + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + struct cpm_ioport16 __iomem *iop = mm_gc->regs; + u16 pin_mask; + + pin_mask = 1 << (15 - gpio); + + setbits16(&iop->dir, pin_mask); + + cpm1_gpio16_set(gc, gpio, val); + + return 0; +} + +static int cpm1_gpio16_dir_in(struct gpio_chip *gc, unsigned int gpio) +{ + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + struct cpm_ioport16 __iomem *iop = mm_gc->regs; + u16 pin_mask; + + pin_mask = 1 << (15 - gpio); + + clrbits16(&iop->dir, pin_mask); + + return 0; +} + +int cpm1_gpiochip_add16(struct device_node *np) +{ + struct cpm1_gpio16_chip *cpm1_gc; + struct of_mm_gpio_chip *mm_gc; + struct of_gpio_chip *of_gc; + struct gpio_chip *gc; + + cpm1_gc = kzalloc(sizeof(*cpm1_gc), GFP_KERNEL); + if (!cpm1_gc) + return -ENOMEM; + + spin_lock_init(&cpm1_gc->lock); + + mm_gc = &cpm1_gc->mm_gc; + of_gc = &mm_gc->of_gc; + gc = &of_gc->gc; + + mm_gc->save_regs = cpm1_gpio16_save_regs; + of_gc->gpio_cells = 2; + gc->ngpio = 16; + gc->direction_input = cpm1_gpio16_dir_in; + gc->direction_output = cpm1_gpio16_dir_out; + gc->get = cpm1_gpio16_get; + gc->set = cpm1_gpio16_set; + + return of_mm_gpiochip_add(np, mm_gc); +} + +struct cpm1_gpio32_chip { + struct of_mm_gpio_chip mm_gc; + spinlock_t lock; + + /* shadowed data register to clear/set bits safely */ + u32 cpdata; +}; + +static inline struct cpm1_gpio32_chip * +to_cpm1_gpio32_chip(struct of_mm_gpio_chip *mm_gc) +{ + return container_of(mm_gc, struct cpm1_gpio32_chip, mm_gc); +} + +static void cpm1_gpio32_save_regs(struct of_mm_gpio_chip *mm_gc) +{ + struct cpm1_gpio32_chip *cpm1_gc = to_cpm1_gpio32_chip(mm_gc); + struct cpm_ioport32b __iomem *iop = mm_gc->regs; + + cpm1_gc->cpdata = in_be32(&iop->dat); +} + +static int cpm1_gpio32_get(struct gpio_chip *gc, unsigned int gpio) +{ + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + struct cpm_ioport32b __iomem *iop = mm_gc->regs; + u32 pin_mask; + + pin_mask = 1 << (31 - gpio); + + return !!(in_be32(&iop->dat) & pin_mask); +} + +static void cpm1_gpio32_set(struct gpio_chip *gc, unsigned int gpio, int value) +{ + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + struct cpm1_gpio32_chip *cpm1_gc = to_cpm1_gpio32_chip(mm_gc); + struct cpm_ioport32b __iomem *iop = mm_gc->regs; + unsigned long flags; + u32 pin_mask = 1 << (31 - gpio); + + spin_lock_irqsave(&cpm1_gc->lock, flags); + + if (value) + cpm1_gc->cpdata |= pin_mask; + else + cpm1_gc->cpdata &= ~pin_mask; + + out_be32(&iop->dat, cpm1_gc->cpdata); + + spin_unlock_irqrestore(&cpm1_gc->lock, flags); +} + +static int cpm1_gpio32_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) +{ + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + struct cpm_ioport32b __iomem *iop = mm_gc->regs; + u32 pin_mask; + + pin_mask = 1 << (31 - gpio); + + setbits32(&iop->dir, pin_mask); + + cpm1_gpio32_set(gc, gpio, val); + + return 0; +} + +static int cpm1_gpio32_dir_in(struct gpio_chip *gc, unsigned int gpio) +{ + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + struct cpm_ioport32b __iomem *iop = mm_gc->regs; + u32 pin_mask; + + pin_mask = 1 << (31 - gpio); + + clrbits32(&iop->dir, pin_mask); + + return 0; +} + +int cpm1_gpiochip_add32(struct device_node *np) +{ + struct cpm1_gpio32_chip *cpm1_gc; + struct of_mm_gpio_chip *mm_gc; + struct of_gpio_chip *of_gc; + struct gpio_chip *gc; + + cpm1_gc = kzalloc(sizeof(*cpm1_gc), GFP_KERNEL); + if (!cpm1_gc) + return -ENOMEM; + + spin_lock_init(&cpm1_gc->lock); + + mm_gc = &cpm1_gc->mm_gc; + of_gc = &mm_gc->of_gc; + gc = &of_gc->gc; + + mm_gc->save_regs = cpm1_gpio32_save_regs; + of_gc->gpio_cells = 2; + gc->ngpio = 32; + gc->direction_input = cpm1_gpio32_dir_in; + gc->direction_output = cpm1_gpio32_dir_out; + gc->get = cpm1_gpio32_get; + gc->set = cpm1_gpio32_set; + + return of_mm_gpiochip_add(np, mm_gc); +} + +static int cpm_init_par_io(void) +{ + struct device_node *np; + + for_each_compatible_node(np, NULL, "fsl,cpm1-pario-bank-a") + cpm1_gpiochip_add16(np); + + for_each_compatible_node(np, NULL, "fsl,cpm1-pario-bank-b") + cpm1_gpiochip_add32(np); + + for_each_compatible_node(np, NULL, "fsl,cpm1-pario-bank-c") + cpm1_gpiochip_add16(np); + + for_each_compatible_node(np, NULL, "fsl,cpm1-pario-bank-d") + cpm1_gpiochip_add16(np); + + /* Port E uses CPM2 layout */ + for_each_compatible_node(np, NULL, "fsl,cpm1-pario-bank-e") + cpm2_gpiochip_add32(np); + return 0; +} +arch_initcall(cpm_init_par_io); + +#endif /* CONFIG_8xx_GPIO */ diff --git a/arch/powerpc/sysdev/cpm2.c b/arch/powerpc/sysdev/cpm2.c index 5a6c5dfc53ef..f1c3395633b9 100644 --- a/arch/powerpc/sysdev/cpm2.c +++ b/arch/powerpc/sysdev/cpm2.c @@ -115,16 +115,10 @@ EXPORT_SYMBOL(cpm_command); * Baud rate clocks are zero-based in the driver code (as that maps * to port numbers). Documentation uses 1-based numbering. */ -#define BRG_INT_CLK (get_brgfreq()) -#define BRG_UART_CLK (BRG_INT_CLK/16) - -/* This function is used by UARTS, or anything else that uses a 16x - * oversampled clock. - */ -void -cpm_setbrg(uint brg, uint rate) +void __cpm2_setbrg(uint brg, uint rate, uint clk, int div16, int src) { u32 __iomem *bp; + u32 val; /* This is good enough to get SMCs running..... */ @@ -135,34 +129,14 @@ cpm_setbrg(uint brg, uint rate) brg -= 4; } bp += brg; - out_be32(bp, (((BRG_UART_CLK / rate) - 1) << 1) | CPM_BRG_EN); - - cpm2_unmap(bp); -} - -/* This function is used to set high speed synchronous baud rate - * clocks. - */ -void -cpm2_fastbrg(uint brg, uint rate, int div16) -{ - u32 __iomem *bp; - u32 val; - - if (brg < 4) { - bp = cpm2_map_size(im_brgc1, 16); - } else { - bp = cpm2_map_size(im_brgc5, 16); - brg -= 4; - } - bp += brg; - val = ((BRG_INT_CLK / rate) << 1) | CPM_BRG_EN; + val = (((clk / rate) - 1) << 1) | CPM_BRG_EN | src; if (div16) val |= CPM_BRG_DIV16; out_be32(bp, val); cpm2_unmap(bp); } +EXPORT_SYMBOL(__cpm2_setbrg); int cpm2_clk_setup(enum cpm_clk_target target, int clock, int mode) { @@ -377,3 +351,14 @@ void cpm2_set_pin(int port, int pin, int flags) else clrbits32(&iop[port].odr, pin); } + +static int cpm_init_par_io(void) +{ + struct device_node *np; + + for_each_compatible_node(np, NULL, "fsl,cpm2-pario-bank") + cpm2_gpiochip_add32(np); + return 0; +} +arch_initcall(cpm_init_par_io); + diff --git a/arch/powerpc/sysdev/cpm_common.c b/arch/powerpc/sysdev/cpm_common.c index e4b7296acb2c..53da8a079f96 100644 --- a/arch/powerpc/sysdev/cpm_common.c +++ b/arch/powerpc/sysdev/cpm_common.c @@ -19,6 +19,8 @@ #include <linux/init.h> #include <linux/of_device.h> +#include <linux/spinlock.h> +#include <linux/of.h> #include <asm/udbg.h> #include <asm/io.h> @@ -28,6 +30,10 @@ #include <mm/mmu_decl.h> +#if defined(CONFIG_CPM2) || defined(CONFIG_8xx_GPIO) +#include <linux/of_gpio.h> +#endif + #ifdef CONFIG_PPC_EARLY_DEBUG_CPM static u32 __iomem *cpm_udbg_txdesc = (u32 __iomem __force *)CONFIG_PPC_EARLY_DEBUG_CPM_ADDR; @@ -207,3 +213,120 @@ dma_addr_t cpm_muram_dma(void __iomem *addr) return muram_pbase + ((u8 __iomem *)addr - muram_vbase); } EXPORT_SYMBOL(cpm_muram_dma); + +#if defined(CONFIG_CPM2) || defined(CONFIG_8xx_GPIO) + +struct cpm2_ioports { + u32 dir, par, sor, odr, dat; + u32 res[3]; +}; + +struct cpm2_gpio32_chip { + struct of_mm_gpio_chip mm_gc; + spinlock_t lock; + + /* shadowed data register to clear/set bits safely */ + u32 cpdata; +}; + +static inline struct cpm2_gpio32_chip * +to_cpm2_gpio32_chip(struct of_mm_gpio_chip *mm_gc) +{ + return container_of(mm_gc, struct cpm2_gpio32_chip, mm_gc); +} + +static void cpm2_gpio32_save_regs(struct of_mm_gpio_chip *mm_gc) +{ + struct cpm2_gpio32_chip *cpm2_gc = to_cpm2_gpio32_chip(mm_gc); + struct cpm2_ioports __iomem *iop = mm_gc->regs; + + cpm2_gc->cpdata = in_be32(&iop->dat); +} + +static int cpm2_gpio32_get(struct gpio_chip *gc, unsigned int gpio) +{ + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + struct cpm2_ioports __iomem *iop = mm_gc->regs; + u32 pin_mask; + + pin_mask = 1 << (31 - gpio); + + return !!(in_be32(&iop->dat) & pin_mask); +} + +static void cpm2_gpio32_set(struct gpio_chip *gc, unsigned int gpio, int value) +{ + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + struct cpm2_gpio32_chip *cpm2_gc = to_cpm2_gpio32_chip(mm_gc); + struct cpm2_ioports __iomem *iop = mm_gc->regs; + unsigned long flags; + u32 pin_mask = 1 << (31 - gpio); + + spin_lock_irqsave(&cpm2_gc->lock, flags); + + if (value) + cpm2_gc->cpdata |= pin_mask; + else + cpm2_gc->cpdata &= ~pin_mask; + + out_be32(&iop->dat, cpm2_gc->cpdata); + + spin_unlock_irqrestore(&cpm2_gc->lock, flags); +} + +static int cpm2_gpio32_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) +{ + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + struct cpm2_ioports __iomem *iop = mm_gc->regs; + u32 pin_mask; + + pin_mask = 1 << (31 - gpio); + + setbits32(&iop->dir, pin_mask); + + cpm2_gpio32_set(gc, gpio, val); + + return 0; +} + +static int cpm2_gpio32_dir_in(struct gpio_chip *gc, unsigned int gpio) +{ + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + struct cpm2_ioports __iomem *iop = mm_gc->regs; + u32 pin_mask; + + pin_mask = 1 << (31 - gpio); + + clrbits32(&iop->dir, pin_mask); + + return 0; +} + +int cpm2_gpiochip_add32(struct device_node *np) +{ + struct cpm2_gpio32_chip *cpm2_gc; + struct of_mm_gpio_chip *mm_gc; + struct of_gpio_chip *of_gc; + struct gpio_chip *gc; + + cpm2_gc = kzalloc(sizeof(*cpm2_gc), GFP_KERNEL); + if (!cpm2_gc) + return -ENOMEM; + + spin_lock_init(&cpm2_gc->lock); + + mm_gc = &cpm2_gc->mm_gc; + of_gc = &mm_gc->of_gc; + gc = &of_gc->gc; + + mm_gc->save_regs = cpm2_gpio32_save_regs; + of_gc->gpio_cells = 2; + gc->ngpio = 32; + gc->direction_input = cpm2_gpio32_dir_in; + gc->direction_output = cpm2_gpio32_dir_out; + gc->get = cpm2_gpio32_get; + gc->set = cpm2_gpio32_set; + + return of_mm_gpiochip_add(np, mm_gc); +} +#endif /* CONFIG_CPM2 || CONFIG_8xx_GPIO */ diff --git a/arch/powerpc/sysdev/rtc_cmos_setup.c b/arch/powerpc/sysdev/rtc_cmos_setup.c index c09ddc0dbeb3..c1879ebfd4f4 100644 --- a/arch/powerpc/sysdev/rtc_cmos_setup.c +++ b/arch/powerpc/sysdev/rtc_cmos_setup.c @@ -21,6 +21,7 @@ static int __init add_rtc(void) struct device_node *np; struct platform_device *pd; struct resource res[2]; + unsigned int num_res = 1; int ret; memset(&res, 0, sizeof(res)); @@ -41,14 +42,24 @@ static int __init add_rtc(void) if (res[0].start != RTC_PORT(0)) return -EINVAL; - /* Use a fixed interrupt value of 8 since on PPC if we are using this - * its off an i8259 which we ensure has interrupt numbers 0..15. */ - res[1].start = 8; - res[1].end = 8; - res[1].flags = IORESOURCE_IRQ; + np = of_find_compatible_node(NULL, NULL, "chrp,iic"); + if (!np) + np = of_find_compatible_node(NULL, NULL, "pnpPNP,000"); + if (np) { + of_node_put(np); + /* + * Use a fixed interrupt value of 8 since on PPC if we are + * using this its off an i8259 which we ensure has interrupt + * numbers 0..15. + */ + res[1].start = 8; + res[1].end = 8; + res[1].flags = IORESOURCE_IRQ; + num_res++; + } pd = platform_device_register_simple("rtc_cmos", -1, - &res[0], 2); + &res[0], num_res); if (IS_ERR(pd)) return PTR_ERR(pd); |