diff options
Diffstat (limited to 'arch')
472 files changed, 14726 insertions, 8052 deletions
diff --git a/arch/alpha/kernel/module.c b/arch/alpha/kernel/module.c index bd03dc94c72..026ba9af6d6 100644 --- a/arch/alpha/kernel/module.c +++ b/arch/alpha/kernel/module.c @@ -119,8 +119,7 @@ module_frob_arch_sections(Elf64_Ehdr *hdr, Elf64_Shdr *sechdrs, } nsyms = symtab->sh_size / sizeof(Elf64_Sym); - chains = kmalloc(nsyms * sizeof(struct got_entry), GFP_KERNEL); - memset(chains, 0, nsyms * sizeof(struct got_entry)); + chains = kcalloc(nsyms, sizeof(struct got_entry), GFP_KERNEL); got->sh_size = 0; got->sh_addralign = 8; diff --git a/arch/alpha/kernel/ptrace.c b/arch/alpha/kernel/ptrace.c index 0cd060598f9..83a78184226 100644 --- a/arch/alpha/kernel/ptrace.c +++ b/arch/alpha/kernel/ptrace.c @@ -315,9 +315,7 @@ do_sys_ptrace(long request, long pid, long addr, long data, /* When I and D space are separate, this will have to be fixed. */ case PTRACE_POKETEXT: /* write the word at location addr. */ case PTRACE_POKEDATA: - tmp = data; - copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 1); - ret = (copied == sizeof(tmp)) ? 0 : -EIO; + ret = generic_ptrace_pokedata(child, addr, data); break; case PTRACE_POKEUSR: /* write the specified register */ diff --git a/arch/alpha/kernel/smp.c b/arch/alpha/kernel/smp.c index 80cfb758ee2..b28731437c3 100644 --- a/arch/alpha/kernel/smp.c +++ b/arch/alpha/kernel/smp.c @@ -65,7 +65,7 @@ enum ipi_message_type { }; /* Set to a secondary's cpuid when it comes online. */ -static int smp_secondary_alive __initdata = 0; +static int smp_secondary_alive __devinitdata = 0; /* Which cpus ids came online. */ cpumask_t cpu_online_map; @@ -173,7 +173,7 @@ smp_callin(void) } /* Wait until hwrpb->txrdy is clear for cpu. Return -1 on timeout. */ -static int __init +static int __devinit wait_for_txrdy (unsigned long cpumask) { unsigned long timeout; @@ -358,7 +358,7 @@ secondary_cpu_start(int cpuid, struct task_struct *idle) /* * Bring one cpu online. */ -static int __init +static int __devinit smp_boot_one_cpu(int cpuid) { struct task_struct *idle; diff --git a/arch/alpha/kernel/traps.c b/arch/alpha/kernel/traps.c index d6e665d567b..ec0f05e0d8f 100644 --- a/arch/alpha/kernel/traps.c +++ b/arch/alpha/kernel/traps.c @@ -184,6 +184,7 @@ die_if_kernel(char * str, struct pt_regs *regs, long err, unsigned long *r9_15) #endif printk("%s(%d): %s %ld\n", current->comm, current->pid, str, err); dik_show_regs(regs, r9_15); + add_taint(TAINT_DIE); dik_show_trace((unsigned long *)(regs+1)); dik_show_code((unsigned int *)regs->pc); diff --git a/arch/alpha/kernel/vmlinux.lds.S b/arch/alpha/kernel/vmlinux.lds.S index 449e76f118d..fe13daa5cb2 100644 --- a/arch/alpha/kernel/vmlinux.lds.S +++ b/arch/alpha/kernel/vmlinux.lds.S @@ -3,7 +3,7 @@ OUTPUT_FORMAT("elf64-alpha") OUTPUT_ARCH(alpha) ENTRY(__start) -PHDRS { kernel PT_LOAD ; } +PHDRS { kernel PT_LOAD; note PT_NOTE; } jiffies = jiffies_64; SECTIONS { @@ -28,6 +28,9 @@ SECTIONS __ex_table : { *(__ex_table) } __stop___ex_table = .; + NOTES :kernel :note + .dummy : { *(.dummy) } :kernel + RODATA /* Will be freed after init */ @@ -69,10 +72,7 @@ SECTIONS . = ALIGN(8); SECURITY_INIT - . = ALIGN(8192); - __per_cpu_start = .; - .data.percpu : { *(.data.percpu) } - __per_cpu_end = .; + PERCPU(8192) . = ALIGN(2*8192); __init_end = .; diff --git a/arch/alpha/lib/checksum.c b/arch/alpha/lib/checksum.c index ab3761c437a..8698e0746f9 100644 --- a/arch/alpha/lib/checksum.c +++ b/arch/alpha/lib/checksum.c @@ -69,6 +69,7 @@ __wsum csum_tcpudp_nofold(__be32 saddr, __be32 daddr, result = (result & 0xffffffff) + (result >> 32); return (__force __wsum)result; } +EXPORT_SYMBOL(csum_tcpudp_nofold); /* * Do a 64-bit checksum on an arbitrary memory area.. diff --git a/arch/alpha/mm/fault.c b/arch/alpha/mm/fault.c index f5862792a16..a0e18da594d 100644 --- a/arch/alpha/mm/fault.c +++ b/arch/alpha/mm/fault.c @@ -148,21 +148,17 @@ do_page_fault(unsigned long address, unsigned long mmcsr, the fault. */ fault = handle_mm_fault(mm, vma, address, cause > 0); up_read(&mm->mmap_sem); - - switch (fault) { - case VM_FAULT_MINOR: - current->min_flt++; - break; - case VM_FAULT_MAJOR: - current->maj_flt++; - break; - case VM_FAULT_SIGBUS: - goto do_sigbus; - case VM_FAULT_OOM: - goto out_of_memory; - default: + if (unlikely(fault & VM_FAULT_ERROR)) { + if (fault & VM_FAULT_OOM) + goto out_of_memory; + else if (fault & VM_FAULT_SIGBUS) + goto do_sigbus; BUG(); } + if (fault & VM_FAULT_MAJOR) + current->maj_flt++; + else + current->min_flt++; return; /* Something tried to access memory that isn't in our memory map. diff --git a/arch/arm/configs/badge4_defconfig b/arch/arm/configs/badge4_defconfig index 821865f7560..b2bbf217c70 100644 --- a/arch/arm/configs/badge4_defconfig +++ b/arch/arm/configs/badge4_defconfig @@ -708,7 +708,6 @@ CONFIG_I2C_ALGOPCF=m # I2C Hardware Bus support # CONFIG_I2C_ELEKTOR=m -# CONFIG_I2C_ISA is not set # CONFIG_I2C_PARPORT is not set # CONFIG_I2C_PARPORT_LIGHT is not set # CONFIG_I2C_STUB is not set diff --git a/arch/arm/configs/clps7500_defconfig b/arch/arm/configs/clps7500_defconfig index af9ae538913..49e9f9d8b3d 100644 --- a/arch/arm/configs/clps7500_defconfig +++ b/arch/arm/configs/clps7500_defconfig @@ -536,7 +536,6 @@ CONFIG_I2C_ALGOBIT=y # I2C Hardware Bus support # # CONFIG_I2C_ELEKTOR is not set -# CONFIG_I2C_ISA is not set # CONFIG_I2C_PARPORT is not set # CONFIG_I2C_PARPORT_LIGHT is not set # CONFIG_I2C_PCA_ISA is not set diff --git a/arch/arm/configs/footbridge_defconfig b/arch/arm/configs/footbridge_defconfig index 2a612d23120..299dc22294a 100644 --- a/arch/arm/configs/footbridge_defconfig +++ b/arch/arm/configs/footbridge_defconfig @@ -748,7 +748,6 @@ CONFIG_I2C=m # CONFIG_I2C_ELEKTOR is not set # CONFIG_I2C_I801 is not set # CONFIG_I2C_I810 is not set -# CONFIG_I2C_ISA is not set # CONFIG_I2C_NFORCE2 is not set # CONFIG_I2C_PARPORT is not set # CONFIG_I2C_PARPORT_LIGHT is not set diff --git a/arch/arm/configs/neponset_defconfig b/arch/arm/configs/neponset_defconfig index e86794a10fc..92ccdc6492f 100644 --- a/arch/arm/configs/neponset_defconfig +++ b/arch/arm/configs/neponset_defconfig @@ -698,7 +698,6 @@ CONFIG_I2C_ALGOBIT=y # I2C Hardware Bus support # # CONFIG_I2C_ELEKTOR is not set -# CONFIG_I2C_ISA is not set # CONFIG_I2C_PARPORT_LIGHT is not set # CONFIG_I2C_STUB is not set # CONFIG_I2C_PCA_ISA is not set diff --git a/arch/arm/configs/picotux200_defconfig b/arch/arm/configs/picotux200_defconfig index 339c48953a6..3c0c4f192dc 100644 --- a/arch/arm/configs/picotux200_defconfig +++ b/arch/arm/configs/picotux200_defconfig @@ -735,7 +735,6 @@ CONFIG_I2C_CHARDEV=m # I2C Hardware Bus support # CONFIG_I2C_AT91=m -CONFIG_I2C_ISA=m # CONFIG_I2C_OCORES is not set # CONFIG_I2C_PARPORT_LIGHT is not set # CONFIG_I2C_STUB is not set diff --git a/arch/arm/configs/rpc_defconfig b/arch/arm/configs/rpc_defconfig index bc091264d35..8452dc8c7cc 100644 --- a/arch/arm/configs/rpc_defconfig +++ b/arch/arm/configs/rpc_defconfig @@ -558,7 +558,6 @@ CONFIG_I2C_ALGOBIT=y # # I2C Hardware Bus support # -# CONFIG_I2C_ISA is not set # CONFIG_I2C_PARPORT is not set # CONFIG_I2C_PARPORT_LIGHT is not set # CONFIG_I2C_STUB is not set diff --git a/arch/arm/configs/s3c2410_defconfig b/arch/arm/configs/s3c2410_defconfig index a850da377a2..1d5150e4d6b 100644 --- a/arch/arm/configs/s3c2410_defconfig +++ b/arch/arm/configs/s3c2410_defconfig @@ -826,7 +826,6 @@ CONFIG_I2C_ALGOBIT=m # I2C Hardware Bus support # # CONFIG_I2C_ELEKTOR is not set -CONFIG_I2C_ISA=m # CONFIG_I2C_OCORES is not set # CONFIG_I2C_PARPORT is not set # CONFIG_I2C_PARPORT_LIGHT is not set diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c index 6f2f46c2e40..78c9f1a3d41 100644 --- a/arch/arm/kernel/ptrace.c +++ b/arch/arm/kernel/ptrace.c @@ -657,7 +657,6 @@ static int ptrace_setcrunchregs(struct task_struct *tsk, void __user *ufp) long arch_ptrace(struct task_struct *child, long request, long addr, long data) { - unsigned long tmp; int ret; switch (request) { @@ -666,12 +665,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) */ case PTRACE_PEEKTEXT: case PTRACE_PEEKDATA: - ret = access_process_vm(child, addr, &tmp, - sizeof(unsigned long), 0); - if (ret == sizeof(unsigned long)) - ret = put_user(tmp, (unsigned long __user *) data); - else - ret = -EIO; + ret = generic_ptrace_peekdata(child, addr, data); break; case PTRACE_PEEKUSR: @@ -683,12 +677,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) */ case PTRACE_POKETEXT: case PTRACE_POKEDATA: - ret = access_process_vm(child, addr, &data, - sizeof(unsigned long), 1); - if (ret == sizeof(unsigned long)) - ret = 0; - else - ret = -EIO; + ret = generic_ptrace_pokedata(child, addr, data); break; case PTRACE_POKEUSR: diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c index 237f4999b9a..f2114bcf09d 100644 --- a/arch/arm/kernel/traps.c +++ b/arch/arm/kernel/traps.c @@ -249,6 +249,7 @@ NORET_TYPE void die(const char *str, struct pt_regs *regs, int err) bust_spinlocks(1); __die(str, err, thread, regs); bust_spinlocks(0); + add_taint(TAINT_DIE); spin_unlock_irq(&die_lock); if (in_interrupt()) diff --git a/arch/arm/kernel/vmlinux.lds.S b/arch/arm/kernel/vmlinux.lds.S index 2b7a8f5d8cf..5ff5406666b 100644 --- a/arch/arm/kernel/vmlinux.lds.S +++ b/arch/arm/kernel/vmlinux.lds.S @@ -66,6 +66,7 @@ SECTIONS . = ALIGN(4096); __per_cpu_start = .; *(.data.percpu) + *(.data.percpu.shared_aligned) __per_cpu_end = .; #ifndef CONFIG_XIP_KERNEL __init_begin = _stext; diff --git a/arch/arm/mach-at91/board-csb337.c b/arch/arm/mach-at91/board-csb337.c index e18a41e61f0..dde089922e3 100644 --- a/arch/arm/mach-at91/board-csb337.c +++ b/arch/arm/mach-at91/board-csb337.c @@ -23,6 +23,7 @@ #include <linux/mm.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/i2c.h> #include <linux/spi/spi.h> #include <linux/mtd/physmap.h> @@ -83,6 +84,13 @@ static struct at91_udc_data __initdata csb337_udc_data = { .pullup_pin = AT91_PIN_PA24, }; +static struct i2c_board_info __initdata csb337_i2c_devices[] = { + { I2C_BOARD_INFO("rtc-ds1307", 0x68), + .type = "ds1307", + }, +}; + + static struct at91_cf_data __initdata csb337_cf_data = { /* * connector P4 on the CSB 337 mates to @@ -161,6 +169,8 @@ static void __init csb337_board_init(void) at91_add_device_udc(&csb337_udc_data); /* I2C */ at91_add_device_i2c(); + i2c_register_board_info(0, csb337_i2c_devices, + ARRAY_SIZE(csb337_i2c_devices)); /* Compact Flash */ at91_set_gpio_input(AT91_PIN_PB22, 1); /* IOIS16 */ at91_add_device_cf(&csb337_cf_data); diff --git a/arch/arm/mach-iop13xx/pci.c b/arch/arm/mach-iop13xx/pci.c index 9d63d7f260c..99d94cb1baf 100644 --- a/arch/arm/mach-iop13xx/pci.c +++ b/arch/arm/mach-iop13xx/pci.c @@ -1002,11 +1002,10 @@ int iop13xx_pci_setup(int nr, struct pci_sys_data *sys) if (nr > 1) return 0; - res = kmalloc(sizeof(struct resource) * 2, GFP_KERNEL); + res = kcalloc(2, sizeof(struct resource), GFP_KERNEL); if (!res) panic("PCI: unable to alloc resources"); - memset(res, 0, sizeof(struct resource) * 2); /* 'nr' assumptions: * ATUX is always 0 diff --git a/arch/arm/mach-iop32x/n2100.c b/arch/arm/mach-iop32x/n2100.c index 390a97d39e5..1873bd8cd1b 100644 --- a/arch/arm/mach-iop32x/n2100.c +++ b/arch/arm/mach-iop32x/n2100.c @@ -25,6 +25,7 @@ #include <linux/serial_core.h> #include <linux/serial_8250.h> #include <linux/mtd/physmap.h> +#include <linux/i2c.h> #include <linux/platform_device.h> #include <linux/reboot.h> #include <asm/hardware.h> @@ -199,6 +200,12 @@ static struct platform_device n2100_serial_device = { .resource = &n2100_uart_resource, }; +static struct i2c_board_info __initdata n2100_i2c_devices[] = { + { + I2C_BOARD_INFO("rtc-rs5c372", 0x32), + .type = "rs5c372b", + }, +}; /* * Pull PCA9532 GPIO #8 low to power off the machine. @@ -248,6 +255,9 @@ static void __init n2100_init_machine(void) platform_device_register(&iop3xx_dma_0_channel); platform_device_register(&iop3xx_dma_1_channel); + i2c_register_board_info(0, n2100_i2c_devices, + ARRAY_SIZE(n2100_i2c_devices)); + pm_power_off = n2100_power_off; init_timer(&power_button_poll_timer); diff --git a/arch/arm/mm/fault.c b/arch/arm/mm/fault.c index 75d491448e4..c04124a095c 100644 --- a/arch/arm/mm/fault.c +++ b/arch/arm/mm/fault.c @@ -183,20 +183,20 @@ good_area: */ survive: fault = handle_mm_fault(mm, vma, addr & PAGE_MASK, fsr & (1 << 11)); - - /* - * Handle the "normal" cases first - successful and sigbus - */ - switch (fault) { - case VM_FAULT_MAJOR: + if (unlikely(fault & VM_FAULT_ERROR)) { + if (fault & VM_FAULT_OOM) + goto out_of_memory; + else if (fault & VM_FAULT_SIGBUS) + return fault; + BUG(); + } + if (fault & VM_FAULT_MAJOR) tsk->maj_flt++; - return fault; - case VM_FAULT_MINOR: + else tsk->min_flt++; - case VM_FAULT_SIGBUS: - return fault; - } + return fault; +out_of_memory: if (!is_init(tsk)) goto out; @@ -249,7 +249,7 @@ do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs) /* * Handle the "normal" case first - VM_FAULT_MAJOR / VM_FAULT_MINOR */ - if (fault >= VM_FAULT_MINOR) + if (likely(!(fault & VM_FAULT_ERROR))) return 0; /* @@ -259,8 +259,7 @@ do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs) if (!user_mode(regs)) goto no_context; - switch (fault) { - case VM_FAULT_OOM: + if (fault & VM_FAULT_OOM) { /* * We ran out of memory, or some other thing * happened to us that made us unable to handle @@ -269,17 +268,15 @@ do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs) printk("VM: killing process %s\n", tsk->comm); do_exit(SIGKILL); return 0; - - case VM_FAULT_SIGBUS: + } + if (fault & VM_FAULT_SIGBUS) { /* * We had some memory, but were unable to * successfully fix up this page fault. */ sig = SIGBUS; code = BUS_ADRERR; - break; - - default: + } else { /* * Something tried to access memory that * isn't in our memory map.. @@ -287,7 +284,6 @@ do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs) sig = SIGSEGV; code = fault == VM_FAULT_BADACCESS ? SEGV_ACCERR : SEGV_MAPERR; - break; } __do_user_fault(tsk, addr, fsr, sig, code, regs); diff --git a/arch/arm26/kernel/ptrace.c b/arch/arm26/kernel/ptrace.c index 41692795672..0fefb86970c 100644 --- a/arch/arm26/kernel/ptrace.c +++ b/arch/arm26/kernel/ptrace.c @@ -531,7 +531,6 @@ static int ptrace_setfpregs(struct task_struct *tsk, void *ufp) long arch_ptrace(struct task_struct *child, long request, long addr, long data) { - unsigned long tmp; int ret; switch (request) { @@ -540,12 +539,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) */ case PTRACE_PEEKTEXT: case PTRACE_PEEKDATA: - ret = access_process_vm(child, addr, &tmp, - sizeof(unsigned long), 0); - if (ret == sizeof(unsigned long)) - ret = put_user(tmp, (unsigned long *) data); - else - ret = -EIO; + ret = generic_ptrace_peekdata(child, addr, data); break; case PTRACE_PEEKUSR: @@ -557,12 +551,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) */ case PTRACE_POKETEXT: case PTRACE_POKEDATA: - ret = access_process_vm(child, addr, &data, - sizeof(unsigned long), 1); - if (ret == sizeof(unsigned long)) - ret = 0; - else - ret = -EIO; + ret = generic_ptrace_pokedata(child, addr, data); break; case PTRACE_POKEUSR: diff --git a/arch/arm26/kernel/traps.c b/arch/arm26/kernel/traps.c index d594fb59e94..2911e2eae80 100644 --- a/arch/arm26/kernel/traps.c +++ b/arch/arm26/kernel/traps.c @@ -185,6 +185,7 @@ NORET_TYPE void die(const char *str, struct pt_regs *regs, int err) printk("Internal error: %s: %x\n", str, err); printk("CPU: %d\n", smp_processor_id()); show_regs(regs); + add_taint(TAINT_DIE); printk("Process %s (pid: %d, stack limit = 0x%p)\n", current->comm, current->pid, end_of_stack(tsk)); diff --git a/arch/arm26/mm/fault.c b/arch/arm26/mm/fault.c index 93c0cee0fb5..dec638a0c8d 100644 --- a/arch/arm26/mm/fault.c +++ b/arch/arm26/mm/fault.c @@ -170,20 +170,20 @@ good_area: */ survive: fault = handle_mm_fault(mm, vma, addr & PAGE_MASK, DO_COW(fsr)); - - /* - * Handle the "normal" cases first - successful and sigbus - */ - switch (fault) { - case VM_FAULT_MAJOR: + if (unlikely(fault & VM_FAULT_ERROR)) { + if (fault & VM_FAULT_OOM) + goto out_of_memory; + else if (fault & VM_FAULT_SIGBUS) + return fault; + BUG(); + } + if (fault & VM_FAULT_MAJOR) tsk->maj_flt++; - return fault; - case VM_FAULT_MINOR: + else tsk->min_flt++; - case VM_FAULT_SIGBUS: - return fault; - } + return fault; +out_of_memory: fault = -3; /* out of memory */ if (!is_init(tsk)) goto out; @@ -225,13 +225,11 @@ int do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs) /* * Handle the "normal" case first */ - switch (fault) { - case VM_FAULT_MINOR: - case VM_FAULT_MAJOR: + if (likely(!(fault & VM_FAULT_ERROR))) return 0; - case VM_FAULT_SIGBUS: + if (fault & VM_FAULT_SIGBUS) goto do_sigbus; - } + /* else VM_FAULT_OOM */ /* * If we are in kernel mode at this point, we diff --git a/arch/avr32/Kconfig b/arch/avr32/Kconfig index 3ec76586877..d12346aaa88 100644 --- a/arch/avr32/Kconfig +++ b/arch/avr32/Kconfig @@ -113,6 +113,10 @@ config BOARD_ATNGW100 bool "ATNGW100 Network Gateway" endchoice +if BOARD_ATSTK1000 +source "arch/avr32/boards/atstk1000/Kconfig" +endif + choice prompt "Boot loader type" default LOADER_U_BOOT @@ -185,6 +189,27 @@ config CMDLINE endmenu +menu "Power managment options" + +menu "CPU Frequency scaling" + +source "drivers/cpufreq/Kconfig" + +config CPU_FREQ_AT32AP + bool "CPU frequency driver for AT32AP" + depends on CPU_FREQ && PLATFORM_AT32AP + default n + help + This enables the CPU frequency driver for AT32AP processors. + + For details, take a look in <file:Documentation/cpu-freq>. + + If in doubt, say N. + +endmenu + +endmenu + menu "Bus options" config PCI diff --git a/arch/avr32/boards/atstk1000/Kconfig b/arch/avr32/boards/atstk1000/Kconfig new file mode 100644 index 00000000000..71bc7d364fb --- /dev/null +++ b/arch/avr32/boards/atstk1000/Kconfig @@ -0,0 +1,53 @@ +# STK1000 customization + +if BOARD_ATSTK1002 + +config BOARD_ATSTK1002_CUSTOM + bool "Non-default STK-1002 jumper settings" + help + You will normally leave the jumpers on the CPU card at their + default settings. If you need to use certain peripherals, + you will need to change some of those jumpers. + +if BOARD_ATSTK1002_CUSTOM + +config BOARD_ATSTK1002_SW1_CUSTOM + bool "SW1: use SSC1 (not SPI0)" + help + This also prevents using the external DAC as an audio interface, + and means you can't initialize the on-board QVGA display. + +config BOARD_ATSTK1002_SW2_CUSTOM + bool "SW2: use IRDA or TIMER0 (not UART-A, MMC/SD, and PS2-A)" + help + If you change this you'll want an updated boot loader putting + the console on UART-C not UART-A. + +config BOARD_ATSTK1002_SW3_CUSTOM + bool "SW3: use TIMER1 (not SSC0 and GCLK)" + help + This also prevents using the external DAC as an audio interface. + +config BOARD_ATSTK1002_SW4_CUSTOM + bool "SW4: use ISI/Camera (not GPIOs, SPI1, and PS2-B)" + help + To use the camera interface you'll need a custom card (on the + PCI-format connector) connect a video sensor. + +config BOARD_ATSTK1002_SW5_CUSTOM + bool "SW5: use MACB1 (not LCDC)" + +config BOARD_ATSTK1002_SW6_CUSTOM + bool "SW6: more GPIOs (not MACB0)" + +endif # custom + +config BOARD_ATSTK1002_SPI1 + bool "Configure SPI1 controller" + depends on !BOARD_ATSTK1002_SW4_CUSTOM + help + All the signals for the second SPI controller are available on + GPIO lines and accessed through the J1 jumper block. Say "y" + here to configure that SPI controller. + +endif # stk 1002 diff --git a/arch/avr32/boards/atstk1000/atstk1002.c b/arch/avr32/boards/atstk1000/atstk1002.c index e253e86a1a3..cb93eabb9c6 100644 --- a/arch/avr32/boards/atstk1000/atstk1002.c +++ b/arch/avr32/boards/atstk1000/atstk1002.c @@ -27,15 +27,27 @@ #include "atstk1000.h" -#define SW2_DEFAULT /* MMCI and UART_A available */ struct eth_addr { u8 addr[6]; }; static struct eth_addr __initdata hw_addr[2]; -static struct eth_platform_data __initdata eth_data[2]; +static struct eth_platform_data __initdata eth_data[2] = { + { + /* + * The MDIO pullups on STK1000 are a bit too weak for + * the autodetection to work properly, so we have to + * mask out everything but the correct address. + */ + .phy_mask = ~(1U << 16), + }, + { + .phy_mask = ~(1U << 17), + }, +}; +#ifndef CONFIG_BOARD_ATSTK1002_SW1_CUSTOM static struct spi_board_info spi0_board_info[] __initdata = { { /* QVGA display */ @@ -45,6 +57,13 @@ static struct spi_board_info spi0_board_info[] __initdata = { .mode = SPI_MODE_3, }, }; +#endif + +#ifdef CONFIG_BOARD_ATSTK1002_SPI1 +static struct spi_board_info spi1_board_info[] __initdata = { { + /* patch in custom entries here */ +} }; +#endif /* * The next two functions should go away as the boot loader is @@ -103,10 +122,10 @@ static void __init set_hw_addr(struct platform_device *pdev) void __init setup_board(void) { -#ifdef SW2_DEFAULT - at32_map_usart(1, 0); /* USART 1/A: /dev/ttyS0, DB9 */ -#else +#ifdef CONFIG_BOARD_ATSTK1002_SW2_CUSTOM at32_map_usart(0, 1); /* USART 0/B: /dev/ttyS1, IRDA */ +#else + at32_map_usart(1, 0); /* USART 1/A: /dev/ttyS0, DB9 */ #endif /* USART 2/unused: expansion connector */ at32_map_usart(3, 2); /* USART 3/C: /dev/ttyS2, DB9 */ @@ -140,18 +159,31 @@ static int __init atstk1002_init(void) at32_add_system_devices(); -#ifdef SW2_DEFAULT - at32_add_device_usart(0); -#else +#ifdef CONFIG_BOARD_ATSTK1002_SW2_CUSTOM at32_add_device_usart(1); +#else + at32_add_device_usart(0); #endif at32_add_device_usart(2); +#ifndef CONFIG_BOARD_ATSTK1002_SW6_CUSTOM set_hw_addr(at32_add_device_eth(0, ð_data[0])); - +#endif +#ifndef CONFIG_BOARD_ATSTK1002_SW1_CUSTOM at32_add_device_spi(0, spi0_board_info, ARRAY_SIZE(spi0_board_info)); +#endif +#ifdef CONFIG_BOARD_ATSTK1002_SPI1 + at32_add_device_spi(1, spi1_board_info, ARRAY_SIZE(spi1_board_info)); +#endif +#ifdef CONFIG_BOARD_ATSTK1002_SW5_CUSTOM + set_hw_addr(at32_add_device_eth(1, ð_data[1])); +#else at32_add_device_lcdc(0, &atstk1000_lcdc_data, fbmem_start, fbmem_size); +#endif +#ifndef CONFIG_BOARD_ATSTK1002_SW3_CUSTOM + at32_add_device_ssc(0, ATMEL_SSC_TX); +#endif return 0; } diff --git a/arch/avr32/kernel/ptrace.c b/arch/avr32/kernel/ptrace.c index 3c36c2d1614..39060cbeb2a 100644 --- a/arch/avr32/kernel/ptrace.c +++ b/arch/avr32/kernel/ptrace.c @@ -153,7 +153,6 @@ static int ptrace_setregs(struct task_struct *tsk, const void __user *uregs) long arch_ptrace(struct task_struct *child, long request, long addr, long data) { - unsigned long tmp; int ret; pr_debug("arch_ptrace(%ld, %d, %#lx, %#lx)\n", @@ -166,11 +165,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) /* Read the word at location addr in the child process */ case PTRACE_PEEKTEXT: case PTRACE_PEEKDATA: - ret = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); - if (ret == sizeof(tmp)) - ret = put_user(tmp, (unsigned long __user *)data); - else - ret = -EIO; + ret = generic_ptrace_peekdata(child, addr, data); break; case PTRACE_PEEKUSR: @@ -181,11 +176,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) /* Write the word in data at location addr */ case PTRACE_POKETEXT: case PTRACE_POKEDATA: - ret = access_process_vm(child, addr, &data, sizeof(data), 1); - if (ret == sizeof(data)) - ret = 0; - else - ret = -EIO; + ret = generic_ptrace_pokedata(child, addr, data); break; case PTRACE_POKEUSR: diff --git a/arch/avr32/kernel/setup.c b/arch/avr32/kernel/setup.c index b279d66acf5..d08b0bc6b2b 100644 --- a/arch/avr32/kernel/setup.c +++ b/arch/avr32/kernel/setup.c @@ -313,7 +313,7 @@ __tagtable(ATAG_MEM, parse_tag_mem); static int __init parse_tag_rdimg(struct tag *tag) { -#ifdef CONFIG_INITRD +#ifdef CONFIG_BLK_DEV_INITRD struct tag_mem_range *mem = &tag->u.mem_range; int ret; @@ -323,7 +323,7 @@ static int __init parse_tag_rdimg(struct tag *tag) return 0; } - ret = add_reserved_region(mem->start, mem->start + mem->size - 1, + ret = add_reserved_region(mem->addr, mem->addr + mem->size - 1, "initrd"); if (ret) { printk(KERN_WARNING diff --git a/arch/avr32/kernel/traps.c b/arch/avr32/kernel/traps.c index aaa792815cd..9a73ce7eb50 100644 --- a/arch/avr32/kernel/traps.c +++ b/arch/avr32/kernel/traps.c @@ -56,6 +56,7 @@ void NORET_TYPE die(const char *str, struct pt_regs *regs, long err) show_regs_log_lvl(regs, KERN_EMERG); show_stack_log_lvl(current, regs->sp, regs, KERN_EMERG); bust_spinlocks(0); + add_taint(TAINT_DIE); spin_unlock_irq(&die_lock); if (in_interrupt()) diff --git a/arch/avr32/mach-at32ap/Makefile b/arch/avr32/mach-at32ap/Makefile index f1d395724ac..a8b445046e3 100644 --- a/arch/avr32/mach-at32ap/Makefile +++ b/arch/avr32/mach-at32ap/Makefile @@ -1,3 +1,4 @@ obj-y += at32ap.o clock.o intc.o extint.o pio.o hsmc.o obj-$(CONFIG_CPU_AT32AP7000) += at32ap7000.o obj-$(CONFIG_CPU_AT32AP7000) += time-tc.o +obj-$(CONFIG_CPU_FREQ_AT32AP) += cpufreq.o diff --git a/arch/avr32/mach-at32ap/at32ap.c b/arch/avr32/mach-at32ap/at32ap.c index 90f207e8e96..7c4987f3287 100644 --- a/arch/avr32/mach-at32ap/at32ap.c +++ b/arch/avr32/mach-at32ap/at32ap.c @@ -11,41 +11,10 @@ #include <linux/init.h> #include <linux/platform_device.h> -#include <asm/io.h> - #include <asm/arch/init.h> -#include <asm/arch/sm.h> - -struct at32_sm system_manager; - -static int __init at32_sm_init(void) -{ - struct resource *regs; - struct at32_sm *sm = &system_manager; - int ret = -ENXIO; - - regs = platform_get_resource(&at32_sm_device, IORESOURCE_MEM, 0); - if (!regs) - goto fail; - - spin_lock_init(&sm->lock); - sm->pdev = &at32_sm_device; - - ret = -ENOMEM; - sm->regs = ioremap(regs->start, regs->end - regs->start + 1); - if (!sm->regs) - goto fail; - - return 0; - -fail: - printk(KERN_ERR "Failed to initialize System Manager: %d\n", ret); - return ret; -} void __init setup_platform(void) { - at32_sm_init(); at32_clock_init(); at32_portmux_init(); } diff --git a/arch/avr32/mach-at32ap/at32ap7000.c b/arch/avr32/mach-at32ap/at32ap7000.c index 4dda42d3f6d..64cc5583ddf 100644 --- a/arch/avr32/mach-at32ap/at32ap7000.c +++ b/arch/avr32/mach-at32ap/at32ap7000.c @@ -17,14 +17,20 @@ #include <asm/arch/at32ap7000.h> #include <asm/arch/board.h> #include <asm/arch/portmux.h> -#include <asm/arch/sm.h> #include <video/atmel_lcdc.h> #include "clock.h" #include "hmatrix.h" #include "pio.h" -#include "sm.h" +#include "pm.h" + +/* + * We can reduce the code size a bit by using a constant here. Since + * this file is completely chip-specific, it's safe to not use + * ioremap. Generic drivers should of course never do this. + */ +#define AT32_PM_BASE 0xfff00000 #define PBMEM(base) \ { \ @@ -88,6 +94,8 @@ static struct clk devname##_##_name = { \ .index = _index, \ } +static DEFINE_SPINLOCK(pm_lock); + unsigned long at32ap7000_osc_rates[3] = { [0] = 32768, /* FIXME: these are ATSTK1002-specific */ @@ -104,11 +112,11 @@ static unsigned long pll_get_rate(struct clk *clk, unsigned long control) { unsigned long div, mul, rate; - if (!(control & SM_BIT(PLLEN))) + if (!(control & PM_BIT(PLLEN))) return 0; - div = SM_BFEXT(PLLDIV, control) + 1; - mul = SM_BFEXT(PLLMUL, control) + 1; + div = PM_BFEXT(PLLDIV, control) + 1; + mul = PM_BFEXT(PLLMUL, control) + 1; rate = clk->parent->get_rate(clk->parent); rate = (rate + div / 2) / div; @@ -121,7 +129,7 @@ static unsigned long pll0_get_rate(struct clk *clk) { u32 control; - control = sm_readl(&system_manager, PM_PLL0); + control = pm_readl(PLL0); return pll_get_rate(clk, control); } @@ -130,7 +138,7 @@ static unsigned long pll1_get_rate(struct clk *clk) { u32 control; - control = sm_readl(&system_manager, PM_PLL1); + control = pm_readl(PLL1); return pll_get_rate(clk, control); } @@ -187,108 +195,139 @@ static unsigned long bus_clk_get_rate(struct clk *clk, unsigned int shift) static void cpu_clk_mode(struct clk *clk, int enabled) { - struct at32_sm *sm = &system_manager; unsigned long flags; u32 mask; - spin_lock_irqsave(&sm->lock, flags); - mask = sm_readl(sm, PM_CPU_MASK); + spin_lock_irqsave(&pm_lock, flags); + mask = pm_readl(CPU_MASK); if (enabled) mask |= 1 << clk->index; else mask &= ~(1 << clk->index); - sm_writel(sm, PM_CPU_MASK, mask); - spin_unlock_irqrestore(&sm->lock, flags); + pm_writel(CPU_MASK, mask); + spin_unlock_irqrestore(&pm_lock, flags); } static unsigned long cpu_clk_get_rate(struct clk *clk) { unsigned long cksel, shift = 0; - cksel = sm_readl(&system_manager, PM_CKSEL); - if (cksel & SM_BIT(CPUDIV)) - shift = SM_BFEXT(CPUSEL, cksel) + 1; + cksel = pm_readl(CKSEL); + if (cksel & PM_BIT(CPUDIV)) + shift = PM_BFEXT(CPUSEL, cksel) + 1; return bus_clk_get_rate(clk, shift); } +static long cpu_clk_set_rate(struct clk *clk, unsigned long rate, int apply) +{ + u32 control; + unsigned long parent_rate, child_div, actual_rate, div; + + parent_rate = clk->parent->get_rate(clk->parent); + control = pm_readl(CKSEL); + + if (control & PM_BIT(HSBDIV)) + child_div = 1 << (PM_BFEXT(HSBSEL, control) + 1); + else + child_div = 1; + + if (rate > 3 * (parent_rate / 4) || child_div == 1) { + actual_rate = parent_rate; + control &= ~PM_BIT(CPUDIV); + } else { + unsigned int cpusel; + div = (parent_rate + rate / 2) / rate; + if (div > child_div) + div = child_div; + cpusel = (div > 1) ? (fls(div) - 2) : 0; + control = PM_BIT(CPUDIV) | PM_BFINS(CPUSEL, cpusel, control); + actual_rate = parent_rate / (1 << (cpusel + 1)); + } + + pr_debug("clk %s: new rate %lu (actual rate %lu)\n", + clk->name, rate, actual_rate); + + if (apply) + pm_writel(CKSEL, control); + + return actual_rate; +} + static void hsb_clk_mode(struct clk *clk, int enabled) { - struct at32_sm *sm = &system_manager; unsigned long flags; u32 mask; - spin_lock_irqsave(&sm->lock, flags); - mask = sm_readl(sm, PM_HSB_MASK); + spin_lock_irqsave(&pm_lock, flags); + mask = pm_readl(HSB_MASK); if (enabled) mask |= 1 << clk->index; else mask &= ~(1 << clk->index); - sm_writel(sm, PM_HSB_MASK, mask); - spin_unlock_irqrestore(&sm->lock, flags); + pm_writel(HSB_MASK, mask); + spin_unlock_irqrestore(&pm_lock, flags); } static unsigned long hsb_clk_get_rate(struct clk *clk) { unsigned long cksel, shift = 0; - cksel = sm_readl(&system_manager, PM_CKSEL); - if (cksel & SM_BIT(HSBDIV)) - shift = SM_BFEXT(HSBSEL, cksel) + 1; + cksel = pm_readl(CKSEL); + if (cksel & PM_BIT(HSBDIV)) + shift = PM_BFEXT(HSBSEL, cksel) + 1; return bus_clk_get_rate(clk, shift); } static void pba_clk_mode(struct clk *clk, int enabled) { - struct at32_sm *sm = &system_manager; unsigned long flags; u32 mask; - spin_lock_irqsave(&sm->lock, flags); - mask = sm_readl(sm, PM_PBA_MASK); + spin_lock_irqsave(&pm_lock, flags); + mask = pm_readl(PBA_MASK); if (enabled) mask |= 1 << clk->index; else mask &= ~(1 << clk->index); - sm_writel(sm, PM_PBA_MASK, mask); - spin_unlock_irqrestore(&sm->lock, flags); + pm_writel(PBA_MASK, mask); + spin_unlock_irqrestore(&pm_lock, flags); } static unsigned long pba_clk_get_rate(struct clk *clk) { unsigned long cksel, shift = 0; - cksel = sm_readl(&system_manager, PM_CKSEL); - if (cksel & SM_BIT(PBADIV)) - shift = SM_BFEXT(PBASEL, cksel) + 1; + cksel = pm_readl(CKSEL); + if (cksel & PM_BIT(PBADIV)) + shift = PM_BFEXT(PBASEL, cksel) + 1; return bus_clk_get_rate(clk, shift); } static void pbb_clk_mode(struct clk *clk, int enabled) { - struct at32_sm *sm = &system_manager; unsigned long flags; u32 mask; - spin_lock_irqsave(&sm->lock, flags); - mask = sm_readl(sm, PM_PBB_MASK); + spin_lock_irqsave(&pm_lock, flags); + mask = pm_readl(PBB_MASK); if (enabled) mask |= 1 << clk->index; else mask &= ~(1 << clk->index); - sm_writel(sm, PM_PBB_MASK, mask); - spin_unlock_irqrestore(&sm->lock, flags); + pm_writel(PBB_MASK, mask); + spin_unlock_irqrestore(&pm_lock, flags); } static unsigned long pbb_clk_get_rate(struct clk *clk) { unsigned long cksel, shift = 0; - cksel = sm_readl(&system_manager, PM_CKSEL); - if (cksel & SM_BIT(PBBDIV)) - shift = SM_BFEXT(PBBSEL, cksel) + 1; + cksel = pm_readl(CKSEL); + if (cksel & PM_BIT(PBBDIV)) + shift = PM_BFEXT(PBBSEL, cksel) + 1; return bus_clk_get_rate(clk, shift); } @@ -296,6 +335,7 @@ static unsigned long pbb_clk_get_rate(struct clk *clk) static struct clk cpu_clk = { .name = "cpu", .get_rate = cpu_clk_get_rate, + .set_rate = cpu_clk_set_rate, .users = 1, }; static struct clk hsb_clk = { @@ -327,12 +367,12 @@ static void genclk_mode(struct clk *clk, int enabled) { u32 control; - control = sm_readl(&system_manager, PM_GCCTRL + 4 * clk->index); + control = pm_readl(GCCTRL(clk->index)); if (enabled) - control |= SM_BIT(CEN); + control |= PM_BIT(CEN); else - control &= ~SM_BIT(CEN); - sm_writel(&system_manager, PM_GCCTRL + 4 * clk->index, control); + control &= ~PM_BIT(CEN); + pm_writel(GCCTRL(clk->index), control); } static unsigned long genclk_get_rate(struct clk *clk) @@ -340,9 +380,9 @@ static unsigned long genclk_get_rate(struct clk *clk) u32 control; unsigned long div = 1; - control = sm_readl(&system_manager, PM_GCCTRL + 4 * clk->index); - if (control & SM_BIT(DIVEN)) - div = 2 * (SM_BFEXT(DIV, control) + 1); + control = pm_readl(GCCTRL(clk->index)); + if (control & PM_BIT(DIVEN)) + div = 2 * (PM_BFEXT(DIV, control) + 1); return clk->parent->get_rate(clk->parent) / div; } @@ -353,23 +393,22 @@ static long genclk_set_rate(struct clk *clk, unsigned long rate, int apply) unsigned long parent_rate, actual_rate, div; parent_rate = clk->parent->get_rate(clk->parent); - control = sm_readl(&system_manager, PM_GCCTRL + 4 * clk->index); + control = pm_readl(GCCTRL(clk->index)); if (rate > 3 * parent_rate / 4) { actual_rate = parent_rate; - control &= ~SM_BIT(DIVEN); + control &= ~PM_BIT(DIVEN); } else { div = (parent_rate + rate) / (2 * rate) - 1; - control = SM_BFINS(DIV, div, control) | SM_BIT(DIVEN); + control = PM_BFINS(DIV, div, control) | PM_BIT(DIVEN); actual_rate = parent_rate / (2 * (div + 1)); } - printk("clk %s: new rate %lu (actual rate %lu)\n", - clk->name, rate, actual_rate); + dev_dbg(clk->dev, "clk %s: new rate %lu (actual rate %lu)\n", + clk->name, rate, actual_rate); if (apply) - sm_writel(&system_manager, PM_GCCTRL + 4 * clk->index, - control); + pm_writel(GCCTRL(clk->index), control); return actual_rate; } @@ -378,24 +417,24 @@ int genclk_set_parent(struct clk *clk, struct clk *parent) { u32 control; - printk("clk %s: new parent %s (was %s)\n", - clk->name, parent->name, clk->parent->name); + dev_dbg(clk->dev, "clk %s: new parent %s (was %s)\n", + clk->name, parent->name, clk->parent->name); - control = sm_readl(&system_manager, PM_GCCTRL + 4 * clk->index); + control = pm_readl(GCCTRL(clk->index)); if (parent == &osc1 || parent == &pll1) - control |= SM_BIT(OSCSEL); + control |= PM_BIT(OSCSEL); else if (parent == &osc0 || parent == &pll0) - control &= ~SM_BIT(OSCSEL); + control &= ~PM_BIT(OSCSEL); else return -EINVAL; if (parent == &pll0 || parent == &pll1) - control |= SM_BIT(PLLSEL); + control |= PM_BIT(PLLSEL); else - control &= ~SM_BIT(PLLSEL); + control &= ~PM_BIT(PLLSEL); - sm_writel(&system_manager, PM_GCCTRL + 4 * clk->index, control); + pm_writel(GCCTRL(clk->index), control); clk->parent = parent; return 0; @@ -408,11 +447,11 @@ static void __init genclk_init_parent(struct clk *clk) BUG_ON(clk->index > 7); - control = sm_readl(&system_manager, PM_GCCTRL + 4 * clk->index); - if (control & SM_BIT(OSCSEL)) - parent = (control & SM_BIT(PLLSEL)) ? &pll1 : &osc1; + control = pm_readl(GCCTRL(clk->index)); + if (control & PM_BIT(OSCSEL)) + parent = (control & PM_BIT(PLLSEL)) ? &pll1 : &osc1; else - parent = (control & SM_BIT(PLLSEL)) ? &pll0 : &osc0; + parent = (control & PM_BIT(PLLSEL)) ? &pll0 : &osc0; clk->parent = parent; } @@ -420,21 +459,53 @@ static void __init genclk_init_parent(struct clk *clk) /* -------------------------------------------------------------------- * System peripherals * -------------------------------------------------------------------- */ -static struct resource sm_resource[] = { - PBMEM(0xfff00000), - NAMED_IRQ(19, "eim"), - NAMED_IRQ(20, "pm"), - NAMED_IRQ(21, "rtc"), +static struct resource at32_pm0_resource[] = { + { + .start = 0xfff00000, + .end = 0xfff0007f, + .flags = IORESOURCE_MEM, + }, + IRQ(20), }; -struct platform_device at32_sm_device = { - .name = "sm", - .id = 0, - .resource = sm_resource, - .num_resources = ARRAY_SIZE(sm_resource), + +static struct resource at32ap700x_rtc0_resource[] = { + { + .start = 0xfff00080, + .end = 0xfff000af, + .flags = IORESOURCE_MEM, + }, + IRQ(21), +}; + +static struct resource at32_wdt0_resource[] = { + { + .start = 0xfff000b0, + .end = 0xfff000bf, + .flags = IORESOURCE_MEM, + }, +}; + +static struct resource at32_eic0_resource[] = { + { + .start = 0xfff00100, + .end = 0xfff0013f, + .flags = IORESOURCE_MEM, + }, + IRQ(19), }; -static struct clk at32_sm_pclk = { + +DEFINE_DEV(at32_pm, 0); +DEFINE_DEV(at32ap700x_rtc, 0); +DEFINE_DEV(at32_wdt, 0); +DEFINE_DEV(at32_eic, 0); + +/* + * Peripheral clock for PM, RTC, WDT and EIC. PM will ensure that this + * is always running. + */ +static struct clk at32_pm_pclk = { .name = "pclk", - .dev = &at32_sm_device.dev, + .dev = &at32_pm0_device.dev, .parent = &pbb_clk, .mode = pbb_clk_mode, .get_rate = pbb_clk_get_rate, @@ -583,10 +654,11 @@ DEV_CLK(mck, pio4, pba, 14); void __init at32_add_system_devices(void) { - system_manager.eim_first_irq = EIM_IRQ_BASE; - - platform_device_register(&at32_sm_device); + platform_device_register(&at32_pm0_device); platform_device_register(&at32_intc0_device); + platform_device_register(&at32ap700x_rtc0_device); + platform_device_register(&at32_wdt0_device); + platform_device_register(&at32_eic0_device); platform_device_register(&smc0_device); platform_device_register(&pdc_device); @@ -1013,6 +1085,89 @@ err_dup_modedb: } /* -------------------------------------------------------------------- + * SSC + * -------------------------------------------------------------------- */ +static struct resource ssc0_resource[] = { + PBMEM(0xffe01c00), + IRQ(10), +}; +DEFINE_DEV(ssc, 0); +DEV_CLK(pclk, ssc0, pba, 7); + +static struct resource ssc1_resource[] = { + PBMEM(0xffe02000), + IRQ(11), +}; +DEFINE_DEV(ssc, 1); +DEV_CLK(pclk, ssc1, pba, 8); + +static struct resource ssc2_resource[] = { + PBMEM(0xffe02400), + IRQ(12), +}; +DEFINE_DEV(ssc, 2); +DEV_CLK(pclk, ssc2, pba, 9); + +struct platform_device *__init +at32_add_device_ssc(unsigned int id, unsigned int flags) +{ + struct platform_device *pdev; + + switch (id) { + case 0: + pdev = &ssc0_device; + if (flags & ATMEL_SSC_RF) + select_peripheral(PA(21), PERIPH_A, 0); /* RF */ + if (flags & ATMEL_SSC_RK) + select_peripheral(PA(22), PERIPH_A, 0); /* RK */ + if (flags & ATMEL_SSC_TK) + select_peripheral(PA(23), PERIPH_A, 0); /* TK */ + if (flags & ATMEL_SSC_TF) + select_peripheral(PA(24), PERIPH_A, 0); /* TF */ + if (flags & ATMEL_SSC_TD) + select_peripheral(PA(25), PERIPH_A, 0); /* TD */ + if (flags & ATMEL_SSC_RD) + select_peripheral(PA(26), PERIPH_A, 0); /* RD */ + break; + case 1: + pdev = &ssc1_device; + if (flags & ATMEL_SSC_RF) + select_peripheral(PA(0), PERIPH_B, 0); /* RF */ + if (flags & ATMEL_SSC_RK) + select_peripheral(PA(1), PERIPH_B, 0); /* RK */ + if (flags & ATMEL_SSC_TK) + select_peripheral(PA(2), PERIPH_B, 0); /* TK */ + if (flags & ATMEL_SSC_TF) + select_peripheral(PA(3), PERIPH_B, 0); /* TF */ + if (flags & ATMEL_SSC_TD) + select_peripheral(PA(4), PERIPH_B, 0); /* TD */ + if (flags & ATMEL_SSC_RD) + select_peripheral(PA(5), PERIPH_B, 0); /* RD */ + break; + case 2: + pdev = &ssc2_device; + if (flags & ATMEL_SSC_TD) + select_peripheral(PB(13), PERIPH_A, 0); /* TD */ + if (flags & ATMEL_SSC_RD) + select_peripheral(PB(14), PERIPH_A, 0); /* RD */ + if (flags & ATMEL_SSC_TK) + select_peripheral(PB(15), PERIPH_A, 0); /* TK */ + if (flags & ATMEL_SSC_TF) + select_peripheral(PB(16), PERIPH_A, 0); /* TF */ + if (flags & ATMEL_SSC_RF) + select_peripheral(PB(17), PERIPH_A, 0); /* RF */ + if (flags & ATMEL_SSC_RK) + select_peripheral(PB(18), PERIPH_A, 0); /* RK */ + break; + default: + return NULL; + } + + platform_device_register(pdev); + return pdev; +} + +/* -------------------------------------------------------------------- * GCLK * -------------------------------------------------------------------- */ static struct clk gclk0 = { @@ -1066,7 +1221,7 @@ struct clk *at32_clock_list[] = { &hsb_clk, &pba_clk, &pbb_clk, - &at32_sm_pclk, + &at32_pm_pclk, &at32_intc0_pclk, &hmatrix_clk, &ebi_clk, @@ -1094,6 +1249,9 @@ struct clk *at32_clock_list[] = { &atmel_spi1_spi_clk, &atmel_lcdfb0_hck1, &atmel_lcdfb0_pixclk, + &ssc0_pclk, + &ssc1_pclk, + &ssc2_pclk, &gclk0, &gclk1, &gclk2, @@ -1113,18 +1271,20 @@ void __init at32_portmux_init(void) void __init at32_clock_init(void) { - struct at32_sm *sm = &system_manager; u32 cpu_mask = 0, hsb_mask = 0, pba_mask = 0, pbb_mask = 0; int i; - if (sm_readl(sm, PM_MCCTRL) & SM_BIT(PLLSEL)) + if (pm_readl(MCCTRL) & PM_BIT(PLLSEL)) { main_clock = &pll0; - else + cpu_clk.parent = &pll0; + } else { main_clock = &osc0; + cpu_clk.parent = &osc0; + } - if (sm_readl(sm, PM_PLL0) & SM_BIT(PLLOSC)) + if (pm_readl(PLL0) & PM_BIT(PLLOSC)) pll0.parent = &osc1; - if (sm_readl(sm, PM_PLL1) & SM_BIT(PLLOSC)) + if (pm_readl(PLL1) & PM_BIT(PLLOSC)) pll1.parent = &osc1; genclk_init_parent(&gclk0); @@ -1157,8 +1317,8 @@ void __init at32_clock_init(void) pbb_mask |= 1 << clk->index; } - sm_writel(sm, PM_CPU_MASK, cpu_mask); - sm_writel(sm, PM_HSB_MASK, hsb_mask); - sm_writel(sm, PM_PBA_MASK, pba_mask); - sm_writel(sm, PM_PBB_MASK, pbb_mask); + pm_writel(CPU_MASK, cpu_mask); + pm_writel(HSB_MASK, hsb_mask); + pm_writel(PBA_MASK, pba_mask); + pm_writel(PBB_MASK, pbb_mask); } diff --git a/arch/avr32/mach-at32ap/cpufreq.c b/arch/avr32/mach-at32ap/cpufreq.c new file mode 100644 index 00000000000..235524b7919 --- /dev/null +++ b/arch/avr32/mach-at32ap/cpufreq.c @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2004-2007 Atmel Corporation + * + * Based on MIPS implementation arch/mips/kernel/time.c + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/*#define DEBUG*/ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/init.h> +#include <linux/cpufreq.h> +#include <linux/io.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <asm/system.h> + +static struct clk *cpuclk; + +static int at32_verify_speed(struct cpufreq_policy *policy) +{ + if (policy->cpu != 0) + return -EINVAL; + + cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq, + policy->cpuinfo.max_freq); + return 0; +} + +static unsigned int at32_get_speed(unsigned int cpu) +{ + /* No SMP support */ + if (cpu) + return 0; + return (unsigned int)((clk_get_rate(cpuclk) + 500) / 1000); +} + +static int at32_set_target(struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation) +{ + struct cpufreq_freqs freqs; + long freq; + + /* Convert target_freq from kHz to Hz */ + freq = clk_round_rate(cpuclk, target_freq * 1000); + + /* Check if policy->min <= new_freq <= policy->max */ + if(freq < (policy->min * 1000) || freq > (policy->max * 1000)) + return -EINVAL; + + pr_debug("cpufreq: requested frequency %u Hz\n", target_freq * 1000); + + freqs.old = at32_get_speed(0); + freqs.new = (freq + 500) / 1000; + freqs.cpu = 0; + freqs.flags = 0; + + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + clk_set_rate(cpuclk, freq); + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + + pr_debug("cpufreq: set frequency %lu Hz\n", freq); + + return 0; +} + +static int __init at32_cpufreq_driver_init(struct cpufreq_policy *policy) +{ + if (policy->cpu != 0) + return -EINVAL; + + cpuclk = clk_get(NULL, "cpu"); + if (IS_ERR(cpuclk)) { + pr_debug("cpufreq: could not get CPU clk\n"); + return PTR_ERR(cpuclk); + } + + policy->cpuinfo.min_freq = (clk_round_rate(cpuclk, 1) + 500) / 1000; + policy->cpuinfo.max_freq = (clk_round_rate(cpuclk, ~0UL) + 500) / 1000; + policy->cpuinfo.transition_latency = 0; + policy->cur = at32_get_speed(0); + policy->min = policy->cpuinfo.min_freq; + policy->max = policy->cpuinfo.max_freq; + policy->governor = CPUFREQ_DEFAULT_GOVERNOR; + + printk("cpufreq: AT32AP CPU frequency driver\n"); + + return 0; +} + +static struct cpufreq_driver at32_driver = { + .name = "at32ap", + .owner = THIS_MODULE, + .init = at32_cpufreq_driver_init, + .verify = at32_verify_speed, + .target = at32_set_target, + .get = at32_get_speed, + .flags = CPUFREQ_STICKY, +}; + +static int __init at32_cpufreq_init(void) +{ + return cpufreq_register_driver(&at32_driver); +} + +arch_initcall(at32_cpufreq_init); diff --git a/arch/avr32/mach-at32ap/extint.c b/arch/avr32/mach-at32ap/extint.c index 4a60eccfebd..8acd0109003 100644 --- a/arch/avr32/mach-at32ap/extint.c +++ b/arch/avr32/mach-at32ap/extint.c @@ -17,42 +17,83 @@ #include <asm/io.h> -#include <asm/arch/sm.h> - -#include "sm.h" +/* EIC register offsets */ +#define EIC_IER 0x0000 +#define EIC_IDR 0x0004 +#define EIC_IMR 0x0008 +#define EIC_ISR 0x000c +#define EIC_ICR 0x0010 +#define EIC_MODE 0x0014 +#define EIC_EDGE 0x0018 +#define EIC_LEVEL 0x001c +#define EIC_TEST 0x0020 +#define EIC_NMIC 0x0024 + +/* Bitfields in TEST */ +#define EIC_TESTEN_OFFSET 31 +#define EIC_TESTEN_SIZE 1 + +/* Bitfields in NMIC */ +#define EIC_EN_OFFSET 0 +#define EIC_EN_SIZE 1 + +/* Bit manipulation macros */ +#define EIC_BIT(name) \ + (1 << EIC_##name##_OFFSET) +#define EIC_BF(name,value) \ + (((value) & ((1 << EIC_##name##_SIZE) - 1)) \ + << EIC_##name##_OFFSET) +#define EIC_BFEXT(name,value) \ + (((value) >> EIC_##name##_OFFSET) \ + & ((1 << EIC_##name##_SIZE) - 1)) +#define EIC_BFINS(name,value,old) \ + (((old) & ~(((1 << EIC_##name##_SIZE) - 1) \ + << EIC_##name##_OFFSET)) \ + | EIC_BF(name,value)) + +/* Register access macros */ +#define eic_readl(port,reg) \ + __raw_readl((port)->regs + EIC_##reg) +#define eic_writel(port,reg,value) \ + __raw_writel((value), (port)->regs + EIC_##reg) + +struct eic { + void __iomem *regs; + struct irq_chip *chip; + unsigned int first_irq; +}; -static void eim_ack_irq(unsigned int irq) +static void eic_ack_irq(unsigned int irq) { - struct at32_sm *sm = get_irq_chip_data(irq); - sm_writel(sm, EIM_ICR, 1 << (irq - sm->eim_first_irq)); + struct eic *eic = get_irq_chip_data(irq); + eic_writel(eic, ICR, 1 << (irq - eic->first_irq)); } -static void eim_mask_irq(unsigned int irq) +static void eic_mask_irq(unsigned int irq) { - struct at32_sm *sm = get_irq_chip_data(irq); - sm_writel(sm, EIM_IDR, 1 << (irq - sm->eim_first_irq)); + struct eic *eic = get_irq_chip_data(irq); + eic_writel(eic, IDR, 1 << (irq - eic->first_irq)); } -static void eim_mask_ack_irq(unsigned int irq) +static void eic_mask_ack_irq(unsigned int irq) { - struct at32_sm *sm = get_irq_chip_data(irq); - sm_writel(sm, EIM_ICR, 1 << (irq - sm->eim_first_irq)); - sm_writel(sm, EIM_IDR, 1 << (irq - sm->eim_first_irq)); + struct eic *eic = get_irq_chip_data(irq); + eic_writel(eic, ICR, 1 << (irq - eic->first_irq)); + eic_writel(eic, IDR, 1 << (irq - eic->first_irq)); } -static void eim_unmask_irq(unsigned int irq) +static void eic_unmask_irq(unsigned int irq) { - struct at32_sm *sm = get_irq_chip_data(irq); - sm_writel(sm, EIM_IER, 1 << (irq - sm->eim_first_irq)); + struct eic *eic = get_irq_chip_data(irq); + eic_writel(eic, IER, 1 << (irq - eic->first_irq)); } -static int eim_set_irq_type(unsigned int irq, unsigned int flow_type) +static int eic_set_irq_type(unsigned int irq, unsigned int flow_type) { - struct at32_sm *sm = get_irq_chip_data(irq); + struct eic *eic = get_irq_chip_data(irq); struct irq_desc *desc; - unsigned int i = irq - sm->eim_first_irq; + unsigned int i = irq - eic->first_irq; u32 mode, edge, level; - unsigned long flags; int ret = 0; flow_type &= IRQ_TYPE_SENSE_MASK; @@ -60,11 +101,10 @@ static int eim_set_irq_type(unsigned int irq, unsigned int flow_type) flow_type = IRQ_TYPE_LEVEL_LOW; desc = &irq_desc[irq]; - spin_lock_irqsave(&sm->lock, flags); - mode = sm_readl(sm, EIM_MODE); - edge = sm_readl(sm, EIM_EDGE); - level = sm_readl(sm, EIM_LEVEL); + mode = eic_readl(eic, MODE); + edge = eic_readl(eic, EDGE); + level = eic_readl(eic, LEVEL); switch (flow_type) { case IRQ_TYPE_LEVEL_LOW: @@ -89,9 +129,9 @@ static int eim_set_irq_type(unsigned int irq, unsigned int flow_type) } if (ret == 0) { - sm_writel(sm, EIM_MODE, mode); - sm_writel(sm, EIM_EDGE, edge); - sm_writel(sm, EIM_LEVEL, level); + eic_writel(eic, MODE, mode); + eic_writel(eic, EDGE, edge); + eic_writel(eic, LEVEL, level); if (flow_type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)) flow_type |= IRQ_LEVEL; @@ -99,35 +139,33 @@ static int eim_set_irq_type(unsigned int irq, unsigned int flow_type) desc->status |= flow_type; } - spin_unlock_irqrestore(&sm->lock, flags); - return ret; } -struct irq_chip eim_chip = { - .name = "eim", - .ack = eim_ack_irq, - .mask = eim_mask_irq, - .mask_ack = eim_mask_ack_irq, - .unmask = eim_unmask_irq, - .set_type = eim_set_irq_type, +struct irq_chip eic_chip = { + .name = "eic", + .ack = eic_ack_irq, + .mask = eic_mask_irq, + .mask_ack = eic_mask_ack_irq, + .unmask = eic_unmask_irq, + .set_type = eic_set_irq_type, }; -static void demux_eim_irq(unsigned int irq, struct irq_desc *desc) +static void demux_eic_irq(unsigned int irq, struct irq_desc *desc) { - struct at32_sm *sm = desc->handler_data; + struct eic *eic = desc->handler_data; struct irq_desc *ext_desc; unsigned long status, pending; unsigned int i, ext_irq; - status = sm_readl(sm, EIM_ISR); - pending = status & sm_readl(sm, EIM_IMR); + status = eic_readl(eic, ISR); + pending = status & eic_readl(eic, IMR); while (pending) { i = fls(pending) - 1; pending &= ~(1 << i); - ext_irq = i + sm->eim_first_irq; + ext_irq = i + eic->first_irq; ext_desc = irq_desc + ext_irq; if (ext_desc->status & IRQ_LEVEL) handle_level_irq(ext_irq, ext_desc); @@ -136,51 +174,85 @@ static void demux_eim_irq(unsigned int irq, struct irq_desc *desc) } } -static int __init eim_init(void) +static int __init eic_probe(struct platform_device *pdev) { - struct at32_sm *sm = &system_manager; + struct eic *eic; + struct resource *regs; unsigned int i; unsigned int nr_irqs; unsigned int int_irq; + int ret; u32 pattern; - /* - * The EIM is really the same module as SM, so register - * mapping, etc. has been taken care of already. - */ + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + int_irq = platform_get_irq(pdev, 0); + if (!regs || !int_irq) { + dev_dbg(&pdev->dev, "missing regs and/or irq resource\n"); + return -ENXIO; + } + + ret = -ENOMEM; + eic = kzalloc(sizeof(struct eic), GFP_KERNEL); + if (!eic) { + dev_dbg(&pdev->dev, "no memory for eic structure\n"); + goto err_kzalloc; + } + + eic->first_irq = EIM_IRQ_BASE + 32 * pdev->id; + eic->regs = ioremap(regs->start, regs->end - regs->start + 1); + if (!eic->regs) { + dev_dbg(&pdev->dev, "failed to map regs\n"); + goto err_ioremap; + } /* * Find out how many interrupt lines that are actually * implemented in hardware. */ - sm_writel(sm, EIM_IDR, ~0UL); - sm_writel(sm, EIM_MODE, ~0UL); - pattern = sm_readl(sm, EIM_MODE); + eic_writel(eic, IDR, ~0UL); + eic_writel(eic, MODE, ~0UL); + pattern = eic_readl(eic, MODE); nr_irqs = fls(pattern); /* Trigger on falling edge unless overridden by driver */ - sm_writel(sm, EIM_MODE, 0UL); - sm_writel(sm, EIM_EDGE, 0UL); + eic_writel(eic, MODE, 0UL); + eic_writel(eic, EDGE, 0UL); - sm->eim_chip = &eim_chip; + eic->chip = &eic_chip; for (i = 0; i < nr_irqs; i++) { /* NOTE the handler we set here is ignored by the demux */ - set_irq_chip_and_handler(sm->eim_first_irq + i, &eim_chip, + set_irq_chip_and_handler(eic->first_irq + i, &eic_chip, handle_level_irq); - set_irq_chip_data(sm->eim_first_irq + i, sm); + set_irq_chip_data(eic->first_irq + i, eic); } - int_irq = platform_get_irq_byname(sm->pdev, "eim"); - - set_irq_chained_handler(int_irq, demux_eim_irq); - set_irq_data(int_irq, sm); + set_irq_chained_handler(int_irq, demux_eic_irq); + set_irq_data(int_irq, eic); - printk("EIM: External Interrupt Module at 0x%p, IRQ %u\n", - sm->regs, int_irq); - printk("EIM: Handling %u external IRQs, starting with IRQ %u\n", - nr_irqs, sm->eim_first_irq); + dev_info(&pdev->dev, + "External Interrupt Controller at 0x%p, IRQ %u\n", + eic->regs, int_irq); + dev_info(&pdev->dev, + "Handling %u external IRQs, starting with IRQ %u\n", + nr_irqs, eic->first_irq); return 0; + +err_ioremap: + kfree(eic); +err_kzalloc: + return ret; +} + +static struct platform_driver eic_driver = { + .driver = { + .name = "at32_eic", + }, +}; + +static int __init eic_init(void) +{ + return platform_driver_probe(&eic_driver, eic_probe); } -arch_initcall(eim_init); +arch_initcall(eic_init); diff --git a/arch/avr32/mach-at32ap/pm.h b/arch/avr32/mach-at32ap/pm.h new file mode 100644 index 00000000000..a1f8aced0a8 --- /dev/null +++ b/arch/avr32/mach-at32ap/pm.h @@ -0,0 +1,112 @@ +/* + * Register definitions for the Power Manager (PM) + */ +#ifndef __ARCH_AVR32_MACH_AT32AP_PM_H__ +#define __ARCH_AVR32_MACH_AT32AP_PM_H__ + +/* PM register offsets */ +#define PM_MCCTRL 0x0000 +#define PM_CKSEL 0x0004 +#define PM_CPU_MASK 0x0008 +#define PM_HSB_MASK 0x000c +#define PM_PBA_MASK 0x0010 +#define PM_PBB_MASK 0x0014 +#define PM_PLL0 0x0020 +#define PM_PLL1 0x0024 +#define PM_IER 0x0040 +#define PM_IDR 0x0044 +#define PM_IMR 0x0048 +#define PM_ISR 0x004c +#define PM_ICR 0x0050 +#define PM_GCCTRL(x) (0x0060 + 4 * (x)) +#define PM_RCAUSE 0x00c0 + +/* Bitfields in CKSEL */ +#define PM_CPUSEL_OFFSET 0 +#define PM_CPUSEL_SIZE 3 +#define PM_CPUDIV_OFFSET 7 +#define PM_CPUDIV_SIZE 1 +#define PM_HSBSEL_OFFSET 8 +#define PM_HSBSEL_SIZE 3 +#define PM_HSBDIV_OFFSET 15 +#define PM_HSBDIV_SIZE 1 +#define PM_PBASEL_OFFSET 16 +#define PM_PBASEL_SIZE 3 +#define PM_PBADIV_OFFSET 23 +#define PM_PBADIV_SIZE 1 +#define PM_PBBSEL_OFFSET 24 +#define PM_PBBSEL_SIZE 3 +#define PM_PBBDIV_OFFSET 31 +#define PM_PBBDIV_SIZE 1 + +/* Bitfields in PLL0 */ +#define PM_PLLEN_OFFSET 0 +#define PM_PLLEN_SIZE 1 +#define PM_PLLOSC_OFFSET 1 +#define PM_PLLOSC_SIZE 1 +#define PM_PLLOPT_OFFSET 2 +#define PM_PLLOPT_SIZE 3 +#define PM_PLLDIV_OFFSET 8 +#define PM_PLLDIV_SIZE 8 +#define PM_PLLMUL_OFFSET 16 +#define PM_PLLMUL_SIZE 8 +#define PM_PLLCOUNT_OFFSET 24 +#define PM_PLLCOUNT_SIZE 6 +#define PM_PLLTEST_OFFSET 31 +#define PM_PLLTEST_SIZE 1 + +/* Bitfields in ICR */ +#define PM_LOCK0_OFFSET 0 +#define PM_LOCK0_SIZE 1 +#define PM_LOCK1_OFFSET 1 +#define PM_LOCK1_SIZE 1 +#define PM_WAKE_OFFSET 2 +#define PM_WAKE_SIZE 1 +#define PM_CKRDY_OFFSET 5 +#define PM_CKRDY_SIZE 1 +#define PM_MSKRDY_OFFSET 6 +#define PM_MSKRDY_SIZE 1 + +/* Bitfields in GCCTRL0 */ +#define PM_OSCSEL_OFFSET 0 +#define PM_OSCSEL_SIZE 1 +#define PM_PLLSEL_OFFSET 1 +#define PM_PLLSEL_SIZE 1 +#define PM_CEN_OFFSET 2 +#define PM_CEN_SIZE 1 +#define PM_DIVEN_OFFSET 4 +#define PM_DIVEN_SIZE 1 +#define PM_DIV_OFFSET 8 +#define PM_DIV_SIZE 8 + +/* Bitfields in RCAUSE */ +#define PM_POR_OFFSET 0 +#define PM_POR_SIZE 1 +#define PM_EXT_OFFSET 2 +#define PM_EXT_SIZE 1 +#define PM_WDT_OFFSET 3 +#define PM_WDT_SIZE 1 +#define PM_NTAE_OFFSET 4 +#define PM_NTAE_SIZE 1 + +/* Bit manipulation macros */ +#define PM_BIT(name) \ + (1 << PM_##name##_OFFSET) +#define PM_BF(name,value) \ + (((value) & ((1 << PM_##name##_SIZE) - 1)) \ + << PM_##name##_OFFSET) +#define PM_BFEXT(name,value) \ + (((value) >> PM_##name##_OFFSET) \ + & ((1 << PM_##name##_SIZE) - 1)) +#define PM_BFINS(name,value,old)\ + (((old) & ~(((1 << PM_##name##_SIZE) - 1) \ + << PM_##name##_OFFSET)) \ + | PM_BF(name,value)) + +/* Register access macros */ +#define pm_readl(reg) \ + __raw_readl((void __iomem *)AT32_PM_BASE + PM_##reg) +#define pm_writel(reg,value) \ + __raw_writel((value), (void __iomem *)AT32_PM_BASE + PM_##reg) + +#endif /* __ARCH_AVR32_MACH_AT32AP_PM_H__ */ diff --git a/arch/avr32/mach-at32ap/sm.h b/arch/avr32/mach-at32ap/sm.h deleted file mode 100644 index cad02b512bc..00000000000 --- a/arch/avr32/mach-at32ap/sm.h +++ /dev/null @@ -1,242 +0,0 @@ -/* - * Register definitions for SM - * - * System Manager - */ -#ifndef __ASM_AVR32_SM_H__ -#define __ASM_AVR32_SM_H__ - -/* SM register offsets */ -#define SM_PM_MCCTRL 0x0000 -#define SM_PM_CKSEL 0x0004 -#define SM_PM_CPU_MASK 0x0008 -#define SM_PM_HSB_MASK 0x000c -#define SM_PM_PBA_MASK 0x0010 -#define SM_PM_PBB_MASK 0x0014 -#define SM_PM_PLL0 0x0020 -#define SM_PM_PLL1 0x0024 -#define SM_PM_VCTRL 0x0030 -#define SM_PM_VMREF 0x0034 -#define SM_PM_VMV 0x0038 -#define SM_PM_IER 0x0040 -#define SM_PM_IDR 0x0044 -#define SM_PM_IMR 0x0048 -#define SM_PM_ISR 0x004c -#define SM_PM_ICR 0x0050 -#define SM_PM_GCCTRL 0x0060 -#define SM_RTC_CTRL 0x0080 -#define SM_RTC_VAL 0x0084 -#define SM_RTC_TOP 0x0088 -#define SM_RTC_IER 0x0090 -#define SM_RTC_IDR 0x0094 -#define SM_RTC_IMR 0x0098 -#define SM_RTC_ISR 0x009c -#define SM_RTC_ICR 0x00a0 -#define SM_WDT_CTRL 0x00b0 -#define SM_WDT_CLR 0x00b4 -#define SM_WDT_EXT 0x00b8 -#define SM_RC_RCAUSE 0x00c0 -#define SM_EIM_IER 0x0100 -#define SM_EIM_IDR 0x0104 -#define SM_EIM_IMR 0x0108 -#define SM_EIM_ISR 0x010c -#define SM_EIM_ICR 0x0110 -#define SM_EIM_MODE 0x0114 -#define SM_EIM_EDGE 0x0118 -#define SM_EIM_LEVEL 0x011c -#define SM_EIM_TEST 0x0120 -#define SM_EIM_NMIC 0x0124 - -/* Bitfields in PM_MCCTRL */ - -/* Bitfields in PM_CKSEL */ -#define SM_CPUSEL_OFFSET 0 -#define SM_CPUSEL_SIZE 3 -#define SM_CPUDIV_OFFSET 7 -#define SM_CPUDIV_SIZE 1 -#define SM_HSBSEL_OFFSET 8 -#define SM_HSBSEL_SIZE 3 -#define SM_HSBDIV_OFFSET 15 -#define SM_HSBDIV_SIZE 1 -#define SM_PBASEL_OFFSET 16 -#define SM_PBASEL_SIZE 3 -#define SM_PBADIV_OFFSET 23 -#define SM_PBADIV_SIZE 1 -#define SM_PBBSEL_OFFSET 24 -#define SM_PBBSEL_SIZE 3 -#define SM_PBBDIV_OFFSET 31 -#define SM_PBBDIV_SIZE 1 - -/* Bitfields in PM_CPU_MASK */ - -/* Bitfields in PM_HSB_MASK */ - -/* Bitfields in PM_PBA_MASK */ - -/* Bitfields in PM_PBB_MASK */ - -/* Bitfields in PM_PLL0 */ -#define SM_PLLEN_OFFSET 0 -#define SM_PLLEN_SIZE 1 -#define SM_PLLOSC_OFFSET 1 -#define SM_PLLOSC_SIZE 1 -#define SM_PLLOPT_OFFSET 2 -#define SM_PLLOPT_SIZE 3 -#define SM_PLLDIV_OFFSET 8 -#define SM_PLLDIV_SIZE 8 -#define SM_PLLMUL_OFFSET 16 -#define SM_PLLMUL_SIZE 8 -#define SM_PLLCOUNT_OFFSET 24 -#define SM_PLLCOUNT_SIZE 6 -#define SM_PLLTEST_OFFSET 31 -#define SM_PLLTEST_SIZE 1 - -/* Bitfields in PM_PLL1 */ - -/* Bitfields in PM_VCTRL */ -#define SM_VAUTO_OFFSET 0 -#define SM_VAUTO_SIZE 1 -#define SM_PM_VCTRL_VAL_OFFSET 8 -#define SM_PM_VCTRL_VAL_SIZE 7 - -/* Bitfields in PM_VMREF */ -#define SM_REFSEL_OFFSET 0 -#define SM_REFSEL_SIZE 4 - -/* Bitfields in PM_VMV */ -#define SM_PM_VMV_VAL_OFFSET 0 -#define SM_PM_VMV_VAL_SIZE 8 - -/* Bitfields in PM_IER */ - -/* Bitfields in PM_IDR */ - -/* Bitfields in PM_IMR */ - -/* Bitfields in PM_ISR */ - -/* Bitfields in PM_ICR */ -#define SM_LOCK0_OFFSET 0 -#define SM_LOCK0_SIZE 1 -#define SM_LOCK1_OFFSET 1 -#define SM_LOCK1_SIZE 1 -#define SM_WAKE_OFFSET 2 -#define SM_WAKE_SIZE 1 -#define SM_VOK_OFFSET 3 -#define SM_VOK_SIZE 1 -#define SM_VMRDY_OFFSET 4 -#define SM_VMRDY_SIZE 1 -#define SM_CKRDY_OFFSET 5 -#define SM_CKRDY_SIZE 1 - -/* Bitfields in PM_GCCTRL */ -#define SM_OSCSEL_OFFSET 0 -#define SM_OSCSEL_SIZE 1 -#define SM_PLLSEL_OFFSET 1 -#define SM_PLLSEL_SIZE 1 -#define SM_CEN_OFFSET 2 -#define SM_CEN_SIZE 1 -#define SM_CPC_OFFSET 3 -#define SM_CPC_SIZE 1 -#define SM_DIVEN_OFFSET 4 -#define SM_DIVEN_SIZE 1 -#define SM_DIV_OFFSET 8 -#define SM_DIV_SIZE 8 - -/* Bitfields in RTC_CTRL */ -#define SM_PCLR_OFFSET 1 -#define SM_PCLR_SIZE 1 -#define SM_TOPEN_OFFSET 2 -#define SM_TOPEN_SIZE 1 -#define SM_CLKEN_OFFSET 3 -#define SM_CLKEN_SIZE 1 -#define SM_PSEL_OFFSET 8 -#define SM_PSEL_SIZE 16 - -/* Bitfields in RTC_VAL */ -#define SM_RTC_VAL_VAL_OFFSET 0 -#define SM_RTC_VAL_VAL_SIZE 31 - -/* Bitfields in RTC_TOP */ -#define SM_RTC_TOP_VAL_OFFSET 0 -#define SM_RTC_TOP_VAL_SIZE 32 - -/* Bitfields in RTC_IER */ - -/* Bitfields in RTC_IDR */ - -/* Bitfields in RTC_IMR */ - -/* Bitfields in RTC_ISR */ - -/* Bitfields in RTC_ICR */ -#define SM_TOPI_OFFSET 0 -#define SM_TOPI_SIZE 1 - -/* Bitfields in WDT_CTRL */ -#define SM_KEY_OFFSET 24 -#define SM_KEY_SIZE 8 - -/* Bitfields in WDT_CLR */ - -/* Bitfields in WDT_EXT */ - -/* Bitfields in RC_RCAUSE */ -#define SM_POR_OFFSET 0 -#define SM_POR_SIZE 1 -#define SM_BOD_OFFSET 1 -#define SM_BOD_SIZE 1 -#define SM_EXT_OFFSET 2 -#define SM_EXT_SIZE 1 -#define SM_WDT_OFFSET 3 -#define SM_WDT_SIZE 1 -#define SM_NTAE_OFFSET 4 -#define SM_NTAE_SIZE 1 -#define SM_SERP_OFFSET 5 -#define SM_SERP_SIZE 1 - -/* Bitfields in EIM_IER */ - -/* Bitfields in EIM_IDR */ - -/* Bitfields in EIM_IMR */ - -/* Bitfields in EIM_ISR */ - -/* Bitfields in EIM_ICR */ - -/* Bitfields in EIM_MODE */ - -/* Bitfields in EIM_EDGE */ -#define SM_INT0_OFFSET 0 -#define SM_INT0_SIZE 1 -#define SM_INT1_OFFSET 1 -#define SM_INT1_SIZE 1 -#define SM_INT2_OFFSET 2 -#define SM_INT2_SIZE 1 -#define SM_INT3_OFFSET 3 -#define SM_INT3_SIZE 1 - -/* Bitfields in EIM_LEVEL */ - -/* Bitfields in EIM_TEST */ -#define SM_TESTEN_OFFSET 31 -#define SM_TESTEN_SIZE 1 - -/* Bitfields in EIM_NMIC */ -#define SM_EN_OFFSET 0 -#define SM_EN_SIZE 1 - -/* Bit manipulation macros */ -#define SM_BIT(name) (1 << SM_##name##_OFFSET) -#define SM_BF(name,value) (((value) & ((1 << SM_##name##_SIZE) - 1)) << SM_##name##_OFFSET) -#define SM_BFEXT(name,value) (((value) >> SM_##name##_OFFSET) & ((1 << SM_##name##_SIZE) - 1)) -#define SM_BFINS(name,value,old) (((old) & ~(((1 << SM_##name##_SIZE) - 1) << SM_##name##_OFFSET)) | SM_BF(name,value)) - -/* Register access macros */ -#define sm_readl(port,reg) \ - __raw_readl((port)->regs + SM_##reg) -#define sm_writel(port,reg,value) \ - __raw_writel((value), (port)->regs + SM_##reg) - -#endif /* __ASM_AVR32_SM_H__ */ diff --git a/arch/avr32/mm/fault.c b/arch/avr32/mm/fault.c index 4b2495285d9..ae2d2c593b2 100644 --- a/arch/avr32/mm/fault.c +++ b/arch/avr32/mm/fault.c @@ -64,6 +64,7 @@ asmlinkage void do_page_fault(unsigned long ecr, struct pt_regs *regs) int writeaccess; long signr; int code; + int fault; if (notify_page_fault(regs, ecr)) return; @@ -132,20 +133,18 @@ good_area: * fault. */ survive: - switch (handle_mm_fault(mm, vma, address, writeaccess)) { - case VM_FAULT_MINOR: - tsk->min_flt++; - break; - case VM_FAULT_MAJOR: - tsk->maj_flt++; - break; - case VM_FAULT_SIGBUS: - goto do_sigbus; - case VM_FAULT_OOM: - goto out_of_memory; - default: + fault = handle_mm_fault(mm, vma, address, writeaccess); + if (unlikely(fault & VM_FAULT_ERROR)) { + if (fault & VM_FAULT_OOM) + goto out_of_memory; + else if (fault & VM_FAULT_SIGBUS) + goto do_sigbus; BUG(); } + if (fault & VM_FAULT_MAJOR) + tsk->maj_flt++; + else + tsk->min_flt++; up_read(&mm->mmap_sem); return; diff --git a/arch/blackfin/mm/blackfin_sram.c b/arch/blackfin/mm/blackfin_sram.c index 16c6169ed01..b99ea883cd2 100644 --- a/arch/blackfin/mm/blackfin_sram.c +++ b/arch/blackfin/mm/blackfin_sram.c @@ -521,10 +521,9 @@ void *sram_alloc_with_lsl(size_t size, unsigned long flags) struct sram_list_struct *lsl = NULL; struct mm_struct *mm = current->mm; - lsl = kmalloc(sizeof(struct sram_list_struct), GFP_KERNEL); + lsl = kzalloc(sizeof(struct sram_list_struct), GFP_KERNEL); if (!lsl) return NULL; - memset(lsl, 0, sizeof(*lsl)); if (flags & L1_INST_SRAM) addr = l1_inst_sram_alloc(size); diff --git a/arch/cris/arch-v10/drivers/pcf8563.c b/arch/cris/arch-v10/drivers/pcf8563.c index d47cfbf98d6..1de0026bb94 100644 --- a/arch/cris/arch-v10/drivers/pcf8563.c +++ b/arch/cris/arch-v10/drivers/pcf8563.c @@ -180,9 +180,7 @@ err: void __exit pcf8563_exit(void) { - if (unregister_chrdev(PCF8563_MAJOR, DEVICE_NAME) < 0) { - printk(KERN_INFO "%s: Unable to unregister device.\n", PCF8563_NAME); - } + unregister_chrdev(PCF8563_MAJOR, DEVICE_NAME); } /* diff --git a/arch/cris/arch-v10/kernel/ptrace.c b/arch/cris/arch-v10/kernel/ptrace.c index fd2129a0458..f4f9db698b4 100644 --- a/arch/cris/arch-v10/kernel/ptrace.c +++ b/arch/cris/arch-v10/kernel/ptrace.c @@ -83,19 +83,9 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) switch (request) { /* Read word at location address. */ case PTRACE_PEEKTEXT: - case PTRACE_PEEKDATA: { - unsigned long tmp; - int copied; - - copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); - ret = -EIO; - - if (copied != sizeof(tmp)) - break; - - ret = put_user(tmp,datap); + case PTRACE_PEEKDATA: + ret = generic_ptrace_peekdata(child, addr, data); break; - } /* Read the word at location address in the USER area. */ case PTRACE_PEEKUSR: { @@ -113,12 +103,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) /* Write the word at location address. */ case PTRACE_POKETEXT: case PTRACE_POKEDATA: - ret = 0; - - if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data)) - break; - - ret = -EIO; + ret = generic_ptrace_pokedata(child, addr, data); break; /* Write the word at location address in the USER area. */ diff --git a/arch/cris/arch-v32/drivers/pcf8563.c b/arch/cris/arch-v32/drivers/pcf8563.c index fa8d50007e4..da479a14f83 100644 --- a/arch/cris/arch-v32/drivers/pcf8563.c +++ b/arch/cris/arch-v32/drivers/pcf8563.c @@ -193,9 +193,7 @@ err: void __exit pcf8563_exit(void) { - if (unregister_chrdev(PCF8563_MAJOR, DEVICE_NAME) < 0) { - printk(KERN_INFO "%s: Unable to unregister device.\n", PCF8563_NAME); - } + unregister_chrdev(PCF8563_MAJOR, DEVICE_NAME); } /* diff --git a/arch/cris/arch-v32/drivers/pci/dma.c b/arch/cris/arch-v32/drivers/pci/dma.c index 832fc63504d..66f9500fbc0 100644 --- a/arch/cris/arch-v32/drivers/pci/dma.c +++ b/arch/cris/arch-v32/drivers/pci/dma.c @@ -91,14 +91,12 @@ int dma_declare_coherent_memory(struct device *dev, dma_addr_t bus_addr, if (!mem_base) goto out; - dev->dma_mem = kmalloc(sizeof(struct dma_coherent_mem), GFP_KERNEL); + dev->dma_mem = kzalloc(sizeof(struct dma_coherent_mem), GFP_KERNEL); if (!dev->dma_mem) goto out; - memset(dev->dma_mem, 0, sizeof(struct dma_coherent_mem)); - dev->dma_mem->bitmap = kmalloc(bitmap_size, GFP_KERNEL); + dev->dma_mem->bitmap = kzalloc(bitmap_size, GFP_KERNEL); if (!dev->dma_mem->bitmap) goto free1_out; - memset(dev->dma_mem->bitmap, 0, bitmap_size); dev->dma_mem->virt_base = mem_base; dev->dma_mem->device_base = device_addr; diff --git a/arch/cris/arch-v32/kernel/ptrace.c b/arch/cris/arch-v32/kernel/ptrace.c index d4d57b74133..38ece0cd47c 100644 --- a/arch/cris/arch-v32/kernel/ptrace.c +++ b/arch/cris/arch-v32/kernel/ptrace.c @@ -146,12 +146,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) /* Write the word at location address. */ case PTRACE_POKETEXT: case PTRACE_POKEDATA: - ret = 0; - - if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data)) - break; - - ret = -EIO; + ret = generic_ptrace_pokedata(child, addr, data); break; /* Write the word at location address in the USER area. */ diff --git a/arch/cris/arch-v32/vmlinux.lds.S b/arch/cris/arch-v32/vmlinux.lds.S index e3a32ac17ab..b076c134c0b 100644 --- a/arch/cris/arch-v32/vmlinux.lds.S +++ b/arch/cris/arch-v32/vmlinux.lds.S @@ -91,10 +91,7 @@ SECTIONS } SECURITY_INIT - . = ALIGN (8192); - __per_cpu_start = .; - .data.percpu : { *(.data.percpu) } - __per_cpu_end = .; + PERCPU(8192) #ifdef CONFIG_BLK_DEV_INITRD .init.ramfs : { diff --git a/arch/cris/mm/fault.c b/arch/cris/mm/fault.c index c73e91f1299..8672ab7d797 100644 --- a/arch/cris/mm/fault.c +++ b/arch/cris/mm/fault.c @@ -179,6 +179,7 @@ do_page_fault(unsigned long address, struct pt_regs *regs, struct mm_struct *mm; struct vm_area_struct * vma; siginfo_t info; + int fault; D(printk("Page fault for %lX on %X at %lX, prot %d write %d\n", address, smp_processor_id(), instruction_pointer(regs), @@ -283,18 +284,18 @@ do_page_fault(unsigned long address, struct pt_regs *regs, * the fault. */ - switch (handle_mm_fault(mm, vma, address, writeaccess & 1)) { - case VM_FAULT_MINOR: - tsk->min_flt++; - break; - case VM_FAULT_MAJOR: - tsk->maj_flt++; - break; - case VM_FAULT_SIGBUS: - goto do_sigbus; - default: - goto out_of_memory; + fault = handle_mm_fault(mm, vma, address, writeaccess & 1); + if (unlikely(fault & VM_FAULT_ERROR)) { + if (fault & VM_FAULT_OOM) + goto out_of_memory; + else if (fault & VM_FAULT_SIGBUS) + goto do_sigbus; + BUG(); } + if (fault & VM_FAULT_MAJOR) + tsk->maj_flt++; + else + tsk->min_flt++; up_read(&mm->mmap_sem); return; diff --git a/arch/frv/Makefile b/arch/frv/Makefile index 038e3a8457e..9bf7345c5cc 100644 --- a/arch/frv/Makefile +++ b/arch/frv/Makefile @@ -88,7 +88,7 @@ ASFLAGS += -mno-fdpic # make sure the .S files get compiled with debug info # and disable optimisations that are unhelpful whilst debugging ifdef CONFIG_DEBUG_INFO -CFLAGS += -O1 +#CFLAGS += -O1 AFLAGS += -Wa,--gdwarf2 ASFLAGS += -Wa,--gdwarf2 endif diff --git a/arch/frv/kernel/ptrace.c b/arch/frv/kernel/ptrace.c index ce88fb95ee5..709e9bdc612 100644 --- a/arch/frv/kernel/ptrace.c +++ b/arch/frv/kernel/ptrace.c @@ -112,20 +112,12 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) switch (request) { /* when I and D space are separate, these will need to be fixed. */ case PTRACE_PEEKTEXT: /* read word at location addr. */ - case PTRACE_PEEKDATA: { - int copied; - + case PTRACE_PEEKDATA: ret = -EIO; if (is_user_addr_valid(child, addr, sizeof(tmp)) < 0) break; - - copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); - if (copied != sizeof(tmp)) - break; - - ret = put_user(tmp,(unsigned long *) data); + ret = generic_ptrace_peekdata(child, addr, data); break; - } /* read the word at location addr in the USER area. */ case PTRACE_PEEKUSR: { @@ -176,9 +168,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) ret = -EIO; if (is_user_addr_valid(child, addr, sizeof(tmp)) < 0) break; - if (access_process_vm(child, addr, &data, sizeof(data), 1) != sizeof(data)) - break; - ret = 0; + ret = generic_ptrace_pokedata(child, addr, data); break; case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ diff --git a/arch/frv/kernel/setup.c b/arch/frv/kernel/setup.c index c1c32e4c863..a74c08786b2 100644 --- a/arch/frv/kernel/setup.c +++ b/arch/frv/kernel/setup.c @@ -29,6 +29,7 @@ #include <linux/serial.h> #include <linux/serial_core.h> #include <linux/serial_reg.h> +#include <linux/serial_8250.h> #include <asm/setup.h> #include <asm/irq.h> diff --git a/arch/frv/kernel/vmlinux.lds.S b/arch/frv/kernel/vmlinux.lds.S index 481dc137464..3b71e0c8639 100644 --- a/arch/frv/kernel/vmlinux.lds.S +++ b/arch/frv/kernel/vmlinux.lds.S @@ -57,10 +57,7 @@ SECTIONS __alt_instructions_end = .; .altinstr_replacement : { *(.altinstr_replacement) } - . = ALIGN(4096); - __per_cpu_start = .; - .data.percpu : { *(.data.percpu) } - __per_cpu_end = .; + PERCPU(4096) #ifdef CONFIG_BLK_DEV_INITRD . = ALIGN(4096); diff --git a/arch/frv/mm/fault.c b/arch/frv/mm/fault.c index 3f12296c368..6798fa0257b 100644 --- a/arch/frv/mm/fault.c +++ b/arch/frv/mm/fault.c @@ -40,6 +40,7 @@ asmlinkage void do_page_fault(int datammu, unsigned long esr0, unsigned long ear pud_t *pue; pte_t *pte; int write; + int fault; #if 0 const char *atxc[16] = { @@ -162,18 +163,18 @@ asmlinkage void do_page_fault(int datammu, unsigned long esr0, unsigned long ear * make sure we exit gracefully rather than endlessly redo * the fault. */ - switch (handle_mm_fault(mm, vma, ear0, write)) { - case VM_FAULT_MINOR: - current->min_flt++; - break; - case VM_FAULT_MAJOR: - current->maj_flt++; - break; - case VM_FAULT_SIGBUS: - goto do_sigbus; - default: - goto out_of_memory; + fault = handle_mm_fault(mm, vma, ear0, write); + if (unlikely(fault & VM_FAULT_ERROR)) { + if (fault & VM_FAULT_OOM) + goto out_of_memory; + else if (fault & VM_FAULT_SIGBUS) + goto do_sigbus; + BUG(); } + if (fault & VM_FAULT_MAJOR) + current->maj_flt++; + else + current->min_flt++; up_read(&mm->mmap_sem); return; diff --git a/arch/h8300/kernel/ptrace.c b/arch/h8300/kernel/ptrace.c index 8a7a991b8f7..d32bbf02fc4 100644 --- a/arch/h8300/kernel/ptrace.c +++ b/arch/h8300/kernel/ptrace.c @@ -111,10 +111,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) /* when I and D space are separate, this will have to be fixed. */ case PTRACE_POKETEXT: /* write the word at location addr. */ case PTRACE_POKEDATA: - ret = 0; - if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data)) - break; - ret = -EIO; + ret = generic_ptrace_pokedata(child, addr, data); break; case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ diff --git a/arch/i386/Kconfig b/arch/i386/Kconfig index 8531a540ca8..7a11b905ef4 100644 --- a/arch/i386/Kconfig +++ b/arch/i386/Kconfig @@ -222,6 +222,8 @@ config PARAVIRT However, when run without a hypervisor the kernel is theoretically slower. If in doubt, say N. +source "arch/i386/xen/Kconfig" + config VMI bool "VMI Paravirt-ops support" depends on PARAVIRT @@ -1222,8 +1224,8 @@ if INSTRUMENTATION source "arch/i386/oprofile/Kconfig" config KPROBES - bool "Kprobes (EXPERIMENTAL)" - depends on KALLSYMS && EXPERIMENTAL && MODULES + bool "Kprobes" + depends on KALLSYMS && MODULES help Kprobes allows you to trap at almost any kernel address and execute a callback function. register_kprobe() establishes diff --git a/arch/i386/Makefile b/arch/i386/Makefile index bd28f9f9b4b..01f0ff0daaf 100644 --- a/arch/i386/Makefile +++ b/arch/i386/Makefile @@ -93,6 +93,9 @@ mflags-$(CONFIG_X86_ES7000) := -Iinclude/asm-i386/mach-es7000 mcore-$(CONFIG_X86_ES7000) := mach-default core-$(CONFIG_X86_ES7000) := arch/i386/mach-es7000/ +# Xen paravirtualization support +core-$(CONFIG_XEN) += arch/i386/xen/ + # default subarch .h files mflags-y += -Iinclude/asm-i386/mach-default @@ -108,6 +111,7 @@ drivers-$(CONFIG_PCI) += arch/i386/pci/ # must be linked after kernel/ drivers-$(CONFIG_OPROFILE) += arch/i386/oprofile/ drivers-$(CONFIG_PM) += arch/i386/power/ +drivers-$(CONFIG_FB) += arch/i386/video/ CFLAGS += $(mflags-y) AFLAGS += $(mflags-y) diff --git a/arch/i386/boot/Makefile b/arch/i386/boot/Makefile index 08678a0a3d1..93386a4e40b 100644 --- a/arch/i386/boot/Makefile +++ b/arch/i386/boot/Makefile @@ -39,7 +39,7 @@ setup-y += printf.o string.o tty.o video.o version.o voyager.o setup-y += video-vga.o setup-y += video-vesa.o setup-y += video-bios.o - +targets += $(setup-y) hostprogs-y := tools/build HOSTCFLAGS_build.o := $(LINUXINCLUDE) diff --git a/arch/i386/boot/boot.h b/arch/i386/boot/boot.h index 0329c4fe4f8..dec70c9b605 100644 --- a/arch/i386/boot/boot.h +++ b/arch/i386/boot/boot.h @@ -56,7 +56,7 @@ static inline u16 inw(u16 port) static inline void outl(u32 v, u16 port) { - asm volatile("outl %0,%1" : : "a" (v), "dn" (port)); + asm volatile("outl %0,%1" : : "a" (v), "dN" (port)); } static inline u32 inl(u32 port) { diff --git a/arch/i386/boot/compressed/relocs.c b/arch/i386/boot/compressed/relocs.c index ce4fda261aa..b0e21c3cee5 100644 --- a/arch/i386/boot/compressed/relocs.c +++ b/arch/i386/boot/compressed/relocs.c @@ -31,6 +31,8 @@ static const char* safe_abs_relocs[] = { "__kernel_rt_sigreturn", "__kernel_sigreturn", "SYSENTER_RETURN", + "xen_irq_disable_direct_reloc", + "xen_save_fl_direct_reloc", }; static int is_safe_abs_reloc(const char* sym_name) diff --git a/arch/i386/boot/cpucheck.c b/arch/i386/boot/cpucheck.c index 8b0f4473b08..991e8ceae1d 100644 --- a/arch/i386/boot/cpucheck.c +++ b/arch/i386/boot/cpucheck.c @@ -115,8 +115,8 @@ static int has_eflag(u32 mask) "pushfl ; " "popl %1 ; " "popfl" - : "=r" (f0), "=r" (f1) - : "g" (mask)); + : "=&r" (f0), "=&r" (f1) + : "ri" (mask)); return !!((f0^f1) & mask); } diff --git a/arch/i386/boot/mca.c b/arch/i386/boot/mca.c index 9b68bd1aef1..68222f2d4b6 100644 --- a/arch/i386/boot/mca.c +++ b/arch/i386/boot/mca.c @@ -26,7 +26,7 @@ int query_mca(void) "setc %0 ; " "movw %%es, %1 ; " "popw %%es" - : "=acdSDm" (err), "=acdSDm" (es), "=b" (bx) + : "=acd" (err), "=acdSD" (es), "=b" (bx) : "a" (0xc000)); if (err) diff --git a/arch/i386/boot/pm.c b/arch/i386/boot/pm.c index 3fa53e15ed7..1df025c7326 100644 --- a/arch/i386/boot/pm.c +++ b/arch/i386/boot/pm.c @@ -65,7 +65,7 @@ static void move_kernel_around(void) "popw %%ds ; " "popw %%es" : "+c" (dwords) - : "rm" (dst_seg), "rm" (src_seg) + : "r" (dst_seg), "r" (src_seg) : "esi", "edi"); syssize -= paras; diff --git a/arch/i386/boot/tools/build.c b/arch/i386/boot/tools/build.c index 886f47d8a48..b4248740ff0 100644 --- a/arch/i386/boot/tools/build.c +++ b/arch/i386/boot/tools/build.c @@ -5,7 +5,7 @@ */ /* - * This file builds a disk-image from three different files: + * This file builds a disk-image from two different files: * * - setup: 8086 machine code, sets up system parm * - system: 80386 code for actual system diff --git a/arch/i386/boot/tty.c b/arch/i386/boot/tty.c index a8db78736b0..9c668aad351 100644 --- a/arch/i386/boot/tty.c +++ b/arch/i386/boot/tty.c @@ -31,7 +31,7 @@ void __attribute__((section(".inittext"))) putchar(int ch) /* int $0x10 is known to have bugs involving touching registers it shouldn't. Be extra conservative... */ - asm volatile("pushal; int $0x10; popal" + asm volatile("pushal; pushw %%ds; int $0x10; popw %%ds; popal" : : "b" (0x0007), "c" (0x0001), "a" (0x0e00|ch)); } diff --git a/arch/i386/boot/video.c b/arch/i386/boot/video.c index 3bb3573cd6a..958130ef004 100644 --- a/arch/i386/boot/video.c +++ b/arch/i386/boot/video.c @@ -195,7 +195,7 @@ static void vga_recalc_vertical(void) { unsigned int font_size, rows; u16 crtc; - u8 ov; + u8 pt, ov; set_fs(0); font_size = rdfs8(0x485); /* BIOS: font size (pixels) */ @@ -206,7 +206,12 @@ static void vga_recalc_vertical(void) crtc = vga_crtc(); + pt = in_idx(crtc, 0x11); + pt &= ~0x80; /* Unlock CR0-7 */ + out_idx(pt, crtc, 0x11); + out_idx((u8)rows, crtc, 0x12); /* Lower height register */ + ov = in_idx(crtc, 0x07); /* Overflow register */ ov &= 0xbd; ov |= (rows >> (8-1)) & 0x02; @@ -411,7 +416,7 @@ static void restore_screen(void) "1: rep;stosl ; " "popw %%es" : "+D" (dst), "+c" (npad) - : "bdSm" (video_segment), + : "bdS" (video_segment), "a" (0x07200720)); } diff --git a/arch/i386/boot/video.h b/arch/i386/boot/video.h index 29eca1710b2..b92447d5121 100644 --- a/arch/i386/boot/video.h +++ b/arch/i386/boot/video.h @@ -117,8 +117,15 @@ extern int graphic_mode; /* Graphics mode with linear frame buffer */ * int $0x10 is notorious for touching registers it shouldn't. * gcc doesn't like %ebp being clobbered, so define it as a push/pop * sequence here. + * + * A number of systems, including the original PC can clobber %bp in + * certain circumstances, like when scrolling. There exists at least + * one Trident video card which could clobber DS under a set of + * circumstances that we are unlikely to encounter (scrolling when + * using an extended graphics mode of more than 800x600 pixels), but + * it's cheap insurance to deal with that here. */ -#define INT10 "pushl %%ebp; int $0x10; popl %%ebp" +#define INT10 "pushl %%ebp; pushw %%ds; int $0x10; popw %%ds; popl %%ebp" /* Accessing VGA indexed registers */ static inline u8 in_idx(u16 port, u8 index) diff --git a/arch/i386/boot/voyager.c b/arch/i386/boot/voyager.c index 9221614d0db..61c8fe0453b 100644 --- a/arch/i386/boot/voyager.c +++ b/arch/i386/boot/voyager.c @@ -32,7 +32,7 @@ int query_voyager(void) "setc %0 ; " "movw %%es, %1 ; " "popw %%es" - : "=qm" (err), "=rm" (es), "=D" (di) + : "=q" (err), "=r" (es), "=D" (di) : "a" (0xffc0)); if (err) diff --git a/arch/i386/kernel/acpi/sleep.c b/arch/i386/kernel/acpi/sleep.c index 4ee83577bf6..c42b5ab49de 100644 --- a/arch/i386/kernel/acpi/sleep.c +++ b/arch/i386/kernel/acpi/sleep.c @@ -14,7 +14,7 @@ /* address in low memory of the wakeup routine. */ unsigned long acpi_wakeup_address = 0; -unsigned long acpi_video_flags; +unsigned long acpi_realmode_flags; extern char wakeup_start, wakeup_end; extern unsigned long FASTCALL(acpi_copy_wakeup_routine(unsigned long)); @@ -68,9 +68,11 @@ static int __init acpi_sleep_setup(char *str) { while ((str != NULL) && (*str != '\0')) { if (strncmp(str, "s3_bios", 7) == 0) - acpi_video_flags = 1; + acpi_realmode_flags |= 1; if (strncmp(str, "s3_mode", 7) == 0) - acpi_video_flags |= 2; + acpi_realmode_flags |= 2; + if (strncmp(str, "s3_beep", 7) == 0) + acpi_realmode_flags |= 4; str = strchr(str, ','); if (str != NULL) str += strspn(str, ", \t"); @@ -80,9 +82,11 @@ static int __init acpi_sleep_setup(char *str) __setup("acpi_sleep=", acpi_sleep_setup); +/* Ouch, we want to delete this. We already have better version in userspace, in + s2ram from suspend.sf.net project */ static __init int reset_videomode_after_s3(struct dmi_system_id *d) { - acpi_video_flags |= 2; + acpi_realmode_flags |= 2; return 0; } diff --git a/arch/i386/kernel/acpi/wakeup.S b/arch/i386/kernel/acpi/wakeup.S index a2295a34b2c..ed0a0f2c159 100644 --- a/arch/i386/kernel/acpi/wakeup.S +++ b/arch/i386/kernel/acpi/wakeup.S @@ -13,6 +13,21 @@ # cs = 0x1234, eip = 0x05 # +#define BEEP \ + inb $97, %al; \ + outb %al, $0x80; \ + movb $3, %al; \ + outb %al, $97; \ + outb %al, $0x80; \ + movb $-74, %al; \ + outb %al, $67; \ + outb %al, $0x80; \ + movb $-119, %al; \ + outb %al, $66; \ + outb %al, $0x80; \ + movb $15, %al; \ + outb %al, $66; + ALIGN .align 4096 ENTRY(wakeup_start) @@ -31,6 +46,11 @@ wakeup_code: movw %cs, %ax movw %ax, %ds # Make ds:0 point to wakeup_start movw %ax, %ss + + testl $4, realmode_flags - wakeup_code + jz 1f + BEEP +1: mov $(wakeup_stack - wakeup_code), %sp # Private stack is needed for ASUS board movw $0x0e00 + 'S', %fs:(0x12) @@ -41,7 +61,7 @@ wakeup_code: cmpl $0x12345678, %eax jne bogus_real_magic - testl $1, video_flags - wakeup_code + testl $1, realmode_flags - wakeup_code jz 1f lcall $0xc000,$3 movw %cs, %ax @@ -49,7 +69,7 @@ wakeup_code: movw %ax, %ss 1: - testl $2, video_flags - wakeup_code + testl $2, realmode_flags - wakeup_code jz 1f mov video_mode - wakeup_code, %ax call mode_set @@ -88,7 +108,11 @@ wakeup_code: cmpl $0x12345678, %eax jne bogus_real_magic - ljmpl $__KERNEL_CS,$wakeup_pmode_return + testl $8, realmode_flags - wakeup_code + jz 1f + BEEP +1: + ljmpl $__KERNEL_CS, $wakeup_pmode_return real_save_gdt: .word 0 .long 0 @@ -97,7 +121,8 @@ real_save_cr3: .long 0 real_save_cr4: .long 0 real_magic: .long 0 video_mode: .long 0 -video_flags: .long 0 +realmode_flags: .long 0 +beep_flags: .long 0 real_efer_save_restore: .long 0 real_save_efer_edx: .long 0 real_save_efer_eax: .long 0 @@ -260,8 +285,8 @@ ENTRY(acpi_copy_wakeup_routine) movl saved_videomode, %edx movl %edx, video_mode - wakeup_start (%eax) - movl acpi_video_flags, %edx - movl %edx, video_flags - wakeup_start (%eax) + movl acpi_realmode_flags, %edx + movl %edx, realmode_flags - wakeup_start (%eax) movl $0x12345678, real_magic - wakeup_start (%eax) movl $0x12345678, saved_magic popl %ebx diff --git a/arch/i386/kernel/apm.c b/arch/i386/kernel/apm.c index 4112afe712b..47001d50a08 100644 --- a/arch/i386/kernel/apm.c +++ b/arch/i386/kernel/apm.c @@ -222,6 +222,7 @@ #include <linux/capability.h> #include <linux/device.h> #include <linux/kernel.h> +#include <linux/freezer.h> #include <linux/smp.h> #include <linux/dmi.h> #include <linux/suspend.h> @@ -2311,7 +2312,6 @@ static int __init apm_init(void) remove_proc_entry("apm", NULL); return err; } - kapmd_task->flags |= PF_NOFREEZE; wake_up_process(kapmd_task); if (num_online_cpus() > 1 && !smp ) { diff --git a/arch/i386/kernel/asm-offsets.c b/arch/i386/kernel/asm-offsets.c index 27a776c9044..7288ac88d74 100644 --- a/arch/i386/kernel/asm-offsets.c +++ b/arch/i386/kernel/asm-offsets.c @@ -17,6 +17,13 @@ #include <asm/thread_info.h> #include <asm/elf.h> +#include <xen/interface/xen.h> + +#ifdef CONFIG_LGUEST_GUEST +#include <linux/lguest.h> +#include "../../../drivers/lguest/lg.h" +#endif + #define DEFINE(sym, val) \ asm volatile("\n->" #sym " %0 " #val : : "i" (val)) @@ -59,6 +66,7 @@ void foo(void) OFFSET(TI_addr_limit, thread_info, addr_limit); OFFSET(TI_restart_block, thread_info, restart_block); OFFSET(TI_sysenter_return, thread_info, sysenter_return); + OFFSET(TI_cpu, thread_info, cpu); BLANK(); OFFSET(GDS_size, Xgt_desc_struct, size); @@ -115,4 +123,25 @@ void foo(void) OFFSET(PARAVIRT_iret, paravirt_ops, iret); OFFSET(PARAVIRT_read_cr0, paravirt_ops, read_cr0); #endif + +#ifdef CONFIG_XEN + BLANK(); + OFFSET(XEN_vcpu_info_mask, vcpu_info, evtchn_upcall_mask); + OFFSET(XEN_vcpu_info_pending, vcpu_info, evtchn_upcall_pending); +#endif + +#ifdef CONFIG_LGUEST_GUEST + BLANK(); + OFFSET(LGUEST_DATA_irq_enabled, lguest_data, irq_enabled); + OFFSET(LGUEST_PAGES_host_gdt_desc, lguest_pages, state.host_gdt_desc); + OFFSET(LGUEST_PAGES_host_idt_desc, lguest_pages, state.host_idt_desc); + OFFSET(LGUEST_PAGES_host_cr3, lguest_pages, state.host_cr3); + OFFSET(LGUEST_PAGES_host_sp, lguest_pages, state.host_sp); + OFFSET(LGUEST_PAGES_guest_gdt_desc, lguest_pages,state.guest_gdt_desc); + OFFSET(LGUEST_PAGES_guest_idt_desc, lguest_pages,state.guest_idt_desc); + OFFSET(LGUEST_PAGES_guest_gdt, lguest_pages, state.guest_gdt); + OFFSET(LGUEST_PAGES_regs_trapnum, lguest_pages, regs.trapnum); + OFFSET(LGUEST_PAGES_regs_errcode, lguest_pages, regs.errcode); + OFFSET(LGUEST_PAGES_regs, lguest_pages, regs); +#endif } diff --git a/arch/i386/kernel/cpu/mcheck/therm_throt.c b/arch/i386/kernel/cpu/mcheck/therm_throt.c index 7ba7c3abd3a..1203dc5ab87 100644 --- a/arch/i386/kernel/cpu/mcheck/therm_throt.c +++ b/arch/i386/kernel/cpu/mcheck/therm_throt.c @@ -134,19 +134,21 @@ static __cpuinit int thermal_throttle_cpu_callback(struct notifier_block *nfb, int err; sys_dev = get_cpu_sysdev(cpu); - mutex_lock(&therm_cpu_lock); switch (action) { case CPU_ONLINE: case CPU_ONLINE_FROZEN: + mutex_lock(&therm_cpu_lock); err = thermal_throttle_add_dev(sys_dev); + mutex_unlock(&therm_cpu_lock); WARN_ON(err); break; case CPU_DEAD: case CPU_DEAD_FROZEN: + mutex_lock(&therm_cpu_lock); thermal_throttle_remove_dev(sys_dev); + mutex_unlock(&therm_cpu_lock); break; } - mutex_unlock(&therm_cpu_lock); return NOTIFY_OK; } diff --git a/arch/i386/kernel/efi.c b/arch/i386/kernel/efi.c index a1808022ea1..2452c6fbe99 100644 --- a/arch/i386/kernel/efi.c +++ b/arch/i386/kernel/efi.c @@ -278,7 +278,7 @@ void efi_memmap_walk(efi_freemem_callback_t callback, void *arg) struct range { unsigned long start; unsigned long end; - } prev, curr; + } uninitialized_var(prev), curr; efi_memory_desc_t *md; unsigned long start, end; void *p; diff --git a/arch/i386/kernel/entry.S b/arch/i386/kernel/entry.S index 3c3c220488c..a714d6b4350 100644 --- a/arch/i386/kernel/entry.S +++ b/arch/i386/kernel/entry.S @@ -409,8 +409,6 @@ restore_nocheck_notrace: 1: INTERRUPT_RETURN .section .fixup,"ax" iret_exc: - TRACE_IRQS_ON - ENABLE_INTERRUPTS(CLBR_NONE) pushl $0 # no error code pushl $do_iret_error jmp error_code @@ -1023,6 +1021,91 @@ ENTRY(kernel_thread_helper) CFI_ENDPROC ENDPROC(kernel_thread_helper) +#ifdef CONFIG_XEN +ENTRY(xen_hypervisor_callback) + CFI_STARTPROC + pushl $0 + CFI_ADJUST_CFA_OFFSET 4 + SAVE_ALL + TRACE_IRQS_OFF + + /* Check to see if we got the event in the critical + region in xen_iret_direct, after we've reenabled + events and checked for pending events. This simulates + iret instruction's behaviour where it delivers a + pending interrupt when enabling interrupts. */ + movl PT_EIP(%esp),%eax + cmpl $xen_iret_start_crit,%eax + jb 1f + cmpl $xen_iret_end_crit,%eax + jae 1f + + call xen_iret_crit_fixup + +1: mov %esp, %eax + call xen_evtchn_do_upcall + jmp ret_from_intr + CFI_ENDPROC +ENDPROC(xen_hypervisor_callback) + +# Hypervisor uses this for application faults while it executes. +# We get here for two reasons: +# 1. Fault while reloading DS, ES, FS or GS +# 2. Fault while executing IRET +# Category 1 we fix up by reattempting the load, and zeroing the segment +# register if the load fails. +# Category 2 we fix up by jumping to do_iret_error. We cannot use the +# normal Linux return path in this case because if we use the IRET hypercall +# to pop the stack frame we end up in an infinite loop of failsafe callbacks. +# We distinguish between categories by maintaining a status value in EAX. +ENTRY(xen_failsafe_callback) + CFI_STARTPROC + pushl %eax + CFI_ADJUST_CFA_OFFSET 4 + movl $1,%eax +1: mov 4(%esp),%ds +2: mov 8(%esp),%es +3: mov 12(%esp),%fs +4: mov 16(%esp),%gs + testl %eax,%eax + popl %eax + CFI_ADJUST_CFA_OFFSET -4 + lea 16(%esp),%esp + CFI_ADJUST_CFA_OFFSET -16 + jz 5f + addl $16,%esp + jmp iret_exc # EAX != 0 => Category 2 (Bad IRET) +5: pushl $0 # EAX == 0 => Category 1 (Bad segment) + CFI_ADJUST_CFA_OFFSET 4 + SAVE_ALL + jmp ret_from_exception + CFI_ENDPROC + +.section .fixup,"ax" +6: xorl %eax,%eax + movl %eax,4(%esp) + jmp 1b +7: xorl %eax,%eax + movl %eax,8(%esp) + jmp 2b +8: xorl %eax,%eax + movl %eax,12(%esp) + jmp 3b +9: xorl %eax,%eax + movl %eax,16(%esp) + jmp 4b +.previous +.section __ex_table,"a" + .align 4 + .long 1b,6b + .long 2b,7b + .long 3b,8b + .long 4b,9b +.previous +ENDPROC(xen_failsafe_callback) + +#endif /* CONFIG_XEN */ + .section .rodata,"a" #include "syscall_table.S" diff --git a/arch/i386/kernel/head.S b/arch/i386/kernel/head.S index 82714668d43..7c52b222207 100644 --- a/arch/i386/kernel/head.S +++ b/arch/i386/kernel/head.S @@ -510,7 +510,8 @@ ENTRY(_stext) /* * BSS section */ -.section ".bss.page_aligned","w" +.section ".bss.page_aligned","wa" + .align PAGE_SIZE_asm ENTRY(swapper_pg_dir) .fill 1024,4,0 ENTRY(swapper_pg_pmd) @@ -538,6 +539,8 @@ fault_msg: .ascii "Int %d: CR2 %p err %p EIP %p CS %p flags %p\n" .asciz "Stack: %p %p %p %p %p %p %p %p\n" +#include "../xen/xen-head.S" + /* * The IDT and GDT 'descriptors' are a strange 48-bit object * only used by the lidt and lgdt instructions. They are not diff --git a/arch/i386/kernel/init_task.c b/arch/i386/kernel/init_task.c index cff95d10a4d..d26fc063a76 100644 --- a/arch/i386/kernel/init_task.c +++ b/arch/i386/kernel/init_task.c @@ -42,5 +42,5 @@ EXPORT_SYMBOL(init_task); * per-CPU TSS segments. Threads are completely 'soft' on Linux, * no more per-task TSS's. */ -DEFINE_PER_CPU(struct tss_struct, init_tss) ____cacheline_internodealigned_in_smp = INIT_TSS; +DEFINE_PER_CPU_SHARED_ALIGNED(struct tss_struct, init_tss) = INIT_TSS; diff --git a/arch/i386/kernel/io_apic.c b/arch/i386/kernel/io_apic.c index 7f8b7af2b95..21db8f56c9a 100644 --- a/arch/i386/kernel/io_apic.c +++ b/arch/i386/kernel/io_apic.c @@ -667,6 +667,7 @@ static int balanced_irq(void *unused) set_pending_irq(i, cpumask_of_cpu(0)); } + set_freezable(); for ( ; ; ) { time_remaining = schedule_timeout_interruptible(time_remaining); try_to_freeze(); diff --git a/arch/i386/kernel/irq.c b/arch/i386/kernel/irq.c index d2daf672f4a..ba44d40b066 100644 --- a/arch/i386/kernel/irq.c +++ b/arch/i386/kernel/irq.c @@ -21,7 +21,7 @@ #include <asm/apic.h> #include <asm/uaccess.h> -DEFINE_PER_CPU(irq_cpustat_t, irq_stat) ____cacheline_internodealigned_in_smp; +DEFINE_PER_CPU_SHARED_ALIGNED(irq_cpustat_t, irq_stat); EXPORT_PER_CPU_SYMBOL(irq_stat); DEFINE_PER_CPU(struct pt_regs *, irq_regs); diff --git a/arch/i386/kernel/nmi.c b/arch/i386/kernel/nmi.c index fba121f7973..03b7f5584d7 100644 --- a/arch/i386/kernel/nmi.c +++ b/arch/i386/kernel/nmi.c @@ -295,7 +295,7 @@ static unsigned int last_irq_sums [NR_CPUS], alert_counter [NR_CPUS]; -void touch_nmi_watchdog (void) +void touch_nmi_watchdog(void) { if (nmi_watchdog > 0) { unsigned cpu; @@ -304,8 +304,10 @@ void touch_nmi_watchdog (void) * Just reset the alert counters, (other CPUs might be * spinning on locks we hold): */ - for_each_present_cpu (cpu) - alert_counter[cpu] = 0; + for_each_present_cpu(cpu) { + if (alert_counter[cpu]) + alert_counter[cpu] = 0; + } } /* diff --git a/arch/i386/kernel/paravirt.c b/arch/i386/kernel/paravirt.c index faab09abca5..53f07a8275e 100644 --- a/arch/i386/kernel/paravirt.c +++ b/arch/i386/kernel/paravirt.c @@ -228,6 +228,41 @@ static int __init print_banner(void) } core_initcall(print_banner); +static struct resource reserve_ioports = { + .start = 0, + .end = IO_SPACE_LIMIT, + .name = "paravirt-ioport", + .flags = IORESOURCE_IO | IORESOURCE_BUSY, +}; + +static struct resource reserve_iomem = { + .start = 0, + .end = -1, + .name = "paravirt-iomem", + .flags = IORESOURCE_MEM | IORESOURCE_BUSY, +}; + +/* + * Reserve the whole legacy IO space to prevent any legacy drivers + * from wasting time probing for their hardware. This is a fairly + * brute-force approach to disabling all non-virtual drivers. + * + * Note that this must be called very early to have any effect. + */ +int paravirt_disable_iospace(void) +{ + int ret; + + ret = request_resource(&ioport_resource, &reserve_ioports); + if (ret == 0) { + ret = request_resource(&iomem_resource, &reserve_iomem); + if (ret) + release_resource(&reserve_ioports); + } + + return ret; +} + struct paravirt_ops paravirt_ops = { .name = "bare hardware", .paravirt_enabled = 0, @@ -267,7 +302,7 @@ struct paravirt_ops paravirt_ops = { .write_msr = native_write_msr_safe, .read_tsc = native_read_tsc, .read_pmc = native_read_pmc, - .get_scheduled_cycles = native_read_tsc, + .sched_clock = native_sched_clock, .get_cpu_khz = native_calculate_cpu_khz, .load_tr_desc = native_load_tr_desc, .set_ldt = native_set_ldt, diff --git a/arch/i386/kernel/ptrace.c b/arch/i386/kernel/ptrace.c index 0c0ceec5de0..0c8f00e69c4 100644 --- a/arch/i386/kernel/ptrace.c +++ b/arch/i386/kernel/ptrace.c @@ -164,14 +164,22 @@ static unsigned long convert_eip_to_linear(struct task_struct *child, struct pt_ u32 *desc; unsigned long base; - down(&child->mm->context.sem); - desc = child->mm->context.ldt + (seg & ~7); - base = (desc[0] >> 16) | ((desc[1] & 0xff) << 16) | (desc[1] & 0xff000000); + seg &= ~7UL; - /* 16-bit code segment? */ - if (!((desc[1] >> 22) & 1)) - addr &= 0xffff; - addr += base; + down(&child->mm->context.sem); + if (unlikely((seg >> 3) >= child->mm->context.size)) + addr = -1L; /* bogus selector, access would fault */ + else { + desc = child->mm->context.ldt + seg; + base = ((desc[0] >> 16) | + ((desc[1] & 0xff) << 16) | + (desc[1] & 0xff000000)); + + /* 16-bit code segment? */ + if (!((desc[1] >> 22) & 1)) + addr &= 0xffff; + addr += base; + } up(&child->mm->context.sem); } return addr; @@ -358,17 +366,9 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) switch (request) { /* when I and D space are separate, these will need to be fixed. */ case PTRACE_PEEKTEXT: /* read word at location addr. */ - case PTRACE_PEEKDATA: { - unsigned long tmp; - int copied; - - copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); - ret = -EIO; - if (copied != sizeof(tmp)) - break; - ret = put_user(tmp, datap); + case PTRACE_PEEKDATA: + ret = generic_ptrace_peekdata(child, addr, data); break; - } /* read the word at location addr in the USER area. */ case PTRACE_PEEKUSR: { @@ -395,10 +395,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) /* when I and D space are separate, this will have to be fixed. */ case PTRACE_POKETEXT: /* write the word at location addr. */ case PTRACE_POKEDATA: - ret = 0; - if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data)) - break; - ret = -EIO; + ret = generic_ptrace_pokedata(child, addr, data); break; case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c index 2d61e65eeb5..74871d066c2 100644 --- a/arch/i386/kernel/setup.c +++ b/arch/i386/kernel/setup.c @@ -601,6 +601,8 @@ void __init setup_arch(char **cmdline_p) * NOTE: at this point the bootmem allocator is fully available. */ + paravirt_post_allocator_init(); + dmi_scan_machine(); #ifdef CONFIG_X86_GENERICARCH diff --git a/arch/i386/kernel/smp.c b/arch/i386/kernel/smp.c index 6299c080f6e..2d35d850202 100644 --- a/arch/i386/kernel/smp.c +++ b/arch/i386/kernel/smp.c @@ -22,6 +22,7 @@ #include <asm/mtrr.h> #include <asm/tlbflush.h> +#include <asm/mmu_context.h> #include <mach_apic.h> /* @@ -249,13 +250,13 @@ static unsigned long flush_va; static DEFINE_SPINLOCK(tlbstate_lock); /* - * We cannot call mmdrop() because we are in interrupt context, + * We cannot call mmdrop() because we are in interrupt context, * instead update mm->cpu_vm_mask. * * We need to reload %cr3 since the page tables may be going * away from under us.. */ -static inline void leave_mm (unsigned long cpu) +void leave_mm(unsigned long cpu) { if (per_cpu(cpu_tlbstate, cpu).state == TLBSTATE_OK) BUG(); diff --git a/arch/i386/kernel/smpboot.c b/arch/i386/kernel/smpboot.c index 0b2954534b8..5910d3fac56 100644 --- a/arch/i386/kernel/smpboot.c +++ b/arch/i386/kernel/smpboot.c @@ -148,7 +148,7 @@ void __init smp_alloc_memory(void) * a given CPU */ -static void __cpuinit smp_store_cpu_info(int id) +void __cpuinit smp_store_cpu_info(int id) { struct cpuinfo_x86 *c = cpu_data + id; @@ -308,8 +308,7 @@ cpumask_t cpu_coregroup_map(int cpu) /* representing cpus for which sibling maps can be computed */ static cpumask_t cpu_sibling_setup_map; -static inline void -set_cpu_sibling_map(int cpu) +void set_cpu_sibling_map(int cpu) { int i; struct cpuinfo_x86 *c = cpu_data; @@ -1144,8 +1143,7 @@ void __init native_smp_prepare_boot_cpu(void) } #ifdef CONFIG_HOTPLUG_CPU -static void -remove_siblinginfo(int cpu) +void remove_siblinginfo(int cpu) { int sibling; struct cpuinfo_x86 *c = cpu_data; diff --git a/arch/i386/kernel/smpcommon.c b/arch/i386/kernel/smpcommon.c index 1868ae18eb4..bbfe85a0f69 100644 --- a/arch/i386/kernel/smpcommon.c +++ b/arch/i386/kernel/smpcommon.c @@ -47,7 +47,7 @@ int smp_call_function(void (*func) (void *info), void *info, int nonatomic, EXPORT_SYMBOL(smp_call_function); /** - * smp_call_function_single - Run a function on another CPU + * smp_call_function_single - Run a function on a specific CPU * @cpu: The target CPU. Cannot be the calling CPU. * @func: The function to run. This must be fast and non-blocking. * @info: An arbitrary pointer to pass to the function. @@ -66,9 +66,11 @@ int smp_call_function_single(int cpu, void (*func) (void *info), void *info, int ret; int me = get_cpu(); if (cpu == me) { - WARN_ON(1); + local_irq_disable(); + func(info); + local_irq_enable(); put_cpu(); - return -EBUSY; + return 0; } ret = smp_call_function_mask(cpumask_of_cpu(cpu), func, info, wait); diff --git a/arch/i386/kernel/syscall_table.S b/arch/i386/kernel/syscall_table.S index bf6adce5226..8344c70adf6 100644 --- a/arch/i386/kernel/syscall_table.S +++ b/arch/i386/kernel/syscall_table.S @@ -323,3 +323,4 @@ ENTRY(sys_call_table) .long sys_signalfd .long sys_timerfd .long sys_eventfd + .long sys_fallocate diff --git a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c index 28bd1c5163e..3e7753c78b9 100644 --- a/arch/i386/kernel/traps.c +++ b/arch/i386/kernel/traps.c @@ -41,6 +41,10 @@ #include <linux/mca.h> #endif +#if defined(CONFIG_EDAC) +#include <linux/edac.h> +#endif + #include <asm/processor.h> #include <asm/system.h> #include <asm/io.h> @@ -433,6 +437,7 @@ void die(const char * str, struct pt_regs * regs, long err) bust_spinlocks(0); die.lock_owner = -1; + add_taint(TAINT_DIE); spin_unlock_irqrestore(&die.lock, flags); if (!regs) @@ -517,10 +522,12 @@ fastcall void do_##name(struct pt_regs * regs, long error_code) \ do_trap(trapnr, signr, str, 0, regs, error_code, NULL); \ } -#define DO_ERROR_INFO(trapnr, signr, str, name, sicode, siaddr) \ +#define DO_ERROR_INFO(trapnr, signr, str, name, sicode, siaddr, irq) \ fastcall void do_##name(struct pt_regs * regs, long error_code) \ { \ siginfo_t info; \ + if (irq) \ + local_irq_enable(); \ info.si_signo = signr; \ info.si_errno = 0; \ info.si_code = sicode; \ @@ -560,13 +567,13 @@ DO_VM86_ERROR( 3, SIGTRAP, "int3", int3) #endif DO_VM86_ERROR( 4, SIGSEGV, "overflow", overflow) DO_VM86_ERROR( 5, SIGSEGV, "bounds", bounds) -DO_ERROR_INFO( 6, SIGILL, "invalid opcode", invalid_op, ILL_ILLOPN, regs->eip) +DO_ERROR_INFO( 6, SIGILL, "invalid opcode", invalid_op, ILL_ILLOPN, regs->eip, 0) DO_ERROR( 9, SIGFPE, "coprocessor segment overrun", coprocessor_segment_overrun) DO_ERROR(10, SIGSEGV, "invalid TSS", invalid_TSS) DO_ERROR(11, SIGBUS, "segment not present", segment_not_present) DO_ERROR(12, SIGBUS, "stack segment", stack_segment) -DO_ERROR_INFO(17, SIGBUS, "alignment check", alignment_check, BUS_ADRALN, 0) -DO_ERROR_INFO(32, SIGSEGV, "iret exception", iret_error, ILL_BADSTK, 0) +DO_ERROR_INFO(17, SIGBUS, "alignment check", alignment_check, BUS_ADRALN, 0, 0) +DO_ERROR_INFO(32, SIGSEGV, "iret exception", iret_error, ILL_BADSTK, 0, 1) fastcall void __kprobes do_general_protection(struct pt_regs * regs, long error_code) @@ -635,6 +642,14 @@ mem_parity_error(unsigned char reason, struct pt_regs * regs) printk(KERN_EMERG "Uhhuh. NMI received for unknown reason %02x on " "CPU %d.\n", reason, smp_processor_id()); printk(KERN_EMERG "You have some hardware problem, likely on the PCI bus.\n"); + +#if defined(CONFIG_EDAC) + if(edac_handler_set()) { + edac_atomic_assert_error(); + return; + } +#endif + if (panic_on_unrecovered_nmi) panic("NMI: Not continuing"); @@ -1053,6 +1068,7 @@ asmlinkage void math_state_restore(void) thread->status |= TS_USEDFPU; /* So we fnsave on switch_to() */ tsk->fpu_counter++; } +EXPORT_SYMBOL_GPL(math_state_restore); #ifndef CONFIG_MATH_EMULATION diff --git a/arch/i386/kernel/tsc.c b/arch/i386/kernel/tsc.c index ea63a30ca3e..debd7dbb415 100644 --- a/arch/i386/kernel/tsc.c +++ b/arch/i386/kernel/tsc.c @@ -27,6 +27,7 @@ static int tsc_enabled; * an extra value to store the TSC freq */ unsigned int tsc_khz; +EXPORT_SYMBOL_GPL(tsc_khz); int tsc_disable; @@ -58,10 +59,11 @@ __setup("notsc", tsc_setup); */ static int tsc_unstable; -static inline int check_tsc_unstable(void) +int check_tsc_unstable(void) { return tsc_unstable; } +EXPORT_SYMBOL_GPL(check_tsc_unstable); /* Accellerators for sched_clock() * convert from cycles(64bits) => nanoseconds (64bits) @@ -84,7 +86,7 @@ static inline int check_tsc_unstable(void) * * -johnstul@us.ibm.com "math is hard, lets go shopping!" */ -static unsigned long cyc2ns_scale __read_mostly; +unsigned long cyc2ns_scale __read_mostly; #define CYC2NS_SCALE_FACTOR 10 /* 2^10, carefully chosen */ @@ -93,15 +95,10 @@ static inline void set_cyc2ns_scale(unsigned long cpu_khz) cyc2ns_scale = (1000000 << CYC2NS_SCALE_FACTOR)/cpu_khz; } -static inline unsigned long long cycles_2_ns(unsigned long long cyc) -{ - return (cyc * cyc2ns_scale) >> CYC2NS_SCALE_FACTOR; -} - /* * Scheduler clock - returns current time in nanosec units. */ -unsigned long long sched_clock(void) +unsigned long long native_sched_clock(void) { unsigned long long this_offset; @@ -118,12 +115,24 @@ unsigned long long sched_clock(void) return (jiffies_64 - INITIAL_JIFFIES) * (1000000000 / HZ); /* read the Time Stamp Counter: */ - get_scheduled_cycles(this_offset); + rdtscll(this_offset); /* return the value in ns */ return cycles_2_ns(this_offset); } +/* We need to define a real function for sched_clock, to override the + weak default version */ +#ifdef CONFIG_PARAVIRT +unsigned long long sched_clock(void) +{ + return paravirt_sched_clock(); +} +#else +unsigned long long sched_clock(void) + __attribute__((alias("native_sched_clock"))); +#endif + unsigned long native_calculate_cpu_khz(void) { unsigned long long start, end; diff --git a/arch/i386/kernel/vmi.c b/arch/i386/kernel/vmi.c index c12720d7cbc..72042bb7ec9 100644 --- a/arch/i386/kernel/vmi.c +++ b/arch/i386/kernel/vmi.c @@ -362,7 +362,7 @@ static void *vmi_kmap_atomic_pte(struct page *page, enum km_type type) } #endif -static void vmi_allocate_pt(u32 pfn) +static void vmi_allocate_pt(struct mm_struct *mm, u32 pfn) { vmi_set_page_type(pfn, VMI_PAGE_L1); vmi_ops.allocate_page(pfn, VMI_PAGE_L1, 0, 0, 0); @@ -891,7 +891,7 @@ static inline int __init activate_vmi(void) paravirt_ops.setup_boot_clock = vmi_time_bsp_init; paravirt_ops.setup_secondary_clock = vmi_time_ap_init; #endif - paravirt_ops.get_scheduled_cycles = vmi_get_sched_cycles; + paravirt_ops.sched_clock = vmi_sched_clock; paravirt_ops.get_cpu_khz = vmi_cpu_khz; /* We have true wallclock functions; disable CMOS clock sync */ diff --git a/arch/i386/kernel/vmiclock.c b/arch/i386/kernel/vmiclock.c index 26a37f8a876..f9b845f4e69 100644 --- a/arch/i386/kernel/vmiclock.c +++ b/arch/i386/kernel/vmiclock.c @@ -64,10 +64,10 @@ int vmi_set_wallclock(unsigned long now) return 0; } -/* paravirt_ops.get_scheduled_cycles = vmi_get_sched_cycles */ -unsigned long long vmi_get_sched_cycles(void) +/* paravirt_ops.sched_clock = vmi_sched_clock */ +unsigned long long vmi_sched_clock(void) { - return vmi_timer_ops.get_cycle_counter(VMI_CYCLES_AVAILABLE); + return cycles_2_ns(vmi_timer_ops.get_cycle_counter(VMI_CYCLES_AVAILABLE)); } /* paravirt_ops.get_cpu_khz = vmi_cpu_khz */ diff --git a/arch/i386/kernel/vmlinux.lds.S b/arch/i386/kernel/vmlinux.lds.S index aa87b06c7c8..7d72cce0052 100644 --- a/arch/i386/kernel/vmlinux.lds.S +++ b/arch/i386/kernel/vmlinux.lds.S @@ -60,7 +60,9 @@ SECTIONS __stop___ex_table = .; } - BUG_TABLE + NOTES :text :note + + BUG_TABLE :text . = ALIGN(4); .tracedata : AT(ADDR(.tracedata) - LOAD_OFFSET) { @@ -88,6 +90,7 @@ SECTIONS . = ALIGN(4096); .data.page_aligned : AT(ADDR(.data.page_aligned) - LOAD_OFFSET) { + *(.data.page_aligned) *(.data.idt) } @@ -180,6 +183,7 @@ SECTIONS .data.percpu : AT(ADDR(.data.percpu) - LOAD_OFFSET) { __per_cpu_start = .; *(.data.percpu) + *(.data.percpu.shared_aligned) __per_cpu_end = .; } . = ALIGN(4096); @@ -206,6 +210,4 @@ SECTIONS STABS_DEBUG DWARF_DEBUG - - NOTES } diff --git a/arch/i386/kernel/vsyscall-note.S b/arch/i386/kernel/vsyscall-note.S index d4b5be4f3d5..271f16a8ca0 100644 --- a/arch/i386/kernel/vsyscall-note.S +++ b/arch/i386/kernel/vsyscall-note.S @@ -3,23 +3,40 @@ * Here we can supply some information useful to userland. */ -#include <linux/uts.h> #include <linux/version.h> +#include <linux/elfnote.h> -#define ASM_ELF_NOTE_BEGIN(name, flags, vendor, type) \ - .section name, flags; \ - .balign 4; \ - .long 1f - 0f; /* name length */ \ - .long 3f - 2f; /* data length */ \ - .long type; /* note type */ \ -0: .asciz vendor; /* vendor name */ \ -1: .balign 4; \ -2: +/* Ideally this would use UTS_NAME, but using a quoted string here + doesn't work. Remember to change this when changing the + kernel's name. */ +ELFNOTE_START(Linux, 0, "a") + .long LINUX_VERSION_CODE +ELFNOTE_END -#define ASM_ELF_NOTE_END \ -3: .balign 4; /* pad out section */ \ - .previous +#ifdef CONFIG_XEN - ASM_ELF_NOTE_BEGIN(".note.kernel-version", "a", UTS_SYSNAME, 0) - .long LINUX_VERSION_CODE - ASM_ELF_NOTE_END +/* + * Add a special note telling glibc's dynamic linker a fake hardware + * flavor that it will use to choose the search path for libraries in the + * same way it uses real hardware capabilities like "mmx". + * We supply "nosegneg" as the fake capability, to indicate that we + * do not like negative offsets in instructions using segment overrides, + * since we implement those inefficiently. This makes it possible to + * install libraries optimized to avoid those access patterns in someplace + * like /lib/i686/tls/nosegneg. Note that an /etc/ld.so.conf.d/file + * corresponding to the bits here is needed to make ldconfig work right. + * It should contain: + * hwcap 1 nosegneg + * to match the mapping of bit to name that we give here. + */ + +/* Bit used for the pseudo-hwcap for non-negative segments. We use + bit 1 to avoid bugs in some versions of glibc when bit 0 is + used; the choice is otherwise arbitrary. */ +#define VDSO_NOTE_NONEGSEG_BIT 1 + +ELFNOTE_START(GNU, 2, "a") + .long 1, 1<<VDSO_NOTE_NONEGSEG_BIT /* ncaps, mask */ + .byte VDSO_NOTE_NONEGSEG_BIT; .asciz "nosegneg" /* bit, name */ +ELFNOTE_END +#endif diff --git a/arch/i386/mach-voyager/voyager_thread.c b/arch/i386/mach-voyager/voyager_thread.c index b4b24e0e45e..f9d59533815 100644 --- a/arch/i386/mach-voyager/voyager_thread.c +++ b/arch/i386/mach-voyager/voyager_thread.c @@ -52,7 +52,7 @@ execute(const char *string) NULL, }; - if ((ret = call_usermodehelper(argv[0], argv, envp, 1)) != 0) { + if ((ret = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC)) != 0) { printk(KERN_ERR "Voyager failed to run \"%s\": %i\n", string, ret); } diff --git a/arch/i386/mm/fault.c b/arch/i386/mm/fault.c index 1ecb3e43b52..e92a1012493 100644 --- a/arch/i386/mm/fault.c +++ b/arch/i386/mm/fault.c @@ -303,6 +303,7 @@ fastcall void __kprobes do_page_fault(struct pt_regs *regs, struct vm_area_struct * vma; unsigned long address; int write, si_code; + int fault; /* get the address */ address = read_cr2(); @@ -422,20 +423,18 @@ good_area: * make sure we exit gracefully rather than endlessly redo * the fault. */ - switch (handle_mm_fault(mm, vma, address, write)) { - case VM_FAULT_MINOR: - tsk->min_flt++; - break; - case VM_FAULT_MAJOR: - tsk->maj_flt++; - break; - case VM_FAULT_SIGBUS: - goto do_sigbus; - case VM_FAULT_OOM: + fault = handle_mm_fault(mm, vma, address, write); + if (unlikely(fault & VM_FAULT_ERROR)) { + if (fault & VM_FAULT_OOM) goto out_of_memory; - default: - BUG(); + else if (fault & VM_FAULT_SIGBUS) + goto do_sigbus; + BUG(); } + if (fault & VM_FAULT_MAJOR) + tsk->maj_flt++; + else + tsk->min_flt++; /* * Did it hit the DOS screen memory VA from vm86 mode? diff --git a/arch/i386/mm/init.c b/arch/i386/mm/init.c index 7135946d366..6a68b1ae061 100644 --- a/arch/i386/mm/init.c +++ b/arch/i386/mm/init.c @@ -87,7 +87,7 @@ static pte_t * __init one_page_table_init(pmd_t *pmd) if (!(pmd_val(*pmd) & _PAGE_PRESENT)) { pte_t *page_table = (pte_t *) alloc_bootmem_low_pages(PAGE_SIZE); - paravirt_alloc_pt(__pa(page_table) >> PAGE_SHIFT); + paravirt_alloc_pt(&init_mm, __pa(page_table) >> PAGE_SHIFT); set_pmd(pmd, __pmd(__pa(page_table) | _PAGE_TABLE)); BUG_ON(page_table != pte_offset_kernel(pmd, 0)); } @@ -473,6 +473,7 @@ void zap_low_mappings (void) static int disable_nx __initdata = 0; u64 __supported_pte_mask __read_mostly = ~_PAGE_NX; +EXPORT_SYMBOL_GPL(__supported_pte_mask); /* * noexec = on|off diff --git a/arch/i386/mm/pageattr.c b/arch/i386/mm/pageattr.c index 2eb14a73be9..37992ffb163 100644 --- a/arch/i386/mm/pageattr.c +++ b/arch/i386/mm/pageattr.c @@ -60,7 +60,7 @@ static struct page *split_large_page(unsigned long address, pgprot_t prot, address = __pa(address); addr = address & LARGE_PAGE_MASK; pbase = (pte_t *)page_address(base); - paravirt_alloc_pt(page_to_pfn(base)); + paravirt_alloc_pt(&init_mm, page_to_pfn(base)); for (i = 0; i < PTRS_PER_PTE; i++, addr += PAGE_SIZE) { set_pte(&pbase[i], pfn_pte(addr >> PAGE_SHIFT, addr == address ? prot : ref_prot)); diff --git a/arch/i386/video/Makefile b/arch/i386/video/Makefile new file mode 100644 index 00000000000..2c447c94adc --- /dev/null +++ b/arch/i386/video/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_FB) += fbdev.o diff --git a/arch/i386/video/fbdev.c b/arch/i386/video/fbdev.c new file mode 100644 index 00000000000..48fb38d7d2c --- /dev/null +++ b/arch/i386/video/fbdev.c @@ -0,0 +1,32 @@ +/* + * arch/i386/video/fbdev.c - i386 Framebuffer + * + * Copyright (C) 2007 Antonino Daplas <adaplas@gmail.com> + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + * + */ +#include <linux/fb.h> +#include <linux/pci.h> + +int fb_is_primary_device(struct fb_info *info) +{ + struct device *device = info->device; + struct pci_dev *pci_dev = NULL; + struct resource *res = NULL; + int retval = 0; + + if (device) + pci_dev = to_pci_dev(device); + + if (pci_dev) + res = &pci_dev->resource[PCI_ROM_RESOURCE]; + + if (res && res->flags & IORESOURCE_ROM_SHADOW) + retval = 1; + + return retval; +} +EXPORT_SYMBOL(fb_is_primary_device); diff --git a/arch/i386/xen/Kconfig b/arch/i386/xen/Kconfig new file mode 100644 index 00000000000..9df99e1885a --- /dev/null +++ b/arch/i386/xen/Kconfig @@ -0,0 +1,11 @@ +# +# This Kconfig describes xen options +# + +config XEN + bool "Enable support for Xen hypervisor" + depends on PARAVIRT && X86_CMPXCHG && X86_TSC && !NEED_MULTIPLE_NODES + help + This is the Linux Xen port. Enabling this will allow the + kernel to boot in a paravirtualized environment under the + Xen hypervisor. diff --git a/arch/i386/xen/Makefile b/arch/i386/xen/Makefile new file mode 100644 index 00000000000..343df246bd3 --- /dev/null +++ b/arch/i386/xen/Makefile @@ -0,0 +1,4 @@ +obj-y := enlighten.o setup.o features.o multicalls.o mmu.o \ + events.o time.o manage.o xen-asm.o + +obj-$(CONFIG_SMP) += smp.o diff --git a/arch/i386/xen/enlighten.c b/arch/i386/xen/enlighten.c new file mode 100644 index 00000000000..9a8c1181c00 --- /dev/null +++ b/arch/i386/xen/enlighten.c @@ -0,0 +1,1144 @@ +/* + * Core of Xen paravirt_ops implementation. + * + * This file contains the xen_paravirt_ops structure itself, and the + * implementations for: + * - privileged instructions + * - interrupt flags + * - segment operations + * - booting and setup + * + * Jeremy Fitzhardinge <jeremy@xensource.com>, XenSource Inc, 2007 + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/smp.h> +#include <linux/preempt.h> +#include <linux/hardirq.h> +#include <linux/percpu.h> +#include <linux/delay.h> +#include <linux/start_kernel.h> +#include <linux/sched.h> +#include <linux/bootmem.h> +#include <linux/module.h> +#include <linux/mm.h> +#include <linux/page-flags.h> +#include <linux/highmem.h> +#include <linux/smp.h> + +#include <xen/interface/xen.h> +#include <xen/interface/physdev.h> +#include <xen/interface/vcpu.h> +#include <xen/interface/sched.h> +#include <xen/features.h> +#include <xen/page.h> + +#include <asm/paravirt.h> +#include <asm/page.h> +#include <asm/xen/hypercall.h> +#include <asm/xen/hypervisor.h> +#include <asm/fixmap.h> +#include <asm/processor.h> +#include <asm/setup.h> +#include <asm/desc.h> +#include <asm/pgtable.h> +#include <asm/tlbflush.h> +#include <asm/reboot.h> + +#include "xen-ops.h" +#include "mmu.h" +#include "multicalls.h" + +EXPORT_SYMBOL_GPL(hypercall_page); + +DEFINE_PER_CPU(enum paravirt_lazy_mode, xen_lazy_mode); + +DEFINE_PER_CPU(struct vcpu_info *, xen_vcpu); +DEFINE_PER_CPU(struct vcpu_info, xen_vcpu_info); +DEFINE_PER_CPU(unsigned long, xen_cr3); + +struct start_info *xen_start_info; +EXPORT_SYMBOL_GPL(xen_start_info); + +static /* __initdata */ struct shared_info dummy_shared_info; + +/* + * Point at some empty memory to start with. We map the real shared_info + * page as soon as fixmap is up and running. + */ +struct shared_info *HYPERVISOR_shared_info = (void *)&dummy_shared_info; + +/* + * Flag to determine whether vcpu info placement is available on all + * VCPUs. We assume it is to start with, and then set it to zero on + * the first failure. This is because it can succeed on some VCPUs + * and not others, since it can involve hypervisor memory allocation, + * or because the guest failed to guarantee all the appropriate + * constraints on all VCPUs (ie buffer can't cross a page boundary). + * + * Note that any particular CPU may be using a placed vcpu structure, + * but we can only optimise if the all are. + * + * 0: not available, 1: available + */ +static int have_vcpu_info_placement = 1; + +static void __init xen_vcpu_setup(int cpu) +{ + struct vcpu_register_vcpu_info info; + int err; + struct vcpu_info *vcpup; + + per_cpu(xen_vcpu, cpu) = &HYPERVISOR_shared_info->vcpu_info[cpu]; + + if (!have_vcpu_info_placement) + return; /* already tested, not available */ + + vcpup = &per_cpu(xen_vcpu_info, cpu); + + info.mfn = virt_to_mfn(vcpup); + info.offset = offset_in_page(vcpup); + + printk(KERN_DEBUG "trying to map vcpu_info %d at %p, mfn %x, offset %d\n", + cpu, vcpup, info.mfn, info.offset); + + /* Check to see if the hypervisor will put the vcpu_info + structure where we want it, which allows direct access via + a percpu-variable. */ + err = HYPERVISOR_vcpu_op(VCPUOP_register_vcpu_info, cpu, &info); + + if (err) { + printk(KERN_DEBUG "register_vcpu_info failed: err=%d\n", err); + have_vcpu_info_placement = 0; + } else { + /* This cpu is using the registered vcpu info, even if + later ones fail to. */ + per_cpu(xen_vcpu, cpu) = vcpup; + + printk(KERN_DEBUG "cpu %d using vcpu_info at %p\n", + cpu, vcpup); + } +} + +static void __init xen_banner(void) +{ + printk(KERN_INFO "Booting paravirtualized kernel on %s\n", + paravirt_ops.name); + printk(KERN_INFO "Hypervisor signature: %s\n", xen_start_info->magic); +} + +static void xen_cpuid(unsigned int *eax, unsigned int *ebx, + unsigned int *ecx, unsigned int *edx) +{ + unsigned maskedx = ~0; + + /* + * Mask out inconvenient features, to try and disable as many + * unsupported kernel subsystems as possible. + */ + if (*eax == 1) + maskedx = ~((1 << X86_FEATURE_APIC) | /* disable APIC */ + (1 << X86_FEATURE_ACPI) | /* disable ACPI */ + (1 << X86_FEATURE_ACC)); /* thermal monitoring */ + + asm(XEN_EMULATE_PREFIX "cpuid" + : "=a" (*eax), + "=b" (*ebx), + "=c" (*ecx), + "=d" (*edx) + : "0" (*eax), "2" (*ecx)); + *edx &= maskedx; +} + +static void xen_set_debugreg(int reg, unsigned long val) +{ + HYPERVISOR_set_debugreg(reg, val); +} + +static unsigned long xen_get_debugreg(int reg) +{ + return HYPERVISOR_get_debugreg(reg); +} + +static unsigned long xen_save_fl(void) +{ + struct vcpu_info *vcpu; + unsigned long flags; + + vcpu = x86_read_percpu(xen_vcpu); + + /* flag has opposite sense of mask */ + flags = !vcpu->evtchn_upcall_mask; + + /* convert to IF type flag + -0 -> 0x00000000 + -1 -> 0xffffffff + */ + return (-flags) & X86_EFLAGS_IF; +} + +static void xen_restore_fl(unsigned long flags) +{ + struct vcpu_info *vcpu; + + /* convert from IF type flag */ + flags = !(flags & X86_EFLAGS_IF); + + /* There's a one instruction preempt window here. We need to + make sure we're don't switch CPUs between getting the vcpu + pointer and updating the mask. */ + preempt_disable(); + vcpu = x86_read_percpu(xen_vcpu); + vcpu->evtchn_upcall_mask = flags; + preempt_enable_no_resched(); + + /* Doesn't matter if we get preempted here, because any + pending event will get dealt with anyway. */ + + if (flags == 0) { + preempt_check_resched(); + barrier(); /* unmask then check (avoid races) */ + if (unlikely(vcpu->evtchn_upcall_pending)) + force_evtchn_callback(); + } +} + +static void xen_irq_disable(void) +{ + /* There's a one instruction preempt window here. We need to + make sure we're don't switch CPUs between getting the vcpu + pointer and updating the mask. */ + preempt_disable(); + x86_read_percpu(xen_vcpu)->evtchn_upcall_mask = 1; + preempt_enable_no_resched(); +} + +static void xen_irq_enable(void) +{ + struct vcpu_info *vcpu; + + /* There's a one instruction preempt window here. We need to + make sure we're don't switch CPUs between getting the vcpu + pointer and updating the mask. */ + preempt_disable(); + vcpu = x86_read_percpu(xen_vcpu); + vcpu->evtchn_upcall_mask = 0; + preempt_enable_no_resched(); + + /* Doesn't matter if we get preempted here, because any + pending event will get dealt with anyway. */ + + barrier(); /* unmask then check (avoid races) */ + if (unlikely(vcpu->evtchn_upcall_pending)) + force_evtchn_callback(); +} + +static void xen_safe_halt(void) +{ + /* Blocking includes an implicit local_irq_enable(). */ + if (HYPERVISOR_sched_op(SCHEDOP_block, 0) != 0) + BUG(); +} + +static void xen_halt(void) +{ + if (irqs_disabled()) + HYPERVISOR_vcpu_op(VCPUOP_down, smp_processor_id(), NULL); + else + xen_safe_halt(); +} + +static void xen_set_lazy_mode(enum paravirt_lazy_mode mode) +{ + BUG_ON(preemptible()); + + switch (mode) { + case PARAVIRT_LAZY_NONE: + BUG_ON(x86_read_percpu(xen_lazy_mode) == PARAVIRT_LAZY_NONE); + break; + + case PARAVIRT_LAZY_MMU: + case PARAVIRT_LAZY_CPU: + BUG_ON(x86_read_percpu(xen_lazy_mode) != PARAVIRT_LAZY_NONE); + break; + + case PARAVIRT_LAZY_FLUSH: + /* flush if necessary, but don't change state */ + if (x86_read_percpu(xen_lazy_mode) != PARAVIRT_LAZY_NONE) + xen_mc_flush(); + return; + } + + xen_mc_flush(); + x86_write_percpu(xen_lazy_mode, mode); +} + +static unsigned long xen_store_tr(void) +{ + return 0; +} + +static void xen_set_ldt(const void *addr, unsigned entries) +{ + unsigned long linear_addr = (unsigned long)addr; + struct mmuext_op *op; + struct multicall_space mcs = xen_mc_entry(sizeof(*op)); + + op = mcs.args; + op->cmd = MMUEXT_SET_LDT; + if (linear_addr) { + /* ldt my be vmalloced, use arbitrary_virt_to_machine */ + xmaddr_t maddr; + maddr = arbitrary_virt_to_machine((unsigned long)addr); + linear_addr = (unsigned long)maddr.maddr; + } + op->arg1.linear_addr = linear_addr; + op->arg2.nr_ents = entries; + + MULTI_mmuext_op(mcs.mc, op, 1, NULL, DOMID_SELF); + + xen_mc_issue(PARAVIRT_LAZY_CPU); +} + +static void xen_load_gdt(const struct Xgt_desc_struct *dtr) +{ + unsigned long *frames; + unsigned long va = dtr->address; + unsigned int size = dtr->size + 1; + unsigned pages = (size + PAGE_SIZE - 1) / PAGE_SIZE; + int f; + struct multicall_space mcs; + + /* A GDT can be up to 64k in size, which corresponds to 8192 + 8-byte entries, or 16 4k pages.. */ + + BUG_ON(size > 65536); + BUG_ON(va & ~PAGE_MASK); + + mcs = xen_mc_entry(sizeof(*frames) * pages); + frames = mcs.args; + + for (f = 0; va < dtr->address + size; va += PAGE_SIZE, f++) { + frames[f] = virt_to_mfn(va); + make_lowmem_page_readonly((void *)va); + } + + MULTI_set_gdt(mcs.mc, frames, size / sizeof(struct desc_struct)); + + xen_mc_issue(PARAVIRT_LAZY_CPU); +} + +static void load_TLS_descriptor(struct thread_struct *t, + unsigned int cpu, unsigned int i) +{ + struct desc_struct *gdt = get_cpu_gdt_table(cpu); + xmaddr_t maddr = virt_to_machine(&gdt[GDT_ENTRY_TLS_MIN+i]); + struct multicall_space mc = __xen_mc_entry(0); + + MULTI_update_descriptor(mc.mc, maddr.maddr, t->tls_array[i]); +} + +static void xen_load_tls(struct thread_struct *t, unsigned int cpu) +{ + xen_mc_batch(); + + load_TLS_descriptor(t, cpu, 0); + load_TLS_descriptor(t, cpu, 1); + load_TLS_descriptor(t, cpu, 2); + + xen_mc_issue(PARAVIRT_LAZY_CPU); + + /* + * XXX sleazy hack: If we're being called in a lazy-cpu zone, + * it means we're in a context switch, and %gs has just been + * saved. This means we can zero it out to prevent faults on + * exit from the hypervisor if the next process has no %gs. + * Either way, it has been saved, and the new value will get + * loaded properly. This will go away as soon as Xen has been + * modified to not save/restore %gs for normal hypercalls. + */ + if (xen_get_lazy_mode() == PARAVIRT_LAZY_CPU) + loadsegment(gs, 0); +} + +static void xen_write_ldt_entry(struct desc_struct *dt, int entrynum, + u32 low, u32 high) +{ + unsigned long lp = (unsigned long)&dt[entrynum]; + xmaddr_t mach_lp = virt_to_machine(lp); + u64 entry = (u64)high << 32 | low; + + preempt_disable(); + + xen_mc_flush(); + if (HYPERVISOR_update_descriptor(mach_lp.maddr, entry)) + BUG(); + + preempt_enable(); +} + +static int cvt_gate_to_trap(int vector, u32 low, u32 high, + struct trap_info *info) +{ + u8 type, dpl; + + type = (high >> 8) & 0x1f; + dpl = (high >> 13) & 3; + + if (type != 0xf && type != 0xe) + return 0; + + info->vector = vector; + info->address = (high & 0xffff0000) | (low & 0x0000ffff); + info->cs = low >> 16; + info->flags = dpl; + /* interrupt gates clear IF */ + if (type == 0xe) + info->flags |= 4; + + return 1; +} + +/* Locations of each CPU's IDT */ +static DEFINE_PER_CPU(struct Xgt_desc_struct, idt_desc); + +/* Set an IDT entry. If the entry is part of the current IDT, then + also update Xen. */ +static void xen_write_idt_entry(struct desc_struct *dt, int entrynum, + u32 low, u32 high) +{ + unsigned long p = (unsigned long)&dt[entrynum]; + unsigned long start, end; + + preempt_disable(); + + start = __get_cpu_var(idt_desc).address; + end = start + __get_cpu_var(idt_desc).size + 1; + + xen_mc_flush(); + + write_dt_entry(dt, entrynum, low, high); + + if (p >= start && (p + 8) <= end) { + struct trap_info info[2]; + + info[1].address = 0; + + if (cvt_gate_to_trap(entrynum, low, high, &info[0])) + if (HYPERVISOR_set_trap_table(info)) + BUG(); + } + + preempt_enable(); +} + +static void xen_convert_trap_info(const struct Xgt_desc_struct *desc, + struct trap_info *traps) +{ + unsigned in, out, count; + + count = (desc->size+1) / 8; + BUG_ON(count > 256); + + for (in = out = 0; in < count; in++) { + const u32 *entry = (u32 *)(desc->address + in * 8); + + if (cvt_gate_to_trap(in, entry[0], entry[1], &traps[out])) + out++; + } + traps[out].address = 0; +} + +void xen_copy_trap_info(struct trap_info *traps) +{ + const struct Xgt_desc_struct *desc = &__get_cpu_var(idt_desc); + + xen_convert_trap_info(desc, traps); +} + +/* Load a new IDT into Xen. In principle this can be per-CPU, so we + hold a spinlock to protect the static traps[] array (static because + it avoids allocation, and saves stack space). */ +static void xen_load_idt(const struct Xgt_desc_struct *desc) +{ + static DEFINE_SPINLOCK(lock); + static struct trap_info traps[257]; + + spin_lock(&lock); + + __get_cpu_var(idt_desc) = *desc; + + xen_convert_trap_info(desc, traps); + + xen_mc_flush(); + if (HYPERVISOR_set_trap_table(traps)) + BUG(); + + spin_unlock(&lock); +} + +/* Write a GDT descriptor entry. Ignore LDT descriptors, since + they're handled differently. */ +static void xen_write_gdt_entry(struct desc_struct *dt, int entry, + u32 low, u32 high) +{ + preempt_disable(); + + switch ((high >> 8) & 0xff) { + case DESCTYPE_LDT: + case DESCTYPE_TSS: + /* ignore */ + break; + + default: { + xmaddr_t maddr = virt_to_machine(&dt[entry]); + u64 desc = (u64)high << 32 | low; + + xen_mc_flush(); + if (HYPERVISOR_update_descriptor(maddr.maddr, desc)) + BUG(); + } + + } + + preempt_enable(); +} + +static void xen_load_esp0(struct tss_struct *tss, + struct thread_struct *thread) +{ + struct multicall_space mcs = xen_mc_entry(0); + MULTI_stack_switch(mcs.mc, __KERNEL_DS, thread->esp0); + xen_mc_issue(PARAVIRT_LAZY_CPU); +} + +static void xen_set_iopl_mask(unsigned mask) +{ + struct physdev_set_iopl set_iopl; + + /* Force the change at ring 0. */ + set_iopl.iopl = (mask == 0) ? 1 : (mask >> 12) & 3; + HYPERVISOR_physdev_op(PHYSDEVOP_set_iopl, &set_iopl); +} + +static void xen_io_delay(void) +{ +} + +#ifdef CONFIG_X86_LOCAL_APIC +static unsigned long xen_apic_read(unsigned long reg) +{ + return 0; +} + +static void xen_apic_write(unsigned long reg, unsigned long val) +{ + /* Warn to see if there's any stray references */ + WARN_ON(1); +} +#endif + +static void xen_flush_tlb(void) +{ + struct mmuext_op *op; + struct multicall_space mcs = xen_mc_entry(sizeof(*op)); + + op = mcs.args; + op->cmd = MMUEXT_TLB_FLUSH_LOCAL; + MULTI_mmuext_op(mcs.mc, op, 1, NULL, DOMID_SELF); + + xen_mc_issue(PARAVIRT_LAZY_MMU); +} + +static void xen_flush_tlb_single(unsigned long addr) +{ + struct mmuext_op *op; + struct multicall_space mcs = xen_mc_entry(sizeof(*op)); + + op = mcs.args; + op->cmd = MMUEXT_INVLPG_LOCAL; + op->arg1.linear_addr = addr & PAGE_MASK; + MULTI_mmuext_op(mcs.mc, op, 1, NULL, DOMID_SELF); + + xen_mc_issue(PARAVIRT_LAZY_MMU); +} + +static void xen_flush_tlb_others(const cpumask_t *cpus, struct mm_struct *mm, + unsigned long va) +{ + struct { + struct mmuext_op op; + cpumask_t mask; + } *args; + cpumask_t cpumask = *cpus; + struct multicall_space mcs; + + /* + * A couple of (to be removed) sanity checks: + * + * - current CPU must not be in mask + * - mask must exist :) + */ + BUG_ON(cpus_empty(cpumask)); + BUG_ON(cpu_isset(smp_processor_id(), cpumask)); + BUG_ON(!mm); + + /* If a CPU which we ran on has gone down, OK. */ + cpus_and(cpumask, cpumask, cpu_online_map); + if (cpus_empty(cpumask)) + return; + + mcs = xen_mc_entry(sizeof(*args)); + args = mcs.args; + args->mask = cpumask; + args->op.arg2.vcpumask = &args->mask; + + if (va == TLB_FLUSH_ALL) { + args->op.cmd = MMUEXT_TLB_FLUSH_MULTI; + } else { + args->op.cmd = MMUEXT_INVLPG_MULTI; + args->op.arg1.linear_addr = va; + } + + MULTI_mmuext_op(mcs.mc, &args->op, 1, NULL, DOMID_SELF); + + xen_mc_issue(PARAVIRT_LAZY_MMU); +} + +static void xen_write_cr2(unsigned long cr2) +{ + x86_read_percpu(xen_vcpu)->arch.cr2 = cr2; +} + +static unsigned long xen_read_cr2(void) +{ + return x86_read_percpu(xen_vcpu)->arch.cr2; +} + +static unsigned long xen_read_cr2_direct(void) +{ + return x86_read_percpu(xen_vcpu_info.arch.cr2); +} + +static void xen_write_cr4(unsigned long cr4) +{ + /* never allow TSC to be disabled */ + native_write_cr4(cr4 & ~X86_CR4_TSD); +} + +static unsigned long xen_read_cr3(void) +{ + return x86_read_percpu(xen_cr3); +} + +static void xen_write_cr3(unsigned long cr3) +{ + BUG_ON(preemptible()); + + if (cr3 == x86_read_percpu(xen_cr3)) { + /* just a simple tlb flush */ + xen_flush_tlb(); + return; + } + + x86_write_percpu(xen_cr3, cr3); + + + { + struct mmuext_op *op; + struct multicall_space mcs = xen_mc_entry(sizeof(*op)); + unsigned long mfn = pfn_to_mfn(PFN_DOWN(cr3)); + + op = mcs.args; + op->cmd = MMUEXT_NEW_BASEPTR; + op->arg1.mfn = mfn; + + MULTI_mmuext_op(mcs.mc, op, 1, NULL, DOMID_SELF); + + xen_mc_issue(PARAVIRT_LAZY_CPU); + } +} + +/* Early in boot, while setting up the initial pagetable, assume + everything is pinned. */ +static __init void xen_alloc_pt_init(struct mm_struct *mm, u32 pfn) +{ + BUG_ON(mem_map); /* should only be used early */ + make_lowmem_page_readonly(__va(PFN_PHYS(pfn))); +} + +/* This needs to make sure the new pte page is pinned iff its being + attached to a pinned pagetable. */ +static void xen_alloc_pt(struct mm_struct *mm, u32 pfn) +{ + struct page *page = pfn_to_page(pfn); + + if (PagePinned(virt_to_page(mm->pgd))) { + SetPagePinned(page); + + if (!PageHighMem(page)) + make_lowmem_page_readonly(__va(PFN_PHYS(pfn))); + else + /* make sure there are no stray mappings of + this page */ + kmap_flush_unused(); + } +} + +/* This should never happen until we're OK to use struct page */ +static void xen_release_pt(u32 pfn) +{ + struct page *page = pfn_to_page(pfn); + + if (PagePinned(page)) { + if (!PageHighMem(page)) + make_lowmem_page_readwrite(__va(PFN_PHYS(pfn))); + } +} + +#ifdef CONFIG_HIGHPTE +static void *xen_kmap_atomic_pte(struct page *page, enum km_type type) +{ + pgprot_t prot = PAGE_KERNEL; + + if (PagePinned(page)) + prot = PAGE_KERNEL_RO; + + if (0 && PageHighMem(page)) + printk("mapping highpte %lx type %d prot %s\n", + page_to_pfn(page), type, + (unsigned long)pgprot_val(prot) & _PAGE_RW ? "WRITE" : "READ"); + + return kmap_atomic_prot(page, type, prot); +} +#endif + +static __init pte_t mask_rw_pte(pte_t *ptep, pte_t pte) +{ + /* If there's an existing pte, then don't allow _PAGE_RW to be set */ + if (pte_val_ma(*ptep) & _PAGE_PRESENT) + pte = __pte_ma(((pte_val_ma(*ptep) & _PAGE_RW) | ~_PAGE_RW) & + pte_val_ma(pte)); + + return pte; +} + +/* Init-time set_pte while constructing initial pagetables, which + doesn't allow RO pagetable pages to be remapped RW */ +static __init void xen_set_pte_init(pte_t *ptep, pte_t pte) +{ + pte = mask_rw_pte(ptep, pte); + + xen_set_pte(ptep, pte); +} + +static __init void xen_pagetable_setup_start(pgd_t *base) +{ + pgd_t *xen_pgd = (pgd_t *)xen_start_info->pt_base; + + /* special set_pte for pagetable initialization */ + paravirt_ops.set_pte = xen_set_pte_init; + + init_mm.pgd = base; + /* + * copy top-level of Xen-supplied pagetable into place. For + * !PAE we can use this as-is, but for PAE it is a stand-in + * while we copy the pmd pages. + */ + memcpy(base, xen_pgd, PTRS_PER_PGD * sizeof(pgd_t)); + + if (PTRS_PER_PMD > 1) { + int i; + /* + * For PAE, need to allocate new pmds, rather than + * share Xen's, since Xen doesn't like pmd's being + * shared between address spaces. + */ + for (i = 0; i < PTRS_PER_PGD; i++) { + if (pgd_val_ma(xen_pgd[i]) & _PAGE_PRESENT) { + pmd_t *pmd = (pmd_t *)alloc_bootmem_low_pages(PAGE_SIZE); + + memcpy(pmd, (void *)pgd_page_vaddr(xen_pgd[i]), + PAGE_SIZE); + + make_lowmem_page_readonly(pmd); + + set_pgd(&base[i], __pgd(1 + __pa(pmd))); + } else + pgd_clear(&base[i]); + } + } + + /* make sure zero_page is mapped RO so we can use it in pagetables */ + make_lowmem_page_readonly(empty_zero_page); + make_lowmem_page_readonly(base); + /* + * Switch to new pagetable. This is done before + * pagetable_init has done anything so that the new pages + * added to the table can be prepared properly for Xen. + */ + xen_write_cr3(__pa(base)); +} + +static __init void xen_pagetable_setup_done(pgd_t *base) +{ + /* This will work as long as patching hasn't happened yet + (which it hasn't) */ + paravirt_ops.alloc_pt = xen_alloc_pt; + paravirt_ops.set_pte = xen_set_pte; + + if (!xen_feature(XENFEAT_auto_translated_physmap)) { + /* + * Create a mapping for the shared info page. + * Should be set_fixmap(), but shared_info is a machine + * address with no corresponding pseudo-phys address. + */ + set_pte_mfn(fix_to_virt(FIX_PARAVIRT_BOOTMAP), + PFN_DOWN(xen_start_info->shared_info), + PAGE_KERNEL); + + HYPERVISOR_shared_info = + (struct shared_info *)fix_to_virt(FIX_PARAVIRT_BOOTMAP); + + } else + HYPERVISOR_shared_info = + (struct shared_info *)__va(xen_start_info->shared_info); + + /* Actually pin the pagetable down, but we can't set PG_pinned + yet because the page structures don't exist yet. */ + { + struct mmuext_op op; +#ifdef CONFIG_X86_PAE + op.cmd = MMUEXT_PIN_L3_TABLE; +#else + op.cmd = MMUEXT_PIN_L3_TABLE; +#endif + op.arg1.mfn = pfn_to_mfn(PFN_DOWN(__pa(base))); + if (HYPERVISOR_mmuext_op(&op, 1, NULL, DOMID_SELF)) + BUG(); + } +} + +/* This is called once we have the cpu_possible_map */ +void __init xen_setup_vcpu_info_placement(void) +{ + int cpu; + + for_each_possible_cpu(cpu) + xen_vcpu_setup(cpu); + + /* xen_vcpu_setup managed to place the vcpu_info within the + percpu area for all cpus, so make use of it */ + if (have_vcpu_info_placement) { + printk(KERN_INFO "Xen: using vcpu_info placement\n"); + + paravirt_ops.save_fl = xen_save_fl_direct; + paravirt_ops.restore_fl = xen_restore_fl_direct; + paravirt_ops.irq_disable = xen_irq_disable_direct; + paravirt_ops.irq_enable = xen_irq_enable_direct; + paravirt_ops.read_cr2 = xen_read_cr2_direct; + paravirt_ops.iret = xen_iret_direct; + } +} + +static unsigned xen_patch(u8 type, u16 clobbers, void *insns, unsigned len) +{ + char *start, *end, *reloc; + unsigned ret; + + start = end = reloc = NULL; + +#define SITE(x) \ + case PARAVIRT_PATCH(x): \ + if (have_vcpu_info_placement) { \ + start = (char *)xen_##x##_direct; \ + end = xen_##x##_direct_end; \ + reloc = xen_##x##_direct_reloc; \ + } \ + goto patch_site + + switch (type) { + SITE(irq_enable); + SITE(irq_disable); + SITE(save_fl); + SITE(restore_fl); +#undef SITE + + patch_site: + if (start == NULL || (end-start) > len) + goto default_patch; + + ret = paravirt_patch_insns(insns, len, start, end); + + /* Note: because reloc is assigned from something that + appears to be an array, gcc assumes it's non-null, + but doesn't know its relationship with start and + end. */ + if (reloc > start && reloc < end) { + int reloc_off = reloc - start; + long *relocp = (long *)(insns + reloc_off); + long delta = start - (char *)insns; + + *relocp += delta; + } + break; + + default_patch: + default: + ret = paravirt_patch_default(type, clobbers, insns, len); + break; + } + + return ret; +} + +static const struct paravirt_ops xen_paravirt_ops __initdata = { + .paravirt_enabled = 1, + .shared_kernel_pmd = 0, + + .name = "Xen", + .banner = xen_banner, + + .patch = xen_patch, + + .memory_setup = xen_memory_setup, + .arch_setup = xen_arch_setup, + .init_IRQ = xen_init_IRQ, + .post_allocator_init = xen_mark_init_mm_pinned, + + .time_init = xen_time_init, + .set_wallclock = xen_set_wallclock, + .get_wallclock = xen_get_wallclock, + .get_cpu_khz = xen_cpu_khz, + .sched_clock = xen_sched_clock, + + .cpuid = xen_cpuid, + + .set_debugreg = xen_set_debugreg, + .get_debugreg = xen_get_debugreg, + + .clts = native_clts, + + .read_cr0 = native_read_cr0, + .write_cr0 = native_write_cr0, + + .read_cr2 = xen_read_cr2, + .write_cr2 = xen_write_cr2, + + .read_cr3 = xen_read_cr3, + .write_cr3 = xen_write_cr3, + + .read_cr4 = native_read_cr4, + .read_cr4_safe = native_read_cr4_safe, + .write_cr4 = xen_write_cr4, + + .save_fl = xen_save_fl, + .restore_fl = xen_restore_fl, + .irq_disable = xen_irq_disable, + .irq_enable = xen_irq_enable, + .safe_halt = xen_safe_halt, + .halt = xen_halt, + .wbinvd = native_wbinvd, + + .read_msr = native_read_msr_safe, + .write_msr = native_write_msr_safe, + .read_tsc = native_read_tsc, + .read_pmc = native_read_pmc, + + .iret = (void *)&hypercall_page[__HYPERVISOR_iret], + .irq_enable_sysexit = NULL, /* never called */ + + .load_tr_desc = paravirt_nop, + .set_ldt = xen_set_ldt, + .load_gdt = xen_load_gdt, + .load_idt = xen_load_idt, + .load_tls = xen_load_tls, + + .store_gdt = native_store_gdt, + .store_idt = native_store_idt, + .store_tr = xen_store_tr, + + .write_ldt_entry = xen_write_ldt_entry, + .write_gdt_entry = xen_write_gdt_entry, + .write_idt_entry = xen_write_idt_entry, + .load_esp0 = xen_load_esp0, + + .set_iopl_mask = xen_set_iopl_mask, + .io_delay = xen_io_delay, + +#ifdef CONFIG_X86_LOCAL_APIC + .apic_write = xen_apic_write, + .apic_write_atomic = xen_apic_write, + .apic_read = xen_apic_read, + .setup_boot_clock = paravirt_nop, + .setup_secondary_clock = paravirt_nop, + .startup_ipi_hook = paravirt_nop, +#endif + + .flush_tlb_user = xen_flush_tlb, + .flush_tlb_kernel = xen_flush_tlb, + .flush_tlb_single = xen_flush_tlb_single, + .flush_tlb_others = xen_flush_tlb_others, + + .pte_update = paravirt_nop, + .pte_update_defer = paravirt_nop, + + .pagetable_setup_start = xen_pagetable_setup_start, + .pagetable_setup_done = xen_pagetable_setup_done, + + .alloc_pt = xen_alloc_pt_init, + .release_pt = xen_release_pt, + .alloc_pd = paravirt_nop, + .alloc_pd_clone = paravirt_nop, + .release_pd = paravirt_nop, + +#ifdef CONFIG_HIGHPTE + .kmap_atomic_pte = xen_kmap_atomic_pte, +#endif + + .set_pte = NULL, /* see xen_pagetable_setup_* */ + .set_pte_at = xen_set_pte_at, + .set_pmd = xen_set_pmd, + + .pte_val = xen_pte_val, + .pgd_val = xen_pgd_val, + + .make_pte = xen_make_pte, + .make_pgd = xen_make_pgd, + +#ifdef CONFIG_X86_PAE + .set_pte_atomic = xen_set_pte_atomic, + .set_pte_present = xen_set_pte_at, + .set_pud = xen_set_pud, + .pte_clear = xen_pte_clear, + .pmd_clear = xen_pmd_clear, + + .make_pmd = xen_make_pmd, + .pmd_val = xen_pmd_val, +#endif /* PAE */ + + .activate_mm = xen_activate_mm, + .dup_mmap = xen_dup_mmap, + .exit_mmap = xen_exit_mmap, + + .set_lazy_mode = xen_set_lazy_mode, +}; + +#ifdef CONFIG_SMP +static const struct smp_ops xen_smp_ops __initdata = { + .smp_prepare_boot_cpu = xen_smp_prepare_boot_cpu, + .smp_prepare_cpus = xen_smp_prepare_cpus, + .cpu_up = xen_cpu_up, + .smp_cpus_done = xen_smp_cpus_done, + + .smp_send_stop = xen_smp_send_stop, + .smp_send_reschedule = xen_smp_send_reschedule, + .smp_call_function_mask = xen_smp_call_function_mask, +}; +#endif /* CONFIG_SMP */ + +static void xen_reboot(int reason) +{ +#ifdef CONFIG_SMP + smp_send_stop(); +#endif + + if (HYPERVISOR_sched_op(SCHEDOP_shutdown, reason)) + BUG(); +} + +static void xen_restart(char *msg) +{ + xen_reboot(SHUTDOWN_reboot); +} + +static void xen_emergency_restart(void) +{ + xen_reboot(SHUTDOWN_reboot); +} + +static void xen_machine_halt(void) +{ + xen_reboot(SHUTDOWN_poweroff); +} + +static void xen_crash_shutdown(struct pt_regs *regs) +{ + xen_reboot(SHUTDOWN_crash); +} + +static const struct machine_ops __initdata xen_machine_ops = { + .restart = xen_restart, + .halt = xen_machine_halt, + .power_off = xen_machine_halt, + .shutdown = xen_machine_halt, + .crash_shutdown = xen_crash_shutdown, + .emergency_restart = xen_emergency_restart, +}; + + +/* First C function to be called on Xen boot */ +asmlinkage void __init xen_start_kernel(void) +{ + pgd_t *pgd; + + if (!xen_start_info) + return; + + BUG_ON(memcmp(xen_start_info->magic, "xen-3.0", 7) != 0); + + /* Install Xen paravirt ops */ + paravirt_ops = xen_paravirt_ops; + machine_ops = xen_machine_ops; + +#ifdef CONFIG_SMP + smp_ops = xen_smp_ops; +#endif + + xen_setup_features(); + + /* Get mfn list */ + if (!xen_feature(XENFEAT_auto_translated_physmap)) + phys_to_machine_mapping = (unsigned long *)xen_start_info->mfn_list; + + pgd = (pgd_t *)xen_start_info->pt_base; + + init_pg_tables_end = __pa(pgd) + xen_start_info->nr_pt_frames*PAGE_SIZE; + + init_mm.pgd = pgd; /* use the Xen pagetables to start */ + + /* keep using Xen gdt for now; no urgent need to change it */ + + x86_write_percpu(xen_cr3, __pa(pgd)); + +#ifdef CONFIG_SMP + /* Don't do the full vcpu_info placement stuff until we have a + possible map. */ + per_cpu(xen_vcpu, 0) = &HYPERVISOR_shared_info->vcpu_info[0]; +#else + /* May as well do it now, since there's no good time to call + it later on UP. */ + xen_setup_vcpu_info_placement(); +#endif + + paravirt_ops.kernel_rpl = 1; + if (xen_feature(XENFEAT_supervisor_mode_kernel)) + paravirt_ops.kernel_rpl = 0; + + /* set the limit of our address space */ + reserve_top_address(-HYPERVISOR_VIRT_START + 2 * PAGE_SIZE); + + /* set up basic CPUID stuff */ + cpu_detect(&new_cpu_data); + new_cpu_data.hard_math = 1; + new_cpu_data.x86_capability[0] = cpuid_edx(1); + + /* Poke various useful things into boot_params */ + LOADER_TYPE = (9 << 4) | 0; + INITRD_START = xen_start_info->mod_start ? __pa(xen_start_info->mod_start) : 0; + INITRD_SIZE = xen_start_info->mod_len; + + /* Start the world */ + start_kernel(); +} diff --git a/arch/i386/xen/events.c b/arch/i386/xen/events.c new file mode 100644 index 00000000000..8904acc20f8 --- /dev/null +++ b/arch/i386/xen/events.c @@ -0,0 +1,590 @@ +/* + * Xen event channels + * + * Xen models interrupts with abstract event channels. Because each + * domain gets 1024 event channels, but NR_IRQ is not that large, we + * must dynamically map irqs<->event channels. The event channels + * interface with the rest of the kernel by defining a xen interrupt + * chip. When an event is recieved, it is mapped to an irq and sent + * through the normal interrupt processing path. + * + * There are four kinds of events which can be mapped to an event + * channel: + * + * 1. Inter-domain notifications. This includes all the virtual + * device events, since they're driven by front-ends in another domain + * (typically dom0). + * 2. VIRQs, typically used for timers. These are per-cpu events. + * 3. IPIs. + * 4. Hardware interrupts. Not supported at present. + * + * Jeremy Fitzhardinge <jeremy@xensource.com>, XenSource Inc, 2007 + */ + +#include <linux/linkage.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/module.h> +#include <linux/string.h> + +#include <asm/ptrace.h> +#include <asm/irq.h> +#include <asm/sync_bitops.h> +#include <asm/xen/hypercall.h> + +#include <xen/events.h> +#include <xen/interface/xen.h> +#include <xen/interface/event_channel.h> + +#include "xen-ops.h" + +/* + * This lock protects updates to the following mapping and reference-count + * arrays. The lock does not need to be acquired to read the mapping tables. + */ +static DEFINE_SPINLOCK(irq_mapping_update_lock); + +/* IRQ <-> VIRQ mapping. */ +static DEFINE_PER_CPU(int, virq_to_irq[NR_VIRQS]) = {[0 ... NR_VIRQS-1] = -1}; + +/* IRQ <-> IPI mapping */ +static DEFINE_PER_CPU(int, ipi_to_irq[XEN_NR_IPIS]) = {[0 ... XEN_NR_IPIS-1] = -1}; + +/* Packed IRQ information: binding type, sub-type index, and event channel. */ +struct packed_irq +{ + unsigned short evtchn; + unsigned char index; + unsigned char type; +}; + +static struct packed_irq irq_info[NR_IRQS]; + +/* Binding types. */ +enum { + IRQT_UNBOUND, + IRQT_PIRQ, + IRQT_VIRQ, + IRQT_IPI, + IRQT_EVTCHN +}; + +/* Convenient shorthand for packed representation of an unbound IRQ. */ +#define IRQ_UNBOUND mk_irq_info(IRQT_UNBOUND, 0, 0) + +static int evtchn_to_irq[NR_EVENT_CHANNELS] = { + [0 ... NR_EVENT_CHANNELS-1] = -1 +}; +static unsigned long cpu_evtchn_mask[NR_CPUS][NR_EVENT_CHANNELS/BITS_PER_LONG]; +static u8 cpu_evtchn[NR_EVENT_CHANNELS]; + +/* Reference counts for bindings to IRQs. */ +static int irq_bindcount[NR_IRQS]; + +/* Xen will never allocate port zero for any purpose. */ +#define VALID_EVTCHN(chn) ((chn) != 0) + +/* + * Force a proper event-channel callback from Xen after clearing the + * callback mask. We do this in a very simple manner, by making a call + * down into Xen. The pending flag will be checked by Xen on return. + */ +void force_evtchn_callback(void) +{ + (void)HYPERVISOR_xen_version(0, NULL); +} +EXPORT_SYMBOL_GPL(force_evtchn_callback); + +static struct irq_chip xen_dynamic_chip; + +/* Constructor for packed IRQ information. */ +static inline struct packed_irq mk_irq_info(u32 type, u32 index, u32 evtchn) +{ + return (struct packed_irq) { evtchn, index, type }; +} + +/* + * Accessors for packed IRQ information. + */ +static inline unsigned int evtchn_from_irq(int irq) +{ + return irq_info[irq].evtchn; +} + +static inline unsigned int index_from_irq(int irq) +{ + return irq_info[irq].index; +} + +static inline unsigned int type_from_irq(int irq) +{ + return irq_info[irq].type; +} + +static inline unsigned long active_evtchns(unsigned int cpu, + struct shared_info *sh, + unsigned int idx) +{ + return (sh->evtchn_pending[idx] & + cpu_evtchn_mask[cpu][idx] & + ~sh->evtchn_mask[idx]); +} + +static void bind_evtchn_to_cpu(unsigned int chn, unsigned int cpu) +{ + int irq = evtchn_to_irq[chn]; + + BUG_ON(irq == -1); +#ifdef CONFIG_SMP + irq_desc[irq].affinity = cpumask_of_cpu(cpu); +#endif + + __clear_bit(chn, cpu_evtchn_mask[cpu_evtchn[chn]]); + __set_bit(chn, cpu_evtchn_mask[cpu]); + + cpu_evtchn[chn] = cpu; +} + +static void init_evtchn_cpu_bindings(void) +{ +#ifdef CONFIG_SMP + int i; + /* By default all event channels notify CPU#0. */ + for (i = 0; i < NR_IRQS; i++) + irq_desc[i].affinity = cpumask_of_cpu(0); +#endif + + memset(cpu_evtchn, 0, sizeof(cpu_evtchn)); + memset(cpu_evtchn_mask[0], ~0, sizeof(cpu_evtchn_mask[0])); +} + +static inline unsigned int cpu_from_evtchn(unsigned int evtchn) +{ + return cpu_evtchn[evtchn]; +} + +static inline void clear_evtchn(int port) +{ + struct shared_info *s = HYPERVISOR_shared_info; + sync_clear_bit(port, &s->evtchn_pending[0]); +} + +static inline void set_evtchn(int port) +{ + struct shared_info *s = HYPERVISOR_shared_info; + sync_set_bit(port, &s->evtchn_pending[0]); +} + + +/** + * notify_remote_via_irq - send event to remote end of event channel via irq + * @irq: irq of event channel to send event to + * + * Unlike notify_remote_via_evtchn(), this is safe to use across + * save/restore. Notifications on a broken connection are silently + * dropped. + */ +void notify_remote_via_irq(int irq) +{ + int evtchn = evtchn_from_irq(irq); + + if (VALID_EVTCHN(evtchn)) + notify_remote_via_evtchn(evtchn); +} +EXPORT_SYMBOL_GPL(notify_remote_via_irq); + +static void mask_evtchn(int port) +{ + struct shared_info *s = HYPERVISOR_shared_info; + sync_set_bit(port, &s->evtchn_mask[0]); +} + +static void unmask_evtchn(int port) +{ + struct shared_info *s = HYPERVISOR_shared_info; + unsigned int cpu = get_cpu(); + + BUG_ON(!irqs_disabled()); + + /* Slow path (hypercall) if this is a non-local port. */ + if (unlikely(cpu != cpu_from_evtchn(port))) { + struct evtchn_unmask unmask = { .port = port }; + (void)HYPERVISOR_event_channel_op(EVTCHNOP_unmask, &unmask); + } else { + struct vcpu_info *vcpu_info = __get_cpu_var(xen_vcpu); + + sync_clear_bit(port, &s->evtchn_mask[0]); + + /* + * The following is basically the equivalent of + * 'hw_resend_irq'. Just like a real IO-APIC we 'lose + * the interrupt edge' if the channel is masked. + */ + if (sync_test_bit(port, &s->evtchn_pending[0]) && + !sync_test_and_set_bit(port / BITS_PER_LONG, + &vcpu_info->evtchn_pending_sel)) + vcpu_info->evtchn_upcall_pending = 1; + } + + put_cpu(); +} + +static int find_unbound_irq(void) +{ + int irq; + + /* Only allocate from dynirq range */ + for (irq = 0; irq < NR_IRQS; irq++) + if (irq_bindcount[irq] == 0) + break; + + if (irq == NR_IRQS) + panic("No available IRQ to bind to: increase NR_IRQS!\n"); + + return irq; +} + +int bind_evtchn_to_irq(unsigned int evtchn) +{ + int irq; + + spin_lock(&irq_mapping_update_lock); + + irq = evtchn_to_irq[evtchn]; + + if (irq == -1) { + irq = find_unbound_irq(); + + dynamic_irq_init(irq); + set_irq_chip_and_handler_name(irq, &xen_dynamic_chip, + handle_level_irq, "event"); + + evtchn_to_irq[evtchn] = irq; + irq_info[irq] = mk_irq_info(IRQT_EVTCHN, 0, evtchn); + } + + irq_bindcount[irq]++; + + spin_unlock(&irq_mapping_update_lock); + + return irq; +} +EXPORT_SYMBOL_GPL(bind_evtchn_to_irq); + +static int bind_ipi_to_irq(unsigned int ipi, unsigned int cpu) +{ + struct evtchn_bind_ipi bind_ipi; + int evtchn, irq; + + spin_lock(&irq_mapping_update_lock); + + irq = per_cpu(ipi_to_irq, cpu)[ipi]; + if (irq == -1) { + irq = find_unbound_irq(); + if (irq < 0) + goto out; + + dynamic_irq_init(irq); + set_irq_chip_and_handler_name(irq, &xen_dynamic_chip, + handle_level_irq, "ipi"); + + bind_ipi.vcpu = cpu; + if (HYPERVISOR_event_channel_op(EVTCHNOP_bind_ipi, + &bind_ipi) != 0) + BUG(); + evtchn = bind_ipi.port; + + evtchn_to_irq[evtchn] = irq; + irq_info[irq] = mk_irq_info(IRQT_IPI, ipi, evtchn); + + per_cpu(ipi_to_irq, cpu)[ipi] = irq; + + bind_evtchn_to_cpu(evtchn, cpu); + } + + irq_bindcount[irq]++; + + out: + spin_unlock(&irq_mapping_update_lock); + return irq; +} + + +static int bind_virq_to_irq(unsigned int virq, unsigned int cpu) +{ + struct evtchn_bind_virq bind_virq; + int evtchn, irq; + + spin_lock(&irq_mapping_update_lock); + + irq = per_cpu(virq_to_irq, cpu)[virq]; + + if (irq == -1) { + bind_virq.virq = virq; + bind_virq.vcpu = cpu; + if (HYPERVISOR_event_channel_op(EVTCHNOP_bind_virq, + &bind_virq) != 0) + BUG(); + evtchn = bind_virq.port; + + irq = find_unbound_irq(); + + dynamic_irq_init(irq); + set_irq_chip_and_handler_name(irq, &xen_dynamic_chip, + handle_level_irq, "virq"); + + evtchn_to_irq[evtchn] = irq; + irq_info[irq] = mk_irq_info(IRQT_VIRQ, virq, evtchn); + + per_cpu(virq_to_irq, cpu)[virq] = irq; + + bind_evtchn_to_cpu(evtchn, cpu); + } + + irq_bindcount[irq]++; + + spin_unlock(&irq_mapping_update_lock); + + return irq; +} + +static void unbind_from_irq(unsigned int irq) +{ + struct evtchn_close close; + int evtchn = evtchn_from_irq(irq); + + spin_lock(&irq_mapping_update_lock); + + if (VALID_EVTCHN(evtchn) && (--irq_bindcount[irq] == 0)) { + close.port = evtchn; + if (HYPERVISOR_event_channel_op(EVTCHNOP_close, &close) != 0) + BUG(); + + switch (type_from_irq(irq)) { + case IRQT_VIRQ: + per_cpu(virq_to_irq, cpu_from_evtchn(evtchn)) + [index_from_irq(irq)] = -1; + break; + default: + break; + } + + /* Closed ports are implicitly re-bound to VCPU0. */ + bind_evtchn_to_cpu(evtchn, 0); + + evtchn_to_irq[evtchn] = -1; + irq_info[irq] = IRQ_UNBOUND; + + dynamic_irq_init(irq); + } + + spin_unlock(&irq_mapping_update_lock); +} + +int bind_evtchn_to_irqhandler(unsigned int evtchn, + irqreturn_t (*handler)(int, void *), + unsigned long irqflags, + const char *devname, void *dev_id) +{ + unsigned int irq; + int retval; + + irq = bind_evtchn_to_irq(evtchn); + retval = request_irq(irq, handler, irqflags, devname, dev_id); + if (retval != 0) { + unbind_from_irq(irq); + return retval; + } + + return irq; +} +EXPORT_SYMBOL_GPL(bind_evtchn_to_irqhandler); + +int bind_virq_to_irqhandler(unsigned int virq, unsigned int cpu, + irqreturn_t (*handler)(int, void *), + unsigned long irqflags, const char *devname, void *dev_id) +{ + unsigned int irq; + int retval; + + irq = bind_virq_to_irq(virq, cpu); + retval = request_irq(irq, handler, irqflags, devname, dev_id); + if (retval != 0) { + unbind_from_irq(irq); + return retval; + } + + return irq; +} +EXPORT_SYMBOL_GPL(bind_virq_to_irqhandler); + +int bind_ipi_to_irqhandler(enum ipi_vector ipi, + unsigned int cpu, + irq_handler_t handler, + unsigned long irqflags, + const char *devname, + void *dev_id) +{ + int irq, retval; + + irq = bind_ipi_to_irq(ipi, cpu); + if (irq < 0) + return irq; + + retval = request_irq(irq, handler, irqflags, devname, dev_id); + if (retval != 0) { + unbind_from_irq(irq); + return retval; + } + + return irq; +} + +void unbind_from_irqhandler(unsigned int irq, void *dev_id) +{ + free_irq(irq, dev_id); + unbind_from_irq(irq); +} +EXPORT_SYMBOL_GPL(unbind_from_irqhandler); + +void xen_send_IPI_one(unsigned int cpu, enum ipi_vector vector) +{ + int irq = per_cpu(ipi_to_irq, cpu)[vector]; + BUG_ON(irq < 0); + notify_remote_via_irq(irq); +} + + +/* + * Search the CPUs pending events bitmasks. For each one found, map + * the event number to an irq, and feed it into do_IRQ() for + * handling. + * + * Xen uses a two-level bitmap to speed searching. The first level is + * a bitset of words which contain pending event bits. The second + * level is a bitset of pending events themselves. + */ +fastcall void xen_evtchn_do_upcall(struct pt_regs *regs) +{ + int cpu = get_cpu(); + struct shared_info *s = HYPERVISOR_shared_info; + struct vcpu_info *vcpu_info = __get_cpu_var(xen_vcpu); + unsigned long pending_words; + + vcpu_info->evtchn_upcall_pending = 0; + + /* NB. No need for a barrier here -- XCHG is a barrier on x86. */ + pending_words = xchg(&vcpu_info->evtchn_pending_sel, 0); + while (pending_words != 0) { + unsigned long pending_bits; + int word_idx = __ffs(pending_words); + pending_words &= ~(1UL << word_idx); + + while ((pending_bits = active_evtchns(cpu, s, word_idx)) != 0) { + int bit_idx = __ffs(pending_bits); + int port = (word_idx * BITS_PER_LONG) + bit_idx; + int irq = evtchn_to_irq[port]; + + if (irq != -1) { + regs->orig_eax = ~irq; + do_IRQ(regs); + } + } + } + + put_cpu(); +} + +/* Rebind an evtchn so that it gets delivered to a specific cpu */ +static void rebind_irq_to_cpu(unsigned irq, unsigned tcpu) +{ + struct evtchn_bind_vcpu bind_vcpu; + int evtchn = evtchn_from_irq(irq); + + if (!VALID_EVTCHN(evtchn)) + return; + + /* Send future instances of this interrupt to other vcpu. */ + bind_vcpu.port = evtchn; + bind_vcpu.vcpu = tcpu; + + /* + * If this fails, it usually just indicates that we're dealing with a + * virq or IPI channel, which don't actually need to be rebound. Ignore + * it, but don't do the xenlinux-level rebind in that case. + */ + if (HYPERVISOR_event_channel_op(EVTCHNOP_bind_vcpu, &bind_vcpu) >= 0) + bind_evtchn_to_cpu(evtchn, tcpu); +} + + +static void set_affinity_irq(unsigned irq, cpumask_t dest) +{ + unsigned tcpu = first_cpu(dest); + rebind_irq_to_cpu(irq, tcpu); +} + +static void enable_dynirq(unsigned int irq) +{ + int evtchn = evtchn_from_irq(irq); + + if (VALID_EVTCHN(evtchn)) + unmask_evtchn(evtchn); +} + +static void disable_dynirq(unsigned int irq) +{ + int evtchn = evtchn_from_irq(irq); + + if (VALID_EVTCHN(evtchn)) + mask_evtchn(evtchn); +} + +static void ack_dynirq(unsigned int irq) +{ + int evtchn = evtchn_from_irq(irq); + + move_native_irq(irq); + + if (VALID_EVTCHN(evtchn)) + clear_evtchn(evtchn); +} + +static int retrigger_dynirq(unsigned int irq) +{ + int evtchn = evtchn_from_irq(irq); + int ret = 0; + + if (VALID_EVTCHN(evtchn)) { + set_evtchn(evtchn); + ret = 1; + } + + return ret; +} + +static struct irq_chip xen_dynamic_chip __read_mostly = { + .name = "xen-dyn", + .mask = disable_dynirq, + .unmask = enable_dynirq, + .ack = ack_dynirq, + .set_affinity = set_affinity_irq, + .retrigger = retrigger_dynirq, +}; + +void __init xen_init_IRQ(void) +{ + int i; + + init_evtchn_cpu_bindings(); + + /* No event channels are 'live' right now. */ + for (i = 0; i < NR_EVENT_CHANNELS; i++) + mask_evtchn(i); + + /* Dynamic IRQ space is currently unbound. Zero the refcnts. */ + for (i = 0; i < NR_IRQS; i++) + irq_bindcount[i] = 0; + + irq_ctx_init(smp_processor_id()); +} diff --git a/arch/i386/xen/features.c b/arch/i386/xen/features.c new file mode 100644 index 00000000000..0707714e40d --- /dev/null +++ b/arch/i386/xen/features.c @@ -0,0 +1,29 @@ +/****************************************************************************** + * features.c + * + * Xen feature flags. + * + * Copyright (c) 2006, Ian Campbell, XenSource Inc. + */ +#include <linux/types.h> +#include <linux/cache.h> +#include <linux/module.h> +#include <asm/xen/hypervisor.h> +#include <xen/features.h> + +u8 xen_features[XENFEAT_NR_SUBMAPS * 32] __read_mostly; +EXPORT_SYMBOL_GPL(xen_features); + +void xen_setup_features(void) +{ + struct xen_feature_info fi; + int i, j; + + for (i = 0; i < XENFEAT_NR_SUBMAPS; i++) { + fi.submap_idx = i; + if (HYPERVISOR_xen_version(XENVER_get_features, &fi) < 0) + break; + for (j = 0; j < 32; j++) + xen_features[i * 32 + j] = !!(fi.submap & 1<<j); + } +} diff --git a/arch/i386/xen/manage.c b/arch/i386/xen/manage.c new file mode 100644 index 00000000000..aa7af9e6abc --- /dev/null +++ b/arch/i386/xen/manage.c @@ -0,0 +1,143 @@ +/* + * Handle extern requests for shutdown, reboot and sysrq + */ +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/reboot.h> +#include <linux/sysrq.h> + +#include <xen/xenbus.h> + +#define SHUTDOWN_INVALID -1 +#define SHUTDOWN_POWEROFF 0 +#define SHUTDOWN_SUSPEND 2 +/* Code 3 is SHUTDOWN_CRASH, which we don't use because the domain can only + * report a crash, not be instructed to crash! + * HALT is the same as POWEROFF, as far as we're concerned. The tools use + * the distinction when we return the reason code to them. + */ +#define SHUTDOWN_HALT 4 + +/* Ignore multiple shutdown requests. */ +static int shutting_down = SHUTDOWN_INVALID; + +static void shutdown_handler(struct xenbus_watch *watch, + const char **vec, unsigned int len) +{ + char *str; + struct xenbus_transaction xbt; + int err; + + if (shutting_down != SHUTDOWN_INVALID) + return; + + again: + err = xenbus_transaction_start(&xbt); + if (err) + return; + + str = (char *)xenbus_read(xbt, "control", "shutdown", NULL); + /* Ignore read errors and empty reads. */ + if (XENBUS_IS_ERR_READ(str)) { + xenbus_transaction_end(xbt, 1); + return; + } + + xenbus_write(xbt, "control", "shutdown", ""); + + err = xenbus_transaction_end(xbt, 0); + if (err == -EAGAIN) { + kfree(str); + goto again; + } + + if (strcmp(str, "poweroff") == 0 || + strcmp(str, "halt") == 0) + orderly_poweroff(false); + else if (strcmp(str, "reboot") == 0) + ctrl_alt_del(); + else { + printk(KERN_INFO "Ignoring shutdown request: %s\n", str); + shutting_down = SHUTDOWN_INVALID; + } + + kfree(str); +} + +static void sysrq_handler(struct xenbus_watch *watch, const char **vec, + unsigned int len) +{ + char sysrq_key = '\0'; + struct xenbus_transaction xbt; + int err; + + again: + err = xenbus_transaction_start(&xbt); + if (err) + return; + if (!xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key)) { + printk(KERN_ERR "Unable to read sysrq code in " + "control/sysrq\n"); + xenbus_transaction_end(xbt, 1); + return; + } + + if (sysrq_key != '\0') + xenbus_printf(xbt, "control", "sysrq", "%c", '\0'); + + err = xenbus_transaction_end(xbt, 0); + if (err == -EAGAIN) + goto again; + + if (sysrq_key != '\0') + handle_sysrq(sysrq_key, NULL); +} + +static struct xenbus_watch shutdown_watch = { + .node = "control/shutdown", + .callback = shutdown_handler +}; + +static struct xenbus_watch sysrq_watch = { + .node = "control/sysrq", + .callback = sysrq_handler +}; + +static int setup_shutdown_watcher(void) +{ + int err; + + err = register_xenbus_watch(&shutdown_watch); + if (err) { + printk(KERN_ERR "Failed to set shutdown watcher\n"); + return err; + } + + err = register_xenbus_watch(&sysrq_watch); + if (err) { + printk(KERN_ERR "Failed to set sysrq watcher\n"); + return err; + } + + return 0; +} + +static int shutdown_event(struct notifier_block *notifier, + unsigned long event, + void *data) +{ + setup_shutdown_watcher(); + return NOTIFY_DONE; +} + +static int __init setup_shutdown_event(void) +{ + static struct notifier_block xenstore_notifier = { + .notifier_call = shutdown_event + }; + register_xenstore_notifier(&xenstore_notifier); + + return 0; +} + +subsys_initcall(setup_shutdown_event); diff --git a/arch/i386/xen/mmu.c b/arch/i386/xen/mmu.c new file mode 100644 index 00000000000..4ae038aa6c2 --- /dev/null +++ b/arch/i386/xen/mmu.c @@ -0,0 +1,564 @@ +/* + * Xen mmu operations + * + * This file contains the various mmu fetch and update operations. + * The most important job they must perform is the mapping between the + * domain's pfn and the overall machine mfns. + * + * Xen allows guests to directly update the pagetable, in a controlled + * fashion. In other words, the guest modifies the same pagetable + * that the CPU actually uses, which eliminates the overhead of having + * a separate shadow pagetable. + * + * In order to allow this, it falls on the guest domain to map its + * notion of a "physical" pfn - which is just a domain-local linear + * address - into a real "machine address" which the CPU's MMU can + * use. + * + * A pgd_t/pmd_t/pte_t will typically contain an mfn, and so can be + * inserted directly into the pagetable. When creating a new + * pte/pmd/pgd, it converts the passed pfn into an mfn. Conversely, + * when reading the content back with __(pgd|pmd|pte)_val, it converts + * the mfn back into a pfn. + * + * The other constraint is that all pages which make up a pagetable + * must be mapped read-only in the guest. This prevents uncontrolled + * guest updates to the pagetable. Xen strictly enforces this, and + * will disallow any pagetable update which will end up mapping a + * pagetable page RW, and will disallow using any writable page as a + * pagetable. + * + * Naively, when loading %cr3 with the base of a new pagetable, Xen + * would need to validate the whole pagetable before going on. + * Naturally, this is quite slow. The solution is to "pin" a + * pagetable, which enforces all the constraints on the pagetable even + * when it is not actively in use. This menas that Xen can be assured + * that it is still valid when you do load it into %cr3, and doesn't + * need to revalidate it. + * + * Jeremy Fitzhardinge <jeremy@xensource.com>, XenSource Inc, 2007 + */ +#include <linux/sched.h> +#include <linux/highmem.h> +#include <linux/bug.h> +#include <linux/sched.h> + +#include <asm/pgtable.h> +#include <asm/tlbflush.h> +#include <asm/mmu_context.h> +#include <asm/paravirt.h> + +#include <asm/xen/hypercall.h> +#include <asm/xen/hypervisor.h> + +#include <xen/page.h> +#include <xen/interface/xen.h> + +#include "multicalls.h" +#include "mmu.h" + +xmaddr_t arbitrary_virt_to_machine(unsigned long address) +{ + pte_t *pte = lookup_address(address); + unsigned offset = address & PAGE_MASK; + + BUG_ON(pte == NULL); + + return XMADDR((pte_mfn(*pte) << PAGE_SHIFT) + offset); +} + +void make_lowmem_page_readonly(void *vaddr) +{ + pte_t *pte, ptev; + unsigned long address = (unsigned long)vaddr; + + pte = lookup_address(address); + BUG_ON(pte == NULL); + + ptev = pte_wrprotect(*pte); + + if (HYPERVISOR_update_va_mapping(address, ptev, 0)) + BUG(); +} + +void make_lowmem_page_readwrite(void *vaddr) +{ + pte_t *pte, ptev; + unsigned long address = (unsigned long)vaddr; + + pte = lookup_address(address); + BUG_ON(pte == NULL); + + ptev = pte_mkwrite(*pte); + + if (HYPERVISOR_update_va_mapping(address, ptev, 0)) + BUG(); +} + + +void xen_set_pmd(pmd_t *ptr, pmd_t val) +{ + struct multicall_space mcs; + struct mmu_update *u; + + preempt_disable(); + + mcs = xen_mc_entry(sizeof(*u)); + u = mcs.args; + u->ptr = virt_to_machine(ptr).maddr; + u->val = pmd_val_ma(val); + MULTI_mmu_update(mcs.mc, u, 1, NULL, DOMID_SELF); + + xen_mc_issue(PARAVIRT_LAZY_MMU); + + preempt_enable(); +} + +/* + * Associate a virtual page frame with a given physical page frame + * and protection flags for that frame. + */ +void set_pte_mfn(unsigned long vaddr, unsigned long mfn, pgprot_t flags) +{ + pgd_t *pgd; + pud_t *pud; + pmd_t *pmd; + pte_t *pte; + + pgd = swapper_pg_dir + pgd_index(vaddr); + if (pgd_none(*pgd)) { + BUG(); + return; + } + pud = pud_offset(pgd, vaddr); + if (pud_none(*pud)) { + BUG(); + return; + } + pmd = pmd_offset(pud, vaddr); + if (pmd_none(*pmd)) { + BUG(); + return; + } + pte = pte_offset_kernel(pmd, vaddr); + /* <mfn,flags> stored as-is, to permit clearing entries */ + xen_set_pte(pte, mfn_pte(mfn, flags)); + + /* + * It's enough to flush this one mapping. + * (PGE mappings get flushed as well) + */ + __flush_tlb_one(vaddr); +} + +void xen_set_pte_at(struct mm_struct *mm, unsigned long addr, + pte_t *ptep, pte_t pteval) +{ + if (mm == current->mm || mm == &init_mm) { + if (xen_get_lazy_mode() == PARAVIRT_LAZY_MMU) { + struct multicall_space mcs; + mcs = xen_mc_entry(0); + + MULTI_update_va_mapping(mcs.mc, addr, pteval, 0); + xen_mc_issue(PARAVIRT_LAZY_MMU); + return; + } else + if (HYPERVISOR_update_va_mapping(addr, pteval, 0) == 0) + return; + } + xen_set_pte(ptep, pteval); +} + +#ifdef CONFIG_X86_PAE +void xen_set_pud(pud_t *ptr, pud_t val) +{ + struct multicall_space mcs; + struct mmu_update *u; + + preempt_disable(); + + mcs = xen_mc_entry(sizeof(*u)); + u = mcs.args; + u->ptr = virt_to_machine(ptr).maddr; + u->val = pud_val_ma(val); + MULTI_mmu_update(mcs.mc, u, 1, NULL, DOMID_SELF); + + xen_mc_issue(PARAVIRT_LAZY_MMU); + + preempt_enable(); +} + +void xen_set_pte(pte_t *ptep, pte_t pte) +{ + ptep->pte_high = pte.pte_high; + smp_wmb(); + ptep->pte_low = pte.pte_low; +} + +void xen_set_pte_atomic(pte_t *ptep, pte_t pte) +{ + set_64bit((u64 *)ptep, pte_val_ma(pte)); +} + +void xen_pte_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep) +{ + ptep->pte_low = 0; + smp_wmb(); /* make sure low gets written first */ + ptep->pte_high = 0; +} + +void xen_pmd_clear(pmd_t *pmdp) +{ + xen_set_pmd(pmdp, __pmd(0)); +} + +unsigned long long xen_pte_val(pte_t pte) +{ + unsigned long long ret = 0; + + if (pte.pte_low) { + ret = ((unsigned long long)pte.pte_high << 32) | pte.pte_low; + ret = machine_to_phys(XMADDR(ret)).paddr | 1; + } + + return ret; +} + +unsigned long long xen_pmd_val(pmd_t pmd) +{ + unsigned long long ret = pmd.pmd; + if (ret) + ret = machine_to_phys(XMADDR(ret)).paddr | 1; + return ret; +} + +unsigned long long xen_pgd_val(pgd_t pgd) +{ + unsigned long long ret = pgd.pgd; + if (ret) + ret = machine_to_phys(XMADDR(ret)).paddr | 1; + return ret; +} + +pte_t xen_make_pte(unsigned long long pte) +{ + if (pte & 1) + pte = phys_to_machine(XPADDR(pte)).maddr; + + return (pte_t){ pte, pte >> 32 }; +} + +pmd_t xen_make_pmd(unsigned long long pmd) +{ + if (pmd & 1) + pmd = phys_to_machine(XPADDR(pmd)).maddr; + + return (pmd_t){ pmd }; +} + +pgd_t xen_make_pgd(unsigned long long pgd) +{ + if (pgd & _PAGE_PRESENT) + pgd = phys_to_machine(XPADDR(pgd)).maddr; + + return (pgd_t){ pgd }; +} +#else /* !PAE */ +void xen_set_pte(pte_t *ptep, pte_t pte) +{ + *ptep = pte; +} + +unsigned long xen_pte_val(pte_t pte) +{ + unsigned long ret = pte.pte_low; + + if (ret & _PAGE_PRESENT) + ret = machine_to_phys(XMADDR(ret)).paddr; + + return ret; +} + +unsigned long xen_pgd_val(pgd_t pgd) +{ + unsigned long ret = pgd.pgd; + if (ret) + ret = machine_to_phys(XMADDR(ret)).paddr | 1; + return ret; +} + +pte_t xen_make_pte(unsigned long pte) +{ + if (pte & _PAGE_PRESENT) + pte = phys_to_machine(XPADDR(pte)).maddr; + + return (pte_t){ pte }; +} + +pgd_t xen_make_pgd(unsigned long pgd) +{ + if (pgd & _PAGE_PRESENT) + pgd = phys_to_machine(XPADDR(pgd)).maddr; + + return (pgd_t){ pgd }; +} +#endif /* CONFIG_X86_PAE */ + + + +/* + (Yet another) pagetable walker. This one is intended for pinning a + pagetable. This means that it walks a pagetable and calls the + callback function on each page it finds making up the page table, + at every level. It walks the entire pagetable, but it only bothers + pinning pte pages which are below pte_limit. In the normal case + this will be TASK_SIZE, but at boot we need to pin up to + FIXADDR_TOP. But the important bit is that we don't pin beyond + there, because then we start getting into Xen's ptes. +*/ +static int pgd_walk(pgd_t *pgd_base, int (*func)(struct page *, unsigned), + unsigned long limit) +{ + pgd_t *pgd = pgd_base; + int flush = 0; + unsigned long addr = 0; + unsigned long pgd_next; + + BUG_ON(limit > FIXADDR_TOP); + + if (xen_feature(XENFEAT_auto_translated_physmap)) + return 0; + + for (; addr != FIXADDR_TOP; pgd++, addr = pgd_next) { + pud_t *pud; + unsigned long pud_limit, pud_next; + + pgd_next = pud_limit = pgd_addr_end(addr, FIXADDR_TOP); + + if (!pgd_val(*pgd)) + continue; + + pud = pud_offset(pgd, 0); + + if (PTRS_PER_PUD > 1) /* not folded */ + flush |= (*func)(virt_to_page(pud), 0); + + for (; addr != pud_limit; pud++, addr = pud_next) { + pmd_t *pmd; + unsigned long pmd_limit; + + pud_next = pud_addr_end(addr, pud_limit); + + if (pud_next < limit) + pmd_limit = pud_next; + else + pmd_limit = limit; + + if (pud_none(*pud)) + continue; + + pmd = pmd_offset(pud, 0); + + if (PTRS_PER_PMD > 1) /* not folded */ + flush |= (*func)(virt_to_page(pmd), 0); + + for (; addr != pmd_limit; pmd++) { + addr += (PAGE_SIZE * PTRS_PER_PTE); + if ((pmd_limit-1) < (addr-1)) { + addr = pmd_limit; + break; + } + + if (pmd_none(*pmd)) + continue; + + flush |= (*func)(pmd_page(*pmd), 0); + } + } + } + + flush |= (*func)(virt_to_page(pgd_base), UVMF_TLB_FLUSH); + + return flush; +} + +static int pin_page(struct page *page, unsigned flags) +{ + unsigned pgfl = test_and_set_bit(PG_pinned, &page->flags); + int flush; + + if (pgfl) + flush = 0; /* already pinned */ + else if (PageHighMem(page)) + /* kmaps need flushing if we found an unpinned + highpage */ + flush = 1; + else { + void *pt = lowmem_page_address(page); + unsigned long pfn = page_to_pfn(page); + struct multicall_space mcs = __xen_mc_entry(0); + + flush = 0; + + MULTI_update_va_mapping(mcs.mc, (unsigned long)pt, + pfn_pte(pfn, PAGE_KERNEL_RO), + flags); + } + + return flush; +} + +/* This is called just after a mm has been created, but it has not + been used yet. We need to make sure that its pagetable is all + read-only, and can be pinned. */ +void xen_pgd_pin(pgd_t *pgd) +{ + struct multicall_space mcs; + struct mmuext_op *op; + + xen_mc_batch(); + + if (pgd_walk(pgd, pin_page, TASK_SIZE)) { + /* re-enable interrupts for kmap_flush_unused */ + xen_mc_issue(0); + kmap_flush_unused(); + xen_mc_batch(); + } + + mcs = __xen_mc_entry(sizeof(*op)); + op = mcs.args; + +#ifdef CONFIG_X86_PAE + op->cmd = MMUEXT_PIN_L3_TABLE; +#else + op->cmd = MMUEXT_PIN_L2_TABLE; +#endif + op->arg1.mfn = pfn_to_mfn(PFN_DOWN(__pa(pgd))); + MULTI_mmuext_op(mcs.mc, op, 1, NULL, DOMID_SELF); + + xen_mc_issue(0); +} + +/* The init_mm pagetable is really pinned as soon as its created, but + that's before we have page structures to store the bits. So do all + the book-keeping now. */ +static __init int mark_pinned(struct page *page, unsigned flags) +{ + SetPagePinned(page); + return 0; +} + +void __init xen_mark_init_mm_pinned(void) +{ + pgd_walk(init_mm.pgd, mark_pinned, FIXADDR_TOP); +} + +static int unpin_page(struct page *page, unsigned flags) +{ + unsigned pgfl = test_and_clear_bit(PG_pinned, &page->flags); + + if (pgfl && !PageHighMem(page)) { + void *pt = lowmem_page_address(page); + unsigned long pfn = page_to_pfn(page); + struct multicall_space mcs = __xen_mc_entry(0); + + MULTI_update_va_mapping(mcs.mc, (unsigned long)pt, + pfn_pte(pfn, PAGE_KERNEL), + flags); + } + + return 0; /* never need to flush on unpin */ +} + +/* Release a pagetables pages back as normal RW */ +static void xen_pgd_unpin(pgd_t *pgd) +{ + struct mmuext_op *op; + struct multicall_space mcs; + + xen_mc_batch(); + + mcs = __xen_mc_entry(sizeof(*op)); + + op = mcs.args; + op->cmd = MMUEXT_UNPIN_TABLE; + op->arg1.mfn = pfn_to_mfn(PFN_DOWN(__pa(pgd))); + + MULTI_mmuext_op(mcs.mc, op, 1, NULL, DOMID_SELF); + + pgd_walk(pgd, unpin_page, TASK_SIZE); + + xen_mc_issue(0); +} + +void xen_activate_mm(struct mm_struct *prev, struct mm_struct *next) +{ + spin_lock(&next->page_table_lock); + xen_pgd_pin(next->pgd); + spin_unlock(&next->page_table_lock); +} + +void xen_dup_mmap(struct mm_struct *oldmm, struct mm_struct *mm) +{ + spin_lock(&mm->page_table_lock); + xen_pgd_pin(mm->pgd); + spin_unlock(&mm->page_table_lock); +} + + +#ifdef CONFIG_SMP +/* Another cpu may still have their %cr3 pointing at the pagetable, so + we need to repoint it somewhere else before we can unpin it. */ +static void drop_other_mm_ref(void *info) +{ + struct mm_struct *mm = info; + + if (__get_cpu_var(cpu_tlbstate).active_mm == mm) + leave_mm(smp_processor_id()); +} + +static void drop_mm_ref(struct mm_struct *mm) +{ + if (current->active_mm == mm) { + if (current->mm == mm) + load_cr3(swapper_pg_dir); + else + leave_mm(smp_processor_id()); + } + + if (!cpus_empty(mm->cpu_vm_mask)) + xen_smp_call_function_mask(mm->cpu_vm_mask, drop_other_mm_ref, + mm, 1); +} +#else +static void drop_mm_ref(struct mm_struct *mm) +{ + if (current->active_mm == mm) + load_cr3(swapper_pg_dir); +} +#endif + +/* + * While a process runs, Xen pins its pagetables, which means that the + * hypervisor forces it to be read-only, and it controls all updates + * to it. This means that all pagetable updates have to go via the + * hypervisor, which is moderately expensive. + * + * Since we're pulling the pagetable down, we switch to use init_mm, + * unpin old process pagetable and mark it all read-write, which + * allows further operations on it to be simple memory accesses. + * + * The only subtle point is that another CPU may be still using the + * pagetable because of lazy tlb flushing. This means we need need to + * switch all CPUs off this pagetable before we can unpin it. + */ +void xen_exit_mmap(struct mm_struct *mm) +{ + get_cpu(); /* make sure we don't move around */ + drop_mm_ref(mm); + put_cpu(); + + spin_lock(&mm->page_table_lock); + xen_pgd_unpin(mm->pgd); + spin_unlock(&mm->page_table_lock); +} diff --git a/arch/i386/xen/mmu.h b/arch/i386/xen/mmu.h new file mode 100644 index 00000000000..c9ff27f3ac3 --- /dev/null +++ b/arch/i386/xen/mmu.h @@ -0,0 +1,60 @@ +#ifndef _XEN_MMU_H + +#include <linux/linkage.h> +#include <asm/page.h> + +/* + * Page-directory addresses above 4GB do not fit into architectural %cr3. + * When accessing %cr3, or equivalent field in vcpu_guest_context, guests + * must use the following accessor macros to pack/unpack valid MFNs. + * + * Note that Xen is using the fact that the pagetable base is always + * page-aligned, and putting the 12 MSB of the address into the 12 LSB + * of cr3. + */ +#define xen_pfn_to_cr3(pfn) (((unsigned)(pfn) << 12) | ((unsigned)(pfn) >> 20)) +#define xen_cr3_to_pfn(cr3) (((unsigned)(cr3) >> 12) | ((unsigned)(cr3) << 20)) + + +void set_pte_mfn(unsigned long vaddr, unsigned long pfn, pgprot_t flags); + +void xen_set_pte(pte_t *ptep, pte_t pteval); +void xen_set_pte_at(struct mm_struct *mm, unsigned long addr, + pte_t *ptep, pte_t pteval); +void xen_set_pmd(pmd_t *pmdp, pmd_t pmdval); + +void xen_activate_mm(struct mm_struct *prev, struct mm_struct *next); +void xen_dup_mmap(struct mm_struct *oldmm, struct mm_struct *mm); +void xen_exit_mmap(struct mm_struct *mm); + +void xen_pgd_pin(pgd_t *pgd); +//void xen_pgd_unpin(pgd_t *pgd); + +#ifdef CONFIG_X86_PAE +unsigned long long xen_pte_val(pte_t); +unsigned long long xen_pmd_val(pmd_t); +unsigned long long xen_pgd_val(pgd_t); + +pte_t xen_make_pte(unsigned long long); +pmd_t xen_make_pmd(unsigned long long); +pgd_t xen_make_pgd(unsigned long long); + +void xen_set_pte_at(struct mm_struct *mm, unsigned long addr, + pte_t *ptep, pte_t pteval); +void xen_set_pte_atomic(pte_t *ptep, pte_t pte); +void xen_set_pud(pud_t *ptr, pud_t val); +void xen_pte_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep); +void xen_pmd_clear(pmd_t *pmdp); + + +#else +unsigned long xen_pte_val(pte_t); +unsigned long xen_pmd_val(pmd_t); +unsigned long xen_pgd_val(pgd_t); + +pte_t xen_make_pte(unsigned long); +pmd_t xen_make_pmd(unsigned long); +pgd_t xen_make_pgd(unsigned long); +#endif + +#endif /* _XEN_MMU_H */ diff --git a/arch/i386/xen/multicalls.c b/arch/i386/xen/multicalls.c new file mode 100644 index 00000000000..c837e8e463d --- /dev/null +++ b/arch/i386/xen/multicalls.c @@ -0,0 +1,90 @@ +/* + * Xen hypercall batching. + * + * Xen allows multiple hypercalls to be issued at once, using the + * multicall interface. This allows the cost of trapping into the + * hypervisor to be amortized over several calls. + * + * This file implements a simple interface for multicalls. There's a + * per-cpu buffer of outstanding multicalls. When you want to queue a + * multicall for issuing, you can allocate a multicall slot for the + * call and its arguments, along with storage for space which is + * pointed to by the arguments (for passing pointers to structures, + * etc). When the multicall is actually issued, all the space for the + * commands and allocated memory is freed for reuse. + * + * Multicalls are flushed whenever any of the buffers get full, or + * when explicitly requested. There's no way to get per-multicall + * return results back. It will BUG if any of the multicalls fail. + * + * Jeremy Fitzhardinge <jeremy@xensource.com>, XenSource Inc, 2007 + */ +#include <linux/percpu.h> +#include <linux/hardirq.h> + +#include <asm/xen/hypercall.h> + +#include "multicalls.h" + +#define MC_BATCH 32 +#define MC_ARGS (MC_BATCH * 16 / sizeof(u64)) + +struct mc_buffer { + struct multicall_entry entries[MC_BATCH]; + u64 args[MC_ARGS]; + unsigned mcidx, argidx; +}; + +static DEFINE_PER_CPU(struct mc_buffer, mc_buffer); +DEFINE_PER_CPU(unsigned long, xen_mc_irq_flags); + +void xen_mc_flush(void) +{ + struct mc_buffer *b = &__get_cpu_var(mc_buffer); + int ret = 0; + unsigned long flags; + + BUG_ON(preemptible()); + + /* Disable interrupts in case someone comes in and queues + something in the middle */ + local_irq_save(flags); + + if (b->mcidx) { + int i; + + if (HYPERVISOR_multicall(b->entries, b->mcidx) != 0) + BUG(); + for (i = 0; i < b->mcidx; i++) + if (b->entries[i].result < 0) + ret++; + b->mcidx = 0; + b->argidx = 0; + } else + BUG_ON(b->argidx != 0); + + local_irq_restore(flags); + + BUG_ON(ret); +} + +struct multicall_space __xen_mc_entry(size_t args) +{ + struct mc_buffer *b = &__get_cpu_var(mc_buffer); + struct multicall_space ret; + unsigned argspace = (args + sizeof(u64) - 1) / sizeof(u64); + + BUG_ON(preemptible()); + BUG_ON(argspace > MC_ARGS); + + if (b->mcidx == MC_BATCH || + (b->argidx + argspace) > MC_ARGS) + xen_mc_flush(); + + ret.mc = &b->entries[b->mcidx]; + b->mcidx++; + ret.args = &b->args[b->argidx]; + b->argidx += argspace; + + return ret; +} diff --git a/arch/i386/xen/multicalls.h b/arch/i386/xen/multicalls.h new file mode 100644 index 00000000000..e6f7530b156 --- /dev/null +++ b/arch/i386/xen/multicalls.h @@ -0,0 +1,45 @@ +#ifndef _XEN_MULTICALLS_H +#define _XEN_MULTICALLS_H + +#include "xen-ops.h" + +/* Multicalls */ +struct multicall_space +{ + struct multicall_entry *mc; + void *args; +}; + +/* Allocate room for a multicall and its args */ +struct multicall_space __xen_mc_entry(size_t args); + +DECLARE_PER_CPU(unsigned long, xen_mc_irq_flags); + +/* Call to start a batch of multiple __xen_mc_entry()s. Must be + paired with xen_mc_issue() */ +static inline void xen_mc_batch(void) +{ + /* need to disable interrupts until this entry is complete */ + local_irq_save(__get_cpu_var(xen_mc_irq_flags)); +} + +static inline struct multicall_space xen_mc_entry(size_t args) +{ + xen_mc_batch(); + return __xen_mc_entry(args); +} + +/* Flush all pending multicalls */ +void xen_mc_flush(void); + +/* Issue a multicall if we're not in a lazy mode */ +static inline void xen_mc_issue(unsigned mode) +{ + if ((xen_get_lazy_mode() & mode) == 0) + xen_mc_flush(); + + /* restore flags saved in xen_mc_batch */ + local_irq_restore(x86_read_percpu(xen_mc_irq_flags)); +} + +#endif /* _XEN_MULTICALLS_H */ diff --git a/arch/i386/xen/setup.c b/arch/i386/xen/setup.c new file mode 100644 index 00000000000..2fe6eac510f --- /dev/null +++ b/arch/i386/xen/setup.c @@ -0,0 +1,96 @@ +/* + * Machine specific setup for xen + * + * Jeremy Fitzhardinge <jeremy@xensource.com>, XenSource Inc, 2007 + */ + +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/pm.h> + +#include <asm/elf.h> +#include <asm/e820.h> +#include <asm/setup.h> +#include <asm/xen/hypervisor.h> +#include <asm/xen/hypercall.h> + +#include <xen/interface/physdev.h> +#include <xen/features.h> + +#include "xen-ops.h" + +/* These are code, but not functions. Defined in entry.S */ +extern const char xen_hypervisor_callback[]; +extern const char xen_failsafe_callback[]; + +unsigned long *phys_to_machine_mapping; +EXPORT_SYMBOL(phys_to_machine_mapping); + +/** + * machine_specific_memory_setup - Hook for machine specific memory setup. + **/ + +char * __init xen_memory_setup(void) +{ + unsigned long max_pfn = xen_start_info->nr_pages; + + e820.nr_map = 0; + add_memory_region(0, PFN_PHYS(max_pfn), E820_RAM); + + return "Xen"; +} + +static void xen_idle(void) +{ + local_irq_disable(); + + if (need_resched()) + local_irq_enable(); + else { + current_thread_info()->status &= ~TS_POLLING; + smp_mb__after_clear_bit(); + safe_halt(); + current_thread_info()->status |= TS_POLLING; + } +} + +void __init xen_arch_setup(void) +{ + struct physdev_set_iopl set_iopl; + int rc; + + HYPERVISOR_vm_assist(VMASST_CMD_enable, VMASST_TYPE_4gb_segments); + HYPERVISOR_vm_assist(VMASST_CMD_enable, VMASST_TYPE_writable_pagetables); + + if (!xen_feature(XENFEAT_auto_translated_physmap)) + HYPERVISOR_vm_assist(VMASST_CMD_enable, VMASST_TYPE_pae_extended_cr3); + + HYPERVISOR_set_callbacks(__KERNEL_CS, (unsigned long)xen_hypervisor_callback, + __KERNEL_CS, (unsigned long)xen_failsafe_callback); + + set_iopl.iopl = 1; + rc = HYPERVISOR_physdev_op(PHYSDEVOP_set_iopl, &set_iopl); + if (rc != 0) + printk(KERN_INFO "physdev_op failed %d\n", rc); + +#ifdef CONFIG_ACPI + if (!(xen_start_info->flags & SIF_INITDOMAIN)) { + printk(KERN_INFO "ACPI in unprivileged domain disabled\n"); + disable_acpi(); + } +#endif + + memcpy(boot_command_line, xen_start_info->cmd_line, + MAX_GUEST_CMDLINE > COMMAND_LINE_SIZE ? + COMMAND_LINE_SIZE : MAX_GUEST_CMDLINE); + + pm_idle = xen_idle; + +#ifdef CONFIG_SMP + /* fill cpus_possible with all available cpus */ + xen_fill_possible_map(); +#endif + + paravirt_disable_iospace(); +} diff --git a/arch/i386/xen/smp.c b/arch/i386/xen/smp.c new file mode 100644 index 00000000000..557b8e24706 --- /dev/null +++ b/arch/i386/xen/smp.c @@ -0,0 +1,404 @@ +/* + * Xen SMP support + * + * This file implements the Xen versions of smp_ops. SMP under Xen is + * very straightforward. Bringing a CPU up is simply a matter of + * loading its initial context and setting it running. + * + * IPIs are handled through the Xen event mechanism. + * + * Because virtual CPUs can be scheduled onto any real CPU, there's no + * useful topology information for the kernel to make use of. As a + * result, all CPUs are treated as if they're single-core and + * single-threaded. + * + * This does not handle HOTPLUG_CPU yet. + */ +#include <linux/sched.h> +#include <linux/err.h> +#include <linux/smp.h> + +#include <asm/paravirt.h> +#include <asm/desc.h> +#include <asm/pgtable.h> +#include <asm/cpu.h> + +#include <xen/interface/xen.h> +#include <xen/interface/vcpu.h> + +#include <asm/xen/interface.h> +#include <asm/xen/hypercall.h> + +#include <xen/page.h> +#include <xen/events.h> + +#include "xen-ops.h" +#include "mmu.h" + +static cpumask_t cpu_initialized_map; +static DEFINE_PER_CPU(int, resched_irq); +static DEFINE_PER_CPU(int, callfunc_irq); + +/* + * Structure and data for smp_call_function(). This is designed to minimise + * static memory requirements. It also looks cleaner. + */ +static DEFINE_SPINLOCK(call_lock); + +struct call_data_struct { + void (*func) (void *info); + void *info; + atomic_t started; + atomic_t finished; + int wait; +}; + +static irqreturn_t xen_call_function_interrupt(int irq, void *dev_id); + +static struct call_data_struct *call_data; + +/* + * Reschedule call back. Nothing to do, + * all the work is done automatically when + * we return from the interrupt. + */ +static irqreturn_t xen_reschedule_interrupt(int irq, void *dev_id) +{ + return IRQ_HANDLED; +} + +static __cpuinit void cpu_bringup_and_idle(void) +{ + int cpu = smp_processor_id(); + + cpu_init(); + + preempt_disable(); + per_cpu(cpu_state, cpu) = CPU_ONLINE; + + xen_setup_cpu_clockevents(); + + /* We can take interrupts now: we're officially "up". */ + local_irq_enable(); + + wmb(); /* make sure everything is out */ + cpu_idle(); +} + +static int xen_smp_intr_init(unsigned int cpu) +{ + int rc; + const char *resched_name, *callfunc_name; + + per_cpu(resched_irq, cpu) = per_cpu(callfunc_irq, cpu) = -1; + + resched_name = kasprintf(GFP_KERNEL, "resched%d", cpu); + rc = bind_ipi_to_irqhandler(XEN_RESCHEDULE_VECTOR, + cpu, + xen_reschedule_interrupt, + IRQF_DISABLED|IRQF_PERCPU|IRQF_NOBALANCING, + resched_name, + NULL); + if (rc < 0) + goto fail; + per_cpu(resched_irq, cpu) = rc; + + callfunc_name = kasprintf(GFP_KERNEL, "callfunc%d", cpu); + rc = bind_ipi_to_irqhandler(XEN_CALL_FUNCTION_VECTOR, + cpu, + xen_call_function_interrupt, + IRQF_DISABLED|IRQF_PERCPU|IRQF_NOBALANCING, + callfunc_name, + NULL); + if (rc < 0) + goto fail; + per_cpu(callfunc_irq, cpu) = rc; + + return 0; + + fail: + if (per_cpu(resched_irq, cpu) >= 0) + unbind_from_irqhandler(per_cpu(resched_irq, cpu), NULL); + if (per_cpu(callfunc_irq, cpu) >= 0) + unbind_from_irqhandler(per_cpu(callfunc_irq, cpu), NULL); + return rc; +} + +void __init xen_fill_possible_map(void) +{ + int i, rc; + + for (i = 0; i < NR_CPUS; i++) { + rc = HYPERVISOR_vcpu_op(VCPUOP_is_up, i, NULL); + if (rc >= 0) + cpu_set(i, cpu_possible_map); + } +} + +void __init xen_smp_prepare_boot_cpu(void) +{ + int cpu; + + BUG_ON(smp_processor_id() != 0); + native_smp_prepare_boot_cpu(); + + /* We've switched to the "real" per-cpu gdt, so make sure the + old memory can be recycled */ + make_lowmem_page_readwrite(&per_cpu__gdt_page); + + for (cpu = 0; cpu < NR_CPUS; cpu++) { + cpus_clear(cpu_sibling_map[cpu]); + cpus_clear(cpu_core_map[cpu]); + } + + xen_setup_vcpu_info_placement(); +} + +void __init xen_smp_prepare_cpus(unsigned int max_cpus) +{ + unsigned cpu; + + for (cpu = 0; cpu < NR_CPUS; cpu++) { + cpus_clear(cpu_sibling_map[cpu]); + cpus_clear(cpu_core_map[cpu]); + } + + smp_store_cpu_info(0); + set_cpu_sibling_map(0); + + if (xen_smp_intr_init(0)) + BUG(); + + cpu_initialized_map = cpumask_of_cpu(0); + + /* Restrict the possible_map according to max_cpus. */ + while ((num_possible_cpus() > 1) && (num_possible_cpus() > max_cpus)) { + for (cpu = NR_CPUS-1; !cpu_isset(cpu, cpu_possible_map); cpu--) + continue; + cpu_clear(cpu, cpu_possible_map); + } + + for_each_possible_cpu (cpu) { + struct task_struct *idle; + + if (cpu == 0) + continue; + + idle = fork_idle(cpu); + if (IS_ERR(idle)) + panic("failed fork for CPU %d", cpu); + + cpu_set(cpu, cpu_present_map); + } + + //init_xenbus_allowed_cpumask(); +} + +static __cpuinit int +cpu_initialize_context(unsigned int cpu, struct task_struct *idle) +{ + struct vcpu_guest_context *ctxt; + struct gdt_page *gdt = &per_cpu(gdt_page, cpu); + + if (cpu_test_and_set(cpu, cpu_initialized_map)) + return 0; + + ctxt = kzalloc(sizeof(*ctxt), GFP_KERNEL); + if (ctxt == NULL) + return -ENOMEM; + + ctxt->flags = VGCF_IN_KERNEL; + ctxt->user_regs.ds = __USER_DS; + ctxt->user_regs.es = __USER_DS; + ctxt->user_regs.fs = __KERNEL_PERCPU; + ctxt->user_regs.gs = 0; + ctxt->user_regs.ss = __KERNEL_DS; + ctxt->user_regs.eip = (unsigned long)cpu_bringup_and_idle; + ctxt->user_regs.eflags = 0x1000; /* IOPL_RING1 */ + + memset(&ctxt->fpu_ctxt, 0, sizeof(ctxt->fpu_ctxt)); + + xen_copy_trap_info(ctxt->trap_ctxt); + + ctxt->ldt_ents = 0; + + BUG_ON((unsigned long)gdt->gdt & ~PAGE_MASK); + make_lowmem_page_readonly(gdt->gdt); + + ctxt->gdt_frames[0] = virt_to_mfn(gdt->gdt); + ctxt->gdt_ents = ARRAY_SIZE(gdt->gdt); + + ctxt->user_regs.cs = __KERNEL_CS; + ctxt->user_regs.esp = idle->thread.esp0 - sizeof(struct pt_regs); + + ctxt->kernel_ss = __KERNEL_DS; + ctxt->kernel_sp = idle->thread.esp0; + + ctxt->event_callback_cs = __KERNEL_CS; + ctxt->event_callback_eip = (unsigned long)xen_hypervisor_callback; + ctxt->failsafe_callback_cs = __KERNEL_CS; + ctxt->failsafe_callback_eip = (unsigned long)xen_failsafe_callback; + + per_cpu(xen_cr3, cpu) = __pa(swapper_pg_dir); + ctxt->ctrlreg[3] = xen_pfn_to_cr3(virt_to_mfn(swapper_pg_dir)); + + if (HYPERVISOR_vcpu_op(VCPUOP_initialise, cpu, ctxt)) + BUG(); + + kfree(ctxt); + return 0; +} + +int __cpuinit xen_cpu_up(unsigned int cpu) +{ + struct task_struct *idle = idle_task(cpu); + int rc; + +#if 0 + rc = cpu_up_check(cpu); + if (rc) + return rc; +#endif + + init_gdt(cpu); + per_cpu(current_task, cpu) = idle; + irq_ctx_init(cpu); + xen_setup_timer(cpu); + + /* make sure interrupts start blocked */ + per_cpu(xen_vcpu, cpu)->evtchn_upcall_mask = 1; + + rc = cpu_initialize_context(cpu, idle); + if (rc) + return rc; + + if (num_online_cpus() == 1) + alternatives_smp_switch(1); + + rc = xen_smp_intr_init(cpu); + if (rc) + return rc; + + smp_store_cpu_info(cpu); + set_cpu_sibling_map(cpu); + /* This must be done before setting cpu_online_map */ + wmb(); + + cpu_set(cpu, cpu_online_map); + + rc = HYPERVISOR_vcpu_op(VCPUOP_up, cpu, NULL); + BUG_ON(rc); + + return 0; +} + +void xen_smp_cpus_done(unsigned int max_cpus) +{ +} + +static void stop_self(void *v) +{ + int cpu = smp_processor_id(); + + /* make sure we're not pinning something down */ + load_cr3(swapper_pg_dir); + /* should set up a minimal gdt */ + + HYPERVISOR_vcpu_op(VCPUOP_down, cpu, NULL); + BUG(); +} + +void xen_smp_send_stop(void) +{ + smp_call_function(stop_self, NULL, 0, 0); +} + +void xen_smp_send_reschedule(int cpu) +{ + xen_send_IPI_one(cpu, XEN_RESCHEDULE_VECTOR); +} + + +static void xen_send_IPI_mask(cpumask_t mask, enum ipi_vector vector) +{ + unsigned cpu; + + cpus_and(mask, mask, cpu_online_map); + + for_each_cpu_mask(cpu, mask) + xen_send_IPI_one(cpu, vector); +} + +static irqreturn_t xen_call_function_interrupt(int irq, void *dev_id) +{ + void (*func) (void *info) = call_data->func; + void *info = call_data->info; + int wait = call_data->wait; + + /* + * Notify initiating CPU that I've grabbed the data and am + * about to execute the function + */ + mb(); + atomic_inc(&call_data->started); + /* + * At this point the info structure may be out of scope unless wait==1 + */ + irq_enter(); + (*func)(info); + irq_exit(); + + if (wait) { + mb(); /* commit everything before setting finished */ + atomic_inc(&call_data->finished); + } + + return IRQ_HANDLED; +} + +int xen_smp_call_function_mask(cpumask_t mask, void (*func)(void *), + void *info, int wait) +{ + struct call_data_struct data; + int cpus; + + /* Holding any lock stops cpus from going down. */ + spin_lock(&call_lock); + + cpu_clear(smp_processor_id(), mask); + + cpus = cpus_weight(mask); + if (!cpus) { + spin_unlock(&call_lock); + return 0; + } + + /* Can deadlock when called with interrupts disabled */ + WARN_ON(irqs_disabled()); + + data.func = func; + data.info = info; + atomic_set(&data.started, 0); + data.wait = wait; + if (wait) + atomic_set(&data.finished, 0); + + call_data = &data; + mb(); /* write everything before IPI */ + + /* Send a message to other CPUs and wait for them to respond */ + xen_send_IPI_mask(mask, XEN_CALL_FUNCTION_VECTOR); + + /* Make sure other vcpus get a chance to run. + XXX too severe? Maybe we should check the other CPU's states? */ + HYPERVISOR_sched_op(SCHEDOP_yield, 0); + + /* Wait for response */ + while (atomic_read(&data.started) != cpus || + (wait && atomic_read(&data.finished) != cpus)) + cpu_relax(); + + spin_unlock(&call_lock); + + return 0; +} diff --git a/arch/i386/xen/time.c b/arch/i386/xen/time.c new file mode 100644 index 00000000000..51fdabf1fd4 --- /dev/null +++ b/arch/i386/xen/time.c @@ -0,0 +1,590 @@ +/* + * Xen time implementation. + * + * This is implemented in terms of a clocksource driver which uses + * the hypervisor clock as a nanosecond timebase, and a clockevent + * driver which uses the hypervisor's timer mechanism. + * + * Jeremy Fitzhardinge <jeremy@xensource.com>, XenSource Inc, 2007 + */ +#include <linux/kernel.h> +#include <linux/interrupt.h> +#include <linux/clocksource.h> +#include <linux/clockchips.h> +#include <linux/kernel_stat.h> + +#include <asm/xen/hypervisor.h> +#include <asm/xen/hypercall.h> + +#include <xen/events.h> +#include <xen/interface/xen.h> +#include <xen/interface/vcpu.h> + +#include "xen-ops.h" + +#define XEN_SHIFT 22 + +/* Xen may fire a timer up to this many ns early */ +#define TIMER_SLOP 100000 +#define NS_PER_TICK (1000000000LL / HZ) + +static cycle_t xen_clocksource_read(void); + +/* These are perodically updated in shared_info, and then copied here. */ +struct shadow_time_info { + u64 tsc_timestamp; /* TSC at last update of time vals. */ + u64 system_timestamp; /* Time, in nanosecs, since boot. */ + u32 tsc_to_nsec_mul; + int tsc_shift; + u32 version; +}; + +static DEFINE_PER_CPU(struct shadow_time_info, shadow_time); + +/* runstate info updated by Xen */ +static DEFINE_PER_CPU(struct vcpu_runstate_info, runstate); + +/* snapshots of runstate info */ +static DEFINE_PER_CPU(struct vcpu_runstate_info, runstate_snapshot); + +/* unused ns of stolen and blocked time */ +static DEFINE_PER_CPU(u64, residual_stolen); +static DEFINE_PER_CPU(u64, residual_blocked); + +/* return an consistent snapshot of 64-bit time/counter value */ +static u64 get64(const u64 *p) +{ + u64 ret; + + if (BITS_PER_LONG < 64) { + u32 *p32 = (u32 *)p; + u32 h, l; + + /* + * Read high then low, and then make sure high is + * still the same; this will only loop if low wraps + * and carries into high. + * XXX some clean way to make this endian-proof? + */ + do { + h = p32[1]; + barrier(); + l = p32[0]; + barrier(); + } while (p32[1] != h); + + ret = (((u64)h) << 32) | l; + } else + ret = *p; + + return ret; +} + +/* + * Runstate accounting + */ +static void get_runstate_snapshot(struct vcpu_runstate_info *res) +{ + u64 state_time; + struct vcpu_runstate_info *state; + + BUG_ON(preemptible()); + + state = &__get_cpu_var(runstate); + + /* + * The runstate info is always updated by the hypervisor on + * the current CPU, so there's no need to use anything + * stronger than a compiler barrier when fetching it. + */ + do { + state_time = get64(&state->state_entry_time); + barrier(); + *res = *state; + barrier(); + } while (get64(&state->state_entry_time) != state_time); +} + +static void setup_runstate_info(int cpu) +{ + struct vcpu_register_runstate_memory_area area; + + area.addr.v = &per_cpu(runstate, cpu); + + if (HYPERVISOR_vcpu_op(VCPUOP_register_runstate_memory_area, + cpu, &area)) + BUG(); +} + +static void do_stolen_accounting(void) +{ + struct vcpu_runstate_info state; + struct vcpu_runstate_info *snap; + s64 blocked, runnable, offline, stolen; + cputime_t ticks; + + get_runstate_snapshot(&state); + + WARN_ON(state.state != RUNSTATE_running); + + snap = &__get_cpu_var(runstate_snapshot); + + /* work out how much time the VCPU has not been runn*ing* */ + blocked = state.time[RUNSTATE_blocked] - snap->time[RUNSTATE_blocked]; + runnable = state.time[RUNSTATE_runnable] - snap->time[RUNSTATE_runnable]; + offline = state.time[RUNSTATE_offline] - snap->time[RUNSTATE_offline]; + + *snap = state; + + /* Add the appropriate number of ticks of stolen time, + including any left-overs from last time. Passing NULL to + account_steal_time accounts the time as stolen. */ + stolen = runnable + offline + __get_cpu_var(residual_stolen); + + if (stolen < 0) + stolen = 0; + + ticks = 0; + while (stolen >= NS_PER_TICK) { + ticks++; + stolen -= NS_PER_TICK; + } + __get_cpu_var(residual_stolen) = stolen; + account_steal_time(NULL, ticks); + + /* Add the appropriate number of ticks of blocked time, + including any left-overs from last time. Passing idle to + account_steal_time accounts the time as idle/wait. */ + blocked += __get_cpu_var(residual_blocked); + + if (blocked < 0) + blocked = 0; + + ticks = 0; + while (blocked >= NS_PER_TICK) { + ticks++; + blocked -= NS_PER_TICK; + } + __get_cpu_var(residual_blocked) = blocked; + account_steal_time(idle_task(smp_processor_id()), ticks); +} + +/* + * Xen sched_clock implementation. Returns the number of unstolen + * nanoseconds, which is nanoseconds the VCPU spent in RUNNING+BLOCKED + * states. + */ +unsigned long long xen_sched_clock(void) +{ + struct vcpu_runstate_info state; + cycle_t now; + u64 ret; + s64 offset; + + /* + * Ideally sched_clock should be called on a per-cpu basis + * anyway, so preempt should already be disabled, but that's + * not current practice at the moment. + */ + preempt_disable(); + + now = xen_clocksource_read(); + + get_runstate_snapshot(&state); + + WARN_ON(state.state != RUNSTATE_running); + + offset = now - state.state_entry_time; + if (offset < 0) + offset = 0; + + ret = state.time[RUNSTATE_blocked] + + state.time[RUNSTATE_running] + + offset; + + preempt_enable(); + + return ret; +} + + +/* Get the CPU speed from Xen */ +unsigned long xen_cpu_khz(void) +{ + u64 cpu_khz = 1000000ULL << 32; + const struct vcpu_time_info *info = + &HYPERVISOR_shared_info->vcpu_info[0].time; + + do_div(cpu_khz, info->tsc_to_system_mul); + if (info->tsc_shift < 0) + cpu_khz <<= -info->tsc_shift; + else + cpu_khz >>= info->tsc_shift; + + return cpu_khz; +} + +/* + * Reads a consistent set of time-base values from Xen, into a shadow data + * area. + */ +static unsigned get_time_values_from_xen(void) +{ + struct vcpu_time_info *src; + struct shadow_time_info *dst; + + /* src is shared memory with the hypervisor, so we need to + make sure we get a consistent snapshot, even in the face of + being preempted. */ + src = &__get_cpu_var(xen_vcpu)->time; + dst = &__get_cpu_var(shadow_time); + + do { + dst->version = src->version; + rmb(); /* fetch version before data */ + dst->tsc_timestamp = src->tsc_timestamp; + dst->system_timestamp = src->system_time; + dst->tsc_to_nsec_mul = src->tsc_to_system_mul; + dst->tsc_shift = src->tsc_shift; + rmb(); /* test version after fetching data */ + } while ((src->version & 1) | (dst->version ^ src->version)); + + return dst->version; +} + +/* + * Scale a 64-bit delta by scaling and multiplying by a 32-bit fraction, + * yielding a 64-bit result. + */ +static inline u64 scale_delta(u64 delta, u32 mul_frac, int shift) +{ + u64 product; +#ifdef __i386__ + u32 tmp1, tmp2; +#endif + + if (shift < 0) + delta >>= -shift; + else + delta <<= shift; + +#ifdef __i386__ + __asm__ ( + "mul %5 ; " + "mov %4,%%eax ; " + "mov %%edx,%4 ; " + "mul %5 ; " + "xor %5,%5 ; " + "add %4,%%eax ; " + "adc %5,%%edx ; " + : "=A" (product), "=r" (tmp1), "=r" (tmp2) + : "a" ((u32)delta), "1" ((u32)(delta >> 32)), "2" (mul_frac) ); +#elif __x86_64__ + __asm__ ( + "mul %%rdx ; shrd $32,%%rdx,%%rax" + : "=a" (product) : "0" (delta), "d" ((u64)mul_frac) ); +#else +#error implement me! +#endif + + return product; +} + +static u64 get_nsec_offset(struct shadow_time_info *shadow) +{ + u64 now, delta; + now = native_read_tsc(); + delta = now - shadow->tsc_timestamp; + return scale_delta(delta, shadow->tsc_to_nsec_mul, shadow->tsc_shift); +} + +static cycle_t xen_clocksource_read(void) +{ + struct shadow_time_info *shadow = &get_cpu_var(shadow_time); + cycle_t ret; + unsigned version; + + do { + version = get_time_values_from_xen(); + barrier(); + ret = shadow->system_timestamp + get_nsec_offset(shadow); + barrier(); + } while (version != __get_cpu_var(xen_vcpu)->time.version); + + put_cpu_var(shadow_time); + + return ret; +} + +static void xen_read_wallclock(struct timespec *ts) +{ + const struct shared_info *s = HYPERVISOR_shared_info; + u32 version; + u64 delta; + struct timespec now; + + /* get wallclock at system boot */ + do { + version = s->wc_version; + rmb(); /* fetch version before time */ + now.tv_sec = s->wc_sec; + now.tv_nsec = s->wc_nsec; + rmb(); /* fetch time before checking version */ + } while ((s->wc_version & 1) | (version ^ s->wc_version)); + + delta = xen_clocksource_read(); /* time since system boot */ + delta += now.tv_sec * (u64)NSEC_PER_SEC + now.tv_nsec; + + now.tv_nsec = do_div(delta, NSEC_PER_SEC); + now.tv_sec = delta; + + set_normalized_timespec(ts, now.tv_sec, now.tv_nsec); +} + +unsigned long xen_get_wallclock(void) +{ + struct timespec ts; + + xen_read_wallclock(&ts); + + return ts.tv_sec; +} + +int xen_set_wallclock(unsigned long now) +{ + /* do nothing for domU */ + return -1; +} + +static struct clocksource xen_clocksource __read_mostly = { + .name = "xen", + .rating = 400, + .read = xen_clocksource_read, + .mask = ~0, + .mult = 1<<XEN_SHIFT, /* time directly in nanoseconds */ + .shift = XEN_SHIFT, + .flags = CLOCK_SOURCE_IS_CONTINUOUS, +}; + +/* + Xen clockevent implementation + + Xen has two clockevent implementations: + + The old timer_op one works with all released versions of Xen prior + to version 3.0.4. This version of the hypervisor provides a + single-shot timer with nanosecond resolution. However, sharing the + same event channel is a 100Hz tick which is delivered while the + vcpu is running. We don't care about or use this tick, but it will + cause the core time code to think the timer fired too soon, and + will end up resetting it each time. It could be filtered, but + doing so has complications when the ktime clocksource is not yet + the xen clocksource (ie, at boot time). + + The new vcpu_op-based timer interface allows the tick timer period + to be changed or turned off. The tick timer is not useful as a + periodic timer because events are only delivered to running vcpus. + The one-shot timer can report when a timeout is in the past, so + set_next_event is capable of returning -ETIME when appropriate. + This interface is used when available. +*/ + + +/* + Get a hypervisor absolute time. In theory we could maintain an + offset between the kernel's time and the hypervisor's time, and + apply that to a kernel's absolute timeout. Unfortunately the + hypervisor and kernel times can drift even if the kernel is using + the Xen clocksource, because ntp can warp the kernel's clocksource. +*/ +static s64 get_abs_timeout(unsigned long delta) +{ + return xen_clocksource_read() + delta; +} + +static void xen_timerop_set_mode(enum clock_event_mode mode, + struct clock_event_device *evt) +{ + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + /* unsupported */ + WARN_ON(1); + break; + + case CLOCK_EVT_MODE_ONESHOT: + break; + + case CLOCK_EVT_MODE_UNUSED: + case CLOCK_EVT_MODE_SHUTDOWN: + HYPERVISOR_set_timer_op(0); /* cancel timeout */ + break; + } +} + +static int xen_timerop_set_next_event(unsigned long delta, + struct clock_event_device *evt) +{ + WARN_ON(evt->mode != CLOCK_EVT_MODE_ONESHOT); + + if (HYPERVISOR_set_timer_op(get_abs_timeout(delta)) < 0) + BUG(); + + /* We may have missed the deadline, but there's no real way of + knowing for sure. If the event was in the past, then we'll + get an immediate interrupt. */ + + return 0; +} + +static const struct clock_event_device xen_timerop_clockevent = { + .name = "xen", + .features = CLOCK_EVT_FEAT_ONESHOT, + + .max_delta_ns = 0xffffffff, + .min_delta_ns = TIMER_SLOP, + + .mult = 1, + .shift = 0, + .rating = 500, + + .set_mode = xen_timerop_set_mode, + .set_next_event = xen_timerop_set_next_event, +}; + + + +static void xen_vcpuop_set_mode(enum clock_event_mode mode, + struct clock_event_device *evt) +{ + int cpu = smp_processor_id(); + + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + WARN_ON(1); /* unsupported */ + break; + + case CLOCK_EVT_MODE_ONESHOT: + if (HYPERVISOR_vcpu_op(VCPUOP_stop_periodic_timer, cpu, NULL)) + BUG(); + break; + + case CLOCK_EVT_MODE_UNUSED: + case CLOCK_EVT_MODE_SHUTDOWN: + if (HYPERVISOR_vcpu_op(VCPUOP_stop_singleshot_timer, cpu, NULL) || + HYPERVISOR_vcpu_op(VCPUOP_stop_periodic_timer, cpu, NULL)) + BUG(); + break; + } +} + +static int xen_vcpuop_set_next_event(unsigned long delta, + struct clock_event_device *evt) +{ + int cpu = smp_processor_id(); + struct vcpu_set_singleshot_timer single; + int ret; + + WARN_ON(evt->mode != CLOCK_EVT_MODE_ONESHOT); + + single.timeout_abs_ns = get_abs_timeout(delta); + single.flags = VCPU_SSHOTTMR_future; + + ret = HYPERVISOR_vcpu_op(VCPUOP_set_singleshot_timer, cpu, &single); + + BUG_ON(ret != 0 && ret != -ETIME); + + return ret; +} + +static const struct clock_event_device xen_vcpuop_clockevent = { + .name = "xen", + .features = CLOCK_EVT_FEAT_ONESHOT, + + .max_delta_ns = 0xffffffff, + .min_delta_ns = TIMER_SLOP, + + .mult = 1, + .shift = 0, + .rating = 500, + + .set_mode = xen_vcpuop_set_mode, + .set_next_event = xen_vcpuop_set_next_event, +}; + +static const struct clock_event_device *xen_clockevent = + &xen_timerop_clockevent; +static DEFINE_PER_CPU(struct clock_event_device, xen_clock_events); + +static irqreturn_t xen_timer_interrupt(int irq, void *dev_id) +{ + struct clock_event_device *evt = &__get_cpu_var(xen_clock_events); + irqreturn_t ret; + + ret = IRQ_NONE; + if (evt->event_handler) { + evt->event_handler(evt); + ret = IRQ_HANDLED; + } + + do_stolen_accounting(); + + return ret; +} + +void xen_setup_timer(int cpu) +{ + const char *name; + struct clock_event_device *evt; + int irq; + + printk(KERN_INFO "installing Xen timer for CPU %d\n", cpu); + + name = kasprintf(GFP_KERNEL, "timer%d", cpu); + if (!name) + name = "<timer kasprintf failed>"; + + irq = bind_virq_to_irqhandler(VIRQ_TIMER, cpu, xen_timer_interrupt, + IRQF_DISABLED|IRQF_PERCPU|IRQF_NOBALANCING, + name, NULL); + + evt = &per_cpu(xen_clock_events, cpu); + memcpy(evt, xen_clockevent, sizeof(*evt)); + + evt->cpumask = cpumask_of_cpu(cpu); + evt->irq = irq; + + setup_runstate_info(cpu); +} + +void xen_setup_cpu_clockevents(void) +{ + BUG_ON(preemptible()); + + clockevents_register_device(&__get_cpu_var(xen_clock_events)); +} + +__init void xen_time_init(void) +{ + int cpu = smp_processor_id(); + + get_time_values_from_xen(); + + clocksource_register(&xen_clocksource); + + if (HYPERVISOR_vcpu_op(VCPUOP_stop_periodic_timer, cpu, NULL) == 0) { + /* Successfully turned off 100Hz tick, so we have the + vcpuop-based timer interface */ + printk(KERN_DEBUG "Xen: using vcpuop timer interface\n"); + xen_clockevent = &xen_vcpuop_clockevent; + } + + /* Set initial system time with full resolution */ + xen_read_wallclock(&xtime); + set_normalized_timespec(&wall_to_monotonic, + -xtime.tv_sec, -xtime.tv_nsec); + + tsc_disable = 0; + + xen_setup_timer(cpu); + xen_setup_cpu_clockevents(); +} diff --git a/arch/i386/xen/xen-asm.S b/arch/i386/xen/xen-asm.S new file mode 100644 index 00000000000..1a43b60c0c6 --- /dev/null +++ b/arch/i386/xen/xen-asm.S @@ -0,0 +1,291 @@ +/* + Asm versions of Xen pv-ops, suitable for either direct use or inlining. + The inline versions are the same as the direct-use versions, with the + pre- and post-amble chopped off. + + This code is encoded for size rather than absolute efficiency, + with a view to being able to inline as much as possible. + + We only bother with direct forms (ie, vcpu in pda) of the operations + here; the indirect forms are better handled in C, since they're + generally too large to inline anyway. + */ + +#include <linux/linkage.h> + +#include <asm/asm-offsets.h> +#include <asm/thread_info.h> +#include <asm/percpu.h> +#include <asm/processor-flags.h> +#include <asm/segment.h> + +#include <xen/interface/xen.h> + +#define RELOC(x, v) .globl x##_reloc; x##_reloc=v +#define ENDPATCH(x) .globl x##_end; x##_end=. + +/* Pseudo-flag used for virtual NMI, which we don't implement yet */ +#define XEN_EFLAGS_NMI 0x80000000 + +/* + Enable events. This clears the event mask and tests the pending + event status with one and operation. If there are pending + events, then enter the hypervisor to get them handled. + */ +ENTRY(xen_irq_enable_direct) + /* Clear mask and test pending */ + andw $0x00ff, PER_CPU_VAR(xen_vcpu_info)+XEN_vcpu_info_pending + /* Preempt here doesn't matter because that will deal with + any pending interrupts. The pending check may end up being + run on the wrong CPU, but that doesn't hurt. */ + jz 1f +2: call check_events +1: +ENDPATCH(xen_irq_enable_direct) + ret + ENDPROC(xen_irq_enable_direct) + RELOC(xen_irq_enable_direct, 2b+1) + + +/* + Disabling events is simply a matter of making the event mask + non-zero. + */ +ENTRY(xen_irq_disable_direct) + movb $1, PER_CPU_VAR(xen_vcpu_info)+XEN_vcpu_info_mask +ENDPATCH(xen_irq_disable_direct) + ret + ENDPROC(xen_irq_disable_direct) + RELOC(xen_irq_disable_direct, 0) + +/* + (xen_)save_fl is used to get the current interrupt enable status. + Callers expect the status to be in X86_EFLAGS_IF, and other bits + may be set in the return value. We take advantage of this by + making sure that X86_EFLAGS_IF has the right value (and other bits + in that byte are 0), but other bits in the return value are + undefined. We need to toggle the state of the bit, because + Xen and x86 use opposite senses (mask vs enable). + */ +ENTRY(xen_save_fl_direct) + testb $0xff, PER_CPU_VAR(xen_vcpu_info)+XEN_vcpu_info_mask + setz %ah + addb %ah,%ah +ENDPATCH(xen_save_fl_direct) + ret + ENDPROC(xen_save_fl_direct) + RELOC(xen_save_fl_direct, 0) + + +/* + In principle the caller should be passing us a value return + from xen_save_fl_direct, but for robustness sake we test only + the X86_EFLAGS_IF flag rather than the whole byte. After + setting the interrupt mask state, it checks for unmasked + pending events and enters the hypervisor to get them delivered + if so. + */ +ENTRY(xen_restore_fl_direct) + testb $X86_EFLAGS_IF>>8, %ah + setz PER_CPU_VAR(xen_vcpu_info)+XEN_vcpu_info_mask + /* Preempt here doesn't matter because that will deal with + any pending interrupts. The pending check may end up being + run on the wrong CPU, but that doesn't hurt. */ + + /* check for unmasked and pending */ + cmpw $0x0001, PER_CPU_VAR(xen_vcpu_info)+XEN_vcpu_info_pending + jz 1f +2: call check_events +1: +ENDPATCH(xen_restore_fl_direct) + ret + ENDPROC(xen_restore_fl_direct) + RELOC(xen_restore_fl_direct, 2b+1) + +/* + This is run where a normal iret would be run, with the same stack setup: + 8: eflags + 4: cs + esp-> 0: eip + + This attempts to make sure that any pending events are dealt + with on return to usermode, but there is a small window in + which an event can happen just before entering usermode. If + the nested interrupt ends up setting one of the TIF_WORK_MASK + pending work flags, they will not be tested again before + returning to usermode. This means that a process can end up + with pending work, which will be unprocessed until the process + enters and leaves the kernel again, which could be an + unbounded amount of time. This means that a pending signal or + reschedule event could be indefinitely delayed. + + The fix is to notice a nested interrupt in the critical + window, and if one occurs, then fold the nested interrupt into + the current interrupt stack frame, and re-process it + iteratively rather than recursively. This means that it will + exit via the normal path, and all pending work will be dealt + with appropriately. + + Because the nested interrupt handler needs to deal with the + current stack state in whatever form its in, we keep things + simple by only using a single register which is pushed/popped + on the stack. + + Non-direct iret could be done in the same way, but it would + require an annoying amount of code duplication. We'll assume + that direct mode will be the common case once the hypervisor + support becomes commonplace. + */ +ENTRY(xen_iret_direct) + /* test eflags for special cases */ + testl $(X86_EFLAGS_VM | XEN_EFLAGS_NMI), 8(%esp) + jnz hyper_iret + + push %eax + ESP_OFFSET=4 # bytes pushed onto stack + + /* Store vcpu_info pointer for easy access. Do it this + way to avoid having to reload %fs */ +#ifdef CONFIG_SMP + GET_THREAD_INFO(%eax) + movl TI_cpu(%eax),%eax + movl __per_cpu_offset(,%eax,4),%eax + lea per_cpu__xen_vcpu_info(%eax),%eax +#else + movl $per_cpu__xen_vcpu_info, %eax +#endif + + /* check IF state we're restoring */ + testb $X86_EFLAGS_IF>>8, 8+1+ESP_OFFSET(%esp) + + /* Maybe enable events. Once this happens we could get a + recursive event, so the critical region starts immediately + afterwards. However, if that happens we don't end up + resuming the code, so we don't have to be worried about + being preempted to another CPU. */ + setz XEN_vcpu_info_mask(%eax) +xen_iret_start_crit: + + /* check for unmasked and pending */ + cmpw $0x0001, XEN_vcpu_info_pending(%eax) + + /* If there's something pending, mask events again so we + can jump back into xen_hypervisor_callback */ + sete XEN_vcpu_info_mask(%eax) + + popl %eax + + /* From this point on the registers are restored and the stack + updated, so we don't need to worry about it if we're preempted */ +iret_restore_end: + + /* Jump to hypervisor_callback after fixing up the stack. + Events are masked, so jumping out of the critical + region is OK. */ + je xen_hypervisor_callback + + iret +xen_iret_end_crit: + +hyper_iret: + /* put this out of line since its very rarely used */ + jmp hypercall_page + __HYPERVISOR_iret * 32 + + .globl xen_iret_start_crit, xen_iret_end_crit + +/* + This is called by xen_hypervisor_callback in entry.S when it sees + that the EIP at the time of interrupt was between xen_iret_start_crit + and xen_iret_end_crit. We're passed the EIP in %eax so we can do + a more refined determination of what to do. + + The stack format at this point is: + ---------------- + ss : (ss/esp may be present if we came from usermode) + esp : + eflags } outer exception info + cs } + eip } + ---------------- <- edi (copy dest) + eax : outer eax if it hasn't been restored + ---------------- + eflags } nested exception info + cs } (no ss/esp because we're nested + eip } from the same ring) + orig_eax }<- esi (copy src) + - - - - - - - - + fs } + es } + ds } SAVE_ALL state + eax } + : : + ebx } + ---------------- + return addr <- esp + ---------------- + + In order to deliver the nested exception properly, we need to shift + everything from the return addr up to the error code so it + sits just under the outer exception info. This means that when we + handle the exception, we do it in the context of the outer exception + rather than starting a new one. + + The only caveat is that if the outer eax hasn't been + restored yet (ie, it's still on stack), we need to insert + its value into the SAVE_ALL state before going on, since + it's usermode state which we eventually need to restore. + */ +ENTRY(xen_iret_crit_fixup) + /* offsets +4 for return address */ + + /* + Paranoia: Make sure we're really coming from userspace. + One could imagine a case where userspace jumps into the + critical range address, but just before the CPU delivers a GP, + it decides to deliver an interrupt instead. Unlikely? + Definitely. Easy to avoid? Yes. The Intel documents + explicitly say that the reported EIP for a bad jump is the + jump instruction itself, not the destination, but some virtual + environments get this wrong. + */ + movl PT_CS+4(%esp), %ecx + andl $SEGMENT_RPL_MASK, %ecx + cmpl $USER_RPL, %ecx + je 2f + + lea PT_ORIG_EAX+4(%esp), %esi + lea PT_EFLAGS+4(%esp), %edi + + /* If eip is before iret_restore_end then stack + hasn't been restored yet. */ + cmp $iret_restore_end, %eax + jae 1f + + movl 0+4(%edi),%eax /* copy EAX */ + movl %eax, PT_EAX+4(%esp) + + lea ESP_OFFSET(%edi),%edi /* move dest up over saved regs */ + + /* set up the copy */ +1: std + mov $(PT_EIP+4) / 4, %ecx /* copy ret+saved regs up to orig_eax */ + rep movsl + cld + + lea 4(%edi),%esp /* point esp to new frame */ +2: ret + + +/* + Force an event check by making a hypercall, + but preserve regs before making the call. + */ +check_events: + push %eax + push %ecx + push %edx + call force_evtchn_callback + pop %edx + pop %ecx + pop %eax + ret diff --git a/arch/i386/xen/xen-head.S b/arch/i386/xen/xen-head.S new file mode 100644 index 00000000000..2998d55a001 --- /dev/null +++ b/arch/i386/xen/xen-head.S @@ -0,0 +1,36 @@ +/* Xen-specific pieces of head.S, intended to be included in the right + place in head.S */ + +#ifdef CONFIG_XEN + +#include <linux/elfnote.h> +#include <asm/boot.h> +#include <xen/interface/elfnote.h> + +ENTRY(startup_xen) + movl %esi,xen_start_info + cld + movl $(init_thread_union+THREAD_SIZE),%esp + jmp xen_start_kernel + +.pushsection ".bss.page_aligned" + .align PAGE_SIZE_asm +ENTRY(hypercall_page) + .skip 0x1000 +.popsection + + ELFNOTE(Xen, XEN_ELFNOTE_GUEST_OS, .asciz "linux") + ELFNOTE(Xen, XEN_ELFNOTE_GUEST_VERSION, .asciz "2.6") + ELFNOTE(Xen, XEN_ELFNOTE_XEN_VERSION, .asciz "xen-3.0") + ELFNOTE(Xen, XEN_ELFNOTE_VIRT_BASE, .long __PAGE_OFFSET) + ELFNOTE(Xen, XEN_ELFNOTE_ENTRY, .long startup_xen) + ELFNOTE(Xen, XEN_ELFNOTE_HYPERCALL_PAGE, .long hypercall_page) + ELFNOTE(Xen, XEN_ELFNOTE_FEATURES, .asciz "!writable_page_tables|pae_pgdir_above_4gb") +#ifdef CONFIG_X86_PAE + ELFNOTE(Xen, XEN_ELFNOTE_PAE_MODE, .asciz "yes") +#else + ELFNOTE(Xen, XEN_ELFNOTE_PAE_MODE, .asciz "no") +#endif + ELFNOTE(Xen, XEN_ELFNOTE_LOADER, .asciz "generic") + +#endif /*CONFIG_XEN */ diff --git a/arch/i386/xen/xen-ops.h b/arch/i386/xen/xen-ops.h new file mode 100644 index 00000000000..b9aaea45f07 --- /dev/null +++ b/arch/i386/xen/xen-ops.h @@ -0,0 +1,71 @@ +#ifndef XEN_OPS_H +#define XEN_OPS_H + +#include <linux/init.h> + +/* These are code, but not functions. Defined in entry.S */ +extern const char xen_hypervisor_callback[]; +extern const char xen_failsafe_callback[]; + +void xen_copy_trap_info(struct trap_info *traps); + +DECLARE_PER_CPU(struct vcpu_info *, xen_vcpu); +DECLARE_PER_CPU(unsigned long, xen_cr3); + +extern struct start_info *xen_start_info; +extern struct shared_info *HYPERVISOR_shared_info; + +char * __init xen_memory_setup(void); +void __init xen_arch_setup(void); +void __init xen_init_IRQ(void); + +void xen_setup_timer(int cpu); +void xen_setup_cpu_clockevents(void); +unsigned long xen_cpu_khz(void); +void __init xen_time_init(void); +unsigned long xen_get_wallclock(void); +int xen_set_wallclock(unsigned long time); +unsigned long long xen_sched_clock(void); + +void xen_mark_init_mm_pinned(void); + +DECLARE_PER_CPU(enum paravirt_lazy_mode, xen_lazy_mode); + +static inline unsigned xen_get_lazy_mode(void) +{ + return x86_read_percpu(xen_lazy_mode); +} + +void __init xen_fill_possible_map(void); + +void __init xen_setup_vcpu_info_placement(void); +void xen_smp_prepare_boot_cpu(void); +void xen_smp_prepare_cpus(unsigned int max_cpus); +int xen_cpu_up(unsigned int cpu); +void xen_smp_cpus_done(unsigned int max_cpus); + +void xen_smp_send_stop(void); +void xen_smp_send_reschedule(int cpu); +int xen_smp_call_function (void (*func) (void *info), void *info, int nonatomic, + int wait); +int xen_smp_call_function_single(int cpu, void (*func) (void *info), void *info, + int nonatomic, int wait); + +int xen_smp_call_function_mask(cpumask_t mask, void (*func)(void *), + void *info, int wait); + + +/* Declare an asm function, along with symbols needed to make it + inlineable */ +#define DECL_ASM(ret, name, ...) \ + ret name(__VA_ARGS__); \ + extern char name##_end[]; \ + extern char name##_reloc[] \ + +DECL_ASM(void, xen_irq_enable_direct, void); +DECL_ASM(void, xen_irq_disable_direct, void); +DECL_ASM(unsigned long, xen_save_fl_direct, void); +DECL_ASM(void, xen_restore_fl_direct, unsigned long); + +void xen_iret_direct(void); +#endif /* XEN_OPS_H */ diff --git a/arch/ia64/Kconfig b/arch/ia64/Kconfig index db9ddff9584..616c96e7348 100644 --- a/arch/ia64/Kconfig +++ b/arch/ia64/Kconfig @@ -582,8 +582,8 @@ menu "Instrumentation Support" source "arch/ia64/oprofile/Kconfig" config KPROBES - bool "Kprobes (EXPERIMENTAL)" - depends on KALLSYMS && EXPERIMENTAL && MODULES + bool "Kprobes" + depends on KALLSYMS && MODULES help Kprobes allows you to trap at almost any kernel address and execute a callback function. register_kprobe() establishes diff --git a/arch/ia64/hp/common/sba_iommu.c b/arch/ia64/hp/common/sba_iommu.c index c1dca226b47..cd4adf52f17 100644 --- a/arch/ia64/hp/common/sba_iommu.c +++ b/arch/ia64/hp/common/sba_iommu.c @@ -34,6 +34,7 @@ #include <linux/efi.h> #include <linux/nodemask.h> #include <linux/bitops.h> /* hweight64() */ +#include <linux/crash_dump.h> #include <asm/delay.h> /* ia64_get_itc() */ #include <asm/io.h> @@ -43,6 +44,8 @@ #include <asm/acpi-ext.h> +extern int swiotlb_late_init_with_default_size (size_t size); + #define PFX "IOC: " /* @@ -2026,11 +2029,24 @@ sba_init(void) if (!ia64_platform_is("hpzx1") && !ia64_platform_is("hpzx1_swiotlb")) return 0; +#if defined(CONFIG_IA64_GENERIC) && defined(CONFIG_CRASH_DUMP) + /* If we are booting a kdump kernel, the sba_iommu will + * cause devices that were not shutdown properly to MCA + * as soon as they are turned back on. Our only option for + * a successful kdump kernel boot is to use the swiotlb. + */ + if (elfcorehdr_addr < ELFCORE_ADDR_MAX) { + if (swiotlb_late_init_with_default_size(64 * (1<<20)) != 0) + panic("Unable to initialize software I/O TLB:" + " Try machvec=dig boot option"); + machvec_init("dig"); + return 0; + } +#endif + acpi_bus_register_driver(&acpi_sba_ioc_driver); if (!ioc_list) { #ifdef CONFIG_IA64_GENERIC - extern int swiotlb_late_init_with_default_size (size_t size); - /* * If we didn't find something sba_iommu can claim, we * need to setup the swiotlb and switch to the dig machvec. diff --git a/arch/ia64/hp/sim/boot/fw-emu.c b/arch/ia64/hp/sim/boot/fw-emu.c index 300acd913d9..1189d035d31 100644 --- a/arch/ia64/hp/sim/boot/fw-emu.c +++ b/arch/ia64/hp/sim/boot/fw-emu.c @@ -329,11 +329,6 @@ sys_fw_init (const char *args, int arglen) strcpy(sal_systab->product_id, "HP-simulator"); #endif -#ifdef CONFIG_IA64_SDV - strcpy(sal_systab->oem_id, "Intel"); - strcpy(sal_systab->product_id, "SDV"); -#endif - /* fill in an entry point: */ sal_ed->type = SAL_DESC_ENTRY_POINT; sal_ed->pal_proc = __pa(pal_desc[0]); diff --git a/arch/ia64/hp/sim/simserial.c b/arch/ia64/hp/sim/simserial.c index 324ea7565e2..ef252df50e1 100644 --- a/arch/ia64/hp/sim/simserial.c +++ b/arch/ia64/hp/sim/simserial.c @@ -36,10 +36,6 @@ #include <asm/hw_irq.h> #include <asm/uaccess.h> -#ifdef CONFIG_KDB -# include <linux/kdb.h> -#endif - #undef SIMSERIAL_DEBUG /* define this to get some debug information */ #define KEYBOARD_INTR 3 /* must match with simulator! */ diff --git a/arch/ia64/ia32/binfmt_elf32.c b/arch/ia64/ia32/binfmt_elf32.c index 6f4d3d06f0e..e1189ba1ca5 100644 --- a/arch/ia64/ia32/binfmt_elf32.c +++ b/arch/ia64/ia32/binfmt_elf32.c @@ -195,62 +195,27 @@ ia64_elf32_init (struct pt_regs *regs) ia32_load_state(current); } +/* + * Undo the override of setup_arg_pages() without this ia32_setup_arg_pages() + * will suffer infinite self recursion. + */ +#undef setup_arg_pages + int ia32_setup_arg_pages (struct linux_binprm *bprm, int executable_stack) { - unsigned long stack_base; - struct vm_area_struct *mpnt; - struct mm_struct *mm = current->mm; - int i, ret; - - stack_base = IA32_STACK_TOP - MAX_ARG_PAGES*PAGE_SIZE; - mm->arg_start = bprm->p + stack_base; - - bprm->p += stack_base; - if (bprm->loader) - bprm->loader += stack_base; - bprm->exec += stack_base; - - mpnt = kmem_cache_zalloc(vm_area_cachep, GFP_KERNEL); - if (!mpnt) - return -ENOMEM; - - down_write(¤t->mm->mmap_sem); - { - mpnt->vm_mm = current->mm; - mpnt->vm_start = PAGE_MASK & (unsigned long) bprm->p; - mpnt->vm_end = IA32_STACK_TOP; - if (executable_stack == EXSTACK_ENABLE_X) - mpnt->vm_flags = VM_STACK_FLAGS | VM_EXEC; - else if (executable_stack == EXSTACK_DISABLE_X) - mpnt->vm_flags = VM_STACK_FLAGS & ~VM_EXEC; - else - mpnt->vm_flags = VM_STACK_FLAGS; - mpnt->vm_page_prot = (mpnt->vm_flags & VM_EXEC)? - PAGE_COPY_EXEC: PAGE_COPY; - if ((ret = insert_vm_struct(current->mm, mpnt))) { - up_write(¤t->mm->mmap_sem); - kmem_cache_free(vm_area_cachep, mpnt); - return ret; - } - current->mm->stack_vm = current->mm->total_vm = vma_pages(mpnt); + int ret; + + ret = setup_arg_pages(bprm, IA32_STACK_TOP, executable_stack); + if (!ret) { + /* + * Can't do it in ia64_elf32_init(). Needs to be done before + * calls to elf32_map() + */ + current->thread.ppl = ia32_init_pp_list(); } - for (i = 0 ; i < MAX_ARG_PAGES ; i++) { - struct page *page = bprm->page[i]; - if (page) { - bprm->page[i] = NULL; - install_arg_page(mpnt, page, stack_base); - } - stack_base += PAGE_SIZE; - } - up_write(¤t->mm->mmap_sem); - - /* Can't do it in ia64_elf32_init(). Needs to be done before calls to - elf32_map() */ - current->thread.ppl = ia32_init_pp_list(); - - return 0; + return ret; } static void diff --git a/arch/ia64/kernel/efi.c b/arch/ia64/kernel/efi.c index 75ec3478d8a..73ca86d0381 100644 --- a/arch/ia64/kernel/efi.c +++ b/arch/ia64/kernel/efi.c @@ -28,6 +28,7 @@ #include <linux/time.h> #include <linux/efi.h> #include <linux/kexec.h> +#include <linux/mm.h> #include <asm/io.h> #include <asm/kregs.h> diff --git a/arch/ia64/kernel/fsys.S b/arch/ia64/kernel/fsys.S index 8589e84a27c..3f926c2dc70 100644 --- a/arch/ia64/kernel/fsys.S +++ b/arch/ia64/kernel/fsys.S @@ -247,6 +247,9 @@ ENTRY(fsys_gettimeofday) .time_redo: .pred.rel.mutex p8,p9,p10 ld4.acq r28 = [r29] // xtime_lock.sequence. Must come first for locking purposes + ;; + and r28 = ~1,r28 // Make sequence even to force retry if odd + ;; (p8) mov r2 = ar.itc // CPU_TIMER. 36 clocks latency!!! add r22 = IA64_TIME_INTERPOLATOR_LAST_COUNTER_OFFSET,r20 (p9) ld8 r2 = [r30] // readq(ti->address). Could also have latency issues.. @@ -284,7 +287,6 @@ EX(.fail_efault, probe.w.fault r31, 3) // This takes 5 cycles and we have spare (p15) ld8 r17 = [r19],-IA64_TIMESPEC_TV_NSEC_OFFSET (p7) cmp.ne p7,p0 = r25,r3 // if cmpxchg not successful redo // simulate tbit.nz.or p7,p0 = r28,0 - and r28 = ~1,r28 // Make sequence even to force retry if odd getf.sig r2 = f8 mf add r8 = r8,r18 // Add time interpolator offset diff --git a/arch/ia64/kernel/kprobes.c b/arch/ia64/kernel/kprobes.c index 5bc46f15134..5dc98b5abcf 100644 --- a/arch/ia64/kernel/kprobes.c +++ b/arch/ia64/kernel/kprobes.c @@ -936,10 +936,15 @@ static void ia64_get_bsp_cfm(struct unw_frame_info *info, void *arg) return; } +unsigned long arch_deref_entry_point(void *entry) +{ + return ((struct fnptr *)entry)->ip; +} + int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs) { struct jprobe *jp = container_of(p, struct jprobe, kp); - unsigned long addr = ((struct fnptr *)(jp->entry))->ip; + unsigned long addr = arch_deref_entry_point(jp->entry); struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); struct param_bsp_cfm pa; int bytes; diff --git a/arch/ia64/kernel/setup.c b/arch/ia64/kernel/setup.c index 4d9864cc92c..cf06fe79904 100644 --- a/arch/ia64/kernel/setup.c +++ b/arch/ia64/kernel/setup.c @@ -980,15 +980,6 @@ cpu_init (void) pm_idle = default_idle; } -/* - * On SMP systems, when the scheduler does migration-cost autodetection, - * it needs a way to flush as much of the CPU's caches as possible. - */ -void sched_cacheflush(void) -{ - ia64_sal_cache_flush(3); -} - void __init check_bugs (void) { diff --git a/arch/ia64/kernel/smp.c b/arch/ia64/kernel/smp.c index b3a47f986e1..9f72838db26 100644 --- a/arch/ia64/kernel/smp.c +++ b/arch/ia64/kernel/smp.c @@ -82,7 +82,7 @@ static volatile struct call_data_struct *call_data; #define IPI_KDUMP_CPU_STOP 3 /* This needs to be cacheline aligned because it is written to by *other* CPUs. */ -static DEFINE_PER_CPU(u64, ipi_operation) ____cacheline_aligned; +static DEFINE_PER_CPU_SHARED_ALIGNED(u64, ipi_operation); extern void cpu_halt (void); diff --git a/arch/ia64/kernel/traps.c b/arch/ia64/kernel/traps.c index 15ad85da15a..3aeaf15e468 100644 --- a/arch/ia64/kernel/traps.c +++ b/arch/ia64/kernel/traps.c @@ -69,6 +69,7 @@ die (const char *str, struct pt_regs *regs, long err) bust_spinlocks(0); die.lock_owner = -1; + add_taint(TAINT_DIE); spin_unlock_irq(&die.lock); if (panic_on_oops) diff --git a/arch/ia64/kernel/vmlinux.lds.S b/arch/ia64/kernel/vmlinux.lds.S index 5a65965c8b5..860f251d2fc 100644 --- a/arch/ia64/kernel/vmlinux.lds.S +++ b/arch/ia64/kernel/vmlinux.lds.S @@ -206,6 +206,7 @@ SECTIONS { __per_cpu_start = .; *(.data.percpu) + *(.data.percpu.shared_aligned) __per_cpu_end = .; } . = __phys_per_cpu_start + PERCPU_PAGE_SIZE; /* ensure percpu data fits diff --git a/arch/ia64/lib/checksum.c b/arch/ia64/lib/checksum.c index 4411d9baeb2..9fc955026f8 100644 --- a/arch/ia64/lib/checksum.c +++ b/arch/ia64/lib/checksum.c @@ -60,6 +60,7 @@ csum_tcpudp_nofold (__be32 saddr, __be32 daddr, unsigned short len, result = (result & 0xffffffff) + (result >> 32); return (__force __wsum)result; } +EXPORT_SYMBOL(csum_tcpudp_nofold); extern unsigned long do_csum (const unsigned char *, long); diff --git a/arch/ia64/mm/fault.c b/arch/ia64/mm/fault.c index b87f785c241..73ccb6010c0 100644 --- a/arch/ia64/mm/fault.c +++ b/arch/ia64/mm/fault.c @@ -80,6 +80,7 @@ ia64_do_page_fault (unsigned long address, unsigned long isr, struct pt_regs *re struct mm_struct *mm = current->mm; struct siginfo si; unsigned long mask; + int fault; /* mmap_sem is performance critical.... */ prefetchw(&mm->mmap_sem); @@ -147,26 +148,25 @@ ia64_do_page_fault (unsigned long address, unsigned long isr, struct pt_regs *re * sure we exit gracefully rather than endlessly redo the * fault. */ - switch (handle_mm_fault(mm, vma, address, (mask & VM_WRITE) != 0)) { - case VM_FAULT_MINOR: - ++current->min_flt; - break; - case VM_FAULT_MAJOR: - ++current->maj_flt; - break; - case VM_FAULT_SIGBUS: + fault = handle_mm_fault(mm, vma, address, (mask & VM_WRITE) != 0); + if (unlikely(fault & VM_FAULT_ERROR)) { /* * We ran out of memory, or some other thing happened * to us that made us unable to handle the page fault * gracefully. */ - signal = SIGBUS; - goto bad_area; - case VM_FAULT_OOM: - goto out_of_memory; - default: + if (fault & VM_FAULT_OOM) { + goto out_of_memory; + } else if (fault & VM_FAULT_SIGBUS) { + signal = SIGBUS; + goto bad_area; + } BUG(); } + if (fault & VM_FAULT_MAJOR) + current->maj_flt++; + else + current->min_flt++; up_read(&mm->mmap_sem); return; diff --git a/arch/ia64/sn/kernel/sn2/sn_hwperf.c b/arch/ia64/sn/kernel/sn2/sn_hwperf.c index 6da9854751c..df8d5bed611 100644 --- a/arch/ia64/sn/kernel/sn2/sn_hwperf.c +++ b/arch/ia64/sn/kernel/sn2/sn_hwperf.c @@ -750,9 +750,10 @@ sn_hwperf_ioctl(struct inode *in, struct file *fp, u32 op, u64 arg) goto error; } else if ((r = sn_hwperf_enum_objects(&nobj, &objs)) == 0) { + int cpuobj_index = 0; + memset(p, 0, a.sz); for (i = 0; i < nobj; i++) { - int cpuobj_index = 0; if (!SN_HWPERF_IS_NODE(objs + i)) continue; node = sn_hwperf_obj_to_cnode(objs + i); diff --git a/arch/m32r/kernel/ptrace.c b/arch/m32r/kernel/ptrace.c index 5f02b314487..57a92ef31a9 100644 --- a/arch/m32r/kernel/ptrace.c +++ b/arch/m32r/kernel/ptrace.c @@ -595,7 +595,6 @@ void ptrace_disable(struct task_struct *child) static int do_ptrace(long request, struct task_struct *child, long addr, long data) { - unsigned long tmp; int ret; switch (request) { @@ -604,11 +603,7 @@ do_ptrace(long request, struct task_struct *child, long addr, long data) */ case PTRACE_PEEKTEXT: case PTRACE_PEEKDATA: - ret = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); - if (ret == sizeof(tmp)) - ret = put_user(tmp,(unsigned long __user *) data); - else - ret = -EIO; + ret = generic_ptrace_peekdata(child, addr, data); break; /* @@ -624,15 +619,9 @@ do_ptrace(long request, struct task_struct *child, long addr, long data) */ case PTRACE_POKETEXT: case PTRACE_POKEDATA: - ret = access_process_vm(child, addr, &data, sizeof(data), 1); - if (ret == sizeof(data)) { - ret = 0; - if (request == PTRACE_POKETEXT) { - invalidate_cache(); - } - } else { - ret = -EIO; - } + ret = generic_ptrace_pokedata(child, addr, data); + if (ret == 0 && request == PTRACE_POKETEXT) + invalidate_cache(); break; /* diff --git a/arch/m32r/kernel/vmlinux.lds.S b/arch/m32r/kernel/vmlinux.lds.S index 4e2d5b9f0a9..942a8c7a441 100644 --- a/arch/m32r/kernel/vmlinux.lds.S +++ b/arch/m32r/kernel/vmlinux.lds.S @@ -110,10 +110,7 @@ SECTIONS __initramfs_end = .; #endif - . = ALIGN(4096); - __per_cpu_start = .; - .data.percpu : { *(.data.percpu) } - __per_cpu_end = .; + PERCPU(4096) . = ALIGN(4096); __init_end = .; /* freed after init ends here */ diff --git a/arch/m32r/m32104ut/defconfig.m32104ut b/arch/m32r/m32104ut/defconfig.m32104ut index 7b68fe8d921..1f88f493a9e 100644 --- a/arch/m32r/m32104ut/defconfig.m32104ut +++ b/arch/m32r/m32104ut/defconfig.m32104ut @@ -699,7 +699,6 @@ CONFIG_I2C_ALGOPCF=m # I2C Hardware Bus support # CONFIG_I2C_ELEKTOR=m -CONFIG_I2C_ISA=m # CONFIG_I2C_OCORES is not set # CONFIG_I2C_PARPORT is not set # CONFIG_I2C_PARPORT_LIGHT is not set diff --git a/arch/m32r/mm/fault.c b/arch/m32r/mm/fault.c index f3935ba2494..676a1c443d2 100644 --- a/arch/m32r/mm/fault.c +++ b/arch/m32r/mm/fault.c @@ -80,6 +80,7 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code, struct vm_area_struct * vma; unsigned long page, addr; int write; + int fault; siginfo_t info; /* @@ -195,20 +196,18 @@ survive: */ addr = (address & PAGE_MASK); set_thread_fault_code(error_code); - switch (handle_mm_fault(mm, vma, addr, write)) { - case VM_FAULT_MINOR: - tsk->min_flt++; - break; - case VM_FAULT_MAJOR: - tsk->maj_flt++; - break; - case VM_FAULT_SIGBUS: - goto do_sigbus; - case VM_FAULT_OOM: + fault = handle_mm_fault(mm, vma, addr, write); + if (unlikely(fault & VM_FAULT_ERROR)) { + if (fault & VM_FAULT_OOM) goto out_of_memory; - default: - BUG(); + else if (fault & VM_FAULT_SIGBUS) + goto do_sigbus; + BUG(); } + if (fault & VM_FAULT_MAJOR) + tsk->maj_flt++; + else + tsk->min_flt++; set_thread_fault_code(0); up_read(&mm->mmap_sem); return; diff --git a/arch/m68k/kernel/ptrace.c b/arch/m68k/kernel/ptrace.c index cdba9fd6d82..2cf0690b788 100644 --- a/arch/m68k/kernel/ptrace.c +++ b/arch/m68k/kernel/ptrace.c @@ -128,10 +128,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) /* when I and D space are separate, these will need to be fixed. */ case PTRACE_PEEKTEXT: /* read word at location addr. */ case PTRACE_PEEKDATA: - i = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); - if (i != sizeof(tmp)) - goto out_eio; - ret = put_user(tmp, (unsigned long *)data); + ret = generic_ptrace_peekdata(child, addr, data); break; /* read the word at location addr in the USER area. */ @@ -160,8 +157,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) /* when I and D space are separate, this will have to be fixed. */ case PTRACE_POKETEXT: /* write the word at location addr. */ case PTRACE_POKEDATA: - if (access_process_vm(child, addr, &data, sizeof(data), 1) != sizeof(data)) - goto out_eio; + ret = generic_ptrace_pokedata(child, addr, data); break; case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ diff --git a/arch/m68k/kernel/traps.c b/arch/m68k/kernel/traps.c index a27a4fa3329..4e2752a0e89 100644 --- a/arch/m68k/kernel/traps.c +++ b/arch/m68k/kernel/traps.c @@ -1170,6 +1170,7 @@ void die_if_kernel (char *str, struct pt_regs *fp, int nr) console_verbose(); printk("%s: %08x\n",str,nr); show_registers(fp); + add_taint(TAINT_DIE); do_exit(SIGSEGV); } diff --git a/arch/m68k/lib/checksum.c b/arch/m68k/lib/checksum.c index cf6bb51945a..6216f12a756 100644 --- a/arch/m68k/lib/checksum.c +++ b/arch/m68k/lib/checksum.c @@ -422,3 +422,4 @@ csum_partial_copy_nocheck(const void *src, void *dst, int len, __wsum sum) ); return(sum); } +EXPORT_SYMBOL(csum_partial_copy_nocheck); diff --git a/arch/m68k/mm/fault.c b/arch/m68k/mm/fault.c index 2adbeb16e1b..578b48f47b9 100644 --- a/arch/m68k/mm/fault.c +++ b/arch/m68k/mm/fault.c @@ -159,18 +159,17 @@ good_area: #ifdef DEBUG printk("handle_mm_fault returns %d\n",fault); #endif - switch (fault) { - case VM_FAULT_MINOR: - current->min_flt++; - break; - case VM_FAULT_MAJOR: - current->maj_flt++; - break; - case VM_FAULT_SIGBUS: - goto bus_err; - default: - goto out_of_memory; + if (unlikely(fault & VM_FAULT_ERROR)) { + if (fault & VM_FAULT_OOM) + goto out_of_memory; + else if (fault & VM_FAULT_SIGBUS) + goto bus_err; + BUG(); } + if (fault & VM_FAULT_MAJOR) + current->maj_flt++; + else + current->min_flt++; up_read(&mm->mmap_sem); return 0; diff --git a/arch/m68knommu/Kconfig b/arch/m68knommu/Kconfig index adc64a2bafb..1175ceff8b2 100644 --- a/arch/m68knommu/Kconfig +++ b/arch/m68knommu/Kconfig @@ -45,6 +45,10 @@ config GENERIC_HWEIGHT bool default y +config GENERIC_HARDIRQS + bool + default y + config GENERIC_CALIBRATE_DELAY bool default y diff --git a/arch/m68knommu/kernel/Makefile b/arch/m68knommu/kernel/Makefile index 1c6cd1ab571..1524b39ad63 100644 --- a/arch/m68knommu/kernel/Makefile +++ b/arch/m68knommu/kernel/Makefile @@ -4,8 +4,8 @@ extra-y := vmlinux.lds -obj-y += dma.o entry.o init_task.o m68k_ksyms.o process.o ptrace.o semaphore.o \ - setup.o signal.o syscalltable.o sys_m68k.o time.o traps.o +obj-y += dma.o entry.o init_task.o irq.o m68k_ksyms.o process.o ptrace.o \ + semaphore.o setup.o signal.o syscalltable.o sys_m68k.o time.o traps.o obj-$(CONFIG_MODULES) += module.o obj-$(CONFIG_COMEMPCI) += comempci.o diff --git a/arch/m68knommu/kernel/asm-offsets.c b/arch/m68knommu/kernel/asm-offsets.c index 7cd183d346e..d97b89bae53 100644 --- a/arch/m68knommu/kernel/asm-offsets.c +++ b/arch/m68knommu/kernel/asm-offsets.c @@ -15,7 +15,6 @@ #include <linux/hardirq.h> #include <asm/bootinfo.h> #include <asm/irq.h> -#include <asm/irqnode.h> #include <asm/thread_info.h> #define DEFINE(sym, val) \ @@ -72,10 +71,6 @@ int main(void) #else /* bitfields are a bit difficult */ DEFINE(PT_VECTOR, offsetof(struct pt_regs, pc) + 4); - /* offsets into the irq_handler struct */ - DEFINE(IRQ_HANDLER, offsetof(struct irq_node, handler)); - DEFINE(IRQ_DEVID, offsetof(struct irq_node, dev_id)); - DEFINE(IRQ_NEXT, offsetof(struct irq_node, next)); #endif /* offsets into the kernel_stat struct */ diff --git a/arch/m68knommu/kernel/irq.c b/arch/m68knommu/kernel/irq.c new file mode 100644 index 00000000000..bba1bb48a21 --- /dev/null +++ b/arch/m68knommu/kernel/irq.c @@ -0,0 +1,82 @@ +/* + * irq.c + * + * (C) Copyright 2007, Greg Ungerer <gerg@snapgear.com> + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + */ + +#include <linux/types.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/kernel_stat.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/seq_file.h> +#include <asm/system.h> +#include <asm/traps.h> + +asmlinkage void do_IRQ(int irq, struct pt_regs *regs) +{ + struct pt_regs *oldregs = set_irq_regs(regs); + + irq_enter(); + __do_IRQ(irq); + irq_exit(); + + set_irq_regs(oldregs); +} + +void ack_bad_irq(unsigned int irq) +{ + printk(KERN_ERR "IRQ: unexpected irq=%d\n", irq); +} + +static struct irq_chip m_irq_chip = { + .name = "M68K-INTC", + .enable = enable_vector, + .disable = disable_vector, + .ack = ack_vector, +}; + +void __init init_IRQ(void) +{ + int irq; + + init_vectors(); + + for (irq = 0; (irq < NR_IRQS); irq++) { + irq_desc[irq].status = IRQ_DISABLED; + irq_desc[irq].action = NULL; + irq_desc[irq].depth = 1; + irq_desc[irq].chip = &m_irq_chip; + } +} + +int show_interrupts(struct seq_file *p, void *v) +{ + struct irqaction *ap; + int irq = *((loff_t *) v); + + if (irq == 0) + seq_puts(p, " CPU0\n"); + + if (irq < NR_IRQS) { + ap = irq_desc[irq].action; + if (ap) { + seq_printf(p, "%3d: ", irq); + seq_printf(p, "%10u ", kstat_irqs(irq)); + seq_printf(p, "%14s ", irq_desc[irq].chip->name); + + seq_printf(p, "%s", ap->name); + for (ap = ap->next; ap; ap = ap->next) + seq_printf(p, ", %s", ap->name); + seq_putc(p, '\n'); + } + } + + return 0; +} + diff --git a/arch/m68knommu/kernel/m68k_ksyms.c b/arch/m68knommu/kernel/m68k_ksyms.c index 25327c9eadd..f795062aba1 100644 --- a/arch/m68knommu/kernel/m68k_ksyms.c +++ b/arch/m68knommu/kernel/m68k_ksyms.c @@ -81,8 +81,6 @@ EXPORT_SYMBOL(__mulsi3); EXPORT_SYMBOL(__udivsi3); EXPORT_SYMBOL(__umodsi3); -EXPORT_SYMBOL(is_in_rom); - #ifdef CONFIG_COLDFIRE extern unsigned int *dma_device_address; extern unsigned long dma_base_addr, _ramend; diff --git a/arch/m68knommu/kernel/ptrace.c b/arch/m68knommu/kernel/ptrace.c index f54b6a3dfec..ef70ca070ce 100644 --- a/arch/m68knommu/kernel/ptrace.c +++ b/arch/m68knommu/kernel/ptrace.c @@ -106,17 +106,9 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) switch (request) { /* when I and D space are separate, these will need to be fixed. */ case PTRACE_PEEKTEXT: /* read word at location addr. */ - case PTRACE_PEEKDATA: { - unsigned long tmp; - int copied; - - copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); - ret = -EIO; - if (copied != sizeof(tmp)) - break; - ret = put_user(tmp,(unsigned long *) data); + case PTRACE_PEEKDATA: + ret = generic_ptrace_peekdata(child, addr, data); break; - } /* read the word at location addr in the USER area. */ case PTRACE_PEEKUSR: { @@ -159,10 +151,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) /* when I and D space are separate, this will have to be fixed. */ case PTRACE_POKETEXT: /* write the word at location addr. */ case PTRACE_POKEDATA: - ret = 0; - if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data)) - break; - ret = -EIO; + ret = generic_ptrace_pokedata(child, addr, data); break; case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ diff --git a/arch/m68knommu/kernel/traps.c b/arch/m68knommu/kernel/traps.c index bed5f47bf56..437a061d8b9 100644 --- a/arch/m68knommu/kernel/traps.c +++ b/arch/m68knommu/kernel/traps.c @@ -62,8 +62,6 @@ static char const * const vec_names[] = { void __init trap_init(void) { - if (mach_trap_init) - mach_trap_init(); } void die_if_kernel(char *str, struct pt_regs *fp, int nr) @@ -82,7 +80,8 @@ void die_if_kernel(char *str, struct pt_regs *fp, int nr) printk(KERN_EMERG "Process %s (pid: %d, stackpage=%08lx)\n", current->comm, current->pid, PAGE_SIZE+(unsigned long)current); - show_stack(NULL, (unsigned long *)fp); + show_stack(NULL, (unsigned long *)(fp + 1)); + add_taint(TAINT_DIE); do_exit(SIGSEGV); } diff --git a/arch/m68knommu/mm/memory.c b/arch/m68knommu/mm/memory.c index 1a66b71035a..f93b88b51f9 100644 --- a/arch/m68knommu/mm/memory.c +++ b/arch/m68knommu/mm/memory.c @@ -33,23 +33,3 @@ unsigned long kernel_map(unsigned long paddr, unsigned long size, return paddr; } - -int is_in_rom(unsigned long addr) -{ - extern unsigned long _ramstart, _ramend; - - /* - * What we are really trying to do is determine if addr is - * in an allocated kernel memory region. If not then assume - * we cannot free it or otherwise de-allocate it. Ideally - * we could restrict this to really being in a ROM or flash, - * but that would need to be done on a board by board basis, - * not globally. - */ - if ((addr < _ramstart) || (addr >= _ramend)) - return(1); - - /* Default case, not in ROM */ - return(0); -} - diff --git a/arch/m68knommu/platform/5307/Makefile b/arch/m68knommu/platform/5307/Makefile index 2fd37dcc309..719a313494b 100644 --- a/arch/m68knommu/platform/5307/Makefile +++ b/arch/m68knommu/platform/5307/Makefile @@ -16,7 +16,7 @@ ifdef CONFIG_FULLDEBUG AFLAGS += -DDEBUGGER_COMPATIBLE_CACHE=1 endif -obj-$(CONFIG_COLDFIRE) += entry.o vectors.o ints.o +obj-$(CONFIG_COLDFIRE) += entry.o vectors.o obj-$(CONFIG_M5206) += timers.o obj-$(CONFIG_M5206e) += timers.o obj-$(CONFIG_M520x) += pit.o diff --git a/arch/m68knommu/platform/5307/entry.S b/arch/m68knommu/platform/5307/entry.S index f0dba84d910..c358aebe0af 100644 --- a/arch/m68knommu/platform/5307/entry.S +++ b/arch/m68knommu/platform/5307/entry.S @@ -1,7 +1,7 @@ /* * linux/arch/m68knommu/platform/5307/entry.S * - * Copyright (C) 1999-2002, Greg Ungerer (gerg@snapgear.com) + * Copyright (C) 1999-2007, Greg Ungerer (gerg@snapgear.com) * Copyright (C) 1998 D. Jeff Dionne <jeff@lineo.ca>, * Kenneth Albanowski <kjahds@kjahds.com>, * Copyright (C) 2000 Lineo Inc. (www.lineo.com) @@ -155,34 +155,21 @@ Lsignal_return: /* * This is the generic interrupt handler (for all hardware interrupt - * sources). It figures out the vector number and calls the appropriate - * interrupt service routine directly. + * sources). Calls upto high level code to do all the work. */ ENTRY(inthandler) SAVE_ALL moveq #-1,%d0 movel %d0,%sp@(PT_ORIG_D0) - addql #1,local_irq_count movew %sp@(PT_FORMATVEC),%d0 /* put exception # in d0 */ andl #0x03fc,%d0 /* mask out vector only */ - leal per_cpu__kstat+STAT_IRQ,%a0 - addql #1,%a0@(%d0) - + movel %sp,%sp@- /* push regs arg */ lsrl #2,%d0 /* calculate real vector # */ - movel %d0,%d1 /* calculate array offset */ - lsll #4,%d1 - lea irq_list,%a0 - addl %d1,%a0 /* pointer to array struct */ - - movel %sp,%sp@- /* push regs arg onto stack */ - movel %a0@(8),%sp@- /* push devid arg */ - movel %d0,%sp@- /* push vector # on stack */ - - movel %a0@,%a0 /* get function to call */ - jbsr %a0@ /* call vector handler */ - lea %sp@(12),%sp /* pop parameters off stack */ + movel %d0,%sp@- /* push vector number */ + jbsr do_IRQ /* call high level irq handler */ + lea %sp@(8),%sp /* pop args off stack */ bra ret_from_interrupt /* this was fallthrough */ @@ -198,24 +185,15 @@ ENTRY(fasthandler) movew %sp@(PT_FORMATVEC),%d0 andl #0x03fc,%d0 /* mask out vector only */ - leal per_cpu__kstat+STAT_IRQ,%a0 - addql #1,%a0@(%d0) - - movel %sp,%sp@- /* push regs arg onto stack */ - clrl %sp@- /* push devid arg */ + movel %sp,%sp@- /* push regs arg */ lsrl #2,%d0 /* calculate real vector # */ - movel %d0,%sp@- /* push vector # on stack */ - - lsll #4,%d0 /* adjust for array offset */ - lea irq_list,%a0 - movel %a0@(%d0),%a0 /* get function to call */ - jbsr %a0@ /* call vector handler */ - lea %sp@(12),%sp /* pop parameters off stack */ + movel %d0,%sp@- /* push vector number */ + jbsr do_IRQ /* call high level irq handler */ + lea %sp@(8),%sp /* pop args off stack */ RESTORE_LOCAL ENTRY(ret_from_interrupt) - subql #1,local_irq_count jeq 2f 1: RESTORE_ALL diff --git a/arch/m68knommu/platform/5307/ints.c b/arch/m68knommu/platform/5307/ints.c deleted file mode 100644 index 751633038c4..00000000000 --- a/arch/m68knommu/platform/5307/ints.c +++ /dev/null @@ -1,279 +0,0 @@ -/* - * linux/arch/m68knommu/kernel/ints.c -- General interrupt handling code - * - * Copyright (C) 1999-2002 Greg Ungerer (gerg@snapgear.com) - * Copyright (C) 1998 D. Jeff Dionne <jeff@lineo.ca>, - * Kenneth Albanowski <kjahds@kjahds.com>, - * Copyright (C) 2000 Lineo Inc. (www.lineo.com) - * - * Based on: - * - * linux/arch/m68k/kernel/ints.c -- Linux/m68k general interrupt handling code - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file COPYING in the main directory of this archive - * for more details. - */ - -#include <linux/module.h> -#include <linux/types.h> -#include <linux/init.h> -#include <linux/sched.h> -#include <linux/interrupt.h> -#include <linux/kernel_stat.h> -#include <linux/errno.h> -#include <linux/seq_file.h> - -#include <asm/system.h> -#include <asm/irq.h> -#include <asm/irqnode.h> -#include <asm/traps.h> -#include <asm/page.h> -#include <asm/machdep.h> - -/* - * This table stores the address info for each vector handler. - */ -struct irq_entry irq_list[SYS_IRQS]; - -#define NUM_IRQ_NODES 16 -static irq_node_t nodes[NUM_IRQ_NODES]; - -/* The number of spurious interrupts */ -volatile unsigned int num_spurious; - -unsigned int local_irq_count[NR_CPUS]; - -static irqreturn_t default_irq_handler(int irq, void *ptr) -{ -#if 1 - printk(KERN_INFO "%s(%d): default irq handler vec=%d [0x%x]\n", - __FILE__, __LINE__, irq, irq); -#endif - return(IRQ_HANDLED); -} - -/* - * void init_IRQ(void) - * - * Parameters: None - * - * Returns: Nothing - * - * This function should be called during kernel startup to initialize - * the IRQ handling routines. - */ - -void __init init_IRQ(void) -{ - int i; - - for (i = 0; i < SYS_IRQS; i++) { - if (mach_default_handler) - irq_list[i].handler = mach_default_handler; - else - irq_list[i].handler = default_irq_handler; - irq_list[i].flags = IRQ_FLG_STD; - irq_list[i].dev_id = NULL; - irq_list[i].devname = NULL; - } - - for (i = 0; i < NUM_IRQ_NODES; i++) - nodes[i].handler = NULL; - - if (mach_init_IRQ) - mach_init_IRQ(); -} - -irq_node_t *new_irq_node(void) -{ - irq_node_t *node; - short i; - - for (node = nodes, i = NUM_IRQ_NODES-1; i >= 0; node++, i--) - if (!node->handler) - return node; - - printk(KERN_INFO "new_irq_node: out of nodes\n"); - return NULL; -} - -int request_irq( - unsigned int irq, - irq_handler_t handler, - unsigned long flags, - const char *devname, - void *dev_id) -{ - if (irq < 0 || irq >= NR_IRQS) { - printk(KERN_WARNING "%s: Incorrect IRQ %d from %s\n", __FUNCTION__, - irq, devname); - return -ENXIO; - } - - if (!(irq_list[irq].flags & IRQ_FLG_STD)) { - if (irq_list[irq].flags & IRQ_FLG_LOCK) { - printk(KERN_WARNING "%s: IRQ %d from %s is not replaceable\n", - __FUNCTION__, irq, irq_list[irq].devname); - return -EBUSY; - } - if (flags & IRQ_FLG_REPLACE) { - printk(KERN_WARNING "%s: %s can't replace IRQ %d from %s\n", - __FUNCTION__, devname, irq, irq_list[irq].devname); - return -EBUSY; - } - } - - if (flags & IRQ_FLG_FAST) { - extern asmlinkage void fasthandler(void); - extern void set_evector(int vecnum, void (*handler)(void)); - set_evector(irq, fasthandler); - } - - irq_list[irq].handler = handler; - irq_list[irq].flags = flags; - irq_list[irq].dev_id = dev_id; - irq_list[irq].devname = devname; - return 0; -} - -EXPORT_SYMBOL(request_irq); - -void free_irq(unsigned int irq, void *dev_id) -{ - if (irq >= NR_IRQS) { - printk(KERN_WARNING "%s: Incorrect IRQ %d\n", __FUNCTION__, irq); - return; - } - - if (irq_list[irq].dev_id != dev_id) - printk(KERN_WARNING "%s: Removing probably wrong IRQ %d from %s\n", - __FUNCTION__, irq, irq_list[irq].devname); - - if (irq_list[irq].flags & IRQ_FLG_FAST) { - extern asmlinkage void inthandler(void); - extern void set_evector(int vecnum, void (*handler)(void)); - set_evector(irq, inthandler); - } - - if (mach_default_handler) - irq_list[irq].handler = mach_default_handler; - else - irq_list[irq].handler = default_irq_handler; - irq_list[irq].flags = IRQ_FLG_STD; - irq_list[irq].dev_id = NULL; - irq_list[irq].devname = NULL; -} - -EXPORT_SYMBOL(free_irq); - - -int sys_request_irq(unsigned int irq, irq_handler_t handler, - unsigned long flags, const char *devname, void *dev_id) -{ - if (irq > IRQ7) { - printk(KERN_WARNING "%s: Incorrect IRQ %d from %s\n", - __FUNCTION__, irq, devname); - return -ENXIO; - } - -#if 0 - if (!(irq_list[irq].flags & IRQ_FLG_STD)) { - if (irq_list[irq].flags & IRQ_FLG_LOCK) { - printk(KERN_WARNING "%s: IRQ %d from %s is not replaceable\n", - __FUNCTION__, irq, irq_list[irq].devname); - return -EBUSY; - } - if (!(flags & IRQ_FLG_REPLACE)) { - printk(KERN_WARNING "%s: %s can't replace IRQ %d from %s\n", - __FUNCTION__, devname, irq, irq_list[irq].devname); - return -EBUSY; - } - } -#endif - - irq_list[irq].handler = handler; - irq_list[irq].flags = flags; - irq_list[irq].dev_id = dev_id; - irq_list[irq].devname = devname; - return 0; -} - -void sys_free_irq(unsigned int irq, void *dev_id) -{ - if (irq > IRQ7) { - printk(KERN_WARNING "%s: Incorrect IRQ %d\n", __FUNCTION__, irq); - return; - } - - if (irq_list[irq].dev_id != dev_id) - printk(KERN_WARNING "%s: Removing probably wrong IRQ %d from %s\n", - __FUNCTION__, irq, irq_list[irq].devname); - - irq_list[irq].handler = mach_default_handler; - irq_list[irq].flags = 0; - irq_list[irq].dev_id = NULL; - irq_list[irq].devname = NULL; -} - -/* - * Do we need these probe functions on the m68k? - * - * ... may be useful with ISA devices - */ -unsigned long probe_irq_on (void) -{ - return 0; -} - -EXPORT_SYMBOL(probe_irq_on); - -int probe_irq_off (unsigned long irqs) -{ - return 0; -} - -EXPORT_SYMBOL(probe_irq_off); - -asmlinkage void process_int(unsigned long vec, struct pt_regs *fp) -{ - if (vec >= VEC_INT1 && vec <= VEC_INT7) { - vec -= VEC_SPUR; - kstat_cpu(0).irqs[vec]++; - irq_list[vec].handler(vec, irq_list[vec].dev_id); - } else { - if (mach_process_int) - mach_process_int(vec, fp); - else - panic("Can't process interrupt vector %ld\n", vec); - return; - } -} - - -int show_interrupts(struct seq_file *p, void *v) -{ - int i = *(loff_t *) v; - - if (i < NR_IRQS) { - if (! (irq_list[i].flags & IRQ_FLG_STD)) { - seq_printf(p, "%3d: %10u ", i, - (i ? kstat_cpu(0).irqs[i] : num_spurious)); - if (irq_list[i].flags & IRQ_FLG_LOCK) - seq_printf(p, "L "); - else - seq_printf(p, " "); - seq_printf(p, "%s\n", irq_list[i].devname); - } - } - - if (i == NR_IRQS && mach_get_irq_list) - mach_get_irq_list(p, v); - return 0; -} - -void init_irq_proc(void) -{ - /* Insert /proc/irq driver here */ -} - diff --git a/arch/m68knommu/platform/5307/vectors.c b/arch/m68knommu/platform/5307/vectors.c index 2a8b0d044ce..6cf89462023 100644 --- a/arch/m68knommu/platform/5307/vectors.c +++ b/arch/m68knommu/platform/5307/vectors.c @@ -3,23 +3,17 @@ /* * linux/arch/m68knommu/platform/5307/vectors.c * - * Copyright (C) 1999-2003, Greg Ungerer <gerg@snapgear.com> + * Copyright (C) 1999-2007, Greg Ungerer <gerg@snapgear.com> */ /***************************************************************************/ #include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/param.h> #include <linux/init.h> -#include <linux/unistd.h> -#include <linux/delay.h> -#include <asm/irq.h> -#include <asm/dma.h> +#include <linux/irq.h> #include <asm/traps.h> #include <asm/machdep.h> #include <asm/coldfire.h> -#include <asm/mcftimer.h> #include <asm/mcfsim.h> #include <asm/mcfdma.h> #include <asm/mcfwdebug.h> @@ -56,7 +50,7 @@ asmlinkage void trap(void); asmlinkage void system_call(void); asmlinkage void inthandler(void); -void __init coldfire_trap_init(void) +void __init init_vectors(void) { int i; @@ -86,6 +80,23 @@ void __init coldfire_trap_init(void) /***************************************************************************/ +void enable_vector(unsigned int irq) +{ + /* Currently no action on ColdFire */ +} + +void disable_vector(unsigned int irq) +{ + /* Currently no action on ColdFire */ +} + +void ack_vector(unsigned int irq) +{ + /* Currently no action on ColdFire */ +} + +/***************************************************************************/ + void coldfire_reset(void) { HARD_RESET_NOW(); diff --git a/arch/m68knommu/platform/68328/entry.S b/arch/m68knommu/platform/68328/entry.S index f9786271545..b1aef72f3ba 100644 --- a/arch/m68knommu/platform/68328/entry.S +++ b/arch/m68knommu/platform/68328/entry.S @@ -133,7 +133,6 @@ Lreturn: */ inthandler1: SAVE_ALL - addql #1,local_irq_count /* put exception # in d0*/ movew %sp@(PT_VECTOR), %d0 and #0x3ff, %d0 @@ -145,7 +144,6 @@ inthandler1: inthandler2: SAVE_ALL - addql #1,local_irq_count /* put exception # in d0*/ movew %sp@(PT_VECTOR), %d0 and #0x3ff, %d0 @@ -157,7 +155,6 @@ inthandler2: inthandler3: SAVE_ALL - addql #1,local_irq_count /* put exception # in d0*/ movew %sp@(PT_VECTOR), %d0 and #0x3ff, %d0 @@ -169,7 +166,6 @@ inthandler3: inthandler4: SAVE_ALL - addql #1,local_irq_count /* put exception # in d0*/ movew %sp@(PT_VECTOR), %d0 and #0x3ff, %d0 @@ -181,7 +177,6 @@ inthandler4: inthandler5: SAVE_ALL - addql #1,local_irq_count /* put exception # in d0*/ movew %sp@(PT_VECTOR), %d0 and #0x3ff, %d0 @@ -193,7 +188,6 @@ inthandler5: inthandler6: SAVE_ALL - addql #1,local_irq_count /* put exception # in d0*/ movew %sp@(PT_VECTOR), %d0 and #0x3ff, %d0 @@ -205,7 +199,6 @@ inthandler6: inthandler7: SAVE_ALL - addql #1,local_irq_count /* put exception # in d0*/ movew %sp@(PT_VECTOR), %d0 and #0x3ff, %d0 @@ -217,7 +210,6 @@ inthandler7: inthandler: SAVE_ALL - addql #1,local_irq_count /* put exception # in d0*/ movew %sp@(PT_VECTOR), %d0 and #0x3ff, %d0 @@ -228,7 +220,6 @@ inthandler: bra ret_from_interrupt ret_from_interrupt: - subql #1,local_irq_count jeq 1f 2: RESTORE_ALL @@ -238,7 +229,6 @@ ret_from_interrupt: jhi 2b /* check if we need to do software interrupts */ - movel local_irq_count,%d0 jeq ret_from_exception pea ret_from_exception diff --git a/arch/m68knommu/platform/68328/ints.c b/arch/m68knommu/platform/68328/ints.c index 3de6e337554..72e56d554f4 100644 --- a/arch/m68knommu/platform/68328/ints.c +++ b/arch/m68knommu/platform/68328/ints.c @@ -9,21 +9,14 @@ * Copyright 1999 D. Jeff Dionne <jeff@rt-control.com> */ -#include <linux/module.h> #include <linux/types.h> #include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/kernel_stat.h> -#include <linux/errno.h> +#include <linux/init.h> #include <linux/interrupt.h> - -#include <asm/system.h> -#include <asm/irq.h> -#include <asm/irqnode.h> +#include <linux/irq.h> #include <asm/traps.h> #include <asm/io.h> #include <asm/machdep.h> -#include <asm/setup.h> #if defined(CONFIG_M68328) #include <asm/MC68328.h> @@ -79,16 +72,12 @@ extern e_vector *_ramvec; /* The number of spurious interrupts */ volatile unsigned int num_spurious; -unsigned int local_irq_count[NR_CPUS]; - -/* irq node variables for the 32 (potential) on chip sources */ -static irq_node_t int_irq_list[NR_IRQS]; /* * This function should be called during kernel startup to initialize - * the IRQ handling routines. + * the machine vector table. */ -void init_IRQ(void) +void __init init_vectors(void) { int i; @@ -108,96 +97,10 @@ void init_IRQ(void) IVR = 0x40; /* Set DragonBall IVR (interrupt base) to 64 */ - /* initialize handlers */ - for (i = 0; i < NR_IRQS; i++) { - int_irq_list[i].handler = bad_interrupt; - int_irq_list[i].flags = IRQ_FLG_STD; - int_irq_list[i].dev_id = NULL; - int_irq_list[i].devname = NULL; - } - /* turn off all interrupts */ IMR = ~0; } -int request_irq( - unsigned int irq, - irq_handler_t handler, - unsigned long flags, - const char *devname, - void *dev_id) -{ - if (irq >= NR_IRQS) { - printk (KERN_ERR "%s: Unknown IRQ %d from %s\n", __FUNCTION__, irq, devname); - return -ENXIO; - } - - if (!(int_irq_list[irq].flags & IRQ_FLG_STD)) { - if (int_irq_list[irq].flags & IRQ_FLG_LOCK) { - printk(KERN_ERR "%s: IRQ %d from %s is not replaceable\n", - __FUNCTION__, irq, int_irq_list[irq].devname); - return -EBUSY; - } - if (flags & IRQ_FLG_REPLACE) { - printk(KERN_ERR "%s: %s can't replace IRQ %d from %s\n", - __FUNCTION__, devname, irq, int_irq_list[irq].devname); - return -EBUSY; - } - } - - int_irq_list[irq].handler = handler; - int_irq_list[irq].flags = flags; - int_irq_list[irq].dev_id = dev_id; - int_irq_list[irq].devname = devname; - - IMR &= ~(1<<irq); - - return 0; -} - -EXPORT_SYMBOL(request_irq); - -void free_irq(unsigned int irq, void *dev_id) -{ - if (irq >= NR_IRQS) { - printk (KERN_ERR "%s: Unknown IRQ %d\n", __FUNCTION__, irq); - return; - } - - if (int_irq_list[irq].dev_id != dev_id) - printk(KERN_INFO "%s: removing probably wrong IRQ %d from %s\n", - __FUNCTION__, irq, int_irq_list[irq].devname); - - int_irq_list[irq].handler = bad_interrupt; - int_irq_list[irq].flags = IRQ_FLG_STD; - int_irq_list[irq].dev_id = NULL; - int_irq_list[irq].devname = NULL; - - IMR |= 1<<irq; -} - -EXPORT_SYMBOL(free_irq); - -int show_interrupts(struct seq_file *p, void *v) -{ - int i = *(loff_t *) v; - - if (i < NR_IRQS) { - if (int_irq_list[i].devname) { - seq_printf(p, "%3d: %10u ", i, kstat_cpu(0).irqs[i]); - if (int_irq_list[i].flags & IRQ_FLG_LOCK) - seq_printf(p, "L "); - else - seq_printf(p, " "); - seq_printf(p, "%s\n", int_irq_list[i].devname); - } - } - if (i == NR_IRQS) - seq_printf(p, " : %10u spurious\n", num_spurious); - - return 0; -} - /* The 68k family did not have a good way to determine the source * of interrupts until later in the family. The EC000 core does * not provide the vector number on the stack, we vector everything @@ -255,14 +158,23 @@ void process_int(int vec, struct pt_regs *fp) irq++; } - kstat_cpu(0).irqs[irq]++; - - if (int_irq_list[irq].handler) { - int_irq_list[irq].handler(irq, int_irq_list[irq].dev_id, fp); - } else { - printk(KERN_ERR "unregistered interrupt %d!\nTurning it off in the IMR...\n", irq); - IMR |= mask; - } + do_IRQ(irq, fp); pend &= ~mask; } } + +void enable_vector(unsigned int irq) +{ + IMR &= ~(1<<irq); +} + +void disable_vector(unsigned int irq) +{ + IMR |= (1<<irq); +} + +void ack_vector(unsigned int irq) +{ + /* Nothing needed */ +} + diff --git a/arch/m68knommu/platform/68360/entry.S b/arch/m68knommu/platform/68360/entry.S index f1af8977f29..55dfefe3864 100644 --- a/arch/m68knommu/platform/68360/entry.S +++ b/arch/m68knommu/platform/68360/entry.S @@ -120,23 +120,21 @@ Lreturn: RESTORE_ALL /* - * This is the main interrupt handler, responsible for calling process_int() + * This is the main interrupt handler, responsible for calling do_IRQ() */ inthandler: SAVE_ALL - addql #1,local_irq_count /* put exception # in d0*/ movew %sp@(PT_VECTOR), %d0 and.l #0x3ff, %d0 lsr.l #0x02, %d0 movel %sp,%sp@- movel %d0,%sp@- /* put vector # on stack*/ - jbsr process_int /* process the IRQ*/ + jbsr do_IRQ /* process the IRQ*/ 3: addql #8,%sp /* pop parameters off stack*/ bra ret_from_interrupt ret_from_interrupt: - subql #1,local_irq_count jeq 1f 2: RESTORE_ALL diff --git a/arch/m68knommu/platform/68360/ints.c b/arch/m68knommu/platform/68360/ints.c index 4df3c146eb7..c36781157e0 100644 --- a/arch/m68knommu/platform/68360/ints.c +++ b/arch/m68knommu/platform/68360/ints.c @@ -10,20 +10,13 @@ * Copyright (c) 1999 D. Jeff Dionne <jeff@uclinux.org> */ -#include <linux/module.h> #include <linux/types.h> #include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/kernel_stat.h> -#include <linux/errno.h> - -#include <asm/system.h> -#include <asm/irq.h> -#include <asm/irqnode.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/irq.h> #include <asm/traps.h> -#include <asm/io.h> #include <asm/machdep.h> -#include <asm/setup.h> #include <asm/m68360.h> /* from quicc/commproc.c: */ @@ -36,26 +29,19 @@ extern void cpm_interrupt_init(void); asmlinkage void system_call(void); asmlinkage void buserr(void); asmlinkage void trap(void); -asmlinkage irqreturn_t bad_interrupt(void); -asmlinkage irqreturn_t inthandler(void); +asmlinkage void bad_interrupt(void); +asmlinkage void inthandler(void); extern void *_ramvec[]; /* The number of spurious interrupts */ volatile unsigned int num_spurious; -unsigned int local_irq_count[NR_CPUS]; - -/* irq node variables for the 32 (potential) on chip sources */ -static irq_node_t int_irq_list[INTERNAL_IRQS]; - -static short int_irq_ablecount[INTERNAL_IRQS]; /* * This function should be called during kernel startup to initialize - * IRQ handling routines. + * the vector table. */ - -void init_IRQ(void) +void init_vectors(void) { int i; int vba = (CPM_VECTOR_BASE<<4); @@ -79,7 +65,6 @@ void init_IRQ(void) _ramvec[32] = system_call; _ramvec[33] = trap; - cpm_interrupt_init(); /* set up CICR for vector base address and irq level */ @@ -124,212 +109,20 @@ void init_IRQ(void) /* turn off all CPM interrupts */ pquicc->intr_cimr = 0x00000000; - - /* initialize handlers */ - for (i = 0; i < INTERNAL_IRQS; i++) { - int_irq_list[i].handler = NULL; - int_irq_list[i].flags = IRQ_FLG_STD; - int_irq_list[i].dev_id = NULL; - int_irq_list[i].devname = NULL; - } -} - -#if 0 -void M68360_insert_irq(irq_node_t **list, irq_node_t *node) -{ - unsigned long flags; - irq_node_t *cur; - - if (!node->dev_id) - printk(KERN_INFO "%s: Warning: dev_id of %s is zero\n", - __FUNCTION__, node->devname); - - local_irq_save(flags); - - cur = *list; - - while (cur) { - list = &cur->next; - cur = cur->next; - } - - node->next = cur; - *list = node; - - local_irq_restore(flags); } -void M68360_delete_irq(irq_node_t **list, void *dev_id) +void enable_vector(unsigned int irq) { - unsigned long flags; - irq_node_t *node; - - local_irq_save(flags); - - for (node = *list; node; list = &node->next, node = *list) { - if (node->dev_id == dev_id) { - *list = node->next; - /* Mark it as free. */ - node->handler = NULL; - local_irq_restore(flags); - return; - } - } - local_irq_restore(flags); - printk (KERN_INFO "%s: tried to remove invalid irq\n", __FUNCTION__); + pquicc->intr_cimr |= (1 << irq); } -#endif -int request_irq( - unsigned int irq, - irqreturn_t (*handler)(int, void *, struct pt_regs *), - unsigned long flags, - const char *devname, - void *dev_id) +void disable_vector(unsigned int irq) { - int mask = (1<<irq); - - irq += (CPM_VECTOR_BASE<<4); - - if (irq >= INTERNAL_IRQS) { - printk (KERN_ERR "%s: Unknown IRQ %d from %s\n", __FUNCTION__, irq, devname); - return -ENXIO; - } - - if (!(int_irq_list[irq].flags & IRQ_FLG_STD)) { - if (int_irq_list[irq].flags & IRQ_FLG_LOCK) { - printk(KERN_ERR "%s: IRQ %d from %s is not replaceable\n", - __FUNCTION__, irq, int_irq_list[irq].devname); - return -EBUSY; - } - if (flags & IRQ_FLG_REPLACE) { - printk(KERN_ERR "%s: %s can't replace IRQ %d from %s\n", - __FUNCTION__, devname, irq, int_irq_list[irq].devname); - return -EBUSY; - } - } - int_irq_list[irq].handler = handler; - int_irq_list[irq].flags = flags; - int_irq_list[irq].dev_id = dev_id; - int_irq_list[irq].devname = devname; - - /* enable in the CIMR */ - if (!int_irq_ablecount[irq]) - pquicc->intr_cimr |= mask; - /* *(volatile unsigned long *)0xfffff304 &= ~(1<<irq); */ - - return 0; + pquicc->intr_cimr &= ~(1 << irq); } -EXPORT_SYMBOL(request_irq); - -void free_irq(unsigned int irq, void *dev_id) +void ack_vector(unsigned int irq) { - if (irq >= INTERNAL_IRQS) { - printk (KERN_ERR "%s: Unknown IRQ %d\n", __FUNCTION__, irq); - return; - } - - if (int_irq_list[irq].dev_id != dev_id) - printk(KERN_INFO "%s: removing probably wrong IRQ %d from %s\n", - __FUNCTION__, irq, int_irq_list[irq].devname); - int_irq_list[irq].handler = NULL; - int_irq_list[irq].flags = IRQ_FLG_STD; - int_irq_list[irq].dev_id = NULL; - int_irq_list[irq].devname = NULL; - - *(volatile unsigned long *)0xfffff304 |= 1<<irq; + pquicc->intr_cisr = (1 << irq); } -EXPORT_SYMBOL(free_irq); - -#if 0 -/* - * Enable/disable a particular machine specific interrupt source. - * Note that this may affect other interrupts in case of a shared interrupt. - * This function should only be called for a _very_ short time to change some - * internal data, that may not be changed by the interrupt at the same time. - * int_(enable|disable)_irq calls may also be nested. - */ -void M68360_enable_irq(unsigned int irq) -{ - if (irq >= INTERNAL_IRQS) { - printk(KERN_ERR "%s: Unknown IRQ %d\n", __FUNCTION__, irq); - return; - } - - if (--int_irq_ablecount[irq]) - return; - - /* enable the interrupt */ - *(volatile unsigned long *)0xfffff304 &= ~(1<<irq); -} - -void M68360_disable_irq(unsigned int irq) -{ - if (irq >= INTERNAL_IRQS) { - printk(KERN_ERR "%s: Unknown IRQ %d\n", __FUNCTION__, irq); - return; - } - - if (int_irq_ablecount[irq]++) - return; - - /* disable the interrupt */ - *(volatile unsigned long *)0xfffff304 |= 1<<irq; -} -#endif - -int show_interrupts(struct seq_file *p, void *v) -{ - int i = *(loff_t *) v; - - if (i < NR_IRQS) { - if (int_irq_list[i].devname) { - seq_printf(p, "%3d: %10u ", i, kstat_cpu(0).irqs[i]); - if (int_irq_list[i].flags & IRQ_FLG_LOCK) - seq_printf(p, "L "); - else - seq_printf(p, " "); - seq_printf(p, "%s\n", int_irq_list[i].devname); - } - } - if (i == NR_IRQS) - seq_printf(p, " : %10u spurious\n", num_spurious); - - return 0; -} - -/* The 68k family did not have a good way to determine the source - * of interrupts until later in the family. The EC000 core does - * not provide the vector number on the stack, we vector everything - * into one vector and look in the blasted mask register... - * This code is designed to be fast, almost constant time, not clean! - */ -void process_int(int vec, struct pt_regs *fp) -{ - int irq; - int mask; - - /* unsigned long pend = *(volatile unsigned long *)0xfffff30c; */ - - /* irq = vec + (CPM_VECTOR_BASE<<4); */ - irq = vec; - - /* unsigned long pend = *(volatile unsigned long *)pquicc->intr_cipr; */ - - /* Bugger all that weirdness. For the moment, I seem to know where I came from; - * vec is passed from a specific ISR, so I'll use it. */ - - if (int_irq_list[irq].handler) { - int_irq_list[irq].handler(irq , int_irq_list[irq].dev_id, fp); - kstat_cpu(0).irqs[irq]++; - pquicc->intr_cisr = (1 << vec); /* indicate that irq has been serviced */ - } else { - printk(KERN_ERR "unregistered interrupt %d!\nTurning it off in the CIMR...\n", irq); - /* *(volatile unsigned long *)0xfffff304 |= mask; */ - pquicc->intr_cimr &= ~(1 << vec); - num_spurious += 1; - } - return(IRQ_HANDLED); -} diff --git a/arch/mips/basler/excite/excite_setup.c b/arch/mips/basler/excite/excite_setup.c index 2f0e4c08eb0..56003188f17 100644 --- a/arch/mips/basler/excite/excite_setup.c +++ b/arch/mips/basler/excite/excite_setup.c @@ -26,6 +26,7 @@ #include <linux/tty.h> #include <linux/serial_core.h> #include <linux/serial.h> +#include <linux/serial_8250.h> #include <linux/ioport.h> #include <linux/spinlock.h> #include <asm/bootinfo.h> diff --git a/arch/mips/gt64120/wrppmc/setup.c b/arch/mips/gt64120/wrppmc/setup.c index ea965529e5e..ed58c13b603 100644 --- a/arch/mips/gt64120/wrppmc/setup.c +++ b/arch/mips/gt64120/wrppmc/setup.c @@ -14,6 +14,7 @@ #include <linux/tty.h> #include <linux/serial.h> #include <linux/serial_core.h> +#include <linux/serial_8250.h> #include <linux/pm.h> #include <asm/io.h> diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c index b5a7b46bbc4..893e7bccf22 100644 --- a/arch/mips/kernel/ptrace.c +++ b/arch/mips/kernel/ptrace.c @@ -174,17 +174,9 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) switch (request) { /* when I and D space are separate, these will need to be fixed. */ case PTRACE_PEEKTEXT: /* read word at location addr. */ - case PTRACE_PEEKDATA: { - unsigned long tmp; - int copied; - - copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); - ret = -EIO; - if (copied != sizeof(tmp)) - break; - ret = put_user(tmp,(unsigned long __user *) data); + case PTRACE_PEEKDATA: + ret = generic_ptrace_peekdata(child, addr, data); break; - } /* Read the word at location addr in the USER area. */ case PTRACE_PEEKUSR: { @@ -313,11 +305,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) /* when I and D space are separate, this will have to be fixed. */ case PTRACE_POKETEXT: /* write the word at location addr. */ case PTRACE_POKEDATA: - ret = 0; - if (access_process_vm(child, addr, &data, sizeof(data), 1) - == sizeof(data)) - break; - ret = -EIO; + ret = generic_ptrace_pokedata(child, addr, data); break; case PTRACE_POKEUSR: { diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c index 37c562c4c81..ce277cb34dd 100644 --- a/arch/mips/kernel/traps.c +++ b/arch/mips/kernel/traps.c @@ -326,6 +326,7 @@ void __noreturn die(const char * str, struct pt_regs * regs) #endif /* CONFIG_MIPS_MT_SMTC */ printk("%s[#%d]:\n", str, ++die_counter); show_registers(regs); + add_taint(TAINT_DIE); spin_unlock_irq(&die_lock); if (in_interrupt()) diff --git a/arch/mips/kernel/vmlinux.lds.S b/arch/mips/kernel/vmlinux.lds.S index 9b9992cd562..bc9bae2a73f 100644 --- a/arch/mips/kernel/vmlinux.lds.S +++ b/arch/mips/kernel/vmlinux.lds.S @@ -119,10 +119,7 @@ SECTIONS .init.ramfs : { *(.init.ramfs) } __initramfs_end = .; #endif - . = ALIGN(_PAGE_SIZE); - __per_cpu_start = .; - .data.percpu : { *(.data.percpu) } - __per_cpu_end = .; + PERCPU(_PAGE_SIZE) . = ALIGN(_PAGE_SIZE); __init_end = .; /* freed after init ends here */ diff --git a/arch/mips/mips-boards/atlas/atlas_setup.c b/arch/mips/mips-boards/atlas/atlas_setup.c index 1cc6ebbedfd..c68358a476d 100644 --- a/arch/mips/mips-boards/atlas/atlas_setup.c +++ b/arch/mips/mips-boards/atlas/atlas_setup.c @@ -22,6 +22,7 @@ #include <linux/tty.h> #include <linux/serial.h> #include <linux/serial_core.h> +#include <linux/serial_8250.h> #include <asm/cpu.h> #include <asm/bootinfo.h> diff --git a/arch/mips/mips-boards/sead/sead_setup.c b/arch/mips/mips-boards/sead/sead_setup.c index bb801409d39..5f70eaf01fa 100644 --- a/arch/mips/mips-boards/sead/sead_setup.c +++ b/arch/mips/mips-boards/sead/sead_setup.c @@ -23,6 +23,7 @@ #include <linux/tty.h> #include <linux/serial.h> #include <linux/serial_core.h> +#include <linux/serial_8250.h> #include <asm/cpu.h> #include <asm/bootinfo.h> diff --git a/arch/mips/mipssim/sim_setup.c b/arch/mips/mipssim/sim_setup.c index 60e66906be6..17819b59410 100644 --- a/arch/mips/mipssim/sim_setup.c +++ b/arch/mips/mipssim/sim_setup.c @@ -26,6 +26,7 @@ #include <linux/tty.h> #include <linux/serial.h> #include <linux/serial_core.h> +#include <linux/serial_8250.h> #include <asm/cpu.h> #include <asm/bootinfo.h> diff --git a/arch/mips/mm/fault.c b/arch/mips/mm/fault.c index 7ebea331edb..521771b373d 100644 --- a/arch/mips/mm/fault.c +++ b/arch/mips/mm/fault.c @@ -39,6 +39,7 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long write, struct mm_struct *mm = tsk->mm; const int field = sizeof(unsigned long) * 2; siginfo_t info; + int fault; #if 0 printk("Cpu%d[%s:%d:%0*lx:%ld:%0*lx]\n", raw_smp_processor_id(), @@ -102,20 +103,18 @@ survive: * make sure we exit gracefully rather than endlessly redo * the fault. */ - switch (handle_mm_fault(mm, vma, address, write)) { - case VM_FAULT_MINOR: - tsk->min_flt++; - break; - case VM_FAULT_MAJOR: - tsk->maj_flt++; - break; - case VM_FAULT_SIGBUS: - goto do_sigbus; - case VM_FAULT_OOM: - goto out_of_memory; - default: + fault = handle_mm_fault(mm, vma, address, write); + if (unlikely(fault & VM_FAULT_ERROR)) { + if (fault & VM_FAULT_OOM) + goto out_of_memory; + else if (fault & VM_FAULT_SIGBUS) + goto do_sigbus; BUG(); } + if (fault & VM_FAULT_MAJOR) + tsk->maj_flt++; + else + tsk->min_flt++; up_read(&mm->mmap_sem); return; diff --git a/arch/mips/pmc-sierra/msp71xx/msp_serial.c b/arch/mips/pmc-sierra/msp71xx/msp_serial.c index c41b53faa8f..e25bac537d7 100644 --- a/arch/mips/pmc-sierra/msp71xx/msp_serial.c +++ b/arch/mips/pmc-sierra/msp71xx/msp_serial.c @@ -32,6 +32,7 @@ #include <asm/io.h> #include <asm/processor.h> #include <asm/serial.h> +#include <linux/serial_8250.h> #include <msp_prom.h> #include <msp_int.h> diff --git a/arch/mips/pmc-sierra/yosemite/setup.c b/arch/mips/pmc-sierra/yosemite/setup.c index 6a6e15e4000..f7f93ae24c3 100644 --- a/arch/mips/pmc-sierra/yosemite/setup.c +++ b/arch/mips/pmc-sierra/yosemite/setup.c @@ -39,6 +39,7 @@ #include <linux/tty.h> #include <linux/serial.h> #include <linux/serial_core.h> +#include <linux/serial_8250.h> #include <asm/time.h> #include <asm/bootinfo.h> diff --git a/arch/mips/sibyte/bcm1480/setup.c b/arch/mips/sibyte/bcm1480/setup.c index bdaac34ae70..89f29233cae 100644 --- a/arch/mips/sibyte/bcm1480/setup.c +++ b/arch/mips/sibyte/bcm1480/setup.c @@ -31,6 +31,7 @@ unsigned int sb1_pass; unsigned int soc_pass; unsigned int soc_type; +EXPORT_SYMBOL(soc_type); unsigned int periph_rev; unsigned int zbbus_mhz; diff --git a/arch/mips/sibyte/sb1250/setup.c b/arch/mips/sibyte/sb1250/setup.c index f4a6169aa0a..2d5c6d8b41f 100644 --- a/arch/mips/sibyte/sb1250/setup.c +++ b/arch/mips/sibyte/sb1250/setup.c @@ -31,6 +31,7 @@ unsigned int sb1_pass; unsigned int soc_pass; unsigned int soc_type; +EXPORT_SYMBOL(soc_type); unsigned int periph_rev; unsigned int zbbus_mhz; EXPORT_SYMBOL(zbbus_mhz); diff --git a/arch/parisc/kernel/ptrace.c b/arch/parisc/kernel/ptrace.c index 8a0db376e91..26ec774c502 100644 --- a/arch/parisc/kernel/ptrace.c +++ b/arch/parisc/kernel/ptrace.c @@ -87,10 +87,9 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) switch (request) { case PTRACE_PEEKTEXT: /* read word at location addr. */ case PTRACE_PEEKDATA: { - int copied; - #ifdef CONFIG_64BIT if (__is_compat_task(child)) { + int copied; unsigned int tmp; addr &= 0xffffffffL; @@ -105,15 +104,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) } else #endif - { - unsigned long tmp; - - copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); - ret = -EIO; - if (copied != sizeof(tmp)) - goto out_tsk; - ret = put_user(tmp,(unsigned long *) data); - } + ret = generic_ptrace_peekdata(child, addr, data); goto out_tsk; } diff --git a/arch/parisc/kernel/traps.c b/arch/parisc/kernel/traps.c index f9bca2d74b3..bbf029a184a 100644 --- a/arch/parisc/kernel/traps.c +++ b/arch/parisc/kernel/traps.c @@ -264,6 +264,7 @@ KERN_CRIT " || ||\n"); show_regs(regs); dump_stack(); + add_taint(TAINT_DIE); if (in_interrupt()) panic("Fatal exception in interrupt"); diff --git a/arch/parisc/kernel/unwind.c b/arch/parisc/kernel/unwind.c index 322167737de..cf780cb3b91 100644 --- a/arch/parisc/kernel/unwind.c +++ b/arch/parisc/kernel/unwind.c @@ -242,7 +242,7 @@ static void unwind_frame_regs(struct unwind_frame_info *info) #ifdef CONFIG_KALLSYMS /* Handle some frequent special cases.... */ { - char symname[KSYM_NAME_LEN+1]; + char symname[KSYM_NAME_LEN]; char *modname; kallsyms_lookup(info->ip, NULL, NULL, &modname, diff --git a/arch/parisc/kernel/vmlinux.lds.S b/arch/parisc/kernel/vmlinux.lds.S index 4d96ba4b984..d4e6a93c8d9 100644 --- a/arch/parisc/kernel/vmlinux.lds.S +++ b/arch/parisc/kernel/vmlinux.lds.S @@ -181,10 +181,9 @@ SECTIONS .init.ramfs : { *(.init.ramfs) } __initramfs_end = .; #endif - . = ALIGN(ASM_PAGE_SIZE); - __per_cpu_start = .; - .data.percpu : { *(.data.percpu) } - __per_cpu_end = .; + + PERCPU(ASM_PAGE_SIZE) + . = ALIGN(ASM_PAGE_SIZE); __init_end = .; /* freed after init ends here */ diff --git a/arch/parisc/mm/fault.c b/arch/parisc/mm/fault.c index f6f67554c62..7899ab87785 100644 --- a/arch/parisc/mm/fault.c +++ b/arch/parisc/mm/fault.c @@ -147,6 +147,7 @@ void do_page_fault(struct pt_regs *regs, unsigned long code, struct mm_struct *mm = tsk->mm; const struct exception_table_entry *fix; unsigned long acc_type; + int fault; if (in_atomic() || !mm) goto no_context; @@ -173,23 +174,23 @@ good_area: * fault. */ - switch (handle_mm_fault(mm, vma, address, (acc_type & VM_WRITE) != 0)) { - case VM_FAULT_MINOR: - ++current->min_flt; - break; - case VM_FAULT_MAJOR: - ++current->maj_flt; - break; - case VM_FAULT_SIGBUS: + fault = handle_mm_fault(mm, vma, address, (acc_type & VM_WRITE) != 0); + if (unlikely(fault & VM_FAULT_ERROR)) { /* * We hit a shared mapping outside of the file, or some * other thing happened to us that made us unable to * handle the page fault gracefully. */ - goto bad_area; - default: - goto out_of_memory; + if (fault & VM_FAULT_OOM) + goto out_of_memory; + else if (fault & VM_FAULT_SIGBUS) + goto bad_area; + BUG(); } + if (fault & VM_FAULT_MAJOR) + current->maj_flt++; + else + current->min_flt++; up_read(&mm->mmap_sem); return; diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 6b8b83ebca7..d860b640a14 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -4,17 +4,7 @@ mainmenu "Linux/PowerPC Kernel Configuration" -config PPC64 - bool "64-bit kernel" - default n - help - This option selects whether a 32-bit or a 64-bit kernel - will be built. - -config PPC_PM_NEEDS_RTC_LIB - bool - select RTC_LIB - default y if PM +source "arch/powerpc/platforms/Kconfig.cputype" config PPC32 bool @@ -135,123 +125,6 @@ config PPC64_SWSUSP depends on PPC64 && (BROKEN || (PPC_PMAC64 && EXPERIMENTAL)) default y -menu "Processor support" -choice - prompt "Processor Type" - depends on PPC32 - default 6xx - -config CLASSIC32 - bool "52xx/6xx/7xx/74xx" - select PPC_FPU - select 6xx - help - There are four families of PowerPC chips supported. The more common - types (601, 603, 604, 740, 750, 7400), the Motorola embedded - versions (821, 823, 850, 855, 860, 52xx, 82xx, 83xx), the AMCC - embedded versions (403 and 405) and the high end 64 bit Power - processors (POWER 3, POWER4, and IBM PPC970 also known as G5). - - This option is the catch-all for 6xx types, including some of the - embedded versions. Unless there is see an option for the specific - chip family you are using, you want this option. - - You do not want this if you are building a kernel for a 64 bit - IBM RS/6000 or an Apple G5, choose 6xx. - - If unsure, select this option - - Note that the kernel runs in 32-bit mode even on 64-bit chips. - -config PPC_82xx - bool "Freescale 82xx" - select 6xx - select PPC_FPU - -config PPC_83xx - bool "Freescale 83xx" - select 6xx - select FSL_SOC - select 83xx - select PPC_FPU - select WANT_DEVICE_TREE - -config PPC_85xx - bool "Freescale 85xx" - select E500 - select FSL_SOC - select 85xx - select WANT_DEVICE_TREE - -config PPC_86xx - bool "Freescale 86xx" - select 6xx - select FSL_SOC - select FSL_PCIE - select PPC_FPU - select ALTIVEC - help - The Freescale E600 SoCs have 74xx cores. - -config PPC_8xx - bool "Freescale 8xx" - select FSL_SOC - select 8xx - -config 40x - bool "AMCC 40x" - select PPC_DCR_NATIVE - -config 44x - bool "AMCC 44x" - select PPC_DCR_NATIVE - select WANT_DEVICE_TREE - -config E200 - bool "Freescale e200" - -endchoice - -config POWER4_ONLY - bool "Optimize for POWER4" - depends on PPC64 - default n - ---help--- - Cause the compiler to optimize for POWER4/POWER5/PPC970 processors. - The resulting binary will not work on POWER3 or RS64 processors - when compiled with binutils 2.15 or later. - -config POWER3 - bool - depends on PPC64 - default y if !POWER4_ONLY - -config POWER4 - depends on PPC64 - def_bool y - -config 6xx - bool - -# this is temp to handle compat with arch=ppc -config 8xx - bool - -# this is temp to handle compat with arch=ppc -config 83xx - bool - -# this is temp to handle compat with arch=ppc -config 85xx - bool - -config E500 - bool - -config PPC_FPU - bool - default y if PPC64 - config PPC_DCR_NATIVE bool default n @@ -270,134 +143,6 @@ config PPC_OF_PLATFORM_PCI depends on PPC64 # not supported on 32 bits yet default n -config 4xx - bool - depends on 40x || 44x - default y - -config BOOKE - bool - depends on E200 || E500 || 44x - default y - -config FSL_BOOKE - bool - depends on E200 || E500 - default y - -config PTE_64BIT - bool - depends on 44x || E500 - default y if 44x - default y if E500 && PHYS_64BIT - -config PHYS_64BIT - bool 'Large physical address support' if E500 - depends on 44x || E500 - select RESOURCES_64BIT - default y if 44x - ---help--- - This option enables kernel support for larger than 32-bit physical - addresses. This features is not be available on all e500 cores. - - If in doubt, say N here. - -config ALTIVEC - bool "AltiVec Support" - depends on CLASSIC32 || POWER4 - ---help--- - This option enables kernel support for the Altivec extensions to the - PowerPC processor. The kernel currently supports saving and restoring - altivec registers, and turning on the 'altivec enable' bit so user - processes can execute altivec instructions. - - This option is only usefully if you have a processor that supports - altivec (G4, otherwise known as 74xx series), but does not have - any affect on a non-altivec cpu (it does, however add code to the - kernel). - - If in doubt, say Y here. - -config SPE - bool "SPE Support" - depends on E200 || E500 - default y - ---help--- - This option enables kernel support for the Signal Processing - Extensions (SPE) to the PowerPC processor. The kernel currently - supports saving and restoring SPE registers, and turning on the - 'spe enable' bit so user processes can execute SPE instructions. - - This option is only useful if you have a processor that supports - SPE (e500, otherwise known as 85xx series), but does not have any - effect on a non-spe cpu (it does, however add code to the kernel). - - If in doubt, say Y here. - -config PPC_STD_MMU - bool - depends on 6xx || POWER3 || POWER4 || PPC64 - default y - -config PPC_STD_MMU_32 - def_bool y - depends on PPC_STD_MMU && PPC32 - -config PPC_MM_SLICES - bool - default y if HUGETLB_PAGE - default n - -config VIRT_CPU_ACCOUNTING - bool "Deterministic task and CPU time accounting" - depends on PPC64 - default y - help - Select this option to enable more accurate task and CPU time - accounting. This is done by reading a CPU counter on each - kernel entry and exit and on transitions within the kernel - between system, softirq and hardirq state, so there is a - small performance impact. This also enables accounting of - stolen time on logically-partitioned systems running on - IBM POWER5-based machines. - - If in doubt, say Y here. - -config SMP - depends on PPC_STD_MMU - bool "Symmetric multi-processing support" - ---help--- - This enables support for systems with more than one CPU. If you have - a system with only one CPU, say N. If you have a system with more - than one CPU, say Y. Note that the kernel does not currently - support SMP machines with 603/603e/603ev or PPC750 ("G3") processors - since they have inadequate hardware support for multiprocessor - operation. - - If you say N here, the kernel will run on single and multiprocessor - machines, but will use only one CPU of a multiprocessor machine. If - you say Y here, the kernel will run on single-processor machines. - On a single-processor machine, the kernel will run faster if you say - N here. - - If you don't know what to do here, say N. - -config NR_CPUS - int "Maximum number of CPUs (2-128)" - range 2 128 - depends on SMP - default "32" if PPC64 - default "4" - -config NOT_COHERENT_CACHE - bool - depends on 4xx || 8xx || E200 - default y - -config CONFIG_CHECK_CACHE_COHERENCY - bool -endmenu - source "init/Kconfig" source "arch/powerpc/platforms/Kconfig" @@ -677,10 +422,6 @@ config SBUS config FSL_SOC bool -config FSL_PCIE - bool - depends on PPC_86xx - # Yes MCA RS/6000s exist but Linux-PPC does not currently support any config MCA bool @@ -688,10 +429,10 @@ config MCA config PCI bool "PCI support" if 40x || CPM2 || PPC_83xx || PPC_85xx || PPC_86xx \ || PPC_MPC52xx || (EMBEDDED && (PPC_PSERIES || PPC_ISERIES)) \ - || MPC7448HPC2 || PPC_PS3 || PPC_HOLLY - default y if !40x && !CPM2 && !8xx && !APUS && !PPC_83xx \ + || PPC_PS3 + default y if !40x && !CPM2 && !8xx && !PPC_83xx \ && !PPC_85xx && !PPC_86xx - default PCI_PERMEDIA if !4xx && !CPM2 && !8xx && APUS + default PCI_PERMEDIA if !4xx && !CPM2 && !8xx default PCI_QSPAN if !4xx && !CPM2 && 8xx select ARCH_SUPPORTS_MSI help @@ -899,8 +640,8 @@ menu "Instrumentation Support" source "arch/powerpc/oprofile/Kconfig" config KPROBES - bool "Kprobes (EXPERIMENTAL)" - depends on !BOOKE && !4xx && KALLSYMS && EXPERIMENTAL && MODULES + bool "Kprobes" + depends on !BOOKE && !4xx && KALLSYMS && MODULES help Kprobes allows you to trap at almost any kernel address and execute a callback function. register_kprobe() establishes diff --git a/arch/powerpc/Makefile b/arch/powerpc/Makefile index 85be60504eb..6c1e36c33fa 100644 --- a/arch/powerpc/Makefile +++ b/arch/powerpc/Makefile @@ -148,7 +148,7 @@ all: $(KBUILD_IMAGE) CPPFLAGS_vmlinux.lds := -Upowerpc -BOOT_TARGETS = zImage zImage.initrd zImage.dts zImage.dts_initrd uImage +BOOT_TARGETS = zImage zImage.initrd uImage PHONY += $(BOOT_TARGETS) diff --git a/arch/powerpc/boot/44x.c b/arch/powerpc/boot/44x.c index d51377d9024..9f64e840bef 100644 --- a/arch/powerpc/boot/44x.c +++ b/arch/powerpc/boot/44x.c @@ -38,3 +38,48 @@ void ibm44x_fixup_memsize(void) dt_fixup_memory(0, memsize); } + +#define SPRN_DBCR0 0x134 +#define DBCR0_RST_SYSTEM 0x30000000 + +void ibm44x_dbcr_reset(void) +{ + unsigned long tmp; + + asm volatile ( + "mfspr %0,%1\n" + "oris %0,%0,%2@h\n" + "mtspr %1,%0" + : "=&r"(tmp) : "i"(SPRN_DBCR0), "i"(DBCR0_RST_SYSTEM) + ); + +} + +/* Read 4xx EBC bus bridge registers to get mappings of the peripheral + * banks into the OPB address space */ +void ibm4xx_fixup_ebc_ranges(const char *ebc) +{ + void *devp; + u32 bxcr; + u32 ranges[EBC_NUM_BANKS*4]; + u32 *p = ranges; + int i; + + for (i = 0; i < EBC_NUM_BANKS; i++) { + mtdcr(DCRN_EBC0_CFGADDR, EBC_BXCR(i)); + bxcr = mfdcr(DCRN_EBC0_CFGDATA); + + if ((bxcr & EBC_BXCR_BU) != EBC_BXCR_BU_OFF) { + *p++ = i; + *p++ = 0; + *p++ = bxcr & EBC_BXCR_BAS; + *p++ = EBC_BXCR_BANK_SIZE(bxcr); + } + } + + devp = finddevice(ebc); + if (! devp) + fatal("Couldn't locate EBC node %s\n\r", ebc); + + setprop(devp, "ranges", ranges, (p - ranges) * sizeof(u32)); +} diff --git a/arch/powerpc/boot/44x.h b/arch/powerpc/boot/44x.h index 7b129ad043e..577982c9a3c 100644 --- a/arch/powerpc/boot/44x.h +++ b/arch/powerpc/boot/44x.h @@ -11,6 +11,9 @@ #define _PPC_BOOT_44X_H_ void ibm44x_fixup_memsize(void); +void ibm4xx_fixup_ebc_ranges(const char *ebc); + +void ibm44x_dbcr_reset(void); void ebony_init(void *mac0, void *mac1); #endif /* _PPC_BOOT_44X_H_ */ diff --git a/arch/powerpc/boot/Makefile b/arch/powerpc/boot/Makefile index ff2701949ee..61a6f34ca5e 100644 --- a/arch/powerpc/boot/Makefile +++ b/arch/powerpc/boot/Makefile @@ -43,10 +43,11 @@ $(addprefix $(obj)/,$(zlib) gunzip_util.o main.o): \ src-wlib := string.S crt0.S stdio.c main.c flatdevtree.c flatdevtree_misc.c \ ns16550.c serial.c simple_alloc.c div64.S util.S \ - gunzip_util.c elf_util.c $(zlib) devtree.c \ - 44x.c ebony.c mv64x60.c mpsc.c mv64x60_i2c.c + gunzip_util.c elf_util.c $(zlib) devtree.c oflib.c ofconsole.c \ + 44x.c ebony.c mv64x60.c mpsc.c mv64x60_i2c.c cuboot.c src-plat := of.c cuboot-83xx.c cuboot-85xx.c holly.c \ - cuboot-ebony.c treeboot-ebony.c prpmc2800.c + cuboot-ebony.c treeboot-ebony.c prpmc2800.c \ + ps3-head.S ps3-hvcall.S ps3.c src-boot := $(src-wlib) $(src-plat) empty.c src-boot := $(addprefix $(obj)/, $(src-boot)) @@ -75,11 +76,11 @@ $(addprefix $(obj)/,$(zliblinuxheader)): $(obj)/%: $(srctree)/include/linux/% $(obj)/empty.c: @touch $@ -$(obj)/zImage.lds $(obj)/zImage.coff.lds: $(obj)/%: $(srctree)/$(src)/%.S +$(obj)/zImage.lds $(obj)/zImage.coff.lds $(obj)/zImage.ps3.lds: $(obj)/%: $(srctree)/$(src)/%.S @cp $< $@ clean-files := $(zlib) $(zlibheader) $(zliblinuxheader) \ - empty.c zImage.coff.lds zImage.lds + empty.c zImage zImage.coff.lds zImage.ps3.lds zImage.lds quiet_cmd_bootcc = BOOTCC $@ cmd_bootcc = $(CROSS32CC) -Wp,-MD,$(depfile) $(BOOTCFLAGS) -c -o $@ $< @@ -102,7 +103,7 @@ hostprogs-y := addnote addRamDisk hack-coff mktree targets += $(patsubst $(obj)/%,%,$(obj-boot) wrapper.a) extra-y := $(obj)/wrapper.a $(obj-plat) $(obj)/empty.o \ - $(obj)/zImage.lds $(obj)/zImage.coff.lds + $(obj)/zImage.lds $(obj)/zImage.coff.lds $(obj)/zImage.ps3.lds wrapper :=$(srctree)/$(src)/wrapper wrapperbits := $(extra-y) $(addprefix $(obj)/,addnote hack-coff mktree) \ @@ -132,7 +133,7 @@ image-$(CONFIG_PPC_CELLEB) += zImage.pseries image-$(CONFIG_PPC_CHRP) += zImage.chrp image-$(CONFIG_PPC_EFIKA) += zImage.chrp image-$(CONFIG_PPC_PMAC) += zImage.pmac -image-$(CONFIG_PPC_HOLLY) += zImage.holly-elf +image-$(CONFIG_PPC_HOLLY) += zImage.holly image-$(CONFIG_PPC_PRPMC2800) += zImage.prpmc2800 image-$(CONFIG_PPC_ISERIES) += zImage.iseries image-$(CONFIG_DEFAULT_UIMAGE) += uImage @@ -157,55 +158,43 @@ targets += $(image-y) $(initrd-y) $(addprefix $(obj)/, $(initrd-y)): $(obj)/ramdisk.image.gz -dts- := $(patsubst zImage%, zImage.dts%, $(image-n) $(image-)) -dts-y := $(patsubst zImage%, zImage.dts%, $(image-y)) -dts-y := $(filter-out $(image-y), $(dts-y)) -targets += $(image-y) $(dts-y) - -dts_initrd- := $(patsubst zImage%, zImage.dts_initrd%, $(image-n) $(image-)) -dts_initrd-y := $(patsubst zImage%, zImage.dts_initrd%, $(image-y)) -dts_initrd-y := $(filter-out $(image-y), $(dts_initrd-y)) -targets += $(image-y) $(dts_initrd-y) - -$(addprefix $(obj)/, $(dts_initrd-y)): $(obj)/ramdisk.image.gz +# If CONFIG_WANT_DEVICE_TREE is set and CONFIG_DEVICE_TREE isn't an +# empty string, define 'dts' to be path to the dts +# CONFIG_DEVICE_TREE will have "" around it, make sure to strip them +ifeq ($(CONFIG_WANT_DEVICE_TREE),y) +ifneq ($(CONFIG_DEVICE_TREE),"") +dts = $(if $(shell echo $(CONFIG_DEVICE_TREE) | grep '^/'),\ + ,$(srctree)/$(src)/dts/)$(CONFIG_DEVICE_TREE:"%"=%) +endif +endif # Don't put the ramdisk on the pattern rule; when its missing make will try # the pattern rule with less dependencies that also matches (even with the # hard dependency listed). -$(obj)/zImage.dts_initrd.%: vmlinux $(wrapperbits) $(dts) $(obj)/ramdisk.image.gz +$(obj)/zImage.initrd.%: vmlinux $(wrapperbits) $(dts) $(call if_changed,wrap,$*,$(dts),,$(obj)/ramdisk.image.gz) -$(obj)/zImage.dts.%: vmlinux $(wrapperbits) $(dts) +$(obj)/zImage.%: vmlinux $(wrapperbits) $(dts) $(call if_changed,wrap,$*,$(dts)) -$(obj)/zImage.initrd.%: vmlinux $(wrapperbits) - $(call if_changed,wrap,$*,,,$(obj)/ramdisk.image.gz) - -$(obj)/zImage.%: vmlinux $(wrapperbits) - $(call if_changed,wrap,$*) - -$(obj)/zImage.iseries: vmlinux +# This cannot be in the root of $(src) as the zImage rule always adds a $(obj) +# prefix +$(obj)/vmlinux.strip: vmlinux $(STRIP) -s -R .comment $< -o $@ -$(obj)/zImage.ps3: vmlinux +$(obj)/zImage.iseries: vmlinux $(STRIP) -s -R .comment $< -o $@ -$(obj)/zImage.initrd.ps3: vmlinux - @echo " WARNING zImage.initrd.ps3 not supported (yet)" - -$(obj)/zImage.holly-elf: vmlinux $(wrapperbits) - $(call if_changed,wrap,holly,$(obj)/dts/holly.dts,,) +$(obj)/zImage.ps3: vmlinux $(wrapper) $(wrapperbits) $(srctree)/$(src)/dts/ps3.dts + $(STRIP) -s -R .comment $< -o vmlinux.strip + $(call cmd,wrap,ps3,$(srctree)/$(src)/dts/ps3.dts,,) -$(obj)/zImage.initrd.holly-elf: vmlinux $(wrapperbits) $(obj)/ramdisk.image.gz - $(call if_changed,wrap,holly,$(obj)/dts/holly.dts,,$(obj)/ramdisk.image.gz) +$(obj)/zImage.initrd.ps3: vmlinux $(wrapper) $(wrapperbits) $(srctree)/$(src)/dts/ps3.dts $(obj)/ramdisk.image.gz + $(call cmd,wrap,ps3,$(srctree)/$(src)/dts/ps3.dts,,$(obj)/ramdisk.image.gz) $(obj)/uImage: vmlinux $(wrapperbits) $(call if_changed,wrap,uboot) -# CONFIG_DEVICE_TREE will have "" around it, make sure to strip them -dts = $(if $(shell echo $(CONFIG_DEVICE_TREE) | grep '^/'),\ - ,$(srctree)/$(src)/dts/)$(CONFIG_DEVICE_TREE:"%"=%) - $(obj)/cuImage.%: vmlinux $(dts) $(wrapperbits) $(call if_changed,wrap,cuboot-$*,$(dts)) @@ -215,22 +204,22 @@ $(obj)/treeImage.initrd.%: vmlinux $(dts) $(wrapperbits) $(obj)/treeImage.%: vmlinux $(dts) $(wrapperbits) $(call if_changed,wrap,treeboot-$*,$(dts)) +# If there isn't a platform selected then just strip the vmlinux. +ifeq (,$(image-y)) +image-y := vmlinux.strip +endif + $(obj)/zImage: $(addprefix $(obj)/, $(image-y)) @rm -f $@; ln $< $@ $(obj)/zImage.initrd: $(addprefix $(obj)/, $(initrd-y)) @rm -f $@; ln $< $@ -$(obj)/zImage.dts: $(addprefix $(obj)/, $(dts-y)) - @rm -f $@; ln $< $@ -$(obj)/zImage.dts_initrd: $(addprefix $(obj)/, $(dts_initrd-y)) - @rm -f $@; ln $< $@ - install: $(CONFIGURE) $(addprefix $(obj)/, $(image-y)) sh -x $(srctree)/$(src)/install.sh "$(KERNELRELEASE)" vmlinux System.map "$(INSTALL_PATH)" $< # anything not in $(targets) -clean-files += $(image-) $(initrd-) zImage zImage.initrd cuImage.* \ - treeImage.* zImage.dts zImage.dts_initrd +clean-files += $(image-) $(initrd-) zImage zImage.initrd cuImage.* treeImage.* \ + otheros.bld # clean up files cached by wrapper clean-kernel := vmlinux.strip vmlinux.bin diff --git a/arch/powerpc/boot/cuboot-83xx.c b/arch/powerpc/boot/cuboot-83xx.c index 9af554eea54..296025d8b29 100644 --- a/arch/powerpc/boot/cuboot-83xx.c +++ b/arch/powerpc/boot/cuboot-83xx.c @@ -12,12 +12,12 @@ #include "ops.h" #include "stdio.h" +#include "cuboot.h" #define TARGET_83xx #include "ppcboot.h" static bd_t bd; -extern char _end[]; extern char _dtb_start[], _dtb_end[]; static void platform_fixups(void) @@ -52,16 +52,7 @@ static void platform_fixups(void) void platform_init(unsigned long r3, unsigned long r4, unsigned long r5, unsigned long r6, unsigned long r7) { - unsigned long end_of_ram = bd.bi_memstart + bd.bi_memsize; - unsigned long avail_ram = end_of_ram - (unsigned long)_end; - - memcpy(&bd, (bd_t *)r3, sizeof(bd)); - loader_info.initrd_addr = r4; - loader_info.initrd_size = r4 ? r5 - r4 : 0; - loader_info.cmdline = (char *)r6; - loader_info.cmdline_len = r7 - r6; - - simple_alloc_init(_end, avail_ram - 1024*1024, 32, 64); + CUBOOT_INIT(); ft_init(_dtb_start, _dtb_end - _dtb_start, 32); serial_console_init(); platform_ops.fixups = platform_fixups; diff --git a/arch/powerpc/boot/cuboot-85xx.c b/arch/powerpc/boot/cuboot-85xx.c index e2560317f27..10f0f697c93 100644 --- a/arch/powerpc/boot/cuboot-85xx.c +++ b/arch/powerpc/boot/cuboot-85xx.c @@ -12,12 +12,12 @@ #include "ops.h" #include "stdio.h" +#include "cuboot.h" #define TARGET_85xx #include "ppcboot.h" static bd_t bd; -extern char _end[]; extern char _dtb_start[], _dtb_end[]; static void platform_fixups(void) @@ -53,16 +53,7 @@ static void platform_fixups(void) void platform_init(unsigned long r3, unsigned long r4, unsigned long r5, unsigned long r6, unsigned long r7) { - unsigned long end_of_ram = bd.bi_memstart + bd.bi_memsize; - unsigned long avail_ram = end_of_ram - (unsigned long)_end; - - memcpy(&bd, (bd_t *)r3, sizeof(bd)); - loader_info.initrd_addr = r4; - loader_info.initrd_size = r4 ? r5 - r4 : 0; - loader_info.cmdline = (char *)r6; - loader_info.cmdline_len = r7 - r6; - - simple_alloc_init(_end, avail_ram - 1024*1024, 32, 64); + CUBOOT_INIT(); ft_init(_dtb_start, _dtb_end - _dtb_start, 32); serial_console_init(); platform_ops.fixups = platform_fixups; diff --git a/arch/powerpc/boot/cuboot-ebony.c b/arch/powerpc/boot/cuboot-ebony.c index 4464c5f67ac..c5f37ce172e 100644 --- a/arch/powerpc/boot/cuboot-ebony.c +++ b/arch/powerpc/boot/cuboot-ebony.c @@ -15,28 +15,16 @@ #include "ops.h" #include "stdio.h" #include "44x.h" +#include "cuboot.h" #define TARGET_44x #include "ppcboot.h" static bd_t bd; -extern char _end[]; - -BSS_STACK(4096); void platform_init(unsigned long r3, unsigned long r4, unsigned long r5, unsigned long r6, unsigned long r7) { - unsigned long end_of_ram = bd.bi_memstart + bd.bi_memsize; - unsigned long avail_ram = end_of_ram - (unsigned long)_end; - - memcpy(&bd, (bd_t *)r3, sizeof(bd)); - loader_info.initrd_addr = r4; - loader_info.initrd_size = r4 ? r5 : 0; - loader_info.cmdline = (char *)r6; - loader_info.cmdline_len = r7 - r6; - - simple_alloc_init(_end, avail_ram, 32, 64); - + CUBOOT_INIT(); ebony_init(&bd.bi_enetaddr, &bd.bi_enet1addr); } diff --git a/arch/powerpc/boot/cuboot.c b/arch/powerpc/boot/cuboot.c new file mode 100644 index 00000000000..65795468ad6 --- /dev/null +++ b/arch/powerpc/boot/cuboot.c @@ -0,0 +1,35 @@ +/* + * Compatibility for old (not device tree aware) U-Boot versions + * + * Author: Scott Wood <scottwood@freescale.com> + * Consolidated using macros by David Gibson <david@gibson.dropbear.id.au> + * + * Copyright 2007 David Gibson, IBM Corporation. + * Copyright (c) 2007 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#include "ops.h" +#include "stdio.h" + +#include "ppcboot.h" + +extern char _end[]; +extern char _dtb_start[], _dtb_end[]; + +void cuboot_init(unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7, + unsigned long end_of_ram) +{ + unsigned long avail_ram = end_of_ram - (unsigned long)_end; + + loader_info.initrd_addr = r4; + loader_info.initrd_size = r4 ? r5 - r4 : 0; + loader_info.cmdline = (char *)r6; + loader_info.cmdline_len = r7 - r6; + + simple_alloc_init(_end, avail_ram - 1024*1024, 32, 64); +} diff --git a/arch/powerpc/boot/cuboot.h b/arch/powerpc/boot/cuboot.h new file mode 100644 index 00000000000..cd2aa7f348f --- /dev/null +++ b/arch/powerpc/boot/cuboot.h @@ -0,0 +1,14 @@ +#ifndef _PPC_BOOT_CUBOOT_H_ +#define _PPC_BOOT_CUBOOT_H_ + +void cuboot_init(unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7, + unsigned long end_of_ram); + +#define CUBOOT_INIT() \ + do { \ + memcpy(&bd, (bd_t *)r3, sizeof(bd)); \ + cuboot_init(r4, r5, r6, r7, bd.bi_memstart + bd.bi_memsize); \ + } while (0) + +#endif /* _PPC_BOOT_CUBOOT_H_ */ diff --git a/arch/powerpc/boot/dcr.h b/arch/powerpc/boot/dcr.h index 877bc97b1e9..14b44aa96fe 100644 --- a/arch/powerpc/boot/dcr.h +++ b/arch/powerpc/boot/dcr.h @@ -26,6 +26,43 @@ static const unsigned long sdram_bxcr[] = { SDRAM0_B0CR, SDRAM0_B1CR, SDRAM0_B2C #define SDRAM_CONFIG_BANK_SIZE(reg) \ (0x00400000 << ((reg & SDRAM_CONFIG_SIZE_MASK) >> 17)) +/* 440GP External Bus Controller (EBC) */ +#define DCRN_EBC0_CFGADDR 0x012 +#define DCRN_EBC0_CFGDATA 0x013 +#define EBC_NUM_BANKS 8 +#define EBC_B0CR 0x00 +#define EBC_B1CR 0x01 +#define EBC_B2CR 0x02 +#define EBC_B3CR 0x03 +#define EBC_B4CR 0x04 +#define EBC_B5CR 0x05 +#define EBC_B6CR 0x06 +#define EBC_B7CR 0x07 +#define EBC_BXCR(n) (n) +#define EBC_BXCR_BAS 0xfff00000 +#define EBC_BXCR_BS 0x000e0000 +#define EBC_BXCR_BANK_SIZE(reg) \ + (0x100000 << (((reg) & EBC_BXCR_BS) >> 17)) +#define EBC_BXCR_BU 0x00018000 +#define EBC_BXCR_BU_OFF 0x00000000 +#define EBC_BXCR_BU_RO 0x00008000 +#define EBC_BXCR_BU_WO 0x00010000 +#define EBC_BXCR_BU_RW 0x00018000 +#define EBC_BXCR_BW 0x00006000 +#define EBC_B0AP 0x10 +#define EBC_B1AP 0x11 +#define EBC_B2AP 0x12 +#define EBC_B3AP 0x13 +#define EBC_B4AP 0x14 +#define EBC_B5AP 0x15 +#define EBC_B6AP 0x16 +#define EBC_B7AP 0x17 +#define EBC_BXAP(n) (0x10+(n)) +#define EBC_BEAR 0x20 +#define EBC_BESR 0x21 +#define EBC_CFG 0x23 +#define EBC_CID 0x24 + /* 440GP Clock, PM, chip control */ #define DCRN_CPC0_SR 0x0b0 #define DCRN_CPC0_ER 0x0b1 diff --git a/arch/powerpc/boot/dts/ebony.dts b/arch/powerpc/boot/dts/ebony.dts index 0ec02f4726b..c5f99613fc7 100644 --- a/arch/powerpc/boot/dts/ebony.dts +++ b/arch/powerpc/boot/dts/ebony.dts @@ -31,8 +31,8 @@ reg = <0>; clock-frequency = <0>; // Filled in by zImage timebase-frequency = <0>; // Filled in by zImage - i-cache-line-size = <32>; - d-cache-line-size = <32>; + i-cache-line-size = <20>; + d-cache-line-size = <20>; i-cache-size = <8000>; /* 32 kB */ d-cache-size = <8000>; /* 32 kB */ dcr-controller; @@ -135,11 +135,9 @@ #address-cells = <2>; #size-cells = <1>; clock-frequency = <0>; // Filled in by zImage - ranges = <0 00000000 fff00000 100000 - 1 00000000 48000000 100000 - 2 00000000 ff800000 400000 - 3 00000000 48200000 100000 - 7 00000000 48300000 100000>; + // ranges property is supplied by zImage + // based on firmware's configuration of the + // EBC bridge interrupts = <5 4>; interrupt-parent = <&UIC1>; diff --git a/arch/powerpc/boot/dts/holly.dts b/arch/powerpc/boot/dts/holly.dts index 254499b107f..80a4fab8ee3 100644 --- a/arch/powerpc/boot/dts/holly.dts +++ b/arch/powerpc/boot/dts/holly.dts @@ -46,7 +46,7 @@ tsi109@c0000000 { device_type = "tsi-bridge"; - compatible = "tsi-bridge"; + compatible = "tsi109-bridge", "tsi108-bridge"; #address-cells = <1>; #size-cells = <1>; ranges = <00000000 c0000000 00010000>; @@ -54,52 +54,55 @@ i2c@7000 { device_type = "i2c"; - compatible = "tsi-i2c"; - interrupt-parent = < &/tsi109@c0000000/pic@7400 >; + compatible = "tsi109-i2c", "tsi108-i2c"; + interrupt-parent = <&MPIC>; interrupts = <e 2>; reg = <7000 400>; }; - mdio@6000 { + MDIO: mdio@6000 { device_type = "mdio"; - compatible = "tsi-ethernet"; + compatible = "tsi109-mdio", "tsi108-mdio"; + reg = <6000 50>; + #address-cells = <1>; + #size-cells = <0>; - PHY1: ethernet-phy@6000 { - device_type = "ethernet-phy"; - compatible = "bcm54xx"; - reg = <6000 50>; - phy-id = <1>; + PHY1: ethernet-phy@1 { + compatible = "bcm5461a"; + reg = <1>; + txc-rxc-delay-disable; }; - PHY2: ethernet-phy@6400 { - device_type = "ethernet-phy"; - compatible = "bcm54xx"; - reg = <6000 50>; - phy-id = <2>; + PHY2: ethernet-phy@2 { + compatible = "bcm5461a"; + reg = <2>; + txc-rxc-delay-disable; }; }; ethernet@6200 { device_type = "network"; - compatible = "tsi-ethernet"; + compatible = "tsi109-ethernet", "tsi108-ethernet"; #address-cells = <1>; #size-cells = <0>; reg = <6000 200>; local-mac-address = [ 00 00 00 00 00 00 ]; - interrupt-parent = < &/tsi109@c0000000/pic@7400 >; + interrupt-parent = <&MPIC>; interrupts = <10 2>; + mdio-handle = <&MDIO>; phy-handle = <&PHY1>; }; ethernet@6600 { device_type = "network"; - compatible = "tsi-ethernet"; + compatible = "tsi109-ethernet", "tsi108-ethernet"; #address-cells = <1>; #size-cells = <0>; reg = <6400 200>; local-mac-address = [ 00 00 00 00 00 00 ]; - interrupt-parent = < &/tsi109@c0000000/pic@7400 >; + interrupt-parent = <&MPIC>; interrupts = <11 2>; + mdio-handle = <&MDIO>; phy-handle = <&PHY2>; }; @@ -110,7 +113,7 @@ virtual-reg = <c0007808>; clock-frequency = <3F9C6000>; current-speed = <1c200>; - interrupt-parent = < &/tsi109@c0000000/pic@7400 >; + interrupt-parent = <&MPIC>; interrupts = <c 2>; }; @@ -121,7 +124,7 @@ virtual-reg = <c0007c08>; clock-frequency = <3F9C6000>; current-speed = <1c200>; - interrupt-parent = < &/tsi109@c0000000/pic@7400 >; + interrupt-parent = <&MPIC>; interrupts = <d 2>; }; @@ -136,7 +139,7 @@ pci@1000 { device_type = "pci"; - compatible = "tsi109"; + compatible = "tsi109-pci", "tsi108-pci"; #interrupt-cells = <1>; #size-cells = <2>; #address-cells = <3>; @@ -150,7 +153,7 @@ ranges = <02000000 0 40000000 40000000 0 10000000 01000000 0 00000000 7e000000 0 00010000>; clock-frequency = <7f28154>; - interrupt-parent = < &/tsi109@c0000000/pic@7400 >; + interrupt-parent = <&MPIC>; interrupts = <17 2>; interrupt-map-mask = <f800 0 0 7>; /*----------------------------------------------------+ @@ -186,13 +189,12 @@ #address-cells = <0>; #interrupt-cells = <2>; interrupts = <17 2>; - interrupt-parent = < &/tsi109@c0000000/pic@7400 >; + interrupt-parent = <&MPIC>; }; }; }; chosen { linux,stdout-path = "/tsi109@c0000000/serial@7808"; - bootargs = "console=ttyS0,115200"; }; }; diff --git a/arch/powerpc/boot/dts/mpc7448hpc2.dts b/arch/powerpc/boot/dts/mpc7448hpc2.dts index 765c306ecf8..0e3d314a715 100644 --- a/arch/powerpc/boot/dts/mpc7448hpc2.dts +++ b/arch/powerpc/boot/dts/mpc7448hpc2.dts @@ -45,7 +45,7 @@ #address-cells = <1>; #size-cells = <1>; #interrupt-cells = <2>; - device_type = "tsi-bridge"; + device_type = "tsi108-bridge"; ranges = <00000000 c0000000 00010000>; reg = <c0000000 00010000>; bus-frequency = <0>; @@ -55,27 +55,26 @@ interrupts = <E 0>; reg = <7000 400>; device_type = "i2c"; - compatible = "tsi-i2c"; + compatible = "tsi108-i2c"; }; - mdio@6000 { + MDIO: mdio@6000 { device_type = "mdio"; - compatible = "tsi-ethernet"; + compatible = "tsi108-mdio"; + reg = <6000 50>; + #address-cells = <1>; + #size-cells = <0>; - phy8: ethernet-phy@6000 { + phy8: ethernet-phy@8 { interrupt-parent = <&mpic>; interrupts = <2 1>; - reg = <6000 50>; - phy-id = <8>; - device_type = "ethernet-phy"; + reg = <8>; }; - phy9: ethernet-phy@6400 { + phy9: ethernet-phy@9 { interrupt-parent = <&mpic>; interrupts = <2 1>; - reg = <6000 50>; - phy-id = <9>; - device_type = "ethernet-phy"; + reg = <9>; }; }; @@ -83,12 +82,12 @@ ethernet@6200 { #size-cells = <0>; device_type = "network"; - model = "TSI-ETH"; - compatible = "tsi-ethernet"; + compatible = "tsi108-ethernet"; reg = <6000 200>; address = [ 00 06 D2 00 00 01 ]; interrupts = <10 2>; interrupt-parent = <&mpic>; + mdio-handle = <&MDIO>; phy-handle = <&phy8>; }; @@ -96,12 +95,12 @@ #address-cells = <1>; #size-cells = <0>; device_type = "network"; - model = "TSI-ETH"; - compatible = "tsi-ethernet"; + compatible = "tsi108-ethernet"; reg = <6400 200>; address = [ 00 06 D2 00 00 02 ]; interrupts = <11 2>; interrupt-parent = <&mpic>; + mdio-handle = <&MDIO>; phy-handle = <&phy9>; }; @@ -135,7 +134,7 @@ big-endian; }; pci@1000 { - compatible = "tsi10x"; + compatible = "tsi108-pci"; device_type = "pci"; #interrupt-cells = <1>; #size-cells = <2>; diff --git a/arch/powerpc/boot/dts/mpc8272ads.dts b/arch/powerpc/boot/dts/mpc8272ads.dts index 423eedcf634..1934b800278 100644 --- a/arch/powerpc/boot/dts/mpc8272ads.dts +++ b/arch/powerpc/boot/dts/mpc8272ads.dts @@ -14,12 +14,10 @@ compatible = "MPC8260ADS"; #address-cells = <1>; #size-cells = <1>; - linux,phandle = <100>; cpus { #address-cells = <1>; #size-cells = <0>; - linux,phandle = <200>; PowerPC,8272@0 { device_type = "cpu"; @@ -32,12 +30,10 @@ bus-frequency = <0>; clock-frequency = <0>; 32-bit; - linux,phandle = <201>; }; }; - interrupt-controller@f8200000 { - linux,phandle = <f8200000>; + pci_pic: interrupt-controller@f8200000 { #address-cells = <0>; #interrupt-cells = <2>; interrupt-controller; @@ -47,15 +43,13 @@ }; memory { device_type = "memory"; - linux,phandle = <300>; reg = <00000000 4000000 f4500000 00000020>; }; chosen { name = "chosen"; linux,platform = <0>; - interrupt-controller = <10c00>; - linux,phandle = <400>; + interrupt-controller = <&Cpm_pic>; }; soc8272@f0000000 { @@ -70,20 +64,17 @@ device_type = "mdio"; compatible = "fs_enet"; reg = <0 0>; - linux,phandle = <24520>; #address-cells = <1>; #size-cells = <0>; - ethernet-phy@0 { - linux,phandle = <2452000>; - interrupt-parent = <10c00>; + phy0:ethernet-phy@0 { + interrupt-parent = <&Cpm_pic>; interrupts = <17 4>; reg = <0>; bitbang = [ 12 12 13 02 02 01 ]; device_type = "ethernet-phy"; }; - ethernet-phy@1 { - linux,phandle = <2452001>; - interrupt-parent = <10c00>; + phy1:ethernet-phy@1 { + interrupt-parent = <&Cpm_pic>; interrupts = <17 4>; bitbang = [ 12 12 13 02 02 01 ]; reg = <3>; @@ -101,8 +92,8 @@ reg = <11300 20 8400 100 11380 30>; mac-address = [ 00 11 2F 99 43 54 ]; interrupts = <20 2>; - interrupt-parent = <10c00>; - phy-handle = <2452000>; + interrupt-parent = <&Cpm_pic>; + phy-handle = <&Phy0>; rx-clock = <13>; tx-clock = <12>; }; @@ -115,14 +106,13 @@ reg = <11320 20 8500 100 113b0 30>; mac-address = [ 00 11 2F 99 44 54 ]; interrupts = <21 2>; - interrupt-parent = <10c00>; - phy-handle = <2452001>; + interrupt-parent = <&Cpm_pic>; + phy-handle = <&Phy1>; rx-clock = <17>; tx-clock = <18>; }; cpm@f0000000 { - linux,phandle = <f0000000>; #address-cells = <1>; #size-cells = <1>; #interrupt-cells = <2>; @@ -142,7 +132,7 @@ reg = <11a00 20 8000 100>; current-speed = <1c200>; interrupts = <28 2>; - interrupt-parent = <10c00>; + interrupt-parent = <&Cpm_pic>; clock-setup = <0 00ffffff>; rx-clock = <1>; tx-clock = <1>; @@ -156,15 +146,14 @@ reg = <11a60 20 8300 100>; current-speed = <1c200>; interrupts = <2b 2>; - interrupt-parent = <10c00>; + interrupt-parent = <&Cpm_pic>; clock-setup = <1b ffffff00>; rx-clock = <4>; tx-clock = <4>; }; }; - interrupt-controller@10c00 { - linux,phandle = <10c00>; + cpm_pic:interrupt-controller@10c00 { #address-cells = <0>; #interrupt-cells = <2>; interrupt-controller; @@ -174,7 +163,6 @@ compatible = "CPM2"; }; pci@0500 { - linux,phandle = <0500>; #interrupt-cells = <1>; #size-cells = <2>; #address-cells = <3>; @@ -202,7 +190,7 @@ c000 0 0 2 f8200000 43 8 c000 0 0 3 f8200000 40 8 c000 0 0 4 f8200000 41 8>; - interrupt-parent = <10c00>; + interrupt-parent = <&Cpm_pic>; interrupts = <14 8>; bus-range = <0 0>; ranges = <02000000 0 80000000 80000000 0 40000000 @@ -216,7 +204,7 @@ compatible = "talitos"; reg = <30000 10000>; interrupts = <b 2>; - interrupt-parent = <10c00>; + interrupt-parent = <&Cpm_pic>; num-channels = <4>; channel-fifo-len = <18>; exec-units-mask = <0000007e>; diff --git a/arch/powerpc/boot/dts/mpc832x_mds.dts b/arch/powerpc/boot/dts/mpc832x_mds.dts index 112dd5198fe..4fc0c4d34aa 100644 --- a/arch/powerpc/boot/dts/mpc832x_mds.dts +++ b/arch/powerpc/boot/dts/mpc832x_mds.dts @@ -272,7 +272,13 @@ reg = <2200 200>; interrupts = <22>; interrupt-parent = < &qeic >; - mac-address = [ 00 04 9f 00 23 23 ]; + /* + * mac-address is deprecated and will be removed + * in 2.6.25. Only recent versions of + * U-Boot support local-mac-address, however. + */ + mac-address = [ 00 00 00 00 00 00 ]; + local-mac-address = [ 00 00 00 00 00 00 ]; rx-clock = <19>; tx-clock = <1a>; phy-handle = < &phy3 >; @@ -287,7 +293,13 @@ reg = <3000 200>; interrupts = <23>; interrupt-parent = < &qeic >; - mac-address = [ 00 11 22 33 44 55 ]; + /* + * mac-address is deprecated and will be removed + * in 2.6.25. Only recent versions of + * U-Boot support local-mac-address, however. + */ + mac-address = [ 00 00 00 00 00 00 ]; + local-mac-address = [ 00 00 00 00 00 00 ]; rx-clock = <17>; tx-clock = <18>; phy-handle = < &phy4 >; diff --git a/arch/powerpc/boot/dts/mpc832x_rdb.dts b/arch/powerpc/boot/dts/mpc832x_rdb.dts index be4c35784e4..447c03ffabb 100644 --- a/arch/powerpc/boot/dts/mpc832x_rdb.dts +++ b/arch/powerpc/boot/dts/mpc832x_rdb.dts @@ -231,7 +231,13 @@ reg = <3000 200>; interrupts = <21>; interrupt-parent = <&qeic>; - mac-address = [ 00 04 9f ef 03 02 ]; + /* + * mac-address is deprecated and will be removed + * in 2.6.25. Only recent versions of + * U-Boot support local-mac-address, however. + */ + mac-address = [ 00 00 00 00 00 00 ]; + local-mac-address = [ 00 00 00 00 00 00 ]; rx-clock = <20>; tx-clock = <13>; phy-handle = <&phy00>; @@ -246,7 +252,13 @@ reg = <2200 200>; interrupts = <22>; interrupt-parent = <&qeic>; - mac-address = [ 00 04 9f ef 03 01 ]; + /* + * mac-address is deprecated and will be removed + * in 2.6.25. Only recent versions of + * U-Boot support local-mac-address, however. + */ + mac-address = [ 00 00 00 00 00 00 ]; + local-mac-address = [ 00 00 00 00 00 00 ]; rx-clock = <19>; tx-clock = <1a>; phy-handle = <&phy04>; diff --git a/arch/powerpc/boot/dts/mpc8349emitx.dts b/arch/powerpc/boot/dts/mpc8349emitx.dts index db0d0030327..ae9bca57545 100644 --- a/arch/powerpc/boot/dts/mpc8349emitx.dts +++ b/arch/powerpc/boot/dts/mpc8349emitx.dts @@ -131,6 +131,11 @@ model = "TSEC"; compatible = "gianfar"; reg = <24000 1000>; + /* + * address is deprecated and will be removed + * in 2.6.25. Only recent versions of + * U-Boot support local-mac-address, however. + */ address = [ 00 00 00 00 00 00 ]; local-mac-address = [ 00 00 00 00 00 00 ]; interrupts = <20 8 21 8 22 8>; @@ -145,6 +150,11 @@ model = "TSEC"; compatible = "gianfar"; reg = <25000 1000>; + /* + * address is deprecated and will be removed + * in 2.6.25. Only recent versions of + * U-Boot support local-mac-address, however. + */ address = [ 00 00 00 00 00 00 ]; local-mac-address = [ 00 00 00 00 00 00 ]; interrupts = <23 8 24 8 25 8>; diff --git a/arch/powerpc/boot/dts/mpc834x_mds.dts b/arch/powerpc/boot/dts/mpc834x_mds.dts index df773fafe9d..310e877826b 100644 --- a/arch/powerpc/boot/dts/mpc834x_mds.dts +++ b/arch/powerpc/boot/dts/mpc834x_mds.dts @@ -136,6 +136,11 @@ model = "TSEC"; compatible = "gianfar"; reg = <24000 1000>; + /* + * address is deprecated and will be removed + * in 2.6.25. Only recent versions of + * U-Boot support local-mac-address, however. + */ address = [ 00 00 00 00 00 00 ]; local-mac-address = [ 00 00 00 00 00 00 ]; interrupts = <20 8 21 8 22 8>; @@ -150,6 +155,11 @@ model = "TSEC"; compatible = "gianfar"; reg = <25000 1000>; + /* + * address is deprecated and will be removed + * in 2.6.25. Only recent versions of + * U-Boot support local-mac-address, however. + */ address = [ 00 00 00 00 00 00 ]; local-mac-address = [ 00 00 00 00 00 00 ]; interrupts = <23 8 24 8 25 8>; diff --git a/arch/powerpc/boot/dts/mpc836x_mds.dts b/arch/powerpc/boot/dts/mpc836x_mds.dts index 38c8594df3a..1e914f31dd9 100644 --- a/arch/powerpc/boot/dts/mpc836x_mds.dts +++ b/arch/powerpc/boot/dts/mpc836x_mds.dts @@ -301,7 +301,13 @@ reg = <2000 200>; interrupts = <20>; interrupt-parent = < &qeic >; - mac-address = [ 00 04 9f 00 23 23 ]; + /* + * mac-address is deprecated and will be removed + * in 2.6.25. Only recent versions of + * U-Boot support local-mac-address, however. + */ + mac-address = [ 00 00 00 00 00 00 ]; + local-mac-address = [ 00 00 00 00 00 00 ]; rx-clock = <0>; tx-clock = <19>; phy-handle = < &phy0 >; @@ -317,7 +323,13 @@ reg = <3000 200>; interrupts = <21>; interrupt-parent = < &qeic >; - mac-address = [ 00 11 22 33 44 55 ]; + /* + * mac-address is deprecated and will be removed + * in 2.6.25. Only recent versions of + * U-Boot support local-mac-address, however. + */ + mac-address = [ 00 00 00 00 00 00 ]; + local-mac-address = [ 00 00 00 00 00 00 ]; rx-clock = <0>; tx-clock = <14>; phy-handle = < &phy1 >; diff --git a/arch/powerpc/boot/dts/mpc8540ads.dts b/arch/powerpc/boot/dts/mpc8540ads.dts index d91e81c009f..364a969f5c2 100644 --- a/arch/powerpc/boot/dts/mpc8540ads.dts +++ b/arch/powerpc/boot/dts/mpc8540ads.dts @@ -52,7 +52,7 @@ compatible = "fsl,8540-memory-controller"; reg = <2000 1000>; interrupt-parent = <&mpic>; - interrupts = <2 2>; + interrupts = <12 2>; }; l2-cache-controller@20000 { @@ -61,14 +61,14 @@ cache-line-size = <20>; // 32 bytes cache-size = <40000>; // L2, 256K interrupt-parent = <&mpic>; - interrupts = <0 2>; + interrupts = <10 2>; }; i2c@3000 { device_type = "i2c"; compatible = "fsl-i2c"; reg = <3000 100>; - interrupts = <1b 2>; + interrupts = <2b 2>; interrupt-parent = <&mpic>; dfsrr; }; @@ -81,19 +81,19 @@ reg = <24520 20>; phy0: ethernet-phy@0 { interrupt-parent = <&mpic>; - interrupts = <35 1>; + interrupts = <5 1>; reg = <0>; device_type = "ethernet-phy"; }; phy1: ethernet-phy@1 { interrupt-parent = <&mpic>; - interrupts = <35 1>; + interrupts = <5 1>; reg = <1>; device_type = "ethernet-phy"; }; phy3: ethernet-phy@3 { interrupt-parent = <&mpic>; - interrupts = <37 1>; + interrupts = <7 1>; reg = <3>; device_type = "ethernet-phy"; }; @@ -106,9 +106,14 @@ model = "TSEC"; compatible = "gianfar"; reg = <24000 1000>; - address = [ 00 E0 0C 00 73 00 ]; - local-mac-address = [ 00 E0 0C 00 73 00 ]; - interrupts = <d 2 e 2 12 2>; + /* + * address is deprecated and will be removed + * in 2.6.25. Only recent versions of + * U-Boot support local-mac-address, however. + */ + address = [ 00 00 00 00 00 00 ]; + local-mac-address = [ 00 00 00 00 00 00 ]; + interrupts = <1d 2 1e 2 22 2>; interrupt-parent = <&mpic>; phy-handle = <&phy0>; }; @@ -120,9 +125,14 @@ model = "TSEC"; compatible = "gianfar"; reg = <25000 1000>; - address = [ 00 E0 0C 00 73 01 ]; - local-mac-address = [ 00 E0 0C 00 73 01 ]; - interrupts = <13 2 14 2 18 2>; + /* + * address is deprecated and will be removed + * in 2.6.25. Only recent versions of + * U-Boot support local-mac-address, however. + */ + address = [ 00 00 00 00 00 00 ]; + local-mac-address = [ 00 00 00 00 00 00 ]; + interrupts = <23 2 24 2 28 2>; interrupt-parent = <&mpic>; phy-handle = <&phy1>; }; @@ -134,9 +144,14 @@ model = "FEC"; compatible = "gianfar"; reg = <26000 1000>; - address = [ 00 E0 0C 00 73 02 ]; - local-mac-address = [ 00 E0 0C 00 73 02 ]; - interrupts = <19 2>; + /* + * address is deprecated and will be removed + * in 2.6.25. Only recent versions of + * U-Boot support local-mac-address, however. + */ + address = [ 00 00 00 00 00 00 ]; + local-mac-address = [ 00 00 00 00 00 00 ]; + interrupts = <29 2>; interrupt-parent = <&mpic>; phy-handle = <&phy3>; }; @@ -146,7 +161,7 @@ compatible = "ns16550"; reg = <4500 100>; // reg base, size clock-frequency = <0>; // should we fill in in uboot? - interrupts = <1a 2>; + interrupts = <2a 2>; interrupt-parent = <&mpic>; }; @@ -155,7 +170,7 @@ compatible = "ns16550"; reg = <4600 100>; // reg base, size clock-frequency = <0>; // should we fill in in uboot? - interrupts = <1a 2>; + interrupts = <2a 2>; interrupt-parent = <&mpic>; }; pci@8000 { @@ -163,78 +178,78 @@ interrupt-map = < /* IDSEL 0x02 */ - 1000 0 0 1 &mpic 31 1 - 1000 0 0 2 &mpic 32 1 - 1000 0 0 3 &mpic 33 1 - 1000 0 0 4 &mpic 34 1 + 1000 0 0 1 &mpic 1 1 + 1000 0 0 2 &mpic 2 1 + 1000 0 0 3 &mpic 3 1 + 1000 0 0 4 &mpic 4 1 /* IDSEL 0x03 */ - 1800 0 0 1 &mpic 34 1 - 1800 0 0 2 &mpic 31 1 - 1800 0 0 3 &mpic 32 1 - 1800 0 0 4 &mpic 33 1 + 1800 0 0 1 &mpic 4 1 + 1800 0 0 2 &mpic 1 1 + 1800 0 0 3 &mpic 2 1 + 1800 0 0 4 &mpic 3 1 /* IDSEL 0x04 */ - 2000 0 0 1 &mpic 33 1 - 2000 0 0 2 &mpic 34 1 - 2000 0 0 3 &mpic 31 1 - 2000 0 0 4 &mpic 32 1 + 2000 0 0 1 &mpic 3 1 + 2000 0 0 2 &mpic 4 1 + 2000 0 0 3 &mpic 1 1 + 2000 0 0 4 &mpic 2 1 /* IDSEL 0x05 */ - 2800 0 0 1 &mpic 32 1 - 2800 0 0 2 &mpic 33 1 - 2800 0 0 3 &mpic 34 1 - 2800 0 0 4 &mpic 31 1 + 2800 0 0 1 &mpic 2 1 + 2800 0 0 2 &mpic 3 1 + 2800 0 0 3 &mpic 4 1 + 2800 0 0 4 &mpic 1 1 /* IDSEL 0x0c */ - 6000 0 0 1 &mpic 31 1 - 6000 0 0 2 &mpic 32 1 - 6000 0 0 3 &mpic 33 1 - 6000 0 0 4 &mpic 34 1 + 6000 0 0 1 &mpic 1 1 + 6000 0 0 2 &mpic 2 1 + 6000 0 0 3 &mpic 3 1 + 6000 0 0 4 &mpic 4 1 /* IDSEL 0x0d */ - 6800 0 0 1 &mpic 34 1 - 6800 0 0 2 &mpic 31 1 - 6800 0 0 3 &mpic 32 1 - 6800 0 0 4 &mpic 33 1 + 6800 0 0 1 &mpic 4 1 + 6800 0 0 2 &mpic 1 1 + 6800 0 0 3 &mpic 2 1 + 6800 0 0 4 &mpic 3 1 /* IDSEL 0x0e */ - 7000 0 0 1 &mpic 33 1 - 7000 0 0 2 &mpic 34 1 - 7000 0 0 3 &mpic 31 1 - 7000 0 0 4 &mpic 32 1 + 7000 0 0 1 &mpic 3 1 + 7000 0 0 2 &mpic 4 1 + 7000 0 0 3 &mpic 1 1 + 7000 0 0 4 &mpic 2 1 /* IDSEL 0x0f */ - 7800 0 0 1 &mpic 32 1 - 7800 0 0 2 &mpic 33 1 - 7800 0 0 3 &mpic 34 1 - 7800 0 0 4 &mpic 31 1 + 7800 0 0 1 &mpic 2 1 + 7800 0 0 2 &mpic 3 1 + 7800 0 0 3 &mpic 4 1 + 7800 0 0 4 &mpic 1 1 /* IDSEL 0x12 */ - 9000 0 0 1 &mpic 31 1 - 9000 0 0 2 &mpic 32 1 - 9000 0 0 3 &mpic 33 1 - 9000 0 0 4 &mpic 34 1 + 9000 0 0 1 &mpic 1 1 + 9000 0 0 2 &mpic 2 1 + 9000 0 0 3 &mpic 3 1 + 9000 0 0 4 &mpic 4 1 /* IDSEL 0x13 */ - 9800 0 0 1 &mpic 34 1 - 9800 0 0 2 &mpic 31 1 - 9800 0 0 3 &mpic 32 1 - 9800 0 0 4 &mpic 33 1 + 9800 0 0 1 &mpic 4 1 + 9800 0 0 2 &mpic 1 1 + 9800 0 0 3 &mpic 2 1 + 9800 0 0 4 &mpic 3 1 /* IDSEL 0x14 */ - a000 0 0 1 &mpic 33 1 - a000 0 0 2 &mpic 34 1 - a000 0 0 3 &mpic 31 1 - a000 0 0 4 &mpic 32 1 + a000 0 0 1 &mpic 3 1 + a000 0 0 2 &mpic 4 1 + a000 0 0 3 &mpic 1 1 + a000 0 0 4 &mpic 2 1 /* IDSEL 0x15 */ - a800 0 0 1 &mpic 32 1 - a800 0 0 2 &mpic 33 1 - a800 0 0 3 &mpic 34 1 - a800 0 0 4 &mpic 31 1>; + a800 0 0 1 &mpic 2 1 + a800 0 0 2 &mpic 3 1 + a800 0 0 3 &mpic 4 1 + a800 0 0 4 &mpic 1 1>; interrupt-parent = <&mpic>; - interrupts = <08 2>; + interrupts = <18 2>; bus-range = <0 0>; ranges = <02000000 0 80000000 80000000 0 20000000 01000000 0 00000000 e2000000 0 00100000>; diff --git a/arch/powerpc/boot/dts/mpc8541cds.dts b/arch/powerpc/boot/dts/mpc8541cds.dts index 4f2c3af2e05..070206fffe8 100644 --- a/arch/powerpc/boot/dts/mpc8541cds.dts +++ b/arch/powerpc/boot/dts/mpc8541cds.dts @@ -52,7 +52,7 @@ compatible = "fsl,8541-memory-controller"; reg = <2000 1000>; interrupt-parent = <&mpic>; - interrupts = <2 2>; + interrupts = <12 2>; }; l2-cache-controller@20000 { @@ -61,14 +61,14 @@ cache-line-size = <20>; // 32 bytes cache-size = <40000>; // L2, 256K interrupt-parent = <&mpic>; - interrupts = <0 2>; + interrupts = <10 2>; }; i2c@3000 { device_type = "i2c"; compatible = "fsl-i2c"; reg = <3000 100>; - interrupts = <1b 2>; + interrupts = <2b 2>; interrupt-parent = <&mpic>; dfsrr; }; @@ -81,13 +81,13 @@ reg = <24520 20>; phy0: ethernet-phy@0 { interrupt-parent = <&mpic>; - interrupts = <35 0>; + interrupts = <5 1>; reg = <0>; device_type = "ethernet-phy"; }; phy1: ethernet-phy@1 { interrupt-parent = <&mpic>; - interrupts = <35 0>; + interrupts = <5 1>; reg = <1>; device_type = "ethernet-phy"; }; @@ -100,8 +100,8 @@ model = "TSEC"; compatible = "gianfar"; reg = <24000 1000>; - local-mac-address = [ 00 E0 0C 00 73 00 ]; - interrupts = <d 2 e 2 12 2>; + local-mac-address = [ 00 00 00 00 00 00 ]; + interrupts = <1d 2 1e 2 22 2>; interrupt-parent = <&mpic>; phy-handle = <&phy0>; }; @@ -113,8 +113,8 @@ model = "TSEC"; compatible = "gianfar"; reg = <25000 1000>; - local-mac-address = [ 00 E0 0C 00 73 01 ]; - interrupts = <13 2 14 2 18 2>; + local-mac-address = [ 00 00 00 00 00 00 ]; + interrupts = <23 2 24 2 28 2>; interrupt-parent = <&mpic>; phy-handle = <&phy1>; }; @@ -124,7 +124,7 @@ compatible = "ns16550"; reg = <4500 100>; // reg base, size clock-frequency = <0>; // should we fill in in uboot? - interrupts = <1a 2>; + interrupts = <2a 2>; interrupt-parent = <&mpic>; }; @@ -133,7 +133,7 @@ compatible = "ns16550"; reg = <4600 100>; // reg base, size clock-frequency = <0>; // should we fill in in uboot? - interrupts = <1a 2>; + interrupts = <2a 2>; interrupt-parent = <&mpic>; }; @@ -142,49 +142,49 @@ interrupt-map = < /* IDSEL 0x10 */ - 08000 0 0 1 &mpic 30 1 - 08000 0 0 2 &mpic 31 1 - 08000 0 0 3 &mpic 32 1 - 08000 0 0 4 &mpic 33 1 + 08000 0 0 1 &mpic 0 1 + 08000 0 0 2 &mpic 1 1 + 08000 0 0 3 &mpic 2 1 + 08000 0 0 4 &mpic 3 1 /* IDSEL 0x11 */ - 08800 0 0 1 &mpic 30 1 - 08800 0 0 2 &mpic 31 1 - 08800 0 0 3 &mpic 32 1 - 08800 0 0 4 &mpic 33 1 + 08800 0 0 1 &mpic 0 1 + 08800 0 0 2 &mpic 1 1 + 08800 0 0 3 &mpic 2 1 + 08800 0 0 4 &mpic 3 1 /* IDSEL 0x12 (Slot 1) */ - 09000 0 0 1 &mpic 30 1 - 09000 0 0 2 &mpic 31 1 - 09000 0 0 3 &mpic 32 1 - 09000 0 0 4 &mpic 33 1 + 09000 0 0 1 &mpic 0 1 + 09000 0 0 2 &mpic 1 1 + 09000 0 0 3 &mpic 2 1 + 09000 0 0 4 &mpic 3 1 /* IDSEL 0x13 (Slot 2) */ - 09800 0 0 1 &mpic 31 1 - 09800 0 0 2 &mpic 32 1 - 09800 0 0 3 &mpic 33 1 - 09800 0 0 4 &mpic 30 1 + 09800 0 0 1 &mpic 1 1 + 09800 0 0 2 &mpic 2 1 + 09800 0 0 3 &mpic 3 1 + 09800 0 0 4 &mpic 0 1 /* IDSEL 0x14 (Slot 3) */ - 0a000 0 0 1 &mpic 32 1 - 0a000 0 0 2 &mpic 33 1 - 0a000 0 0 3 &mpic 30 1 - 0a000 0 0 4 &mpic 31 1 + 0a000 0 0 1 &mpic 2 1 + 0a000 0 0 2 &mpic 3 1 + 0a000 0 0 3 &mpic 0 1 + 0a000 0 0 4 &mpic 1 1 /* IDSEL 0x15 (Slot 4) */ - 0a800 0 0 1 &mpic 33 1 - 0a800 0 0 2 &mpic 30 1 - 0a800 0 0 3 &mpic 31 1 - 0a800 0 0 4 &mpic 32 1 + 0a800 0 0 1 &mpic 3 1 + 0a800 0 0 2 &mpic 0 1 + 0a800 0 0 3 &mpic 1 1 + 0a800 0 0 4 &mpic 2 1 /* Bus 1 (Tundra Bridge) */ /* IDSEL 0x12 (ISA bridge) */ - 19000 0 0 1 &mpic 30 1 - 19000 0 0 2 &mpic 31 1 - 19000 0 0 3 &mpic 32 1 - 19000 0 0 4 &mpic 33 1>; + 19000 0 0 1 &mpic 0 1 + 19000 0 0 2 &mpic 1 1 + 19000 0 0 3 &mpic 2 1 + 19000 0 0 4 &mpic 3 1>; interrupt-parent = <&mpic>; - interrupts = <08 2>; + interrupts = <18 2>; bus-range = <0 0>; ranges = <02000000 0 80000000 80000000 0 20000000 01000000 0 00000000 e2000000 0 00100000>; @@ -216,12 +216,12 @@ interrupt-map = < /* IDSEL 0x15 */ - a800 0 0 1 &mpic 3b 1 - a800 0 0 2 &mpic 3b 1 - a800 0 0 3 &mpic 3b 1 - a800 0 0 4 &mpic 3b 1>; + a800 0 0 1 &mpic b 1 + a800 0 0 2 &mpic b 1 + a800 0 0 3 &mpic b 1 + a800 0 0 4 &mpic b 1>; interrupt-parent = <&mpic>; - interrupts = <09 2>; + interrupts = <19 2>; bus-range = <0 0>; ranges = <02000000 0 a0000000 a0000000 0 20000000 01000000 0 00000000 e3000000 0 00100000>; diff --git a/arch/powerpc/boot/dts/mpc8544ds.dts b/arch/powerpc/boot/dts/mpc8544ds.dts index 3033599e74e..82859259246 100644 --- a/arch/powerpc/boot/dts/mpc8544ds.dts +++ b/arch/powerpc/boot/dts/mpc8544ds.dts @@ -52,7 +52,7 @@ compatible = "fsl,8544-memory-controller"; reg = <2000 1000>; interrupt-parent = <&mpic>; - interrupts = <2 2>; + interrupts = <12 2>; }; l2-cache-controller@20000 { @@ -61,14 +61,14 @@ cache-line-size = <20>; // 32 bytes cache-size = <40000>; // L2, 256K interrupt-parent = <&mpic>; - interrupts = <0 2>; + interrupts = <10 2>; }; i2c@3000 { device_type = "i2c"; compatible = "fsl-i2c"; reg = <3000 100>; - interrupts = <1b 2>; + interrupts = <2b 2>; interrupt-parent = <&mpic>; dfsrr; }; @@ -81,13 +81,13 @@ reg = <24520 20>; phy0: ethernet-phy@0 { interrupt-parent = <&mpic>; - interrupts = <3a 1>; + interrupts = <a 1>; reg = <0>; device_type = "ethernet-phy"; }; phy1: ethernet-phy@1 { interrupt-parent = <&mpic>; - interrupts = <3a 1>; + interrupts = <a 1>; reg = <1>; device_type = "ethernet-phy"; }; @@ -101,7 +101,7 @@ compatible = "gianfar"; reg = <24000 1000>; local-mac-address = [ 00 00 00 00 00 00 ]; - interrupts = <d 2 e 2 12 2>; + interrupts = <1d 2 1e 2 22 2>; interrupt-parent = <&mpic>; phy-handle = <&phy0>; }; @@ -114,7 +114,7 @@ compatible = "gianfar"; reg = <26000 1000>; local-mac-address = [ 00 00 00 00 00 00 ]; - interrupts = <f 2 10 2 11 2>; + interrupts = <1f 2 20 2 21 2>; interrupt-parent = <&mpic>; phy-handle = <&phy1>; }; @@ -124,7 +124,7 @@ compatible = "ns16550"; reg = <4500 100>; clock-frequency = <0>; - interrupts = <1a 2>; + interrupts = <2a 2>; interrupt-parent = <&mpic>; }; @@ -133,7 +133,7 @@ compatible = "ns16550"; reg = <4600 100>; clock-frequency = <0>; - interrupts = <1a 2>; + interrupts = <2a 2>; interrupt-parent = <&mpic>; }; diff --git a/arch/powerpc/boot/dts/mpc8548cds.dts b/arch/powerpc/boot/dts/mpc8548cds.dts index ad96381033c..9d0b84b66cd 100644 --- a/arch/powerpc/boot/dts/mpc8548cds.dts +++ b/arch/powerpc/boot/dts/mpc8548cds.dts @@ -52,7 +52,7 @@ compatible = "fsl,8548-memory-controller"; reg = <2000 1000>; interrupt-parent = <&mpic>; - interrupts = <2 2>; + interrupts = <12 2>; }; l2-cache-controller@20000 { @@ -61,14 +61,14 @@ cache-line-size = <20>; // 32 bytes cache-size = <80000>; // L2, 512K interrupt-parent = <&mpic>; - interrupts = <0 2>; + interrupts = <10 2>; }; i2c@3000 { device_type = "i2c"; compatible = "fsl-i2c"; reg = <3000 100>; - interrupts = <1b 2>; + interrupts = <2b 2>; interrupt-parent = <&mpic>; dfsrr; }; @@ -81,25 +81,25 @@ reg = <24520 20>; phy0: ethernet-phy@0 { interrupt-parent = <&mpic>; - interrupts = <35 0>; + interrupts = <5 1>; reg = <0>; device_type = "ethernet-phy"; }; phy1: ethernet-phy@1 { interrupt-parent = <&mpic>; - interrupts = <35 0>; + interrupts = <5 1>; reg = <1>; device_type = "ethernet-phy"; }; phy2: ethernet-phy@2 { interrupt-parent = <&mpic>; - interrupts = <35 0>; + interrupts = <5 1>; reg = <2>; device_type = "ethernet-phy"; }; phy3: ethernet-phy@3 { interrupt-parent = <&mpic>; - interrupts = <35 0>; + interrupts = <5 1>; reg = <3>; device_type = "ethernet-phy"; }; @@ -112,8 +112,8 @@ model = "eTSEC"; compatible = "gianfar"; reg = <24000 1000>; - local-mac-address = [ 00 E0 0C 00 73 00 ]; - interrupts = <d 2 e 2 12 2>; + local-mac-address = [ 00 00 00 00 00 00 ]; + interrupts = <1d 2 1e 2 22 2>; interrupt-parent = <&mpic>; phy-handle = <&phy0>; }; @@ -125,8 +125,8 @@ model = "eTSEC"; compatible = "gianfar"; reg = <25000 1000>; - local-mac-address = [ 00 E0 0C 00 73 01 ]; - interrupts = <13 2 14 2 18 2>; + local-mac-address = [ 00 00 00 00 00 00 ]; + interrupts = <23 2 24 2 28 2>; interrupt-parent = <&mpic>; phy-handle = <&phy1>; }; @@ -139,8 +139,8 @@ model = "eTSEC"; compatible = "gianfar"; reg = <26000 1000>; - local-mac-address = [ 00 E0 0C 00 73 02 ]; - interrupts = <f 2 10 2 11 2>; + local-mac-address = [ 00 00 00 00 00 00 ]; + interrupts = <1f 2 20 2 21 2>; interrupt-parent = <&mpic>; phy-handle = <&phy2>; }; @@ -152,8 +152,8 @@ model = "eTSEC"; compatible = "gianfar"; reg = <27000 1000>; - local-mac-address = [ 00 E0 0C 00 73 03 ]; - interrupts = <15 2 16 2 17 2>; + local-mac-address = [ 00 00 00 00 00 00 ]; + interrupts = <25 2 26 2 27 2>; interrupt-parent = <&mpic>; phy-handle = <&phy3>; }; @@ -164,7 +164,7 @@ compatible = "ns16550"; reg = <4500 100>; // reg base, size clock-frequency = <0>; // should we fill in in uboot? - interrupts = <1a 2>; + interrupts = <2a 2>; interrupt-parent = <&mpic>; }; @@ -173,58 +173,64 @@ compatible = "ns16550"; reg = <4600 100>; // reg base, size clock-frequency = <0>; // should we fill in in uboot? - interrupts = <1a 2>; + interrupts = <2a 2>; interrupt-parent = <&mpic>; }; + global-utilities@e0000 { //global utilities reg + compatible = "fsl,mpc8548-guts"; + reg = <e0000 1000>; + fsl,has-rstcr; + }; + pci1: pci@8000 { interrupt-map-mask = <1f800 0 0 7>; interrupt-map = < /* IDSEL 0x10 */ - 08000 0 0 1 &mpic 30 1 - 08000 0 0 2 &mpic 31 1 - 08000 0 0 3 &mpic 32 1 - 08000 0 0 4 &mpic 33 1 + 08000 0 0 1 &mpic 0 1 + 08000 0 0 2 &mpic 1 1 + 08000 0 0 3 &mpic 2 1 + 08000 0 0 4 &mpic 3 1 /* IDSEL 0x11 */ - 08800 0 0 1 &mpic 30 1 - 08800 0 0 2 &mpic 31 1 - 08800 0 0 3 &mpic 32 1 - 08800 0 0 4 &mpic 33 1 + 08800 0 0 1 &mpic 0 1 + 08800 0 0 2 &mpic 1 1 + 08800 0 0 3 &mpic 2 1 + 08800 0 0 4 &mpic 3 1 /* IDSEL 0x12 (Slot 1) */ - 09000 0 0 1 &mpic 30 1 - 09000 0 0 2 &mpic 31 1 - 09000 0 0 3 &mpic 32 1 - 09000 0 0 4 &mpic 33 1 + 09000 0 0 1 &mpic 0 1 + 09000 0 0 2 &mpic 1 1 + 09000 0 0 3 &mpic 2 1 + 09000 0 0 4 &mpic 3 1 /* IDSEL 0x13 (Slot 2) */ - 09800 0 0 1 &mpic 31 1 - 09800 0 0 2 &mpic 32 1 - 09800 0 0 3 &mpic 33 1 - 09800 0 0 4 &mpic 30 1 + 09800 0 0 1 &mpic 1 1 + 09800 0 0 2 &mpic 2 1 + 09800 0 0 3 &mpic 3 1 + 09800 0 0 4 &mpic 0 1 /* IDSEL 0x14 (Slot 3) */ - 0a000 0 0 1 &mpic 32 1 - 0a000 0 0 2 &mpic 33 1 - 0a000 0 0 3 &mpic 30 1 - 0a000 0 0 4 &mpic 31 1 + 0a000 0 0 1 &mpic 2 1 + 0a000 0 0 2 &mpic 3 1 + 0a000 0 0 3 &mpic 0 1 + 0a000 0 0 4 &mpic 1 1 /* IDSEL 0x15 (Slot 4) */ - 0a800 0 0 1 &mpic 33 1 - 0a800 0 0 2 &mpic 30 1 - 0a800 0 0 3 &mpic 31 1 - 0a800 0 0 4 &mpic 32 1 + 0a800 0 0 1 &mpic 3 1 + 0a800 0 0 2 &mpic 0 1 + 0a800 0 0 3 &mpic 1 1 + 0a800 0 0 4 &mpic 2 1 /* Bus 1 (Tundra Bridge) */ /* IDSEL 0x12 (ISA bridge) */ - 19000 0 0 1 &mpic 30 1 - 19000 0 0 2 &mpic 31 1 - 19000 0 0 3 &mpic 32 1 - 19000 0 0 4 &mpic 33 1>; + 19000 0 0 1 &mpic 0 1 + 19000 0 0 2 &mpic 1 1 + 19000 0 0 3 &mpic 2 1 + 19000 0 0 4 &mpic 3 1>; interrupt-parent = <&mpic>; - interrupts = <08 2>; + interrupts = <18 2>; bus-range = <0 0>; ranges = <02000000 0 80000000 80000000 0 20000000 01000000 0 00000000 e2000000 0 00100000>; @@ -256,12 +262,12 @@ interrupt-map = < /* IDSEL 0x15 */ - a800 0 0 1 &mpic 3b 1 - a800 0 0 2 &mpic 3b 1 - a800 0 0 3 &mpic 3b 1 - a800 0 0 4 &mpic 3b 1>; + a800 0 0 1 &mpic b 1 + a800 0 0 2 &mpic b 1 + a800 0 0 3 &mpic b 1 + a800 0 0 4 &mpic b 1>; interrupt-parent = <&mpic>; - interrupts = <09 2>; + interrupts = <19 2>; bus-range = <0 0>; ranges = <02000000 0 a0000000 a0000000 0 20000000 01000000 0 00000000 e3000000 0 00100000>; diff --git a/arch/powerpc/boot/dts/mpc8555cds.dts b/arch/powerpc/boot/dts/mpc8555cds.dts index 951ed92f115..17e45d9a382 100644 --- a/arch/powerpc/boot/dts/mpc8555cds.dts +++ b/arch/powerpc/boot/dts/mpc8555cds.dts @@ -52,7 +52,7 @@ compatible = "fsl,8555-memory-controller"; reg = <2000 1000>; interrupt-parent = <&mpic>; - interrupts = <2 2>; + interrupts = <12 2>; }; l2-cache-controller@20000 { @@ -61,14 +61,14 @@ cache-line-size = <20>; // 32 bytes cache-size = <40000>; // L2, 256K interrupt-parent = <&mpic>; - interrupts = <0 2>; + interrupts = <10 2>; }; i2c@3000 { device_type = "i2c"; compatible = "fsl-i2c"; reg = <3000 100>; - interrupts = <1b 2>; + interrupts = <2b 2>; interrupt-parent = <&mpic>; dfsrr; }; @@ -81,13 +81,13 @@ reg = <24520 20>; phy0: ethernet-phy@0 { interrupt-parent = <&mpic>; - interrupts = <35 0>; + interrupts = <5 1>; reg = <0>; device_type = "ethernet-phy"; }; phy1: ethernet-phy@1 { interrupt-parent = <&mpic>; - interrupts = <35 0>; + interrupts = <5 1>; reg = <1>; device_type = "ethernet-phy"; }; @@ -100,8 +100,8 @@ model = "TSEC"; compatible = "gianfar"; reg = <24000 1000>; - local-mac-address = [ 00 E0 0C 00 73 00 ]; - interrupts = <0d 2 0e 2 12 2>; + local-mac-address = [ 00 00 00 00 00 00 ]; + interrupts = <1d 2 1e 2 22 2>; interrupt-parent = <&mpic>; phy-handle = <&phy0>; }; @@ -113,8 +113,8 @@ model = "TSEC"; compatible = "gianfar"; reg = <25000 1000>; - local-mac-address = [ 00 E0 0C 00 73 01 ]; - interrupts = <13 2 14 2 18 2>; + local-mac-address = [ 00 00 00 00 00 00 ]; + interrupts = <23 2 24 2 28 2>; interrupt-parent = <&mpic>; phy-handle = <&phy1>; }; @@ -124,7 +124,7 @@ compatible = "ns16550"; reg = <4500 100>; // reg base, size clock-frequency = <0>; // should we fill in in uboot? - interrupts = <1a 2>; + interrupts = <2a 2>; interrupt-parent = <&mpic>; }; @@ -133,7 +133,7 @@ compatible = "ns16550"; reg = <4600 100>; // reg base, size clock-frequency = <0>; // should we fill in in uboot? - interrupts = <1a 2>; + interrupts = <2a 2>; interrupt-parent = <&mpic>; }; @@ -142,49 +142,49 @@ interrupt-map = < /* IDSEL 0x10 */ - 08000 0 0 1 &mpic 30 1 - 08000 0 0 2 &mpic 31 1 - 08000 0 0 3 &mpic 32 1 - 08000 0 0 4 &mpic 33 1 + 08000 0 0 1 &mpic 0 1 + 08000 0 0 2 &mpic 1 1 + 08000 0 0 3 &mpic 2 1 + 08000 0 0 4 &mpic 3 1 /* IDSEL 0x11 */ - 08800 0 0 1 &mpic 30 1 - 08800 0 0 2 &mpic 31 1 - 08800 0 0 3 &mpic 32 1 - 08800 0 0 4 &mpic 33 1 + 08800 0 0 1 &mpic 0 1 + 08800 0 0 2 &mpic 1 1 + 08800 0 0 3 &mpic 2 1 + 08800 0 0 4 &mpic 3 1 /* IDSEL 0x12 (Slot 1) */ - 09000 0 0 1 &mpic 30 1 - 09000 0 0 2 &mpic 31 1 - 09000 0 0 3 &mpic 32 1 - 09000 0 0 4 &mpic 33 1 + 09000 0 0 1 &mpic 0 1 + 09000 0 0 2 &mpic 1 1 + 09000 0 0 3 &mpic 2 1 + 09000 0 0 4 &mpic 3 1 /* IDSEL 0x13 (Slot 2) */ - 09800 0 0 1 &mpic 31 1 - 09800 0 0 2 &mpic 32 1 - 09800 0 0 3 &mpic 33 1 - 09800 0 0 4 &mpic 30 1 + 09800 0 0 1 &mpic 1 1 + 09800 0 0 2 &mpic 2 1 + 09800 0 0 3 &mpic 3 1 + 09800 0 0 4 &mpic 0 1 /* IDSEL 0x14 (Slot 3) */ - 0a000 0 0 1 &mpic 32 1 - 0a000 0 0 2 &mpic 33 1 - 0a000 0 0 3 &mpic 30 1 - 0a000 0 0 4 &mpic 31 1 + 0a000 0 0 1 &mpic 2 1 + 0a000 0 0 2 &mpic 3 1 + 0a000 0 0 3 &mpic 0 1 + 0a000 0 0 4 &mpic 1 1 /* IDSEL 0x15 (Slot 4) */ - 0a800 0 0 1 &mpic 33 1 - 0a800 0 0 2 &mpic 30 1 - 0a800 0 0 3 &mpic 31 1 - 0a800 0 0 4 &mpic 32 1 + 0a800 0 0 1 &mpic 3 1 + 0a800 0 0 2 &mpic 0 1 + 0a800 0 0 3 &mpic 1 1 + 0a800 0 0 4 &mpic 2 1 /* Bus 1 (Tundra Bridge) */ /* IDSEL 0x12 (ISA bridge) */ - 19000 0 0 1 &mpic 30 1 - 19000 0 0 2 &mpic 31 1 - 19000 0 0 3 &mpic 32 1 - 19000 0 0 4 &mpic 33 1>; + 19000 0 0 1 &mpic 0 1 + 19000 0 0 2 &mpic 1 1 + 19000 0 0 3 &mpic 2 1 + 19000 0 0 4 &mpic 3 1>; interrupt-parent = <&mpic>; - interrupts = <08 2>; + interrupts = <18 2>; bus-range = <0 0>; ranges = <02000000 0 80000000 80000000 0 20000000 01000000 0 00000000 e2000000 0 00100000>; @@ -216,12 +216,12 @@ interrupt-map = < /* IDSEL 0x15 */ - a800 0 0 1 &mpic 3b 1 - a800 0 0 2 &mpic 3b 1 - a800 0 0 3 &mpic 3b 1 - a800 0 0 4 &mpic 3b 1>; + a800 0 0 1 &mpic b 1 + a800 0 0 2 &mpic b 1 + a800 0 0 3 &mpic b 1 + a800 0 0 4 &mpic b 1>; interrupt-parent = <&mpic>; - interrupts = <09 2>; + interrupts = <19 2>; bus-range = <0 0>; ranges = <02000000 0 a0000000 a0000000 0 20000000 01000000 0 00000000 e3000000 0 00100000>; diff --git a/arch/powerpc/boot/dts/mpc8560ads.dts b/arch/powerpc/boot/dts/mpc8560ads.dts index 80682152b0c..21ccaaa2799 100644 --- a/arch/powerpc/boot/dts/mpc8560ads.dts +++ b/arch/powerpc/boot/dts/mpc8560ads.dts @@ -52,7 +52,7 @@ compatible = "fsl,8540-memory-controller"; reg = <2000 1000>; interrupt-parent = <&mpic>; - interrupts = <2 2>; + interrupts = <12 2>; }; l2-cache-controller@20000 { @@ -61,7 +61,7 @@ cache-line-size = <20>; // 32 bytes cache-size = <40000>; // L2, 256K interrupt-parent = <&mpic>; - interrupts = <0 2>; + interrupts = <10 2>; }; mdio@24520 { @@ -72,25 +72,25 @@ #size-cells = <0>; phy0: ethernet-phy@0 { interrupt-parent = <&mpic>; - interrupts = <35 1>; + interrupts = <5 1>; reg = <0>; device_type = "ethernet-phy"; }; phy1: ethernet-phy@1 { interrupt-parent = <&mpic>; - interrupts = <35 1>; + interrupts = <5 1>; reg = <1>; device_type = "ethernet-phy"; }; phy2: ethernet-phy@2 { interrupt-parent = <&mpic>; - interrupts = <37 1>; + interrupts = <7 1>; reg = <2>; device_type = "ethernet-phy"; }; phy3: ethernet-phy@3 { interrupt-parent = <&mpic>; - interrupts = <37 1>; + interrupts = <7 1>; reg = <3>; device_type = "ethernet-phy"; }; @@ -101,8 +101,14 @@ model = "TSEC"; compatible = "gianfar"; reg = <24000 1000>; - address = [ 00 00 0C 00 00 FD ]; - interrupts = <d 2 e 2 12 2>; + /* + * address is deprecated and will be removed + * in 2.6.25. Only recent versions of + * U-Boot support local-mac-address, however. + */ + address = [ 00 00 00 00 00 00 ]; + local-mac-address = [ 00 00 00 00 00 00 ]; + interrupts = <1d 2 1e 2 22 2>; interrupt-parent = <&mpic>; phy-handle = <&phy0>; }; @@ -114,8 +120,14 @@ model = "TSEC"; compatible = "gianfar"; reg = <25000 1000>; - address = [ 00 00 0C 00 01 FD ]; - interrupts = <13 2 14 2 18 2>; + /* + * address is deprecated and will be removed + * in 2.6.25. Only recent versions of + * U-Boot support local-mac-address, however. + */ + address = [ 00 00 00 00 00 00 ]; + local-mac-address = [ 00 00 00 00 00 00 ]; + interrupts = <23 2 24 2 28 2>; interrupt-parent = <&mpic>; phy-handle = <&phy1>; }; @@ -132,79 +144,79 @@ interrupt-map = < /* IDSEL 0x2 */ - 1000 0 0 1 &mpic 31 1 - 1000 0 0 2 &mpic 32 1 - 1000 0 0 3 &mpic 33 1 - 1000 0 0 4 &mpic 34 1 + 1000 0 0 1 &mpic 1 1 + 1000 0 0 2 &mpic 2 1 + 1000 0 0 3 &mpic 3 1 + 1000 0 0 4 &mpic 4 1 /* IDSEL 0x3 */ - 1800 0 0 1 &mpic 34 1 - 1800 0 0 2 &mpic 31 1 - 1800 0 0 3 &mpic 32 1 - 1800 0 0 4 &mpic 33 1 + 1800 0 0 1 &mpic 4 1 + 1800 0 0 2 &mpic 1 1 + 1800 0 0 3 &mpic 2 1 + 1800 0 0 4 &mpic 3 1 /* IDSEL 0x4 */ - 2000 0 0 1 &mpic 33 1 - 2000 0 0 2 &mpic 34 1 - 2000 0 0 3 &mpic 31 1 - 2000 0 0 4 &mpic 32 1 + 2000 0 0 1 &mpic 3 1 + 2000 0 0 2 &mpic 4 1 + 2000 0 0 3 &mpic 1 1 + 2000 0 0 4 &mpic 2 1 /* IDSEL 0x5 */ - 2800 0 0 1 &mpic 32 1 - 2800 0 0 2 &mpic 33 1 - 2800 0 0 3 &mpic 34 1 - 2800 0 0 4 &mpic 31 1 + 2800 0 0 1 &mpic 2 1 + 2800 0 0 2 &mpic 3 1 + 2800 0 0 3 &mpic 4 1 + 2800 0 0 4 &mpic 1 1 /* IDSEL 12 */ - 6000 0 0 1 &mpic 31 1 - 6000 0 0 2 &mpic 32 1 - 6000 0 0 3 &mpic 33 1 - 6000 0 0 4 &mpic 34 1 + 6000 0 0 1 &mpic 1 1 + 6000 0 0 2 &mpic 2 1 + 6000 0 0 3 &mpic 3 1 + 6000 0 0 4 &mpic 4 1 /* IDSEL 13 */ - 6800 0 0 1 &mpic 34 1 - 6800 0 0 2 &mpic 31 1 - 6800 0 0 3 &mpic 32 1 - 6800 0 0 4 &mpic 33 1 + 6800 0 0 1 &mpic 4 1 + 6800 0 0 2 &mpic 1 1 + 6800 0 0 3 &mpic 2 1 + 6800 0 0 4 &mpic 3 1 /* IDSEL 14*/ - 7000 0 0 1 &mpic 33 1 - 7000 0 0 2 &mpic 34 1 - 7000 0 0 3 &mpic 31 1 - 7000 0 0 4 &mpic 32 1 + 7000 0 0 1 &mpic 3 1 + 7000 0 0 2 &mpic 4 1 + 7000 0 0 3 &mpic 1 1 + 7000 0 0 4 &mpic 2 1 /* IDSEL 15 */ - 7800 0 0 1 &mpic 32 1 - 7800 0 0 2 &mpic 33 1 - 7800 0 0 3 &mpic 34 1 - 7800 0 0 4 &mpic 31 1 + 7800 0 0 1 &mpic 2 1 + 7800 0 0 2 &mpic 3 1 + 7800 0 0 3 &mpic 4 1 + 7800 0 0 4 &mpic 1 1 /* IDSEL 18 */ - 9000 0 0 1 &mpic 31 1 - 9000 0 0 2 &mpic 32 1 - 9000 0 0 3 &mpic 33 1 - 9000 0 0 4 &mpic 34 1 + 9000 0 0 1 &mpic 1 1 + 9000 0 0 2 &mpic 2 1 + 9000 0 0 3 &mpic 3 1 + 9000 0 0 4 &mpic 4 1 /* IDSEL 19 */ - 9800 0 0 1 &mpic 34 1 - 9800 0 0 2 &mpic 31 1 - 9800 0 0 3 &mpic 32 1 - 9800 0 0 4 &mpic 33 1 + 9800 0 0 1 &mpic 4 1 + 9800 0 0 2 &mpic 1 1 + 9800 0 0 3 &mpic 2 1 + 9800 0 0 4 &mpic 3 1 /* IDSEL 20 */ - a000 0 0 1 &mpic 33 1 - a000 0 0 2 &mpic 34 1 - a000 0 0 3 &mpic 31 1 - a000 0 0 4 &mpic 32 1 + a000 0 0 1 &mpic 3 1 + a000 0 0 2 &mpic 4 1 + a000 0 0 3 &mpic 1 1 + a000 0 0 4 &mpic 2 1 /* IDSEL 21 */ - a800 0 0 1 &mpic 32 1 - a800 0 0 2 &mpic 33 1 - a800 0 0 3 &mpic 34 1 - a800 0 0 4 &mpic 31 1>; + a800 0 0 1 &mpic 2 1 + a800 0 0 2 &mpic 3 1 + a800 0 0 3 &mpic 4 1 + a800 0 0 4 &mpic 1 1>; interrupt-parent = <&mpic>; - interrupts = <8 0>; + interrupts = <18 2>; bus-range = <0 0>; ranges = <02000000 0 80000000 80000000 0 20000000 01000000 0 00000000 e2000000 0 01000000>; @@ -234,7 +246,7 @@ interrupt-controller; #address-cells = <0>; #interrupt-cells = <2>; - interrupts = <1e 0>; + interrupts = <2e 2>; interrupt-parent = <&mpic>; reg = <90c00 80>; built-in; @@ -275,7 +287,13 @@ model = "FCC"; device-id = <2>; reg = <91320 20 88500 100 913a0 30>; - mac-address = [ 00 00 0C 00 02 FD ]; + /* + * mac-address is deprecated and will be removed + * in 2.6.25. Only recent versions of + * U-Boot support local-mac-address, however. + */ + mac-address = [ 00 00 00 00 00 00 ]; + local-mac-address = [ 00 00 00 00 00 00 ]; clock-setup = <ff00ffff 250000>; rx-clock = <15>; tx-clock = <16>; @@ -290,7 +308,13 @@ model = "FCC"; device-id = <3>; reg = <91340 20 88600 100 913d0 30>; - mac-address = [ 00 00 0C 00 03 FD ]; + /* + * mac-address is deprecated and will be removed + * in 2.6.25. Only recent versions of + * U-Boot support local-mac-address, however. + */ + mac-address = [ 00 00 00 00 00 00 ]; + local-mac-address = [ 00 00 00 00 00 00 ]; clock-setup = <ffff00ff 3700>; rx-clock = <17>; tx-clock = <18>; diff --git a/arch/powerpc/boot/dts/mpc8568mds.dts b/arch/powerpc/boot/dts/mpc8568mds.dts index a123ec9456b..6bb18f2807a 100644 --- a/arch/powerpc/boot/dts/mpc8568mds.dts +++ b/arch/powerpc/boot/dts/mpc8568mds.dts @@ -61,7 +61,7 @@ compatible = "fsl,8568-memory-controller"; reg = <2000 1000>; interrupt-parent = <&mpic>; - interrupts = <2 2>; + interrupts = <12 2>; }; l2-cache-controller@20000 { @@ -70,14 +70,14 @@ cache-line-size = <20>; // 32 bytes cache-size = <80000>; // L2, 512K interrupt-parent = <&mpic>; - interrupts = <0 2>; + interrupts = <10 2>; }; i2c@3000 { device_type = "i2c"; compatible = "fsl-i2c"; reg = <3000 100>; - interrupts = <1b 2>; + interrupts = <2b 2>; interrupt-parent = <&mpic>; dfsrr; }; @@ -86,7 +86,7 @@ device_type = "i2c"; compatible = "fsl-i2c"; reg = <3100 100>; - interrupts = <1b 2>; + interrupts = <2b 2>; interrupt-parent = <&mpic>; dfsrr; }; @@ -99,25 +99,25 @@ reg = <24520 20>; phy0: ethernet-phy@0 { interrupt-parent = <&mpic>; - interrupts = <31 1>; + interrupts = <1 1>; reg = <0>; device_type = "ethernet-phy"; }; phy1: ethernet-phy@1 { interrupt-parent = <&mpic>; - interrupts = <32 1>; + interrupts = <2 1>; reg = <1>; device_type = "ethernet-phy"; }; phy2: ethernet-phy@2 { interrupt-parent = <&mpic>; - interrupts = <31 1>; + interrupts = <1 1>; reg = <2>; device_type = "ethernet-phy"; }; phy3: ethernet-phy@3 { interrupt-parent = <&mpic>; - interrupts = <32 1>; + interrupts = <2 1>; reg = <3>; device_type = "ethernet-phy"; }; @@ -130,8 +130,14 @@ model = "eTSEC"; compatible = "gianfar"; reg = <24000 1000>; + /* + * mac-address is deprecated and will be removed + * in 2.6.25. Only recent versions of + * U-Boot support local-mac-address, however. + */ mac-address = [ 00 00 00 00 00 00 ]; - interrupts = <d 2 e 2 12 2>; + local-mac-address = [ 00 00 00 00 00 00 ]; + interrupts = <1d 2 1e 2 22 2>; interrupt-parent = <&mpic>; phy-handle = <&phy2>; }; @@ -143,8 +149,14 @@ model = "eTSEC"; compatible = "gianfar"; reg = <25000 1000>; - mac-address = [ 00 00 00 00 00 00]; - interrupts = <13 2 14 2 18 2>; + /* + * mac-address is deprecated and will be removed + * in 2.6.25. Only recent versions of + * U-Boot support local-mac-address, however. + */ + mac-address = [ 00 00 00 00 00 00 ]; + local-mac-address = [ 00 00 00 00 00 00 ]; + interrupts = <23 2 24 2 28 2>; interrupt-parent = <&mpic>; phy-handle = <&phy3>; }; @@ -154,7 +166,7 @@ compatible = "ns16550"; reg = <4500 100>; clock-frequency = <0>; - interrupts = <1a 2>; + interrupts = <2a 2>; interrupt-parent = <&mpic>; }; @@ -163,7 +175,7 @@ compatible = "ns16550"; reg = <4600 100>; clock-frequency = <0>; - interrupts = <1a 2>; + interrupts = <2a 2>; interrupt-parent = <&mpic>; }; @@ -172,7 +184,7 @@ model = "SEC2"; compatible = "talitos"; reg = <30000 f000>; - interrupts = <1d 2>; + interrupts = <2d 2>; interrupt-parent = <&mpic>; num-channels = <4>; channel-fifo-len = <18>; @@ -300,7 +312,13 @@ reg = <2000 200>; interrupts = <20>; interrupt-parent = <&qeic>; - mac-address = [ 00 04 9f 00 23 23 ]; + /* + * mac-address is deprecated and will be removed + * in 2.6.25. Only recent versions of + * U-Boot support local-mac-address, however. + */ + mac-address = [ 00 00 00 00 00 00 ]; + local-mac-address = [ 00 00 00 00 00 00 ]; rx-clock = <0>; tx-clock = <19>; phy-handle = <&qe_phy0>; @@ -316,7 +334,13 @@ reg = <3000 200>; interrupts = <21>; interrupt-parent = <&qeic>; - mac-address = [ 00 11 22 33 44 55 ]; + /* + * mac-address is deprecated and will be removed + * in 2.6.25. Only recent versions of + * U-Boot support local-mac-address, however. + */ + mac-address = [ 00 00 00 00 00 00 ]; + local-mac-address = [ 00 00 00 00 00 00 ]; rx-clock = <0>; tx-clock = <14>; phy-handle = <&qe_phy1>; @@ -335,25 +359,25 @@ * gianfar's MDIO bus */ qe_phy0: ethernet-phy@00 { interrupt-parent = <&mpic>; - interrupts = <31 1>; + interrupts = <1 1>; reg = <0>; device_type = "ethernet-phy"; }; qe_phy1: ethernet-phy@01 { interrupt-parent = <&mpic>; - interrupts = <32 1>; + interrupts = <2 1>; reg = <1>; device_type = "ethernet-phy"; }; qe_phy2: ethernet-phy@02 { interrupt-parent = <&mpic>; - interrupts = <31 1>; + interrupts = <1 1>; reg = <2>; device_type = "ethernet-phy"; }; qe_phy3: ethernet-phy@03 { interrupt-parent = <&mpic>; - interrupts = <32 1>; + interrupts = <2 1>; reg = <3>; device_type = "ethernet-phy"; }; @@ -367,7 +391,7 @@ reg = <80 80>; built-in; big-endian; - interrupts = <1e 2 1e 2>; //high:30 low:30 + interrupts = <2e 2 2e 2>; //high:30 low:30 interrupt-parent = <&mpic>; }; diff --git a/arch/powerpc/boot/dts/mpc8641_hpcn.dts b/arch/powerpc/boot/dts/mpc8641_hpcn.dts index 260b264c869..6a78a2b37c0 100644 --- a/arch/powerpc/boot/dts/mpc8641_hpcn.dts +++ b/arch/powerpc/boot/dts/mpc8641_hpcn.dts @@ -56,8 +56,12 @@ #size-cells = <1>; #interrupt-cells = <2>; device_type = "soc"; - ranges = <0 f8000000 00100000>; - reg = <f8000000 00100000>; // CCSRBAR 1M + ranges = <00001000 f8001000 000ff000 + 80000000 80000000 20000000 + e2000000 e2000000 00100000 + a0000000 a0000000 20000000 + e3000000 e3000000 00100000>; + reg = <f8000000 00001000>; // CCSRBAR bus-frequency = <0>; i2c@3000 { @@ -86,25 +90,25 @@ reg = <24520 20>; phy0: ethernet-phy@0 { interrupt-parent = <&mpic>; - interrupts = <4a 1>; + interrupts = <a 1>; reg = <0>; device_type = "ethernet-phy"; }; phy1: ethernet-phy@1 { interrupt-parent = <&mpic>; - interrupts = <4a 1>; + interrupts = <a 1>; reg = <1>; device_type = "ethernet-phy"; }; phy2: ethernet-phy@2 { interrupt-parent = <&mpic>; - interrupts = <4a 1>; + interrupts = <a 1>; reg = <2>; device_type = "ethernet-phy"; }; phy3: ethernet-phy@3 { interrupt-parent = <&mpic>; - interrupts = <4a 1>; + interrupts = <a 1>; reg = <3>; device_type = "ethernet-phy"; }; @@ -117,10 +121,17 @@ model = "TSEC"; compatible = "gianfar"; reg = <24000 1000>; - mac-address = [ 00 E0 0C 00 73 00 ]; + /* + * mac-address is deprecated and will be removed + * in 2.6.25. Only recent versions of + * U-Boot support local-mac-address, however. + */ + mac-address = [ 00 00 00 00 00 00 ]; + local-mac-address = [ 00 00 00 00 00 00 ]; interrupts = <1d 2 1e 2 22 2>; interrupt-parent = <&mpic>; phy-handle = <&phy0>; + phy-connection-type = "rgmii-id"; }; ethernet@25000 { @@ -130,10 +141,17 @@ model = "TSEC"; compatible = "gianfar"; reg = <25000 1000>; - mac-address = [ 00 E0 0C 00 73 01 ]; + /* + * mac-address is deprecated and will be removed + * in 2.6.25. Only recent versions of + * U-Boot support local-mac-address, however. + */ + mac-address = [ 00 00 00 00 00 00 ]; + local-mac-address = [ 00 00 00 00 00 00 ]; interrupts = <23 2 24 2 28 2>; interrupt-parent = <&mpic>; phy-handle = <&phy1>; + phy-connection-type = "rgmii-id"; }; ethernet@26000 { @@ -143,10 +161,17 @@ model = "TSEC"; compatible = "gianfar"; reg = <26000 1000>; - mac-address = [ 00 E0 0C 00 02 FD ]; + /* + * mac-address is deprecated and will be removed + * in 2.6.25. Only recent versions of + * U-Boot support local-mac-address, however. + */ + mac-address = [ 00 00 00 00 00 00 ]; + local-mac-address = [ 00 00 00 00 00 00 ]; interrupts = <1F 2 20 2 21 2>; interrupt-parent = <&mpic>; phy-handle = <&phy2>; + phy-connection-type = "rgmii-id"; }; ethernet@27000 { @@ -156,10 +181,17 @@ model = "TSEC"; compatible = "gianfar"; reg = <27000 1000>; - mac-address = [ 00 E0 0C 00 03 FD ]; + /* + * mac-address is deprecated and will be removed + * in 2.6.25. Only recent versions of + * U-Boot support local-mac-address, however. + */ + mac-address = [ 00 00 00 00 00 00 ]; + local-mac-address = [ 00 00 00 00 00 00 ]; interrupts = <25 2 26 2 27 2>; interrupt-parent = <&mpic>; phy-handle = <&phy3>; + phy-connection-type = "rgmii-id"; }; serial@4500 { device_type = "serial"; @@ -186,7 +218,7 @@ #size-cells = <2>; #address-cells = <3>; reg = <8000 1000>; - bus-range = <0 fe>; + bus-range = <0 ff>; ranges = <02000000 0 80000000 80000000 0 20000000 01000000 0 00000000 e2000000 0 00100000>; clock-frequency = <1fca055>; @@ -285,17 +317,84 @@ f800 0 0 3 &i8259 0 0 f800 0 0 4 &i8259 0 0 >; - i8259: i8259@4d0 { - clock-frequency = <0>; - interrupt-controller; - device_type = "interrupt-controller"; - #address-cells = <0>; - #interrupt-cells = <2>; - built-in; - compatible = "chrp,iic"; - big-endian; - interrupts = <49 2>; - interrupt-parent = <&mpic>; + uli1575@0 { + reg = <0 0 0 0 0>; + #size-cells = <2>; + #address-cells = <3>; + ranges = <02000000 0 80000000 + 02000000 0 80000000 + 0 20000000 + 01000000 0 00000000 + 01000000 0 00000000 + 0 00100000>; + + pci_bridge@0 { + reg = <0 0 0 0 0>; + #size-cells = <2>; + #address-cells = <3>; + ranges = <02000000 0 80000000 + 02000000 0 80000000 + 0 20000000 + 01000000 0 00000000 + 01000000 0 00000000 + 0 00100000>; + + isa@1e { + device_type = "isa"; + #interrupt-cells = <2>; + #size-cells = <1>; + #address-cells = <2>; + reg = <f000 0 0 0 0>; + ranges = <1 0 01000000 0 0 + 00001000>; + interrupt-parent = <&i8259>; + + i8259: interrupt-controller@20 { + reg = <1 20 2 + 1 a0 2 + 1 4d0 2>; + clock-frequency = <0>; + interrupt-controller; + device_type = "interrupt-controller"; + #address-cells = <0>; + #interrupt-cells = <2>; + built-in; + compatible = "chrp,iic"; + interrupts = <9 2>; + interrupt-parent = + <&mpic>; + }; + + i8042@60 { + #size-cells = <0>; + #address-cells = <1>; + reg = <1 60 1 1 64 1>; + interrupts = <1 3 c 3>; + interrupt-parent = + <&i8259>; + + keyboard@0 { + reg = <0>; + compatible = "pnpPNP,303"; + }; + + mouse@1 { + reg = <1>; + compatible = "pnpPNP,f03"; + }; + }; + + rtc@70 { + compatible = + "pnpPNP,b00"; + reg = <1 70 2>; + }; + + gpio@400 { + reg = <1 400 80>; + }; + }; + }; }; }; @@ -316,10 +415,10 @@ interrupt-map-mask = <f800 0 0 7>; interrupt-map = < /* IDSEL 0x0 */ - 0000 0 0 1 &mpic 44 1 - 0000 0 0 2 &mpic 45 1 - 0000 0 0 3 &mpic 46 1 - 0000 0 0 4 &mpic 47 1 + 0000 0 0 1 &mpic 4 1 + 0000 0 0 2 &mpic 5 1 + 0000 0 0 3 &mpic 6 1 + 0000 0 0 4 &mpic 7 1 >; }; diff --git a/arch/powerpc/boot/dts/mpc866ads.dts b/arch/powerpc/boot/dts/mpc866ads.dts index c0d06fd1292..e5e7726ddb0 100644 --- a/arch/powerpc/boot/dts/mpc866ads.dts +++ b/arch/powerpc/boot/dts/mpc866ads.dts @@ -15,12 +15,10 @@ compatible = "mpc8xx"; #address-cells = <1>; #size-cells = <1>; - linux,phandle = <100>; cpus { #address-cells = <1>; #size-cells = <0>; - linux,phandle = <200>; PowerPC,866@0 { device_type = "cpu"; @@ -34,14 +32,12 @@ clock-frequency = <0>; 32-bit; interrupts = <f 2>; // decrementer interrupt - interrupt-parent = <ff000000>; - linux,phandle = <201>; + interrupt-parent = <&Mpc8xx_pic>; }; }; memory { device_type = "memory"; - linux,phandle = <300>; reg = <00000000 800000>; }; @@ -57,11 +53,9 @@ device_type = "mdio"; compatible = "fs_enet"; reg = <e80 8>; - linux,phandle = <e80>; #address-cells = <1>; #size-cells = <0>; - ethernet-phy@f { - linux,phandle = <e800f>; + phy: ethernet-phy@f { reg = <f>; device_type = "ethernet-phy"; }; @@ -75,12 +69,11 @@ reg = <e00 188>; mac-address = [ 00 00 0C 00 01 FD ]; interrupts = <3 1>; - interrupt-parent = <ff000000>; - phy-handle = <e800f>; + interrupt-parent = <&Mpc8xx_pic>; + phy-handle = <&Phy>; }; - pic@ff000000 { - linux,phandle = <ff000000>; + mpc8xx_pic: pic@ff000000 { interrupt-controller; #address-cells = <0>; #interrupt-cells = <2>; @@ -91,7 +84,6 @@ }; cpm@ff000000 { - linux,phandle = <ff000000>; #address-cells = <1>; #size-cells = <1>; #interrupt-cells = <2>; @@ -102,15 +94,14 @@ command-proc = <9c0>; brg-frequency = <0>; interrupts = <0 2>; // cpm error interrupt - interrupt-parent = <930>; + interrupt-parent = <&Cpm_pic>; - pic@930 { - linux,phandle = <930>; + cpm_pic: pic@930 { interrupt-controller; #address-cells = <0>; #interrupt-cells = <2>; interrupts = <5 2 0 2>; - interrupt-parent = <ff000000>; + interrupt-parent = <&Mpc8xx_pic>; reg = <930 20>; built-in; device_type = "cpm-pic"; @@ -128,7 +119,7 @@ tx-clock = <1>; current-speed = <0>; interrupts = <4 3>; - interrupt-parent = <930>; + interrupt-parent = <&Cpm_pic>; }; smc@a90 { @@ -142,7 +133,7 @@ tx-clock = <2>; current-speed = <0>; interrupts = <3 3>; - interrupt-parent = <930>; + interrupt-parent = <&Cpm_pic>; }; scc@a00 { @@ -153,7 +144,7 @@ reg = <a00 18 3c00 80>; mac-address = [ 00 00 0C 00 03 FD ]; interrupts = <1e 3>; - interrupt-parent = <930>; + interrupt-parent = <&Cpm_pic>; }; }; }; diff --git a/arch/powerpc/boot/dts/mpc885ads.dts b/arch/powerpc/boot/dts/mpc885ads.dts index 110bf617060..dc7ab9c8061 100644 --- a/arch/powerpc/boot/dts/mpc885ads.dts +++ b/arch/powerpc/boot/dts/mpc885ads.dts @@ -15,12 +15,10 @@ compatible = "mpc8xx"; #address-cells = <1>; #size-cells = <1>; - linux,phandle = <100>; cpus { #address-cells = <1>; #size-cells = <0>; - linux,phandle = <200>; PowerPC,885@0 { device_type = "cpu"; @@ -34,14 +32,12 @@ clock-frequency = <0>; 32-bit; interrupts = <f 2>; // decrementer interrupt - interrupt-parent = <ff000000>; - linux,phandle = <201>; + interrupt-parent = <&Mpc8xx_pic>; }; }; memory { device_type = "memory"; - linux,phandle = <300>; reg = <00000000 800000>; }; @@ -57,21 +53,17 @@ device_type = "mdio"; compatible = "fs_enet"; reg = <e80 8>; - linux,phandle = <e80>; #address-cells = <1>; #size-cells = <0>; - ethernet-phy@0 { - linux,phandle = <e8000>; + Phy0: ethernet-phy@0 { reg = <0>; device_type = "ethernet-phy"; }; - ethernet-phy@1 { - linux,phandle = <e8001>; + Phy1: ethernet-phy@1 { reg = <1>; device_type = "ethernet-phy"; }; - ethernet-phy@2 { - linux,phandle = <e8002>; + Phy2: ethernet-phy@2 { reg = <2>; device_type = "ethernet-phy"; }; @@ -85,8 +77,8 @@ reg = <e00 188>; mac-address = [ 00 00 0C 00 01 FD ]; interrupts = <3 1>; - interrupt-parent = <ff000000>; - phy-handle = <e8000>; + interrupt-parent = <&Mpc8xx_pic>; + phy-handle = <&Phy1>; }; fec@1e00 { @@ -97,12 +89,11 @@ reg = <1e00 188>; mac-address = [ 00 00 0C 00 02 FD ]; interrupts = <7 1>; - interrupt-parent = <ff000000>; - phy-handle = <e8001>; + interrupt-parent = <&Mpc8xx_pic>; + phy-handle = <&Phy2>; }; - pic@ff000000 { - linux,phandle = <ff000000>; + Mpc8xx_pic: pic@ff000000 { interrupt-controller; #address-cells = <0>; #interrupt-cells = <2>; @@ -112,8 +103,18 @@ compatible = "CPM"; }; + pcmcia@0080 { + #address-cells = <3>; + #interrupt-cells = <1>; + #size-cells = <2>; + compatible = "fsl,pq-pcmcia"; + device_type = "pcmcia"; + reg = <80 80>; + interrupt-parent = <&Mpc8xx_pic>; + interrupts = <d 1>; + }; + cpm@ff000000 { - linux,phandle = <ff000000>; #address-cells = <1>; #size-cells = <1>; #interrupt-cells = <2>; @@ -124,15 +125,14 @@ command-proc = <9c0>; brg-frequency = <0>; interrupts = <0 2>; // cpm error interrupt - interrupt-parent = <930>; + interrupt-parent = <&Cpm_pic>; - pic@930 { - linux,phandle = <930>; + Cpm_pic: pic@930 { interrupt-controller; #address-cells = <0>; #interrupt-cells = <2>; interrupts = <5 2 0 2>; - interrupt-parent = <ff000000>; + interrupt-parent = <&Mpc8xx_pic>; reg = <930 20>; built-in; device_type = "cpm-pic"; @@ -150,7 +150,7 @@ tx-clock = <1>; current-speed = <0>; interrupts = <4 3>; - interrupt-parent = <930>; + interrupt-parent = <&Cpm_pic>; }; smc@a90 { @@ -164,7 +164,7 @@ tx-clock = <2>; current-speed = <0>; interrupts = <3 3>; - interrupt-parent = <930>; + interrupt-parent = <&Cpm_pic>; }; scc@a40 { @@ -175,8 +175,8 @@ reg = <a40 18 3e00 80>; mac-address = [ 00 00 0C 00 03 FD ]; interrupts = <1c 3>; - interrupt-parent = <930>; - phy-handle = <e8002>; + interrupt-parent = <&Cpm_pic>; + phy-handle = <&Phy2>; }; }; }; diff --git a/arch/powerpc/boot/dts/prpmc2800.dts b/arch/powerpc/boot/dts/prpmc2800.dts index 568965a022b..699d0df574d 100644 --- a/arch/powerpc/boot/dts/prpmc2800.dts +++ b/arch/powerpc/boot/dts/prpmc2800.dts @@ -309,7 +309,7 @@ }; chosen { - bootargs = "ip=on console=ttyMM0"; + bootargs = "ip=on"; linux,stdout-path = "/mv64x60@f1000000/mpsc@8000"; }; }; diff --git a/arch/powerpc/boot/dts/ps3.dts b/arch/powerpc/boot/dts/ps3.dts new file mode 100644 index 00000000000..379ded282d5 --- /dev/null +++ b/arch/powerpc/boot/dts/ps3.dts @@ -0,0 +1,68 @@ +/* + * PS3 Game Console device tree. + * + * Copyright (C) 2007 Sony Computer Entertainment Inc. + * Copyright 2007 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 + */ + +/ { + model = "SonyPS3"; + compatible = "sony,ps3"; + #size-cells = <2>; + #address-cells = <2>; + + chosen { + }; + + /* + * We'll get the size of the bootmem block from lv1 after startup, + * so we'll put a null entry here. + */ + + memory { + device_type = "memory"; + reg = <0 0 0 0>; + }; + + /* + * The boot cpu is always zero for PS3. + * + * dtc expects a clock-frequency and timebase-frequency entries, so + * we'll put a null entries here. These will be initialized after + * startup with data from lv1. + * + * Seems the only way currently to indicate a processor has multiple + * threads is with an ibm,ppc-interrupt-server#s entry. We'll put one + * here so we can bring up both of ours. See smp_setup_cpu_maps(). + */ + + cpus { + #size-cells = <0>; + #address-cells = <1>; + + cpu@0 { + device_type = "cpu"; + reg = <0>; + ibm,ppc-interrupt-server#s = <0 1>; + clock-frequency = <0>; + timebase-frequency = <0>; + i-cache-size = <8000>; + d-cache-size = <8000>; + i-cache-line-size = <80>; + d-cache-line-size = <80>; + }; + }; +}; diff --git a/arch/powerpc/boot/ebony.c b/arch/powerpc/boot/ebony.c index b1251ee7a10..75daedafd0a 100644 --- a/arch/powerpc/boot/ebony.c +++ b/arch/powerpc/boot/ebony.c @@ -100,28 +100,13 @@ static void ebony_fixups(void) ibm440gp_fixup_clocks(sysclk, 6 * 1843200); ibm44x_fixup_memsize(); dt_fixup_mac_addresses(ebony_mac0, ebony_mac1); -} - -#define SPRN_DBCR0 0x134 -#define DBCR0_RST_SYSTEM 0x30000000 - -static void ebony_exit(void) -{ - unsigned long tmp; - - asm volatile ( - "mfspr %0,%1\n" - "oris %0,%0,%2@h\n" - "mtspr %1,%0" - : "=&r"(tmp) : "i"(SPRN_DBCR0), "i"(DBCR0_RST_SYSTEM) - ); - + ibm4xx_fixup_ebc_ranges("/plb/opb/ebc"); } void ebony_init(void *mac0, void *mac1) { platform_ops.fixups = ebony_fixups; - platform_ops.exit = ebony_exit; + platform_ops.exit = ibm44x_dbcr_reset; ebony_mac0 = mac0; ebony_mac1 = mac1; ft_init(_dtb_start, _dtb_end - _dtb_start, 32); diff --git a/arch/powerpc/boot/main.c b/arch/powerpc/boot/main.c index 56b56a8d4b2..416dc3857bf 100644 --- a/arch/powerpc/boot/main.c +++ b/arch/powerpc/boot/main.c @@ -36,8 +36,6 @@ struct addr_range { unsigned long size; }; -typedef void (*kernel_entry_t)(unsigned long, unsigned long, void *); - #undef DEBUG static struct addr_range prep_kernel(void) diff --git a/arch/powerpc/boot/of.c b/arch/powerpc/boot/of.c index d16ee3e3f86..385e08b83b7 100644 --- a/arch/powerpc/boot/of.c +++ b/arch/powerpc/boot/of.c @@ -15,8 +15,7 @@ #include "page.h" #include "ops.h" -typedef void *ihandle; -typedef void *phandle; +#include "of.h" extern char _end[]; @@ -25,154 +24,10 @@ extern char _end[]; #define RAM_END (512<<20) /* Fixme: use OF */ #define ONE_MB 0x100000 -int (*prom) (void *); static unsigned long claim_base; -static int call_prom(const char *service, int nargs, int nret, ...) -{ - int i; - struct prom_args { - const char *service; - int nargs; - int nret; - unsigned int args[12]; - } args; - va_list list; - - args.service = service; - args.nargs = nargs; - args.nret = nret; - - va_start(list, nret); - for (i = 0; i < nargs; i++) - args.args[i] = va_arg(list, unsigned int); - va_end(list); - - for (i = 0; i < nret; i++) - args.args[nargs+i] = 0; - - if (prom(&args) < 0) - return -1; - - return (nret > 0)? args.args[nargs]: 0; -} - -static int call_prom_ret(const char *service, int nargs, int nret, - unsigned int *rets, ...) -{ - int i; - struct prom_args { - const char *service; - int nargs; - int nret; - unsigned int args[12]; - } args; - va_list list; - - args.service = service; - args.nargs = nargs; - args.nret = nret; - - va_start(list, rets); - for (i = 0; i < nargs; i++) - args.args[i] = va_arg(list, unsigned int); - va_end(list); - - for (i = 0; i < nret; i++) - args.args[nargs+i] = 0; - - if (prom(&args) < 0) - return -1; - - if (rets != (void *) 0) - for (i = 1; i < nret; ++i) - rets[i-1] = args.args[nargs+i]; - - return (nret > 0)? args.args[nargs]: 0; -} - -/* - * Older OF's require that when claiming a specific range of addresses, - * we claim the physical space in the /memory node and the virtual - * space in the chosen mmu node, and then do a map operation to - * map virtual to physical. - */ -static int need_map = -1; -static ihandle chosen_mmu; -static phandle memory; - -/* returns true if s2 is a prefix of s1 */ -static int string_match(const char *s1, const char *s2) -{ - for (; *s2; ++s2) - if (*s1++ != *s2) - return 0; - return 1; -} - -static int check_of_version(void) -{ - phandle oprom, chosen; - char version[64]; - - oprom = finddevice("/openprom"); - if (oprom == (phandle) -1) - return 0; - if (getprop(oprom, "model", version, sizeof(version)) <= 0) - return 0; - version[sizeof(version)-1] = 0; - printf("OF version = '%s'\r\n", version); - if (!string_match(version, "Open Firmware, 1.") - && !string_match(version, "FirmWorks,3.")) - return 0; - chosen = finddevice("/chosen"); - if (chosen == (phandle) -1) { - chosen = finddevice("/chosen@0"); - if (chosen == (phandle) -1) { - printf("no chosen\n"); - return 0; - } - } - if (getprop(chosen, "mmu", &chosen_mmu, sizeof(chosen_mmu)) <= 0) { - printf("no mmu\n"); - return 0; - } - memory = (ihandle) call_prom("open", 1, 1, "/memory"); - if (memory == (ihandle) -1) { - memory = (ihandle) call_prom("open", 1, 1, "/memory@0"); - if (memory == (ihandle) -1) { - printf("no memory node\n"); - return 0; - } - } - printf("old OF detected\r\n"); - return 1; -} - -static void *claim(unsigned long virt, unsigned long size, unsigned long align) -{ - int ret; - unsigned int result; - - if (need_map < 0) - need_map = check_of_version(); - if (align || !need_map) - return (void *) call_prom("claim", 3, 1, virt, size, align); - - ret = call_prom_ret("call-method", 5, 2, &result, "claim", memory, - align, size, virt); - if (ret != 0 || result == -1) - return (void *) -1; - ret = call_prom_ret("call-method", 5, 2, &result, "claim", chosen_mmu, - align, size, virt); - /* 0x12 == coherent + read/write */ - ret = call_prom("call-method", 6, 1, "map", chosen_mmu, - 0x12, size, virt, virt); - return (void *) virt; -} - static void *of_try_claim(unsigned long size) { unsigned long addr = 0; @@ -184,7 +39,7 @@ static void *of_try_claim(unsigned long size) #ifdef DEBUG printf(" trying: 0x%08lx\n\r", claim_base); #endif - addr = (unsigned long)claim(claim_base, size, 0); + addr = (unsigned long)of_claim(claim_base, size, 0); if ((void *)addr != (void *)-1) break; } @@ -208,64 +63,6 @@ static void of_image_hdr(const void *hdr) } } -static void *of_vmlinux_alloc(unsigned long size) -{ - void *p = malloc(size); - - if (!p) - fatal("Can't allocate memory for kernel image!\n\r"); - - return p; -} - -static void of_exit(void) -{ - call_prom("exit", 0, 0); -} - -/* - * OF device tree routines - */ -static void *of_finddevice(const char *name) -{ - return (phandle) call_prom("finddevice", 1, 1, name); -} - -static int of_getprop(const void *phandle, const char *name, void *buf, - const int buflen) -{ - return call_prom("getprop", 4, 1, phandle, name, buf, buflen); -} - -static int of_setprop(const void *phandle, const char *name, const void *buf, - const int buflen) -{ - return call_prom("setprop", 4, 1, phandle, name, buf, buflen); -} - -/* - * OF console routines - */ -static void *of_stdout_handle; - -static int of_console_open(void) -{ - void *devp; - - if (((devp = finddevice("/chosen")) != NULL) - && (getprop(devp, "stdout", &of_stdout_handle, - sizeof(of_stdout_handle)) - == sizeof(of_stdout_handle))) - return 0; - - return -1; -} - -static void of_console_write(char *buf, int len) -{ - call_prom("write", 3, 1, of_stdout_handle, buf, len); -} - void platform_init(unsigned long a1, unsigned long a2, void *promptr) { platform_ops.image_hdr = of_image_hdr; @@ -277,10 +74,9 @@ void platform_init(unsigned long a1, unsigned long a2, void *promptr) dt_ops.getprop = of_getprop; dt_ops.setprop = of_setprop; - console_ops.open = of_console_open; - console_ops.write = of_console_write; + of_console_init(); - prom = (int (*)(void *))promptr; + of_init(promptr); loader_info.promptr = promptr; if (a1 && a2 && a2 != 0xdeadbeef) { loader_info.initrd_addr = a1; diff --git a/arch/powerpc/boot/of.h b/arch/powerpc/boot/of.h new file mode 100644 index 00000000000..e4c68f7391c --- /dev/null +++ b/arch/powerpc/boot/of.h @@ -0,0 +1,21 @@ +#ifndef _PPC_BOOT_OF_H_ +#define _PPC_BOOT_OF_H_ + +typedef void *phandle; +typedef void *ihandle; + +void of_init(void *promptr); +int of_call_prom(const char *service, int nargs, int nret, ...); +void *of_claim(unsigned long virt, unsigned long size, unsigned long align); +void *of_vmlinux_alloc(unsigned long size); +void of_exit(void); +void *of_finddevice(const char *name); +int of_getprop(const void *phandle, const char *name, void *buf, + const int buflen); +int of_setprop(const void *phandle, const char *name, const void *buf, + const int buflen); + +/* Console functions */ +void of_console_init(void); + +#endif /* _PPC_BOOT_OF_H_ */ diff --git a/arch/powerpc/boot/ofconsole.c b/arch/powerpc/boot/ofconsole.c new file mode 100644 index 00000000000..ce0e0242445 --- /dev/null +++ b/arch/powerpc/boot/ofconsole.c @@ -0,0 +1,45 @@ +/* + * OF console routines + * + * Copyright (C) Paul Mackerras 1997. + * + * 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 <stddef.h> +#include "types.h" +#include "elf.h" +#include "string.h" +#include "stdio.h" +#include "page.h" +#include "ops.h" + +#include "of.h" + +static void *of_stdout_handle; + +static int of_console_open(void) +{ + void *devp; + + if (((devp = of_finddevice("/chosen")) != NULL) + && (of_getprop(devp, "stdout", &of_stdout_handle, + sizeof(of_stdout_handle)) + == sizeof(of_stdout_handle))) + return 0; + + return -1; +} + +static void of_console_write(const char *buf, int len) +{ + of_call_prom("write", 3, 1, of_stdout_handle, buf, len); +} + +void of_console_init(void) +{ + console_ops.open = of_console_open; + console_ops.write = of_console_write; +} diff --git a/arch/powerpc/boot/oflib.c b/arch/powerpc/boot/oflib.c new file mode 100644 index 00000000000..95b8fd69a40 --- /dev/null +++ b/arch/powerpc/boot/oflib.c @@ -0,0 +1,202 @@ +/* + * Copyright (C) Paul Mackerras 1997. + * + * 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 <stddef.h> +#include "types.h" +#include "elf.h" +#include "string.h" +#include "stdio.h" +#include "page.h" +#include "ops.h" + +#include "of.h" + +static int (*prom) (void *); + +void of_init(void *promptr) +{ + prom = (int (*)(void *))promptr; +} + +int of_call_prom(const char *service, int nargs, int nret, ...) +{ + int i; + struct prom_args { + const char *service; + int nargs; + int nret; + unsigned int args[12]; + } args; + va_list list; + + args.service = service; + args.nargs = nargs; + args.nret = nret; + + va_start(list, nret); + for (i = 0; i < nargs; i++) + args.args[i] = va_arg(list, unsigned int); + va_end(list); + + for (i = 0; i < nret; i++) + args.args[nargs+i] = 0; + + if (prom(&args) < 0) + return -1; + + return (nret > 0)? args.args[nargs]: 0; +} + +static int of_call_prom_ret(const char *service, int nargs, int nret, + unsigned int *rets, ...) +{ + int i; + struct prom_args { + const char *service; + int nargs; + int nret; + unsigned int args[12]; + } args; + va_list list; + + args.service = service; + args.nargs = nargs; + args.nret = nret; + + va_start(list, rets); + for (i = 0; i < nargs; i++) + args.args[i] = va_arg(list, unsigned int); + va_end(list); + + for (i = 0; i < nret; i++) + args.args[nargs+i] = 0; + + if (prom(&args) < 0) + return -1; + + if (rets != (void *) 0) + for (i = 1; i < nret; ++i) + rets[i-1] = args.args[nargs+i]; + + return (nret > 0)? args.args[nargs]: 0; +} + +/* returns true if s2 is a prefix of s1 */ +static int string_match(const char *s1, const char *s2) +{ + for (; *s2; ++s2) + if (*s1++ != *s2) + return 0; + return 1; +} + +/* + * Older OF's require that when claiming a specific range of addresses, + * we claim the physical space in the /memory node and the virtual + * space in the chosen mmu node, and then do a map operation to + * map virtual to physical. + */ +static int need_map = -1; +static ihandle chosen_mmu; +static phandle memory; + +static int check_of_version(void) +{ + phandle oprom, chosen; + char version[64]; + + oprom = of_finddevice("/openprom"); + if (oprom == (phandle) -1) + return 0; + if (of_getprop(oprom, "model", version, sizeof(version)) <= 0) + return 0; + version[sizeof(version)-1] = 0; + printf("OF version = '%s'\r\n", version); + if (!string_match(version, "Open Firmware, 1.") + && !string_match(version, "FirmWorks,3.")) + return 0; + chosen = of_finddevice("/chosen"); + if (chosen == (phandle) -1) { + chosen = of_finddevice("/chosen@0"); + if (chosen == (phandle) -1) { + printf("no chosen\n"); + return 0; + } + } + if (of_getprop(chosen, "mmu", &chosen_mmu, sizeof(chosen_mmu)) <= 0) { + printf("no mmu\n"); + return 0; + } + memory = (ihandle) of_call_prom("open", 1, 1, "/memory"); + if (memory == (ihandle) -1) { + memory = (ihandle) of_call_prom("open", 1, 1, "/memory@0"); + if (memory == (ihandle) -1) { + printf("no memory node\n"); + return 0; + } + } + printf("old OF detected\r\n"); + return 1; +} + +void *of_claim(unsigned long virt, unsigned long size, unsigned long align) +{ + int ret; + unsigned int result; + + if (need_map < 0) + need_map = check_of_version(); + if (align || !need_map) + return (void *) of_call_prom("claim", 3, 1, virt, size, align); + + ret = of_call_prom_ret("call-method", 5, 2, &result, "claim", memory, + align, size, virt); + if (ret != 0 || result == -1) + return (void *) -1; + ret = of_call_prom_ret("call-method", 5, 2, &result, "claim", chosen_mmu, + align, size, virt); + /* 0x12 == coherent + read/write */ + ret = of_call_prom("call-method", 6, 1, "map", chosen_mmu, + 0x12, size, virt, virt); + return (void *) virt; +} + +void *of_vmlinux_alloc(unsigned long size) +{ + void *p = malloc(size); + + if (!p) + fatal("Can't allocate memory for kernel image!\n\r"); + + return p; +} + +void of_exit(void) +{ + of_call_prom("exit", 0, 0); +} + +/* + * OF device tree routines + */ +void *of_finddevice(const char *name) +{ + return (phandle) of_call_prom("finddevice", 1, 1, name); +} + +int of_getprop(const void *phandle, const char *name, void *buf, + const int buflen) +{ + return of_call_prom("getprop", 4, 1, phandle, name, buf, buflen); +} + +int of_setprop(const void *phandle, const char *name, const void *buf, + const int buflen) +{ + return of_call_prom("setprop", 4, 1, phandle, name, buf, buflen); +} diff --git a/arch/powerpc/boot/ops.h b/arch/powerpc/boot/ops.h index 959124f3f9a..86077066cd7 100644 --- a/arch/powerpc/boot/ops.h +++ b/arch/powerpc/boot/ops.h @@ -19,6 +19,8 @@ #define MAX_PATH_LEN 256 #define MAX_PROP_LEN 256 /* What should this be? */ +typedef void (*kernel_entry_t)(unsigned long r3, unsigned long r4, void *r5); + /* Platform specific operations */ struct platform_ops { void (*fixups)(void); @@ -51,7 +53,7 @@ extern struct dt_ops dt_ops; /* Console operations */ struct console_ops { int (*open)(void); - void (*write)(char *buf, int len); + void (*write)(const char *buf, int len); void (*edit_cmdline)(char *buf, int len); void (*close)(void); void *data; diff --git a/arch/powerpc/boot/ps3-head.S b/arch/powerpc/boot/ps3-head.S new file mode 100644 index 00000000000..1a6d64a68df --- /dev/null +++ b/arch/powerpc/boot/ps3-head.S @@ -0,0 +1,80 @@ +/* + * PS3 bootwrapper entry. + * + * Copyright (C) 2007 Sony Computer Entertainment Inc. + * Copyright 2007 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 "ppc_asm.h" + + .text + +/* + * __system_reset_overlay - The PS3 first stage entry. + * + * The bootwraper build script copies the 0x100 bytes at symbol + * __system_reset_overlay to offset 0x100 of the rom image. + * + * The PS3 has a single processor with two threads. + */ + + .globl __system_reset_overlay +__system_reset_overlay: + + /* Switch to 32-bit mode. */ + + mfmsr r9 + clrldi r9,r9,1 + mtmsrd r9 + nop + + /* Get thread number in r3 and branch. */ + + mfspr r3, 0x88 + cntlzw. r3, r3 + li r4, 0 + li r5, 0 + beq 1f + + /* Secondary goes to __secondary_hold in kernel. */ + + li r4, 0x60 + mtctr r4 + bctr + + /* Primary delays then goes to _zimage_start in wrapper. */ +1: + or 31, 31, 31 /* db16cyc */ + or 31, 31, 31 /* db16cyc */ + + lis r4, _zimage_start@ha + addi r4, r4, _zimage_start@l + mtctr r4 + bctr + +/* + * __system_reset_kernel - Place holder for the kernel reset vector. + * + * The bootwrapper build script copies 0x100 bytes from offset 0x100 + * of the rom image to the symbol __system_reset_kernel. At runtime + * the bootwrapper program copies the 0x100 bytes at __system_reset_kernel + * to ram address 0x100. This symbol must occupy 0x100 bytes. + */ + + .globl __system_reset_kernel +__system_reset_kernel: + + . = __system_reset_kernel + 0x100 diff --git a/arch/powerpc/boot/ps3-hvcall.S b/arch/powerpc/boot/ps3-hvcall.S new file mode 100644 index 00000000000..c8b7df3210d --- /dev/null +++ b/arch/powerpc/boot/ps3-hvcall.S @@ -0,0 +1,184 @@ +/* + * PS3 bootwrapper hvcalls. + * + * Copyright (C) 2007 Sony Computer Entertainment Inc. + * Copyright 2007 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 "ppc_asm.h" + +/* + * The PS3 hypervisor uses a 64 bit "C" language calling convention. + * The routines here marshal arguments between the 32 bit wrapper + * program and the 64 bit hvcalls. + * + * wrapper lv1 + * 32-bit (h,l) 64-bit + * + * 1: r3,r4 <-> r3 + * 2: r5,r6 <-> r4 + * 3: r7,r8 <-> r5 + * 4: r9,r10 <-> r6 + * 5: 8(r1),12(r1) <-> r7 + * 6: 16(r1),20(r1) <-> r8 + * 7: 24(r1),28(r1) <-> r9 + * 8: 32(r1),36(r1) <-> r10 + * + */ + +.macro GLOBAL name + .section ".text" + .balign 4 + .globl \name +\name: +.endm + +.macro NO_SUPPORT name + GLOBAL \name + b ps3_no_support +.endm + +.macro HVCALL num + li r11, \num + .long 0x44000022 + extsw r3, r3 +.endm + +.macro SAVE_LR offset=4 + mflr r0 + stw r0, \offset(r1) +.endm + +.macro LOAD_LR offset=4 + lwz r0, \offset(r1) + mtlr r0 +.endm + +.macro LOAD_64_REG target,high,low + sldi r11, \high, 32 + or \target, r11, \low +.endm + +.macro LOAD_64_STACK target,offset + ld \target, \offset(r1) +.endm + +.macro LOAD_R3 + LOAD_64_REG r3,r3,r4 +.endm + +.macro LOAD_R4 + LOAD_64_REG r4,r5,r6 +.endm + +.macro LOAD_R5 + LOAD_64_REG r5,r7,r8 +.endm + +.macro LOAD_R6 + LOAD_64_REG r6,r9,r10 +.endm + +.macro LOAD_R7 + LOAD_64_STACK r7,8 +.endm + +.macro LOAD_R8 + LOAD_64_STACK r8,16 +.endm + +.macro LOAD_R9 + LOAD_64_STACK r9,24 +.endm + +.macro LOAD_R10 + LOAD_64_STACK r10,32 +.endm + +.macro LOAD_REGS_0 + stwu 1,-16(1) + stw 3, 8(1) +.endm + +.macro LOAD_REGS_5 + LOAD_R3 + LOAD_R4 + LOAD_R5 + LOAD_R6 + LOAD_R7 +.endm + +.macro LOAD_REGS_6 + LOAD_REGS_5 + LOAD_R8 +.endm + +.macro LOAD_REGS_8 + LOAD_REGS_6 + LOAD_R9 + LOAD_R10 +.endm + +.macro STORE_REGS_0_1 + lwz r11, 8(r1) + std r4, 0(r11) + mr r4, r3 + li r3, 0 + addi r1,r1,16 +.endm + +.macro STORE_REGS_5_2 + lwz r11, 16(r1) + std r4, 0(r11) + lwz r11, 24(r1) + std r5, 0(r11) +.endm + +.macro STORE_REGS_6_1 + lwz r11, 24(r1) + std r4, 0(r11) +.endm + +GLOBAL lv1_get_logical_ppe_id + SAVE_LR + LOAD_REGS_0 + HVCALL 69 + STORE_REGS_0_1 + LOAD_LR + blr + +GLOBAL lv1_get_logical_partition_id + SAVE_LR + LOAD_REGS_0 + HVCALL 74 + STORE_REGS_0_1 + LOAD_LR + blr + +GLOBAL lv1_get_repository_node_value + SAVE_LR + LOAD_REGS_5 + HVCALL 91 + STORE_REGS_5_2 + LOAD_LR + blr + +GLOBAL lv1_panic + SAVE_LR + LOAD_REGS_8 + HVCALL 255 + LOAD_LR + blr diff --git a/arch/powerpc/boot/ps3.c b/arch/powerpc/boot/ps3.c new file mode 100644 index 00000000000..893d59339c2 --- /dev/null +++ b/arch/powerpc/boot/ps3.c @@ -0,0 +1,161 @@ +/* + * PS3 bootwrapper support. + * + * Copyright (C) 2007 Sony Computer Entertainment Inc. + * Copyright 2007 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 <stdarg.h> +#include <stddef.h> +#include "types.h" +#include "elf.h" +#include "string.h" +#include "stdio.h" +#include "page.h" +#include "ops.h" + +extern s64 lv1_panic(u64 in_1); +extern s64 lv1_get_logical_partition_id(u64 *out_1); +extern s64 lv1_get_logical_ppe_id(u64 *out_1); +extern s64 lv1_get_repository_node_value(u64 in_1, u64 in_2, u64 in_3, + u64 in_4, u64 in_5, u64 *out_1, u64 *out_2); + +#ifdef DEBUG +#define DBG(fmt...) printf(fmt) +#else +static inline int __attribute__ ((format (printf, 1, 2))) DBG( + const char *fmt, ...) {return 0;} +#endif + +BSS_STACK(4096); + +/* A buffer that may be edited by tools operating on a zImage binary so as to + * edit the command line passed to vmlinux (by setting /chosen/bootargs). + * The buffer is put in it's own section so that tools may locate it easier. + */ +static char cmdline[COMMAND_LINE_SIZE] + __attribute__((__section__("__builtin_cmdline"))); + +static void prep_cmdline(void *chosen) +{ + if (cmdline[0] == '\0') + getprop(chosen, "bootargs", cmdline, COMMAND_LINE_SIZE-1); + else + setprop_str(chosen, "bootargs", cmdline); + + printf("cmdline: '%s'\n", cmdline); +} + +static void ps3_console_write(const char *buf, int len) +{ +} + +static void ps3_exit(void) +{ + printf("ps3_exit\n"); + + /* lv1_panic will shutdown the lpar. */ + + lv1_panic(0); /* zero = do not reboot */ + while (1); +} + +static int ps3_repository_read_rm_size(u64 *rm_size) +{ + s64 result; + u64 lpar_id; + u64 ppe_id; + u64 v2; + + result = lv1_get_logical_partition_id(&lpar_id); + + if (result) + return -1; + + result = lv1_get_logical_ppe_id(&ppe_id); + + if (result) + return -1; + + /* + * n1: 0000000062690000 : ....bi.. + * n2: 7075000000000000 : pu...... + * n3: 0000000000000001 : ........ + * n4: 726d5f73697a6500 : rm_size. + */ + + result = lv1_get_repository_node_value(lpar_id, 0x0000000062690000ULL, + 0x7075000000000000ULL, ppe_id, 0x726d5f73697a6500ULL, rm_size, + &v2); + + printf("%s:%d: ppe_id %lu \n", __func__, __LINE__, + (unsigned long)ppe_id); + printf("%s:%d: lpar_id %lu \n", __func__, __LINE__, + (unsigned long)lpar_id); + printf("%s:%d: rm_size %llxh \n", __func__, __LINE__, *rm_size); + + return result ? -1 : 0; +} + +void ps3_copy_vectors(void) +{ + extern char __system_reset_kernel[]; + + memcpy((void *)0x100, __system_reset_kernel, 0x100); + flush_cache((void *)0x100, 0x100); +} + +void platform_init(void) +{ + extern char _end[]; + extern char _dtb_start[]; + extern char _initrd_start[]; + extern char _initrd_end[]; + const u32 heapsize = 0x1000000 - (u32)_end; /* 16MiB */ + void *chosen; + unsigned long ft_addr; + u64 rm_size; + + console_ops.write = ps3_console_write; + platform_ops.exit = ps3_exit; + + printf("\n-- PS3 bootwrapper --\n"); + + simple_alloc_init(_end, heapsize, 32, 64); + ft_init(_dtb_start, 0, 4); + + chosen = finddevice("/chosen"); + + ps3_repository_read_rm_size(&rm_size); + dt_fixup_memory(0, rm_size); + + if (_initrd_end > _initrd_start) { + setprop_val(chosen, "linux,initrd-start", (u32)(_initrd_start)); + setprop_val(chosen, "linux,initrd-end", (u32)(_initrd_end)); + } + + prep_cmdline(chosen); + + ft_addr = dt_ops.finalize(); + + ps3_copy_vectors(); + + printf(" flat tree at 0x%lx\n\r", ft_addr); + + ((kernel_entry_t)0)(ft_addr, 0, NULL); + + ps3_exit(); +} diff --git a/arch/powerpc/boot/serial.c b/arch/powerpc/boot/serial.c index 7fd32330a9a..eaa0d3ae351 100644 --- a/arch/powerpc/boot/serial.c +++ b/arch/powerpc/boot/serial.c @@ -27,7 +27,7 @@ static int serial_open(void) return scdp->open(); } -static void serial_write(char *buf, int len) +static void serial_write(const char *buf, int len) { struct serial_console_data *scdp = console_ops.data; diff --git a/arch/powerpc/boot/stdio.c b/arch/powerpc/boot/stdio.c index 0a9feeb9834..5b57800bbc6 100644 --- a/arch/powerpc/boot/stdio.c +++ b/arch/powerpc/boot/stdio.c @@ -190,7 +190,11 @@ int vsprintf(char *buf, const char *fmt, va_list args) /* get the conversion qualifier */ qualifier = -1; - if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || *fmt =='Z') { + if (*fmt == 'l' && *(fmt + 1) == 'l') { + qualifier = 'q'; + fmt += 2; + } else if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' + || *fmt == 'Z') { qualifier = *fmt; ++fmt; } @@ -281,6 +285,10 @@ int vsprintf(char *buf, const char *fmt, va_list args) num = va_arg(args, unsigned long); if (flags & SIGN) num = (signed long) num; + } else if (qualifier == 'q') { + num = va_arg(args, unsigned long long); + if (flags & SIGN) + num = (signed long long) num; } else if (qualifier == 'Z') { num = va_arg(args, size_t); } else if (qualifier == 'h') { diff --git a/arch/powerpc/boot/types.h b/arch/powerpc/boot/types.h index 79d26e70867..31393d17a9c 100644 --- a/arch/powerpc/boot/types.h +++ b/arch/powerpc/boot/types.h @@ -7,6 +7,10 @@ typedef unsigned char u8; typedef unsigned short u16; typedef unsigned int u32; typedef unsigned long long u64; +typedef signed char s8; +typedef short s16; +typedef int s32; +typedef long long s64; #define min(x,y) ({ \ typeof(x) _x = (x); \ diff --git a/arch/powerpc/boot/wrapper b/arch/powerpc/boot/wrapper index da77adc7307..65f68547917 100755 --- a/arch/powerpc/boot/wrapper +++ b/arch/powerpc/boot/wrapper @@ -144,6 +144,15 @@ miboot|uboot) cuboot*) gzip= ;; +ps3) + platformo="$object/ps3-head.o $object/ps3-hvcall.o $object/ps3.o" + lds=$object/zImage.ps3.lds + gzip= + ext=bin + objflags="-O binary --set-section-flags=.bss=contents,alloc,load,data" + ksection=.kernel:vmlinux.bin + isection=.kernel:initrd + ;; esac vmz="$tmpdir/`basename \"$kernel\"`.$ext" @@ -239,4 +248,50 @@ treeboot*) fi exit 0 ;; +ps3) + # The ps3's loader supports loading gzipped binary images from flash + # rom to addr zero. The loader enters the image at addr 0x100. A + # bootwrapper overlay is use to arrange for the kernel to be loaded + # to addr zero and to have a suitable bootwrapper entry at 0x100. + # To construct the rom image, 0x100 bytes from offset 0x100 in the + # kernel is copied to the bootwrapper symbol __system_reset_kernel. + # The 0x100 bytes at the bootwrapper symbol __system_reset_overlay is + # then copied to offset 0x100. At runtime the bootwrapper program + # copies the 0x100 bytes at __system_reset_kernel to addr 0x100. + + system_reset_overlay=0x`${CROSS}nm "$ofile" \ + | grep ' __system_reset_overlay$' \ + | cut -d' ' -f1` + system_reset_overlay=`printf "%d" $system_reset_overlay` + system_reset_kernel=0x`${CROSS}nm "$ofile" \ + | grep ' __system_reset_kernel$' \ + | cut -d' ' -f1` + system_reset_kernel=`printf "%d" $system_reset_kernel` + overlay_dest="256" + overlay_size="256" + + rm -f "$object/otheros.bld" + + ${CROSS}objcopy -O binary "$ofile" "$ofile.bin" + + msg=$(dd if="$ofile.bin" of="$ofile.bin" conv=notrunc \ + skip=$overlay_dest seek=$system_reset_kernel \ + count=$overlay_size bs=1 2>&1) + + if [ $? -ne "0" ]; then + echo $msg + exit 1 + fi + + msg=$(dd if="$ofile.bin" of="$ofile.bin" conv=notrunc \ + skip=$system_reset_overlay seek=$overlay_dest \ + count=$overlay_size bs=1 2>&1) + + if [ $? -ne "0" ]; then + echo $msg + exit 2 + fi + + gzip --force -9 --stdout "$ofile.bin" > "$object/otheros.bld" + ;; esac diff --git a/arch/powerpc/boot/zImage.ps3.lds.S b/arch/powerpc/boot/zImage.ps3.lds.S new file mode 100644 index 00000000000..aaa469c1e60 --- /dev/null +++ b/arch/powerpc/boot/zImage.ps3.lds.S @@ -0,0 +1,50 @@ +OUTPUT_ARCH(powerpc:common) +ENTRY(_zimage_start) +EXTERN(_zimage_start) +SECTIONS +{ + _vmlinux_start = .; + .kernel:vmlinux.bin : { *(.kernel:vmlinux.bin) } + _vmlinux_end = .; + + . = ALIGN(4096); + _dtb_start = .; + .kernel:dtb : { *(.kernel:dtb) } + _dtb_end = .; + + . = ALIGN(4096); + _initrd_start = .; + .kernel:initrd : { *(.kernel:initrd) } + _initrd_end = .; + + _start = .; + .text : + { + *(.text) + *(.fixup) + } + _etext = .; + . = ALIGN(4096); + .data : + { + *(.rodata*) + *(.data*) + *(.sdata*) + __got2_start = .; + *(.got2) + __got2_end = .; + } + + . = ALIGN(4096); + _edata = .; + + . = ALIGN(4096); + __bss_start = .; + .bss : + { + *(.sbss) + *(.bss) + } + . = ALIGN(4096); + _end = . ; +} diff --git a/arch/powerpc/configs/holly_defconfig b/arch/powerpc/configs/holly_defconfig index 32781849ad4..04b94f884aa 100644 --- a/arch/powerpc/configs/holly_defconfig +++ b/arch/powerpc/configs/holly_defconfig @@ -190,10 +190,12 @@ CONFIG_SPLIT_PTLOCK_CPUS=4 # CONFIG_RESOURCES_64BIT is not set CONFIG_ZONE_DMA_FLAG=1 CONFIG_PROC_DEVICETREE=y -# CONFIG_CMDLINE_BOOL is not set +CONFIG_CMDLINE_BOOL=y +CONFIG_CMDLINE="console=ttyS0,115200" # CONFIG_PM is not set # CONFIG_SECCOMP is not set -# CONFIG_WANT_DEVICE_TREE is not set +CONFIG_WANT_DEVICE_TREE=y +CONFIG_DEVICE_TREE="holly.dts" CONFIG_ISA_DMA_API=y # diff --git a/arch/powerpc/configs/ps3_defconfig b/arch/powerpc/configs/ps3_defconfig index 956d1df61e0..d0b43df4442 100644 --- a/arch/powerpc/configs/ps3_defconfig +++ b/arch/powerpc/configs/ps3_defconfig @@ -156,7 +156,11 @@ CONFIG_PS3_HTAB_SIZE=20 CONFIG_PS3_USE_LPAR_ADDR=y CONFIG_PS3_VUART=y CONFIG_PS3_PS3AV=y -CONFIG_PS3_SYS_MANAGER=y +CONFIG_PS3_SYS_MANAGER=m +CONFIG_PS3_STORAGE=y +CONFIG_PS3_DISK=y +CONFIG_PS3_ROM=y +CONFIG_PS3_FLASH=y CONFIG_PPC_CELL=y # CONFIG_PPC_CELL_NATIVE is not set # CONFIG_PPC_IBM_CELL_BLADE is not set @@ -335,7 +339,7 @@ CONFIG_BT=m CONFIG_BT_L2CAP=m CONFIG_BT_SCO=m CONFIG_BT_RFCOMM=m -# CONFIG_BT_RFCOMM_TTY is not set +CONFIG_BT_RFCOMM_TTY=y # CONFIG_BT_BNEP is not set CONFIG_BT_HIDP=m @@ -344,7 +348,9 @@ CONFIG_BT_HIDP=m # CONFIG_BT_HCIUSB=m CONFIG_BT_HCIUSB_SCO=y -# CONFIG_BT_HCIUART is not set +CONFIG_BT_HCIUART=m +CONFIG_BT_HCIUART_H4=y +CONFIG_BT_HCIUART_BCSP=y # CONFIG_BT_HCIBCM203X is not set # CONFIG_BT_HCIBPA10X is not set # CONFIG_BT_HCIBFUSB is not set @@ -435,7 +441,7 @@ CONFIG_CHR_DEV_SG=m # # Some SCSI devices (e.g. CD jukebox) support multiple LUNs # -# CONFIG_SCSI_MULTI_LUN is not set +CONFIG_SCSI_MULTI_LUN=y # CONFIG_SCSI_CONSTANTS is not set # CONFIG_SCSI_LOGGING is not set # CONFIG_SCSI_SCAN_ASYNC is not set @@ -479,6 +485,7 @@ CONFIG_NETDEVICES=y CONFIG_MII=m CONFIG_NETDEV_1000=y CONFIG_NETDEV_10000=y +CONFIG_GELIC_NET=y # # Wireless LAN @@ -546,7 +553,27 @@ CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 # # CONFIG_INPUT_KEYBOARD is not set # CONFIG_INPUT_MOUSE is not set -# CONFIG_INPUT_JOYSTICK is not set +CONFIG_INPUT_JOYSTICK=y +# CONFIG_JOYSTICK_ANALOG is not set +# CONFIG_JOYSTICK_A3D is not set +# CONFIG_JOYSTICK_ADI is not set +# CONFIG_JOYSTICK_COBRA is not set +# CONFIG_JOYSTICK_GF2K is not set +# CONFIG_JOYSTICK_GRIP is not set +# CONFIG_JOYSTICK_GRIP_MP is not set +# CONFIG_JOYSTICK_GUILLEMOT is not set +# CONFIG_JOYSTICK_INTERACT is not set +# CONFIG_JOYSTICK_SIDEWINDER is not set +# CONFIG_JOYSTICK_TMDC is not set +# CONFIG_JOYSTICK_IFORCE is not set +# CONFIG_JOYSTICK_WARRIOR is not set +# CONFIG_JOYSTICK_MAGELLAN is not set +# CONFIG_JOYSTICK_SPACEORB is not set +# CONFIG_JOYSTICK_SPACEBALL is not set +# CONFIG_JOYSTICK_STINGER is not set +# CONFIG_JOYSTICK_TWIDJOY is not set +# CONFIG_JOYSTICK_JOYDUMP is not set +# CONFIG_JOYSTICK_XPAD is not set # CONFIG_INPUT_TABLET is not set # CONFIG_INPUT_TOUCHSCREEN is not set # CONFIG_INPUT_MISC is not set @@ -563,7 +590,7 @@ CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 CONFIG_VT=y CONFIG_VT_CONSOLE=y CONFIG_HW_CONSOLE=y -# CONFIG_VT_HW_CONSOLE_BINDING is not set +CONFIG_VT_HW_CONSOLE_BINDING=y # CONFIG_SERIAL_NONSTANDARD is not set # @@ -1086,7 +1113,7 @@ CONFIG_HAS_DMA=y # # CONFIG_PRINTK_TIME is not set CONFIG_ENABLE_MUST_CHECK=y -# CONFIG_MAGIC_SYSRQ is not set +CONFIG_MAGIC_SYSRQ=y # CONFIG_UNUSED_SYMBOLS is not set # CONFIG_DEBUG_FS is not set # CONFIG_HEADERS_CHECK is not set @@ -1116,16 +1143,7 @@ CONFIG_DEBUG_STACKOVERFLOW=y # CONFIG_DEBUGGER is not set CONFIG_IRQSTACKS=y # CONFIG_BOOTX_TEXT is not set -CONFIG_PPC_EARLY_DEBUG=y -# CONFIG_PPC_EARLY_DEBUG_LPAR is not set -# CONFIG_PPC_EARLY_DEBUG_G5 is not set -# CONFIG_PPC_EARLY_DEBUG_RTAS_PANEL is not set -# CONFIG_PPC_EARLY_DEBUG_RTAS_CONSOLE is not set -# CONFIG_PPC_EARLY_DEBUG_MAPLE is not set -# CONFIG_PPC_EARLY_DEBUG_ISERIES is not set -# CONFIG_PPC_EARLY_DEBUG_PAS_REALMODE is not set -# CONFIG_PPC_EARLY_DEBUG_BEAT is not set -# CONFIG_PPC_EARLY_DEBUG_44x is not set +# CONFIG_PPC_EARLY_DEBUG is not set # # Security options diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile index 3e779f07f21..42c42ecad00 100644 --- a/arch/powerpc/kernel/Makefile +++ b/arch/powerpc/kernel/Makefile @@ -12,7 +12,8 @@ endif obj-y := semaphore.o cputable.o ptrace.o syscalls.o \ irq.o align.o signal_32.o pmc.o vdso.o \ - init_task.o process.o systbl.o idle.o + init_task.o process.o systbl.o idle.o \ + signal.o obj-y += vdso32/ obj-$(CONFIG_PPC64) += setup_64.o binfmt_elf32.o sys_ppc32.o \ signal_64.o ptrace32.o \ @@ -65,9 +66,9 @@ obj-$(CONFIG_PPC_UDBG_16550) += legacy_serial.o udbg_16550.o module-$(CONFIG_PPC64) += module_64.o obj-$(CONFIG_MODULES) += $(module-y) -pci64-$(CONFIG_PPC64) += pci_64.o pci_dn.o +pci64-$(CONFIG_PPC64) += pci_64.o pci_dn.o isa-bridge.o pci32-$(CONFIG_PPC32) := pci_32.o -obj-$(CONFIG_PCI) += $(pci64-y) $(pci32-y) +obj-$(CONFIG_PCI) += $(pci64-y) $(pci32-y) pci-common.o obj-$(CONFIG_PCI_MSI) += msi.o kexec-$(CONFIG_PPC64) := machine_kexec_64.o kexec-$(CONFIG_PPC32) := machine_kexec_32.o diff --git a/arch/powerpc/kernel/cputable.c b/arch/powerpc/kernel/cputable.c index b2b5d664d32..b1f8000952f 100644 --- a/arch/powerpc/kernel/cputable.c +++ b/arch/powerpc/kernel/cputable.c @@ -294,6 +294,21 @@ static struct cpu_spec cpu_specs[] = { .oprofile_mmcra_sipr = MMCRA_SIPR, .platform = "power5", }, + { /* Power5++ */ + .pvr_mask = 0xffffff00, + .pvr_value = 0x003b0300, + .cpu_name = "POWER5+ (gs)", + .cpu_features = CPU_FTRS_POWER5, + .cpu_user_features = COMMON_USER_POWER5_PLUS, + .icache_bsize = 128, + .dcache_bsize = 128, + .num_pmcs = 6, + .oprofile_cpu_type = "ppc64/power5++", + .oprofile_type = PPC_OPROFILE_POWER4, + .oprofile_mmcra_sihv = MMCRA_SIHV, + .oprofile_mmcra_sipr = MMCRA_SIPR, + .platform = "power5+", + }, { /* Power5 GS */ .pvr_mask = 0xffff0000, .pvr_value = 0x003b0000, @@ -1178,8 +1193,8 @@ static struct cpu_spec cpu_specs[] = { .platform = "ppc440", }, { /* 440SP Rev. A */ - .pvr_mask = 0xff000fff, - .pvr_value = 0x53000891, + .pvr_mask = 0xfff00fff, + .pvr_value = 0x53200891, .cpu_name = "440SP Rev. A", .cpu_features = CPU_FTRS_44X, .cpu_user_features = COMMON_USER_BOOKE, @@ -1188,9 +1203,19 @@ static struct cpu_spec cpu_specs[] = { .platform = "ppc440", }, { /* 440SPe Rev. A */ - .pvr_mask = 0xff000fff, - .pvr_value = 0x53000890, - .cpu_name = "440SPe Rev. A", + .pvr_mask = 0xfff00fff, + .pvr_value = 0x53400890, + .cpu_name = "440SPe Rev. A", + .cpu_features = CPU_FTRS_44X, + .cpu_user_features = COMMON_USER_BOOKE, + .icache_bsize = 32, + .dcache_bsize = 32, + .platform = "ppc440", + }, + { /* 440SPe Rev. B */ + .pvr_mask = 0xfff00fff, + .pvr_value = 0x53400891, + .cpu_name = "440SPe Rev. B", .cpu_features = CPU_FTRS_44X, .cpu_user_features = COMMON_USER_BOOKE, .icache_bsize = 32, diff --git a/arch/powerpc/kernel/head_32.S b/arch/powerpc/kernel/head_32.S index c897203198b..7d73a13450b 100644 --- a/arch/powerpc/kernel/head_32.S +++ b/arch/powerpc/kernel/head_32.S @@ -9,7 +9,6 @@ * rewritten by Paul Mackerras. * Copyright (C) 1996 Paul Mackerras. * MPC8xx modifications Copyright (C) 1997 Dan Malek (dmalek@jlc.net). - * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). * * This file contains the low-level support and setup for the * PowerPC platform, including trap and interrupt dispatch. @@ -32,10 +31,6 @@ #include <asm/ppc_asm.h> #include <asm/asm-offsets.h> -#ifdef CONFIG_APUS -#include <asm/amigappc.h> -#endif - /* 601 only have IBAT; cr0.eq is set on 601 when using this macro */ #define LOAD_BAT(n, reg, RA, RB) \ /* see the comment for clear_bats() -- Cort */ \ @@ -92,11 +87,6 @@ _start: * r4: virtual address of boot_infos_t * r5: 0 * - * APUS - * r3: 'APUS' - * r4: physical address of memory base - * Linux/m68k style BootInfo structure at &_end. - * * PREP * This is jumped to on prep systems right after the kernel is relocated * to its proper place in memory by the boot loader. The expected layout @@ -150,14 +140,6 @@ __start: */ bl early_init -#ifdef CONFIG_APUS -/* On APUS the __va/__pa constants need to be set to the correct - * values before continuing. - */ - mr r4,r30 - bl fix_mem_constants -#endif /* CONFIG_APUS */ - /* Switch MMU off, clear BATs and flush TLB. At this point, r3 contains * the physical address we are running at, returned by early_init() */ @@ -167,7 +149,7 @@ __after_mmu_off: bl flush_tlbs bl initial_bats -#if !defined(CONFIG_APUS) && defined(CONFIG_BOOTX_TEXT) +#if defined(CONFIG_BOOTX_TEXT) bl setup_disp_bat #endif @@ -183,7 +165,6 @@ __after_mmu_off: #endif /* CONFIG_6xx */ -#ifndef CONFIG_APUS /* * We need to run with _start at physical address 0. * On CHRP, we are loaded at 0x10000 since OF on CHRP uses @@ -196,7 +177,6 @@ __after_mmu_off: addis r4,r3,KERNELBASE@h /* current address of _start */ cmpwi 0,r4,0 /* are we already running at 0? */ bne relocate_kernel -#endif /* CONFIG_APUS */ /* * we now have the 1st 16M of ram mapped with the bats. * prep needs the mmu to be turned on here, but pmac already has it on. @@ -881,85 +861,6 @@ _GLOBAL(copy_and_flush) addi r6,r6,4 blr -#ifdef CONFIG_APUS -/* - * On APUS the physical base address of the kernel is not known at compile - * time, which means the __pa/__va constants used are incorrect. In the - * __init section is recorded the virtual addresses of instructions using - * these constants, so all that has to be done is fix these before - * continuing the kernel boot. - * - * r4 = The physical address of the kernel base. - */ -fix_mem_constants: - mr r10,r4 - addis r10,r10,-KERNELBASE@h /* virt_to_phys constant */ - neg r11,r10 /* phys_to_virt constant */ - - lis r12,__vtop_table_begin@h - ori r12,r12,__vtop_table_begin@l - add r12,r12,r10 /* table begin phys address */ - lis r13,__vtop_table_end@h - ori r13,r13,__vtop_table_end@l - add r13,r13,r10 /* table end phys address */ - subi r12,r12,4 - subi r13,r13,4 -1: lwzu r14,4(r12) /* virt address of instruction */ - add r14,r14,r10 /* phys address of instruction */ - lwz r15,0(r14) /* instruction, now insert top */ - rlwimi r15,r10,16,16,31 /* half of vp const in low half */ - stw r15,0(r14) /* of instruction and restore. */ - dcbst r0,r14 /* write it to memory */ - sync - icbi r0,r14 /* flush the icache line */ - cmpw r12,r13 - bne 1b - sync /* additional sync needed on g4 */ - isync - -/* - * Map the memory where the exception handlers will - * be copied to when hash constants have been patched. - */ -#ifdef CONFIG_APUS_FAST_EXCEPT - lis r8,0xfff0 -#else - lis r8,0 -#endif - ori r8,r8,0x2 /* 128KB, supervisor */ - mtspr SPRN_DBAT3U,r8 - mtspr SPRN_DBAT3L,r8 - - lis r12,__ptov_table_begin@h - ori r12,r12,__ptov_table_begin@l - add r12,r12,r10 /* table begin phys address */ - lis r13,__ptov_table_end@h - ori r13,r13,__ptov_table_end@l - add r13,r13,r10 /* table end phys address */ - subi r12,r12,4 - subi r13,r13,4 -1: lwzu r14,4(r12) /* virt address of instruction */ - add r14,r14,r10 /* phys address of instruction */ - lwz r15,0(r14) /* instruction, now insert top */ - rlwimi r15,r11,16,16,31 /* half of pv const in low half*/ - stw r15,0(r14) /* of instruction and restore. */ - dcbst r0,r14 /* write it to memory */ - sync - icbi r0,r14 /* flush the icache line */ - cmpw r12,r13 - bne 1b - - sync /* additional sync needed on g4 */ - isync /* No speculative loading until now */ - blr - -/*********************************************************************** - * Please note that on APUS the exception handlers are located at the - * physical address 0xfff0000. For this reason, the exception handlers - * cannot use relative branches to access the code below. - ***********************************************************************/ -#endif /* CONFIG_APUS */ - #ifdef CONFIG_SMP #ifdef CONFIG_GEMINI .globl __secondary_start_gemini @@ -1135,19 +1036,6 @@ start_here: bl __save_cpu_setup bl MMU_init -#ifdef CONFIG_APUS - /* Copy exception code to exception vector base on APUS. */ - lis r4,KERNELBASE@h -#ifdef CONFIG_APUS_FAST_EXCEPT - lis r3,0xfff0 /* Copy to 0xfff00000 */ -#else - lis r3,0 /* Copy to 0x00000000 */ -#endif - li r5,0x4000 /* # bytes of memory to copy */ - li r6,0 - bl copy_and_flush /* copy the first 0x4000 bytes */ -#endif /* CONFIG_APUS */ - /* * Go back to running unmapped so we can load up new values * for SDR1 (hash table pointer) and the segment registers @@ -1324,11 +1212,7 @@ initial_bats: #else ori r8,r8,2 /* R/W access */ #endif /* CONFIG_SMP */ -#ifdef CONFIG_APUS - ori r11,r11,BL_8M<<2|0x2 /* set up 8MB BAT registers for 604 */ -#else ori r11,r11,BL_256M<<2|0x2 /* set up BAT registers for 604 */ -#endif /* CONFIG_APUS */ mtspr SPRN_DBAT0L,r8 /* N.B. 6xx (not 601) have valid */ mtspr SPRN_DBAT0U,r11 /* bit in upper BAT register */ @@ -1338,7 +1222,7 @@ initial_bats: blr -#if !defined(CONFIG_APUS) && defined(CONFIG_BOOTX_TEXT) +#ifdef CONFIG_BOOTX_TEXT setup_disp_bat: /* * setup the display bat prepared for us in prom.c @@ -1362,7 +1246,7 @@ setup_disp_bat: 1: mtspr SPRN_IBAT3L,r8 mtspr SPRN_IBAT3U,r11 blr -#endif /* !defined(CONFIG_APUS) && defined(CONFIG_BOOTX_TEXT) */ +#endif /* CONFIG_BOOTX_TEXT */ #ifdef CONFIG_8260 /* Jump into the system reset for the rom. diff --git a/arch/powerpc/kernel/head_64.S b/arch/powerpc/kernel/head_64.S index 1111fcec767..8cdd48ea439 100644 --- a/arch/powerpc/kernel/head_64.S +++ b/arch/powerpc/kernel/head_64.S @@ -103,8 +103,8 @@ __secondary_hold_acknowledge: . = 0x60 /* - * The following code is used on pSeries to hold secondary processors - * in a spin loop after they have been freed from OpenFirmware, but + * The following code is used to hold secondary processors + * in a spin loop after they have entered the kernel, but * before the bulk of the kernel has been relocated. This code * is relocated to physical address 0x60 before prom_init is run. * All of it must fit below the first exception vector at 0x100. diff --git a/arch/powerpc/kernel/io.c b/arch/powerpc/kernel/io.c index 34ae11494dd..e31aca9208e 100644 --- a/arch/powerpc/kernel/io.c +++ b/arch/powerpc/kernel/io.c @@ -35,7 +35,7 @@ void _insb(const volatile u8 __iomem *port, void *buf, long count) asm volatile("sync"); do { tmp = *port; - asm volatile("eieio"); + eieio(); *tbuf++ = tmp; } while (--count != 0); asm volatile("twi 0,%0,0; isync" : : "r" (tmp)); @@ -66,7 +66,7 @@ void _insw_ns(const volatile u16 __iomem *port, void *buf, long count) asm volatile("sync"); do { tmp = *port; - asm volatile("eieio"); + eieio(); *tbuf++ = tmp; } while (--count != 0); asm volatile("twi 0,%0,0; isync" : : "r" (tmp)); @@ -97,7 +97,7 @@ void _insl_ns(const volatile u32 __iomem *port, void *buf, long count) asm volatile("sync"); do { tmp = *port; - asm volatile("eieio"); + eieio(); *tbuf++ = tmp; } while (--count != 0); asm volatile("twi 0,%0,0; isync" : : "r" (tmp)); @@ -155,21 +155,21 @@ void _memcpy_fromio(void *dest, const volatile void __iomem *src, __asm__ __volatile__ ("sync" : : : "memory"); while(n && (!IO_CHECK_ALIGN(vsrc, 4) || !IO_CHECK_ALIGN(dest, 4))) { *((u8 *)dest) = *((volatile u8 *)vsrc); - __asm__ __volatile__ ("eieio" : : : "memory"); + eieio(); vsrc++; dest++; n--; } while(n > 4) { *((u32 *)dest) = *((volatile u32 *)vsrc); - __asm__ __volatile__ ("eieio" : : : "memory"); + eieio(); vsrc += 4; dest += 4; n -= 4; } while(n) { *((u8 *)dest) = *((volatile u8 *)vsrc); - __asm__ __volatile__ ("eieio" : : : "memory"); + eieio(); vsrc++; dest++; n--; diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c index c2b84c64db2..2fc87862146 100644 --- a/arch/powerpc/kernel/irq.c +++ b/arch/powerpc/kernel/irq.c @@ -7,7 +7,6 @@ * Copyright (C) 1996-2001 Cort Dougan * Adapted for Power Macintosh by Paul Mackerras * Copyright (C) 1996 Paul Mackerras (paulus@cs.anu.edu.au) - * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -337,7 +336,8 @@ void do_IRQ(struct pt_regs *regs) void __init init_IRQ(void) { - ppc_md.init_IRQ(); + if (ppc_md.init_IRQ) + ppc_md.init_IRQ(); #ifdef CONFIG_PPC64 irq_ctx_init(); #endif @@ -597,6 +597,49 @@ static void irq_radix_rdunlock(unsigned long flags) local_irq_restore(flags); } +static int irq_setup_virq(struct irq_host *host, unsigned int virq, + irq_hw_number_t hwirq) +{ + /* Clear IRQ_NOREQUEST flag */ + get_irq_desc(virq)->status &= ~IRQ_NOREQUEST; + + /* map it */ + smp_wmb(); + irq_map[virq].hwirq = hwirq; + smp_mb(); + + if (host->ops->map(host, virq, hwirq)) { + pr_debug("irq: -> mapping failed, freeing\n"); + irq_free_virt(virq, 1); + return -1; + } + + return 0; +} + +unsigned int irq_create_direct_mapping(struct irq_host *host) +{ + unsigned int virq; + + if (host == NULL) + host = irq_default_host; + + BUG_ON(host == NULL); + WARN_ON(host->revmap_type != IRQ_HOST_MAP_NOMAP); + + virq = irq_alloc_virt(host, 1, 0); + if (virq == NO_IRQ) { + pr_debug("irq: create_direct virq allocation failed\n"); + return NO_IRQ; + } + + pr_debug("irq: create_direct obtained virq %d\n", virq); + + if (irq_setup_virq(host, virq, virq)) + return NO_IRQ; + + return virq; +} unsigned int irq_create_mapping(struct irq_host *host, irq_hw_number_t hwirq) @@ -645,18 +688,9 @@ unsigned int irq_create_mapping(struct irq_host *host, } pr_debug("irq: -> obtained virq %d\n", virq); - /* Clear IRQ_NOREQUEST flag */ - get_irq_desc(virq)->status &= ~IRQ_NOREQUEST; - - /* map it */ - smp_wmb(); - irq_map[virq].hwirq = hwirq; - smp_mb(); - if (host->ops->map(host, virq, hwirq)) { - pr_debug("irq: -> mapping failed, freeing\n"); - irq_free_virt(virq, 1); + if (irq_setup_virq(host, virq, hwirq)) return NO_IRQ; - } + return virq; } EXPORT_SYMBOL_GPL(irq_create_mapping); diff --git a/arch/powerpc/kernel/isa-bridge.c b/arch/powerpc/kernel/isa-bridge.c new file mode 100644 index 00000000000..f0f49d1be3d --- /dev/null +++ b/arch/powerpc/kernel/isa-bridge.c @@ -0,0 +1,271 @@ +/* + * Routines for tracking a legacy ISA bridge + * + * Copyrigh 2007 Benjamin Herrenschmidt <benh@kernel.crashing.org>, IBM Corp. + * + * Some bits and pieces moved over from pci_64.c + * + * Copyrigh 2003 Anton Blanchard <anton@au.ibm.com>, IBM 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; either version + * 2 of the License, or (at your option) any later version. + */ + +#define DEBUG + +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/string.h> +#include <linux/init.h> +#include <linux/mm.h> +#include <linux/notifier.h> + +#include <asm/processor.h> +#include <asm/io.h> +#include <asm/prom.h> +#include <asm/pci-bridge.h> +#include <asm/machdep.h> +#include <asm/ppc-pci.h> +#include <asm/firmware.h> + +unsigned long isa_io_base; /* NULL if no ISA bus */ +EXPORT_SYMBOL(isa_io_base); + +/* Cached ISA bridge dev. */ +static struct device_node *isa_bridge_devnode; +struct pci_dev *isa_bridge_pcidev; +EXPORT_SYMBOL_GPL(isa_bridge_pcidev); + +#define ISA_SPACE_MASK 0x1 +#define ISA_SPACE_IO 0x1 + +static void __devinit pci_process_ISA_OF_ranges(struct device_node *isa_node, + unsigned long phb_io_base_phys) +{ + /* We should get some saner parsing here and remove these structs */ + struct pci_address { + u32 a_hi; + u32 a_mid; + u32 a_lo; + }; + + struct isa_address { + u32 a_hi; + u32 a_lo; + }; + + struct isa_range { + struct isa_address isa_addr; + struct pci_address pci_addr; + unsigned int size; + }; + + const struct isa_range *range; + unsigned long pci_addr; + unsigned int isa_addr; + unsigned int size; + int rlen = 0; + + range = of_get_property(isa_node, "ranges", &rlen); + if (range == NULL || (rlen < sizeof(struct isa_range))) + goto inval_range; + + /* From "ISA Binding to 1275" + * The ranges property is laid out as an array of elements, + * each of which comprises: + * cells 0 - 1: an ISA address + * cells 2 - 4: a PCI address + * (size depending on dev->n_addr_cells) + * cell 5: the size of the range + */ + if ((range->isa_addr.a_hi && ISA_SPACE_MASK) != ISA_SPACE_IO) { + range++; + rlen -= sizeof(struct isa_range); + if (rlen < sizeof(struct isa_range)) + goto inval_range; + } + if ((range->isa_addr.a_hi && ISA_SPACE_MASK) != ISA_SPACE_IO) + goto inval_range; + + isa_addr = range->isa_addr.a_lo; + pci_addr = (unsigned long) range->pci_addr.a_mid << 32 | + range->pci_addr.a_lo; + + /* Assume these are both zero. Note: We could fix that and + * do a proper parsing instead ... oh well, that will do for + * now as nobody uses fancy mappings for ISA bridges + */ + if ((pci_addr != 0) || (isa_addr != 0)) { + printk(KERN_ERR "unexpected isa to pci mapping: %s\n", + __FUNCTION__); + return; + } + + /* Align size and make sure it's cropped to 64K */ + size = PAGE_ALIGN(range->size); + if (size > 0x10000) + size = 0x10000; + + printk(KERN_ERR "no ISA IO ranges or unexpected isa range," + "mapping 64k\n"); + + __ioremap_at(phb_io_base_phys, (void *)ISA_IO_BASE, + size, _PAGE_NO_CACHE|_PAGE_GUARDED); + return; + +inval_range: + printk(KERN_ERR "no ISA IO ranges or unexpected isa range," + "mapping 64k\n"); + __ioremap_at(phb_io_base_phys, (void *)ISA_IO_BASE, + 0x10000, _PAGE_NO_CACHE|_PAGE_GUARDED); +} + + +/** + * isa_bridge_find_early - Find and map the ISA IO space early before + * main PCI discovery. This is optionally called by + * the arch code when adding PCI PHBs to get early + * access to ISA IO ports + */ +void __init isa_bridge_find_early(struct pci_controller *hose) +{ + struct device_node *np, *parent = NULL, *tmp; + + /* If we already have an ISA bridge, bail off */ + if (isa_bridge_devnode != NULL) + return; + + /* For each "isa" node in the system. Note : we do a search by + * type and not by name. It might be better to do by name but that's + * what the code used to do and I don't want to break too much at + * once. We can look into changing that separately + */ + for_each_node_by_type(np, "isa") { + /* Look for our hose being a parent */ + for (parent = of_get_parent(np); parent;) { + if (parent == hose->arch_data) { + of_node_put(parent); + break; + } + tmp = parent; + parent = of_get_parent(parent); + of_node_put(tmp); + } + if (parent != NULL) + break; + } + if (np == NULL) + return; + isa_bridge_devnode = np; + + /* Now parse the "ranges" property and setup the ISA mapping */ + pci_process_ISA_OF_ranges(np, hose->io_base_phys); + + /* Set the global ISA io base to indicate we have an ISA bridge */ + isa_io_base = ISA_IO_BASE; + + pr_debug("ISA bridge (early) is %s\n", np->full_name); +} + +/** + * isa_bridge_find_late - Find and map the ISA IO space upon discovery of + * a new ISA bridge + */ +static void __devinit isa_bridge_find_late(struct pci_dev *pdev, + struct device_node *devnode) +{ + struct pci_controller *hose = pci_bus_to_host(pdev->bus); + + /* Store ISA device node and PCI device */ + isa_bridge_devnode = of_node_get(devnode); + isa_bridge_pcidev = pdev; + + /* Now parse the "ranges" property and setup the ISA mapping */ + pci_process_ISA_OF_ranges(devnode, hose->io_base_phys); + + /* Set the global ISA io base to indicate we have an ISA bridge */ + isa_io_base = ISA_IO_BASE; + + pr_debug("ISA bridge (late) is %s on %s\n", + devnode->full_name, pci_name(pdev)); +} + +/** + * isa_bridge_remove - Remove/unmap an ISA bridge + */ +static void isa_bridge_remove(void) +{ + pr_debug("ISA bridge removed !\n"); + + /* Clear the global ISA io base to indicate that we have no more + * ISA bridge. Note that drivers don't quite handle that, though + * we should probably do something about it. But do we ever really + * have ISA bridges being removed on machines using legacy devices ? + */ + isa_io_base = ISA_IO_BASE; + + /* Clear references to the bridge */ + of_node_put(isa_bridge_devnode); + isa_bridge_devnode = NULL; + isa_bridge_pcidev = NULL; + + /* Unmap the ISA area */ + __iounmap_at((void *)ISA_IO_BASE, 0x10000); +} + +/** + * isa_bridge_notify - Get notified of PCI devices addition/removal + */ +static int __devinit isa_bridge_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct device *dev = data; + struct pci_dev *pdev = to_pci_dev(dev); + struct device_node *devnode = pci_device_to_OF_node(pdev); + + switch(action) { + case BUS_NOTIFY_ADD_DEVICE: + /* Check if we have an early ISA device, without PCI dev */ + if (isa_bridge_devnode && isa_bridge_devnode == devnode && + !isa_bridge_pcidev) { + pr_debug("ISA bridge PCI attached: %s\n", + pci_name(pdev)); + isa_bridge_pcidev = pdev; + } + + /* Check if we have no ISA device, and this happens to be one, + * register it as such if it has an OF device + */ + if (!isa_bridge_devnode && devnode && devnode->type && + !strcmp(devnode->type, "isa")) + isa_bridge_find_late(pdev, devnode); + + return 0; + case BUS_NOTIFY_DEL_DEVICE: + /* Check if this our existing ISA device */ + if (pdev == isa_bridge_pcidev || + (devnode && devnode == isa_bridge_devnode)) + isa_bridge_remove(); + return 0; + } + return 0; +} + +static struct notifier_block isa_bridge_notifier = { + .notifier_call = isa_bridge_notify +}; + +/** + * isa_bridge_init - register to be notified of ISA bridge addition/removal + * + */ +static int __init isa_bridge_init(void) +{ + if (firmware_has_feature(FW_FEATURE_ISERIES)) + return 0; + bus_register_notifier(&pci_bus_type, &isa_bridge_notifier); + return 0; +} +arch_initcall(isa_bridge_init); diff --git a/arch/powerpc/kernel/kprobes.c b/arch/powerpc/kernel/kprobes.c index 0c96611f02f..440f5a87271 100644 --- a/arch/powerpc/kernel/kprobes.c +++ b/arch/powerpc/kernel/kprobes.c @@ -492,6 +492,13 @@ int __kprobes kprobe_exceptions_notify(struct notifier_block *self, return ret; } +#ifdef CONFIG_PPC64 +unsigned long arch_deref_entry_point(void *entry) +{ + return (unsigned long)(((func_descr_t *)entry)->entry); +} +#endif + int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs) { struct jprobe *jp = container_of(p, struct jprobe, kp); @@ -500,11 +507,9 @@ int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs) memcpy(&kcb->jprobe_saved_regs, regs, sizeof(struct pt_regs)); /* setup return addr to the jprobe handler routine */ + regs->nip = arch_deref_entry_point(jp->entry); #ifdef CONFIG_PPC64 - regs->nip = (unsigned long)(((func_descr_t *)jp->entry)->entry); regs->gpr[2] = (unsigned long)(((func_descr_t *)jp->entry)->toc); -#else - regs->nip = (unsigned long)jp->entry; #endif return 1; diff --git a/arch/powerpc/kernel/lparcfg.c b/arch/powerpc/kernel/lparcfg.c index c492cee90e0..6444eaa30a2 100644 --- a/arch/powerpc/kernel/lparcfg.c +++ b/arch/powerpc/kernel/lparcfg.c @@ -248,7 +248,7 @@ static void parse_system_parameter_string(struct seq_file *m) } else { int splpar_strlen; int idx, w_idx; - char *workbuffer = kmalloc(SPLPAR_MAXLENGTH, GFP_KERNEL); + char *workbuffer = kzalloc(SPLPAR_MAXLENGTH, GFP_KERNEL); if (!workbuffer) { printk(KERN_ERR "%s %s kmalloc failure at line %d \n", __FILE__, __FUNCTION__, __LINE__); @@ -261,7 +261,6 @@ static void parse_system_parameter_string(struct seq_file *m) splpar_strlen = local_buffer[0] * 256 + local_buffer[1]; local_buffer += 2; /* step over strlen value */ - memset(workbuffer, 0, SPLPAR_MAXLENGTH); w_idx = 0; idx = 0; while ((*local_buffer) && (idx < splpar_strlen)) { diff --git a/arch/powerpc/kernel/misc_32.S b/arch/powerpc/kernel/misc_32.S index 98decf8ebff..e708ab7ca9e 100644 --- a/arch/powerpc/kernel/misc_32.S +++ b/arch/powerpc/kernel/misc_32.S @@ -392,7 +392,7 @@ BEGIN_FTR_SECTION mtspr SPRN_L1CSR0,r3 isync blr -END_FTR_SECTION_IFCLR(CPU_FTR_SPLIT_ID_CACHE) +END_FTR_SECTION_IFSET(CPU_FTR_UNIFIED_ID_CACHE) mfspr r3,SPRN_L1CSR1 ori r3,r3,L1CSR1_ICFI|L1CSR1_ICLFR mtspr SPRN_L1CSR1,r3 @@ -419,7 +419,7 @@ END_FTR_SECTION_IFCLR(CPU_FTR_SPLIT_ID_CACHE) _GLOBAL(__flush_icache_range) BEGIN_FTR_SECTION blr /* for 601, do nothing */ -END_FTR_SECTION_IFCLR(CPU_FTR_SPLIT_ID_CACHE) +END_FTR_SECTION_IFSET(CPU_FTR_COHERENT_ICACHE) li r5,L1_CACHE_BYTES-1 andc r3,r3,r5 subf r4,r3,r4 @@ -514,8 +514,8 @@ _GLOBAL(invalidate_dcache_range) */ _GLOBAL(__flush_dcache_icache) BEGIN_FTR_SECTION - blr /* for 601, do nothing */ -END_FTR_SECTION_IFCLR(CPU_FTR_SPLIT_ID_CACHE) + blr +END_FTR_SECTION_IFSET(CPU_FTR_COHERENT_ICACHE) rlwinm r3,r3,0,0,19 /* Get page base address */ li r4,4096/L1_CACHE_BYTES /* Number of lines in a page */ mtctr r4 @@ -543,7 +543,7 @@ END_FTR_SECTION_IFCLR(CPU_FTR_SPLIT_ID_CACHE) _GLOBAL(__flush_dcache_icache_phys) BEGIN_FTR_SECTION blr /* for 601, do nothing */ -END_FTR_SECTION_IFCLR(CPU_FTR_SPLIT_ID_CACHE) +END_FTR_SECTION_IFSET(CPU_FTR_COHERENT_ICACHE) mfmsr r10 rlwinm r0,r10,0,28,26 /* clear DR */ mtmsr r0 diff --git a/arch/powerpc/kernel/misc_64.S b/arch/powerpc/kernel/misc_64.S index 519861da042..bbb3ba54c51 100644 --- a/arch/powerpc/kernel/misc_64.S +++ b/arch/powerpc/kernel/misc_64.S @@ -646,6 +646,19 @@ _GLOBAL(kexec_sequence) /* turn off mmu */ bl real_mode + /* copy 0x100 bytes starting at start to 0 */ + li r3,0 + mr r4,r30 /* start, aka phys mem offset */ + li r5,0x100 + li r6,0 + bl .copy_and_flush /* (dest, src, copy limit, start offset) */ +1: /* assume normal blr return */ + + /* release other cpus to the new kernel secondary start at 0x60 */ + mflr r5 + li r6,1 + stw r6,kexec_flag-1b(5) + /* clear out hardware hash page table and tlb */ ld r5,0(r27) /* deref function descriptor */ mtctr r5 @@ -676,19 +689,6 @@ _GLOBAL(kexec_sequence) * are the boot cpu ????? * other device tree differences (prop sizes, va vs pa, etc)... */ - - /* copy 0x100 bytes starting at start to 0 */ - li r3,0 - mr r4,r30 - li r5,0x100 - li r6,0 - bl .copy_and_flush /* (dest, src, copy limit, start offset) */ -1: /* assume normal blr return */ - - /* release other cpus to the new kernel secondary start at 0x60 */ - mflr r5 - li r6,1 - stw r6,kexec_flag-1b(5) mr r3,r25 # my phys cpu mr r4,r30 # start, aka phys mem offset mtlr 4 diff --git a/arch/powerpc/kernel/of_platform.c b/arch/powerpc/kernel/of_platform.c index d454f61c9c7..8ded4e7dc87 100644 --- a/arch/powerpc/kernel/of_platform.c +++ b/arch/powerpc/kernel/of_platform.c @@ -222,10 +222,9 @@ struct of_device* of_platform_device_create(struct device_node *np, { struct of_device *dev; - dev = kmalloc(sizeof(*dev), GFP_KERNEL); + dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) return NULL; - memset(dev, 0, sizeof(*dev)); dev->node = of_node_get(np); dev->dma_mask = 0xffffffffUL; @@ -427,14 +426,6 @@ static int __devinit of_pci_phb_probe(struct of_device *dev, /* Process "ranges" property */ pci_process_bridge_OF_ranges(phb, dev->node, 0); - /* Setup IO space. We use the non-dynamic version of that code here, - * which doesn't quite support unplugging. Next kernel release will - * have a better fix for this. - * Note also that we don't do ISA, this will also be fixed with a - * more massive rework. - */ - pci_setup_phb_io(phb, pci_io_base == 0); - /* Init pci_dn data structures */ pci_devs_phb_init_dynamic(phb); diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c new file mode 100644 index 00000000000..94b4a028232 --- /dev/null +++ b/arch/powerpc/kernel/pci-common.c @@ -0,0 +1,457 @@ +/* + * Contains common pci routines for ALL ppc platform + * (based on pci_32.c and pci_64.c) + * + * Port for PPC64 David Engebretsen, IBM Corp. + * Contains common pci routines for ppc64 platform, pSeries and iSeries brands. + * + * Copyright (C) 2003 Anton Blanchard <anton@au.ibm.com>, IBM + * Rework, based on alpha PCI code. + * + * Common pmac/prep/chrp pci routines. -- Cort + * + * 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. + */ + +#undef DEBUG + +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/string.h> +#include <linux/init.h> +#include <linux/bootmem.h> +#include <linux/mm.h> +#include <linux/list.h> +#include <linux/syscalls.h> +#include <linux/irq.h> +#include <linux/vmalloc.h> + +#include <asm/processor.h> +#include <asm/io.h> +#include <asm/prom.h> +#include <asm/pci-bridge.h> +#include <asm/byteorder.h> +#include <asm/machdep.h> +#include <asm/ppc-pci.h> +#include <asm/firmware.h> + +#ifdef DEBUG +#include <asm/udbg.h> +#define DBG(fmt...) printk(fmt) +#else +#define DBG(fmt...) +#endif + +static DEFINE_SPINLOCK(hose_spinlock); + +/* XXX kill that some day ... */ +int global_phb_number; /* Global phb counter */ + +extern struct list_head hose_list; + +/* + * pci_controller(phb) initialized common variables. + */ +static void __devinit pci_setup_pci_controller(struct pci_controller *hose) +{ + memset(hose, 0, sizeof(struct pci_controller)); + + spin_lock(&hose_spinlock); + hose->global_number = global_phb_number++; + list_add_tail(&hose->list_node, &hose_list); + spin_unlock(&hose_spinlock); +} + +struct pci_controller * pcibios_alloc_controller(struct device_node *dev) +{ + struct pci_controller *phb; + + if (mem_init_done) + phb = kmalloc(sizeof(struct pci_controller), GFP_KERNEL); + else + phb = alloc_bootmem(sizeof (struct pci_controller)); + if (phb == NULL) + return NULL; + pci_setup_pci_controller(phb); + phb->arch_data = dev; + phb->is_dynamic = mem_init_done; +#ifdef CONFIG_PPC64 + if (dev) { + int nid = of_node_to_nid(dev); + + if (nid < 0 || !node_online(nid)) + nid = -1; + + PHB_SET_NODE(phb, nid); + } +#endif + return phb; +} + +void pcibios_free_controller(struct pci_controller *phb) +{ + spin_lock(&hose_spinlock); + list_del(&phb->list_node); + spin_unlock(&hose_spinlock); + + if (phb->is_dynamic) + kfree(phb); +} + +/* + * Return the domain number for this bus. + */ +int pci_domain_nr(struct pci_bus *bus) +{ + if (firmware_has_feature(FW_FEATURE_ISERIES)) + return 0; + else { + struct pci_controller *hose = pci_bus_to_host(bus); + + return hose->global_number; + } +} + +EXPORT_SYMBOL(pci_domain_nr); + +#ifdef CONFIG_PPC_OF + +/* This routine is meant to be used early during boot, when the + * PCI bus numbers have not yet been assigned, and you need to + * issue PCI config cycles to an OF device. + * It could also be used to "fix" RTAS config cycles if you want + * to set pci_assign_all_buses to 1 and still use RTAS for PCI + * config cycles. + */ +struct pci_controller* pci_find_hose_for_OF_device(struct device_node* node) +{ + if (!have_of) + return NULL; + while(node) { + struct pci_controller *hose, *tmp; + list_for_each_entry_safe(hose, tmp, &hose_list, list_node) + if (hose->arch_data == node) + return hose; + node = node->parent; + } + return NULL; +} + +static ssize_t pci_show_devspec(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pci_dev *pdev; + struct device_node *np; + + pdev = to_pci_dev (dev); + np = pci_device_to_OF_node(pdev); + if (np == NULL || np->full_name == NULL) + return 0; + return sprintf(buf, "%s", np->full_name); +} +static DEVICE_ATTR(devspec, S_IRUGO, pci_show_devspec, NULL); +#endif /* CONFIG_PPC_OF */ + +/* Add sysfs properties */ +int pcibios_add_platform_entries(struct pci_dev *pdev) +{ +#ifdef CONFIG_PPC_OF + return device_create_file(&pdev->dev, &dev_attr_devspec); +#else + return 0; +#endif /* CONFIG_PPC_OF */ + +} + +char __init *pcibios_setup(char *str) +{ + return str; +} + +/* + * Reads the interrupt pin to determine if interrupt is use by card. + * If the interrupt is used, then gets the interrupt line from the + * openfirmware and sets it in the pci_dev and pci_config line. + */ +int pci_read_irq_line(struct pci_dev *pci_dev) +{ + struct of_irq oirq; + unsigned int virq; + + DBG("Try to map irq for %s...\n", pci_name(pci_dev)); + +#ifdef DEBUG + memset(&oirq, 0xff, sizeof(oirq)); +#endif + /* Try to get a mapping from the device-tree */ + if (of_irq_map_pci(pci_dev, &oirq)) { + u8 line, pin; + + /* If that fails, lets fallback to what is in the config + * space and map that through the default controller. We + * also set the type to level low since that's what PCI + * interrupts are. If your platform does differently, then + * either provide a proper interrupt tree or don't use this + * function. + */ + if (pci_read_config_byte(pci_dev, PCI_INTERRUPT_PIN, &pin)) + return -1; + if (pin == 0) + return -1; + if (pci_read_config_byte(pci_dev, PCI_INTERRUPT_LINE, &line) || + line == 0xff) { + return -1; + } + DBG(" -> no map ! Using irq line %d from PCI config\n", line); + + virq = irq_create_mapping(NULL, line); + if (virq != NO_IRQ) + set_irq_type(virq, IRQ_TYPE_LEVEL_LOW); + } else { + DBG(" -> got one, spec %d cells (0x%08x 0x%08x...) on %s\n", + oirq.size, oirq.specifier[0], oirq.specifier[1], + oirq.controller->full_name); + + virq = irq_create_of_mapping(oirq.controller, oirq.specifier, + oirq.size); + } + if(virq == NO_IRQ) { + DBG(" -> failed to map !\n"); + return -1; + } + + DBG(" -> mapped to linux irq %d\n", virq); + + pci_dev->irq = virq; + + return 0; +} +EXPORT_SYMBOL(pci_read_irq_line); + +/* + * Platform support for /proc/bus/pci/X/Y mmap()s, + * modelled on the sparc64 implementation by Dave Miller. + * -- paulus. + */ + +/* + * Adjust vm_pgoff of VMA such that it is the physical page offset + * corresponding to the 32-bit pci bus offset for DEV requested by the user. + * + * Basically, the user finds the base address for his device which he wishes + * to mmap. They read the 32-bit value from the config space base register, + * add whatever PAGE_SIZE multiple offset they wish, and feed this into the + * offset parameter of mmap on /proc/bus/pci/XXX for that device. + * + * Returns negative error code on failure, zero on success. + */ +static struct resource *__pci_mmap_make_offset(struct pci_dev *dev, + resource_size_t *offset, + enum pci_mmap_state mmap_state) +{ + struct pci_controller *hose = pci_bus_to_host(dev->bus); + unsigned long io_offset = 0; + int i, res_bit; + + if (hose == 0) + return NULL; /* should never happen */ + + /* If memory, add on the PCI bridge address offset */ + if (mmap_state == pci_mmap_mem) { +#if 0 /* See comment in pci_resource_to_user() for why this is disabled */ + *offset += hose->pci_mem_offset; +#endif + res_bit = IORESOURCE_MEM; + } else { + io_offset = (unsigned long)hose->io_base_virt - _IO_BASE; + *offset += io_offset; + res_bit = IORESOURCE_IO; + } + + /* + * Check that the offset requested corresponds to one of the + * resources of the device. + */ + for (i = 0; i <= PCI_ROM_RESOURCE; i++) { + struct resource *rp = &dev->resource[i]; + int flags = rp->flags; + + /* treat ROM as memory (should be already) */ + if (i == PCI_ROM_RESOURCE) + flags |= IORESOURCE_MEM; + + /* Active and same type? */ + if ((flags & res_bit) == 0) + continue; + + /* In the range of this resource? */ + if (*offset < (rp->start & PAGE_MASK) || *offset > rp->end) + continue; + + /* found it! construct the final physical address */ + if (mmap_state == pci_mmap_io) + *offset += hose->io_base_phys - io_offset; + return rp; + } + + return NULL; +} + +/* + * Set vm_page_prot of VMA, as appropriate for this architecture, for a pci + * device mapping. + */ +static pgprot_t __pci_mmap_set_pgprot(struct pci_dev *dev, struct resource *rp, + pgprot_t protection, + enum pci_mmap_state mmap_state, + int write_combine) +{ + unsigned long prot = pgprot_val(protection); + + /* Write combine is always 0 on non-memory space mappings. On + * memory space, if the user didn't pass 1, we check for a + * "prefetchable" resource. This is a bit hackish, but we use + * this to workaround the inability of /sysfs to provide a write + * combine bit + */ + if (mmap_state != pci_mmap_mem) + write_combine = 0; + else if (write_combine == 0) { + if (rp->flags & IORESOURCE_PREFETCH) + write_combine = 1; + } + + /* XXX would be nice to have a way to ask for write-through */ + prot |= _PAGE_NO_CACHE; + if (write_combine) + prot &= ~_PAGE_GUARDED; + else + prot |= _PAGE_GUARDED; + + return __pgprot(prot); +} + +/* + * This one is used by /dev/mem and fbdev who have no clue about the + * PCI device, it tries to find the PCI device first and calls the + * above routine + */ +pgprot_t pci_phys_mem_access_prot(struct file *file, + unsigned long pfn, + unsigned long size, + pgprot_t protection) +{ + struct pci_dev *pdev = NULL; + struct resource *found = NULL; + unsigned long prot = pgprot_val(protection); + unsigned long offset = pfn << PAGE_SHIFT; + int i; + + if (page_is_ram(pfn)) + return __pgprot(prot); + + prot |= _PAGE_NO_CACHE | _PAGE_GUARDED; + + for_each_pci_dev(pdev) { + for (i = 0; i <= PCI_ROM_RESOURCE; i++) { + struct resource *rp = &pdev->resource[i]; + int flags = rp->flags; + + /* Active and same type? */ + if ((flags & IORESOURCE_MEM) == 0) + continue; + /* In the range of this resource? */ + if (offset < (rp->start & PAGE_MASK) || + offset > rp->end) + continue; + found = rp; + break; + } + if (found) + break; + } + if (found) { + if (found->flags & IORESOURCE_PREFETCH) + prot &= ~_PAGE_GUARDED; + pci_dev_put(pdev); + } + + DBG("non-PCI map for %lx, prot: %lx\n", offset, prot); + + return __pgprot(prot); +} + + +/* + * Perform the actual remap of the pages for a PCI device mapping, as + * appropriate for this architecture. The region in the process to map + * is described by vm_start and vm_end members of VMA, the base physical + * address is found in vm_pgoff. + * The pci device structure is provided so that architectures may make mapping + * decisions on a per-device or per-bus basis. + * + * Returns a negative error code on failure, zero on success. + */ +int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, + enum pci_mmap_state mmap_state, int write_combine) +{ + resource_size_t offset = vma->vm_pgoff << PAGE_SHIFT; + struct resource *rp; + int ret; + + rp = __pci_mmap_make_offset(dev, &offset, mmap_state); + if (rp == NULL) + return -EINVAL; + + vma->vm_pgoff = offset >> PAGE_SHIFT; + vma->vm_page_prot = __pci_mmap_set_pgprot(dev, rp, + vma->vm_page_prot, + mmap_state, write_combine); + + ret = remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, + vma->vm_end - vma->vm_start, vma->vm_page_prot); + + return ret; +} + +void pci_resource_to_user(const struct pci_dev *dev, int bar, + const struct resource *rsrc, + resource_size_t *start, resource_size_t *end) +{ + struct pci_controller *hose = pci_bus_to_host(dev->bus); + resource_size_t offset = 0; + + if (hose == NULL) + return; + + if (rsrc->flags & IORESOURCE_IO) + offset = (unsigned long)hose->io_base_virt - _IO_BASE; + + /* We pass a fully fixed up address to userland for MMIO instead of + * a BAR value because X is lame and expects to be able to use that + * to pass to /dev/mem ! + * + * That means that we'll have potentially 64 bits values where some + * userland apps only expect 32 (like X itself since it thinks only + * Sparc has 64 bits MMIO) but if we don't do that, we break it on + * 32 bits CHRPs :-( + * + * Hopefully, the sysfs insterface is immune to that gunk. Once X + * has been fixed (and the fix spread enough), we can re-enable the + * 2 lines below and pass down a BAR value to userland. In that case + * we'll also have to re-enable the matching code in + * __pci_mmap_make_offset(). + * + * BenH. + */ +#if 0 + else if (rsrc->flags & IORESOURCE_MEM) + offset = hose->pci_mem_offset; +#endif + + *start = rsrc->start - offset; + *end = rsrc->end - offset; +} diff --git a/arch/powerpc/kernel/pci_32.c b/arch/powerpc/kernel/pci_32.c index 86982112b0d..0adf077f3f3 100644 --- a/arch/powerpc/kernel/pci_32.c +++ b/arch/powerpc/kernel/pci_32.c @@ -55,8 +55,7 @@ static u8* pci_to_OF_bus_map; */ int pci_assign_all_buses; -struct pci_controller* hose_head; -struct pci_controller** hose_tail = &hose_head; +LIST_HEAD(hose_list); static int pci_bus_count; @@ -573,58 +572,6 @@ pcibios_assign_resources(void) } } - -int -pcibios_enable_resources(struct pci_dev *dev, int mask) -{ - u16 cmd, old_cmd; - int idx; - struct resource *r; - - pci_read_config_word(dev, PCI_COMMAND, &cmd); - old_cmd = cmd; - for (idx=0; idx<6; idx++) { - /* Only set up the requested stuff */ - if (!(mask & (1<<idx))) - continue; - - r = &dev->resource[idx]; - if (r->flags & IORESOURCE_UNSET) { - printk(KERN_ERR "PCI: Device %s not available because of resource collisions\n", pci_name(dev)); - return -EINVAL; - } - if (r->flags & IORESOURCE_IO) - cmd |= PCI_COMMAND_IO; - if (r->flags & IORESOURCE_MEM) - cmd |= PCI_COMMAND_MEMORY; - } - if (dev->resource[PCI_ROM_RESOURCE].start) - cmd |= PCI_COMMAND_MEMORY; - if (cmd != old_cmd) { - printk("PCI: Enabling device %s (%04x -> %04x)\n", pci_name(dev), old_cmd, cmd); - pci_write_config_word(dev, PCI_COMMAND, cmd); - } - return 0; -} - -static int next_controller_index; - -struct pci_controller * __init -pcibios_alloc_controller(void) -{ - struct pci_controller *hose; - - hose = (struct pci_controller *)alloc_bootmem(sizeof(*hose)); - memset(hose, 0, sizeof(struct pci_controller)); - - *hose_tail = hose; - hose_tail = &hose->next; - - hose->index = next_controller_index++; - - return hose; -} - #ifdef CONFIG_PPC_OF /* * Functions below are used on OpenFirmware machines. @@ -670,7 +617,7 @@ void pcibios_make_OF_bus_map(void) { int i; - struct pci_controller* hose; + struct pci_controller *hose, *tmp; struct property *map_prop; struct device_node *dn; @@ -687,7 +634,7 @@ pcibios_make_OF_bus_map(void) pci_to_OF_bus_map[i] = 0xff; /* For each hose, we begin searching bridges */ - for(hose=hose_head; hose; hose=hose->next) { + list_for_each_entry_safe(hose, tmp, &hose_list, list_node) { struct device_node* node; node = (struct device_node *)hose->arch_data; if (!node) @@ -765,7 +712,7 @@ static struct device_node *scan_OF_for_pci_bus(struct pci_bus *bus) /* Are we a root bus ? */ if (bus->self == NULL || bus->parent == NULL) { - struct pci_controller *hose = pci_bus_to_hose(bus->number); + struct pci_controller *hose = pci_bus_to_host(bus); if (hose == NULL) return NULL; return of_node_get(hose->arch_data); @@ -818,27 +765,6 @@ pci_device_to_OF_node(struct pci_dev *dev) } EXPORT_SYMBOL(pci_device_to_OF_node); -/* This routine is meant to be used early during boot, when the - * PCI bus numbers have not yet been assigned, and you need to - * issue PCI config cycles to an OF device. - * It could also be used to "fix" RTAS config cycles if you want - * to set pci_assign_all_buses to 1 and still use RTAS for PCI - * config cycles. - */ -struct pci_controller* pci_find_hose_for_OF_device(struct device_node* node) -{ - if (!have_of) - return NULL; - while(node) { - struct pci_controller* hose; - for (hose=hose_head;hose;hose=hose->next) - if (hose->arch_data == node) - return hose; - node=node->parent; - } - return NULL; -} - static int find_OF_pci_device_filter(struct device_node* node, void* data) { @@ -1027,34 +953,12 @@ pci_create_OF_bus_map(void) } } -static ssize_t pci_show_devspec(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct pci_dev *pdev; - struct device_node *np; - - pdev = to_pci_dev (dev); - np = pci_device_to_OF_node(pdev); - if (np == NULL || np->full_name == NULL) - return 0; - return sprintf(buf, "%s", np->full_name); -} -static DEVICE_ATTR(devspec, S_IRUGO, pci_show_devspec, NULL); - #else /* CONFIG_PPC_OF */ void pcibios_make_OF_bus_map(void) { } #endif /* CONFIG_PPC_OF */ -/* Add sysfs properties */ -int pcibios_add_platform_entries(struct pci_dev *pdev) -{ -#ifdef CONFIG_PPC_OF - return device_create_file(&pdev->dev, &dev_attr_devspec); -#endif /* CONFIG_PPC_OF */ -} - - #ifdef CONFIG_PPC_PMAC /* * This set of routines checks for PCI<->PCI bridges that have closed @@ -1269,14 +1173,14 @@ pcibios_fixup_p2p_bridges(void) static int __init pcibios_init(void) { - struct pci_controller *hose; + struct pci_controller *hose, *tmp; struct pci_bus *bus; - int next_busno; + int next_busno = 0; printk(KERN_INFO "PCI: Probing PCI hardware\n"); /* Scan all of the recorded PCI controllers. */ - for (next_busno = 0, hose = hose_head; hose; hose = hose->next) { + list_for_each_entry_safe(hose, tmp, &hose_list, list_node) { if (pci_assign_all_buses) hose->first_busno = next_busno; hose->last_busno = 0xff; @@ -1319,12 +1223,6 @@ pcibios_init(void) subsys_initcall(pcibios_init); -unsigned long resource_fixup(struct pci_dev * dev, struct resource * res, - unsigned long start, unsigned long size) -{ - return start; -} - void __init pcibios_fixup_bus(struct pci_bus *bus) { struct pci_controller *hose = (struct pci_controller *) bus->sysdata; @@ -1342,7 +1240,7 @@ void __init pcibios_fixup_bus(struct pci_bus *bus) if (!res->flags) { if (io_offset) printk(KERN_ERR "I/O resource not set for host" - " bridge %d\n", hose->index); + " bridge %d\n", hose->global_number); res->start = 0; res->end = IO_SPACE_LIMIT; res->flags = IORESOURCE_IO; @@ -1356,7 +1254,7 @@ void __init pcibios_fixup_bus(struct pci_bus *bus) if (i > 0) continue; printk(KERN_ERR "Memory resource not set for " - "host bridge %d\n", hose->index); + "host bridge %d\n", hose->global_number); res->start = hose->pci_mem_offset; res->end = ~0U; res->flags = IORESOURCE_MEM; @@ -1370,7 +1268,7 @@ void __init pcibios_fixup_bus(struct pci_bus *bus) for (i = 0; i < 4; ++i) { if ((res = bus->resource[i]) == NULL) continue; - if (!res->flags) + if (!res->flags || bus->self->transparent) continue; if (io_offset && (res->flags & IORESOURCE_IO)) { res->start += io_offset; @@ -1395,11 +1293,6 @@ void __init pcibios_fixup_bus(struct pci_bus *bus) } } -char __init *pcibios_setup(char *str) -{ - return str; -} - /* the next one is stolen from the alpha port... */ void __init pcibios_update_irq(struct pci_dev *dev, int irq) @@ -1408,64 +1301,6 @@ pcibios_update_irq(struct pci_dev *dev, int irq) /* XXX FIXME - update OF device tree node interrupt property */ } -#ifdef CONFIG_PPC_MERGE -/* XXX This is a copy of the ppc64 version. This is temporary until we start - * merging the 2 PCI layers - */ -/* - * Reads the interrupt pin to determine if interrupt is use by card. - * If the interrupt is used, then gets the interrupt line from the - * openfirmware and sets it in the pci_dev and pci_config line. - */ -int pci_read_irq_line(struct pci_dev *pci_dev) -{ - struct of_irq oirq; - unsigned int virq; - - DBG("Try to map irq for %s...\n", pci_name(pci_dev)); - - /* Try to get a mapping from the device-tree */ - if (of_irq_map_pci(pci_dev, &oirq)) { - u8 line, pin; - - /* If that fails, lets fallback to what is in the config - * space and map that through the default controller. We - * also set the type to level low since that's what PCI - * interrupts are. If your platform does differently, then - * either provide a proper interrupt tree or don't use this - * function. - */ - if (pci_read_config_byte(pci_dev, PCI_INTERRUPT_PIN, &pin)) - return -1; - if (pin == 0) - return -1; - if (pci_read_config_byte(pci_dev, PCI_INTERRUPT_LINE, &line) || - line == 0xff) { - return -1; - } - DBG(" -> no map ! Using irq line %d from PCI config\n", line); - - virq = irq_create_mapping(NULL, line); - if (virq != NO_IRQ) - set_irq_type(virq, IRQ_TYPE_LEVEL_LOW); - } else { - DBG(" -> got one, spec %d cells (0x%08x...) on %s\n", - oirq.size, oirq.specifier[0], oirq.controller->full_name); - - virq = irq_create_of_mapping(oirq.controller, oirq.specifier, - oirq.size); - } - if(virq == NO_IRQ) { - DBG(" -> failed to map !\n"); - return -1; - } - pci_dev->irq = virq; - - return 0; -} -EXPORT_SYMBOL(pci_read_irq_line); -#endif /* CONFIG_PPC_MERGE */ - int pcibios_enable_device(struct pci_dev *dev, int mask) { u16 cmd, old_cmd; @@ -1497,281 +1332,17 @@ int pcibios_enable_device(struct pci_dev *dev, int mask) return 0; } -struct pci_controller* +static struct pci_controller* pci_bus_to_hose(int bus) { - struct pci_controller* hose = hose_head; + struct pci_controller *hose, *tmp; - for (; hose; hose = hose->next) + list_for_each_entry_safe(hose, tmp, &hose_list, list_node) if (bus >= hose->first_busno && bus <= hose->last_busno) return hose; return NULL; } -void __iomem * -pci_bus_io_base(unsigned int bus) -{ - struct pci_controller *hose; - - hose = pci_bus_to_hose(bus); - if (!hose) - return NULL; - return hose->io_base_virt; -} - -unsigned long -pci_bus_io_base_phys(unsigned int bus) -{ - struct pci_controller *hose; - - hose = pci_bus_to_hose(bus); - if (!hose) - return 0; - return hose->io_base_phys; -} - -unsigned long -pci_bus_mem_base_phys(unsigned int bus) -{ - struct pci_controller *hose; - - hose = pci_bus_to_hose(bus); - if (!hose) - return 0; - return hose->pci_mem_offset; -} - -unsigned long -pci_resource_to_bus(struct pci_dev *pdev, struct resource *res) -{ - /* Hack alert again ! See comments in chrp_pci.c - */ - struct pci_controller* hose = - (struct pci_controller *)pdev->sysdata; - if (hose && res->flags & IORESOURCE_MEM) - return res->start - hose->pci_mem_offset; - /* We may want to do something with IOs here... */ - return res->start; -} - - -static struct resource *__pci_mmap_make_offset(struct pci_dev *dev, - resource_size_t *offset, - enum pci_mmap_state mmap_state) -{ - struct pci_controller *hose = pci_bus_to_hose(dev->bus->number); - unsigned long io_offset = 0; - int i, res_bit; - - if (hose == 0) - return NULL; /* should never happen */ - - /* If memory, add on the PCI bridge address offset */ - if (mmap_state == pci_mmap_mem) { -#if 0 /* See comment in pci_resource_to_user() for why this is disabled */ - *offset += hose->pci_mem_offset; -#endif - res_bit = IORESOURCE_MEM; - } else { - io_offset = hose->io_base_virt - (void __iomem *)_IO_BASE; - *offset += io_offset; - res_bit = IORESOURCE_IO; - } - - /* - * Check that the offset requested corresponds to one of the - * resources of the device. - */ - for (i = 0; i <= PCI_ROM_RESOURCE; i++) { - struct resource *rp = &dev->resource[i]; - int flags = rp->flags; - - /* treat ROM as memory (should be already) */ - if (i == PCI_ROM_RESOURCE) - flags |= IORESOURCE_MEM; - - /* Active and same type? */ - if ((flags & res_bit) == 0) - continue; - - /* In the range of this resource? */ - if (*offset < (rp->start & PAGE_MASK) || *offset > rp->end) - continue; - - /* found it! construct the final physical address */ - if (mmap_state == pci_mmap_io) - *offset += hose->io_base_phys - io_offset; - return rp; - } - - return NULL; -} - -/* - * Set vm_page_prot of VMA, as appropriate for this architecture, for a pci - * device mapping. - */ -static pgprot_t __pci_mmap_set_pgprot(struct pci_dev *dev, struct resource *rp, - pgprot_t protection, - enum pci_mmap_state mmap_state, - int write_combine) -{ - unsigned long prot = pgprot_val(protection); - - /* Write combine is always 0 on non-memory space mappings. On - * memory space, if the user didn't pass 1, we check for a - * "prefetchable" resource. This is a bit hackish, but we use - * this to workaround the inability of /sysfs to provide a write - * combine bit - */ - if (mmap_state != pci_mmap_mem) - write_combine = 0; - else if (write_combine == 0) { - if (rp->flags & IORESOURCE_PREFETCH) - write_combine = 1; - } - - /* XXX would be nice to have a way to ask for write-through */ - prot |= _PAGE_NO_CACHE; - if (write_combine) - prot &= ~_PAGE_GUARDED; - else - prot |= _PAGE_GUARDED; - - return __pgprot(prot); -} - -/* - * This one is used by /dev/mem and fbdev who have no clue about the - * PCI device, it tries to find the PCI device first and calls the - * above routine - */ -pgprot_t pci_phys_mem_access_prot(struct file *file, - unsigned long pfn, - unsigned long size, - pgprot_t protection) -{ - struct pci_dev *pdev = NULL; - struct resource *found = NULL; - unsigned long prot = pgprot_val(protection); - unsigned long offset = pfn << PAGE_SHIFT; - int i; - - if (page_is_ram(pfn)) - return __pgprot(prot); - - prot |= _PAGE_NO_CACHE | _PAGE_GUARDED; - - for_each_pci_dev(pdev) { - for (i = 0; i <= PCI_ROM_RESOURCE; i++) { - struct resource *rp = &pdev->resource[i]; - int flags = rp->flags; - - /* Active and same type? */ - if ((flags & IORESOURCE_MEM) == 0) - continue; - /* In the range of this resource? */ - if (offset < (rp->start & PAGE_MASK) || - offset > rp->end) - continue; - found = rp; - break; - } - if (found) - break; - } - if (found) { - if (found->flags & IORESOURCE_PREFETCH) - prot &= ~_PAGE_GUARDED; - pci_dev_put(pdev); - } - - DBG("non-PCI map for %lx, prot: %lx\n", offset, prot); - - return __pgprot(prot); -} - - -/* - * Perform the actual remap of the pages for a PCI device mapping, as - * appropriate for this architecture. The region in the process to map - * is described by vm_start and vm_end members of VMA, the base physical - * address is found in vm_pgoff. - * The pci device structure is provided so that architectures may make mapping - * decisions on a per-device or per-bus basis. - * - * Returns a negative error code on failure, zero on success. - */ -int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, - enum pci_mmap_state mmap_state, - int write_combine) -{ - resource_size_t offset = vma->vm_pgoff << PAGE_SHIFT; - struct resource *rp; - int ret; - - rp = __pci_mmap_make_offset(dev, &offset, mmap_state); - if (rp == NULL) - return -EINVAL; - - vma->vm_pgoff = offset >> PAGE_SHIFT; - vma->vm_page_prot = __pci_mmap_set_pgprot(dev, rp, - vma->vm_page_prot, - mmap_state, write_combine); - - ret = remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, - vma->vm_end - vma->vm_start, vma->vm_page_prot); - - return ret; -} - -/* Obsolete functions. Should be removed once the symbios driver - * is fixed - */ -unsigned long -phys_to_bus(unsigned long pa) -{ - struct pci_controller *hose; - int i; - - for (hose = hose_head; hose; hose = hose->next) { - for (i = 0; i < 3; ++i) { - if (pa >= hose->mem_resources[i].start - && pa <= hose->mem_resources[i].end) { - /* - * XXX the hose->pci_mem_offset really - * only applies to mem_resources[0]. - * We need a way to store an offset for - * the others. -- paulus - */ - if (i == 0) - pa -= hose->pci_mem_offset; - return pa; - } - } - } - /* hmmm, didn't find it */ - return 0; -} - -unsigned long -pci_phys_to_bus(unsigned long pa, int busnr) -{ - struct pci_controller* hose = pci_bus_to_hose(busnr); - if (!hose) - return pa; - return pa - hose->pci_mem_offset; -} - -unsigned long -pci_bus_to_phys(unsigned int ba, int busnr) -{ - struct pci_controller* hose = pci_bus_to_hose(busnr); - if (!hose) - return ba; - return ba + hose->pci_mem_offset; -} - /* Provide information on locations of various I/O regions in physical * memory. Do this on a per-card basis so that we choose the right * root bridge. @@ -1814,62 +1385,11 @@ long sys_pciconfig_iobase(long which, unsigned long bus, unsigned long devfn) return result; } -void pci_resource_to_user(const struct pci_dev *dev, int bar, - const struct resource *rsrc, - resource_size_t *start, resource_size_t *end) -{ - struct pci_controller *hose = pci_bus_to_hose(dev->bus->number); - resource_size_t offset = 0; - - if (hose == NULL) - return; - - if (rsrc->flags & IORESOURCE_IO) - offset = (unsigned long)hose->io_base_virt - _IO_BASE; - - /* We pass a fully fixed up address to userland for MMIO instead of - * a BAR value because X is lame and expects to be able to use that - * to pass to /dev/mem ! - * - * That means that we'll have potentially 64 bits values where some - * userland apps only expect 32 (like X itself since it thinks only - * Sparc has 64 bits MMIO) but if we don't do that, we break it on - * 32 bits CHRPs :-( - * - * Hopefully, the sysfs insterface is immune to that gunk. Once X - * has been fixed (and the fix spread enough), we can re-enable the - * 2 lines below and pass down a BAR value to userland. In that case - * we'll also have to re-enable the matching code in - * __pci_mmap_make_offset(). - * - * BenH. - */ -#if 0 - else if (rsrc->flags & IORESOURCE_MEM) - offset = hose->pci_mem_offset; -#endif - - *start = rsrc->start - offset; - *end = rsrc->end - offset; -} - -void __init pci_init_resource(struct resource *res, resource_size_t start, - resource_size_t end, int flags, char *name) -{ - res->start = start; - res->end = end; - res->flags = flags; - res->name = name; - res->parent = NULL; - res->sibling = NULL; - res->child = NULL; -} - unsigned long pci_address_to_pio(phys_addr_t address) { - struct pci_controller* hose = hose_head; + struct pci_controller *hose, *tmp; - for (; hose; hose = hose->next) { + list_for_each_entry_safe(hose, tmp, &hose_list, list_node) { unsigned int size = hose->io_resource.end - hose->io_resource.start + 1; if (address >= hose->io_base_phys && diff --git a/arch/powerpc/kernel/pci_64.c b/arch/powerpc/kernel/pci_64.c index e3009a43ac5..a97e23ac197 100644 --- a/arch/powerpc/kernel/pci_64.c +++ b/arch/powerpc/kernel/pci_64.c @@ -22,6 +22,7 @@ #include <linux/list.h> #include <linux/syscalls.h> #include <linux/irq.h> +#include <linux/vmalloc.h> #include <asm/processor.h> #include <asm/io.h> @@ -41,35 +42,23 @@ unsigned long pci_probe_only = 1; int pci_assign_all_buses = 0; -static int pci_initial_scan_done; static void fixup_resource(struct resource *res, struct pci_dev *dev); static void do_bus_setup(struct pci_bus *bus); -static void phbs_remap_io(void); /* pci_io_base -- the base address from which io bars are offsets. * This is the lowest I/O base address (so bar values are always positive), * and it *must* be the start of ISA space if an ISA bus exists because - * ISA drivers use hard coded offsets. If no ISA bus exists a dummy - * page is mapped and isa_io_limit prevents access to it. + * ISA drivers use hard coded offsets. If no ISA bus exists nothing + * is mapped on the first 64K of IO space */ -unsigned long isa_io_base; /* NULL if no ISA bus */ -EXPORT_SYMBOL(isa_io_base); -unsigned long pci_io_base; +unsigned long pci_io_base = ISA_IO_BASE; EXPORT_SYMBOL(pci_io_base); -void iSeries_pcibios_init(void); - LIST_HEAD(hose_list); static struct dma_mapping_ops *pci_dma_ops; -int global_phb_number; /* Global phb counter */ - -/* Cached ISA bridge dev. */ -struct pci_dev *ppc64_isabridge_dev = NULL; -EXPORT_SYMBOL_GPL(ppc64_isabridge_dev); - void set_pci_dma_ops(struct dma_mapping_ops *dma_ops) { pci_dma_ops = dma_ops; @@ -100,7 +89,7 @@ void pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region return; if (res->flags & IORESOURCE_IO) - offset = (unsigned long)hose->io_base_virt - pci_io_base; + offset = (unsigned long)hose->io_base_virt - _IO_BASE; if (res->flags & IORESOURCE_MEM) offset = hose->pci_mem_offset; @@ -119,7 +108,7 @@ void pcibios_bus_to_resource(struct pci_dev *dev, struct resource *res, return; if (res->flags & IORESOURCE_IO) - offset = (unsigned long)hose->io_base_virt - pci_io_base; + offset = (unsigned long)hose->io_base_virt - _IO_BASE; if (res->flags & IORESOURCE_MEM) offset = hose->pci_mem_offset; @@ -156,7 +145,7 @@ void pcibios_align_resource(void *data, struct resource *res, if (res->flags & IORESOURCE_IO) { unsigned long offset = (unsigned long)hose->io_base_virt - - pci_io_base; + _IO_BASE; /* Make sure we start at our min on all hoses */ if (start - offset < PCIBIOS_MIN_IO) start = PCIBIOS_MIN_IO + offset; @@ -180,55 +169,6 @@ void pcibios_align_resource(void *data, struct resource *res, res->start = start; } -static DEFINE_SPINLOCK(hose_spinlock); - -/* - * pci_controller(phb) initialized common variables. - */ -static void __devinit pci_setup_pci_controller(struct pci_controller *hose) -{ - memset(hose, 0, sizeof(struct pci_controller)); - - spin_lock(&hose_spinlock); - hose->global_number = global_phb_number++; - list_add_tail(&hose->list_node, &hose_list); - spin_unlock(&hose_spinlock); -} - -struct pci_controller * pcibios_alloc_controller(struct device_node *dev) -{ - struct pci_controller *phb; - - if (mem_init_done) - phb = kmalloc(sizeof(struct pci_controller), GFP_KERNEL); - else - phb = alloc_bootmem(sizeof (struct pci_controller)); - if (phb == NULL) - return NULL; - pci_setup_pci_controller(phb); - phb->arch_data = dev; - phb->is_dynamic = mem_init_done; - if (dev) { - int nid = of_node_to_nid(dev); - - if (nid < 0 || !node_online(nid)) - nid = -1; - - PHB_SET_NODE(phb, nid); - } - return phb; -} - -void pcibios_free_controller(struct pci_controller *phb) -{ - spin_lock(&hose_spinlock); - list_del(&phb->list_node); - spin_unlock(&hose_spinlock); - - if (phb->is_dynamic) - kfree(phb); -} - void __devinit pcibios_claim_one_bus(struct pci_bus *b) { struct pci_dev *dev; @@ -291,7 +231,6 @@ static unsigned int pci_parse_of_flags(u32 addr0) return flags; } -#define GET_64BIT(prop, i) ((((u64) (prop)[(i)]) << 32) | (prop)[(i)+1]) static void pci_parse_of_addrs(struct device_node *node, struct pci_dev *dev) { @@ -310,8 +249,8 @@ static void pci_parse_of_addrs(struct device_node *node, struct pci_dev *dev) flags = pci_parse_of_flags(addrs[0]); if (!flags) continue; - base = GET_64BIT(addrs, 1); - size = GET_64BIT(addrs, 3); + base = of_read_number(&addrs[1], 2); + size = of_read_number(&addrs[3], 2); if (!size) continue; i = addrs[0] & 0xff; @@ -479,7 +418,7 @@ void __devinit of_scan_pci_bridge(struct device_node *node, i = 1; for (; len >= 32; len -= 32, ranges += 8) { flags = pci_parse_of_flags(ranges[0]); - size = GET_64BIT(ranges, 6); + size = of_read_number(&ranges[6], 2); if (flags == 0 || size == 0) continue; if (flags & IORESOURCE_IO) { @@ -498,7 +437,7 @@ void __devinit of_scan_pci_bridge(struct device_node *node, res = bus->resource[i]; ++i; } - res->start = GET_64BIT(ranges, 1); + res->start = of_read_number(&ranges[1], 2); res->end = res->start + size - 1; res->flags = flags; fixup_resource(res, dev); @@ -537,10 +476,16 @@ void __devinit scan_phb(struct pci_controller *hose) bus->secondary = hose->first_busno; hose->bus = bus; + if (!firmware_has_feature(FW_FEATURE_ISERIES)) + pcibios_map_io_space(bus); + bus->resource[0] = res = &hose->io_resource; - if (res->flags && request_resource(&ioport_resource, res)) + if (res->flags && request_resource(&ioport_resource, res)) { printk(KERN_ERR "Failed to request PCI IO region " "on PCI domain %04x\n", hose->global_number); + DBG("res->start = 0x%016lx, res->end = 0x%016lx\n", + res->start, res->end); + } for (i = 0; i < 3; ++i) { res = &hose->mem_resources[i]; @@ -598,17 +543,6 @@ static int __init pcibios_init(void) if (ppc_md.pcibios_fixup) ppc_md.pcibios_fixup(); - /* Cache the location of the ISA bridge (if we have one) */ - ppc64_isabridge_dev = pci_get_class(PCI_CLASS_BRIDGE_ISA << 8, NULL); - if (ppc64_isabridge_dev != NULL) - printk(KERN_DEBUG "ISA bridge at %s\n", pci_name(ppc64_isabridge_dev)); - - if (!firmware_has_feature(FW_FEATURE_ISERIES)) - /* map in PCI I/O space */ - phbs_remap_io(); - - pci_initial_scan_done = 1; - printk(KERN_DEBUG "PCI: Probing PCI hardware done\n"); return 0; @@ -616,11 +550,6 @@ static int __init pcibios_init(void) subsys_initcall(pcibios_init); -char __init *pcibios_setup(char *str) -{ - return str; -} - int pcibios_enable_device(struct pci_dev *dev, int mask) { u16 cmd, oldcmd; @@ -651,22 +580,6 @@ int pcibios_enable_device(struct pci_dev *dev, int mask) return 0; } -/* - * Return the domain number for this bus. - */ -int pci_domain_nr(struct pci_bus *bus) -{ - if (firmware_has_feature(FW_FEATURE_ISERIES)) - return 0; - else { - struct pci_controller *hose = pci_bus_to_host(bus); - - return hose->global_number; - } -} - -EXPORT_SYMBOL(pci_domain_nr); - /* Decide whether to display the domain number in /proc */ int pci_proc_domain(struct pci_bus *bus) { @@ -678,281 +591,6 @@ int pci_proc_domain(struct pci_bus *bus) } } -/* - * Platform support for /proc/bus/pci/X/Y mmap()s, - * modelled on the sparc64 implementation by Dave Miller. - * -- paulus. - */ - -/* - * Adjust vm_pgoff of VMA such that it is the physical page offset - * corresponding to the 32-bit pci bus offset for DEV requested by the user. - * - * Basically, the user finds the base address for his device which he wishes - * to mmap. They read the 32-bit value from the config space base register, - * add whatever PAGE_SIZE multiple offset they wish, and feed this into the - * offset parameter of mmap on /proc/bus/pci/XXX for that device. - * - * Returns negative error code on failure, zero on success. - */ -static struct resource *__pci_mmap_make_offset(struct pci_dev *dev, - resource_size_t *offset, - enum pci_mmap_state mmap_state) -{ - struct pci_controller *hose = pci_bus_to_host(dev->bus); - unsigned long io_offset = 0; - int i, res_bit; - - if (hose == 0) - return NULL; /* should never happen */ - - /* If memory, add on the PCI bridge address offset */ - if (mmap_state == pci_mmap_mem) { -#if 0 /* See comment in pci_resource_to_user() for why this is disabled */ - *offset += hose->pci_mem_offset; -#endif - res_bit = IORESOURCE_MEM; - } else { - io_offset = (unsigned long)hose->io_base_virt - pci_io_base; - *offset += io_offset; - res_bit = IORESOURCE_IO; - } - - /* - * Check that the offset requested corresponds to one of the - * resources of the device. - */ - for (i = 0; i <= PCI_ROM_RESOURCE; i++) { - struct resource *rp = &dev->resource[i]; - int flags = rp->flags; - - /* treat ROM as memory (should be already) */ - if (i == PCI_ROM_RESOURCE) - flags |= IORESOURCE_MEM; - - /* Active and same type? */ - if ((flags & res_bit) == 0) - continue; - - /* In the range of this resource? */ - if (*offset < (rp->start & PAGE_MASK) || *offset > rp->end) - continue; - - /* found it! construct the final physical address */ - if (mmap_state == pci_mmap_io) - *offset += hose->io_base_phys - io_offset; - return rp; - } - - return NULL; -} - -/* - * Set vm_page_prot of VMA, as appropriate for this architecture, for a pci - * device mapping. - */ -static pgprot_t __pci_mmap_set_pgprot(struct pci_dev *dev, struct resource *rp, - pgprot_t protection, - enum pci_mmap_state mmap_state, - int write_combine) -{ - unsigned long prot = pgprot_val(protection); - - /* Write combine is always 0 on non-memory space mappings. On - * memory space, if the user didn't pass 1, we check for a - * "prefetchable" resource. This is a bit hackish, but we use - * this to workaround the inability of /sysfs to provide a write - * combine bit - */ - if (mmap_state != pci_mmap_mem) - write_combine = 0; - else if (write_combine == 0) { - if (rp->flags & IORESOURCE_PREFETCH) - write_combine = 1; - } - - /* XXX would be nice to have a way to ask for write-through */ - prot |= _PAGE_NO_CACHE; - if (write_combine) - prot &= ~_PAGE_GUARDED; - else - prot |= _PAGE_GUARDED; - - return __pgprot(prot); -} - -/* - * This one is used by /dev/mem and fbdev who have no clue about the - * PCI device, it tries to find the PCI device first and calls the - * above routine - */ -pgprot_t pci_phys_mem_access_prot(struct file *file, - unsigned long pfn, - unsigned long size, - pgprot_t protection) -{ - struct pci_dev *pdev = NULL; - struct resource *found = NULL; - unsigned long prot = pgprot_val(protection); - unsigned long offset = pfn << PAGE_SHIFT; - int i; - - if (page_is_ram(pfn)) - return __pgprot(prot); - - prot |= _PAGE_NO_CACHE | _PAGE_GUARDED; - - for_each_pci_dev(pdev) { - for (i = 0; i <= PCI_ROM_RESOURCE; i++) { - struct resource *rp = &pdev->resource[i]; - int flags = rp->flags; - - /* Active and same type? */ - if ((flags & IORESOURCE_MEM) == 0) - continue; - /* In the range of this resource? */ - if (offset < (rp->start & PAGE_MASK) || - offset > rp->end) - continue; - found = rp; - break; - } - if (found) - break; - } - if (found) { - if (found->flags & IORESOURCE_PREFETCH) - prot &= ~_PAGE_GUARDED; - pci_dev_put(pdev); - } - - DBG("non-PCI map for %lx, prot: %lx\n", offset, prot); - - return __pgprot(prot); -} - - -/* - * Perform the actual remap of the pages for a PCI device mapping, as - * appropriate for this architecture. The region in the process to map - * is described by vm_start and vm_end members of VMA, the base physical - * address is found in vm_pgoff. - * The pci device structure is provided so that architectures may make mapping - * decisions on a per-device or per-bus basis. - * - * Returns a negative error code on failure, zero on success. - */ -int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, - enum pci_mmap_state mmap_state, int write_combine) -{ - resource_size_t offset = vma->vm_pgoff << PAGE_SHIFT; - struct resource *rp; - int ret; - - rp = __pci_mmap_make_offset(dev, &offset, mmap_state); - if (rp == NULL) - return -EINVAL; - - vma->vm_pgoff = offset >> PAGE_SHIFT; - vma->vm_page_prot = __pci_mmap_set_pgprot(dev, rp, - vma->vm_page_prot, - mmap_state, write_combine); - - ret = remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, - vma->vm_end - vma->vm_start, vma->vm_page_prot); - - return ret; -} - -static ssize_t pci_show_devspec(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct pci_dev *pdev; - struct device_node *np; - - pdev = to_pci_dev (dev); - np = pci_device_to_OF_node(pdev); - if (np == NULL || np->full_name == NULL) - return 0; - return sprintf(buf, "%s", np->full_name); -} -static DEVICE_ATTR(devspec, S_IRUGO, pci_show_devspec, NULL); - -int pcibios_add_platform_entries(struct pci_dev *pdev) -{ - return device_create_file(&pdev->dev, &dev_attr_devspec); -} - -#define ISA_SPACE_MASK 0x1 -#define ISA_SPACE_IO 0x1 - -static void __devinit pci_process_ISA_OF_ranges(struct device_node *isa_node, - unsigned long phb_io_base_phys, - void __iomem * phb_io_base_virt) -{ - /* Remove these asap */ - - struct pci_address { - u32 a_hi; - u32 a_mid; - u32 a_lo; - }; - - struct isa_address { - u32 a_hi; - u32 a_lo; - }; - - struct isa_range { - struct isa_address isa_addr; - struct pci_address pci_addr; - unsigned int size; - }; - - const struct isa_range *range; - unsigned long pci_addr; - unsigned int isa_addr; - unsigned int size; - int rlen = 0; - - range = of_get_property(isa_node, "ranges", &rlen); - if (range == NULL || (rlen < sizeof(struct isa_range))) { - printk(KERN_ERR "no ISA ranges or unexpected isa range size," - "mapping 64k\n"); - __ioremap_explicit(phb_io_base_phys, - (unsigned long)phb_io_base_virt, - 0x10000, _PAGE_NO_CACHE | _PAGE_GUARDED); - return; - } - - /* From "ISA Binding to 1275" - * The ranges property is laid out as an array of elements, - * each of which comprises: - * cells 0 - 1: an ISA address - * cells 2 - 4: a PCI address - * (size depending on dev->n_addr_cells) - * cell 5: the size of the range - */ - if ((range->isa_addr.a_hi && ISA_SPACE_MASK) == ISA_SPACE_IO) { - isa_addr = range->isa_addr.a_lo; - pci_addr = (unsigned long) range->pci_addr.a_mid << 32 | - range->pci_addr.a_lo; - - /* Assume these are both zero */ - if ((pci_addr != 0) || (isa_addr != 0)) { - printk(KERN_ERR "unexpected isa to pci mapping: %s\n", - __FUNCTION__); - return; - } - - size = PAGE_ALIGN(range->size); - - __ioremap_explicit(phb_io_base_phys, - (unsigned long) phb_io_base_virt, - size, _PAGE_NO_CACHE | _PAGE_GUARDED); - } -} - void __devinit pci_process_bridge_OF_ranges(struct pci_controller *hose, struct device_node *dev, int prim) { @@ -1047,155 +685,122 @@ void __devinit pci_process_bridge_OF_ranges(struct pci_controller *hose, } } -void __devinit pci_setup_phb_io(struct pci_controller *hose, int primary) +#ifdef CONFIG_HOTPLUG + +int pcibios_unmap_io_space(struct pci_bus *bus) { - unsigned long size = hose->pci_io_size; - unsigned long io_virt_offset; - struct resource *res; - struct device_node *isa_dn; + struct pci_controller *hose; - if (size == 0) - return; + WARN_ON(bus == NULL); - hose->io_base_virt = reserve_phb_iospace(size); - DBG("phb%d io_base_phys 0x%lx io_base_virt 0x%lx\n", - hose->global_number, hose->io_base_phys, - (unsigned long) hose->io_base_virt); - - if (primary) { - pci_io_base = (unsigned long)hose->io_base_virt; - isa_dn = of_find_node_by_type(NULL, "isa"); - if (isa_dn) { - isa_io_base = pci_io_base; - pci_process_ISA_OF_ranges(isa_dn, hose->io_base_phys, - hose->io_base_virt); - of_node_put(isa_dn); - } - } + /* If this is not a PHB, we only flush the hash table over + * the area mapped by this bridge. We don't play with the PTE + * mappings since we might have to deal with sub-page alignemnts + * so flushing the hash table is the only sane way to make sure + * that no hash entries are covering that removed bridge area + * while still allowing other busses overlapping those pages + */ + if (bus->self) { + struct resource *res = bus->resource[0]; - io_virt_offset = (unsigned long)hose->io_base_virt - pci_io_base; - res = &hose->io_resource; - res->start += io_virt_offset; - res->end += io_virt_offset; + DBG("IO unmapping for PCI-PCI bridge %s\n", + pci_name(bus->self)); - /* If this is called after the initial PCI scan, then we need to - * proceed to IO mappings now - */ - if (pci_initial_scan_done) - __ioremap_explicit(hose->io_base_phys, - (unsigned long)hose->io_base_virt, - hose->pci_io_size, - _PAGE_NO_CACHE | _PAGE_GUARDED); -} + __flush_hash_table_range(&init_mm, res->start + _IO_BASE, + res->end - res->start + 1); + return 0; + } -void __devinit pci_setup_phb_io_dynamic(struct pci_controller *hose, - int primary) -{ - unsigned long size = hose->pci_io_size; - unsigned long io_virt_offset; - struct resource *res; + /* Get the host bridge */ + hose = pci_bus_to_host(bus); - if (size == 0) - return; + /* Check if we have IOs allocated */ + if (hose->io_base_alloc == 0) + return 0; - hose->io_base_virt = __ioremap(hose->io_base_phys, size, - _PAGE_NO_CACHE | _PAGE_GUARDED); - DBG("phb%d io_base_phys 0x%lx io_base_virt 0x%lx\n", - hose->global_number, hose->io_base_phys, - (unsigned long) hose->io_base_virt); + DBG("IO unmapping for PHB %s\n", + ((struct device_node *)hose->arch_data)->full_name); + DBG(" alloc=0x%p\n", hose->io_base_alloc); - if (primary) - pci_io_base = (unsigned long)hose->io_base_virt; + /* This is a PHB, we fully unmap the IO area */ + vunmap(hose->io_base_alloc); - io_virt_offset = (unsigned long)hose->io_base_virt - pci_io_base; - res = &hose->io_resource; - res->start += io_virt_offset; - res->end += io_virt_offset; + return 0; } +EXPORT_SYMBOL_GPL(pcibios_unmap_io_space); +#endif /* CONFIG_HOTPLUG */ -static int get_bus_io_range(struct pci_bus *bus, unsigned long *start_phys, - unsigned long *start_virt, unsigned long *size) +int __devinit pcibios_map_io_space(struct pci_bus *bus) { - struct pci_controller *hose = pci_bus_to_host(bus); - struct resource *res; - - if (bus->self) - res = bus->resource[0]; - else - /* Root Bus */ - res = &hose->io_resource; - - if (res->end == 0 && res->start == 0) - return 1; + struct vm_struct *area; + unsigned long phys_page; + unsigned long size_page; + unsigned long io_virt_offset; + struct pci_controller *hose; - *start_virt = pci_io_base + res->start; - *start_phys = *start_virt + hose->io_base_phys - - (unsigned long) hose->io_base_virt; + WARN_ON(bus == NULL); - if (res->end > res->start) - *size = res->end - res->start + 1; - else { - printk("%s(): unexpected region 0x%lx->0x%lx\n", - __FUNCTION__, res->start, res->end); - return 1; + /* If this not a PHB, nothing to do, page tables still exist and + * thus HPTEs will be faulted in when needed + */ + if (bus->self) { + DBG("IO mapping for PCI-PCI bridge %s\n", + pci_name(bus->self)); + DBG(" virt=0x%016lx...0x%016lx\n", + bus->resource[0]->start + _IO_BASE, + bus->resource[0]->end + _IO_BASE); + return 0; } - return 0; -} - -int unmap_bus_range(struct pci_bus *bus) -{ - unsigned long start_phys; - unsigned long start_virt; - unsigned long size; + /* Get the host bridge */ + hose = pci_bus_to_host(bus); + phys_page = _ALIGN_DOWN(hose->io_base_phys, PAGE_SIZE); + size_page = _ALIGN_UP(hose->pci_io_size, PAGE_SIZE); - if (!bus) { - printk(KERN_ERR "%s() expected bus\n", __FUNCTION__); - return 1; - } - - if (get_bus_io_range(bus, &start_phys, &start_virt, &size)) - return 1; - if (__iounmap_explicit((void __iomem *) start_virt, size)) - return 1; - - return 0; -} -EXPORT_SYMBOL(unmap_bus_range); + /* Make sure IO area address is clear */ + hose->io_base_alloc = NULL; -int remap_bus_range(struct pci_bus *bus) -{ - unsigned long start_phys; - unsigned long start_virt; - unsigned long size; + /* If there's no IO to map on that bus, get away too */ + if (hose->pci_io_size == 0 || hose->io_base_phys == 0) + return 0; - if (!bus) { - printk(KERN_ERR "%s() expected bus\n", __FUNCTION__); - return 1; - } - - - if (get_bus_io_range(bus, &start_phys, &start_virt, &size)) - return 1; - if (start_phys == 0) - return 1; - printk(KERN_DEBUG "mapping IO %lx -> %lx, size: %lx\n", start_phys, start_virt, size); - if (__ioremap_explicit(start_phys, start_virt, size, - _PAGE_NO_CACHE | _PAGE_GUARDED)) - return 1; + /* Let's allocate some IO space for that guy. We don't pass + * VM_IOREMAP because we don't care about alignment tricks that + * the core does in that case. Maybe we should due to stupid card + * with incomplete address decoding but I'd rather not deal with + * those outside of the reserved 64K legacy region. + */ + area = __get_vm_area(size_page, 0, PHB_IO_BASE, PHB_IO_END); + if (area == NULL) + return -ENOMEM; + hose->io_base_alloc = area->addr; + hose->io_base_virt = (void __iomem *)(area->addr + + hose->io_base_phys - phys_page); + + DBG("IO mapping for PHB %s\n", + ((struct device_node *)hose->arch_data)->full_name); + DBG(" phys=0x%016lx, virt=0x%p (alloc=0x%p)\n", + hose->io_base_phys, hose->io_base_virt, hose->io_base_alloc); + DBG(" size=0x%016lx (alloc=0x%016lx)\n", + hose->pci_io_size, size_page); + + /* Establish the mapping */ + if (__ioremap_at(phys_page, area->addr, size_page, + _PAGE_NO_CACHE | _PAGE_GUARDED) == NULL) + return -ENOMEM; + + /* Fixup hose IO resource */ + io_virt_offset = (unsigned long)hose->io_base_virt - _IO_BASE; + hose->io_resource.start += io_virt_offset; + hose->io_resource.end += io_virt_offset; + + DBG(" hose->io_resource=0x%016lx...0x%016lx\n", + hose->io_resource.start, hose->io_resource.end); return 0; } -EXPORT_SYMBOL(remap_bus_range); - -static void phbs_remap_io(void) -{ - struct pci_controller *hose, *tmp; - - list_for_each_entry_safe(hose, tmp, &hose_list, list_node) - remap_bus_range(hose->bus); -} +EXPORT_SYMBOL_GPL(pcibios_map_io_space); static void __devinit fixup_resource(struct resource *res, struct pci_dev *dev) { @@ -1203,8 +808,7 @@ static void __devinit fixup_resource(struct resource *res, struct pci_dev *dev) unsigned long offset; if (res->flags & IORESOURCE_IO) { - offset = (unsigned long)hose->io_base_virt - pci_io_base; - + offset = (unsigned long)hose->io_base_virt - _IO_BASE; res->start += offset; res->end += offset; } else if (res->flags & IORESOURCE_MEM) { @@ -1219,9 +823,20 @@ void __devinit pcibios_fixup_device_resources(struct pci_dev *dev, /* Update device resources. */ int i; - for (i = 0; i < PCI_NUM_RESOURCES; i++) - if (dev->resource[i].flags) - fixup_resource(&dev->resource[i], dev); + DBG("%s: Fixup resources:\n", pci_name(dev)); + for (i = 0; i < PCI_NUM_RESOURCES; i++) { + struct resource *res = &dev->resource[i]; + if (!res->flags) + continue; + + DBG(" 0x%02x < %08lx:0x%016lx...0x%016lx\n", + i, res->flags, res->start, res->end); + + fixup_resource(res, dev); + + DBG(" > %08lx:0x%016lx...0x%016lx\n", + res->flags, res->start, res->end); + } } EXPORT_SYMBOL(pcibios_fixup_device_resources); @@ -1291,119 +906,6 @@ void __devinit pcibios_fixup_bus(struct pci_bus *bus) } EXPORT_SYMBOL(pcibios_fixup_bus); -/* - * Reads the interrupt pin to determine if interrupt is use by card. - * If the interrupt is used, then gets the interrupt line from the - * openfirmware and sets it in the pci_dev and pci_config line. - */ -int pci_read_irq_line(struct pci_dev *pci_dev) -{ - struct of_irq oirq; - unsigned int virq; - - DBG("Try to map irq for %s...\n", pci_name(pci_dev)); - -#ifdef DEBUG - memset(&oirq, 0xff, sizeof(oirq)); -#endif - /* Try to get a mapping from the device-tree */ - if (of_irq_map_pci(pci_dev, &oirq)) { - u8 line, pin; - - /* If that fails, lets fallback to what is in the config - * space and map that through the default controller. We - * also set the type to level low since that's what PCI - * interrupts are. If your platform does differently, then - * either provide a proper interrupt tree or don't use this - * function. - */ - if (pci_read_config_byte(pci_dev, PCI_INTERRUPT_PIN, &pin)) - return -1; - if (pin == 0) - return -1; - if (pci_read_config_byte(pci_dev, PCI_INTERRUPT_LINE, &line) || - line == 0xff) { - return -1; - } - DBG(" -> no map ! Using irq line %d from PCI config\n", line); - - virq = irq_create_mapping(NULL, line); - if (virq != NO_IRQ) - set_irq_type(virq, IRQ_TYPE_LEVEL_LOW); - } else { - DBG(" -> got one, spec %d cells (0x%08x 0x%08x...) on %s\n", - oirq.size, oirq.specifier[0], oirq.specifier[1], - oirq.controller->full_name); - - virq = irq_create_of_mapping(oirq.controller, oirq.specifier, - oirq.size); - } - if(virq == NO_IRQ) { - DBG(" -> failed to map !\n"); - return -1; - } - - DBG(" -> mapped to linux irq %d\n", virq); - - pci_dev->irq = virq; - - return 0; -} -EXPORT_SYMBOL(pci_read_irq_line); - -void pci_resource_to_user(const struct pci_dev *dev, int bar, - const struct resource *rsrc, - resource_size_t *start, resource_size_t *end) -{ - struct pci_controller *hose = pci_bus_to_host(dev->bus); - resource_size_t offset = 0; - - if (hose == NULL) - return; - - if (rsrc->flags & IORESOURCE_IO) - offset = (unsigned long)hose->io_base_virt - pci_io_base; - - /* We pass a fully fixed up address to userland for MMIO instead of - * a BAR value because X is lame and expects to be able to use that - * to pass to /dev/mem ! - * - * That means that we'll have potentially 64 bits values where some - * userland apps only expect 32 (like X itself since it thinks only - * Sparc has 64 bits MMIO) but if we don't do that, we break it on - * 32 bits CHRPs :-( - * - * Hopefully, the sysfs insterface is immune to that gunk. Once X - * has been fixed (and the fix spread enough), we can re-enable the - * 2 lines below and pass down a BAR value to userland. In that case - * we'll also have to re-enable the matching code in - * __pci_mmap_make_offset(). - * - * BenH. - */ -#if 0 - else if (rsrc->flags & IORESOURCE_MEM) - offset = hose->pci_mem_offset; -#endif - - *start = rsrc->start - offset; - *end = rsrc->end - offset; -} - -struct pci_controller* pci_find_hose_for_OF_device(struct device_node* node) -{ - if (!have_of) - return NULL; - while(node) { - struct pci_controller *hose, *tmp; - list_for_each_entry_safe(hose, tmp, &hose_list, list_node) - if (hose->arch_data == node) - return hose; - node = node->parent; - } - return NULL; -} - unsigned long pci_address_to_pio(phys_addr_t address) { struct pci_controller *hose, *tmp; @@ -1412,7 +914,7 @@ unsigned long pci_address_to_pio(phys_addr_t address) if (address >= hose->io_base_phys && address < (hose->io_base_phys + hose->pci_io_size)) { unsigned long base = - (unsigned long)hose->io_base_virt - pci_io_base; + (unsigned long)hose->io_base_virt - _IO_BASE; return base + (address - hose->io_base_phys); } } diff --git a/arch/powerpc/kernel/ppc_ksyms.c b/arch/powerpc/kernel/ppc_ksyms.c index c96fa9bd35a..a20f1951a5c 100644 --- a/arch/powerpc/kernel/ppc_ksyms.c +++ b/arch/powerpc/kernel/ppc_ksyms.c @@ -67,7 +67,6 @@ EXPORT_SYMBOL(ISA_DMA_THRESHOLD); EXPORT_SYMBOL(DMA_MODE_READ); EXPORT_SYMBOL(DMA_MODE_WRITE); -EXPORT_SYMBOL(do_signal); EXPORT_SYMBOL(transfer_to_handler); EXPORT_SYMBOL(do_IRQ); EXPORT_SYMBOL(machine_check_exception); @@ -106,10 +105,6 @@ EXPORT_SYMBOL(isa_mem_base); EXPORT_SYMBOL(pci_dram_offset); EXPORT_SYMBOL(pci_alloc_consistent); EXPORT_SYMBOL(pci_free_consistent); -EXPORT_SYMBOL(pci_bus_io_base); -EXPORT_SYMBOL(pci_bus_io_base_phys); -EXPORT_SYMBOL(pci_bus_mem_base_phys); -EXPORT_SYMBOL(pci_bus_to_hose); #endif /* CONFIG_PCI */ EXPORT_SYMBOL(start_thread); diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c index 6e2f03566b0..84f000a45e3 100644 --- a/arch/powerpc/kernel/process.c +++ b/arch/powerpc/kernel/process.c @@ -219,22 +219,26 @@ void discard_lazy_cpu_state(void) } #endif /* CONFIG_SMP */ -#ifdef CONFIG_PPC_MERGE /* XXX for now */ int set_dabr(unsigned long dabr) { +#ifdef CONFIG_PPC_MERGE /* XXX for now */ if (ppc_md.set_dabr) return ppc_md.set_dabr(dabr); +#endif + /* XXX should we have a CPU_FTR_HAS_DABR ? */ +#if defined(CONFIG_PPC64) || defined(CONFIG_6xx) mtspr(SPRN_DABR, dabr); +#endif return 0; } -#endif #ifdef CONFIG_PPC64 DEFINE_PER_CPU(struct cpu_usage, cpu_usage_array); -static DEFINE_PER_CPU(unsigned long, current_dabr); #endif +static DEFINE_PER_CPU(unsigned long, current_dabr); + struct task_struct *__switch_to(struct task_struct *prev, struct task_struct *new) { @@ -299,12 +303,10 @@ struct task_struct *__switch_to(struct task_struct *prev, #endif /* CONFIG_SMP */ -#ifdef CONFIG_PPC64 /* for now */ if (unlikely(__get_cpu_var(current_dabr) != new->thread.dabr)) { set_dabr(new->thread.dabr); __get_cpu_var(current_dabr) = new->thread.dabr; } -#endif /* CONFIG_PPC64 */ new_thread = &new->thread; old_thread = ¤t->thread; @@ -473,12 +475,10 @@ void flush_thread(void) discard_lazy_cpu_state(); -#ifdef CONFIG_PPC64 /* for now */ if (current->thread.dabr) { current->thread.dabr = 0; set_dabr(0); } -#endif } void diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c index af42ddab3ab..37ff99bd98b 100644 --- a/arch/powerpc/kernel/prom.c +++ b/arch/powerpc/kernel/prom.c @@ -52,6 +52,7 @@ #include <asm/pSeries_reconfig.h> #include <asm/pci-bridge.h> #include <asm/kexec.h> +#include <asm/system.h> #ifdef DEBUG #define DBG(fmt...) printk(KERN_ERR fmt) @@ -1005,7 +1006,7 @@ static void __init early_reserve_mem(void) void __init early_init_devtree(void *params) { - DBG(" -> early_init_devtree()\n"); + DBG(" -> early_init_devtree(%p)\n", params); /* Setup flat device-tree pointer */ initial_boot_params = params; @@ -1055,8 +1056,6 @@ void __init early_init_devtree(void *params) DBG(" <- early_init_devtree()\n"); } -#undef printk - int of_n_addr_cells(struct device_node* np) { const int *ip; @@ -1375,8 +1374,17 @@ static void of_node_release(struct kref *kref) struct device_node *node = kref_to_device_node(kref); struct property *prop = node->properties; - if (!OF_IS_DYNAMIC(node)) + /* We should never be releasing nodes that haven't been detached. */ + if (!of_node_check_flag(node, OF_DETACHED)) { + printk("WARNING: Bad of_node_put() on %s\n", node->full_name); + dump_stack(); + kref_init(&node->kref); + return; + } + + if (!of_node_check_flag(node, OF_DYNAMIC)) return; + while (prop) { struct property *next = prop->next; kfree(prop->name); @@ -1432,6 +1440,8 @@ void of_detach_node(const struct device_node *np) write_lock(&devtree_lock); parent = np->parent; + if (!parent) + goto out_unlock; if (allnodes == np) allnodes = np->allnext; @@ -1455,6 +1465,9 @@ void of_detach_node(const struct device_node *np) prevsib->sibling = np->sibling; } + of_node_set_flag(np, OF_DETACHED); + +out_unlock: write_unlock(&devtree_lock); } @@ -1716,22 +1729,18 @@ struct device_node *of_get_cpu_node(int cpu, unsigned int *thread) } EXPORT_SYMBOL(of_get_cpu_node); -#ifdef DEBUG +#if defined(CONFIG_DEBUG_FS) && defined(DEBUG) static struct debugfs_blob_wrapper flat_dt_blob; static int __init export_flat_device_tree(void) { struct dentry *d; - d = debugfs_create_dir("powerpc", NULL); - if (!d) - return 1; - flat_dt_blob.data = initial_boot_params; flat_dt_blob.size = initial_boot_params->totalsize; d = debugfs_create_blob("flat-device-tree", S_IFREG | S_IRUSR, - d, &flat_dt_blob); + powerpc_debugfs_root, &flat_dt_blob); if (!d) return 1; diff --git a/arch/powerpc/kernel/prom_init.c b/arch/powerpc/kernel/prom_init.c index d6047c44103..a1d582e3862 100644 --- a/arch/powerpc/kernel/prom_init.c +++ b/arch/powerpc/kernel/prom_init.c @@ -635,6 +635,7 @@ static void __init early_cmdline_parse(void) /* ibm,dynamic-reconfiguration-memory property supported */ #define OV5_DRCONF_MEMORY 0x20 #define OV5_LARGE_PAGES 0x10 /* large pages supported */ +#define OV5_DONATE_DEDICATE_CPU 0x02 /* donate dedicated CPU support */ /* PCIe/MSI support. Without MSI full PCIe is not supported */ #ifdef CONFIG_PCI_MSI #define OV5_MSI 0x01 /* PCIe/MSI support */ @@ -685,7 +686,8 @@ static unsigned char ibm_architecture_vec[] = { /* option vector 5: PAPR/OF options */ 3 - 2, /* length */ 0, /* don't ignore, don't halt */ - OV5_LPAR | OV5_SPLPAR | OV5_LARGE_PAGES | OV5_DRCONF_MEMORY | OV5_MSI, + OV5_LPAR | OV5_SPLPAR | OV5_LARGE_PAGES | OV5_DRCONF_MEMORY | + OV5_DONATE_DEDICATE_CPU | OV5_MSI, }; /* Old method - ELF header with PT_NOTE sections */ diff --git a/arch/powerpc/kernel/ptrace-common.h b/arch/powerpc/kernel/ptrace-common.h deleted file mode 100644 index 8797ae737a7..00000000000 --- a/arch/powerpc/kernel/ptrace-common.h +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright (c) 2002 Stephen Rothwell, IBM Coproration - * Extracted from ptrace.c and ptrace32.c - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file README.legal in the main directory of - * this archive for more details. - */ - -#ifndef _PPC64_PTRACE_COMMON_H -#define _PPC64_PTRACE_COMMON_H - -#include <asm/system.h> - -/* - * Set of msr bits that gdb can change on behalf of a process. - */ -#define MSR_DEBUGCHANGE (MSR_FE0 | MSR_SE | MSR_BE | MSR_FE1) - -/* - * Get contents of register REGNO in task TASK. - */ -static inline unsigned long get_reg(struct task_struct *task, int regno) -{ - unsigned long tmp = 0; - - /* - * Put the correct FP bits in, they might be wrong as a result - * of our lazy FP restore. - */ - if (regno == PT_MSR) { - tmp = ((unsigned long *)task->thread.regs)[PT_MSR]; - tmp |= task->thread.fpexc_mode; - } else if (regno < (sizeof(struct pt_regs) / sizeof(unsigned long))) { - tmp = ((unsigned long *)task->thread.regs)[regno]; - } - - return tmp; -} - -/* - * Write contents of register REGNO in task TASK. - */ -static inline int put_reg(struct task_struct *task, int regno, - unsigned long data) -{ - if (regno < PT_SOFTE) { - if (regno == PT_MSR) - data = (data & MSR_DEBUGCHANGE) - | (task->thread.regs->msr & ~MSR_DEBUGCHANGE); - ((unsigned long *)task->thread.regs)[regno] = data; - return 0; - } - return -EIO; -} - -static inline void set_single_step(struct task_struct *task) -{ - struct pt_regs *regs = task->thread.regs; - if (regs != NULL) - regs->msr |= MSR_SE; - set_tsk_thread_flag(task, TIF_SINGLESTEP); -} - -static inline void clear_single_step(struct task_struct *task) -{ - struct pt_regs *regs = task->thread.regs; - if (regs != NULL) - regs->msr &= ~MSR_SE; - clear_tsk_thread_flag(task, TIF_SINGLESTEP); -} - -#ifdef CONFIG_ALTIVEC -/* - * Get/set all the altivec registers vr0..vr31, vscr, vrsave, in one go. - * The transfer totals 34 quadword. Quadwords 0-31 contain the - * corresponding vector registers. Quadword 32 contains the vscr as the - * last word (offset 12) within that quadword. Quadword 33 contains the - * vrsave as the first word (offset 0) within the quadword. - * - * This definition of the VMX state is compatible with the current PPC32 - * ptrace interface. This allows signal handling and ptrace to use the - * same structures. This also simplifies the implementation of a bi-arch - * (combined (32- and 64-bit) gdb. - */ - -/* - * Get contents of AltiVec register state in task TASK - */ -static inline int get_vrregs(unsigned long __user *data, - struct task_struct *task) -{ - unsigned long regsize; - - /* copy AltiVec registers VR[0] .. VR[31] */ - regsize = 32 * sizeof(vector128); - if (copy_to_user(data, task->thread.vr, regsize)) - return -EFAULT; - data += (regsize / sizeof(unsigned long)); - - /* copy VSCR */ - regsize = 1 * sizeof(vector128); - if (copy_to_user(data, &task->thread.vscr, regsize)) - return -EFAULT; - data += (regsize / sizeof(unsigned long)); - - /* copy VRSAVE */ - if (put_user(task->thread.vrsave, (u32 __user *)data)) - return -EFAULT; - - return 0; -} - -/* - * Write contents of AltiVec register state into task TASK. - */ -static inline int set_vrregs(struct task_struct *task, - unsigned long __user *data) -{ - unsigned long regsize; - - /* copy AltiVec registers VR[0] .. VR[31] */ - regsize = 32 * sizeof(vector128); - if (copy_from_user(task->thread.vr, data, regsize)) - return -EFAULT; - data += (regsize / sizeof(unsigned long)); - - /* copy VSCR */ - regsize = 1 * sizeof(vector128); - if (copy_from_user(&task->thread.vscr, data, regsize)) - return -EFAULT; - data += (regsize / sizeof(unsigned long)); - - /* copy VRSAVE */ - if (get_user(task->thread.vrsave, (u32 __user *)data)) - return -EFAULT; - - return 0; -} -#endif - -static inline int ptrace_set_debugreg(struct task_struct *task, - unsigned long addr, unsigned long data) -{ - /* We only support one DABR and no IABRS at the moment */ - if (addr > 0) - return -EINVAL; - - /* The bottom 3 bits are flags */ - if ((data & ~0x7UL) >= TASK_SIZE) - return -EIO; - - /* Ensure translation is on */ - if (data && !(data & DABR_TRANSLATION)) - return -EIO; - - task->thread.dabr = data; - return 0; -} - -#endif /* _PPC64_PTRACE_COMMON_H */ diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c index bf76562167c..8a177bd9eab 100644 --- a/arch/powerpc/kernel/ptrace.c +++ b/arch/powerpc/kernel/ptrace.c @@ -35,11 +35,11 @@ #include <asm/pgtable.h> #include <asm/system.h> -#ifdef CONFIG_PPC64 -#include "ptrace-common.h" -#endif +/* + * does not yet catch signals sent when the child dies. + * in exit.c or in signal.c. + */ -#ifdef CONFIG_PPC32 /* * Set of msr bits that gdb can change on behalf of a process. */ @@ -48,65 +48,117 @@ #else #define MSR_DEBUGCHANGE (MSR_SE | MSR_BE) #endif -#endif /* CONFIG_PPC32 */ /* - * does not yet catch signals sent when the child dies. - * in exit.c or in signal.c. + * Max register writeable via put_reg */ - #ifdef CONFIG_PPC32 +#define PT_MAX_PUT_REG PT_MQ +#else +#define PT_MAX_PUT_REG PT_CCR +#endif + /* * Get contents of register REGNO in task TASK. */ -static inline unsigned long get_reg(struct task_struct *task, int regno) +unsigned long ptrace_get_reg(struct task_struct *task, int regno) { - if (regno < sizeof(struct pt_regs) / sizeof(unsigned long) - && task->thread.regs != NULL) + unsigned long tmp = 0; + + if (task->thread.regs == NULL) + return -EIO; + + if (regno == PT_MSR) { + tmp = ((unsigned long *)task->thread.regs)[PT_MSR]; + return tmp | task->thread.fpexc_mode; + } + + if (regno < (sizeof(struct pt_regs) / sizeof(unsigned long))) return ((unsigned long *)task->thread.regs)[regno]; - return (0); + + return -EIO; } /* * Write contents of register REGNO in task TASK. */ -static inline int put_reg(struct task_struct *task, int regno, - unsigned long data) +int ptrace_put_reg(struct task_struct *task, int regno, unsigned long data) { - if (regno <= PT_MQ && task->thread.regs != NULL) { + if (task->thread.regs == NULL) + return -EIO; + + if (regno <= PT_MAX_PUT_REG || regno == PT_TRAP) { if (regno == PT_MSR) data = (data & MSR_DEBUGCHANGE) | (task->thread.regs->msr & ~MSR_DEBUGCHANGE); + /* We prevent mucking around with the reserved area of trap + * which are used internally by the kernel + */ + if (regno == PT_TRAP) + data &= 0xfff0; ((unsigned long *)task->thread.regs)[regno] = data; return 0; } return -EIO; } + +static int get_fpregs(void __user *data, struct task_struct *task, + int has_fpscr) +{ + unsigned int count = has_fpscr ? 33 : 32; + + if (copy_to_user(data, task->thread.fpr, count * sizeof(double))) + return -EFAULT; + return 0; +} + +static int set_fpregs(void __user *data, struct task_struct *task, + int has_fpscr) +{ + unsigned int count = has_fpscr ? 33 : 32; + + if (copy_from_user(task->thread.fpr, data, count * sizeof(double))) + return -EFAULT; + return 0; +} + + #ifdef CONFIG_ALTIVEC /* + * Get/set all the altivec registers vr0..vr31, vscr, vrsave, in one go. + * The transfer totals 34 quadword. Quadwords 0-31 contain the + * corresponding vector registers. Quadword 32 contains the vscr as the + * last word (offset 12) within that quadword. Quadword 33 contains the + * vrsave as the first word (offset 0) within the quadword. + * + * This definition of the VMX state is compatible with the current PPC32 + * ptrace interface. This allows signal handling and ptrace to use the + * same structures. This also simplifies the implementation of a bi-arch + * (combined (32- and 64-bit) gdb. + */ + +/* * Get contents of AltiVec register state in task TASK */ -static inline int get_vrregs(unsigned long __user *data, struct task_struct *task) +static int get_vrregs(unsigned long __user *data, struct task_struct *task) { - int i, j; - - if (!access_ok(VERIFY_WRITE, data, 133 * sizeof(unsigned long))) - return -EFAULT; + unsigned long regsize; /* copy AltiVec registers VR[0] .. VR[31] */ - for (i = 0; i < 32; i++) - for (j = 0; j < 4; j++, data++) - if (__put_user(task->thread.vr[i].u[j], data)) - return -EFAULT; + regsize = 32 * sizeof(vector128); + if (copy_to_user(data, task->thread.vr, regsize)) + return -EFAULT; + data += (regsize / sizeof(unsigned long)); /* copy VSCR */ - for (i = 0; i < 4; i++, data++) - if (__put_user(task->thread.vscr.u[i], data)) - return -EFAULT; + regsize = 1 * sizeof(vector128); + if (copy_to_user(data, &task->thread.vscr, regsize)) + return -EFAULT; + data += (regsize / sizeof(unsigned long)); - /* copy VRSAVE */ - if (__put_user(task->thread.vrsave, data)) + /* copy VRSAVE */ + if (put_user(task->thread.vrsave, (u32 __user *)data)) return -EFAULT; return 0; @@ -115,31 +167,29 @@ static inline int get_vrregs(unsigned long __user *data, struct task_struct *tas /* * Write contents of AltiVec register state into task TASK. */ -static inline int set_vrregs(struct task_struct *task, unsigned long __user *data) +static int set_vrregs(struct task_struct *task, unsigned long __user *data) { - int i, j; - - if (!access_ok(VERIFY_READ, data, 133 * sizeof(unsigned long))) - return -EFAULT; + unsigned long regsize; /* copy AltiVec registers VR[0] .. VR[31] */ - for (i = 0; i < 32; i++) - for (j = 0; j < 4; j++, data++) - if (__get_user(task->thread.vr[i].u[j], data)) - return -EFAULT; + regsize = 32 * sizeof(vector128); + if (copy_from_user(task->thread.vr, data, regsize)) + return -EFAULT; + data += (regsize / sizeof(unsigned long)); /* copy VSCR */ - for (i = 0; i < 4; i++, data++) - if (__get_user(task->thread.vscr.u[i], data)) - return -EFAULT; + regsize = 1 * sizeof(vector128); + if (copy_from_user(&task->thread.vscr, data, regsize)) + return -EFAULT; + data += (regsize / sizeof(unsigned long)); /* copy VRSAVE */ - if (__get_user(task->thread.vrsave, data)) + if (get_user(task->thread.vrsave, (u32 __user *)data)) return -EFAULT; return 0; } -#endif +#endif /* CONFIG_ALTIVEC */ #ifdef CONFIG_SPE @@ -156,7 +206,7 @@ static inline int set_vrregs(struct task_struct *task, unsigned long __user *dat /* * Get contents of SPE register state in task TASK. */ -static inline int get_evrregs(unsigned long *data, struct task_struct *task) +static int get_evrregs(unsigned long *data, struct task_struct *task) { int i; @@ -182,7 +232,7 @@ static inline int get_evrregs(unsigned long *data, struct task_struct *task) /* * Write contents of SPE register state into task TASK. */ -static inline int set_evrregs(struct task_struct *task, unsigned long *data) +static int set_evrregs(struct task_struct *task, unsigned long *data) { int i; @@ -205,8 +255,8 @@ static inline int set_evrregs(struct task_struct *task, unsigned long *data) } #endif /* CONFIG_SPE */ -static inline void -set_single_step(struct task_struct *task) + +static void set_single_step(struct task_struct *task) { struct pt_regs *regs = task->thread.regs; @@ -221,8 +271,7 @@ set_single_step(struct task_struct *task) set_tsk_thread_flag(task, TIF_SINGLESTEP); } -static inline void -clear_single_step(struct task_struct *task) +static void clear_single_step(struct task_struct *task) { struct pt_regs *regs = task->thread.regs; @@ -236,7 +285,25 @@ clear_single_step(struct task_struct *task) } clear_tsk_thread_flag(task, TIF_SINGLESTEP); } -#endif /* CONFIG_PPC32 */ + +static int ptrace_set_debugreg(struct task_struct *task, unsigned long addr, + unsigned long data) +{ + /* We only support one DABR and no IABRS at the moment */ + if (addr > 0) + return -EINVAL; + + /* The bottom 3 bits are flags */ + if ((data & ~0x7UL) >= TASK_SIZE) + return -EIO; + + /* Ensure translation is on */ + if (data && !(data & DABR_TRANSLATION)) + return -EIO; + + task->thread.dabr = data; + return 0; +} /* * Called by kernel/ptrace.c when detaching.. @@ -249,6 +316,62 @@ void ptrace_disable(struct task_struct *child) clear_single_step(child); } +/* + * Here are the old "legacy" powerpc specific getregs/setregs ptrace calls, + * we mark them as obsolete now, they will be removed in a future version + */ +static long arch_ptrace_old(struct task_struct *child, long request, long addr, + long data) +{ + int ret = -EPERM; + + switch(request) { + case PPC_PTRACE_GETREGS: { /* Get GPRs 0 - 31. */ + int i; + unsigned long *reg = &((unsigned long *)child->thread.regs)[0]; + unsigned long __user *tmp = (unsigned long __user *)addr; + + for (i = 0; i < 32; i++) { + ret = put_user(*reg, tmp); + if (ret) + break; + reg++; + tmp++; + } + break; + } + + case PPC_PTRACE_SETREGS: { /* Set GPRs 0 - 31. */ + int i; + unsigned long *reg = &((unsigned long *)child->thread.regs)[0]; + unsigned long __user *tmp = (unsigned long __user *)addr; + + for (i = 0; i < 32; i++) { + ret = get_user(*reg, tmp); + if (ret) + break; + reg++; + tmp++; + } + break; + } + + case PPC_PTRACE_GETFPREGS: { /* Get FPRs 0 - 31. */ + flush_fp_to_thread(child); + ret = get_fpregs((void __user *)addr, child, 0); + break; + } + + case PPC_PTRACE_SETFPREGS: { /* Get FPRs 0 - 31. */ + flush_fp_to_thread(child); + ret = set_fpregs((void __user *)addr, child, 0); + break; + } + + } + return ret; +} + long arch_ptrace(struct task_struct *child, long request, long addr, long data) { int ret = -EPERM; @@ -256,17 +379,9 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) switch (request) { /* when I and D space are separate, these will need to be fixed. */ case PTRACE_PEEKTEXT: /* read word at location addr. */ - case PTRACE_PEEKDATA: { - unsigned long tmp; - int copied; - - copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); - ret = -EIO; - if (copied != sizeof(tmp)) - break; - ret = put_user(tmp,(unsigned long __user *) data); + case PTRACE_PEEKDATA: + ret = generic_ptrace_peekdata(child, addr, data); break; - } /* read the word at location addr in the USER area. */ case PTRACE_PEEKUSR: { @@ -284,11 +399,9 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) #endif break; -#ifdef CONFIG_PPC32 CHECK_FULL_REGS(child->thread.regs); -#endif if (index < PT_FPR0) { - tmp = get_reg(child, (int) index); + tmp = ptrace_get_reg(child, (int) index); } else { flush_fp_to_thread(child); tmp = ((unsigned long *)child->thread.fpr)[index - PT_FPR0]; @@ -300,11 +413,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) /* If I and D space are separate, this will have to be fixed. */ case PTRACE_POKETEXT: /* write the word at location addr. */ case PTRACE_POKEDATA: - ret = 0; - if (access_process_vm(child, addr, &data, sizeof(data), 1) - == sizeof(data)) - break; - ret = -EIO; + ret = generic_ptrace_pokedata(child, addr, data); break; /* write the word at location addr in the USER area */ @@ -323,13 +432,9 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) #endif break; -#ifdef CONFIG_PPC32 CHECK_FULL_REGS(child->thread.regs); -#endif - if (index == PT_ORIG_R3) - break; if (index < PT_FPR0) { - ret = put_reg(child, index, data); + ret = ptrace_put_reg(child, index, data); } else { flush_fp_to_thread(child); ((unsigned long *)child->thread.fpr)[index - PT_FPR0] = data; @@ -384,7 +489,6 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) break; } -#ifdef CONFIG_PPC64 case PTRACE_GET_DEBUGREG: { ret = -EINVAL; /* We only support one DABR and no IABRS at the moment */ @@ -398,73 +502,61 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) case PTRACE_SET_DEBUGREG: ret = ptrace_set_debugreg(child, addr, data); break; -#endif case PTRACE_DETACH: ret = ptrace_detach(child, data); break; - case PPC_PTRACE_GETREGS: { /* Get GPRs 0 - 31. */ - int i; - unsigned long *reg = &((unsigned long *)child->thread.regs)[0]; - unsigned long __user *tmp = (unsigned long __user *)addr; - - for (i = 0; i < 32; i++) { - ret = put_user(*reg, tmp); - if (ret) - break; - reg++; - tmp++; +#ifdef CONFIG_PPC64 + case PTRACE_GETREGS64: +#endif + case PTRACE_GETREGS: { /* Get all pt_regs from the child. */ + int ui; + if (!access_ok(VERIFY_WRITE, (void __user *)data, + sizeof(struct pt_regs))) { + ret = -EIO; + break; + } + ret = 0; + for (ui = 0; ui < PT_REGS_COUNT; ui ++) { + ret |= __put_user(ptrace_get_reg(child, ui), + (unsigned long __user *) data); + data += sizeof(long); } break; } - case PPC_PTRACE_SETREGS: { /* Set GPRs 0 - 31. */ - int i; - unsigned long *reg = &((unsigned long *)child->thread.regs)[0]; - unsigned long __user *tmp = (unsigned long __user *)addr; - - for (i = 0; i < 32; i++) { - ret = get_user(*reg, tmp); +#ifdef CONFIG_PPC64 + case PTRACE_SETREGS64: +#endif + case PTRACE_SETREGS: { /* Set all gp regs in the child. */ + unsigned long tmp; + int ui; + if (!access_ok(VERIFY_READ, (void __user *)data, + sizeof(struct pt_regs))) { + ret = -EIO; + break; + } + ret = 0; + for (ui = 0; ui < PT_REGS_COUNT; ui ++) { + ret = __get_user(tmp, (unsigned long __user *) data); if (ret) break; - reg++; - tmp++; + ptrace_put_reg(child, ui, tmp); + data += sizeof(long); } break; } - case PPC_PTRACE_GETFPREGS: { /* Get FPRs 0 - 31. */ - int i; - unsigned long *reg = &((unsigned long *)child->thread.fpr)[0]; - unsigned long __user *tmp = (unsigned long __user *)addr; - + case PTRACE_GETFPREGS: { /* Get the child FPU state (FPR0...31 + FPSCR) */ flush_fp_to_thread(child); - - for (i = 0; i < 32; i++) { - ret = put_user(*reg, tmp); - if (ret) - break; - reg++; - tmp++; - } + ret = get_fpregs((void __user *)data, child, 1); break; } - case PPC_PTRACE_SETFPREGS: { /* Get FPRs 0 - 31. */ - int i; - unsigned long *reg = &((unsigned long *)child->thread.fpr)[0]; - unsigned long __user *tmp = (unsigned long __user *)addr; - + case PTRACE_SETFPREGS: { /* Set the child FPU state (FPR0...31 + FPSCR) */ flush_fp_to_thread(child); - - for (i = 0; i < 32; i++) { - ret = get_user(*reg, tmp); - if (ret) - break; - reg++; - tmp++; - } + ret = set_fpregs((void __user *)data, child, 1); break; } @@ -499,11 +591,18 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) break; #endif + /* Old reverse args ptrace callss */ + case PPC_PTRACE_GETREGS: /* Get GPRs 0 - 31. */ + case PPC_PTRACE_SETREGS: /* Set GPRs 0 - 31. */ + case PPC_PTRACE_GETFPREGS: /* Get FPRs 0 - 31. */ + case PPC_PTRACE_SETFPREGS: /* Get FPRs 0 - 31. */ + ret = arch_ptrace_old(child, request, addr, data); + break; + default: ret = ptrace_request(child, request, addr, data); break; } - return ret; } diff --git a/arch/powerpc/kernel/ptrace32.c b/arch/powerpc/kernel/ptrace32.c index 9b9a230349b..9e6baeac0fb 100644 --- a/arch/powerpc/kernel/ptrace32.c +++ b/arch/powerpc/kernel/ptrace32.c @@ -33,13 +33,55 @@ #include <asm/pgtable.h> #include <asm/system.h> -#include "ptrace-common.h" - /* * does not yet catch signals sent when the child dies. * in exit.c or in signal.c. */ +/* + * Here are the old "legacy" powerpc specific getregs/setregs ptrace calls, + * we mark them as obsolete now, they will be removed in a future version + */ +static long compat_ptrace_old(struct task_struct *child, long request, + long addr, long data) +{ + int ret = -EPERM; + + switch(request) { + case PPC_PTRACE_GETREGS: { /* Get GPRs 0 - 31. */ + int i; + unsigned long *reg = &((unsigned long *)child->thread.regs)[0]; + unsigned int __user *tmp = (unsigned int __user *)addr; + + for (i = 0; i < 32; i++) { + ret = put_user(*reg, tmp); + if (ret) + break; + reg++; + tmp++; + } + break; + } + + case PPC_PTRACE_SETREGS: { /* Set GPRs 0 - 31. */ + int i; + unsigned long *reg = &((unsigned long *)child->thread.regs)[0]; + unsigned int __user *tmp = (unsigned int __user *)addr; + + for (i = 0; i < 32; i++) { + ret = get_user(*reg, tmp); + if (ret) + break; + reg++; + tmp++; + } + break; + } + + } + return ret; +} + long compat_sys_ptrace(int request, int pid, unsigned long addr, unsigned long data) { @@ -123,7 +165,7 @@ long compat_sys_ptrace(int request, int pid, unsigned long addr, break; if (index < PT_FPR0) { - tmp = get_reg(child, index); + tmp = ptrace_get_reg(child, index); } else { flush_fp_to_thread(child); /* @@ -162,7 +204,9 @@ long compat_sys_ptrace(int request, int pid, unsigned long addr, else part = 0; /* want the 1st half of the register (left-most). */ - /* Validate the input - check to see if address is on the wrong boundary or beyond the end of the user area */ + /* Validate the input - check to see if address is on the wrong boundary + * or beyond the end of the user area + */ if ((addr & 3) || numReg > PT_FPSCR) break; @@ -170,7 +214,7 @@ long compat_sys_ptrace(int request, int pid, unsigned long addr, flush_fp_to_thread(child); tmp = ((unsigned long int *)child->thread.fpr)[numReg - PT_FPR0]; } else { /* register within PT_REGS struct */ - tmp = get_reg(child, numReg); + tmp = ptrace_get_reg(child, numReg); } reg32bits = ((u32*)&tmp)[part]; ret = put_user(reg32bits, (u32 __user *)data); @@ -226,10 +270,8 @@ long compat_sys_ptrace(int request, int pid, unsigned long addr, if ((addr & 3) || (index > PT_FPSCR32)) break; - if (index == PT_ORIG_R3) - break; if (index < PT_FPR0) { - ret = put_reg(child, index, data); + ret = ptrace_put_reg(child, index, data); } else { flush_fp_to_thread(child); /* @@ -258,70 +300,25 @@ long compat_sys_ptrace(int request, int pid, unsigned long addr, /* Determine which register the user wants */ index = (u64)addr >> 2; numReg = index / 2; + /* * Validate the input - check to see if address is on the * wrong boundary or beyond the end of the user area */ if ((addr & 3) || (numReg > PT_FPSCR)) break; - /* Insure it is a register we let them change */ - if ((numReg == PT_ORIG_R3) - || ((numReg > PT_CCR) && (numReg < PT_FPR0))) - break; - if (numReg >= PT_FPR0) { + if (numReg < PT_FPR0) { + unsigned long freg = ptrace_get_reg(child, numReg); + if (index % 2) + freg = (freg & ~0xfffffffful) | (data & 0xfffffffful); + else + freg = (freg & 0xfffffffful) | (data << 32); + ret = ptrace_put_reg(child, numReg, freg); + } else { flush_fp_to_thread(child); + ((unsigned int *)child->thread.regs)[index] = data; + ret = 0; } - if (numReg == PT_MSR) - data = (data & MSR_DEBUGCHANGE) - | (child->thread.regs->msr & ~MSR_DEBUGCHANGE); - ((u32*)child->thread.regs)[index] = data; - ret = 0; - break; - } - - case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ - case PTRACE_CONT: { /* restart after signal. */ - ret = -EIO; - if (!valid_signal(data)) - break; - if (request == PTRACE_SYSCALL) - set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); - else - clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); - child->exit_code = data; - /* make sure the single step bit is not set. */ - clear_single_step(child); - wake_up_process(child); - ret = 0; - break; - } - - /* - * make the child exit. Best I can do is send it a sigkill. - * perhaps it should be put in the status that it wants to - * exit. - */ - case PTRACE_KILL: { - ret = 0; - if (child->exit_state == EXIT_ZOMBIE) /* already dead */ - break; - child->exit_code = SIGKILL; - /* make sure the single step bit is not set. */ - clear_single_step(child); - wake_up_process(child); - break; - } - - case PTRACE_SINGLESTEP: { /* set the trap flag. */ - ret = -EIO; - if (!valid_signal(data)) - break; - clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); - set_single_step(child); - child->exit_code = data; - /* give it a chance to run. */ - wake_up_process(child); - ret = 0; break; } @@ -334,95 +331,67 @@ long compat_sys_ptrace(int request, int pid, unsigned long addr, break; } - case PTRACE_SET_DEBUGREG: - ret = ptrace_set_debugreg(child, addr, data); - break; - - case PTRACE_DETACH: - ret = ptrace_detach(child, data); + case PTRACE_GETEVENTMSG: + ret = put_user(child->ptrace_message, (unsigned int __user *) data); break; - case PPC_PTRACE_GETREGS: { /* Get GPRs 0 - 31. */ - int i; - unsigned long *reg = &((unsigned long *)child->thread.regs)[0]; - unsigned int __user *tmp = (unsigned int __user *)addr; - - for (i = 0; i < 32; i++) { - ret = put_user(*reg, tmp); - if (ret) - break; - reg++; - tmp++; + case PTRACE_GETREGS: { /* Get all pt_regs from the child. */ + int ui; + if (!access_ok(VERIFY_WRITE, (void __user *)data, + PT_REGS_COUNT * sizeof(int))) { + ret = -EIO; + break; } - break; - } - - case PPC_PTRACE_SETREGS: { /* Set GPRs 0 - 31. */ - int i; - unsigned long *reg = &((unsigned long *)child->thread.regs)[0]; - unsigned int __user *tmp = (unsigned int __user *)addr; - - for (i = 0; i < 32; i++) { - ret = get_user(*reg, tmp); - if (ret) - break; - reg++; - tmp++; + ret = 0; + for (ui = 0; ui < PT_REGS_COUNT; ui ++) { + ret |= __put_user(ptrace_get_reg(child, ui), + (unsigned int __user *) data); + data += sizeof(int); } break; } - case PPC_PTRACE_GETFPREGS: { /* Get FPRs 0 - 31. */ - int i; - unsigned long *reg = &((unsigned long *)child->thread.fpr)[0]; - unsigned int __user *tmp = (unsigned int __user *)addr; - - flush_fp_to_thread(child); - - for (i = 0; i < 32; i++) { - ret = put_user(*reg, tmp); - if (ret) - break; - reg++; - tmp++; + case PTRACE_SETREGS: { /* Set all gp regs in the child. */ + unsigned long tmp; + int ui; + if (!access_ok(VERIFY_READ, (void __user *)data, + PT_REGS_COUNT * sizeof(int))) { + ret = -EIO; + break; } - break; - } - - case PPC_PTRACE_SETFPREGS: { /* Get FPRs 0 - 31. */ - int i; - unsigned long *reg = &((unsigned long *)child->thread.fpr)[0]; - unsigned int __user *tmp = (unsigned int __user *)addr; - - flush_fp_to_thread(child); - - for (i = 0; i < 32; i++) { - ret = get_user(*reg, tmp); + ret = 0; + for (ui = 0; ui < PT_REGS_COUNT; ui ++) { + ret = __get_user(tmp, (unsigned int __user *) data); if (ret) break; - reg++; - tmp++; + ptrace_put_reg(child, ui, tmp); + data += sizeof(int); } break; } - case PTRACE_GETEVENTMSG: - ret = put_user(child->ptrace_message, (unsigned int __user *) data); - break; - -#ifdef CONFIG_ALTIVEC + case PTRACE_GETFPREGS: + case PTRACE_SETFPREGS: case PTRACE_GETVRREGS: - /* Get the child altivec register state. */ - flush_altivec_to_thread(child); - ret = get_vrregs((unsigned long __user *)data, child); + case PTRACE_SETVRREGS: + case PTRACE_GETREGS64: + case PTRACE_SETREGS64: + case PPC_PTRACE_GETFPREGS: + case PPC_PTRACE_SETFPREGS: + case PTRACE_KILL: + case PTRACE_SINGLESTEP: + case PTRACE_DETACH: + case PTRACE_SET_DEBUGREG: + case PTRACE_SYSCALL: + case PTRACE_CONT: + ret = arch_ptrace(child, request, addr, data); break; - case PTRACE_SETVRREGS: - /* Set the child altivec register state. */ - flush_altivec_to_thread(child); - ret = set_vrregs(child, (unsigned long __user *)data); + /* Old reverse args ptrace callss */ + case PPC_PTRACE_GETREGS: /* Get GPRs 0 - 31. */ + case PPC_PTRACE_SETREGS: /* Set GPRs 0 - 31. */ + ret = compat_ptrace_old(child, request, addr, data); break; -#endif default: ret = ptrace_request(child, request, addr, data); diff --git a/arch/powerpc/kernel/rtas_pci.c b/arch/powerpc/kernel/rtas_pci.c index f2286822be0..a5de6211b97 100644 --- a/arch/powerpc/kernel/rtas_pci.c +++ b/arch/powerpc/kernel/rtas_pci.c @@ -278,10 +278,8 @@ void __init find_and_init_phbs(void) { struct device_node *node; struct pci_controller *phb; - unsigned int index; struct device_node *root = of_find_node_by_path("/"); - index = 0; for (node = of_get_next_child(root, NULL); node != NULL; node = of_get_next_child(root, node)) { @@ -295,8 +293,7 @@ void __init find_and_init_phbs(void) continue; rtas_setup_phb(phb); pci_process_bridge_OF_ranges(phb, node, 0); - pci_setup_phb_io(phb, index == 0); - index++; + isa_bridge_find_early(phb); } of_node_put(root); @@ -335,7 +332,7 @@ int pcibios_remove_root_bus(struct pci_controller *phb) return 1; } - rc = unmap_bus_range(b); + rc = pcibios_unmap_io_space(b); if (rc) { printk(KERN_ERR "%s: failed to unmap IO on bus %s\n", __FUNCTION__, b->name); diff --git a/arch/powerpc/kernel/setup-common.c b/arch/powerpc/kernel/setup-common.c index ed07a198f8d..4924c48cb1f 100644 --- a/arch/powerpc/kernel/setup-common.c +++ b/arch/powerpc/kernel/setup-common.c @@ -32,6 +32,7 @@ #include <linux/unistd.h> #include <linux/serial.h> #include <linux/serial_8250.h> +#include <linux/debugfs.h> #include <asm/io.h> #include <asm/prom.h> #include <asm/processor.h> @@ -486,6 +487,14 @@ int check_legacy_ioport(unsigned long base_port) switch(base_port) { case I8042_DATA_REG: + if (!(np = of_find_compatible_node(NULL, NULL, "pnpPNP,303"))) + np = of_find_compatible_node(NULL, NULL, "pnpPNP,f03"); + if (np) { + parent = of_get_parent(np); + of_node_put(np); + np = parent; + break; + } np = of_find_node_by_type(NULL, "8042"); break; case FDC_BASE: /* FDC1 */ @@ -571,3 +580,15 @@ static int __init check_cache_coherency(void) late_initcall(check_cache_coherency); #endif /* CONFIG_CHECK_CACHE_COHERENCY */ + +#ifdef CONFIG_DEBUG_FS +struct dentry *powerpc_debugfs_root; + +static int powerpc_debugfs_init(void) +{ + powerpc_debugfs_root = debugfs_create_dir("powerpc", NULL); + + return powerpc_debugfs_root == NULL; +} +arch_initcall(powerpc_debugfs_init); +#endif diff --git a/arch/powerpc/kernel/setup_32.c b/arch/powerpc/kernel/setup_32.c index 35f8f443c14..7ec6ba56d83 100644 --- a/arch/powerpc/kernel/setup_32.c +++ b/arch/powerpc/kernel/setup_32.c @@ -262,13 +262,11 @@ void __init setup_arch(char **cmdline_p) * Systems with OF can look in the properties on the cpu node(s) * for a possibly more accurate value. */ - if (cpu_has_feature(CPU_FTR_SPLIT_ID_CACHE)) { - dcache_bsize = cur_cpu_spec->dcache_bsize; - icache_bsize = cur_cpu_spec->icache_bsize; - ucache_bsize = 0; - } else - ucache_bsize = dcache_bsize = icache_bsize - = cur_cpu_spec->dcache_bsize; + dcache_bsize = cur_cpu_spec->dcache_bsize; + icache_bsize = cur_cpu_spec->icache_bsize; + ucache_bsize = 0; + if (cpu_has_feature(CPU_FTR_UNIFIED_ID_CACHE)) + ucache_bsize = icache_bsize = dcache_bsize; /* reboot on panic */ panic_timeout = 180; diff --git a/arch/powerpc/kernel/signal.c b/arch/powerpc/kernel/signal.c new file mode 100644 index 00000000000..c434d6c4e4e --- /dev/null +++ b/arch/powerpc/kernel/signal.c @@ -0,0 +1,180 @@ +/* + * Common signal handling code for both 32 and 64 bits + * + * Copyright (c) 2007 Benjamin Herrenschmidt, IBM Coproration + * Extracted from signal_32.c and signal_64.c + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file README.legal in the main directory of + * this archive for more details. + */ + +#include <linux/ptrace.h> +#include <linux/signal.h> +#include <asm/uaccess.h> +#include <asm/unistd.h> + +#include "signal.h" + +/* + * Allocate space for the signal frame + */ +void __user * get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, + size_t frame_size) +{ + unsigned long oldsp, newsp; + + /* Default to using normal stack */ + oldsp = regs->gpr[1]; + + /* Check for alt stack */ + if ((ka->sa.sa_flags & SA_ONSTACK) && + current->sas_ss_size && !on_sig_stack(oldsp)) + oldsp = (current->sas_ss_sp + current->sas_ss_size); + + /* Get aligned frame */ + newsp = (oldsp - frame_size) & ~0xFUL; + + /* Check access */ + if (!access_ok(VERIFY_WRITE, (void __user *)newsp, oldsp - newsp)) + return NULL; + + return (void __user *)newsp; +} + + +/* + * Restore the user process's signal mask + */ +void restore_sigmask(sigset_t *set) +{ + sigdelsetmask(set, ~_BLOCKABLE); + spin_lock_irq(¤t->sighand->siglock); + current->blocked = *set; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); +} + +static void check_syscall_restart(struct pt_regs *regs, struct k_sigaction *ka, + int has_handler) +{ + unsigned long ret = regs->gpr[3]; + int restart = 1; + + /* syscall ? */ + if (TRAP(regs) != 0x0C00) + return; + + /* error signalled ? */ + if (!(regs->ccr & 0x10000000)) + return; + + switch (ret) { + case ERESTART_RESTARTBLOCK: + case ERESTARTNOHAND: + /* ERESTARTNOHAND means that the syscall should only be + * restarted if there was no handler for the signal, and since + * we only get here if there is a handler, we dont restart. + */ + restart = !has_handler; + break; + case ERESTARTSYS: + /* ERESTARTSYS means to restart the syscall if there is no + * handler or the handler was registered with SA_RESTART + */ + restart = !has_handler || (ka->sa.sa_flags & SA_RESTART) != 0; + break; + case ERESTARTNOINTR: + /* ERESTARTNOINTR means that the syscall should be + * called again after the signal handler returns. + */ + break; + default: + return; + } + if (restart) { + if (ret == ERESTART_RESTARTBLOCK) + regs->gpr[0] = __NR_restart_syscall; + else + regs->gpr[3] = regs->orig_gpr3; + regs->nip -= 4; + regs->result = 0; + } else { + regs->result = -EINTR; + regs->gpr[3] = EINTR; + regs->ccr |= 0x10000000; + } +} + +int do_signal(sigset_t *oldset, struct pt_regs *regs) +{ + siginfo_t info; + int signr; + struct k_sigaction ka; + int ret; + int is32 = is_32bit_task(); + + if (test_thread_flag(TIF_RESTORE_SIGMASK)) + oldset = ¤t->saved_sigmask; + else if (!oldset) + oldset = ¤t->blocked; + + signr = get_signal_to_deliver(&info, &ka, regs, NULL); + + /* Is there any syscall restart business here ? */ + check_syscall_restart(regs, &ka, signr > 0); + + if (signr <= 0) { + /* No signal to deliver -- put the saved sigmask back */ + if (test_thread_flag(TIF_RESTORE_SIGMASK)) { + clear_thread_flag(TIF_RESTORE_SIGMASK); + sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL); + } + return 0; /* no signals delivered */ + } + + /* + * Reenable the DABR before delivering the signal to + * user space. The DABR will have been cleared if it + * triggered inside the kernel. + */ + if (current->thread.dabr) + set_dabr(current->thread.dabr); + + if (is32) { + if (ka.sa.sa_flags & SA_SIGINFO) + ret = handle_rt_signal32(signr, &ka, &info, oldset, + regs); + else + ret = handle_signal32(signr, &ka, &info, oldset, + regs); + } else { + ret = handle_rt_signal64(signr, &ka, &info, oldset, regs); + } + + if (ret) { + spin_lock_irq(¤t->sighand->siglock); + sigorsets(¤t->blocked, ¤t->blocked, + &ka.sa.sa_mask); + if (!(ka.sa.sa_flags & SA_NODEFER)) + sigaddset(¤t->blocked, signr); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + /* + * A signal was successfully delivered; the saved sigmask is in + * its frame, and we can clear the TIF_RESTORE_SIGMASK flag. + */ + if (test_thread_flag(TIF_RESTORE_SIGMASK)) + clear_thread_flag(TIF_RESTORE_SIGMASK); + } + + return ret; +} + +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) +{ + return do_sigaltstack(uss, uoss, regs->gpr[1]); +} diff --git a/arch/powerpc/kernel/signal.h b/arch/powerpc/kernel/signal.h new file mode 100644 index 00000000000..77efb3d5465 --- /dev/null +++ b/arch/powerpc/kernel/signal.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2007 Benjamin Herrenschmidt, IBM Coproration + * Extracted from signal_32.c and signal_64.c + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file README.legal in the main directory of + * this archive for more details. + */ + +#ifndef _POWERPC_ARCH_SIGNAL_H +#define _POWERPC_ARCH_SIGNAL_H + +#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) + +extern void __user * get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, + size_t frame_size); +extern void restore_sigmask(sigset_t *set); + +extern int handle_signal32(unsigned long sig, struct k_sigaction *ka, + siginfo_t *info, sigset_t *oldset, + struct pt_regs *regs); + +extern int handle_rt_signal32(unsigned long sig, struct k_sigaction *ka, + siginfo_t *info, sigset_t *oldset, + struct pt_regs *regs); + + +#ifdef CONFIG_PPC64 + +static inline int is_32bit_task(void) +{ + return test_thread_flag(TIF_32BIT); +} + +extern int handle_rt_signal64(int signr, struct k_sigaction *ka, + siginfo_t *info, sigset_t *set, + struct pt_regs *regs); + +#else /* CONFIG_PPC64 */ + +static inline int is_32bit_task(void) +{ + return 1; +} + +static inline int handle_rt_signal64(int signr, struct k_sigaction *ka, + siginfo_t *info, sigset_t *set, + struct pt_regs *regs) +{ + return -EFAULT; +} + +#endif /* !defined(CONFIG_PPC64) */ + +#endif /* _POWERPC_ARCH_SIGNAL_H */ diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c index dd1dca5bfa8..590057e9e98 100644 --- a/arch/powerpc/kernel/signal_32.c +++ b/arch/powerpc/kernel/signal_32.c @@ -51,12 +51,11 @@ #include <asm/pgtable.h> #endif -#undef DEBUG_SIG +#include "signal.h" -#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) +#undef DEBUG_SIG #ifdef CONFIG_PPC64 -#define do_signal do_signal32 #define sys_sigsuspend compat_sys_sigsuspend #define sys_rt_sigsuspend compat_sys_rt_sigsuspend #define sys_rt_sigreturn compat_sys_rt_sigreturn @@ -231,8 +230,6 @@ static inline int restore_general_regs(struct pt_regs *regs, #endif /* CONFIG_PPC64 */ -int do_signal(sigset_t *oldset, struct pt_regs *regs); - /* * Atomically swap in the new signal mask, and wait for a signal. */ @@ -251,14 +248,6 @@ long sys_sigsuspend(old_sigset_t mask) return -ERESTARTNOHAND; } -#ifdef CONFIG_PPC32 -long sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss, int r5, - int r6, int r7, int r8, struct pt_regs *regs) -{ - return do_sigaltstack(uss, uoss, regs->gpr[1]); -} -#endif - long sys_sigaction(int sig, struct old_sigaction __user *act, struct old_sigaction __user *oact) { @@ -293,14 +282,17 @@ long sys_sigaction(int sig, struct old_sigaction __user *act, /* * When we have signals to deliver, we set up on the * user stack, going down from the original stack pointer: - * a sigregs struct + * an ABI gap of 56 words + * an mcontext struct * a sigcontext struct * a gap of __SIGNAL_FRAMESIZE bytes * - * Each of these things must be a multiple of 16 bytes in size. + * Each of these things must be a multiple of 16 bytes in size. The following + * structure represent all of this except the __SIGNAL_FRAMESIZE gap * */ -struct sigregs { +struct sigframe { + struct sigcontext sctx; /* the sigcontext */ struct mcontext mctx; /* all the register values */ /* * Programs using the rs6000/xcoff abi can save up to 19 gp @@ -703,44 +695,22 @@ int compat_sys_sigaltstack(u32 __new, u32 __old, int r5, } #endif /* CONFIG_PPC64 */ - -/* - * Restore the user process's signal mask - */ -#ifdef CONFIG_PPC64 -extern void restore_sigmask(sigset_t *set); -#else /* CONFIG_PPC64 */ -static void restore_sigmask(sigset_t *set) -{ - sigdelsetmask(set, ~_BLOCKABLE); - spin_lock_irq(¤t->sighand->siglock); - current->blocked = *set; - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); -} -#endif - /* * Set up a signal frame for a "real-time" signal handler * (one which gets siginfo). */ -static int handle_rt_signal(unsigned long sig, struct k_sigaction *ka, +int handle_rt_signal32(unsigned long sig, struct k_sigaction *ka, siginfo_t *info, sigset_t *oldset, - struct pt_regs *regs, unsigned long newsp) + struct pt_regs *regs) { struct rt_sigframe __user *rt_sf; struct mcontext __user *frame; - unsigned long origsp = newsp; + unsigned long newsp = 0; /* Set up Signal Frame */ /* Put a Real Time Context onto stack */ - newsp -= sizeof(*rt_sf); - rt_sf = (struct rt_sigframe __user *)newsp; - - /* create a stack frame for the caller of the handler */ - newsp -= __SIGNAL_FRAMESIZE + 16; - - if (!access_ok(VERIFY_WRITE, (void __user *)newsp, origsp - newsp)) + rt_sf = get_sigframe(ka, regs, sizeof(*rt_sf)); + if (unlikely(rt_sf == NULL)) goto badframe; /* Put the siginfo & fill in most of the ucontext */ @@ -770,8 +740,12 @@ static int handle_rt_signal(unsigned long sig, struct k_sigaction *ka, current->thread.fpscr.val = 0; /* turn off all fp exceptions */ + /* create a stack frame for the caller of the handler */ + newsp = ((unsigned long)rt_sf) - (__SIGNAL_FRAMESIZE + 16); if (put_user(regs->gpr[1], (u32 __user *)newsp)) goto badframe; + + /* Fill registers for signal handler */ regs->gpr[1] = newsp; regs->gpr[3] = sig; regs->gpr[4] = (unsigned long) &rt_sf->info; @@ -1015,27 +989,18 @@ int sys_debug_setcontext(struct ucontext __user *ctx, /* * OK, we're invoking a handler */ -static int handle_signal(unsigned long sig, struct k_sigaction *ka, - siginfo_t *info, sigset_t *oldset, struct pt_regs *regs, - unsigned long newsp) +int handle_signal32(unsigned long sig, struct k_sigaction *ka, + siginfo_t *info, sigset_t *oldset, struct pt_regs *regs) { struct sigcontext __user *sc; - struct sigregs __user *frame; - unsigned long origsp = newsp; + struct sigframe __user *frame; + unsigned long newsp = 0; /* Set up Signal Frame */ - newsp -= sizeof(struct sigregs); - frame = (struct sigregs __user *) newsp; - - /* Put a sigcontext on the stack */ - newsp -= sizeof(*sc); - sc = (struct sigcontext __user *) newsp; - - /* create a stack frame for the caller of the handler */ - newsp -= __SIGNAL_FRAMESIZE; - - if (!access_ok(VERIFY_WRITE, (void __user *) newsp, origsp - newsp)) + frame = get_sigframe(ka, regs, sizeof(*frame)); + if (unlikely(frame == NULL)) goto badframe; + sc = (struct sigcontext __user *) &frame->sctx; #if _NSIG != 64 #error "Please adjust handle_signal()" @@ -1047,7 +1012,7 @@ static int handle_signal(unsigned long sig, struct k_sigaction *ka, #else || __put_user(oldset->sig[1], &sc->_unused[3]) #endif - || __put_user(to_user_ptr(frame), &sc->regs) + || __put_user(to_user_ptr(&frame->mctx), &sc->regs) || __put_user(sig, &sc->signal)) goto badframe; @@ -1063,8 +1028,11 @@ static int handle_signal(unsigned long sig, struct k_sigaction *ka, current->thread.fpscr.val = 0; /* turn off all fp exceptions */ + /* create a stack frame for the caller of the handler */ + newsp = ((unsigned long)frame) - __SIGNAL_FRAMESIZE; if (put_user(regs->gpr[1], (u32 __user *)newsp)) goto badframe; + regs->gpr[1] = newsp; regs->gpr[3] = sig; regs->gpr[4] = (unsigned long) sc; @@ -1126,106 +1094,3 @@ badframe: force_sig(SIGSEGV, current); return 0; } - -/* - * Note that 'init' is a special process: it doesn't get signals it doesn't - * want to handle. Thus you cannot kill init even with a SIGKILL even by - * mistake. - */ -int do_signal(sigset_t *oldset, struct pt_regs *regs) -{ - siginfo_t info; - struct k_sigaction ka; - unsigned int newsp; - int signr, ret; - -#ifdef CONFIG_PPC32 - if (try_to_freeze()) { - signr = 0; - if (!signal_pending(current)) - goto no_signal; - } -#endif - - if (test_thread_flag(TIF_RESTORE_SIGMASK)) - oldset = ¤t->saved_sigmask; - else if (!oldset) - oldset = ¤t->blocked; - - signr = get_signal_to_deliver(&info, &ka, regs, NULL); -#ifdef CONFIG_PPC32 -no_signal: -#endif - if (TRAP(regs) == 0x0C00 /* System Call! */ - && regs->ccr & 0x10000000 /* error signalled */ - && ((ret = regs->gpr[3]) == ERESTARTSYS - || ret == ERESTARTNOHAND || ret == ERESTARTNOINTR - || ret == ERESTART_RESTARTBLOCK)) { - - if (signr > 0 - && (ret == ERESTARTNOHAND || ret == ERESTART_RESTARTBLOCK - || (ret == ERESTARTSYS - && !(ka.sa.sa_flags & SA_RESTART)))) { - /* make the system call return an EINTR error */ - regs->result = -EINTR; - regs->gpr[3] = EINTR; - /* note that the cr0.SO bit is already set */ - } else { - regs->nip -= 4; /* Back up & retry system call */ - regs->result = 0; - regs->trap = 0; - if (ret == ERESTART_RESTARTBLOCK) - regs->gpr[0] = __NR_restart_syscall; - else - regs->gpr[3] = regs->orig_gpr3; - } - } - - if (signr == 0) { - /* No signal to deliver -- put the saved sigmask back */ - if (test_thread_flag(TIF_RESTORE_SIGMASK)) { - clear_thread_flag(TIF_RESTORE_SIGMASK); - sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL); - } - return 0; /* no signals delivered */ - } - - if ((ka.sa.sa_flags & SA_ONSTACK) && current->sas_ss_size - && !on_sig_stack(regs->gpr[1])) - newsp = current->sas_ss_sp + current->sas_ss_size; - else - newsp = regs->gpr[1]; - newsp &= ~0xfUL; - -#ifdef CONFIG_PPC64 - /* - * Reenable the DABR before delivering the signal to - * user space. The DABR will have been cleared if it - * triggered inside the kernel. - */ - if (current->thread.dabr) - set_dabr(current->thread.dabr); -#endif - - /* Whee! Actually deliver the signal. */ - if (ka.sa.sa_flags & SA_SIGINFO) - ret = handle_rt_signal(signr, &ka, &info, oldset, regs, newsp); - else - ret = handle_signal(signr, &ka, &info, oldset, regs, newsp); - - if (ret) { - spin_lock_irq(¤t->sighand->siglock); - sigorsets(¤t->blocked, ¤t->blocked, - &ka.sa.sa_mask); - if (!(ka.sa.sa_flags & SA_NODEFER)) - sigaddset(¤t->blocked, signr); - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); - /* A signal was successfully delivered; the saved sigmask is in - its frame, and we can clear the TIF_RESTORE_SIGMASK flag */ - if (test_thread_flag(TIF_RESTORE_SIGMASK)) - clear_thread_flag(TIF_RESTORE_SIGMASK); - } - - return ret; -} diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c index b27e26852fd..de895e6d8c6 100644 --- a/arch/powerpc/kernel/signal_64.c +++ b/arch/powerpc/kernel/signal_64.c @@ -34,9 +34,9 @@ #include <asm/syscalls.h> #include <asm/vdso.h> -#define DEBUG_SIG 0 +#include "signal.h" -#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) +#define DEBUG_SIG 0 #define GP_REGS_SIZE min(sizeof(elf_gregset_t), sizeof(struct pt_regs)) #define FP_REGS_SIZE sizeof(elf_fpregset_t) @@ -64,14 +64,6 @@ struct rt_sigframe { char abigap[288]; } __attribute__ ((aligned (16))); -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) -{ - return do_sigaltstack(uss, uoss, regs->gpr[1]); -} - - /* * Set up the sigcontext for the signal frame. */ @@ -208,25 +200,6 @@ static long restore_sigcontext(struct pt_regs *regs, sigset_t *set, int sig, } /* - * Allocate space for the signal frame - */ -static inline void __user * get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, - size_t frame_size) -{ - unsigned long newsp; - - /* Default to using normal stack */ - newsp = regs->gpr[1]; - - if ((ka->sa.sa_flags & SA_ONSTACK) && current->sas_ss_size) { - if (! on_sig_stack(regs->gpr[1])) - newsp = (current->sas_ss_sp + current->sas_ss_size); - } - - return (void __user *)((newsp - frame_size) & -16ul); -} - -/* * Setup the trampoline code on the stack */ static long setup_trampoline(unsigned int syscall, unsigned int __user *tramp) @@ -253,19 +226,6 @@ static long setup_trampoline(unsigned int syscall, unsigned int __user *tramp) } /* - * Restore the user process's signal mask (also used by signal32.c) - */ -void restore_sigmask(sigset_t *set) -{ - sigdelsetmask(set, ~_BLOCKABLE); - spin_lock_irq(¤t->sighand->siglock); - current->blocked = *set; - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); -} - - -/* * Handle {get,set,swap}_context operations */ int sys_swapcontext(struct ucontext __user *old_ctx, @@ -359,7 +319,7 @@ badframe: return 0; } -static int setup_rt_frame(int signr, struct k_sigaction *ka, siginfo_t *info, +int handle_rt_signal64(int signr, struct k_sigaction *ka, siginfo_t *info, sigset_t *set, struct pt_regs *regs) { /* Handler is *really* a pointer to the function descriptor for @@ -373,8 +333,7 @@ static int setup_rt_frame(int signr, struct k_sigaction *ka, siginfo_t *info, long err = 0; frame = get_sigframe(ka, regs, sizeof(*frame)); - - if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) + if (unlikely(frame == NULL)) goto badframe; err |= __put_user(&frame->info, &frame->pinfo); @@ -411,7 +370,7 @@ static int setup_rt_frame(int signr, struct k_sigaction *ka, siginfo_t *info, funct_desc_ptr = (func_descr_t __user *) ka->sa.sa_handler; /* Allocate a dummy caller frame for the signal handler. */ - newsp = (unsigned long)frame - __SIGNAL_FRAMESIZE; + newsp = ((unsigned long)frame) - __SIGNAL_FRAMESIZE; err |= put_user(regs->gpr[1], (unsigned long __user *)newsp); /* Set up "regs" so we "return" to the signal handler. */ @@ -442,134 +401,3 @@ badframe: force_sigsegv(signr, current); return 0; } - - -/* - * OK, we're invoking a handler - */ -static int handle_signal(unsigned long sig, struct k_sigaction *ka, - siginfo_t *info, sigset_t *oldset, struct pt_regs *regs) -{ - int ret; - - /* Set up Signal Frame */ - ret = setup_rt_frame(sig, ka, info, oldset, regs); - - if (ret) { - spin_lock_irq(¤t->sighand->siglock); - sigorsets(¤t->blocked, ¤t->blocked, &ka->sa.sa_mask); - if (!(ka->sa.sa_flags & SA_NODEFER)) - sigaddset(¤t->blocked,sig); - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); - } - - return ret; -} - -static inline void syscall_restart(struct pt_regs *regs, struct k_sigaction *ka) -{ - switch ((int)regs->result) { - case -ERESTART_RESTARTBLOCK: - case -ERESTARTNOHAND: - /* ERESTARTNOHAND means that the syscall should only be - * restarted if there was no handler for the signal, and since - * we only get here if there is a handler, we dont restart. - */ - regs->result = -EINTR; - regs->gpr[3] = EINTR; - regs->ccr |= 0x10000000; - break; - case -ERESTARTSYS: - /* ERESTARTSYS means to restart the syscall if there is no - * handler or the handler was registered with SA_RESTART - */ - if (!(ka->sa.sa_flags & SA_RESTART)) { - regs->result = -EINTR; - regs->gpr[3] = EINTR; - regs->ccr |= 0x10000000; - break; - } - /* fallthrough */ - case -ERESTARTNOINTR: - /* ERESTARTNOINTR means that the syscall should be - * called again after the signal handler returns. - */ - regs->gpr[3] = regs->orig_gpr3; - regs->nip -= 4; - regs->result = 0; - break; - } -} - -/* - * Note that 'init' is a special process: it doesn't get signals it doesn't - * want to handle. Thus you cannot kill init even with a SIGKILL even by - * mistake. - */ -int do_signal(sigset_t *oldset, struct pt_regs *regs) -{ - siginfo_t info; - int signr; - struct k_sigaction ka; - - /* - * If the current thread is 32 bit - invoke the - * 32 bit signal handling code - */ - if (test_thread_flag(TIF_32BIT)) - return do_signal32(oldset, regs); - - if (test_thread_flag(TIF_RESTORE_SIGMASK)) - oldset = ¤t->saved_sigmask; - else if (!oldset) - oldset = ¤t->blocked; - - signr = get_signal_to_deliver(&info, &ka, regs, NULL); - if (signr > 0) { - int ret; - - /* Whee! Actually deliver the signal. */ - if (TRAP(regs) == 0x0C00) - syscall_restart(regs, &ka); - - /* - * Reenable the DABR before delivering the signal to - * user space. The DABR will have been cleared if it - * triggered inside the kernel. - */ - if (current->thread.dabr) - set_dabr(current->thread.dabr); - - ret = handle_signal(signr, &ka, &info, oldset, regs); - - /* If a signal was successfully delivered, the saved sigmask is in - its frame, and we can clear the TIF_RESTORE_SIGMASK flag */ - if (ret && test_thread_flag(TIF_RESTORE_SIGMASK)) - clear_thread_flag(TIF_RESTORE_SIGMASK); - - return ret; - } - - if (TRAP(regs) == 0x0C00) { /* System Call! */ - if ((int)regs->result == -ERESTARTNOHAND || - (int)regs->result == -ERESTARTSYS || - (int)regs->result == -ERESTARTNOINTR) { - regs->gpr[3] = regs->orig_gpr3; - regs->nip -= 4; /* Back up & retry system call */ - regs->result = 0; - } else if ((int)regs->result == -ERESTART_RESTARTBLOCK) { - regs->gpr[0] = __NR_restart_syscall; - regs->nip -= 4; - regs->result = 0; - } - } - /* No signal to deliver -- put the saved sigmask back */ - if (test_thread_flag(TIF_RESTORE_SIGMASK)) { - clear_thread_flag(TIF_RESTORE_SIGMASK); - sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL); - } - - return 0; -} -EXPORT_SYMBOL(do_signal); diff --git a/arch/powerpc/kernel/sys_ppc32.c b/arch/powerpc/kernel/sys_ppc32.c index b42cbf1e2d7..bd85b5fd08c 100644 --- a/arch/powerpc/kernel/sys_ppc32.c +++ b/arch/powerpc/kernel/sys_ppc32.c @@ -773,6 +773,13 @@ asmlinkage int compat_sys_truncate64(const char __user * path, u32 reg4, return sys_truncate(path, (high << 32) | low); } +asmlinkage long compat_sys_fallocate(int fd, int mode, u32 offhi, u32 offlo, + u32 lenhi, u32 lenlo) +{ + return sys_fallocate(fd, mode, ((loff_t)offhi << 32) | offlo, + ((loff_t)lenhi << 32) | lenlo); +} + asmlinkage int compat_sys_ftruncate64(unsigned int fd, u32 reg4, unsigned long high, unsigned long low) { diff --git a/arch/powerpc/kernel/sysfs.c b/arch/powerpc/kernel/sysfs.c index 68991c2d4a1..55d29ed4b7a 100644 --- a/arch/powerpc/kernel/sysfs.c +++ b/arch/powerpc/kernel/sysfs.c @@ -442,12 +442,14 @@ int sysfs_add_device_to_node(struct sys_device *dev, int nid) return sysfs_create_link(&node->sysdev.kobj, &dev->kobj, kobject_name(&dev->kobj)); } +EXPORT_SYMBOL_GPL(sysfs_add_device_to_node); void sysfs_remove_device_from_node(struct sys_device *dev, int nid) { struct node *node = &node_devices[nid]; sysfs_remove_link(&node->sysdev.kobj, kobject_name(&dev->kobj)); } +EXPORT_SYMBOL_GPL(sysfs_remove_device_from_node); #else static void register_nodes(void) @@ -457,9 +459,6 @@ static void register_nodes(void) #endif -EXPORT_SYMBOL_GPL(sysfs_add_device_to_node); -EXPORT_SYMBOL_GPL(sysfs_remove_device_from_node); - /* Only valid if CPU is present. */ static ssize_t show_physical_id(struct sys_device *dev, char *buf) { diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c index 2c8564d54e4..e5df167f782 100644 --- a/arch/powerpc/kernel/time.c +++ b/arch/powerpc/kernel/time.c @@ -77,9 +77,8 @@ /* keep track of when we need to update the rtc */ time_t last_rtc_update; #ifdef CONFIG_PPC_ISERIES -unsigned long iSeries_recal_titan = 0; -unsigned long iSeries_recal_tb = 0; -static unsigned long first_settimeofday = 1; +static unsigned long __initdata iSeries_recal_titan; +static signed long __initdata iSeries_recal_tb; #endif /* The decrementer counts down by 128 every 128ns on a 601. */ @@ -113,8 +112,9 @@ u64 ticklen_to_xs; /* 0.64 fraction */ DEFINE_SPINLOCK(rtc_lock); EXPORT_SYMBOL_GPL(rtc_lock); -u64 tb_to_ns_scale; -unsigned tb_to_ns_shift; +static u64 tb_to_ns_scale __read_mostly; +static unsigned tb_to_ns_shift __read_mostly; +static unsigned long boot_tb __read_mostly; struct gettimeofday_struct do_gtod; @@ -214,7 +214,6 @@ static void account_process_time(struct pt_regs *regs) run_posix_cpu_timers(current); } -#ifdef CONFIG_PPC_SPLPAR /* * Stuff for accounting stolen time. */ @@ -222,19 +221,28 @@ struct cpu_purr_data { int initialized; /* thread is running */ u64 tb; /* last TB value read */ u64 purr; /* last PURR value read */ - spinlock_t lock; }; +/* + * Each entry in the cpu_purr_data array is manipulated only by its + * "owner" cpu -- usually in the timer interrupt but also occasionally + * in process context for cpu online. As long as cpus do not touch + * each others' cpu_purr_data, disabling local interrupts is + * sufficient to serialize accesses. + */ static DEFINE_PER_CPU(struct cpu_purr_data, cpu_purr_data); static void snapshot_tb_and_purr(void *data) { + unsigned long flags; struct cpu_purr_data *p = &__get_cpu_var(cpu_purr_data); + local_irq_save(flags); p->tb = mftb(); p->purr = mfspr(SPRN_PURR); wmb(); p->initialized = 1; + local_irq_restore(flags); } /* @@ -242,15 +250,14 @@ static void snapshot_tb_and_purr(void *data) */ void snapshot_timebases(void) { - int cpu; - if (!cpu_has_feature(CPU_FTR_PURR)) return; - for_each_possible_cpu(cpu) - spin_lock_init(&per_cpu(cpu_purr_data, cpu).lock); on_each_cpu(snapshot_tb_and_purr, NULL, 0, 1); } +/* + * Must be called with interrupts disabled. + */ void calculate_steal_time(void) { u64 tb, purr; @@ -262,7 +269,6 @@ void calculate_steal_time(void) pme = &per_cpu(cpu_purr_data, smp_processor_id()); if (!pme->initialized) return; /* this can happen in early boot */ - spin_lock(&pme->lock); tb = mftb(); purr = mfspr(SPRN_PURR); stolen = (tb - pme->tb) - (purr - pme->purr); @@ -270,9 +276,9 @@ void calculate_steal_time(void) account_steal_time(current, stolen); pme->tb = tb; pme->purr = purr; - spin_unlock(&pme->lock); } +#ifdef CONFIG_PPC_SPLPAR /* * Must be called before the cpu is added to the online map when * a cpu is being brought up at runtime. @@ -284,12 +290,12 @@ static void snapshot_purr(void) if (!cpu_has_feature(CPU_FTR_PURR)) return; + local_irq_save(flags); pme = &per_cpu(cpu_purr_data, smp_processor_id()); - spin_lock_irqsave(&pme->lock, flags); pme->tb = mftb(); pme->purr = mfspr(SPRN_PURR); pme->initialized = 1; - spin_unlock_irqrestore(&pme->lock, flags); + local_irq_restore(flags); } #endif /* CONFIG_PPC_SPLPAR */ @@ -550,10 +556,15 @@ EXPORT_SYMBOL(profile_pc); * returned by the service processor for the timebase frequency. */ -static void iSeries_tb_recal(void) +static int __init iSeries_tb_recal(void) { struct div_result divres; unsigned long titan, tb; + + /* Make sure we only run on iSeries */ + if (!firmware_has_feature(FW_FEATURE_ISERIES)) + return -ENODEV; + tb = get_tb(); titan = HvCallXm_loadTod(); if ( iSeries_recal_titan ) { @@ -594,8 +605,18 @@ static void iSeries_tb_recal(void) } iSeries_recal_titan = titan; iSeries_recal_tb = tb; + + return 0; } -#endif +late_initcall(iSeries_tb_recal); + +/* Called from platform early init */ +void __init iSeries_time_init_early(void) +{ + iSeries_recal_tb = get_tb(); + iSeries_recal_titan = HvCallXm_loadTod(); +} +#endif /* CONFIG_PPC_ISERIES */ /* * For iSeries shared processors, we have to let the hypervisor @@ -735,7 +756,7 @@ unsigned long long sched_clock(void) { if (__USE_RTC()) return get_rtc(); - return mulhdu(get_tb(), tb_to_ns_scale) << tb_to_ns_shift; + return mulhdu(get_tb() - boot_tb, tb_to_ns_scale) << tb_to_ns_shift; } int do_settimeofday(struct timespec *tv) @@ -759,12 +780,6 @@ int do_settimeofday(struct timespec *tv) * to the RTC again, or write to the RTC but then they don't call * settimeofday to perform this operation. */ -#ifdef CONFIG_PPC_ISERIES - if (firmware_has_feature(FW_FEATURE_ISERIES) && first_settimeofday) { - iSeries_tb_recal(); - first_settimeofday = 0; - } -#endif /* Make userspace gettimeofday spin until we're done. */ ++vdso_data->tb_update_count; @@ -960,6 +975,8 @@ void __init time_init(void) } tb_to_ns_scale = scale; tb_to_ns_shift = shift; + /* Save the current timebase to pretty up CONFIG_PRINTK_TIME */ + boot_tb = get_tb(); tm = get_boot_time(); diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index 3b8427e6283..2bb1cb91178 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c @@ -149,6 +149,7 @@ int die(const char *str, struct pt_regs *regs, long err) bust_spinlocks(0); die.lock_owner = -1; + add_taint(TAINT_DIE); spin_unlock_irqrestore(&die.lock, flags); if (kexec_should_crash(current) || diff --git a/arch/powerpc/kernel/vdso.c b/arch/powerpc/kernel/vdso.c index 4245579edb4..cef01e4e898 100644 --- a/arch/powerpc/kernel/vdso.c +++ b/arch/powerpc/kernel/vdso.c @@ -670,7 +670,7 @@ static int __init vdso_init(void) /* * Fill up the "systemcfg" stuff for backward compatiblity */ - strcpy(vdso_data->eye_catcher, "SYSTEMCFG:PPC64"); + strcpy((char *)vdso_data->eye_catcher, "SYSTEMCFG:PPC64"); vdso_data->version.major = SYSTEMCFG_MAJOR; vdso_data->version.minor = SYSTEMCFG_MINOR; vdso_data->processor = mfspr(SPRN_PVR); diff --git a/arch/powerpc/kernel/vmlinux.lds.S b/arch/powerpc/kernel/vmlinux.lds.S index 24ff7d98311..0c458556399 100644 --- a/arch/powerpc/kernel/vmlinux.lds.S +++ b/arch/powerpc/kernel/vmlinux.lds.S @@ -7,6 +7,7 @@ #define PROVIDE32(x) PROVIDE(x) #endif #include <asm-generic/vmlinux.lds.h> +#include <asm/cache.h> ENTRY(_stext) @@ -62,6 +63,8 @@ SECTIONS __stop___ex_table = .; } + NOTES + BUG_TABLE /* @@ -143,6 +146,7 @@ SECTIONS .data.percpu : { __per_cpu_start = .; *(.data.percpu) + *(.data.percpu.shared_aligned) __per_cpu_end = .; } @@ -213,6 +217,11 @@ SECTIONS *(.data.cacheline_aligned) } + . = ALIGN(L1_CACHE_BYTES); + .data.read_mostly : { + *(.data.read_mostly) + } + . = ALIGN(PAGE_SIZE); __data_nosave : { __nosave_begin = .; diff --git a/arch/powerpc/mm/44x_mmu.c b/arch/powerpc/mm/44x_mmu.c index ca4dcb07a93..c3df5047653 100644 --- a/arch/powerpc/mm/44x_mmu.c +++ b/arch/powerpc/mm/44x_mmu.c @@ -12,7 +12,6 @@ * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au) * and Cort Dougan (PReP) (cort@cs.nmt.edu) * Copyright (C) 1996 Paul Mackerras - * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). * * Derived from "arch/i386/mm/init.c" * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds diff --git a/arch/powerpc/mm/4xx_mmu.c b/arch/powerpc/mm/4xx_mmu.c index 838e09db71d..7ff2609b64d 100644 --- a/arch/powerpc/mm/4xx_mmu.c +++ b/arch/powerpc/mm/4xx_mmu.c @@ -9,7 +9,6 @@ * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au) * and Cort Dougan (PReP) (cort@cs.nmt.edu) * Copyright (C) 1996 Paul Mackerras - * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). * * Derived from "arch/i386/mm/init.c" * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds diff --git a/arch/powerpc/mm/Makefile b/arch/powerpc/mm/Makefile index 4f839c6a976..7e4d27ad3de 100644 --- a/arch/powerpc/mm/Makefile +++ b/arch/powerpc/mm/Makefile @@ -11,8 +11,7 @@ obj-$(CONFIG_PPC32) += init_32.o pgtable_32.o mmu_context_32.o hash-$(CONFIG_PPC_NATIVE) := hash_native_64.o obj-$(CONFIG_PPC64) += init_64.o pgtable_64.o mmu_context_64.o \ hash_utils_64.o hash_low_64.o tlb_64.o \ - slb_low.o slb.o stab.o mmap.o imalloc.o \ - $(hash-y) + slb_low.o slb.o stab.o mmap.o $(hash-y) obj-$(CONFIG_PPC_STD_MMU_32) += ppc_mmu_32.o hash_low_32.o tlb_32.o obj-$(CONFIG_40x) += 4xx_mmu.o obj-$(CONFIG_44x) += 44x_mmu.o diff --git a/arch/powerpc/mm/fault.c b/arch/powerpc/mm/fault.c index 115b25f50bf..3767211b3d0 100644 --- a/arch/powerpc/mm/fault.c +++ b/arch/powerpc/mm/fault.c @@ -145,7 +145,7 @@ int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address, struct mm_struct *mm = current->mm; siginfo_t info; int code = SEGV_MAPERR; - int is_write = 0; + int is_write = 0, ret; int trap = TRAP(regs); int is_exec = trap == 0x400; @@ -330,22 +330,18 @@ good_area: * the fault. */ survive: - switch (handle_mm_fault(mm, vma, address, is_write)) { - - case VM_FAULT_MINOR: - current->min_flt++; - break; - case VM_FAULT_MAJOR: - current->maj_flt++; - break; - case VM_FAULT_SIGBUS: - goto do_sigbus; - case VM_FAULT_OOM: - goto out_of_memory; - default: + ret = handle_mm_fault(mm, vma, address, is_write); + if (unlikely(ret & VM_FAULT_ERROR)) { + if (ret & VM_FAULT_OOM) + goto out_of_memory; + else if (ret & VM_FAULT_SIGBUS) + goto do_sigbus; BUG(); } - + if (ret & VM_FAULT_MAJOR) + current->maj_flt++; + else + current->min_flt++; up_read(&mm->mmap_sem); return 0; @@ -380,7 +376,7 @@ out_of_memory: } printk("VM: killing process %s\n", current->comm); if (user_mode(regs)) - do_exit(SIGKILL); + do_group_exit(SIGKILL); return SIGKILL; do_sigbus: diff --git a/arch/powerpc/mm/fsl_booke_mmu.c b/arch/powerpc/mm/fsl_booke_mmu.c index 123da03ab11..afab247d472 100644 --- a/arch/powerpc/mm/fsl_booke_mmu.c +++ b/arch/powerpc/mm/fsl_booke_mmu.c @@ -14,7 +14,6 @@ * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au) * and Cort Dougan (PReP) (cort@cs.nmt.edu) * Copyright (C) 1996 Paul Mackerras - * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). * * Derived from "arch/i386/mm/init.c" * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds diff --git a/arch/powerpc/mm/hash_native_64.c b/arch/powerpc/mm/hash_native_64.c index 4a20d890e2f..6ba9b47e55a 100644 --- a/arch/powerpc/mm/hash_native_64.c +++ b/arch/powerpc/mm/hash_native_64.c @@ -104,7 +104,7 @@ static inline void tlbie(unsigned long va, int psize, int local) spin_unlock(&native_tlbie_lock); } -static inline void native_lock_hpte(hpte_t *hptep) +static inline void native_lock_hpte(struct hash_pte *hptep) { unsigned long *word = &hptep->v; @@ -116,7 +116,7 @@ static inline void native_lock_hpte(hpte_t *hptep) } } -static inline void native_unlock_hpte(hpte_t *hptep) +static inline void native_unlock_hpte(struct hash_pte *hptep) { unsigned long *word = &hptep->v; @@ -128,7 +128,7 @@ static long native_hpte_insert(unsigned long hpte_group, unsigned long va, unsigned long pa, unsigned long rflags, unsigned long vflags, int psize) { - hpte_t *hptep = htab_address + hpte_group; + struct hash_pte *hptep = htab_address + hpte_group; unsigned long hpte_v, hpte_r; int i; @@ -163,7 +163,7 @@ static long native_hpte_insert(unsigned long hpte_group, unsigned long va, hptep->r = hpte_r; /* Guarantee the second dword is visible before the valid bit */ - __asm__ __volatile__ ("eieio" : : : "memory"); + eieio(); /* * Now set the first dword including the valid bit * NOTE: this also unlocks the hpte @@ -177,7 +177,7 @@ static long native_hpte_insert(unsigned long hpte_group, unsigned long va, static long native_hpte_remove(unsigned long hpte_group) { - hpte_t *hptep; + struct hash_pte *hptep; int i; int slot_offset; unsigned long hpte_v; @@ -217,7 +217,7 @@ static long native_hpte_remove(unsigned long hpte_group) static long native_hpte_updatepp(unsigned long slot, unsigned long newpp, unsigned long va, int psize, int local) { - hpte_t *hptep = htab_address + slot; + struct hash_pte *hptep = htab_address + slot; unsigned long hpte_v, want_v; int ret = 0; @@ -233,15 +233,14 @@ static long native_hpte_updatepp(unsigned long slot, unsigned long newpp, /* Even if we miss, we need to invalidate the TLB */ if (!HPTE_V_COMPARE(hpte_v, want_v) || !(hpte_v & HPTE_V_VALID)) { DBG_LOW(" -> miss\n"); - native_unlock_hpte(hptep); ret = -1; } else { DBG_LOW(" -> hit\n"); /* Update the HPTE */ hptep->r = (hptep->r & ~(HPTE_R_PP | HPTE_R_N)) | (newpp & (HPTE_R_PP | HPTE_R_N | HPTE_R_C)); - native_unlock_hpte(hptep); } + native_unlock_hpte(hptep); /* Ensure it is out of the tlb too. */ tlbie(va, psize, local); @@ -251,7 +250,7 @@ static long native_hpte_updatepp(unsigned long slot, unsigned long newpp, static long native_hpte_find(unsigned long va, int psize) { - hpte_t *hptep; + struct hash_pte *hptep; unsigned long hash; unsigned long i, j; long slot; @@ -294,7 +293,7 @@ static void native_hpte_updateboltedpp(unsigned long newpp, unsigned long ea, { unsigned long vsid, va; long slot; - hpte_t *hptep; + struct hash_pte *hptep; vsid = get_kernel_vsid(ea); va = (vsid << 28) | (ea & 0x0fffffff); @@ -315,7 +314,7 @@ static void native_hpte_updateboltedpp(unsigned long newpp, unsigned long ea, static void native_hpte_invalidate(unsigned long slot, unsigned long va, int psize, int local) { - hpte_t *hptep = htab_address + slot; + struct hash_pte *hptep = htab_address + slot; unsigned long hpte_v; unsigned long want_v; unsigned long flags; @@ -345,7 +344,7 @@ static void native_hpte_invalidate(unsigned long slot, unsigned long va, #define LP_BITS 8 #define LP_MASK(i) ((0xFF >> (i)) << LP_SHIFT) -static void hpte_decode(hpte_t *hpte, unsigned long slot, +static void hpte_decode(struct hash_pte *hpte, unsigned long slot, int *psize, unsigned long *va) { unsigned long hpte_r = hpte->r; @@ -415,7 +414,7 @@ static void hpte_decode(hpte_t *hpte, unsigned long slot, static void native_hpte_clear(void) { unsigned long slot, slots, flags; - hpte_t *hptep = htab_address; + struct hash_pte *hptep = htab_address; unsigned long hpte_v, va; unsigned long pteg_count; int psize; @@ -462,7 +461,7 @@ static void native_hpte_clear(void) static void native_flush_hash_range(unsigned long number, int local) { unsigned long va, hash, index, hidx, shift, slot; - hpte_t *hptep; + struct hash_pte *hptep; unsigned long hpte_v; unsigned long want_v; unsigned long flags; diff --git a/arch/powerpc/mm/hash_utils_64.c b/arch/powerpc/mm/hash_utils_64.c index 4f2f4534a9d..2ce9491b48d 100644 --- a/arch/powerpc/mm/hash_utils_64.c +++ b/arch/powerpc/mm/hash_utils_64.c @@ -87,7 +87,7 @@ extern unsigned long dart_tablebase; static unsigned long _SDR1; struct mmu_psize_def mmu_psize_defs[MMU_PAGE_COUNT]; -hpte_t *htab_address; +struct hash_pte *htab_address; unsigned long htab_size_bytes; unsigned long htab_hash_mask; int mmu_linear_psize = MMU_PAGE_4K; diff --git a/arch/powerpc/mm/imalloc.c b/arch/powerpc/mm/imalloc.c deleted file mode 100644 index c831815c31f..00000000000 --- a/arch/powerpc/mm/imalloc.c +++ /dev/null @@ -1,313 +0,0 @@ -/* - * c 2001 PPC 64 Team, IBM 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; either version - * 2 of the License, or (at your option) any later version. - */ - -#include <linux/slab.h> -#include <linux/vmalloc.h> - -#include <asm/uaccess.h> -#include <asm/pgalloc.h> -#include <asm/pgtable.h> -#include <linux/mutex.h> -#include <asm/cacheflush.h> - -#include "mmu_decl.h" - -static DEFINE_MUTEX(imlist_mutex); -struct vm_struct * imlist = NULL; - -static int get_free_im_addr(unsigned long size, unsigned long *im_addr) -{ - unsigned long addr; - struct vm_struct **p, *tmp; - - addr = ioremap_bot; - for (p = &imlist; (tmp = *p) ; p = &tmp->next) { - if (size + addr < (unsigned long) tmp->addr) - break; - if ((unsigned long)tmp->addr >= ioremap_bot) - addr = tmp->size + (unsigned long) tmp->addr; - if (addr >= IMALLOC_END-size) - return 1; - } - *im_addr = addr; - - return 0; -} - -/* Return whether the region described by v_addr and size is a subset - * of the region described by parent - */ -static inline int im_region_is_subset(unsigned long v_addr, unsigned long size, - struct vm_struct *parent) -{ - return (int) (v_addr >= (unsigned long) parent->addr && - v_addr < (unsigned long) parent->addr + parent->size && - size < parent->size); -} - -/* Return whether the region described by v_addr and size is a superset - * of the region described by child - */ -static int im_region_is_superset(unsigned long v_addr, unsigned long size, - struct vm_struct *child) -{ - struct vm_struct parent; - - parent.addr = (void *) v_addr; - parent.size = size; - - return im_region_is_subset((unsigned long) child->addr, child->size, - &parent); -} - -/* Return whether the region described by v_addr and size overlaps - * the region described by vm. Overlapping regions meet the - * following conditions: - * 1) The regions share some part of the address space - * 2) The regions aren't identical - * 3) Neither region is a subset of the other - */ -static int im_region_overlaps(unsigned long v_addr, unsigned long size, - struct vm_struct *vm) -{ - if (im_region_is_superset(v_addr, size, vm)) - return 0; - - return (v_addr + size > (unsigned long) vm->addr + vm->size && - v_addr < (unsigned long) vm->addr + vm->size) || - (v_addr < (unsigned long) vm->addr && - v_addr + size > (unsigned long) vm->addr); -} - -/* Determine imalloc status of region described by v_addr and size. - * Can return one of the following: - * IM_REGION_UNUSED - Entire region is unallocated in imalloc space. - * IM_REGION_SUBSET - Region is a subset of a region that is already - * allocated in imalloc space. - * vm will be assigned to a ptr to the parent region. - * IM_REGION_EXISTS - Exact region already allocated in imalloc space. - * vm will be assigned to a ptr to the existing imlist - * member. - * IM_REGION_OVERLAPS - Region overlaps an allocated region in imalloc space. - * IM_REGION_SUPERSET - Region is a superset of a region that is already - * allocated in imalloc space. - */ -static int im_region_status(unsigned long v_addr, unsigned long size, - struct vm_struct **vm) -{ - struct vm_struct *tmp; - - for (tmp = imlist; tmp; tmp = tmp->next) - if (v_addr < (unsigned long) tmp->addr + tmp->size) - break; - - *vm = NULL; - if (tmp) { - if (im_region_overlaps(v_addr, size, tmp)) - return IM_REGION_OVERLAP; - - *vm = tmp; - if (im_region_is_subset(v_addr, size, tmp)) { - /* Return with tmp pointing to superset */ - return IM_REGION_SUBSET; - } - if (im_region_is_superset(v_addr, size, tmp)) { - /* Return with tmp pointing to first subset */ - return IM_REGION_SUPERSET; - } - else if (v_addr == (unsigned long) tmp->addr && - size == tmp->size) { - /* Return with tmp pointing to exact region */ - return IM_REGION_EXISTS; - } - } - - return IM_REGION_UNUSED; -} - -static struct vm_struct * split_im_region(unsigned long v_addr, - unsigned long size, struct vm_struct *parent) -{ - struct vm_struct *vm1 = NULL; - struct vm_struct *vm2 = NULL; - struct vm_struct *new_vm = NULL; - - vm1 = kmalloc(sizeof(*vm1), GFP_KERNEL); - if (vm1 == NULL) { - printk(KERN_ERR "%s() out of memory\n", __FUNCTION__); - return NULL; - } - - if (v_addr == (unsigned long) parent->addr) { - /* Use existing parent vm_struct to represent child, allocate - * new one for the remainder of parent range - */ - vm1->size = parent->size - size; - vm1->addr = (void *) (v_addr + size); - vm1->next = parent->next; - - parent->size = size; - parent->next = vm1; - new_vm = parent; - } else if (v_addr + size == (unsigned long) parent->addr + - parent->size) { - /* Allocate new vm_struct to represent child, use existing - * parent one for remainder of parent range - */ - vm1->size = size; - vm1->addr = (void *) v_addr; - vm1->next = parent->next; - new_vm = vm1; - - parent->size -= size; - parent->next = vm1; - } else { - /* Allocate two new vm_structs for the new child and - * uppermost remainder, and use existing parent one for the - * lower remainder of parent range - */ - vm2 = kmalloc(sizeof(*vm2), GFP_KERNEL); - if (vm2 == NULL) { - printk(KERN_ERR "%s() out of memory\n", __FUNCTION__); - kfree(vm1); - return NULL; - } - - vm1->size = size; - vm1->addr = (void *) v_addr; - vm1->next = vm2; - new_vm = vm1; - - vm2->size = ((unsigned long) parent->addr + parent->size) - - (v_addr + size); - vm2->addr = (void *) v_addr + size; - vm2->next = parent->next; - - parent->size = v_addr - (unsigned long) parent->addr; - parent->next = vm1; - } - - return new_vm; -} - -static struct vm_struct * __add_new_im_area(unsigned long req_addr, - unsigned long size) -{ - struct vm_struct **p, *tmp, *area; - - for (p = &imlist; (tmp = *p) ; p = &tmp->next) { - if (req_addr + size <= (unsigned long)tmp->addr) - break; - } - - area = kmalloc(sizeof(*area), GFP_KERNEL); - if (!area) - return NULL; - area->flags = 0; - area->addr = (void *)req_addr; - area->size = size; - area->next = *p; - *p = area; - - return area; -} - -static struct vm_struct * __im_get_area(unsigned long req_addr, - unsigned long size, - int criteria) -{ - struct vm_struct *tmp; - int status; - - status = im_region_status(req_addr, size, &tmp); - if ((criteria & status) == 0) { - return NULL; - } - - switch (status) { - case IM_REGION_UNUSED: - tmp = __add_new_im_area(req_addr, size); - break; - case IM_REGION_SUBSET: - tmp = split_im_region(req_addr, size, tmp); - break; - case IM_REGION_EXISTS: - /* Return requested region */ - break; - case IM_REGION_SUPERSET: - /* Return first existing subset of requested region */ - break; - default: - printk(KERN_ERR "%s() unexpected imalloc region status\n", - __FUNCTION__); - tmp = NULL; - } - - return tmp; -} - -struct vm_struct * im_get_free_area(unsigned long size) -{ - struct vm_struct *area; - unsigned long addr; - - mutex_lock(&imlist_mutex); - if (get_free_im_addr(size, &addr)) { - printk(KERN_ERR "%s() cannot obtain addr for size 0x%lx\n", - __FUNCTION__, size); - area = NULL; - goto next_im_done; - } - - area = __im_get_area(addr, size, IM_REGION_UNUSED); - if (area == NULL) { - printk(KERN_ERR - "%s() cannot obtain area for addr 0x%lx size 0x%lx\n", - __FUNCTION__, addr, size); - } -next_im_done: - mutex_unlock(&imlist_mutex); - return area; -} - -struct vm_struct * im_get_area(unsigned long v_addr, unsigned long size, - int criteria) -{ - struct vm_struct *area; - - mutex_lock(&imlist_mutex); - area = __im_get_area(v_addr, size, criteria); - mutex_unlock(&imlist_mutex); - return area; -} - -void im_free(void * addr) -{ - struct vm_struct **p, *tmp; - - if (!addr) - return; - if ((unsigned long) addr & ~PAGE_MASK) { - printk(KERN_ERR "Trying to %s bad address (%p)\n", __FUNCTION__, addr); - return; - } - mutex_lock(&imlist_mutex); - for (p = &imlist ; (tmp = *p) ; p = &tmp->next) { - if (tmp->addr == addr) { - *p = tmp->next; - unmap_vm_area(tmp); - kfree(tmp); - mutex_unlock(&imlist_mutex); - return; - } - } - mutex_unlock(&imlist_mutex); - printk(KERN_ERR "Trying to %s nonexistent area (%p)\n", __FUNCTION__, - addr); -} diff --git a/arch/powerpc/mm/init_32.c b/arch/powerpc/mm/init_32.c index 5fce6ccecb8..e1f5ded851f 100644 --- a/arch/powerpc/mm/init_32.c +++ b/arch/powerpc/mm/init_32.c @@ -5,7 +5,6 @@ * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au) * and Cort Dougan (PReP) (cort@cs.nmt.edu) * Copyright (C) 1996 Paul Mackerras - * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). * PPC44x/36-bit changes by Matt Porter (mporter@mvista.com) * * Derived from "arch/i386/mm/init.c" diff --git a/arch/powerpc/mm/init_64.c b/arch/powerpc/mm/init_64.c index 7312a265545..1d6edf724c8 100644 --- a/arch/powerpc/mm/init_64.c +++ b/arch/powerpc/mm/init_64.c @@ -5,7 +5,6 @@ * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au) * and Cort Dougan (PReP) (cort@cs.nmt.edu) * Copyright (C) 1996 Paul Mackerras - * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). * * Derived from "arch/i386/mm/init.c" * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds diff --git a/arch/powerpc/mm/mem.c b/arch/powerpc/mm/mem.c index 0266a94d83b..f0e7eedb1ba 100644 --- a/arch/powerpc/mm/mem.c +++ b/arch/powerpc/mm/mem.c @@ -5,7 +5,6 @@ * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au) * and Cort Dougan (PReP) (cort@cs.nmt.edu) * Copyright (C) 1996 Paul Mackerras - * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). * PPC44x/36-bit changes by Matt Porter (mporter@mvista.com) * * Derived from "arch/i386/mm/init.c" @@ -129,8 +128,6 @@ int __devinit arch_add_memory(int nid, u64 start, u64 size) zone = pgdata->node_zones; return __add_pages(zone, start_pfn, nr_pages); - - return 0; } /* diff --git a/arch/powerpc/mm/mmu_context_32.c b/arch/powerpc/mm/mmu_context_32.c index 792086b0100..cc32ba41d90 100644 --- a/arch/powerpc/mm/mmu_context_32.c +++ b/arch/powerpc/mm/mmu_context_32.c @@ -11,7 +11,6 @@ * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au) * and Cort Dougan (PReP) (cort@cs.nmt.edu) * Copyright (C) 1996 Paul Mackerras - * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). * * Derived from "arch/i386/mm/init.c" * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds diff --git a/arch/powerpc/mm/mmu_decl.h b/arch/powerpc/mm/mmu_decl.h index 2558c34eeda..c94a64fd3c0 100644 --- a/arch/powerpc/mm/mmu_decl.h +++ b/arch/powerpc/mm/mmu_decl.h @@ -8,7 +8,6 @@ * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au) * and Cort Dougan (PReP) (cort@cs.nmt.edu) * Copyright (C) 1996 Paul Mackerras - * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). * * Derived from "arch/i386/mm/init.c" * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds @@ -40,8 +39,8 @@ extern int __map_without_bats; extern unsigned long ioremap_base; extern unsigned int rtas_data, rtas_size; -struct _PTE; -extern struct _PTE *Hash, *Hash_end; +struct hash_pte; +extern struct hash_pte *Hash, *Hash_end; extern unsigned long Hash_size, Hash_mask; extern unsigned int num_tlbcam_entries; @@ -90,16 +89,4 @@ static inline void flush_HPTE(unsigned context, unsigned long va, else _tlbie(va); } -#else /* CONFIG_PPC64 */ -/* imalloc region types */ -#define IM_REGION_UNUSED 0x1 -#define IM_REGION_SUBSET 0x2 -#define IM_REGION_EXISTS 0x4 -#define IM_REGION_OVERLAP 0x8 -#define IM_REGION_SUPERSET 0x10 - -extern struct vm_struct * im_get_free_area(unsigned long size); -extern struct vm_struct * im_get_area(unsigned long v_addr, unsigned long size, - int region_type); -extern void im_free(void *addr); #endif diff --git a/arch/powerpc/mm/pgtable_32.c b/arch/powerpc/mm/pgtable_32.c index f6ae1a57d65..64488723162 100644 --- a/arch/powerpc/mm/pgtable_32.c +++ b/arch/powerpc/mm/pgtable_32.c @@ -8,7 +8,6 @@ * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au) * and Cort Dougan (PReP) (cort@cs.nmt.edu) * Copyright (C) 1996 Paul Mackerras - * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). * * Derived from "arch/i386/mm/init.c" * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds @@ -37,7 +36,6 @@ unsigned long ioremap_base; unsigned long ioremap_bot; EXPORT_SYMBOL(ioremap_bot); /* aka VMALLOC_END */ -int io_bat_index; #if defined(CONFIG_6xx) || defined(CONFIG_POWER3) #define HAVE_BATS 1 @@ -300,51 +298,6 @@ void __init mapin_ram(void) } } -/* is x a power of 4? */ -#define is_power_of_4(x) is_power_of_2(x) && (ffs(x) & 1) - -/* - * Set up a mapping for a block of I/O. - * virt, phys, size must all be page-aligned. - * This should only be called before ioremap is called. - */ -void __init io_block_mapping(unsigned long virt, phys_addr_t phys, - unsigned int size, int flags) -{ - int i; - - if (virt > KERNELBASE && virt < ioremap_bot) - ioremap_bot = ioremap_base = virt; - -#ifdef HAVE_BATS - /* - * Use a BAT for this if possible... - */ - if (io_bat_index < 2 && is_power_of_2(size) - && (virt & (size - 1)) == 0 && (phys & (size - 1)) == 0) { - setbat(io_bat_index, virt, phys, size, flags); - ++io_bat_index; - return; - } -#endif /* HAVE_BATS */ - -#ifdef HAVE_TLBCAM - /* - * Use a CAM for this if possible... - */ - if (tlbcam_index < num_tlbcam_entries && is_power_of_4(size) - && (virt & (size - 1)) == 0 && (phys & (size - 1)) == 0) { - settlbcam(tlbcam_index, virt, phys, size, flags, 0); - ++tlbcam_index; - return; - } -#endif /* HAVE_TLBCAM */ - - /* No BATs available, put it in the page tables. */ - for (i = 0; i < size; i += PAGE_SIZE) - map_page(virt + i, phys + i, flags); -} - /* Scan the real Linux page tables and return a PTE pointer for * a virtual address in a context. * Returns true (1) if PTE was found, zero otherwise. The pointer to @@ -379,82 +332,6 @@ get_pteptr(struct mm_struct *mm, unsigned long addr, pte_t **ptep, pmd_t **pmdp) return(retval); } -/* Find physical address for this virtual address. Normally used by - * I/O functions, but anyone can call it. - */ -unsigned long iopa(unsigned long addr) -{ - unsigned long pa; - - /* I don't know why this won't work on PMacs or CHRP. It - * appears there is some bug, or there is some implicit - * mapping done not properly represented by BATs or in page - * tables.......I am actively working on resolving this, but - * can't hold up other stuff. -- Dan - */ - pte_t *pte; - struct mm_struct *mm; - - /* Check the BATs */ - pa = v_mapped_by_bats(addr); - if (pa) - return pa; - - /* Allow mapping of user addresses (within the thread) - * for DMA if necessary. - */ - if (addr < TASK_SIZE) - mm = current->mm; - else - mm = &init_mm; - - pa = 0; - if (get_pteptr(mm, addr, &pte, NULL)) { - pa = (pte_val(*pte) & PAGE_MASK) | (addr & ~PAGE_MASK); - pte_unmap(pte); - } - - return(pa); -} - -/* This is will find the virtual address for a physical one.... - * Swiped from APUS, could be dangerous :-). - * This is only a placeholder until I really find a way to make this - * work. -- Dan - */ -unsigned long -mm_ptov (unsigned long paddr) -{ - unsigned long ret; -#if 0 - if (paddr < 16*1024*1024) - ret = ZTWO_VADDR(paddr); - else { - int i; - - for (i = 0; i < kmap_chunk_count;){ - unsigned long phys = kmap_chunks[i++]; - unsigned long size = kmap_chunks[i++]; - unsigned long virt = kmap_chunks[i++]; - if (paddr >= phys - && paddr < (phys + size)){ - ret = virt + paddr - phys; - goto exit; - } - } - - ret = (unsigned long) __va(paddr); - } -exit: -#ifdef DEBUGPV - printk ("PTOV(%lx)=%lx\n", paddr, ret); -#endif -#else - ret = (unsigned long)paddr + KERNELBASE; -#endif - return ret; -} - #ifdef CONFIG_DEBUG_PAGEALLOC static int __change_page_attr(struct page *page, pgprot_t prot) diff --git a/arch/powerpc/mm/pgtable_64.c b/arch/powerpc/mm/pgtable_64.c index ad6e135bf21..3dfd10db931 100644 --- a/arch/powerpc/mm/pgtable_64.c +++ b/arch/powerpc/mm/pgtable_64.c @@ -7,7 +7,6 @@ * Modifications by Paul Mackerras (PowerMac) (paulus@samba.org) * and Cort Dougan (PReP) (cort@cs.nmt.edu) * Copyright (C) 1996 Paul Mackerras - * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). * * Derived from "arch/i386/mm/init.c" * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds @@ -34,41 +33,27 @@ #include <linux/stddef.h> #include <linux/vmalloc.h> #include <linux/init.h> -#include <linux/delay.h> -#include <linux/bootmem.h> -#include <linux/highmem.h> -#include <linux/idr.h> -#include <linux/nodemask.h> -#include <linux/module.h> #include <asm/pgalloc.h> #include <asm/page.h> #include <asm/prom.h> -#include <asm/lmb.h> -#include <asm/rtas.h> #include <asm/io.h> #include <asm/mmu_context.h> #include <asm/pgtable.h> #include <asm/mmu.h> -#include <asm/uaccess.h> #include <asm/smp.h> #include <asm/machdep.h> #include <asm/tlb.h> -#include <asm/eeh.h> #include <asm/processor.h> -#include <asm/mmzone.h> #include <asm/cputable.h> #include <asm/sections.h> #include <asm/system.h> -#include <asm/iommu.h> #include <asm/abs_addr.h> -#include <asm/vdso.h> #include <asm/firmware.h> #include "mmu_decl.h" -unsigned long ioremap_bot = IMALLOC_BASE; -static unsigned long phbs_io_bot = PHBS_IO_BASE; +unsigned long ioremap_bot = IOREMAP_BASE; /* * map_io_page currently only called by __ioremap @@ -102,8 +87,8 @@ static int map_io_page(unsigned long ea, unsigned long pa, int flags) * entry in the hardware page table. * */ - if (htab_bolt_mapping(ea, ea + PAGE_SIZE, pa, flags, - mmu_io_psize)) { + if (htab_bolt_mapping(ea, (unsigned long)ea + PAGE_SIZE, + pa, flags, mmu_io_psize)) { printk(KERN_ERR "Failed to do bolted mapping IO " "memory at %016lx !\n", pa); return -ENOMEM; @@ -113,8 +98,11 @@ static int map_io_page(unsigned long ea, unsigned long pa, int flags) } -static void __iomem * __ioremap_com(phys_addr_t addr, unsigned long pa, - unsigned long ea, unsigned long size, +/** + * __ioremap_at - Low level function to establish the page tables + * for an IO mapping + */ +void __iomem * __ioremap_at(phys_addr_t pa, void *ea, unsigned long size, unsigned long flags) { unsigned long i; @@ -122,17 +110,35 @@ static void __iomem * __ioremap_com(phys_addr_t addr, unsigned long pa, if ((flags & _PAGE_PRESENT) == 0) flags |= pgprot_val(PAGE_KERNEL); + WARN_ON(pa & ~PAGE_MASK); + WARN_ON(((unsigned long)ea) & ~PAGE_MASK); + WARN_ON(size & ~PAGE_MASK); + for (i = 0; i < size; i += PAGE_SIZE) - if (map_io_page(ea+i, pa+i, flags)) + if (map_io_page((unsigned long)ea+i, pa+i, flags)) return NULL; - return (void __iomem *) (ea + (addr & ~PAGE_MASK)); + return (void __iomem *)ea; +} + +/** + * __iounmap_from - Low level function to tear down the page tables + * for an IO mapping. This is used for mappings that + * are manipulated manually, like partial unmapping of + * PCI IOs or ISA space. + */ +void __iounmap_at(void *ea, unsigned long size) +{ + WARN_ON(((unsigned long)ea) & ~PAGE_MASK); + WARN_ON(size & ~PAGE_MASK); + + unmap_kernel_range((unsigned long)ea, size); } void __iomem * __ioremap(phys_addr_t addr, unsigned long size, unsigned long flags) { - unsigned long pa, ea; + phys_addr_t paligned; void __iomem *ret; /* @@ -144,27 +150,30 @@ void __iomem * __ioremap(phys_addr_t addr, unsigned long size, * IMALLOC_END * */ - pa = addr & PAGE_MASK; - size = PAGE_ALIGN(addr + size) - pa; + paligned = addr & PAGE_MASK; + size = PAGE_ALIGN(addr + size) - paligned; - if ((size == 0) || (pa == 0)) + if ((size == 0) || (paligned == 0)) return NULL; if (mem_init_done) { struct vm_struct *area; - area = im_get_free_area(size); + + area = __get_vm_area(size, VM_IOREMAP, + ioremap_bot, IOREMAP_END); if (area == NULL) return NULL; - ea = (unsigned long)(area->addr); - ret = __ioremap_com(addr, pa, ea, size, flags); + ret = __ioremap_at(paligned, area->addr, size, flags); if (!ret) - im_free(area->addr); + vunmap(area->addr); } else { - ea = ioremap_bot; - ret = __ioremap_com(addr, pa, ea, size, flags); + ret = __ioremap_at(paligned, (void *)ioremap_bot, size, flags); if (ret) ioremap_bot += size; } + + if (ret) + ret += addr & ~PAGE_MASK; return ret; } @@ -187,62 +196,9 @@ void __iomem * ioremap_flags(phys_addr_t addr, unsigned long size, } -#define IS_PAGE_ALIGNED(_val) ((_val) == ((_val) & PAGE_MASK)) - -int __ioremap_explicit(phys_addr_t pa, unsigned long ea, - unsigned long size, unsigned long flags) -{ - struct vm_struct *area; - void __iomem *ret; - - /* For now, require page-aligned values for pa, ea, and size */ - if (!IS_PAGE_ALIGNED(pa) || !IS_PAGE_ALIGNED(ea) || - !IS_PAGE_ALIGNED(size)) { - printk(KERN_ERR "unaligned value in %s\n", __FUNCTION__); - return 1; - } - - if (!mem_init_done) { - /* Two things to consider in this case: - * 1) No records will be kept (imalloc, etc) that the region - * has been remapped - * 2) It won't be easy to iounmap() the region later (because - * of 1) - */ - ; - } else { - area = im_get_area(ea, size, - IM_REGION_UNUSED|IM_REGION_SUBSET|IM_REGION_EXISTS); - if (area == NULL) { - /* Expected when PHB-dlpar is in play */ - return 1; - } - if (ea != (unsigned long) area->addr) { - printk(KERN_ERR "unexpected addr return from " - "im_get_area\n"); - return 1; - } - } - - ret = __ioremap_com(pa, pa, ea, size, flags); - if (ret == NULL) { - printk(KERN_ERR "ioremap_explicit() allocation failure !\n"); - return 1; - } - if (ret != (void *) ea) { - printk(KERN_ERR "__ioremap_com() returned unexpected addr\n"); - return 1; - } - - return 0; -} - /* * Unmap an IO region and remove it from imalloc'd list. * Access to IO memory should be serialized by driver. - * This code is modeled after vmalloc code - unmap_vm_area() - * - * XXX what about calls before mem_init_done (ie python_countermeasures()) */ void __iounmap(volatile void __iomem *token) { @@ -251,9 +207,14 @@ void __iounmap(volatile void __iomem *token) if (!mem_init_done) return; - addr = (void *) ((unsigned long __force) token & PAGE_MASK); - - im_free(addr); + addr = (void *) ((unsigned long __force) + PCI_FIX_ADDR(token) & PAGE_MASK); + if ((unsigned long)addr < ioremap_bot) { + printk(KERN_WARNING "Attempt to iounmap early bolted mapping" + " at 0x%p\n", addr); + return; + } + vunmap(addr); } void iounmap(volatile void __iomem *token) @@ -264,77 +225,8 @@ void iounmap(volatile void __iomem *token) __iounmap(token); } -static int iounmap_subset_regions(unsigned long addr, unsigned long size) -{ - struct vm_struct *area; - - /* Check whether subsets of this region exist */ - area = im_get_area(addr, size, IM_REGION_SUPERSET); - if (area == NULL) - return 1; - - while (area) { - iounmap((void __iomem *) area->addr); - area = im_get_area(addr, size, - IM_REGION_SUPERSET); - } - - return 0; -} - -int __iounmap_explicit(volatile void __iomem *start, unsigned long size) -{ - struct vm_struct *area; - unsigned long addr; - int rc; - - addr = (unsigned long __force) start & PAGE_MASK; - - /* Verify that the region either exists or is a subset of an existing - * region. In the latter case, split the parent region to create - * the exact region - */ - area = im_get_area(addr, size, - IM_REGION_EXISTS | IM_REGION_SUBSET); - if (area == NULL) { - /* Determine whether subset regions exist. If so, unmap */ - rc = iounmap_subset_regions(addr, size); - if (rc) { - printk(KERN_ERR - "%s() cannot unmap nonexistent range 0x%lx\n", - __FUNCTION__, addr); - return 1; - } - } else { - iounmap((void __iomem *) area->addr); - } - /* - * FIXME! This can't be right: - iounmap(area->addr); - * Maybe it should be "iounmap(area);" - */ - return 0; -} - EXPORT_SYMBOL(ioremap); EXPORT_SYMBOL(ioremap_flags); EXPORT_SYMBOL(__ioremap); EXPORT_SYMBOL(iounmap); EXPORT_SYMBOL(__iounmap); - -static DEFINE_SPINLOCK(phb_io_lock); - -void __iomem * reserve_phb_iospace(unsigned long size) -{ - void __iomem *virt_addr; - - if (phbs_io_bot >= IMALLOC_BASE) - panic("reserve_phb_iospace(): phb io space overflow\n"); - - spin_lock(&phb_io_lock); - virt_addr = (void __iomem *) phbs_io_bot; - phbs_io_bot += size; - spin_unlock(&phb_io_lock); - - return virt_addr; -} diff --git a/arch/powerpc/mm/ppc_mmu_32.c b/arch/powerpc/mm/ppc_mmu_32.c index ec1421a20aa..5c45d474cfc 100644 --- a/arch/powerpc/mm/ppc_mmu_32.c +++ b/arch/powerpc/mm/ppc_mmu_32.c @@ -11,7 +11,6 @@ * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au) * and Cort Dougan (PReP) (cort@cs.nmt.edu) * Copyright (C) 1996 Paul Mackerras - * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). * * Derived from "arch/i386/mm/init.c" * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds @@ -35,12 +34,12 @@ #include "mmu_decl.h" -PTE *Hash, *Hash_end; +struct hash_pte *Hash, *Hash_end; unsigned long Hash_size, Hash_mask; unsigned long _SDR1; union ubat { /* BAT register values to be loaded */ - BAT bat; + struct ppc_bat bat; u32 word[2]; } BATS[8][2]; /* 8 pairs of IBAT, DBAT */ @@ -245,7 +244,7 @@ void __init MMU_init_hw(void) cacheable_memzero(Hash, Hash_size); _SDR1 = __pa(Hash) | SDR1_LOW_BITS; - Hash_end = (PTE *) ((unsigned long)Hash + Hash_size); + Hash_end = (struct hash_pte *) ((unsigned long)Hash + Hash_size); printk("Total memory = %ldMB; using %ldkB for hash table (at %p)\n", total_memory >> 20, Hash_size >> 10, Hash); diff --git a/arch/powerpc/mm/stab.c b/arch/powerpc/mm/stab.c index 132c6bc66ce..28492bbdee8 100644 --- a/arch/powerpc/mm/stab.c +++ b/arch/powerpc/mm/stab.c @@ -55,7 +55,7 @@ static int make_ste(unsigned long stab, unsigned long esid, unsigned long vsid) for (entry = 0; entry < 8; entry++, ste++) { if (!(ste->esid_data & STE_ESID_V)) { ste->vsid_data = vsid_data; - asm volatile("eieio":::"memory"); + eieio(); ste->esid_data = esid_data; return (global_entry | entry); } @@ -101,7 +101,7 @@ static int make_ste(unsigned long stab, unsigned long esid, unsigned long vsid) asm volatile("sync" : : : "memory"); /* Order update */ castout_ste->vsid_data = vsid_data; - asm volatile("eieio" : : : "memory"); /* Order update */ + eieio(); /* Order update */ castout_ste->esid_data = esid_data; asm volatile("slbie %0" : : "r" (old_esid << SID_SHIFT)); diff --git a/arch/powerpc/mm/tlb_32.c b/arch/powerpc/mm/tlb_32.c index 6a69417cbc0..06c7e77e097 100644 --- a/arch/powerpc/mm/tlb_32.c +++ b/arch/powerpc/mm/tlb_32.c @@ -11,7 +11,6 @@ * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au) * and Cort Dougan (PReP) (cort@cs.nmt.edu) * Copyright (C) 1996 Paul Mackerras - * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). * * Derived from "arch/i386/mm/init.c" * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds diff --git a/arch/powerpc/mm/tlb_64.c b/arch/powerpc/mm/tlb_64.c index 2bfc4d7e1aa..cbd34fc813e 100644 --- a/arch/powerpc/mm/tlb_64.c +++ b/arch/powerpc/mm/tlb_64.c @@ -8,7 +8,6 @@ * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au) * and Cort Dougan (PReP) (cort@cs.nmt.edu) * Copyright (C) 1996 Paul Mackerras - * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). * * Derived from "arch/i386/mm/init.c" * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds @@ -239,3 +238,59 @@ void pte_free_finish(void) pte_free_submit(*batchp); *batchp = NULL; } + +/** + * __flush_hash_table_range - Flush all HPTEs for a given address range + * from the hash table (and the TLB). But keeps + * the linux PTEs intact. + * + * @mm : mm_struct of the target address space (generally init_mm) + * @start : starting address + * @end : ending address (not included in the flush) + * + * This function is mostly to be used by some IO hotplug code in order + * to remove all hash entries from a given address range used to map IO + * space on a removed PCI-PCI bidge without tearing down the full mapping + * since 64K pages may overlap with other bridges when using 64K pages + * with 4K HW pages on IO space. + * + * Because of that usage pattern, it's only available with CONFIG_HOTPLUG + * and is implemented for small size rather than speed. + */ +#ifdef CONFIG_HOTPLUG + +void __flush_hash_table_range(struct mm_struct *mm, unsigned long start, + unsigned long end) +{ + unsigned long flags; + + start = _ALIGN_DOWN(start, PAGE_SIZE); + end = _ALIGN_UP(end, PAGE_SIZE); + + BUG_ON(!mm->pgd); + + /* Note: Normally, we should only ever use a batch within a + * PTE locked section. This violates the rule, but will work + * since we don't actually modify the PTEs, we just flush the + * hash while leaving the PTEs intact (including their reference + * to being hashed). This is not the most performance oriented + * way to do things but is fine for our needs here. + */ + local_irq_save(flags); + arch_enter_lazy_mmu_mode(); + for (; start < end; start += PAGE_SIZE) { + pte_t *ptep = find_linux_pte(mm->pgd, start); + unsigned long pte; + + if (ptep == NULL) + continue; + pte = pte_val(*ptep); + if (!(pte & _PAGE_HASHPTE)) + continue; + hpte_need_flush(mm, start, ptep, pte, 0); + } + arch_leave_lazy_mmu_mode(); + local_irq_restore(flags); +} + +#endif /* CONFIG_HOTPLUG */ diff --git a/arch/powerpc/oprofile/op_model_power4.c b/arch/powerpc/oprofile/op_model_power4.c index fe597a154d4..a7c206b665a 100644 --- a/arch/powerpc/oprofile/op_model_power4.c +++ b/arch/powerpc/oprofile/op_model_power4.c @@ -1,5 +1,7 @@ /* * Copyright (C) 2004 Anton Blanchard <anton@au.ibm.com>, IBM + * Added mmcra[slot] support: + * Copyright (C) 2006-2007 Will Schmidt <willschm@us.ibm.com>, IBM * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -181,11 +183,17 @@ static void __attribute_used__ kernel_unknown_bucket(void) * On GQ and newer the MMCRA stores the HV and PR bits at the time * the SIAR was sampled. We use that to work out if the SIAR was sampled in * the hypervisor, our exception vectors or RTAS. + * If the MMCRA_SAMPLE_ENABLE bit is set, we can use the MMCRA[slot] bits + * to more accurately identify the address of the sampled instruction. The + * mmcra[slot] bits represent the slot number of a sampled instruction + * within an instruction group. The slot will contain a value between 1 + * and 5 if MMCRA_SAMPLE_ENABLE is set, otherwise 0. */ static unsigned long get_pc(struct pt_regs *regs) { unsigned long pc = mfspr(SPRN_SIAR); unsigned long mmcra; + unsigned long slot; /* Cant do much about it */ if (!cur_cpu_spec->oprofile_mmcra_sihv) @@ -193,6 +201,12 @@ static unsigned long get_pc(struct pt_regs *regs) mmcra = mfspr(SPRN_MMCRA); + if (mmcra & MMCRA_SAMPLE_ENABLE) { + slot = ((mmcra & MMCRA_SLOT) >> MMCRA_SLOT_SHIFT); + if (slot > 1) + pc += 4 * (slot - 1); + } + /* Were we in the hypervisor? */ if (firmware_has_feature(FW_FEATURE_LPAR) && (mmcra & cur_cpu_spec->oprofile_mmcra_sihv)) diff --git a/arch/powerpc/platforms/52xx/efika.c b/arch/powerpc/platforms/52xx/efika.c index f591a9fc19b..4be6e7a17b6 100644 --- a/arch/powerpc/platforms/52xx/efika.c +++ b/arch/powerpc/platforms/52xx/efika.c @@ -54,7 +54,7 @@ static int rtas_read_config(struct pci_bus *bus, unsigned int devfn, int offset, struct pci_controller *hose = bus->sysdata; unsigned long addr = (offset & 0xff) | ((devfn & 0xff) << 8) | (((bus->number - hose->first_busno) & 0xff) << 16) - | (hose->index << 24); + | (hose->global_number << 24); int ret = -1; int rval; @@ -69,7 +69,7 @@ static int rtas_write_config(struct pci_bus *bus, unsigned int devfn, struct pci_controller *hose = bus->sysdata; unsigned long addr = (offset & 0xff) | ((devfn & 0xff) << 8) | (((bus->number - hose->first_busno) & 0xff) << 16) - | (hose->index << 24); + | (hose->global_number << 24); int rval; rval = rtas_call(rtas_token("write-pci-config"), 3, 1, NULL, @@ -83,7 +83,7 @@ static struct pci_ops rtas_pci_ops = { }; -void __init efika_pcisetup(void) +static void __init efika_pcisetup(void) { const int *bus_range; int len; @@ -128,7 +128,7 @@ void __init efika_pcisetup(void) printk(" controlled by %s\n", pcictrl->full_name); printk("\n"); - hose = pcibios_alloc_controller(); + hose = pcibios_alloc_controller(of_node_get(pcictrl)); if (!hose) { printk(KERN_WARNING EFIKA_PLATFORM_NAME ": Can't allocate PCI controller structure for %s\n", @@ -136,7 +136,6 @@ void __init efika_pcisetup(void) return; } - hose->arch_data = of_node_get(pcictrl); hose->first_busno = bus_range[0]; hose->last_busno = bus_range[1]; hose->ops = &rtas_pci_ops; @@ -145,7 +144,7 @@ void __init efika_pcisetup(void) } #else -void __init efika_pcisetup(void) +static void __init efika_pcisetup(void) {} #endif @@ -252,6 +251,8 @@ define_machine(efika) .progress = rtas_progress, .get_boot_time = rtas_get_boot_time, .calibrate_decr = generic_calibrate_decr, +#ifdef CONFIG_PCI .phys_mem_access_prot = pci_phys_mem_access_prot, +#endif }; diff --git a/arch/powerpc/platforms/52xx/lite5200.c b/arch/powerpc/platforms/52xx/lite5200.c index 1cfc00dfb99..5c46e898fd4 100644 --- a/arch/powerpc/platforms/52xx/lite5200.c +++ b/arch/powerpc/platforms/52xx/lite5200.c @@ -156,7 +156,7 @@ static void __init lite5200_setup_arch(void) } -void lite5200_show_cpuinfo(struct seq_file *m) +static void lite5200_show_cpuinfo(struct seq_file *m) { struct device_node* np = of_find_all_nodes(NULL); const char *model = NULL; diff --git a/arch/powerpc/platforms/52xx/mpc52xx_pci.c b/arch/powerpc/platforms/52xx/mpc52xx_pci.c index 34d34a26d30..4c6c82a684b 100644 --- a/arch/powerpc/platforms/52xx/mpc52xx_pci.c +++ b/arch/powerpc/platforms/52xx/mpc52xx_pci.c @@ -112,18 +112,18 @@ mpc52xx_pci_read_config(struct pci_bus *bus, unsigned int devfn, u32 value; if (ppc_md.pci_exclude_device) - if (ppc_md.pci_exclude_device(bus->number, devfn)) + if (ppc_md.pci_exclude_device(hose, bus->number, devfn)) return PCIBIOS_DEVICE_NOT_FOUND; out_be32(hose->cfg_addr, (1 << 31) | - ((bus->number - hose->bus_offset) << 16) | + (bus->number << 16) | (devfn << 8) | (offset & 0xfc)); mb(); #if defined(CONFIG_PPC_MPC5200_BUGFIX) - if (bus->number != hose->bus_offset) { + if (bus->number) { /* workaround for the bug 435 of the MPC5200 (L25R); * Don't do 32 bits config access during type-1 cycles */ switch (len) { @@ -169,18 +169,18 @@ mpc52xx_pci_write_config(struct pci_bus *bus, unsigned int devfn, u32 value, mask; if (ppc_md.pci_exclude_device) - if (ppc_md.pci_exclude_device(bus->number, devfn)) + if (ppc_md.pci_exclude_device(hose, bus->number, devfn)) return PCIBIOS_DEVICE_NOT_FOUND; out_be32(hose->cfg_addr, (1 << 31) | - ((bus->number - hose->bus_offset) << 16) | + (bus->number << 16) | (devfn << 8) | (offset & 0xfc)); mb(); #if defined(CONFIG_PPC_MPC5200_BUGFIX) - if (bus->number != hose->bus_offset) { + if (bus->number) { /* workaround for the bug 435 of the MPC5200 (L25R); * Don't do 32 bits config access during type-1 cycles */ switch (len) { @@ -385,17 +385,13 @@ mpc52xx_add_bridge(struct device_node *node) * tree are needed to configure the 52xx PCI controller. Rather * than parse the tree here, let pci_process_bridge_OF_ranges() * do it for us and extract the values after the fact */ - hose = pcibios_alloc_controller(); + hose = pcibios_alloc_controller(node); if (!hose) return -ENOMEM; - hose->arch_data = node; - hose->set_cfg_type = 1; - hose->first_busno = bus_range ? bus_range[0] : 0; hose->last_busno = bus_range ? bus_range[1] : 0xff; - hose->bus_offset = 0; hose->ops = &mpc52xx_pci_ops; pci_regs = ioremap(rsrc.start, rsrc.end - rsrc.start + 1); diff --git a/arch/powerpc/platforms/52xx/mpc52xx_pm.c b/arch/powerpc/platforms/52xx/mpc52xx_pm.c index fd40044d16c..ee2e7639c63 100644 --- a/arch/powerpc/platforms/52xx/mpc52xx_pm.c +++ b/arch/powerpc/platforms/52xx/mpc52xx_pm.c @@ -9,8 +9,8 @@ /* these are defined in mpc52xx_sleep.S, and only used here */ -extern void mpc52xx_deep_sleep(void *sram, void *sdram_regs, - struct mpc52xx_cdm *, struct mpc52xx_intr *); +extern void mpc52xx_deep_sleep(void __iomem *sram, void __iomem *sdram_regs, + struct mpc52xx_cdm __iomem *, struct mpc52xx_intr __iomem*); extern void mpc52xx_ds_sram(void); extern const long mpc52xx_ds_sram_size; extern void mpc52xx_ds_cached(void); @@ -21,7 +21,7 @@ static void __iomem *sdram; static struct mpc52xx_cdm __iomem *cdm; static struct mpc52xx_intr __iomem *intr; static struct mpc52xx_gpio_wkup __iomem *gpiow; -static void *sram; +static void __iomem *sram; static int sram_size; struct mpc52xx_suspend mpc52xx_suspend; @@ -100,7 +100,7 @@ int mpc52xx_pm_enter(suspend_state_t state) u32 clk_enables; u32 msr, hid0; u32 intr_main_mask; - void __iomem * irq_0x500 = (void *)CONFIG_KERNEL_START + 0x500; + void __iomem * irq_0x500 = (void __iomem *)CONFIG_KERNEL_START + 0x500; unsigned long irq_0x500_stop = (unsigned long)irq_0x500 + mpc52xx_ds_cached_size; char saved_0x500[mpc52xx_ds_cached_size]; diff --git a/arch/powerpc/platforms/82xx/Kconfig b/arch/powerpc/platforms/82xx/Kconfig index de7fce9cb6e..89fde43895c 100644 --- a/arch/powerpc/platforms/82xx/Kconfig +++ b/arch/powerpc/platforms/82xx/Kconfig @@ -1,5 +1,5 @@ choice - prompt "Machine Type" + prompt "82xx Board Type" depends on PPC_82xx default MPC82xx_ADS diff --git a/arch/powerpc/platforms/82xx/mpc82xx_ads.c b/arch/powerpc/platforms/82xx/mpc82xx_ads.c index 47cb09f0805..da20832b27f 100644 --- a/arch/powerpc/platforms/82xx/mpc82xx_ads.c +++ b/arch/powerpc/platforms/82xx/mpc82xx_ads.c @@ -49,7 +49,7 @@ #include <linux/fs_enet_pd.h> #include <sysdev/fsl_soc.h> -#include <../sysdev/cpm2_pic.h> +#include <sysdev/cpm2_pic.h> #include "pq2ads.h" @@ -507,7 +507,8 @@ void m82xx_pci_init_irq(void) return; } -static int m82xx_pci_exclude_device(u_char bus, u_char devfn) +static int m82xx_pci_exclude_device(struct pci_controller *hose, + u_char bus, u_char devfn) { if (bus == 0 && PCI_SLOT(devfn) == 0) return PCIBIOS_DEVICE_NOT_FOUND; @@ -515,7 +516,7 @@ static int m82xx_pci_exclude_device(u_char bus, u_char devfn) return PCIBIOS_SUCCESSFUL; } -void __init add_bridge(struct device_node *np) +static void __init mpc82xx_add_bridge(struct device_node *np) { int len; struct pci_controller *hose; @@ -542,19 +543,13 @@ void __init add_bridge(struct device_node *np) pci_assign_all_buses = 1; - hose = pcibios_alloc_controller(); + hose = pcibios_alloc_controller(np); if (!hose) return; - hose->arch_data = np; - hose->set_cfg_type = 1; - hose->first_busno = bus_range ? bus_range[0] : 0; hose->last_busno = bus_range ? bus_range[1] : 0xff; - hose->bus_offset = 0; - - hose->set_cfg_type = 1; setup_indirect_pci(hose, r.start + offsetof(pci_cpm2_t, pci_cfg_addr), @@ -584,7 +579,7 @@ static void __init mpc82xx_ads_setup_arch(void) #ifdef CONFIG_PCI ppc_md.pci_exclude_device = m82xx_pci_exclude_device; for (np = NULL; (np = of_find_node_by_type(np, "pci")) != NULL;) - add_bridge(np); + mpc82xx_add_bridge(np); of_node_put(np); #endif diff --git a/arch/powerpc/platforms/83xx/Kconfig b/arch/powerpc/platforms/83xx/Kconfig index 19cafdf6df9..ec305f18abd 100644 --- a/arch/powerpc/platforms/83xx/Kconfig +++ b/arch/powerpc/platforms/83xx/Kconfig @@ -1,5 +1,5 @@ choice - prompt "Machine Type" + prompt "83xx Board Type" depends on PPC_83xx default MPC834x_MDS diff --git a/arch/powerpc/platforms/83xx/Makefile b/arch/powerpc/platforms/83xx/Makefile index 31a91b53f52..5a98f885779 100644 --- a/arch/powerpc/platforms/83xx/Makefile +++ b/arch/powerpc/platforms/83xx/Makefile @@ -1,7 +1,7 @@ # # Makefile for the PowerPC 83xx linux kernel. # -obj-y := misc.o +obj-y := misc.o usb.o obj-$(CONFIG_PCI) += pci.o obj-$(CONFIG_MPC8313_RDB) += mpc8313_rdb.o obj-$(CONFIG_MPC832x_RDB) += mpc832x_rdb.o diff --git a/arch/powerpc/platforms/83xx/mpc8313_rdb.c b/arch/powerpc/platforms/83xx/mpc8313_rdb.c index 96970ac887e..3edfe170a03 100644 --- a/arch/powerpc/platforms/83xx/mpc8313_rdb.c +++ b/arch/powerpc/platforms/83xx/mpc8313_rdb.c @@ -28,11 +28,6 @@ #define DBG(fmt...) #endif -#ifndef CONFIG_PCI -unsigned long isa_io_base = 0; -unsigned long isa_mem_base = 0; -#endif - /* ************************************************************************ * * Setup the architecture @@ -49,10 +44,11 @@ static void __init mpc8313_rdb_setup_arch(void) #ifdef CONFIG_PCI for (np = NULL; (np = of_find_node_by_type(np, "pci")) != NULL;) - add_bridge(np); + mpc83xx_add_bridge(np); ppc_md.pci_exclude_device = mpc83xx_exclude_device; #endif + mpc831x_usb_cfg(); } void __init mpc8313_rdb_init_IRQ(void) diff --git a/arch/powerpc/platforms/83xx/mpc832x_mds.c b/arch/powerpc/platforms/83xx/mpc832x_mds.c index 94843ed52a9..b39cb52c6fb 100644 --- a/arch/powerpc/platforms/83xx/mpc832x_mds.c +++ b/arch/powerpc/platforms/83xx/mpc832x_mds.c @@ -49,11 +49,6 @@ #define DBG(fmt...) #endif -#ifndef CONFIG_PCI -unsigned long isa_io_base = 0; -unsigned long isa_mem_base = 0; -#endif - static u8 *bcsr_regs = NULL; /* ************************************************************************ @@ -80,7 +75,7 @@ static void __init mpc832x_sys_setup_arch(void) #ifdef CONFIG_PCI for (np = NULL; (np = of_find_node_by_type(np, "pci")) != NULL;) - add_bridge(np); + mpc83xx_add_bridge(np); ppc_md.pci_exclude_device = mpc83xx_exclude_device; #endif diff --git a/arch/powerpc/platforms/83xx/mpc832x_rdb.c b/arch/powerpc/platforms/83xx/mpc832x_rdb.c index 3db68b73fc3..b2b28a44738 100644 --- a/arch/powerpc/platforms/83xx/mpc832x_rdb.c +++ b/arch/powerpc/platforms/83xx/mpc832x_rdb.c @@ -32,11 +32,6 @@ #define DBG(fmt...) #endif -#ifndef CONFIG_PCI -unsigned long isa_io_base = 0; -unsigned long isa_mem_base = 0; -#endif - /* ************************************************************************ * * Setup the architecture @@ -53,7 +48,7 @@ static void __init mpc832x_rdb_setup_arch(void) #ifdef CONFIG_PCI for (np = NULL; (np = of_find_node_by_type(np, "pci")) != NULL;) - add_bridge(np); + mpc83xx_add_bridge(np); ppc_md.pci_exclude_device = mpc83xx_exclude_device; #endif diff --git a/arch/powerpc/platforms/83xx/mpc834x_itx.c b/arch/powerpc/platforms/83xx/mpc834x_itx.c index 40a01947d68..47ba5446f63 100644 --- a/arch/powerpc/platforms/83xx/mpc834x_itx.c +++ b/arch/powerpc/platforms/83xx/mpc834x_itx.c @@ -38,11 +38,6 @@ #include "mpc83xx.h" -#ifndef CONFIG_PCI -unsigned long isa_io_base = 0; -unsigned long isa_mem_base = 0; -#endif - /* ************************************************************************ * * Setup the architecture @@ -59,10 +54,12 @@ static void __init mpc834x_itx_setup_arch(void) #ifdef CONFIG_PCI for (np = NULL; (np = of_find_node_by_type(np, "pci")) != NULL;) - add_bridge(np); + mpc83xx_add_bridge(np); ppc_md.pci_exclude_device = mpc83xx_exclude_device; #endif + + mpc834x_usb_cfg(); } static void __init mpc834x_itx_init_IRQ(void) diff --git a/arch/powerpc/platforms/83xx/mpc834x_mds.c b/arch/powerpc/platforms/83xx/mpc834x_mds.c index 10394b2d7e7..4c9ff9cadfe 100644 --- a/arch/powerpc/platforms/83xx/mpc834x_mds.c +++ b/arch/powerpc/platforms/83xx/mpc834x_mds.c @@ -38,61 +38,17 @@ #include "mpc83xx.h" -#ifndef CONFIG_PCI -unsigned long isa_io_base = 0; -unsigned long isa_mem_base = 0; -#endif - #define BCSR5_INT_USB 0x02 -/* Note: This is only for PB, not for PB+PIB - * On PB only port0 is connected using ULPI */ -static int mpc834x_usb_cfg(void) +static int mpc834xemds_usb_cfg(void) { - unsigned long sccr, sicrl; - void __iomem *immap; + struct device_node *np; void __iomem *bcsr_regs = NULL; u8 bcsr5; - struct device_node *np = NULL; - int port0_is_dr = 0; - - if ((np = of_find_compatible_node(NULL, "usb", "fsl-usb2-dr")) != NULL) - port0_is_dr = 1; - if ((np = of_find_compatible_node(NULL, "usb", "fsl-usb2-mph")) != NULL){ - if (port0_is_dr) { - printk(KERN_WARNING - "There is only one USB port on PB board! \n"); - return -1; - } else if (!port0_is_dr) - /* No usb port enabled */ - return -1; - } - - immap = ioremap(get_immrbase(), 0x1000); - if (!immap) - return -1; - - /* Configure clock */ - sccr = in_be32(immap + MPC83XX_SCCR_OFFS); - if (port0_is_dr) - sccr |= MPC83XX_SCCR_USB_DRCM_11; /* 1:3 */ - else - sccr |= MPC83XX_SCCR_USB_MPHCM_11; /* 1:3 */ - out_be32(immap + MPC83XX_SCCR_OFFS, sccr); - - /* Configure Pin */ - sicrl = in_be32(immap + MPC83XX_SICRL_OFFS); - /* set port0 only */ - if (port0_is_dr) - sicrl |= MPC83XX_SICRL_USB0; - else - sicrl &= ~(MPC83XX_SICRL_USB0); - out_be32(immap + MPC83XX_SICRL_OFFS, sicrl); - - iounmap(immap); + mpc834x_usb_cfg(); /* Map BCSR area */ np = of_find_node_by_name(NULL, "bcsr"); - if (np != 0) { + if (np) { struct resource res; of_address_to_resource(np, 0, &res); @@ -129,12 +85,12 @@ static void __init mpc834x_mds_setup_arch(void) #ifdef CONFIG_PCI for (np = NULL; (np = of_find_node_by_type(np, "pci")) != NULL;) - add_bridge(np); + mpc83xx_add_bridge(np); ppc_md.pci_exclude_device = mpc83xx_exclude_device; #endif - mpc834x_usb_cfg(); + mpc834xemds_usb_cfg(); } static void __init mpc834x_mds_init_IRQ(void) diff --git a/arch/powerpc/platforms/83xx/mpc836x_mds.c b/arch/powerpc/platforms/83xx/mpc836x_mds.c index bceeff8bbfd..0e615fd65c1 100644 --- a/arch/powerpc/platforms/83xx/mpc836x_mds.c +++ b/arch/powerpc/platforms/83xx/mpc836x_mds.c @@ -55,11 +55,6 @@ #define DBG(fmt...) #endif -#ifndef CONFIG_PCI -unsigned long isa_io_base = 0; -unsigned long isa_mem_base = 0; -#endif - static u8 *bcsr_regs = NULL; /* ************************************************************************ @@ -86,7 +81,7 @@ static void __init mpc836x_mds_setup_arch(void) #ifdef CONFIG_PCI for (np = NULL; (np = of_find_node_by_type(np, "pci")) != NULL;) - add_bridge(np); + mpc83xx_add_bridge(np); ppc_md.pci_exclude_device = mpc83xx_exclude_device; #endif diff --git a/arch/powerpc/platforms/83xx/mpc83xx.h b/arch/powerpc/platforms/83xx/mpc83xx.h index 9cd03b59c8f..589ee55730f 100644 --- a/arch/powerpc/platforms/83xx/mpc83xx.h +++ b/arch/powerpc/platforms/83xx/mpc83xx.h @@ -3,9 +3,11 @@ #include <linux/init.h> #include <linux/device.h> +#include <asm/pci-bridge.h> /* System Clock Control Register */ #define MPC83XX_SCCR_OFFS 0xA08 +#define MPC83XX_SCCR_USB_MASK 0x00f00000 #define MPC83XX_SCCR_USB_MPHCM_11 0x00c00000 #define MPC83XX_SCCR_USB_MPHCM_01 0x00400000 #define MPC83XX_SCCR_USB_MPHCM_10 0x00800000 @@ -15,21 +17,43 @@ /* system i/o configuration register low */ #define MPC83XX_SICRL_OFFS 0x114 -#define MPC83XX_SICRL_USB0 0x40000000 -#define MPC83XX_SICRL_USB1 0x20000000 +#define MPC834X_SICRL_USB_MASK 0x60000000 +#define MPC834X_SICRL_USB0 0x40000000 +#define MPC834X_SICRL_USB1 0x20000000 +#define MPC831X_SICRL_USB_MASK 0x00000c00 +#define MPC831X_SICRL_USB_ULPI 0x00000800 /* system i/o configuration register high */ #define MPC83XX_SICRH_OFFS 0x118 -#define MPC83XX_SICRH_USB_UTMI 0x00020000 +#define MPC834X_SICRH_USB_UTMI 0x00020000 +#define MPC831X_SICRH_USB_MASK 0x000000e0 +#define MPC831X_SICRH_USB_ULPI 0x000000a0 + +/* USB Control Register */ +#define FSL_USB2_CONTROL_OFFS 0x500 +#define CONTROL_UTMI_PHY_EN 0x00000200 +#define CONTROL_REFSEL_48MHZ 0x00000080 +#define CONTROL_PHY_CLK_SEL_ULPI 0x00000400 +#define CONTROL_OTG_PORT 0x00000020 + +/* USB PORTSC Registers */ +#define FSL_USB2_PORTSC1_OFFS 0x184 +#define FSL_USB2_PORTSC2_OFFS 0x188 +#define PORTSCX_PTW_16BIT 0x10000000 +#define PORTSCX_PTS_UTMI 0x00000000 +#define PORTSCX_PTS_ULPI 0x80000000 /* * Declaration for the various functions exported by the * mpc83xx_* files. Mostly for use by mpc83xx_setup */ -extern int add_bridge(struct device_node *dev); -extern int mpc83xx_exclude_device(u_char bus, u_char devfn); +extern int mpc83xx_add_bridge(struct device_node *dev); +extern int mpc83xx_exclude_device(struct pci_controller *hose, + u_char bus, u_char devfn); extern void mpc83xx_restart(char *cmd); extern long mpc83xx_time_init(void); +extern int mpc834x_usb_cfg(void); +extern int mpc831x_usb_cfg(void); #endif /* __MPC83XX_H__ */ diff --git a/arch/powerpc/platforms/83xx/pci.c b/arch/powerpc/platforms/83xx/pci.c index 774457d09e9..c0e2b89154e 100644 --- a/arch/powerpc/platforms/83xx/pci.c +++ b/arch/powerpc/platforms/83xx/pci.c @@ -33,19 +33,14 @@ #define DBG(x...) #endif -int mpc83xx_pci2_busno; - -int mpc83xx_exclude_device(u_char bus, u_char devfn) +int mpc83xx_exclude_device(struct pci_controller *hose, u_char bus, u_char devfn) { - if (bus == 0 && PCI_SLOT(devfn) == 0) + if ((bus == hose->first_busno) && PCI_SLOT(devfn) == 0) return PCIBIOS_DEVICE_NOT_FOUND; - if (mpc83xx_pci2_busno) - if (bus == (mpc83xx_pci2_busno) && PCI_SLOT(devfn) == 0) - return PCIBIOS_DEVICE_NOT_FOUND; return PCIBIOS_SUCCESSFUL; } -int __init add_bridge(struct device_node *dev) +int __init mpc83xx_add_bridge(struct device_node *dev) { int len; struct pci_controller *hose; @@ -66,11 +61,10 @@ int __init add_bridge(struct device_node *dev) " bus 0\n", dev->full_name); } - hose = pcibios_alloc_controller(); + pci_assign_all_buses = 1; + hose = pcibios_alloc_controller(dev); if (!hose) return -ENOMEM; - hose->arch_data = dev; - hose->set_cfg_type = 1; hose->first_busno = bus_range ? bus_range[0] : 0; hose->last_busno = bus_range ? bus_range[1] : 0xff; @@ -86,8 +80,6 @@ int __init add_bridge(struct device_node *dev) if ((rsrc.start & 0xfffff) == 0x8600) { setup_indirect_pci(hose, immr + 0x8380, immr + 0x8384); primary = 0; - hose->bus_offset = hose->first_busno; - mpc83xx_pci2_busno = hose->first_busno; } printk(KERN_INFO "Found MPC83xx PCI host bridge at 0x%016llx. " diff --git a/arch/powerpc/platforms/83xx/usb.c b/arch/powerpc/platforms/83xx/usb.c new file mode 100644 index 00000000000..e7fdf013cd3 --- /dev/null +++ b/arch/powerpc/platforms/83xx/usb.c @@ -0,0 +1,181 @@ +/* + * Freescale 83xx USB SOC setup code + * + * Copyright (C) 2007 Freescale Semiconductor, Inc. + * Author: Li Yang + * + * 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 <linux/stddef.h> +#include <linux/kernel.h> +#include <linux/errno.h> + +#include <asm/io.h> +#include <asm/prom.h> +#include <sysdev/fsl_soc.h> + +#include "mpc83xx.h" + + +#ifdef CONFIG_MPC834x +int mpc834x_usb_cfg(void) +{ + unsigned long sccr, sicrl, sicrh; + void __iomem *immap; + struct device_node *np = NULL; + int port0_is_dr = 0, port1_is_dr = 0; + const void *prop, *dr_mode; + + immap = ioremap(get_immrbase(), 0x1000); + if (!immap) + return -ENOMEM; + + /* Read registers */ + /* Note: DR and MPH must use the same clock setting in SCCR */ + sccr = in_be32(immap + MPC83XX_SCCR_OFFS) & ~MPC83XX_SCCR_USB_MASK; + sicrl = in_be32(immap + MPC83XX_SICRL_OFFS) & ~MPC834X_SICRL_USB_MASK; + sicrh = in_be32(immap + MPC83XX_SICRH_OFFS) & ~MPC834X_SICRH_USB_UTMI; + + np = of_find_compatible_node(NULL, "usb", "fsl-usb2-dr"); + if (np) { + sccr |= MPC83XX_SCCR_USB_DRCM_11; /* 1:3 */ + + prop = of_get_property(np, "phy_type", NULL); + if (prop && (!strcmp(prop, "utmi") || + !strcmp(prop, "utmi_wide"))) { + sicrl |= MPC834X_SICRL_USB0 | MPC834X_SICRL_USB1; + sicrh |= MPC834X_SICRH_USB_UTMI; + port1_is_dr = 1; + } else if (prop && !strcmp(prop, "serial")) { + dr_mode = of_get_property(np, "dr_mode", NULL); + if (dr_mode && !strcmp(dr_mode, "otg")) { + sicrl |= MPC834X_SICRL_USB0 | MPC834X_SICRL_USB1; + port1_is_dr = 1; + } else { + sicrl |= MPC834X_SICRL_USB0; + } + } else if (prop && !strcmp(prop, "ulpi")) { + sicrl |= MPC834X_SICRL_USB0; + } else { + printk(KERN_WARNING "834x USB PHY type not supported\n"); + } + port0_is_dr = 1; + of_node_put(np); + } + np = of_find_compatible_node(NULL, "usb", "fsl-usb2-mph"); + if (np) { + sccr |= MPC83XX_SCCR_USB_MPHCM_11; /* 1:3 */ + + prop = of_get_property(np, "port0", NULL); + if (prop) { + if (port0_is_dr) + printk(KERN_WARNING + "834x USB port0 can't be used by both DR and MPH!\n"); + sicrl |= MPC834X_SICRL_USB0; + } + prop = of_get_property(np, "port1", NULL); + if (prop) { + if (port1_is_dr) + printk(KERN_WARNING + "834x USB port1 can't be used by both DR and MPH!\n"); + sicrl |= MPC834X_SICRL_USB1; + } + of_node_put(np); + } + + /* Write back */ + out_be32(immap + MPC83XX_SCCR_OFFS, sccr); + out_be32(immap + MPC83XX_SICRL_OFFS, sicrl); + out_be32(immap + MPC83XX_SICRH_OFFS, sicrh); + + iounmap(immap); + return 0; +} +#endif /* CONFIG_MPC834x */ + +#ifdef CONFIG_PPC_MPC831x +int mpc831x_usb_cfg(void) +{ + u32 temp; + void __iomem *immap, *usb_regs; + struct device_node *np = NULL; + const void *prop; + struct resource res; + int ret = 0; +#ifdef CONFIG_USB_OTG + const void *dr_mode; +#endif + + np = of_find_compatible_node(NULL, "usb", "fsl-usb2-dr"); + if (!np) + return -ENODEV; + prop = of_get_property(np, "phy_type", NULL); + + /* Map IMMR space for pin and clock settings */ + immap = ioremap(get_immrbase(), 0x1000); + if (!immap) { + of_node_put(np); + return -ENOMEM; + } + + /* Configure clock */ + temp = in_be32(immap + MPC83XX_SCCR_OFFS); + temp &= ~MPC83XX_SCCR_USB_MASK; + temp |= MPC83XX_SCCR_USB_DRCM_11; /* 1:3 */ + out_be32(immap + MPC83XX_SCCR_OFFS, temp); + + /* Configure pin mux for ULPI. There is no pin mux for UTMI */ + if (!strcmp(prop, "ulpi")) { + temp = in_be32(immap + MPC83XX_SICRL_OFFS); + temp &= ~MPC831X_SICRL_USB_MASK; + temp |= MPC831X_SICRL_USB_ULPI; + out_be32(immap + MPC83XX_SICRL_OFFS, temp); + + temp = in_be32(immap + MPC83XX_SICRH_OFFS); + temp &= ~MPC831X_SICRH_USB_MASK; + temp |= MPC831X_SICRH_USB_ULPI; + out_be32(immap + MPC83XX_SICRH_OFFS, temp); + } + + iounmap(immap); + + /* Map USB SOC space */ + ret = of_address_to_resource(np, 0, &res); + if (ret) { + of_node_put(np); + return ret; + } + usb_regs = ioremap(res.start, res.end - res.start + 1); + + /* Using on-chip PHY */ + if (!strcmp(prop, "utmi_wide") || + !strcmp(prop, "utmi")) { + /* Set UTMI_PHY_EN, REFSEL to 48MHZ */ + out_be32(usb_regs + FSL_USB2_CONTROL_OFFS, + CONTROL_UTMI_PHY_EN | CONTROL_REFSEL_48MHZ); + /* Using external UPLI PHY */ + } else if (!strcmp(prop, "ulpi")) { + /* Set PHY_CLK_SEL to ULPI */ + temp = CONTROL_PHY_CLK_SEL_ULPI; +#ifdef CONFIG_USB_OTG + /* Set OTG_PORT */ + dr_mode = of_get_property(np, "dr_mode", NULL); + if (dr_mode && !strcmp(dr_mode, "otg")) + temp |= CONTROL_OTG_PORT; +#endif /* CONFIG_USB_OTG */ + out_be32(usb_regs + FSL_USB2_CONTROL_OFFS, temp); + } else { + printk(KERN_WARNING "831x USB PHY type not supported\n"); + ret = -EINVAL; + } + + iounmap(usb_regs); + of_node_put(np); + return ret; +} +#endif /* CONFIG_PPC_MPC831x */ diff --git a/arch/powerpc/platforms/85xx/misc.c b/arch/powerpc/platforms/85xx/misc.c index 3e62fcb04c1..4fe376e9c3b 100644 --- a/arch/powerpc/platforms/85xx/misc.c +++ b/arch/powerpc/platforms/85xx/misc.c @@ -13,11 +13,43 @@ #include <linux/irq.h> #include <linux/module.h> #include <asm/irq.h> +#include <asm/io.h> +#include <asm/prom.h> +#include <sysdev/fsl_soc.h> + +static __be32 __iomem *rstcr; extern void abort(void); +static int __init mpc85xx_rstcr(void) +{ + struct device_node *np; + np = of_find_node_by_name(NULL, "global-utilities"); + if ((np && of_get_property(np, "fsl,has-rstcr", NULL))) { + const u32 *prop = of_get_property(np, "reg", NULL); + if (prop) { + /* map reset control register + * 0xE00B0 is offset of reset control register + */ + rstcr = ioremap(get_immrbase() + *prop + 0xB0, 0xff); + if (!rstcr) + printk (KERN_EMERG "Error: reset control " + "register not mapped!\n"); + } + } else + printk (KERN_INFO "rstcr compatible register does not exist!\n"); + if (np) + of_node_put(np); + return 0; +} + +arch_initcall(mpc85xx_rstcr); + void mpc85xx_restart(char *cmd) { local_irq_disable(); + if (rstcr) + /* set reset control register */ + out_be32(rstcr, 0x2); /* HRESET_REQ */ abort(); } diff --git a/arch/powerpc/platforms/85xx/mpc8544_ds.c b/arch/powerpc/platforms/85xx/mpc8544_ds.c index bec84ffe708..6fb90aab879 100644 --- a/arch/powerpc/platforms/85xx/mpc8544_ds.c +++ b/arch/powerpc/platforms/85xx/mpc8544_ds.c @@ -61,24 +61,11 @@ void __init mpc8544_ds_pic_init(void) return; } - /* Alloc mpic structure and per isu has 16 INT entries. */ mpic = mpic_alloc(np, r.start, MPIC_PRIMARY | MPIC_WANTS_RESET | MPIC_BIG_ENDIAN, - 16, 64, " OPENPIC "); + 0, 256, " OpenPIC "); BUG_ON(mpic == NULL); - /* - * 48 Internal Interrupts - */ - mpic_assign_isu(mpic, 0, r.start + 0x10200); - mpic_assign_isu(mpic, 1, r.start + 0x10400); - mpic_assign_isu(mpic, 2, r.start + 0x10600); - - /* - * 16 External interrupts - */ - mpic_assign_isu(mpic, 3, r.start + 0x10000); - mpic_init(mpic); #ifdef CONFIG_PPC_I8259 diff --git a/arch/powerpc/platforms/85xx/mpc85xx.h b/arch/powerpc/platforms/85xx/mpc85xx.h index 83415db3337..7286ffac2c1 100644 --- a/arch/powerpc/platforms/85xx/mpc85xx.h +++ b/arch/powerpc/platforms/85xx/mpc85xx.h @@ -15,4 +15,4 @@ */ extern void mpc85xx_restart(char *); -extern int add_bridge(struct device_node *dev); +extern int mpc85xx_add_bridge(struct device_node *dev); diff --git a/arch/powerpc/platforms/85xx/mpc85xx_ads.c b/arch/powerpc/platforms/85xx/mpc85xx_ads.c index 5d27621f092..7235f702394 100644 --- a/arch/powerpc/platforms/85xx/mpc85xx_ads.c +++ b/arch/powerpc/platforms/85xx/mpc85xx_ads.c @@ -38,13 +38,9 @@ #include <asm/fs_pd.h> #endif -#ifndef CONFIG_PCI -unsigned long isa_io_base = 0; -unsigned long isa_mem_base = 0; -#endif - #ifdef CONFIG_PCI -static int mpc85xx_exclude_device(u_char bus, u_char devfn) +static int mpc85xx_exclude_device(struct pci_controller *hose, + u_char bus, u_char devfn) { if (bus == 0 && PCI_SLOT(devfn) == 0) return PCIBIOS_DEVICE_NOT_FOUND; @@ -91,30 +87,10 @@ static void __init mpc85xx_ads_pic_init(void) mpic = mpic_alloc(np, r.start, MPIC_PRIMARY | MPIC_WANTS_RESET | MPIC_BIG_ENDIAN, - 4, 0, " OpenPIC "); + 0, 256, " OpenPIC "); BUG_ON(mpic == NULL); of_node_put(np); - mpic_assign_isu(mpic, 0, r.start + 0x10200); - mpic_assign_isu(mpic, 1, r.start + 0x10280); - mpic_assign_isu(mpic, 2, r.start + 0x10300); - mpic_assign_isu(mpic, 3, r.start + 0x10380); - mpic_assign_isu(mpic, 4, r.start + 0x10400); - mpic_assign_isu(mpic, 5, r.start + 0x10480); - mpic_assign_isu(mpic, 6, r.start + 0x10500); - mpic_assign_isu(mpic, 7, r.start + 0x10580); - - /* Unused on this platform (leave room for 8548) */ - mpic_assign_isu(mpic, 8, r.start + 0x10600); - mpic_assign_isu(mpic, 9, r.start + 0x10680); - mpic_assign_isu(mpic, 10, r.start + 0x10700); - mpic_assign_isu(mpic, 11, r.start + 0x10780); - - /* External Interrupts */ - mpic_assign_isu(mpic, 12, r.start + 0x10000); - mpic_assign_isu(mpic, 13, r.start + 0x10080); - mpic_assign_isu(mpic, 14, r.start + 0x10100); - mpic_init(mpic); #ifdef CONFIG_CPM2 @@ -241,7 +217,7 @@ static void __init mpc85xx_ads_setup_arch(void) #ifdef CONFIG_PCI for (np = NULL; (np = of_find_node_by_type(np, "pci")) != NULL;) - add_bridge(np); + mpc85xx_add_bridge(np); ppc_md.pci_exclude_device = mpc85xx_exclude_device; #endif } diff --git a/arch/powerpc/platforms/85xx/mpc85xx_cds.c b/arch/powerpc/platforms/85xx/mpc85xx_cds.c index 1490eb3ce0d..50c8d645836 100644 --- a/arch/powerpc/platforms/85xx/mpc85xx_cds.c +++ b/arch/powerpc/platforms/85xx/mpc85xx_cds.c @@ -47,11 +47,6 @@ #include <sysdev/fsl_soc.h> #include "mpc85xx.h" -#ifndef CONFIG_PCI -unsigned long isa_io_base = 0; -unsigned long isa_mem_base = 0; -#endif - static int cds_pci_slot = 2; static volatile u8 *cadmus; @@ -60,15 +55,11 @@ static volatile u8 *cadmus; #define ARCADIA_HOST_BRIDGE_IDSEL 17 #define ARCADIA_2ND_BRIDGE_IDSEL 3 -extern int mpc85xx_pci2_busno; - -static int mpc85xx_exclude_device(u_char bus, u_char devfn) +static int mpc85xx_exclude_device(struct pci_controller *hose, + u_char bus, u_char devfn) { - if (bus == 0 && PCI_SLOT(devfn) == 0) + if ((bus == hose->first_busno) && PCI_SLOT(devfn) == 0) return PCIBIOS_DEVICE_NOT_FOUND; - if (mpc85xx_pci2_busno) - if (bus == (mpc85xx_pci2_busno) && PCI_SLOT(devfn) == 0) - return PCIBIOS_DEVICE_NOT_FOUND; /* We explicitly do not go past the Tundra 320 Bridge */ if ((bus == 1) && (PCI_SLOT(devfn) == ARCADIA_2ND_BRIDGE_IDSEL)) return PCIBIOS_DEVICE_NOT_FOUND; @@ -78,52 +69,44 @@ static int mpc85xx_exclude_device(u_char bus, u_char devfn) return PCIBIOS_SUCCESSFUL; } -static void __init mpc85xx_cds_pcibios_fixup(void) +static void __init mpc85xx_cds_pci_irq_fixup(struct pci_dev *dev) { - struct pci_dev *dev; - u_char c; - - if ((dev = pci_get_device(PCI_VENDOR_ID_VIA, - PCI_DEVICE_ID_VIA_82C586_1, NULL))) { + u_char c; + if (dev->vendor == PCI_VENDOR_ID_VIA) { + switch (dev->device) { + case PCI_DEVICE_ID_VIA_82C586_1: + /* + * U-Boot does not set the enable bits + * for the IDE device. Force them on here. + */ + pci_read_config_byte(dev, 0x40, &c); + c |= 0x03; /* IDE: Chip Enable Bits */ + pci_write_config_byte(dev, 0x40, c); + + /* + * Since only primary interface works, force the + * IDE function to standard primary IDE interrupt + * w/ 8259 offset + */ + dev->irq = 14; + pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); + break; /* - * U-Boot does not set the enable bits - * for the IDE device. Force them on here. + * Force legacy USB interrupt routing */ - pci_read_config_byte(dev, 0x40, &c); - c |= 0x03; /* IDE: Chip Enable Bits */ - pci_write_config_byte(dev, 0x40, c); - - /* - * Since only primary interface works, force the - * IDE function to standard primary IDE interrupt - * w/ 8259 offset + case PCI_DEVICE_ID_VIA_82C586_2: + /* There are two USB controllers. + * Identify them by functon number */ - dev->irq = 14; - pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); - pci_dev_put(dev); - } - - /* - * Force legacy USB interrupt routing - */ - if ((dev = pci_get_device(PCI_VENDOR_ID_VIA, - PCI_DEVICE_ID_VIA_82C586_2, NULL))) { - dev->irq = 10; - pci_write_config_byte(dev, PCI_INTERRUPT_LINE, 10); - pci_dev_put(dev); - } - - if ((dev = pci_get_device(PCI_VENDOR_ID_VIA, - PCI_DEVICE_ID_VIA_82C586_2, dev))) { - dev->irq = 11; - pci_write_config_byte(dev, PCI_INTERRUPT_LINE, 11); - pci_dev_put(dev); + if (PCI_FUNC(dev->devfn)) + dev->irq = 11; + else + dev->irq = 10; + pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); + default: + break; + } } - - /* Now map all the PCI irqs */ - dev = NULL; - for_each_pci_dev(dev) - pci_read_irq_line(dev); } #ifdef CONFIG_PPC_I8259 @@ -165,33 +148,12 @@ static void __init mpc85xx_cds_pic_init(void) mpic = mpic_alloc(np, r.start, MPIC_PRIMARY | MPIC_WANTS_RESET | MPIC_BIG_ENDIAN, - 4, 0, " OpenPIC "); + 0, 256, " OpenPIC "); BUG_ON(mpic == NULL); /* Return the mpic node */ of_node_put(np); - mpic_assign_isu(mpic, 0, r.start + 0x10200); - mpic_assign_isu(mpic, 1, r.start + 0x10280); - mpic_assign_isu(mpic, 2, r.start + 0x10300); - mpic_assign_isu(mpic, 3, r.start + 0x10380); - mpic_assign_isu(mpic, 4, r.start + 0x10400); - mpic_assign_isu(mpic, 5, r.start + 0x10480); - mpic_assign_isu(mpic, 6, r.start + 0x10500); - mpic_assign_isu(mpic, 7, r.start + 0x10580); - - /* Used only for 8548 so far, but no harm in - * allocating them for everyone */ - mpic_assign_isu(mpic, 8, r.start + 0x10600); - mpic_assign_isu(mpic, 9, r.start + 0x10680); - mpic_assign_isu(mpic, 10, r.start + 0x10700); - mpic_assign_isu(mpic, 11, r.start + 0x10780); - - /* External Interrupts */ - mpic_assign_isu(mpic, 12, r.start + 0x10000); - mpic_assign_isu(mpic, 13, r.start + 0x10080); - mpic_assign_isu(mpic, 14, r.start + 0x10100); - mpic_init(mpic); #ifdef CONFIG_PPC_I8259 @@ -257,9 +219,9 @@ static void __init mpc85xx_cds_setup_arch(void) #ifdef CONFIG_PCI for (np = NULL; (np = of_find_node_by_type(np, "pci")) != NULL;) - add_bridge(np); + mpc85xx_add_bridge(np); - ppc_md.pcibios_fixup = mpc85xx_cds_pcibios_fixup; + ppc_md.pci_irq_fixup = mpc85xx_cds_pci_irq_fixup; ppc_md.pci_exclude_device = mpc85xx_exclude_device; #endif } diff --git a/arch/powerpc/platforms/85xx/mpc85xx_mds.c b/arch/powerpc/platforms/85xx/mpc85xx_mds.c index e3dddbfe66f..004b80bd0b8 100644 --- a/arch/powerpc/platforms/85xx/mpc85xx_mds.c +++ b/arch/powerpc/platforms/85xx/mpc85xx_mds.c @@ -59,11 +59,6 @@ #define DBG(fmt...) #endif -#ifndef CONFIG_PCI -unsigned long isa_io_base = 0; -unsigned long isa_mem_base = 0; -#endif - /* ************************************************************************ * * Setup the architecture @@ -100,7 +95,7 @@ static void __init mpc85xx_mds_setup_arch(void) #ifdef CONFIG_PCI for (np = NULL; (np = of_find_node_by_type(np, "pci")) != NULL;) { - add_bridge(np); + mpc85xx_add_bridge(np); } of_node_put(np); #endif @@ -181,29 +176,10 @@ static void __init mpc85xx_mds_pic_init(void) mpic = mpic_alloc(np, r.start, MPIC_PRIMARY | MPIC_WANTS_RESET | MPIC_BIG_ENDIAN, - 4, 0, " OpenPIC "); + 0, 256, " OpenPIC "); BUG_ON(mpic == NULL); of_node_put(np); - /* Internal Interrupts */ - mpic_assign_isu(mpic, 0, r.start + 0x10200); - mpic_assign_isu(mpic, 1, r.start + 0x10280); - mpic_assign_isu(mpic, 2, r.start + 0x10300); - mpic_assign_isu(mpic, 3, r.start + 0x10380); - mpic_assign_isu(mpic, 4, r.start + 0x10400); - mpic_assign_isu(mpic, 5, r.start + 0x10480); - mpic_assign_isu(mpic, 6, r.start + 0x10500); - mpic_assign_isu(mpic, 7, r.start + 0x10580); - mpic_assign_isu(mpic, 8, r.start + 0x10600); - mpic_assign_isu(mpic, 9, r.start + 0x10680); - mpic_assign_isu(mpic, 10, r.start + 0x10700); - mpic_assign_isu(mpic, 11, r.start + 0x10780); - - /* External Interrupts */ - mpic_assign_isu(mpic, 12, r.start + 0x10000); - mpic_assign_isu(mpic, 13, r.start + 0x10080); - mpic_assign_isu(mpic, 14, r.start + 0x10100); - mpic_init(mpic); #ifdef CONFIG_QUICC_ENGINE diff --git a/arch/powerpc/platforms/85xx/pci.c b/arch/powerpc/platforms/85xx/pci.c index 48f17e23d77..8118417b736 100644 --- a/arch/powerpc/platforms/85xx/pci.c +++ b/arch/powerpc/platforms/85xx/pci.c @@ -33,10 +33,8 @@ #define DBG(x...) #endif -int mpc85xx_pci2_busno = 0; - #ifdef CONFIG_PCI -int __init add_bridge(struct device_node *dev) +int __init mpc85xx_add_bridge(struct device_node *dev) { int len; struct pci_controller *hose; @@ -57,11 +55,10 @@ int __init add_bridge(struct device_node *dev) " bus 0\n", dev->full_name); } - hose = pcibios_alloc_controller(); + pci_assign_all_buses = 1; + hose = pcibios_alloc_controller(dev); if (!hose) return -ENOMEM; - hose->arch_data = dev; - hose->set_cfg_type = 1; hose->first_busno = bus_range ? bus_range[0] : 0; hose->last_busno = bus_range ? bus_range[1] : 0xff; @@ -74,8 +71,6 @@ int __init add_bridge(struct device_node *dev) if ((rsrc.start & 0xfffff) == 0x9000) { setup_indirect_pci(hose, immr + 0x9000, immr + 0x9004); primary = 0; - hose->bus_offset = hose->first_busno; - mpc85xx_pci2_busno = hose->first_busno; } printk(KERN_INFO "Found MPC85xx PCI host bridge at 0x%016llx. " diff --git a/arch/powerpc/platforms/86xx/Kconfig b/arch/powerpc/platforms/86xx/Kconfig index d1bcff50046..0faebfdc159 100644 --- a/arch/powerpc/platforms/86xx/Kconfig +++ b/arch/powerpc/platforms/86xx/Kconfig @@ -1,5 +1,5 @@ choice - prompt "Machine Type" + prompt "86xx Board Type" depends on PPC_86xx default MPC8641_HPCN diff --git a/arch/powerpc/platforms/86xx/mpc86xx.h b/arch/powerpc/platforms/86xx/mpc86xx.h index 2834462590b..23f7ed2a7f8 100644 --- a/arch/powerpc/platforms/86xx/mpc86xx.h +++ b/arch/powerpc/platforms/86xx/mpc86xx.h @@ -15,15 +15,10 @@ * mpc86xx_* files. Mostly for use by mpc86xx_setup(). */ -extern int add_bridge(struct device_node *dev); +extern int mpc86xx_add_bridge(struct device_node *dev); -extern int mpc86xx_exclude_device(u_char bus, u_char devfn); - -extern void setup_indirect_pcie(struct pci_controller *hose, - u32 cfg_addr, u32 cfg_data); -extern void setup_indirect_pcie_nomap(struct pci_controller *hose, - void __iomem *cfg_addr, - void __iomem *cfg_data); +extern int mpc86xx_exclude_device(struct pci_controller *hose, + u_char bus, u_char devfn); extern void __init mpc86xx_smp_init(void); diff --git a/arch/powerpc/platforms/86xx/mpc86xx_hpcn.c b/arch/powerpc/platforms/86xx/mpc86xx_hpcn.c index 1051702c8d4..5b01ec7c13d 100644 --- a/arch/powerpc/platforms/86xx/mpc86xx_hpcn.c +++ b/arch/powerpc/platforms/86xx/mpc86xx_hpcn.c @@ -44,13 +44,6 @@ #define DBG(fmt...) do { } while(0) #endif -#ifndef CONFIG_PCI -unsigned long isa_io_base = 0; -unsigned long isa_mem_base = 0; -unsigned long pci_dram_offset = 0; -#endif - - #ifdef CONFIG_PCI static void mpc86xx_8259_cascade(unsigned int irq, struct irq_desc *desc) { @@ -81,22 +74,9 @@ mpc86xx_hpcn_init_irq(void) /* Alloc mpic structure and per isu has 16 INT entries. */ mpic1 = mpic_alloc(np, res.start, MPIC_PRIMARY | MPIC_WANTS_RESET | MPIC_BIG_ENDIAN, - 16, NR_IRQS - 4, - " MPIC "); + 0, 256, " MPIC "); BUG_ON(mpic1 == NULL); - mpic_assign_isu(mpic1, 0, res.start + 0x10000); - - /* 48 Internal Interrupts */ - mpic_assign_isu(mpic1, 1, res.start + 0x10200); - mpic_assign_isu(mpic1, 2, res.start + 0x10400); - mpic_assign_isu(mpic1, 3, res.start + 0x10600); - - /* 16 External interrupts - * Moving them from [0 - 15] to [64 - 79] - */ - mpic_assign_isu(mpic1, 4, res.start + 0x10000); - mpic_init(mpic1); #ifdef CONFIG_PCI @@ -319,6 +299,7 @@ static void __devinit quirk_uli5229(struct pci_dev *dev) { unsigned short temp; pci_write_config_word(dev, 0x04, 0x0405); + dev->class &= ~0x5; pci_read_config_word(dev, 0x4a, &temp); temp |= 0x1000; pci_write_config_word(dev, 0x4a, temp); @@ -364,9 +345,7 @@ mpc86xx_hpcn_setup_arch(void) #ifdef CONFIG_PCI for (np = NULL; (np = of_find_node_by_type(np, "pci")) != NULL;) - add_bridge(np); - - ppc_md.pci_exclude_device = mpc86xx_exclude_device; + mpc86xx_add_bridge(np); #endif printk("MPC86xx HPCN board from Freescale Semiconductor\n"); diff --git a/arch/powerpc/platforms/86xx/pci.c b/arch/powerpc/platforms/86xx/pci.c index 8235c562661..73cd5b05a84 100644 --- a/arch/powerpc/platforms/86xx/pci.c +++ b/arch/powerpc/platforms/86xx/pci.c @@ -122,7 +122,6 @@ static void __init mpc86xx_setup_pcie(struct pci_controller *hose, u32 pcie_offset, u32 pcie_size) { u16 cmd; - unsigned int temps; DBG("PCIE host controller register offset 0x%08x, size 0x%08x.\n", pcie_offset, pcie_size); @@ -133,22 +132,49 @@ mpc86xx_setup_pcie(struct pci_controller *hose, u32 pcie_offset, u32 pcie_size) early_write_config_word(hose, 0, 0, PCI_COMMAND, cmd); early_write_config_byte(hose, 0, 0, PCI_LATENCY_TIMER, 0x80); - - /* PCIE Bus, Fix the MPC8641D host bridge's location to bus 0xFF. */ - early_read_config_dword(hose, 0, 0, PCI_PRIMARY_BUS, &temps); - temps = (temps & 0xff000000) | (0xff) | (0x0 << 8) | (0xfe << 16); - early_write_config_dword(hose, 0, 0, PCI_PRIMARY_BUS, temps); } -int mpc86xx_exclude_device(u_char bus, u_char devfn) +static void __devinit quirk_fsl_pcie_transparent(struct pci_dev *dev) { - if (bus == 0 && PCI_SLOT(devfn) == 0) - return PCIBIOS_DEVICE_NOT_FOUND; + struct resource *res; + int i, res_idx = PCI_BRIDGE_RESOURCES; + struct pci_controller *hose; - return PCIBIOS_SUCCESSFUL; + /* + * Make the bridge be transparent. + */ + dev->transparent = 1; + + hose = pci_bus_to_host(dev->bus); + if (!hose) { + printk(KERN_ERR "Can't find hose for bus %d\n", + dev->bus->number); + return; + } + + if (hose->io_resource.flags) { + res = &dev->resource[res_idx++]; + res->start = hose->io_resource.start; + res->end = hose->io_resource.end; + res->flags = hose->io_resource.flags; + } + + for (i = 0; i < 3; i++) { + res = &dev->resource[res_idx + i]; + res->start = hose->mem_resources[i].start; + res->end = hose->mem_resources[i].end; + res->flags = hose->mem_resources[i].flags; + } } -int __init add_bridge(struct device_node *dev) + +DECLARE_PCI_FIXUP_EARLY(0x1957, 0x7010, quirk_fsl_pcie_transparent); +DECLARE_PCI_FIXUP_EARLY(0x1957, 0x7011, quirk_fsl_pcie_transparent); + +#define PCIE_LTSSM 0x404 /* PCIe Link Training and Status */ +#define PCIE_LTSSM_L0 0x16 /* L0 state */ + +int __init mpc86xx_add_bridge(struct device_node *dev) { int len; struct pci_controller *hose; @@ -156,6 +182,7 @@ int __init add_bridge(struct device_node *dev) const int *bus_range; int has_address = 0; int primary = 0; + u16 val; DBG("Adding PCIE host bridge %s\n", dev->full_name); @@ -168,17 +195,23 @@ int __init add_bridge(struct device_node *dev) printk(KERN_WARNING "Can't get bus-range for %s, assume" " bus 0\n", dev->full_name); - hose = pcibios_alloc_controller(); + pci_assign_all_buses = 1; + hose = pcibios_alloc_controller(dev); if (!hose) return -ENOMEM; - hose->arch_data = dev; - hose->set_cfg_type = 1; - /* last_busno = 0xfe cause by MPC8641 PCIE bug */ + hose->indirect_type = PPC_INDIRECT_TYPE_EXT_REG | + PPC_INDIRECT_TYPE_SURPRESS_PRIMARY_BUS; + hose->first_busno = bus_range ? bus_range[0] : 0x0; - hose->last_busno = bus_range ? bus_range[1] : 0xfe; + hose->last_busno = bus_range ? bus_range[1] : 0xff; + + setup_indirect_pci(hose, rsrc.start, rsrc.start + 0x4); - setup_indirect_pcie(hose, rsrc.start, rsrc.start + 0x4); + /* Probe the hose link training status */ + early_read_config_word(hose, 0, 0, PCIE_LTSSM, &val); + if (val < PCIE_LTSSM_L0) + return -ENXIO; /* Setup the PCIE host controller. */ mpc86xx_setup_pcie(hose, rsrc.start, rsrc.end - rsrc.start + 1); diff --git a/arch/powerpc/platforms/8xx/m8xx_setup.c b/arch/powerpc/platforms/8xx/m8xx_setup.c index 0901dbada35..f1693550c70 100644 --- a/arch/powerpc/platforms/8xx/m8xx_setup.c +++ b/arch/powerpc/platforms/8xx/m8xx_setup.c @@ -32,6 +32,7 @@ #include <linux/root_dev.h> #include <linux/time.h> #include <linux/rtc.h> +#include <linux/fsl_devices.h> #include <asm/mmu.h> #include <asm/reg.h> @@ -49,6 +50,10 @@ #include "sysdev/mpc8xx_pic.h" +#ifdef CONFIG_PCMCIA_M8XX +struct mpc8xx_pcmcia_ops m8xx_pcmcia_ops; +#endif + void m8xx_calibrate_decr(void); extern void m8xx_wdt_handler_install(bd_t *bp); extern int cpm_pic_init(void); diff --git a/arch/powerpc/platforms/8xx/mpc885ads_setup.c b/arch/powerpc/platforms/8xx/mpc885ads_setup.c index c36e475d93d..5a808d611ae 100644 --- a/arch/powerpc/platforms/8xx/mpc885ads_setup.c +++ b/arch/powerpc/platforms/8xx/mpc885ads_setup.c @@ -22,6 +22,7 @@ #include <linux/fs_enet_pd.h> #include <linux/fs_uart_pd.h> +#include <linux/fsl_devices.h> #include <linux/mii.h> #include <asm/delay.h> @@ -39,7 +40,7 @@ #include <asm/prom.h> extern void cpm_reset(void); -extern void mpc8xx_show_cpuinfo(struct seq_file*); +extern void mpc8xx_show_cpuinfo(struct seq_file *); extern void mpc8xx_restart(char *cmd); extern void mpc8xx_calibrate_decr(void); extern int mpc8xx_set_rtc_time(struct rtc_time *tm); @@ -47,9 +48,73 @@ extern void mpc8xx_get_rtc_time(struct rtc_time *tm); extern void m8xx_pic_init(void); extern unsigned int mpc8xx_get_irq(void); -static void init_smc1_uart_ioports(struct fs_uart_platform_info* fpi); -static void init_smc2_uart_ioports(struct fs_uart_platform_info* fpi); -static void init_scc3_ioports(struct fs_platform_info* ptr); +static void init_smc1_uart_ioports(struct fs_uart_platform_info *fpi); +static void init_smc2_uart_ioports(struct fs_uart_platform_info *fpi); +static void init_scc3_ioports(struct fs_platform_info *ptr); + +#ifdef CONFIG_PCMCIA_M8XX +static void pcmcia_hw_setup(int slot, int enable) +{ + unsigned *bcsr_io; + + bcsr_io = ioremap(BCSR1, sizeof(unsigned long)); + if (enable) + clrbits32(bcsr_io, BCSR1_PCCEN); + else + setbits32(bcsr_io, BCSR1_PCCEN); + + iounmap(bcsr_io); +} + +static int pcmcia_set_voltage(int slot, int vcc, int vpp) +{ + u32 reg = 0; + unsigned *bcsr_io; + + bcsr_io = ioremap(BCSR1, sizeof(unsigned long)); + + switch (vcc) { + case 0: + break; + case 33: + reg |= BCSR1_PCCVCC0; + break; + case 50: + reg |= BCSR1_PCCVCC1; + break; + default: + return 1; + } + + switch (vpp) { + case 0: + break; + case 33: + case 50: + if (vcc == vpp) + reg |= BCSR1_PCCVPP1; + else + return 1; + break; + case 120: + if ((vcc == 33) || (vcc == 50)) + reg |= BCSR1_PCCVPP0; + else + return 1; + default: + return 1; + } + + /* first, turn off all power */ + clrbits32(bcsr_io, 0x00610000); + + /* enable new powersettings */ + setbits32(bcsr_io, reg); + + iounmap(bcsr_io); + return 0; +} +#endif void __init mpc885ads_board_setup(void) { @@ -62,7 +127,7 @@ void __init mpc885ads_board_setup(void) #endif bcsr_io = ioremap(BCSR1, sizeof(unsigned long)); - cp = (cpm8xx_t *)immr_map(im_cpm); + cp = (cpm8xx_t *) immr_map(im_cpm); if (bcsr_io == NULL) { printk(KERN_CRIT "Could not remap BCSR\n"); @@ -75,13 +140,13 @@ void __init mpc885ads_board_setup(void) out_8(&(cp->cp_smc[0].smc_smcm), tmpval8); clrbits16(&cp->cp_smc[0].smc_smcmr, SMCMR_REN | SMCMR_TEN); /* brg1 */ #else - setbits32(bcsr_io,BCSR1_RS232EN_1); + setbits32(bcsr_io, BCSR1_RS232EN_1); out_be16(&cp->cp_smc[0].smc_smcmr, 0); out_8(&cp->cp_smc[0].smc_smce, 0); #endif #ifdef CONFIG_SERIAL_CPM_SMC2 - clrbits32(bcsr_io,BCSR1_RS232EN_2); + clrbits32(bcsr_io, BCSR1_RS232EN_2); clrbits32(&cp->cp_simode, 0xe0000000 >> 1); setbits32(&cp->cp_simode, 0x20000000 >> 1); /* brg2 */ tmpval8 = in_8(&(cp->cp_smc[1].smc_smcm)) | (SMCM_RX | SMCM_TX); @@ -90,7 +155,7 @@ void __init mpc885ads_board_setup(void) init_smc2_uart_ioports(0); #else - setbits32(bcsr_io,BCSR1_RS232EN_2); + setbits32(bcsr_io, BCSR1_RS232EN_2); out_be16(&cp->cp_smc[1].smc_smcmr, 0); out_8(&cp->cp_smc[1].smc_smce, 0); #endif @@ -99,29 +164,34 @@ void __init mpc885ads_board_setup(void) #ifdef CONFIG_FS_ENET /* use MDC for MII (common) */ - io_port = (iop8xx_t*)immr_map(im_ioport); + io_port = (iop8xx_t *) immr_map(im_ioport); setbits16(&io_port->iop_pdpar, 0x0080); clrbits16(&io_port->iop_pddir, 0x0080); bcsr_io = ioremap(BCSR5, sizeof(unsigned long)); - clrbits32(bcsr_io,BCSR5_MII1_EN); - clrbits32(bcsr_io,BCSR5_MII1_RST); + clrbits32(bcsr_io, BCSR5_MII1_EN); + clrbits32(bcsr_io, BCSR5_MII1_RST); #ifndef CONFIG_FC_ENET_HAS_SCC - clrbits32(bcsr_io,BCSR5_MII2_EN); - clrbits32(bcsr_io,BCSR5_MII2_RST); + clrbits32(bcsr_io, BCSR5_MII2_EN); + clrbits32(bcsr_io, BCSR5_MII2_RST); #endif iounmap(bcsr_io); immr_unmap(io_port); #endif -} +#ifdef CONFIG_PCMCIA_M8XX + /*Set up board specific hook-ups */ + m8xx_pcmcia_ops.hw_ctrl = pcmcia_hw_setup; + m8xx_pcmcia_ops.voltage_set = pcmcia_set_voltage; +#endif +} -static void init_fec1_ioports(struct fs_platform_info* ptr) +static void init_fec1_ioports(struct fs_platform_info *ptr) { - cpm8xx_t *cp = (cpm8xx_t *)immr_map(im_cpm); - iop8xx_t *io_port = (iop8xx_t *)immr_map(im_ioport); + cpm8xx_t *cp = (cpm8xx_t *) immr_map(im_cpm); + iop8xx_t *io_port = (iop8xx_t *) immr_map(im_ioport); /* configure FEC1 pins */ setbits16(&io_port->iop_papar, 0xf830); @@ -143,11 +213,10 @@ static void init_fec1_ioports(struct fs_platform_info* ptr) immr_unmap(cp); } - -static void init_fec2_ioports(struct fs_platform_info* ptr) +static void init_fec2_ioports(struct fs_platform_info *ptr) { - cpm8xx_t *cp = (cpm8xx_t *)immr_map(im_cpm); - iop8xx_t *io_port = (iop8xx_t *)immr_map(im_ioport); + cpm8xx_t *cp = (cpm8xx_t *) immr_map(im_cpm); + iop8xx_t *io_port = (iop8xx_t *) immr_map(im_ioport); /* configure FEC2 pins */ setbits32(&cp->cp_pepar, 0x0003fffc); @@ -177,15 +246,15 @@ void init_fec_ioports(struct fs_platform_info *fpi) } } -static void init_scc3_ioports(struct fs_platform_info* fpi) +static void init_scc3_ioports(struct fs_platform_info *fpi) { unsigned *bcsr_io; iop8xx_t *io_port; cpm8xx_t *cp; bcsr_io = ioremap(BCSR_ADDR, BCSR_SIZE); - io_port = (iop8xx_t *)immr_map(im_ioport); - cp = (cpm8xx_t *)immr_map(im_cpm); + io_port = (iop8xx_t *) immr_map(im_ioport); + cp = (cpm8xx_t *) immr_map(im_cpm); if (bcsr_io == NULL) { printk(KERN_CRIT "Could not remap BCSR\n"); @@ -194,9 +263,9 @@ static void init_scc3_ioports(struct fs_platform_info* fpi) /* Enable the PHY. */ - clrbits32(bcsr_io+4, BCSR4_ETH10_RST); + clrbits32(bcsr_io + 4, BCSR4_ETH10_RST); udelay(1000); - setbits32(bcsr_io+4, BCSR4_ETH10_RST); + setbits32(bcsr_io + 4, BCSR4_ETH10_RST); /* Configure port A pins for Txd and Rxd. */ setbits16(&io_port->iop_papar, PA_ENET_RXD | PA_ENET_TXD); @@ -212,8 +281,7 @@ static void init_scc3_ioports(struct fs_platform_info* fpi) */ setbits32(&cp->cp_pepar, PE_ENET_TCLK | PE_ENET_RCLK); clrbits32(&cp->cp_pepar, PE_ENET_TENA); - clrbits32(&cp->cp_pedir, - PE_ENET_TCLK | PE_ENET_RCLK | PE_ENET_TENA); + clrbits32(&cp->cp_pedir, PE_ENET_TCLK | PE_ENET_RCLK | PE_ENET_TENA); clrbits32(&cp->cp_peso, PE_ENET_TCLK | PE_ENET_RCLK); setbits32(&cp->cp_peso, PE_ENET_TENA); @@ -237,7 +305,7 @@ static void init_scc3_ioports(struct fs_platform_info* fpi) clrbits32(&cp->cp_pedir, PE_ENET_TENA); setbits32(&cp->cp_peso, PE_ENET_TENA); - setbits32(bcsr_io+4, BCSR1_ETHEN); + setbits32(bcsr_io + 4, BCSR1_ETHEN); iounmap(bcsr_io); immr_unmap(io_port); immr_unmap(cp); @@ -257,50 +325,48 @@ void init_scc_ioports(struct fs_platform_info *fpi) } } - - -static void init_smc1_uart_ioports(struct fs_uart_platform_info* ptr) +static void init_smc1_uart_ioports(struct fs_uart_platform_info *ptr) { - unsigned *bcsr_io; + unsigned *bcsr_io; cpm8xx_t *cp; - cp = (cpm8xx_t *)immr_map(im_cpm); + cp = (cpm8xx_t *) immr_map(im_cpm); setbits32(&cp->cp_pepar, 0x000000c0); clrbits32(&cp->cp_pedir, 0x000000c0); clrbits32(&cp->cp_peso, 0x00000040); setbits32(&cp->cp_peso, 0x00000080); immr_unmap(cp); - bcsr_io = ioremap(BCSR1, sizeof(unsigned long)); + bcsr_io = ioremap(BCSR1, sizeof(unsigned long)); - if (bcsr_io == NULL) { - printk(KERN_CRIT "Could not remap BCSR1\n"); - return; - } - clrbits32(bcsr_io,BCSR1_RS232EN_1); - iounmap(bcsr_io); + if (bcsr_io == NULL) { + printk(KERN_CRIT "Could not remap BCSR1\n"); + return; + } + clrbits32(bcsr_io, BCSR1_RS232EN_1); + iounmap(bcsr_io); } -static void init_smc2_uart_ioports(struct fs_uart_platform_info* fpi) +static void init_smc2_uart_ioports(struct fs_uart_platform_info *fpi) { - unsigned *bcsr_io; + unsigned *bcsr_io; cpm8xx_t *cp; - cp = (cpm8xx_t *)immr_map(im_cpm); + cp = (cpm8xx_t *) immr_map(im_cpm); setbits32(&cp->cp_pepar, 0x00000c00); clrbits32(&cp->cp_pedir, 0x00000c00); clrbits32(&cp->cp_peso, 0x00000400); setbits32(&cp->cp_peso, 0x00000800); immr_unmap(cp); - bcsr_io = ioremap(BCSR1, sizeof(unsigned long)); + bcsr_io = ioremap(BCSR1, sizeof(unsigned long)); - if (bcsr_io == NULL) { - printk(KERN_CRIT "Could not remap BCSR1\n"); - return; - } - clrbits32(bcsr_io,BCSR1_RS232EN_2); - iounmap(bcsr_io); + if (bcsr_io == NULL) { + printk(KERN_CRIT "Could not remap BCSR1\n"); + return; + } + clrbits32(bcsr_io, BCSR1_RS232EN_2); + iounmap(bcsr_io); } void init_smc_ioports(struct fs_uart_platform_info *data) @@ -373,15 +439,11 @@ static int __init mpc885ads_probe(void) return 1; } -define_machine(mpc885_ads) { - .name = "MPC885 ADS", - .probe = mpc885ads_probe, - .setup_arch = mpc885ads_setup_arch, - .init_IRQ = m8xx_pic_init, - .show_cpuinfo = mpc8xx_show_cpuinfo, - .get_irq = mpc8xx_get_irq, - .restart = mpc8xx_restart, - .calibrate_decr = mpc8xx_calibrate_decr, - .set_rtc_time = mpc8xx_set_rtc_time, - .get_rtc_time = mpc8xx_get_rtc_time, -}; +define_machine(mpc885_ads) +{ +.name = "MPC885 ADS",.probe = mpc885ads_probe,.setup_arch = + mpc885ads_setup_arch,.init_IRQ = + m8xx_pic_init,.show_cpuinfo = mpc8xx_show_cpuinfo,.get_irq = + mpc8xx_get_irq,.restart = mpc8xx_restart,.calibrate_decr = + mpc8xx_calibrate_decr,.set_rtc_time = + mpc8xx_set_rtc_time,.get_rtc_time = mpc8xx_get_rtc_time,}; diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig index 361acfa2894..33545d352e9 100644 --- a/arch/powerpc/platforms/Kconfig +++ b/arch/powerpc/platforms/Kconfig @@ -2,7 +2,7 @@ menu "Platform support" choice prompt "Machine type" - depends on PPC64 || CLASSIC32 + depends on PPC64 || 6xx default PPC_MULTIPLATFORM config PPC_MULTIPLATFORM @@ -16,15 +16,30 @@ config EMBEDDED6xx bool "Embedded 6xx/7xx/7xxx-based board" depends on PPC32 && (BROKEN||BROKEN_ON_SMP) -config APUS - bool "Amiga-APUS" - depends on PPC32 && BROKEN +config PPC_82xx + bool "Freescale 82xx" + depends on 6xx + +config PPC_83xx + bool "Freescale 83xx" + depends on 6xx + select FSL_SOC + select 83xx + select WANT_DEVICE_TREE + +config PPC_86xx + bool "Freescale 86xx" + depends on 6xx + select FSL_SOC + select ALTIVEC help - Select APUS if configuring for a PowerUP Amiga. - More information is available at: - <http://linux-apus.sourceforge.net/>. + The Freescale E600 SoCs have 74xx cores. endchoice +config CLASSIC32 + def_bool y + depends on 6xx && PPC_MULTIPLATFORM + source "arch/powerpc/platforms/pseries/Kconfig" source "arch/powerpc/platforms/iseries/Kconfig" source "arch/powerpc/platforms/chrp/Kconfig" diff --git a/arch/powerpc/platforms/Kconfig.cputype b/arch/powerpc/platforms/Kconfig.cputype new file mode 100644 index 00000000000..b8b5fde9466 --- /dev/null +++ b/arch/powerpc/platforms/Kconfig.cputype @@ -0,0 +1,221 @@ +config PPC64 + bool "64-bit kernel" + default n + help + This option selects whether a 32-bit or a 64-bit kernel + will be built. + +menu "Processor support" +choice + prompt "Processor Type" + depends on PPC32 + default 6xx + help + There are five families of 32 bit PowerPC chips supported. + The most common ones are the desktop and server CPUs (601, 603, + 604, 740, 750, 74xx) CPUs from Freescale and IBM, with their + embedded 52xx/82xx/83xx/86xx counterparts. + The other embeeded parts, namely 4xx, 8xx, e200 (55xx) and e500 + (85xx) each form a family of their own that is not compatible + with the others. + + If unsure, select 52xx/6xx/7xx/74xx/82xx/83xx/86xx. + +config 6xx + bool "52xx/6xx/7xx/74xx/82xx/83xx/86xx" + select PPC_FPU + +config PPC_85xx + bool "Freescale 85xx" + select E500 + select FSL_SOC + select 85xx + select WANT_DEVICE_TREE + +config PPC_8xx + bool "Freescale 8xx" + select FSL_SOC + select 8xx + +config 40x + bool "AMCC 40x" + select PPC_DCR_NATIVE + +config 44x + bool "AMCC 44x" + select PPC_DCR_NATIVE + select WANT_DEVICE_TREE + +config E200 + bool "Freescale e200" + +endchoice + +config POWER4_ONLY + bool "Optimize for POWER4" + depends on PPC64 + default n + ---help--- + Cause the compiler to optimize for POWER4/POWER5/PPC970 processors. + The resulting binary will not work on POWER3 or RS64 processors + when compiled with binutils 2.15 or later. + +config POWER3 + bool + depends on PPC64 + default y if !POWER4_ONLY + +config POWER4 + depends on PPC64 + def_bool y + +config 6xx + bool + +# this is temp to handle compat with arch=ppc +config 8xx + bool + +# this is temp to handle compat with arch=ppc +config 83xx + bool + +# this is temp to handle compat with arch=ppc +config 85xx + bool + +config E500 + bool + +config PPC_FPU + bool + default y if PPC64 + +config 4xx + bool + depends on 40x || 44x + default y + +config BOOKE + bool + depends on E200 || E500 || 44x + default y + +config FSL_BOOKE + bool + depends on E200 || E500 + default y + +config PTE_64BIT + bool + depends on 44x || E500 + default y if 44x + default y if E500 && PHYS_64BIT + +config PHYS_64BIT + bool 'Large physical address support' if E500 + depends on 44x || E500 + select RESOURCES_64BIT + default y if 44x + ---help--- + This option enables kernel support for larger than 32-bit physical + addresses. This features is not be available on all e500 cores. + + If in doubt, say N here. + +config ALTIVEC + bool "AltiVec Support" + depends on CLASSIC32 || POWER4 + ---help--- + This option enables kernel support for the Altivec extensions to the + PowerPC processor. The kernel currently supports saving and restoring + altivec registers, and turning on the 'altivec enable' bit so user + processes can execute altivec instructions. + + This option is only usefully if you have a processor that supports + altivec (G4, otherwise known as 74xx series), but does not have + any affect on a non-altivec cpu (it does, however add code to the + kernel). + + If in doubt, say Y here. + +config SPE + bool "SPE Support" + depends on E200 || E500 + default y + ---help--- + This option enables kernel support for the Signal Processing + Extensions (SPE) to the PowerPC processor. The kernel currently + supports saving and restoring SPE registers, and turning on the + 'spe enable' bit so user processes can execute SPE instructions. + + This option is only useful if you have a processor that supports + SPE (e500, otherwise known as 85xx series), but does not have any + effect on a non-spe cpu (it does, however add code to the kernel). + + If in doubt, say Y here. + +config PPC_STD_MMU + bool + depends on 6xx || POWER3 || POWER4 || PPC64 + default y + +config PPC_STD_MMU_32 + def_bool y + depends on PPC_STD_MMU && PPC32 + +config PPC_MM_SLICES + bool + default y if HUGETLB_PAGE + default n + +config VIRT_CPU_ACCOUNTING + bool "Deterministic task and CPU time accounting" + depends on PPC64 + default y + help + Select this option to enable more accurate task and CPU time + accounting. This is done by reading a CPU counter on each + kernel entry and exit and on transitions within the kernel + between system, softirq and hardirq state, so there is a + small performance impact. This also enables accounting of + stolen time on logically-partitioned systems running on + IBM POWER5-based machines. + + If in doubt, say Y here. + +config SMP + depends on PPC_STD_MMU + bool "Symmetric multi-processing support" + ---help--- + This enables support for systems with more than one CPU. If you have + a system with only one CPU, say N. If you have a system with more + than one CPU, say Y. Note that the kernel does not currently + support SMP machines with 603/603e/603ev or PPC750 ("G3") processors + since they have inadequate hardware support for multiprocessor + operation. + + If you say N here, the kernel will run on single and multiprocessor + machines, but will use only one CPU of a multiprocessor machine. If + you say Y here, the kernel will run on single-processor machines. + On a single-processor machine, the kernel will run faster if you say + N here. + + If you don't know what to do here, say N. + +config NR_CPUS + int "Maximum number of CPUs (2-128)" + range 2 128 + depends on SMP + default "32" if PPC64 + default "4" + +config NOT_COHERENT_CACHE + bool + depends on 4xx || 8xx || E200 + default y + +config CONFIG_CHECK_CACHE_COHERENCY + bool + +endmenu diff --git a/arch/powerpc/platforms/apus/Kconfig b/arch/powerpc/platforms/apus/Kconfig deleted file mode 100644 index 6bde3bffed8..00000000000 --- a/arch/powerpc/platforms/apus/Kconfig +++ /dev/null @@ -1,130 +0,0 @@ - -config AMIGA - bool - depends on APUS - default y - help - This option enables support for the Amiga series of computers. - -config ZORRO - bool - depends on APUS - default y - help - This enables support for the Zorro bus in the Amiga. If you have - expansion cards in your Amiga that conform to the Amiga - AutoConfig(tm) specification, say Y, otherwise N. Note that even - expansion cards that do not fit in the Zorro slots but fit in e.g. - the CPU slot may fall in this category, so you have to say Y to let - Linux use these. - -config ABSTRACT_CONSOLE - bool - depends on APUS - default y - -config APUS_FAST_EXCEPT - bool - depends on APUS - default y - -config AMIGA_PCMCIA - bool "Amiga 1200/600 PCMCIA support" - depends on APUS && EXPERIMENTAL - help - Include support in the kernel for pcmcia on Amiga 1200 and Amiga - 600. If you intend to use pcmcia cards say Y; otherwise say N. - -config AMIGA_BUILTIN_SERIAL - tristate "Amiga builtin serial support" - depends on APUS - help - If you want to use your Amiga's built-in serial port in Linux, - answer Y. - - To compile this driver as a module, choose M here. - -config GVPIOEXT - tristate "GVP IO-Extender support" - depends on APUS - help - If you want to use a GVP IO-Extender serial card in Linux, say Y. - Otherwise, say N. - -config GVPIOEXT_LP - tristate "GVP IO-Extender parallel printer support" - depends on GVPIOEXT - help - Say Y to enable driving a printer from the parallel port on your - GVP IO-Extender card, N otherwise. - -config GVPIOEXT_PLIP - tristate "GVP IO-Extender PLIP support" - depends on GVPIOEXT - help - Say Y to enable doing IP over the parallel port on your GVP - IO-Extender card, N otherwise. - -config MULTIFACE_III_TTY - tristate "Multiface Card III serial support" - depends on APUS - help - If you want to use a Multiface III card's serial port in Linux, - answer Y. - - To compile this driver as a module, choose M here. - -config A2232 - tristate "Commodore A2232 serial support (EXPERIMENTAL)" - depends on EXPERIMENTAL && APUS - ---help--- - This option supports the 2232 7-port serial card shipped with the - Amiga 2000 and other Zorro-bus machines, dating from 1989. At - a max of 19,200 bps, the ports are served by a 6551 ACIA UART chip - each, plus a 8520 CIA, and a master 6502 CPU and buffer as well. The - ports were connected with 8 pin DIN connectors on the card bracket, - for which 8 pin to DB25 adapters were supplied. The card also had - jumpers internally to toggle various pinning configurations. - - This driver can be built as a module; but then "generic_serial" - will also be built as a module. This has to be loaded before - "ser_a2232". If you want to do this, answer M here. - -config WHIPPET_SERIAL - tristate "Hisoft Whippet PCMCIA serial support" - depends on AMIGA_PCMCIA - help - HiSoft has a web page at <http://www.hisoft.co.uk/>, but there - is no listing for the Whippet in their Amiga section. - -config APNE - tristate "PCMCIA NE2000 support" - depends on AMIGA_PCMCIA - help - If you have a PCMCIA NE2000 compatible adapter, say Y. Otherwise, - say N. - - To compile this driver as a module, choose M here: the - module will be called apne. - -config SERIAL_CONSOLE - bool "Support for serial port console" - depends on APUS && (AMIGA_BUILTIN_SERIAL=y || GVPIOEXT=y || MULTIFACE_III_TTY=y) - -config HEARTBEAT - bool "Use power LED as a heartbeat" - depends on APUS - help - Use the power-on LED on your machine as a load meter. The exact - behavior is platform-dependent, but normally the flash frequency is - a hyperbolic function of the 5-minute load average. - -config PROC_HARDWARE - bool "/proc/hardware support" - depends on APUS - -source "drivers/zorro/Kconfig" - -config PCI_PERMEDIA - bool "PCI for Permedia2" - depends on !4xx && !8xx && APUS diff --git a/arch/powerpc/platforms/cell/io-workarounds.c b/arch/powerpc/platforms/cell/io-workarounds.c index 7fb92f23f38..9d7c2ef940a 100644 --- a/arch/powerpc/platforms/cell/io-workarounds.c +++ b/arch/powerpc/platforms/cell/io-workarounds.c @@ -102,7 +102,7 @@ static void spider_io_flush(const volatile void __iomem *addr) vaddr = (unsigned long)PCI_FIX_ADDR(addr); /* Check if it's in allowed range for PIO */ - if (vaddr < PHBS_IO_BASE || vaddr >= IMALLOC_BASE) + if (vaddr < PHB_IO_BASE || vaddr > PHB_IO_END) return; /* Try to find a PTE. If not, clear the paddr, we'll do diff --git a/arch/powerpc/platforms/cell/spu_base.c b/arch/powerpc/platforms/cell/spu_base.c index a7f5a7653c6..96a8f609690 100644 --- a/arch/powerpc/platforms/cell/spu_base.c +++ b/arch/powerpc/platforms/cell/spu_base.c @@ -31,6 +31,7 @@ #include <linux/mm.h> #include <linux/io.h> #include <linux/mutex.h> +#include <linux/linux_logo.h> #include <asm/spu.h> #include <asm/spu_priv1.h> #include <asm/xmon.h> @@ -183,7 +184,7 @@ static int __spu_trap_data_seg(struct spu *spu, unsigned long ea) spu->slb_replace = 0; spu_restart_dma(spu); - + spu->stats.slb_flt++; return 0; } @@ -332,6 +333,7 @@ spu_irq_class_2(int irq, void *data) if (stat & 0x10) /* SPU mailbox threshold */ spu->wbox_callback(spu); + spu->stats.class2_intr++; return stat ? IRQ_HANDLED : IRQ_NONE; } @@ -462,8 +464,18 @@ void spu_free(struct spu *spu) } EXPORT_SYMBOL_GPL(spu_free); +static int spu_shutdown(struct sys_device *sysdev) +{ + struct spu *spu = container_of(sysdev, struct spu, sysdev); + + spu_free_irqs(spu); + spu_destroy_spu(spu); + return 0; +} + struct sysdev_class spu_sysdev_class = { - set_kset_name("spu") + set_kset_name("spu"), + .shutdown = spu_shutdown, }; int spu_add_sysdev_attr(struct sysdev_attribute *attr) @@ -574,6 +586,9 @@ static int __init create_spu(void *data) spin_unlock_irqrestore(&spu_list_lock, flags); mutex_unlock(&spu_mutex); + spu->stats.utilization_state = SPU_UTIL_IDLE; + spu->stats.tstamp = jiffies; + goto out; out_free_irqs: @@ -586,6 +601,45 @@ out: return ret; } +static const char *spu_state_names[] = { + "user", "system", "iowait", "idle" +}; + +static unsigned long long spu_acct_time(struct spu *spu, + enum spu_utilization_state state) +{ + unsigned long long time = spu->stats.times[state]; + + if (spu->stats.utilization_state == state) + time += jiffies - spu->stats.tstamp; + + return jiffies_to_msecs(time); +} + + +static ssize_t spu_stat_show(struct sys_device *sysdev, char *buf) +{ + struct spu *spu = container_of(sysdev, struct spu, sysdev); + + return sprintf(buf, "%s %llu %llu %llu %llu " + "%llu %llu %llu %llu %llu %llu %llu %llu\n", + spu_state_names[spu->stats.utilization_state], + spu_acct_time(spu, SPU_UTIL_USER), + spu_acct_time(spu, SPU_UTIL_SYSTEM), + spu_acct_time(spu, SPU_UTIL_IOWAIT), + spu_acct_time(spu, SPU_UTIL_IDLE), + spu->stats.vol_ctx_switch, + spu->stats.invol_ctx_switch, + spu->stats.slb_flt, + spu->stats.hash_flt, + spu->stats.min_flt, + spu->stats.maj_flt, + spu->stats.class2_intr, + spu->stats.libassist); +} + +static SYSDEV_ATTR(stat, 0644, spu_stat_show, NULL); + static int __init init_spu_base(void) { int i, ret = 0; @@ -603,14 +657,28 @@ static int __init init_spu_base(void) ret = spu_enumerate_spus(create_spu); - if (ret) { + if (ret < 0) { printk(KERN_WARNING "%s: Error initializing spus\n", __FUNCTION__); goto out_unregister_sysdev_class; } + if (ret > 0) { + /* + * We cannot put the forward declaration in + * <linux/linux_logo.h> because of conflicting session type + * conflicts for const and __initdata with different compiler + * versions + */ + extern const struct linux_logo logo_spe_clut224; + + fb_append_extra_logo(&logo_spe_clut224, ret); + } + xmon_register_spus(&spu_full_list); + spu_add_sysdev_attr(&attr_stat); + return 0; out_unregister_sysdev_class: diff --git a/arch/powerpc/platforms/cell/spu_manage.c b/arch/powerpc/platforms/cell/spu_manage.c index 1d4562ae463..75ed50fcc3d 100644 --- a/arch/powerpc/platforms/cell/spu_manage.c +++ b/arch/powerpc/platforms/cell/spu_manage.c @@ -279,6 +279,7 @@ static int __init of_enumerate_spus(int (*fn)(void *data)) { int ret; struct device_node *node; + unsigned int n = 0; ret = -ENODEV; for (node = of_find_node_by_type(NULL, "spe"); @@ -289,8 +290,9 @@ static int __init of_enumerate_spus(int (*fn)(void *data)) __FUNCTION__, node->name); break; } + n++; } - return ret; + return ret ? ret : n; } static int __init of_create_spu(struct spu *spu, void *data) diff --git a/arch/powerpc/platforms/cell/spufs/backing_ops.c b/arch/powerpc/platforms/cell/spufs/backing_ops.c index d32db9ffc6e..07a0e815abf 100644 --- a/arch/powerpc/platforms/cell/spufs/backing_ops.c +++ b/arch/powerpc/platforms/cell/spufs/backing_ops.c @@ -320,6 +320,12 @@ static int spu_backing_set_mfc_query(struct spu_context * ctx, u32 mask, /* FIXME: what are the side-effects of this? */ prob->dma_querymask_RW = mask; prob->dma_querytype_RW = mode; + /* In the current implementation, the SPU context is always + * acquired in runnable state when new bits are added to the + * mask (tagwait), so it's sufficient just to mask + * dma_tagstatus_R with the 'mask' parameter here. + */ + ctx->csa.prob.dma_tagstatus_R &= mask; out: spin_unlock(&ctx->csa.register_lock); diff --git a/arch/powerpc/platforms/cell/spufs/context.c b/arch/powerpc/platforms/cell/spufs/context.c index 7c51cb54bca..6d7bd60f538 100644 --- a/arch/powerpc/platforms/cell/spufs/context.c +++ b/arch/powerpc/platforms/cell/spufs/context.c @@ -23,10 +23,14 @@ #include <linux/fs.h> #include <linux/mm.h> #include <linux/slab.h> +#include <asm/atomic.h> #include <asm/spu.h> #include <asm/spu_csa.h> #include "spufs.h" + +atomic_t nr_spu_contexts = ATOMIC_INIT(0); + struct spu_context *alloc_spu_context(struct spu_gang *gang) { struct spu_context *ctx; @@ -53,10 +57,12 @@ struct spu_context *alloc_spu_context(struct spu_gang *gang) INIT_LIST_HEAD(&ctx->rq); if (gang) spu_gang_add_ctx(gang, ctx); - ctx->rt_priority = current->rt_priority; - ctx->policy = current->policy; - ctx->prio = current->prio; - INIT_DELAYED_WORK(&ctx->sched_work, spu_sched_tick); + ctx->cpus_allowed = current->cpus_allowed; + spu_set_timeslice(ctx); + ctx->stats.execution_state = SPUCTX_UTIL_USER; + ctx->stats.tstamp = jiffies; + + atomic_inc(&nr_spu_contexts); goto out; out_free: kfree(ctx); @@ -76,6 +82,7 @@ void destroy_spu_context(struct kref *kref) if (ctx->gang) spu_gang_remove_ctx(ctx->gang, ctx); BUG_ON(!list_empty(&ctx->rq)); + atomic_dec(&nr_spu_contexts); kfree(ctx); } diff --git a/arch/powerpc/platforms/cell/spufs/fault.c b/arch/powerpc/platforms/cell/spufs/fault.c index 0f75c07e29d..07f88de0544 100644 --- a/arch/powerpc/platforms/cell/spufs/fault.c +++ b/arch/powerpc/platforms/cell/spufs/fault.c @@ -33,7 +33,8 @@ * function. Currently, there are a few corner cases that we haven't had * to handle fortunately. */ -static int spu_handle_mm_fault(struct mm_struct *mm, unsigned long ea, unsigned long dsisr) +static int spu_handle_mm_fault(struct mm_struct *mm, unsigned long ea, + unsigned long dsisr, unsigned *flt) { struct vm_area_struct *vma; unsigned long is_write; @@ -73,22 +74,21 @@ good_area: goto bad_area; } ret = 0; - switch (handle_mm_fault(mm, vma, ea, is_write)) { - case VM_FAULT_MINOR: - current->min_flt++; - break; - case VM_FAULT_MAJOR: - current->maj_flt++; - break; - case VM_FAULT_SIGBUS: - ret = -EFAULT; - goto bad_area; - case VM_FAULT_OOM: - ret = -ENOMEM; - goto bad_area; - default: + fault = handle_mm_fault(mm, vma, ea, is_write); + if (unlikely(fault & VM_FAULT_ERROR)) { + if (fault & VM_FAULT_OOM) { + ret = -ENOMEM; + goto bad_area; + } else if (fault & VM_FAULT_SIGBUS) { + ret = -EFAULT; + goto bad_area; + } BUG(); } + if (fault & VM_FAULT_MAJOR) + current->maj_flt++; + else + current->min_flt++; up_read(&mm->mmap_sem); return ret; @@ -153,6 +153,7 @@ int spufs_handle_class1(struct spu_context *ctx) { u64 ea, dsisr, access; unsigned long flags; + unsigned flt = 0; int ret; /* @@ -178,9 +179,17 @@ int spufs_handle_class1(struct spu_context *ctx) if (!(dsisr & (MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED))) return 0; + spuctx_switch_state(ctx, SPUCTX_UTIL_IOWAIT); + pr_debug("ctx %p: ea %016lx, dsisr %016lx state %d\n", ctx, ea, dsisr, ctx->state); + ctx->stats.hash_flt++; + if (ctx->state == SPU_STATE_RUNNABLE) { + ctx->spu->stats.hash_flt++; + spu_switch_state(ctx->spu, SPU_UTIL_IOWAIT); + } + /* we must not hold the lock when entering spu_handle_mm_fault */ spu_release(ctx); @@ -192,7 +201,7 @@ int spufs_handle_class1(struct spu_context *ctx) /* hashing failed, so try the actual fault handler */ if (ret) - ret = spu_handle_mm_fault(current->mm, ea, dsisr); + ret = spu_handle_mm_fault(current->mm, ea, dsisr, &flt); spu_acquire(ctx); /* @@ -201,11 +210,23 @@ int spufs_handle_class1(struct spu_context *ctx) * In case of unhandled error report the problem to user space. */ if (!ret) { + if (flt == VM_FAULT_MINOR) + ctx->stats.min_flt++; + else + ctx->stats.maj_flt++; + if (ctx->state == SPU_STATE_RUNNABLE) { + if (flt == VM_FAULT_MINOR) + ctx->spu->stats.min_flt++; + else + ctx->spu->stats.maj_flt++; + } + if (ctx->spu) ctx->ops->restart_dma(ctx); } else spufs_handle_dma_error(ctx, ea, SPE_EVENT_SPE_DATA_STORAGE); + spuctx_switch_state(ctx, SPUCTX_UTIL_SYSTEM); return ret; } EXPORT_SYMBOL_GPL(spufs_handle_class1); diff --git a/arch/powerpc/platforms/cell/spufs/file.c b/arch/powerpc/platforms/cell/spufs/file.c index b1e7e2f8a2e..c2814ea96af 100644 --- a/arch/powerpc/platforms/cell/spufs/file.c +++ b/arch/powerpc/platforms/cell/spufs/file.c @@ -28,6 +28,7 @@ #include <linux/pagemap.h> #include <linux/poll.h> #include <linux/ptrace.h> +#include <linux/seq_file.h> #include <asm/io.h> #include <asm/semaphore.h> @@ -39,6 +40,7 @@ #define SPUFS_MMAP_4K (PAGE_SIZE == 0x1000) + static int spufs_mem_open(struct inode *inode, struct file *file) { @@ -216,12 +218,12 @@ unsigned long spufs_get_unmapped_area(struct file *file, unsigned long addr, #endif /* CONFIG_SPU_FS_64K_LS */ static const struct file_operations spufs_mem_fops = { - .open = spufs_mem_open, - .release = spufs_mem_release, - .read = spufs_mem_read, - .write = spufs_mem_write, - .llseek = generic_file_llseek, - .mmap = spufs_mem_mmap, + .open = spufs_mem_open, + .release = spufs_mem_release, + .read = spufs_mem_read, + .write = spufs_mem_write, + .llseek = generic_file_llseek, + .mmap = spufs_mem_mmap, #ifdef CONFIG_SPU_FS_64K_LS .get_unmapped_area = spufs_get_unmapped_area, #endif @@ -1497,14 +1499,15 @@ static ssize_t spufs_mfc_write(struct file *file, const char __user *buffer, if (status) ret = status; } - spu_release(ctx); if (ret) - goto out; + goto out_unlock; ctx->tagwait |= 1 << cmd.tag; ret = size; +out_unlock: + spu_release(ctx); out: return ret; } @@ -1515,14 +1518,14 @@ static unsigned int spufs_mfc_poll(struct file *file,poll_table *wait) u32 free_elements, tagstatus; unsigned int mask; + poll_wait(file, &ctx->mfc_wq, wait); + spu_acquire(ctx); ctx->ops->set_mfc_query(ctx, ctx->tagwait, 2); free_elements = ctx->ops->get_mfc_free_elements(ctx); tagstatus = ctx->ops->read_mfc_tagstatus(ctx); spu_release(ctx); - poll_wait(file, &ctx->mfc_wq, wait); - mask = 0; if (free_elements & 0xffff) mask |= POLLOUT | POLLWRNORM; @@ -1797,6 +1800,29 @@ static int spufs_info_open(struct inode *inode, struct file *file) return 0; } +static int spufs_caps_show(struct seq_file *s, void *private) +{ + struct spu_context *ctx = s->private; + + if (!(ctx->flags & SPU_CREATE_NOSCHED)) + seq_puts(s, "sched\n"); + if (!(ctx->flags & SPU_CREATE_ISOLATE)) + seq_puts(s, "step\n"); + return 0; +} + +static int spufs_caps_open(struct inode *inode, struct file *file) +{ + return single_open(file, spufs_caps_show, SPUFS_I(inode)->i_ctx); +} + +static const struct file_operations spufs_caps_fops = { + .open = spufs_caps_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + static ssize_t __spufs_mbox_info_read(struct spu_context *ctx, char __user *buf, size_t len, loff_t *pos) { @@ -2014,7 +2040,105 @@ static const struct file_operations spufs_proxydma_info_fops = { .read = spufs_proxydma_info_read, }; +static int spufs_show_tid(struct seq_file *s, void *private) +{ + struct spu_context *ctx = s->private; + + seq_printf(s, "%d\n", ctx->tid); + return 0; +} + +static int spufs_tid_open(struct inode *inode, struct file *file) +{ + return single_open(file, spufs_show_tid, SPUFS_I(inode)->i_ctx); +} + +static const struct file_operations spufs_tid_fops = { + .open = spufs_tid_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static const char *ctx_state_names[] = { + "user", "system", "iowait", "loaded" +}; + +static unsigned long long spufs_acct_time(struct spu_context *ctx, + enum spuctx_execution_state state) +{ + unsigned long time = ctx->stats.times[state]; + + if (ctx->stats.execution_state == state) + time += jiffies - ctx->stats.tstamp; + + return jiffies_to_msecs(time); +} + +static unsigned long long spufs_slb_flts(struct spu_context *ctx) +{ + unsigned long long slb_flts = ctx->stats.slb_flt; + + if (ctx->state == SPU_STATE_RUNNABLE) { + slb_flts += (ctx->spu->stats.slb_flt - + ctx->stats.slb_flt_base); + } + + return slb_flts; +} + +static unsigned long long spufs_class2_intrs(struct spu_context *ctx) +{ + unsigned long long class2_intrs = ctx->stats.class2_intr; + + if (ctx->state == SPU_STATE_RUNNABLE) { + class2_intrs += (ctx->spu->stats.class2_intr - + ctx->stats.class2_intr_base); + } + + return class2_intrs; +} + + +static int spufs_show_stat(struct seq_file *s, void *private) +{ + struct spu_context *ctx = s->private; + + spu_acquire(ctx); + seq_printf(s, "%s %llu %llu %llu %llu " + "%llu %llu %llu %llu %llu %llu %llu %llu\n", + ctx_state_names[ctx->stats.execution_state], + spufs_acct_time(ctx, SPUCTX_UTIL_USER), + spufs_acct_time(ctx, SPUCTX_UTIL_SYSTEM), + spufs_acct_time(ctx, SPUCTX_UTIL_IOWAIT), + spufs_acct_time(ctx, SPUCTX_UTIL_LOADED), + ctx->stats.vol_ctx_switch, + ctx->stats.invol_ctx_switch, + spufs_slb_flts(ctx), + ctx->stats.hash_flt, + ctx->stats.min_flt, + ctx->stats.maj_flt, + spufs_class2_intrs(ctx), + ctx->stats.libassist); + spu_release(ctx); + return 0; +} + +static int spufs_stat_open(struct inode *inode, struct file *file) +{ + return single_open(file, spufs_show_stat, SPUFS_I(inode)->i_ctx); +} + +static const struct file_operations spufs_stat_fops = { + .open = spufs_stat_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + + struct tree_descr spufs_dir_contents[] = { + { "capabilities", &spufs_caps_fops, 0444, }, { "mem", &spufs_mem_fops, 0666, }, { "regs", &spufs_regs_fops, 0666, }, { "mbox", &spufs_mbox_fops, 0444, }, @@ -2046,10 +2170,13 @@ struct tree_descr spufs_dir_contents[] = { { "wbox_info", &spufs_wbox_info_fops, 0444, }, { "dma_info", &spufs_dma_info_fops, 0444, }, { "proxydma_info", &spufs_proxydma_info_fops, 0444, }, + { "tid", &spufs_tid_fops, 0444, }, + { "stat", &spufs_stat_fops, 0444, }, {}, }; struct tree_descr spufs_dir_nosched_contents[] = { + { "capabilities", &spufs_caps_fops, 0444, }, { "mem", &spufs_mem_fops, 0666, }, { "mbox", &spufs_mbox_fops, 0444, }, { "ibox", &spufs_ibox_fops, 0444, }, @@ -2068,6 +2195,8 @@ struct tree_descr spufs_dir_nosched_contents[] = { { "psmap", &spufs_psmap_fops, 0666, }, { "phys-id", &spufs_id_ops, 0666, }, { "object-id", &spufs_object_id_ops, 0666, }, + { "tid", &spufs_tid_fops, 0444, }, + { "stat", &spufs_stat_fops, 0444, }, {}, }; diff --git a/arch/powerpc/platforms/cell/spufs/inode.c b/arch/powerpc/platforms/cell/spufs/inode.c index 9807206e021..f37460e5bfd 100644 --- a/arch/powerpc/platforms/cell/spufs/inode.c +++ b/arch/powerpc/platforms/cell/spufs/inode.c @@ -232,10 +232,6 @@ static int spufs_dir_close(struct inode *inode, struct file *file) return dcache_dir_close(inode, file); } -const struct inode_operations spufs_dir_inode_operations = { - .lookup = simple_lookup, -}; - const struct file_operations spufs_context_fops = { .open = dcache_dir_open, .release = spufs_dir_close, @@ -269,7 +265,7 @@ spufs_mkdir(struct inode *dir, struct dentry *dentry, unsigned int flags, goto out_iput; ctx->flags = flags; - inode->i_op = &spufs_dir_inode_operations; + inode->i_op = &simple_dir_inode_operations; inode->i_fop = &simple_dir_operations; if (flags & SPU_CREATE_NOSCHED) ret = spufs_fill_dir(dentry, spufs_dir_nosched_contents, @@ -386,7 +382,7 @@ spufs_mkgang(struct inode *dir, struct dentry *dentry, int mode) if (!gang) goto out_iput; - inode->i_op = &spufs_dir_inode_operations; + inode->i_op = &simple_dir_inode_operations; inode->i_fop = &simple_dir_operations; d_instantiate(dentry, inode); @@ -593,7 +589,7 @@ spufs_create_root(struct super_block *sb, void *data) if (!inode) goto out; - inode->i_op = &spufs_dir_inode_operations; + inode->i_op = &simple_dir_inode_operations; inode->i_fop = &simple_dir_operations; SPUFS_I(inode)->i_ctx = NULL; diff --git a/arch/powerpc/platforms/cell/spufs/run.c b/arch/powerpc/platforms/cell/spufs/run.c index 57626600b1a..58ae13b7de8 100644 --- a/arch/powerpc/platforms/cell/spufs/run.c +++ b/arch/powerpc/platforms/cell/spufs/run.c @@ -29,7 +29,8 @@ static inline int spu_stopped(struct spu_context *ctx, u32 * stat) spu = ctx->spu; pte_fault = spu->dsisr & (MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED); - return (!(*stat & 0x1) || pte_fault || spu->class_0_pending) ? 1 : 0; + return (!(*stat & SPU_STATUS_RUNNING) || pte_fault || spu->class_0_pending) ? + 1 : 0; } static int spu_setup_isolated(struct spu_context *ctx) @@ -142,8 +143,11 @@ static int spu_run_init(struct spu_context *ctx, u32 * npc) runcntl = SPU_RUNCNTL_RUNNABLE; ctx->ops->runcntl_write(ctx, runcntl); } else { - spu_start_tick(ctx); + unsigned long mode = SPU_PRIVCNTL_MODE_NORMAL; ctx->ops->npc_write(ctx, *npc); + if (test_thread_flag(TIF_SINGLESTEP)) + mode = SPU_PRIVCNTL_MODE_SINGLE_STEP; + out_be64(&ctx->spu->priv2->spu_privcntl_RW, mode); ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_RUNNABLE); } @@ -155,7 +159,6 @@ static int spu_run_fini(struct spu_context *ctx, u32 * npc, { int ret = 0; - spu_stop_tick(ctx); *status = ctx->ops->status_read(ctx); *npc = ctx->ops->npc_read(ctx); spu_release(ctx); @@ -298,9 +301,22 @@ long spufs_run_spu(struct file *file, struct spu_context *ctx, ctx->ops->master_start(ctx); ctx->event_return = 0; - ret = spu_acquire_runnable(ctx, 0); - if (ret) - return ret; + spu_acquire(ctx); + if (ctx->state == SPU_STATE_SAVED) { + __spu_update_sched_info(ctx); + + ret = spu_activate(ctx, 0); + if (ret) { + spu_release(ctx); + goto out; + } + } else { + /* + * We have to update the scheduling priority under active_mutex + * to protect against find_victim(). + */ + spu_update_sched_info(ctx); + } ret = spu_run_init(ctx, npc); if (ret) { @@ -325,16 +341,20 @@ long spufs_run_spu(struct file *file, struct spu_context *ctx, if (unlikely(ctx->state != SPU_STATE_RUNNABLE)) { ret = spu_reacquire_runnable(ctx, npc, &status); - if (ret) { - spu_stop_tick(ctx); + if (ret) goto out2; - } continue; } ret = spu_process_events(ctx); } while (!ret && !(status & (SPU_STATUS_STOPPED_BY_STOP | - SPU_STATUS_STOPPED_BY_HALT))); + SPU_STATUS_STOPPED_BY_HALT | + SPU_STATUS_SINGLE_STEP))); + + if ((status & SPU_STATUS_STOPPED_BY_STOP) && + (((status >> SPU_STOP_STATUS_SHIFT) & 0x3f00) == 0x2100) && + (ctx->state == SPU_STATE_RUNNABLE)) + ctx->stats.libassist++; ctx->ops->master_stop(ctx); ret = spu_run_fini(ctx, npc, &status); @@ -344,10 +364,15 @@ out2: if ((ret == 0) || ((ret == -ERESTARTSYS) && ((status & SPU_STATUS_STOPPED_BY_HALT) || + (status & SPU_STATUS_SINGLE_STEP) || ((status & SPU_STATUS_STOPPED_BY_STOP) && (status >> SPU_STOP_STATUS_SHIFT != 0x2104))))) ret = status; + /* Note: we don't need to force_sig SIGTRAP on single-step + * since we have TIF_SINGLESTEP set, thus the kernel will do + * it upon return from the syscall anyawy + */ if ((status & SPU_STATUS_STOPPED_BY_STOP) && (status >> SPU_STOP_STATUS_SHIFT) == 0x3fff) { force_sig(SIGTRAP, current); diff --git a/arch/powerpc/platforms/cell/spufs/sched.c b/arch/powerpc/platforms/cell/spufs/sched.c index 3b831e07f1e..e5b4dd1db28 100644 --- a/arch/powerpc/platforms/cell/spufs/sched.c +++ b/arch/powerpc/platforms/cell/spufs/sched.c @@ -35,6 +35,10 @@ #include <linux/numa.h> #include <linux/mutex.h> #include <linux/notifier.h> +#include <linux/kthread.h> +#include <linux/pid_namespace.h> +#include <linux/proc_fs.h> +#include <linux/seq_file.h> #include <asm/io.h> #include <asm/mmu_context.h> @@ -43,54 +47,126 @@ #include <asm/spu_priv1.h> #include "spufs.h" -#define SPU_TIMESLICE (HZ) - struct spu_prio_array { DECLARE_BITMAP(bitmap, MAX_PRIO); struct list_head runq[MAX_PRIO]; spinlock_t runq_lock; struct list_head active_list[MAX_NUMNODES]; struct mutex active_mutex[MAX_NUMNODES]; + int nr_active[MAX_NUMNODES]; + int nr_waiting; }; +static unsigned long spu_avenrun[3]; static struct spu_prio_array *spu_prio; -static struct workqueue_struct *spu_sched_wq; +static struct task_struct *spusched_task; +static struct timer_list spusched_timer; + +/* + * Priority of a normal, non-rt, non-niced'd process (aka nice level 0). + */ +#define NORMAL_PRIO 120 + +/* + * Frequency of the spu scheduler tick. By default we do one SPU scheduler + * tick for every 10 CPU scheduler ticks. + */ +#define SPUSCHED_TICK (10) -static inline int node_allowed(int node) +/* + * These are the 'tuning knobs' of the scheduler: + * + * Minimum timeslice is 5 msecs (or 1 spu scheduler tick, whichever is + * larger), default timeslice is 100 msecs, maximum timeslice is 800 msecs. + */ +#define MIN_SPU_TIMESLICE max(5 * HZ / (1000 * SPUSCHED_TICK), 1) +#define DEF_SPU_TIMESLICE (100 * HZ / (1000 * SPUSCHED_TICK)) + +#define MAX_USER_PRIO (MAX_PRIO - MAX_RT_PRIO) +#define SCALE_PRIO(x, prio) \ + max(x * (MAX_PRIO - prio) / (MAX_USER_PRIO / 2), MIN_SPU_TIMESLICE) + +/* + * scale user-nice values [ -20 ... 0 ... 19 ] to time slice values: + * [800ms ... 100ms ... 5ms] + * + * The higher a thread's priority, the bigger timeslices + * it gets during one round of execution. But even the lowest + * priority thread gets MIN_TIMESLICE worth of execution time. + */ +void spu_set_timeslice(struct spu_context *ctx) { - cpumask_t mask; + if (ctx->prio < NORMAL_PRIO) + ctx->time_slice = SCALE_PRIO(DEF_SPU_TIMESLICE * 4, ctx->prio); + else + ctx->time_slice = SCALE_PRIO(DEF_SPU_TIMESLICE, ctx->prio); +} - if (!nr_cpus_node(node)) - return 0; - mask = node_to_cpumask(node); - if (!cpus_intersects(mask, current->cpus_allowed)) - return 0; - return 1; +/* + * Update scheduling information from the owning thread. + */ +void __spu_update_sched_info(struct spu_context *ctx) +{ + /* + * 32-Bit assignment are atomic on powerpc, and we don't care about + * memory ordering here because retriving the controlling thread is + * per defintion racy. + */ + ctx->tid = current->pid; + + /* + * We do our own priority calculations, so we normally want + * ->static_prio to start with. Unfortunately thies field + * contains junk for threads with a realtime scheduling + * policy so we have to look at ->prio in this case. + */ + if (rt_prio(current->prio)) + ctx->prio = current->prio; + else + ctx->prio = current->static_prio; + ctx->policy = current->policy; + + /* + * A lot of places that don't hold active_mutex poke into + * cpus_allowed, including grab_runnable_context which + * already holds the runq_lock. So abuse runq_lock + * to protect this field aswell. + */ + spin_lock(&spu_prio->runq_lock); + ctx->cpus_allowed = current->cpus_allowed; + spin_unlock(&spu_prio->runq_lock); } -void spu_start_tick(struct spu_context *ctx) +void spu_update_sched_info(struct spu_context *ctx) { - if (ctx->policy == SCHED_RR) { - /* - * Make sure the exiting bit is cleared. - */ - clear_bit(SPU_SCHED_EXITING, &ctx->sched_flags); - mb(); - queue_delayed_work(spu_sched_wq, &ctx->sched_work, SPU_TIMESLICE); - } + int node = ctx->spu->node; + + mutex_lock(&spu_prio->active_mutex[node]); + __spu_update_sched_info(ctx); + mutex_unlock(&spu_prio->active_mutex[node]); } -void spu_stop_tick(struct spu_context *ctx) +static int __node_allowed(struct spu_context *ctx, int node) { - if (ctx->policy == SCHED_RR) { - /* - * While the work can be rearming normally setting this flag - * makes sure it does not rearm itself anymore. - */ - set_bit(SPU_SCHED_EXITING, &ctx->sched_flags); - mb(); - cancel_delayed_work(&ctx->sched_work); + if (nr_cpus_node(node)) { + cpumask_t mask = node_to_cpumask(node); + + if (cpus_intersects(mask, ctx->cpus_allowed)) + return 1; } + + return 0; +} + +static int node_allowed(struct spu_context *ctx, int node) +{ + int rval; + + spin_lock(&spu_prio->runq_lock); + rval = __node_allowed(ctx, node); + spin_unlock(&spu_prio->runq_lock); + + return rval; } /** @@ -99,9 +175,18 @@ void spu_stop_tick(struct spu_context *ctx) */ static void spu_add_to_active_list(struct spu *spu) { - mutex_lock(&spu_prio->active_mutex[spu->node]); - list_add_tail(&spu->list, &spu_prio->active_list[spu->node]); - mutex_unlock(&spu_prio->active_mutex[spu->node]); + int node = spu->node; + + mutex_lock(&spu_prio->active_mutex[node]); + spu_prio->nr_active[node]++; + list_add_tail(&spu->list, &spu_prio->active_list[node]); + mutex_unlock(&spu_prio->active_mutex[node]); +} + +static void __spu_remove_from_active_list(struct spu *spu) +{ + list_del_init(&spu->list); + spu_prio->nr_active[spu->node]--; } /** @@ -113,7 +198,7 @@ static void spu_remove_from_active_list(struct spu *spu) int node = spu->node; mutex_lock(&spu_prio->active_mutex[node]); - list_del_init(&spu->list); + __spu_remove_from_active_list(spu); mutex_unlock(&spu_prio->active_mutex[node]); } @@ -144,6 +229,10 @@ static void spu_bind_context(struct spu *spu, struct spu_context *ctx) { pr_debug("%s: pid=%d SPU=%d NODE=%d\n", __FUNCTION__, current->pid, spu->number, spu->node); + + ctx->stats.slb_flt_base = spu->stats.slb_flt; + ctx->stats.class2_intr_base = spu->stats.class2_intr; + spu->ctx = ctx; spu->flags = 0; ctx->spu = spu; @@ -161,8 +250,8 @@ static void spu_bind_context(struct spu *spu, struct spu_context *ctx) spu->timestamp = jiffies; spu_cpu_affinity_set(spu, raw_smp_processor_id()); spu_switch_notify(spu, ctx); - spu_add_to_active_list(spu); ctx->state = SPU_STATE_RUNNABLE; + spu_switch_state(spu, SPU_UTIL_SYSTEM); } /** @@ -175,7 +264,8 @@ static void spu_unbind_context(struct spu *spu, struct spu_context *ctx) pr_debug("%s: unbind pid=%d SPU=%d NODE=%d\n", __FUNCTION__, spu->pid, spu->number, spu->node); - spu_remove_from_active_list(spu); + spu_switch_state(spu, SPU_UTIL_IDLE); + spu_switch_notify(spu, NULL); spu_unmap_mappings(ctx); spu_save(&ctx->csa, spu); @@ -192,6 +282,11 @@ static void spu_unbind_context(struct spu *spu, struct spu_context *ctx) ctx->spu = NULL; spu->flags = 0; spu->ctx = NULL; + + ctx->stats.slb_flt += + (spu->stats.slb_flt - ctx->stats.slb_flt_base); + ctx->stats.class2_intr += + (spu->stats.class2_intr - ctx->stats.class2_intr_base); } /** @@ -200,20 +295,39 @@ static void spu_unbind_context(struct spu *spu, struct spu_context *ctx) */ static void __spu_add_to_rq(struct spu_context *ctx) { - int prio = ctx->prio; - - list_add_tail(&ctx->rq, &spu_prio->runq[prio]); - set_bit(prio, spu_prio->bitmap); + /* + * Unfortunately this code path can be called from multiple threads + * on behalf of a single context due to the way the problem state + * mmap support works. + * + * Fortunately we need to wake up all these threads at the same time + * and can simply skip the runqueue addition for every but the first + * thread getting into this codepath. + * + * It's still quite hacky, and long-term we should proxy all other + * threads through the owner thread so that spu_run is in control + * of all the scheduling activity for a given context. + */ + if (list_empty(&ctx->rq)) { + list_add_tail(&ctx->rq, &spu_prio->runq[ctx->prio]); + set_bit(ctx->prio, spu_prio->bitmap); + if (!spu_prio->nr_waiting++) + __mod_timer(&spusched_timer, jiffies + SPUSCHED_TICK); + } } static void __spu_del_from_rq(struct spu_context *ctx) { int prio = ctx->prio; - if (!list_empty(&ctx->rq)) + if (!list_empty(&ctx->rq)) { + if (!--spu_prio->nr_waiting) + del_timer(&spusched_timer); list_del_init(&ctx->rq); - if (list_empty(&spu_prio->runq[prio])) - clear_bit(prio, spu_prio->bitmap); + + if (list_empty(&spu_prio->runq[prio])) + clear_bit(prio, spu_prio->bitmap); + } } static void spu_prio_wait(struct spu_context *ctx) @@ -244,7 +358,7 @@ static struct spu *spu_get_idle(struct spu_context *ctx) for (n = 0; n < MAX_NUMNODES; n++, node++) { node = (node < MAX_NUMNODES) ? node : 0; - if (!node_allowed(node)) + if (!node_allowed(ctx, node)) continue; spu = spu_alloc_node(node); if (spu) @@ -276,15 +390,15 @@ static struct spu *find_victim(struct spu_context *ctx) node = cpu_to_node(raw_smp_processor_id()); for (n = 0; n < MAX_NUMNODES; n++, node++) { node = (node < MAX_NUMNODES) ? node : 0; - if (!node_allowed(node)) + if (!node_allowed(ctx, node)) continue; mutex_lock(&spu_prio->active_mutex[node]); list_for_each_entry(spu, &spu_prio->active_list[node], list) { struct spu_context *tmp = spu->ctx; - if (tmp->rt_priority < ctx->rt_priority && - (!victim || tmp->rt_priority < victim->rt_priority)) + if (tmp->prio > ctx->prio && + (!victim || tmp->prio > victim->prio)) victim = spu->ctx; } mutex_unlock(&spu_prio->active_mutex[node]); @@ -312,7 +426,10 @@ static struct spu *find_victim(struct spu_context *ctx) victim = NULL; goto restart; } + spu_remove_from_active_list(spu); spu_unbind_context(spu, victim); + victim->stats.invol_ctx_switch++; + spu->stats.invol_ctx_switch++; mutex_unlock(&victim->state_mutex); /* * We need to break out of the wait loop in spu_run @@ -338,22 +455,30 @@ static struct spu *find_victim(struct spu_context *ctx) */ int spu_activate(struct spu_context *ctx, unsigned long flags) { - - if (ctx->spu) - return 0; + spuctx_switch_state(ctx, SPUCTX_UTIL_SYSTEM); do { struct spu *spu; + /* + * If there are multiple threads waiting for a single context + * only one actually binds the context while the others will + * only be able to acquire the state_mutex once the context + * already is in runnable state. + */ + if (ctx->spu) + return 0; + spu = spu_get_idle(ctx); /* * If this is a realtime thread we try to get it running by * preempting a lower priority thread. */ - if (!spu && ctx->rt_priority) + if (!spu && rt_prio(ctx->prio)) spu = find_victim(ctx); if (spu) { spu_bind_context(spu, ctx); + spu_add_to_active_list(spu); return 0; } @@ -369,23 +494,28 @@ int spu_activate(struct spu_context *ctx, unsigned long flags) * Remove the highest priority context on the runqueue and return it * to the caller. Returns %NULL if no runnable context was found. */ -static struct spu_context *grab_runnable_context(int prio) +static struct spu_context *grab_runnable_context(int prio, int node) { - struct spu_context *ctx = NULL; + struct spu_context *ctx; int best; spin_lock(&spu_prio->runq_lock); best = sched_find_first_bit(spu_prio->bitmap); - if (best < prio) { + while (best < prio) { struct list_head *rq = &spu_prio->runq[best]; - BUG_ON(list_empty(rq)); - - ctx = list_entry(rq->next, struct spu_context, rq); - __spu_del_from_rq(ctx); + list_for_each_entry(ctx, rq, rq) { + /* XXX(hch): check for affinity here aswell */ + if (__node_allowed(ctx, node)) { + __spu_del_from_rq(ctx); + goto found; + } + } + best++; } + ctx = NULL; + found: spin_unlock(&spu_prio->runq_lock); - return ctx; } @@ -395,9 +525,12 @@ static int __spu_deactivate(struct spu_context *ctx, int force, int max_prio) struct spu_context *new = NULL; if (spu) { - new = grab_runnable_context(max_prio); + new = grab_runnable_context(max_prio, spu->node); if (new || force) { + spu_remove_from_active_list(spu); spu_unbind_context(spu, ctx); + ctx->stats.vol_ctx_switch++; + spu->stats.vol_ctx_switch++; spu_free(spu); if (new) wake_up(&new->stop_wq); @@ -417,7 +550,17 @@ static int __spu_deactivate(struct spu_context *ctx, int force, int max_prio) */ void spu_deactivate(struct spu_context *ctx) { + /* + * We must never reach this for a nosched context, + * but handle the case gracefull instead of panicing. + */ + if (ctx->flags & SPU_CREATE_NOSCHED) { + WARN_ON(1); + return; + } + __spu_deactivate(ctx, 1, MAX_PRIO); + spuctx_switch_state(ctx, SPUCTX_UTIL_USER); } /** @@ -432,56 +575,178 @@ void spu_yield(struct spu_context *ctx) { if (!(ctx->flags & SPU_CREATE_NOSCHED)) { mutex_lock(&ctx->state_mutex); - __spu_deactivate(ctx, 0, MAX_PRIO); + if (__spu_deactivate(ctx, 0, MAX_PRIO)) + spuctx_switch_state(ctx, SPUCTX_UTIL_USER); + else { + spuctx_switch_state(ctx, SPUCTX_UTIL_LOADED); + spu_switch_state(ctx->spu, SPU_UTIL_USER); + } mutex_unlock(&ctx->state_mutex); } } -void spu_sched_tick(struct work_struct *work) +static void spusched_tick(struct spu_context *ctx) { - struct spu_context *ctx = - container_of(work, struct spu_context, sched_work.work); - int preempted; + if (ctx->flags & SPU_CREATE_NOSCHED) + return; + if (ctx->policy == SCHED_FIFO) + return; + + if (--ctx->time_slice) + return; /* - * If this context is being stopped avoid rescheduling from the - * scheduler tick because we would block on the state_mutex. - * The caller will yield the spu later on anyway. + * Unfortunately active_mutex ranks outside of state_mutex, so + * we have to trylock here. If we fail give the context another + * tick and try again. */ - if (test_bit(SPU_SCHED_EXITING, &ctx->sched_flags)) - return; + if (mutex_trylock(&ctx->state_mutex)) { + struct spu *spu = ctx->spu; + struct spu_context *new; - mutex_lock(&ctx->state_mutex); - preempted = __spu_deactivate(ctx, 0, ctx->prio + 1); - mutex_unlock(&ctx->state_mutex); + new = grab_runnable_context(ctx->prio + 1, spu->node); + if (new) { - if (preempted) { - /* - * We need to break out of the wait loop in spu_run manually - * to ensure this context gets put on the runqueue again - * ASAP. - */ - wake_up(&ctx->stop_wq); + __spu_remove_from_active_list(spu); + spu_unbind_context(spu, ctx); + ctx->stats.invol_ctx_switch++; + spu->stats.invol_ctx_switch++; + spu_free(spu); + wake_up(&new->stop_wq); + /* + * We need to break out of the wait loop in + * spu_run manually to ensure this context + * gets put on the runqueue again ASAP. + */ + wake_up(&ctx->stop_wq); + } + spu_set_timeslice(ctx); + mutex_unlock(&ctx->state_mutex); } else { - spu_start_tick(ctx); + ctx->time_slice++; } } -int __init spu_sched_init(void) +/** + * count_active_contexts - count nr of active tasks + * + * Return the number of tasks currently running or waiting to run. + * + * Note that we don't take runq_lock / active_mutex here. Reading + * a single 32bit value is atomic on powerpc, and we don't care + * about memory ordering issues here. + */ +static unsigned long count_active_contexts(void) { - int i; + int nr_active = 0, node; - spu_sched_wq = create_singlethread_workqueue("spusched"); - if (!spu_sched_wq) - return 1; + for (node = 0; node < MAX_NUMNODES; node++) + nr_active += spu_prio->nr_active[node]; + nr_active += spu_prio->nr_waiting; - spu_prio = kzalloc(sizeof(struct spu_prio_array), GFP_KERNEL); - if (!spu_prio) { - printk(KERN_WARNING "%s: Unable to allocate priority queue.\n", - __FUNCTION__); - destroy_workqueue(spu_sched_wq); - return 1; + return nr_active; +} + +/** + * spu_calc_load - given tick count, update the avenrun load estimates. + * @tick: tick count + * + * No locking against reading these values from userspace, as for + * the CPU loadavg code. + */ +static void spu_calc_load(unsigned long ticks) +{ + unsigned long active_tasks; /* fixed-point */ + static int count = LOAD_FREQ; + + count -= ticks; + + if (unlikely(count < 0)) { + active_tasks = count_active_contexts() * FIXED_1; + do { + CALC_LOAD(spu_avenrun[0], EXP_1, active_tasks); + CALC_LOAD(spu_avenrun[1], EXP_5, active_tasks); + CALC_LOAD(spu_avenrun[2], EXP_15, active_tasks); + count += LOAD_FREQ; + } while (count < 0); } +} + +static void spusched_wake(unsigned long data) +{ + mod_timer(&spusched_timer, jiffies + SPUSCHED_TICK); + wake_up_process(spusched_task); + spu_calc_load(SPUSCHED_TICK); +} + +static int spusched_thread(void *unused) +{ + struct spu *spu, *next; + int node; + + while (!kthread_should_stop()) { + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + for (node = 0; node < MAX_NUMNODES; node++) { + mutex_lock(&spu_prio->active_mutex[node]); + list_for_each_entry_safe(spu, next, + &spu_prio->active_list[node], + list) + spusched_tick(spu->ctx); + mutex_unlock(&spu_prio->active_mutex[node]); + } + } + + return 0; +} + +#define LOAD_INT(x) ((x) >> FSHIFT) +#define LOAD_FRAC(x) LOAD_INT(((x) & (FIXED_1-1)) * 100) + +static int show_spu_loadavg(struct seq_file *s, void *private) +{ + int a, b, c; + + a = spu_avenrun[0] + (FIXED_1/200); + b = spu_avenrun[1] + (FIXED_1/200); + c = spu_avenrun[2] + (FIXED_1/200); + + /* + * Note that last_pid doesn't really make much sense for the + * SPU loadavg (it even seems very odd on the CPU side..), + * but we include it here to have a 100% compatible interface. + */ + seq_printf(s, "%d.%02d %d.%02d %d.%02d %ld/%d %d\n", + LOAD_INT(a), LOAD_FRAC(a), + LOAD_INT(b), LOAD_FRAC(b), + LOAD_INT(c), LOAD_FRAC(c), + count_active_contexts(), + atomic_read(&nr_spu_contexts), + current->nsproxy->pid_ns->last_pid); + return 0; +} + +static int spu_loadavg_open(struct inode *inode, struct file *file) +{ + return single_open(file, show_spu_loadavg, NULL); +} + +static const struct file_operations spu_loadavg_fops = { + .open = spu_loadavg_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +int __init spu_sched_init(void) +{ + struct proc_dir_entry *entry; + int err = -ENOMEM, i; + + spu_prio = kzalloc(sizeof(struct spu_prio_array), GFP_KERNEL); + if (!spu_prio) + goto out; + for (i = 0; i < MAX_PRIO; i++) { INIT_LIST_HEAD(&spu_prio->runq[i]); __clear_bit(i, spu_prio->bitmap); @@ -492,7 +757,30 @@ int __init spu_sched_init(void) INIT_LIST_HEAD(&spu_prio->active_list[i]); } spin_lock_init(&spu_prio->runq_lock); + + setup_timer(&spusched_timer, spusched_wake, 0); + + spusched_task = kthread_run(spusched_thread, NULL, "spusched"); + if (IS_ERR(spusched_task)) { + err = PTR_ERR(spusched_task); + goto out_free_spu_prio; + } + + entry = create_proc_entry("spu_loadavg", 0, NULL); + if (!entry) + goto out_stop_kthread; + entry->proc_fops = &spu_loadavg_fops; + + pr_debug("spusched: tick: %d, min ticks: %d, default ticks: %d\n", + SPUSCHED_TICK, MIN_SPU_TIMESLICE, DEF_SPU_TIMESLICE); return 0; + + out_stop_kthread: + kthread_stop(spusched_task); + out_free_spu_prio: + kfree(spu_prio); + out: + return err; } void __exit spu_sched_exit(void) @@ -500,6 +788,11 @@ void __exit spu_sched_exit(void) struct spu *spu, *tmp; int node; + remove_proc_entry("spu_loadavg", NULL); + + del_timer_sync(&spusched_timer); + kthread_stop(spusched_task); + for (node = 0; node < MAX_NUMNODES; node++) { mutex_lock(&spu_prio->active_mutex[node]); list_for_each_entry_safe(spu, tmp, &spu_prio->active_list[node], @@ -510,5 +803,4 @@ void __exit spu_sched_exit(void) mutex_unlock(&spu_prio->active_mutex[node]); } kfree(spu_prio); - destroy_workqueue(spu_sched_wq); } diff --git a/arch/powerpc/platforms/cell/spufs/spu_restore.c b/arch/powerpc/platforms/cell/spufs/spu_restore.c index 0bf723dcd67..4e19ed7a075 100644 --- a/arch/powerpc/platforms/cell/spufs/spu_restore.c +++ b/arch/powerpc/platforms/cell/spufs/spu_restore.c @@ -296,7 +296,7 @@ static inline void restore_complete(void) * This code deviates from the documented sequence in the * following aspects: * - * 1. The EA for LSCSA is passed from PPE in the + * 1. The EA for LSCSA is passed from PPE in the * signal notification channels. * 2. The register spill area is pulled by SPU * into LS, rather than pushed by PPE. diff --git a/arch/powerpc/platforms/cell/spufs/spu_save.c b/arch/powerpc/platforms/cell/spufs/spu_save.c index 196033b8a57..ae95cc1701e 100644 --- a/arch/powerpc/platforms/cell/spufs/spu_save.c +++ b/arch/powerpc/platforms/cell/spufs/spu_save.c @@ -44,7 +44,7 @@ static inline void save_event_mask(void) * Read the SPU_RdEventMsk channel and save to the LSCSA. */ offset = LSCSA_QW_OFFSET(event_mask); - regs_spill[offset].slot[0] = spu_readch(SPU_RdEventStatMask); + regs_spill[offset].slot[0] = spu_readch(SPU_RdEventMask); } static inline void save_tag_mask(void) diff --git a/arch/powerpc/platforms/cell/spufs/spufs.h b/arch/powerpc/platforms/cell/spufs/spufs.h index 47617e8014a..08b3530288a 100644 --- a/arch/powerpc/platforms/cell/spufs/spufs.h +++ b/arch/powerpc/platforms/cell/spufs/spufs.h @@ -26,6 +26,7 @@ #include <linux/mutex.h> #include <linux/spinlock.h> #include <linux/fs.h> +#include <linux/cpumask.h> #include <asm/spu.h> #include <asm/spu_csa.h> @@ -39,9 +40,17 @@ enum { struct spu_context_ops; struct spu_gang; -/* ctx->sched_flags */ -enum { - SPU_SCHED_EXITING = 0, +/* + * This is the state for spu utilization reporting to userspace. + * Because this state is visible to userspace it must never change and needs + * to be kept strictly separate from any internal state kept by the kernel. + */ +enum spuctx_execution_state { + SPUCTX_UTIL_USER = 0, + SPUCTX_UTIL_SYSTEM, + SPUCTX_UTIL_IOWAIT, + SPUCTX_UTIL_LOADED, + SPUCTX_UTIL_MAX }; struct spu_context { @@ -81,13 +90,34 @@ struct spu_context { struct list_head gang_list; struct spu_gang *gang; + /* owner thread */ + pid_t tid; + /* scheduler fields */ - struct list_head rq; - struct delayed_work sched_work; + struct list_head rq; + unsigned int time_slice; unsigned long sched_flags; - unsigned long rt_priority; + cpumask_t cpus_allowed; int policy; int prio; + + /* statistics */ + struct { + /* updates protected by ctx->state_mutex */ + enum spuctx_execution_state execution_state; + unsigned long tstamp; /* time of last ctx switch */ + unsigned long times[SPUCTX_UTIL_MAX]; + unsigned long long vol_ctx_switch; + unsigned long long invol_ctx_switch; + unsigned long long min_flt; + unsigned long long maj_flt; + unsigned long long hash_flt; + unsigned long long slb_flt; + unsigned long long slb_flt_base; /* # at last ctx switch */ + unsigned long long class2_intr; + unsigned long long class2_intr_base; /* # at last ctx switch */ + unsigned long long libassist; + } stats; }; struct spu_gang { @@ -177,6 +207,7 @@ void spu_gang_add_ctx(struct spu_gang *gang, struct spu_context *ctx); int spufs_handle_class1(struct spu_context *ctx); /* context management */ +extern atomic_t nr_spu_contexts; static inline void spu_acquire(struct spu_context *ctx) { mutex_lock(&ctx->state_mutex); @@ -200,9 +231,9 @@ void spu_acquire_saved(struct spu_context *ctx); int spu_activate(struct spu_context *ctx, unsigned long flags); void spu_deactivate(struct spu_context *ctx); void spu_yield(struct spu_context *ctx); -void spu_start_tick(struct spu_context *ctx); -void spu_stop_tick(struct spu_context *ctx); -void spu_sched_tick(struct work_struct *work); +void spu_set_timeslice(struct spu_context *ctx); +void spu_update_sched_info(struct spu_context *ctx); +void __spu_update_sched_info(struct spu_context *ctx); int __init spu_sched_init(void); void __exit spu_sched_exit(void); @@ -210,7 +241,7 @@ extern char *isolated_loader; /* * spufs_wait - * Same as wait_event_interruptible(), except that here + * Same as wait_event_interruptible(), except that here * we need to call spu_release(ctx) before sleeping, and * then spu_acquire(ctx) when awoken. */ @@ -256,4 +287,37 @@ struct spufs_coredump_reader { extern struct spufs_coredump_reader spufs_coredump_read[]; extern int spufs_coredump_num_notes; +/* + * This function is a little bit too large for an inline, but + * as fault.c is built into the kernel we can't move it out of + * line. + */ +static inline void spuctx_switch_state(struct spu_context *ctx, + enum spuctx_execution_state new_state) +{ + WARN_ON(!mutex_is_locked(&ctx->state_mutex)); + + if (ctx->stats.execution_state != new_state) { + unsigned long curtime = jiffies; + + ctx->stats.times[ctx->stats.execution_state] += + curtime - ctx->stats.tstamp; + ctx->stats.tstamp = curtime; + ctx->stats.execution_state = new_state; + } +} + +static inline void spu_switch_state(struct spu *spu, + enum spuctx_execution_state new_state) +{ + if (spu->stats.utilization_state != new_state) { + unsigned long curtime = jiffies; + + spu->stats.times[spu->stats.utilization_state] += + curtime - spu->stats.tstamp; + spu->stats.tstamp = curtime; + spu->stats.utilization_state = new_state; + } +} + #endif diff --git a/arch/powerpc/platforms/cell/spufs/switch.c b/arch/powerpc/platforms/cell/spufs/switch.c index 71a0b41adb8..9c506ba08cd 100644 --- a/arch/powerpc/platforms/cell/spufs/switch.c +++ b/arch/powerpc/platforms/cell/spufs/switch.c @@ -70,7 +70,7 @@ } #endif /* debug */ -#define POLL_WHILE_FALSE(_c) POLL_WHILE_TRUE(!(_c)) +#define POLL_WHILE_FALSE(_c) POLL_WHILE_TRUE(!(_c)) static inline void acquire_spu_lock(struct spu *spu) { @@ -387,6 +387,19 @@ static inline void save_ppu_querytype(struct spu_state *csa, struct spu *spu) csa->prob.dma_querytype_RW = in_be32(&prob->dma_querytype_RW); } +static inline void save_ppu_tagstatus(struct spu_state *csa, struct spu *spu) +{ + struct spu_problem __iomem *prob = spu->problem; + + /* Save the Prxy_TagStatus register in the CSA. + * + * It is unnecessary to restore dma_tagstatus_R, however, + * dma_tagstatus_R in the CSA is accessed via backing_ops, so + * we must save it. + */ + csa->prob.dma_tagstatus_R = in_be32(&prob->dma_tagstatus_R); +} + static inline void save_mfc_csr_tsq(struct spu_state *csa, struct spu *spu) { struct spu_priv2 __iomem *priv2 = spu->priv2; @@ -1812,6 +1825,7 @@ static void save_csa(struct spu_state *prev, struct spu *spu) save_mfc_queues(prev, spu); /* Step 19. */ save_ppu_querymask(prev, spu); /* Step 20. */ save_ppu_querytype(prev, spu); /* Step 21. */ + save_ppu_tagstatus(prev, spu); /* NEW. */ save_mfc_csr_tsq(prev, spu); /* Step 22. */ save_mfc_csr_cmd(prev, spu); /* Step 23. */ save_mfc_csr_ato(prev, spu); /* Step 24. */ @@ -1930,7 +1944,7 @@ static void harvest(struct spu_state *prev, struct spu *spu) reset_spu_privcntl(prev, spu); /* Step 16. */ reset_spu_lslr(prev, spu); /* Step 17. */ setup_mfc_sr1(prev, spu); /* Step 18. */ - spu_invalidate_slbs(spu); /* Step 19. */ + spu_invalidate_slbs(spu); /* Step 19. */ reset_ch_part1(prev, spu); /* Step 20. */ reset_ch_part2(prev, spu); /* Step 21. */ enable_interrupts(prev, spu); /* Step 22. */ diff --git a/arch/powerpc/platforms/chrp/Kconfig b/arch/powerpc/platforms/chrp/Kconfig index d2c69053196..22b4b4e3b6f 100644 --- a/arch/powerpc/platforms/chrp/Kconfig +++ b/arch/powerpc/platforms/chrp/Kconfig @@ -8,4 +8,5 @@ config PPC_CHRP select PPC_MPC106 select PPC_UDBG_16550 select PPC_NATIVE + select PCI default y diff --git a/arch/powerpc/platforms/chrp/Makefile b/arch/powerpc/platforms/chrp/Makefile index 902feb1ac43..4b3bfadc70f 100644 --- a/arch/powerpc/platforms/chrp/Makefile +++ b/arch/powerpc/platforms/chrp/Makefile @@ -1,4 +1,3 @@ -obj-y += setup.o time.o pegasos_eth.o -obj-$(CONFIG_PCI) += pci.o +obj-y += setup.o time.o pegasos_eth.o pci.o obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_NVRAM) += nvram.o diff --git a/arch/powerpc/platforms/chrp/pci.c b/arch/powerpc/platforms/chrp/pci.c index d32fedc991d..3690624e49d 100644 --- a/arch/powerpc/platforms/chrp/pci.c +++ b/arch/powerpc/platforms/chrp/pci.c @@ -99,7 +99,7 @@ int rtas_read_config(struct pci_bus *bus, unsigned int devfn, int offset, struct pci_controller *hose = bus->sysdata; unsigned long addr = (offset & 0xff) | ((devfn & 0xff) << 8) | (((bus->number - hose->first_busno) & 0xff) << 16) - | (hose->index << 24); + | (hose->global_number << 24); int ret = -1; int rval; @@ -114,7 +114,7 @@ int rtas_write_config(struct pci_bus *bus, unsigned int devfn, int offset, struct pci_controller *hose = bus->sysdata; unsigned long addr = (offset & 0xff) | ((devfn & 0xff) << 8) | (((bus->number - hose->first_busno) & 0xff) << 16) - | (hose->index << 24); + | (hose->global_number << 24); int rval; rval = rtas_call(rtas_token("write-pci-config"), 3, 1, NULL, @@ -254,13 +254,12 @@ chrp_find_bridges(void) printk(" at %llx", (unsigned long long)r.start); printk("\n"); - hose = pcibios_alloc_controller(); + hose = pcibios_alloc_controller(dev); if (!hose) { printk("Can't allocate PCI controller structure for %s\n", dev->full_name); continue; } - hose->arch_data = dev; hose->first_busno = bus_range[0]; hose->last_busno = bus_range[1]; diff --git a/arch/powerpc/platforms/embedded6xx/Kconfig b/arch/powerpc/platforms/embedded6xx/Kconfig index f2d26268ca6..bec772674e4 100644 --- a/arch/powerpc/platforms/embedded6xx/Kconfig +++ b/arch/powerpc/platforms/embedded6xx/Kconfig @@ -28,6 +28,7 @@ config PPC_HOLLY bool "PPC750GX/CL with TSI10x bridge (Hickory/Holly)" select TSI108_BRIDGE select PPC_UDBG_16550 + select WANT_DEVICE_TREE help Select PPC_HOLLY if configuring for an IBM 750GX/CL Eval Board with TSI108/9 bridge (Hickory/Holly) @@ -44,6 +45,7 @@ endchoice config TSI108_BRIDGE bool depends on MPC7448HPC2 || PPC_HOLLY + select PCI select MPIC select MPIC_WEIRD default y diff --git a/arch/powerpc/platforms/embedded6xx/holly.c b/arch/powerpc/platforms/embedded6xx/holly.c index 3a0b4a01401..6292e36dc57 100644 --- a/arch/powerpc/platforms/embedded6xx/holly.c +++ b/arch/powerpc/platforms/embedded6xx/holly.c @@ -45,7 +45,7 @@ #define HOLLY_PCI_CFG_PHYS 0x7c000000 -int holly_exclude_device(u_char bus, u_char devfn) +int holly_exclude_device(struct pci_controller *hose, u_char bus, u_char devfn) { if (bus == 0 && PCI_SLOT(devfn) == 0) return PCIBIOS_DEVICE_NOT_FOUND; diff --git a/arch/powerpc/platforms/embedded6xx/linkstation.c b/arch/powerpc/platforms/embedded6xx/linkstation.c index b412f006a9c..f4d0a7a603f 100644 --- a/arch/powerpc/platforms/embedded6xx/linkstation.c +++ b/arch/powerpc/platforms/embedded6xx/linkstation.c @@ -54,8 +54,9 @@ static struct mtd_partition linkstation_physmap_partitions[] = { }, }; -static int __init add_bridge(struct device_node *dev) +static int __init linkstation_add_bridge(struct device_node *dev) { +#ifdef CONFIG_PCI int len; struct pci_controller *hose; const int *bus_range; @@ -67,18 +68,17 @@ static int __init add_bridge(struct device_node *dev) printk(KERN_WARNING "Can't get bus-range for %s, assume" " bus 0\n", dev->full_name); - hose = pcibios_alloc_controller(); + hose = pcibios_alloc_controller(dev); if (hose == NULL) return -ENOMEM; hose->first_busno = bus_range ? bus_range[0] : 0; hose->last_busno = bus_range ? bus_range[1] : 0xff; - hose->arch_data = dev; setup_indirect_pci(hose, 0xfec00000, 0xfee00000); /* Interpret the "ranges" property */ /* This also maps the I/O region and sets isa_io/mem_base */ pci_process_bridge_OF_ranges(hose, dev, 1); - +#endif return 0; } @@ -92,7 +92,7 @@ static void __init linkstation_setup_arch(void) /* Lookup PCI host bridges */ for (np = NULL; (np = of_find_node_by_type(np, "pci")) != NULL;) - add_bridge(np); + linkstation_add_bridge(np); printk(KERN_INFO "BUFFALO Network Attached Storage Series\n"); printk(KERN_INFO "(C) 2002-2005 BUFFALO INC.\n"); diff --git a/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c b/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c index 4542e0c837c..1e3cc69487b 100644 --- a/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c +++ b/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c @@ -54,15 +54,10 @@ #define MPC7448HPC2_PCI_CFG_PHYS 0xfb000000 -#ifndef CONFIG_PCI -isa_io_base = MPC7448_HPC2_ISA_IO_BASE; -isa_mem_base = MPC7448_HPC2_ISA_MEM_BASE; -pci_dram_offset = MPC7448_HPC2_PCI_MEM_OFFSET; -#endif - extern void _nmask_and_or_msr(unsigned long nmask, unsigned long or_val); -int mpc7448_hpc2_exclude_device(u_char bus, u_char devfn) +int mpc7448_hpc2_exclude_device(struct pci_controller *hose, + u_char bus, u_char devfn) { if (bus == 0 && PCI_SLOT(devfn) == 0) return PCIBIOS_DEVICE_NOT_FOUND; diff --git a/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.h b/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.h index a543a5242e3..f7e0e0c7f8d 100644 --- a/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.h +++ b/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.h @@ -18,9 +18,4 @@ #include <asm/ppcboot.h> -/* Base Addresses for the PCI bus - */ -#define MPC7448_HPC2_PCI_MEM_OFFSET (0x00000000) -#define MPC7448_HPC2_ISA_IO_BASE (0x00000000) -#define MPC7448_HPC2_ISA_MEM_BASE (0x00000000) #endif /* __PPC_PLATFORMS_MPC7448_HPC2_H */ diff --git a/arch/powerpc/platforms/iseries/call_hpt.h b/arch/powerpc/platforms/iseries/call_hpt.h index a843b0f87b7..8d95fe4b554 100644 --- a/arch/powerpc/platforms/iseries/call_hpt.h +++ b/arch/powerpc/platforms/iseries/call_hpt.h @@ -76,24 +76,25 @@ static inline u64 HvCallHpt_invalidateSetSwBitsGet(u32 hpteIndex, u8 bitson, return compressedStatus; } -static inline u64 HvCallHpt_findValid(hpte_t *hpte, u64 vpn) +static inline u64 HvCallHpt_findValid(struct hash_pte *hpte, u64 vpn) { return HvCall3Ret16(HvCallHptFindValid, hpte, vpn, 0, 0); } -static inline u64 HvCallHpt_findNextValid(hpte_t *hpte, u32 hpteIndex, +static inline u64 HvCallHpt_findNextValid(struct hash_pte *hpte, u32 hpteIndex, u8 bitson, u8 bitsoff) { return HvCall3Ret16(HvCallHptFindNextValid, hpte, hpteIndex, bitson, bitsoff); } -static inline void HvCallHpt_get(hpte_t *hpte, u32 hpteIndex) +static inline void HvCallHpt_get(struct hash_pte *hpte, u32 hpteIndex) { HvCall2Ret16(HvCallHptGet, hpte, hpteIndex, 0); } -static inline void HvCallHpt_addValidate(u32 hpteIndex, u32 hBit, hpte_t *hpte) +static inline void HvCallHpt_addValidate(u32 hpteIndex, u32 hBit, + struct hash_pte *hpte) { HvCall4(HvCallHptAddValidate, hpteIndex, hBit, hpte->v, hpte->r); } diff --git a/arch/powerpc/platforms/iseries/htab.c b/arch/powerpc/platforms/iseries/htab.c index ed44dfceaa4..b4e2c7a038e 100644 --- a/arch/powerpc/platforms/iseries/htab.c +++ b/arch/powerpc/platforms/iseries/htab.c @@ -44,7 +44,7 @@ long iSeries_hpte_insert(unsigned long hpte_group, unsigned long va, unsigned long vflags, int psize) { long slot; - hpte_t lhpte; + struct hash_pte lhpte; int secondary = 0; BUG_ON(psize != MMU_PAGE_4K); @@ -99,7 +99,7 @@ long iSeries_hpte_insert(unsigned long hpte_group, unsigned long va, static unsigned long iSeries_hpte_getword0(unsigned long slot) { - hpte_t hpte; + struct hash_pte hpte; HvCallHpt_get(&hpte, slot); return hpte.v; @@ -144,7 +144,7 @@ static long iSeries_hpte_remove(unsigned long hpte_group) static long iSeries_hpte_updatepp(unsigned long slot, unsigned long newpp, unsigned long va, int psize, int local) { - hpte_t hpte; + struct hash_pte hpte; unsigned long want_v; iSeries_hlock(slot); @@ -176,7 +176,7 @@ static long iSeries_hpte_updatepp(unsigned long slot, unsigned long newpp, */ static long iSeries_hpte_find(unsigned long vpn) { - hpte_t hpte; + struct hash_pte hpte; long slot; /* diff --git a/arch/powerpc/platforms/iseries/pci.c b/arch/powerpc/platforms/iseries/pci.c index 9c974227155..da87162000f 100644 --- a/arch/powerpc/platforms/iseries/pci.c +++ b/arch/powerpc/platforms/iseries/pci.c @@ -742,6 +742,11 @@ void __init iSeries_pcibios_init(void) /* Install IO hooks */ ppc_pci_io = iseries_pci_io; + /* iSeries has no IO space in the common sense, it needs to set + * the IO base to 0 + */ + pci_io_base = 0; + if (root == NULL) { printk(KERN_CRIT "iSeries_pcibios_init: can't find root " "of device tree\n"); @@ -763,7 +768,7 @@ void __init iSeries_pcibios_init(void) if (phb == NULL) continue; - phb->pci_mem_offset = phb->local_number = bus; + phb->pci_mem_offset = bus; phb->first_busno = bus; phb->last_busno = bus; phb->ops = &iSeries_pci_ops; diff --git a/arch/powerpc/platforms/iseries/setup.c b/arch/powerpc/platforms/iseries/setup.c index 7f5dcee814d..13a8b1908de 100644 --- a/arch/powerpc/platforms/iseries/setup.c +++ b/arch/powerpc/platforms/iseries/setup.c @@ -79,8 +79,6 @@ extern void iSeries_pci_final_fixup(void); static void iSeries_pci_final_fixup(void) { } #endif -extern unsigned long iSeries_recal_tb; -extern unsigned long iSeries_recal_titan; struct MemoryBlock { unsigned long absStart; @@ -292,8 +290,8 @@ static void __init iSeries_init_early(void) { DBG(" -> iSeries_init_early()\n"); - iSeries_recal_tb = get_tb(); - iSeries_recal_titan = HvCallXm_loadTod(); + /* Snapshot the timebase, for use in later recalibration */ + iSeries_time_init_early(); /* * Initialize the DMA/TCE management diff --git a/arch/powerpc/platforms/maple/pci.c b/arch/powerpc/platforms/maple/pci.c index 7aaa5bbc936..fceaae40fe7 100644 --- a/arch/powerpc/platforms/maple/pci.c +++ b/arch/powerpc/platforms/maple/pci.c @@ -444,7 +444,7 @@ static void __init setup_u3_ht(struct pci_controller* hose) u3_ht = hose; } -static int __init add_bridge(struct device_node *dev) +static int __init maple_add_bridge(struct device_node *dev) { int len; struct pci_controller *hose; @@ -519,23 +519,6 @@ void __devinit maple_pci_irq_fixup(struct pci_dev *dev) DBG(" <- maple_pci_irq_fixup\n"); } -static void __init maple_fixup_phb_resources(void) -{ - struct pci_controller *hose, *tmp; - - list_for_each_entry_safe(hose, tmp, &hose_list, list_node) { - unsigned long offset = (unsigned long)hose->io_base_virt - pci_io_base; - - hose->io_resource.start += offset; - hose->io_resource.end += offset; - - printk(KERN_INFO "PCI Host %d, io start: %llx; io end: %llx\n", - hose->global_number, - (unsigned long long)hose->io_resource.start, - (unsigned long long)hose->io_resource.end); - } -} - void __init maple_pci_init(void) { struct device_node *np, *root; @@ -558,7 +541,7 @@ void __init maple_pci_init(void) continue; if ((of_device_is_compatible(np, "u4-pcie") || of_device_is_compatible(np, "u3-agp")) && - add_bridge(np) == 0) + maple_add_bridge(np) == 0) of_node_get(np); if (of_device_is_compatible(np, "u3-ht")) { @@ -570,27 +553,9 @@ void __init maple_pci_init(void) /* Now setup the HyperTransport host if we found any */ - if (ht && add_bridge(ht) != 0) + if (ht && maple_add_bridge(ht) != 0) of_node_put(ht); - /* - * We need to call pci_setup_phb_io for the HT bridge first - * so it gets the I/O port numbers starting at 0, and we - * need to call it for the AGP bridge after that so it gets - * small positive I/O port numbers. - */ - if (u3_ht) - pci_setup_phb_io(u3_ht, 1); - if (u3_agp) - pci_setup_phb_io(u3_agp, 0); - if (u4_pcie) - pci_setup_phb_io(u4_pcie, 0); - - /* Fixup the IO resources on our host bridges as the common code - * does it only for childs of the host bridges - */ - maple_fixup_phb_resources(); - /* Setup the linkage between OF nodes and PHBs */ pci_devs_phb_init(); diff --git a/arch/powerpc/platforms/pasemi/Kconfig b/arch/powerpc/platforms/pasemi/Kconfig index 7c5076e38ea..95cd90fd81c 100644 --- a/arch/powerpc/platforms/pasemi/Kconfig +++ b/arch/powerpc/platforms/pasemi/Kconfig @@ -25,4 +25,13 @@ config PPC_PASEMI_MDIO help Driver for MDIO via GPIO on PWRficient platforms +config ELECTRA_IDE + tristate "Electra IDE driver" + default y + depends on PPC_PASEMI && ATA + select PATA_PLATFORM + help + This includes driver support for the Electra on-board IDE + interface. + endmenu diff --git a/arch/powerpc/platforms/pasemi/Makefile b/arch/powerpc/platforms/pasemi/Makefile index 2cd2a4f26a4..f47fcac7e58 100644 --- a/arch/powerpc/platforms/pasemi/Makefile +++ b/arch/powerpc/platforms/pasemi/Makefile @@ -1,3 +1,4 @@ obj-y += setup.o pci.o time.o idle.o powersave.o iommu.o obj-$(CONFIG_PPC_PASEMI_MDIO) += gpio_mdio.o +obj-$(CONFIG_ELECTRA_IDE) += electra_ide.o obj-$(CONFIG_PPC_PASEMI_CPUFREQ) += cpufreq.o diff --git a/arch/powerpc/platforms/pasemi/electra_ide.c b/arch/powerpc/platforms/pasemi/electra_ide.c new file mode 100644 index 00000000000..12fb0c94926 --- /dev/null +++ b/arch/powerpc/platforms/pasemi/electra_ide.c @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2007 PA Semi, Inc + * + * Maintained by: Olof Johansson <olof@lixom.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 <linux/platform_device.h> + +#include <asm/prom.h> +#include <asm/system.h> + +/* The electra IDE interface is incredibly simple: Just a device on the localbus + * with interrupts hooked up to one of the GPIOs. The device tree contains the + * address window and interrupt mappings already, and the pata_platform driver handles + * the rest. We just need to hook the two up. + */ + +#define MAX_IFS 4 /* really, we have only one */ + +static struct platform_device *pdevs[MAX_IFS]; + +static int __devinit electra_ide_init(void) +{ + struct device_node *np; + struct resource r[3]; + int ret = 0; + int i; + + np = of_find_compatible_node(NULL, "ide", "electra-ide"); + i = 0; + + while (np && i < MAX_IFS) { + memset(r, 0, sizeof(r)); + + /* pata_platform wants two address ranges: one for the base registers, + * another for the control (altstatus). It's located at offset 0x3f6 in + * the window, but the device tree only has one large register window + * that covers both ranges. So we need to split it up by hand here: + */ + + ret = of_address_to_resource(np, 0, &r[0]); + if (ret) + goto out; + ret = of_address_to_resource(np, 0, &r[1]); + if (ret) + goto out; + + r[1].start += 0x3f6; + r[0].end = r[1].start-1; + + r[2].start = irq_of_parse_and_map(np, 0); + r[2].end = irq_of_parse_and_map(np, 0); + r[2].flags = IORESOURCE_IRQ; + + pr_debug("registering platform device at 0x%lx/0x%lx, irq is %ld\n", + r[0].start, r[1].start, r[2].start); + pdevs[i] = platform_device_register_simple("pata_platform", i, r, 3); + if (IS_ERR(pdevs[i])) { + ret = PTR_ERR(pdevs[i]); + pdevs[i] = NULL; + goto out; + } + np = of_find_compatible_node(np, "ide", "electra-ide"); + } +out: + return ret; +} +module_init(electra_ide_init); + +static void __devexit electra_ide_exit(void) +{ + int i; + + for (i = 0; i < MAX_IFS; i++) + if (pdevs[i]) + platform_device_unregister(pdevs[i]); +} +module_exit(electra_ide_exit); + + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR ("Olof Johansson <olof@lixom.net>"); +MODULE_DESCRIPTION("PA Semi Electra IDE driver"); diff --git a/arch/powerpc/platforms/pasemi/pci.c b/arch/powerpc/platforms/pasemi/pci.c index bbc6dfcfaa9..ab1f5f62bcd 100644 --- a/arch/powerpc/platforms/pasemi/pci.c +++ b/arch/powerpc/platforms/pasemi/pci.c @@ -132,7 +132,7 @@ static void __init setup_pa_pxp(struct pci_controller *hose) hose->cfg_data = ioremap(0xe0000000, 0x10000000); } -static int __init add_bridge(struct device_node *dev) +static int __init pas_add_bridge(struct device_node *dev) { struct pci_controller *hose; @@ -150,29 +150,11 @@ static int __init add_bridge(struct device_node *dev) printk(KERN_INFO "Found PA-PXP PCI host bridge.\n"); /* Interpret the "ranges" property */ - /* This also maps the I/O region and sets isa_io/mem_base */ pci_process_bridge_OF_ranges(hose, dev, 1); - pci_setup_phb_io(hose, 1); return 0; } - -static void __init pas_fixup_phb_resources(void) -{ - struct pci_controller *hose, *tmp; - - list_for_each_entry_safe(hose, tmp, &hose_list, list_node) { - unsigned long offset = (unsigned long)hose->io_base_virt - pci_io_base; - hose->io_resource.start += offset; - hose->io_resource.end += offset; - printk(KERN_INFO "PCI Host %d, io start: %lx; io end: %lx\n", - hose->global_number, - hose->io_resource.start, hose->io_resource.end); - } -} - - void __init pas_pci_init(void) { struct device_node *np, *root; @@ -185,13 +167,11 @@ void __init pas_pci_init(void) } for (np = NULL; (np = of_get_next_child(root, np)) != NULL;) - if (np->name && !strcmp(np->name, "pxp") && !add_bridge(np)) + if (np->name && !strcmp(np->name, "pxp") && !pas_add_bridge(np)) of_node_get(np); of_node_put(root); - pas_fixup_phb_resources(); - /* Setup the linkage between OF nodes and PHBs */ pci_devs_phb_init(); diff --git a/arch/powerpc/platforms/pasemi/setup.c b/arch/powerpc/platforms/pasemi/setup.c index c5a3f61f8d8..ffe6528048b 100644 --- a/arch/powerpc/platforms/pasemi/setup.c +++ b/arch/powerpc/platforms/pasemi/setup.c @@ -239,7 +239,7 @@ static int __init pas_probe(void) return 1; } -define_machine(pas) { +define_machine(pasemi) { .name = "PA Semi PA6T-1682M", .probe = pas_probe, .setup_arch = pas_setup_arch, diff --git a/arch/powerpc/platforms/powermac/Kconfig b/arch/powerpc/platforms/powermac/Kconfig index 5b7afe50039..055990ca8ce 100644 --- a/arch/powerpc/platforms/powermac/Kconfig +++ b/arch/powerpc/platforms/powermac/Kconfig @@ -2,6 +2,7 @@ config PPC_PMAC bool "Apple PowerMac based machines" depends on PPC_MULTIPLATFORM select MPIC + select PCI select PPC_INDIRECT_PCI if PPC32 select PPC_MPC106 if PPC32 select PPC_NATIVE diff --git a/arch/powerpc/platforms/powermac/low_i2c.c b/arch/powerpc/platforms/powermac/low_i2c.c index 3f507ab9c5e..efdf5eb81ec 100644 --- a/arch/powerpc/platforms/powermac/low_i2c.c +++ b/arch/powerpc/platforms/powermac/low_i2c.c @@ -42,6 +42,7 @@ #include <linux/interrupt.h> #include <linux/completion.h> #include <linux/timer.h> +#include <linux/mutex.h> #include <asm/keylargo.h> #include <asm/uninorth.h> #include <asm/io.h> @@ -84,7 +85,7 @@ struct pmac_i2c_bus void *hostdata; int channel; /* some hosts have multiple */ int mode; /* current mode */ - struct semaphore sem; + struct mutex mutex; int opened; int polled; /* open mode */ struct platform_device *platform_dev; @@ -104,7 +105,7 @@ static LIST_HEAD(pmac_i2c_busses); struct pmac_i2c_host_kw { - struct semaphore mutex; /* Access mutex for use by + struct mutex mutex; /* Access mutex for use by * i2c-keywest */ void __iomem *base; /* register base address */ int bsteps; /* register stepping */ @@ -375,14 +376,14 @@ static void kw_i2c_timeout(unsigned long data) static int kw_i2c_open(struct pmac_i2c_bus *bus) { struct pmac_i2c_host_kw *host = bus->hostdata; - down(&host->mutex); + mutex_lock(&host->mutex); return 0; } static void kw_i2c_close(struct pmac_i2c_bus *bus) { struct pmac_i2c_host_kw *host = bus->hostdata; - up(&host->mutex); + mutex_unlock(&host->mutex); } static int kw_i2c_xfer(struct pmac_i2c_bus *bus, u8 addrdir, int subsize, @@ -498,7 +499,7 @@ static struct pmac_i2c_host_kw *__init kw_i2c_host_init(struct device_node *np) kfree(host); return NULL; } - init_MUTEX(&host->mutex); + mutex_init(&host->mutex); init_completion(&host->complete); spin_lock_init(&host->lock); init_timer(&host->timeout_timer); @@ -571,7 +572,7 @@ static void __init kw_i2c_add(struct pmac_i2c_host_kw *host, bus->open = kw_i2c_open; bus->close = kw_i2c_close; bus->xfer = kw_i2c_xfer; - init_MUTEX(&bus->sem); + mutex_init(&bus->mutex); if (controller == busnode) bus->flags = pmac_i2c_multibus; list_add(&bus->link, &pmac_i2c_busses); @@ -798,7 +799,7 @@ static void __init pmu_i2c_probe(void) bus->mode = pmac_i2c_mode_std; bus->hostdata = bus + 1; bus->xfer = pmu_i2c_xfer; - init_MUTEX(&bus->sem); + mutex_init(&bus->mutex); bus->flags = pmac_i2c_multibus; list_add(&bus->link, &pmac_i2c_busses); @@ -921,7 +922,7 @@ static void __init smu_i2c_probe(void) bus->mode = pmac_i2c_mode_std; bus->hostdata = bus + 1; bus->xfer = smu_i2c_xfer; - init_MUTEX(&bus->sem); + mutex_init(&bus->mutex); bus->flags = 0; list_add(&bus->link, &pmac_i2c_busses); @@ -1093,13 +1094,13 @@ int pmac_i2c_open(struct pmac_i2c_bus *bus, int polled) { int rc; - down(&bus->sem); + mutex_lock(&bus->mutex); bus->polled = polled || pmac_i2c_force_poll; bus->opened = 1; bus->mode = pmac_i2c_mode_std; if (bus->open && (rc = bus->open(bus)) != 0) { bus->opened = 0; - up(&bus->sem); + mutex_unlock(&bus->mutex); return rc; } return 0; @@ -1112,7 +1113,7 @@ void pmac_i2c_close(struct pmac_i2c_bus *bus) if (bus->close) bus->close(bus); bus->opened = 0; - up(&bus->sem); + mutex_unlock(&bus->mutex); } EXPORT_SYMBOL_GPL(pmac_i2c_close); diff --git a/arch/powerpc/platforms/powermac/pci.c b/arch/powerpc/platforms/powermac/pci.c index c4af9e21ac9..92586db1975 100644 --- a/arch/powerpc/platforms/powermac/pci.c +++ b/arch/powerpc/platforms/powermac/pci.c @@ -35,8 +35,6 @@ #define DBG(x...) #endif -static int add_bridge(struct device_node *dev); - /* XXX Could be per-controller, but I don't think we risk anything by * assuming we won't have both UniNorth and Bandit */ static int has_uninorth; @@ -897,7 +895,7 @@ static void __init setup_u3_ht(struct pci_controller* hose) * "pci" (a MPC106) and no bandit or chaos bridges, and contrariwise, * if we have one or more bandit or chaos bridges, we don't have a MPC106. */ -static int __init add_bridge(struct device_node *dev) +static int __init pmac_add_bridge(struct device_node *dev) { int len; struct pci_controller *hose; @@ -918,15 +916,9 @@ static int __init add_bridge(struct device_node *dev) " bus 0\n", dev->full_name); } - /* XXX Different prototypes, to be merged */ -#ifdef CONFIG_PPC64 hose = pcibios_alloc_controller(dev); -#else - hose = pcibios_alloc_controller(); -#endif if (!hose) return -ENOMEM; - hose->arch_data = dev; hose->first_busno = bus_range ? bus_range[0] : 0; hose->last_busno = bus_range ? bus_range[1] : 0xff; @@ -1006,19 +998,6 @@ void __devinit pmac_pci_irq_fixup(struct pci_dev *dev) #endif /* CONFIG_PPC32 */ } -#ifdef CONFIG_PPC64 -static void __init pmac_fixup_phb_resources(void) -{ - struct pci_controller *hose, *tmp; - - list_for_each_entry_safe(hose, tmp, &hose_list, list_node) { - printk(KERN_INFO "PCI Host %d, io start: %lx; io end: %lx\n", - hose->global_number, - hose->io_resource.start, hose->io_resource.end); - } -} -#endif - void __init pmac_pci_init(void) { struct device_node *np, *root; @@ -1036,7 +1015,7 @@ void __init pmac_pci_init(void) if (strcmp(np->name, "bandit") == 0 || strcmp(np->name, "chaos") == 0 || strcmp(np->name, "pci") == 0) { - if (add_bridge(np) == 0) + if (pmac_add_bridge(np) == 0) of_node_get(np); } if (strcmp(np->name, "ht") == 0) { @@ -1050,28 +1029,9 @@ void __init pmac_pci_init(void) /* Probe HT last as it relies on the agp resources to be already * setup */ - if (ht && add_bridge(ht) != 0) + if (ht && pmac_add_bridge(ht) != 0) of_node_put(ht); - /* - * We need to call pci_setup_phb_io for the HT bridge first - * so it gets the I/O port numbers starting at 0, and we - * need to call it for the AGP bridge after that so it gets - * small positive I/O port numbers. - */ - if (u3_ht) - pci_setup_phb_io(u3_ht, 1); - if (u3_agp) - pci_setup_phb_io(u3_agp, 0); - if (u4_pcie) - pci_setup_phb_io(u4_pcie, 0); - - /* - * On ppc64, fixup the IO resources on our host bridges as - * the common code does it only for children of the host bridges - */ - pmac_fixup_phb_resources(); - /* Setup the linkage between OF nodes and PHBs */ pci_devs_phb_init(); diff --git a/arch/powerpc/platforms/ps3/Kconfig b/arch/powerpc/platforms/ps3/Kconfig index 40f0008af4d..a05079b0769 100644 --- a/arch/powerpc/platforms/ps3/Kconfig +++ b/arch/powerpc/platforms/ps3/Kconfig @@ -7,6 +7,7 @@ config PPC_PS3 select USB_OHCI_BIG_ENDIAN_MMIO select USB_ARCH_HAS_EHCI select USB_EHCI_BIG_ENDIAN_MMIO + select MEMORY_HOTPLUG help This option enables support for the Sony PS3 game console and other platforms using the PS3 hypervisor. @@ -73,18 +74,12 @@ config PS3_USE_LPAR_ADDR config PS3_VUART depends on PPC_PS3 - bool "PS3 Virtual UART support" if PS3_ADVANCED - 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. + tristate config PS3_PS3AV + depends on PPC_PS3 tristate "PS3 AV settings driver" if PS3_ADVANCED - depends on PS3_VUART + select PS3_VUART default y help Include support for the PS3 AV Settings driver. @@ -93,13 +88,18 @@ config PS3_PS3AV general, all users will say Y or M. config PS3_SYS_MANAGER - bool "PS3 System Manager driver" if PS3_ADVANCED - depends on PS3_VUART - default y + depends on PPC_PS3 + tristate "PS3 System Manager driver" if PS3_ADVANCED + select PS3_VUART + default m help Include support for the PS3 System Manager. This support is required for system control. In - general, all users will say Y. + general, all users will say Y or M. + +config PS3_STORAGE + depends on PPC_PS3 + tristate endmenu diff --git a/arch/powerpc/platforms/ps3/Makefile b/arch/powerpc/platforms/ps3/Makefile index a0048fcf086..ac1bdf844ec 100644 --- a/arch/powerpc/platforms/ps3/Makefile +++ b/arch/powerpc/platforms/ps3/Makefile @@ -4,3 +4,4 @@ obj-y += system-bus.o obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_SPU_BASE) += spu.o +obj-y += device-init.o diff --git a/arch/powerpc/platforms/ps3/device-init.c b/arch/powerpc/platforms/ps3/device-init.c new file mode 100644 index 00000000000..825ebb2cbc2 --- /dev/null +++ b/arch/powerpc/platforms/ps3/device-init.c @@ -0,0 +1,785 @@ +/* + * PS3 device registration routines. + * + * Copyright (C) 2007 Sony Computer Entertainment Inc. + * Copyright 2007 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 <linux/delay.h> +#include <linux/freezer.h> +#include <linux/kernel.h> +#include <linux/kthread.h> +#include <linux/init.h> + +#include <asm/firmware.h> +#include <asm/lv1call.h> +#include <asm/ps3stor.h> + +#include "platform.h" + +/** + * ps3_setup_gelic_device - Setup and register a gelic device instance. + * + * Allocates memory for a struct ps3_system_bus_device instance, initialises the + * structure members, and registers the device instance with the system bus. + */ + +static int __init ps3_setup_gelic_device( + const struct ps3_repository_device *repo) +{ + int result; + struct layout { + struct ps3_system_bus_device dev; + struct ps3_dma_region d_region; + } *p; + + pr_debug(" -> %s:%d\n", __func__, __LINE__); + + BUG_ON(repo->bus_type != PS3_BUS_TYPE_SB); + BUG_ON(repo->dev_type != PS3_DEV_TYPE_SB_GELIC); + + p = kzalloc(sizeof(struct layout), GFP_KERNEL); + + if (!p) { + result = -ENOMEM; + goto fail_malloc; + } + + p->dev.match_id = PS3_MATCH_ID_GELIC; + p->dev.dev_type = PS3_DEVICE_TYPE_SB; + p->dev.bus_id = repo->bus_id; + p->dev.dev_id = repo->dev_id; + p->dev.d_region = &p->d_region; + + result = ps3_repository_find_interrupt(repo, + PS3_INTERRUPT_TYPE_EVENT_PORT, &p->dev.interrupt_id); + + if (result) { + pr_debug("%s:%d ps3_repository_find_interrupt failed\n", + __func__, __LINE__); + goto fail_find_interrupt; + } + + BUG_ON(p->dev.interrupt_id != 0); + + result = ps3_dma_region_init(&p->dev, p->dev.d_region, PS3_DMA_64K, + PS3_DMA_OTHER, NULL, 0); + + if (result) { + pr_debug("%s:%d ps3_dma_region_init failed\n", + __func__, __LINE__); + goto fail_dma_init; + } + + result = ps3_system_bus_device_register(&p->dev); + + if (result) { + pr_debug("%s:%d ps3_system_bus_device_register failed\n", + __func__, __LINE__); + goto fail_device_register; + } + + pr_debug(" <- %s:%d\n", __func__, __LINE__); + return result; + +fail_device_register: +fail_dma_init: +fail_find_interrupt: + kfree(p); +fail_malloc: + pr_debug(" <- %s:%d: fail.\n", __func__, __LINE__); + return result; +} + +static int __init_refok ps3_setup_uhc_device( + const struct ps3_repository_device *repo, enum ps3_match_id match_id, + enum ps3_interrupt_type interrupt_type, enum ps3_reg_type reg_type) +{ + int result; + struct layout { + struct ps3_system_bus_device dev; + struct ps3_dma_region d_region; + struct ps3_mmio_region m_region; + } *p; + u64 bus_addr; + u64 len; + + pr_debug(" -> %s:%d\n", __func__, __LINE__); + + BUG_ON(repo->bus_type != PS3_BUS_TYPE_SB); + BUG_ON(repo->dev_type != PS3_DEV_TYPE_SB_USB); + + p = kzalloc(sizeof(struct layout), GFP_KERNEL); + + if (!p) { + result = -ENOMEM; + goto fail_malloc; + } + + p->dev.match_id = match_id; + p->dev.dev_type = PS3_DEVICE_TYPE_SB; + p->dev.bus_id = repo->bus_id; + p->dev.dev_id = repo->dev_id; + p->dev.d_region = &p->d_region; + p->dev.m_region = &p->m_region; + + result = ps3_repository_find_interrupt(repo, + interrupt_type, &p->dev.interrupt_id); + + if (result) { + pr_debug("%s:%d ps3_repository_find_interrupt failed\n", + __func__, __LINE__); + goto fail_find_interrupt; + } + + result = ps3_repository_find_reg(repo, reg_type, + &bus_addr, &len); + + if (result) { + pr_debug("%s:%d ps3_repository_find_reg failed\n", + __func__, __LINE__); + goto fail_find_reg; + } + + result = ps3_dma_region_init(&p->dev, p->dev.d_region, PS3_DMA_64K, + PS3_DMA_INTERNAL, NULL, 0); + + if (result) { + pr_debug("%s:%d ps3_dma_region_init failed\n", + __func__, __LINE__); + goto fail_dma_init; + } + + result = ps3_mmio_region_init(&p->dev, p->dev.m_region, bus_addr, len, + PS3_MMIO_4K); + + if (result) { + pr_debug("%s:%d ps3_mmio_region_init failed\n", + __func__, __LINE__); + goto fail_mmio_init; + } + + result = ps3_system_bus_device_register(&p->dev); + + if (result) { + pr_debug("%s:%d ps3_system_bus_device_register failed\n", + __func__, __LINE__); + goto fail_device_register; + } + + pr_debug(" <- %s:%d\n", __func__, __LINE__); + return result; + +fail_device_register: +fail_mmio_init: +fail_dma_init: +fail_find_reg: +fail_find_interrupt: + kfree(p); +fail_malloc: + pr_debug(" <- %s:%d: fail.\n", __func__, __LINE__); + return result; +} + +static int __init ps3_setup_ehci_device( + const struct ps3_repository_device *repo) +{ + return ps3_setup_uhc_device(repo, PS3_MATCH_ID_EHCI, + PS3_INTERRUPT_TYPE_SB_EHCI, PS3_REG_TYPE_SB_EHCI); +} + +static int __init ps3_setup_ohci_device( + const struct ps3_repository_device *repo) +{ + return ps3_setup_uhc_device(repo, PS3_MATCH_ID_OHCI, + PS3_INTERRUPT_TYPE_SB_OHCI, PS3_REG_TYPE_SB_OHCI); +} + +static int __init ps3_setup_vuart_device(enum ps3_match_id match_id, + unsigned int port_number) +{ + int result; + struct layout { + struct ps3_system_bus_device dev; + } *p; + + pr_debug(" -> %s:%d: match_id %u, port %u\n", __func__, __LINE__, + match_id, port_number); + + p = kzalloc(sizeof(struct layout), GFP_KERNEL); + + if (!p) + return -ENOMEM; + + p->dev.match_id = match_id; + p->dev.dev_type = PS3_DEVICE_TYPE_VUART; + p->dev.port_number = port_number; + + result = ps3_system_bus_device_register(&p->dev); + + if (result) + pr_debug("%s:%d ps3_system_bus_device_register failed\n", + __func__, __LINE__); + + pr_debug(" <- %s:%d\n", __func__, __LINE__); + return result; +} + +static int ps3stor_wait_for_completion(u64 dev_id, u64 tag, + unsigned int timeout) +{ + int result = -1; + unsigned int retries = 0; + u64 status; + + for (retries = 0; retries < timeout; retries++) { + result = lv1_storage_check_async_status(dev_id, tag, &status); + if (!result) + break; + + msleep(1); + } + + if (result) + pr_debug("%s:%u: check_async_status: %s, status %lx\n", + __func__, __LINE__, ps3_result(result), status); + + return result; +} + +/** + * ps3_storage_wait_for_device - Wait for a storage device to become ready. + * @repo: The repository device to wait for. + * + * Uses the hypervisor's storage device notification mechanism to wait until + * a storage device is ready. The device notification mechanism uses a + * psuedo device (id = -1) to asynchronously notify the guest when storage + * devices become ready. The notification device has a block size of 512 + * bytes. + */ + +static int ps3_storage_wait_for_device(const struct ps3_repository_device *repo) +{ + int result; + const u64 notification_dev_id = (u64)-1LL; + const unsigned int timeout = HZ; + u64 lpar; + u64 tag; + struct { + u64 operation_code; /* must be zero */ + u64 event_mask; /* 1 = device ready */ + } *notify_cmd; + struct { + u64 event_type; /* notify_device_ready */ + u64 bus_id; + u64 dev_id; + u64 dev_type; + u64 dev_port; + } *notify_event; + enum { + notify_device_ready = 1 + }; + + pr_debug(" -> %s:%u: bus_id %u, dev_id %u, dev_type %u\n", __func__, + __LINE__, repo->bus_id, repo->dev_id, repo->dev_type); + + notify_cmd = kzalloc(512, GFP_KERNEL); + notify_event = (void *)notify_cmd; + if (!notify_cmd) + return -ENOMEM; + + lpar = ps3_mm_phys_to_lpar(__pa(notify_cmd)); + + result = lv1_open_device(repo->bus_id, notification_dev_id, 0); + if (result) { + printk(KERN_ERR "%s:%u: lv1_open_device %s\n", __func__, + __LINE__, ps3_result(result)); + result = -ENODEV; + goto fail_free; + } + + /* Setup and write the request for device notification. */ + + notify_cmd->operation_code = 0; /* must be zero */ + notify_cmd->event_mask = 0x01; /* device ready */ + + result = lv1_storage_write(notification_dev_id, 0, 0, 1, 0, lpar, + &tag); + if (result) { + printk(KERN_ERR "%s:%u: write failed %s\n", __func__, __LINE__, + ps3_result(result)); + result = -ENODEV; + goto fail_close; + } + + /* Wait for the write completion */ + + result = ps3stor_wait_for_completion(notification_dev_id, tag, + timeout); + if (result) { + printk(KERN_ERR "%s:%u: write not completed %s\n", __func__, + __LINE__, ps3_result(result)); + result = -ENODEV; + goto fail_close; + } + + /* Loop here processing the requested notification events. */ + + result = -ENODEV; + while (1) { + memset(notify_event, 0, sizeof(*notify_event)); + + result = lv1_storage_read(notification_dev_id, 0, 0, 1, 0, + lpar, &tag); + if (result) { + printk(KERN_ERR "%s:%u: write failed %s\n", __func__, + __LINE__, ps3_result(result)); + break; + } + + result = ps3stor_wait_for_completion(notification_dev_id, tag, + timeout); + if (result) { + printk(KERN_ERR "%s:%u: read not completed %s\n", + __func__, __LINE__, ps3_result(result)); + break; + } + + if (notify_event->event_type != notify_device_ready || + notify_event->bus_id != repo->bus_id) { + pr_debug("%s:%u: bad notify_event: event %lu, " + "dev_id %lu, dev_type %lu\n", + __func__, __LINE__, notify_event->event_type, + notify_event->dev_id, notify_event->dev_type); + break; + } + + if (notify_event->dev_id == repo->dev_id && + notify_event->dev_type == repo->dev_type) { + pr_debug("%s:%u: device ready: dev_id %u\n", __func__, + __LINE__, repo->dev_id); + result = 0; + break; + } + + if (notify_event->dev_id == repo->dev_id && + notify_event->dev_type == PS3_DEV_TYPE_NOACCESS) { + pr_debug("%s:%u: no access: dev_id %u\n", __func__, + __LINE__, repo->dev_id); + break; + } + } + +fail_close: + lv1_close_device(repo->bus_id, notification_dev_id); +fail_free: + kfree(notify_cmd); + pr_debug(" <- %s:%u\n", __func__, __LINE__); + return result; +} + +static int ps3_setup_storage_dev(const struct ps3_repository_device *repo, + enum ps3_match_id match_id) +{ + int result; + struct ps3_storage_device *p; + u64 port, blk_size, num_blocks; + unsigned int num_regions, i; + + pr_debug(" -> %s:%u: match_id %u\n", __func__, __LINE__, match_id); + + result = ps3_repository_read_stor_dev_info(repo->bus_index, + repo->dev_index, &port, + &blk_size, &num_blocks, + &num_regions); + if (result) { + printk(KERN_ERR "%s:%u: _read_stor_dev_info failed %d\n", + __func__, __LINE__, result); + return -ENODEV; + } + + pr_debug("%s:%u: index %u:%u: port %lu blk_size %lu num_blocks %lu " + "num_regions %u\n", __func__, __LINE__, repo->bus_index, + repo->dev_index, port, blk_size, num_blocks, num_regions); + + p = kzalloc(sizeof(struct ps3_storage_device) + + num_regions * sizeof(struct ps3_storage_region), + GFP_KERNEL); + if (!p) { + result = -ENOMEM; + goto fail_malloc; + } + + p->sbd.match_id = match_id; + p->sbd.dev_type = PS3_DEVICE_TYPE_SB; + p->sbd.bus_id = repo->bus_id; + p->sbd.dev_id = repo->dev_id; + p->sbd.d_region = &p->dma_region; + p->blk_size = blk_size; + p->num_regions = num_regions; + + result = ps3_repository_find_interrupt(repo, + PS3_INTERRUPT_TYPE_EVENT_PORT, + &p->sbd.interrupt_id); + if (result) { + printk(KERN_ERR "%s:%u: find_interrupt failed %d\n", __func__, + __LINE__, result); + result = -ENODEV; + goto fail_find_interrupt; + } + + /* FIXME: Arrange to only do this on a 'cold' boot */ + + result = ps3_storage_wait_for_device(repo); + if (result) { + printk(KERN_ERR "%s:%u: storage_notification failed %d\n", + __func__, __LINE__, result); + result = -ENODEV; + goto fail_probe_notification; + } + + for (i = 0; i < num_regions; i++) { + unsigned int id; + u64 start, size; + + result = ps3_repository_read_stor_dev_region(repo->bus_index, + repo->dev_index, + i, &id, &start, + &size); + if (result) { + printk(KERN_ERR + "%s:%u: read_stor_dev_region failed %d\n", + __func__, __LINE__, result); + result = -ENODEV; + goto fail_read_region; + } + pr_debug("%s:%u: region %u: id %u start %lu size %lu\n", + __func__, __LINE__, i, id, start, size); + + p->regions[i].id = id; + p->regions[i].start = start; + p->regions[i].size = size; + } + + result = ps3_system_bus_device_register(&p->sbd); + if (result) { + pr_debug("%s:%u ps3_system_bus_device_register failed\n", + __func__, __LINE__); + goto fail_device_register; + } + + pr_debug(" <- %s:%u\n", __func__, __LINE__); + return 0; + +fail_device_register: +fail_read_region: +fail_probe_notification: +fail_find_interrupt: + kfree(p); +fail_malloc: + pr_debug(" <- %s:%u: fail.\n", __func__, __LINE__); + return result; +} + +static int __init ps3_register_vuart_devices(void) +{ + int result; + unsigned int port_number; + + pr_debug(" -> %s:%d\n", __func__, __LINE__); + + result = ps3_repository_read_vuart_av_port(&port_number); + if (result) + port_number = 0; /* av default */ + + result = ps3_setup_vuart_device(PS3_MATCH_ID_AV_SETTINGS, port_number); + WARN_ON(result); + + result = ps3_repository_read_vuart_sysmgr_port(&port_number); + if (result) + port_number = 2; /* sysmgr default */ + + result = ps3_setup_vuart_device(PS3_MATCH_ID_SYSTEM_MANAGER, + port_number); + WARN_ON(result); + + pr_debug(" <- %s:%d\n", __func__, __LINE__); + return result; +} + +static int __init ps3_register_sound_devices(void) +{ + int result; + struct layout { + struct ps3_system_bus_device dev; + struct ps3_dma_region d_region; + struct ps3_mmio_region m_region; + } *p; + + pr_debug(" -> %s:%d\n", __func__, __LINE__); + + p = kzalloc(sizeof(*p), GFP_KERNEL); + if (!p) + return -ENOMEM; + + p->dev.match_id = PS3_MATCH_ID_SOUND; + p->dev.dev_type = PS3_DEVICE_TYPE_IOC0; + p->dev.d_region = &p->d_region; + p->dev.m_region = &p->m_region; + + result = ps3_system_bus_device_register(&p->dev); + + if (result) + pr_debug("%s:%d ps3_system_bus_device_register failed\n", + __func__, __LINE__); + + pr_debug(" <- %s:%d\n", __func__, __LINE__); + return result; +} + +static int __init ps3_register_graphics_devices(void) +{ + int result; + struct layout { + struct ps3_system_bus_device dev; + } *p; + + pr_debug(" -> %s:%d\n", __func__, __LINE__); + + p = kzalloc(sizeof(struct layout), GFP_KERNEL); + + if (!p) + return -ENOMEM; + + p->dev.match_id = PS3_MATCH_ID_GRAPHICS; + p->dev.dev_type = PS3_DEVICE_TYPE_IOC0; + + result = ps3_system_bus_device_register(&p->dev); + + if (result) + pr_debug("%s:%d ps3_system_bus_device_register failed\n", + __func__, __LINE__); + + pr_debug(" <- %s:%d\n", __func__, __LINE__); + return result; +} + +/** + * ps3_register_repository_device - Register a device from the repositiory info. + * + */ + +static int ps3_register_repository_device( + const struct ps3_repository_device *repo) +{ + int result; + + switch (repo->dev_type) { + case PS3_DEV_TYPE_SB_GELIC: + result = ps3_setup_gelic_device(repo); + if (result) { + pr_debug("%s:%d ps3_setup_gelic_device failed\n", + __func__, __LINE__); + } + break; + case PS3_DEV_TYPE_SB_USB: + + /* Each USB device has both an EHCI and an OHCI HC */ + + result = ps3_setup_ehci_device(repo); + + if (result) { + pr_debug("%s:%d ps3_setup_ehci_device failed\n", + __func__, __LINE__); + } + + result = ps3_setup_ohci_device(repo); + + if (result) { + pr_debug("%s:%d ps3_setup_ohci_device failed\n", + __func__, __LINE__); + } + break; + case PS3_DEV_TYPE_STOR_DISK: + result = ps3_setup_storage_dev(repo, PS3_MATCH_ID_STOR_DISK); + + /* Some devices are not accessable from the Other OS lpar. */ + if (result == -ENODEV) { + result = 0; + pr_debug("%s:%u: not accessable\n", __func__, + __LINE__); + } + + if (result) + pr_debug("%s:%u ps3_setup_storage_dev failed\n", + __func__, __LINE__); + break; + + case PS3_DEV_TYPE_STOR_ROM: + result = ps3_setup_storage_dev(repo, PS3_MATCH_ID_STOR_ROM); + if (result) + pr_debug("%s:%u ps3_setup_storage_dev failed\n", + __func__, __LINE__); + break; + + case PS3_DEV_TYPE_STOR_FLASH: + result = ps3_setup_storage_dev(repo, PS3_MATCH_ID_STOR_FLASH); + if (result) + pr_debug("%s:%u ps3_setup_storage_dev failed\n", + __func__, __LINE__); + break; + + default: + result = 0; + pr_debug("%s:%u: unsupported dev_type %u\n", __func__, __LINE__, + repo->dev_type); + } + + return result; +} + +/** + * ps3_probe_thread - Background repository probing at system startup. + * + * This implementation only supports background probing on a single bus. + */ + +static int ps3_probe_thread(void *data) +{ + struct ps3_repository_device *repo = data; + int result; + unsigned int ms = 250; + + pr_debug(" -> %s:%u: kthread started\n", __func__, __LINE__); + + do { + try_to_freeze(); + + pr_debug("%s:%u: probing...\n", __func__, __LINE__); + + do { + result = ps3_repository_find_device(repo); + + if (result == -ENODEV) + pr_debug("%s:%u: nothing new\n", __func__, + __LINE__); + else if (result) + pr_debug("%s:%u: find device error.\n", + __func__, __LINE__); + else { + pr_debug("%s:%u: found device\n", __func__, + __LINE__); + ps3_register_repository_device(repo); + ps3_repository_bump_device(repo); + ms = 250; + } + } while (!result); + + pr_debug("%s:%u: ms %u\n", __func__, __LINE__, ms); + + if ( ms > 60000) + break; + + msleep_interruptible(ms); + + /* An exponential backoff. */ + ms <<= 1; + + } while (!kthread_should_stop()); + + pr_debug(" <- %s:%u: kthread finished\n", __func__, __LINE__); + + return 0; +} + +/** + * ps3_start_probe_thread - Starts the background probe thread. + * + */ + +static int __init ps3_start_probe_thread(enum ps3_bus_type bus_type) +{ + int result; + struct task_struct *task; + static struct ps3_repository_device repo; /* must be static */ + + pr_debug(" -> %s:%d\n", __func__, __LINE__); + + memset(&repo, 0, sizeof(repo)); + + repo.bus_type = bus_type; + + result = ps3_repository_find_bus(repo.bus_type, 0, &repo.bus_index); + + if (result) { + printk(KERN_ERR "%s: Cannot find bus (%d)\n", __func__, result); + return -ENODEV; + } + + result = ps3_repository_read_bus_id(repo.bus_index, &repo.bus_id); + + if (result) { + printk(KERN_ERR "%s: read_bus_id failed %d\n", __func__, + result); + return -ENODEV; + } + + task = kthread_run(ps3_probe_thread, &repo, "ps3-probe-%u", bus_type); + + if (IS_ERR(task)) { + result = PTR_ERR(task); + printk(KERN_ERR "%s: kthread_run failed %d\n", __func__, + result); + return result; + } + + pr_debug(" <- %s:%d\n", __func__, __LINE__); + return 0; +} + +/** + * ps3_register_devices - Probe the system and register devices found. + * + * A device_initcall() routine. + */ + +static int __init ps3_register_devices(void) +{ + int result; + + if (!firmware_has_feature(FW_FEATURE_PS3_LV1)) + return -ENODEV; + + pr_debug(" -> %s:%d\n", __func__, __LINE__); + + /* ps3_repository_dump_bus_info(); */ + + result = ps3_start_probe_thread(PS3_BUS_TYPE_STORAGE); + + ps3_register_vuart_devices(); + + ps3_register_graphics_devices(); + + ps3_repository_find_devices(PS3_BUS_TYPE_SB, + ps3_register_repository_device); + + ps3_register_sound_devices(); + + pr_debug(" <- %s:%d\n", __func__, __LINE__); + return 0; +} + +device_initcall(ps3_register_devices); diff --git a/arch/powerpc/platforms/ps3/htab.c b/arch/powerpc/platforms/ps3/htab.c index a1409e450c7..5d2e176a1b1 100644 --- a/arch/powerpc/platforms/ps3/htab.c +++ b/arch/powerpc/platforms/ps3/htab.c @@ -29,12 +29,12 @@ #include "platform.h" #if defined(DEBUG) -#define DBG(fmt...) udbg_printf(fmt) +#define DBG udbg_printf #else -#define DBG(fmt...) do{if(0)printk(fmt);}while(0) +#define DBG pr_debug #endif -static hpte_t *htab; +static struct hash_pte *htab; static unsigned long htab_addr; static unsigned char *bolttab; static unsigned char *inusetab; @@ -44,8 +44,8 @@ static DEFINE_SPINLOCK(ps3_bolttab_lock); #define debug_dump_hpte(_a, _b, _c, _d, _e, _f, _g) \ _debug_dump_hpte(_a, _b, _c, _d, _e, _f, _g, __func__, __LINE__) static void _debug_dump_hpte(unsigned long pa, unsigned long va, - unsigned long group, unsigned long bitmap, hpte_t lhpte, int psize, - unsigned long slot, const char* func, int line) + unsigned long group, unsigned long bitmap, struct hash_pte lhpte, + int psize, unsigned long slot, const char* func, int line) { DBG("%s:%d: pa = %lxh\n", func, line, pa); DBG("%s:%d: lpar = %lxh\n", func, line, @@ -63,7 +63,7 @@ static long ps3_hpte_insert(unsigned long hpte_group, unsigned long va, unsigned long pa, unsigned long rflags, unsigned long vflags, int psize) { unsigned long slot; - hpte_t lhpte; + struct hash_pte lhpte; int secondary = 0; unsigned long result; unsigned long bitmap; @@ -234,10 +234,17 @@ static void ps3_hpte_invalidate(unsigned long slot, unsigned long va, static void ps3_hpte_clear(void) { - /* Make sure to clean up the frame buffer device first */ - ps3fb_cleanup(); + int result; - lv1_unmap_htab(htab_addr); + DBG(" -> %s:%d\n", __func__, __LINE__); + + result = lv1_unmap_htab(htab_addr); + BUG_ON(result); + + ps3_mm_shutdown(); + ps3_mm_vas_destroy(); + + DBG(" <- %s:%d\n", __func__, __LINE__); } void __init ps3_hpte_init(unsigned long htab_size) @@ -255,7 +262,7 @@ void __init ps3_hpte_init(unsigned long htab_size) ppc64_pft_size = __ilog2(htab_size); - bitmap_size = htab_size / sizeof(hpte_t) / 8; + bitmap_size = htab_size / sizeof(struct hash_pte) / 8; bolttab = __va(lmb_alloc(bitmap_size, 1)); inusetab = __va(lmb_alloc(bitmap_size, 1)); @@ -273,8 +280,8 @@ void __init ps3_map_htab(void) result = lv1_map_htab(0, &htab_addr); - htab = (hpte_t *)__ioremap(htab_addr, htab_size, - pgprot_val(PAGE_READONLY_X)); + htab = (__force struct hash_pte *)ioremap_flags(htab_addr, htab_size, + pgprot_val(PAGE_READONLY_X)); DBG("%s:%d: lpar %016lxh, virt %016lxh\n", __func__, __LINE__, htab_addr, (unsigned long)htab); diff --git a/arch/powerpc/platforms/ps3/interrupt.c b/arch/powerpc/platforms/ps3/interrupt.c index ec9030dbb5f..67e32ec9b37 100644 --- a/arch/powerpc/platforms/ps3/interrupt.c +++ b/arch/powerpc/platforms/ps3/interrupt.c @@ -30,9 +30,9 @@ #include "platform.h" #if defined(DEBUG) -#define DBG(fmt...) udbg_printf(fmt) +#define DBG udbg_printf #else -#define DBG(fmt...) do{if(0)printk(fmt);}while(0) +#define DBG pr_debug #endif /** @@ -78,19 +78,85 @@ struct ps3_bmp { /** * struct ps3_private - a per cpu data structure * @bmp: ps3_bmp structure - * @node: HV logical_ppe_id - * @cpu: HV thread_id + * @ppe_id: HV logical_ppe_id + * @thread_id: HV thread_id */ struct ps3_private { struct ps3_bmp bmp __attribute__ ((aligned (PS3_BMP_MINALIGN))); - u64 node; - unsigned int cpu; + u64 ppe_id; + u64 thread_id; }; static DEFINE_PER_CPU(struct ps3_private, ps3_private); /** + * ps3_chip_mask - Set an interrupt mask bit in ps3_bmp. + * @virq: The assigned Linux virq. + * + * Sets ps3_bmp.mask and calls lv1_did_update_interrupt_mask(). + */ + +static void ps3_chip_mask(unsigned int virq) +{ + struct ps3_private *pd = get_irq_chip_data(virq); + unsigned long flags; + + pr_debug("%s:%d: thread_id %lu, virq %d\n", __func__, __LINE__, + pd->thread_id, virq); + + local_irq_save(flags); + clear_bit(63 - virq, &pd->bmp.mask); + lv1_did_update_interrupt_mask(pd->ppe_id, pd->thread_id); + local_irq_restore(flags); +} + +/** + * ps3_chip_unmask - Clear an interrupt mask bit in ps3_bmp. + * @virq: The assigned Linux virq. + * + * Clears ps3_bmp.mask and calls lv1_did_update_interrupt_mask(). + */ + +static void ps3_chip_unmask(unsigned int virq) +{ + struct ps3_private *pd = get_irq_chip_data(virq); + unsigned long flags; + + pr_debug("%s:%d: thread_id %lu, virq %d\n", __func__, __LINE__, + pd->thread_id, virq); + + local_irq_save(flags); + set_bit(63 - virq, &pd->bmp.mask); + lv1_did_update_interrupt_mask(pd->ppe_id, pd->thread_id); + local_irq_restore(flags); +} + +/** + * ps3_chip_eoi - HV end-of-interrupt. + * @virq: The assigned Linux virq. + * + * Calls lv1_end_of_interrupt_ext(). + */ + +static void ps3_chip_eoi(unsigned int virq) +{ + const struct ps3_private *pd = get_irq_chip_data(virq); + lv1_end_of_interrupt_ext(pd->ppe_id, pd->thread_id, virq); +} + +/** + * ps3_irq_chip - Represents the ps3_bmp as a Linux struct irq_chip. + */ + +static struct irq_chip ps3_irq_chip = { + .typename = "ps3", + .mask = ps3_chip_mask, + .unmask = ps3_chip_unmask, + .eoi = ps3_chip_eoi, +}; + +/** * ps3_virq_setup - virq related setup. * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be * serviced on. @@ -134,6 +200,8 @@ int ps3_virq_setup(enum ps3_cpu_binding cpu, unsigned long outlet, goto fail_set; } + ps3_chip_mask(*virq); + return result; fail_set: @@ -153,8 +221,8 @@ int ps3_virq_destroy(unsigned int virq) { const struct ps3_private *pd = get_irq_chip_data(virq); - pr_debug("%s:%d: node %lu, cpu %d, virq %u\n", __func__, __LINE__, - pd->node, pd->cpu, virq); + pr_debug("%s:%d: ppe_id %lu, thread_id %lu, virq %u\n", __func__, + __LINE__, pd->ppe_id, pd->thread_id, virq); set_irq_chip_data(virq, NULL); irq_dispose_mapping(virq); @@ -190,7 +258,8 @@ int ps3_irq_plug_setup(enum ps3_cpu_binding cpu, unsigned long outlet, /* Binds outlet to cpu + virq. */ - result = lv1_connect_irq_plug_ext(pd->node, pd->cpu, *virq, outlet, 0); + result = lv1_connect_irq_plug_ext(pd->ppe_id, pd->thread_id, *virq, + outlet, 0); if (result) { pr_info("%s:%d: lv1_connect_irq_plug_ext failed: %s\n", @@ -222,10 +291,12 @@ int ps3_irq_plug_destroy(unsigned int virq) int result; const struct ps3_private *pd = get_irq_chip_data(virq); - pr_debug("%s:%d: node %lu, cpu %d, virq %u\n", __func__, __LINE__, - pd->node, pd->cpu, virq); + pr_debug("%s:%d: ppe_id %lu, thread_id %lu, virq %u\n", __func__, + __LINE__, pd->ppe_id, pd->thread_id, virq); + + ps3_chip_mask(virq); - result = lv1_disconnect_irq_plug_ext(pd->node, pd->cpu, virq); + result = lv1_disconnect_irq_plug_ext(pd->ppe_id, pd->thread_id, virq); if (result) pr_info("%s:%d: lv1_disconnect_irq_plug_ext failed: %s\n", @@ -282,7 +353,9 @@ int ps3_event_receive_port_destroy(unsigned int virq) { int result; - pr_debug(" -> %s:%d virq: %u\n", __func__, __LINE__, virq); + pr_debug(" -> %s:%d virq %u\n", __func__, __LINE__, virq); + + ps3_chip_mask(virq); result = lv1_destruct_event_receive_port(virq_to_hw(virq)); @@ -290,17 +363,14 @@ int ps3_event_receive_port_destroy(unsigned int virq) pr_debug("%s:%d: lv1_destruct_event_receive_port failed: %s\n", __func__, __LINE__, ps3_result(result)); - /* lv1_destruct_event_receive_port() destroys the IRQ plug, - * so don't call ps3_irq_plug_destroy() here. + /* + * Don't call ps3_virq_destroy() here since ps3_smp_cleanup_cpu() + * calls from interrupt context (smp_call_function) when kexecing. */ - result = ps3_virq_destroy(virq); - BUG_ON(result); - pr_debug(" <- %s:%d\n", __func__, __LINE__); return result; } -EXPORT_SYMBOL_GPL(ps3_event_receive_port_destroy); int ps3_send_event_locally(unsigned int virq) { @@ -311,17 +381,15 @@ int ps3_send_event_locally(unsigned int virq) * ps3_sb_event_receive_port_setup - Setup a system bus event receive port. * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be * serviced on. - * @did: The HV device identifier read from the system repository. - * @interrupt_id: The device interrupt id read from the system repository. + * @dev: The system bus device instance. * @virq: The assigned Linux virq. * * An event irq represents a virtual device interrupt. The interrupt_id * coresponds to the software interrupt number. */ -int ps3_sb_event_receive_port_setup(enum ps3_cpu_binding cpu, - const struct ps3_device_id *did, unsigned int interrupt_id, - unsigned int *virq) +int ps3_sb_event_receive_port_setup(struct ps3_system_bus_device *dev, + enum ps3_cpu_binding cpu, unsigned int *virq) { /* this should go in system-bus.c */ @@ -332,8 +400,8 @@ int ps3_sb_event_receive_port_setup(enum ps3_cpu_binding cpu, if (result) return result; - result = lv1_connect_interrupt_event_receive_port(did->bus_id, - did->dev_id, virq_to_hw(*virq), interrupt_id); + result = lv1_connect_interrupt_event_receive_port(dev->bus_id, + dev->dev_id, virq_to_hw(*virq), dev->interrupt_id); if (result) { pr_debug("%s:%d: lv1_connect_interrupt_event_receive_port" @@ -345,24 +413,24 @@ int ps3_sb_event_receive_port_setup(enum ps3_cpu_binding cpu, } pr_debug("%s:%d: interrupt_id %u, virq %u\n", __func__, __LINE__, - interrupt_id, *virq); + dev->interrupt_id, *virq); return 0; } EXPORT_SYMBOL(ps3_sb_event_receive_port_setup); -int ps3_sb_event_receive_port_destroy(const struct ps3_device_id *did, - unsigned int interrupt_id, unsigned int virq) +int ps3_sb_event_receive_port_destroy(struct ps3_system_bus_device *dev, + unsigned int virq) { /* this should go in system-bus.c */ int result; pr_debug(" -> %s:%d: interrupt_id %u, virq %u\n", __func__, __LINE__, - interrupt_id, virq); + dev->interrupt_id, virq); - result = lv1_disconnect_interrupt_event_receive_port(did->bus_id, - did->dev_id, virq_to_hw(virq), interrupt_id); + result = lv1_disconnect_interrupt_event_receive_port(dev->bus_id, + dev->dev_id, virq_to_hw(virq), dev->interrupt_id); if (result) pr_debug("%s:%d: lv1_disconnect_interrupt_event_receive_port" @@ -372,6 +440,14 @@ int ps3_sb_event_receive_port_destroy(const struct ps3_device_id *did, result = ps3_event_receive_port_destroy(virq); BUG_ON(result); + /* + * ps3_event_receive_port_destroy() destroys the IRQ plug, + * so don't call ps3_irq_plug_destroy() here. + */ + + result = ps3_virq_destroy(virq); + BUG_ON(result); + pr_debug(" <- %s:%d\n", __func__, __LINE__); return result; } @@ -412,16 +488,24 @@ EXPORT_SYMBOL_GPL(ps3_io_irq_setup); int ps3_io_irq_destroy(unsigned int virq) { int result; + unsigned long outlet = virq_to_hw(virq); - result = lv1_destruct_io_irq_outlet(virq_to_hw(virq)); + ps3_chip_mask(virq); - if (result) - pr_debug("%s:%d: lv1_destruct_io_irq_outlet failed: %s\n", - __func__, __LINE__, ps3_result(result)); + /* + * lv1_destruct_io_irq_outlet() will destroy the IRQ plug, + * so call ps3_irq_plug_destroy() first. + */ result = ps3_irq_plug_destroy(virq); BUG_ON(result); + result = lv1_destruct_io_irq_outlet(outlet); + + if (result) + pr_debug("%s:%d: lv1_destruct_io_irq_outlet failed: %s\n", + __func__, __LINE__, ps3_result(result)); + return result; } EXPORT_SYMBOL_GPL(ps3_io_irq_destroy); @@ -461,11 +545,13 @@ int ps3_vuart_irq_setup(enum ps3_cpu_binding cpu, void* virt_addr_bmp, return result; } +EXPORT_SYMBOL_GPL(ps3_vuart_irq_setup); int ps3_vuart_irq_destroy(unsigned int virq) { int result; + ps3_chip_mask(virq); result = lv1_deconfigure_virtual_uart_irq(); if (result) { @@ -479,6 +565,7 @@ int ps3_vuart_irq_destroy(unsigned int virq) return result; } +EXPORT_SYMBOL_GPL(ps3_vuart_irq_destroy); /** * ps3_spe_irq_setup - Setup an spe virq. @@ -514,9 +601,14 @@ int ps3_spe_irq_setup(enum ps3_cpu_binding cpu, unsigned long spe_id, int ps3_spe_irq_destroy(unsigned int virq) { - int result = ps3_irq_plug_destroy(virq); + int result; + + ps3_chip_mask(virq); + + result = ps3_irq_plug_destroy(virq); BUG_ON(result); - return 0; + + return result; } @@ -533,7 +625,7 @@ static void _dump_64_bmp(const char *header, const u64 *p, unsigned cpu, *p & 0xffff); } -static void __attribute__ ((unused)) _dump_256_bmp(const char *header, +static void __maybe_unused _dump_256_bmp(const char *header, const u64 *p, unsigned cpu, const char* func, int line) { pr_debug("%s:%d: %s %u {%016lx:%016lx:%016lx:%016lx}\n", @@ -546,86 +638,25 @@ static void _dump_bmp(struct ps3_private* pd, const char* func, int line) unsigned long flags; spin_lock_irqsave(&pd->bmp.lock, flags); - _dump_64_bmp("stat", &pd->bmp.status, pd->cpu, func, line); - _dump_64_bmp("mask", &pd->bmp.mask, pd->cpu, func, line); + _dump_64_bmp("stat", &pd->bmp.status, pd->thread_id, func, line); + _dump_64_bmp("mask", &pd->bmp.mask, pd->thread_id, func, line); spin_unlock_irqrestore(&pd->bmp.lock, flags); } #define dump_mask(_x) _dump_mask(_x, __func__, __LINE__) -static void __attribute__ ((unused)) _dump_mask(struct ps3_private* pd, +static void __maybe_unused _dump_mask(struct ps3_private *pd, const char* func, int line) { unsigned long flags; spin_lock_irqsave(&pd->bmp.lock, flags); - _dump_64_bmp("mask", &pd->bmp.mask, pd->cpu, func, line); + _dump_64_bmp("mask", &pd->bmp.mask, pd->thread_id, func, line); spin_unlock_irqrestore(&pd->bmp.lock, flags); } #else static void dump_bmp(struct ps3_private* pd) {}; #endif /* defined(DEBUG) */ -static void ps3_chip_mask(unsigned int virq) -{ - struct ps3_private *pd = get_irq_chip_data(virq); - u64 bit = 0x8000000000000000UL >> virq; - u64 *p = &pd->bmp.mask; - u64 old; - unsigned long flags; - - pr_debug("%s:%d: cpu %u, virq %d\n", __func__, __LINE__, pd->cpu, virq); - - local_irq_save(flags); - asm volatile( - "1: ldarx %0,0,%3\n" - "andc %0,%0,%2\n" - "stdcx. %0,0,%3\n" - "bne- 1b" - : "=&r" (old), "+m" (*p) - : "r" (bit), "r" (p) - : "cc" ); - - lv1_did_update_interrupt_mask(pd->node, pd->cpu); - local_irq_restore(flags); -} - -static void ps3_chip_unmask(unsigned int virq) -{ - struct ps3_private *pd = get_irq_chip_data(virq); - u64 bit = 0x8000000000000000UL >> virq; - u64 *p = &pd->bmp.mask; - u64 old; - unsigned long flags; - - pr_debug("%s:%d: cpu %u, virq %d\n", __func__, __LINE__, pd->cpu, virq); - - local_irq_save(flags); - asm volatile( - "1: ldarx %0,0,%3\n" - "or %0,%0,%2\n" - "stdcx. %0,0,%3\n" - "bne- 1b" - : "=&r" (old), "+m" (*p) - : "r" (bit), "r" (p) - : "cc" ); - - lv1_did_update_interrupt_mask(pd->node, pd->cpu); - local_irq_restore(flags); -} - -static void ps3_chip_eoi(unsigned int virq) -{ - const struct ps3_private *pd = get_irq_chip_data(virq); - lv1_end_of_interrupt_ext(pd->node, pd->cpu, virq); -} - -static struct irq_chip irq_chip = { - .typename = "ps3", - .mask = ps3_chip_mask, - .unmask = ps3_chip_unmask, - .eoi = ps3_chip_eoi, -}; - static void ps3_host_unmap(struct irq_host *h, unsigned int virq) { set_irq_chip_data(virq, NULL); @@ -637,7 +668,7 @@ static int ps3_host_map(struct irq_host *h, unsigned int virq, pr_debug("%s:%d: hwirq %lu, virq %u\n", __func__, __LINE__, hwirq, virq); - set_irq_chip_and_handler(virq, &irq_chip, handle_fasteoi_irq); + set_irq_chip_and_handler(virq, &ps3_irq_chip, handle_fasteoi_irq); return 0; } @@ -657,7 +688,7 @@ void __init ps3_register_ipi_debug_brk(unsigned int cpu, unsigned int virq) cpu, virq, pd->bmp.ipi_debug_brk_mask); } -unsigned int ps3_get_irq(void) +static unsigned int ps3_get_irq(void) { struct ps3_private *pd = &__get_cpu_var(ps3_private); u64 x = (pd->bmp.status & pd->bmp.mask); @@ -672,8 +703,8 @@ unsigned int ps3_get_irq(void) plug &= 0x3f; if (unlikely(plug) == NO_IRQ) { - pr_debug("%s:%d: no plug found: cpu %u\n", __func__, __LINE__, - pd->cpu); + pr_debug("%s:%d: no plug found: thread_id %lu\n", __func__, + __LINE__, pd->thread_id); dump_bmp(&per_cpu(ps3_private, 0)); dump_bmp(&per_cpu(ps3_private, 1)); return NO_IRQ; @@ -703,16 +734,16 @@ void __init ps3_init_IRQ(void) for_each_possible_cpu(cpu) { struct ps3_private *pd = &per_cpu(ps3_private, cpu); - lv1_get_logical_ppe_id(&pd->node); - pd->cpu = get_hard_smp_processor_id(cpu); + lv1_get_logical_ppe_id(&pd->ppe_id); + pd->thread_id = get_hard_smp_processor_id(cpu); spin_lock_init(&pd->bmp.lock); - pr_debug("%s:%d: node %lu, cpu %d, bmp %lxh\n", __func__, - __LINE__, pd->node, pd->cpu, + pr_debug("%s:%d: ppe_id %lu, thread_id %lu, bmp %lxh\n", + __func__, __LINE__, pd->ppe_id, pd->thread_id, ps3_mm_phys_to_lpar(__pa(&pd->bmp))); - result = lv1_configure_irq_state_bitmap(pd->node, pd->cpu, - ps3_mm_phys_to_lpar(__pa(&pd->bmp))); + result = lv1_configure_irq_state_bitmap(pd->ppe_id, + pd->thread_id, ps3_mm_phys_to_lpar(__pa(&pd->bmp))); if (result) pr_debug("%s:%d: lv1_configure_irq_state_bitmap failed:" @@ -722,3 +753,16 @@ void __init ps3_init_IRQ(void) ppc_md.get_irq = ps3_get_irq; } + +void ps3_shutdown_IRQ(int cpu) +{ + int result; + u64 ppe_id; + u64 thread_id = get_hard_smp_processor_id(cpu); + + lv1_get_logical_ppe_id(&ppe_id); + result = lv1_configure_irq_state_bitmap(ppe_id, thread_id, 0); + + DBG("%s:%d: lv1_configure_irq_state_bitmap (%lu:%lu/%d) %s\n", __func__, + __LINE__, ppe_id, thread_id, cpu, ps3_result(result)); +} diff --git a/arch/powerpc/platforms/ps3/mm.c b/arch/powerpc/platforms/ps3/mm.c index f8a3e206c58..7bb3e162097 100644 --- a/arch/powerpc/platforms/ps3/mm.c +++ b/arch/powerpc/platforms/ps3/mm.c @@ -30,9 +30,9 @@ #include "platform.h" #if defined(DEBUG) -#define DBG(fmt...) udbg_printf(fmt) +#define DBG udbg_printf #else -#define DBG(fmt...) do{if(0)printk(fmt);}while(0) +#define DBG pr_debug #endif enum { @@ -115,7 +115,8 @@ struct map { }; #define debug_dump_map(x) _debug_dump_map(x, __func__, __LINE__) -static void _debug_dump_map(const struct map* m, const char* func, int line) +static void __maybe_unused _debug_dump_map(const struct map *m, + const char *func, int line) { DBG("%s:%d: map.total = %lxh\n", func, line, m->total); DBG("%s:%d: map.rm.size = %lxh\n", func, line, m->rm.size); @@ -212,9 +213,15 @@ fail: void ps3_mm_vas_destroy(void) { + int result; + + DBG("%s:%d: map.vas_id = %lu\n", __func__, __LINE__, map.vas_id); + if (map.vas_id) { - lv1_select_virtual_address_space(0); - lv1_destruct_virtual_address_space(map.vas_id); + result = lv1_select_virtual_address_space(0); + BUG_ON(result); + result = lv1_destruct_virtual_address_space(map.vas_id); + BUG_ON(result); map.vas_id = 0; } } @@ -232,7 +239,7 @@ void ps3_mm_vas_destroy(void) * @size is rounded down to a multiple of the vas large page size. */ -int ps3_mm_region_create(struct mem_region *r, unsigned long size) +static int ps3_mm_region_create(struct mem_region *r, unsigned long size) { int result; unsigned long muid; @@ -273,10 +280,14 @@ zero_region: * @r: pointer to struct mem_region */ -void ps3_mm_region_destroy(struct mem_region *r) +static void ps3_mm_region_destroy(struct mem_region *r) { + int result; + + DBG("%s:%d: r->base = %lxh\n", __func__, __LINE__, r->base); if (r->base) { - lv1_release_memory(r->base); + result = lv1_release_memory(r->base); + BUG_ON(result); r->size = r->base = r->offset = 0; map.total = map.rm.size; } @@ -329,31 +340,34 @@ core_initcall(ps3_mm_add_memory); /*============================================================================*/ /** - * dma_lpar_to_bus - Translate an lpar address to ioc mapped bus address. + * dma_sb_lpar_to_bus - Translate an lpar address to ioc mapped bus address. * @r: pointer to dma region structure * @lpar_addr: HV lpar address */ -static unsigned long dma_lpar_to_bus(struct ps3_dma_region *r, +static unsigned long dma_sb_lpar_to_bus(struct ps3_dma_region *r, unsigned long lpar_addr) { - BUG_ON(lpar_addr >= map.r1.base + map.r1.size); - return r->bus_addr + (lpar_addr <= map.rm.size ? lpar_addr - : lpar_addr - map.r1.offset); + if (lpar_addr >= map.rm.size) + lpar_addr -= map.r1.offset; + BUG_ON(lpar_addr < r->offset); + BUG_ON(lpar_addr >= r->offset + r->len); + return r->bus_addr + lpar_addr - r->offset; } #define dma_dump_region(_a) _dma_dump_region(_a, __func__, __LINE__) -static void _dma_dump_region(const struct ps3_dma_region *r, const char* func, - int line) +static void __maybe_unused _dma_dump_region(const struct ps3_dma_region *r, + const char *func, int line) { - DBG("%s:%d: dev %u:%u\n", func, line, r->did.bus_id, - r->did.dev_id); + DBG("%s:%d: dev %u:%u\n", func, line, r->dev->bus_id, + r->dev->dev_id); DBG("%s:%d: page_size %u\n", func, line, r->page_size); DBG("%s:%d: bus_addr %lxh\n", func, line, r->bus_addr); DBG("%s:%d: len %lxh\n", func, line, r->len); + DBG("%s:%d: offset %lxh\n", func, line, r->offset); } -/** + /** * dma_chunk - A chunk of dma pages mapped by the io controller. * @region - The dma region that owns this chunk. * @lpar_addr: Starting lpar address of the area to map. @@ -381,10 +395,11 @@ static void _dma_dump_chunk (const struct dma_chunk* c, const char* func, int line) { DBG("%s:%d: r.dev %u:%u\n", func, line, - c->region->did.bus_id, c->region->did.dev_id); + c->region->dev->bus_id, c->region->dev->dev_id); DBG("%s:%d: r.bus_addr %lxh\n", func, line, c->region->bus_addr); DBG("%s:%d: r.page_size %u\n", func, line, c->region->page_size); DBG("%s:%d: r.len %lxh\n", func, line, c->region->len); + DBG("%s:%d: r.offset %lxh\n", func, line, c->region->offset); DBG("%s:%d: c.lpar_addr %lxh\n", func, line, c->lpar_addr); DBG("%s:%d: c.bus_addr %lxh\n", func, line, c->bus_addr); DBG("%s:%d: c.len %lxh\n", func, line, c->len); @@ -395,39 +410,68 @@ static struct dma_chunk * dma_find_chunk(struct ps3_dma_region *r, { struct dma_chunk *c; unsigned long aligned_bus = _ALIGN_DOWN(bus_addr, 1 << r->page_size); - unsigned long aligned_len = _ALIGN_UP(len, 1 << r->page_size); + unsigned long aligned_len = _ALIGN_UP(len+bus_addr-aligned_bus, + 1 << r->page_size); list_for_each_entry(c, &r->chunk_list.head, link) { /* intersection */ - if (aligned_bus >= c->bus_addr - && aligned_bus < c->bus_addr + c->len - && aligned_bus + aligned_len <= c->bus_addr + c->len) { + if (aligned_bus >= c->bus_addr && + aligned_bus + aligned_len <= c->bus_addr + c->len) return c; - } + /* below */ - if (aligned_bus + aligned_len <= c->bus_addr) { + if (aligned_bus + aligned_len <= c->bus_addr) continue; - } + /* above */ - if (aligned_bus >= c->bus_addr + c->len) { + if (aligned_bus >= c->bus_addr + c->len) continue; - } /* we don't handle the multi-chunk case for now */ - dma_dump_chunk(c); BUG(); } return NULL; } -static int dma_free_chunk(struct dma_chunk *c) +static struct dma_chunk *dma_find_chunk_lpar(struct ps3_dma_region *r, + unsigned long lpar_addr, unsigned long len) +{ + struct dma_chunk *c; + unsigned long aligned_lpar = _ALIGN_DOWN(lpar_addr, 1 << r->page_size); + unsigned long aligned_len = _ALIGN_UP(len + lpar_addr - aligned_lpar, + 1 << r->page_size); + + list_for_each_entry(c, &r->chunk_list.head, link) { + /* intersection */ + if (c->lpar_addr <= aligned_lpar && + aligned_lpar < c->lpar_addr + c->len) { + if (aligned_lpar + aligned_len <= c->lpar_addr + c->len) + return c; + else { + dma_dump_chunk(c); + BUG(); + } + } + /* below */ + if (aligned_lpar + aligned_len <= c->lpar_addr) { + continue; + } + /* above */ + if (c->lpar_addr + c->len <= aligned_lpar) { + continue; + } + } + return NULL; +} + +static int dma_sb_free_chunk(struct dma_chunk *c) { int result = 0; if (c->bus_addr) { - result = lv1_unmap_device_dma_region(c->region->did.bus_id, - c->region->did.dev_id, c->bus_addr, c->len); + result = lv1_unmap_device_dma_region(c->region->dev->bus_id, + c->region->dev->dev_id, c->bus_addr, c->len); BUG_ON(result); } @@ -435,8 +479,39 @@ static int dma_free_chunk(struct dma_chunk *c) return result; } +static int dma_ioc0_free_chunk(struct dma_chunk *c) +{ + int result = 0; + int iopage; + unsigned long offset; + struct ps3_dma_region *r = c->region; + + DBG("%s:start\n", __func__); + for (iopage = 0; iopage < (c->len >> r->page_size); iopage++) { + offset = (1 << r->page_size) * iopage; + /* put INVALID entry */ + result = lv1_put_iopte(0, + c->bus_addr + offset, + c->lpar_addr + offset, + r->ioid, + 0); + DBG("%s: bus=%#lx, lpar=%#lx, ioid=%d\n", __func__, + c->bus_addr + offset, + c->lpar_addr + offset, + r->ioid); + + if (result) { + DBG("%s:%d: lv1_put_iopte failed: %s\n", __func__, + __LINE__, ps3_result(result)); + } + } + kfree(c); + DBG("%s:end\n", __func__); + return result; +} + /** - * dma_map_pages - Maps dma pages into the io controller bus address space. + * dma_sb_map_pages - Maps dma pages into the io controller bus address space. * @r: Pointer to a struct ps3_dma_region. * @phys_addr: Starting physical address of the area to map. * @len: Length in bytes of the area to map. @@ -446,8 +521,8 @@ static int dma_free_chunk(struct dma_chunk *c) * make the HV call to add the pages into the io controller address space. */ -static int dma_map_pages(struct ps3_dma_region *r, unsigned long phys_addr, - unsigned long len, struct dma_chunk **c_out) +static int dma_sb_map_pages(struct ps3_dma_region *r, unsigned long phys_addr, + unsigned long len, struct dma_chunk **c_out, u64 iopte_flag) { int result; struct dma_chunk *c; @@ -461,13 +536,13 @@ static int dma_map_pages(struct ps3_dma_region *r, unsigned long phys_addr, c->region = r; c->lpar_addr = ps3_mm_phys_to_lpar(phys_addr); - c->bus_addr = dma_lpar_to_bus(r, c->lpar_addr); + c->bus_addr = dma_sb_lpar_to_bus(r, c->lpar_addr); c->len = len; - result = lv1_map_device_dma_region(c->region->did.bus_id, - c->region->did.dev_id, c->lpar_addr, c->bus_addr, c->len, - 0xf800000000000000UL); - + BUG_ON(iopte_flag != 0xf800000000000000UL); + result = lv1_map_device_dma_region(c->region->dev->bus_id, + c->region->dev->dev_id, c->lpar_addr, + c->bus_addr, c->len, iopte_flag); if (result) { DBG("%s:%d: lv1_map_device_dma_region failed: %s\n", __func__, __LINE__, ps3_result(result)); @@ -487,26 +562,120 @@ fail_alloc: return result; } +static int dma_ioc0_map_pages(struct ps3_dma_region *r, unsigned long phys_addr, + unsigned long len, struct dma_chunk **c_out, + u64 iopte_flag) +{ + int result; + struct dma_chunk *c, *last; + int iopage, pages; + unsigned long offset; + + DBG(KERN_ERR "%s: phy=%#lx, lpar%#lx, len=%#lx\n", __func__, + phys_addr, ps3_mm_phys_to_lpar(phys_addr), len); + c = kzalloc(sizeof(struct dma_chunk), GFP_ATOMIC); + + if (!c) { + result = -ENOMEM; + goto fail_alloc; + } + + c->region = r; + c->len = len; + c->lpar_addr = ps3_mm_phys_to_lpar(phys_addr); + /* allocate IO address */ + if (list_empty(&r->chunk_list.head)) { + /* first one */ + c->bus_addr = r->bus_addr; + } else { + /* derive from last bus addr*/ + last = list_entry(r->chunk_list.head.next, + struct dma_chunk, link); + c->bus_addr = last->bus_addr + last->len; + DBG("%s: last bus=%#lx, len=%#lx\n", __func__, + last->bus_addr, last->len); + } + + /* FIXME: check whether length exceeds region size */ + + /* build ioptes for the area */ + pages = len >> r->page_size; + DBG("%s: pgsize=%#x len=%#lx pages=%#x iopteflag=%#lx\n", __func__, + r->page_size, r->len, pages, iopte_flag); + for (iopage = 0; iopage < pages; iopage++) { + offset = (1 << r->page_size) * iopage; + result = lv1_put_iopte(0, + c->bus_addr + offset, + c->lpar_addr + offset, + r->ioid, + iopte_flag); + if (result) { + printk(KERN_WARNING "%s:%d: lv1_map_device_dma_region " + "failed: %s\n", __func__, __LINE__, + ps3_result(result)); + goto fail_map; + } + DBG("%s: pg=%d bus=%#lx, lpar=%#lx, ioid=%#x\n", __func__, + iopage, c->bus_addr + offset, c->lpar_addr + offset, + r->ioid); + } + + /* be sure that last allocated one is inserted at head */ + list_add(&c->link, &r->chunk_list.head); + + *c_out = c; + DBG("%s: end\n", __func__); + return 0; + +fail_map: + for (iopage--; 0 <= iopage; iopage--) { + lv1_put_iopte(0, + c->bus_addr + offset, + c->lpar_addr + offset, + r->ioid, + 0); + } + kfree(c); +fail_alloc: + *c_out = NULL; + return result; +} + /** - * dma_region_create - Create a device dma region. + * dma_sb_region_create - Create a device dma region. * @r: Pointer to a struct ps3_dma_region. * * This is the lowest level dma region create routine, and is the one that * will make the HV call to create the region. */ -static int dma_region_create(struct ps3_dma_region* r) +static int dma_sb_region_create(struct ps3_dma_region *r) { int result; - r->len = _ALIGN_UP(map.total, 1 << r->page_size); + pr_info(" -> %s:%d:\n", __func__, __LINE__); + + BUG_ON(!r); + + if (!r->dev->bus_id) { + pr_info("%s:%d: %u:%u no dma\n", __func__, __LINE__, + r->dev->bus_id, r->dev->dev_id); + return 0; + } + + DBG("%s:%u: len = 0x%lx, page_size = %u, offset = 0x%lx\n", __func__, + __LINE__, r->len, r->page_size, r->offset); + + BUG_ON(!r->len); + BUG_ON(!r->page_size); + BUG_ON(!r->region_ops); + INIT_LIST_HEAD(&r->chunk_list.head); spin_lock_init(&r->chunk_list.lock); - result = lv1_allocate_device_dma_region(r->did.bus_id, r->did.dev_id, - r->len, r->page_size, r->region_type, &r->bus_addr); - - dma_dump_region(r); + result = lv1_allocate_device_dma_region(r->dev->bus_id, r->dev->dev_id, + roundup_pow_of_two(r->len), r->page_size, r->region_type, + &r->bus_addr); if (result) { DBG("%s:%d: lv1_allocate_device_dma_region failed: %s\n", @@ -517,6 +686,27 @@ static int dma_region_create(struct ps3_dma_region* r) return result; } +static int dma_ioc0_region_create(struct ps3_dma_region *r) +{ + int result; + + INIT_LIST_HEAD(&r->chunk_list.head); + spin_lock_init(&r->chunk_list.lock); + + result = lv1_allocate_io_segment(0, + r->len, + r->page_size, + &r->bus_addr); + if (result) { + DBG("%s:%d: lv1_allocate_io_segment failed: %s\n", + __func__, __LINE__, ps3_result(result)); + r->len = r->bus_addr = 0; + } + DBG("%s: len=%#lx, pg=%d, bus=%#lx\n", __func__, + r->len, r->page_size, r->bus_addr); + return result; +} + /** * dma_region_free - Free a device dma region. * @r: Pointer to a struct ps3_dma_region. @@ -525,31 +715,62 @@ static int dma_region_create(struct ps3_dma_region* r) * will make the HV call to free the region. */ -static int dma_region_free(struct ps3_dma_region* r) +static int dma_sb_region_free(struct ps3_dma_region *r) { int result; struct dma_chunk *c; struct dma_chunk *tmp; + BUG_ON(!r); + + if (!r->dev->bus_id) { + pr_info("%s:%d: %u:%u no dma\n", __func__, __LINE__, + r->dev->bus_id, r->dev->dev_id); + return 0; + } + list_for_each_entry_safe(c, tmp, &r->chunk_list.head, link) { list_del(&c->link); - dma_free_chunk(c); + dma_sb_free_chunk(c); } - result = lv1_free_device_dma_region(r->did.bus_id, r->did.dev_id, + result = lv1_free_device_dma_region(r->dev->bus_id, r->dev->dev_id, r->bus_addr); if (result) DBG("%s:%d: lv1_free_device_dma_region failed: %s\n", __func__, __LINE__, ps3_result(result)); - r->len = r->bus_addr = 0; + r->bus_addr = 0; + + return result; +} + +static int dma_ioc0_region_free(struct ps3_dma_region *r) +{ + int result; + struct dma_chunk *c, *n; + + DBG("%s: start\n", __func__); + list_for_each_entry_safe(c, n, &r->chunk_list.head, link) { + list_del(&c->link); + dma_ioc0_free_chunk(c); + } + + result = lv1_release_io_segment(0, r->bus_addr); + + if (result) + DBG("%s:%d: lv1_free_device_dma_region failed: %s\n", + __func__, __LINE__, ps3_result(result)); + + r->bus_addr = 0; + DBG("%s: end\n", __func__); return result; } /** - * dma_map_area - Map an area of memory into a device dma region. + * dma_sb_map_area - Map an area of memory into a device dma region. * @r: Pointer to a struct ps3_dma_region. * @virt_addr: Starting virtual address of the area to map. * @len: Length in bytes of the area to map. @@ -559,16 +780,19 @@ static int dma_region_free(struct ps3_dma_region* r) * This is the common dma mapping routine. */ -static int dma_map_area(struct ps3_dma_region *r, unsigned long virt_addr, - unsigned long len, unsigned long *bus_addr) +static int dma_sb_map_area(struct ps3_dma_region *r, unsigned long virt_addr, + unsigned long len, unsigned long *bus_addr, + u64 iopte_flag) { int result; unsigned long flags; struct dma_chunk *c; unsigned long phys_addr = is_kernel_addr(virt_addr) ? __pa(virt_addr) : virt_addr; - - *bus_addr = dma_lpar_to_bus(r, ps3_mm_phys_to_lpar(phys_addr)); + unsigned long aligned_phys = _ALIGN_DOWN(phys_addr, 1 << r->page_size); + unsigned long aligned_len = _ALIGN_UP(len + phys_addr - aligned_phys, + 1 << r->page_size); + *bus_addr = dma_sb_lpar_to_bus(r, ps3_mm_phys_to_lpar(phys_addr)); if (!USE_DYNAMIC_DMA) { unsigned long lpar_addr = ps3_mm_phys_to_lpar(phys_addr); @@ -588,17 +812,18 @@ static int dma_map_area(struct ps3_dma_region *r, unsigned long virt_addr, c = dma_find_chunk(r, *bus_addr, len); if (c) { + DBG("%s:%d: reusing mapped chunk", __func__, __LINE__); + dma_dump_chunk(c); c->usage_count++; spin_unlock_irqrestore(&r->chunk_list.lock, flags); return 0; } - result = dma_map_pages(r, _ALIGN_DOWN(phys_addr, 1 << r->page_size), - _ALIGN_UP(len, 1 << r->page_size), &c); + result = dma_sb_map_pages(r, aligned_phys, aligned_len, &c, iopte_flag); if (result) { *bus_addr = 0; - DBG("%s:%d: dma_map_pages failed (%d)\n", + DBG("%s:%d: dma_sb_map_pages failed (%d)\n", __func__, __LINE__, result); spin_unlock_irqrestore(&r->chunk_list.lock, flags); return result; @@ -610,8 +835,57 @@ static int dma_map_area(struct ps3_dma_region *r, unsigned long virt_addr, return result; } +static int dma_ioc0_map_area(struct ps3_dma_region *r, unsigned long virt_addr, + unsigned long len, unsigned long *bus_addr, + u64 iopte_flag) +{ + int result; + unsigned long flags; + struct dma_chunk *c; + unsigned long phys_addr = is_kernel_addr(virt_addr) ? __pa(virt_addr) + : virt_addr; + unsigned long aligned_phys = _ALIGN_DOWN(phys_addr, 1 << r->page_size); + unsigned long aligned_len = _ALIGN_UP(len + phys_addr - aligned_phys, + 1 << r->page_size); + + DBG(KERN_ERR "%s: vaddr=%#lx, len=%#lx\n", __func__, + virt_addr, len); + DBG(KERN_ERR "%s: ph=%#lx a_ph=%#lx a_l=%#lx\n", __func__, + phys_addr, aligned_phys, aligned_len); + + spin_lock_irqsave(&r->chunk_list.lock, flags); + c = dma_find_chunk_lpar(r, ps3_mm_phys_to_lpar(phys_addr), len); + + if (c) { + /* FIXME */ + BUG(); + *bus_addr = c->bus_addr + phys_addr - aligned_phys; + c->usage_count++; + spin_unlock_irqrestore(&r->chunk_list.lock, flags); + return 0; + } + + result = dma_ioc0_map_pages(r, aligned_phys, aligned_len, &c, + iopte_flag); + + if (result) { + *bus_addr = 0; + DBG("%s:%d: dma_ioc0_map_pages failed (%d)\n", + __func__, __LINE__, result); + spin_unlock_irqrestore(&r->chunk_list.lock, flags); + return result; + } + *bus_addr = c->bus_addr + phys_addr - aligned_phys; + DBG("%s: va=%#lx pa=%#lx a_pa=%#lx bus=%#lx\n", __func__, + virt_addr, phys_addr, aligned_phys, *bus_addr); + c->usage_count = 1; + + spin_unlock_irqrestore(&r->chunk_list.lock, flags); + return result; +} + /** - * dma_unmap_area - Unmap an area of memory from a device dma region. + * dma_sb_unmap_area - Unmap an area of memory from a device dma region. * @r: Pointer to a struct ps3_dma_region. * @bus_addr: The starting ioc bus address of the area to unmap. * @len: Length in bytes of the area to unmap. @@ -619,7 +893,7 @@ static int dma_map_area(struct ps3_dma_region *r, unsigned long virt_addr, * This is the common dma unmap routine. */ -int dma_unmap_area(struct ps3_dma_region *r, unsigned long bus_addr, +static int dma_sb_unmap_area(struct ps3_dma_region *r, unsigned long bus_addr, unsigned long len) { unsigned long flags; @@ -631,7 +905,8 @@ int dma_unmap_area(struct ps3_dma_region *r, unsigned long bus_addr, if (!c) { unsigned long aligned_bus = _ALIGN_DOWN(bus_addr, 1 << r->page_size); - unsigned long aligned_len = _ALIGN_UP(len, 1 << r->page_size); + unsigned long aligned_len = _ALIGN_UP(len + bus_addr + - aligned_bus, 1 << r->page_size); DBG("%s:%d: not found: bus_addr %lxh\n", __func__, __LINE__, bus_addr); DBG("%s:%d: not found: len %lxh\n", @@ -647,94 +922,166 @@ int dma_unmap_area(struct ps3_dma_region *r, unsigned long bus_addr, if (!c->usage_count) { list_del(&c->link); - dma_free_chunk(c); + dma_sb_free_chunk(c); } spin_unlock_irqrestore(&r->chunk_list.lock, flags); return 0; } +static int dma_ioc0_unmap_area(struct ps3_dma_region *r, + unsigned long bus_addr, unsigned long len) +{ + unsigned long flags; + struct dma_chunk *c; + + DBG("%s: start a=%#lx l=%#lx\n", __func__, bus_addr, len); + spin_lock_irqsave(&r->chunk_list.lock, flags); + c = dma_find_chunk(r, bus_addr, len); + + if (!c) { + unsigned long aligned_bus = _ALIGN_DOWN(bus_addr, + 1 << r->page_size); + unsigned long aligned_len = _ALIGN_UP(len + bus_addr + - aligned_bus, + 1 << r->page_size); + DBG("%s:%d: not found: bus_addr %lxh\n", + __func__, __LINE__, bus_addr); + DBG("%s:%d: not found: len %lxh\n", + __func__, __LINE__, len); + DBG("%s:%d: not found: aligned_bus %lxh\n", + __func__, __LINE__, aligned_bus); + DBG("%s:%d: not found: aligned_len %lxh\n", + __func__, __LINE__, aligned_len); + BUG(); + } + + c->usage_count--; + + if (!c->usage_count) { + list_del(&c->link); + dma_ioc0_free_chunk(c); + } + + spin_unlock_irqrestore(&r->chunk_list.lock, flags); + DBG("%s: end\n", __func__); + return 0; +} + /** - * dma_region_create_linear - Setup a linear dma maping for a device. + * dma_sb_region_create_linear - Setup a linear dma mapping for a device. * @r: Pointer to a struct ps3_dma_region. * * This routine creates an HV dma region for the device and maps all available * ram into the io controller bus address space. */ -static int dma_region_create_linear(struct ps3_dma_region *r) +static int dma_sb_region_create_linear(struct ps3_dma_region *r) { int result; - unsigned long tmp; - - /* force 16M dma pages for linear mapping */ - - if (r->page_size != PS3_DMA_16M) { - pr_info("%s:%d: forcing 16M pages for linear map\n", - __func__, __LINE__); - r->page_size = PS3_DMA_16M; + unsigned long virt_addr, len, tmp; + + if (r->len > 16*1024*1024) { /* FIXME: need proper fix */ + /* force 16M dma pages for linear mapping */ + if (r->page_size != PS3_DMA_16M) { + pr_info("%s:%d: forcing 16M pages for linear map\n", + __func__, __LINE__); + r->page_size = PS3_DMA_16M; + r->len = _ALIGN_UP(r->len, 1 << r->page_size); + } } - result = dma_region_create(r); + result = dma_sb_region_create(r); BUG_ON(result); - result = dma_map_area(r, map.rm.base, map.rm.size, &tmp); - BUG_ON(result); - - if (USE_LPAR_ADDR) - result = dma_map_area(r, map.r1.base, map.r1.size, - &tmp); - else - result = dma_map_area(r, map.rm.size, map.r1.size, - &tmp); + if (r->offset < map.rm.size) { + /* Map (part of) 1st RAM chunk */ + virt_addr = map.rm.base + r->offset; + len = map.rm.size - r->offset; + if (len > r->len) + len = r->len; + result = dma_sb_map_area(r, virt_addr, len, &tmp, + IOPTE_PP_W | IOPTE_PP_R | IOPTE_SO_RW | IOPTE_M); + BUG_ON(result); + } - BUG_ON(result); + if (r->offset + r->len > map.rm.size) { + /* Map (part of) 2nd RAM chunk */ + virt_addr = USE_LPAR_ADDR ? map.r1.base : map.rm.size; + len = r->len; + if (r->offset >= map.rm.size) + virt_addr += r->offset - map.rm.size; + else + len -= map.rm.size - r->offset; + result = dma_sb_map_area(r, virt_addr, len, &tmp, + IOPTE_PP_W | IOPTE_PP_R | IOPTE_SO_RW | IOPTE_M); + BUG_ON(result); + } return result; } /** - * dma_region_free_linear - Free a linear dma mapping for a device. + * dma_sb_region_free_linear - Free a linear dma mapping for a device. * @r: Pointer to a struct ps3_dma_region. * * This routine will unmap all mapped areas and free the HV dma region. */ -static int dma_region_free_linear(struct ps3_dma_region *r) +static int dma_sb_region_free_linear(struct ps3_dma_region *r) { int result; + unsigned long bus_addr, len, lpar_addr; + + if (r->offset < map.rm.size) { + /* Unmap (part of) 1st RAM chunk */ + lpar_addr = map.rm.base + r->offset; + len = map.rm.size - r->offset; + if (len > r->len) + len = r->len; + bus_addr = dma_sb_lpar_to_bus(r, lpar_addr); + result = dma_sb_unmap_area(r, bus_addr, len); + BUG_ON(result); + } - result = dma_unmap_area(r, dma_lpar_to_bus(r, 0), map.rm.size); - BUG_ON(result); - - result = dma_unmap_area(r, dma_lpar_to_bus(r, map.r1.base), - map.r1.size); - BUG_ON(result); + if (r->offset + r->len > map.rm.size) { + /* Unmap (part of) 2nd RAM chunk */ + lpar_addr = map.r1.base; + len = r->len; + if (r->offset >= map.rm.size) + lpar_addr += r->offset - map.rm.size; + else + len -= map.rm.size - r->offset; + bus_addr = dma_sb_lpar_to_bus(r, lpar_addr); + result = dma_sb_unmap_area(r, bus_addr, len); + BUG_ON(result); + } - result = dma_region_free(r); + result = dma_sb_region_free(r); BUG_ON(result); return result; } /** - * dma_map_area_linear - Map an area of memory into a device dma region. + * dma_sb_map_area_linear - Map an area of memory into a device dma region. * @r: Pointer to a struct ps3_dma_region. * @virt_addr: Starting virtual address of the area to map. * @len: Length in bytes of the area to map. * @bus_addr: A pointer to return the starting ioc bus address of the area to * map. * - * This routine just returns the coresponding bus address. Actual mapping + * This routine just returns the corresponding bus address. Actual mapping * occurs in dma_region_create_linear(). */ -static int dma_map_area_linear(struct ps3_dma_region *r, - unsigned long virt_addr, unsigned long len, unsigned long *bus_addr) +static int dma_sb_map_area_linear(struct ps3_dma_region *r, + unsigned long virt_addr, unsigned long len, unsigned long *bus_addr, + u64 iopte_flag) { unsigned long phys_addr = is_kernel_addr(virt_addr) ? __pa(virt_addr) : virt_addr; - *bus_addr = dma_lpar_to_bus(r, ps3_mm_phys_to_lpar(phys_addr)); + *bus_addr = dma_sb_lpar_to_bus(r, ps3_mm_phys_to_lpar(phys_addr)); return 0; } @@ -744,42 +1091,98 @@ static int dma_map_area_linear(struct ps3_dma_region *r, * @bus_addr: The starting ioc bus address of the area to unmap. * @len: Length in bytes of the area to unmap. * - * This routine does nothing. Unmapping occurs in dma_region_free_linear(). + * This routine does nothing. Unmapping occurs in dma_sb_region_free_linear(). */ -static int dma_unmap_area_linear(struct ps3_dma_region *r, +static int dma_sb_unmap_area_linear(struct ps3_dma_region *r, unsigned long bus_addr, unsigned long len) { return 0; +}; + +static const struct ps3_dma_region_ops ps3_dma_sb_region_ops = { + .create = dma_sb_region_create, + .free = dma_sb_region_free, + .map = dma_sb_map_area, + .unmap = dma_sb_unmap_area +}; + +static const struct ps3_dma_region_ops ps3_dma_sb_region_linear_ops = { + .create = dma_sb_region_create_linear, + .free = dma_sb_region_free_linear, + .map = dma_sb_map_area_linear, + .unmap = dma_sb_unmap_area_linear +}; + +static const struct ps3_dma_region_ops ps3_dma_ioc0_region_ops = { + .create = dma_ioc0_region_create, + .free = dma_ioc0_region_free, + .map = dma_ioc0_map_area, + .unmap = dma_ioc0_unmap_area +}; + +int ps3_dma_region_init(struct ps3_system_bus_device *dev, + struct ps3_dma_region *r, enum ps3_dma_page_size page_size, + enum ps3_dma_region_type region_type, void *addr, unsigned long len) +{ + unsigned long lpar_addr; + + lpar_addr = addr ? ps3_mm_phys_to_lpar(__pa(addr)) : 0; + + r->dev = dev; + r->page_size = page_size; + r->region_type = region_type; + r->offset = lpar_addr; + if (r->offset >= map.rm.size) + r->offset -= map.r1.offset; + r->len = len ? len : _ALIGN_UP(map.total, 1 << r->page_size); + + switch (dev->dev_type) { + case PS3_DEVICE_TYPE_SB: + r->region_ops = (USE_DYNAMIC_DMA) + ? &ps3_dma_sb_region_ops + : &ps3_dma_sb_region_linear_ops; + break; + case PS3_DEVICE_TYPE_IOC0: + r->region_ops = &ps3_dma_ioc0_region_ops; + break; + default: + BUG(); + return -EINVAL; + } + return 0; } +EXPORT_SYMBOL(ps3_dma_region_init); int ps3_dma_region_create(struct ps3_dma_region *r) { - return (USE_DYNAMIC_DMA) - ? dma_region_create(r) - : dma_region_create_linear(r); + BUG_ON(!r); + BUG_ON(!r->region_ops); + BUG_ON(!r->region_ops->create); + return r->region_ops->create(r); } +EXPORT_SYMBOL(ps3_dma_region_create); int ps3_dma_region_free(struct ps3_dma_region *r) { - return (USE_DYNAMIC_DMA) - ? dma_region_free(r) - : dma_region_free_linear(r); + BUG_ON(!r); + BUG_ON(!r->region_ops); + BUG_ON(!r->region_ops->free); + return r->region_ops->free(r); } +EXPORT_SYMBOL(ps3_dma_region_free); int ps3_dma_map(struct ps3_dma_region *r, unsigned long virt_addr, - unsigned long len, unsigned long *bus_addr) + unsigned long len, unsigned long *bus_addr, + u64 iopte_flag) { - return (USE_DYNAMIC_DMA) - ? dma_map_area(r, virt_addr, len, bus_addr) - : dma_map_area_linear(r, virt_addr, len, bus_addr); + return r->region_ops->map(r, virt_addr, len, bus_addr, iopte_flag); } int ps3_dma_unmap(struct ps3_dma_region *r, unsigned long bus_addr, unsigned long len) { - return (USE_DYNAMIC_DMA) ? dma_unmap_area(r, bus_addr, len) - : dma_unmap_area_linear(r, bus_addr, len); + return r->region_ops->unmap(r, bus_addr, len); } /*============================================================================*/ @@ -810,12 +1213,13 @@ void __init ps3_mm_init(void) BUG_ON(map.rm.base); BUG_ON(!map.rm.size); - lmb_add(map.rm.base, map.rm.size); - lmb_analyze(); /* arrange to do this in ps3_mm_add_memory */ ps3_mm_region_create(&map.r1, map.total - map.rm.size); + /* correct map.total for the real total amount of memory we use */ + map.total = map.rm.size + map.r1.size; + DBG(" <- %s:%d\n", __func__, __LINE__); } diff --git a/arch/powerpc/platforms/ps3/os-area.c b/arch/powerpc/platforms/ps3/os-area.c index 5c3da08bc0c..b70e474014f 100644 --- a/arch/powerpc/platforms/ps3/os-area.c +++ b/arch/powerpc/platforms/ps3/os-area.c @@ -133,7 +133,7 @@ struct saved_params { } static saved_params; #define dump_header(_a) _dump_header(_a, __func__, __LINE__) -static void _dump_header(const struct os_area_header __iomem *h, const char* func, +static void _dump_header(const struct os_area_header *h, const char *func, int line) { pr_debug("%s:%d: h.magic_num: '%s'\n", func, line, @@ -151,7 +151,7 @@ static void _dump_header(const struct os_area_header __iomem *h, const char* fun } #define dump_params(_a) _dump_params(_a, __func__, __LINE__) -static void _dump_params(const struct os_area_params __iomem *p, const char* func, +static void _dump_params(const struct os_area_params *p, const char *func, int line) { pr_debug("%s:%d: p.boot_flag: %u\n", func, line, p->boot_flag); diff --git a/arch/powerpc/platforms/ps3/platform.h b/arch/powerpc/platforms/ps3/platform.h index ca04f03305c..87d52060fec 100644 --- a/arch/powerpc/platforms/ps3/platform.h +++ b/arch/powerpc/platforms/ps3/platform.h @@ -41,6 +41,7 @@ void ps3_mm_shutdown(void); /* irq */ void ps3_init_IRQ(void); +void ps3_shutdown_IRQ(int cpu); void __init ps3_register_ipi_debug_brk(unsigned int cpu, unsigned int virq); /* smp */ @@ -82,6 +83,7 @@ enum ps3_dev_type { PS3_DEV_TYPE_STOR_ROM = TYPE_ROM, /* 5 */ PS3_DEV_TYPE_SB_GPIO = 6, PS3_DEV_TYPE_STOR_FLASH = TYPE_RBC, /* 14 */ + PS3_DEV_TYPE_NOACCESS = 255, }; int ps3_repository_read_bus_str(unsigned int bus_index, const char *bus_str, @@ -129,24 +131,28 @@ int ps3_repository_read_dev_reg(unsigned int bus_index, /* repository bus enumerators */ struct ps3_repository_device { + enum ps3_bus_type bus_type; unsigned int bus_index; + unsigned int bus_id; + enum ps3_dev_type dev_type; unsigned int dev_index; - struct ps3_device_id did; + unsigned int dev_id; }; -int ps3_repository_find_device(enum ps3_bus_type bus_type, - enum ps3_dev_type dev_type, - const struct ps3_repository_device *start_dev, - struct ps3_repository_device *dev); -static inline int ps3_repository_find_first_device( - enum ps3_bus_type bus_type, enum ps3_dev_type dev_type, - struct ps3_repository_device *dev) +static inline struct ps3_repository_device *ps3_repository_bump_device( + struct ps3_repository_device *repo) { - return ps3_repository_find_device(bus_type, dev_type, NULL, dev); + repo->dev_index++; + return repo; } -int ps3_repository_find_interrupt(const struct ps3_repository_device *dev, +int ps3_repository_find_device(struct ps3_repository_device *repo); +int ps3_repository_find_devices(enum ps3_bus_type bus_type, + int (*callback)(const struct ps3_repository_device *repo)); +int ps3_repository_find_bus(enum ps3_bus_type bus_type, unsigned int from, + unsigned int *bus_index); +int ps3_repository_find_interrupt(const struct ps3_repository_device *repo, enum ps3_interrupt_type intr_type, unsigned int *interrupt_id); -int ps3_repository_find_reg(const struct ps3_repository_device *dev, +int ps3_repository_find_reg(const struct ps3_repository_device *repo, enum ps3_reg_type reg_type, u64 *bus_addr, u64 *len); /* repository block device info */ @@ -216,4 +222,19 @@ int ps3_repository_read_num_spu_resource_id(unsigned int *num_resource_id); int ps3_repository_read_spu_resource_id(unsigned int res_index, enum ps3_spu_resource_type* resource_type, unsigned int *resource_id); +/* repository vuart info */ + +int ps3_repository_read_vuart_av_port(unsigned int *port); +int ps3_repository_read_vuart_sysmgr_port(unsigned int *port); + +/* Page table entries */ +#define IOPTE_PP_W 0x8000000000000000ul /* protection: write */ +#define IOPTE_PP_R 0x4000000000000000ul /* protection: read */ +#define IOPTE_M 0x2000000000000000ul /* coherency required */ +#define IOPTE_SO_R 0x1000000000000000ul /* ordering: writes */ +#define IOPTE_SO_RW 0x1800000000000000ul /* ordering: r & w */ +#define IOPTE_RPN_Mask 0x07fffffffffff000ul /* RPN */ +#define IOPTE_H 0x0000000000000800ul /* cache hint */ +#define IOPTE_IOID_Mask 0x00000000000007fful /* ioid */ + #endif diff --git a/arch/powerpc/platforms/ps3/repository.c b/arch/powerpc/platforms/ps3/repository.c index ae586a0e5d3..8cc37cfea0f 100644 --- a/arch/powerpc/platforms/ps3/repository.c +++ b/arch/powerpc/platforms/ps3/repository.c @@ -138,7 +138,7 @@ static int read_node(unsigned int lpar_id, u64 n1, u64 n2, u64 n3, u64 n4, pr_debug("%s:%d: lv1_get_repository_node_value failed: %s\n", __func__, __LINE__, ps3_result(result)); dump_node_name(lpar_id, n1, n2, n3, n4); - return result; + return -ENOENT; } dump_node(lpar_id, n1, n2, n3, n4, v1, v2); @@ -155,7 +155,7 @@ static int read_node(unsigned int lpar_id, u64 n1, u64 n2, u64 n3, u64 n4, pr_debug("%s:%d: warning: discarding non-zero v2: %016lx\n", __func__, __LINE__, v2); - return result; + return 0; } int ps3_repository_read_bus_str(unsigned int bus_index, const char *bus_str, @@ -314,324 +314,140 @@ int ps3_repository_read_dev_reg(unsigned int bus_index, reg_index, bus_addr, len); } -#if defined(DEBUG) -int ps3_repository_dump_resource_info(unsigned int bus_index, - unsigned int dev_index) -{ - int result = 0; - unsigned int res_index; - pr_debug(" -> %s:%d: (%u:%u)\n", __func__, __LINE__, - bus_index, dev_index); - for (res_index = 0; res_index < 10; res_index++) { - enum ps3_interrupt_type intr_type; - unsigned int interrupt_id; +int ps3_repository_find_device(struct ps3_repository_device *repo) +{ + int result; + struct ps3_repository_device tmp = *repo; + unsigned int num_dev; - result = ps3_repository_read_dev_intr(bus_index, dev_index, - res_index, &intr_type, &interrupt_id); + BUG_ON(repo->bus_index > 10); + BUG_ON(repo->dev_index > 10); - if (result) { - if (result != LV1_NO_ENTRY) - pr_debug("%s:%d ps3_repository_read_dev_intr" - " (%u:%u) failed\n", __func__, __LINE__, - bus_index, dev_index); - break; - } + result = ps3_repository_read_bus_num_dev(tmp.bus_index, &num_dev); - pr_debug("%s:%d (%u:%u) intr_type %u, interrupt_id %u\n", - __func__, __LINE__, bus_index, dev_index, intr_type, - interrupt_id); + if (result) { + pr_debug("%s:%d read_bus_num_dev failed\n", __func__, __LINE__); + return result; } - for (res_index = 0; res_index < 10; res_index++) { - enum ps3_reg_type reg_type; - u64 bus_addr; - u64 len; - - result = ps3_repository_read_dev_reg(bus_index, dev_index, - res_index, ®_type, &bus_addr, &len); + pr_debug("%s:%d: bus_type %u, bus_index %u, bus_id %u, num_dev %u\n", + __func__, __LINE__, tmp.bus_type, tmp.bus_index, tmp.bus_id, + num_dev); - if (result) { - if (result != LV1_NO_ENTRY) - pr_debug("%s:%d ps3_repository_read_dev_reg" - " (%u:%u) failed\n", __func__, __LINE__, - bus_index, dev_index); - break; - } - - pr_debug("%s:%d (%u:%u) reg_type %u, bus_addr %lxh, len %lxh\n", - __func__, __LINE__, bus_index, dev_index, reg_type, - bus_addr, len); + if (tmp.dev_index >= num_dev) { + pr_debug("%s:%d: no device found\n", __func__, __LINE__); + return -ENODEV; } - pr_debug(" <- %s:%d\n", __func__, __LINE__); - return result; -} - -static int dump_stor_dev_info(unsigned int bus_index, unsigned int dev_index) -{ - int result = 0; - unsigned int num_regions, region_index; - u64 port, blk_size, num_blocks; - - pr_debug(" -> %s:%d: (%u:%u)\n", __func__, __LINE__, - bus_index, dev_index); + result = ps3_repository_read_dev_type(tmp.bus_index, tmp.dev_index, + &tmp.dev_type); - result = ps3_repository_read_stor_dev_info(bus_index, dev_index, &port, - &blk_size, &num_blocks, &num_regions); if (result) { - pr_debug("%s:%d ps3_repository_read_stor_dev_info" - " (%u:%u) failed\n", __func__, __LINE__, - bus_index, dev_index); - goto out; + pr_debug("%s:%d read_dev_type failed\n", __func__, __LINE__); + return result; } - pr_debug("%s:%d (%u:%u): port %lu, blk_size %lu, num_blocks " - "%lu, num_regions %u\n", - __func__, __LINE__, bus_index, dev_index, port, - blk_size, num_blocks, num_regions); - - for (region_index = 0; region_index < num_regions; region_index++) { - unsigned int region_id; - u64 region_start, region_size; - - result = ps3_repository_read_stor_dev_region(bus_index, - dev_index, region_index, ®ion_id, ®ion_start, - ®ion_size); - if (result) { - pr_debug("%s:%d ps3_repository_read_stor_dev_region" - " (%u:%u) failed\n", __func__, __LINE__, - bus_index, dev_index); - break; - } + result = ps3_repository_read_dev_id(tmp.bus_index, tmp.dev_index, + &tmp.dev_id); - pr_debug("%s:%d (%u:%u) region_id %u, start %lxh, size %lxh\n", - __func__, __LINE__, bus_index, dev_index, region_id, - region_start, region_size); + if (result) { + pr_debug("%s:%d ps3_repository_read_dev_id failed\n", __func__, + __LINE__); + return result; } -out: - pr_debug(" <- %s:%d\n", __func__, __LINE__); - return result; -} - -static int dump_device_info(unsigned int bus_index, enum ps3_bus_type bus_type, - unsigned int num_dev) -{ - int result = 0; - unsigned int dev_index; - - pr_debug(" -> %s:%d: bus_%u\n", __func__, __LINE__, bus_index); - - for (dev_index = 0; dev_index < num_dev; dev_index++) { - enum ps3_dev_type dev_type; - unsigned int dev_id; - - result = ps3_repository_read_dev_type(bus_index, dev_index, - &dev_type); - - if (result) { - pr_debug("%s:%d ps3_repository_read_dev_type" - " (%u:%u) failed\n", __func__, __LINE__, - bus_index, dev_index); - break; - } - - result = ps3_repository_read_dev_id(bus_index, dev_index, - &dev_id); - - if (result) { - pr_debug("%s:%d ps3_repository_read_dev_id" - " (%u:%u) failed\n", __func__, __LINE__, - bus_index, dev_index); - continue; - } + pr_debug("%s:%d: found: dev_type %u, dev_index %u, dev_id %u\n", + __func__, __LINE__, tmp.dev_type, tmp.dev_index, tmp.dev_id); - pr_debug("%s:%d (%u:%u): dev_type %u, dev_id %u\n", __func__, - __LINE__, bus_index, dev_index, dev_type, dev_id); - - ps3_repository_dump_resource_info(bus_index, dev_index); - - if (bus_type == PS3_BUS_TYPE_STORAGE) - dump_stor_dev_info(bus_index, dev_index); - } - - pr_debug(" <- %s:%d\n", __func__, __LINE__); - return result; + *repo = tmp; + return 0; } -int ps3_repository_dump_bus_info(void) +int __devinit ps3_repository_find_devices(enum ps3_bus_type bus_type, + int (*callback)(const struct ps3_repository_device *repo)) { int result = 0; - unsigned int bus_index; + struct ps3_repository_device repo; - pr_debug(" -> %s:%d\n", __func__, __LINE__); + pr_debug(" -> %s:%d: find bus_type %u\n", __func__, __LINE__, bus_type); - for (bus_index = 0; bus_index < 10; bus_index++) { - enum ps3_bus_type bus_type; - unsigned int bus_id; - unsigned int num_dev; + for (repo.bus_index = 0; repo.bus_index < 10; repo.bus_index++) { - result = ps3_repository_read_bus_type(bus_index, &bus_type); + result = ps3_repository_read_bus_type(repo.bus_index, + &repo.bus_type); if (result) { pr_debug("%s:%d read_bus_type(%u) failed\n", - __func__, __LINE__, bus_index); + __func__, __LINE__, repo.bus_index); break; } - result = ps3_repository_read_bus_id(bus_index, &bus_id); - - if (result) { - pr_debug("%s:%d read_bus_id(%u) failed\n", - __func__, __LINE__, bus_index); + if (repo.bus_type != bus_type) { + pr_debug("%s:%d: skip, bus_type %u\n", __func__, + __LINE__, repo.bus_type); continue; } - if (bus_index != bus_id) - pr_debug("%s:%d bus_index != bus_id\n", - __func__, __LINE__); - - result = ps3_repository_read_bus_num_dev(bus_index, &num_dev); + result = ps3_repository_read_bus_id(repo.bus_index, + &repo.bus_id); if (result) { - pr_debug("%s:%d read_bus_num_dev(%u) failed\n", - __func__, __LINE__, bus_index); + pr_debug("%s:%d read_bus_id(%u) failed\n", + __func__, __LINE__, repo.bus_index); continue; } - pr_debug("%s:%d bus_%u: bus_type %u, bus_id %u, num_dev %u\n", - __func__, __LINE__, bus_index, bus_type, bus_id, - num_dev); + for (repo.dev_index = 0; ; repo.dev_index++) { + result = ps3_repository_find_device(&repo); - dump_device_info(bus_index, bus_type, num_dev); - } + if (result == -ENODEV) { + result = 0; + break; + } else if (result) + break; - pr_debug(" <- %s:%d\n", __func__, __LINE__); - return result; -} -#endif /* defined(DEBUG) */ - -static int find_device(unsigned int bus_index, unsigned int num_dev, - unsigned int start_dev_index, enum ps3_dev_type dev_type, - struct ps3_repository_device *dev) -{ - int result = 0; - unsigned int dev_index; + result = callback(&repo); - pr_debug("%s:%d: find dev_type %u\n", __func__, __LINE__, dev_type); - - dev->dev_index = UINT_MAX; - - for (dev_index = start_dev_index; dev_index < num_dev; dev_index++) { - enum ps3_dev_type x; - - result = ps3_repository_read_dev_type(bus_index, dev_index, - &x); - - if (result) { - pr_debug("%s:%d read_dev_type failed\n", - __func__, __LINE__); - return result; + if (result) { + pr_debug("%s:%d: abort at callback\n", __func__, + __LINE__); + break; + } } - - if (x == dev_type) - break; - } - - if (dev_index == num_dev) - return -1; - - pr_debug("%s:%d: found dev_type %u at dev_index %u\n", - __func__, __LINE__, dev_type, dev_index); - - result = ps3_repository_read_dev_id(bus_index, dev_index, - &dev->did.dev_id); - - if (result) { - pr_debug("%s:%d read_dev_id failed\n", - __func__, __LINE__); - return result; + break; } - dev->dev_index = dev_index; - - pr_debug("%s:%d found: dev_id %u\n", __func__, __LINE__, - dev->did.dev_id); - + pr_debug(" <- %s:%d\n", __func__, __LINE__); return result; } -int ps3_repository_find_device (enum ps3_bus_type bus_type, - enum ps3_dev_type dev_type, - const struct ps3_repository_device *start_dev, - struct ps3_repository_device *dev) +int ps3_repository_find_bus(enum ps3_bus_type bus_type, unsigned int from, + unsigned int *bus_index) { - int result = 0; - unsigned int bus_index; - unsigned int num_dev; - - pr_debug("%s:%d: find bus_type %u, dev_type %u\n", __func__, __LINE__, - bus_type, dev_type); - - BUG_ON(start_dev && start_dev->bus_index > 10); - - for (bus_index = start_dev ? start_dev->bus_index : 0; bus_index < 10; - bus_index++) { - enum ps3_bus_type x; - - result = ps3_repository_read_bus_type(bus_index, &x); + unsigned int i; + enum ps3_bus_type type; + int error; - if (result) { + for (i = from; i < 10; i++) { + error = ps3_repository_read_bus_type(i, &type); + if (error) { pr_debug("%s:%d read_bus_type failed\n", __func__, __LINE__); - dev->bus_index = UINT_MAX; - return result; + *bus_index = UINT_MAX; + return error; + } + if (type == bus_type) { + *bus_index = i; + return 0; } - if (x == bus_type) - break; - } - - if (bus_index >= 10) - return -ENODEV; - - pr_debug("%s:%d: found bus_type %u at bus_index %u\n", - __func__, __LINE__, bus_type, bus_index); - - result = ps3_repository_read_bus_num_dev(bus_index, &num_dev); - - if (result) { - pr_debug("%s:%d read_bus_num_dev failed\n", - __func__, __LINE__); - return result; - } - - result = find_device(bus_index, num_dev, start_dev - ? start_dev->dev_index + 1 : 0, dev_type, dev); - - if (result) { - pr_debug("%s:%d get_did failed\n", __func__, __LINE__); - return result; - } - - result = ps3_repository_read_bus_id(bus_index, &dev->did.bus_id); - - if (result) { - pr_debug("%s:%d read_bus_id failed\n", - __func__, __LINE__); - return result; } - - dev->bus_index = bus_index; - - pr_debug("%s:%d found: bus_id %u, dev_id %u\n", - __func__, __LINE__, dev->did.bus_id, dev->did.dev_id); - - return result; + *bus_index = UINT_MAX; + return -ENODEV; } -int ps3_repository_find_interrupt(const struct ps3_repository_device *dev, +int ps3_repository_find_interrupt(const struct ps3_repository_device *repo, enum ps3_interrupt_type intr_type, unsigned int *interrupt_id) { int result = 0; @@ -645,8 +461,8 @@ int ps3_repository_find_interrupt(const struct ps3_repository_device *dev, enum ps3_interrupt_type t; unsigned int id; - result = ps3_repository_read_dev_intr(dev->bus_index, - dev->dev_index, res_index, &t, &id); + result = ps3_repository_read_dev_intr(repo->bus_index, + repo->dev_index, res_index, &t, &id); if (result) { pr_debug("%s:%d read_dev_intr failed\n", @@ -669,7 +485,7 @@ int ps3_repository_find_interrupt(const struct ps3_repository_device *dev, return result; } -int ps3_repository_find_reg(const struct ps3_repository_device *dev, +int ps3_repository_find_reg(const struct ps3_repository_device *repo, enum ps3_reg_type reg_type, u64 *bus_addr, u64 *len) { int result = 0; @@ -684,8 +500,8 @@ int ps3_repository_find_reg(const struct ps3_repository_device *dev, u64 a; u64 l; - result = ps3_repository_read_dev_reg(dev->bus_index, - dev->dev_index, res_index, &t, &a, &l); + result = ps3_repository_read_dev_reg(repo->bus_index, + repo->dev_index, res_index, &t, &a, &l); if (result) { pr_debug("%s:%d read_dev_reg failed\n", @@ -965,6 +781,36 @@ int ps3_repository_read_boot_dat_size(unsigned int *size) return result; } +int ps3_repository_read_vuart_av_port(unsigned int *port) +{ + int result; + u64 v1; + + result = read_node(PS3_LPAR_ID_CURRENT, + make_first_field("bi", 0), + make_field("vir_uart", 0), + make_field("port", 0), + make_field("avset", 0), + &v1, 0); + *port = v1; + return result; +} + +int ps3_repository_read_vuart_sysmgr_port(unsigned int *port) +{ + int result; + u64 v1; + + result = read_node(PS3_LPAR_ID_CURRENT, + make_first_field("bi", 0), + make_field("vir_uart", 0), + make_field("port", 0), + make_field("sysmgr", 0), + &v1, 0); + *port = v1; + return result; +} + /** * ps3_repository_read_boot_dat_info - Get address and size of cell_ext_os_area. * address: lpar address of cell_ext_os_area @@ -1026,3 +872,205 @@ int ps3_repository_read_be_tb_freq(unsigned int be_index, u64 *tb_freq) return result ? result : ps3_repository_read_tb_freq(node_id, tb_freq); } + +#if defined(DEBUG) + +int ps3_repository_dump_resource_info(const struct ps3_repository_device *repo) +{ + int result = 0; + unsigned int res_index; + + pr_debug(" -> %s:%d: (%u:%u)\n", __func__, __LINE__, + repo->bus_index, repo->dev_index); + + for (res_index = 0; res_index < 10; res_index++) { + enum ps3_interrupt_type intr_type; + unsigned int interrupt_id; + + result = ps3_repository_read_dev_intr(repo->bus_index, + repo->dev_index, res_index, &intr_type, &interrupt_id); + + if (result) { + if (result != LV1_NO_ENTRY) + pr_debug("%s:%d ps3_repository_read_dev_intr" + " (%u:%u) failed\n", __func__, __LINE__, + repo->bus_index, repo->dev_index); + break; + } + + pr_debug("%s:%d (%u:%u) intr_type %u, interrupt_id %u\n", + __func__, __LINE__, repo->bus_index, repo->dev_index, + intr_type, interrupt_id); + } + + for (res_index = 0; res_index < 10; res_index++) { + enum ps3_reg_type reg_type; + u64 bus_addr; + u64 len; + + result = ps3_repository_read_dev_reg(repo->bus_index, + repo->dev_index, res_index, ®_type, &bus_addr, &len); + + if (result) { + if (result != LV1_NO_ENTRY) + pr_debug("%s:%d ps3_repository_read_dev_reg" + " (%u:%u) failed\n", __func__, __LINE__, + repo->bus_index, repo->dev_index); + break; + } + + pr_debug("%s:%d (%u:%u) reg_type %u, bus_addr %lxh, len %lxh\n", + __func__, __LINE__, repo->bus_index, repo->dev_index, + reg_type, bus_addr, len); + } + + pr_debug(" <- %s:%d\n", __func__, __LINE__); + return result; +} + +static int dump_stor_dev_info(struct ps3_repository_device *repo) +{ + int result = 0; + unsigned int num_regions, region_index; + u64 port, blk_size, num_blocks; + + pr_debug(" -> %s:%d: (%u:%u)\n", __func__, __LINE__, + repo->bus_index, repo->dev_index); + + result = ps3_repository_read_stor_dev_info(repo->bus_index, + repo->dev_index, &port, &blk_size, &num_blocks, &num_regions); + if (result) { + pr_debug("%s:%d ps3_repository_read_stor_dev_info" + " (%u:%u) failed\n", __func__, __LINE__, + repo->bus_index, repo->dev_index); + goto out; + } + + pr_debug("%s:%d (%u:%u): port %lu, blk_size %lu, num_blocks " + "%lu, num_regions %u\n", + __func__, __LINE__, repo->bus_index, repo->dev_index, port, + blk_size, num_blocks, num_regions); + + for (region_index = 0; region_index < num_regions; region_index++) { + unsigned int region_id; + u64 region_start, region_size; + + result = ps3_repository_read_stor_dev_region(repo->bus_index, + repo->dev_index, region_index, ®ion_id, + ®ion_start, ®ion_size); + if (result) { + pr_debug("%s:%d ps3_repository_read_stor_dev_region" + " (%u:%u) failed\n", __func__, __LINE__, + repo->bus_index, repo->dev_index); + break; + } + + pr_debug("%s:%d (%u:%u) region_id %u, start %lxh, size %lxh\n", + __func__, __LINE__, repo->bus_index, repo->dev_index, + region_id, region_start, region_size); + } + +out: + pr_debug(" <- %s:%d\n", __func__, __LINE__); + return result; +} + +static int dump_device_info(struct ps3_repository_device *repo, + unsigned int num_dev) +{ + int result = 0; + + pr_debug(" -> %s:%d: bus_%u\n", __func__, __LINE__, repo->bus_index); + + for (repo->dev_index = 0; repo->dev_index < num_dev; + repo->dev_index++) { + + result = ps3_repository_read_dev_type(repo->bus_index, + repo->dev_index, &repo->dev_type); + + if (result) { + pr_debug("%s:%d ps3_repository_read_dev_type" + " (%u:%u) failed\n", __func__, __LINE__, + repo->bus_index, repo->dev_index); + break; + } + + result = ps3_repository_read_dev_id(repo->bus_index, + repo->dev_index, &repo->dev_id); + + if (result) { + pr_debug("%s:%d ps3_repository_read_dev_id" + " (%u:%u) failed\n", __func__, __LINE__, + repo->bus_index, repo->dev_index); + continue; + } + + pr_debug("%s:%d (%u:%u): dev_type %u, dev_id %u\n", __func__, + __LINE__, repo->bus_index, repo->dev_index, + repo->dev_type, repo->dev_id); + + ps3_repository_dump_resource_info(repo); + + if (repo->bus_type == PS3_BUS_TYPE_STORAGE) + dump_stor_dev_info(repo); + } + + pr_debug(" <- %s:%d\n", __func__, __LINE__); + return result; +} + +int ps3_repository_dump_bus_info(void) +{ + int result = 0; + struct ps3_repository_device repo; + + pr_debug(" -> %s:%d\n", __func__, __LINE__); + + memset(&repo, 0, sizeof(repo)); + + for (repo.bus_index = 0; repo.bus_index < 10; repo.bus_index++) { + unsigned int num_dev; + + result = ps3_repository_read_bus_type(repo.bus_index, + &repo.bus_type); + + if (result) { + pr_debug("%s:%d read_bus_type(%u) failed\n", + __func__, __LINE__, repo.bus_index); + break; + } + + result = ps3_repository_read_bus_id(repo.bus_index, + &repo.bus_id); + + if (result) { + pr_debug("%s:%d read_bus_id(%u) failed\n", + __func__, __LINE__, repo.bus_index); + continue; + } + + if (repo.bus_index != repo.bus_id) + pr_debug("%s:%d bus_index != bus_id\n", + __func__, __LINE__); + + result = ps3_repository_read_bus_num_dev(repo.bus_index, + &num_dev); + + if (result) { + pr_debug("%s:%d read_bus_num_dev(%u) failed\n", + __func__, __LINE__, repo.bus_index); + continue; + } + + pr_debug("%s:%d bus_%u: bus_type %u, bus_id %u, num_dev %u\n", + __func__, __LINE__, repo.bus_index, repo.bus_type, + repo.bus_id, num_dev); + + dump_device_info(&repo, num_dev); + } + + pr_debug(" <- %s:%d\n", __func__, __LINE__); + return result; +} + +#endif /* defined(DEBUG) */ diff --git a/arch/powerpc/platforms/ps3/setup.c b/arch/powerpc/platforms/ps3/setup.c index 93539676662..aa05288de64 100644 --- a/arch/powerpc/platforms/ps3/setup.c +++ b/arch/powerpc/platforms/ps3/setup.c @@ -37,27 +37,35 @@ #include "platform.h" #if defined(DEBUG) -#define DBG(fmt...) udbg_printf(fmt) +#define DBG udbg_printf #else -#define DBG(fmt...) do{if(0)printk(fmt);}while(0) +#define DBG pr_debug #endif #if !defined(CONFIG_SMP) static void smp_send_stop(void) {} #endif -int ps3_get_firmware_version(union ps3_firmware_version *v) +static union ps3_firmware_version ps3_firmware_version; + +void ps3_get_firmware_version(union ps3_firmware_version *v) { - int result = lv1_get_version_info(&v->raw); + *v = ps3_firmware_version; +} +EXPORT_SYMBOL_GPL(ps3_get_firmware_version); - if (result) { - v->raw = 0; - return -1; - } +int ps3_compare_firmware_version(u16 major, u16 minor, u16 rev) +{ + union ps3_firmware_version x; + + x.pad = 0; + x.major = major; + x.minor = minor; + x.rev = rev; - return result; + return (ps3_firmware_version.raw - x.raw); } -EXPORT_SYMBOL_GPL(ps3_get_firmware_version); +EXPORT_SYMBOL_GPL(ps3_compare_firmware_version); static void ps3_power_save(void) { @@ -99,7 +107,8 @@ static void ps3_panic(char *str) while(1); } -#ifdef CONFIG_FB_PS3 +#if defined(CONFIG_FB_PS3) || defined(CONFIG_FB_PS3_MODULE) || \ + defined(CONFIG_PS3_FLASH) || defined(CONFIG_PS3_FLASH_MODULE) static void prealloc(struct ps3_prealloc *p) { if (!p->size) @@ -115,12 +124,15 @@ static void prealloc(struct ps3_prealloc *p) printk(KERN_INFO "%s: %lu bytes at %p\n", p->name, p->size, p->address); } +#endif +#if defined(CONFIG_FB_PS3) || defined(CONFIG_FB_PS3_MODULE) struct ps3_prealloc ps3fb_videomemory = { - .name = "ps3fb videomemory", - .size = CONFIG_FB_PS3_DEFAULT_SIZE_M*1024*1024, - .align = 1024*1024 /* the GPU requires 1 MiB alignment */ + .name = "ps3fb videomemory", + .size = CONFIG_FB_PS3_DEFAULT_SIZE_M*1024*1024, + .align = 1024*1024 /* the GPU requires 1 MiB alignment */ }; +EXPORT_SYMBOL_GPL(ps3fb_videomemory); #define prealloc_ps3fb_videomemory() prealloc(&ps3fb_videomemory) static int __init early_parse_ps3fb(char *p) @@ -137,6 +149,30 @@ early_param("ps3fb", early_parse_ps3fb); #define prealloc_ps3fb_videomemory() do { } while (0) #endif +#if defined(CONFIG_PS3_FLASH) || defined(CONFIG_PS3_FLASH_MODULE) +struct ps3_prealloc ps3flash_bounce_buffer = { + .name = "ps3flash bounce buffer", + .size = 256*1024, + .align = 256*1024 +}; +EXPORT_SYMBOL_GPL(ps3flash_bounce_buffer); +#define prealloc_ps3flash_bounce_buffer() prealloc(&ps3flash_bounce_buffer) + +static int __init early_parse_ps3flash(char *p) +{ + if (!p) + return 1; + + if (!strcmp(p, "off")) + ps3flash_bounce_buffer.size = 0; + + return 0; +} +early_param("ps3flash", early_parse_ps3flash); +#else +#define prealloc_ps3flash_bounce_buffer() do { } while (0) +#endif + static int ps3_set_dabr(u64 dabr) { enum {DABR_USER = 1, DABR_KERNEL = 2,}; @@ -146,13 +182,13 @@ static int ps3_set_dabr(u64 dabr) static void __init ps3_setup_arch(void) { - union ps3_firmware_version v; DBG(" -> %s:%d\n", __func__, __LINE__); - ps3_get_firmware_version(&v); - printk(KERN_INFO "PS3 firmware version %u.%u.%u\n", v.major, v.minor, - v.rev); + lv1_get_version_info(&ps3_firmware_version.raw); + printk(KERN_INFO "PS3 firmware version %u.%u.%u\n", + ps3_firmware_version.major, ps3_firmware_version.minor, + ps3_firmware_version.rev); ps3_spu_set_platform(); ps3_map_htab(); @@ -166,6 +202,8 @@ static void __init ps3_setup_arch(void) #endif prealloc_ps3fb_videomemory(); + prealloc_ps3flash_bounce_buffer(); + ppc_md.power_save = ps3_power_save; DBG(" <- %s:%d\n", __func__, __LINE__); @@ -184,7 +222,7 @@ static int __init ps3_probe(void) DBG(" -> %s:%d\n", __func__, __LINE__); dt_root = of_get_flat_dt_root(); - if (!of_flat_dt_is_compatible(dt_root, "PS3")) + if (!of_flat_dt_is_compatible(dt_root, "sony,ps3")) return 0; powerpc_firmware_features |= FW_FEATURE_PS3_POSSIBLE; @@ -201,31 +239,12 @@ static int __init ps3_probe(void) #if defined(CONFIG_KEXEC) static void ps3_kexec_cpu_down(int crash_shutdown, int secondary) { - DBG(" -> %s:%d\n", __func__, __LINE__); - - if (secondary) { - int cpu; - for_each_online_cpu(cpu) - if (cpu) - ps3_smp_cleanup_cpu(cpu); - } else - ps3_smp_cleanup_cpu(0); - - DBG(" <- %s:%d\n", __func__, __LINE__); -} - -static void ps3_machine_kexec(struct kimage *image) -{ - unsigned long ppe_id; - - DBG(" -> %s:%d\n", __func__, __LINE__); + int cpu = smp_processor_id(); - lv1_get_logical_ppe_id(&ppe_id); - lv1_configure_irq_state_bitmap(ppe_id, 0, 0); - ps3_mm_shutdown(); - ps3_mm_vas_destroy(); + DBG(" -> %s:%d: (%d)\n", __func__, __LINE__, cpu); - default_machine_kexec(image); + ps3_smp_cleanup_cpu(cpu); + ps3_shutdown_IRQ(cpu); DBG(" <- %s:%d\n", __func__, __LINE__); } @@ -247,7 +266,7 @@ define_machine(ps3) { .power_off = ps3_power_off, #if defined(CONFIG_KEXEC) .kexec_cpu_down = ps3_kexec_cpu_down, - .machine_kexec = ps3_machine_kexec, + .machine_kexec = default_machine_kexec, .machine_kexec_prepare = default_machine_kexec_prepare, .machine_crash_shutdown = default_machine_crash_shutdown, #endif diff --git a/arch/powerpc/platforms/ps3/smp.c b/arch/powerpc/platforms/ps3/smp.c index 53416ec5198..f0b12f21236 100644 --- a/arch/powerpc/platforms/ps3/smp.c +++ b/arch/powerpc/platforms/ps3/smp.c @@ -27,9 +27,9 @@ #include "platform.h" #if defined(DEBUG) -#define DBG(fmt...) udbg_printf(fmt) +#define DBG udbg_printf #else -#define DBG(fmt...) do{if(0)printk(fmt);}while(0) +#define DBG pr_debug #endif static irqreturn_t ipi_function_handler(int irq, void *msg) @@ -39,11 +39,11 @@ static irqreturn_t ipi_function_handler(int irq, void *msg) } /** - * virqs - a per cpu array of virqs for ipi use + * ps3_ipi_virqs - a per cpu array of virqs for ipi use */ #define MSG_COUNT 4 -static DEFINE_PER_CPU(unsigned int, virqs[MSG_COUNT]); +static DEFINE_PER_CPU(unsigned int, ps3_ipi_virqs[MSG_COUNT]); static const char *names[MSG_COUNT] = { "ipi call", @@ -62,7 +62,7 @@ static void do_message_pass(int target, int msg) return; } - virq = per_cpu(virqs, target)[msg]; + virq = per_cpu(ps3_ipi_virqs, target)[msg]; result = ps3_send_event_locally(virq); if (result) @@ -94,13 +94,13 @@ static int ps3_smp_probe(void) static void __init ps3_smp_setup_cpu(int cpu) { int result; - unsigned int *virqs = per_cpu(virqs, cpu); + unsigned int *virqs = per_cpu(ps3_ipi_virqs, cpu); int i; DBG(" -> %s:%d: (%d)\n", __func__, __LINE__, cpu); /* - * Check assumptions on virqs[] indexing. If this + * Check assumptions on ps3_ipi_virqs[] indexing. If this * check fails, then a different mapping of PPC_MSG_ * to index needs to be setup. */ @@ -132,13 +132,13 @@ static void __init ps3_smp_setup_cpu(int cpu) void ps3_smp_cleanup_cpu(int cpu) { - unsigned int *virqs = per_cpu(virqs, cpu); + unsigned int *virqs = per_cpu(ps3_ipi_virqs, cpu); int i; DBG(" -> %s:%d: (%d)\n", __func__, __LINE__, cpu); for (i = 0; i < MSG_COUNT; i++) { - free_irq(virqs[i], (void*)(long)i); + /* Can't call free_irq from interrupt context. */ ps3_event_receive_port_destroy(virqs[i]); virqs[i] = NO_IRQ; } diff --git a/arch/powerpc/platforms/ps3/spu.c b/arch/powerpc/platforms/ps3/spu.c index 651437cb2c1..502d80ed982 100644 --- a/arch/powerpc/platforms/ps3/spu.c +++ b/arch/powerpc/platforms/ps3/spu.c @@ -182,15 +182,18 @@ static int __init setup_areas(struct spu *spu) { struct table {char* name; unsigned long addr; unsigned long size;}; - spu_pdata(spu)->shadow = __ioremap( - spu_pdata(spu)->shadow_addr, sizeof(struct spe_shadow), - pgprot_val(PAGE_READONLY) | _PAGE_NO_CACHE | _PAGE_GUARDED); + spu_pdata(spu)->shadow = ioremap_flags(spu_pdata(spu)->shadow_addr, + sizeof(struct spe_shadow), + pgprot_val(PAGE_READONLY) | + _PAGE_NO_CACHE); if (!spu_pdata(spu)->shadow) { pr_debug("%s:%d: ioremap shadow failed\n", __func__, __LINE__); goto fail_ioremap; } - spu->local_store = ioremap(spu->local_store_phys, LS_SIZE); + spu->local_store = (__force void *)ioremap_flags(spu->local_store_phys, + LS_SIZE, _PAGE_NO_CACHE); + if (!spu->local_store) { pr_debug("%s:%d: ioremap local_store failed\n", __func__, __LINE__); @@ -199,6 +202,7 @@ static int __init setup_areas(struct spu *spu) spu->problem = ioremap(spu->problem_phys, sizeof(struct spu_problem)); + if (!spu->problem) { pr_debug("%s:%d: ioremap problem failed\n", __func__, __LINE__); goto fail_ioremap; @@ -206,6 +210,7 @@ static int __init setup_areas(struct spu *spu) spu->priv2 = ioremap(spu_pdata(spu)->priv2_addr, sizeof(struct spu_priv2)); + if (!spu->priv2) { pr_debug("%s:%d: ioremap priv2 failed\n", __func__, __LINE__); goto fail_ioremap; @@ -400,11 +405,13 @@ static int __init ps3_enumerate_spus(int (*fn)(void *data)) } } - if (result) + if (result) { printk(KERN_WARNING "%s:%d: Error initializing spus\n", __func__, __LINE__); + return result; + } - return result; + return num_resource_id; } const struct spu_management_ops spu_management_ps3_ops = { diff --git a/arch/powerpc/platforms/ps3/system-bus.c b/arch/powerpc/platforms/ps3/system-bus.c index 6bda51027cc..4bb634a17e4 100644 --- a/arch/powerpc/platforms/ps3/system-bus.c +++ b/arch/powerpc/platforms/ps3/system-bus.c @@ -30,22 +30,228 @@ #include "platform.h" +static struct device ps3_system_bus = { + .bus_id = "ps3_system", +}; + +/* FIXME: need device usage counters! */ +struct { + struct mutex mutex; + int sb_11; /* usb 0 */ + int sb_12; /* usb 0 */ + int gpu; +} static usage_hack; + +static int ps3_is_device(struct ps3_system_bus_device *dev, + unsigned int bus_id, unsigned int dev_id) +{ + return dev->bus_id == bus_id && dev->dev_id == dev_id; +} + +static int ps3_open_hv_device_sb(struct ps3_system_bus_device *dev) +{ + int result; + + BUG_ON(!dev->bus_id); + mutex_lock(&usage_hack.mutex); + + if (ps3_is_device(dev, 1, 1)) { + usage_hack.sb_11++; + if (usage_hack.sb_11 > 1) { + result = 0; + goto done; + } + } + + if (ps3_is_device(dev, 1, 2)) { + usage_hack.sb_12++; + if (usage_hack.sb_12 > 1) { + result = 0; + goto done; + } + } + + result = lv1_open_device(dev->bus_id, dev->dev_id, 0); + + if (result) { + pr_debug("%s:%d: lv1_open_device failed: %s\n", __func__, + __LINE__, ps3_result(result)); + result = -EPERM; + } + +done: + mutex_unlock(&usage_hack.mutex); + return result; +} + +static int ps3_close_hv_device_sb(struct ps3_system_bus_device *dev) +{ + int result; + + BUG_ON(!dev->bus_id); + mutex_lock(&usage_hack.mutex); + + if (ps3_is_device(dev, 1, 1)) { + usage_hack.sb_11--; + if (usage_hack.sb_11) { + result = 0; + goto done; + } + } + + if (ps3_is_device(dev, 1, 2)) { + usage_hack.sb_12--; + if (usage_hack.sb_12) { + result = 0; + goto done; + } + } + + result = lv1_close_device(dev->bus_id, dev->dev_id); + BUG_ON(result); + +done: + mutex_unlock(&usage_hack.mutex); + return result; +} + +static int ps3_open_hv_device_gpu(struct ps3_system_bus_device *dev) +{ + int result; + + mutex_lock(&usage_hack.mutex); + + usage_hack.gpu++; + if (usage_hack.gpu > 1) { + result = 0; + goto done; + } + + result = lv1_gpu_open(0); + + if (result) { + pr_debug("%s:%d: lv1_gpu_open failed: %s\n", __func__, + __LINE__, ps3_result(result)); + result = -EPERM; + } + +done: + mutex_unlock(&usage_hack.mutex); + return result; +} + +static int ps3_close_hv_device_gpu(struct ps3_system_bus_device *dev) +{ + int result; + + mutex_lock(&usage_hack.mutex); + + usage_hack.gpu--; + if (usage_hack.gpu) { + result = 0; + goto done; + } + + result = lv1_gpu_close(); + BUG_ON(result); + +done: + mutex_unlock(&usage_hack.mutex); + return result; +} + +int ps3_open_hv_device(struct ps3_system_bus_device *dev) +{ + BUG_ON(!dev); + pr_debug("%s:%d: match_id: %u\n", __func__, __LINE__, dev->match_id); + + switch (dev->match_id) { + case PS3_MATCH_ID_EHCI: + case PS3_MATCH_ID_OHCI: + case PS3_MATCH_ID_GELIC: + case PS3_MATCH_ID_STOR_DISK: + case PS3_MATCH_ID_STOR_ROM: + case PS3_MATCH_ID_STOR_FLASH: + return ps3_open_hv_device_sb(dev); + + case PS3_MATCH_ID_SOUND: + case PS3_MATCH_ID_GRAPHICS: + return ps3_open_hv_device_gpu(dev); + + case PS3_MATCH_ID_AV_SETTINGS: + case PS3_MATCH_ID_SYSTEM_MANAGER: + pr_debug("%s:%d: unsupported match_id: %u\n", __func__, + __LINE__, dev->match_id); + pr_debug("%s:%d: bus_id: %u\n", __func__, + __LINE__, dev->bus_id); + BUG(); + return -EINVAL; + + default: + break; + } + + pr_debug("%s:%d: unknown match_id: %u\n", __func__, __LINE__, + dev->match_id); + BUG(); + return -ENODEV; +} +EXPORT_SYMBOL_GPL(ps3_open_hv_device); + +int ps3_close_hv_device(struct ps3_system_bus_device *dev) +{ + BUG_ON(!dev); + pr_debug("%s:%d: match_id: %u\n", __func__, __LINE__, dev->match_id); + + switch (dev->match_id) { + case PS3_MATCH_ID_EHCI: + case PS3_MATCH_ID_OHCI: + case PS3_MATCH_ID_GELIC: + case PS3_MATCH_ID_STOR_DISK: + case PS3_MATCH_ID_STOR_ROM: + case PS3_MATCH_ID_STOR_FLASH: + return ps3_close_hv_device_sb(dev); + + case PS3_MATCH_ID_SOUND: + case PS3_MATCH_ID_GRAPHICS: + return ps3_close_hv_device_gpu(dev); + + case PS3_MATCH_ID_AV_SETTINGS: + case PS3_MATCH_ID_SYSTEM_MANAGER: + pr_debug("%s:%d: unsupported match_id: %u\n", __func__, + __LINE__, dev->match_id); + pr_debug("%s:%d: bus_id: %u\n", __func__, + __LINE__, dev->bus_id); + BUG(); + return -EINVAL; + + default: + break; + } + + pr_debug("%s:%d: unknown match_id: %u\n", __func__, __LINE__, + dev->match_id); + BUG(); + return -ENODEV; +} +EXPORT_SYMBOL_GPL(ps3_close_hv_device); + #define dump_mmio_region(_a) _dump_mmio_region(_a, __func__, __LINE__) static void _dump_mmio_region(const struct ps3_mmio_region* r, const char* func, int line) { - pr_debug("%s:%d: dev %u:%u\n", func, line, r->did.bus_id, - r->did.dev_id); + pr_debug("%s:%d: dev %u:%u\n", func, line, r->dev->bus_id, + r->dev->dev_id); pr_debug("%s:%d: bus_addr %lxh\n", func, line, r->bus_addr); pr_debug("%s:%d: len %lxh\n", func, line, r->len); pr_debug("%s:%d: lpar_addr %lxh\n", func, line, r->lpar_addr); } -int ps3_mmio_region_create(struct ps3_mmio_region *r) +static int ps3_sb_mmio_region_create(struct ps3_mmio_region *r) { int result; - result = lv1_map_device_mmio_region(r->did.bus_id, r->did.dev_id, + result = lv1_map_device_mmio_region(r->dev->bus_id, r->dev->dev_id, r->bus_addr, r->len, r->page_size, &r->lpar_addr); if (result) { @@ -57,13 +263,26 @@ int ps3_mmio_region_create(struct ps3_mmio_region *r) dump_mmio_region(r); return result; } + +static int ps3_ioc0_mmio_region_create(struct ps3_mmio_region *r) +{ + /* device specific; do nothing currently */ + return 0; +} + +int ps3_mmio_region_create(struct ps3_mmio_region *r) +{ + return r->mmio_ops->create(r); +} EXPORT_SYMBOL_GPL(ps3_mmio_region_create); -int ps3_free_mmio_region(struct ps3_mmio_region *r) +static int ps3_sb_free_mmio_region(struct ps3_mmio_region *r) { int result; - result = lv1_unmap_device_mmio_region(r->did.bus_id, r->did.dev_id, + dump_mmio_region(r); +; + result = lv1_unmap_device_mmio_region(r->dev->bus_id, r->dev->dev_id, r->lpar_addr); if (result) @@ -73,14 +292,60 @@ int ps3_free_mmio_region(struct ps3_mmio_region *r) r->lpar_addr = 0; return result; } + +static int ps3_ioc0_free_mmio_region(struct ps3_mmio_region *r) +{ + /* device specific; do nothing currently */ + return 0; +} + + +int ps3_free_mmio_region(struct ps3_mmio_region *r) +{ + return r->mmio_ops->free(r); +} + EXPORT_SYMBOL_GPL(ps3_free_mmio_region); +static const struct ps3_mmio_region_ops ps3_mmio_sb_region_ops = { + .create = ps3_sb_mmio_region_create, + .free = ps3_sb_free_mmio_region +}; + +static const struct ps3_mmio_region_ops ps3_mmio_ioc0_region_ops = { + .create = ps3_ioc0_mmio_region_create, + .free = ps3_ioc0_free_mmio_region +}; + +int ps3_mmio_region_init(struct ps3_system_bus_device *dev, + struct ps3_mmio_region *r, unsigned long bus_addr, unsigned long len, + enum ps3_mmio_page_size page_size) +{ + r->dev = dev; + r->bus_addr = bus_addr; + r->len = len; + r->page_size = page_size; + switch (dev->dev_type) { + case PS3_DEVICE_TYPE_SB: + r->mmio_ops = &ps3_mmio_sb_region_ops; + break; + case PS3_DEVICE_TYPE_IOC0: + r->mmio_ops = &ps3_mmio_ioc0_region_ops; + break; + default: + BUG(); + return -EINVAL; + } + return 0; +} +EXPORT_SYMBOL_GPL(ps3_mmio_region_init); + static int ps3_system_bus_match(struct device *_dev, struct device_driver *_drv) { int result; - struct ps3_system_bus_driver *drv = to_ps3_system_bus_driver(_drv); - struct ps3_system_bus_device *dev = to_ps3_system_bus_device(_dev); + struct ps3_system_bus_driver *drv = ps3_drv_to_system_bus_drv(_drv); + struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); result = dev->match_id == drv->match_id; @@ -92,32 +357,14 @@ static int ps3_system_bus_match(struct device *_dev, static int ps3_system_bus_probe(struct device *_dev) { - int result; - struct ps3_system_bus_device *dev = to_ps3_system_bus_device(_dev); - struct ps3_system_bus_driver *drv = - to_ps3_system_bus_driver(_dev->driver); - - result = lv1_open_device(dev->did.bus_id, dev->did.dev_id, 0); - - if (result) { - pr_debug("%s:%d: lv1_open_device failed (%d)\n", - __func__, __LINE__, result); - result = -EACCES; - goto clean_none; - } - - if (dev->d_region->did.bus_id) { - result = ps3_dma_region_create(dev->d_region); + int result = 0; + struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); + struct ps3_system_bus_driver *drv; - if (result) { - pr_debug("%s:%d: ps3_dma_region_create failed (%d)\n", - __func__, __LINE__, result); - BUG_ON("check region type"); - result = -EINVAL; - goto clean_device; - } - } + BUG_ON(!dev); + pr_info(" -> %s:%d: %s\n", __func__, __LINE__, _dev->bus_id); + drv = ps3_system_bus_dev_to_system_bus_drv(dev); BUG_ON(!drv); if (drv->probe) @@ -126,56 +373,127 @@ static int ps3_system_bus_probe(struct device *_dev) pr_info("%s:%d: %s no probe method\n", __func__, __LINE__, dev->core.bus_id); - if (result) { - pr_debug("%s:%d: drv->probe failed\n", __func__, __LINE__); - goto clean_dma; - } - - return result; - -clean_dma: - ps3_dma_region_free(dev->d_region); -clean_device: - lv1_close_device(dev->did.bus_id, dev->did.dev_id); -clean_none: + pr_info(" <- %s:%d: %s\n", __func__, __LINE__, dev->core.bus_id); return result; } static int ps3_system_bus_remove(struct device *_dev) { - struct ps3_system_bus_device *dev = to_ps3_system_bus_device(_dev); - struct ps3_system_bus_driver *drv = - to_ps3_system_bus_driver(_dev->driver); + int result = 0; + struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); + struct ps3_system_bus_driver *drv; + + BUG_ON(!dev); + pr_info(" -> %s:%d: %s\n", __func__, __LINE__, _dev->bus_id); + + drv = ps3_system_bus_dev_to_system_bus_drv(dev); + BUG_ON(!drv); if (drv->remove) - drv->remove(dev); + result = drv->remove(dev); else - pr_info("%s:%d: %s no remove method\n", __func__, __LINE__, - dev->core.bus_id); + dev_dbg(&dev->core, "%s:%d %s: no remove method\n", + __func__, __LINE__, drv->core.name); + + pr_info(" <- %s:%d: %s\n", __func__, __LINE__, dev->core.bus_id); + return result; +} + +static void ps3_system_bus_shutdown(struct device *_dev) +{ + struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); + struct ps3_system_bus_driver *drv; + + BUG_ON(!dev); + + dev_dbg(&dev->core, " -> %s:%d: match_id %d\n", __func__, __LINE__, + dev->match_id); + + if (!dev->core.driver) { + dev_dbg(&dev->core, "%s:%d: no driver bound\n", __func__, + __LINE__); + return; + } + + drv = ps3_system_bus_dev_to_system_bus_drv(dev); + + BUG_ON(!drv); + + dev_dbg(&dev->core, "%s:%d: %s -> %s\n", __func__, __LINE__, + dev->core.bus_id, drv->core.name); + + if (drv->shutdown) + drv->shutdown(dev); + else if (drv->remove) { + dev_dbg(&dev->core, "%s:%d %s: no shutdown, calling remove\n", + __func__, __LINE__, drv->core.name); + drv->remove(dev); + } else { + dev_dbg(&dev->core, "%s:%d %s: no shutdown method\n", + __func__, __LINE__, drv->core.name); + BUG(); + } + + dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__); +} + +static int ps3_system_bus_uevent(struct device *_dev, char **envp, + int num_envp, char *buffer, int buffer_size) +{ + struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); + int i = 0, length = 0; - ps3_dma_region_free(dev->d_region); - ps3_free_mmio_region(dev->m_region); - lv1_close_device(dev->did.bus_id, dev->did.dev_id); + if (add_uevent_var(envp, num_envp, &i, buffer, buffer_size, + &length, "MODALIAS=ps3:%d", + dev->match_id)) + return -ENOMEM; + envp[i] = NULL; return 0; } +static ssize_t modalias_show(struct device *_dev, struct device_attribute *a, + char *buf) +{ + struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); + int len = snprintf(buf, PAGE_SIZE, "ps3:%d\n", dev->match_id); + + return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len; +} + +static struct device_attribute ps3_system_bus_dev_attrs[] = { + __ATTR_RO(modalias), + __ATTR_NULL, +}; + struct bus_type ps3_system_bus_type = { .name = "ps3_system_bus", .match = ps3_system_bus_match, + .uevent = ps3_system_bus_uevent, .probe = ps3_system_bus_probe, .remove = ps3_system_bus_remove, + .shutdown = ps3_system_bus_shutdown, + .dev_attrs = ps3_system_bus_dev_attrs, }; -int __init ps3_system_bus_init(void) +static int __init ps3_system_bus_init(void) { int result; if (!firmware_has_feature(FW_FEATURE_PS3_LV1)) return -ENODEV; + pr_debug(" -> %s:%d\n", __func__, __LINE__); + + mutex_init(&usage_hack.mutex); + + result = device_register(&ps3_system_bus); + BUG_ON(result); + result = bus_register(&ps3_system_bus_type); BUG_ON(result); + + pr_debug(" <- %s:%d\n", __func__, __LINE__); return result; } @@ -185,16 +503,13 @@ core_initcall(ps3_system_bus_init); * Returns the virtual address of the buffer and sets dma_handle * to the dma address (mapping) of the first page. */ - static void * ps3_alloc_coherent(struct device *_dev, size_t size, - dma_addr_t *dma_handle, gfp_t flag) + dma_addr_t *dma_handle, gfp_t flag) { int result; - struct ps3_system_bus_device *dev = to_ps3_system_bus_device(_dev); + struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); unsigned long virt_addr; - BUG_ON(!dev->d_region->bus_addr); - flag &= ~(__GFP_DMA | __GFP_HIGHMEM); flag |= __GFP_ZERO; @@ -205,7 +520,8 @@ static void * ps3_alloc_coherent(struct device *_dev, size_t size, goto clean_none; } - result = ps3_dma_map(dev->d_region, virt_addr, size, dma_handle); + result = ps3_dma_map(dev->d_region, virt_addr, size, dma_handle, + IOPTE_PP_W | IOPTE_PP_R | IOPTE_SO_RW | IOPTE_M); if (result) { pr_debug("%s:%d: ps3_dma_map failed (%d)\n", @@ -226,7 +542,7 @@ clean_none: static void ps3_free_coherent(struct device *_dev, size_t size, void *vaddr, dma_addr_t dma_handle) { - struct ps3_system_bus_device *dev = to_ps3_system_bus_device(_dev); + struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); ps3_dma_unmap(dev->d_region, dma_handle, size); free_pages((unsigned long)vaddr, get_order(size)); @@ -239,15 +555,16 @@ static void ps3_free_coherent(struct device *_dev, size_t size, void *vaddr, * byte within the page as vaddr. */ -static dma_addr_t ps3_map_single(struct device *_dev, void *ptr, size_t size, +static dma_addr_t ps3_sb_map_single(struct device *_dev, void *ptr, size_t size, enum dma_data_direction direction) { - struct ps3_system_bus_device *dev = to_ps3_system_bus_device(_dev); + struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); int result; unsigned long bus_addr; result = ps3_dma_map(dev->d_region, (unsigned long)ptr, size, - &bus_addr); + &bus_addr, + IOPTE_PP_R | IOPTE_PP_W | IOPTE_SO_RW | IOPTE_M); if (result) { pr_debug("%s:%d: ps3_dma_map failed (%d)\n", @@ -257,10 +574,44 @@ static dma_addr_t ps3_map_single(struct device *_dev, void *ptr, size_t size, return bus_addr; } +static dma_addr_t ps3_ioc0_map_single(struct device *_dev, void *ptr, + size_t size, + enum dma_data_direction direction) +{ + struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); + int result; + unsigned long bus_addr; + u64 iopte_flag; + + iopte_flag = IOPTE_M; + switch (direction) { + case DMA_BIDIRECTIONAL: + iopte_flag |= IOPTE_PP_R | IOPTE_PP_W | IOPTE_SO_RW; + break; + case DMA_TO_DEVICE: + iopte_flag |= IOPTE_PP_R | IOPTE_SO_R; + break; + case DMA_FROM_DEVICE: + iopte_flag |= IOPTE_PP_W | IOPTE_SO_RW; + break; + default: + /* not happned */ + BUG(); + }; + result = ps3_dma_map(dev->d_region, (unsigned long)ptr, size, + &bus_addr, iopte_flag); + + if (result) { + pr_debug("%s:%d: ps3_dma_map failed (%d)\n", + __func__, __LINE__, result); + } + return bus_addr; +} + static void ps3_unmap_single(struct device *_dev, dma_addr_t dma_addr, size_t size, enum dma_data_direction direction) { - struct ps3_system_bus_device *dev = to_ps3_system_bus_device(_dev); + struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); int result; result = ps3_dma_unmap(dev->d_region, dma_addr, size); @@ -271,20 +622,20 @@ static void ps3_unmap_single(struct device *_dev, dma_addr_t dma_addr, } } -static int ps3_map_sg(struct device *_dev, struct scatterlist *sg, int nents, +static int ps3_sb_map_sg(struct device *_dev, struct scatterlist *sg, int nents, enum dma_data_direction direction) { #if defined(CONFIG_PS3_DYNAMIC_DMA) BUG_ON("do"); return -EPERM; #else - struct ps3_system_bus_device *dev = to_ps3_system_bus_device(_dev); + struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); int i; for (i = 0; i < nents; i++, sg++) { int result = ps3_dma_map(dev->d_region, page_to_phys(sg->page) + sg->offset, sg->length, - &sg->dma_address); + &sg->dma_address, 0); if (result) { pr_debug("%s:%d: ps3_dma_map failed (%d)\n", @@ -299,7 +650,15 @@ static int ps3_map_sg(struct device *_dev, struct scatterlist *sg, int nents, #endif } -static void ps3_unmap_sg(struct device *_dev, struct scatterlist *sg, +static int ps3_ioc0_map_sg(struct device *_dev, struct scatterlist *sg, + int nents, + enum dma_data_direction direction) +{ + BUG(); + return 0; +} + +static void ps3_sb_unmap_sg(struct device *_dev, struct scatterlist *sg, int nents, enum dma_data_direction direction) { #if defined(CONFIG_PS3_DYNAMIC_DMA) @@ -307,18 +666,34 @@ static void ps3_unmap_sg(struct device *_dev, struct scatterlist *sg, #endif } +static void ps3_ioc0_unmap_sg(struct device *_dev, struct scatterlist *sg, + int nents, enum dma_data_direction direction) +{ + BUG(); +} + static int ps3_dma_supported(struct device *_dev, u64 mask) { return mask >= DMA_32BIT_MASK; } -static struct dma_mapping_ops ps3_dma_ops = { +static struct dma_mapping_ops ps3_sb_dma_ops = { .alloc_coherent = ps3_alloc_coherent, .free_coherent = ps3_free_coherent, - .map_single = ps3_map_single, + .map_single = ps3_sb_map_single, .unmap_single = ps3_unmap_single, - .map_sg = ps3_map_sg, - .unmap_sg = ps3_unmap_sg, + .map_sg = ps3_sb_map_sg, + .unmap_sg = ps3_sb_unmap_sg, + .dma_supported = ps3_dma_supported +}; + +static struct dma_mapping_ops ps3_ioc0_dma_ops = { + .alloc_coherent = ps3_alloc_coherent, + .free_coherent = ps3_free_coherent, + .map_single = ps3_ioc0_map_single, + .unmap_single = ps3_unmap_single, + .map_sg = ps3_ioc0_map_sg, + .unmap_sg = ps3_ioc0_unmap_sg, .dma_supported = ps3_dma_supported }; @@ -328,7 +703,7 @@ static struct dma_mapping_ops ps3_dma_ops = { static void ps3_system_bus_release_device(struct device *_dev) { - struct ps3_system_bus_device *dev = to_ps3_system_bus_device(_dev); + struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); kfree(dev); } @@ -343,19 +718,38 @@ static void ps3_system_bus_release_device(struct device *_dev) int ps3_system_bus_device_register(struct ps3_system_bus_device *dev) { int result; - static unsigned int dev_count = 1; + static unsigned int dev_ioc0_count; + static unsigned int dev_sb_count; + static unsigned int dev_vuart_count; - dev->core.parent = NULL; + if (!dev->core.parent) + dev->core.parent = &ps3_system_bus; dev->core.bus = &ps3_system_bus_type; dev->core.release = ps3_system_bus_release_device; + switch (dev->dev_type) { + case PS3_DEVICE_TYPE_IOC0: + dev->core.archdata.dma_ops = &ps3_ioc0_dma_ops; + snprintf(dev->core.bus_id, sizeof(dev->core.bus_id), + "ioc0_%02x", ++dev_ioc0_count); + break; + case PS3_DEVICE_TYPE_SB: + dev->core.archdata.dma_ops = &ps3_sb_dma_ops; + snprintf(dev->core.bus_id, sizeof(dev->core.bus_id), + "sb_%02x", ++dev_sb_count); + + break; + case PS3_DEVICE_TYPE_VUART: + snprintf(dev->core.bus_id, sizeof(dev->core.bus_id), + "vuart_%02x", ++dev_vuart_count); + break; + default: + BUG(); + }; + dev->core.archdata.of_node = NULL; - dev->core.archdata.dma_ops = &ps3_dma_ops; dev->core.archdata.numa_node = 0; - snprintf(dev->core.bus_id, sizeof(dev->core.bus_id), "sb_%02x", - dev_count++); - pr_debug("%s:%d add %s\n", __func__, __LINE__, dev->core.bus_id); result = device_register(&dev->core); @@ -368,9 +762,15 @@ int ps3_system_bus_driver_register(struct ps3_system_bus_driver *drv) { int result; + pr_debug(" -> %s:%d: %s\n", __func__, __LINE__, drv->core.name); + + if (!firmware_has_feature(FW_FEATURE_PS3_LV1)) + return -ENODEV; + drv->core.bus = &ps3_system_bus_type; result = driver_register(&drv->core); + pr_debug(" <- %s:%d: %s\n", __func__, __LINE__, drv->core.name); return result; } @@ -378,7 +778,9 @@ EXPORT_SYMBOL_GPL(ps3_system_bus_driver_register); void ps3_system_bus_driver_unregister(struct ps3_system_bus_driver *drv) { + pr_debug(" -> %s:%d: %s\n", __func__, __LINE__, drv->core.name); driver_unregister(&drv->core); + pr_debug(" <- %s:%d: %s\n", __func__, __LINE__, drv->core.name); } EXPORT_SYMBOL_GPL(ps3_system_bus_driver_unregister); diff --git a/arch/powerpc/platforms/ps3/time.c b/arch/powerpc/platforms/ps3/time.c index 1bae8b19b36..802a9ccacb5 100644 --- a/arch/powerpc/platforms/ps3/time.c +++ b/arch/powerpc/platforms/ps3/time.c @@ -39,7 +39,7 @@ static void _dump_tm(const struct rtc_time *tm, const char* func, int line) } #define dump_time(_a) _dump_time(_a, __func__, __LINE__) -static void __attribute__ ((unused)) _dump_time(int time, const char* func, +static void __maybe_unused _dump_time(int time, const char *func, int line) { struct rtc_time tm; diff --git a/arch/powerpc/platforms/pseries/Makefile b/arch/powerpc/platforms/pseries/Makefile index ae1fc92dc1c..992ba6753cf 100644 --- a/arch/powerpc/platforms/pseries/Makefile +++ b/arch/powerpc/platforms/pseries/Makefile @@ -8,7 +8,7 @@ obj-y := lpar.o hvCall.o nvram.o reconfig.o \ obj-$(CONFIG_SMP) += smp.o 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_EEH) += eeh.o eeh_cache.o eeh_driver.o eeh_event.o eeh_sysfs.o obj-$(CONFIG_KEXEC) += kexec.o obj-$(CONFIG_PCI) += pci.o pci_dlpar.o obj-$(CONFIG_PCI_MSI) += msi.o diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c index 5f3e6d8659f..b8770395013 100644 --- a/arch/powerpc/platforms/pseries/eeh.c +++ b/arch/powerpc/platforms/pseries/eeh.c @@ -1,6 +1,8 @@ /* * eeh.c - * Copyright (C) 2001 Dave Engebretsen & Todd Inglett IBM Corporation + * Copyright IBM Corporation 2001, 2005, 2006 + * Copyright Dave Engebretsen & Todd Inglett 2001 + * Copyright Linas Vepstas 2005, 2006 * * 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 @@ -15,6 +17,8 @@ * 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 + * + * Please address comments and feedback to Linas Vepstas <linas@austin.ibm.com> */ #include <linux/delay.h> @@ -117,7 +121,6 @@ static unsigned long no_cfg_addr; static unsigned long ignored_check; static unsigned long total_mmio_ffs; static unsigned long false_positives; -static unsigned long ignored_failures; static unsigned long slot_resets; #define IS_BRIDGE(class_code) (((class_code)<<16) == PCI_BASE_CLASS_BRIDGE) @@ -505,6 +508,7 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev) printk(KERN_WARNING "EEH: read_slot_reset_state() failed; rc=%d dn=%s\n", ret, dn->full_name); false_positives++; + pdn->eeh_false_positives ++; rc = 0; goto dn_unlock; } @@ -513,6 +517,7 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev) * they are empty when they don't have children. */ if ((rets[0] == 5) && (dn->child == NULL)) { false_positives++; + pdn->eeh_false_positives ++; rc = 0; goto dn_unlock; } @@ -522,6 +527,7 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev) printk(KERN_WARNING "EEH: event on unsupported device, rc=%d dn=%s\n", ret, dn->full_name); false_positives++; + pdn->eeh_false_positives ++; rc = 0; goto dn_unlock; } @@ -529,6 +535,7 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev) /* If not the kind of error we know about, punt. */ if (rets[0] != 1 && rets[0] != 2 && rets[0] != 4 && rets[0] != 5) { false_positives++; + pdn->eeh_false_positives ++; rc = 0; goto dn_unlock; } @@ -921,6 +928,7 @@ static void *early_enable_eeh(struct device_node *dn, void *data) pdn->eeh_mode = 0; pdn->eeh_check_count = 0; pdn->eeh_freeze_count = 0; + pdn->eeh_false_positives = 0; if (status && strcmp(status, "ok") != 0) return NULL; /* ignore devices with bad status */ @@ -1139,7 +1147,8 @@ static void eeh_add_device_late(struct pci_dev *dev) pdn = PCI_DN(dn); pdn->pcidev = dev; - pci_addr_cache_insert_device (dev); + pci_addr_cache_insert_device(dev); + eeh_sysfs_add_device(dev); } void eeh_add_device_tree_late(struct pci_bus *bus) @@ -1178,6 +1187,7 @@ static void eeh_remove_device(struct pci_dev *dev) printk(KERN_DEBUG "EEH: remove device %s\n", pci_name(dev)); #endif pci_addr_cache_remove_device(dev); + eeh_sysfs_remove_device(dev); dn = pci_device_to_OF_node(dev); if (PCI_DN(dn)->pcidev) { @@ -1214,11 +1224,10 @@ static int proc_eeh_show(struct seq_file *m, void *v) "check not wanted=%ld\n" "eeh_total_mmio_ffs=%ld\n" "eeh_false_positives=%ld\n" - "eeh_ignored_failures=%ld\n" "eeh_slot_resets=%ld\n", no_device, no_dn, no_cfg_addr, ignored_check, total_mmio_ffs, - false_positives, ignored_failures, + false_positives, slot_resets); } diff --git a/arch/powerpc/platforms/pseries/eeh_cache.c b/arch/powerpc/platforms/pseries/eeh_cache.c index f2bae04424f..e49c815eae2 100644 --- a/arch/powerpc/platforms/pseries/eeh_cache.c +++ b/arch/powerpc/platforms/pseries/eeh_cache.c @@ -2,7 +2,8 @@ * eeh_cache.c * PCI address cache; allows the lookup of PCI devices based on I/O address * - * Copyright (C) 2004 Linas Vepstas <linas@austin.ibm.com> IBM Corporation + * Copyright IBM Corporation 2004 + * Copyright Linas Vepstas <linas@austin.ibm.com> 2004 * * 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 @@ -295,6 +296,8 @@ void __init pci_addr_cache_build(void) continue; pci_dev_get (dev); /* matching put is in eeh_remove_device() */ PCI_DN(dn)->pcidev = dev; + + eeh_sysfs_add_device(dev); } #ifdef DEBUG diff --git a/arch/powerpc/platforms/pseries/eeh_driver.c b/arch/powerpc/platforms/pseries/eeh_driver.c index 161a5844ab6..15e015ef686 100644 --- a/arch/powerpc/platforms/pseries/eeh_driver.c +++ b/arch/powerpc/platforms/pseries/eeh_driver.c @@ -1,6 +1,7 @@ /* * PCI Error Recovery Driver for RPA-compliant PPC64 platform. - * Copyright (C) 2004, 2005 Linas Vepstas <linas@linas.org> + * Copyright IBM Corp. 2004 2005 + * Copyright Linas Vepstas <linas@linas.org> 2004, 2005 * * All rights reserved. * @@ -19,8 +20,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * - * Send feedback to <linas@us.ibm.com> - * + * Send comments and feedback to Linas Vepstas <linas@austin.ibm.com> */ #include <linux/delay.h> #include <linux/interrupt.h> diff --git a/arch/powerpc/platforms/pseries/eeh_sysfs.c b/arch/powerpc/platforms/pseries/eeh_sysfs.c new file mode 100644 index 00000000000..15e13b56890 --- /dev/null +++ b/arch/powerpc/platforms/pseries/eeh_sysfs.c @@ -0,0 +1,87 @@ +/* + * Sysfs entries for PCI Error Recovery for PAPR-compliant platform. + * Copyright IBM Corporation 2007 + * Copyright Linas Vepstas <linas@austin.ibm.com> 2007 + * + * All rights reserved. + * + * 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. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Send comments and feedback to Linas Vepstas <linas@austin.ibm.com> + */ +#include <linux/pci.h> +#include <asm/ppc-pci.h> +#include <asm/pci-bridge.h> +#include <linux/kobject.h> + +/** + * EEH_SHOW_ATTR -- create sysfs entry for eeh statistic + * @_name: name of file in sysfs directory + * @_memb: name of member in struct pci_dn to access + * @_format: printf format for display + * + * All of the attributes look very similar, so just + * auto-gen a cut-n-paste routine to display them. + */ +#define EEH_SHOW_ATTR(_name,_memb,_format) \ +static ssize_t eeh_show_##_name(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct pci_dev *pdev = to_pci_dev(dev); \ + struct device_node *dn = pci_device_to_OF_node(pdev); \ + struct pci_dn *pdn; \ + \ + if (!dn || PCI_DN(dn) == NULL) \ + return 0; \ + \ + pdn = PCI_DN(dn); \ + return sprintf(buf, _format "\n", pdn->_memb); \ +} \ +static DEVICE_ATTR(_name, S_IRUGO, eeh_show_##_name, NULL); + + +EEH_SHOW_ATTR(eeh_mode, eeh_mode, "0x%x"); +EEH_SHOW_ATTR(eeh_config_addr, eeh_config_addr, "0x%x"); +EEH_SHOW_ATTR(eeh_pe_config_addr, eeh_pe_config_addr, "0x%x"); +EEH_SHOW_ATTR(eeh_check_count, eeh_check_count, "%d"); +EEH_SHOW_ATTR(eeh_freeze_count, eeh_freeze_count, "%d"); +EEH_SHOW_ATTR(eeh_false_positives, eeh_false_positives, "%d"); + +void eeh_sysfs_add_device(struct pci_dev *pdev) +{ + int rc=0; + + rc += device_create_file(&pdev->dev, &dev_attr_eeh_mode); + rc += device_create_file(&pdev->dev, &dev_attr_eeh_config_addr); + rc += device_create_file(&pdev->dev, &dev_attr_eeh_pe_config_addr); + rc += device_create_file(&pdev->dev, &dev_attr_eeh_check_count); + rc += device_create_file(&pdev->dev, &dev_attr_eeh_false_positives); + rc += device_create_file(&pdev->dev, &dev_attr_eeh_freeze_count); + + if (rc) + printk(KERN_WARNING "EEH: Unable to create sysfs entries\n"); +} + +void eeh_sysfs_remove_device(struct pci_dev *pdev) +{ + device_remove_file(&pdev->dev, &dev_attr_eeh_mode); + device_remove_file(&pdev->dev, &dev_attr_eeh_config_addr); + device_remove_file(&pdev->dev, &dev_attr_eeh_pe_config_addr); + device_remove_file(&pdev->dev, &dev_attr_eeh_check_count); + device_remove_file(&pdev->dev, &dev_attr_eeh_false_positives); + device_remove_file(&pdev->dev, &dev_attr_eeh_freeze_count); +} + diff --git a/arch/powerpc/platforms/pseries/lpar.c b/arch/powerpc/platforms/pseries/lpar.c index 362dfbc260a..8cc6eeeaae2 100644 --- a/arch/powerpc/platforms/pseries/lpar.c +++ b/arch/powerpc/platforms/pseries/lpar.c @@ -373,12 +373,23 @@ static void pSeries_lpar_hptab_clear(void) { unsigned long size_bytes = 1UL << ppc64_pft_size; unsigned long hpte_count = size_bytes >> 4; - unsigned long dummy1, dummy2; + unsigned long dummy1, dummy2, dword0; + long lpar_rc; int i; /* TODO: Use bulk call */ - for (i = 0; i < hpte_count; i++) - plpar_pte_remove_raw(0, i, 0, &dummy1, &dummy2); + for (i = 0; i < hpte_count; i++) { + /* dont remove HPTEs with VRMA mappings */ + lpar_rc = plpar_pte_remove_raw(H_ANDCOND, i, HPTE_V_1TB_SEG, + &dummy1, &dummy2); + if (lpar_rc == H_NOT_FOUND) { + lpar_rc = plpar_pte_read_raw(0, i, &dword0, &dummy1); + if (!lpar_rc && ((dword0 & HPTE_V_VRMA_MASK) + != HPTE_V_VRMA_MASK)) + /* Can be hpte for 1TB Seg. So remove it */ + plpar_pte_remove_raw(0, i, 0, &dummy1, &dummy2); + } + } } /* diff --git a/arch/powerpc/platforms/pseries/pci_dlpar.c b/arch/powerpc/platforms/pseries/pci_dlpar.c index ffaf6c5c517..47f0e0857f0 100644 --- a/arch/powerpc/platforms/pseries/pci_dlpar.c +++ b/arch/powerpc/platforms/pseries/pci_dlpar.c @@ -110,8 +110,6 @@ pcibios_fixup_new_pci_devices(struct pci_bus *bus, int fix_bus) } } } - - eeh_add_device_tree_late(bus); } EXPORT_SYMBOL_GPL(pcibios_fixup_new_pci_devices); @@ -139,6 +137,8 @@ pcibios_pci_config_bridge(struct pci_dev *dev) /* Make the discovered devices available */ pci_bus_add_devices(child_bus); + + eeh_add_device_tree_late(child_bus); return 0; } @@ -171,6 +171,7 @@ pcibios_add_pci_devices(struct pci_bus * bus) if (!list_empty(&bus->devices)) { pcibios_fixup_new_pci_devices(bus, 0); pci_bus_add_devices(bus); + eeh_add_device_tree_late(bus); } } else if (mode == PCI_PROBE_NORMAL) { /* use legacy probe */ @@ -179,6 +180,7 @@ pcibios_add_pci_devices(struct pci_bus * bus) if (num) { pcibios_fixup_new_pci_devices(bus, 1); pci_bus_add_devices(bus); + eeh_add_device_tree_late(bus); } list_for_each_entry(dev, &bus->devices, bus_list) @@ -200,8 +202,6 @@ struct pci_controller * __devinit init_phb_dynamic(struct device_node *dn) rtas_setup_phb(phb); pci_process_bridge_OF_ranges(phb, dn, 0); - pci_setup_phb_io_dynamic(phb, primary); - pci_devs_phb_init_dynamic(phb); if (dn->child) @@ -210,6 +210,7 @@ struct pci_controller * __devinit init_phb_dynamic(struct device_node *dn) scan_phb(phb); pcibios_fixup_new_pci_devices(phb->bus, 0); pci_bus_add_devices(phb->bus); + eeh_add_device_tree_late(phb->bus); return phb; } diff --git a/arch/powerpc/platforms/pseries/plpar_wrappers.h b/arch/powerpc/platforms/pseries/plpar_wrappers.h index 2e4d10c9eea..d003c80fa31 100644 --- a/arch/powerpc/platforms/pseries/plpar_wrappers.h +++ b/arch/powerpc/platforms/pseries/plpar_wrappers.h @@ -108,6 +108,21 @@ static inline long plpar_pte_read(unsigned long flags, unsigned long ptex, return rc; } +/* plpar_pte_read_raw can be called in real mode. It calls plpar_hcall_raw */ +static inline long plpar_pte_read_raw(unsigned long flags, unsigned long ptex, + unsigned long *old_pteh_ret, unsigned long *old_ptel_ret) +{ + long rc; + unsigned long retbuf[PLPAR_HCALL_BUFSIZE]; + + rc = plpar_hcall_raw(H_READ, retbuf, flags, ptex); + + *old_pteh_ret = retbuf[0]; + *old_ptel_ret = retbuf[1]; + + return rc; +} + static inline long plpar_pte_protect(unsigned long flags, unsigned long ptex, unsigned long avpn) { diff --git a/arch/powerpc/platforms/pseries/pseries.h b/arch/powerpc/platforms/pseries/pseries.h index 2729d559fd9..61e19f78b92 100644 --- a/arch/powerpc/platforms/pseries/pseries.h +++ b/arch/powerpc/platforms/pseries/pseries.h @@ -33,6 +33,8 @@ static inline void setup_kexec_cpu_down_xics(void) { } static inline void setup_kexec_cpu_down_mpic(void) { } #endif +extern void pSeries_final_fixup(void); + /* Poweron flag used for enabling auto ups restart */ extern unsigned long rtas_poweron_auto; diff --git a/arch/powerpc/platforms/pseries/reconfig.c b/arch/powerpc/platforms/pseries/reconfig.c index 5aa97aff339..c02f8742c54 100644 --- a/arch/powerpc/platforms/pseries/reconfig.c +++ b/arch/powerpc/platforms/pseries/reconfig.c @@ -123,7 +123,7 @@ static int pSeries_reconfig_add_node(const char *path, struct property *proplist strcpy(np->full_name, path); np->properties = proplist; - OF_MARK_DYNAMIC(np); + of_node_set_flag(np, OF_DYNAMIC); kref_init(&np->kref); np->parent = derive_parent(path); diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c index a031d99becb..59e69f085cb 100644 --- a/arch/powerpc/platforms/pseries/setup.c +++ b/arch/powerpc/platforms/pseries/setup.c @@ -399,6 +399,7 @@ static void pseries_dedicated_idle_sleep(void) * a good time to find other work to dispatch. */ get_lppaca()->idle = 1; + get_lppaca()->donate_dedicated_cpu = 1; /* * We come in with interrupts disabled, and need_resched() @@ -431,6 +432,7 @@ static void pseries_dedicated_idle_sleep(void) out: HMT_medium(); + get_lppaca()->donate_dedicated_cpu = 0; get_lppaca()->idle = 0; } diff --git a/arch/powerpc/platforms/pseries/xics.c b/arch/powerpc/platforms/pseries/xics.c index f1df942072b..5bd90a7eb76 100644 --- a/arch/powerpc/platforms/pseries/xics.c +++ b/arch/powerpc/platforms/pseries/xics.c @@ -156,9 +156,9 @@ static inline void lpar_qirr_info(int n_cpu , u8 value) #ifdef CONFIG_SMP -static int get_irq_server(unsigned int virq) +static int get_irq_server(unsigned int virq, unsigned int strict_check) { - unsigned int server; + int server; /* For the moment only implement delivery to all cpus or one cpu */ cpumask_t cpumask = irq_desc[virq].affinity; cpumask_t tmp = CPU_MASK_NONE; @@ -166,22 +166,25 @@ static int get_irq_server(unsigned int virq) if (!distribute_irqs) return default_server; - if (cpus_equal(cpumask, CPU_MASK_ALL)) { - server = default_distrib_server; - } else { + if (!cpus_equal(cpumask, CPU_MASK_ALL)) { cpus_and(tmp, cpu_online_map, cpumask); - if (cpus_empty(tmp)) - server = default_distrib_server; - else - server = get_hard_smp_processor_id(first_cpu(tmp)); + server = first_cpu(tmp); + + if (server < NR_CPUS) + return get_hard_smp_processor_id(server); + + if (strict_check) + return -1; } - return server; + if (cpus_equal(cpu_online_map, cpu_present_map)) + return default_distrib_server; + return default_server; } #else -static int get_irq_server(unsigned int virq) +static int get_irq_server(unsigned int virq, unsigned int strict_check) { return default_server; } @@ -192,7 +195,7 @@ static void xics_unmask_irq(unsigned int virq) { unsigned int irq; int call_status; - unsigned int server; + int server; pr_debug("xics: unmask virq %d\n", virq); @@ -201,7 +204,7 @@ static void xics_unmask_irq(unsigned int virq) if (irq == XICS_IPI || irq == XICS_IRQ_SPURIOUS) return; - server = get_irq_server(virq); + server = get_irq_server(virq, 0); call_status = rtas_call(ibm_set_xive, 3, 1, NULL, irq, server, DEFAULT_PRIORITY); @@ -398,8 +401,7 @@ static void xics_set_affinity(unsigned int virq, cpumask_t cpumask) unsigned int irq; int status; int xics_status[2]; - unsigned long newmask; - cpumask_t tmp = CPU_MASK_NONE; + int irq_server; irq = (unsigned int)irq_map[virq].hwirq; if (irq == XICS_IPI || irq == XICS_IRQ_SPURIOUS) @@ -413,18 +415,21 @@ static void xics_set_affinity(unsigned int virq, cpumask_t cpumask) return; } - /* For the moment only implement delivery to all cpus or one cpu */ - if (cpus_equal(cpumask, CPU_MASK_ALL)) { - newmask = default_distrib_server; - } else { - cpus_and(tmp, cpu_online_map, cpumask); - if (cpus_empty(tmp)) - return; - newmask = get_hard_smp_processor_id(first_cpu(tmp)); + /* + * For the moment only implement delivery to all cpus or one cpu. + * Get current irq_server for the given irq + */ + irq_server = get_irq_server(irq, 1); + if (irq_server == -1) { + char cpulist[128]; + cpumask_scnprintf(cpulist, sizeof(cpulist), cpumask); + printk(KERN_WARNING "xics_set_affinity: No online cpus in " + "the mask %s for irq %d\n", cpulist, virq); + return; } status = rtas_call(ibm_set_xive, 3, 1, NULL, - irq, newmask, xics_status[1]); + irq, irq_server, xics_status[1]); if (status) { printk(KERN_ERR "xics_set_affinity: irq=%u ibm,set-xive " diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile index c3ce0bd12c0..f65078c3d3b 100644 --- a/arch/powerpc/sysdev/Makefile +++ b/arch/powerpc/sysdev/Makefile @@ -5,7 +5,6 @@ endif mpic-msi-obj-$(CONFIG_PCI_MSI) += mpic_msi.o mpic_u3msi.o obj-$(CONFIG_MPIC) += mpic.o $(mpic-msi-obj-y) -obj-$(CONFIG_PPC_INDIRECT_PCI) += indirect_pci.o obj-$(CONFIG_PPC_MPC106) += grackle.o obj-$(CONFIG_PPC_DCR) += dcr.o obj-$(CONFIG_PPC_DCR_NATIVE) += dcr-low.o @@ -13,16 +12,19 @@ obj-$(CONFIG_PPC_PMI) += pmi.o obj-$(CONFIG_U3_DART) += dart_iommu.o obj-$(CONFIG_MMIO_NVRAM) += mmio_nvram.o obj-$(CONFIG_FSL_SOC) += fsl_soc.o -obj-$(CONFIG_FSL_PCIE) += fsl_pcie.o obj-$(CONFIG_TSI108_BRIDGE) += tsi108_pci.o tsi108_dev.o obj-$(CONFIG_QUICC_ENGINE) += qe_lib/ mv64x60-$(CONFIG_PCI) += mv64x60_pci.o obj-$(CONFIG_MV64X60) += $(mv64x60-y) mv64x60_pic.o mv64x60_dev.o +obj-$(CONFIG_RTC_DRV_CMOS) += rtc_cmos_setup.o # contains only the suspend handler for time +ifeq ($(CONFIG_RTC_CLASS),) obj-$(CONFIG_PM) += timer.o +endif ifeq ($(CONFIG_PPC_MERGE),y) +obj-$(CONFIG_PPC_INDIRECT_PCI) += indirect_pci.o obj-$(CONFIG_PPC_I8259) += i8259.o obj-$(CONFIG_PPC_83xx) += ipic.o obj-$(CONFIG_4xx) += uic.o diff --git a/arch/powerpc/sysdev/fsl_pcie.c b/arch/powerpc/sysdev/fsl_pcie.c deleted file mode 100644 index 041c07e8b66..00000000000 --- a/arch/powerpc/sysdev/fsl_pcie.c +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Support for indirect PCI bridges. - * - * Copyright (C) 1998 Gabriel Paubert. - * - * 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. - * - * "Temporary" MPC8548 Errata file - - * The standard indirect_pci code should work with future silicon versions. - */ - -#include <linux/kernel.h> -#include <linux/pci.h> -#include <linux/delay.h> -#include <linux/string.h> -#include <linux/init.h> -#include <linux/bootmem.h> - -#include <asm/io.h> -#include <asm/prom.h> -#include <asm/pci-bridge.h> -#include <asm/machdep.h> - -#define PCI_CFG_OUT out_be32 - -/* ERRATA PCI-Ex 14 PCIE Controller timeout */ -#define PCIE_FIX out_be32(hose->cfg_addr+0x4, 0x0400ffff) - - -static int -indirect_read_config_pcie(struct pci_bus *bus, unsigned int devfn, int offset, - int len, u32 *val) -{ - struct pci_controller *hose = bus->sysdata; - volatile void __iomem *cfg_data; - u32 temp; - - if (ppc_md.pci_exclude_device) - if (ppc_md.pci_exclude_device(bus->number, devfn)) - return PCIBIOS_DEVICE_NOT_FOUND; - - /* Possible artifact of CDCpp50937 needs further investigation */ - if (devfn != 0x0 && bus->number == 0xff) - return PCIBIOS_DEVICE_NOT_FOUND; - - PCIE_FIX; - if (bus->number == 0xff) { - PCI_CFG_OUT(hose->cfg_addr, - (0x80000000 | ((offset & 0xf00) << 16) | - ((bus->number - hose->bus_offset) << 16) - | (devfn << 8) | ((offset & 0xfc) ))); - } else { - PCI_CFG_OUT(hose->cfg_addr, - (0x80000001 | ((offset & 0xf00) << 16) | - ((bus->number - hose->bus_offset) << 16) - | (devfn << 8) | ((offset & 0xfc) ))); - } - - /* - * Note: the caller has already checked that offset is - * suitably aligned and that len is 1, 2 or 4. - */ - /* ERRATA PCI-Ex 12 - Configuration Address/Data Alignment */ - cfg_data = hose->cfg_data; - PCIE_FIX; - temp = in_le32(cfg_data); - switch (len) { - case 1: - *val = (temp >> (((offset & 3))*8)) & 0xff; - break; - case 2: - *val = (temp >> (((offset & 3))*8)) & 0xffff; - break; - default: - *val = temp; - break; - } - return PCIBIOS_SUCCESSFUL; -} - -static int -indirect_write_config_pcie(struct pci_bus *bus, unsigned int devfn, int offset, - int len, u32 val) -{ - struct pci_controller *hose = bus->sysdata; - volatile void __iomem *cfg_data; - u32 temp; - - if (ppc_md.pci_exclude_device) - if (ppc_md.pci_exclude_device(bus->number, devfn)) - return PCIBIOS_DEVICE_NOT_FOUND; - - /* Possible artifact of CDCpp50937 needs further investigation */ - if (devfn != 0x0 && bus->number == 0xff) - return PCIBIOS_DEVICE_NOT_FOUND; - - PCIE_FIX; - if (bus->number == 0xff) { - PCI_CFG_OUT(hose->cfg_addr, - (0x80000000 | ((offset & 0xf00) << 16) | - ((bus->number - hose->bus_offset) << 16) - | (devfn << 8) | ((offset & 0xfc) ))); - } else { - PCI_CFG_OUT(hose->cfg_addr, - (0x80000001 | ((offset & 0xf00) << 16) | - ((bus->number - hose->bus_offset) << 16) - | (devfn << 8) | ((offset & 0xfc) ))); - } - - /* - * Note: the caller has already checked that offset is - * suitably aligned and that len is 1, 2 or 4. - */ - /* ERRATA PCI-Ex 12 - Configuration Address/Data Alignment */ - cfg_data = hose->cfg_data; - switch (len) { - case 1: - PCIE_FIX; - temp = in_le32(cfg_data); - temp = (temp & ~(0xff << ((offset & 3) * 8))) | - (val << ((offset & 3) * 8)); - PCIE_FIX; - out_le32(cfg_data, temp); - break; - case 2: - PCIE_FIX; - temp = in_le32(cfg_data); - temp = (temp & ~(0xffff << ((offset & 3) * 8))); - temp |= (val << ((offset & 3) * 8)) ; - PCIE_FIX; - out_le32(cfg_data, temp); - break; - default: - PCIE_FIX; - out_le32(cfg_data, val); - break; - } - PCIE_FIX; - return PCIBIOS_SUCCESSFUL; -} - -static struct pci_ops indirect_pcie_ops = { - indirect_read_config_pcie, - indirect_write_config_pcie -}; - -void __init -setup_indirect_pcie_nomap(struct pci_controller* hose, void __iomem * cfg_addr, - void __iomem * cfg_data) -{ - hose->cfg_addr = cfg_addr; - hose->cfg_data = cfg_data; - hose->ops = &indirect_pcie_ops; -} - -void __init -setup_indirect_pcie(struct pci_controller* hose, u32 cfg_addr, u32 cfg_data) -{ - unsigned long base = cfg_addr & PAGE_MASK; - void __iomem *mbase, *addr, *data; - - mbase = ioremap(base, PAGE_SIZE); - addr = mbase + (cfg_addr & ~PAGE_MASK); - if ((cfg_data & PAGE_MASK) != base) - mbase = ioremap(cfg_data & PAGE_MASK, PAGE_SIZE); - data = mbase + (cfg_data & ~PAGE_MASK); - setup_indirect_pcie_nomap(hose, addr, data); -} diff --git a/arch/powerpc/sysdev/fsl_soc.c b/arch/powerpc/sysdev/fsl_soc.c index cad17572435..3289fab01e9 100644 --- a/arch/powerpc/sysdev/fsl_soc.c +++ b/arch/powerpc/sysdev/fsl_soc.c @@ -197,6 +197,7 @@ static int __init gfar_of_init(void) struct gianfar_platform_data gfar_data; const unsigned int *id; const char *model; + const char *ctype; const void *mac_addr; const phandle *ph; int n_res = 2; @@ -254,6 +255,14 @@ static int __init gfar_of_init(void) FSL_GIANFAR_DEV_HAS_VLAN | FSL_GIANFAR_DEV_HAS_EXTENDED_HASH; + ctype = of_get_property(np, "phy-connection-type", NULL); + + /* We only care about rgmii-id. The rest are autodetected */ + if (ctype && !strcmp(ctype, "rgmii-id")) + gfar_data.interface = PHY_INTERFACE_MODE_RGMII_ID; + else + gfar_data.interface = PHY_INTERFACE_MODE_MII; + ph = of_get_property(np, "phy-handle", NULL); phy = of_find_node_by_phandle(*ph); @@ -1028,6 +1037,19 @@ err: arch_initcall(fs_enet_of_init); +static int __init fsl_pcmcia_of_init(void) +{ + struct device_node *np = NULL; + /* + * Register all the devices which type is "pcmcia" + */ + while ((np = of_find_compatible_node(np, + "pcmcia", "fsl,pq-pcmcia")) != NULL) + of_platform_device_create(np, "m8xx-pcmcia", NULL); + return 0; +} + +arch_initcall(fsl_pcmcia_of_init); static const char *smc_regs = "regs"; static const char *smc_pram = "pram"; diff --git a/arch/powerpc/sysdev/indirect_pci.c b/arch/powerpc/sysdev/indirect_pci.c index e7148846970..c7e6e859b39 100644 --- a/arch/powerpc/sysdev/indirect_pci.c +++ b/arch/powerpc/sysdev/indirect_pci.c @@ -33,18 +33,27 @@ indirect_read_config(struct pci_bus *bus, unsigned int devfn, int offset, struct pci_controller *hose = bus->sysdata; volatile void __iomem *cfg_data; u8 cfg_type = 0; + u32 bus_no, reg; if (ppc_md.pci_exclude_device) - if (ppc_md.pci_exclude_device(bus->number, devfn)) + if (ppc_md.pci_exclude_device(hose, bus->number, devfn)) return PCIBIOS_DEVICE_NOT_FOUND; - if (hose->set_cfg_type) + if (hose->indirect_type & PPC_INDIRECT_TYPE_SET_CFG_TYPE) if (bus->number != hose->first_busno) cfg_type = 1; - PCI_CFG_OUT(hose->cfg_addr, - (0x80000000 | ((bus->number - hose->bus_offset) << 16) - | (devfn << 8) | ((offset & 0xfc) | cfg_type))); + bus_no = (bus->number == hose->first_busno) ? + hose->self_busno : bus->number; + + if (hose->indirect_type & PPC_INDIRECT_TYPE_EXT_REG) + reg = ((offset & 0xf00) << 16) | (offset & 0xfc); + else + reg = offset & 0xfc; + + PCI_CFG_OUT(hose->cfg_addr, + (0x80000000 | (bus_no << 16) + | (devfn << 8) | reg | cfg_type)); /* * Note: the caller has already checked that offset is @@ -72,18 +81,33 @@ indirect_write_config(struct pci_bus *bus, unsigned int devfn, int offset, struct pci_controller *hose = bus->sysdata; volatile void __iomem *cfg_data; u8 cfg_type = 0; + u32 bus_no, reg; if (ppc_md.pci_exclude_device) - if (ppc_md.pci_exclude_device(bus->number, devfn)) + if (ppc_md.pci_exclude_device(hose, bus->number, devfn)) return PCIBIOS_DEVICE_NOT_FOUND; - if (hose->set_cfg_type) + if (hose->indirect_type & PPC_INDIRECT_TYPE_SET_CFG_TYPE) if (bus->number != hose->first_busno) cfg_type = 1; - PCI_CFG_OUT(hose->cfg_addr, - (0x80000000 | ((bus->number - hose->bus_offset) << 16) - | (devfn << 8) | ((offset & 0xfc) | cfg_type))); + bus_no = (bus->number == hose->first_busno) ? + hose->self_busno : bus->number; + + if (hose->indirect_type & PPC_INDIRECT_TYPE_EXT_REG) + reg = ((offset & 0xf00) << 16) | (offset & 0xfc); + else + reg = offset & 0xfc; + + PCI_CFG_OUT(hose->cfg_addr, + (0x80000000 | (bus_no << 16) + | (devfn << 8) | reg | cfg_type)); + + /* surpress setting of PCI_PRIMARY_BUS */ + if (hose->indirect_type & PPC_INDIRECT_TYPE_SURPRESS_PRIMARY_BUS) + if ((offset == PCI_PRIMARY_BUS) && + (bus->number == hose->first_busno)) + val &= 0xffffff00; /* * Note: the caller has already checked that offset is diff --git a/arch/powerpc/sysdev/mpc8xx_pic.h b/arch/powerpc/sysdev/mpc8xx_pic.h index afa2ee6717c..9fe00eebdc8 100644 --- a/arch/powerpc/sysdev/mpc8xx_pic.h +++ b/arch/powerpc/sysdev/mpc8xx_pic.h @@ -4,9 +4,16 @@ #include <linux/irq.h> #include <linux/interrupt.h> -extern struct hw_interrupt_type mpc8xx_pic; - int mpc8xx_pic_init(void); unsigned int mpc8xx_get_irq(void); +/* + * Some internal interrupt registers use an 8-bit mask for the interrupt + * level instead of a number. + */ +static inline uint mk_int_int_mask(uint mask) +{ + return (1 << (7 - (mask/2))); +} + #endif /* _PPC_KERNEL_PPC8xx_H */ diff --git a/arch/powerpc/sysdev/mv64x60_dev.c b/arch/powerpc/sysdev/mv64x60_dev.c index 4b0a9c88eeb..b618fa60aef 100644 --- a/arch/powerpc/sysdev/mv64x60_dev.c +++ b/arch/powerpc/sysdev/mv64x60_dev.c @@ -12,6 +12,7 @@ #include <linux/stddef.h> #include <linux/kernel.h> #include <linux/init.h> +#include <linux/console.h> #include <linux/mv643xx.h> #include <linux/platform_device.h> @@ -420,3 +421,30 @@ error: return err; } arch_initcall(mv64x60_device_setup); + +static int __init mv64x60_add_mpsc_console(void) +{ + struct device_node *np = NULL; + const char *prop; + + prop = of_get_property(of_chosen, "linux,stdout-path", NULL); + if (prop == NULL) + goto not_mpsc; + + np = of_find_node_by_path(prop); + if (!np) + goto not_mpsc; + + if (!of_device_is_compatible(np, "marvell,mpsc")) + goto not_mpsc; + + prop = of_get_property(np, "block-index", NULL); + if (!prop) + goto not_mpsc; + + add_preferred_console("ttyMM", *(int *)prop, NULL); + +not_mpsc: + return 0; +} +console_initcall(mv64x60_add_mpsc_console); diff --git a/arch/powerpc/sysdev/mv64x60_pci.c b/arch/powerpc/sysdev/mv64x60_pci.c index b5aef4cbc8d..45db86c2363 100644 --- a/arch/powerpc/sysdev/mv64x60_pci.c +++ b/arch/powerpc/sysdev/mv64x60_pci.c @@ -137,18 +137,15 @@ static int __init mv64x60_add_bridge(struct device_node *dev) printk(KERN_WARNING "Can't get bus-range for %s, assume" " bus 0\n", dev->full_name); - hose = pcibios_alloc_controller(); + hose = pcibios_alloc_controller(dev); if (!hose) return -ENOMEM; - hose->arch_data = dev; - hose->set_cfg_type = 1; - hose->first_busno = bus_range ? bus_range[0] : 0; hose->last_busno = bus_range ? bus_range[1] : 0xff; setup_indirect_pci(hose, rsrc.start, rsrc.start + 4); - hose->bus_offset = hose->first_busno; + hose->self_busno = hose->first_busno; printk(KERN_INFO "Found MV64x60 PCI host bridge at 0x%016llx. " "Firmware bus number: %d->%d\n", diff --git a/arch/powerpc/sysdev/qe_lib/ucc.c b/arch/powerpc/sysdev/qe_lib/ucc.c index ac12a44d516..f970e5415ac 100644 --- a/arch/powerpc/sysdev/qe_lib/ucc.c +++ b/arch/powerpc/sysdev/qe_lib/ucc.c @@ -18,6 +18,7 @@ #include <linux/errno.h> #include <linux/slab.h> #include <linux/stddef.h> +#include <linux/module.h> #include <asm/irq.h> #include <asm/io.h> @@ -40,6 +41,7 @@ int ucc_set_qe_mux_mii_mng(int ucc_num) return 0; } +EXPORT_SYMBOL(ucc_set_qe_mux_mii_mng); int ucc_set_type(int ucc_num, struct ucc_common *regs, enum ucc_speed_type speed) diff --git a/arch/powerpc/sysdev/qe_lib/ucc_fast.c b/arch/powerpc/sysdev/qe_lib/ucc_fast.c index 9143236853f..3df202e8d33 100644 --- a/arch/powerpc/sysdev/qe_lib/ucc_fast.c +++ b/arch/powerpc/sysdev/qe_lib/ucc_fast.c @@ -19,6 +19,7 @@ #include <linux/stddef.h> #include <linux/interrupt.h> #include <linux/err.h> +#include <linux/module.h> #include <asm/io.h> #include <asm/immap_qe.h> @@ -70,6 +71,7 @@ void ucc_fast_dump_regs(struct ucc_fast_private * uccf) printk(KERN_INFO "guemr : addr - 0x%08x, val - 0x%02x", (u32) & uccf->uf_regs->guemr, uccf->uf_regs->guemr); } +EXPORT_SYMBOL(ucc_fast_dump_regs); u32 ucc_fast_get_qe_cr_subblock(int uccf_num) { @@ -85,11 +87,13 @@ u32 ucc_fast_get_qe_cr_subblock(int uccf_num) default: return QE_CR_SUBBLOCK_INVALID; } } +EXPORT_SYMBOL(ucc_fast_get_qe_cr_subblock); void ucc_fast_transmit_on_demand(struct ucc_fast_private * uccf) { out_be16(&uccf->uf_regs->utodr, UCC_FAST_TOD); } +EXPORT_SYMBOL(ucc_fast_transmit_on_demand); void ucc_fast_enable(struct ucc_fast_private * uccf, enum comm_dir mode) { @@ -110,6 +114,7 @@ void ucc_fast_enable(struct ucc_fast_private * uccf, enum comm_dir mode) } out_be32(&uf_regs->gumr, gumr); } +EXPORT_SYMBOL(ucc_fast_enable); void ucc_fast_disable(struct ucc_fast_private * uccf, enum comm_dir mode) { @@ -130,6 +135,7 @@ void ucc_fast_disable(struct ucc_fast_private * uccf, enum comm_dir mode) } out_be32(&uf_regs->gumr, gumr); } +EXPORT_SYMBOL(ucc_fast_disable); int ucc_fast_init(struct ucc_fast_info * uf_info, struct ucc_fast_private ** uccf_ret) { @@ -341,6 +347,7 @@ int ucc_fast_init(struct ucc_fast_info * uf_info, struct ucc_fast_private ** ucc *uccf_ret = uccf; return 0; } +EXPORT_SYMBOL(ucc_fast_init); void ucc_fast_free(struct ucc_fast_private * uccf) { @@ -355,3 +362,4 @@ void ucc_fast_free(struct ucc_fast_private * uccf) kfree(uccf); } +EXPORT_SYMBOL(ucc_fast_free); diff --git a/arch/powerpc/sysdev/rtc_cmos_setup.c b/arch/powerpc/sysdev/rtc_cmos_setup.c new file mode 100644 index 00000000000..e276048b8c5 --- /dev/null +++ b/arch/powerpc/sysdev/rtc_cmos_setup.c @@ -0,0 +1,49 @@ +/* + * Setup code for PC-style Real-Time Clock. + * + * Author: Wade Farnsworth <wfarnsworth@mvista.com> + * + * 2007 (c) MontaVista Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include <linux/platform_device.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/mc146818rtc.h> + +#include <asm/prom.h> + +static int __init add_rtc(void) +{ + struct device_node *np; + struct platform_device *pd; + struct resource res; + int ret; + + np = of_find_compatible_node(NULL, NULL, "pnpPNP,b00"); + if (!np) + return -ENODEV; + + ret = of_address_to_resource(np, 0, &res); + of_node_put(np); + if (ret) + return ret; + + /* + * RTC_PORT(x) is hardcoded in asm/mc146818rtc.h. Verify that the + * address provided by the device node matches. + */ + if (res.start != RTC_PORT(0)) + return -EINVAL; + + pd = platform_device_register_simple("rtc_cmos", -1, + &res, 1); + if (IS_ERR(pd)) + return PTR_ERR(pd); + + return 0; +} +fs_initcall(add_rtc); diff --git a/arch/powerpc/sysdev/timer.c b/arch/powerpc/sysdev/timer.c index 4a01748b421..e81e7ec2e79 100644 --- a/arch/powerpc/sysdev/timer.c +++ b/arch/powerpc/sysdev/timer.c @@ -24,7 +24,12 @@ static int timer_resume(struct sys_device *dev) /* get current RTC time and convert to seconds */ get_rtc_time(&cur_rtc_tm); - rtc_tm_to_time(&cur_rtc_tm, &cur_rtc_time); + cur_rtc_time = mktime(cur_rtc_tm.tm_year + 1900, + cur_rtc_tm.tm_mon + 1, + cur_rtc_tm.tm_mday, + cur_rtc_tm.tm_hour, + cur_rtc_tm.tm_min, + cur_rtc_tm.tm_sec); diff = cur_rtc_time - suspend_rtc_time; @@ -44,7 +49,12 @@ static int timer_suspend(struct sys_device *dev, pm_message_t state) WARN_ON(!ppc_md.get_rtc_time); get_rtc_time(&suspend_rtc_tm); - rtc_tm_to_time(&suspend_rtc_tm, &suspend_rtc_time); + suspend_rtc_time = mktime(suspend_rtc_tm.tm_year + 1900, + suspend_rtc_tm.tm_mon + 1, + suspend_rtc_tm.tm_mday, + suspend_rtc_tm.tm_hour, + suspend_rtc_tm.tm_min, + suspend_rtc_tm.tm_sec); return 0; } diff --git a/arch/powerpc/sysdev/tsi108_dev.c b/arch/powerpc/sysdev/tsi108_dev.c index 7d3b09b7d54..a113d800cbf 100644 --- a/arch/powerpc/sysdev/tsi108_dev.c +++ b/arch/powerpc/sysdev/tsi108_dev.c @@ -72,12 +72,11 @@ static int __init tsi108_eth_of_init(void) int ret; for (np = NULL, i = 0; - (np = of_find_compatible_node(np, "network", "tsi-ethernet")) != NULL; + (np = of_find_compatible_node(np, "network", "tsi108-ethernet")) != NULL; i++) { struct resource r[2]; - struct device_node *phy; + struct device_node *phy, *mdio; hw_info tsi_eth_data; - const unsigned int *id; const unsigned int *phy_id; const void *mac_addr; const phandle *ph; @@ -111,6 +110,13 @@ static int __init tsi108_eth_of_init(void) if (mac_addr) memcpy(tsi_eth_data.mac_addr, mac_addr, 6); + ph = of_get_property(np, "mdio-handle", NULL); + mdio = of_find_node_by_phandle(*ph); + ret = of_address_to_resource(mdio, 0, &res); + of_node_put(mdio); + if (ret) + goto unreg; + ph = of_get_property(np, "phy-handle", NULL); phy = of_find_node_by_phandle(*ph); @@ -119,20 +125,25 @@ static int __init tsi108_eth_of_init(void) goto unreg; } - id = of_get_property(phy, "reg", NULL); - phy_id = of_get_property(phy, "phy-id", NULL); - ret = of_address_to_resource(phy, 0, &res); - if (ret) { - of_node_put(phy); - goto unreg; - } + phy_id = of_get_property(phy, "reg", NULL); + tsi_eth_data.regs = r[0].start; tsi_eth_data.phyregs = res.start; tsi_eth_data.phy = *phy_id; tsi_eth_data.irq_num = irq_of_parse_and_map(np, 0); - if (of_device_is_compatible(phy, "bcm54xx")) + + /* Some boards with the TSI108 bridge (e.g. Holly) + * have a miswiring of the ethernet PHYs which + * requires a workaround. The special + * "txc-rxc-delay-disable" property enables this + * workaround. FIXME: Need to port the tsi108_eth + * driver itself to phylib and use a non-misleading + * name for the workaround flag - it's not actually to + * do with the model of PHY in use */ + if (of_get_property(phy, "txc-rxc-delay-disable", NULL)) tsi_eth_data.phy_type = TSI108_PHY_BCM54XX; of_node_put(phy); + ret = platform_device_add_data(tsi_eth_dev, &tsi_eth_data, sizeof(hw_info)); diff --git a/arch/powerpc/sysdev/tsi108_pci.c b/arch/powerpc/sysdev/tsi108_pci.c index 2153163fa59..90db8a720fe 100644 --- a/arch/powerpc/sysdev/tsi108_pci.c +++ b/arch/powerpc/sysdev/tsi108_pci.c @@ -64,9 +64,10 @@ tsi108_direct_write_config(struct pci_bus *bus, unsigned int devfunc, int offset, int len, u32 val) { volatile unsigned char *cfg_addr; + struct pci_controller *hose = bus->sysdata; if (ppc_md.pci_exclude_device) - if (ppc_md.pci_exclude_device(bus->number, devfunc)) + if (ppc_md.pci_exclude_device(hose, bus->number, devfunc)) return PCIBIOS_DEVICE_NOT_FOUND; cfg_addr = (unsigned char *)(tsi_mk_config_addr(bus->number, @@ -149,10 +150,11 @@ tsi108_direct_read_config(struct pci_bus *bus, unsigned int devfn, int offset, int len, u32 * val) { volatile unsigned char *cfg_addr; + struct pci_controller *hose = bus->sysdata; u32 temp; if (ppc_md.pci_exclude_device) - if (ppc_md.pci_exclude_device(bus->number, devfn)) + if (ppc_md.pci_exclude_device(hose, bus->number, devfn)) return PCIBIOS_DEVICE_NOT_FOUND; cfg_addr = (unsigned char *)(tsi_mk_config_addr(bus->number, @@ -219,14 +221,12 @@ int __init tsi108_setup_pci(struct device_node *dev, u32 cfg_phys, int primary) " bus 0\n", dev->full_name); } - hose = pcibios_alloc_controller(); + hose = pcibios_alloc_controller(dev); if (!hose) { printk("PCI Host bridge init failed\n"); return -ENOMEM; } - hose->arch_data = dev; - hose->set_cfg_type = 1; hose->first_busno = bus_range ? bus_range[0] : 0; hose->last_busno = bus_range ? bus_range[1] : 0xff; diff --git a/arch/powerpc/xmon/xmon.c b/arch/powerpc/xmon/xmon.c index 28fdf4f50c2..669e6566ad7 100644 --- a/arch/powerpc/xmon/xmon.c +++ b/arch/powerpc/xmon/xmon.c @@ -2634,7 +2634,7 @@ static int __init setup_xmon_sysrq(void) __initcall(setup_xmon_sysrq); #endif /* CONFIG_MAGIC_SYSRQ */ -int __initdata xmon_early, xmon_off; +static int __initdata xmon_early, xmon_off; static int __init early_parse_xmon(char *p) { diff --git a/arch/ppc/configs/ev64260_defconfig b/arch/ppc/configs/ev64260_defconfig index 84cc142a67b..587e9a3b949 100644 --- a/arch/ppc/configs/ev64260_defconfig +++ b/arch/ppc/configs/ev64260_defconfig @@ -531,7 +531,6 @@ CONFIG_I2C_CHARDEV=m # CONFIG_I2C_AMD8111 is not set # CONFIG_I2C_I801 is not set # CONFIG_I2C_I810 is not set -# CONFIG_I2C_ISA is not set # CONFIG_I2C_NFORCE2 is not set # CONFIG_I2C_PARPORT_LIGHT is not set # CONFIG_I2C_PIIX4 is not set diff --git a/arch/ppc/configs/mpc8540_ads_defconfig b/arch/ppc/configs/mpc8540_ads_defconfig index c5c86025e26..bf676ebd99a 100644 --- a/arch/ppc/configs/mpc8540_ads_defconfig +++ b/arch/ppc/configs/mpc8540_ads_defconfig @@ -452,7 +452,6 @@ CONFIG_I2C_CHARDEV=y # CONFIG_I2C_AMD8111 is not set # CONFIG_I2C_I801 is not set # CONFIG_I2C_I810 is not set -# CONFIG_I2C_ISA is not set CONFIG_I2C_MPC=y # CONFIG_I2C_NFORCE2 is not set # CONFIG_I2C_PARPORT_LIGHT is not set diff --git a/arch/ppc/configs/mpc8548_cds_defconfig b/arch/ppc/configs/mpc8548_cds_defconfig index abe034f24b8..f36fc5db540 100644 --- a/arch/ppc/configs/mpc8548_cds_defconfig +++ b/arch/ppc/configs/mpc8548_cds_defconfig @@ -413,7 +413,6 @@ CONFIG_I2C_CHARDEV=y # # I2C Hardware Bus support # -# CONFIG_I2C_ISA is not set CONFIG_I2C_MPC=y # CONFIG_I2C_PARPORT_LIGHT is not set # CONFIG_I2C_PCA_ISA is not set diff --git a/arch/ppc/configs/mpc8555_cds_defconfig b/arch/ppc/configs/mpc8555_cds_defconfig index 15abebf46b9..4f1e320acfb 100644 --- a/arch/ppc/configs/mpc8555_cds_defconfig +++ b/arch/ppc/configs/mpc8555_cds_defconfig @@ -518,7 +518,6 @@ CONFIG_I2C_CHARDEV=y # CONFIG_I2C_I801 is not set # CONFIG_I2C_I810 is not set # CONFIG_I2C_PIIX4 is not set -# CONFIG_I2C_ISA is not set CONFIG_I2C_MPC=y # CONFIG_I2C_NFORCE2 is not set # CONFIG_I2C_PARPORT_LIGHT is not set diff --git a/arch/ppc/configs/mpc8560_ads_defconfig b/arch/ppc/configs/mpc8560_ads_defconfig index f834fb541ad..f12d48fcbba 100644 --- a/arch/ppc/configs/mpc8560_ads_defconfig +++ b/arch/ppc/configs/mpc8560_ads_defconfig @@ -489,7 +489,6 @@ CONFIG_I2C_CHARDEV=y # CONFIG_I2C_I801 is not set # CONFIG_I2C_I810 is not set # CONFIG_I2C_PIIX4 is not set -# CONFIG_I2C_ISA is not set CONFIG_I2C_MPC=y # CONFIG_I2C_NFORCE2 is not set # CONFIG_I2C_PARPORT_LIGHT is not set diff --git a/arch/ppc/configs/radstone_ppc7d_defconfig b/arch/ppc/configs/radstone_ppc7d_defconfig index ca4d1fd0ca0..9f64532f2a8 100644 --- a/arch/ppc/configs/radstone_ppc7d_defconfig +++ b/arch/ppc/configs/radstone_ppc7d_defconfig @@ -710,7 +710,6 @@ CONFIG_I2C_CHARDEV=y # CONFIG_I2C_I801 is not set # CONFIG_I2C_I810 is not set # CONFIG_I2C_PIIX4 is not set -# CONFIG_I2C_ISA is not set # CONFIG_I2C_MPC is not set # CONFIG_I2C_NFORCE2 is not set # CONFIG_I2C_PARPORT_LIGHT is not set diff --git a/arch/ppc/configs/stx_gp3_defconfig b/arch/ppc/configs/stx_gp3_defconfig index 3fedc43e44a..70d6f842aa9 100644 --- a/arch/ppc/configs/stx_gp3_defconfig +++ b/arch/ppc/configs/stx_gp3_defconfig @@ -661,7 +661,6 @@ CONFIG_I2C_ALGOBIT=m # CONFIG_I2C_I801 is not set # CONFIG_I2C_I810 is not set # CONFIG_I2C_PIIX4 is not set -# CONFIG_I2C_ISA is not set # CONFIG_I2C_MPC is not set # CONFIG_I2C_NFORCE2 is not set # CONFIG_I2C_PARPORT is not set diff --git a/arch/ppc/configs/sycamore_defconfig b/arch/ppc/configs/sycamore_defconfig index 758114cfea5..6996cca18f3 100644 --- a/arch/ppc/configs/sycamore_defconfig +++ b/arch/ppc/configs/sycamore_defconfig @@ -461,7 +461,6 @@ CONFIG_I2C_CHARDEV=y # CONFIG_I2C_I801 is not set # CONFIG_I2C_I810 is not set # CONFIG_I2C_IBM_IIC is not set -# CONFIG_I2C_ISA is not set # CONFIG_I2C_NFORCE2 is not set # CONFIG_I2C_PARPORT_LIGHT is not set # CONFIG_I2C_PIIX4 is not set diff --git a/arch/ppc/kernel/misc.S b/arch/ppc/kernel/misc.S index d319f9ba237..0da55368655 100644 --- a/arch/ppc/kernel/misc.S +++ b/arch/ppc/kernel/misc.S @@ -328,7 +328,7 @@ BEGIN_FTR_SECTION mtspr SPRN_L1CSR0,r3 isync blr -END_FTR_SECTION_IFCLR(CPU_FTR_SPLIT_ID_CACHE) +END_FTR_SECTION_IFSET(CPU_FTR_UNIFIED_ID_CACHE) mfspr r3,SPRN_L1CSR1 ori r3,r3,L1CSR1_ICFI|L1CSR1_ICLFR mtspr SPRN_L1CSR1,r3 @@ -355,7 +355,7 @@ END_FTR_SECTION_IFCLR(CPU_FTR_SPLIT_ID_CACHE) _GLOBAL(__flush_icache_range) BEGIN_FTR_SECTION blr /* for 601, do nothing */ -END_FTR_SECTION_IFCLR(CPU_FTR_SPLIT_ID_CACHE) +END_FTR_SECTION_IFSET(CPU_FTR_COHERENT_ICACHE) li r5,L1_CACHE_BYTES-1 andc r3,r3,r5 subf r4,r3,r4 @@ -472,7 +472,7 @@ _GLOBAL(flush_dcache_all) _GLOBAL(__flush_dcache_icache) BEGIN_FTR_SECTION blr /* for 601, do nothing */ -END_FTR_SECTION_IFCLR(CPU_FTR_SPLIT_ID_CACHE) +END_FTR_SECTION_IFSET(CPU_FTR_COHERENT_ICACHE) rlwinm r3,r3,0,0,19 /* Get page base address */ li r4,4096/L1_CACHE_BYTES /* Number of lines in a page */ mtctr r4 @@ -500,7 +500,7 @@ END_FTR_SECTION_IFCLR(CPU_FTR_SPLIT_ID_CACHE) _GLOBAL(__flush_dcache_icache_phys) BEGIN_FTR_SECTION blr /* for 601, do nothing */ -END_FTR_SECTION_IFCLR(CPU_FTR_SPLIT_ID_CACHE) +END_FTR_SECTION_IFSET(CPU_FTR_COHERENT_ICACHE) mfmsr r10 rlwinm r0,r10,0,28,26 /* clear DR */ mtmsr r0 diff --git a/arch/ppc/kernel/ppc_ksyms.c b/arch/ppc/kernel/ppc_ksyms.c index a4165209ac7..63f0a987139 100644 --- a/arch/ppc/kernel/ppc_ksyms.c +++ b/arch/ppc/kernel/ppc_ksyms.c @@ -64,7 +64,6 @@ extern unsigned long mm_ptov (unsigned long paddr); EXPORT_SYMBOL(clear_pages); EXPORT_SYMBOL(clear_user_page); -EXPORT_SYMBOL(do_signal); EXPORT_SYMBOL(transfer_to_handler); EXPORT_SYMBOL(do_IRQ); EXPORT_SYMBOL(machine_check_exception); diff --git a/arch/ppc/kernel/setup.c b/arch/ppc/kernel/setup.c index c79704f5409..967c1ef59a6 100644 --- a/arch/ppc/kernel/setup.c +++ b/arch/ppc/kernel/setup.c @@ -526,7 +526,7 @@ void __init setup_arch(char **cmdline_p) * Systems with OF can look in the properties on the cpu node(s) * for a possibly more accurate value. */ - if (cpu_has_feature(CPU_FTR_SPLIT_ID_CACHE)) { + if (! cpu_has_feature(CPU_FTR_UNIFIED_ID_CACHE)) { dcache_bsize = cur_cpu_spec->dcache_bsize; icache_bsize = cur_cpu_spec->icache_bsize; ucache_bsize = 0; diff --git a/arch/ppc/kernel/traps.c b/arch/ppc/kernel/traps.c index 0eaef7c8378..3f3b292eb77 100644 --- a/arch/ppc/kernel/traps.c +++ b/arch/ppc/kernel/traps.c @@ -92,6 +92,7 @@ int die(const char * str, struct pt_regs * fp, long err) if (nl) printk("\n"); show_regs(fp); + add_taint(TAINT_DIE); spin_unlock_irq(&die_lock); /* do_exit() should take care of panic'ing from an interrupt * context so we don't handle it here diff --git a/arch/ppc/kernel/vmlinux.lds.S b/arch/ppc/kernel/vmlinux.lds.S index 19db8746ff1..c0aac3ff9e9 100644 --- a/arch/ppc/kernel/vmlinux.lds.S +++ b/arch/ppc/kernel/vmlinux.lds.S @@ -130,10 +130,7 @@ SECTIONS __ftr_fixup : { *(__ftr_fixup) } __stop___ftr_fixup = .; - . = ALIGN(4096); - __per_cpu_start = .; - .data.percpu : { *(.data.percpu) } - __per_cpu_end = .; + PERCPU(4096) #ifdef CONFIG_BLK_DEV_INITRD . = ALIGN(4096); diff --git a/arch/ppc/mm/fault.c b/arch/ppc/mm/fault.c index 465f451f3bc..b98244e277f 100644 --- a/arch/ppc/mm/fault.c +++ b/arch/ppc/mm/fault.c @@ -96,6 +96,7 @@ int do_page_fault(struct pt_regs *regs, unsigned long address, struct mm_struct *mm = current->mm; siginfo_t info; int code = SEGV_MAPERR; + int fault; #if defined(CONFIG_4xx) || defined (CONFIG_BOOKE) int is_write = error_code & ESR_DST; #else @@ -249,20 +250,18 @@ good_area: * the fault. */ survive: - switch (handle_mm_fault(mm, vma, address, is_write)) { - case VM_FAULT_MINOR: - current->min_flt++; - break; - case VM_FAULT_MAJOR: - current->maj_flt++; - break; - case VM_FAULT_SIGBUS: - goto do_sigbus; - case VM_FAULT_OOM: - goto out_of_memory; - default: + fault = handle_mm_fault(mm, vma, address, is_write); + if (unlikely(fault & VM_FAULT_ERROR)) { + if (fault & VM_FAULT_OOM) + goto out_of_memory; + else if (fault & VM_FAULT_SIGBUS) + goto do_sigbus; BUG(); } + if (fault & VM_FAULT_MAJOR) + current->maj_flt++; + else + current->min_flt++; up_read(&mm->mmap_sem); /* diff --git a/arch/ppc/mm/tlb.c b/arch/ppc/mm/tlb.c index fa29740a28f..4ff260bc9dd 100644 --- a/arch/ppc/mm/tlb.c +++ b/arch/ppc/mm/tlb.c @@ -27,6 +27,7 @@ #include <linux/mm.h> #include <linux/init.h> #include <linux/highmem.h> +#include <linux/pagemap.h> #include <asm/tlbflush.h> #include <asm/tlb.h> diff --git a/arch/ppc/platforms/4xx/bamboo.c b/arch/ppc/platforms/4xx/bamboo.c index 349660b84a0..017623c9bc4 100644 --- a/arch/ppc/platforms/4xx/bamboo.c +++ b/arch/ppc/platforms/4xx/bamboo.c @@ -29,6 +29,7 @@ #include <linux/tty.h> #include <linux/serial.h> #include <linux/serial_core.h> +#include <linux/serial_8250.h> #include <linux/ethtool.h> #include <asm/system.h> diff --git a/arch/ppc/platforms/4xx/bubinga.c b/arch/ppc/platforms/4xx/bubinga.c index 1a7f075b754..cd696be55ac 100644 --- a/arch/ppc/platforms/4xx/bubinga.c +++ b/arch/ppc/platforms/4xx/bubinga.c @@ -21,6 +21,7 @@ #include <linux/tty.h> #include <linux/serial.h> #include <linux/serial_core.h> +#include <linux/serial_8250.h> #include <asm/system.h> #include <asm/pci-bridge.h> diff --git a/arch/ppc/platforms/4xx/cpci405.c b/arch/ppc/platforms/4xx/cpci405.c index 8474b05b795..2e7e25dd84c 100644 --- a/arch/ppc/platforms/4xx/cpci405.c +++ b/arch/ppc/platforms/4xx/cpci405.c @@ -23,6 +23,7 @@ #include <asm/todc.h> #include <linux/serial.h> #include <linux/serial_core.h> +#include <linux/serial_8250.h> #include <asm/ocp.h> #include <asm/ibm_ocp_pci.h> #include <platforms/4xx/ibm405gp.h> diff --git a/arch/ppc/platforms/4xx/ebony.c b/arch/ppc/platforms/4xx/ebony.c index f0f9cc8480c..05d7184d7e1 100644 --- a/arch/ppc/platforms/4xx/ebony.c +++ b/arch/ppc/platforms/4xx/ebony.c @@ -32,6 +32,7 @@ #include <linux/tty.h> #include <linux/serial.h> #include <linux/serial_core.h> +#include <linux/serial_8250.h> #include <asm/system.h> #include <asm/pgtable.h> diff --git a/arch/ppc/platforms/4xx/luan.c b/arch/ppc/platforms/4xx/luan.c index 61706ef3711..4b169610f15 100644 --- a/arch/ppc/platforms/4xx/luan.c +++ b/arch/ppc/platforms/4xx/luan.c @@ -30,6 +30,7 @@ #include <linux/tty.h> #include <linux/serial.h> #include <linux/serial_core.h> +#include <linux/serial_8250.h> #include <asm/system.h> #include <asm/pgtable.h> diff --git a/arch/ppc/platforms/4xx/ocotea.c b/arch/ppc/platforms/4xx/ocotea.c index 5e994e146ba..fd0f971881d 100644 --- a/arch/ppc/platforms/4xx/ocotea.c +++ b/arch/ppc/platforms/4xx/ocotea.c @@ -30,6 +30,7 @@ #include <linux/tty.h> #include <linux/serial.h> #include <linux/serial_core.h> +#include <linux/serial_8250.h> #include <asm/system.h> #include <asm/pgtable.h> diff --git a/arch/ppc/platforms/4xx/taishan.c b/arch/ppc/platforms/4xx/taishan.c index 5d9af8ddb15..888c492b4a4 100644 --- a/arch/ppc/platforms/4xx/taishan.c +++ b/arch/ppc/platforms/4xx/taishan.c @@ -30,6 +30,7 @@ #include <linux/tty.h> #include <linux/serial.h> #include <linux/serial_core.h> +#include <linux/serial_8250.h> #include <linux/platform_device.h> #include <linux/mtd/partitions.h> #include <linux/mtd/nand.h> diff --git a/arch/ppc/platforms/4xx/yucca.c b/arch/ppc/platforms/4xx/yucca.c index 346787df0dd..a83b0baea01 100644 --- a/arch/ppc/platforms/4xx/yucca.c +++ b/arch/ppc/platforms/4xx/yucca.c @@ -31,6 +31,7 @@ #include <linux/tty.h> #include <linux/serial.h> #include <linux/serial_core.h> +#include <linux/serial_8250.h> #include <asm/system.h> #include <asm/pgtable.h> diff --git a/arch/ppc/platforms/85xx/sbc8560.c b/arch/ppc/platforms/85xx/sbc8560.c index 1d10ab98f66..3d7addbdecf 100644 --- a/arch/ppc/platforms/85xx/sbc8560.c +++ b/arch/ppc/platforms/85xx/sbc8560.c @@ -26,6 +26,7 @@ #include <linux/serial.h> #include <linux/tty.h> /* for linux/serial_core.h */ #include <linux/serial_core.h> +#include <linux/serial_8250.h> #include <linux/initrd.h> #include <linux/module.h> #include <linux/fsl_devices.h> diff --git a/arch/ppc/platforms/chestnut.c b/arch/ppc/platforms/chestnut.c index a764ae71cbc..248684f50dd 100644 --- a/arch/ppc/platforms/chestnut.c +++ b/arch/ppc/platforms/chestnut.c @@ -25,6 +25,7 @@ #include <linux/ide.h> #include <linux/serial.h> #include <linux/serial_core.h> +#include <linux/serial_8250.h> #include <linux/mtd/physmap.h> #include <asm/system.h> #include <asm/pgtable.h> diff --git a/arch/ppc/platforms/ev64260.c b/arch/ppc/platforms/ev64260.c index 4957a7bcde2..976270d537c 100644 --- a/arch/ppc/platforms/ev64260.c +++ b/arch/ppc/platforms/ev64260.c @@ -35,6 +35,7 @@ #include <linux/serial.h> #include <linux/tty.h> #include <linux/serial_core.h> +#include <linux/serial_8250.h> #else #include <linux/mv643xx.h> #endif diff --git a/arch/ppc/platforms/prep_setup.c b/arch/ppc/platforms/prep_setup.c index 6f21110a974..3c56654bfc6 100644 --- a/arch/ppc/platforms/prep_setup.c +++ b/arch/ppc/platforms/prep_setup.c @@ -69,9 +69,6 @@ TODC_ALLOC(); -unsigned char ucBoardRev; -unsigned char ucBoardRevMaj, ucBoardRevMin; - extern unsigned char prep_nvram_read_val(int addr); extern void prep_nvram_write_val(int addr, unsigned char val); diff --git a/arch/ppc/platforms/radstone_ppc7d.c b/arch/ppc/platforms/radstone_ppc7d.c index b55860734a7..44d4398a36f 100644 --- a/arch/ppc/platforms/radstone_ppc7d.c +++ b/arch/ppc/platforms/radstone_ppc7d.c @@ -35,6 +35,7 @@ #include <linux/serial.h> #include <linux/tty.h> /* for linux/serial_core.h */ #include <linux/serial_core.h> +#include <linux/serial_8250.h> #include <linux/mv643xx.h> #include <linux/netdevice.h> #include <linux/platform_device.h> diff --git a/arch/ppc/platforms/spruce.c b/arch/ppc/platforms/spruce.c index 3c784278487..f4de50ba292 100644 --- a/arch/ppc/platforms/spruce.c +++ b/arch/ppc/platforms/spruce.c @@ -27,6 +27,7 @@ #include <linux/serial.h> #include <linux/tty.h> #include <linux/serial_core.h> +#include <linux/serial_8250.h> #include <asm/system.h> #include <asm/pgtable.h> diff --git a/arch/ppc/syslib/Makefile b/arch/ppc/syslib/Makefile index 95694159b22..543795be58c 100644 --- a/arch/ppc/syslib/Makefile +++ b/arch/ppc/syslib/Makefile @@ -7,6 +7,7 @@ CFLAGS_btext.o += -fPIC wdt-mpc8xx-$(CONFIG_8xx_WDT) += m8xx_wdt.o +obj-$(CONFIG_PPC_INDIRECT_PCI) += indirect_pci.o obj-$(CONFIG_PPCBUG_NVRAM) += prep_nvram.o obj-$(CONFIG_PPC_OCP) += ocp.o obj-$(CONFIG_IBM_OCP) += ibm_ocp.o diff --git a/arch/ppc/syslib/indirect_pci.c b/arch/ppc/syslib/indirect_pci.c new file mode 100644 index 00000000000..83b323a7d02 --- /dev/null +++ b/arch/ppc/syslib/indirect_pci.c @@ -0,0 +1,134 @@ +/* + * Support for indirect PCI bridges. + * + * Copyright (C) 1998 Gabriel Paubert. + * + * 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 <linux/kernel.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/string.h> +#include <linux/init.h> + +#include <asm/io.h> +#include <asm/prom.h> +#include <asm/pci-bridge.h> +#include <asm/machdep.h> + +#ifdef CONFIG_PPC_INDIRECT_PCI_BE +#define PCI_CFG_OUT out_be32 +#else +#define PCI_CFG_OUT out_le32 +#endif + +static int +indirect_read_config(struct pci_bus *bus, unsigned int devfn, int offset, + int len, u32 *val) +{ + struct pci_controller *hose = bus->sysdata; + volatile void __iomem *cfg_data; + u8 cfg_type = 0; + + if (ppc_md.pci_exclude_device) + if (ppc_md.pci_exclude_device(bus->number, devfn)) + return PCIBIOS_DEVICE_NOT_FOUND; + + if (hose->set_cfg_type) + if (bus->number != hose->first_busno) + cfg_type = 1; + + PCI_CFG_OUT(hose->cfg_addr, + (0x80000000 | ((bus->number - hose->bus_offset) << 16) + | (devfn << 8) | ((offset & 0xfc) | cfg_type))); + + /* + * Note: the caller has already checked that offset is + * suitably aligned and that len is 1, 2 or 4. + */ + cfg_data = hose->cfg_data + (offset & 3); + switch (len) { + case 1: + *val = in_8(cfg_data); + break; + case 2: + *val = in_le16(cfg_data); + break; + default: + *val = in_le32(cfg_data); + break; + } + return PCIBIOS_SUCCESSFUL; +} + +static int +indirect_write_config(struct pci_bus *bus, unsigned int devfn, int offset, + int len, u32 val) +{ + struct pci_controller *hose = bus->sysdata; + volatile void __iomem *cfg_data; + u8 cfg_type = 0; + + if (ppc_md.pci_exclude_device) + if (ppc_md.pci_exclude_device(bus->number, devfn)) + return PCIBIOS_DEVICE_NOT_FOUND; + + if (hose->set_cfg_type) + if (bus->number != hose->first_busno) + cfg_type = 1; + + PCI_CFG_OUT(hose->cfg_addr, + (0x80000000 | ((bus->number - hose->bus_offset) << 16) + | (devfn << 8) | ((offset & 0xfc) | cfg_type))); + + /* + * Note: the caller has already checked that offset is + * suitably aligned and that len is 1, 2 or 4. + */ + cfg_data = hose->cfg_data + (offset & 3); + switch (len) { + case 1: + out_8(cfg_data, val); + break; + case 2: + out_le16(cfg_data, val); + break; + default: + out_le32(cfg_data, val); + break; + } + return PCIBIOS_SUCCESSFUL; +} + +static struct pci_ops indirect_pci_ops = +{ + indirect_read_config, + indirect_write_config +}; + +void __init +setup_indirect_pci_nomap(struct pci_controller* hose, void __iomem * cfg_addr, + void __iomem * cfg_data) +{ + hose->cfg_addr = cfg_addr; + hose->cfg_data = cfg_data; + hose->ops = &indirect_pci_ops; +} + +void __init +setup_indirect_pci(struct pci_controller* hose, u32 cfg_addr, u32 cfg_data) +{ + unsigned long base = cfg_addr & PAGE_MASK; + void __iomem *mbase, *addr, *data; + + mbase = ioremap(base, PAGE_SIZE); + addr = mbase + (cfg_addr & ~PAGE_MASK); + if ((cfg_data & PAGE_MASK) != base) + mbase = ioremap(cfg_data & PAGE_MASK, PAGE_SIZE); + data = mbase + (cfg_data & ~PAGE_MASK); + setup_indirect_pci_nomap(hose, addr, data); +} diff --git a/arch/ppc/syslib/virtex_devices.c b/arch/ppc/syslib/virtex_devices.c index 16546788e23..ace4ec08de5 100644 --- a/arch/ppc/syslib/virtex_devices.c +++ b/arch/ppc/syslib/virtex_devices.c @@ -71,6 +71,21 @@ }, \ } +/* + * ML300/ML403 Video Device: shortcut macro for single instance + */ +#define XPAR_TFT(num) { \ + .name = "xilinxfb", \ + .id = num, \ + .num_resources = 1, \ + .resource = (struct resource[]) { \ + { \ + .start = XPAR_TFT_##num##_BASEADDR, \ + .end = XPAR_TFT_##num##_BASEADDR+7, \ + .flags = IORESOURCE_IO, \ + }, \ + }, \ +} /* UART 8250 driver platform data table */ struct plat_serial8250_port virtex_serial_platform_data[] = { @@ -146,20 +161,17 @@ struct platform_device virtex_platform_devices[] = { XPAR_SYSACE(1), #endif - /* ML300/403 reference design framebuffer */ #if defined(XPAR_TFT_0_BASEADDR) - { - .name = "xilinxfb", - .id = 0, - .num_resources = 1, - .resource = (struct resource[]) { - { - .start = XPAR_TFT_0_BASEADDR, - .end = XPAR_TFT_0_BASEADDR+7, - .flags = IORESOURCE_IO, - }, - }, - }, + XPAR_TFT(0), +#endif +#if defined(XPAR_TFT_1_BASEADDR) + XPAR_TFT(1), +#endif +#if defined(XPAR_TFT_2_BASEADDR) + XPAR_TFT(2), +#endif +#if defined(XPAR_TFT_3_BASEADDR) + XPAR_TFT(3), #endif }; diff --git a/arch/ppc/syslib/virtex_devices.h b/arch/ppc/syslib/virtex_devices.h index 3d4be1412f6..9f38d92ae53 100644 --- a/arch/ppc/syslib/virtex_devices.h +++ b/arch/ppc/syslib/virtex_devices.h @@ -31,4 +31,11 @@ void __init virtex_early_serial_map(void); */ int virtex_device_fixup(struct platform_device *dev); +/* SPI Controller IP */ +struct xspi_platform_data { + s16 bus_num; + u16 num_chipselect; + u32 speed_hz; +}; + #endif /* __ASM_VIRTEX_DEVICES_H__ */ diff --git a/arch/s390/defconfig b/arch/s390/defconfig index 485b60c1983..2aae23dba4b 100644 --- a/arch/s390/defconfig +++ b/arch/s390/defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit -# Linux kernel version: 2.6.21 -# Thu May 10 15:18:19 2007 +# Linux kernel version: 2.6.22 +# Tue Jul 17 12:50:23 2007 # CONFIG_MMU=y CONFIG_ZONE_DMA=y @@ -32,12 +32,11 @@ CONFIG_LOCALVERSION="" CONFIG_LOCALVERSION_AUTO=y CONFIG_SWAP=y CONFIG_SYSVIPC=y -# CONFIG_IPC_NS is not set CONFIG_SYSVIPC_SYSCTL=y CONFIG_POSIX_MQUEUE=y # CONFIG_BSD_PROCESS_ACCT is not set # CONFIG_TASKSTATS is not set -# CONFIG_UTS_NS is not set +# CONFIG_USER_NS is not set CONFIG_AUDIT=y # CONFIG_AUDITSYSCALL is not set CONFIG_IKCONFIG=y @@ -61,20 +60,19 @@ CONFIG_BUG=y CONFIG_ELF_CORE=y CONFIG_BASE_FULL=y CONFIG_FUTEX=y +CONFIG_ANON_INODES=y CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y CONFIG_SHMEM=y CONFIG_VM_EVENT_COUNTERS=y -CONFIG_SLUB_DEBUG=y CONFIG_SLAB=y # CONFIG_SLUB is not set # CONFIG_SLOB is not set CONFIG_RT_MUTEXES=y # CONFIG_TINY_SHMEM is not set CONFIG_BASE_SMALL=0 - -# -# Loadable module support -# CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y # CONFIG_MODULE_FORCE_UNLOAD is not set @@ -82,12 +80,9 @@ CONFIG_MODVERSIONS=y # CONFIG_MODULE_SRCVERSION_ALL is not set CONFIG_KMOD=y CONFIG_STOP_MACHINE=y - -# -# Block layer -# CONFIG_BLOCK=y # CONFIG_BLK_DEV_IO_TRACE is not set +CONFIG_BLK_DEV_BSG=y # # IO Schedulers @@ -151,6 +146,7 @@ CONFIG_FLAT_NODE_MEM_MAP=y CONFIG_SPLIT_PTLOCK_CPUS=4 CONFIG_RESOURCES_64BIT=y CONFIG_ZONE_DMA_FLAG=1 +CONFIG_VIRT_TO_BUS=y CONFIG_HOLES_IN_ZONE=y # @@ -248,25 +244,13 @@ CONFIG_IPV6_SIT=y # CONFIG_IPV6_MULTIPLE_TABLES is not set # CONFIG_NETWORK_SECMARK is not set # CONFIG_NETFILTER is not set - -# -# DCCP Configuration (EXPERIMENTAL) -# # CONFIG_IP_DCCP is not set - -# -# SCTP Configuration (EXPERIMENTAL) -# CONFIG_IP_SCTP=m # CONFIG_SCTP_DBG_MSG is not set # CONFIG_SCTP_DBG_OBJCNT is not set # CONFIG_SCTP_HMAC_NONE is not set # CONFIG_SCTP_HMAC_SHA1 is not set CONFIG_SCTP_HMAC_MD5=y - -# -# TIPC Configuration (EXPERIMENTAL) -# # CONFIG_TIPC is not set # CONFIG_ATM is not set # CONFIG_BRIDGE is not set @@ -293,6 +277,7 @@ CONFIG_NET_SCH_CBQ=m # CONFIG_NET_SCH_HTB is not set # CONFIG_NET_SCH_HFSC is not set CONFIG_NET_SCH_PRIO=m +CONFIG_NET_SCH_RR=m CONFIG_NET_SCH_RED=m CONFIG_NET_SCH_SFQ=m CONFIG_NET_SCH_TEQL=m @@ -317,10 +302,14 @@ CONFIG_CLS_U32_MARK=y CONFIG_NET_CLS_RSVP=m CONFIG_NET_CLS_RSVP6=m # CONFIG_NET_EMATCH is not set -# CONFIG_NET_CLS_ACT is not set +CONFIG_NET_CLS_ACT=y +CONFIG_NET_ACT_POLICE=y +# CONFIG_NET_ACT_GACT is not set +# CONFIG_NET_ACT_MIRRED is not set +# CONFIG_NET_ACT_PEDIT is not set +# CONFIG_NET_ACT_SIMP is not set CONFIG_NET_CLS_POLICE=y # CONFIG_NET_CLS_IND is not set -CONFIG_NET_ESTIMATOR=y # # Network testing @@ -329,6 +318,7 @@ CONFIG_NET_ESTIMATOR=y # CONFIG_NET_TCPPROBE is not set # CONFIG_AF_RXRPC is not set # CONFIG_RFKILL is not set +# CONFIG_NET_9P is not set # CONFIG_PCMCIA is not set CONFIG_CCW=y @@ -345,15 +335,8 @@ CONFIG_PREVENT_FIRMWARE_BUILD=y # CONFIG_DEBUG_DRIVER is not set # CONFIG_DEBUG_DEVRES is not set CONFIG_SYS_HYPERVISOR=y - -# -# Connector - unified userspace <-> kernelspace linker -# # CONFIG_CONNECTOR is not set - -# -# Block devices -# +CONFIG_BLK_DEV=y # CONFIG_BLK_DEV_COW_COMMON is not set CONFIG_BLK_DEV_LOOP=m # CONFIG_BLK_DEV_CRYPTOLOOP is not set @@ -376,17 +359,15 @@ CONFIG_DASD_ECKD=y CONFIG_DASD_FBA=y CONFIG_DASD_DIAG=y CONFIG_DASD_EER=y - -# -# Misc devices -# -# CONFIG_BLINK is not set +CONFIG_MISC_DEVICES=y +# CONFIG_EEPROM_93CX6 is not set # # SCSI device support # # CONFIG_RAID_ATTRS is not set CONFIG_SCSI=y +# CONFIG_SCSI_DMA is not set # CONFIG_SCSI_TGT is not set CONFIG_SCSI_NETLINK=y CONFIG_SCSI_PROC_FS=y @@ -447,40 +428,21 @@ CONFIG_DM_MIRROR=y CONFIG_DM_ZERO=y CONFIG_DM_MULTIPATH=y # CONFIG_DM_MULTIPATH_EMC is not set +# CONFIG_DM_MULTIPATH_RDAC is not set # CONFIG_DM_DELAY is not set - -# -# Network device support -# CONFIG_NETDEVICES=y +# CONFIG_NETDEVICES_MULTIQUEUE is not set +# CONFIG_IFB is not set CONFIG_DUMMY=m CONFIG_BONDING=m +# CONFIG_MACVLAN is not set CONFIG_EQUALIZER=m CONFIG_TUN=m - -# -# Ethernet (10 or 100Mbit) -# CONFIG_NET_ETHERNET=y # CONFIG_MII is not set - -# -# Ethernet (1000 Mbit) -# - -# -# Ethernet (10000 Mbit) -# -CONFIG_MLX4_DEBUG=y - -# -# Token Ring devices -# +CONFIG_NETDEV_1000=y +CONFIG_NETDEV_10000=y # CONFIG_TR is not set - -# -# Wan interfaces -# # CONFIG_WAN is not set # @@ -511,10 +473,6 @@ CONFIG_CCWGROUP=y CONFIG_UNIX98_PTYS=y CONFIG_LEGACY_PTYS=y CONFIG_LEGACY_PTY_COUNT=256 - -# -# Watchdog Cards -# # CONFIG_WATCHDOG is not set CONFIG_HW_RANDOM=m # CONFIG_R3964 is not set @@ -554,6 +512,8 @@ CONFIG_S390_TAPE_34XX=m # CONFIG_VMCP is not set # CONFIG_MONREADER is not set CONFIG_MONWRITER=m +CONFIG_S390_VMUR=m +# CONFIG_POWER_SUPPLY is not set # # File systems @@ -655,7 +615,6 @@ CONFIG_SUNRPC=y # CONFIG_NCP_FS is not set # CONFIG_CODA_FS is not set # CONFIG_AFS_FS is not set -# CONFIG_9P_FS is not set # # Partition Types @@ -712,6 +671,7 @@ CONFIG_MAGIC_SYSRQ=y CONFIG_DEBUG_FS=y CONFIG_HEADERS_CHECK=y CONFIG_DEBUG_KERNEL=y +# CONFIG_SCHED_DEBUG is not set # CONFIG_SCHEDSTATS is not set # CONFIG_TIMER_STATS is not set # CONFIG_DEBUG_SLAB is not set @@ -740,10 +700,6 @@ CONFIG_FORCED_INLINING=y # # CONFIG_KEYS is not set # CONFIG_SECURITY is not set - -# -# Cryptographic options -# CONFIG_CRYPTO=y CONFIG_CRYPTO_ALGAPI=y CONFIG_CRYPTO_BLKCIPHER=y @@ -782,10 +738,7 @@ CONFIG_CRYPTO_FCRYPT=m # CONFIG_CRYPTO_CRC32C is not set CONFIG_CRYPTO_CAMELLIA=m # CONFIG_CRYPTO_TEST is not set - -# -# Hardware crypto devices -# +CONFIG_CRYPTO_HW=y # CONFIG_CRYPTO_SHA1_S390 is not set # CONFIG_CRYPTO_SHA256_S390 is not set # CONFIG_CRYPTO_DES_S390 is not set @@ -800,6 +753,7 @@ CONFIG_ZCRYPT=m CONFIG_BITREVERSE=m # CONFIG_CRC_CCITT is not set # CONFIG_CRC16 is not set +# CONFIG_CRC_ITU_T is not set CONFIG_CRC32=m # CONFIG_LIBCRC32C is not set CONFIG_PLIST=y diff --git a/arch/s390/kernel/dis.c b/arch/s390/kernel/dis.c index a057ebf108a..d3057318f2b 100644 --- a/arch/s390/kernel/dis.c +++ b/arch/s390/kernel/dis.c @@ -240,8 +240,8 @@ static const unsigned char formats[][7] = { [INSTR_RXY_FRRD] = { 0xff, F_8,D20_20,X_12,B_16,0,0 },/* e.g. ley */ [INSTR_RX_FRRD] = { 0xff, F_8,D_20,X_12,B_16,0,0 }, /* e.g. ae */ [INSTR_RX_RRRD] = { 0xff, R_8,D_20,X_12,B_16,0,0 }, /* e.g. l */ - [INSTR_RX_URRD] = { 0x00, U4_8,D_20,X_12,B_16,0,0 }, /* e.g. bc */ - [INSTR_SI_URD] = { 0x00, D_20,B_16,U8_8,0,0,0 }, /* e.g. cli */ + [INSTR_RX_URRD] = { 0xff, U4_8,D_20,X_12,B_16,0,0 }, /* e.g. bc */ + [INSTR_SI_URD] = { 0xff, D_20,B_16,U8_8,0,0,0 }, /* e.g. cli */ [INSTR_SIY_URD] = { 0xff, D20_20,B_16,U8_8,0,0,0 }, /* e.g. tmy */ [INSTR_SSE_RDRD] = { 0xff, D_20,B_16,D_36,B_32,0,0 }, /* e.g. mvsdk */ [INSTR_SS_L0RDRD] = { 0xff, D_20,L8_8,B_16,D_36,B_32,0 }, @@ -1190,7 +1190,8 @@ static int print_insn(char *buffer, unsigned char *code, unsigned long addr) else if (operand->flags & OPERAND_CR) ptr += sprintf(ptr, "%%c%i", value); else if (operand->flags & OPERAND_PCREL) - ptr += sprintf(ptr, "%lx", value + addr); + ptr += sprintf(ptr, "%lx", (signed int) value + + addr); else if (operand->flags & OPERAND_SIGNED) ptr += sprintf(ptr, "%i", value); else diff --git a/arch/s390/kernel/ptrace.c b/arch/s390/kernel/ptrace.c index 2a8f0872ea8..f4503ca2763 100644 --- a/arch/s390/kernel/ptrace.c +++ b/arch/s390/kernel/ptrace.c @@ -294,7 +294,6 @@ poke_user(struct task_struct *child, addr_t addr, addr_t data) static int do_ptrace_normal(struct task_struct *child, long request, long addr, long data) { - unsigned long tmp; ptrace_area parea; int copied, ret; @@ -304,10 +303,7 @@ do_ptrace_normal(struct task_struct *child, long request, long addr, long data) /* Remove high order bit from address (only for 31 bit). */ addr &= PSW_ADDR_INSN; /* read word at location addr. */ - copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); - if (copied != sizeof(tmp)) - return -EIO; - return put_user(tmp, (unsigned long __force __user *) data); + return generic_ptrace_peekdata(child, addr, data); case PTRACE_PEEKUSR: /* read the word at location addr in the USER area. */ @@ -318,10 +314,7 @@ do_ptrace_normal(struct task_struct *child, long request, long addr, long data) /* Remove high order bit from address (only for 31 bit). */ addr &= PSW_ADDR_INSN; /* write the word at location addr. */ - copied = access_process_vm(child, addr, &data, sizeof(data),1); - if (copied != sizeof(data)) - return -EIO; - return 0; + return generic_ptrace_pokedata(child, addr, data); case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ diff --git a/arch/s390/kernel/stacktrace.c b/arch/s390/kernel/stacktrace.c index 515ff9011dd..da692472996 100644 --- a/arch/s390/kernel/stacktrace.c +++ b/arch/s390/kernel/stacktrace.c @@ -12,7 +12,6 @@ #include <linux/kallsyms.h> static unsigned long save_context_stack(struct stack_trace *trace, - unsigned int *skip, unsigned long sp, unsigned long low, unsigned long high) @@ -28,10 +27,10 @@ static unsigned long save_context_stack(struct stack_trace *trace, sf = (struct stack_frame *)sp; while(1) { addr = sf->gprs[8] & PSW_ADDR_INSN; - if (!(*skip)) + if (!trace->skip) trace->entries[trace->nr_entries++] = addr; else - (*skip)--; + trace->skip--; if (trace->nr_entries >= trace->max_entries) return sp; low = sp; @@ -48,10 +47,10 @@ static unsigned long save_context_stack(struct stack_trace *trace, return sp; regs = (struct pt_regs *)sp; addr = regs->psw.addr & PSW_ADDR_INSN; - if (!(*skip)) + if (!trace->skip) trace->entries[trace->nr_entries++] = addr; else - (*skip)--; + trace->skip--; if (trace->nr_entries >= trace->max_entries) return sp; low = sp; @@ -65,20 +64,17 @@ void save_stack_trace(struct stack_trace *trace) unsigned long orig_sp, new_sp; orig_sp = sp & PSW_ADDR_INSN; - - new_sp = save_context_stack(trace, &trace->skip, orig_sp, - S390_lowcore.panic_stack - PAGE_SIZE, - S390_lowcore.panic_stack); + new_sp = save_context_stack(trace, orig_sp, + S390_lowcore.panic_stack - PAGE_SIZE, + S390_lowcore.panic_stack); if (new_sp != orig_sp) return; - new_sp = save_context_stack(trace, &trace->skip, new_sp, - S390_lowcore.async_stack - ASYNC_SIZE, - S390_lowcore.async_stack); + new_sp = save_context_stack(trace, new_sp, + S390_lowcore.async_stack - ASYNC_SIZE, + S390_lowcore.async_stack); if (new_sp != orig_sp) return; - - save_context_stack(trace, &trace->skip, new_sp, + save_context_stack(trace, new_sp, S390_lowcore.thread_info, S390_lowcore.thread_info + THREAD_SIZE); - return; } diff --git a/arch/s390/kernel/traps.c b/arch/s390/kernel/traps.c index 81e03b9c384..8ec9def83cc 100644 --- a/arch/s390/kernel/traps.c +++ b/arch/s390/kernel/traps.c @@ -262,6 +262,7 @@ void die(const char * str, struct pt_regs * regs, long err) print_modules(); show_regs(regs); bust_spinlocks(0); + add_taint(TAINT_DIE); spin_unlock_irq(&die_lock); if (in_interrupt()) panic("Fatal exception in interrupt"); diff --git a/arch/s390/kernel/vmlinux.lds.S b/arch/s390/kernel/vmlinux.lds.S index 7158a804a5e..6ab7d4ee13a 100644 --- a/arch/s390/kernel/vmlinux.lds.S +++ b/arch/s390/kernel/vmlinux.lds.S @@ -45,6 +45,8 @@ SECTIONS __ex_table : { *(__ex_table) } __stop___ex_table = .; + NOTES + BUG_TABLE .data : { /* Data */ @@ -107,10 +109,7 @@ SECTIONS . = ALIGN(2); __initramfs_end = .; #endif - . = ALIGN(4096); - __per_cpu_start = .; - .data.percpu : { *(.data.percpu) } - __per_cpu_end = .; + PERCPU(4096) . = ALIGN(4096); __init_end = .; /* freed after init ends here */ diff --git a/arch/s390/lib/uaccess_pt.c b/arch/s390/lib/uaccess_pt.c index 63181671e3e..60604b2819b 100644 --- a/arch/s390/lib/uaccess_pt.c +++ b/arch/s390/lib/uaccess_pt.c @@ -20,6 +20,7 @@ static int __handle_fault(struct mm_struct *mm, unsigned long address, { struct vm_area_struct *vma; int ret = -EFAULT; + int fault; if (in_atomic()) return ret; @@ -44,20 +45,18 @@ static int __handle_fault(struct mm_struct *mm, unsigned long address, } survive: - switch (handle_mm_fault(mm, vma, address, write_access)) { - case VM_FAULT_MINOR: - current->min_flt++; - break; - case VM_FAULT_MAJOR: - current->maj_flt++; - break; - case VM_FAULT_SIGBUS: - goto out_sigbus; - case VM_FAULT_OOM: - goto out_of_memory; - default: + fault = handle_mm_fault(mm, vma, address, write_access); + if (unlikely(fault & VM_FAULT_ERROR)) { + if (fault & VM_FAULT_OOM) + goto out_of_memory; + else if (fault & VM_FAULT_SIGBUS) + goto out_sigbus; BUG(); } + if (fault & VM_FAULT_MAJOR) + current->maj_flt++; + else + current->min_flt++; ret = 0; out: up_read(&mm->mmap_sem); diff --git a/arch/s390/mm/fault.c b/arch/s390/mm/fault.c index d855cdbf8fb..54055194e9a 100644 --- a/arch/s390/mm/fault.c +++ b/arch/s390/mm/fault.c @@ -307,6 +307,7 @@ do_exception(struct pt_regs *regs, unsigned long error_code, int write) unsigned long address; int space; int si_code; + int fault; if (notify_page_fault(regs, error_code)) return; @@ -377,23 +378,22 @@ survive: * make sure we exit gracefully rather than endlessly redo * the fault. */ - switch (handle_mm_fault(mm, vma, address, write)) { - case VM_FAULT_MINOR: - tsk->min_flt++; - break; - case VM_FAULT_MAJOR: - tsk->maj_flt++; - break; - case VM_FAULT_SIGBUS: - do_sigbus(regs, error_code, address); - return; - case VM_FAULT_OOM: - if (do_out_of_memory(regs, error_code, address)) - goto survive; - return; - default: + fault = handle_mm_fault(mm, vma, address, write); + if (unlikely(fault & VM_FAULT_ERROR)) { + if (fault & VM_FAULT_OOM) { + if (do_out_of_memory(regs, error_code, address)) + goto survive; + return; + } else if (fault & VM_FAULT_SIGBUS) { + do_sigbus(regs, error_code, address); + return; + } BUG(); } + if (fault & VM_FAULT_MAJOR) + tsk->maj_flt++; + else + tsk->min_flt++; up_read(&mm->mmap_sem); /* diff --git a/arch/sh/kernel/ptrace.c b/arch/sh/kernel/ptrace.c index f2eaa485d04..891d1d46c90 100644 --- a/arch/sh/kernel/ptrace.c +++ b/arch/sh/kernel/ptrace.c @@ -91,17 +91,8 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) switch (request) { /* when I and D space are separate, these will need to be fixed. */ case PTRACE_PEEKTEXT: /* read word at location addr. */ - case PTRACE_PEEKDATA: { - unsigned long tmp; - int copied; - - copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); - ret = -EIO; - if (copied != sizeof(tmp)) - break; - ret = put_user(tmp,(unsigned long __user *) data); - break; - } + case PTRACE_PEEKDATA: + ret = generic_ptrace_peekdata(child, addr, data); /* read the word at location addr in the USER area. */ case PTRACE_PEEKUSR: { @@ -135,10 +126,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) /* when I and D space are separate, this will have to be fixed. */ case PTRACE_POKETEXT: /* write the word at location addr. */ case PTRACE_POKEDATA: - ret = 0; - if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data)) - break; - ret = -EIO; + ret = generic_ptrace_pokedata(child, addr, data); break; case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ diff --git a/arch/sh/kernel/traps.c b/arch/sh/kernel/traps.c index 05a40f3c30b..502d43e4785 100644 --- a/arch/sh/kernel/traps.c +++ b/arch/sh/kernel/traps.c @@ -103,6 +103,7 @@ void die(const char * str, struct pt_regs * regs, long err) (unsigned long)task_stack_page(current)); bust_spinlocks(0); + add_taint(TAINT_DIE); spin_unlock_irq(&die_lock); if (kexec_should_crash(current)) diff --git a/arch/sh/kernel/vmlinux.lds.S b/arch/sh/kernel/vmlinux.lds.S index 0696402f446..5ba216180b3 100644 --- a/arch/sh/kernel/vmlinux.lds.S +++ b/arch/sh/kernel/vmlinux.lds.S @@ -60,10 +60,7 @@ SECTIONS . = ALIGN(PAGE_SIZE); __nosave_end = .; - . = ALIGN(PAGE_SIZE); - __per_cpu_start = .; - .data.percpu : { *(.data.percpu) } - __per_cpu_end = .; + PERCPU(PAGE_SIZE) .data.cacheline_aligned : { *(.data.cacheline_aligned) } _edata = .; /* End of data section */ diff --git a/arch/sh/mm/fault.c b/arch/sh/mm/fault.c index 0b3eaf6fbb2..964c6767dc7 100644 --- a/arch/sh/mm/fault.c +++ b/arch/sh/mm/fault.c @@ -33,6 +33,7 @@ asmlinkage void __kprobes do_page_fault(struct pt_regs *regs, struct mm_struct *mm; struct vm_area_struct * vma; int si_code; + int fault; siginfo_t info; trace_hardirqs_on(); @@ -124,20 +125,18 @@ good_area: * the fault. */ survive: - switch (handle_mm_fault(mm, vma, address, writeaccess)) { - case VM_FAULT_MINOR: - tsk->min_flt++; - break; - case VM_FAULT_MAJOR: - tsk->maj_flt++; - break; - case VM_FAULT_SIGBUS: - goto do_sigbus; - case VM_FAULT_OOM: + fault = handle_mm_fault(mm, vma, address, writeaccess); + if (unlikely(fault & VM_FAULT_ERROR)) { + if (fault & VM_FAULT_OOM) goto out_of_memory; - default: - BUG(); + else if (fault & VM_FAULT_SIGBUS) + goto do_sigbus; + BUG(); } + if (fault & VM_FAULT_MAJOR) + tsk->maj_flt++; + else + tsk->min_flt++; up_read(&mm->mmap_sem); return; diff --git a/arch/sh64/kernel/ptrace.c b/arch/sh64/kernel/ptrace.c index 4e95e18b46d..df06c647746 100644 --- a/arch/sh64/kernel/ptrace.c +++ b/arch/sh64/kernel/ptrace.c @@ -129,17 +129,9 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) switch (request) { /* when I and D space are separate, these will need to be fixed. */ case PTRACE_PEEKTEXT: /* read word at location addr. */ - case PTRACE_PEEKDATA: { - unsigned long tmp; - int copied; - - copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); - ret = -EIO; - if (copied != sizeof(tmp)) - break; - ret = put_user(tmp,(unsigned long *) data); + case PTRACE_PEEKDATA: + ret = generic_ptrace_peekdata(child, addr, data); break; - } /* read the word at location addr in the USER area. */ case PTRACE_PEEKUSR: { @@ -166,10 +158,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) /* when I and D space are separate, this will have to be fixed. */ case PTRACE_POKETEXT: /* write the word at location addr. */ case PTRACE_POKEDATA: - ret = 0; - if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data)) - break; - ret = -EIO; + ret = generic_ptrace_pokedata(child, addr, data); break; case PTRACE_POKEUSR: diff --git a/arch/sh64/kernel/vmlinux.lds.S b/arch/sh64/kernel/vmlinux.lds.S index 02aea86c590..8ac9c7c5f84 100644 --- a/arch/sh64/kernel/vmlinux.lds.S +++ b/arch/sh64/kernel/vmlinux.lds.S @@ -87,7 +87,10 @@ SECTIONS . = ALIGN(PAGE_SIZE); __per_cpu_start = .; - .data.percpu : C_PHYS(.data.percpu) { *(.data.percpu) } + .data.percpu : C_PHYS(.data.percpu) { + *(.data.percpu) + *(.data.percpu.shared_aligned) + } __per_cpu_end = . ; .data.cacheline_aligned : C_PHYS(.data.cacheline_aligned) { *(.data.cacheline_aligned) } diff --git a/arch/sh64/lib/c-checksum.c b/arch/sh64/lib/c-checksum.c index 4b2676380de..bd550176024 100644 --- a/arch/sh64/lib/c-checksum.c +++ b/arch/sh64/lib/c-checksum.c @@ -213,3 +213,4 @@ __wsum csum_tcpudp_nofold(__be32 saddr, __be32 daddr, return (__wsum)result; } +EXPORT_SYMBOL(csum_tcpudp_nofold); diff --git a/arch/sh64/mm/fault.c b/arch/sh64/mm/fault.c index 3cd93ba5d82..0d069d82141 100644 --- a/arch/sh64/mm/fault.c +++ b/arch/sh64/mm/fault.c @@ -127,6 +127,7 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long writeaccess, struct vm_area_struct * vma; const struct exception_table_entry *fixup; pte_t *pte; + int fault; #if defined(CONFIG_SH64_PROC_TLB) ++calls_to_do_slow_page_fault; @@ -221,18 +222,19 @@ good_area: * the fault. */ survive: - switch (handle_mm_fault(mm, vma, address, writeaccess)) { - case VM_FAULT_MINOR: - tsk->min_flt++; - break; - case VM_FAULT_MAJOR: - tsk->maj_flt++; - break; - case VM_FAULT_SIGBUS: - goto do_sigbus; - default: - goto out_of_memory; + fault = handle_mm_fault(mm, vma, address, writeaccess); + if (unlikely(fault & VM_FAULT_ERROR)) { + if (fault & VM_FAULT_OOM) + goto out_of_memory; + else if (fault & VM_FAULT_SIGBUS) + goto do_sigbus; + BUG(); } + if (fault & VM_FAULT_MAJOR) + tsk->maj_flt++; + else + tsk->min_flt++; + /* If we get here, the page fault has been handled. Do the TLB refill now from the newly-setup PTE, to avoid having to fault again right away on the same instruction. */ diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig index 8567cc90194..603d83ad65c 100644 --- a/arch/sparc/Kconfig +++ b/arch/sparc/Kconfig @@ -21,6 +21,9 @@ config GENERIC_ISA_DMA bool default y +config ARCH_NO_VIRT_TO_BUS + def_bool y + source "init/Kconfig" menu "General machine setup" @@ -217,6 +220,9 @@ source "drivers/pci/Kconfig" endif +config NO_DMA + def_bool !PCI + config SUN_OPENPROMFS tristate "Openprom tree appears in /proc/openprom" help diff --git a/arch/sparc/kernel/traps.c b/arch/sparc/kernel/traps.c index dc9ffea2a4f..3bc3bff51e0 100644 --- a/arch/sparc/kernel/traps.c +++ b/arch/sparc/kernel/traps.c @@ -101,6 +101,7 @@ void die_if_kernel(char *str, struct pt_regs *regs) printk("%s(%d): %s [#%d]\n", current->comm, current->pid, str, ++die_counter); show_regs(regs); + add_taint(TAINT_DIE); __SAVE; __SAVE; __SAVE; __SAVE; __SAVE; __SAVE; __SAVE; __SAVE; diff --git a/arch/sparc/kernel/vmlinux.lds.S b/arch/sparc/kernel/vmlinux.lds.S index f75a1b82278..47583887abc 100644 --- a/arch/sparc/kernel/vmlinux.lds.S +++ b/arch/sparc/kernel/vmlinux.lds.S @@ -65,10 +65,7 @@ SECTIONS __initramfs_end = .; #endif - . = ALIGN(4096); - __per_cpu_start = .; - .data.percpu : { *(.data.percpu) } - __per_cpu_end = .; + PERCPU(4096) . = ALIGN(4096); __init_end = .; . = ALIGN(32); diff --git a/arch/sparc/mm/fault.c b/arch/sparc/mm/fault.c index c3483365db4..50747fe4435 100644 --- a/arch/sparc/mm/fault.c +++ b/arch/sparc/mm/fault.c @@ -226,6 +226,7 @@ asmlinkage void do_sparc_fault(struct pt_regs *regs, int text_fault, int write, unsigned long g2; siginfo_t info; int from_user = !(regs->psr & PSR_PS); + int fault; if(text_fault) address = regs->pc; @@ -289,19 +290,18 @@ good_area: * make sure we exit gracefully rather than endlessly redo * the fault. */ - switch (handle_mm_fault(mm, vma, address, write)) { - case VM_FAULT_SIGBUS: - goto do_sigbus; - case VM_FAULT_OOM: - goto out_of_memory; - case VM_FAULT_MAJOR: + fault = handle_mm_fault(mm, vma, address, write); + if (unlikely(fault & VM_FAULT_ERROR)) { + if (fault & VM_FAULT_OOM) + goto out_of_memory; + else if (fault & VM_FAULT_SIGBUS) + goto do_sigbus; + BUG(); + } + if (fault & VM_FAULT_MAJOR) current->maj_flt++; - break; - case VM_FAULT_MINOR: - default: + else current->min_flt++; - break; - } up_read(&mm->mmap_sem); return; diff --git a/arch/sparc64/Kconfig b/arch/sparc64/Kconfig index b84b6af1241..df6ee71894d 100644 --- a/arch/sparc64/Kconfig +++ b/arch/sparc64/Kconfig @@ -62,6 +62,9 @@ config AUDIT_ARCH bool default y +config ARCH_NO_VIRT_TO_BUS + def_bool y + choice prompt "Kernel page size" default SPARC64_PAGE_SIZE_8KB diff --git a/arch/sparc64/defconfig b/arch/sparc64/defconfig index 65840a62bb9..45ebf91a280 100644 --- a/arch/sparc64/defconfig +++ b/arch/sparc64/defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit -# Linux kernel version: 2.6.22-rc1 -# Mon May 14 04:17:48 2007 +# Linux kernel version: 2.6.22 +# Tue Jul 17 01:19:52 2007 # CONFIG_SPARC=y CONFIG_SPARC64=y @@ -42,12 +42,11 @@ CONFIG_LOCALVERSION="" # CONFIG_LOCALVERSION_AUTO is not set CONFIG_SWAP=y CONFIG_SYSVIPC=y -# CONFIG_IPC_NS is not set CONFIG_SYSVIPC_SYSCTL=y CONFIG_POSIX_MQUEUE=y # CONFIG_BSD_PROCESS_ACCT is not set # CONFIG_TASKSTATS is not set -# CONFIG_UTS_NS is not set +# CONFIG_USER_NS is not set # CONFIG_AUDIT is not set # CONFIG_IKCONFIG is not set CONFIG_LOG_BUF_SHIFT=18 @@ -82,22 +81,15 @@ CONFIG_SLUB=y CONFIG_RT_MUTEXES=y # CONFIG_TINY_SHMEM is not set CONFIG_BASE_SMALL=0 - -# -# Loadable module support -# CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y CONFIG_MODULE_FORCE_UNLOAD=y CONFIG_MODVERSIONS=y CONFIG_MODULE_SRCVERSION_ALL=y CONFIG_KMOD=y - -# -# Block layer -# CONFIG_BLOCK=y CONFIG_BLK_DEV_IO_TRACE=y +CONFIG_BLK_DEV_BSG=y # # IO Schedulers @@ -156,12 +148,15 @@ CONFIG_SPLIT_PTLOCK_CPUS=4 CONFIG_RESOURCES_64BIT=y CONFIG_ZONE_DMA_FLAG=0 CONFIG_NR_QUICK=1 +CONFIG_VIRT_TO_BUS=y CONFIG_SBUS=y CONFIG_SBUSCHAR=y CONFIG_SUN_AUXIO=y CONFIG_SUN_IO=y +# CONFIG_SUN_LDOMS is not set CONFIG_PCI=y CONFIG_PCI_DOMAINS=y +CONFIG_PCI_SYSCALL=y CONFIG_ARCH_SUPPORTS_MSI=y CONFIG_PCI_MSI=y # CONFIG_PCI_DEBUG is not set @@ -246,10 +241,6 @@ CONFIG_IPV6_TUNNEL=m # CONFIG_IPV6_MULTIPLE_TABLES is not set # CONFIG_NETWORK_SECMARK is not set # CONFIG_NETFILTER is not set - -# -# DCCP Configuration (EXPERIMENTAL) -# CONFIG_IP_DCCP=m CONFIG_INET_DCCP_DIAG=m CONFIG_IP_DCCP_ACKVEC=y @@ -269,15 +260,7 @@ CONFIG_IP_DCCP_CCID3_RTO=100 # # CONFIG_IP_DCCP_DEBUG is not set # CONFIG_NET_DCCPPROBE is not set - -# -# SCTP Configuration (EXPERIMENTAL) -# # CONFIG_IP_SCTP is not set - -# -# TIPC Configuration (EXPERIMENTAL) -# # CONFIG_TIPC is not set # CONFIG_ATM is not set # CONFIG_BRIDGE is not set @@ -314,6 +297,7 @@ CONFIG_NET_TCPPROBE=m # CONFIG_MAC80211 is not set # CONFIG_IEEE80211 is not set # CONFIG_RFKILL is not set +# CONFIG_NET_9P is not set # # Device Drivers @@ -328,26 +312,10 @@ CONFIG_FW_LOADER=y # CONFIG_DEBUG_DRIVER is not set # CONFIG_DEBUG_DEVRES is not set # CONFIG_SYS_HYPERVISOR is not set - -# -# Connector - unified userspace <-> kernelspace linker -# CONFIG_CONNECTOR=m # CONFIG_MTD is not set - -# -# Parallel port support -# # CONFIG_PARPORT is not set - -# -# Plug and Play support -# -# CONFIG_PNPACPI is not set - -# -# Block devices -# +CONFIG_BLK_DEV=y # CONFIG_BLK_DEV_FD is not set # CONFIG_BLK_CPQ_DA is not set # CONFIG_BLK_CPQ_CISS_DA is not set @@ -364,18 +332,11 @@ CONFIG_CDROM_PKTCDVD=m CONFIG_CDROM_PKTCDVD_BUFFERS=8 CONFIG_CDROM_PKTCDVD_WCACHE=y CONFIG_ATA_OVER_ETH=m - -# -# Misc devices -# +CONFIG_MISC_DEVICES=y # CONFIG_PHANTOM is not set +# CONFIG_EEPROM_93CX6 is not set # CONFIG_SGI_IOC4 is not set # CONFIG_TIFM_CORE is not set -# CONFIG_BLINK is not set - -# -# ATA/ATAPI/MFM/RLL support -# CONFIG_IDE=y CONFIG_BLK_DEV_IDE=y @@ -440,6 +401,7 @@ CONFIG_BLK_DEV_IDEDMA=y # CONFIG_RAID_ATTRS=m CONFIG_SCSI=y +CONFIG_SCSI_DMA=y # CONFIG_SCSI_TGT is not set CONFIG_SCSI_NETLINK=y CONFIG_SCSI_PROC_FS=y @@ -505,7 +467,6 @@ CONFIG_ISCSI_TCP=m # CONFIG_SCSI_DC395x is not set # CONFIG_SCSI_DC390T is not set # CONFIG_SCSI_DEBUG is not set -# CONFIG_SCSI_ESP_CORE is not set # CONFIG_SCSI_SUNESP is not set # CONFIG_SCSI_SRP is not set # CONFIG_ATA is not set @@ -545,30 +506,16 @@ CONFIG_DM_ZERO=m # # CONFIG_FIREWIRE is not set # CONFIG_IEEE1394 is not set - -# -# I2O device support -# # CONFIG_I2O is not set - -# -# Network device support -# CONFIG_NETDEVICES=y +# CONFIG_NETDEVICES_MULTIQUEUE is not set CONFIG_DUMMY=m # CONFIG_BONDING is not set +# CONFIG_MACVLAN is not set # CONFIG_EQUALIZER is not set # CONFIG_TUN is not set - -# -# ARCnet devices -# # CONFIG_ARCNET is not set # CONFIG_PHYLIB is not set - -# -# Ethernet (10 or 100Mbit) -# CONFIG_NET_ETHERNET=y CONFIG_MII=m # CONFIG_SUNLANCE is not set @@ -578,10 +525,6 @@ CONFIG_MII=m # CONFIG_SUNGEM is not set CONFIG_CASSINI=m # CONFIG_NET_VENDOR_3COM is not set - -# -# Tulip family network device support -# # CONFIG_NET_TULIP is not set # CONFIG_HP100 is not set CONFIG_NET_PCI=y @@ -617,7 +560,6 @@ CONFIG_E1000_NAPI=y # CONFIG_SIS190 is not set # CONFIG_SKGE is not set # CONFIG_SKY2 is not set -# CONFIG_SK98LIN is not set # CONFIG_VIA_VELOCITY is not set CONFIG_TIGON3=m CONFIG_BNX2=m @@ -631,11 +573,6 @@ CONFIG_NETDEV_10000=y # CONFIG_MYRI10GE is not set # CONFIG_NETXEN_NIC is not set # CONFIG_MLX4_CORE is not set -CONFIG_MLX4_DEBUG=y - -# -# Token Ring devices -# # CONFIG_TR is not set # @@ -665,6 +602,7 @@ CONFIG_PPP_DEFLATE=m CONFIG_PPP_BSDCOMP=m CONFIG_PPP_MPPE=m CONFIG_PPPOE=m +# CONFIG_PPPOL2TP is not set # CONFIG_SLIP is not set CONFIG_SLHC=m # CONFIG_NET_FC is not set @@ -677,10 +615,6 @@ CONFIG_SLHC=m # ISDN subsystem # # CONFIG_ISDN is not set - -# -# Telephony Support -# # CONFIG_PHONE is not set # @@ -688,6 +622,7 @@ CONFIG_SLHC=m # CONFIG_INPUT=y # CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set # # Userland interfaces @@ -733,7 +668,6 @@ CONFIG_INPUT_SPARCSPKR=y # CONFIG_INPUT_POWERMATE is not set # CONFIG_INPUT_YEALINK is not set # CONFIG_INPUT_UINPUT is not set -# CONFIG_INPUT_POLLDEV is not set # # Hardware I/O ports @@ -773,10 +707,6 @@ CONFIG_SERIAL_CORE_CONSOLE=y # CONFIG_SERIAL_JSM is not set CONFIG_UNIX98_PTYS=y # CONFIG_LEGACY_PTYS is not set - -# -# IPMI -# # CONFIG_IPMI_HANDLER is not set # CONFIG_WATCHDOG is not set # CONFIG_HW_RANDOM is not set @@ -785,10 +715,6 @@ CONFIG_RTC=y # CONFIG_APPLICOM is not set # CONFIG_DRM is not set # CONFIG_RAW_DRIVER is not set - -# -# TPM devices -# # CONFIG_TCG_TPM is not set CONFIG_DEVPORT=y CONFIG_I2C=y @@ -822,6 +748,7 @@ CONFIG_I2C_ALGOBIT=y # CONFIG_I2C_SIS5595 is not set # CONFIG_I2C_SIS630 is not set # CONFIG_I2C_SIS96X is not set +# CONFIG_I2C_TAOS_EVM is not set # CONFIG_I2C_STUB is not set # CONFIG_I2C_TINY_USB is not set # CONFIG_I2C_VIA is not set @@ -833,11 +760,13 @@ CONFIG_I2C_ALGOBIT=y # # CONFIG_SENSORS_DS1337 is not set # CONFIG_SENSORS_DS1374 is not set +# CONFIG_DS1682 is not set # CONFIG_SENSORS_EEPROM is not set # CONFIG_SENSORS_PCF8574 is not set # CONFIG_SENSORS_PCA9539 is not set # CONFIG_SENSORS_PCF8591 is not set # CONFIG_SENSORS_MAX6875 is not set +# CONFIG_SENSORS_TSL2550 is not set # CONFIG_I2C_DEBUG_CORE is not set # CONFIG_I2C_DEBUG_ALGO is not set # CONFIG_I2C_DEBUG_BUS is not set @@ -848,11 +777,8 @@ CONFIG_I2C_ALGOBIT=y # # CONFIG_SPI is not set # CONFIG_SPI_MASTER is not set - -# -# Dallas's 1-wire bus -# # CONFIG_W1 is not set +# CONFIG_POWER_SUPPLY is not set CONFIG_HWMON=y # CONFIG_HWMON_VID is not set # CONFIG_SENSORS_ABITUGURU is not set @@ -949,6 +875,8 @@ CONFIG_FB_TILEBLITTING=y # CONFIG_FB_ASILIANT is not set # CONFIG_FB_IMSTT is not set # CONFIG_FB_SBUS is not set +# CONFIG_FB_XVR500 is not set +# CONFIG_FB_XVR2500 is not set # CONFIG_FB_S1D13XXX is not set # CONFIG_FB_NVIDIA is not set # CONFIG_FB_RIVA is not set @@ -970,9 +898,6 @@ CONFIG_FB_RADEON_I2C=y # CONFIG_FB_TRIDENT is not set # CONFIG_FB_ARK is not set # CONFIG_FB_PM3 is not set -# CONFIG_FB_XVR500 is not set -# CONFIG_FB_XVR2500 is not set -# CONFIG_FB_PCI is not set # CONFIG_FB_VIRTUAL is not set # @@ -1118,10 +1043,7 @@ CONFIG_SND_SUN_CS4231=m # # CONFIG_SOUND_PRIME is not set CONFIG_AC97_BUS=m - -# -# HID Devices -# +CONFIG_HID_SUPPORT=y CONFIG_HID=y # CONFIG_HID_DEBUG is not set @@ -1132,10 +1054,7 @@ CONFIG_USB_HID=y # CONFIG_USB_HIDINPUT_POWERBOOK is not set # CONFIG_HID_FF is not set CONFIG_USB_HIDDEV=y - -# -# USB support -# +CONFIG_USB_SUPPORT=y CONFIG_USB_ARCH_HAS_HCD=y CONFIG_USB_ARCH_HAS_OHCI=y CONFIG_USB_ARCH_HAS_EHCI=y @@ -1157,7 +1076,6 @@ CONFIG_USB_EHCI_HCD=m # CONFIG_USB_EHCI_SPLIT_ISO is not set # CONFIG_USB_EHCI_ROOT_HUB_TT is not set # CONFIG_USB_EHCI_TT_NEWSCHED is not set -# CONFIG_USB_EHCI_BIG_ENDIAN_MMIO is not set # CONFIG_USB_ISP116X_HCD is not set CONFIG_USB_OHCI_HCD=y # CONFIG_USB_OHCI_BIG_ENDIAN_DESC is not set @@ -1165,6 +1083,7 @@ CONFIG_USB_OHCI_HCD=y CONFIG_USB_OHCI_LITTLE_ENDIAN=y CONFIG_USB_UHCI_HCD=m # CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_R8A66597_HCD is not set # # USB Device Class drivers @@ -1256,17 +1175,9 @@ CONFIG_USB_STORAGE=m # # LED Triggers # - -# -# InfiniBand support -# # CONFIG_INFINIBAND is not set # -# EDAC - error detection and reporting (RAS) (EXPERIMENTAL) -# - -# # Real Time Clock # # CONFIG_RTC_CLASS is not set @@ -1387,7 +1298,6 @@ CONFIG_RAMFS=y # CONFIG_NCP_FS is not set # CONFIG_CODA_FS is not set # CONFIG_AFS_FS is not set -# CONFIG_9P_FS is not set # # Partition Types @@ -1465,8 +1375,10 @@ CONFIG_DEBUG_FS=y CONFIG_DEBUG_KERNEL=y # CONFIG_DEBUG_SHIRQ is not set CONFIG_DETECT_SOFTLOCKUP=y +# CONFIG_SCHED_DEBUG is not set CONFIG_SCHEDSTATS=y # CONFIG_TIMER_STATS is not set +# CONFIG_SLUB_DEBUG_ON is not set # CONFIG_DEBUG_RT_MUTEXES is not set # CONFIG_RT_MUTEX_TESTER is not set # CONFIG_DEBUG_SPINLOCK is not set @@ -1496,10 +1408,10 @@ CONFIG_FORCED_INLINING=y CONFIG_KEYS=y # CONFIG_KEYS_DEBUG_PROC_KEYS is not set # CONFIG_SECURITY is not set - -# -# Cryptographic options -# +CONFIG_XOR_BLOCKS=m +CONFIG_ASYNC_CORE=m +CONFIG_ASYNC_MEMCPY=m +CONFIG_ASYNC_XOR=m CONFIG_CRYPTO=y CONFIG_CRYPTO_ALGAPI=y CONFIG_CRYPTO_BLKCIPHER=y @@ -1539,10 +1451,7 @@ CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_CRC32C=m CONFIG_CRYPTO_CAMELLIA=m CONFIG_CRYPTO_TEST=m - -# -# Hardware crypto devices -# +CONFIG_CRYPTO_HW=y # # Library routines diff --git a/arch/sparc64/kernel/ds.c b/arch/sparc64/kernel/ds.c index 1c587107cef..fa1f04d756a 100644 --- a/arch/sparc64/kernel/ds.c +++ b/arch/sparc64/kernel/ds.c @@ -228,7 +228,7 @@ static struct ds_cap_state *find_cap_by_string(const char *name) return NULL; } -static int ds_send(struct ldc_channel *lp, void *data, int len) +static int __ds_send(struct ldc_channel *lp, void *data, int len) { int err, limit = 1000; @@ -243,6 +243,18 @@ static int ds_send(struct ldc_channel *lp, void *data, int len) return err; } +static int ds_send(struct ldc_channel *lp, void *data, int len) +{ + unsigned long flags; + int err; + + spin_lock_irqsave(&ds_lock, flags); + err = __ds_send(lp, data, len); + spin_unlock_irqrestore(&ds_lock, flags); + + return err; +} + struct ds_md_update_req { __u64 req_num; }; @@ -267,6 +279,8 @@ static void md_update_data(struct ldc_channel *lp, printk(KERN_INFO PFX "Machine description update.\n"); + mdesc_update(); + memset(&pkt, 0, sizeof(pkt)); pkt.data.tag.type = DS_DATA; pkt.data.tag.len = sizeof(pkt) - sizeof(struct ds_msg_tag); @@ -275,8 +289,6 @@ static void md_update_data(struct ldc_channel *lp, pkt.res.result = DS_OK; ds_send(lp, &pkt, sizeof(pkt)); - - mdesc_update(); } struct ds_shutdown_req { @@ -391,18 +403,6 @@ struct dr_cpu_resp_entry { __u32 str_off; }; -/* DR cpu requests get queued onto the work list by the - * dr_cpu_data() callback. The list is protected by - * ds_lock, and processed by dr_cpu_process() in order. - */ -static LIST_HEAD(dr_cpu_work_list); -static DECLARE_WAIT_QUEUE_HEAD(dr_cpu_wait); - -struct dr_cpu_queue_entry { - struct list_head list; - char req[0]; -}; - static void __dr_cpu_send_error(struct ds_cap_state *cp, struct ds_data *data) { struct dr_cpu_tag *tag = (struct dr_cpu_tag *) (data + 1); @@ -425,7 +425,7 @@ static void __dr_cpu_send_error(struct ds_cap_state *cp, struct ds_data *data) pkt.data.tag.len = msg_len - sizeof(struct ds_msg_tag); - ds_send(dp->lp, &pkt, msg_len); + __ds_send(dp->lp, &pkt, msg_len); } static void dr_cpu_send_error(struct ds_cap_state *cp, struct ds_data *data) @@ -555,7 +555,7 @@ static int dr_cpu_configure(struct ds_cap_state *cp, u64 req_num, } spin_lock_irqsave(&ds_lock, flags); - ds_send(ds_info->lp, resp, resp_len); + __ds_send(ds_info->lp, resp, resp_len); spin_unlock_irqrestore(&ds_lock, flags); kfree(resp); @@ -596,7 +596,7 @@ static int dr_cpu_unconfigure(struct ds_cap_state *cp, u64 req_num, } spin_lock_irqsave(&ds_lock, flags); - ds_send(ds_info->lp, resp, resp_len); + __ds_send(ds_info->lp, resp, resp_len); spin_unlock_irqrestore(&ds_lock, flags); kfree(resp); @@ -604,107 +604,49 @@ static int dr_cpu_unconfigure(struct ds_cap_state *cp, u64 req_num, return 0; } -static void process_dr_cpu_list(struct ds_cap_state *cp) +static void dr_cpu_data(struct ldc_channel *lp, + struct ds_cap_state *cp, + void *buf, int len) { - struct dr_cpu_queue_entry *qp, *tmp; - unsigned long flags; - LIST_HEAD(todo); + struct ds_data *data = buf; + struct dr_cpu_tag *tag = (struct dr_cpu_tag *) (data + 1); + u32 *cpu_list = (u32 *) (tag + 1); + u64 req_num = tag->req_num; cpumask_t mask; + unsigned int i; + int err; - spin_lock_irqsave(&ds_lock, flags); - list_splice(&dr_cpu_work_list, &todo); - INIT_LIST_HEAD(&dr_cpu_work_list); - spin_unlock_irqrestore(&ds_lock, flags); - - list_for_each_entry_safe(qp, tmp, &todo, list) { - struct ds_data *data = (struct ds_data *) qp->req; - struct dr_cpu_tag *tag = (struct dr_cpu_tag *) (data + 1); - u32 *cpu_list = (u32 *) (tag + 1); - u64 req_num = tag->req_num; - unsigned int i; - int err; - - switch (tag->type) { - case DR_CPU_CONFIGURE: - case DR_CPU_UNCONFIGURE: - case DR_CPU_FORCE_UNCONFIGURE: - break; - - default: - dr_cpu_send_error(cp, data); - goto next; - } - - purge_dups(cpu_list, tag->num_records); - - cpus_clear(mask); - for (i = 0; i < tag->num_records; i++) { - if (cpu_list[i] == CPU_SENTINEL) - continue; - - if (cpu_list[i] < NR_CPUS) - cpu_set(cpu_list[i], mask); - } - - if (tag->type == DR_CPU_CONFIGURE) - err = dr_cpu_configure(cp, req_num, &mask); - else - err = dr_cpu_unconfigure(cp, req_num, &mask); - - if (err) - dr_cpu_send_error(cp, data); + switch (tag->type) { + case DR_CPU_CONFIGURE: + case DR_CPU_UNCONFIGURE: + case DR_CPU_FORCE_UNCONFIGURE: + break; -next: - list_del(&qp->list); - kfree(qp); + default: + dr_cpu_send_error(cp, data); + return; } -} -static int dr_cpu_thread(void *__unused) -{ - struct ds_cap_state *cp; - DEFINE_WAIT(wait); + purge_dups(cpu_list, tag->num_records); - cp = find_cap_by_string("dr-cpu"); - - while (1) { - prepare_to_wait(&dr_cpu_wait, &wait, TASK_INTERRUPTIBLE); - if (list_empty(&dr_cpu_work_list)) - schedule(); - finish_wait(&dr_cpu_wait, &wait); - - if (kthread_should_stop()) - break; + cpus_clear(mask); + for (i = 0; i < tag->num_records; i++) { + if (cpu_list[i] == CPU_SENTINEL) + continue; - process_dr_cpu_list(cp); + if (cpu_list[i] < NR_CPUS) + cpu_set(cpu_list[i], mask); } - return 0; -} - -static void dr_cpu_data(struct ldc_channel *lp, - struct ds_cap_state *dp, - void *buf, int len) -{ - struct dr_cpu_queue_entry *qp; - struct ds_data *dpkt = buf; - struct dr_cpu_tag *rp; - - rp = (struct dr_cpu_tag *) (dpkt + 1); + if (tag->type == DR_CPU_CONFIGURE) + err = dr_cpu_configure(cp, req_num, &mask); + else + err = dr_cpu_unconfigure(cp, req_num, &mask); - qp = kmalloc(sizeof(struct dr_cpu_queue_entry) + len, GFP_ATOMIC); - if (!qp) { - struct ds_cap_state *cp; - - cp = find_cap_by_string("dr-cpu"); - __dr_cpu_send_error(cp, dpkt); - } else { - memcpy(&qp->req, buf, len); - list_add_tail(&qp->list, &dr_cpu_work_list); - wake_up(&dr_cpu_wait); - } + if (err) + dr_cpu_send_error(cp, data); } -#endif +#endif /* CONFIG_HOTPLUG_CPU */ struct ds_pri_msg { __u64 req_num; @@ -820,7 +762,7 @@ void ldom_set_var(const char *var, const char *value) ds_var_doorbell = 0; ds_var_response = -1; - ds_send(dp->lp, &pkt, msg_len); + __ds_send(dp->lp, &pkt, msg_len); spin_unlock_irqrestore(&ds_lock, flags); loops = 1000; @@ -904,7 +846,7 @@ static int register_services(struct ds_info *dp) pbuf.req.minor = 0; strcpy(pbuf.req.svc_id, cp->service_id); - err = ds_send(lp, &pbuf, msg_len); + err = __ds_send(lp, &pbuf, msg_len); if (err > 0) cp->state = CAP_STATE_REG_SENT; } @@ -960,27 +902,97 @@ conn_reset: return -ECONNRESET; } +static void __send_ds_nack(struct ds_info *dp, u64 handle) +{ + struct ds_data_nack nack = { + .tag = { + .type = DS_NACK, + .len = (sizeof(struct ds_data_nack) - + sizeof(struct ds_msg_tag)), + }, + .handle = handle, + .result = DS_INV_HDL, + }; + + __ds_send(dp->lp, &nack, sizeof(nack)); +} + +static LIST_HEAD(ds_work_list); +static DECLARE_WAIT_QUEUE_HEAD(ds_wait); + +struct ds_queue_entry { + struct list_head list; + int req_len; + int __pad; + u64 req[0]; +}; + +static void process_ds_work(void) +{ + struct ds_queue_entry *qp, *tmp; + static struct ds_info *dp; + unsigned long flags; + LIST_HEAD(todo); + + spin_lock_irqsave(&ds_lock, flags); + list_splice(&ds_work_list, &todo); + INIT_LIST_HEAD(&ds_work_list); + spin_unlock_irqrestore(&ds_lock, flags); + + dp = ds_info; + + list_for_each_entry_safe(qp, tmp, &todo, list) { + struct ds_data *dpkt = (struct ds_data *) qp->req; + struct ds_cap_state *cp = find_cap(dpkt->handle); + int req_len = qp->req_len; + + if (!cp) { + printk(KERN_ERR PFX "Data for unknown handle %lu\n", + dpkt->handle); + + spin_lock_irqsave(&ds_lock, flags); + __send_ds_nack(dp, dpkt->handle); + spin_unlock_irqrestore(&ds_lock, flags); + } else { + cp->data(dp->lp, cp, dpkt, req_len); + } + + list_del(&qp->list); + kfree(qp); + } +} + +static int ds_thread(void *__unused) +{ + DEFINE_WAIT(wait); + + while (1) { + prepare_to_wait(&ds_wait, &wait, TASK_INTERRUPTIBLE); + if (list_empty(&ds_work_list)) + schedule(); + finish_wait(&ds_wait, &wait); + + if (kthread_should_stop()) + break; + + process_ds_work(); + } + + return 0; +} + static int ds_data(struct ds_info *dp, struct ds_msg_tag *pkt, int len) { struct ds_data *dpkt = (struct ds_data *) pkt; - struct ds_cap_state *cp = find_cap(dpkt->handle); - - if (!cp) { - struct ds_data_nack nack = { - .tag = { - .type = DS_NACK, - .len = (sizeof(struct ds_data_nack) - - sizeof(struct ds_msg_tag)), - }, - .handle = dpkt->handle, - .result = DS_INV_HDL, - }; - - printk(KERN_ERR PFX "Data for unknown handle %lu\n", - dpkt->handle); - ds_send(dp->lp, &nack, sizeof(nack)); + struct ds_queue_entry *qp; + + qp = kmalloc(sizeof(struct ds_queue_entry) + len, GFP_ATOMIC); + if (!qp) { + __send_ds_nack(dp, dpkt->handle); } else { - cp->data(dp->lp, cp, dpkt, len); + memcpy(&qp->req, pkt, len); + list_add_tail(&qp->list, &ds_work_list); + wake_up(&ds_wait); } return 0; } @@ -996,11 +1008,24 @@ static void ds_up(struct ds_info *dp) req.ver.major = 1; req.ver.minor = 0; - err = ds_send(lp, &req, sizeof(req)); + err = __ds_send(lp, &req, sizeof(req)); if (err > 0) dp->hs_state = DS_HS_START; } +static void ds_reset(struct ds_info *dp) +{ + int i; + + dp->hs_state = 0; + + for (i = 0; i < ARRAY_SIZE(ds_states); i++) { + struct ds_cap_state *cp = &ds_states[i]; + + cp->state = CAP_STATE_UNKNOWN; + } +} + static void ds_event(void *arg, int event) { struct ds_info *dp = arg; @@ -1016,6 +1041,12 @@ static void ds_event(void *arg, int event) return; } + if (event == LDC_EVENT_RESET) { + ds_reset(dp); + spin_unlock_irqrestore(&ds_lock, flags); + return; + } + if (event != LDC_EVENT_DATA_READY) { printk(KERN_WARNING PFX "Unexpected LDC event %d\n", event); spin_unlock_irqrestore(&ds_lock, flags); @@ -1148,9 +1179,7 @@ static int __init ds_init(void) for (i = 0; i < ARRAY_SIZE(ds_states); i++) ds_states[i].handle = ((u64)i << 32); -#ifdef CONFIG_HOTPLUG_CPU - kthread_run(dr_cpu_thread, NULL, "kdrcpud"); -#endif + kthread_run(ds_thread, NULL, "kldomd"); return vio_register_driver(&ds_driver); } diff --git a/arch/sparc64/kernel/hvtramp.S b/arch/sparc64/kernel/hvtramp.S index 76a090e2c2a..a55c252e18c 100644 --- a/arch/sparc64/kernel/hvtramp.S +++ b/arch/sparc64/kernel/hvtramp.S @@ -10,6 +10,7 @@ #include <asm/hvtramp.h> #include <asm/pstate.h> #include <asm/ptrace.h> +#include <asm/head.h> #include <asm/asi.h> .text @@ -28,7 +29,7 @@ * First setup basic privileged cpu state. */ hv_cpu_startup: - wrpr %g0, 0, %gl + SET_GL(0) wrpr %g0, 15, %pil wrpr %g0, 0, %canrestore wrpr %g0, 0, %otherwin diff --git a/arch/sparc64/kernel/mdesc.c b/arch/sparc64/kernel/mdesc.c index 62a38979394..302ba5e5a0b 100644 --- a/arch/sparc64/kernel/mdesc.c +++ b/arch/sparc64/kernel/mdesc.c @@ -137,7 +137,7 @@ static struct mdesc_handle *mdesc_kmalloc(unsigned int mdesc_size) sizeof(struct mdesc_hdr) + mdesc_size); - base = kmalloc(handle_size + 15, GFP_KERNEL); + base = kmalloc(handle_size + 15, GFP_KERNEL | __GFP_NOFAIL); if (base) { struct mdesc_handle *hp; unsigned long addr; @@ -214,18 +214,83 @@ void mdesc_release(struct mdesc_handle *hp) } EXPORT_SYMBOL(mdesc_release); -static void do_mdesc_update(struct work_struct *work) +static DEFINE_MUTEX(mdesc_mutex); +static struct mdesc_notifier_client *client_list; + +void mdesc_register_notifier(struct mdesc_notifier_client *client) +{ + u64 node; + + mutex_lock(&mdesc_mutex); + client->next = client_list; + client_list = client; + + mdesc_for_each_node_by_name(cur_mdesc, node, client->node_name) + client->add(cur_mdesc, node); + + mutex_unlock(&mdesc_mutex); +} + +/* Run 'func' on nodes which are in A but not in B. */ +static void invoke_on_missing(const char *name, + struct mdesc_handle *a, + struct mdesc_handle *b, + void (*func)(struct mdesc_handle *, u64)) +{ + u64 node; + + mdesc_for_each_node_by_name(a, node, name) { + const u64 *id = mdesc_get_property(a, node, "id", NULL); + int found = 0; + u64 fnode; + + mdesc_for_each_node_by_name(b, fnode, name) { + const u64 *fid = mdesc_get_property(b, fnode, + "id", NULL); + + if (*id == *fid) { + found = 1; + break; + } + } + if (!found) + func(a, node); + } +} + +static void notify_one(struct mdesc_notifier_client *p, + struct mdesc_handle *old_hp, + struct mdesc_handle *new_hp) +{ + invoke_on_missing(p->node_name, old_hp, new_hp, p->remove); + invoke_on_missing(p->node_name, new_hp, old_hp, p->add); +} + +static void mdesc_notify_clients(struct mdesc_handle *old_hp, + struct mdesc_handle *new_hp) +{ + struct mdesc_notifier_client *p = client_list; + + while (p) { + notify_one(p, old_hp, new_hp); + p = p->next; + } +} + +void mdesc_update(void) { unsigned long len, real_len, status; struct mdesc_handle *hp, *orig_hp; unsigned long flags; + mutex_lock(&mdesc_mutex); + (void) sun4v_mach_desc(0UL, 0UL, &len); hp = mdesc_alloc(len, &kmalloc_mdesc_memops); if (!hp) { printk(KERN_ERR "MD: mdesc alloc fails\n"); - return; + goto out; } status = sun4v_mach_desc(__pa(&hp->mdesc), len, &real_len); @@ -234,25 +299,25 @@ static void do_mdesc_update(struct work_struct *work) status); atomic_dec(&hp->refcnt); mdesc_free(hp); - return; + goto out; } spin_lock_irqsave(&mdesc_lock, flags); orig_hp = cur_mdesc; cur_mdesc = hp; + spin_unlock_irqrestore(&mdesc_lock, flags); + mdesc_notify_clients(orig_hp, hp); + + spin_lock_irqsave(&mdesc_lock, flags); if (atomic_dec_and_test(&orig_hp->refcnt)) mdesc_free(orig_hp); else list_add(&orig_hp->list, &mdesc_zombie_list); spin_unlock_irqrestore(&mdesc_lock, flags); -} - -static DECLARE_WORK(mdesc_update_work, do_mdesc_update); -void mdesc_update(void) -{ - schedule_work(&mdesc_update_work); +out: + mutex_unlock(&mdesc_mutex); } static struct mdesc_elem *node_block(struct mdesc_hdr *mdesc) @@ -278,13 +343,14 @@ u64 mdesc_node_by_name(struct mdesc_handle *hp, u64 last_node = hp->mdesc.node_sz / 16; u64 ret; - if (from_node == MDESC_NODE_NULL) - from_node = 0; - - if (from_node >= last_node) + if (from_node == MDESC_NODE_NULL) { + ret = from_node = 0; + } else if (from_node >= last_node) { return MDESC_NODE_NULL; + } else { + ret = ep[from_node].d.val; + } - ret = ep[from_node].d.val; while (ret < last_node) { if (ep[ret].tag != MD_NODE) return MDESC_NODE_NULL; diff --git a/arch/sparc64/kernel/setup.c b/arch/sparc64/kernel/setup.c index dc928e49e34..aafde3dd9fd 100644 --- a/arch/sparc64/kernel/setup.c +++ b/arch/sparc64/kernel/setup.c @@ -454,9 +454,9 @@ static int show_cpuinfo(struct seq_file *m, void *__unused) ncpus_probed, num_online_cpus(), dcache_parity_tl1_occurred, - icache_parity_tl1_occurred, + icache_parity_tl1_occurred #ifndef CONFIG_SMP - cpu_data(0).clock_tick + , cpu_data(0).clock_tick #endif ); #ifdef CONFIG_SMP diff --git a/arch/sparc64/kernel/signal.c b/arch/sparc64/kernel/signal.c index 203e8730100..fb13775b368 100644 --- a/arch/sparc64/kernel/signal.c +++ b/arch/sparc64/kernel/signal.c @@ -289,9 +289,7 @@ void do_rt_sigreturn(struct pt_regs *regs) struct rt_signal_frame __user *sf; unsigned long tpc, tnpc, tstate; __siginfo_fpu_t __user *fpu_save; - mm_segment_t old_fs; sigset_t set; - stack_t st; int err; /* Always make any pending restarted system calls return -EINTR */ @@ -327,20 +325,13 @@ void do_rt_sigreturn(struct pt_regs *regs) err |= restore_fpu_state(regs, &sf->fpu_state); err |= __copy_from_user(&set, &sf->mask, sizeof(sigset_t)); - err |= __copy_from_user(&st, &sf->stack, sizeof(stack_t)); - + err |= do_sigaltstack(&sf->stack, NULL, (unsigned long)sf); + if (err) goto segv; - + regs->tpc = tpc; regs->tnpc = tnpc; - - /* It is more difficult to avoid calling this function than to - call it and ignore errors. */ - old_fs = get_fs(); - set_fs(KERNEL_DS); - do_sigaltstack((const stack_t __user *) &st, NULL, (unsigned long)sf); - set_fs(old_fs); sigdelsetmask(&set, ~_BLOCKABLE); spin_lock_irq(¤t->sighand->siglock); diff --git a/arch/sparc64/kernel/traps.c b/arch/sparc64/kernel/traps.c index 00a9e3286c8..6ef2d299fb1 100644 --- a/arch/sparc64/kernel/traps.c +++ b/arch/sparc64/kernel/traps.c @@ -2225,6 +2225,7 @@ void die_if_kernel(char *str, struct pt_regs *regs) notify_die(DIE_OOPS, str, regs, 0, 255, SIGSEGV); __asm__ __volatile__("flushw"); __show_regs(regs); + add_taint(TAINT_DIE); if (regs->tstate & TSTATE_PRIV) { struct reg_window *rw = (struct reg_window *) (regs->u_regs[UREG_FP] + STACK_BIAS); diff --git a/arch/sparc64/kernel/vio.c b/arch/sparc64/kernel/vio.c index 49569b44ea1..8d3cc4fdb55 100644 --- a/arch/sparc64/kernel/vio.c +++ b/arch/sparc64/kernel/vio.c @@ -201,10 +201,11 @@ static void vio_fill_channel_info(struct mdesc_handle *hp, u64 mp, static struct vio_dev *vio_create_one(struct mdesc_handle *hp, u64 mp, struct device *parent) { - const char *type, *compat; + const char *type, *compat, *bus_id_name; struct device_node *dp; struct vio_dev *vdev; int err, tlen, clen; + const u64 *id; type = mdesc_get_property(hp, mp, "device-type", &tlen); if (!type) { @@ -220,6 +221,16 @@ static struct vio_dev *vio_create_one(struct mdesc_handle *hp, u64 mp, return NULL; } + bus_id_name = type; + if (!strcmp(type, "domain-services-port")) + bus_id_name = "ds"; + + if (strlen(bus_id_name) >= KOBJ_NAME_LEN - 4) { + printk(KERN_ERR "VIO: bus_id_name [%s] is too long.\n", + bus_id_name); + return NULL; + } + compat = mdesc_get_property(hp, mp, "device-type", &clen); if (!compat) { clen = 0; @@ -249,7 +260,14 @@ static struct vio_dev *vio_create_one(struct mdesc_handle *hp, u64 mp, vio_fill_channel_info(hp, mp, vdev); - snprintf(vdev->dev.bus_id, BUS_ID_SIZE, "%lx", mp); + id = mdesc_get_property(hp, mp, "id", NULL); + if (!id) + snprintf(vdev->dev.bus_id, BUS_ID_SIZE, "%s", + bus_id_name); + else + snprintf(vdev->dev.bus_id, BUS_ID_SIZE, "%s-%lu", + bus_id_name, *id); + vdev->dev.parent = parent; vdev->dev.bus = &vio_bus_type; vdev->dev.release = vio_dev_release; @@ -269,6 +287,8 @@ static struct vio_dev *vio_create_one(struct mdesc_handle *hp, u64 mp, } vdev->dp = dp; + printk(KERN_ERR "VIO: Adding device %s\n", vdev->dev.bus_id); + err = device_register(&vdev->dev); if (err) { printk(KERN_ERR "VIO: Could not register device %s, err=%d\n", @@ -283,46 +303,46 @@ static struct vio_dev *vio_create_one(struct mdesc_handle *hp, u64 mp, return vdev; } -static void walk_tree(struct mdesc_handle *hp, u64 n, struct vio_dev *parent) +static void vio_add(struct mdesc_handle *hp, u64 node) { - u64 a; - - mdesc_for_each_arc(a, hp, n, MDESC_ARC_TYPE_FWD) { - struct vio_dev *vdev; - u64 target; - - target = mdesc_arc_target(hp, a); - vdev = vio_create_one(hp, target, &parent->dev); - if (vdev) - walk_tree(hp, target, vdev); - } + (void) vio_create_one(hp, node, &root_vdev->dev); } -static void create_devices(struct mdesc_handle *hp, u64 root) +static int vio_md_node_match(struct device *dev, void *arg) { - u64 mp; + struct vio_dev *vdev = to_vio_dev(dev); - root_vdev = vio_create_one(hp, root, NULL); - if (!root_vdev) { - printk(KERN_ERR "VIO: Coult not create root device.\n"); - return; - } + if (vdev->mp == (u64) arg) + return 1; - walk_tree(hp, root, root_vdev); + return 0; +} + +static void vio_remove(struct mdesc_handle *hp, u64 node) +{ + struct device *dev; - /* Domain services is odd as it doesn't sit underneath the - * channel-devices node, so we plug it in manually. - */ - mp = mdesc_node_by_name(hp, MDESC_NODE_NULL, "domain-services"); - if (mp != MDESC_NODE_NULL) { - struct vio_dev *parent = vio_create_one(hp, mp, - &root_vdev->dev); + dev = device_find_child(&root_vdev->dev, (void *) node, + vio_md_node_match); + if (dev) { + printk(KERN_INFO "VIO: Removing device %s\n", dev->bus_id); - if (parent) - walk_tree(hp, mp, parent); + device_unregister(dev); } } +static struct mdesc_notifier_client vio_device_notifier = { + .add = vio_add, + .remove = vio_remove, + .node_name = "virtual-device-port", +}; + +static struct mdesc_notifier_client vio_ds_notifier = { + .add = vio_add, + .remove = vio_remove, + .node_name = "domain-services-port", +}; + const char *channel_devices_node = "channel-devices"; const char *channel_devices_compat = "SUNW,sun4v-channel-devices"; const char *cfg_handle_prop = "cfg-handle"; @@ -381,11 +401,19 @@ static int __init vio_init(void) cdev_cfg_handle = *cfg_handle; - create_devices(hp, root); + root_vdev = vio_create_one(hp, root, NULL); + err = -ENODEV; + if (!root_vdev) { + printk(KERN_ERR "VIO: Coult not create root device.\n"); + goto out_release; + } + + mdesc_register_notifier(&vio_device_notifier); + mdesc_register_notifier(&vio_ds_notifier); mdesc_release(hp); - return 0; + return err; out_release: mdesc_release(hp); diff --git a/arch/sparc64/kernel/viohs.c b/arch/sparc64/kernel/viohs.c index 15613add45d..09126fc338b 100644 --- a/arch/sparc64/kernel/viohs.c +++ b/arch/sparc64/kernel/viohs.c @@ -78,6 +78,24 @@ static int start_handshake(struct vio_driver_state *vio) return 0; } +static void flush_rx_dring(struct vio_driver_state *vio) +{ + struct vio_dring_state *dr; + u64 ident; + + BUG_ON(!(vio->dr_state & VIO_DR_STATE_RXREG)); + + dr = &vio->drings[VIO_DRIVER_RX_RING]; + ident = dr->ident; + + BUG_ON(!vio->desc_buf); + kfree(vio->desc_buf); + vio->desc_buf = NULL; + + memset(dr, 0, sizeof(*dr)); + dr->ident = ident; +} + void vio_link_state_change(struct vio_driver_state *vio, int event) { if (event == LDC_EVENT_UP) { @@ -98,6 +116,16 @@ void vio_link_state_change(struct vio_driver_state *vio, int event) break; } start_handshake(vio); + } else if (event == LDC_EVENT_RESET) { + vio->hs_state = VIO_HS_INVALID; + + if (vio->dr_state & VIO_DR_STATE_RXREG) + flush_rx_dring(vio); + + vio->dr_state = 0x00; + memset(&vio->ver, 0, sizeof(vio->ver)); + + ldc_disconnect(vio->lp); } } EXPORT_SYMBOL(vio_link_state_change); @@ -396,6 +424,8 @@ static int process_dreg_info(struct vio_driver_state *vio, if (vio->dr_state & VIO_DR_STATE_RXREG) goto send_nack; + BUG_ON(vio->desc_buf); + vio->desc_buf = kzalloc(pkt->descr_size, GFP_ATOMIC); if (!vio->desc_buf) goto send_nack; diff --git a/arch/sparc64/kernel/vmlinux.lds.S b/arch/sparc64/kernel/vmlinux.lds.S index 3ad10f3027e..481861764de 100644 --- a/arch/sparc64/kernel/vmlinux.lds.S +++ b/arch/sparc64/kernel/vmlinux.lds.S @@ -90,10 +90,8 @@ SECTIONS __initramfs_end = .; #endif - . = ALIGN(PAGE_SIZE); - __per_cpu_start = .; - .data.percpu : { *(.data.percpu) } - __per_cpu_end = .; + PERCPU(PAGE_SIZE) + . = ALIGN(PAGE_SIZE); __init_end = .; __bss_start = .; diff --git a/arch/sparc64/mm/fault.c b/arch/sparc64/mm/fault.c index b582024d219..17123e9ecf7 100644 --- a/arch/sparc64/mm/fault.c +++ b/arch/sparc64/mm/fault.c @@ -278,7 +278,7 @@ asmlinkage void __kprobes do_sparc64_fault(struct pt_regs *regs) struct mm_struct *mm = current->mm; struct vm_area_struct *vma; unsigned int insn = 0; - int si_code, fault_code; + int si_code, fault_code, fault; unsigned long address, mm_rss; fault_code = get_thread_fault_code(); @@ -415,20 +415,18 @@ good_area: goto bad_area; } - switch (handle_mm_fault(mm, vma, address, (fault_code & FAULT_CODE_WRITE))) { - case VM_FAULT_MINOR: - current->min_flt++; - break; - case VM_FAULT_MAJOR: - current->maj_flt++; - break; - case VM_FAULT_SIGBUS: - goto do_sigbus; - case VM_FAULT_OOM: - goto out_of_memory; - default: + fault = handle_mm_fault(mm, vma, address, (fault_code & FAULT_CODE_WRITE)); + if (unlikely(fault & VM_FAULT_ERROR)) { + if (fault & VM_FAULT_OOM) + goto out_of_memory; + else if (fault & VM_FAULT_SIGBUS) + goto do_sigbus; BUG(); } + if (fault & VM_FAULT_MAJOR) + current->maj_flt++; + else + current->min_flt++; up_read(&mm->mmap_sem); diff --git a/arch/sparc64/solaris/socksys.c b/arch/sparc64/solaris/socksys.c index e94f6e5d945..7736411f244 100644 --- a/arch/sparc64/solaris/socksys.c +++ b/arch/sparc64/solaris/socksys.c @@ -199,6 +199,5 @@ int __init init_socksys(void) void __exit cleanup_socksys(void) { - if (unregister_chrdev(30, "socksys")) - printk ("Couldn't unregister socksys character device\n"); + unregister_chrdev(30, "socksys"); } diff --git a/arch/um/drivers/pcap_user.c b/arch/um/drivers/pcap_user.c index 483aa15222a..1316456e2a2 100644 --- a/arch/um/drivers/pcap_user.c +++ b/arch/um/drivers/pcap_user.c @@ -53,7 +53,7 @@ static int pcap_open(void *data) return -EIO; } - pri->compiled = um_kmalloc(sizeof(struct bpf_program)); + pri->compiled = kmalloc(sizeof(struct bpf_program), UM_GFP_KERNEL); if(pri->compiled == NULL){ printk(UM_KERN_ERR "pcap_open : kmalloc failed\n"); return -ENOMEM; diff --git a/arch/um/kernel/ptrace.c b/arch/um/kernel/ptrace.c index 627742d8943..6916c8888db 100644 --- a/arch/um/kernel/ptrace.c +++ b/arch/um/kernel/ptrace.c @@ -52,17 +52,9 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) switch (request) { /* when I and D space are separate, these will need to be fixed. */ case PTRACE_PEEKTEXT: /* read word at location addr. */ - case PTRACE_PEEKDATA: { - unsigned long tmp; - int copied; - - ret = -EIO; - copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); - if (copied != sizeof(tmp)) - break; - ret = put_user(tmp, p); + case PTRACE_PEEKDATA: + ret = generic_ptrace_peekdata(child, addr, data); break; - } /* read the word at location addr in the USER area. */ case PTRACE_PEEKUSR: @@ -72,11 +64,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) /* when I and D space are separate, this will have to be fixed. */ case PTRACE_POKETEXT: /* write the word at location addr. */ case PTRACE_POKEDATA: - ret = -EIO; - if (access_process_vm(child, addr, &data, sizeof(data), - 1) != sizeof(data)) - break; - ret = 0; + ret = generic_ptrace_pokedata(child, addr, data); break; case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ diff --git a/arch/um/kernel/trap.c b/arch/um/kernel/trap.c index abab90c3803..3850d53f79f 100644 --- a/arch/um/kernel/trap.c +++ b/arch/um/kernel/trap.c @@ -76,23 +76,24 @@ good_area: goto out; do { + int fault; survive: - switch (handle_mm_fault(mm, vma, address, is_write)){ - case VM_FAULT_MINOR: - current->min_flt++; - break; - case VM_FAULT_MAJOR: - current->maj_flt++; - break; - case VM_FAULT_SIGBUS: - err = -EACCES; - goto out; - case VM_FAULT_OOM: - err = -ENOMEM; - goto out_of_memory; - default: + fault = handle_mm_fault(mm, vma, address, is_write); + if (unlikely(fault & VM_FAULT_ERROR)) { + if (fault & VM_FAULT_OOM) { + err = -ENOMEM; + goto out_of_memory; + } else if (fault & VM_FAULT_SIGBUS) { + err = -EACCES; + goto out; + } BUG(); } + if (fault & VM_FAULT_MAJOR) + current->maj_flt++; + else + current->min_flt++; + pgd = pgd_offset(mm, address); pud = pud_offset(pgd, address); pmd = pmd_offset(pud, address); diff --git a/arch/v850/kernel/ptrace.c b/arch/v850/kernel/ptrace.c index a9b09343097..a458ac941b2 100644 --- a/arch/v850/kernel/ptrace.c +++ b/arch/v850/kernel/ptrace.c @@ -117,24 +117,16 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) int rval; switch (request) { - unsigned long val, copied; + unsigned long val; case PTRACE_PEEKTEXT: /* read word at location addr. */ case PTRACE_PEEKDATA: - copied = access_process_vm(child, addr, &val, sizeof(val), 0); - rval = -EIO; - if (copied != sizeof(val)) - break; - rval = put_user(val, (unsigned long *)data); + rval = generic_ptrace_peekdata(child, addr, data); goto out; case PTRACE_POKETEXT: /* write the word at location addr. */ case PTRACE_POKEDATA: - rval = 0; - if (access_process_vm(child, addr, &data, sizeof(data), 1) - == sizeof(data)) - break; - rval = -EIO; + rval = generic_ptrace_pokedata(child, addr, data); goto out; /* Read/write the word at location ADDR in the registers. */ diff --git a/arch/x86_64/Kconfig b/arch/x86_64/Kconfig index 8bdd25ac154..14bf8ce3ea2 100644 --- a/arch/x86_64/Kconfig +++ b/arch/x86_64/Kconfig @@ -774,8 +774,8 @@ menu "Instrumentation Support" source "arch/x86_64/oprofile/Kconfig" config KPROBES - bool "Kprobes (EXPERIMENTAL)" - depends on KALLSYMS && EXPERIMENTAL && MODULES + bool "Kprobes" + depends on KALLSYMS && MODULES help Kprobes allows you to trap at almost any kernel address and execute a callback function. register_kprobe() establishes diff --git a/arch/x86_64/ia32/ia32_aout.c b/arch/x86_64/ia32/ia32_aout.c index fe83edb93c1..08781370256 100644 --- a/arch/x86_64/ia32/ia32_aout.c +++ b/arch/x86_64/ia32/ia32_aout.c @@ -404,7 +404,7 @@ beyond_if: set_brk(current->mm->start_brk, current->mm->brk); - retval = ia32_setup_arg_pages(bprm, IA32_STACK_TOP, EXSTACK_DEFAULT); + retval = setup_arg_pages(bprm, IA32_STACK_TOP, EXSTACK_DEFAULT); if (retval < 0) { /* Someone check-me: is this error path enough? */ send_sig(SIGKILL, current, 0); diff --git a/arch/x86_64/ia32/ia32_binfmt.c b/arch/x86_64/ia32/ia32_binfmt.c index 185399baaf6..ed56a8806ea 100644 --- a/arch/x86_64/ia32/ia32_binfmt.c +++ b/arch/x86_64/ia32/ia32_binfmt.c @@ -232,9 +232,6 @@ do { \ #define load_elf_binary load_elf32_binary #define ELF_PLAT_INIT(r, load_addr) elf32_init(r) -#define setup_arg_pages(bprm, stack_top, exec_stack) \ - ia32_setup_arg_pages(bprm, stack_top, exec_stack) -int ia32_setup_arg_pages(struct linux_binprm *bprm, unsigned long stack_top, int executable_stack); #undef start_thread #define start_thread(regs,new_rip,new_rsp) do { \ @@ -286,61 +283,6 @@ static void elf32_init(struct pt_regs *regs) me->thread.es = __USER_DS; } -int ia32_setup_arg_pages(struct linux_binprm *bprm, unsigned long stack_top, - int executable_stack) -{ - unsigned long stack_base; - struct vm_area_struct *mpnt; - struct mm_struct *mm = current->mm; - int i, ret; - - stack_base = stack_top - MAX_ARG_PAGES * PAGE_SIZE; - mm->arg_start = bprm->p + stack_base; - - bprm->p += stack_base; - if (bprm->loader) - bprm->loader += stack_base; - bprm->exec += stack_base; - - mpnt = kmem_cache_zalloc(vm_area_cachep, GFP_KERNEL); - if (!mpnt) - return -ENOMEM; - - down_write(&mm->mmap_sem); - { - mpnt->vm_mm = mm; - mpnt->vm_start = PAGE_MASK & (unsigned long) bprm->p; - mpnt->vm_end = stack_top; - if (executable_stack == EXSTACK_ENABLE_X) - mpnt->vm_flags = VM_STACK_FLAGS | VM_EXEC; - else if (executable_stack == EXSTACK_DISABLE_X) - mpnt->vm_flags = VM_STACK_FLAGS & ~VM_EXEC; - else - mpnt->vm_flags = VM_STACK_FLAGS; - mpnt->vm_page_prot = (mpnt->vm_flags & VM_EXEC) ? - PAGE_COPY_EXEC : PAGE_COPY; - if ((ret = insert_vm_struct(mm, mpnt))) { - up_write(&mm->mmap_sem); - kmem_cache_free(vm_area_cachep, mpnt); - return ret; - } - mm->stack_vm = mm->total_vm = vma_pages(mpnt); - } - - for (i = 0 ; i < MAX_ARG_PAGES ; i++) { - struct page *page = bprm->page[i]; - if (page) { - bprm->page[i] = NULL; - install_arg_page(mpnt, page, stack_base); - } - stack_base += PAGE_SIZE; - } - up_write(&mm->mmap_sem); - - return 0; -} -EXPORT_SYMBOL(ia32_setup_arg_pages); - #ifdef CONFIG_SYSCTL /* Register vsyscall32 into the ABI table */ #include <linux/sysctl.h> diff --git a/arch/x86_64/ia32/ia32entry.S b/arch/x86_64/ia32/ia32entry.S index 782dea81943..3f66e970d86 100644 --- a/arch/x86_64/ia32/ia32entry.S +++ b/arch/x86_64/ia32/ia32entry.S @@ -719,4 +719,5 @@ ia32_sys_call_table: .quad compat_sys_signalfd .quad compat_sys_timerfd .quad sys_eventfd + .quad sys32_fallocate ia32_syscall_end: diff --git a/arch/x86_64/ia32/sys_ia32.c b/arch/x86_64/ia32/sys_ia32.c index 99a78a3cce7..bee96d61443 100644 --- a/arch/x86_64/ia32/sys_ia32.c +++ b/arch/x86_64/ia32/sys_ia32.c @@ -879,3 +879,11 @@ asmlinkage long sys32_fadvise64(int fd, unsigned offset_lo, unsigned offset_hi, return sys_fadvise64_64(fd, ((u64)offset_hi << 32) | offset_lo, len, advice); } + +asmlinkage long sys32_fallocate(int fd, int mode, unsigned offset_lo, + unsigned offset_hi, unsigned len_lo, + unsigned len_hi) +{ + return sys_fallocate(fd, mode, ((u64)offset_hi << 32) | offset_lo, + ((u64)len_hi << 32) | len_lo); +} diff --git a/arch/x86_64/kernel/acpi/sleep.c b/arch/x86_64/kernel/acpi/sleep.c index 195b7034a14..4277f2b27e6 100644 --- a/arch/x86_64/kernel/acpi/sleep.c +++ b/arch/x86_64/kernel/acpi/sleep.c @@ -55,7 +55,7 @@ /* address in low memory of the wakeup routine. */ unsigned long acpi_wakeup_address = 0; -unsigned long acpi_video_flags; +unsigned long acpi_realmode_flags; extern char wakeup_start, wakeup_end; extern unsigned long acpi_copy_wakeup_routine(unsigned long); @@ -103,9 +103,11 @@ static int __init acpi_sleep_setup(char *str) { while ((str != NULL) && (*str != '\0')) { if (strncmp(str, "s3_bios", 7) == 0) - acpi_video_flags = 1; + acpi_realmode_flags |= 1; if (strncmp(str, "s3_mode", 7) == 0) - acpi_video_flags |= 2; + acpi_realmode_flags |= 2; + if (strncmp(str, "s3_beep", 7) == 0) + acpi_realmode_flags |= 4; str = strchr(str, ','); if (str != NULL) str += strspn(str, ", \t"); diff --git a/arch/x86_64/kernel/acpi/wakeup.S b/arch/x86_64/kernel/acpi/wakeup.S index 8550a6ffa27..13f1480cbec 100644 --- a/arch/x86_64/kernel/acpi/wakeup.S +++ b/arch/x86_64/kernel/acpi/wakeup.S @@ -16,6 +16,21 @@ # cs = 0x1234, eip = 0x05 # +#define BEEP \ + inb $97, %al; \ + outb %al, $0x80; \ + movb $3, %al; \ + outb %al, $97; \ + outb %al, $0x80; \ + movb $-74, %al; \ + outb %al, $67; \ + outb %al, $0x80; \ + movb $-119, %al; \ + outb %al, $66; \ + outb %al, $0x80; \ + movb $15, %al; \ + outb %al, $66; + ALIGN .align 16 @@ -33,6 +48,13 @@ wakeup_code: movw %cs, %ax movw %ax, %ds # Make ds:0 point to wakeup_start movw %ax, %ss + + # Data segment must be set up before we can see whether to beep. + testl $4, realmode_flags - wakeup_code + jz 1f + BEEP +1: + # Private stack is needed for ASUS board mov $(wakeup_stack - wakeup_code), %sp @@ -48,7 +70,7 @@ wakeup_code: testl %eax, %eax jnz no_longmode - testl $1, video_flags - wakeup_code + testl $1, realmode_flags - wakeup_code jz 1f lcall $0xc000,$3 movw %cs, %ax @@ -56,7 +78,7 @@ wakeup_code: movw %ax, %ss 1: - testl $2, video_flags - wakeup_code + testl $2, realmode_flags - wakeup_code jz 1f mov video_mode - wakeup_code, %ax call mode_seta @@ -230,7 +252,7 @@ gdt_48a: real_magic: .quad 0 video_mode: .quad 0 -video_flags: .quad 0 +realmode_flags: .quad 0 .code16 bogus_real_magic: @@ -346,8 +368,8 @@ ENTRY(acpi_copy_wakeup_routine) movl saved_video_mode, %edx movl %edx, video_mode - wakeup_start (,%rdi) - movl acpi_video_flags, %edx - movl %edx, video_flags - wakeup_start (,%rdi) + movl acpi_realmode_flags, %edx + movl %edx, realmode_flags - wakeup_start (,%rdi) movq $0x12345678, real_magic - wakeup_start (,%rdi) movq $0x123456789abcdef0, %rdx movq %rdx, saved_magic diff --git a/arch/x86_64/kernel/early_printk.c b/arch/x86_64/kernel/early_printk.c index 296d2b0c5d8..fd9aff3f389 100644 --- a/arch/x86_64/kernel/early_printk.c +++ b/arch/x86_64/kernel/early_printk.c @@ -6,6 +6,7 @@ #include <asm/io.h> #include <asm/processor.h> #include <asm/fcntl.h> +#include <xen/hvc-console.h> /* Simple VGA output */ @@ -242,6 +243,10 @@ static int __init setup_early_printk(char *buf) simnow_init(buf + 6); early_console = &simnow_console; keep_early = 1; +#ifdef CONFIG_HVC_XEN + } else if (!strncmp(buf, "xen", 3)) { + early_console = &xenboot_console; +#endif } if (keep_early) diff --git a/arch/x86_64/kernel/init_task.c b/arch/x86_64/kernel/init_task.c index 3dc5854ba21..4ff33d4f855 100644 --- a/arch/x86_64/kernel/init_task.c +++ b/arch/x86_64/kernel/init_task.c @@ -44,7 +44,7 @@ EXPORT_SYMBOL(init_task); * section. Since TSS's are completely CPU-local, we want them * on exact cacheline boundaries, to eliminate cacheline ping-pong. */ -DEFINE_PER_CPU(struct tss_struct, init_tss) ____cacheline_internodealigned_in_smp = INIT_TSS; +DEFINE_PER_CPU_SHARED_ALIGNED(struct tss_struct, init_tss) = INIT_TSS; /* Copies of the original ist values from the tss are only accessed during * debugging, no special alignment required. diff --git a/arch/x86_64/kernel/mce.c b/arch/x86_64/kernel/mce.c index aa1d1599179..f3fb8174559 100644 --- a/arch/x86_64/kernel/mce.c +++ b/arch/x86_64/kernel/mce.c @@ -174,7 +174,7 @@ static void do_mce_trigger(void) if (events != atomic_read(&mce_logged) && trigger[0]) { /* Small race window, but should be harmless. */ atomic_set(&mce_logged, events); - call_usermodehelper(trigger, trigger_argv, NULL, -1); + call_usermodehelper(trigger, trigger_argv, NULL, UMH_NO_WAIT); } } diff --git a/arch/x86_64/kernel/nmi.c b/arch/x86_64/kernel/nmi.c index 931c64bad5e..edbbc59b752 100644 --- a/arch/x86_64/kernel/nmi.c +++ b/arch/x86_64/kernel/nmi.c @@ -296,7 +296,7 @@ static DEFINE_PER_CPU(unsigned, last_irq_sum); static DEFINE_PER_CPU(local_t, alert_counter); static DEFINE_PER_CPU(int, nmi_touch); -void touch_nmi_watchdog (void) +void touch_nmi_watchdog(void) { if (nmi_watchdog > 0) { unsigned cpu; @@ -306,8 +306,10 @@ void touch_nmi_watchdog (void) * do it ourselves because the alert count increase is not * atomic. */ - for_each_present_cpu (cpu) - per_cpu(nmi_touch, cpu) = 1; + for_each_present_cpu(cpu) { + if (per_cpu(nmi_touch, cpu) != 1) + per_cpu(nmi_touch, cpu) = 1; + } } touch_softlockup_watchdog(); diff --git a/arch/x86_64/kernel/ptrace.c b/arch/x86_64/kernel/ptrace.c index 9409117b9f1..e83cc67155a 100644 --- a/arch/x86_64/kernel/ptrace.c +++ b/arch/x86_64/kernel/ptrace.c @@ -102,16 +102,25 @@ unsigned long convert_rip_to_linear(struct task_struct *child, struct pt_regs *r u32 *desc; unsigned long base; - down(&child->mm->context.sem); - desc = child->mm->context.ldt + (seg & ~7); - base = (desc[0] >> 16) | ((desc[1] & 0xff) << 16) | (desc[1] & 0xff000000); + seg &= ~7UL; - /* 16-bit code segment? */ - if (!((desc[1] >> 22) & 1)) - addr &= 0xffff; - addr += base; + down(&child->mm->context.sem); + if (unlikely((seg >> 3) >= child->mm->context.size)) + addr = -1L; /* bogus selector, access would fault */ + else { + desc = child->mm->context.ldt + seg; + base = ((desc[0] >> 16) | + ((desc[1] & 0xff) << 16) | + (desc[1] & 0xff000000)); + + /* 16-bit code segment? */ + if (!((desc[1] >> 22) & 1)) + addr &= 0xffff; + addr += base; + } up(&child->mm->context.sem); } + return addr; } @@ -313,17 +322,9 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) switch (request) { /* when I and D space are separate, these will need to be fixed. */ case PTRACE_PEEKTEXT: /* read word at location addr. */ - case PTRACE_PEEKDATA: { - unsigned long tmp; - int copied; - - copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); - ret = -EIO; - if (copied != sizeof(tmp)) - break; - ret = put_user(tmp,(unsigned long __user *) data); + case PTRACE_PEEKDATA: + ret = generic_ptrace_peekdata(child, addr, data); break; - } /* read the word at location addr in the USER area. */ case PTRACE_PEEKUSR: { @@ -367,10 +368,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) /* when I and D space are separate, this will have to be fixed. */ case PTRACE_POKETEXT: /* write the word at location addr. */ case PTRACE_POKEDATA: - ret = 0; - if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data)) - break; - ret = -EIO; + ret = generic_ptrace_pokedata(child, addr, data); break; case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ diff --git a/arch/x86_64/kernel/smp.c b/arch/x86_64/kernel/smp.c index 2ff46859162..0694940b2e7 100644 --- a/arch/x86_64/kernel/smp.c +++ b/arch/x86_64/kernel/smp.c @@ -357,7 +357,7 @@ __smp_call_function_single(int cpu, void (*func) (void *info), void *info, } /* - * smp_call_function_single - Run a function on another CPU + * smp_call_function_single - Run a function on a specific CPU * @func: The function to run. This must be fast and non-blocking. * @info: An arbitrary pointer to pass to the function. * @nonatomic: Currently unused. @@ -374,14 +374,18 @@ int smp_call_function_single (int cpu, void (*func) (void *info), void *info, { /* prevent preemption and reschedule on another processor */ int me = get_cpu(); + + /* Can deadlock when called with interrupts disabled */ + WARN_ON(irqs_disabled()); + if (cpu == me) { + local_irq_disable(); + func(info); + local_irq_enable(); put_cpu(); return 0; } - /* Can deadlock when called with interrupts disabled */ - WARN_ON(irqs_disabled()); - spin_lock_bh(&call_lock); __smp_call_function_single(cpu, func, info, nonatomic, wait); spin_unlock_bh(&call_lock); diff --git a/arch/x86_64/kernel/traps.c b/arch/x86_64/kernel/traps.c index 7fa155c394d..8713ad4a4db 100644 --- a/arch/x86_64/kernel/traps.c +++ b/arch/x86_64/kernel/traps.c @@ -34,6 +34,10 @@ #include <linux/bug.h> #include <linux/kdebug.h> +#if defined(CONFIG_EDAC) +#include <linux/edac.h> +#endif + #include <asm/system.h> #include <asm/io.h> #include <asm/atomic.h> @@ -330,6 +334,7 @@ static int print_trace_stack(void *data, char *name) static void print_trace_address(void *data, unsigned long addr) { + touch_nmi_watchdog(); printk_address(addr); } @@ -518,6 +523,7 @@ void __kprobes __die(const char * str, struct pt_regs * regs, long err) printk("\n"); notify_die(DIE_OOPS, str, regs, err, current->thread.trap_no, SIGSEGV); show_registers(regs); + add_taint(TAINT_DIE); /* Executive summary in case the oops scrolled away */ printk(KERN_ALERT "RIP "); printk_address(regs->rip); @@ -717,6 +723,13 @@ mem_parity_error(unsigned char reason, struct pt_regs * regs) reason); printk(KERN_EMERG "You have some hardware problem, likely on the PCI bus.\n"); +#if defined(CONFIG_EDAC) + if(edac_handler_set()) { + edac_atomic_assert_error(); + return; + } +#endif + if (panic_on_unrecovered_nmi) panic("NMI: Not continuing"); diff --git a/arch/x86_64/kernel/tsc.c b/arch/x86_64/kernel/tsc.c index 48f9a8e6aa9..e850aa01e1b 100644 --- a/arch/x86_64/kernel/tsc.c +++ b/arch/x86_64/kernel/tsc.c @@ -44,7 +44,7 @@ unsigned long long sched_clock(void) static int tsc_unstable; -static inline int check_tsc_unstable(void) +inline int check_tsc_unstable(void) { return tsc_unstable; } diff --git a/arch/x86_64/kernel/vmlinux.lds.S b/arch/x86_64/kernel/vmlinux.lds.S index dbccfda8364..5c57ea4591c 100644 --- a/arch/x86_64/kernel/vmlinux.lds.S +++ b/arch/x86_64/kernel/vmlinux.lds.S @@ -48,7 +48,9 @@ SECTIONS __ex_table : AT(ADDR(__ex_table) - LOAD_OFFSET) { *(__ex_table) } __stop___ex_table = .; - BUG_TABLE + NOTES :text :note + + BUG_TABLE :text RODATA @@ -194,10 +196,8 @@ SECTIONS __initramfs_end = .; #endif - . = ALIGN(4096); - __per_cpu_start = .; - .data.percpu : AT(ADDR(.data.percpu) - LOAD_OFFSET) { *(.data.percpu) } - __per_cpu_end = .; + PERCPU(4096) + . = ALIGN(4096); __init_end = .; diff --git a/arch/x86_64/mm/fault.c b/arch/x86_64/mm/fault.c index 635e58d443d..84f11728fc7 100644 --- a/arch/x86_64/mm/fault.c +++ b/arch/x86_64/mm/fault.c @@ -317,7 +317,7 @@ asmlinkage void __kprobes do_page_fault(struct pt_regs *regs, struct vm_area_struct * vma; unsigned long address; const struct exception_table_entry *fixup; - int write; + int write, fault; unsigned long flags; siginfo_t info; @@ -450,19 +450,18 @@ good_area: * make sure we exit gracefully rather than endlessly redo * the fault. */ - switch (handle_mm_fault(mm, vma, address, write)) { - case VM_FAULT_MINOR: - tsk->min_flt++; - break; - case VM_FAULT_MAJOR: - tsk->maj_flt++; - break; - case VM_FAULT_SIGBUS: - goto do_sigbus; - default: - goto out_of_memory; + fault = handle_mm_fault(mm, vma, address, write); + if (unlikely(fault & VM_FAULT_ERROR)) { + if (fault & VM_FAULT_OOM) + goto out_of_memory; + else if (fault & VM_FAULT_SIGBUS) + goto do_sigbus; + BUG(); } - + if (fault & VM_FAULT_MAJOR) + tsk->maj_flt++; + else + tsk->min_flt++; up_read(&mm->mmap_sem); return; diff --git a/arch/xtensa/kernel/ptrace.c b/arch/xtensa/kernel/ptrace.c index 14104ff6309..06a13d9b69d 100644 --- a/arch/xtensa/kernel/ptrace.c +++ b/arch/xtensa/kernel/ptrace.c @@ -50,18 +50,8 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) switch (request) { case PTRACE_PEEKTEXT: /* read word at location addr. */ case PTRACE_PEEKDATA: - { - unsigned long tmp; - int copied; - - copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); - ret = -EIO; - if (copied != sizeof(tmp)) - break; - ret = put_user(tmp,(unsigned long *) data); - + ret = generic_ptrace_peekdata(child, addr, data); goto out; - } /* Read the word at location addr in the USER area. */ @@ -138,10 +128,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) case PTRACE_POKETEXT: /* write the word at location addr. */ case PTRACE_POKEDATA: - if (access_process_vm(child, addr, &data, sizeof(data), 1) - == sizeof(data)) - break; - ret = -EIO; + ret = generic_ptrace_pokedata(child, addr, data); goto out; case PTRACE_POKEUSR: diff --git a/arch/xtensa/kernel/traps.c b/arch/xtensa/kernel/traps.c index 693ab268485..c5e62f9d9f5 100644 --- a/arch/xtensa/kernel/traps.c +++ b/arch/xtensa/kernel/traps.c @@ -482,6 +482,7 @@ void die(const char * str, struct pt_regs * regs, long err) if (!user_mode(regs)) show_stack(NULL, (unsigned long*)regs->areg[1]); + add_taint(TAINT_DIE); spin_unlock_irq(&die_lock); if (in_interrupt()) diff --git a/arch/xtensa/kernel/vmlinux.lds.S b/arch/xtensa/kernel/vmlinux.lds.S index bb3f1f3097a..ac4ed52034d 100644 --- a/arch/xtensa/kernel/vmlinux.lds.S +++ b/arch/xtensa/kernel/vmlinux.lds.S @@ -191,10 +191,7 @@ SECTIONS __initramfs_end = .; #endif - . = ALIGN(4096); - __per_cpu_start = .; - .data.percpu : { *(.data.percpu) } - __per_cpu_end = .; + PERCPU(4096) /* We need this dummy segment here */ diff --git a/arch/xtensa/mm/fault.c b/arch/xtensa/mm/fault.c index 3dc6f2f07bb..16004067add 100644 --- a/arch/xtensa/mm/fault.c +++ b/arch/xtensa/mm/fault.c @@ -41,6 +41,7 @@ void do_page_fault(struct pt_regs *regs) siginfo_t info; int is_write, is_exec; + int fault; info.si_code = SEGV_MAPERR; @@ -102,20 +103,18 @@ good_area: * the fault. */ survive: - switch (handle_mm_fault(mm, vma, address, is_write)) { - case VM_FAULT_MINOR: - current->min_flt++; - break; - case VM_FAULT_MAJOR: - current->maj_flt++; - break; - case VM_FAULT_SIGBUS: - goto do_sigbus; - case VM_FAULT_OOM: - goto out_of_memory; - default: + fault = handle_mm_fault(mm, vma, address, is_write); + if (unlikely(fault & VM_FAULT_ERROR)) { + if (fault & VM_FAULT_OOM) + goto out_of_memory; + else if (fault & VM_FAULT_SIGBUS) + goto do_sigbus; BUG(); } + if (fault & VM_FAULT_MAJOR) + current->maj_flt++; + else + current->min_flt++; up_read(&mm->mmap_sem); return; |