diff options
author | Ingo Molnar <mingo@elte.hu> | 2008-10-03 19:28:46 +0200 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-10-03 19:28:46 +0200 |
commit | f68ec0c24755e5cdb779be6240925f2175311d84 (patch) | |
tree | a7b7128e61a8456385d82bd1c7ca5f14eecbf2ca /arch/powerpc/platforms | |
parent | 98920dc3d1113b883cbc73e3293446d3525c6042 (diff) | |
parent | 94aca1dac6f6d21f4b07e4864baf7768cabcc6e7 (diff) |
Merge commit 'v2.6.27-rc8' into x86/setup
Diffstat (limited to 'arch/powerpc/platforms')
79 files changed, 2631 insertions, 571 deletions
diff --git a/arch/powerpc/platforms/44x/warp-nand.c b/arch/powerpc/platforms/44x/warp-nand.c index e55746b824b..89ecd76127d 100644 --- a/arch/powerpc/platforms/44x/warp-nand.c +++ b/arch/powerpc/platforms/44x/warp-nand.c @@ -24,7 +24,7 @@ static struct resource warp_ndfc = { .start = WARP_NAND_FLASH_REG_ADDR, - .end = WARP_NAND_FLASH_REG_ADDR + WARP_NAND_FLASH_REG_SIZE, + .end = WARP_NAND_FLASH_REG_ADDR + WARP_NAND_FLASH_REG_SIZE - 1, .flags = IORESOURCE_MEM, }; diff --git a/arch/powerpc/platforms/44x/warp.c b/arch/powerpc/platforms/44x/warp.c index 9565995cba7..960edf89be5 100644 --- a/arch/powerpc/platforms/44x/warp.c +++ b/arch/powerpc/platforms/44x/warp.c @@ -30,18 +30,6 @@ static __initdata struct of_device_id warp_of_bus[] = { {}, }; -static __initdata struct i2c_board_info warp_i2c_info[] = { - { I2C_BOARD_INFO("ad7414", 0x4a) } -}; - -static int __init warp_arch_init(void) -{ - /* This should go away once support is moved to the dts. */ - i2c_register_board_info(0, warp_i2c_info, ARRAY_SIZE(warp_i2c_info)); - return 0; -} -machine_arch_initcall(warp, warp_arch_init); - static int __init warp_device_probe(void) { of_platform_bus_probe(NULL, warp_of_bus, NULL); @@ -223,7 +211,7 @@ static void pika_setup_critical_temp(struct i2c_client *client) /* These registers are in 1 degree increments. */ i2c_smbus_write_byte_data(client, 2, 65); /* Thigh */ - i2c_smbus_write_byte_data(client, 3, 55); /* Tlow */ + i2c_smbus_write_byte_data(client, 3, 0); /* Tlow */ np = of_find_compatible_node(NULL, NULL, "adi,ad7414"); if (np == NULL) { @@ -289,8 +277,15 @@ found_it: printk(KERN_INFO "PIKA DTM thread running.\n"); while (!kthread_should_stop()) { - u16 temp = swab16(i2c_smbus_read_word_data(client, 0)); - out_be32(fpga + 0x20, temp); + int val; + + val = i2c_smbus_read_word_data(client, 0); + if (val < 0) + dev_dbg(&client->dev, "DTM read temp failed.\n"); + else { + s16 temp = swab16(val); + out_be32(fpga + 0x20, temp); + } pika_dtm_check_fan(fpga); diff --git a/arch/powerpc/platforms/52xx/Kconfig b/arch/powerpc/platforms/52xx/Kconfig index acd2fc8cf49..696a5ee4962 100644 --- a/arch/powerpc/platforms/52xx/Kconfig +++ b/arch/powerpc/platforms/52xx/Kconfig @@ -1,8 +1,8 @@ config PPC_MPC52xx bool "52xx-based boards" depends on PPC_MULTIPLATFORM && PPC32 - select FSL_SOC select PPC_CLOCK + select PPC_PCI_CHOICE config PPC_MPC5200_SIMPLE bool "Generic support for simple MPC5200 based boards" @@ -47,6 +47,7 @@ config PPC_MPC5200_BUGFIX config PPC_MPC5200_GPIO bool "MPC5200 GPIO support" depends on PPC_MPC52xx - select HAVE_GPIO_LIB + select ARCH_REQUIRE_GPIOLIB + select GENERIC_GPIO help Enable gpiolib support for mpc5200 based boards diff --git a/arch/powerpc/platforms/52xx/Makefile b/arch/powerpc/platforms/52xx/Makefile index daf0e1568d6..b8a52062738 100644 --- a/arch/powerpc/platforms/52xx/Makefile +++ b/arch/powerpc/platforms/52xx/Makefile @@ -1,10 +1,8 @@ # # Makefile for 52xx based boards # -ifeq ($(CONFIG_PPC_MERGE),y) obj-y += mpc52xx_pic.o mpc52xx_common.o obj-$(CONFIG_PCI) += mpc52xx_pci.o -endif obj-$(CONFIG_PPC_MPC5200_SIMPLE) += mpc5200_simple.o obj-$(CONFIG_PPC_EFIKA) += efika.o @@ -15,4 +13,4 @@ ifeq ($(CONFIG_PPC_LITE5200),y) obj-$(CONFIG_PM) += lite5200_sleep.o lite5200_pm.o endif -obj-$(CONFIG_PPC_MPC5200_GPIO) += mpc52xx_gpio.o
\ No newline at end of file +obj-$(CONFIG_PPC_MPC5200_GPIO) += mpc52xx_gpio.o diff --git a/arch/powerpc/platforms/82xx/Kconfig b/arch/powerpc/platforms/82xx/Kconfig index 1c8034bfa79..75eb1ede549 100644 --- a/arch/powerpc/platforms/82xx/Kconfig +++ b/arch/powerpc/platforms/82xx/Kconfig @@ -30,6 +30,7 @@ config EP8248E select 8272 select 8260 select FSL_SOC + select PHYLIB select MDIO_BITBANG help This enables support for the Embedded Planet EP8248E board. diff --git a/arch/powerpc/platforms/82xx/ep8248e.c b/arch/powerpc/platforms/82xx/ep8248e.c index 373e993a5ed..d5770fdf7f0 100644 --- a/arch/powerpc/platforms/82xx/ep8248e.c +++ b/arch/powerpc/platforms/82xx/ep8248e.c @@ -59,7 +59,6 @@ static void __init ep8248e_pic_init(void) of_node_put(np); } -#ifdef CONFIG_FS_ENET_MDIO_FCC static void ep8248e_set_mdc(struct mdiobb_ctrl *ctrl, int level) { if (level) @@ -165,7 +164,6 @@ static struct of_platform_driver ep8248e_mdio_driver = { .probe = ep8248e_mdio_probe, .remove = ep8248e_mdio_remove, }; -#endif struct cpm_pin { int port, pin, flags; @@ -298,9 +296,7 @@ static __initdata struct of_device_id of_bus_ids[] = { static int __init declare_of_platform_devices(void) { of_platform_bus_probe(NULL, of_bus_ids, NULL); -#ifdef CONFIG_FS_ENET_MDIO_FCC of_register_platform_driver(&ep8248e_mdio_driver); -#endif return 0; } diff --git a/arch/powerpc/platforms/83xx/Kconfig b/arch/powerpc/platforms/83xx/Kconfig index 27d9bf86de0..6159c5d4e5f 100644 --- a/arch/powerpc/platforms/83xx/Kconfig +++ b/arch/powerpc/platforms/83xx/Kconfig @@ -2,7 +2,8 @@ menuconfig PPC_83xx bool "83xx-based boards" depends on 6xx && PPC_MULTIPLATFORM select PPC_UDBG_16550 - select PPC_INDIRECT_PCI + select PPC_PCI_CHOICE + select FSL_PCI if PCI select FSL_SOC select IPIC diff --git a/arch/powerpc/platforms/83xx/Makefile b/arch/powerpc/platforms/83xx/Makefile index f331fd7dd83..ba5028e2989 100644 --- a/arch/powerpc/platforms/83xx/Makefile +++ b/arch/powerpc/platforms/83xx/Makefile @@ -2,7 +2,7 @@ # Makefile for the PowerPC 83xx linux kernel. # obj-y := misc.o usb.o -obj-$(CONFIG_PCI) += pci.o +obj-$(CONFIG_SUSPEND) += suspend.o suspend-asm.o obj-$(CONFIG_MPC831x_RDB) += mpc831x_rdb.o obj-$(CONFIG_MPC832x_RDB) += mpc832x_rdb.o obj-$(CONFIG_MPC834x_MDS) += mpc834x_mds.o diff --git a/arch/powerpc/platforms/83xx/mpc831x_rdb.c b/arch/powerpc/platforms/83xx/mpc831x_rdb.c index c4db5172b27..a428f8d1ac8 100644 --- a/arch/powerpc/platforms/83xx/mpc831x_rdb.c +++ b/arch/powerpc/platforms/83xx/mpc831x_rdb.c @@ -19,6 +19,7 @@ #include <asm/time.h> #include <asm/ipic.h> #include <asm/udbg.h> +#include <sysdev/fsl_pci.h> #include "mpc83xx.h" diff --git a/arch/powerpc/platforms/83xx/mpc832x_mds.c b/arch/powerpc/platforms/83xx/mpc832x_mds.c index 6dbc6eabcb0..ec43477caa6 100644 --- a/arch/powerpc/platforms/83xx/mpc832x_mds.c +++ b/arch/powerpc/platforms/83xx/mpc832x_mds.c @@ -36,6 +36,7 @@ #include <asm/prom.h> #include <asm/udbg.h> #include <sysdev/fsl_soc.h> +#include <sysdev/fsl_pci.h> #include <asm/qe.h> #include <asm/qe_ic.h> @@ -104,6 +105,7 @@ static void __init mpc832x_sys_setup_arch(void) static struct of_device_id mpc832x_ids[] = { { .type = "soc", }, { .compatible = "soc", }, + { .compatible = "simple-bus", }, { .type = "qe", }, { .compatible = "fsl,qe", }, {}, diff --git a/arch/powerpc/platforms/83xx/mpc832x_rdb.c b/arch/powerpc/platforms/83xx/mpc832x_rdb.c index e7f706b624f..0300268ce5b 100644 --- a/arch/powerpc/platforms/83xx/mpc832x_rdb.c +++ b/arch/powerpc/platforms/83xx/mpc832x_rdb.c @@ -27,6 +27,7 @@ #include <asm/qe.h> #include <asm/qe_ic.h> #include <sysdev/fsl_soc.h> +#include <sysdev/fsl_pci.h> #include "mpc83xx.h" @@ -114,6 +115,7 @@ static void __init mpc832x_rdb_setup_arch(void) static struct of_device_id mpc832x_ids[] = { { .type = "soc", }, { .compatible = "soc", }, + { .compatible = "simple-bus", }, { .type = "qe", }, { .compatible = "fsl,qe", }, {}, diff --git a/arch/powerpc/platforms/83xx/mpc834x_itx.c b/arch/powerpc/platforms/83xx/mpc834x_itx.c index 50e8f632061..76092d37c7d 100644 --- a/arch/powerpc/platforms/83xx/mpc834x_itx.c +++ b/arch/powerpc/platforms/83xx/mpc834x_itx.c @@ -35,11 +35,13 @@ #include <asm/prom.h> #include <asm/udbg.h> #include <sysdev/fsl_soc.h> +#include <sysdev/fsl_pci.h> #include "mpc83xx.h" static struct of_device_id __initdata mpc834x_itx_ids[] = { { .compatible = "fsl,pq2pro-localbus", }, + { .compatible = "simple-bus", }, {}, }; diff --git a/arch/powerpc/platforms/83xx/mpc834x_mds.c b/arch/powerpc/platforms/83xx/mpc834x_mds.c index 2b8a0a3f855..fc3f2ed1f3e 100644 --- a/arch/powerpc/platforms/83xx/mpc834x_mds.c +++ b/arch/powerpc/platforms/83xx/mpc834x_mds.c @@ -35,6 +35,7 @@ #include <asm/prom.h> #include <asm/udbg.h> #include <sysdev/fsl_soc.h> +#include <sysdev/fsl_pci.h> #include "mpc83xx.h" @@ -110,6 +111,7 @@ static void __init mpc834x_mds_init_IRQ(void) static struct of_device_id mpc834x_ids[] = { { .type = "soc", }, { .compatible = "soc", }, + { .compatible = "simple-bus", }, {}, }; diff --git a/arch/powerpc/platforms/83xx/mpc836x_mds.c b/arch/powerpc/platforms/83xx/mpc836x_mds.c index c2e5de60c05..9d46e5bdd10 100644 --- a/arch/powerpc/platforms/83xx/mpc836x_mds.c +++ b/arch/powerpc/platforms/83xx/mpc836x_mds.c @@ -42,6 +42,7 @@ #include <asm/prom.h> #include <asm/udbg.h> #include <sysdev/fsl_soc.h> +#include <sysdev/fsl_pci.h> #include <asm/qe.h> #include <asm/qe_ic.h> @@ -135,6 +136,7 @@ static void __init mpc836x_mds_setup_arch(void) static struct of_device_id mpc836x_ids[] = { { .type = "soc", }, { .compatible = "soc", }, + { .compatible = "simple-bus", }, { .type = "qe", }, { .compatible = "fsl,qe", }, {}, diff --git a/arch/powerpc/platforms/83xx/mpc836x_rdk.c b/arch/powerpc/platforms/83xx/mpc836x_rdk.c index c10dec4bf17..a5273bb28e1 100644 --- a/arch/powerpc/platforms/83xx/mpc836x_rdk.c +++ b/arch/powerpc/platforms/83xx/mpc836x_rdk.c @@ -23,6 +23,7 @@ #include <asm/qe.h> #include <asm/qe_ic.h> #include <sysdev/fsl_soc.h> +#include <sysdev/fsl_pci.h> #include "mpc83xx.h" diff --git a/arch/powerpc/platforms/83xx/mpc837x_mds.c b/arch/powerpc/platforms/83xx/mpc837x_mds.c index 64d17b0d645..be62de23bea 100644 --- a/arch/powerpc/platforms/83xx/mpc837x_mds.c +++ b/arch/powerpc/platforms/83xx/mpc837x_mds.c @@ -19,6 +19,7 @@ #include <asm/ipic.h> #include <asm/udbg.h> #include <asm/prom.h> +#include <sysdev/fsl_pci.h> #include "mpc83xx.h" diff --git a/arch/powerpc/platforms/83xx/mpc837x_rdb.c b/arch/powerpc/platforms/83xx/mpc837x_rdb.c index c00356bdb1d..da030afa2e2 100644 --- a/arch/powerpc/platforms/83xx/mpc837x_rdb.c +++ b/arch/powerpc/platforms/83xx/mpc837x_rdb.c @@ -17,6 +17,7 @@ #include <asm/time.h> #include <asm/ipic.h> #include <asm/udbg.h> +#include <sysdev/fsl_pci.h> #include "mpc83xx.h" diff --git a/arch/powerpc/platforms/83xx/mpc83xx.h b/arch/powerpc/platforms/83xx/mpc83xx.h index 88a3b5cabb1..2a7cbabb410 100644 --- a/arch/powerpc/platforms/83xx/mpc83xx.h +++ b/arch/powerpc/platforms/83xx/mpc83xx.h @@ -26,6 +26,8 @@ #define MPC834X_SICRL_USB1 0x20000000 #define MPC831X_SICRL_USB_MASK 0x00000c00 #define MPC831X_SICRL_USB_ULPI 0x00000800 +#define MPC8315_SICRL_USB_MASK 0x000000fc +#define MPC8315_SICRL_USB_ULPI 0x00000054 #define MPC837X_SICRL_USB_MASK 0xf0000000 #define MPC837X_SICRL_USB_ULPI 0x50000000 @@ -34,6 +36,8 @@ #define MPC834X_SICRH_USB_UTMI 0x00020000 #define MPC831X_SICRH_USB_MASK 0x000000e0 #define MPC831X_SICRH_USB_ULPI 0x000000a0 +#define MPC8315_SICRH_USB_MASK 0x0000ff00 +#define MPC8315_SICRH_USB_ULPI 0x00000000 /* USB Control Register */ #define FSL_USB2_CONTROL_OFFS 0x500 @@ -55,7 +59,6 @@ * mpc83xx_* files. Mostly for use by mpc83xx_setup */ -extern int mpc83xx_add_bridge(struct device_node *dev); extern void mpc83xx_restart(char *cmd); extern long mpc83xx_time_init(void); extern int mpc834x_usb_cfg(void); diff --git a/arch/powerpc/platforms/83xx/pci.c b/arch/powerpc/platforms/83xx/pci.c deleted file mode 100644 index 14f1080c6c9..00000000000 --- a/arch/powerpc/platforms/83xx/pci.c +++ /dev/null @@ -1,91 +0,0 @@ -/* - * FSL SoC setup code - * - * Maintained by Kumar Gala (see MAINTAINERS for contact information) - * - * 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/init.h> -#include <linux/errno.h> -#include <linux/pci.h> -#include <linux/delay.h> -#include <linux/irq.h> -#include <linux/module.h> - -#include <asm/system.h> -#include <asm/atomic.h> -#include <asm/io.h> -#include <asm/pci-bridge.h> -#include <asm/prom.h> -#include <sysdev/fsl_soc.h> - -#undef DEBUG - -#ifdef DEBUG -#define DBG(x...) printk(x) -#else -#define DBG(x...) -#endif - -int __init mpc83xx_add_bridge(struct device_node *dev) -{ - int len; - struct pci_controller *hose; - struct resource rsrc; - const int *bus_range; - int primary = 1, has_address = 0; - phys_addr_t immr = get_immrbase(); - - DBG("Adding PCI host bridge %s\n", dev->full_name); - - /* Fetch host bridge registers address */ - has_address = (of_address_to_resource(dev, 0, &rsrc) == 0); - - /* Get bus range if any */ - bus_range = of_get_property(dev, "bus-range", &len); - if (bus_range == NULL || len < 2 * sizeof(int)) { - printk(KERN_WARNING "Can't get bus-range for %s, assume" - " bus 0\n", dev->full_name); - } - - ppc_pci_flags |= PPC_PCI_REASSIGN_ALL_BUS; - hose = pcibios_alloc_controller(dev); - if (!hose) - return -ENOMEM; - - hose->first_busno = bus_range ? bus_range[0] : 0; - hose->last_busno = bus_range ? bus_range[1] : 0xff; - - /* MPC83xx supports up to two host controllers one at 0x8500 from immrbar - * the other at 0x8600, we consider the 0x8500 the primary controller - */ - /* PCI 1 */ - if ((rsrc.start & 0xfffff) == 0x8500) { - setup_indirect_pci(hose, immr + 0x8300, immr + 0x8304, 0); - } - /* PCI 2 */ - if ((rsrc.start & 0xfffff) == 0x8600) { - setup_indirect_pci(hose, immr + 0x8380, immr + 0x8384, 0); - primary = 0; - } - - printk(KERN_INFO "Found MPC83xx PCI host bridge at 0x%016llx. " - "Firmware bus number: %d->%d\n", - (unsigned long long)rsrc.start, hose->first_busno, - hose->last_busno); - - DBG(" ->Hose at 0x%p, cfg_addr=0x%p,cfg_data=0x%p\n", - hose, hose->cfg_addr, hose->cfg_data); - - /* Interpret the "ranges" property */ - /* This also maps the I/O region and sets isa_io/mem_base */ - pci_process_bridge_OF_ranges(hose, dev, primary); - - return 0; -} diff --git a/arch/powerpc/platforms/83xx/sbc834x.c b/arch/powerpc/platforms/83xx/sbc834x.c index cf382474a83..156c4e21800 100644 --- a/arch/powerpc/platforms/83xx/sbc834x.c +++ b/arch/powerpc/platforms/83xx/sbc834x.c @@ -37,6 +37,7 @@ #include <asm/prom.h> #include <asm/udbg.h> #include <sysdev/fsl_soc.h> +#include <sysdev/fsl_pci.h> #include "mpc83xx.h" @@ -82,6 +83,7 @@ static void __init sbc834x_init_IRQ(void) static struct __initdata of_device_id sbc834x_ids[] = { { .type = "soc", }, { .compatible = "soc", }, + { .compatible = "simple-bus", }, {}, }; diff --git a/arch/powerpc/platforms/83xx/suspend-asm.S b/arch/powerpc/platforms/83xx/suspend-asm.S new file mode 100644 index 00000000000..1930543c98d --- /dev/null +++ b/arch/powerpc/platforms/83xx/suspend-asm.S @@ -0,0 +1,533 @@ +/* + * Enter and leave deep sleep state on MPC83xx + * + * Copyright (c) 2006-2008 Freescale Semiconductor, Inc. + * Author: Scott Wood <scottwood@freescale.com> + * + * 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 <asm/page.h> +#include <asm/ppc_asm.h> +#include <asm/reg.h> +#include <asm/asm-offsets.h> + +#define SS_MEMSAVE 0x00 /* First 8 bytes of RAM */ +#define SS_HID 0x08 /* 3 HIDs */ +#define SS_IABR 0x14 /* 2 IABRs */ +#define SS_IBCR 0x1c +#define SS_DABR 0x20 /* 2 DABRs */ +#define SS_DBCR 0x28 +#define SS_SP 0x2c +#define SS_SR 0x30 /* 16 segment registers */ +#define SS_R2 0x70 +#define SS_MSR 0x74 +#define SS_SDR1 0x78 +#define SS_LR 0x7c +#define SS_SPRG 0x80 /* 4 SPRGs */ +#define SS_DBAT 0x90 /* 8 DBATs */ +#define SS_IBAT 0xd0 /* 8 IBATs */ +#define SS_TB 0x110 +#define SS_CR 0x118 +#define SS_GPREG 0x11c /* r12-r31 */ +#define STATE_SAVE_SIZE 0x16c + + .section .data + .align 5 + +mpc83xx_sleep_save_area: + .space STATE_SAVE_SIZE +immrbase: + .long 0 + + .section .text + .align 5 + + /* r3 = physical address of IMMR */ +_GLOBAL(mpc83xx_enter_deep_sleep) + lis r4, immrbase@ha + stw r3, immrbase@l(r4) + + /* The first 2 words of memory are used to communicate with the + * bootloader, to tell it how to resume. + * + * The first word is the magic number 0xf5153ae5, and the second + * is the pointer to mpc83xx_deep_resume. + * + * The original content of these two words is saved in SS_MEMSAVE. + */ + + lis r3, mpc83xx_sleep_save_area@h + ori r3, r3, mpc83xx_sleep_save_area@l + + lis r4, KERNELBASE@h + lwz r5, 0(r4) + lwz r6, 4(r4) + + stw r5, SS_MEMSAVE+0(r3) + stw r6, SS_MEMSAVE+4(r3) + + mfspr r5, SPRN_HID0 + mfspr r6, SPRN_HID1 + mfspr r7, SPRN_HID2 + + stw r5, SS_HID+0(r3) + stw r6, SS_HID+4(r3) + stw r7, SS_HID+8(r3) + + mfspr r4, SPRN_IABR + mfspr r5, SPRN_IABR2 + mfspr r6, SPRN_IBCR + mfspr r7, SPRN_DABR + mfspr r8, SPRN_DABR2 + mfspr r9, SPRN_DBCR + + stw r4, SS_IABR+0(r3) + stw r5, SS_IABR+4(r3) + stw r6, SS_IBCR(r3) + stw r7, SS_DABR+0(r3) + stw r8, SS_DABR+4(r3) + stw r9, SS_DBCR(r3) + + mfspr r4, SPRN_SPRG0 + mfspr r5, SPRN_SPRG1 + mfspr r6, SPRN_SPRG2 + mfspr r7, SPRN_SPRG3 + mfsdr1 r8 + + stw r4, SS_SPRG+0(r3) + stw r5, SS_SPRG+4(r3) + stw r6, SS_SPRG+8(r3) + stw r7, SS_SPRG+12(r3) + stw r8, SS_SDR1(r3) + + mfspr r4, SPRN_DBAT0U + mfspr r5, SPRN_DBAT0L + mfspr r6, SPRN_DBAT1U + mfspr r7, SPRN_DBAT1L + + stw r4, SS_DBAT+0x00(r3) + stw r5, SS_DBAT+0x04(r3) + stw r6, SS_DBAT+0x08(r3) + stw r7, SS_DBAT+0x0c(r3) + + mfspr r4, SPRN_DBAT2U + mfspr r5, SPRN_DBAT2L + mfspr r6, SPRN_DBAT3U + mfspr r7, SPRN_DBAT3L + + stw r4, SS_DBAT+0x10(r3) + stw r5, SS_DBAT+0x14(r3) + stw r6, SS_DBAT+0x18(r3) + stw r7, SS_DBAT+0x1c(r3) + + mfspr r4, SPRN_DBAT4U + mfspr r5, SPRN_DBAT4L + mfspr r6, SPRN_DBAT5U + mfspr r7, SPRN_DBAT5L + + stw r4, SS_DBAT+0x20(r3) + stw r5, SS_DBAT+0x24(r3) + stw r6, SS_DBAT+0x28(r3) + stw r7, SS_DBAT+0x2c(r3) + + mfspr r4, SPRN_DBAT6U + mfspr r5, SPRN_DBAT6L + mfspr r6, SPRN_DBAT7U + mfspr r7, SPRN_DBAT7L + + stw r4, SS_DBAT+0x30(r3) + stw r5, SS_DBAT+0x34(r3) + stw r6, SS_DBAT+0x38(r3) + stw r7, SS_DBAT+0x3c(r3) + + mfspr r4, SPRN_IBAT0U + mfspr r5, SPRN_IBAT0L + mfspr r6, SPRN_IBAT1U + mfspr r7, SPRN_IBAT1L + + stw r4, SS_IBAT+0x00(r3) + stw r5, SS_IBAT+0x04(r3) + stw r6, SS_IBAT+0x08(r3) + stw r7, SS_IBAT+0x0c(r3) + + mfspr r4, SPRN_IBAT2U + mfspr r5, SPRN_IBAT2L + mfspr r6, SPRN_IBAT3U + mfspr r7, SPRN_IBAT3L + + stw r4, SS_IBAT+0x10(r3) + stw r5, SS_IBAT+0x14(r3) + stw r6, SS_IBAT+0x18(r3) + stw r7, SS_IBAT+0x1c(r3) + + mfspr r4, SPRN_IBAT4U + mfspr r5, SPRN_IBAT4L + mfspr r6, SPRN_IBAT5U + mfspr r7, SPRN_IBAT5L + + stw r4, SS_IBAT+0x20(r3) + stw r5, SS_IBAT+0x24(r3) + stw r6, SS_IBAT+0x28(r3) + stw r7, SS_IBAT+0x2c(r3) + + mfspr r4, SPRN_IBAT6U + mfspr r5, SPRN_IBAT6L + mfspr r6, SPRN_IBAT7U + mfspr r7, SPRN_IBAT7L + + stw r4, SS_IBAT+0x30(r3) + stw r5, SS_IBAT+0x34(r3) + stw r6, SS_IBAT+0x38(r3) + stw r7, SS_IBAT+0x3c(r3) + + mfmsr r4 + mflr r5 + mfcr r6 + + stw r4, SS_MSR(r3) + stw r5, SS_LR(r3) + stw r6, SS_CR(r3) + stw r1, SS_SP(r3) + stw r2, SS_R2(r3) + +1: mftbu r4 + mftb r5 + mftbu r6 + cmpw r4, r6 + bne 1b + + stw r4, SS_TB+0(r3) + stw r5, SS_TB+4(r3) + + stmw r12, SS_GPREG(r3) + + li r4, 0 + addi r6, r3, SS_SR-4 +1: mfsrin r5, r4 + stwu r5, 4(r6) + addis r4, r4, 0x1000 + cmpwi r4, 0 + bne 1b + + /* Disable machine checks and critical exceptions */ + mfmsr r4 + rlwinm r4, r4, 0, ~MSR_CE + rlwinm r4, r4, 0, ~MSR_ME + mtmsr r4 + isync + +#define TMP_VIRT_IMMR 0xf0000000 +#define DEFAULT_IMMR_VALUE 0xff400000 +#define IMMRBAR_BASE 0x0000 + + lis r4, immrbase@ha + lwz r4, immrbase@l(r4) + + /* Use DBAT0 to address the current IMMR space */ + + ori r4, r4, 0x002a + mtspr SPRN_DBAT0L, r4 + lis r8, TMP_VIRT_IMMR@h + ori r4, r8, 0x001e /* 1 MByte accessable from Kernel Space only */ + mtspr SPRN_DBAT0U, r4 + isync + + /* Use DBAT1 to address the original IMMR space */ + + lis r4, DEFAULT_IMMR_VALUE@h + ori r4, r4, 0x002a + mtspr SPRN_DBAT1L, r4 + lis r9, (TMP_VIRT_IMMR + 0x01000000)@h + ori r4, r9, 0x001e /* 1 MByte accessable from Kernel Space only */ + mtspr SPRN_DBAT1U, r4 + isync + + /* Use DBAT2 to address the beginning of RAM. This isn't done + * using the normal virtual mapping, because with page debugging + * enabled it will be read-only. + */ + + li r4, 0x0002 + mtspr SPRN_DBAT2L, r4 + lis r4, KERNELBASE@h + ori r4, r4, 0x001e /* 1 MByte accessable from Kernel Space only */ + mtspr SPRN_DBAT2U, r4 + isync + + /* Flush the cache with our BAT, as there will be TLB misses + * otherwise if page debugging is enabled, and these misses + * will disturb the PLRU algorithm. + */ + + bl __flush_disable_L1 + + /* Keep the i-cache enabled, so the hack below for low-boot + * flash will work. + */ + mfspr r3, SPRN_HID0 + ori r3, r3, HID0_ICE + mtspr SPRN_HID0, r3 + isync + + lis r6, 0xf515 + ori r6, r6, 0x3ae5 + + lis r7, mpc83xx_deep_resume@h + ori r7, r7, mpc83xx_deep_resume@l + tophys(r7, r7) + + lis r5, KERNELBASE@h + stw r6, 0(r5) + stw r7, 4(r5) + + /* Reset BARs */ + + li r4, 0 + stw r4, 0x0024(r8) + stw r4, 0x002c(r8) + stw r4, 0x0034(r8) + stw r4, 0x003c(r8) + stw r4, 0x0064(r8) + stw r4, 0x006c(r8) + + /* Rev 1 of the 8313 has problems with wakeup events that are + * pending during the transition to deep sleep state (such as if + * the PCI host sets the state to D3 and then D0 in rapid + * succession). This check shrinks the race window somewhat. + * + * See erratum PCI23, though the problem is not limited + * to PCI. + */ + + lwz r3, 0x0b04(r8) + andi. r3, r3, 1 + bne- mpc83xx_deep_resume + + /* Move IMMR back to the default location, following the + * procedure specified in the MPC8313 manual. + */ + lwz r4, IMMRBAR_BASE(r8) + isync + lis r4, DEFAULT_IMMR_VALUE@h + stw r4, IMMRBAR_BASE(r8) + lis r4, KERNELBASE@h + lwz r4, 0(r4) + isync + lwz r4, IMMRBAR_BASE(r9) + mr r8, r9 + isync + + /* Check the Reset Configuration Word to see whether flash needs + * to be mapped at a low address or a high address. + */ + + lwz r4, 0x0904(r8) + andis. r4, r4, 0x0400 + li r4, 0 + beq boot_low + lis r4, 0xff80 +boot_low: + stw r4, 0x0020(r8) + lis r7, 0x8000 + ori r7, r7, 0x0016 + + mfspr r5, SPRN_HID0 + rlwinm r5, r5, 0, ~(HID0_DOZE | HID0_NAP) + oris r5, r5, HID0_SLEEP@h + mtspr SPRN_HID0, r5 + isync + + mfmsr r5 + oris r5, r5, MSR_POW@h + + /* Enable the flash mapping at the appropriate address. This + * mapping will override the RAM mapping if booting low, so there's + * no need to disable the latter. This must be done inside the same + * cache line as setting MSR_POW, so that no instruction fetches + * from RAM happen after the flash mapping is turned on. + */ + + .align 5 + stw r7, 0x0024(r8) + sync + isync + mtmsr r5 + isync +1: b 1b + +mpc83xx_deep_resume: + lis r4, 1f@h + ori r4, r4, 1f@l + tophys(r4, r4) + mtsrr0 r4 + + mfmsr r4 + rlwinm r4, r4, 0, ~(MSR_IR | MSR_DR) + mtsrr1 r4 + + rfi + +1: tlbia + bl __inval_enable_L1 + + lis r3, mpc83xx_sleep_save_area@h + ori r3, r3, mpc83xx_sleep_save_area@l + tophys(r3, r3) + + lwz r5, SS_MEMSAVE+0(r3) + lwz r6, SS_MEMSAVE+4(r3) + + stw r5, 0(0) + stw r6, 4(0) + + lwz r5, SS_HID+0(r3) + lwz r6, SS_HID+4(r3) + lwz r7, SS_HID+8(r3) + + mtspr SPRN_HID0, r5 + mtspr SPRN_HID1, r6 + mtspr SPRN_HID2, r7 + + lwz r4, SS_IABR+0(r3) + lwz r5, SS_IABR+4(r3) + lwz r6, SS_IBCR(r3) + lwz r7, SS_DABR+0(r3) + lwz r8, SS_DABR+4(r3) + lwz r9, SS_DBCR(r3) + + mtspr SPRN_IABR, r4 + mtspr SPRN_IABR2, r5 + mtspr SPRN_IBCR, r6 + mtspr SPRN_DABR, r7 + mtspr SPRN_DABR2, r8 + mtspr SPRN_DBCR, r9 + + li r4, 0 + addi r6, r3, SS_SR-4 +1: lwzu r5, 4(r6) + mtsrin r5, r4 + addis r4, r4, 0x1000 + cmpwi r4, 0 + bne 1b + + lwz r4, SS_DBAT+0x00(r3) + lwz r5, SS_DBAT+0x04(r3) + lwz r6, SS_DBAT+0x08(r3) + lwz r7, SS_DBAT+0x0c(r3) + + mtspr SPRN_DBAT0U, r4 + mtspr SPRN_DBAT0L, r5 + mtspr SPRN_DBAT1U, r6 + mtspr SPRN_DBAT1L, r7 + + lwz r4, SS_DBAT+0x10(r3) + lwz r5, SS_DBAT+0x14(r3) + lwz r6, SS_DBAT+0x18(r3) + lwz r7, SS_DBAT+0x1c(r3) + + mtspr SPRN_DBAT2U, r4 + mtspr SPRN_DBAT2L, r5 + mtspr SPRN_DBAT3U, r6 + mtspr SPRN_DBAT3L, r7 + + lwz r4, SS_DBAT+0x20(r3) + lwz r5, SS_DBAT+0x24(r3) + lwz r6, SS_DBAT+0x28(r3) + lwz r7, SS_DBAT+0x2c(r3) + + mtspr SPRN_DBAT4U, r4 + mtspr SPRN_DBAT4L, r5 + mtspr SPRN_DBAT5U, r6 + mtspr SPRN_DBAT5L, r7 + + lwz r4, SS_DBAT+0x30(r3) + lwz r5, SS_DBAT+0x34(r3) + lwz r6, SS_DBAT+0x38(r3) + lwz r7, SS_DBAT+0x3c(r3) + + mtspr SPRN_DBAT6U, r4 + mtspr SPRN_DBAT6L, r5 + mtspr SPRN_DBAT7U, r6 + mtspr SPRN_DBAT7L, r7 + + lwz r4, SS_IBAT+0x00(r3) + lwz r5, SS_IBAT+0x04(r3) + lwz r6, SS_IBAT+0x08(r3) + lwz r7, SS_IBAT+0x0c(r3) + + mtspr SPRN_IBAT0U, r4 + mtspr SPRN_IBAT0L, r5 + mtspr SPRN_IBAT1U, r6 + mtspr SPRN_IBAT1L, r7 + + lwz r4, SS_IBAT+0x10(r3) + lwz r5, SS_IBAT+0x14(r3) + lwz r6, SS_IBAT+0x18(r3) + lwz r7, SS_IBAT+0x1c(r3) + + mtspr SPRN_IBAT2U, r4 + mtspr SPRN_IBAT2L, r5 + mtspr SPRN_IBAT3U, r6 + mtspr SPRN_IBAT3L, r7 + + lwz r4, SS_IBAT+0x20(r3) + lwz r5, SS_IBAT+0x24(r3) + lwz r6, SS_IBAT+0x28(r3) + lwz r7, SS_IBAT+0x2c(r3) + + mtspr SPRN_IBAT4U, r4 + mtspr SPRN_IBAT4L, r5 + mtspr SPRN_IBAT5U, r6 + mtspr SPRN_IBAT5L, r7 + + lwz r4, SS_IBAT+0x30(r3) + lwz r5, SS_IBAT+0x34(r3) + lwz r6, SS_IBAT+0x38(r3) + lwz r7, SS_IBAT+0x3c(r3) + + mtspr SPRN_IBAT6U, r4 + mtspr SPRN_IBAT6L, r5 + mtspr SPRN_IBAT7U, r6 + mtspr SPRN_IBAT7L, r7 + + lwz r4, SS_SPRG+0(r3) + lwz r5, SS_SPRG+4(r3) + lwz r6, SS_SPRG+8(r3) + lwz r7, SS_SPRG+12(r3) + lwz r8, SS_SDR1(r3) + + mtspr SPRN_SPRG0, r4 + mtspr SPRN_SPRG1, r5 + mtspr SPRN_SPRG2, r6 + mtspr SPRN_SPRG3, r7 + mtsdr1 r8 + + lwz r4, SS_MSR(r3) + lwz r5, SS_LR(r3) + lwz r6, SS_CR(r3) + lwz r1, SS_SP(r3) + lwz r2, SS_R2(r3) + + mtsrr1 r4 + mtsrr0 r5 + mtcr r6 + + li r4, 0 + mtspr SPRN_TBWL, r4 + + lwz r4, SS_TB+0(r3) + lwz r5, SS_TB+4(r3) + + mtspr SPRN_TBWU, r4 + mtspr SPRN_TBWL, r5 + + lmw r12, SS_GPREG(r3) + + /* Kick decrementer */ + li r0, 1 + mtdec r0 + + rfi diff --git a/arch/powerpc/platforms/83xx/suspend.c b/arch/powerpc/platforms/83xx/suspend.c new file mode 100644 index 00000000000..08e65fc8b98 --- /dev/null +++ b/arch/powerpc/platforms/83xx/suspend.c @@ -0,0 +1,388 @@ +/* + * MPC83xx suspend support + * + * Author: Scott Wood <scottwood@freescale.com> + * + * Copyright (c) 2006-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 <linux/init.h> +#include <linux/pm.h> +#include <linux/types.h> +#include <linux/ioport.h> +#include <linux/interrupt.h> +#include <linux/wait.h> +#include <linux/kthread.h> +#include <linux/freezer.h> +#include <linux/suspend.h> +#include <linux/fsl_devices.h> +#include <linux/of_platform.h> + +#include <asm/reg.h> +#include <asm/io.h> +#include <asm/time.h> +#include <asm/mpc6xx.h> + +#include <sysdev/fsl_soc.h> + +#define PMCCR1_NEXT_STATE 0x0C /* Next state for power management */ +#define PMCCR1_NEXT_STATE_SHIFT 2 +#define PMCCR1_CURR_STATE 0x03 /* Current state for power management*/ +#define IMMR_RCW_OFFSET 0x900 +#define RCW_PCI_HOST 0x80000000 + +void mpc83xx_enter_deep_sleep(phys_addr_t immrbase); + +struct mpc83xx_pmc { + u32 config; +#define PMCCR_DLPEN 2 /* DDR SDRAM low power enable */ +#define PMCCR_SLPEN 1 /* System low power enable */ + + u32 event; + u32 mask; +/* All but PMCI are deep-sleep only */ +#define PMCER_GPIO 0x100 +#define PMCER_PCI 0x080 +#define PMCER_USB 0x040 +#define PMCER_ETSEC1 0x020 +#define PMCER_ETSEC2 0x010 +#define PMCER_TIMER 0x008 +#define PMCER_INT1 0x004 +#define PMCER_INT2 0x002 +#define PMCER_PMCI 0x001 +#define PMCER_ALL 0x1FF + + /* deep-sleep only */ + u32 config1; +#define PMCCR1_USE_STATE 0x80000000 +#define PMCCR1_PME_EN 0x00000080 +#define PMCCR1_ASSERT_PME 0x00000040 +#define PMCCR1_POWER_OFF 0x00000020 + + /* deep-sleep only */ + u32 config2; +}; + +struct mpc83xx_rcw { + u32 rcwlr; + u32 rcwhr; +}; + +struct mpc83xx_clock { + u32 spmr; + u32 occr; + u32 sccr; +}; + +struct pmc_type { + int has_deep_sleep; +}; + +static struct of_device *pmc_dev; +static int has_deep_sleep, deep_sleeping; +static int pmc_irq; +static struct mpc83xx_pmc __iomem *pmc_regs; +static struct mpc83xx_clock __iomem *clock_regs; +static int is_pci_agent, wake_from_pci; +static phys_addr_t immrbase; +static int pci_pm_state; +static DECLARE_WAIT_QUEUE_HEAD(agent_wq); + +int fsl_deep_sleep(void) +{ + return deep_sleeping; +} + +static int mpc83xx_change_state(void) +{ + u32 curr_state; + u32 reg_cfg1 = in_be32(&pmc_regs->config1); + + if (is_pci_agent) { + pci_pm_state = (reg_cfg1 & PMCCR1_NEXT_STATE) >> + PMCCR1_NEXT_STATE_SHIFT; + curr_state = reg_cfg1 & PMCCR1_CURR_STATE; + + if (curr_state != pci_pm_state) { + reg_cfg1 &= ~PMCCR1_CURR_STATE; + reg_cfg1 |= pci_pm_state; + out_be32(&pmc_regs->config1, reg_cfg1); + + wake_up(&agent_wq); + return 1; + } + } + + return 0; +} + +static irqreturn_t pmc_irq_handler(int irq, void *dev_id) +{ + u32 event = in_be32(&pmc_regs->event); + int ret = IRQ_NONE; + + if (mpc83xx_change_state()) + ret = IRQ_HANDLED; + + if (event) { + out_be32(&pmc_regs->event, event); + ret = IRQ_HANDLED; + } + + return ret; +} + +static int mpc83xx_suspend_enter(suspend_state_t state) +{ + int ret = -EAGAIN; + + /* Don't go to sleep if there's a race where pci_pm_state changes + * between the agent thread checking it and the PM code disabling + * interrupts. + */ + if (wake_from_pci) { + if (pci_pm_state != (deep_sleeping ? 3 : 2)) + goto out; + + out_be32(&pmc_regs->config1, + in_be32(&pmc_regs->config1) | PMCCR1_PME_EN); + } + + /* Put the system into low-power mode and the RAM + * into self-refresh mode once the core goes to + * sleep. + */ + + out_be32(&pmc_regs->config, PMCCR_SLPEN | PMCCR_DLPEN); + + /* If it has deep sleep (i.e. it's an 831x or compatible), + * disable power to the core upon entering sleep mode. This will + * require going through the boot firmware upon a wakeup event. + */ + + if (deep_sleeping) { + out_be32(&pmc_regs->mask, PMCER_ALL); + + out_be32(&pmc_regs->config1, + in_be32(&pmc_regs->config1) | PMCCR1_POWER_OFF); + + enable_kernel_fp(); + + mpc83xx_enter_deep_sleep(immrbase); + + out_be32(&pmc_regs->config1, + in_be32(&pmc_regs->config1) & ~PMCCR1_POWER_OFF); + + out_be32(&pmc_regs->mask, PMCER_PMCI); + } else { + out_be32(&pmc_regs->mask, PMCER_PMCI); + + mpc6xx_enter_standby(); + } + + ret = 0; + +out: + out_be32(&pmc_regs->config1, + in_be32(&pmc_regs->config1) & ~PMCCR1_PME_EN); + + return ret; +} + +static void mpc83xx_suspend_finish(void) +{ + deep_sleeping = 0; +} + +static int mpc83xx_suspend_valid(suspend_state_t state) +{ + return state == PM_SUSPEND_STANDBY || state == PM_SUSPEND_MEM; +} + +static int mpc83xx_suspend_begin(suspend_state_t state) +{ + switch (state) { + case PM_SUSPEND_STANDBY: + deep_sleeping = 0; + return 0; + + case PM_SUSPEND_MEM: + if (has_deep_sleep) + deep_sleeping = 1; + + return 0; + + default: + return -EINVAL; + } +} + +static int agent_thread_fn(void *data) +{ + while (1) { + wait_event_interruptible(agent_wq, pci_pm_state >= 2); + try_to_freeze(); + + if (signal_pending(current) || pci_pm_state < 2) + continue; + + /* With a preemptible kernel (or SMP), this could race with + * a userspace-driven suspend request. It's probably best + * to avoid mixing the two with such a configuration (or + * else fix it by adding a mutex to state_store that we can + * synchronize with). + */ + + wake_from_pci = 1; + + pm_suspend(pci_pm_state == 3 ? PM_SUSPEND_MEM : + PM_SUSPEND_STANDBY); + + wake_from_pci = 0; + } + + return 0; +} + +static void mpc83xx_set_agent(void) +{ + out_be32(&pmc_regs->config1, PMCCR1_USE_STATE); + out_be32(&pmc_regs->mask, PMCER_PMCI); + + kthread_run(agent_thread_fn, NULL, "PCI power mgt"); +} + +static int mpc83xx_is_pci_agent(void) +{ + struct mpc83xx_rcw __iomem *rcw_regs; + int ret; + + rcw_regs = ioremap(get_immrbase() + IMMR_RCW_OFFSET, + sizeof(struct mpc83xx_rcw)); + + if (!rcw_regs) + return -ENOMEM; + + ret = !(in_be32(&rcw_regs->rcwhr) & RCW_PCI_HOST); + + iounmap(rcw_regs); + return ret; +} + +static struct platform_suspend_ops mpc83xx_suspend_ops = { + .valid = mpc83xx_suspend_valid, + .begin = mpc83xx_suspend_begin, + .enter = mpc83xx_suspend_enter, + .finish = mpc83xx_suspend_finish, +}; + +static int pmc_probe(struct of_device *ofdev, + const struct of_device_id *match) +{ + struct device_node *np = ofdev->node; + struct resource res; + struct pmc_type *type = match->data; + int ret = 0; + + if (!of_device_is_available(np)) + return -ENODEV; + + has_deep_sleep = type->has_deep_sleep; + immrbase = get_immrbase(); + pmc_dev = ofdev; + + is_pci_agent = mpc83xx_is_pci_agent(); + if (is_pci_agent < 0) + return is_pci_agent; + + ret = of_address_to_resource(np, 0, &res); + if (ret) + return -ENODEV; + + pmc_irq = irq_of_parse_and_map(np, 0); + if (pmc_irq != NO_IRQ) { + ret = request_irq(pmc_irq, pmc_irq_handler, IRQF_SHARED, + "pmc", ofdev); + + if (ret) + return -EBUSY; + } + + pmc_regs = ioremap(res.start, sizeof(struct mpc83xx_pmc)); + + if (!pmc_regs) { + ret = -ENOMEM; + goto out; + } + + ret = of_address_to_resource(np, 1, &res); + if (ret) { + ret = -ENODEV; + goto out_pmc; + } + + clock_regs = ioremap(res.start, sizeof(struct mpc83xx_pmc)); + + if (!clock_regs) { + ret = -ENOMEM; + goto out_pmc; + } + + if (is_pci_agent) + mpc83xx_set_agent(); + + suspend_set_ops(&mpc83xx_suspend_ops); + return 0; + +out_pmc: + iounmap(pmc_regs); +out: + if (pmc_irq != NO_IRQ) + free_irq(pmc_irq, ofdev); + + return ret; +} + +static int pmc_remove(struct of_device *ofdev) +{ + return -EPERM; +}; + +static struct pmc_type pmc_types[] = { + { + .has_deep_sleep = 1, + }, + { + .has_deep_sleep = 0, + } +}; + +static struct of_device_id pmc_match[] = { + { + .compatible = "fsl,mpc8313-pmc", + .data = &pmc_types[0], + }, + { + .compatible = "fsl,mpc8349-pmc", + .data = &pmc_types[1], + }, + {} +}; + +static struct of_platform_driver pmc_driver = { + .name = "mpc83xx-pmc", + .match_table = pmc_match, + .probe = pmc_probe, + .remove = pmc_remove +}; + +static int pmc_init(void) +{ + return of_register_platform_driver(&pmc_driver); +} + +module_init(pmc_init); diff --git a/arch/powerpc/platforms/83xx/usb.c b/arch/powerpc/platforms/83xx/usb.c index 64bcf0a33c7..cc99c280aad 100644 --- a/arch/powerpc/platforms/83xx/usb.c +++ b/arch/powerpc/platforms/83xx/usb.c @@ -137,15 +137,21 @@ int mpc831x_usb_cfg(void) /* Configure pin mux for ULPI. There is no pin mux for UTMI */ if (prop && !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); + if (of_device_is_compatible(immr_node, "fsl,mpc8315-immr")) { + clrsetbits_be32(immap + MPC83XX_SICRL_OFFS, + MPC8315_SICRL_USB_MASK, + MPC8315_SICRL_USB_ULPI); + clrsetbits_be32(immap + MPC83XX_SICRH_OFFS, + MPC8315_SICRH_USB_MASK, + MPC8315_SICRH_USB_ULPI); + } else { + clrsetbits_be32(immap + MPC83XX_SICRL_OFFS, + MPC831X_SICRL_USB_MASK, + MPC831X_SICRL_USB_ULPI); + clrsetbits_be32(immap + MPC83XX_SICRH_OFFS, + MPC831X_SICRH_USB_MASK, + MPC831X_SICRH_USB_ULPI); + } } iounmap(immap); diff --git a/arch/powerpc/platforms/85xx/Kconfig b/arch/powerpc/platforms/85xx/Kconfig index cebea5cadbc..291675b0097 100644 --- a/arch/powerpc/platforms/85xx/Kconfig +++ b/arch/powerpc/platforms/85xx/Kconfig @@ -2,8 +2,8 @@ menuconfig MPC85xx bool "Machine Type" depends on PPC_85xx select PPC_UDBG_16550 - select PPC_INDIRECT_PCI if PCI select MPIC + select PPC_PCI_CHOICE select FSL_PCI if PCI select SERIAL_8250_SHARE_IRQ if SERIAL_8250 default y @@ -86,7 +86,6 @@ config TQM8548 help This option enables support for the TQ Components TQM8548 board. select DEFAULT_UIMAGE - select PPC_CPM_NEW_BINDING select TQM85xx config TQM8555 diff --git a/arch/powerpc/platforms/85xx/ksi8560.c b/arch/powerpc/platforms/85xx/ksi8560.c index 2145adeb220..8a3b117b6ce 100644 --- a/arch/powerpc/platforms/85xx/ksi8560.c +++ b/arch/powerpc/platforms/85xx/ksi8560.c @@ -222,6 +222,7 @@ static void ksi8560_show_cpuinfo(struct seq_file *m) static struct of_device_id __initdata of_bus_ids[] = { { .type = "soc", }, + { .type = "simple-bus", }, { .name = "cpm", }, { .name = "localbus", }, {}, diff --git a/arch/powerpc/platforms/85xx/mpc8536_ds.c b/arch/powerpc/platforms/85xx/mpc8536_ds.c index 6b846aa1ced..1bf5aefdfeb 100644 --- a/arch/powerpc/platforms/85xx/mpc8536_ds.c +++ b/arch/powerpc/platforms/85xx/mpc8536_ds.c @@ -91,6 +91,7 @@ static void __init mpc8536_ds_setup_arch(void) static struct of_device_id __initdata mpc8536_ds_ids[] = { { .type = "soc", }, { .compatible = "soc", }, + { .compatible = "simple-bus", }, {}, }; diff --git a/arch/powerpc/platforms/85xx/mpc85xx_ads.c b/arch/powerpc/platforms/85xx/mpc85xx_ads.c index ba498d6f2d0..d17807a6b89 100644 --- a/arch/powerpc/platforms/85xx/mpc85xx_ads.c +++ b/arch/powerpc/platforms/85xx/mpc85xx_ads.c @@ -230,6 +230,7 @@ static struct of_device_id __initdata of_bus_ids[] = { { .type = "soc", }, { .name = "cpm", }, { .name = "localbus", }, + { .compatible = "simple-bus", }, {}, }; diff --git a/arch/powerpc/platforms/85xx/mpc85xx_ds.c b/arch/powerpc/platforms/85xx/mpc85xx_ds.c index 25f41cd2d33..483b65cbaba 100644 --- a/arch/powerpc/platforms/85xx/mpc85xx_ds.c +++ b/arch/powerpc/platforms/85xx/mpc85xx_ds.c @@ -115,7 +115,6 @@ void __init mpc85xx_ds_pic_init(void) #ifdef CONFIG_PCI static int primary_phb_addr; -extern int uses_fsl_uli_m1575; extern int uli_exclude_device(struct pci_controller *hose, u_char bus, u_char devfn); @@ -161,7 +160,6 @@ static void __init mpc85xx_ds_setup_arch(void) } } - uses_fsl_uli_m1575 = 1; ppc_md.pci_exclude_device = mpc85xx_exclude_device; #endif @@ -188,6 +186,7 @@ static int __init mpc8544_ds_probe(void) static struct of_device_id __initdata mpc85xxds_ids[] = { { .type = "soc", }, { .compatible = "soc", }, + { .compatible = "simple-bus", }, {}, }; diff --git a/arch/powerpc/platforms/85xx/mpc85xx_mds.c b/arch/powerpc/platforms/85xx/mpc85xx_mds.c index 43a459f63e3..2494c515591 100644 --- a/arch/powerpc/platforms/85xx/mpc85xx_mds.c +++ b/arch/powerpc/platforms/85xx/mpc85xx_mds.c @@ -260,6 +260,7 @@ machine_arch_initcall(mpc85xx_mds, board_fixups); static struct of_device_id mpc85xx_ids[] = { { .type = "soc", }, { .compatible = "soc", }, + { .compatible = "simple-bus", }, { .type = "qe", }, { .compatible = "fsl,qe", }, {}, diff --git a/arch/powerpc/platforms/85xx/sbc8560.c b/arch/powerpc/platforms/85xx/sbc8560.c index 2c580cd24e4..6509ade7166 100644 --- a/arch/powerpc/platforms/85xx/sbc8560.c +++ b/arch/powerpc/platforms/85xx/sbc8560.c @@ -217,6 +217,7 @@ static struct of_device_id __initdata of_bus_ids[] = { { .type = "soc", }, { .name = "cpm", }, { .name = "localbus", }, + { .compatible = "simple-bus", }, {}, }; diff --git a/arch/powerpc/platforms/86xx/Kconfig b/arch/powerpc/platforms/86xx/Kconfig index 80a81e02bb5..9355a526943 100644 --- a/arch/powerpc/platforms/86xx/Kconfig +++ b/arch/powerpc/platforms/86xx/Kconfig @@ -27,6 +27,7 @@ config SBC8641D config MPC8610_HPCD bool "Freescale MPC8610 HPCD" select DEFAULT_UIMAGE + select FSL_ULI1575 help This option enables support for the MPC8610 HPCD board. @@ -34,6 +35,7 @@ endif config MPC8641 bool + select PPC_PCI_CHOICE select FSL_PCI if PCI select PPC_UDBG_16550 select MPIC @@ -41,6 +43,7 @@ config MPC8641 config MPC8610 bool + select PPC_PCI_CHOICE select FSL_PCI if PCI select PPC_UDBG_16550 select MPIC diff --git a/arch/powerpc/platforms/86xx/mpc8610_hpcd.c b/arch/powerpc/platforms/86xx/mpc8610_hpcd.c index 30725302884..5eedb710896 100644 --- a/arch/powerpc/platforms/86xx/mpc8610_hpcd.c +++ b/arch/powerpc/platforms/86xx/mpc8610_hpcd.c @@ -58,93 +58,6 @@ static int __init mpc8610_declare_of_platform_devices(void) } machine_device_initcall(mpc86xx_hpcd, mpc8610_declare_of_platform_devices); -#ifdef CONFIG_PCI -static void __devinit quirk_uli1575(struct pci_dev *dev) -{ - u32 temp32; - - /* Disable INTx */ - pci_read_config_dword(dev, 0x48, &temp32); - pci_write_config_dword(dev, 0x48, (temp32 | 1<<26)); - - /* Enable sideband interrupt */ - pci_read_config_dword(dev, 0x90, &temp32); - pci_write_config_dword(dev, 0x90, (temp32 | 1<<22)); -} - -static void __devinit quirk_uli5288(struct pci_dev *dev) -{ - unsigned char c; - unsigned short temp; - - /* Interrupt Disable, Needed when SATA disabled */ - pci_read_config_word(dev, PCI_COMMAND, &temp); - temp |= 1<<10; - pci_write_config_word(dev, PCI_COMMAND, temp); - - pci_read_config_byte(dev, 0x83, &c); - c |= 0x80; - pci_write_config_byte(dev, 0x83, c); - - pci_write_config_byte(dev, PCI_CLASS_PROG, 0x01); - pci_write_config_byte(dev, PCI_CLASS_DEVICE, 0x06); - - pci_read_config_byte(dev, 0x83, &c); - c &= 0x7f; - pci_write_config_byte(dev, 0x83, c); -} - -/* - * Since 8259PIC was disabled on the board, the IDE device can not - * use the legacy IRQ, we need to let the IDE device work under - * native mode and use the interrupt line like other PCI devices. - * IRQ14 is a sideband interrupt from IDE device to CPU and we use this - * as the interrupt for IDE device. - */ -static void __devinit quirk_uli5229(struct pci_dev *dev) -{ - unsigned char c; - - pci_read_config_byte(dev, 0x4b, &c); - c |= 0x10; - pci_write_config_byte(dev, 0x4b, c); -} - -/* - * SATA interrupt pin bug fix - * There's a chip bug for 5288, The interrupt pin should be 2, - * not the read only value 1, So it use INTB#, not INTA# which - * actually used by the IDE device 5229. - * As of this bug, during the PCI initialization, 5288 read the - * irq of IDE device from the device tree, this function fix this - * bug by re-assigning a correct irq to 5288. - * - */ -static void __devinit final_uli5288(struct pci_dev *dev) -{ - struct pci_controller *hose = pci_bus_to_host(dev->bus); - struct device_node *hosenode = hose ? hose->dn : NULL; - struct of_irq oirq; - int virq, pin = 2; - u32 laddr[3]; - - if (!hosenode) - return; - - laddr[0] = (hose->first_busno << 16) | (PCI_DEVFN(31, 0) << 8); - laddr[1] = laddr[2] = 0; - of_irq_map_raw(hosenode, &pin, 1, laddr, &oirq); - virq = irq_create_of_mapping(oirq.controller, oirq.specifier, - oirq.size); - dev->irq = virq; -} - -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AL, 0x1575, quirk_uli1575); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AL, 0x5288, quirk_uli5288); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AL, 0x5229, quirk_uli5229); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AL, 0x5288, final_uli5288); -#endif /* CONFIG_PCI */ - #if defined(CONFIG_FB_FSL_DIU) || defined(CONFIG_FB_FSL_DIU_MODULE) static u32 get_busfreq(void) diff --git a/arch/powerpc/platforms/86xx/mpc86xx_hpcn.c b/arch/powerpc/platforms/86xx/mpc86xx_hpcn.c index 7916599c912..f712d9c0991 100644 --- a/arch/powerpc/platforms/86xx/mpc86xx_hpcn.c +++ b/arch/powerpc/platforms/86xx/mpc86xx_hpcn.c @@ -45,7 +45,6 @@ #endif #ifdef CONFIG_PCI -extern int uses_fsl_uli_m1575; extern int uli_exclude_device(struct pci_controller *hose, u_char bus, u_char devfn); @@ -87,7 +86,6 @@ mpc86xx_hpcn_setup_arch(void) fsl_add_bridge(np, 0); } - uses_fsl_uli_m1575 = 1; ppc_md.pci_exclude_device = mpc86xx_exclude_device; #endif diff --git a/arch/powerpc/platforms/86xx/mpc86xx_smp.c b/arch/powerpc/platforms/86xx/mpc86xx_smp.c index 835f2dc24dc..014e26cda08 100644 --- a/arch/powerpc/platforms/86xx/mpc86xx_smp.c +++ b/arch/powerpc/platforms/86xx/mpc86xx_smp.c @@ -19,7 +19,7 @@ #include <asm/page.h> #include <asm/pgtable.h> #include <asm/pci-bridge.h> -#include <asm-powerpc/mpic.h> +#include <asm/mpic.h> #include <asm/mpc86xx.h> #include <asm/cacheflush.h> diff --git a/arch/powerpc/platforms/8xx/Kconfig b/arch/powerpc/platforms/8xx/Kconfig index 6fc849e51e4..71d7562e190 100644 --- a/arch/powerpc/platforms/8xx/Kconfig +++ b/arch/powerpc/platforms/8xx/Kconfig @@ -105,6 +105,16 @@ config 8xx_COPYBACK If in doubt, say Y here. +config 8xx_GPIO + bool "GPIO API Support" + select GENERIC_GPIO + select ARCH_REQUIRE_GPIOLIB + help + Saying Y here will cause the ports on an MPC8xx processor to be used + with the GPIO API. If you say N here, the kernel needs less memory. + + If in doubt, say Y here. + config 8xx_CPU6 bool "CPU6 Silicon Errata (860 Pre Rev. C)" help diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig index 690c1f46e69..4c900efa164 100644 --- a/arch/powerpc/platforms/Kconfig +++ b/arch/powerpc/platforms/Kconfig @@ -253,17 +253,15 @@ config CPM2 depends on MPC85xx || 8260 select CPM select PPC_LIB_RHEAP + select PPC_PCI_CHOICE + select ARCH_REQUIRE_GPIOLIB + select GENERIC_GPIO help The CPM2 (Communications Processor Module) is a coprocessor on embedded CPUs made by Freescale. Selecting this option means that you wish to build a kernel for a machine with a CPM2 coprocessor on it (826x, 827x, 8560). -config PPC_CPM_NEW_BINDING - bool - depends on CPM1 || CPM2 - default y - config AXON_RAM tristate "Axon DDR2 memory device driver" depends on PPC_IBM_CELL_BLADE @@ -285,6 +283,7 @@ config FSL_ULI1575 config CPM bool + select PPC_CLOCK config OF_RTC bool diff --git a/arch/powerpc/platforms/Kconfig.cputype b/arch/powerpc/platforms/Kconfig.cputype index 5bc4b611ff8..7f651273386 100644 --- a/arch/powerpc/platforms/Kconfig.cputype +++ b/arch/powerpc/platforms/Kconfig.cputype @@ -42,12 +42,14 @@ config 40x select PPC_DCR_NATIVE select PPC_UDBG_16550 select 4xx_SOC + select PPC_PCI_CHOICE config 44x bool "AMCC 44x" select PPC_DCR_NATIVE select PPC_UDBG_16550 select 4xx_SOC + select PPC_PCI_CHOICE config E200 bool "Freescale e200" @@ -84,9 +86,6 @@ config TUNE_CELL machines. When building a kernel that is supposed to run only on Cell, you should also select the POWER4_ONLY option. -config 6xx - bool - # this is temp to handle compat with arch=ppc config 8xx bool diff --git a/arch/powerpc/platforms/Makefile b/arch/powerpc/platforms/Makefile index 423a0234dc3..8079e0b4fd6 100644 --- a/arch/powerpc/platforms/Makefile +++ b/arch/powerpc/platforms/Makefile @@ -1,13 +1,7 @@ obj-$(CONFIG_FSL_ULI1575) += fsl_uli1575.o -ifeq ($(CONFIG_PPC_MERGE),y) obj-$(CONFIG_PPC_PMAC) += powermac/ -else -ifeq ($(CONFIG_PPC64),y) -obj-$(CONFIG_PPC_PMAC) += powermac/ -endif -endif obj-$(CONFIG_PPC_CHRP) += chrp/ obj-$(CONFIG_40x) += 40x/ obj-$(CONFIG_44x) += 44x/ diff --git a/arch/powerpc/platforms/cell/Kconfig b/arch/powerpc/platforms/cell/Kconfig index 3959fcfe731..c14d7d8d96c 100644 --- a/arch/powerpc/platforms/cell/Kconfig +++ b/arch/powerpc/platforms/cell/Kconfig @@ -83,6 +83,22 @@ config CBE_RAS depends on PPC_CELL_NATIVE default y +config PPC_IBM_CELL_RESETBUTTON + bool "IBM Cell Blade Pinhole reset button" + depends on CBE_RAS && PPC_IBM_CELL_BLADE + default y + help + Support Pinhole Resetbutton on IBM Cell blades. + This adds a method to trigger system reset via front panel pinhole button. + +config PPC_IBM_CELL_POWERBUTTON + tristate "IBM Cell Blade power button" + depends on PPC_IBM_CELL_BLADE && PPC_PMI && INPUT_EVDEV + default y + help + Support Powerbutton on IBM Cell blades. + This will enable the powerbutton as an input device. + config CBE_THERM tristate "CBE thermal support" default m @@ -107,6 +123,15 @@ config CBE_CPUFREQ_PMI processor will not only be able to run at lower speed, but also at lower core voltage. +config CBE_CPUFREQ_SPU_GOVERNOR + tristate "CBE frequency scaling based on SPU usage" + depends on SPU_FS && CPU_FREQ + default m + help + This governor checks for spu usage to adjust the cpu frequency. + If no spu is running on a given cpu, that cpu will be throttled to + the minimal possible frequency. + endmenu config OPROFILE_CELL diff --git a/arch/powerpc/platforms/cell/Makefile b/arch/powerpc/platforms/cell/Makefile index c2a7e4e5ddf..7fd830872c4 100644 --- a/arch/powerpc/platforms/cell/Makefile +++ b/arch/powerpc/platforms/cell/Makefile @@ -8,6 +8,9 @@ obj-$(CONFIG_CBE_THERM) += cbe_thermal.o obj-$(CONFIG_CBE_CPUFREQ_PMI) += cbe_cpufreq_pmi.o obj-$(CONFIG_CBE_CPUFREQ) += cbe-cpufreq.o cbe-cpufreq-y += cbe_cpufreq_pervasive.o cbe_cpufreq.o +obj-$(CONFIG_CBE_CPUFREQ_SPU_GOVERNOR) += cpufreq_spudemand.o + +obj-$(CONFIG_PPC_IBM_CELL_POWERBUTTON) += cbe_powerbutton.o ifeq ($(CONFIG_SMP),y) obj-$(CONFIG_PPC_CELL_NATIVE) += smp.o diff --git a/arch/powerpc/platforms/cell/cbe_cpufreq_pmi.c b/arch/powerpc/platforms/cell/cbe_cpufreq_pmi.c index 69288f65314..3233fe84d15 100644 --- a/arch/powerpc/platforms/cell/cbe_cpufreq_pmi.c +++ b/arch/powerpc/platforms/cell/cbe_cpufreq_pmi.c @@ -96,6 +96,12 @@ static int pmi_notifier(struct notifier_block *nb, struct cpufreq_frequency_table *cbe_freqs; u8 node; + /* Should this really be called for CPUFREQ_ADJUST, CPUFREQ_INCOMPATIBLE + * and CPUFREQ_NOTIFY policy events?) + */ + if (event == CPUFREQ_START) + return 0; + cbe_freqs = cpufreq_frequency_get_table(policy->cpu); node = cbe_cpu_to_node(policy->cpu); diff --git a/arch/powerpc/platforms/cell/cbe_powerbutton.c b/arch/powerpc/platforms/cell/cbe_powerbutton.c new file mode 100644 index 00000000000..dcddaa5fcb6 --- /dev/null +++ b/arch/powerpc/platforms/cell/cbe_powerbutton.c @@ -0,0 +1,117 @@ +/* + * driver for powerbutton on IBM cell blades + * + * (C) Copyright IBM Corp. 2005-2008 + * + * Author: Christian Krafft <krafft@de.ibm.com> + * + * 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, 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. 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. + */ + +#include <linux/input.h> +#include <linux/platform_device.h> +#include <asm/pmi.h> +#include <asm/prom.h> + +static struct input_dev *button_dev; +static struct platform_device *button_pdev; + +static void cbe_powerbutton_handle_pmi(pmi_message_t pmi_msg) +{ + BUG_ON(pmi_msg.type != PMI_TYPE_POWER_BUTTON); + + input_report_key(button_dev, KEY_POWER, 1); + input_sync(button_dev); + input_report_key(button_dev, KEY_POWER, 0); + input_sync(button_dev); +} + +static struct pmi_handler cbe_pmi_handler = { + .type = PMI_TYPE_POWER_BUTTON, + .handle_pmi_message = cbe_powerbutton_handle_pmi, +}; + +static int __init cbe_powerbutton_init(void) +{ + int ret = 0; + struct input_dev *dev; + + if (!machine_is_compatible("IBM,CBPLUS-1.0")) { + printk(KERN_ERR "%s: Not a cell blade.\n", __func__); + ret = -ENODEV; + goto out; + } + + dev = input_allocate_device(); + if (!dev) { + ret = -ENOMEM; + printk(KERN_ERR "%s: Not enough memory.\n", __func__); + goto out; + } + + set_bit(EV_KEY, dev->evbit); + set_bit(KEY_POWER, dev->keybit); + + dev->name = "Power Button"; + dev->id.bustype = BUS_HOST; + + /* this makes the button look like an acpi power button + * no clue whether anyone relies on that though */ + dev->id.product = 0x02; + dev->phys = "LNXPWRBN/button/input0"; + + button_pdev = platform_device_register_simple("power_button", 0, NULL, 0); + if (IS_ERR(button_pdev)) { + ret = PTR_ERR(button_pdev); + goto out_free_input; + } + + dev->dev.parent = &button_pdev->dev; + ret = input_register_device(dev); + if (ret) { + printk(KERN_ERR "%s: Failed to register device\n", __func__); + goto out_free_pdev; + } + + button_dev = dev; + + ret = pmi_register_handler(&cbe_pmi_handler); + if (ret) { + printk(KERN_ERR "%s: Failed to register with pmi.\n", __func__); + goto out_free_pdev; + } + + goto out; + +out_free_pdev: + platform_device_unregister(button_pdev); +out_free_input: + input_free_device(dev); +out: + return ret; +} + +static void __exit cbe_powerbutton_exit(void) +{ + pmi_unregister_handler(&cbe_pmi_handler); + platform_device_unregister(button_pdev); + input_free_device(button_dev); +} + +module_init(cbe_powerbutton_init); +module_exit(cbe_powerbutton_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Christian Krafft <krafft@de.ibm.com>"); diff --git a/arch/powerpc/platforms/cell/cbe_thermal.c b/arch/powerpc/platforms/cell/cbe_thermal.c index 4852bf312d8..4d4c8c16912 100644 --- a/arch/powerpc/platforms/cell/cbe_thermal.c +++ b/arch/powerpc/platforms/cell/cbe_thermal.c @@ -97,7 +97,8 @@ static u8 spu_read_register_value(struct sys_device *sysdev, union spe_reg __iom return value.spe[spu->spe_id]; } -static ssize_t spu_show_temp(struct sys_device *sysdev, char *buf) +static ssize_t spu_show_temp(struct sys_device *sysdev, struct sysdev_attribute *attr, + char *buf) { u8 value; struct cbe_pmd_regs __iomem *pmd_regs; @@ -146,32 +147,38 @@ static ssize_t store_throttle(struct cbe_pmd_regs __iomem *pmd_regs, const char return size; } -static ssize_t spu_show_throttle_end(struct sys_device *sysdev, char *buf) +static ssize_t spu_show_throttle_end(struct sys_device *sysdev, + struct sysdev_attribute *attr, char *buf) { return show_throttle(get_pmd_regs(sysdev), buf, 0); } -static ssize_t spu_show_throttle_begin(struct sys_device *sysdev, char *buf) +static ssize_t spu_show_throttle_begin(struct sys_device *sysdev, + struct sysdev_attribute *attr, char *buf) { return show_throttle(get_pmd_regs(sysdev), buf, 8); } -static ssize_t spu_show_throttle_full_stop(struct sys_device *sysdev, char *buf) +static ssize_t spu_show_throttle_full_stop(struct sys_device *sysdev, + struct sysdev_attribute *attr, char *buf) { return show_throttle(get_pmd_regs(sysdev), buf, 16); } -static ssize_t spu_store_throttle_end(struct sys_device *sysdev, const char *buf, size_t size) +static ssize_t spu_store_throttle_end(struct sys_device *sysdev, + struct sysdev_attribute *attr, const char *buf, size_t size) { return store_throttle(get_pmd_regs(sysdev), buf, size, 0); } -static ssize_t spu_store_throttle_begin(struct sys_device *sysdev, const char *buf, size_t size) +static ssize_t spu_store_throttle_begin(struct sys_device *sysdev, + struct sysdev_attribute *attr, const char *buf, size_t size) { return store_throttle(get_pmd_regs(sysdev), buf, size, 8); } -static ssize_t spu_store_throttle_full_stop(struct sys_device *sysdev, const char *buf, size_t size) +static ssize_t spu_store_throttle_full_stop(struct sys_device *sysdev, + struct sysdev_attribute *attr, const char *buf, size_t size) { return store_throttle(get_pmd_regs(sysdev), buf, size, 16); } @@ -192,43 +199,51 @@ static ssize_t ppe_show_temp(struct sys_device *sysdev, char *buf, int pos) /* shows the temperature of the DTS on the PPE, * located near the linear thermal sensor */ -static ssize_t ppe_show_temp0(struct sys_device *sysdev, char *buf) +static ssize_t ppe_show_temp0(struct sys_device *sysdev, + struct sysdev_attribute *attr, char *buf) { return ppe_show_temp(sysdev, buf, 32); } /* shows the temperature of the second DTS on the PPE */ -static ssize_t ppe_show_temp1(struct sys_device *sysdev, char *buf) +static ssize_t ppe_show_temp1(struct sys_device *sysdev, + struct sysdev_attribute *attr, char *buf) { return ppe_show_temp(sysdev, buf, 0); } -static ssize_t ppe_show_throttle_end(struct sys_device *sysdev, char *buf) +static ssize_t ppe_show_throttle_end(struct sys_device *sysdev, + struct sysdev_attribute *attr, char *buf) { return show_throttle(cbe_get_cpu_pmd_regs(sysdev->id), buf, 32); } -static ssize_t ppe_show_throttle_begin(struct sys_device *sysdev, char *buf) +static ssize_t ppe_show_throttle_begin(struct sys_device *sysdev, + struct sysdev_attribute *attr, char *buf) { return show_throttle(cbe_get_cpu_pmd_regs(sysdev->id), buf, 40); } -static ssize_t ppe_show_throttle_full_stop(struct sys_device *sysdev, char *buf) +static ssize_t ppe_show_throttle_full_stop(struct sys_device *sysdev, + struct sysdev_attribute *attr, char *buf) { return show_throttle(cbe_get_cpu_pmd_regs(sysdev->id), buf, 48); } -static ssize_t ppe_store_throttle_end(struct sys_device *sysdev, const char *buf, size_t size) +static ssize_t ppe_store_throttle_end(struct sys_device *sysdev, + struct sysdev_attribute *attr, const char *buf, size_t size) { return store_throttle(cbe_get_cpu_pmd_regs(sysdev->id), buf, size, 32); } -static ssize_t ppe_store_throttle_begin(struct sys_device *sysdev, const char *buf, size_t size) +static ssize_t ppe_store_throttle_begin(struct sys_device *sysdev, + struct sysdev_attribute *attr, const char *buf, size_t size) { return store_throttle(cbe_get_cpu_pmd_regs(sysdev->id), buf, size, 40); } -static ssize_t ppe_store_throttle_full_stop(struct sys_device *sysdev, const char *buf, size_t size) +static ssize_t ppe_store_throttle_full_stop(struct sys_device *sysdev, + struct sysdev_attribute *attr, const char *buf, size_t size) { return store_throttle(cbe_get_cpu_pmd_regs(sysdev->id), buf, size, 48); } diff --git a/arch/powerpc/platforms/cell/celleb_scc_pciex.c b/arch/powerpc/platforms/cell/celleb_scc_pciex.c index 0e04f8fb152..3e7e0f1568e 100644 --- a/arch/powerpc/platforms/cell/celleb_scc_pciex.c +++ b/arch/powerpc/platforms/cell/celleb_scc_pciex.c @@ -281,7 +281,7 @@ static int __init scc_pciex_iowa_init(struct iowa_bus *bus, void *data) dummy_page_da = dma_map_single(bus->phb->parent, dummy_page_va, PAGE_SIZE, DMA_FROM_DEVICE); - if (dma_mapping_error(dummy_page_da)) { + if (dma_mapping_error(bus->phb->parent, dummy_page_da)) { pr_err("PCIEX:Map dummy page failed.\n"); kfree(dummy_page_va); return -1; diff --git a/arch/powerpc/platforms/cell/cpufreq_spudemand.c b/arch/powerpc/platforms/cell/cpufreq_spudemand.c new file mode 100644 index 00000000000..a3c6c01bd6d --- /dev/null +++ b/arch/powerpc/platforms/cell/cpufreq_spudemand.c @@ -0,0 +1,184 @@ +/* + * spu aware cpufreq governor for the cell processor + * + * © Copyright IBM Corporation 2006-2008 + * + * Author: Christian Krafft <krafft@de.ibm.com> + * + * 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, 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. 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. + */ + +#include <linux/cpufreq.h> +#include <linux/sched.h> +#include <linux/timer.h> +#include <linux/workqueue.h> +#include <asm/atomic.h> +#include <asm/machdep.h> +#include <asm/spu.h> + +#define POLL_TIME 100000 /* in µs */ +#define EXP 753 /* exp(-1) in fixed-point */ + +struct spu_gov_info_struct { + unsigned long busy_spus; /* fixed-point */ + struct cpufreq_policy *policy; + struct delayed_work work; + unsigned int poll_int; /* µs */ +}; +static DEFINE_PER_CPU(struct spu_gov_info_struct, spu_gov_info); + +static struct workqueue_struct *kspugov_wq; + +static int calc_freq(struct spu_gov_info_struct *info) +{ + int cpu; + int busy_spus; + + cpu = info->policy->cpu; + busy_spus = atomic_read(&cbe_spu_info[cpu_to_node(cpu)].busy_spus); + + CALC_LOAD(info->busy_spus, EXP, busy_spus * FIXED_1); + pr_debug("cpu %d: busy_spus=%d, info->busy_spus=%ld\n", + cpu, busy_spus, info->busy_spus); + + return info->policy->max * info->busy_spus / FIXED_1; +} + +static void spu_gov_work(struct work_struct *work) +{ + struct spu_gov_info_struct *info; + int delay; + unsigned long target_freq; + + info = container_of(work, struct spu_gov_info_struct, work.work); + + /* after cancel_delayed_work_sync we unset info->policy */ + BUG_ON(info->policy == NULL); + + target_freq = calc_freq(info); + __cpufreq_driver_target(info->policy, target_freq, CPUFREQ_RELATION_H); + + delay = usecs_to_jiffies(info->poll_int); + queue_delayed_work_on(info->policy->cpu, kspugov_wq, &info->work, delay); +} + +static void spu_gov_init_work(struct spu_gov_info_struct *info) +{ + int delay = usecs_to_jiffies(info->poll_int); + INIT_DELAYED_WORK_DEFERRABLE(&info->work, spu_gov_work); + queue_delayed_work_on(info->policy->cpu, kspugov_wq, &info->work, delay); +} + +static void spu_gov_cancel_work(struct spu_gov_info_struct *info) +{ + cancel_delayed_work_sync(&info->work); +} + +static int spu_gov_govern(struct cpufreq_policy *policy, unsigned int event) +{ + unsigned int cpu = policy->cpu; + struct spu_gov_info_struct *info, *affected_info; + int i; + int ret = 0; + + info = &per_cpu(spu_gov_info, cpu); + + switch (event) { + case CPUFREQ_GOV_START: + if (!cpu_online(cpu)) { + printk(KERN_ERR "cpu %d is not online\n", cpu); + ret = -EINVAL; + break; + } + + if (!policy->cur) { + printk(KERN_ERR "no cpu specified in policy\n"); + ret = -EINVAL; + break; + } + + /* initialize spu_gov_info for all affected cpus */ + for_each_cpu_mask(i, policy->cpus) { + affected_info = &per_cpu(spu_gov_info, i); + affected_info->policy = policy; + } + + info->poll_int = POLL_TIME; + + /* setup timer */ + spu_gov_init_work(info); + + break; + + case CPUFREQ_GOV_STOP: + /* cancel timer */ + spu_gov_cancel_work(info); + + /* clean spu_gov_info for all affected cpus */ + for_each_cpu_mask (i, policy->cpus) { + info = &per_cpu(spu_gov_info, i); + info->policy = NULL; + } + + break; + } + + return ret; +} + +static struct cpufreq_governor spu_governor = { + .name = "spudemand", + .governor = spu_gov_govern, + .owner = THIS_MODULE, +}; + +/* + * module init and destoy + */ + +static int __init spu_gov_init(void) +{ + int ret; + + kspugov_wq = create_workqueue("kspugov"); + if (!kspugov_wq) { + printk(KERN_ERR "creation of kspugov failed\n"); + ret = -EFAULT; + goto out; + } + + ret = cpufreq_register_governor(&spu_governor); + if (ret) { + printk(KERN_ERR "registration of governor failed\n"); + destroy_workqueue(kspugov_wq); + goto out; + } +out: + return ret; +} + +static void __exit spu_gov_exit(void) +{ + cpufreq_unregister_governor(&spu_governor); + destroy_workqueue(kspugov_wq); +} + + +module_init(spu_gov_init); +module_exit(spu_gov_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Christian Krafft <krafft@de.ibm.com>"); + diff --git a/arch/powerpc/platforms/cell/iommu.c b/arch/powerpc/platforms/cell/iommu.c index eeacb3a52ca..e06420af5fe 100644 --- a/arch/powerpc/platforms/cell/iommu.c +++ b/arch/powerpc/platforms/cell/iommu.c @@ -172,8 +172,9 @@ static void invalidate_tce_cache(struct cbe_iommu *iommu, unsigned long *pte, } } -static void tce_build_cell(struct iommu_table *tbl, long index, long npages, - unsigned long uaddr, enum dma_data_direction direction) +static int tce_build_cell(struct iommu_table *tbl, long index, long npages, + unsigned long uaddr, enum dma_data_direction direction, + struct dma_attrs *attrs) { int i; unsigned long *io_pte, base_pte; @@ -198,6 +199,8 @@ static void tce_build_cell(struct iommu_table *tbl, long index, long npages, base_pte = IOPTE_PP_W | IOPTE_PP_R | IOPTE_M | IOPTE_SO_RW | (window->ioid & IOPTE_IOID_Mask); #endif + if (unlikely(dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs))) + base_pte &= ~IOPTE_SO_RW; io_pte = (unsigned long *)tbl->it_base + (index - tbl->it_offset); @@ -210,6 +213,7 @@ static void tce_build_cell(struct iommu_table *tbl, long index, long npages, pr_debug("tce_build_cell(index=%lx,n=%lx,dir=%d,base_pte=%lx)\n", index, npages, direction, base_pte); + return 0; } static void tce_free_cell(struct iommu_table *tbl, long index, long npages) @@ -519,7 +523,7 @@ cell_iommu_setup_window(struct cbe_iommu *iommu, struct device_node *np, __set_bit(0, window->table.it_map); tce_build_cell(&window->table, window->table.it_offset, 1, - (unsigned long)iommu->pad_page, DMA_TO_DEVICE); + (unsigned long)iommu->pad_page, DMA_TO_DEVICE, NULL); window->table.it_hint = window->table.it_blocksize; return window; @@ -538,7 +542,9 @@ static struct cbe_iommu *cell_iommu_for_node(int nid) static unsigned long cell_dma_direct_offset; static unsigned long dma_iommu_fixed_base; -struct dma_mapping_ops dma_iommu_fixed_ops; + +/* iommu_fixed_is_weak is set if booted with iommu_fixed=weak */ +static int iommu_fixed_is_weak; static struct iommu_table *cell_get_iommu_table(struct device *dev) { @@ -562,6 +568,98 @@ static struct iommu_table *cell_get_iommu_table(struct device *dev) return &window->table; } +/* A coherent allocation implies strong ordering */ + +static void *dma_fixed_alloc_coherent(struct device *dev, size_t size, + dma_addr_t *dma_handle, gfp_t flag) +{ + if (iommu_fixed_is_weak) + return iommu_alloc_coherent(dev, cell_get_iommu_table(dev), + size, dma_handle, + device_to_mask(dev), flag, + dev->archdata.numa_node); + else + return dma_direct_ops.alloc_coherent(dev, size, dma_handle, + flag); +} + +static void dma_fixed_free_coherent(struct device *dev, size_t size, + void *vaddr, dma_addr_t dma_handle) +{ + if (iommu_fixed_is_weak) + iommu_free_coherent(cell_get_iommu_table(dev), size, vaddr, + dma_handle); + else + dma_direct_ops.free_coherent(dev, size, vaddr, dma_handle); +} + +static dma_addr_t dma_fixed_map_single(struct device *dev, void *ptr, + size_t size, + enum dma_data_direction direction, + struct dma_attrs *attrs) +{ + if (iommu_fixed_is_weak == dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs)) + return dma_direct_ops.map_single(dev, ptr, size, direction, + attrs); + else + return iommu_map_single(dev, cell_get_iommu_table(dev), ptr, + size, device_to_mask(dev), direction, + attrs); +} + +static void dma_fixed_unmap_single(struct device *dev, dma_addr_t dma_addr, + size_t size, + enum dma_data_direction direction, + struct dma_attrs *attrs) +{ + if (iommu_fixed_is_weak == dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs)) + dma_direct_ops.unmap_single(dev, dma_addr, size, direction, + attrs); + else + iommu_unmap_single(cell_get_iommu_table(dev), dma_addr, size, + direction, attrs); +} + +static int dma_fixed_map_sg(struct device *dev, struct scatterlist *sg, + int nents, enum dma_data_direction direction, + struct dma_attrs *attrs) +{ + if (iommu_fixed_is_weak == dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs)) + return dma_direct_ops.map_sg(dev, sg, nents, direction, attrs); + else + return iommu_map_sg(dev, cell_get_iommu_table(dev), sg, nents, + device_to_mask(dev), direction, attrs); +} + +static void dma_fixed_unmap_sg(struct device *dev, struct scatterlist *sg, + int nents, enum dma_data_direction direction, + struct dma_attrs *attrs) +{ + if (iommu_fixed_is_weak == dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs)) + dma_direct_ops.unmap_sg(dev, sg, nents, direction, attrs); + else + iommu_unmap_sg(cell_get_iommu_table(dev), sg, nents, direction, + attrs); +} + +static int dma_fixed_dma_supported(struct device *dev, u64 mask) +{ + return mask == DMA_64BIT_MASK; +} + +static int dma_set_mask_and_switch(struct device *dev, u64 dma_mask); + +struct dma_mapping_ops dma_iommu_fixed_ops = { + .alloc_coherent = dma_fixed_alloc_coherent, + .free_coherent = dma_fixed_free_coherent, + .map_single = dma_fixed_map_single, + .unmap_single = dma_fixed_unmap_single, + .map_sg = dma_fixed_map_sg, + .unmap_sg = dma_fixed_unmap_sg, + .dma_supported = dma_fixed_dma_supported, + .set_dma_mask = dma_set_mask_and_switch, +}; + static void cell_dma_dev_setup_fixed(struct device *dev); static void cell_dma_dev_setup(struct device *dev) @@ -918,9 +1016,16 @@ static void cell_iommu_setup_fixed_ptab(struct cbe_iommu *iommu, pr_debug("iommu: mapping 0x%lx pages from 0x%lx\n", fsize, fbase); - base_pte = IOPTE_PP_W | IOPTE_PP_R | IOPTE_M | IOPTE_SO_RW + base_pte = IOPTE_PP_W | IOPTE_PP_R | IOPTE_M | (cell_iommu_get_ioid(np) & IOPTE_IOID_Mask); + if (iommu_fixed_is_weak) + pr_info("IOMMU: Using weak ordering for fixed mapping\n"); + else { + pr_info("IOMMU: Using strong ordering for fixed mapping\n"); + base_pte |= IOPTE_SO_RW; + } + for (uaddr = 0; uaddr < fsize; uaddr += (1 << 24)) { /* Don't touch the dynamic region */ ioaddr = uaddr + fbase; @@ -1036,9 +1141,6 @@ static int __init cell_iommu_fixed_mapping_init(void) cell_iommu_setup_window(iommu, np, dbase, dsize, 0); } - dma_iommu_fixed_ops = dma_direct_ops; - dma_iommu_fixed_ops.set_dma_mask = dma_set_mask_and_switch; - dma_iommu_ops.set_dma_mask = dma_set_mask_and_switch; set_pci_dma_ops(&dma_iommu_ops); @@ -1049,9 +1151,23 @@ static int iommu_fixed_disabled; static int __init setup_iommu_fixed(char *str) { + struct device_node *pciep; + if (strcmp(str, "off") == 0) iommu_fixed_disabled = 1; + /* If we can find a pcie-endpoint in the device tree assume that + * we're on a triblade or a CAB so by default the fixed mapping + * should be set to be weakly ordered; but only if the boot + * option WASN'T set for strong ordering + */ + pciep = of_find_node_by_type(NULL, "pcie-endpoint"); + + if (strcmp(str, "weak") == 0 || (pciep && strcmp(str, "strong") != 0)) + iommu_fixed_is_weak = 1; + + of_node_put(pciep); + return 1; } __setup("iommu_fixed=", setup_iommu_fixed); diff --git a/arch/powerpc/platforms/cell/pervasive.c b/arch/powerpc/platforms/cell/pervasive.c index 8a3631ce912..efdacc82957 100644 --- a/arch/powerpc/platforms/cell/pervasive.c +++ b/arch/powerpc/platforms/cell/pervasive.c @@ -38,8 +38,6 @@ #include "pervasive.h" -static int sysreset_hack; - static void cbe_power_save(void) { unsigned long ctrl, thread_switch_control; @@ -87,9 +85,6 @@ static void cbe_power_save(void) static int cbe_system_reset_exception(struct pt_regs *regs) { - int cpu; - struct cbe_pmd_regs __iomem *pmd; - switch (regs->msr & SRR1_WAKEMASK) { case SRR1_WAKEEE: do_IRQ(regs); @@ -98,19 +93,7 @@ static int cbe_system_reset_exception(struct pt_regs *regs) timer_interrupt(regs); break; case SRR1_WAKEMT: - /* - * The BMC can inject user triggered system reset exceptions, - * but cannot set the system reset reason in srr1, - * so check an extra register here. - */ - if (sysreset_hack && (cpu = smp_processor_id()) == 0) { - pmd = cbe_get_cpu_pmd_regs(cpu); - if (in_be64(&pmd->ras_esc_0) & 0xffff) { - out_be64(&pmd->ras_esc_0, 0); - return 0; - } - } - break; + return cbe_sysreset_hack(); #ifdef CONFIG_CBE_RAS case SRR1_WAKESYSERR: cbe_system_error_exception(regs); @@ -134,8 +117,6 @@ void __init cbe_pervasive_init(void) if (!cpu_has_feature(CPU_FTR_PAUSE_ZERO)) return; - sysreset_hack = machine_is_compatible("IBM,CBPLUS-1.0"); - for_each_possible_cpu(cpu) { struct cbe_pmd_regs __iomem *regs = cbe_get_cpu_pmd_regs(cpu); if (!regs) @@ -144,12 +125,6 @@ void __init cbe_pervasive_init(void) /* Enable Pause(0) control bit */ out_be64(®s->pmcr, in_be64(®s->pmcr) | CBE_PMD_PAUSE_ZERO_CONTROL); - - /* Enable JTAG system-reset hack */ - if (sysreset_hack) - out_be32(®s->fir_mode_reg, - in_be32(®s->fir_mode_reg) | - CBE_PMD_FIR_MODE_M8); } ppc_md.power_save = cbe_power_save; diff --git a/arch/powerpc/platforms/cell/pervasive.h b/arch/powerpc/platforms/cell/pervasive.h index 7b50947f804..fd4d7b7092b 100644 --- a/arch/powerpc/platforms/cell/pervasive.h +++ b/arch/powerpc/platforms/cell/pervasive.h @@ -30,4 +30,13 @@ extern void cbe_system_error_exception(struct pt_regs *regs); extern void cbe_maintenance_exception(struct pt_regs *regs); extern void cbe_thermal_exception(struct pt_regs *regs); +#ifdef CONFIG_PPC_IBM_CELL_RESETBUTTON +extern int cbe_sysreset_hack(void); +#else +static inline int cbe_sysreset_hack(void) +{ + return 1; +} +#endif /* CONFIG_PPC_IBM_CELL_RESETBUTTON */ + #endif diff --git a/arch/powerpc/platforms/cell/ras.c b/arch/powerpc/platforms/cell/ras.c index 505f9b9bdf0..2a14b052abc 100644 --- a/arch/powerpc/platforms/cell/ras.c +++ b/arch/powerpc/platforms/cell/ras.c @@ -236,6 +236,52 @@ static struct notifier_block cbe_ptcal_reboot_notifier = { .notifier_call = cbe_ptcal_notify_reboot }; +#ifdef CONFIG_PPC_IBM_CELL_RESETBUTTON +static int sysreset_hack; + +static int __init cbe_sysreset_init(void) +{ + struct cbe_pmd_regs __iomem *regs; + + sysreset_hack = machine_is_compatible("IBM,CBPLUS-1.0"); + if (!sysreset_hack) + return 0; + + regs = cbe_get_cpu_pmd_regs(0); + if (!regs) + return 0; + + /* Enable JTAG system-reset hack */ + out_be32(®s->fir_mode_reg, + in_be32(®s->fir_mode_reg) | + CBE_PMD_FIR_MODE_M8); + + return 0; +} +device_initcall(cbe_sysreset_init); + +int cbe_sysreset_hack(void) +{ + struct cbe_pmd_regs __iomem *regs; + + /* + * The BMC can inject user triggered system reset exceptions, + * but cannot set the system reset reason in srr1, + * so check an extra register here. + */ + if (sysreset_hack && (smp_processor_id() == 0)) { + regs = cbe_get_cpu_pmd_regs(0); + if (!regs) + return 0; + if (in_be64(®s->ras_esc_0) & 0x0000ffff) { + out_be64(®s->ras_esc_0, 0); + return 0; + } + } + return 1; +} +#endif /* CONFIG_PPC_IBM_CELL_RESETBUTTON */ + int __init cbe_ptcal_init(void) { int ret; diff --git a/arch/powerpc/platforms/cell/spider-pci.c b/arch/powerpc/platforms/cell/spider-pci.c index 418b605ac35..5122ec14527 100644 --- a/arch/powerpc/platforms/cell/spider-pci.c +++ b/arch/powerpc/platforms/cell/spider-pci.c @@ -111,7 +111,7 @@ static int __init spiderpci_pci_setup_chip(struct pci_controller *phb, dummy_page_da = dma_map_single(phb->parent, dummy_page_va, PAGE_SIZE, DMA_FROM_DEVICE); - if (dma_mapping_error(dummy_page_da)) { + if (dma_mapping_error(phb->parent, dummy_page_da)) { pr_err("SPIDER-IOWA:Map dummy page filed.\n"); kfree(dummy_page_va); return -1; diff --git a/arch/powerpc/platforms/cell/spu_base.c b/arch/powerpc/platforms/cell/spu_base.c index 78f905bc6a4..a5bdb89a17c 100644 --- a/arch/powerpc/platforms/cell/spu_base.c +++ b/arch/powerpc/platforms/cell/spu_base.c @@ -703,7 +703,8 @@ static unsigned long long spu_acct_time(struct spu *spu, } -static ssize_t spu_stat_show(struct sys_device *sysdev, char *buf) +static ssize_t spu_stat_show(struct sys_device *sysdev, + struct sysdev_attribute *attr, char *buf) { struct spu *spu = container_of(sysdev, struct spu, sysdev); diff --git a/arch/powerpc/platforms/cell/spufs/file.c b/arch/powerpc/platforms/cell/spufs/file.c index 99c73066b82..010a51f5979 100644 --- a/arch/powerpc/platforms/cell/spufs/file.c +++ b/arch/powerpc/platforms/cell/spufs/file.c @@ -288,9 +288,32 @@ spufs_mem_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) return VM_FAULT_NOPAGE; } +static int spufs_mem_mmap_access(struct vm_area_struct *vma, + unsigned long address, + void *buf, int len, int write) +{ + struct spu_context *ctx = vma->vm_file->private_data; + unsigned long offset = address - vma->vm_start; + char *local_store; + + if (write && !(vma->vm_flags & VM_WRITE)) + return -EACCES; + if (spu_acquire(ctx)) + return -EINTR; + if ((offset + len) > vma->vm_end) + len = vma->vm_end - offset; + local_store = ctx->ops->get_ls(ctx); + if (write) + memcpy_toio(local_store + offset, buf, len); + else + memcpy_fromio(buf, local_store + offset, len); + spu_release(ctx); + return len; +} static struct vm_operations_struct spufs_mem_mmap_vmops = { .fault = spufs_mem_mmap_fault, + .access = spufs_mem_mmap_access, }; static int spufs_mem_mmap(struct file *file, struct vm_area_struct *vma) diff --git a/arch/powerpc/platforms/cell/spufs/inode.c b/arch/powerpc/platforms/cell/spufs/inode.c index 7123472801d..690ca7b0dcf 100644 --- a/arch/powerpc/platforms/cell/spufs/inode.c +++ b/arch/powerpc/platforms/cell/spufs/inode.c @@ -78,7 +78,7 @@ spufs_destroy_inode(struct inode *inode) } static void -spufs_init_once(struct kmem_cache *cachep, void *p) +spufs_init_once(void *p) { struct spufs_inode_info *ei = p; diff --git a/arch/powerpc/platforms/cell/spufs/run.c b/arch/powerpc/platforms/cell/spufs/run.c index f7edba6cb79..c9bb7cfd3dc 100644 --- a/arch/powerpc/platforms/cell/spufs/run.c +++ b/arch/powerpc/platforms/cell/spufs/run.c @@ -206,11 +206,6 @@ static int spu_run_init(struct spu_context *ctx, u32 *npc) (SPU_RUNCNTL_RUNNABLE | SPU_RUNCNTL_ISOLATE); if (runcntl == 0) runcntl = SPU_RUNCNTL_RUNNABLE; - } - - if (ctx->flags & SPU_CREATE_NOSCHED) { - spuctx_switch_state(ctx, SPU_UTIL_USER); - ctx->ops->runcntl_write(ctx, runcntl); } else { unsigned long privcntl; @@ -219,9 +214,15 @@ static int spu_run_init(struct spu_context *ctx, u32 *npc) else privcntl = SPU_PRIVCNTL_MODE_NORMAL; - ctx->ops->npc_write(ctx, *npc); ctx->ops->privcntl_write(ctx, privcntl); - ctx->ops->runcntl_write(ctx, runcntl); + ctx->ops->npc_write(ctx, *npc); + } + + ctx->ops->runcntl_write(ctx, runcntl); + + if (ctx->flags & SPU_CREATE_NOSCHED) { + spuctx_switch_state(ctx, SPU_UTIL_USER); + } else { if (ctx->state == SPU_STATE_SAVED) { ret = spu_activate(ctx, 0); diff --git a/arch/powerpc/platforms/cell/spufs/sched.c b/arch/powerpc/platforms/cell/spufs/sched.c index 34654743363..67595bc380d 100644 --- a/arch/powerpc/platforms/cell/spufs/sched.c +++ b/arch/powerpc/platforms/cell/spufs/sched.c @@ -312,11 +312,28 @@ static struct spu *aff_ref_location(struct spu_context *ctx, int mem_aff, */ node = cpu_to_node(raw_smp_processor_id()); for (n = 0; n < MAX_NUMNODES; n++, node++) { + int available_spus; + node = (node < MAX_NUMNODES) ? node : 0; if (!node_allowed(ctx, node)) continue; + + available_spus = 0; mutex_lock(&cbe_spu_info[node].list_mutex); list_for_each_entry(spu, &cbe_spu_info[node].spus, cbe_list) { + if (spu->ctx && spu->ctx->gang + && spu->ctx->aff_offset == 0) + available_spus -= + (spu->ctx->gang->contexts - 1); + else + available_spus++; + } + if (available_spus < ctx->gang->contexts) { + mutex_unlock(&cbe_spu_info[node].list_mutex); + continue; + } + + list_for_each_entry(spu, &cbe_spu_info[node].spus, cbe_list) { if ((!mem_aff || spu->has_mem_affinity) && sched_spu(spu)) { mutex_unlock(&cbe_spu_info[node].list_mutex); @@ -389,6 +406,9 @@ static int has_affinity(struct spu_context *ctx) if (list_empty(&ctx->aff_list)) return 0; + if (atomic_read(&ctx->gang->aff_sched_count) == 0) + ctx->gang->aff_ref_spu = NULL; + if (!gang->aff_ref_spu) { if (!(gang->aff_flags & AFF_MERGED)) aff_merge_remaining_ctxs(gang); @@ -416,14 +436,8 @@ static void spu_unbind_context(struct spu *spu, struct spu_context *ctx) if (spu->ctx->flags & SPU_CREATE_NOSCHED) atomic_dec(&cbe_spu_info[spu->node].reserved_spus); - if (ctx->gang){ - mutex_lock(&ctx->gang->aff_mutex); - if (has_affinity(ctx)) { - if (atomic_dec_and_test(&ctx->gang->aff_sched_count)) - ctx->gang->aff_ref_spu = NULL; - } - mutex_unlock(&ctx->gang->aff_mutex); - } + if (ctx->gang) + atomic_dec_if_positive(&ctx->gang->aff_sched_count); spu_switch_notify(spu, NULL); spu_unmap_mappings(ctx); @@ -562,10 +576,7 @@ static struct spu *spu_get_idle(struct spu_context *ctx) goto found; mutex_unlock(&cbe_spu_info[node].list_mutex); - mutex_lock(&ctx->gang->aff_mutex); - if (atomic_dec_and_test(&ctx->gang->aff_sched_count)) - ctx->gang->aff_ref_spu = NULL; - mutex_unlock(&ctx->gang->aff_mutex); + atomic_dec(&ctx->gang->aff_sched_count); goto not_found; } mutex_unlock(&ctx->gang->aff_mutex); @@ -630,9 +641,12 @@ static struct spu *find_victim(struct spu_context *ctx) if (tmp && tmp->prio > ctx->prio && !(tmp->flags & SPU_CREATE_NOSCHED) && - (!victim || tmp->prio > victim->prio)) + (!victim || tmp->prio > victim->prio)) { victim = spu->ctx; + } } + if (victim) + get_spu_context(victim); mutex_unlock(&cbe_spu_info[node].list_mutex); if (victim) { @@ -647,6 +661,7 @@ static struct spu *find_victim(struct spu_context *ctx) * look at another context or give up after X retries. */ if (!mutex_trylock(&victim->state_mutex)) { + put_spu_context(victim); victim = NULL; goto restart; } @@ -659,6 +674,7 @@ static struct spu *find_victim(struct spu_context *ctx) * restart the search. */ mutex_unlock(&victim->state_mutex); + put_spu_context(victim); victim = NULL; goto restart; } @@ -676,6 +692,7 @@ static struct spu *find_victim(struct spu_context *ctx) spu_add_to_rq(victim); mutex_unlock(&victim->state_mutex); + put_spu_context(victim); return spu; } @@ -711,17 +728,33 @@ static void spu_schedule(struct spu *spu, struct spu_context *ctx) /* not a candidate for interruptible because it's called either from the scheduler thread or from spu_deactivate */ mutex_lock(&ctx->state_mutex); - __spu_schedule(spu, ctx); + if (ctx->state == SPU_STATE_SAVED) + __spu_schedule(spu, ctx); spu_release(ctx); } -static void spu_unschedule(struct spu *spu, struct spu_context *ctx) +/** + * spu_unschedule - remove a context from a spu, and possibly release it. + * @spu: The SPU to unschedule from + * @ctx: The context currently scheduled on the SPU + * @free_spu Whether to free the SPU for other contexts + * + * Unbinds the context @ctx from the SPU @spu. If @free_spu is non-zero, the + * SPU is made available for other contexts (ie, may be returned by + * spu_get_idle). If this is zero, the caller is expected to schedule another + * context to this spu. + * + * Should be called with ctx->state_mutex held. + */ +static void spu_unschedule(struct spu *spu, struct spu_context *ctx, + int free_spu) { int node = spu->node; mutex_lock(&cbe_spu_info[node].list_mutex); cbe_spu_info[node].nr_active--; - spu->alloc_state = SPU_FREE; + if (free_spu) + spu->alloc_state = SPU_FREE; spu_unbind_context(spu, ctx); ctx->stats.invol_ctx_switch++; spu->stats.invol_ctx_switch++; @@ -821,7 +854,7 @@ static int __spu_deactivate(struct spu_context *ctx, int force, int max_prio) if (spu) { new = grab_runnable_context(max_prio, spu->node); if (new || force) { - spu_unschedule(spu, ctx); + spu_unschedule(spu, ctx, new == NULL); if (new) { if (new->flags & SPU_CREATE_NOSCHED) wake_up(&new->stop_wq); @@ -894,7 +927,7 @@ static noinline void spusched_tick(struct spu_context *ctx) new = grab_runnable_context(ctx->prio + 1, spu->node); if (new) { - spu_unschedule(spu, ctx); + spu_unschedule(spu, ctx, 0); if (test_bit(SPU_SCHED_SPU_RUN, &ctx->sched_flags)) spu_add_to_rq(ctx); } else { @@ -974,9 +1007,11 @@ static int spusched_thread(void *unused) struct spu_context *ctx = spu->ctx; if (ctx) { + get_spu_context(ctx); mutex_unlock(mtx); spusched_tick(ctx); mutex_lock(mtx); + put_spu_context(ctx); } } mutex_unlock(mtx); @@ -1019,7 +1054,7 @@ void spuctx_switch_state(struct spu_context *ctx, node = spu->node; if (old_state == SPU_UTIL_USER) atomic_dec(&cbe_spu_info[node].busy_spus); - if (new_state == SPU_UTIL_USER); + if (new_state == SPU_UTIL_USER) atomic_inc(&cbe_spu_info[node].busy_spus); } } diff --git a/arch/powerpc/platforms/cell/spufs/sputrace.c b/arch/powerpc/platforms/cell/spufs/sputrace.c index 8c0e95766a6..92d20e993ed 100644 --- a/arch/powerpc/platforms/cell/spufs/sputrace.c +++ b/arch/powerpc/platforms/cell/spufs/sputrace.c @@ -196,8 +196,7 @@ static int __init sputrace_init(void) struct proc_dir_entry *entry; int i, error = -ENOMEM; - sputrace_log = kcalloc(sizeof(struct sputrace), - bufsize, GFP_KERNEL); + sputrace_log = kcalloc(bufsize, sizeof(struct sputrace), GFP_KERNEL); if (!sputrace_log) goto out; diff --git a/arch/powerpc/platforms/chrp/pci.c b/arch/powerpc/platforms/chrp/pci.c index 609c46db4a1..768c262b936 100644 --- a/arch/powerpc/platforms/chrp/pci.c +++ b/arch/powerpc/platforms/chrp/pci.c @@ -367,7 +367,7 @@ static void chrp_pci_fixup_vt8231_ata(struct pci_dev *viaide) viaisa = pci_get_device(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8231, NULL); if (!viaisa) return; - printk("Fixing VIA IDE, force legacy mode on '%s'\n", viaide->dev.bus_id); + dev_info(&viaide->dev, "Fixing VIA IDE, force legacy mode on\n"); pci_read_config_byte(viaide, PCI_CLASS_PROG, &progif); pci_write_config_byte(viaide, PCI_CLASS_PROG, progif & ~0x5); diff --git a/arch/powerpc/platforms/fsl_uli1575.c b/arch/powerpc/platforms/fsl_uli1575.c index afc9141be63..ef74a0763ec 100644 --- a/arch/powerpc/platforms/fsl_uli1575.c +++ b/arch/powerpc/platforms/fsl_uli1575.c @@ -51,15 +51,13 @@ u8 uli_pirq_to_irq[8] = { ULI_8259_NONE, /* PIRQH */ }; -/* set in board code if you want this quirks to do something */ -int uses_fsl_uli_m1575; - /* Bridge */ static void __devinit early_uli5249(struct pci_dev *dev) { unsigned char temp; - if (!uses_fsl_uli_m1575) + if (!machine_is(mpc86xx_hpcn) && !machine_is(mpc8544_ds) && + !machine_is(mpc8572_ds)) return; pci_write_config_word(dev, PCI_COMMAND, PCI_COMMAND_IO | @@ -82,7 +80,8 @@ static void __devinit quirk_uli1575(struct pci_dev *dev) { int i; - if (!uses_fsl_uli_m1575) + if (!machine_is(mpc86xx_hpcn) && !machine_is(mpc8544_ds) && + !machine_is(mpc8572_ds)) return; /* @@ -150,7 +149,8 @@ static void __devinit quirk_final_uli1575(struct pci_dev *dev) * IRQ 14: Edge * IRQ 15: Edge */ - if (!uses_fsl_uli_m1575) + if (!machine_is(mpc86xx_hpcn) && !machine_is(mpc8544_ds) && + !machine_is(mpc8572_ds)) return; outb(0xfa, 0x4d0); @@ -176,7 +176,8 @@ static void __devinit quirk_uli5288(struct pci_dev *dev) unsigned char c; unsigned int d; - if (!uses_fsl_uli_m1575) + if (!machine_is(mpc86xx_hpcn) && !machine_is(mpc8544_ds) && + !machine_is(mpc8572_ds)) return; /* read/write lock */ @@ -200,7 +201,8 @@ static void __devinit quirk_uli5229(struct pci_dev *dev) { unsigned short temp; - if (!uses_fsl_uli_m1575) + if (!machine_is(mpc86xx_hpcn) && !machine_is(mpc8544_ds) && + !machine_is(mpc8572_ds)) return; pci_write_config_word(dev, PCI_COMMAND, PCI_COMMAND_INTX_DISABLE | @@ -221,7 +223,7 @@ static void __devinit quirk_final_uli5249(struct pci_dev *dev) for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) { if ((bus->resource[i]) && (bus->resource[i]->flags & IORESOURCE_MEM)) { - dummy = ioremap(bus->resource[i]->start, 0x4); + dummy = ioremap(bus->resource[i]->end - 3, 0x4); if (dummy) { in_8(dummy); iounmap(dummy); @@ -238,6 +240,103 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AL, 0x5229, quirk_uli5229); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AL, 0x5249, quirk_final_uli5249); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AL, 0x1575, quirk_final_uli1575); +static void __devinit hpcd_quirk_uli1575(struct pci_dev *dev) +{ + u32 temp32; + + if (!machine_is(mpc86xx_hpcd)) + return; + + /* Disable INTx */ + pci_read_config_dword(dev, 0x48, &temp32); + pci_write_config_dword(dev, 0x48, (temp32 | 1<<26)); + + /* Enable sideband interrupt */ + pci_read_config_dword(dev, 0x90, &temp32); + pci_write_config_dword(dev, 0x90, (temp32 | 1<<22)); +} + +static void __devinit hpcd_quirk_uli5288(struct pci_dev *dev) +{ + unsigned char c; + unsigned short temp; + + if (!machine_is(mpc86xx_hpcd)) + return; + + /* Interrupt Disable, Needed when SATA disabled */ + pci_read_config_word(dev, PCI_COMMAND, &temp); + temp |= 1<<10; + pci_write_config_word(dev, PCI_COMMAND, temp); + + pci_read_config_byte(dev, 0x83, &c); + c |= 0x80; + pci_write_config_byte(dev, 0x83, c); + + pci_write_config_byte(dev, PCI_CLASS_PROG, 0x01); + pci_write_config_byte(dev, PCI_CLASS_DEVICE, 0x06); + + pci_read_config_byte(dev, 0x83, &c); + c &= 0x7f; + pci_write_config_byte(dev, 0x83, c); +} + +/* + * Since 8259PIC was disabled on the board, the IDE device can not + * use the legacy IRQ, we need to let the IDE device work under + * native mode and use the interrupt line like other PCI devices. + * IRQ14 is a sideband interrupt from IDE device to CPU and we use this + * as the interrupt for IDE device. + */ +static void __devinit hpcd_quirk_uli5229(struct pci_dev *dev) +{ + unsigned char c; + + if (!machine_is(mpc86xx_hpcd)) + return; + + pci_read_config_byte(dev, 0x4b, &c); + c |= 0x10; + pci_write_config_byte(dev, 0x4b, c); +} + +/* + * SATA interrupt pin bug fix + * There's a chip bug for 5288, The interrupt pin should be 2, + * not the read only value 1, So it use INTB#, not INTA# which + * actually used by the IDE device 5229. + * As of this bug, during the PCI initialization, 5288 read the + * irq of IDE device from the device tree, this function fix this + * bug by re-assigning a correct irq to 5288. + * + */ +static void __devinit hpcd_final_uli5288(struct pci_dev *dev) +{ + struct pci_controller *hose = pci_bus_to_host(dev->bus); + struct device_node *hosenode = hose ? hose->dn : NULL; + struct of_irq oirq; + int virq, pin = 2; + u32 laddr[3]; + + if (!machine_is(mpc86xx_hpcd)) + return; + + if (!hosenode) + return; + + laddr[0] = (hose->first_busno << 16) | (PCI_DEVFN(31, 0) << 8); + laddr[1] = laddr[2] = 0; + of_irq_map_raw(hosenode, &pin, 1, laddr, &oirq); + virq = irq_create_of_mapping(oirq.controller, oirq.specifier, + oirq.size); + dev->irq = virq; +} + +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AL, 0x1575, hpcd_quirk_uli1575); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AL, 0x5288, hpcd_quirk_uli5288); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AL, 0x5229, hpcd_quirk_uli5229); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AL, 0x5288, hpcd_final_uli5288); + int uli_exclude_device(struct pci_controller *hose, u_char bus, u_char devfn) { diff --git a/arch/powerpc/platforms/iseries/Kconfig b/arch/powerpc/platforms/iseries/Kconfig index 761d9e971fc..45ffd8e542f 100644 --- a/arch/powerpc/platforms/iseries/Kconfig +++ b/arch/powerpc/platforms/iseries/Kconfig @@ -2,19 +2,11 @@ config PPC_ISERIES bool "IBM Legacy iSeries" depends on PPC_MULTIPLATFORM && PPC64 select PPC_INDIRECT_IO + select PPC_PCI_CHOICE if EMBEDDED menu "iSeries device drivers" depends on PPC_ISERIES -config VIOCONS - bool "iSeries Virtual Console Support (Obsolete)" - depends on !HVC_ISERIES - default n - help - This is the old virtual console driver for legacy iSeries. - You should use the iSeries Hypervisor Virtual Console - support instead. - config VIODASD tristate "iSeries Virtual I/O disk support" help @@ -37,5 +29,5 @@ endmenu config VIOPATH bool - depends on VIOCONS || VIODASD || VIOCD || VIOTAPE || ISERIES_VETH + depends on VIODASD || VIOCD || VIOTAPE || ISERIES_VETH default y diff --git a/arch/powerpc/platforms/iseries/iommu.c b/arch/powerpc/platforms/iseries/iommu.c index ab5d8687c3c..bb464d1211b 100644 --- a/arch/powerpc/platforms/iseries/iommu.c +++ b/arch/powerpc/platforms/iseries/iommu.c @@ -41,8 +41,9 @@ #include <asm/iseries/hv_call_event.h> #include <asm/iseries/iommu.h> -static void tce_build_iSeries(struct iommu_table *tbl, long index, long npages, - unsigned long uaddr, enum dma_data_direction direction) +static int tce_build_iSeries(struct iommu_table *tbl, long index, long npages, + unsigned long uaddr, enum dma_data_direction direction, + struct dma_attrs *attrs) { u64 rc; u64 tce, rpn; @@ -70,6 +71,7 @@ static void tce_build_iSeries(struct iommu_table *tbl, long index, long npages, index++; uaddr += TCE_PAGE_SIZE; } + return 0; } static void tce_free_iSeries(struct iommu_table *tbl, long index, long npages) diff --git a/arch/powerpc/platforms/iseries/mf.c b/arch/powerpc/platforms/iseries/mf.c index 1dc7295746d..731d7b15774 100644 --- a/arch/powerpc/platforms/iseries/mf.c +++ b/arch/powerpc/platforms/iseries/mf.c @@ -871,7 +871,7 @@ static int proc_mf_dump_cmdline(char *page, char **start, off_t off, count = 256 - off; dma_addr = iseries_hv_map(page, off + count, DMA_FROM_DEVICE); - if (dma_mapping_error(dma_addr)) + if (dma_mapping_error(NULL, dma_addr)) return -ENOMEM; memset(page, 0, off + count); memset(&vsp_cmd, 0, sizeof(vsp_cmd)); diff --git a/arch/powerpc/platforms/iseries/setup.c b/arch/powerpc/platforms/iseries/setup.c index b72120751bb..70b688c1aef 100644 --- a/arch/powerpc/platforms/iseries/setup.c +++ b/arch/powerpc/platforms/iseries/setup.c @@ -561,7 +561,7 @@ static void yield_shared_processor(void) static void iseries_shared_idle(void) { while (1) { - tick_nohz_stop_sched_tick(); + tick_nohz_stop_sched_tick(1); while (!need_resched() && !hvlpevent_is_pending()) { local_irq_disable(); ppc64_runlatch_off(); @@ -591,7 +591,7 @@ static void iseries_dedicated_idle(void) set_thread_flag(TIF_POLLING_NRFLAG); while (1) { - tick_nohz_stop_sched_tick(); + tick_nohz_stop_sched_tick(1); if (!need_resched()) { while (!need_resched()) { ppc64_runlatch_off(); diff --git a/arch/powerpc/platforms/pasemi/iommu.c b/arch/powerpc/platforms/pasemi/iommu.c index 86967bdd877..a0ff03a3d8d 100644 --- a/arch/powerpc/platforms/pasemi/iommu.c +++ b/arch/powerpc/platforms/pasemi/iommu.c @@ -83,9 +83,10 @@ static u32 *iob_l2_base; static struct iommu_table iommu_table_iobmap; static int iommu_table_iobmap_inited; -static void iobmap_build(struct iommu_table *tbl, long index, +static int iobmap_build(struct iommu_table *tbl, long index, long npages, unsigned long uaddr, - enum dma_data_direction direction) + enum dma_data_direction direction, + struct dma_attrs *attrs) { u32 *ip; u32 rpn; @@ -107,6 +108,7 @@ static void iobmap_build(struct iommu_table *tbl, long index, uaddr += IOBMAP_PAGE_SIZE; bus_addr += IOBMAP_PAGE_SIZE; } + return 0; } diff --git a/arch/powerpc/platforms/powermac/Makefile b/arch/powerpc/platforms/powermac/Makefile index 89774177b20..be60d64be7a 100644 --- a/arch/powerpc/platforms/powermac/Makefile +++ b/arch/powerpc/platforms/powermac/Makefile @@ -2,12 +2,12 @@ CFLAGS_bootx_init.o += -fPIC ifdef CONFIG_FTRACE # Do not trace early boot code -CFLAGS_REMOVE_bootx_init.o = -pg +CFLAGS_REMOVE_bootx_init.o = -pg -mno-sched-epilog endif obj-y += pic.o setup.o time.o feature.o pci.o \ sleep.o low_i2c.o cache.o pfunc_core.o \ - pfunc_base.o + pfunc_base.o udbg_scc.o udbg_adb.o obj-$(CONFIG_PMAC_BACKLIGHT) += backlight.o obj-$(CONFIG_CPU_FREQ_PMAC) += cpufreq_32.o obj-$(CONFIG_CPU_FREQ_PMAC64) += cpufreq_64.o @@ -19,4 +19,3 @@ obj-$(CONFIG_NVRAM:m=y) += nvram.o obj-$(CONFIG_PPC64) += nvram.o obj-$(CONFIG_PPC32) += bootx_init.o obj-$(CONFIG_SMP) += smp.o -obj-$(CONFIG_PPC_MERGE) += udbg_scc.o udbg_adb.o diff --git a/arch/powerpc/platforms/powermac/setup.c b/arch/powerpc/platforms/powermac/setup.c index 00bd0166d07..88ccf3a08a9 100644 --- a/arch/powerpc/platforms/powermac/setup.c +++ b/arch/powerpc/platforms/powermac/setup.c @@ -97,8 +97,6 @@ extern struct machdep_calls pmac_md; int sccdbg; #endif -extern void zs_kgdb_hook(int tty_num); - sys_ctrler_t sys_ctrler = SYS_CTRLER_UNKNOWN; EXPORT_SYMBOL(sys_ctrler); @@ -329,10 +327,6 @@ static void __init pmac_setup_arch(void) l2cr_init(); #endif /* CONFIG_PPC32 */ -#ifdef CONFIG_KGDB - zs_kgdb_hook(0); -#endif - find_via_cuda(); find_via_pmu(); smu_init(); @@ -547,6 +541,78 @@ static int __init pmac_declare_of_platform_devices(void) } machine_device_initcall(powermac, pmac_declare_of_platform_devices); +#ifdef CONFIG_SERIAL_PMACZILOG_CONSOLE +/* + * This is called very early, as part of console_init() (typically just after + * time_init()). This function is respondible for trying to find a good + * default console on serial ports. It tries to match the open firmware + * default output with one of the available serial console drivers. + */ +static int __init check_pmac_serial_console(void) +{ + struct device_node *prom_stdout = NULL; + int offset = 0; + const char *name; +#ifdef CONFIG_SERIAL_PMACZILOG_TTYS + char *devname = "ttyS"; +#else + char *devname = "ttyPZ"; +#endif + + pr_debug(" -> check_pmac_serial_console()\n"); + + /* The user has requested a console so this is already set up. */ + if (strstr(boot_command_line, "console=")) { + pr_debug(" console was specified !\n"); + return -EBUSY; + } + + if (!of_chosen) { + pr_debug(" of_chosen is NULL !\n"); + return -ENODEV; + } + + /* We are getting a weird phandle from OF ... */ + /* ... So use the full path instead */ + name = of_get_property(of_chosen, "linux,stdout-path", NULL); + if (name == NULL) { + pr_debug(" no linux,stdout-path !\n"); + return -ENODEV; + } + prom_stdout = of_find_node_by_path(name); + if (!prom_stdout) { + pr_debug(" can't find stdout package %s !\n", name); + return -ENODEV; + } + pr_debug("stdout is %s\n", prom_stdout->full_name); + + name = of_get_property(prom_stdout, "name", NULL); + if (!name) { + pr_debug(" stdout package has no name !\n"); + goto not_found; + } + + if (strcmp(name, "ch-a") == 0) + offset = 0; + else if (strcmp(name, "ch-b") == 0) + offset = 1; + else + goto not_found; + of_node_put(prom_stdout); + + pr_debug("Found serial console at %s%d\n", devname, offset); + + return add_preferred_console(devname, offset, NULL); + + not_found: + pr_debug("No preferred console found !\n"); + of_node_put(prom_stdout); + return -ENODEV; +} +console_initcall(check_pmac_serial_console); + +#endif /* CONFIG_SERIAL_PMACZILOG_CONSOLE */ + /* * Called very early, MMU is off, device-tree isn't unflattened */ diff --git a/arch/powerpc/platforms/powermac/udbg_scc.c b/arch/powerpc/platforms/powermac/udbg_scc.c index 47de4d3fc16..572771fd846 100644 --- a/arch/powerpc/platforms/powermac/udbg_scc.c +++ b/arch/powerpc/platforms/powermac/udbg_scc.c @@ -125,13 +125,23 @@ void udbg_scc_init(int force_scc) out_8(sccc, 0xc0); /* If SCC was the OF output port, read the BRG value, else - * Setup for 57600 8N1 + * Setup for 38400 or 57600 8N1 depending on the machine */ if (ch_def != NULL) { out_8(sccc, 13); scc_inittab[1] = in_8(sccc); out_8(sccc, 12); scc_inittab[3] = in_8(sccc); + } else if (machine_is_compatible("RackMac1,1") + || machine_is_compatible("RackMac1,2") + || machine_is_compatible("MacRISC4")) { + /* Xserves and G5s default to 57600 */ + scc_inittab[1] = 0; + scc_inittab[3] = 0; + } else { + /* Others default to 38400 */ + scc_inittab[1] = 0; + scc_inittab[3] = 1; } for (i = 0; i < sizeof(scc_inittab); ++i) diff --git a/arch/powerpc/platforms/ps3/Kconfig b/arch/powerpc/platforms/ps3/Kconfig index a5f4e95dfc3..920cf7a454b 100644 --- a/arch/powerpc/platforms/ps3/Kconfig +++ b/arch/powerpc/platforms/ps3/Kconfig @@ -8,6 +8,7 @@ config PPC_PS3 select USB_ARCH_HAS_EHCI select USB_EHCI_BIG_ENDIAN_MMIO select MEMORY_HOTPLUG + select PPC_PCI_CHOICE help This option enables support for the Sony PS3 game console and other platforms using the PS3 hypervisor. Enabling this diff --git a/arch/powerpc/platforms/ps3/device-init.c b/arch/powerpc/platforms/ps3/device-init.c index 3866debfa3c..ffdd8e963fb 100644 --- a/arch/powerpc/platforms/ps3/device-init.c +++ b/arch/powerpc/platforms/ps3/device-init.c @@ -486,6 +486,7 @@ static int __init ps3_register_graphics_devices(void) return -ENOMEM; p->dev.match_id = PS3_MATCH_ID_GRAPHICS; + p->dev.match_sub_id = PS3_MATCH_SUB_ID_FB; p->dev.dev_type = PS3_DEVICE_TYPE_IOC0; result = ps3_system_bus_device_register(&p->dev); diff --git a/arch/powerpc/platforms/ps3/htab.c b/arch/powerpc/platforms/ps3/htab.c index 1cf901fa903..6eb1d4d182c 100644 --- a/arch/powerpc/platforms/ps3/htab.c +++ b/arch/powerpc/platforms/ps3/htab.c @@ -29,138 +29,75 @@ #include "platform.h" -#if defined(DEBUG) -#define DBG udbg_printf -#else -#define DBG pr_debug -#endif - -static struct hash_pte *htab; -static unsigned long htab_addr; -static unsigned char *bolttab; -static unsigned char *inusetab; - -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, 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, - ps3_mm_phys_to_lpar(pa)); - DBG("%s:%d: va = %lxh\n", func, line, va); - DBG("%s:%d: group = %lxh\n", func, line, group); - DBG("%s:%d: bitmap = %lxh\n", func, line, bitmap); - DBG("%s:%d: hpte.v = %lxh\n", func, line, lhpte.v); - DBG("%s:%d: hpte.r = %lxh\n", func, line, lhpte.r); - DBG("%s:%d: psize = %xh\n", func, line, psize); - DBG("%s:%d: slot = %lxh\n", func, line, slot); -} +/** + * enum lpar_vas_id - id of LPAR virtual address space. + * @lpar_vas_id_current: Current selected virtual address space + * + * Identify the target LPAR address space. + */ + +enum ps3_lpar_vas_id { + PS3_LPAR_VAS_ID_CURRENT = 0, +}; + + +static DEFINE_SPINLOCK(ps3_htab_lock); static long ps3_hpte_insert(unsigned long hpte_group, unsigned long va, unsigned long pa, unsigned long rflags, unsigned long vflags, int psize, int ssize) { - unsigned long slot; - struct hash_pte lhpte; - int secondary = 0; - unsigned long result; - unsigned long bitmap; + int result; + u64 hpte_v, hpte_r; + u64 inserted_index; + u64 evicted_v, evicted_r; + u64 hpte_v_array[4], hpte_rs; unsigned long flags; - unsigned long p_pteg, s_pteg, b_index, b_mask, cb, ci; - - vflags &= ~HPTE_V_SECONDARY; /* this bit is ignored */ - - lhpte.v = hpte_encode_v(va, psize, MMU_SEGSIZE_256M) | - vflags | HPTE_V_VALID; - lhpte.r = hpte_encode_r(ps3_mm_phys_to_lpar(pa), psize) | rflags; - - p_pteg = hpte_group / HPTES_PER_GROUP; - s_pteg = ~p_pteg & htab_hash_mask; - - spin_lock_irqsave(&ps3_bolttab_lock, flags); - - BUG_ON(bolttab[p_pteg] == 0xff && bolttab[s_pteg] == 0xff); + long ret = -1; - bitmap = (inusetab[p_pteg] << 8) | inusetab[s_pteg]; + /* + * lv1_insert_htab_entry() will search for victim + * entry in both primary and secondary pte group + */ + vflags &= ~HPTE_V_SECONDARY; - if (bitmap == 0xffff) { - /* - * PTEG is full. Search for victim. - */ - bitmap &= ~((bolttab[p_pteg] << 8) | bolttab[s_pteg]); - do { - ci = mftb() & 15; - cb = 0x8000UL >> ci; - } while ((cb & bitmap) == 0); - } else { - /* - * search free slot in hardware order - * [primary] 0, 2, 4, 6, 1, 3, 5, 7 - * [secondary] 0, 2, 4, 6, 1, 3, 5, 7 - */ - for (ci = 0; ci < HPTES_PER_GROUP; ci += 2) { - cb = 0x8000UL >> ci; - if ((cb & bitmap) == 0) - goto found; - } - for (ci = 1; ci < HPTES_PER_GROUP; ci += 2) { - cb = 0x8000UL >> ci; - if ((cb & bitmap) == 0) - goto found; - } - for (ci = HPTES_PER_GROUP; ci < HPTES_PER_GROUP*2; ci += 2) { - cb = 0x8000UL >> ci; - if ((cb & bitmap) == 0) - goto found; - } - for (ci = HPTES_PER_GROUP+1; ci < HPTES_PER_GROUP*2; ci += 2) { - cb = 0x8000UL >> ci; - if ((cb & bitmap) == 0) - goto found; - } - } + hpte_v = hpte_encode_v(va, psize, ssize) | vflags | HPTE_V_VALID; + hpte_r = hpte_encode_r(ps3_mm_phys_to_lpar(pa), psize) | rflags; -found: - if (ci < HPTES_PER_GROUP) { - slot = p_pteg * HPTES_PER_GROUP + ci; - } else { - slot = s_pteg * HPTES_PER_GROUP + (ci & 7); - /* lhpte.dw0.dw0.h = 1; */ - vflags |= HPTE_V_SECONDARY; - lhpte.v |= HPTE_V_SECONDARY; - } + spin_lock_irqsave(&ps3_htab_lock, flags); - result = lv1_write_htab_entry(0, slot, lhpte.v, lhpte.r); + /* talk hvc to replace entries BOLTED == 0 */ + result = lv1_insert_htab_entry(PS3_LPAR_VAS_ID_CURRENT, hpte_group, + hpte_v, hpte_r, + HPTE_V_BOLTED, 0, + &inserted_index, + &evicted_v, &evicted_r); if (result) { - debug_dump_hpte(pa, va, hpte_group, bitmap, lhpte, psize, slot); + /* all entries bolted !*/ + pr_info("%s:result=%d va=%lx pa=%lx ix=%lx v=%lx r=%lx\n", + __func__, result, va, pa, hpte_group, hpte_v, hpte_r); BUG(); } /* - * If used slot is not in primary HPTE group, - * the slot should be in secondary HPTE group. + * see if the entry is inserted into secondary pteg */ + result = lv1_read_htab_entries(PS3_LPAR_VAS_ID_CURRENT, + inserted_index & ~0x3UL, + &hpte_v_array[0], &hpte_v_array[1], + &hpte_v_array[2], &hpte_v_array[3], + &hpte_rs); + BUG_ON(result); - if ((hpte_group ^ slot) & ~(HPTES_PER_GROUP - 1)) { - secondary = 1; - b_index = s_pteg; - } else { - secondary = 0; - b_index = p_pteg; - } + if (hpte_v_array[inserted_index % 4] & HPTE_V_SECONDARY) + ret = (inserted_index & 7) | (1 << 3); + else + ret = inserted_index & 7; - b_mask = (lhpte.v & HPTE_V_BOLTED) ? 1 << 7 : 0 << 7; - bolttab[b_index] |= b_mask >> (slot & 7); - b_mask = 1 << 7; - inusetab[b_index] |= b_mask >> (slot & 7); - spin_unlock_irqrestore(&ps3_bolttab_lock, flags); + spin_unlock_irqrestore(&ps3_htab_lock, flags); - return (slot & 7) | (secondary << 3); + return ret; } static long ps3_hpte_remove(unsigned long hpte_group) @@ -172,39 +109,48 @@ static long ps3_hpte_remove(unsigned long hpte_group) static long ps3_hpte_updatepp(unsigned long slot, unsigned long newpp, unsigned long va, int psize, int ssize, int local) { + int result; + u64 hpte_v, want_v, hpte_rs; + u64 hpte_v_array[4]; unsigned long flags; - unsigned long result; - unsigned long pteg, bit; - unsigned long hpte_v, want_v; + long ret; - want_v = hpte_encode_v(va, psize, MMU_SEGSIZE_256M); + want_v = hpte_encode_v(va, psize, ssize); - spin_lock_irqsave(&ps3_bolttab_lock, flags); + spin_lock_irqsave(&ps3_htab_lock, flags); - hpte_v = htab[slot].v; - if (!HPTE_V_COMPARE(hpte_v, want_v) || !(hpte_v & HPTE_V_VALID)) { - spin_unlock_irqrestore(&ps3_bolttab_lock, flags); - - /* ps3_hpte_insert() will be used to update PTE */ - return -1; - } - - result = lv1_write_htab_entry(0, slot, 0, 0); + result = lv1_read_htab_entries(PS3_LPAR_VAS_ID_CURRENT, slot & ~0x3UL, + &hpte_v_array[0], &hpte_v_array[1], + &hpte_v_array[2], &hpte_v_array[3], + &hpte_rs); if (result) { - DBG("%s: va=%lx slot=%lx psize=%d result = %ld (0x%lx)\n", - __func__, va, slot, psize, result, result); + pr_info("%s: res=%d read va=%lx slot=%lx psize=%d\n", + __func__, result, va, slot, psize); BUG(); } - pteg = slot / HPTES_PER_GROUP; - bit = slot % HPTES_PER_GROUP; - inusetab[pteg] &= ~(0x80 >> bit); + hpte_v = hpte_v_array[slot % 4]; - spin_unlock_irqrestore(&ps3_bolttab_lock, flags); + /* + * As lv1_read_htab_entries() does not give us the RPN, we can + * not synthesize the new hpte_r value here, and therefore can + * not update the hpte with lv1_insert_htab_entry(), so we + * insted invalidate it and ask the caller to update it via + * ps3_hpte_insert() by returning a -1 value. + */ + if (!HPTE_V_COMPARE(hpte_v, want_v) || !(hpte_v & HPTE_V_VALID)) { + /* not found */ + ret = -1; + } else { + /* entry found, just invalidate it */ + result = lv1_write_htab_entry(PS3_LPAR_VAS_ID_CURRENT, + slot, 0, 0); + ret = -1; + } - /* ps3_hpte_insert() will be used to update PTE */ - return -1; + spin_unlock_irqrestore(&ps3_htab_lock, flags); + return ret; } static void ps3_hpte_updateboltedpp(unsigned long newpp, unsigned long ea, @@ -217,45 +163,35 @@ static void ps3_hpte_invalidate(unsigned long slot, unsigned long va, int psize, int ssize, int local) { unsigned long flags; - unsigned long result; - unsigned long pteg, bit; + int result; + + spin_lock_irqsave(&ps3_htab_lock, flags); - spin_lock_irqsave(&ps3_bolttab_lock, flags); - result = lv1_write_htab_entry(0, slot, 0, 0); + result = lv1_write_htab_entry(PS3_LPAR_VAS_ID_CURRENT, slot, 0, 0); if (result) { - DBG("%s: va=%lx slot=%lx psize=%d result = %ld (0x%lx)\n", - __func__, va, slot, psize, result, result); + pr_info("%s: res=%d va=%lx slot=%lx psize=%d\n", + __func__, result, va, slot, psize); BUG(); } - pteg = slot / HPTES_PER_GROUP; - bit = slot % HPTES_PER_GROUP; - inusetab[pteg] &= ~(0x80 >> bit); - spin_unlock_irqrestore(&ps3_bolttab_lock, flags); + spin_unlock_irqrestore(&ps3_htab_lock, flags); } static void ps3_hpte_clear(void) { - int result; - - DBG(" -> %s:%d\n", __func__, __LINE__); + unsigned long hpte_count = (1UL << ppc64_pft_size) >> 4; + u64 i; - result = lv1_unmap_htab(htab_addr); - BUG_ON(result); + for (i = 0; i < hpte_count; i++) + lv1_write_htab_entry(PS3_LPAR_VAS_ID_CURRENT, i, 0, 0); ps3_mm_shutdown(); ps3_mm_vas_destroy(); - - DBG(" <- %s:%d\n", __func__, __LINE__); } void __init ps3_hpte_init(unsigned long htab_size) { - long bitmap_size; - - DBG(" -> %s:%d\n", __func__, __LINE__); - ppc_md.hpte_invalidate = ps3_hpte_invalidate; ppc_md.hpte_updatepp = ps3_hpte_updatepp; ppc_md.hpte_updateboltedpp = ps3_hpte_updateboltedpp; @@ -264,28 +200,5 @@ void __init ps3_hpte_init(unsigned long htab_size) ppc_md.hpte_clear_all = ps3_hpte_clear; ppc64_pft_size = __ilog2(htab_size); - - bitmap_size = htab_size / sizeof(struct hash_pte) / 8; - - bolttab = __va(lmb_alloc(bitmap_size, 1)); - inusetab = __va(lmb_alloc(bitmap_size, 1)); - - memset(bolttab, 0, bitmap_size); - memset(inusetab, 0, bitmap_size); - - DBG(" <- %s:%d\n", __func__, __LINE__); } -void __init ps3_map_htab(void) -{ - long result; - unsigned long htab_size = (1UL << ppc64_pft_size); - - result = lv1_map_htab(0, &htab_addr); - - 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/setup.c b/arch/powerpc/platforms/ps3/setup.c index a413abbd412..77bc330263c 100644 --- a/arch/powerpc/platforms/ps3/setup.c +++ b/arch/powerpc/platforms/ps3/setup.c @@ -201,7 +201,6 @@ static void __init ps3_setup_arch(void) ps3_firmware_version.rev); ps3_spu_set_platform(); - ps3_map_htab(); #ifdef CONFIG_SMP smp_init_ps3(); diff --git a/arch/powerpc/platforms/ps3/spu.c b/arch/powerpc/platforms/ps3/spu.c index d135cef9ed6..ccae3d446b9 100644 --- a/arch/powerpc/platforms/ps3/spu.c +++ b/arch/powerpc/platforms/ps3/spu.c @@ -186,14 +186,24 @@ static void spu_unmap(struct spu *spu) iounmap(spu_pdata(spu)->shadow); } +/** + * setup_areas - Map the spu regions into the address space. + * + * The current HV requires the spu shadow regs to be mapped with the + * PTE page protection bits set as read-only (PP=3). This implementation + * uses the low level __ioremap() to bypass the page protection settings + * inforced by ioremap_flags() to get the needed PTE bits set for the + * shadow regs. + */ + static int __init setup_areas(struct spu *spu) { struct table {char* name; unsigned long addr; unsigned long size;}; + static const unsigned long shadow_flags = _PAGE_NO_CACHE | 3; - spu_pdata(spu)->shadow = ioremap_flags(spu_pdata(spu)->shadow_addr, - sizeof(struct spe_shadow), - pgprot_val(PAGE_READONLY) | - _PAGE_NO_CACHE); + spu_pdata(spu)->shadow = __ioremap(spu_pdata(spu)->shadow_addr, + sizeof(struct spe_shadow), + shadow_flags); if (!spu_pdata(spu)->shadow) { pr_debug("%s:%d: ioremap shadow failed\n", __func__, __LINE__); goto fail_ioremap; diff --git a/arch/powerpc/platforms/ps3/system-bus.c b/arch/powerpc/platforms/ps3/system-bus.c index d66c3628a11..280ee88cb0b 100644 --- a/arch/powerpc/platforms/ps3/system-bus.c +++ b/arch/powerpc/platforms/ps3/system-bus.c @@ -347,16 +347,23 @@ static int ps3_system_bus_match(struct 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; + if (!dev->match_sub_id) + result = dev->match_id == drv->match_id; + else + result = dev->match_sub_id == drv->match_sub_id && + dev->match_id == drv->match_id; if (result) - pr_info("%s:%d: dev=%u(%s), drv=%u(%s): match\n", __func__, - __LINE__, dev->match_id, dev->core.bus_id, - drv->match_id, drv->core.name); + pr_info("%s:%d: dev=%u.%u(%s), drv=%u.%u(%s): match\n", + __func__, __LINE__, + dev->match_id, dev->match_sub_id, dev->core.bus_id, + drv->match_id, drv->match_sub_id, drv->core.name); else - pr_debug("%s:%d: dev=%u(%s), drv=%u(%s): miss\n", __func__, - __LINE__, dev->match_id, dev->core.bus_id, - drv->match_id, drv->core.name); + pr_debug("%s:%d: dev=%u.%u(%s), drv=%u.%u(%s): miss\n", + __func__, __LINE__, + dev->match_id, dev->match_sub_id, dev->core.bus_id, + drv->match_id, drv->match_sub_id, drv->core.name); + return result; } diff --git a/arch/powerpc/platforms/pseries/Kconfig b/arch/powerpc/platforms/pseries/Kconfig index 07fe5b69b9e..97619fd51e3 100644 --- a/arch/powerpc/platforms/pseries/Kconfig +++ b/arch/powerpc/platforms/pseries/Kconfig @@ -7,6 +7,7 @@ config PPC_PSERIES select RTAS_ERROR_LOGGING select PPC_UDBG_16550 select PPC_NATIVE + select PPC_PCI_CHOICE if EMBEDDED default y config PPC_SPLPAR @@ -39,3 +40,26 @@ config PPC_PSERIES_DEBUG depends on PPC_PSERIES && PPC_EARLY_DEBUG bool "Enable extra debug logging in platforms/pseries" default y + +config PPC_SMLPAR + bool "Support for shared-memory logical partitions" + depends on PPC_PSERIES + select LPARCFG + default n + help + Select this option to enable shared memory partition support. + With this option a system running in an LPAR can be given more + memory than physically available and will allow firmware to + balance memory across many LPARs. + +config CMM + tristate "Collaborative memory management" + depends on PPC_SMLPAR + default y + help + Select this option, if you want to enable the kernel interface + to reduce the memory size of the system. This is accomplished + by allocating pages of memory and put them "on hold". This only + makes sense for a system running in an LPAR where the unused pages + will be reused for other LPARs. The interface allows firmware to + balance memory across many LPARs. diff --git a/arch/powerpc/platforms/pseries/Makefile b/arch/powerpc/platforms/pseries/Makefile index 554c6e42ef2..dfe574af2dc 100644 --- a/arch/powerpc/platforms/pseries/Makefile +++ b/arch/powerpc/platforms/pseries/Makefile @@ -24,3 +24,4 @@ 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_CMM) += cmm.o diff --git a/arch/powerpc/platforms/pseries/cmm.c b/arch/powerpc/platforms/pseries/cmm.c new file mode 100644 index 00000000000..38fe32a7cc7 --- /dev/null +++ b/arch/powerpc/platforms/pseries/cmm.c @@ -0,0 +1,472 @@ +/* + * Collaborative memory management interface. + * + * Copyright (C) 2008 IBM Corporation + * Author(s): Brian King (brking@linux.vnet.ibm.com), + * + * 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. 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/ctype.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/init.h> +#include <linux/kthread.h> +#include <linux/module.h> +#include <linux/oom.h> +#include <linux/sched.h> +#include <linux/stringify.h> +#include <linux/swap.h> +#include <linux/sysdev.h> +#include <asm/firmware.h> +#include <asm/hvcall.h> +#include <asm/mmu.h> +#include <asm/pgalloc.h> +#include <asm/uaccess.h> + +#include "plpar_wrappers.h" + +#define CMM_DRIVER_VERSION "1.0.0" +#define CMM_DEFAULT_DELAY 1 +#define CMM_DEBUG 0 +#define CMM_DISABLE 0 +#define CMM_OOM_KB 1024 +#define CMM_MIN_MEM_MB 256 +#define KB2PAGES(_p) ((_p)>>(PAGE_SHIFT-10)) +#define PAGES2KB(_p) ((_p)<<(PAGE_SHIFT-10)) + +static unsigned int delay = CMM_DEFAULT_DELAY; +static unsigned int oom_kb = CMM_OOM_KB; +static unsigned int cmm_debug = CMM_DEBUG; +static unsigned int cmm_disabled = CMM_DISABLE; +static unsigned long min_mem_mb = CMM_MIN_MEM_MB; +static struct sys_device cmm_sysdev; + +MODULE_AUTHOR("Brian King <brking@linux.vnet.ibm.com>"); +MODULE_DESCRIPTION("IBM System p Collaborative Memory Manager"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(CMM_DRIVER_VERSION); + +module_param_named(delay, delay, uint, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(delay, "Delay (in seconds) between polls to query hypervisor paging requests. " + "[Default=" __stringify(CMM_DEFAULT_DELAY) "]"); +module_param_named(oom_kb, oom_kb, uint, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(oom_kb, "Amount of memory in kb to free on OOM. " + "[Default=" __stringify(CMM_OOM_KB) "]"); +module_param_named(min_mem_mb, min_mem_mb, ulong, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(min_mem_mb, "Minimum amount of memory (in MB) to not balloon. " + "[Default=" __stringify(CMM_MIN_MEM_MB) "]"); +module_param_named(debug, cmm_debug, uint, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Enable module debugging logging. Set to 1 to enable. " + "[Default=" __stringify(CMM_DEBUG) "]"); + +#define CMM_NR_PAGES ((PAGE_SIZE - sizeof(void *) - sizeof(unsigned long)) / sizeof(unsigned long)) + +#define cmm_dbg(...) if (cmm_debug) { printk(KERN_INFO "cmm: "__VA_ARGS__); } + +struct cmm_page_array { + struct cmm_page_array *next; + unsigned long index; + unsigned long page[CMM_NR_PAGES]; +}; + +static unsigned long loaned_pages; +static unsigned long loaned_pages_target; +static unsigned long oom_freed_pages; + +static struct cmm_page_array *cmm_page_list; +static DEFINE_SPINLOCK(cmm_lock); + +static struct task_struct *cmm_thread_ptr; + +/** + * cmm_alloc_pages - Allocate pages and mark them as loaned + * @nr: number of pages to allocate + * + * Return value: + * number of pages requested to be allocated which were not + **/ +static long cmm_alloc_pages(long nr) +{ + struct cmm_page_array *pa, *npa; + unsigned long addr; + long rc; + + cmm_dbg("Begin request for %ld pages\n", nr); + + while (nr) { + addr = __get_free_page(GFP_NOIO | __GFP_NOWARN | + __GFP_NORETRY | __GFP_NOMEMALLOC); + if (!addr) + break; + spin_lock(&cmm_lock); + pa = cmm_page_list; + if (!pa || pa->index >= CMM_NR_PAGES) { + /* Need a new page for the page list. */ + spin_unlock(&cmm_lock); + npa = (struct cmm_page_array *)__get_free_page(GFP_NOIO | __GFP_NOWARN | + __GFP_NORETRY | __GFP_NOMEMALLOC); + if (!npa) { + pr_info("%s: Can not allocate new page list\n", __FUNCTION__); + free_page(addr); + break; + } + spin_lock(&cmm_lock); + pa = cmm_page_list; + + if (!pa || pa->index >= CMM_NR_PAGES) { + npa->next = pa; + npa->index = 0; + pa = npa; + cmm_page_list = pa; + } else + free_page((unsigned long) npa); + } + + if ((rc = plpar_page_set_loaned(__pa(addr)))) { + pr_err("%s: Can not set page to loaned. rc=%ld\n", __FUNCTION__, rc); + spin_unlock(&cmm_lock); + free_page(addr); + break; + } + + pa->page[pa->index++] = addr; + loaned_pages++; + totalram_pages--; + spin_unlock(&cmm_lock); + nr--; + } + + cmm_dbg("End request with %ld pages unfulfilled\n", nr); + return nr; +} + +/** + * cmm_free_pages - Free pages and mark them as active + * @nr: number of pages to free + * + * Return value: + * number of pages requested to be freed which were not + **/ +static long cmm_free_pages(long nr) +{ + struct cmm_page_array *pa; + unsigned long addr; + + cmm_dbg("Begin free of %ld pages.\n", nr); + spin_lock(&cmm_lock); + pa = cmm_page_list; + while (nr) { + if (!pa || pa->index <= 0) + break; + addr = pa->page[--pa->index]; + + if (pa->index == 0) { + pa = pa->next; + free_page((unsigned long) cmm_page_list); + cmm_page_list = pa; + } + + plpar_page_set_active(__pa(addr)); + free_page(addr); + loaned_pages--; + nr--; + totalram_pages++; + } + spin_unlock(&cmm_lock); + cmm_dbg("End request with %ld pages unfulfilled\n", nr); + return nr; +} + +/** + * cmm_oom_notify - OOM notifier + * @self: notifier block struct + * @dummy: not used + * @parm: returned - number of pages freed + * + * Return value: + * NOTIFY_OK + **/ +static int cmm_oom_notify(struct notifier_block *self, + unsigned long dummy, void *parm) +{ + unsigned long *freed = parm; + long nr = KB2PAGES(oom_kb); + + cmm_dbg("OOM processing started\n"); + nr = cmm_free_pages(nr); + loaned_pages_target = loaned_pages; + *freed += KB2PAGES(oom_kb) - nr; + oom_freed_pages += KB2PAGES(oom_kb) - nr; + cmm_dbg("OOM processing complete\n"); + return NOTIFY_OK; +} + +/** + * cmm_get_mpp - Read memory performance parameters + * + * Makes hcall to query the current page loan request from the hypervisor. + * + * Return value: + * nothing + **/ +static void cmm_get_mpp(void) +{ + int rc; + struct hvcall_mpp_data mpp_data; + unsigned long active_pages_target; + signed long page_loan_request; + + rc = h_get_mpp(&mpp_data); + + if (rc != H_SUCCESS) + return; + + page_loan_request = div_s64((s64)mpp_data.loan_request, PAGE_SIZE); + loaned_pages_target = page_loan_request + loaned_pages; + if (loaned_pages_target > oom_freed_pages) + loaned_pages_target -= oom_freed_pages; + else + loaned_pages_target = 0; + + active_pages_target = totalram_pages + loaned_pages - loaned_pages_target; + + if ((min_mem_mb * 1024 * 1024) > (active_pages_target * PAGE_SIZE)) + loaned_pages_target = totalram_pages + loaned_pages - + ((min_mem_mb * 1024 * 1024) / PAGE_SIZE); + + cmm_dbg("delta = %ld, loaned = %lu, target = %lu, oom = %lu, totalram = %lu\n", + page_loan_request, loaned_pages, loaned_pages_target, + oom_freed_pages, totalram_pages); +} + +static struct notifier_block cmm_oom_nb = { + .notifier_call = cmm_oom_notify +}; + +/** + * cmm_thread - CMM task thread + * @dummy: not used + * + * Return value: + * 0 + **/ +static int cmm_thread(void *dummy) +{ + unsigned long timeleft; + + while (1) { + timeleft = msleep_interruptible(delay * 1000); + + if (kthread_should_stop() || timeleft) { + loaned_pages_target = loaned_pages; + break; + } + + cmm_get_mpp(); + + if (loaned_pages_target > loaned_pages) { + if (cmm_alloc_pages(loaned_pages_target - loaned_pages)) + loaned_pages_target = loaned_pages; + } else if (loaned_pages_target < loaned_pages) + cmm_free_pages(loaned_pages - loaned_pages_target); + } + return 0; +} + +#define CMM_SHOW(name, format, args...) \ + static ssize_t show_##name(struct sys_device *dev, \ + struct sysdev_attribute *attr, \ + char *buf) \ + { \ + return sprintf(buf, format, ##args); \ + } \ + static SYSDEV_ATTR(name, S_IRUGO, show_##name, NULL) + +CMM_SHOW(loaned_kb, "%lu\n", PAGES2KB(loaned_pages)); +CMM_SHOW(loaned_target_kb, "%lu\n", PAGES2KB(loaned_pages_target)); + +static ssize_t show_oom_pages(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) +{ + return sprintf(buf, "%lu\n", PAGES2KB(oom_freed_pages)); +} + +static ssize_t store_oom_pages(struct sys_device *dev, + struct sysdev_attribute *attr, + const char *buf, size_t count) +{ + unsigned long val = simple_strtoul (buf, NULL, 10); + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (val != 0) + return -EBADMSG; + + oom_freed_pages = 0; + return count; +} + +static SYSDEV_ATTR(oom_freed_kb, S_IWUSR| S_IRUGO, + show_oom_pages, store_oom_pages); + +static struct sysdev_attribute *cmm_attrs[] = { + &attr_loaned_kb, + &attr_loaned_target_kb, + &attr_oom_freed_kb, +}; + +static struct sysdev_class cmm_sysdev_class = { + .name = "cmm", +}; + +/** + * cmm_sysfs_register - Register with sysfs + * + * Return value: + * 0 on success / other on failure + **/ +static int cmm_sysfs_register(struct sys_device *sysdev) +{ + int i, rc; + + if ((rc = sysdev_class_register(&cmm_sysdev_class))) + return rc; + + sysdev->id = 0; + sysdev->cls = &cmm_sysdev_class; + + if ((rc = sysdev_register(sysdev))) + goto class_unregister; + + for (i = 0; i < ARRAY_SIZE(cmm_attrs); i++) { + if ((rc = sysdev_create_file(sysdev, cmm_attrs[i]))) + goto fail; + } + + return 0; + +fail: + while (--i >= 0) + sysdev_remove_file(sysdev, cmm_attrs[i]); + sysdev_unregister(sysdev); +class_unregister: + sysdev_class_unregister(&cmm_sysdev_class); + return rc; +} + +/** + * cmm_unregister_sysfs - Unregister from sysfs + * + **/ +static void cmm_unregister_sysfs(struct sys_device *sysdev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(cmm_attrs); i++) + sysdev_remove_file(sysdev, cmm_attrs[i]); + sysdev_unregister(sysdev); + sysdev_class_unregister(&cmm_sysdev_class); +} + +/** + * cmm_init - Module initialization + * + * Return value: + * 0 on success / other on failure + **/ +static int cmm_init(void) +{ + int rc = -ENOMEM; + + if (!firmware_has_feature(FW_FEATURE_CMO)) + return -EOPNOTSUPP; + + if ((rc = register_oom_notifier(&cmm_oom_nb)) < 0) + return rc; + + if ((rc = cmm_sysfs_register(&cmm_sysdev))) + goto out_oom_notifier; + + if (cmm_disabled) + return rc; + + cmm_thread_ptr = kthread_run(cmm_thread, NULL, "cmmthread"); + if (IS_ERR(cmm_thread_ptr)) { + rc = PTR_ERR(cmm_thread_ptr); + goto out_unregister_sysfs; + } + + return rc; + +out_unregister_sysfs: + cmm_unregister_sysfs(&cmm_sysdev); +out_oom_notifier: + unregister_oom_notifier(&cmm_oom_nb); + return rc; +} + +/** + * cmm_exit - Module exit + * + * Return value: + * nothing + **/ +static void cmm_exit(void) +{ + if (cmm_thread_ptr) + kthread_stop(cmm_thread_ptr); + unregister_oom_notifier(&cmm_oom_nb); + cmm_free_pages(loaned_pages); + cmm_unregister_sysfs(&cmm_sysdev); +} + +/** + * cmm_set_disable - Disable/Enable CMM + * + * Return value: + * 0 on success / other on failure + **/ +static int cmm_set_disable(const char *val, struct kernel_param *kp) +{ + int disable = simple_strtoul(val, NULL, 10); + + if (disable != 0 && disable != 1) + return -EINVAL; + + if (disable && !cmm_disabled) { + if (cmm_thread_ptr) + kthread_stop(cmm_thread_ptr); + cmm_thread_ptr = NULL; + cmm_free_pages(loaned_pages); + } else if (!disable && cmm_disabled) { + cmm_thread_ptr = kthread_run(cmm_thread, NULL, "cmmthread"); + if (IS_ERR(cmm_thread_ptr)) + return PTR_ERR(cmm_thread_ptr); + } + + cmm_disabled = disable; + return 0; +} + +module_param_call(disable, cmm_set_disable, param_get_uint, + &cmm_disabled, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(disable, "Disable CMM. Set to 1 to disable. " + "[Default=" __stringify(CMM_DISABLE) "]"); + +module_init(cmm_init); +module_exit(cmm_exit); diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c index c027f0a70a0..54816d75b57 100644 --- a/arch/powerpc/platforms/pseries/eeh.c +++ b/arch/powerpc/platforms/pseries/eeh.c @@ -75,9 +75,9 @@ */ /* If a device driver keeps reading an MMIO register in an interrupt - * handler after a slot isolation event has occurred, we assume it - * is broken and panic. This sets the threshold for how many read - * attempts we allow before panicking. + * handler after a slot isolation event, it might be broken. + * This sets the threshold for how many read attempts we allow + * before printing an error message. */ #define EEH_MAX_FAILS 2100000 @@ -470,6 +470,7 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev) unsigned long flags; struct pci_dn *pdn; int rc = 0; + const char *location; total_mmio_ffs++; @@ -509,18 +510,15 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev) rc = 1; if (pdn->eeh_mode & EEH_MODE_ISOLATED) { pdn->eeh_check_count ++; - if (pdn->eeh_check_count >= EEH_MAX_FAILS) { - printk (KERN_ERR "EEH: Device driver ignored %d bad reads, panicing\n", - pdn->eeh_check_count); + if (pdn->eeh_check_count % EEH_MAX_FAILS == 0) { + location = of_get_property(dn, "ibm,loc-code", NULL); + printk (KERN_ERR "EEH: %d reads ignored for recovering device at " + "location=%s driver=%s pci addr=%s\n", + pdn->eeh_check_count, location, + dev->driver->name, pci_name(dev)); + printk (KERN_ERR "EEH: Might be infinite loop in %s driver\n", + dev->driver->name); dump_stack(); - msleep(5000); - - /* re-read the slot reset state */ - if (read_slot_reset_state(pdn, rets) != 0) - rets[0] = -1; /* reset state unknown */ - - /* If we are here, then we hit an infinite loop. Stop. */ - panic("EEH: MMIO halt (%d) on device:%s\n", rets[0], pci_name(dev)); } goto dn_unlock; } diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c index 9a12908510f..a8c446697f9 100644 --- a/arch/powerpc/platforms/pseries/iommu.c +++ b/arch/powerpc/platforms/pseries/iommu.c @@ -48,9 +48,10 @@ #include "plpar_wrappers.h" -static void tce_build_pSeries(struct iommu_table *tbl, long index, +static int tce_build_pSeries(struct iommu_table *tbl, long index, long npages, unsigned long uaddr, - enum dma_data_direction direction) + enum dma_data_direction direction, + struct dma_attrs *attrs) { u64 proto_tce; u64 *tcep; @@ -71,6 +72,7 @@ static void tce_build_pSeries(struct iommu_table *tbl, long index, uaddr += TCE_PAGE_SIZE; tcep++; } + return 0; } @@ -93,13 +95,19 @@ static unsigned long tce_get_pseries(struct iommu_table *tbl, long index) return *tcep; } -static void tce_build_pSeriesLP(struct iommu_table *tbl, long tcenum, +static void tce_free_pSeriesLP(struct iommu_table*, long, long); +static void tce_freemulti_pSeriesLP(struct iommu_table*, long, long); + +static int tce_build_pSeriesLP(struct iommu_table *tbl, long tcenum, long npages, unsigned long uaddr, - enum dma_data_direction direction) + enum dma_data_direction direction, + struct dma_attrs *attrs) { - u64 rc; + u64 rc = 0; u64 proto_tce, tce; u64 rpn; + int ret = 0; + long tcenum_start = tcenum, npages_start = npages; rpn = (virt_to_abs(uaddr)) >> TCE_SHIFT; proto_tce = TCE_PCI_READ; @@ -110,6 +118,13 @@ static void tce_build_pSeriesLP(struct iommu_table *tbl, long tcenum, tce = proto_tce | (rpn & TCE_RPN_MASK) << TCE_RPN_SHIFT; rc = plpar_tce_put((u64)tbl->it_index, (u64)tcenum << 12, tce); + if (unlikely(rc == H_NOT_ENOUGH_RESOURCES)) { + ret = (int)rc; + tce_free_pSeriesLP(tbl, tcenum_start, + (npages_start - (npages + 1))); + break; + } + if (rc && printk_ratelimit()) { printk("tce_build_pSeriesLP: plpar_tce_put failed. rc=%ld\n", rc); printk("\tindex = 0x%lx\n", (u64)tbl->it_index); @@ -121,23 +136,27 @@ static void tce_build_pSeriesLP(struct iommu_table *tbl, long tcenum, tcenum++; rpn++; } + return ret; } static DEFINE_PER_CPU(u64 *, tce_page) = NULL; -static void tce_buildmulti_pSeriesLP(struct iommu_table *tbl, long tcenum, +static int tce_buildmulti_pSeriesLP(struct iommu_table *tbl, long tcenum, long npages, unsigned long uaddr, - enum dma_data_direction direction) + enum dma_data_direction direction, + struct dma_attrs *attrs) { - u64 rc; + u64 rc = 0; u64 proto_tce; u64 *tcep; u64 rpn; long l, limit; + long tcenum_start = tcenum, npages_start = npages; + int ret = 0; if (npages == 1) { - tce_build_pSeriesLP(tbl, tcenum, npages, uaddr, direction); - return; + return tce_build_pSeriesLP(tbl, tcenum, npages, uaddr, + direction, attrs); } tcep = __get_cpu_var(tce_page); @@ -149,9 +168,8 @@ static void tce_buildmulti_pSeriesLP(struct iommu_table *tbl, long tcenum, tcep = (u64 *)__get_free_page(GFP_ATOMIC); /* If allocation fails, fall back to the loop implementation */ if (!tcep) { - tce_build_pSeriesLP(tbl, tcenum, npages, uaddr, - direction); - return; + return tce_build_pSeriesLP(tbl, tcenum, npages, uaddr, + direction, attrs); } __get_cpu_var(tce_page) = tcep; } @@ -183,6 +201,13 @@ static void tce_buildmulti_pSeriesLP(struct iommu_table *tbl, long tcenum, tcenum += limit; } while (npages > 0 && !rc); + if (unlikely(rc == H_NOT_ENOUGH_RESOURCES)) { + ret = (int)rc; + tce_freemulti_pSeriesLP(tbl, tcenum_start, + (npages_start - (npages + limit))); + return ret; + } + if (rc && printk_ratelimit()) { printk("tce_buildmulti_pSeriesLP: plpar_tce_put failed. rc=%ld\n", rc); printk("\tindex = 0x%lx\n", (u64)tbl->it_index); @@ -190,6 +215,7 @@ static void tce_buildmulti_pSeriesLP(struct iommu_table *tbl, long tcenum, printk("\ttce[0] val = 0x%lx\n", tcep[0]); show_stack(current, (unsigned long *)__get_SP()); } + return ret; } static void tce_free_pSeriesLP(struct iommu_table *tbl, long tcenum, long npages) diff --git a/arch/powerpc/platforms/pseries/plpar_wrappers.h b/arch/powerpc/platforms/pseries/plpar_wrappers.h index d8680b589dc..d967c1893ab 100644 --- a/arch/powerpc/platforms/pseries/plpar_wrappers.h +++ b/arch/powerpc/platforms/pseries/plpar_wrappers.h @@ -2,6 +2,7 @@ #define _PSERIES_PLPAR_WRAPPERS_H #include <asm/hvcall.h> +#include <asm/page.h> static inline long poll_pending(void) { @@ -42,6 +43,38 @@ static inline long register_slb_shadow(unsigned long cpu, unsigned long vpa) return vpa_call(0x3, cpu, vpa); } +static inline long plpar_page_set_loaned(unsigned long vpa) +{ + unsigned long cmo_page_sz = cmo_get_page_size(); + long rc = 0; + int i; + + for (i = 0; !rc && i < PAGE_SIZE; i += cmo_page_sz) + rc = plpar_hcall_norets(H_PAGE_INIT, H_PAGE_SET_LOANED, vpa + i, 0); + + for (i -= cmo_page_sz; rc && i != 0; i -= cmo_page_sz) + plpar_hcall_norets(H_PAGE_INIT, H_PAGE_SET_ACTIVE, + vpa + i - cmo_page_sz, 0); + + return rc; +} + +static inline long plpar_page_set_active(unsigned long vpa) +{ + unsigned long cmo_page_sz = cmo_get_page_size(); + long rc = 0; + int i; + + for (i = 0; !rc && i < PAGE_SIZE; i += cmo_page_sz) + rc = plpar_hcall_norets(H_PAGE_INIT, H_PAGE_SET_ACTIVE, vpa + i, 0); + + for (i -= cmo_page_sz; rc && i != 0; i -= cmo_page_sz) + plpar_hcall_norets(H_PAGE_INIT, H_PAGE_SET_LOANED, + vpa + i - cmo_page_sz, 0); + + return rc; +} + extern void vpa_init(int cpu); static inline long plpar_pte_enter(unsigned long flags, diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c index 90beb444e1d..7b01d67b4e4 100644 --- a/arch/powerpc/platforms/pseries/setup.c +++ b/arch/powerpc/platforms/pseries/setup.c @@ -68,6 +68,10 @@ #include "plpar_wrappers.h" #include "pseries.h" +int CMO_PrPSP = -1; +int CMO_SecPSP = -1; +unsigned long CMO_PageSize = (ASM_CONST(1) << IOMMU_PAGE_SHIFT); +EXPORT_SYMBOL(CMO_PageSize); int fwnmi_active; /* TRUE if an FWNMI handler is present */ @@ -314,6 +318,85 @@ static int pseries_set_xdabr(unsigned long dabr) H_DABRX_KERNEL | H_DABRX_USER); } +#define CMO_CHARACTERISTICS_TOKEN 44 +#define CMO_MAXLENGTH 1026 + +/** + * fw_cmo_feature_init - FW_FEATURE_CMO is not stored in ibm,hypertas-functions, + * handle that here. (Stolen from parse_system_parameter_string) + */ +void pSeries_cmo_feature_init(void) +{ + char *ptr, *key, *value, *end; + int call_status; + int page_order = IOMMU_PAGE_SHIFT; + + pr_debug(" -> fw_cmo_feature_init()\n"); + spin_lock(&rtas_data_buf_lock); + memset(rtas_data_buf, 0, RTAS_DATA_BUF_SIZE); + call_status = rtas_call(rtas_token("ibm,get-system-parameter"), 3, 1, + NULL, + CMO_CHARACTERISTICS_TOKEN, + __pa(rtas_data_buf), + RTAS_DATA_BUF_SIZE); + + if (call_status != 0) { + spin_unlock(&rtas_data_buf_lock); + pr_debug("CMO not available\n"); + pr_debug(" <- fw_cmo_feature_init()\n"); + return; + } + + end = rtas_data_buf + CMO_MAXLENGTH - 2; + ptr = rtas_data_buf + 2; /* step over strlen value */ + key = value = ptr; + + while (*ptr && (ptr <= end)) { + /* Separate the key and value by replacing '=' with '\0' and + * point the value at the string after the '=' + */ + if (ptr[0] == '=') { + ptr[0] = '\0'; + value = ptr + 1; + } else if (ptr[0] == '\0' || ptr[0] == ',') { + /* Terminate the string containing the key/value pair */ + ptr[0] = '\0'; + + if (key == value) { + pr_debug("Malformed key/value pair\n"); + /* Never found a '=', end processing */ + break; + } + + if (0 == strcmp(key, "CMOPageSize")) + page_order = simple_strtol(value, NULL, 10); + else if (0 == strcmp(key, "PrPSP")) + CMO_PrPSP = simple_strtol(value, NULL, 10); + else if (0 == strcmp(key, "SecPSP")) + CMO_SecPSP = simple_strtol(value, NULL, 10); + value = key = ptr + 1; + } + ptr++; + } + + /* Page size is returned as the power of 2 of the page size, + * convert to the page size in bytes before returning + */ + CMO_PageSize = 1 << page_order; + pr_debug("CMO_PageSize = %lu\n", CMO_PageSize); + + if (CMO_PrPSP != -1 || CMO_SecPSP != -1) { + pr_info("CMO enabled\n"); + pr_debug("CMO enabled, PrPSP=%d, SecPSP=%d\n", CMO_PrPSP, + CMO_SecPSP); + powerpc_firmware_features |= FW_FEATURE_CMO; + } else + pr_debug("CMO not enabled, PrPSP=%d, SecPSP=%d\n", CMO_PrPSP, + CMO_SecPSP); + spin_unlock(&rtas_data_buf_lock); + pr_debug(" <- fw_cmo_feature_init()\n"); +} + /* * Early initialization. Relocation is on but do not reference unbolted pages */ @@ -329,6 +412,7 @@ static void __init pSeries_init_early(void) else if (firmware_has_feature(FW_FEATURE_XDABR)) ppc_md.set_dabr = pseries_set_xdabr; + pSeries_cmo_feature_init(); iommu_init_early_pSeries(); pr_debug(" <- pSeries_init_early()\n"); |