diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-10-21 21:19:54 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-10-21 21:19:54 -0700 |
commit | d4429f608abde89e8bc1e24b43cd503feb95c496 (patch) | |
tree | 4c11afa193593a5e3949391bf35022b4f87ba375 /arch/powerpc/platforms | |
parent | e10117d36ef758da0690c95ecffc09d5dd7da479 (diff) | |
parent | 6a1c9dfe4186f18fed38421b35b40fb9260cbfe1 (diff) | |
download | op-kernel-dev-d4429f608abde89e8bc1e24b43cd503feb95c496.zip op-kernel-dev-d4429f608abde89e8bc1e24b43cd503feb95c496.tar.gz |
Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc
* 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc: (71 commits)
powerpc/44x: Update ppc44x_defconfig
powerpc/watchdog: Make default timeout for Book-E watchdog a Kconfig option
fsl_rio: Add comments for sRIO registers.
powerpc/fsl-booke: Add e55xx (64-bit) smp defconfig
powerpc/fsl-booke: Add p5020 DS board support
powerpc/fsl-booke64: Use TLB CAMs to cover linear mapping on FSL 64-bit chips
powerpc/fsl-booke: Add support for FSL Arch v1.0 MMU in setup_page_sizes
powerpc/fsl-booke: Add support for FSL 64-bit e5500 core
powerpc/85xx: add cache-sram support
powerpc/85xx: add ngPIXIS FPGA device tree node to the P1022DS board
powerpc: Fix compile error with paca code on ppc64e
powerpc/fsl-booke: Add p3041 DS board support
oprofile/fsl emb: Don't set MSR[PMM] until after clearing the interrupt.
powerpc/fsl-booke: Add PCI device ids for P2040/P3041/P5010/P5020 QoirQ chips
powerpc/mpc8xxx_gpio: Add support for 'qoriq-gpio' controllers
powerpc/fsl_booke: Add support to boot from core other than 0
powerpc/p1022: Add probing for individual DMA channels
powerpc/fsl_soc: Search all global-utilities nodes for rstccr
powerpc: Fix invalid page flags in create TLB CAM path for PTE_64BIT
powerpc/mpc83xx: Support for MPC8308 P1M board
...
Fix up conflict with the generic irq_work changes in arch/powerpc/kernel/time.c
Diffstat (limited to 'arch/powerpc/platforms')
28 files changed, 921 insertions, 101 deletions
diff --git a/arch/powerpc/platforms/44x/Kconfig b/arch/powerpc/platforms/44x/Kconfig index 69d668c..0f979c5 100644 --- a/arch/powerpc/platforms/44x/Kconfig +++ b/arch/powerpc/platforms/44x/Kconfig @@ -17,6 +17,16 @@ config BAMBOO help This option enables support for the IBM PPC440EP evaluation board. +config BLUESTONE + bool "Bluestone" + depends on 44x + default n + select PPC44x_SIMPLE + select APM821xx + select IBM_NEW_EMAC_RGMII + help + This option enables support for the APM APM821xx Evaluation board. + config EBONY bool "Ebony" depends on 44x @@ -293,6 +303,12 @@ config 460SX select IBM_NEW_EMAC_ZMII select IBM_NEW_EMAC_TAH +config APM821xx + bool + select PPC_FPU + select IBM_NEW_EMAC_EMAC4 + select IBM_NEW_EMAC_TAH + # 44x errata/workaround config symbols, selected by the CPU models above config IBM440EP_ERR42 bool diff --git a/arch/powerpc/platforms/44x/ppc44x_simple.c b/arch/powerpc/platforms/44x/ppc44x_simple.c index 5f7a29d..7ddcba3 100644 --- a/arch/powerpc/platforms/44x/ppc44x_simple.c +++ b/arch/powerpc/platforms/44x/ppc44x_simple.c @@ -52,6 +52,7 @@ machine_device_initcall(ppc44x_simple, ppc44x_device_probe); static char *board[] __initdata = { "amcc,arches", "amcc,bamboo", + "amcc,bluestone", "amcc,canyonlands", "amcc,glacier", "ibm,ebony", diff --git a/arch/powerpc/platforms/83xx/Kconfig b/arch/powerpc/platforms/83xx/Kconfig index 021763a..73f4135 100644 --- a/arch/powerpc/platforms/83xx/Kconfig +++ b/arch/powerpc/platforms/83xx/Kconfig @@ -10,12 +10,12 @@ menuconfig PPC_83xx if PPC_83xx config MPC830x_RDB - bool "Freescale MPC830x RDB" + bool "Freescale MPC830x RDB and derivatives" select DEFAULT_UIMAGE select PPC_MPC831x select FSL_GTM help - This option enables support for the MPC8308 RDB board. + This option enables support for the MPC8308 RDB and MPC8308 P1M boards. config MPC831x_RDB bool "Freescale MPC831x RDB" diff --git a/arch/powerpc/platforms/83xx/mpc830x_rdb.c b/arch/powerpc/platforms/83xx/mpc830x_rdb.c index ac102ee..846831d 100644 --- a/arch/powerpc/platforms/83xx/mpc830x_rdb.c +++ b/arch/powerpc/platforms/83xx/mpc830x_rdb.c @@ -65,7 +65,8 @@ static int __init mpc830x_rdb_probe(void) unsigned long root = of_get_flat_dt_root(); return of_flat_dt_is_compatible(root, "MPC8308RDB") || - of_flat_dt_is_compatible(root, "fsl,mpc8308rdb"); + of_flat_dt_is_compatible(root, "fsl,mpc8308rdb") || + of_flat_dt_is_compatible(root, "denx,mpc8308_p1m"); } static struct of_device_id __initdata of_bus_ids[] = { diff --git a/arch/powerpc/platforms/85xx/Kconfig b/arch/powerpc/platforms/85xx/Kconfig index bea1f59..b6976e1 100644 --- a/arch/powerpc/platforms/85xx/Kconfig +++ b/arch/powerpc/platforms/85xx/Kconfig @@ -11,6 +11,8 @@ menuconfig FSL_SOC_BOOKE if FSL_SOC_BOOKE +if PPC32 + config MPC8540_ADS bool "Freescale MPC8540 ADS" select DEFAULT_UIMAGE @@ -153,10 +155,20 @@ config SBC8560 help This option enables support for the Wind River SBC8560 board +config P3041_DS + bool "Freescale P3041 DS" + select DEFAULT_UIMAGE + select PPC_E500MC + select PHYS_64BIT + select SWIOTLB + select MPC8xxx_GPIO + select HAS_RAPIDIO + help + This option enables support for the P3041 DS board + config P4080_DS bool "Freescale P4080 DS" select DEFAULT_UIMAGE - select PPC_FSL_BOOK3E select PPC_E500MC select PHYS_64BIT select SWIOTLB @@ -165,6 +177,20 @@ config P4080_DS help This option enables support for the P4080 DS board +endif # PPC32 + +config P5020_DS + bool "Freescale P5020 DS" + select DEFAULT_UIMAGE + select E500 + select PPC_E500MC + select PHYS_64BIT + select SWIOTLB + select MPC8xxx_GPIO + select HAS_RAPIDIO + help + This option enables support for the P5020 DS board + endif # FSL_SOC_BOOKE config TQM85xx diff --git a/arch/powerpc/platforms/85xx/Makefile b/arch/powerpc/platforms/85xx/Makefile index a2ec3f8..dd70db7 100644 --- a/arch/powerpc/platforms/85xx/Makefile +++ b/arch/powerpc/platforms/85xx/Makefile @@ -11,7 +11,9 @@ obj-$(CONFIG_MPC85xx_DS) += mpc85xx_ds.o obj-$(CONFIG_MPC85xx_MDS) += mpc85xx_mds.o obj-$(CONFIG_MPC85xx_RDB) += mpc85xx_rdb.o obj-$(CONFIG_P1022_DS) += p1022_ds.o +obj-$(CONFIG_P3041_DS) += p3041_ds.o corenet_ds.o obj-$(CONFIG_P4080_DS) += p4080_ds.o corenet_ds.o +obj-$(CONFIG_P5020_DS) += p5020_ds.o corenet_ds.o obj-$(CONFIG_STX_GP3) += stx_gp3.o obj-$(CONFIG_TQM85xx) += tqm85xx.o obj-$(CONFIG_SBC8560) += sbc8560.o diff --git a/arch/powerpc/platforms/85xx/p1022_ds.c b/arch/powerpc/platforms/85xx/p1022_ds.c index 34e0090..2b390d1 100644 --- a/arch/powerpc/platforms/85xx/p1022_ds.c +++ b/arch/powerpc/platforms/85xx/p1022_ds.c @@ -112,6 +112,8 @@ static struct of_device_id __initdata p1022_ds_ids[] = { { .compatible = "soc", }, { .compatible = "simple-bus", }, { .compatible = "gianfar", }, + /* So that the DMA channel nodes can be probed individually: */ + { .compatible = "fsl,eloplus-dma", }, {}, }; diff --git a/arch/powerpc/platforms/85xx/p3041_ds.c b/arch/powerpc/platforms/85xx/p3041_ds.c new file mode 100644 index 0000000..0ed52e1 --- /dev/null +++ b/arch/powerpc/platforms/85xx/p3041_ds.c @@ -0,0 +1,64 @@ +/* + * P3041 DS Setup + * + * Maintained by Kumar Gala (see MAINTAINERS for contact information) + * + * Copyright 2009-2010 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 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/kdev_t.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/phy.h> + +#include <asm/system.h> +#include <asm/time.h> +#include <asm/machdep.h> +#include <asm/pci-bridge.h> +#include <mm/mmu_decl.h> +#include <asm/prom.h> +#include <asm/udbg.h> +#include <asm/mpic.h> + +#include <linux/of_platform.h> +#include <sysdev/fsl_soc.h> +#include <sysdev/fsl_pci.h> + +#include "corenet_ds.h" + +/* + * Called very early, device-tree isn't unflattened + */ +static int __init p3041_ds_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + + return of_flat_dt_is_compatible(root, "fsl,P3041DS"); +} + +define_machine(p3041_ds) { + .name = "P3041 DS", + .probe = p3041_ds_probe, + .setup_arch = corenet_ds_setup_arch, + .init_IRQ = corenet_ds_pic_init, +#ifdef CONFIG_PCI + .pcibios_fixup_bus = fsl_pcibios_fixup_bus, +#endif + .get_irq = mpic_get_coreint_irq, + .restart = fsl_rstcr_restart, + .calibrate_decr = generic_calibrate_decr, + .progress = udbg_progress, +}; + +machine_device_initcall(p3041_ds, corenet_ds_publish_devices); + +#ifdef CONFIG_SWIOTLB +machine_arch_initcall(p3041_ds, swiotlb_setup_bus_notifier); +#endif diff --git a/arch/powerpc/platforms/85xx/p5020_ds.c b/arch/powerpc/platforms/85xx/p5020_ds.c new file mode 100644 index 0000000..7467b71 --- /dev/null +++ b/arch/powerpc/platforms/85xx/p5020_ds.c @@ -0,0 +1,69 @@ +/* + * P5020 DS Setup + * + * Maintained by Kumar Gala (see MAINTAINERS for contact information) + * + * Copyright 2009-2010 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 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/kdev_t.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/phy.h> + +#include <asm/system.h> +#include <asm/time.h> +#include <asm/machdep.h> +#include <asm/pci-bridge.h> +#include <mm/mmu_decl.h> +#include <asm/prom.h> +#include <asm/udbg.h> +#include <asm/mpic.h> + +#include <linux/of_platform.h> +#include <sysdev/fsl_soc.h> +#include <sysdev/fsl_pci.h> + +#include "corenet_ds.h" + +/* + * Called very early, device-tree isn't unflattened + */ +static int __init p5020_ds_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + + return of_flat_dt_is_compatible(root, "fsl,P5020DS"); +} + +define_machine(p5020_ds) { + .name = "P5020 DS", + .probe = p5020_ds_probe, + .setup_arch = corenet_ds_setup_arch, + .init_IRQ = corenet_ds_pic_init, +#ifdef CONFIG_PCI + .pcibios_fixup_bus = fsl_pcibios_fixup_bus, +#endif +/* coreint doesn't play nice with lazy EE, use legacy mpic for now */ +#ifdef CONFIG_PPC64 + .get_irq = mpic_get_irq, +#else + .get_irq = mpic_get_coreint_irq, +#endif + .restart = fsl_rstcr_restart, + .calibrate_decr = generic_calibrate_decr, + .progress = udbg_progress, +}; + +machine_device_initcall(p5020_ds, corenet_ds_publish_devices); + +#ifdef CONFIG_SWIOTLB +machine_arch_initcall(p5020_ds, swiotlb_setup_bus_notifier); +#endif diff --git a/arch/powerpc/platforms/85xx/smp.c b/arch/powerpc/platforms/85xx/smp.c index a6b1065..5c91a99 100644 --- a/arch/powerpc/platforms/85xx/smp.c +++ b/arch/powerpc/platforms/85xx/smp.c @@ -16,6 +16,7 @@ #include <linux/delay.h> #include <linux/of.h> #include <linux/kexec.h> +#include <linux/highmem.h> #include <asm/machdep.h> #include <asm/pgtable.h> @@ -79,6 +80,7 @@ smp_85xx_kick_cpu(int nr) local_irq_save(flags); out_be32(bptr_vaddr + BOOT_ENTRY_PIR, nr); +#ifdef CONFIG_PPC32 out_be32(bptr_vaddr + BOOT_ENTRY_ADDR_LOWER, __pa(__early_start)); if (!ioremappable) @@ -88,6 +90,12 @@ smp_85xx_kick_cpu(int nr) /* Wait a bit for the CPU to ack. */ while ((__secondary_hold_acknowledge != nr) && (++n < 1000)) mdelay(1); +#else + out_be64((u64 *)(bptr_vaddr + BOOT_ENTRY_ADDR_UPPER), + __pa((u64)*((unsigned long long *) generic_secondary_smp_init))); + + smp_generic_kick_cpu(nr); +#endif local_irq_restore(flags); @@ -114,19 +122,15 @@ struct smp_ops_t smp_85xx_ops = { }; #ifdef CONFIG_KEXEC -static int kexec_down_cpus = 0; +atomic_t kexec_down_cpus = ATOMIC_INIT(0); void mpc85xx_smp_kexec_cpu_down(int crash_shutdown, int secondary) { - mpic_teardown_this_cpu(1); - - /* When crashing, this gets called on all CPU's we only - * take down the non-boot cpus */ - if (smp_processor_id() != boot_cpuid) - { - local_irq_disable(); - kexec_down_cpus++; + local_irq_disable(); + if (secondary) { + atomic_inc(&kexec_down_cpus); + /* loop forever */ while (1); } } @@ -137,16 +141,65 @@ static void mpc85xx_smp_kexec_down(void *arg) ppc_md.kexec_cpu_down(0,1); } -static void mpc85xx_smp_machine_kexec(struct kimage *image) +static void map_and_flush(unsigned long paddr) { - int timeout = 2000; + struct page *page = pfn_to_page(paddr >> PAGE_SHIFT); + unsigned long kaddr = (unsigned long)kmap(page); + + flush_dcache_range(kaddr, kaddr + PAGE_SIZE); + kunmap(page); +} + +/** + * Before we reset the other cores, we need to flush relevant cache + * out to memory so we don't get anything corrupted, some of these flushes + * are performed out of an overabundance of caution as interrupts are not + * disabled yet and we can switch cores + */ +static void mpc85xx_smp_flush_dcache_kexec(struct kimage *image) +{ + kimage_entry_t *ptr, entry; + unsigned long paddr; int i; - set_cpus_allowed(current, cpumask_of_cpu(boot_cpuid)); + if (image->type == KEXEC_TYPE_DEFAULT) { + /* normal kexec images are stored in temporary pages */ + for (ptr = &image->head; (entry = *ptr) && !(entry & IND_DONE); + ptr = (entry & IND_INDIRECTION) ? + phys_to_virt(entry & PAGE_MASK) : ptr + 1) { + if (!(entry & IND_DESTINATION)) { + map_and_flush(entry); + } + } + /* flush out last IND_DONE page */ + map_and_flush(entry); + } else { + /* crash type kexec images are copied to the crash region */ + for (i = 0; i < image->nr_segments; i++) { + struct kexec_segment *seg = &image->segment[i]; + for (paddr = seg->mem; paddr < seg->mem + seg->memsz; + paddr += PAGE_SIZE) { + map_and_flush(paddr); + } + } + } + + /* also flush the kimage struct to be passed in as well */ + flush_dcache_range((unsigned long)image, + (unsigned long)image + sizeof(*image)); +} + +static void mpc85xx_smp_machine_kexec(struct kimage *image) +{ + int timeout = INT_MAX; + int i, num_cpus = num_present_cpus(); + + mpc85xx_smp_flush_dcache_kexec(image); - smp_call_function(mpc85xx_smp_kexec_down, NULL, 0); + if (image->type == KEXEC_TYPE_DEFAULT) + smp_call_function(mpc85xx_smp_kexec_down, NULL, 0); - while ( (kexec_down_cpus != (num_online_cpus() - 1)) && + while ( (atomic_read(&kexec_down_cpus) != (num_cpus - 1)) && ( timeout > 0 ) ) { timeout--; @@ -155,7 +208,7 @@ static void mpc85xx_smp_machine_kexec(struct kimage *image) if ( !timeout ) printk(KERN_ERR "Unable to bring down secondary cpu(s)"); - for (i = 0; i < num_present_cpus(); i++) + for (i = 0; i < num_cpus; i++) { if ( i == smp_processor_id() ) continue; mpic_reset_core(i); diff --git a/arch/powerpc/platforms/Kconfig.cputype b/arch/powerpc/platforms/Kconfig.cputype index d361f81..111138c 100644 --- a/arch/powerpc/platforms/Kconfig.cputype +++ b/arch/powerpc/platforms/Kconfig.cputype @@ -125,6 +125,7 @@ config 8xx config E500 select FSL_EMB_PERFMON + select PPC_FSL_BOOK3E bool config PPC_E500MC @@ -166,9 +167,14 @@ config BOOKE config FSL_BOOKE bool - depends on E200 || E500 + depends on (E200 || E500) && PPC32 default y +# this is for common code between PPC32 & PPC64 FSL BOOKE +config PPC_FSL_BOOK3E + bool + select FSL_EMB_PERFMON + default y if FSL_BOOKE config PTE_64BIT bool diff --git a/arch/powerpc/platforms/cell/ras.c b/arch/powerpc/platforms/cell/ras.c index 1d3c4ef..5ec1e47 100644 --- a/arch/powerpc/platforms/cell/ras.c +++ b/arch/powerpc/platforms/cell/ras.c @@ -173,8 +173,10 @@ static int __init cbe_ptcal_enable(void) return -ENODEV; size = of_get_property(np, "ibm,cbe-ptcal-size", NULL); - if (!size) + if (!size) { + of_node_put(np); return -ENODEV; + } pr_debug("%s: enabling PTCAL, size = 0x%x\n", __func__, *size); order = get_order(*size); diff --git a/arch/powerpc/platforms/cell/spider-pic.c b/arch/powerpc/platforms/cell/spider-pic.c index 5876e88..3f2e557 100644 --- a/arch/powerpc/platforms/cell/spider-pic.c +++ b/arch/powerpc/platforms/cell/spider-pic.c @@ -258,8 +258,10 @@ static unsigned int __init spider_find_cascade_and_node(struct spider_pic *pic) return NO_IRQ; imap += intsize + 1; tmp = of_get_property(iic, "#interrupt-cells", NULL); - if (tmp == NULL) + if (tmp == NULL) { + of_node_put(iic); return NO_IRQ; + } intsize = *tmp; /* Assume unit is last entry of interrupt specifier */ unit = imap[intsize - 1]; diff --git a/arch/powerpc/platforms/cell/spufs/file.c b/arch/powerpc/platforms/cell/spufs/file.c index 1a40da9..02f7b11 100644 --- a/arch/powerpc/platforms/cell/spufs/file.c +++ b/arch/powerpc/platforms/cell/spufs/file.c @@ -154,6 +154,7 @@ static const struct file_operations __fops = { \ .release = spufs_attr_release, \ .read = spufs_attr_read, \ .write = spufs_attr_write, \ + .llseek = generic_file_llseek, \ }; @@ -521,6 +522,7 @@ static const struct file_operations spufs_cntl_fops = { .release = spufs_cntl_release, .read = simple_attr_read, .write = simple_attr_write, + .llseek = generic_file_llseek, .mmap = spufs_cntl_mmap, }; @@ -714,6 +716,7 @@ static ssize_t spufs_mbox_read(struct file *file, char __user *buf, static const struct file_operations spufs_mbox_fops = { .open = spufs_pipe_open, .read = spufs_mbox_read, + .llseek = no_llseek, }; static ssize_t spufs_mbox_stat_read(struct file *file, char __user *buf, @@ -743,6 +746,7 @@ static ssize_t spufs_mbox_stat_read(struct file *file, char __user *buf, static const struct file_operations spufs_mbox_stat_fops = { .open = spufs_pipe_open, .read = spufs_mbox_stat_read, + .llseek = no_llseek, }; /* low-level ibox access function */ @@ -863,6 +867,7 @@ static const struct file_operations spufs_ibox_fops = { .read = spufs_ibox_read, .poll = spufs_ibox_poll, .fasync = spufs_ibox_fasync, + .llseek = no_llseek, }; static ssize_t spufs_ibox_stat_read(struct file *file, char __user *buf, @@ -890,6 +895,7 @@ static ssize_t spufs_ibox_stat_read(struct file *file, char __user *buf, static const struct file_operations spufs_ibox_stat_fops = { .open = spufs_pipe_open, .read = spufs_ibox_stat_read, + .llseek = no_llseek, }; /* low-level mailbox write */ @@ -1011,6 +1017,7 @@ static const struct file_operations spufs_wbox_fops = { .write = spufs_wbox_write, .poll = spufs_wbox_poll, .fasync = spufs_wbox_fasync, + .llseek = no_llseek, }; static ssize_t spufs_wbox_stat_read(struct file *file, char __user *buf, @@ -1038,6 +1045,7 @@ static ssize_t spufs_wbox_stat_read(struct file *file, char __user *buf, static const struct file_operations spufs_wbox_stat_fops = { .open = spufs_pipe_open, .read = spufs_wbox_stat_read, + .llseek = no_llseek, }; static int spufs_signal1_open(struct inode *inode, struct file *file) @@ -1166,6 +1174,7 @@ static const struct file_operations spufs_signal1_fops = { .read = spufs_signal1_read, .write = spufs_signal1_write, .mmap = spufs_signal1_mmap, + .llseek = no_llseek, }; static const struct file_operations spufs_signal1_nosched_fops = { @@ -1173,6 +1182,7 @@ static const struct file_operations spufs_signal1_nosched_fops = { .release = spufs_signal1_release, .write = spufs_signal1_write, .mmap = spufs_signal1_mmap, + .llseek = no_llseek, }; static int spufs_signal2_open(struct inode *inode, struct file *file) @@ -1305,6 +1315,7 @@ static const struct file_operations spufs_signal2_fops = { .read = spufs_signal2_read, .write = spufs_signal2_write, .mmap = spufs_signal2_mmap, + .llseek = no_llseek, }; static const struct file_operations spufs_signal2_nosched_fops = { @@ -1312,6 +1323,7 @@ static const struct file_operations spufs_signal2_nosched_fops = { .release = spufs_signal2_release, .write = spufs_signal2_write, .mmap = spufs_signal2_mmap, + .llseek = no_llseek, }; /* @@ -1451,6 +1463,7 @@ static const struct file_operations spufs_mss_fops = { .open = spufs_mss_open, .release = spufs_mss_release, .mmap = spufs_mss_mmap, + .llseek = no_llseek, }; static int @@ -1508,6 +1521,7 @@ static const struct file_operations spufs_psmap_fops = { .open = spufs_psmap_open, .release = spufs_psmap_release, .mmap = spufs_psmap_mmap, + .llseek = no_llseek, }; @@ -1871,6 +1885,7 @@ static const struct file_operations spufs_mfc_fops = { .fsync = spufs_mfc_fsync, .fasync = spufs_mfc_fasync, .mmap = spufs_mfc_mmap, + .llseek = no_llseek, }; static int spufs_npc_set(void *data, u64 val) @@ -2246,6 +2261,7 @@ static ssize_t spufs_dma_info_read(struct file *file, char __user *buf, static const struct file_operations spufs_dma_info_fops = { .open = spufs_info_open, .read = spufs_dma_info_read, + .llseek = no_llseek, }; static ssize_t __spufs_proxydma_info_read(struct spu_context *ctx, @@ -2299,6 +2315,7 @@ static ssize_t spufs_proxydma_info_read(struct file *file, char __user *buf, static const struct file_operations spufs_proxydma_info_fops = { .open = spufs_info_open, .read = spufs_proxydma_info_read, + .llseek = no_llseek, }; static int spufs_show_tid(struct seq_file *s, void *private) @@ -2585,6 +2602,7 @@ static const struct file_operations spufs_switch_log_fops = { .read = spufs_switch_log_read, .poll = spufs_switch_log_poll, .release = spufs_switch_log_release, + .llseek = no_llseek, }; /** diff --git a/arch/powerpc/platforms/chrp/nvram.c b/arch/powerpc/platforms/chrp/nvram.c index ba3588f..d3ceff0 100644 --- a/arch/powerpc/platforms/chrp/nvram.c +++ b/arch/powerpc/platforms/chrp/nvram.c @@ -74,8 +74,10 @@ void __init chrp_nvram_init(void) return; nbytes_p = of_get_property(nvram, "#bytes", &proplen); - if (nbytes_p == NULL || proplen != sizeof(unsigned int)) + if (nbytes_p == NULL || proplen != sizeof(unsigned int)) { + of_node_put(nvram); return; + } nvram_size = *nbytes_p; diff --git a/arch/powerpc/platforms/iseries/Makefile b/arch/powerpc/platforms/iseries/Makefile index ce01492..a7602b1 100644 --- a/arch/powerpc/platforms/iseries/Makefile +++ b/arch/powerpc/platforms/iseries/Makefile @@ -1,4 +1,4 @@ -EXTRA_CFLAGS += -mno-minimal-toc +ccflags-y := -mno-minimal-toc obj-y += exception.o obj-y += hvlog.o hvlpconfig.o lpardata.o setup.o dt.o mf.o lpevents.o \ diff --git a/arch/powerpc/platforms/iseries/dt.c b/arch/powerpc/platforms/iseries/dt.c index 7f45a51..fdb7384 100644 --- a/arch/powerpc/platforms/iseries/dt.c +++ b/arch/powerpc/platforms/iseries/dt.c @@ -243,7 +243,7 @@ static void __init dt_cpus(struct iseries_flat_dt *dt) pft_size[1] = __ilog2(HvCallHpt_getHptPages() * HW_PAGE_SIZE); for (i = 0; i < NR_CPUS; i++) { - if (lppaca[i].dyn_proc_status >= 2) + if (lppaca_of(i).dyn_proc_status >= 2) continue; snprintf(p, 32 - (p - buf), "@%d", i); @@ -251,7 +251,7 @@ static void __init dt_cpus(struct iseries_flat_dt *dt) dt_prop_str(dt, "device_type", device_type_cpu); - index = lppaca[i].dyn_hv_phys_proc_index; + index = lppaca_of(i).dyn_hv_phys_proc_index; d = &xIoHriProcessorVpd[index]; dt_prop_u32(dt, "i-cache-size", d->xInstCacheSize * 1024); diff --git a/arch/powerpc/platforms/iseries/smp.c b/arch/powerpc/platforms/iseries/smp.c index 6590850..6c60299 100644 --- a/arch/powerpc/platforms/iseries/smp.c +++ b/arch/powerpc/platforms/iseries/smp.c @@ -91,7 +91,7 @@ static void smp_iSeries_kick_cpu(int nr) BUG_ON((nr < 0) || (nr >= NR_CPUS)); /* Verify that our partition has a processor nr */ - if (lppaca[nr].dyn_proc_status >= 2) + if (lppaca_of(nr).dyn_proc_status >= 2) return; /* The processor is currently spinning, waiting diff --git a/arch/powerpc/platforms/maple/setup.c b/arch/powerpc/platforms/maple/setup.c index 3fff8d9..fe34c3d 100644 --- a/arch/powerpc/platforms/maple/setup.c +++ b/arch/powerpc/platforms/maple/setup.c @@ -358,6 +358,7 @@ static int __init maple_cpc925_edac_setup(void) model = (const unsigned char *)of_get_property(np, "model", NULL); if (!model) { printk(KERN_ERR "%s: Unabel to get model info\n", __func__); + of_node_put(np); return -ENODEV; } diff --git a/arch/powerpc/platforms/powermac/pfunc_core.c b/arch/powerpc/platforms/powermac/pfunc_core.c index cec6359..b0c3777 100644 --- a/arch/powerpc/platforms/powermac/pfunc_core.c +++ b/arch/powerpc/platforms/powermac/pfunc_core.c @@ -837,8 +837,10 @@ struct pmf_function *__pmf_find_function(struct device_node *target, return NULL; find_it: dev = pmf_find_device(actor); - if (dev == NULL) - return NULL; + if (dev == NULL) { + result = NULL; + goto out; + } list_for_each_entry(func, &dev->functions, link) { if (name && strcmp(name, func->name)) @@ -850,8 +852,9 @@ struct pmf_function *__pmf_find_function(struct device_node *target, result = func; break; } - of_node_put(actor); pmf_put_device(dev); +out: + of_node_put(actor); return result; } diff --git a/arch/powerpc/platforms/pseries/Makefile b/arch/powerpc/platforms/pseries/Makefile index 046ace9..59eb8bd 100644 --- a/arch/powerpc/platforms/pseries/Makefile +++ b/arch/powerpc/platforms/pseries/Makefile @@ -1,14 +1,9 @@ -ifeq ($(CONFIG_PPC64),y) -EXTRA_CFLAGS += -mno-minimal-toc -endif - -ifeq ($(CONFIG_PPC_PSERIES_DEBUG),y) -EXTRA_CFLAGS += -DDEBUG -endif +ccflags-$(CONFIG_PPC64) := -mno-minimal-toc +ccflags-$(CONFIG_PPC_PSERIES_DEBUG) += -DDEBUG obj-y := lpar.o hvCall.o nvram.o reconfig.o \ setup.o iommu.o event_sources.o ras.o \ - firmware.o power.o dlpar.o + firmware.o power.o dlpar.o mobility.o obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_XICS) += xics.o obj-$(CONFIG_SCANLOG) += scanlog.o @@ -23,7 +18,7 @@ obj-$(CONFIG_MEMORY_HOTPLUG) += hotplug-memory.o obj-$(CONFIG_HVC_CONSOLE) += hvconsole.o obj-$(CONFIG_HVCS) += hvcserver.o obj-$(CONFIG_HCALL_STATS) += hvCall_inst.o -obj-$(CONFIG_PHYP_DUMP) += phyp_dump.o +obj-$(CONFIG_PHYP_DUMP) += phyp_dump.o obj-$(CONFIG_CMM) += cmm.o obj-$(CONFIG_DTL) += dtl.o diff --git a/arch/powerpc/platforms/pseries/dlpar.c b/arch/powerpc/platforms/pseries/dlpar.c index 72d8054..b74a923 100644 --- a/arch/powerpc/platforms/pseries/dlpar.c +++ b/arch/powerpc/platforms/pseries/dlpar.c @@ -33,7 +33,7 @@ struct cc_workarea { u32 prop_offset; }; -static void dlpar_free_cc_property(struct property *prop) +void dlpar_free_cc_property(struct property *prop) { kfree(prop->name); kfree(prop->value); @@ -55,13 +55,12 @@ static struct property *dlpar_parse_cc_property(struct cc_workarea *ccwa) prop->length = ccwa->prop_length; value = (char *)ccwa + ccwa->prop_offset; - prop->value = kzalloc(prop->length, GFP_KERNEL); + prop->value = kmemdup(value, prop->length, GFP_KERNEL); if (!prop->value) { dlpar_free_cc_property(prop); return NULL; } - memcpy(prop->value, value, prop->length); return prop; } @@ -102,7 +101,7 @@ static void dlpar_free_one_cc_node(struct device_node *dn) kfree(dn); } -static void dlpar_free_cc_nodes(struct device_node *dn) +void dlpar_free_cc_nodes(struct device_node *dn) { if (dn->child) dlpar_free_cc_nodes(dn->child); diff --git a/arch/powerpc/platforms/pseries/dtl.c b/arch/powerpc/platforms/pseries/dtl.c index a00addb5..c371bc0 100644 --- a/arch/powerpc/platforms/pseries/dtl.c +++ b/arch/powerpc/platforms/pseries/dtl.c @@ -23,37 +23,22 @@ #include <linux/init.h> #include <linux/slab.h> #include <linux/debugfs.h> +#include <linux/spinlock.h> #include <asm/smp.h> #include <asm/system.h> #include <asm/uaccess.h> #include <asm/firmware.h> +#include <asm/lppaca.h> #include "plpar_wrappers.h" -/* - * Layout of entries in the hypervisor's DTL buffer. Although we don't - * actually access the internals of an entry (we only need to know the size), - * we might as well define it here for reference. - */ -struct dtl_entry { - u8 dispatch_reason; - u8 preempt_reason; - u16 processor_id; - u32 enqueue_to_dispatch_time; - u32 ready_to_enqueue_time; - u32 waiting_to_ready_time; - u64 timebase; - u64 fault_addr; - u64 srr0; - u64 srr1; -}; - struct dtl { struct dtl_entry *buf; struct dentry *file; int cpu; int buf_entries; u64 last_idx; + spinlock_t lock; }; static DEFINE_PER_CPU(struct dtl, cpu_dtl); @@ -72,25 +57,97 @@ static u8 dtl_event_mask = 0x7; static int dtl_buf_entries = (16 * 85); -static int dtl_enable(struct dtl *dtl) +#ifdef CONFIG_VIRT_CPU_ACCOUNTING +struct dtl_ring { + u64 write_index; + struct dtl_entry *write_ptr; + struct dtl_entry *buf; + struct dtl_entry *buf_end; + u8 saved_dtl_mask; +}; + +static DEFINE_PER_CPU(struct dtl_ring, dtl_rings); + +static atomic_t dtl_count; + +/* + * The cpu accounting code controls the DTL ring buffer, and we get + * given entries as they are processed. + */ +static void consume_dtle(struct dtl_entry *dtle, u64 index) { - unsigned long addr; - int ret, hwcpu; + struct dtl_ring *dtlr = &__get_cpu_var(dtl_rings); + struct dtl_entry *wp = dtlr->write_ptr; + struct lppaca *vpa = local_paca->lppaca_ptr; - /* only allow one reader */ - if (dtl->buf) - return -EBUSY; + if (!wp) + return; - /* we need to store the original allocation size for use during read */ - dtl->buf_entries = dtl_buf_entries; + *wp = *dtle; + barrier(); - dtl->buf = kmalloc_node(dtl->buf_entries * sizeof(struct dtl_entry), - GFP_KERNEL, cpu_to_node(dtl->cpu)); - if (!dtl->buf) { - printk(KERN_WARNING "%s: buffer alloc failed for cpu %d\n", - __func__, dtl->cpu); - return -ENOMEM; - } + /* check for hypervisor ring buffer overflow, ignore this entry if so */ + if (index + N_DISPATCH_LOG < vpa->dtl_idx) + return; + + ++wp; + if (wp == dtlr->buf_end) + wp = dtlr->buf; + dtlr->write_ptr = wp; + + /* incrementing write_index makes the new entry visible */ + smp_wmb(); + ++dtlr->write_index; +} + +static int dtl_start(struct dtl *dtl) +{ + struct dtl_ring *dtlr = &per_cpu(dtl_rings, dtl->cpu); + + dtlr->buf = dtl->buf; + dtlr->buf_end = dtl->buf + dtl->buf_entries; + dtlr->write_index = 0; + + /* setting write_ptr enables logging into our buffer */ + smp_wmb(); + dtlr->write_ptr = dtl->buf; + + /* enable event logging */ + dtlr->saved_dtl_mask = lppaca_of(dtl->cpu).dtl_enable_mask; + lppaca_of(dtl->cpu).dtl_enable_mask |= dtl_event_mask; + + dtl_consumer = consume_dtle; + atomic_inc(&dtl_count); + return 0; +} + +static void dtl_stop(struct dtl *dtl) +{ + struct dtl_ring *dtlr = &per_cpu(dtl_rings, dtl->cpu); + + dtlr->write_ptr = NULL; + smp_wmb(); + + dtlr->buf = NULL; + + /* restore dtl_enable_mask */ + lppaca_of(dtl->cpu).dtl_enable_mask = dtlr->saved_dtl_mask; + + if (atomic_dec_and_test(&dtl_count)) + dtl_consumer = NULL; +} + +static u64 dtl_current_index(struct dtl *dtl) +{ + return per_cpu(dtl_rings, dtl->cpu).write_index; +} + +#else /* CONFIG_VIRT_CPU_ACCOUNTING */ + +static int dtl_start(struct dtl *dtl) +{ + unsigned long addr; + int ret, hwcpu; /* Register our dtl buffer with the hypervisor. The HV expects the * buffer size to be passed in the second word of the buffer */ @@ -102,34 +159,82 @@ static int dtl_enable(struct dtl *dtl) if (ret) { printk(KERN_WARNING "%s: DTL registration for cpu %d (hw %d) " "failed with %d\n", __func__, dtl->cpu, hwcpu, ret); - kfree(dtl->buf); return -EIO; } /* set our initial buffer indices */ - dtl->last_idx = lppaca[dtl->cpu].dtl_idx = 0; + lppaca_of(dtl->cpu).dtl_idx = 0; /* ensure that our updates to the lppaca fields have occurred before * we actually enable the logging */ smp_wmb(); /* enable event logging */ - lppaca[dtl->cpu].dtl_enable_mask = dtl_event_mask; + lppaca_of(dtl->cpu).dtl_enable_mask = dtl_event_mask; return 0; } -static void dtl_disable(struct dtl *dtl) +static void dtl_stop(struct dtl *dtl) { int hwcpu = get_hard_smp_processor_id(dtl->cpu); - lppaca[dtl->cpu].dtl_enable_mask = 0x0; + lppaca_of(dtl->cpu).dtl_enable_mask = 0x0; unregister_dtl(hwcpu, __pa(dtl->buf)); +} + +static u64 dtl_current_index(struct dtl *dtl) +{ + return lppaca_of(dtl->cpu).dtl_idx; +} +#endif /* CONFIG_VIRT_CPU_ACCOUNTING */ + +static int dtl_enable(struct dtl *dtl) +{ + long int n_entries; + long int rc; + struct dtl_entry *buf = NULL; + /* only allow one reader */ + if (dtl->buf) + return -EBUSY; + + n_entries = dtl_buf_entries; + buf = kmalloc_node(n_entries * sizeof(struct dtl_entry), + GFP_KERNEL, cpu_to_node(dtl->cpu)); + if (!buf) { + printk(KERN_WARNING "%s: buffer alloc failed for cpu %d\n", + __func__, dtl->cpu); + return -ENOMEM; + } + + spin_lock(&dtl->lock); + rc = -EBUSY; + if (!dtl->buf) { + /* store the original allocation size for use during read */ + dtl->buf_entries = n_entries; + dtl->buf = buf; + dtl->last_idx = 0; + rc = dtl_start(dtl); + if (rc) + dtl->buf = NULL; + } + spin_unlock(&dtl->lock); + + if (rc) + kfree(buf); + return rc; +} + +static void dtl_disable(struct dtl *dtl) +{ + spin_lock(&dtl->lock); + dtl_stop(dtl); kfree(dtl->buf); dtl->buf = NULL; dtl->buf_entries = 0; + spin_unlock(&dtl->lock); } /* file interface */ @@ -157,8 +262,9 @@ static int dtl_file_release(struct inode *inode, struct file *filp) static ssize_t dtl_file_read(struct file *filp, char __user *buf, size_t len, loff_t *pos) { - int rc, cur_idx, last_idx, n_read, n_req, read_size; + long int rc, n_read, n_req, read_size; struct dtl *dtl; + u64 cur_idx, last_idx, i; if ((len % sizeof(struct dtl_entry)) != 0) return -EINVAL; @@ -171,41 +277,48 @@ static ssize_t dtl_file_read(struct file *filp, char __user *buf, size_t len, /* actual number of entries read */ n_read = 0; - cur_idx = lppaca[dtl->cpu].dtl_idx; + spin_lock(&dtl->lock); + + cur_idx = dtl_current_index(dtl); last_idx = dtl->last_idx; - if (cur_idx - last_idx > dtl->buf_entries) { - pr_debug("%s: hv buffer overflow for cpu %d, samples lost\n", - __func__, dtl->cpu); - } + if (last_idx + dtl->buf_entries <= cur_idx) + last_idx = cur_idx - dtl->buf_entries + 1; + + if (last_idx + n_req > cur_idx) + n_req = cur_idx - last_idx; + + if (n_req > 0) + dtl->last_idx = last_idx + n_req; + + spin_unlock(&dtl->lock); + + if (n_req <= 0) + return 0; - cur_idx %= dtl->buf_entries; - last_idx %= dtl->buf_entries; + i = last_idx % dtl->buf_entries; /* read the tail of the buffer if we've wrapped */ - if (last_idx > cur_idx) { - read_size = min(n_req, dtl->buf_entries - last_idx); + if (i + n_req > dtl->buf_entries) { + read_size = dtl->buf_entries - i; - rc = copy_to_user(buf, &dtl->buf[last_idx], + rc = copy_to_user(buf, &dtl->buf[i], read_size * sizeof(struct dtl_entry)); if (rc) return -EFAULT; - last_idx = 0; + i = 0; n_req -= read_size; n_read += read_size; buf += read_size * sizeof(struct dtl_entry); } /* .. and now the head */ - read_size = min(n_req, cur_idx - last_idx); - rc = copy_to_user(buf, &dtl->buf[last_idx], - read_size * sizeof(struct dtl_entry)); + rc = copy_to_user(buf, &dtl->buf[i], n_req * sizeof(struct dtl_entry)); if (rc) return -EFAULT; - n_read += read_size; - dtl->last_idx += n_read; + n_read += n_req; return n_read * sizeof(struct dtl_entry); } @@ -263,6 +376,7 @@ static int dtl_init(void) /* set up the per-cpu log structures */ for_each_possible_cpu(i) { struct dtl *dtl = &per_cpu(cpu_dtl, i); + spin_lock_init(&dtl->lock); dtl->cpu = i; rc = dtl_setup_file(dtl); diff --git a/arch/powerpc/platforms/pseries/lpar.c b/arch/powerpc/platforms/pseries/lpar.c index cf79b46..f129040 100644 --- a/arch/powerpc/platforms/pseries/lpar.c +++ b/arch/powerpc/platforms/pseries/lpar.c @@ -248,11 +248,13 @@ void vpa_init(int cpu) int hwcpu = get_hard_smp_processor_id(cpu); unsigned long addr; long ret; + struct paca_struct *pp; + struct dtl_entry *dtl; if (cpu_has_feature(CPU_FTR_ALTIVEC)) - lppaca[cpu].vmxregs_in_use = 1; + lppaca_of(cpu).vmxregs_in_use = 1; - addr = __pa(&lppaca[cpu]); + addr = __pa(&lppaca_of(cpu)); ret = register_vpa(hwcpu, addr); if (ret) { @@ -274,6 +276,25 @@ void vpa_init(int cpu) "registration for cpu %d (hw %d) of area %lx " "returns %ld\n", cpu, hwcpu, addr, ret); } + + /* + * Register dispatch trace log, if one has been allocated. + */ + pp = &paca[cpu]; + dtl = pp->dispatch_log; + if (dtl) { + pp->dtl_ridx = 0; + pp->dtl_curr = dtl; + lppaca_of(cpu).dtl_idx = 0; + + /* hypervisor reads buffer length from this field */ + dtl->enqueue_to_dispatch_time = DISPATCH_LOG_BYTES; + ret = register_dtl(hwcpu, __pa(dtl)); + if (ret) + pr_warn("DTL registration failed for cpu %d (%ld)\n", + cpu, ret); + lppaca_of(cpu).dtl_enable_mask = 2; + } } static long pSeries_lpar_hpte_insert(unsigned long hpte_group, diff --git a/arch/powerpc/platforms/pseries/mobility.c b/arch/powerpc/platforms/pseries/mobility.c new file mode 100644 index 0000000..3e7f651 --- /dev/null +++ b/arch/powerpc/platforms/pseries/mobility.c @@ -0,0 +1,362 @@ +/* + * Support for Partition Mobility/Migration + * + * Copyright (C) 2010 Nathan Fontenot + * Copyright (C) 2010 IBM Corporation + * + * 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 <linux/kernel.h> +#include <linux/kobject.h> +#include <linux/smp.h> +#include <linux/completion.h> +#include <linux/device.h> +#include <linux/delay.h> +#include <linux/slab.h> + +#include <asm/rtas.h> +#include "pseries.h" + +static struct kobject *mobility_kobj; + +struct update_props_workarea { + u32 phandle; + u32 state; + u64 reserved; + u32 nprops; +}; + +#define NODE_ACTION_MASK 0xff000000 +#define NODE_COUNT_MASK 0x00ffffff + +#define DELETE_DT_NODE 0x01000000 +#define UPDATE_DT_NODE 0x02000000 +#define ADD_DT_NODE 0x03000000 + +static int mobility_rtas_call(int token, char *buf) +{ + int rc; + + spin_lock(&rtas_data_buf_lock); + + memcpy(rtas_data_buf, buf, RTAS_DATA_BUF_SIZE); + rc = rtas_call(token, 2, 1, NULL, rtas_data_buf, 1); + memcpy(buf, rtas_data_buf, RTAS_DATA_BUF_SIZE); + + spin_unlock(&rtas_data_buf_lock); + return rc; +} + +static int delete_dt_node(u32 phandle) +{ + struct device_node *dn; + + dn = of_find_node_by_phandle(phandle); + if (!dn) + return -ENOENT; + + dlpar_detach_node(dn); + return 0; +} + +static int update_dt_property(struct device_node *dn, struct property **prop, + const char *name, u32 vd, char *value) +{ + struct property *new_prop = *prop; + struct property *old_prop; + int more = 0; + + /* A negative 'vd' value indicates that only part of the new property + * value is contained in the buffer and we need to call + * ibm,update-properties again to get the rest of the value. + * + * A negative value is also the two's compliment of the actual value. + */ + if (vd & 0x80000000) { + vd = ~vd + 1; + more = 1; + } + + if (new_prop) { + /* partial property fixup */ + char *new_data = kzalloc(new_prop->length + vd, GFP_KERNEL); + if (!new_data) + return -ENOMEM; + + memcpy(new_data, new_prop->value, new_prop->length); + memcpy(new_data + new_prop->length, value, vd); + + kfree(new_prop->value); + new_prop->value = new_data; + new_prop->length += vd; + } else { + new_prop = kzalloc(sizeof(*new_prop), GFP_KERNEL); + if (!new_prop) + return -ENOMEM; + + new_prop->name = kstrdup(name, GFP_KERNEL); + if (!new_prop->name) { + kfree(new_prop); + return -ENOMEM; + } + + new_prop->length = vd; + new_prop->value = kzalloc(new_prop->length, GFP_KERNEL); + if (!new_prop->value) { + kfree(new_prop->name); + kfree(new_prop); + return -ENOMEM; + } + + memcpy(new_prop->value, value, vd); + *prop = new_prop; + } + + if (!more) { + old_prop = of_find_property(dn, new_prop->name, NULL); + if (old_prop) + prom_update_property(dn, new_prop, old_prop); + else + prom_add_property(dn, new_prop); + + new_prop = NULL; + } + + return 0; +} + +static int update_dt_node(u32 phandle) +{ + struct update_props_workarea *upwa; + struct device_node *dn; + struct property *prop = NULL; + int i, rc; + char *prop_data; + char *rtas_buf; + int update_properties_token; + + update_properties_token = rtas_token("ibm,update-properties"); + if (update_properties_token == RTAS_UNKNOWN_SERVICE) + return -EINVAL; + + rtas_buf = kzalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL); + if (!rtas_buf) + return -ENOMEM; + + dn = of_find_node_by_phandle(phandle); + if (!dn) { + kfree(rtas_buf); + return -ENOENT; + } + + upwa = (struct update_props_workarea *)&rtas_buf[0]; + upwa->phandle = phandle; + + do { + rc = mobility_rtas_call(update_properties_token, rtas_buf); + if (rc < 0) + break; + + prop_data = rtas_buf + sizeof(*upwa); + + for (i = 0; i < upwa->nprops; i++) { + char *prop_name; + u32 vd; + + prop_name = prop_data + 1; + prop_data += strlen(prop_name) + 1; + vd = *prop_data++; + + switch (vd) { + case 0x00000000: + /* name only property, nothing to do */ + break; + + case 0x80000000: + prop = of_find_property(dn, prop_name, NULL); + prom_remove_property(dn, prop); + prop = NULL; + break; + + default: + rc = update_dt_property(dn, &prop, prop_name, + vd, prop_data); + if (rc) { + printk(KERN_ERR "Could not update %s" + " property\n", prop_name); + } + + prop_data += vd; + } + } + } while (rc == 1); + + of_node_put(dn); + kfree(rtas_buf); + return 0; +} + +static int add_dt_node(u32 parent_phandle, u32 drc_index) +{ + struct device_node *dn; + struct device_node *parent_dn; + int rc; + + dn = dlpar_configure_connector(drc_index); + if (!dn) + return -ENOENT; + + parent_dn = of_find_node_by_phandle(parent_phandle); + if (!parent_dn) { + dlpar_free_cc_nodes(dn); + return -ENOENT; + } + + dn->parent = parent_dn; + rc = dlpar_attach_node(dn); + if (rc) + dlpar_free_cc_nodes(dn); + + of_node_put(parent_dn); + return rc; +} + +static int pseries_devicetree_update(void) +{ + char *rtas_buf; + u32 *data; + int update_nodes_token; + int rc; + + update_nodes_token = rtas_token("ibm,update-nodes"); + if (update_nodes_token == RTAS_UNKNOWN_SERVICE) + return -EINVAL; + + rtas_buf = kzalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL); + if (!rtas_buf) + return -ENOMEM; + + do { + rc = mobility_rtas_call(update_nodes_token, rtas_buf); + if (rc && rc != 1) + break; + + data = (u32 *)rtas_buf + 4; + while (*data & NODE_ACTION_MASK) { + int i; + u32 action = *data & NODE_ACTION_MASK; + int node_count = *data & NODE_COUNT_MASK; + + data++; + + for (i = 0; i < node_count; i++) { + u32 phandle = *data++; + u32 drc_index; + + switch (action) { + case DELETE_DT_NODE: + delete_dt_node(phandle); + break; + case UPDATE_DT_NODE: + update_dt_node(phandle); + break; + case ADD_DT_NODE: + drc_index = *data++; + add_dt_node(phandle, drc_index); + break; + } + } + } + } while (rc == 1); + + kfree(rtas_buf); + return rc; +} + +void post_mobility_fixup(void) +{ + int rc; + int activate_fw_token; + + rc = pseries_devicetree_update(); + if (rc) { + printk(KERN_ERR "Initial post-mobility device tree update " + "failed: %d\n", rc); + return; + } + + activate_fw_token = rtas_token("ibm,activate-firmware"); + if (activate_fw_token == RTAS_UNKNOWN_SERVICE) { + printk(KERN_ERR "Could not make post-mobility " + "activate-fw call.\n"); + return; + } + + rc = rtas_call(activate_fw_token, 0, 1, NULL); + if (!rc) { + rc = pseries_devicetree_update(); + if (rc) + printk(KERN_ERR "Secondary post-mobility device tree " + "update failed: %d\n", rc); + } else { + printk(KERN_ERR "Post-mobility activate-fw failed: %d\n", rc); + return; + } + + return; +} + +static ssize_t migrate_store(struct class *class, struct class_attribute *attr, + const char *buf, size_t count) +{ + struct rtas_args args; + u64 streamid; + int rc; + + rc = strict_strtoull(buf, 0, &streamid); + if (rc) + return rc; + + memset(&args, 0, sizeof(args)); + args.token = rtas_token("ibm,suspend-me"); + args.nargs = 2; + args.nret = 1; + + args.args[0] = streamid >> 32 ; + args.args[1] = streamid & 0xffffffff; + args.rets = &args.args[args.nargs]; + + do { + args.rets[0] = 0; + rc = rtas_ibm_suspend_me(&args); + if (!rc && args.rets[0] == RTAS_NOT_SUSPENDABLE) + ssleep(1); + } while (!rc && args.rets[0] == RTAS_NOT_SUSPENDABLE); + + if (rc) + return rc; + else if (args.rets[0]) + return args.rets[0]; + + post_mobility_fixup(); + return count; +} + +static CLASS_ATTR(migration, S_IWUSR, NULL, migrate_store); + +static int __init mobility_sysfs_init(void) +{ + int rc; + + mobility_kobj = kobject_create_and_add("mobility", kernel_kobj); + if (!mobility_kobj) + return -ENOMEM; + + rc = sysfs_create_file(mobility_kobj, &class_attr_migration.attr); + + return rc; +} +device_initcall(mobility_sysfs_init); diff --git a/arch/powerpc/platforms/pseries/pseries.h b/arch/powerpc/platforms/pseries/pseries.h index 40c93ca..e9f6d28 100644 --- a/arch/powerpc/platforms/pseries/pseries.h +++ b/arch/powerpc/platforms/pseries/pseries.h @@ -17,6 +17,8 @@ struct device_node; extern void request_event_sources_irqs(struct device_node *np, irq_handler_t handler, const char *name); +#include <linux/of.h> + extern void __init fw_feature_init(const char *hypertas, unsigned long len); struct pt_regs; @@ -47,4 +49,11 @@ extern unsigned long rtas_poweron_auto; extern void find_udbg_vterm(void); +/* Dynamic logical Partitioning/Mobility */ +extern void dlpar_free_cc_nodes(struct device_node *); +extern void dlpar_free_cc_property(struct property *); +extern struct device_node *dlpar_configure_connector(u32); +extern int dlpar_attach_node(struct device_node *); +extern int dlpar_detach_node(struct device_node *); + #endif /* _PSERIES_PSERIES_H */ diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c index a6d19e3..d345bfd 100644 --- a/arch/powerpc/platforms/pseries/setup.c +++ b/arch/powerpc/platforms/pseries/setup.c @@ -273,6 +273,58 @@ static struct notifier_block pci_dn_reconfig_nb = { .notifier_call = pci_dn_reconfig_notifier, }; +#ifdef CONFIG_VIRT_CPU_ACCOUNTING +/* + * Allocate space for the dispatch trace log for all possible cpus + * and register the buffers with the hypervisor. This is used for + * computing time stolen by the hypervisor. + */ +static int alloc_dispatch_logs(void) +{ + int cpu, ret; + struct paca_struct *pp; + struct dtl_entry *dtl; + + if (!firmware_has_feature(FW_FEATURE_SPLPAR)) + return 0; + + for_each_possible_cpu(cpu) { + pp = &paca[cpu]; + dtl = kmalloc_node(DISPATCH_LOG_BYTES, GFP_KERNEL, + cpu_to_node(cpu)); + if (!dtl) { + pr_warn("Failed to allocate dispatch trace log for cpu %d\n", + cpu); + pr_warn("Stolen time statistics will be unreliable\n"); + break; + } + + pp->dtl_ridx = 0; + pp->dispatch_log = dtl; + pp->dispatch_log_end = dtl + N_DISPATCH_LOG; + pp->dtl_curr = dtl; + } + + /* Register the DTL for the current (boot) cpu */ + dtl = get_paca()->dispatch_log; + get_paca()->dtl_ridx = 0; + get_paca()->dtl_curr = dtl; + get_paca()->lppaca_ptr->dtl_idx = 0; + + /* hypervisor reads buffer length from this field */ + dtl->enqueue_to_dispatch_time = DISPATCH_LOG_BYTES; + ret = register_dtl(hard_smp_processor_id(), __pa(dtl)); + if (ret) + pr_warn("DTL registration failed for boot cpu %d (%d)\n", + smp_processor_id(), ret); + get_paca()->lppaca_ptr->dtl_enable_mask = 2; + + return 0; +} + +early_initcall(alloc_dispatch_logs); +#endif /* CONFIG_VIRT_CPU_ACCOUNTING */ + static void __init pSeries_setup_arch(void) { /* Discover PIC type and setup ppc_md accordingly */ diff --git a/arch/powerpc/platforms/pseries/xics.c b/arch/powerpc/platforms/pseries/xics.c index 67e2c4b..7b96e5a 100644 --- a/arch/powerpc/platforms/pseries/xics.c +++ b/arch/powerpc/platforms/pseries/xics.c @@ -178,7 +178,7 @@ static int get_irq_server(unsigned int virq, const struct cpumask *cpumask, if (!distribute_irqs) return default_server; - if (!cpumask_equal(cpumask, cpu_all_mask)) { + if (!cpumask_subset(cpu_possible_mask, cpumask)) { int server = cpumask_first_and(cpu_online_mask, cpumask); if (server < nr_cpu_ids) |