diff options
137 files changed, 3722 insertions, 1206 deletions
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 525c13a..be4f99b 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -285,6 +285,10 @@ config IOMMU_VMERGE config IOMMU_HELPER def_bool PPC64 +config PPC_NEED_DMA_SYNC_OPS + def_bool y + depends on NOT_COHERENT_CACHE + config HOTPLUG_CPU bool "Support for enabling/disabling CPUs" depends on SMP && HOTPLUG && EXPERIMENTAL && (PPC_PSERIES || PPC_PMAC) diff --git a/arch/powerpc/Kconfig.debug b/arch/powerpc/Kconfig.debug index 15eb278..08f7cc0 100644 --- a/arch/powerpc/Kconfig.debug +++ b/arch/powerpc/Kconfig.debug @@ -2,6 +2,15 @@ menu "Kernel hacking" source "lib/Kconfig.debug" +config PRINT_STACK_DEPTH + int "Stack depth to print" if DEBUG_KERNEL + default 64 + help + This option allows you to set the stack depth that the kernel + prints in stack traces. This can be useful if your display is + too small and stack traces cause important information to + scroll off the screen. + config DEBUG_STACKOVERFLOW bool "Check for stack overflows" depends on DEBUG_KERNEL diff --git a/arch/powerpc/Makefile b/arch/powerpc/Makefile index 1f06670..72d17f5 100644 --- a/arch/powerpc/Makefile +++ b/arch/powerpc/Makefile @@ -107,7 +107,6 @@ KBUILD_CFLAGS += $(call cc-option,-mno-altivec) # (We use all available options to help semi-broken compilers) KBUILD_CFLAGS += $(call cc-option,-mno-spe) KBUILD_CFLAGS += $(call cc-option,-mspe=no) -KBUILD_CFLAGS += $(call cc-option,-mabi=no-spe) # Enable unit-at-a-time mode when possible. It shrinks the # kernel considerably. diff --git a/arch/powerpc/boot/dts/gef_sbc610.dts b/arch/powerpc/boot/dts/gef_sbc610.dts index e48cfa7..9708b34 100644 --- a/arch/powerpc/boot/dts/gef_sbc610.dts +++ b/arch/powerpc/boot/dts/gef_sbc610.dts @@ -98,6 +98,12 @@ interrupt-parent = <&mpic>; }; + gef_gpio: gpio@7,14000 { + #gpio-cells = <2>; + compatible = "gef,sbc610-gpio"; + reg = <0x7 0x14000 0x24>; + gpio-controller; + }; }; soc@fef00000 { @@ -119,6 +125,11 @@ interrupt-parent = <&mpic>; dfsrr; + rtc@51 { + compatible = "epson,rx8581"; + reg = <0x00000051>; + }; + eti@6b { compatible = "dallas,ds1682"; reg = <0x6b>; diff --git a/arch/powerpc/boot/dts/kuroboxHD.dts b/arch/powerpc/boot/dts/kuroboxHD.dts index 2e5a1a1..8d725d1 100644 --- a/arch/powerpc/boot/dts/kuroboxHD.dts +++ b/arch/powerpc/boot/dts/kuroboxHD.dts @@ -76,7 +76,6 @@ XXXX add flash parts, rtc, ?? interrupt-parent = <&mpic>; rtc@32 { - device_type = "rtc"; compatible = "ricoh,rs5c372a"; reg = <0x32>; }; diff --git a/arch/powerpc/boot/dts/kuroboxHG.dts b/arch/powerpc/boot/dts/kuroboxHG.dts index e4916e6..b13a11e 100644 --- a/arch/powerpc/boot/dts/kuroboxHG.dts +++ b/arch/powerpc/boot/dts/kuroboxHG.dts @@ -76,7 +76,6 @@ XXXX add flash parts, rtc, ?? interrupt-parent = <&mpic>; rtc@32 { - device_type = "rtc"; compatible = "ricoh,rs5c372a"; reg = <0x32>; }; diff --git a/arch/powerpc/boot/dts/lite5200.dts b/arch/powerpc/boot/dts/lite5200.dts index 2cf9a87..3f7a5dc 100644 --- a/arch/powerpc/boot/dts/lite5200.dts +++ b/arch/powerpc/boot/dts/lite5200.dts @@ -130,7 +130,6 @@ rtc@800 { // Real time clock compatible = "fsl,mpc5200-rtc"; - device_type = "rtc"; reg = <0x800 0x100>; interrupts = <1 5 0 1 6 0>; interrupt-parent = <&mpc5200_pic>; diff --git a/arch/powerpc/boot/dts/lite5200b.dts b/arch/powerpc/boot/dts/lite5200b.dts index 7bd5b9c..63e3bb4 100644 --- a/arch/powerpc/boot/dts/lite5200b.dts +++ b/arch/powerpc/boot/dts/lite5200b.dts @@ -130,7 +130,6 @@ rtc@800 { // Real time clock compatible = "fsl,mpc5200b-rtc","fsl,mpc5200-rtc"; - device_type = "rtc"; reg = <0x800 0x100>; interrupts = <1 5 0 1 6 0>; interrupt-parent = <&mpc5200_pic>; diff --git a/arch/powerpc/boot/dts/motionpro.dts b/arch/powerpc/boot/dts/motionpro.dts index 9e3c921..52ba6f9 100644 --- a/arch/powerpc/boot/dts/motionpro.dts +++ b/arch/powerpc/boot/dts/motionpro.dts @@ -248,7 +248,6 @@ fsl5200-clocking; rtc@68 { - device_type = "rtc"; compatible = "dallas,ds1339"; reg = <0x68>; }; diff --git a/arch/powerpc/boot/dts/mpc8315erdb.dts b/arch/powerpc/boot/dts/mpc8315erdb.dts index 6b85067..d3d3097 100644 --- a/arch/powerpc/boot/dts/mpc8315erdb.dts +++ b/arch/powerpc/boot/dts/mpc8315erdb.dts @@ -117,7 +117,6 @@ interrupt-parent = <&ipic>; dfsrr; rtc@68 { - device_type = "rtc"; compatible = "dallas,ds1339"; reg = <0x68>; }; diff --git a/arch/powerpc/boot/dts/mpc8349emitx.dts b/arch/powerpc/boot/dts/mpc8349emitx.dts index 4bdbaf4..5ba5c66 100644 --- a/arch/powerpc/boot/dts/mpc8349emitx.dts +++ b/arch/powerpc/boot/dts/mpc8349emitx.dts @@ -85,7 +85,6 @@ dfsrr; rtc@68 { - device_type = "rtc"; compatible = "dallas,ds1339"; reg = <0x68>; interrupts = <18 0x8>; diff --git a/arch/powerpc/boot/dts/mpc8349emitxgp.dts b/arch/powerpc/boot/dts/mpc8349emitxgp.dts index fa40647..fd4bbc4 100644 --- a/arch/powerpc/boot/dts/mpc8349emitxgp.dts +++ b/arch/powerpc/boot/dts/mpc8349emitxgp.dts @@ -83,7 +83,6 @@ dfsrr; rtc@68 { - device_type = "rtc"; compatible = "dallas,ds1339"; reg = <0x68>; interrupts = <18 0x8>; diff --git a/arch/powerpc/boot/dts/mpc8377_rdb.dts b/arch/powerpc/boot/dts/mpc8377_rdb.dts index 435ef3d..9fe8e4c 100644 --- a/arch/powerpc/boot/dts/mpc8377_rdb.dts +++ b/arch/powerpc/boot/dts/mpc8377_rdb.dts @@ -117,7 +117,6 @@ interrupt-parent = <&ipic>; dfsrr; rtc@68 { - device_type = "rtc"; compatible = "dallas,ds1339"; reg = <0x68>; }; diff --git a/arch/powerpc/boot/dts/mpc8378_rdb.dts b/arch/powerpc/boot/dts/mpc8378_rdb.dts index b11e68f..3a6d528 100644 --- a/arch/powerpc/boot/dts/mpc8378_rdb.dts +++ b/arch/powerpc/boot/dts/mpc8378_rdb.dts @@ -117,7 +117,6 @@ interrupt-parent = <&ipic>; dfsrr; rtc@68 { - device_type = "rtc"; compatible = "dallas,ds1339"; reg = <0x68>; }; diff --git a/arch/powerpc/boot/dts/mpc8379_rdb.dts b/arch/powerpc/boot/dts/mpc8379_rdb.dts index 337af6e..ee64def 100644 --- a/arch/powerpc/boot/dts/mpc8379_rdb.dts +++ b/arch/powerpc/boot/dts/mpc8379_rdb.dts @@ -117,7 +117,6 @@ interrupt-parent = <&ipic>; dfsrr; rtc@68 { - device_type = "rtc"; compatible = "dallas,ds1339"; reg = <0x68>; }; diff --git a/arch/powerpc/boot/dts/mpc8572ds.dts b/arch/powerpc/boot/dts/mpc8572ds.dts index 5c69b2f..a10506e 100644 --- a/arch/powerpc/boot/dts/mpc8572ds.dts +++ b/arch/powerpc/boot/dts/mpc8572ds.dts @@ -63,6 +63,119 @@ device_type = "memory"; }; + localbus@ffe05000 { + #address-cells = <2>; + #size-cells = <1>; + compatible = "fsl,mpc8572-elbc", "fsl,elbc", "simple-bus"; + reg = <0 0xffe05000 0 0x1000>; + interrupts = <19 2>; + interrupt-parent = <&mpic>; + + ranges = <0x0 0x0 0x0 0xe8000000 0x08000000 + 0x1 0x0 0x0 0xe0000000 0x08000000 + 0x2 0x0 0x0 0xffa00000 0x00040000 + 0x3 0x0 0x0 0xffdf0000 0x00008000 + 0x4 0x0 0x0 0xffa40000 0x00040000 + 0x5 0x0 0x0 0xffa80000 0x00040000 + 0x6 0x0 0x0 0xffac0000 0x00040000>; + + nor@0,0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "cfi-flash"; + reg = <0x0 0x0 0x8000000>; + bank-width = <2>; + device-width = <1>; + + ramdisk@0 { + reg = <0x0 0x03000000>; + readl-only; + }; + + diagnostic@3000000 { + reg = <0x03000000 0x00e00000>; + read-only; + }; + + dink@3e00000 { + reg = <0x03e00000 0x00200000>; + read-only; + }; + + kernel@4000000 { + reg = <0x04000000 0x00400000>; + read-only; + }; + + jffs2@4400000 { + reg = <0x04400000 0x03b00000>; + }; + + dtb@7f00000 { + reg = <0x07f00000 0x00080000>; + read-only; + }; + + u-boot@7f80000 { + reg = <0x07f80000 0x00080000>; + read-only; + }; + }; + + nand@2,0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "fsl,mpc8572-fcm-nand", + "fsl,elbc-fcm-nand"; + reg = <0x2 0x0 0x40000>; + + u-boot@0 { + reg = <0x0 0x02000000>; + read-only; + }; + + jffs2@2000000 { + reg = <0x02000000 0x10000000>; + }; + + ramdisk@12000000 { + reg = <0x12000000 0x08000000>; + read-only; + }; + + kernel@1a000000 { + reg = <0x1a000000 0x04000000>; + }; + + dtb@1e000000 { + reg = <0x1e000000 0x01000000>; + read-only; + }; + + empty@1f000000 { + reg = <0x1f000000 0x21000000>; + }; + }; + + nand@4,0 { + compatible = "fsl,mpc8572-fcm-nand", + "fsl,elbc-fcm-nand"; + reg = <0x4 0x0 0x40000>; + }; + + nand@5,0 { + compatible = "fsl,mpc8572-fcm-nand", + "fsl,elbc-fcm-nand"; + reg = <0x5 0x0 0x40000>; + }; + + nand@6,0 { + compatible = "fsl,mpc8572-fcm-nand", + "fsl,elbc-fcm-nand"; + reg = <0x6 0x0 0x40000>; + }; + }; + soc8572@ffe00000 { #address-cells = <1>; #size-cells = <1>; diff --git a/arch/powerpc/boot/dts/mpc8572ds_camp_core0.dts b/arch/powerpc/boot/dts/mpc8572ds_camp_core0.dts new file mode 100644 index 0000000..c114c4e --- /dev/null +++ b/arch/powerpc/boot/dts/mpc8572ds_camp_core0.dts @@ -0,0 +1,483 @@ +/* + * MPC8572 DS Core0 Device Tree Source in CAMP mode. + * + * In CAMP mode, each core needs to have its own dts. Only mpic and L2 cache + * can be shared, all the other devices must be assigned to one core only. + * This dts file allows core0 to have memory, l2, i2c, dma1, global-util, eth0, + * eth1, crypto, pci0, pci1. + * + * Copyright 2007, 2008 Freescale Semiconductor Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +/dts-v1/; +/ { + model = "fsl,MPC8572DS"; + compatible = "fsl,MPC8572DS", "fsl,MPC8572DS-CAMP"; + #address-cells = <1>; + #size-cells = <1>; + + aliases { + ethernet0 = &enet0; + ethernet1 = &enet1; + serial0 = &serial0; + pci0 = &pci0; + pci1 = &pci1; + }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + PowerPC,8572@0 { + device_type = "cpu"; + reg = <0x0>; + d-cache-line-size = <32>; // 32 bytes + i-cache-line-size = <32>; // 32 bytes + d-cache-size = <0x8000>; // L1, 32K + i-cache-size = <0x8000>; // L1, 32K + timebase-frequency = <0>; + bus-frequency = <0>; + clock-frequency = <0>; + next-level-cache = <&L2>; + }; + + }; + + memory { + device_type = "memory"; + reg = <0x0 0x0>; // Filled by U-Boot + }; + + soc8572@ffe00000 { + #address-cells = <1>; + #size-cells = <1>; + device_type = "soc"; + compatible = "simple-bus"; + ranges = <0x0 0xffe00000 0x100000>; + reg = <0xffe00000 0x1000>; // CCSRBAR & soc regs, remove once parse code for immrbase fixed + bus-frequency = <0>; // Filled out by uboot. + + memory-controller@2000 { + compatible = "fsl,mpc8572-memory-controller"; + reg = <0x2000 0x1000>; + interrupt-parent = <&mpic>; + interrupts = <18 2>; + }; + + memory-controller@6000 { + compatible = "fsl,mpc8572-memory-controller"; + reg = <0x6000 0x1000>; + interrupt-parent = <&mpic>; + interrupts = <18 2>; + }; + + L2: l2-cache-controller@20000 { + compatible = "fsl,mpc8572-l2-cache-controller"; + reg = <0x20000 0x1000>; + cache-line-size = <32>; // 32 bytes + cache-size = <0x80000>; // L2, 512K + interrupt-parent = <&mpic>; + interrupts = <16 2>; + }; + + i2c@3000 { + #address-cells = <1>; + #size-cells = <0>; + cell-index = <0>; + compatible = "fsl-i2c"; + reg = <0x3000 0x100>; + interrupts = <43 2>; + interrupt-parent = <&mpic>; + dfsrr; + }; + + i2c@3100 { + #address-cells = <1>; + #size-cells = <0>; + cell-index = <1>; + compatible = "fsl-i2c"; + reg = <0x3100 0x100>; + interrupts = <43 2>; + interrupt-parent = <&mpic>; + dfsrr; + }; + + dma@21300 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "fsl,mpc8572-dma", "fsl,eloplus-dma"; + reg = <0x21300 0x4>; + ranges = <0x0 0x21100 0x200>; + cell-index = <0>; + dma-channel@0 { + compatible = "fsl,mpc8572-dma-channel", + "fsl,eloplus-dma-channel"; + reg = <0x0 0x80>; + cell-index = <0>; + interrupt-parent = <&mpic>; + interrupts = <20 2>; + }; + dma-channel@80 { + compatible = "fsl,mpc8572-dma-channel", + "fsl,eloplus-dma-channel"; + reg = <0x80 0x80>; + cell-index = <1>; + interrupt-parent = <&mpic>; + interrupts = <21 2>; + }; + dma-channel@100 { + compatible = "fsl,mpc8572-dma-channel", + "fsl,eloplus-dma-channel"; + reg = <0x100 0x80>; + cell-index = <2>; + interrupt-parent = <&mpic>; + interrupts = <22 2>; + }; + dma-channel@180 { + compatible = "fsl,mpc8572-dma-channel", + "fsl,eloplus-dma-channel"; + reg = <0x180 0x80>; + cell-index = <3>; + interrupt-parent = <&mpic>; + interrupts = <23 2>; + }; + }; + + mdio@24520 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "fsl,gianfar-mdio"; + reg = <0x24520 0x20>; + + phy0: ethernet-phy@0 { + interrupt-parent = <&mpic>; + interrupts = <10 1>; + reg = <0x0>; + }; + phy1: ethernet-phy@1 { + interrupt-parent = <&mpic>; + interrupts = <10 1>; + reg = <0x1>; + }; + }; + + enet0: ethernet@24000 { + cell-index = <0>; + device_type = "network"; + model = "eTSEC"; + compatible = "gianfar"; + reg = <0x24000 0x1000>; + local-mac-address = [ 00 00 00 00 00 00 ]; + interrupts = <29 2 30 2 34 2>; + interrupt-parent = <&mpic>; + phy-handle = <&phy0>; + phy-connection-type = "rgmii-id"; + }; + + enet1: ethernet@25000 { + cell-index = <1>; + device_type = "network"; + model = "eTSEC"; + compatible = "gianfar"; + reg = <0x25000 0x1000>; + local-mac-address = [ 00 00 00 00 00 00 ]; + interrupts = <35 2 36 2 40 2>; + interrupt-parent = <&mpic>; + phy-handle = <&phy1>; + phy-connection-type = "rgmii-id"; + }; + + serial0: serial@4500 { + cell-index = <0>; + device_type = "serial"; + compatible = "ns16550"; + reg = <0x4500 0x100>; + clock-frequency = <0>; + }; + + global-utilities@e0000 { //global utilities block + compatible = "fsl,mpc8572-guts"; + reg = <0xe0000 0x1000>; + fsl,has-rstcr; + }; + + crypto@30000 { + compatible = "fsl,sec3.0", "fsl,sec2.4", "fsl,sec2.2", + "fsl,sec2.1", "fsl,sec2.0"; + reg = <0x30000 0x10000>; + interrupts = <45 2 58 2>; + interrupt-parent = <&mpic>; + fsl,num-channels = <4>; + fsl,channel-fifo-len = <24>; + fsl,exec-units-mask = <0x9fe>; + fsl,descriptor-types-mask = <0x3ab0ebf>; + }; + + mpic: pic@40000 { + interrupt-controller; + #address-cells = <0>; + #interrupt-cells = <2>; + reg = <0x40000 0x40000>; + compatible = "chrp,open-pic"; + device_type = "open-pic"; + protected-sources = < + 31 32 33 37 38 39 /* enet2 enet3 */ + 76 77 78 79 27 42 /* dma2 pci2 serial*/ + 0xe0 0xe1 0xe2 0xe3 /* msi */ + 0xe4 0xe5 0xe6 0xe7 + >; + }; + }; + + pci0: pcie@ffe08000 { + cell-index = <0>; + compatible = "fsl,mpc8548-pcie"; + device_type = "pci"; + #interrupt-cells = <1>; + #size-cells = <2>; + #address-cells = <3>; + reg = <0xffe08000 0x1000>; + bus-range = <0 255>; + ranges = <0x2000000 0x0 0x80000000 0x80000000 0x0 0x20000000 + 0x1000000 0x0 0x0 0xffc00000 0x0 0x10000>; + clock-frequency = <33333333>; + interrupt-parent = <&mpic>; + interrupts = <24 2>; + interrupt-map-mask = <0xff00 0x0 0x0 0x7>; + interrupt-map = < + /* IDSEL 0x11 func 0 - PCI slot 1 */ + 0x8800 0x0 0x0 0x1 &mpic 0x2 0x1 + 0x8800 0x0 0x0 0x2 &mpic 0x3 0x1 + 0x8800 0x0 0x0 0x3 &mpic 0x4 0x1 + 0x8800 0x0 0x0 0x4 &mpic 0x1 0x1 + + /* IDSEL 0x11 func 1 - PCI slot 1 */ + 0x8900 0x0 0x0 0x1 &mpic 0x2 0x1 + 0x8900 0x0 0x0 0x2 &mpic 0x3 0x1 + 0x8900 0x0 0x0 0x3 &mpic 0x4 0x1 + 0x8900 0x0 0x0 0x4 &mpic 0x1 0x1 + + /* IDSEL 0x11 func 2 - PCI slot 1 */ + 0x8a00 0x0 0x0 0x1 &mpic 0x2 0x1 + 0x8a00 0x0 0x0 0x2 &mpic 0x3 0x1 + 0x8a00 0x0 0x0 0x3 &mpic 0x4 0x1 + 0x8a00 0x0 0x0 0x4 &mpic 0x1 0x1 + + /* IDSEL 0x11 func 3 - PCI slot 1 */ + 0x8b00 0x0 0x0 0x1 &mpic 0x2 0x1 + 0x8b00 0x0 0x0 0x2 &mpic 0x3 0x1 + 0x8b00 0x0 0x0 0x3 &mpic 0x4 0x1 + 0x8b00 0x0 0x0 0x4 &mpic 0x1 0x1 + + /* IDSEL 0x11 func 4 - PCI slot 1 */ + 0x8c00 0x0 0x0 0x1 &mpic 0x2 0x1 + 0x8c00 0x0 0x0 0x2 &mpic 0x3 0x1 + 0x8c00 0x0 0x0 0x3 &mpic 0x4 0x1 + 0x8c00 0x0 0x0 0x4 &mpic 0x1 0x1 + + /* IDSEL 0x11 func 5 - PCI slot 1 */ + 0x8d00 0x0 0x0 0x1 &mpic 0x2 0x1 + 0x8d00 0x0 0x0 0x2 &mpic 0x3 0x1 + 0x8d00 0x0 0x0 0x3 &mpic 0x4 0x1 + 0x8d00 0x0 0x0 0x4 &mpic 0x1 0x1 + + /* IDSEL 0x11 func 6 - PCI slot 1 */ + 0x8e00 0x0 0x0 0x1 &mpic 0x2 0x1 + 0x8e00 0x0 0x0 0x2 &mpic 0x3 0x1 + 0x8e00 0x0 0x0 0x3 &mpic 0x4 0x1 + 0x8e00 0x0 0x0 0x4 &mpic 0x1 0x1 + + /* IDSEL 0x11 func 7 - PCI slot 1 */ + 0x8f00 0x0 0x0 0x1 &mpic 0x2 0x1 + 0x8f00 0x0 0x0 0x2 &mpic 0x3 0x1 + 0x8f00 0x0 0x0 0x3 &mpic 0x4 0x1 + 0x8f00 0x0 0x0 0x4 &mpic 0x1 0x1 + + /* IDSEL 0x12 func 0 - PCI slot 2 */ + 0x9000 0x0 0x0 0x1 &mpic 0x3 0x1 + 0x9000 0x0 0x0 0x2 &mpic 0x4 0x1 + 0x9000 0x0 0x0 0x3 &mpic 0x1 0x1 + 0x9000 0x0 0x0 0x4 &mpic 0x2 0x1 + + /* IDSEL 0x12 func 1 - PCI slot 2 */ + 0x9100 0x0 0x0 0x1 &mpic 0x3 0x1 + 0x9100 0x0 0x0 0x2 &mpic 0x4 0x1 + 0x9100 0x0 0x0 0x3 &mpic 0x1 0x1 + 0x9100 0x0 0x0 0x4 &mpic 0x2 0x1 + + /* IDSEL 0x12 func 2 - PCI slot 2 */ + 0x9200 0x0 0x0 0x1 &mpic 0x3 0x1 + 0x9200 0x0 0x0 0x2 &mpic 0x4 0x1 + 0x9200 0x0 0x0 0x3 &mpic 0x1 0x1 + 0x9200 0x0 0x0 0x4 &mpic 0x2 0x1 + + /* IDSEL 0x12 func 3 - PCI slot 2 */ + 0x9300 0x0 0x0 0x1 &mpic 0x3 0x1 + 0x9300 0x0 0x0 0x2 &mpic 0x4 0x1 + 0x9300 0x0 0x0 0x3 &mpic 0x1 0x1 + 0x9300 0x0 0x0 0x4 &mpic 0x2 0x1 + + /* IDSEL 0x12 func 4 - PCI slot 2 */ + 0x9400 0x0 0x0 0x1 &mpic 0x3 0x1 + 0x9400 0x0 0x0 0x2 &mpic 0x4 0x1 + 0x9400 0x0 0x0 0x3 &mpic 0x1 0x1 + 0x9400 0x0 0x0 0x4 &mpic 0x2 0x1 + + /* IDSEL 0x12 func 5 - PCI slot 2 */ + 0x9500 0x0 0x0 0x1 &mpic 0x3 0x1 + 0x9500 0x0 0x0 0x2 &mpic 0x4 0x1 + 0x9500 0x0 0x0 0x3 &mpic 0x1 0x1 + 0x9500 0x0 0x0 0x4 &mpic 0x2 0x1 + + /* IDSEL 0x12 func 6 - PCI slot 2 */ + 0x9600 0x0 0x0 0x1 &mpic 0x3 0x1 + 0x9600 0x0 0x0 0x2 &mpic 0x4 0x1 + 0x9600 0x0 0x0 0x3 &mpic 0x1 0x1 + 0x9600 0x0 0x0 0x4 &mpic 0x2 0x1 + + /* IDSEL 0x12 func 7 - PCI slot 2 */ + 0x9700 0x0 0x0 0x1 &mpic 0x3 0x1 + 0x9700 0x0 0x0 0x2 &mpic 0x4 0x1 + 0x9700 0x0 0x0 0x3 &mpic 0x1 0x1 + 0x9700 0x0 0x0 0x4 &mpic 0x2 0x1 + + // IDSEL 0x1c USB + 0xe000 0x0 0x0 0x1 &i8259 0xc 0x2 + 0xe100 0x0 0x0 0x2 &i8259 0x9 0x2 + 0xe200 0x0 0x0 0x3 &i8259 0xa 0x2 + 0xe300 0x0 0x0 0x4 &i8259 0xb 0x2 + + // IDSEL 0x1d Audio + 0xe800 0x0 0x0 0x1 &i8259 0x6 0x2 + + // IDSEL 0x1e Legacy + 0xf000 0x0 0x0 0x1 &i8259 0x7 0x2 + 0xf100 0x0 0x0 0x1 &i8259 0x7 0x2 + + // IDSEL 0x1f IDE/SATA + 0xf800 0x0 0x0 0x1 &i8259 0xe 0x2 + 0xf900 0x0 0x0 0x1 &i8259 0x5 0x2 + + >; + + pcie@0 { + reg = <0x0 0x0 0x0 0x0 0x0>; + #size-cells = <2>; + #address-cells = <3>; + device_type = "pci"; + ranges = <0x2000000 0x0 0x80000000 + 0x2000000 0x0 0x80000000 + 0x0 0x20000000 + + 0x1000000 0x0 0x0 + 0x1000000 0x0 0x0 + 0x0 0x100000>; + uli1575@0 { + reg = <0x0 0x0 0x0 0x0 0x0>; + #size-cells = <2>; + #address-cells = <3>; + ranges = <0x2000000 0x0 0x80000000 + 0x2000000 0x0 0x80000000 + 0x0 0x20000000 + + 0x1000000 0x0 0x0 + 0x1000000 0x0 0x0 + 0x0 0x100000>; + isa@1e { + device_type = "isa"; + #interrupt-cells = <2>; + #size-cells = <1>; + #address-cells = <2>; + reg = <0xf000 0x0 0x0 0x0 0x0>; + ranges = <0x1 0x0 0x1000000 0x0 0x0 + 0x1000>; + interrupt-parent = <&i8259>; + + i8259: interrupt-controller@20 { + reg = <0x1 0x20 0x2 + 0x1 0xa0 0x2 + 0x1 0x4d0 0x2>; + interrupt-controller; + device_type = "interrupt-controller"; + #address-cells = <0>; + #interrupt-cells = <2>; + compatible = "chrp,iic"; + interrupts = <9 2>; + interrupt-parent = <&mpic>; + }; + + i8042@60 { + #size-cells = <0>; + #address-cells = <1>; + reg = <0x1 0x60 0x1 0x1 0x64 0x1>; + interrupts = <1 3 12 3>; + interrupt-parent = + <&i8259>; + + keyboard@0 { + reg = <0x0>; + compatible = "pnpPNP,303"; + }; + + mouse@1 { + reg = <0x1>; + compatible = "pnpPNP,f03"; + }; + }; + + rtc@70 { + compatible = "pnpPNP,b00"; + reg = <0x1 0x70 0x2>; + }; + + gpio@400 { + reg = <0x1 0x400 0x80>; + }; + }; + }; + }; + + }; + + pci1: pcie@ffe09000 { + cell-index = <1>; + compatible = "fsl,mpc8548-pcie"; + device_type = "pci"; + #interrupt-cells = <1>; + #size-cells = <2>; + #address-cells = <3>; + reg = <0xffe09000 0x1000>; + bus-range = <0 255>; + ranges = <0x2000000 0x0 0xa0000000 0xa0000000 0x0 0x20000000 + 0x1000000 0x0 0x0 0xffc10000 0x0 0x10000>; + clock-frequency = <33333333>; + interrupt-parent = <&mpic>; + interrupts = <26 2>; + interrupt-map-mask = <0xf800 0x0 0x0 0x7>; + interrupt-map = < + /* IDSEL 0x0 */ + 0000 0x0 0x0 0x1 &mpic 0x4 0x1 + 0000 0x0 0x0 0x2 &mpic 0x5 0x1 + 0000 0x0 0x0 0x3 &mpic 0x6 0x1 + 0000 0x0 0x0 0x4 &mpic 0x7 0x1 + >; + pcie@0 { + reg = <0x0 0x0 0x0 0x0 0x0>; + #size-cells = <2>; + #address-cells = <3>; + device_type = "pci"; + ranges = <0x2000000 0x0 0xa0000000 + 0x2000000 0x0 0xa0000000 + 0x0 0x20000000 + + 0x1000000 0x0 0x0 + 0x1000000 0x0 0x0 + 0x0 0x100000>; + }; + }; +}; diff --git a/arch/powerpc/boot/dts/mpc8572ds_camp_core1.dts b/arch/powerpc/boot/dts/mpc8572ds_camp_core1.dts new file mode 100644 index 0000000..04ecda1 --- /dev/null +++ b/arch/powerpc/boot/dts/mpc8572ds_camp_core1.dts @@ -0,0 +1,234 @@ +/* + * MPC8572 DS Core1 Device Tree Source in CAMP mode. + * + * In CAMP mode, each core needs to have its own dts. Only mpic and L2 cache + * can be shared, all the other devices must be assigned to one core only. + * This dts allows core1 to have l2, dma2, eth2, eth3, pci2, msi. + * + * Please note to add "-b 1" for core1's dts compiling. + * + * Copyright 2007, 2008 Freescale Semiconductor Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +/dts-v1/; +/ { + model = "fsl,MPC8572DS"; + compatible = "fsl,MPC8572DS", "fsl,MPC8572DS-CAMP"; + #address-cells = <1>; + #size-cells = <1>; + + aliases { + ethernet2 = &enet2; + ethernet3 = &enet3; + serial0 = &serial0; + pci2 = &pci2; + }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + PowerPC,8572@1 { + device_type = "cpu"; + reg = <0x1>; + d-cache-line-size = <32>; // 32 bytes + i-cache-line-size = <32>; // 32 bytes + d-cache-size = <0x8000>; // L1, 32K + i-cache-size = <0x8000>; // L1, 32K + timebase-frequency = <0>; + bus-frequency = <0>; + clock-frequency = <0>; + next-level-cache = <&L2>; + }; + }; + + memory { + device_type = "memory"; + reg = <0x0 0x0>; // Filled by U-Boot + }; + + soc8572@ffe00000 { + #address-cells = <1>; + #size-cells = <1>; + device_type = "soc"; + compatible = "simple-bus"; + ranges = <0x0 0xffe00000 0x100000>; + reg = <0xffe00000 0x1000>; // CCSRBAR & soc regs, remove once parse code for immrbase fixed + bus-frequency = <0>; // Filled out by uboot. + + L2: l2-cache-controller@20000 { + compatible = "fsl,mpc8572-l2-cache-controller"; + reg = <0x20000 0x1000>; + cache-line-size = <32>; // 32 bytes + cache-size = <0x80000>; // L2, 512K + interrupt-parent = <&mpic>; + }; + + dma@c300 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "fsl,mpc8572-dma", "fsl,eloplus-dma"; + reg = <0xc300 0x4>; + ranges = <0x0 0xc100 0x200>; + cell-index = <0>; + dma-channel@0 { + compatible = "fsl,mpc8572-dma-channel", + "fsl,eloplus-dma-channel"; + reg = <0x0 0x80>; + cell-index = <0>; + interrupt-parent = <&mpic>; + interrupts = <76 2>; + }; + dma-channel@80 { + compatible = "fsl,mpc8572-dma-channel", + "fsl,eloplus-dma-channel"; + reg = <0x80 0x80>; + cell-index = <1>; + interrupt-parent = <&mpic>; + interrupts = <77 2>; + }; + dma-channel@100 { + compatible = "fsl,mpc8572-dma-channel", + "fsl,eloplus-dma-channel"; + reg = <0x100 0x80>; + cell-index = <2>; + interrupt-parent = <&mpic>; + interrupts = <78 2>; + }; + dma-channel@180 { + compatible = "fsl,mpc8572-dma-channel", + "fsl,eloplus-dma-channel"; + reg = <0x180 0x80>; + cell-index = <3>; + interrupt-parent = <&mpic>; + interrupts = <79 2>; + }; + }; + + mdio@24520 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "fsl,gianfar-mdio"; + reg = <0x24520 0x20>; + + phy2: ethernet-phy@2 { + interrupt-parent = <&mpic>; + reg = <0x2>; + }; + phy3: ethernet-phy@3 { + interrupt-parent = <&mpic>; + reg = <0x3>; + }; + }; + + enet2: ethernet@26000 { + cell-index = <2>; + device_type = "network"; + model = "eTSEC"; + compatible = "gianfar"; + reg = <0x26000 0x1000>; + local-mac-address = [ 00 00 00 00 00 00 ]; + interrupts = <31 2 32 2 33 2>; + interrupt-parent = <&mpic>; + phy-handle = <&phy2>; + phy-connection-type = "rgmii-id"; + }; + + enet3: ethernet@27000 { + cell-index = <3>; + device_type = "network"; + model = "eTSEC"; + compatible = "gianfar"; + reg = <0x27000 0x1000>; + local-mac-address = [ 00 00 00 00 00 00 ]; + interrupts = <37 2 38 2 39 2>; + interrupt-parent = <&mpic>; + phy-handle = <&phy3>; + phy-connection-type = "rgmii-id"; + }; + + msi@41600 { + compatible = "fsl,mpc8572-msi", "fsl,mpic-msi"; + reg = <0x41600 0x80>; + msi-available-ranges = <0 0x100>; + interrupts = < + 0xe0 0 + 0xe1 0 + 0xe2 0 + 0xe3 0 + 0xe4 0 + 0xe5 0 + 0xe6 0 + 0xe7 0>; + interrupt-parent = <&mpic>; + }; + + serial0: serial@4600 { + cell-index = <1>; + device_type = "serial"; + compatible = "ns16550"; + reg = <0x4600 0x100>; + clock-frequency = <0>; + }; + + mpic: pic@40000 { + interrupt-controller; + #address-cells = <0>; + #interrupt-cells = <2>; + reg = <0x40000 0x40000>; + compatible = "chrp,open-pic"; + device_type = "open-pic"; + protected-sources = < + 18 16 10 42 45 58 /* MEM L2 mdio serial crypto */ + 29 30 34 35 36 40 /* enet0 enet1 */ + 24 26 20 21 22 23 /* pcie0 pcie1 dma1 */ + 43 /* i2c */ + 0x1 0x2 0x3 0x4 /* pci slot */ + 0x9 0xa 0xb 0xc /* usb */ + 0x6 0x7 0xe 0x5 /* Audio elgacy SATA */ + >; + }; + }; + + pci2: pcie@ffe0a000 { + cell-index = <2>; + compatible = "fsl,mpc8548-pcie"; + device_type = "pci"; + #interrupt-cells = <1>; + #size-cells = <2>; + #address-cells = <3>; + reg = <0xffe0a000 0x1000>; + bus-range = <0 255>; + ranges = <0x2000000 0x0 0xc0000000 0xc0000000 0x0 0x20000000 + 0x1000000 0x0 0x0 0xffc20000 0x0 0x10000>; + clock-frequency = <33333333>; + interrupt-parent = <&mpic>; + interrupts = <27 2>; + interrupt-map-mask = <0xf800 0x0 0x0 0x7>; + interrupt-map = < + /* IDSEL 0x0 */ + 0000 0x0 0x0 0x1 &mpic 0x0 0x1 + 0000 0x0 0x0 0x2 &mpic 0x1 0x1 + 0000 0x0 0x0 0x3 &mpic 0x2 0x1 + 0000 0x0 0x0 0x4 &mpic 0x3 0x1 + >; + pcie@0 { + reg = <0x0 0x0 0x0 0x0 0x0>; + #size-cells = <2>; + #address-cells = <3>; + device_type = "pci"; + ranges = <0x2000000 0x0 0xc0000000 + 0x2000000 0x0 0xc0000000 + 0x0 0x20000000 + + 0x1000000 0x0 0x0 + 0x1000000 0x0 0x0 + 0x0 0x100000>; + }; + }; +}; diff --git a/arch/powerpc/boot/dts/pcm030.dts b/arch/powerpc/boot/dts/pcm030.dts index 7c1bb95..be2c11c 100644 --- a/arch/powerpc/boot/dts/pcm030.dts +++ b/arch/powerpc/boot/dts/pcm030.dts @@ -143,7 +143,6 @@ rtc@800 { // Real time clock compatible = "fsl,mpc5200b-rtc","fsl,mpc5200-rtc"; - device_type = "rtc"; reg = <0x800 0x100>; interrupts = <0x1 0x5 0x0 0x1 0x6 0x0>; interrupt-parent = <&mpc5200_pic>; @@ -301,7 +300,6 @@ interrupt-parent = <&mpc5200_pic>; fsl5200-clocking; rtc@51 { - device_type = "rtc"; compatible = "nxp,pcf8563"; reg = <0x51>; }; diff --git a/arch/powerpc/boot/dts/tqm5200.dts b/arch/powerpc/boot/dts/tqm5200.dts index 3008bf8..906302e 100644 --- a/arch/powerpc/boot/dts/tqm5200.dts +++ b/arch/powerpc/boot/dts/tqm5200.dts @@ -181,7 +181,6 @@ fsl5200-clocking; rtc@68 { - device_type = "rtc"; compatible = "dallas,ds1307"; reg = <0x68>; }; diff --git a/arch/powerpc/boot/libfdt-wrapper.c b/arch/powerpc/boot/libfdt-wrapper.c index 9276327..bb8b9b3 100644 --- a/arch/powerpc/boot/libfdt-wrapper.c +++ b/arch/powerpc/boot/libfdt-wrapper.c @@ -185,7 +185,7 @@ void fdt_init(void *blob) /* Make sure the dt blob is the right version and so forth */ fdt = blob; - bufsize = fdt_totalsize(fdt) + 4; + bufsize = fdt_totalsize(fdt) + EXPAND_GRANULARITY; buf = malloc(bufsize); if(!buf) fatal("malloc failed. can't relocate the device tree\n\r"); diff --git a/arch/powerpc/configs/86xx/gef_sbc610_defconfig b/arch/powerpc/configs/86xx/gef_sbc610_defconfig index 07ccaf8..cd1ffa4 100644 --- a/arch/powerpc/configs/86xx/gef_sbc610_defconfig +++ b/arch/powerpc/configs/86xx/gef_sbc610_defconfig @@ -1397,8 +1397,11 @@ CONFIG_USB_STORAGE=y # CONFIG_ACCESSIBILITY is not set # CONFIG_INFINIBAND is not set # CONFIG_EDAC is not set -CONFIG_RTC_LIB=m -CONFIG_RTC_CLASS=m +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set # # RTC interfaces @@ -1424,6 +1427,7 @@ CONFIG_RTC_INTF_DEV=y # CONFIG_RTC_DRV_M41T80 is not set # CONFIG_RTC_DRV_S35390A is not set # CONFIG_RTC_DRV_FM3130 is not set +CONFIG_RTC_DRV_RX8581=y # # SPI RTC drivers diff --git a/arch/powerpc/include/asm/atomic.h b/arch/powerpc/include/asm/atomic.h index f3fc733..499be5b 100644 --- a/arch/powerpc/include/asm/atomic.h +++ b/arch/powerpc/include/asm/atomic.h @@ -111,7 +111,7 @@ static __inline__ void atomic_inc(atomic_t *v) bne- 1b" : "=&r" (t), "+m" (v->counter) : "r" (&v->counter) - : "cc"); + : "cc", "xer"); } static __inline__ int atomic_inc_return(atomic_t *v) @@ -128,7 +128,7 @@ static __inline__ int atomic_inc_return(atomic_t *v) ISYNC_ON_SMP : "=&r" (t) : "r" (&v->counter) - : "cc", "memory"); + : "cc", "xer", "memory"); return t; } @@ -155,7 +155,7 @@ static __inline__ void atomic_dec(atomic_t *v) bne- 1b" : "=&r" (t), "+m" (v->counter) : "r" (&v->counter) - : "cc"); + : "cc", "xer"); } static __inline__ int atomic_dec_return(atomic_t *v) @@ -172,7 +172,7 @@ static __inline__ int atomic_dec_return(atomic_t *v) ISYNC_ON_SMP : "=&r" (t) : "r" (&v->counter) - : "cc", "memory"); + : "cc", "xer", "memory"); return t; } @@ -346,7 +346,7 @@ static __inline__ void atomic64_inc(atomic64_t *v) bne- 1b" : "=&r" (t), "+m" (v->counter) : "r" (&v->counter) - : "cc"); + : "cc", "xer"); } static __inline__ long atomic64_inc_return(atomic64_t *v) @@ -362,7 +362,7 @@ static __inline__ long atomic64_inc_return(atomic64_t *v) ISYNC_ON_SMP : "=&r" (t) : "r" (&v->counter) - : "cc", "memory"); + : "cc", "xer", "memory"); return t; } @@ -388,7 +388,7 @@ static __inline__ void atomic64_dec(atomic64_t *v) bne- 1b" : "=&r" (t), "+m" (v->counter) : "r" (&v->counter) - : "cc"); + : "cc", "xer"); } static __inline__ long atomic64_dec_return(atomic64_t *v) @@ -404,7 +404,7 @@ static __inline__ long atomic64_dec_return(atomic64_t *v) ISYNC_ON_SMP : "=&r" (t) : "r" (&v->counter) - : "cc", "memory"); + : "cc", "xer", "memory"); return t; } @@ -431,7 +431,7 @@ static __inline__ long atomic64_dec_if_positive(atomic64_t *v) "\n\ 2:" : "=&r" (t) : "r" (&v->counter) - : "cc", "memory"); + : "cc", "xer", "memory"); return t; } diff --git a/arch/powerpc/include/asm/byteorder.h b/arch/powerpc/include/asm/byteorder.h index b377522..d5de325 100644 --- a/arch/powerpc/include/asm/byteorder.h +++ b/arch/powerpc/include/asm/byteorder.h @@ -11,6 +11,8 @@ #include <asm/types.h> #include <linux/compiler.h> +#define __BIG_ENDIAN + #ifdef __GNUC__ #ifdef __KERNEL__ @@ -21,12 +23,19 @@ static __inline__ __u16 ld_le16(const volatile __u16 *addr) __asm__ __volatile__ ("lhbrx %0,0,%1" : "=r" (val) : "r" (addr), "m" (*addr)); return val; } +#define __arch_swab16p ld_le16 static __inline__ void st_le16(volatile __u16 *addr, const __u16 val) { __asm__ __volatile__ ("sthbrx %1,0,%2" : "=m" (*addr) : "r" (val), "r" (addr)); } +static inline void __arch_swab16s(__u16 *addr) +{ + st_le16(addr, *addr); +} +#define __arch_swab16s __arch_swab16s + static __inline__ __u32 ld_le32(const volatile __u32 *addr) { __u32 val; @@ -34,13 +43,20 @@ static __inline__ __u32 ld_le32(const volatile __u32 *addr) __asm__ __volatile__ ("lwbrx %0,0,%1" : "=r" (val) : "r" (addr), "m" (*addr)); return val; } +#define __arch_swab32p ld_le32 static __inline__ void st_le32(volatile __u32 *addr, const __u32 val) { __asm__ __volatile__ ("stwbrx %1,0,%2" : "=m" (*addr) : "r" (val), "r" (addr)); } -static __inline__ __attribute_const__ __u16 ___arch__swab16(__u16 value) +static inline void __arch_swab32s(__u32 *addr) +{ + st_le32(addr, *addr); +} +#define __arch_swab32s __arch_swab32s + +static inline __attribute_const__ __u16 __arch_swab16(__u16 value) { __u16 result; @@ -49,8 +65,9 @@ static __inline__ __attribute_const__ __u16 ___arch__swab16(__u16 value) : "r" (value), "0" (value >> 8)); return result; } +#define __arch_swab16 __arch_swab16 -static __inline__ __attribute_const__ __u32 ___arch__swab32(__u32 value) +static inline __attribute_const__ __u32 __arch_swab32(__u32 value) { __u32 result; @@ -61,29 +78,16 @@ static __inline__ __attribute_const__ __u32 ___arch__swab32(__u32 value) : "r" (value), "0" (value >> 24)); return result; } - -#define __arch__swab16(x) ___arch__swab16(x) -#define __arch__swab32(x) ___arch__swab32(x) - -/* The same, but returns converted value from the location pointer by addr. */ -#define __arch__swab16p(addr) ld_le16(addr) -#define __arch__swab32p(addr) ld_le32(addr) - -/* The same, but do the conversion in situ, ie. put the value back to addr. */ -#define __arch__swab16s(addr) st_le16(addr,*addr) -#define __arch__swab32s(addr) st_le32(addr,*addr) +#define __arch_swab32 __arch_swab32 #endif /* __KERNEL__ */ -#ifndef __STRICT_ANSI__ -#define __BYTEORDER_HAS_U64__ #ifndef __powerpc64__ #define __SWAB_64_THRU_32__ #endif /* __powerpc64__ */ -#endif /* __STRICT_ANSI__ */ #endif /* __GNUC__ */ -#include <linux/byteorder/big_endian.h> +#include <linux/byteorder.h> #endif /* _ASM_POWERPC_BYTEORDER_H */ diff --git a/arch/powerpc/include/asm/cputable.h b/arch/powerpc/include/asm/cputable.h index 1e94b07..f3d9d74 100644 --- a/arch/powerpc/include/asm/cputable.h +++ b/arch/powerpc/include/asm/cputable.h @@ -163,6 +163,7 @@ extern const char *powerpc_base_platform; #define CPU_FTR_SPE ASM_CONST(0x0000000002000000) #define CPU_FTR_NEED_PAIRED_STWCX ASM_CONST(0x0000000004000000) #define CPU_FTR_LWSYNC ASM_CONST(0x0000000008000000) +#define CPU_FTR_NOEXECUTE ASM_CONST(0x0000000010000000) /* * Add the 64-bit processor unique features in the top half of the word; @@ -177,7 +178,6 @@ extern const char *powerpc_base_platform; #define CPU_FTR_SLB LONG_ASM_CONST(0x0000000100000000) #define CPU_FTR_16M_PAGE LONG_ASM_CONST(0x0000000200000000) #define CPU_FTR_TLBIEL LONG_ASM_CONST(0x0000000400000000) -#define CPU_FTR_NOEXECUTE LONG_ASM_CONST(0x0000000800000000) #define CPU_FTR_IABR LONG_ASM_CONST(0x0000002000000000) #define CPU_FTR_MMCRA LONG_ASM_CONST(0x0000004000000000) #define CPU_FTR_CTRL LONG_ASM_CONST(0x0000008000000000) @@ -194,6 +194,7 @@ extern const char *powerpc_base_platform; #define CPU_FTR_VSX LONG_ASM_CONST(0x0010000000000000) #define CPU_FTR_SAO LONG_ASM_CONST(0x0020000000000000) #define CPU_FTR_CP_USE_DCBTZ LONG_ASM_CONST(0x0040000000000000) +#define CPU_FTR_UNALIGNED_LD_STD LONG_ASM_CONST(0x0080000000000000) #ifndef __ASSEMBLY__ @@ -366,19 +367,20 @@ extern const char *powerpc_base_platform; #define CPU_FTRS_CLASSIC32 (CPU_FTR_COMMON | \ CPU_FTR_USE_TB | CPU_FTR_HPTE_TABLE) #define CPU_FTRS_8XX (CPU_FTR_USE_TB) -#define CPU_FTRS_40X (CPU_FTR_USE_TB | CPU_FTR_NODSISRALIGN) -#define CPU_FTRS_44X (CPU_FTR_USE_TB | CPU_FTR_NODSISRALIGN) +#define CPU_FTRS_40X (CPU_FTR_USE_TB | CPU_FTR_NODSISRALIGN | CPU_FTR_NOEXECUTE) +#define CPU_FTRS_44X (CPU_FTR_USE_TB | CPU_FTR_NODSISRALIGN | CPU_FTR_NOEXECUTE) #define CPU_FTRS_E200 (CPU_FTR_USE_TB | CPU_FTR_SPE_COMP | \ CPU_FTR_NODSISRALIGN | CPU_FTR_COHERENT_ICACHE | \ - CPU_FTR_UNIFIED_ID_CACHE) + CPU_FTR_UNIFIED_ID_CACHE | CPU_FTR_NOEXECUTE) #define CPU_FTRS_E500 (CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | \ - CPU_FTR_SPE_COMP | CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_NODSISRALIGN) + CPU_FTR_SPE_COMP | CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_NODSISRALIGN | \ + CPU_FTR_NOEXECUTE) #define CPU_FTRS_E500_2 (CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | \ CPU_FTR_SPE_COMP | CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_BIG_PHYS | \ - CPU_FTR_NODSISRALIGN) + CPU_FTR_NODSISRALIGN | CPU_FTR_NOEXECUTE) #define CPU_FTRS_E500MC (CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | \ CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_BIG_PHYS | CPU_FTR_NODSISRALIGN | \ - CPU_FTR_L2CSR | CPU_FTR_LWSYNC) + CPU_FTR_L2CSR | CPU_FTR_LWSYNC | CPU_FTR_NOEXECUTE) #define CPU_FTRS_GENERIC_32 (CPU_FTR_COMMON | CPU_FTR_NODSISRALIGN) /* 64-bit CPUs */ @@ -404,7 +406,7 @@ extern const char *powerpc_base_platform; CPU_FTR_MMCRA | CPU_FTR_SMT | \ CPU_FTR_COHERENT_ICACHE | CPU_FTR_LOCKLESS_TLBIE | \ CPU_FTR_PURR | CPU_FTR_SPURR | CPU_FTR_REAL_LE | \ - CPU_FTR_DSCR) + CPU_FTR_DSCR | CPU_FTR_UNALIGNED_LD_STD) #define CPU_FTRS_POWER7 (CPU_FTR_USE_TB | CPU_FTR_LWSYNC | \ CPU_FTR_HPTE_TABLE | CPU_FTR_PPCAS_ARCH_V2 | CPU_FTR_CTRL | \ CPU_FTR_MMCRA | CPU_FTR_SMT | \ @@ -415,7 +417,8 @@ extern const char *powerpc_base_platform; CPU_FTR_HPTE_TABLE | CPU_FTR_PPCAS_ARCH_V2 | CPU_FTR_CTRL | \ CPU_FTR_ALTIVEC_COMP | CPU_FTR_MMCRA | CPU_FTR_SMT | \ CPU_FTR_PAUSE_ZERO | CPU_FTR_CI_LARGE_PAGE | \ - CPU_FTR_CELL_TB_BUG | CPU_FTR_CP_USE_DCBTZ) + CPU_FTR_CELL_TB_BUG | CPU_FTR_CP_USE_DCBTZ | \ + CPU_FTR_UNALIGNED_LD_STD) #define CPU_FTRS_PA6T (CPU_FTR_USE_TB | CPU_FTR_LWSYNC | \ CPU_FTR_HPTE_TABLE | CPU_FTR_PPCAS_ARCH_V2 | \ CPU_FTR_ALTIVEC_COMP | CPU_FTR_CI_LARGE_PAGE | \ diff --git a/arch/powerpc/include/asm/device.h b/arch/powerpc/include/asm/device.h index dfd504c..7d2277c 100644 --- a/arch/powerpc/include/asm/device.h +++ b/arch/powerpc/include/asm/device.h @@ -18,4 +18,16 @@ struct dev_archdata { void *dma_data; }; +static inline void dev_archdata_set_node(struct dev_archdata *ad, + struct device_node *np) +{ + ad->of_node = np; +} + +static inline struct device_node * +dev_archdata_get_node(const struct dev_archdata *ad) +{ + return ad->of_node; +} + #endif /* _ASM_POWERPC_DEVICE_H */ diff --git a/arch/powerpc/include/asm/dma-mapping.h b/arch/powerpc/include/asm/dma-mapping.h index fddb229..9063184 100644 --- a/arch/powerpc/include/asm/dma-mapping.h +++ b/arch/powerpc/include/asm/dma-mapping.h @@ -60,12 +60,6 @@ struct dma_mapping_ops { dma_addr_t *dma_handle, gfp_t flag); void (*free_coherent)(struct device *dev, size_t size, void *vaddr, dma_addr_t dma_handle); - dma_addr_t (*map_single)(struct device *dev, void *ptr, - size_t size, enum dma_data_direction direction, - struct dma_attrs *attrs); - void (*unmap_single)(struct device *dev, dma_addr_t dma_addr, - size_t size, enum dma_data_direction direction, - struct dma_attrs *attrs); int (*map_sg)(struct device *dev, struct scatterlist *sg, int nents, enum dma_data_direction direction, struct dma_attrs *attrs); @@ -82,6 +76,22 @@ struct dma_mapping_ops { dma_addr_t dma_address, size_t size, enum dma_data_direction direction, struct dma_attrs *attrs); +#ifdef CONFIG_PPC_NEED_DMA_SYNC_OPS + void (*sync_single_range_for_cpu)(struct device *hwdev, + dma_addr_t dma_handle, unsigned long offset, + size_t size, + enum dma_data_direction direction); + void (*sync_single_range_for_device)(struct device *hwdev, + dma_addr_t dma_handle, unsigned long offset, + size_t size, + enum dma_data_direction direction); + void (*sync_sg_for_cpu)(struct device *hwdev, + struct scatterlist *sg, int nelems, + enum dma_data_direction direction); + void (*sync_sg_for_device)(struct device *hwdev, + struct scatterlist *sg, int nelems, + enum dma_data_direction direction); +#endif }; /* @@ -149,10 +159,9 @@ static inline int dma_set_mask(struct device *dev, u64 dma_mask) } /* - * TODO: map_/unmap_single will ideally go away, to be completely - * replaced by map/unmap_page. Until then, we allow dma_ops to have - * one or the other, or both by checking to see if the specific - * function requested exists; and if not, falling back on the other set. + * map_/unmap_single actually call through to map/unmap_page now that all the + * dma_mapping_ops have been converted over. We just have to get the page and + * offset to pass through to map_page */ static inline dma_addr_t dma_map_single_attrs(struct device *dev, void *cpu_addr, @@ -164,10 +173,6 @@ static inline dma_addr_t dma_map_single_attrs(struct device *dev, BUG_ON(!dma_ops); - if (dma_ops->map_single) - return dma_ops->map_single(dev, cpu_addr, size, direction, - attrs); - return dma_ops->map_page(dev, virt_to_page(cpu_addr), (unsigned long)cpu_addr % PAGE_SIZE, size, direction, attrs); @@ -183,11 +188,6 @@ static inline void dma_unmap_single_attrs(struct device *dev, BUG_ON(!dma_ops); - if (dma_ops->unmap_single) { - dma_ops->unmap_single(dev, dma_addr, size, direction, attrs); - return; - } - dma_ops->unmap_page(dev, dma_addr, size, direction, attrs); } @@ -201,12 +201,7 @@ static inline dma_addr_t dma_map_page_attrs(struct device *dev, BUG_ON(!dma_ops); - if (dma_ops->map_page) - return dma_ops->map_page(dev, page, offset, size, direction, - attrs); - - return dma_ops->map_single(dev, page_address(page) + offset, size, - direction, attrs); + return dma_ops->map_page(dev, page, offset, size, direction, attrs); } static inline void dma_unmap_page_attrs(struct device *dev, @@ -219,12 +214,7 @@ static inline void dma_unmap_page_attrs(struct device *dev, BUG_ON(!dma_ops); - if (dma_ops->unmap_page) { - dma_ops->unmap_page(dev, dma_address, size, direction, attrs); - return; - } - - dma_ops->unmap_single(dev, dma_address, size, direction, attrs); + dma_ops->unmap_page(dev, dma_address, size, direction, attrs); } static inline int dma_map_sg_attrs(struct device *dev, struct scatterlist *sg, @@ -308,48 +298,79 @@ static inline void dma_unmap_sg(struct device *dev, struct scatterlist *sg, dma_unmap_sg_attrs(dev, sg, nhwentries, direction, NULL); } +#ifdef CONFIG_PPC_NEED_DMA_SYNC_OPS static inline void dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, size_t size, enum dma_data_direction direction) { - BUG_ON(direction == DMA_NONE); - __dma_sync(bus_to_virt(dma_handle), size, direction); + struct dma_mapping_ops *dma_ops = get_dma_ops(dev); + + BUG_ON(!dma_ops); + dma_ops->sync_single_range_for_cpu(dev, dma_handle, 0, + size, direction); } static inline void dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle, size_t size, enum dma_data_direction direction) { - BUG_ON(direction == DMA_NONE); - __dma_sync(bus_to_virt(dma_handle), size, direction); + struct dma_mapping_ops *dma_ops = get_dma_ops(dev); + + BUG_ON(!dma_ops); + dma_ops->sync_single_range_for_device(dev, dma_handle, + 0, size, direction); } static inline void dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sgl, int nents, enum dma_data_direction direction) { - struct scatterlist *sg; - int i; - - BUG_ON(direction == DMA_NONE); + struct dma_mapping_ops *dma_ops = get_dma_ops(dev); - for_each_sg(sgl, sg, nents, i) - __dma_sync_page(sg_page(sg), sg->offset, sg->length, direction); + BUG_ON(!dma_ops); + dma_ops->sync_sg_for_cpu(dev, sgl, nents, direction); } static inline void dma_sync_sg_for_device(struct device *dev, struct scatterlist *sgl, int nents, enum dma_data_direction direction) { - struct scatterlist *sg; - int i; + struct dma_mapping_ops *dma_ops = get_dma_ops(dev); - BUG_ON(direction == DMA_NONE); + BUG_ON(!dma_ops); + dma_ops->sync_sg_for_device(dev, sgl, nents, direction); +} + +static inline void dma_sync_single_range_for_cpu(struct device *dev, + dma_addr_t dma_handle, unsigned long offset, size_t size, + enum dma_data_direction direction) +{ + struct dma_mapping_ops *dma_ops = get_dma_ops(dev); - for_each_sg(sgl, sg, nents, i) - __dma_sync_page(sg_page(sg), sg->offset, sg->length, direction); + BUG_ON(!dma_ops); + dma_ops->sync_single_range_for_cpu(dev, dma_handle, + offset, size, direction); } +static inline void dma_sync_single_range_for_device(struct device *dev, + dma_addr_t dma_handle, unsigned long offset, size_t size, + enum dma_data_direction direction) +{ + struct dma_mapping_ops *dma_ops = get_dma_ops(dev); + + BUG_ON(!dma_ops); + dma_ops->sync_single_range_for_device(dev, dma_handle, offset, + size, direction); +} +#else /* CONFIG_PPC_NEED_DMA_SYNC_OPS */ +#define dma_sync_single_for_cpu(d, h, s, dir) ((void)0) +#define dma_sync_single_for_device(d, h, s, dir) ((void)0) +#define dma_sync_single_range_for_cpu(d, h, o, s, dir) ((void)0) +#define dma_sync_single_range_for_device(d, h, o, s, dir) ((void)0) +#define dma_sync_sg_for_cpu(d, s, n, dir) ((void)0) +#define dma_sync_sg_for_device(d, s, n, dir) ((void)0) +#endif + static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr) { #ifdef CONFIG_PPC64 @@ -382,22 +403,6 @@ static inline int dma_get_cache_alignment(void) #endif } -static inline void dma_sync_single_range_for_cpu(struct device *dev, - dma_addr_t dma_handle, unsigned long offset, size_t size, - enum dma_data_direction direction) -{ - /* just sync everything for now */ - dma_sync_single_for_cpu(dev, dma_handle, offset + size, direction); -} - -static inline void dma_sync_single_range_for_device(struct device *dev, - dma_addr_t dma_handle, unsigned long offset, size_t size, - enum dma_data_direction direction) -{ - /* just sync everything for now */ - dma_sync_single_for_device(dev, dma_handle, offset + size, direction); -} - static inline void dma_cache_sync(struct device *dev, void *vaddr, size_t size, enum dma_data_direction direction) { diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h index b886bec..66ea9b8 100644 --- a/arch/powerpc/include/asm/eeh.h +++ b/arch/powerpc/include/asm/eeh.h @@ -17,8 +17,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef _PPC64_EEH_H -#define _PPC64_EEH_H +#ifndef _POWERPC_EEH_H +#define _POWERPC_EEH_H #ifdef __KERNEL__ #include <linux/init.h> @@ -110,6 +110,7 @@ static inline void eeh_remove_bus_device(struct pci_dev *dev) { } #define EEH_IO_ERROR_VALUE(size) (-1UL) #endif /* CONFIG_EEH */ +#ifdef CONFIG_PPC64 /* * MMIO read/write operations with EEH support. */ @@ -207,5 +208,6 @@ static inline void eeh_readsl(const volatile void __iomem *addr, void * buf, eeh_check_failure(addr, *(u32*)buf); } +#endif /* CONFIG_PPC64 */ #endif /* __KERNEL__ */ -#endif /* _PPC64_EEH_H */ +#endif /* _POWERPC_EEH_H */ diff --git a/arch/powerpc/include/asm/highmem.h b/arch/powerpc/include/asm/highmem.h index 91c5895..7dc52ec 100644 --- a/arch/powerpc/include/asm/highmem.h +++ b/arch/powerpc/include/asm/highmem.h @@ -85,7 +85,7 @@ static inline void *kmap_atomic_prot(struct page *page, enum km_type type, pgpro BUG_ON(!pte_none(*(kmap_pte-idx))); #endif __set_pte_at(&init_mm, vaddr, kmap_pte-idx, mk_pte(page, prot)); - flush_tlb_page(NULL, vaddr); + local_flush_tlb_page(vaddr); return (void*) vaddr; } @@ -113,7 +113,7 @@ static inline void kunmap_atomic(void *kvaddr, enum km_type type) * this pte without first remap it */ pte_clear(&init_mm, vaddr, kmap_pte-idx); - flush_tlb_page(NULL, vaddr); + local_flush_tlb_page(vaddr); #endif pagefault_enable(); } diff --git a/arch/powerpc/include/asm/local.h b/arch/powerpc/include/asm/local.h index 612d832..84b457a 100644 --- a/arch/powerpc/include/asm/local.h +++ b/arch/powerpc/include/asm/local.h @@ -67,7 +67,7 @@ static __inline__ long local_inc_return(local_t *l) bne- 1b" : "=&r" (t) : "r" (&(l->a.counter)) - : "cc", "memory"); + : "cc", "xer", "memory"); return t; } @@ -94,7 +94,7 @@ static __inline__ long local_dec_return(local_t *l) bne- 1b" : "=&r" (t) : "r" (&(l->a.counter)) - : "cc", "memory"); + : "cc", "xer", "memory"); return t; } diff --git a/arch/powerpc/include/asm/lppaca.h b/arch/powerpc/include/asm/lppaca.h index 2fe268b..25aaa97 100644 --- a/arch/powerpc/include/asm/lppaca.h +++ b/arch/powerpc/include/asm/lppaca.h @@ -133,7 +133,8 @@ struct lppaca { //============================================================================= // CACHE_LINE_4-5 0x0180 - 0x027F Contains PMC interrupt data //============================================================================= - u8 pmc_save_area[256]; // PMC interrupt Area x00-xFF + u32 page_ins; // CMO Hint - # page ins by OS x00-x04 + u8 pmc_save_area[252]; // PMC interrupt Area x04-xFF } __attribute__((__aligned__(0x400))); extern struct lppaca lppaca[]; diff --git a/arch/powerpc/include/asm/mmu-fsl-booke.h b/arch/powerpc/include/asm/mmu-fsl-booke.h index 925d93c..5588a41 100644 --- a/arch/powerpc/include/asm/mmu-fsl-booke.h +++ b/arch/powerpc/include/asm/mmu-fsl-booke.h @@ -40,6 +40,8 @@ #define MAS2_M 0x00000004 #define MAS2_G 0x00000002 #define MAS2_E 0x00000001 +#define MAS2_EPN_MASK(size) (~0 << (2*(size) + 10)) +#define MAS2_VAL(addr, size, flags) ((addr) & MAS2_EPN_MASK(size) | (flags)) #define MAS3_RPN 0xFFFFF000 #define MAS3_U0 0x00000200 diff --git a/arch/powerpc/include/asm/mmu_context.h b/arch/powerpc/include/asm/mmu_context.h index 6b993ef..b570209 100644 --- a/arch/powerpc/include/asm/mmu_context.h +++ b/arch/powerpc/include/asm/mmu_context.h @@ -180,6 +180,9 @@ static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next, tsk->thread.pgdir = next->pgd; + if (!cpu_isset(smp_processor_id(), next->cpu_vm_mask)) + cpu_set(smp_processor_id(), next->cpu_vm_mask); + /* No need to flush userspace segments if the mm doesnt change */ if (prev == next) return; diff --git a/arch/powerpc/include/asm/mutex.h b/arch/powerpc/include/asm/mutex.h index 458c1f7..dabc01c 100644 --- a/arch/powerpc/include/asm/mutex.h +++ b/arch/powerpc/include/asm/mutex.h @@ -1,9 +1,134 @@ /* - * Pull in the generic implementation for the mutex fastpath. + * Optimised mutex implementation of include/asm-generic/mutex-dec.h algorithm + */ +#ifndef _ASM_POWERPC_MUTEX_H +#define _ASM_POWERPC_MUTEX_H + +static inline int __mutex_cmpxchg_lock(atomic_t *v, int old, int new) +{ + int t; + + __asm__ __volatile__ ( +"1: lwarx %0,0,%1 # mutex trylock\n\ + cmpw 0,%0,%2\n\ + bne- 2f\n" + PPC405_ERR77(0,%1) +" stwcx. %3,0,%1\n\ + bne- 1b" + ISYNC_ON_SMP + "\n\ +2:" + : "=&r" (t) + : "r" (&v->counter), "r" (old), "r" (new) + : "cc", "memory"); + + return t; +} + +static inline int __mutex_dec_return_lock(atomic_t *v) +{ + int t; + + __asm__ __volatile__( +"1: lwarx %0,0,%1 # mutex lock\n\ + addic %0,%0,-1\n" + PPC405_ERR77(0,%1) +" stwcx. %0,0,%1\n\ + bne- 1b" + ISYNC_ON_SMP + : "=&r" (t) + : "r" (&v->counter) + : "cc", "memory"); + + return t; +} + +static inline int __mutex_inc_return_unlock(atomic_t *v) +{ + int t; + + __asm__ __volatile__( + LWSYNC_ON_SMP +"1: lwarx %0,0,%1 # mutex unlock\n\ + addic %0,%0,1\n" + PPC405_ERR77(0,%1) +" stwcx. %0,0,%1 \n\ + bne- 1b" + : "=&r" (t) + : "r" (&v->counter) + : "cc", "memory"); + + return t; +} + +/** + * __mutex_fastpath_lock - try to take the lock by moving the count + * from 1 to a 0 value + * @count: pointer of type atomic_t + * @fail_fn: function to call if the original value was not 1 + * + * Change the count from 1 to a value lower than 1, and call <fail_fn> if + * it wasn't 1 originally. This function MUST leave the value lower than + * 1 even when the "1" assertion wasn't true. + */ +static inline void +__mutex_fastpath_lock(atomic_t *count, void (*fail_fn)(atomic_t *)) +{ + if (unlikely(__mutex_dec_return_lock(count) < 0)) + fail_fn(count); +} + +/** + * __mutex_fastpath_lock_retval - try to take the lock by moving the count + * from 1 to a 0 value + * @count: pointer of type atomic_t + * @fail_fn: function to call if the original value was not 1 + * + * Change the count from 1 to a value lower than 1, and call <fail_fn> if + * it wasn't 1 originally. This function returns 0 if the fastpath succeeds, + * or anything the slow path function returns. + */ +static inline int +__mutex_fastpath_lock_retval(atomic_t *count, int (*fail_fn)(atomic_t *)) +{ + if (unlikely(__mutex_dec_return_lock(count) < 0)) + return fail_fn(count); + return 0; +} + +/** + * __mutex_fastpath_unlock - try to promote the count from 0 to 1 + * @count: pointer of type atomic_t + * @fail_fn: function to call if the original value was not 0 + * + * Try to promote the count from 0 to 1. If it wasn't 0, call <fail_fn>. + * In the failure case, this function is allowed to either set the value to + * 1, or to set it to a value lower than 1. + */ +static inline void +__mutex_fastpath_unlock(atomic_t *count, void (*fail_fn)(atomic_t *)) +{ + if (unlikely(__mutex_inc_return_unlock(count) <= 0)) + fail_fn(count); +} + +#define __mutex_slowpath_needs_to_unlock() 1 + +/** + * __mutex_fastpath_trylock - try to acquire the mutex, without waiting + * + * @count: pointer of type atomic_t + * @fail_fn: fallback function * - * TODO: implement optimized primitives instead, or leave the generic - * implementation in place, or pick the atomic_xchg() based generic - * implementation. (see asm-generic/mutex-xchg.h for details) + * Change the count from 1 to 0, and return 1 (success), or if the count + * was not 1, then return 0 (failure). */ +static inline int +__mutex_fastpath_trylock(atomic_t *count, int (*fail_fn)(atomic_t *)) +{ + if (likely(__mutex_cmpxchg_lock(count, 1, 0) == 1)) + return 1; + return 0; +} -#include <asm-generic/mutex-dec.h> +#endif diff --git a/arch/powerpc/include/asm/pci-bridge.h b/arch/powerpc/include/asm/pci-bridge.h index 9047af7..fa8b3b7 100644 --- a/arch/powerpc/include/asm/pci-bridge.h +++ b/arch/powerpc/include/asm/pci-bridge.h @@ -241,9 +241,6 @@ extern void pcibios_remove_pci_devices(struct pci_bus *bus); /** Discover new pci devices under this bus, and add them */ extern void pcibios_add_pci_devices(struct pci_bus *bus); -extern void pcibios_fixup_new_pci_devices(struct pci_bus *bus); - -extern int pcibios_remove_root_bus(struct pci_controller *phb); static inline struct pci_controller *pci_bus_to_host(const struct pci_bus *bus) { @@ -290,6 +287,7 @@ extern void pci_process_bridge_OF_ranges(struct pci_controller *hose, /* Allocate & free a PCI host bridge structure */ extern struct pci_controller *pcibios_alloc_controller(struct device_node *dev); extern void pcibios_free_controller(struct pci_controller *phb); +extern void pcibios_setup_phb_resources(struct pci_controller *hose); #ifdef CONFIG_PCI extern unsigned long pci_address_to_pio(phys_addr_t address); diff --git a/arch/powerpc/include/asm/pci.h b/arch/powerpc/include/asm/pci.h index 57a2a49..1c721a6 100644 --- a/arch/powerpc/include/asm/pci.h +++ b/arch/powerpc/include/asm/pci.h @@ -204,15 +204,14 @@ static inline struct resource *pcibios_select_root(struct pci_dev *pdev, return root; } -extern void pcibios_setup_new_device(struct pci_dev *dev); - extern void pcibios_claim_one_bus(struct pci_bus *b); -extern void pcibios_allocate_bus_resources(struct pci_bus *bus); +extern void pcibios_finish_adding_to_bus(struct pci_bus *bus); extern void pcibios_resource_survey(void); extern struct pci_controller *init_phb_dynamic(struct device_node *dn); +extern int remove_phb_dynamic(struct pci_controller *phb); extern struct pci_dev *of_create_pci_dev(struct device_node *node, struct pci_bus *bus, int devfn); @@ -221,6 +220,7 @@ extern void of_scan_pci_bridge(struct device_node *node, struct pci_dev *dev); extern void of_scan_bus(struct device_node *node, struct pci_bus *bus); +extern void of_rescan_bus(struct device_node *node, struct pci_bus *bus); extern int pci_read_irq_line(struct pci_dev *dev); @@ -235,9 +235,8 @@ extern void pci_resource_to_user(const struct pci_dev *dev, int bar, const struct resource *rsrc, resource_size_t *start, resource_size_t *end); -extern void pcibios_do_bus_setup(struct pci_bus *bus); -extern void pcibios_fixup_of_probed_bus(struct pci_bus *bus); - +extern void pcibios_setup_bus_devices(struct pci_bus *bus); +extern void pcibios_setup_bus_self(struct pci_bus *bus); #endif /* __KERNEL__ */ #endif /* __ASM_POWERPC_PCI_H */ diff --git a/arch/powerpc/include/asm/pgalloc-32.h b/arch/powerpc/include/asm/pgalloc-32.h index 58c0714..0815eb4 100644 --- a/arch/powerpc/include/asm/pgalloc-32.h +++ b/arch/powerpc/include/asm/pgalloc-32.h @@ -3,6 +3,8 @@ #include <linux/threads.h> +#define PTE_NONCACHE_NUM 0 /* dummy for now to share code w/ppc64 */ + extern void __bad_pte(pmd_t *pmd); extern pgd_t *pgd_alloc(struct mm_struct *mm); @@ -33,10 +35,13 @@ extern void pgd_free(struct mm_struct *mm, pgd_t *pgd); extern pte_t *pte_alloc_one_kernel(struct mm_struct *mm, unsigned long addr); extern pgtable_t pte_alloc_one(struct mm_struct *mm, unsigned long addr); -extern void pte_free_kernel(struct mm_struct *mm, pte_t *pte); -extern void pte_free(struct mm_struct *mm, pgtable_t pte); -#define __pte_free_tlb(tlb, pte) pte_free((tlb)->mm, (pte)) +static inline void pgtable_free(pgtable_free_t pgf) +{ + void *p = (void *)(pgf.val & ~PGF_CACHENUM_MASK); + + free_page((unsigned long)p); +} #define check_pgt_cache() do { } while (0) diff --git a/arch/powerpc/include/asm/pgalloc-64.h b/arch/powerpc/include/asm/pgalloc-64.h index 812a1d8..afda2bd 100644 --- a/arch/powerpc/include/asm/pgalloc-64.h +++ b/arch/powerpc/include/asm/pgalloc-64.h @@ -7,7 +7,6 @@ * 2 of the License, or (at your option) any later version. */ -#include <linux/mm.h> #include <linux/slab.h> #include <linux/cpumask.h> #include <linux/percpu.h> @@ -108,31 +107,6 @@ static inline pgtable_t pte_alloc_one(struct mm_struct *mm, return page; } -static inline void pte_free_kernel(struct mm_struct *mm, pte_t *pte) -{ - free_page((unsigned long)pte); -} - -static inline void pte_free(struct mm_struct *mm, pgtable_t ptepage) -{ - pgtable_page_dtor(ptepage); - __free_page(ptepage); -} - -#define PGF_CACHENUM_MASK 0x7 - -typedef struct pgtable_free { - unsigned long val; -} pgtable_free_t; - -static inline pgtable_free_t pgtable_free_cache(void *p, int cachenum, - unsigned long mask) -{ - BUG_ON(cachenum > PGF_CACHENUM_MASK); - - return (pgtable_free_t){.val = ((unsigned long) p & ~mask) | cachenum}; -} - static inline void pgtable_free(pgtable_free_t pgf) { void *p = (void *)(pgf.val & ~PGF_CACHENUM_MASK); @@ -144,14 +118,6 @@ static inline void pgtable_free(pgtable_free_t pgf) kmem_cache_free(pgtable_cache[cachenum], p); } -extern void pgtable_free_tlb(struct mmu_gather *tlb, pgtable_free_t pgf); - -#define __pte_free_tlb(tlb,ptepage) \ -do { \ - pgtable_page_dtor(ptepage); \ - pgtable_free_tlb(tlb, pgtable_free_cache(page_address(ptepage), \ - PTE_NONCACHE_NUM, PTE_TABLE_SIZE-1)); \ -} while (0) #define __pmd_free_tlb(tlb, pmd) \ pgtable_free_tlb(tlb, pgtable_free_cache(pmd, \ PMD_CACHE_NUM, PMD_TABLE_SIZE-1)) diff --git a/arch/powerpc/include/asm/pgalloc.h b/arch/powerpc/include/asm/pgalloc.h index b4505ed..5d84802 100644 --- a/arch/powerpc/include/asm/pgalloc.h +++ b/arch/powerpc/include/asm/pgalloc.h @@ -2,11 +2,52 @@ #define _ASM_POWERPC_PGALLOC_H #ifdef __KERNEL__ +#include <linux/mm.h> + +static inline void pte_free_kernel(struct mm_struct *mm, pte_t *pte) +{ + free_page((unsigned long)pte); +} + +static inline void pte_free(struct mm_struct *mm, pgtable_t ptepage) +{ + pgtable_page_dtor(ptepage); + __free_page(ptepage); +} + +typedef struct pgtable_free { + unsigned long val; +} pgtable_free_t; + +#define PGF_CACHENUM_MASK 0x7 + +static inline pgtable_free_t pgtable_free_cache(void *p, int cachenum, + unsigned long mask) +{ + BUG_ON(cachenum > PGF_CACHENUM_MASK); + + return (pgtable_free_t){.val = ((unsigned long) p & ~mask) | cachenum}; +} + #ifdef CONFIG_PPC64 #include <asm/pgalloc-64.h> #else #include <asm/pgalloc-32.h> #endif +extern void pgtable_free_tlb(struct mmu_gather *tlb, pgtable_free_t pgf); + +#ifdef CONFIG_SMP +#define __pte_free_tlb(tlb,ptepage) \ +do { \ + pgtable_page_dtor(ptepage); \ + pgtable_free_tlb(tlb, pgtable_free_cache(page_address(ptepage), \ + PTE_NONCACHE_NUM, PTE_TABLE_SIZE-1)); \ +} while (0) +#else +#define __pte_free_tlb(tlb, pte) pte_free((tlb)->mm, (pte)) +#endif + + #endif /* __KERNEL__ */ #endif /* _ASM_POWERPC_PGALLOC_H */ diff --git a/arch/powerpc/include/asm/processor.h b/arch/powerpc/include/asm/processor.h index 101ed87..cd7a478 100644 --- a/arch/powerpc/include/asm/processor.h +++ b/arch/powerpc/include/asm/processor.h @@ -207,6 +207,11 @@ struct thread_struct { #define INIT_SP_LIMIT \ (_ALIGN_UP(sizeof(init_thread_info), 16) + (unsigned long) &init_stack) +#ifdef CONFIG_SPE +#define SPEFSCR_INIT .spefscr = SPEFSCR_FINVE | SPEFSCR_FDBZE | SPEFSCR_FUNFE | SPEFSCR_FOVFE, +#else +#define SPEFSCR_INIT +#endif #ifdef CONFIG_PPC32 #define INIT_THREAD { \ @@ -215,6 +220,7 @@ struct thread_struct { .fs = KERNEL_DS, \ .pgdir = swapper_pg_dir, \ .fpexc_mode = MSR_FE0 | MSR_FE1, \ + SPEFSCR_INIT \ } #else #define INIT_THREAD { \ diff --git a/arch/powerpc/include/asm/ps3.h b/arch/powerpc/include/asm/ps3.h index f9e34c4..4299365 100644 --- a/arch/powerpc/include/asm/ps3.h +++ b/arch/powerpc/include/asm/ps3.h @@ -516,4 +516,7 @@ void ps3_sync_irq(int node); u32 ps3_get_hw_thread_id(int cpu); u64 ps3_get_spe_id(void *arg); +/* mutex synchronizing GPU accesses and video mode changes */ +extern struct mutex ps3_gpu_mutex; + #endif diff --git a/arch/powerpc/include/asm/ps3av.h b/arch/powerpc/include/asm/ps3av.h index 5aa22cf..cd24ac1 100644 --- a/arch/powerpc/include/asm/ps3av.h +++ b/arch/powerpc/include/asm/ps3av.h @@ -740,8 +740,4 @@ extern int ps3av_audio_mute(int); extern int ps3av_audio_mute_analog(int); extern int ps3av_dev_open(void); extern int ps3av_dev_close(void); -extern void ps3av_register_flip_ctl(void (*flip_ctl)(int on, void *data), - void *flip_data); -extern void ps3av_flip_ctl(int on); - #endif /* _ASM_POWERPC_PS3AV_H_ */ diff --git a/arch/powerpc/include/asm/sfp-machine.h b/arch/powerpc/include/asm/sfp-machine.h index ced34f1..3d9f831 100644 --- a/arch/powerpc/include/asm/sfp-machine.h +++ b/arch/powerpc/include/asm/sfp-machine.h @@ -82,7 +82,7 @@ #define _FP_MUL_MEAT_S(R,X,Y) _FP_MUL_MEAT_1_wide(_FP_WFRACBITS_S,R,X,Y,umul_ppmm) #define _FP_MUL_MEAT_D(R,X,Y) _FP_MUL_MEAT_2_wide(_FP_WFRACBITS_D,R,X,Y,umul_ppmm) -#define _FP_DIV_MEAT_S(R,X,Y) _FP_DIV_MEAT_1_udiv(S,R,X,Y) +#define _FP_DIV_MEAT_S(R,X,Y) _FP_DIV_MEAT_1_udiv_norm(S,R,X,Y) #define _FP_DIV_MEAT_D(R,X,Y) _FP_DIV_MEAT_2_udiv(D,R,X,Y) /* These macros define what NaN looks like. They're supposed to expand to @@ -97,6 +97,20 @@ #define _FP_KEEPNANFRACP 1 +#ifdef FP_EX_BOOKE_E500_SPE +#define FP_EX_INEXACT (1 << 21) +#define FP_EX_INVALID (1 << 20) +#define FP_EX_DIVZERO (1 << 19) +#define FP_EX_UNDERFLOW (1 << 18) +#define FP_EX_OVERFLOW (1 << 17) +#define FP_INHIBIT_RESULTS 0 + +#define __FPU_FPSCR (current->thread.spefscr) +#define __FPU_ENABLED_EXC \ +({ \ + (__FPU_FPSCR >> 2) & 0x1f; \ +}) +#else /* Exception flags. We use the bit positions of the appropriate bits in the FPSCR, which also correspond to the FE_* bits. This makes everything easier ;-). */ @@ -111,22 +125,6 @@ #define FP_EX_DIVZERO (1 << (31 - 5)) #define FP_EX_INEXACT (1 << (31 - 6)) -/* This macro appears to be called when both X and Y are NaNs, and - * has to choose one and copy it to R. i386 goes for the larger of the - * two, sparc64 just picks Y. I don't understand this at all so I'll - * go with sparc64 because it's shorter :-> -- PMM - */ -#define _FP_CHOOSENAN(fs, wc, R, X, Y, OP) \ - do { \ - R##_s = Y##_s; \ - _FP_FRAC_COPY_##wc(R,Y); \ - R##_c = FP_CLS_NAN; \ - } while (0) - - -#include <linux/kernel.h> -#include <linux/sched.h> - #define __FPU_FPSCR (current->thread.fpscr.val) /* We only actually write to the destination register @@ -137,6 +135,32 @@ (__FPU_FPSCR >> 3) & 0x1f; \ }) +#endif + +/* + * If one NaN is signaling and the other is not, + * we choose that one, otherwise we choose X. + */ +#define _FP_CHOOSENAN(fs, wc, R, X, Y, OP) \ + do { \ + if ((_FP_FRAC_HIGH_RAW_##fs(Y) & _FP_QNANBIT_##fs) \ + && !(_FP_FRAC_HIGH_RAW_##fs(X) & _FP_QNANBIT_##fs)) \ + { \ + R##_s = X##_s; \ + _FP_FRAC_COPY_##wc(R,X); \ + } \ + else \ + { \ + R##_s = Y##_s; \ + _FP_FRAC_COPY_##wc(R,Y); \ + } \ + R##_c = FP_CLS_NAN; \ + } while (0) + + +#include <linux/kernel.h> +#include <linux/sched.h> + #define __FPU_TRAP_P(bits) \ ((__FPU_ENABLED_EXC & (bits)) != 0) diff --git a/arch/powerpc/include/asm/smp.h b/arch/powerpc/include/asm/smp.h index 1866cec..c25f73d 100644 --- a/arch/powerpc/include/asm/smp.h +++ b/arch/powerpc/include/asm/smp.h @@ -81,6 +81,13 @@ extern int cpu_to_core_id(int cpu); #define PPC_MSG_CALL_FUNC_SINGLE 2 #define PPC_MSG_DEBUGGER_BREAK 3 +/* + * irq controllers that have dedicated ipis per message and don't + * need additional code in the action handler may use this + */ +extern int smp_request_message_ipi(int virq, int message); +extern const char *smp_ipi_name[]; + void smp_init_iSeries(void); void smp_init_pSeries(void); void smp_init_cell(void); diff --git a/arch/powerpc/include/asm/spinlock.h b/arch/powerpc/include/asm/spinlock.h index f56a843..3686436 100644 --- a/arch/powerpc/include/asm/spinlock.h +++ b/arch/powerpc/include/asm/spinlock.h @@ -277,7 +277,7 @@ static inline void __raw_read_unlock(raw_rwlock_t *rw) bne- 1b" : "=&r"(tmp) : "r"(&rw->lock) - : "cr0", "memory"); + : "cr0", "xer", "memory"); } static inline void __raw_write_unlock(raw_rwlock_t *rw) diff --git a/arch/powerpc/include/asm/synch.h b/arch/powerpc/include/asm/synch.h index 45963e8..28f6ddb 100644 --- a/arch/powerpc/include/asm/synch.h +++ b/arch/powerpc/include/asm/synch.h @@ -5,6 +5,10 @@ #include <linux/stringify.h> #include <asm/feature-fixups.h> +#if defined(__powerpc64__) || defined(CONFIG_PPC_E500MC) +#define __SUBARCH_HAS_LWSYNC +#endif + #ifndef __ASSEMBLY__ extern unsigned int __start___lwsync_fixup, __stop___lwsync_fixup; extern void do_lwsync_fixups(unsigned long value, void *fixup_start, diff --git a/arch/powerpc/include/asm/system.h b/arch/powerpc/include/asm/system.h index d6648c1..2a4be19 100644 --- a/arch/powerpc/include/asm/system.h +++ b/arch/powerpc/include/asm/system.h @@ -23,15 +23,17 @@ * read_barrier_depends() prevents data-dependent loads being reordered * across this point (nop on PPC). * - * We have to use the sync instructions for mb(), since lwsync doesn't - * order loads with respect to previous stores. Lwsync is fine for - * rmb(), though. Note that rmb() actually uses a sync on 32-bit - * architectures. + * *mb() variants without smp_ prefix must order all types of memory + * operations with one another. sync is the only instruction sufficient + * to do this. * - * For wmb(), we use sync since wmb is used in drivers to order - * stores to system memory with respect to writes to the device. - * However, smp_wmb() can be a lighter-weight lwsync or eieio barrier - * on SMP since it is only used to order updates to system memory. + * For the smp_ barriers, ordering is for cacheable memory operations + * only. We have to use the sync instruction for smp_mb(), since lwsync + * doesn't order loads with respect to previous stores. Lwsync can be + * used for smp_rmb() and smp_wmb(). + * + * However, on CPUs that don't support lwsync, lwsync actually maps to a + * heavy-weight sync, so smp_wmb() can be a lighter-weight eieio. */ #define mb() __asm__ __volatile__ ("sync" : : : "memory") #define rmb() __asm__ __volatile__ ("sync" : : : "memory") @@ -45,14 +47,14 @@ #ifdef CONFIG_SMP #ifdef __SUBARCH_HAS_LWSYNC -# define SMPWMB lwsync +# define SMPWMB LWSYNC #else # define SMPWMB eieio #endif #define smp_mb() mb() -#define smp_rmb() rmb() -#define smp_wmb() __asm__ __volatile__ (__stringify(SMPWMB) : : :"memory") +#define smp_rmb() __asm__ __volatile__ (stringify_in_c(LWSYNC) : : :"memory") +#define smp_wmb() __asm__ __volatile__ (stringify_in_c(SMPWMB) : : :"memory") #define smp_read_barrier_depends() read_barrier_depends() #else #define smp_mb() barrier() diff --git a/arch/powerpc/include/asm/time.h b/arch/powerpc/include/asm/time.h index febd581..27ccb76 100644 --- a/arch/powerpc/include/asm/time.h +++ b/arch/powerpc/include/asm/time.h @@ -48,26 +48,6 @@ extern unsigned long ppc_proc_freq; extern unsigned long ppc_tb_freq; #define DEFAULT_TB_FREQ 125000000UL -/* - * By putting all of this stuff into a single struct we - * reduce the number of cache lines touched by do_gettimeofday. - * Both by collecting all of the data in one cache line and - * by touching only one TOC entry on ppc64. - */ -struct gettimeofday_vars { - u64 tb_to_xs; - u64 stamp_xsec; - u64 tb_orig_stamp; -}; - -struct gettimeofday_struct { - unsigned long tb_ticks_per_sec; - struct gettimeofday_vars vars[2]; - struct gettimeofday_vars * volatile varp; - unsigned var_idx; - unsigned tb_to_us; -}; - struct div_result { u64 result_high; u64 result_low; diff --git a/arch/powerpc/include/asm/tlbflush.h b/arch/powerpc/include/asm/tlbflush.h index a2c6bfd..93716a9 100644 --- a/arch/powerpc/include/asm/tlbflush.h +++ b/arch/powerpc/include/asm/tlbflush.h @@ -6,6 +6,7 @@ * * - flush_tlb_mm(mm) flushes the specified mm context TLB's * - flush_tlb_page(vma, vmaddr) flushes one page + * - local_flush_tlb_page(vmaddr) flushes one page on the local processor * - flush_tlb_page_nohash(vma, vmaddr) flushes one page if SW loaded TLB * - flush_tlb_range(vma, start, end) flushes a range of pages * - flush_tlb_kernel_range(start, end) flushes a range of kernel pages @@ -44,6 +45,11 @@ static inline void flush_tlb_mm(struct mm_struct *mm) _tlbil_pid(mm->context.id); } +static inline void local_flush_tlb_page(unsigned long vmaddr) +{ + _tlbil_va(vmaddr, 0); +} + static inline void flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr) { @@ -81,6 +87,10 @@ extern void flush_tlb_page_nohash(struct vm_area_struct *vma, unsigned long addr extern void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, unsigned long end); extern void flush_tlb_kernel_range(unsigned long start, unsigned long end); +static inline void local_flush_tlb_page(unsigned long vmaddr) +{ + flush_tlb_page(NULL, vmaddr); +} #else /* @@ -138,6 +148,10 @@ static inline void flush_tlb_mm(struct mm_struct *mm) { } +static inline void local_flush_tlb_page(unsigned long vmaddr) +{ +} + static inline void flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr) { diff --git a/arch/powerpc/include/asm/vdso_datapage.h b/arch/powerpc/include/asm/vdso_datapage.h index f013932..13c2c28 100644 --- a/arch/powerpc/include/asm/vdso_datapage.h +++ b/arch/powerpc/include/asm/vdso_datapage.h @@ -39,6 +39,7 @@ #ifndef __ASSEMBLY__ #include <linux/unistd.h> +#include <linux/time.h> #define SYSCALL_MAP_SIZE ((__NR_syscalls + 31) / 32) @@ -83,6 +84,7 @@ struct vdso_data { __u32 icache_log_block_size; /* L1 i-cache log block size */ __s32 wtom_clock_sec; /* Wall to monotonic clock */ __s32 wtom_clock_nsec; + struct timespec stamp_xtime; /* xtime as at tb_orig_stamp */ __u32 syscall_map_64[SYSCALL_MAP_SIZE]; /* map of syscalls */ __u32 syscall_map_32[SYSCALL_MAP_SIZE]; /* map of syscalls */ }; @@ -102,6 +104,7 @@ struct vdso_data { __u32 tz_dsttime; /* Type of dst correction 0x5C */ __s32 wtom_clock_sec; /* Wall to monotonic clock */ __s32 wtom_clock_nsec; + struct timespec stamp_xtime; /* xtime as at tb_orig_stamp */ __u32 syscall_map_32[SYSCALL_MAP_SIZE]; /* map of syscalls */ __u32 dcache_block_size; /* L1 d-cache block size */ __u32 icache_block_size; /* L1 i-cache block size */ diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c index 75c5dd0..050abfd 100644 --- a/arch/powerpc/kernel/asm-offsets.c +++ b/arch/powerpc/kernel/asm-offsets.c @@ -306,6 +306,7 @@ int main(void) DEFINE(CFG_SYSCALL_MAP32, offsetof(struct vdso_data, syscall_map_32)); DEFINE(WTOM_CLOCK_SEC, offsetof(struct vdso_data, wtom_clock_sec)); DEFINE(WTOM_CLOCK_NSEC, offsetof(struct vdso_data, wtom_clock_nsec)); + DEFINE(STAMP_XTIME, offsetof(struct vdso_data, stamp_xtime)); DEFINE(CFG_ICACHE_BLOCKSZ, offsetof(struct vdso_data, icache_block_size)); DEFINE(CFG_DCACHE_BLOCKSZ, offsetof(struct vdso_data, dcache_block_size)); DEFINE(CFG_ICACHE_LOGBLOCKSZ, offsetof(struct vdso_data, icache_log_block_size)); diff --git a/arch/powerpc/kernel/dma.c b/arch/powerpc/kernel/dma.c index 3a6eaa8..1c5c8a6 100644 --- a/arch/powerpc/kernel/dma.c +++ b/arch/powerpc/kernel/dma.c @@ -120,6 +120,26 @@ static inline void dma_direct_unmap_page(struct device *dev, { } +#ifdef CONFIG_NOT_COHERENT_CACHE +static inline void dma_direct_sync_sg(struct device *dev, + struct scatterlist *sgl, int nents, + enum dma_data_direction direction) +{ + struct scatterlist *sg; + int i; + + for_each_sg(sgl, sg, nents, i) + __dma_sync_page(sg_page(sg), sg->offset, sg->length, direction); +} + +static inline void dma_direct_sync_single_range(struct device *dev, + dma_addr_t dma_handle, unsigned long offset, size_t size, + enum dma_data_direction direction) +{ + __dma_sync(bus_to_virt(dma_handle+offset), size, direction); +} +#endif + struct dma_mapping_ops dma_direct_ops = { .alloc_coherent = dma_direct_alloc_coherent, .free_coherent = dma_direct_free_coherent, @@ -128,5 +148,11 @@ struct dma_mapping_ops dma_direct_ops = { .dma_supported = dma_direct_dma_supported, .map_page = dma_direct_map_page, .unmap_page = dma_direct_unmap_page, +#ifdef CONFIG_NOT_COHERENT_CACHE + .sync_single_range_for_cpu = dma_direct_sync_single_range, + .sync_single_range_for_device = dma_direct_sync_single_range, + .sync_sg_for_cpu = dma_direct_sync_sg, + .sync_sg_for_device = dma_direct_sync_sg, +#endif }; EXPORT_SYMBOL(dma_direct_ops); diff --git a/arch/powerpc/kernel/head_fsl_booke.S b/arch/powerpc/kernel/head_fsl_booke.S index 590304c..9a4639c 100644 --- a/arch/powerpc/kernel/head_fsl_booke.S +++ b/arch/powerpc/kernel/head_fsl_booke.S @@ -92,6 +92,7 @@ _ENTRY(_start); * if needed */ +_ENTRY(__early_start) /* 1. Find the index of the entry we're executing in */ bl invstr /* Find our address */ invstr: mflr r6 /* Make it accessible */ @@ -235,36 +236,40 @@ skpinv: addi r6,r6,1 /* Increment */ tlbivax 0,r9 TLBSYNC +/* The mapping only needs to be cache-coherent on SMP */ +#ifdef CONFIG_SMP +#define M_IF_SMP MAS2_M +#else +#define M_IF_SMP 0 +#endif + /* 6. Setup KERNELBASE mapping in TLB1[0] */ lis r6,0x1000 /* Set MAS0(TLBSEL) = TLB1(1), ESEL = 0 */ mtspr SPRN_MAS0,r6 lis r6,(MAS1_VALID|MAS1_IPROT)@h ori r6,r6,(MAS1_TSIZE(BOOKE_PAGESZ_64M))@l mtspr SPRN_MAS1,r6 - li r7,0 - lis r6,PAGE_OFFSET@h - ori r6,r6,PAGE_OFFSET@l - rlwimi r6,r7,0,20,31 + lis r6,MAS2_VAL(PAGE_OFFSET, BOOKE_PAGESZ_64M, M_IF_SMP)@h + ori r6,r6,MAS2_VAL(PAGE_OFFSET, BOOKE_PAGESZ_64M, M_IF_SMP)@l mtspr SPRN_MAS2,r6 mtspr SPRN_MAS3,r8 tlbwe /* 7. Jump to KERNELBASE mapping */ - lis r6,KERNELBASE@h - ori r6,r6,KERNELBASE@l - rlwimi r6,r7,0,20,31 + lis r6,(KERNELBASE & ~0xfff)@h + ori r6,r6,(KERNELBASE & ~0xfff)@l lis r7,MSR_KERNEL@h ori r7,r7,MSR_KERNEL@l bl 1f /* Find our address */ 1: mflr r9 rlwimi r6,r9,0,20,31 - addi r6,r6,24 + addi r6,r6,(2f - 1b) mtspr SPRN_SRR0,r6 mtspr SPRN_SRR1,r7 rfi /* start execution out of TLB1[0] entry */ /* 8. Clear out the temp mapping */ - lis r7,0x1000 /* Set MAS0(TLBSEL) = 1 */ +2: lis r7,0x1000 /* Set MAS0(TLBSEL) = 1 */ rlwimi r7,r5,16,4,15 /* Setup MAS0 = TLBSEL | ESEL(r5) */ mtspr SPRN_MAS0,r7 tlbre @@ -344,6 +349,15 @@ skpinv: addi r6,r6,1 /* Increment */ mtspr SPRN_DBSR,r2 #endif +#ifdef CONFIG_SMP + /* Check to see if we're the second processor, and jump + * to the secondary_start code if so + */ + mfspr r24,SPRN_PIR + cmpwi r24,0 + bne __secondary_start +#endif + /* * This is where the main kernel code starts. */ @@ -685,12 +699,13 @@ interrupt_base: /* SPE Floating Point Data */ #ifdef CONFIG_SPE EXCEPTION(0x2030, SPEFloatingPointData, SPEFloatingPointException, EXC_XFER_EE); -#else - EXCEPTION(0x2040, SPEFloatingPointData, unknown_exception, EXC_XFER_EE) -#endif /* CONFIG_SPE */ /* SPE Floating Point Round */ + EXCEPTION(0x2050, SPEFloatingPointRound, SPEFloatingPointRoundException, EXC_XFER_EE) +#else + EXCEPTION(0x2040, SPEFloatingPointData, unknown_exception, EXC_XFER_EE) EXCEPTION(0x2050, SPEFloatingPointRound, unknown_exception, EXC_XFER_EE) +#endif /* CONFIG_SPE */ /* Performance Monitor */ EXCEPTION(0x2060, PerformanceMonitor, performance_monitor_exception, EXC_XFER_STD) @@ -735,6 +750,9 @@ finish_tlb_load: #else rlwimi r12, r11, 26, 27, 31 /* extract WIMGE from pte */ #endif +#ifdef CONFIG_SMP + ori r12, r12, MAS2_M +#endif mtspr SPRN_MAS2, r12 li r10, (_PAGE_HWEXEC | _PAGE_PRESENT) @@ -746,7 +764,7 @@ finish_tlb_load: iseleq r12, r12, r10 #ifdef CONFIG_PTE_64BIT -2: rlwimi r12, r13, 24, 0, 7 /* grab RPN[32:39] */ + rlwimi r12, r13, 24, 0, 7 /* grab RPN[32:39] */ rlwimi r12, r11, 24, 8, 19 /* grab RPN[40:51] */ mtspr SPRN_MAS3, r12 BEGIN_FTR_SECTION @@ -754,7 +772,7 @@ BEGIN_FTR_SECTION mtspr SPRN_MAS7, r10 END_FTR_SECTION_IFSET(CPU_FTR_BIG_PHYS) #else -2: rlwimi r11, r12, 0, 20, 31 /* Extract RPN from PTE and merge with perms */ + rlwimi r11, r12, 0, 20, 31 /* Extract RPN from PTE and merge with perms */ mtspr SPRN_MAS3, r11 #endif #ifdef CONFIG_E200 @@ -1037,6 +1055,63 @@ _GLOBAL(flush_dcache_L1) blr +#ifdef CONFIG_SMP +/* When we get here, r24 needs to hold the CPU # */ + .globl __secondary_start +__secondary_start: + lis r3,__secondary_hold_acknowledge@h + ori r3,r3,__secondary_hold_acknowledge@l + stw r24,0(r3) + + li r3,0 + mr r4,r24 /* Why? */ + bl call_setup_cpu + + lis r3,tlbcam_index@ha + lwz r3,tlbcam_index@l(r3) + mtctr r3 + li r26,0 /* r26 safe? */ + + /* Load each CAM entry */ +1: mr r3,r26 + bl loadcam_entry + addi r26,r26,1 + bdnz 1b + + /* get current_thread_info and current */ + lis r1,secondary_ti@ha + lwz r1,secondary_ti@l(r1) + lwz r2,TI_TASK(r1) + + /* stack */ + addi r1,r1,THREAD_SIZE-STACK_FRAME_OVERHEAD + li r0,0 + stw r0,0(r1) + + /* ptr to current thread */ + addi r4,r2,THREAD /* address of our thread_struct */ + mtspr SPRN_SPRG3,r4 + + /* Setup the defaults for TLB entries */ + li r4,(MAS4_TSIZED(BOOKE_PAGESZ_4K))@l + mtspr SPRN_MAS4,r4 + + /* Jump to start_secondary */ + lis r4,MSR_KERNEL@h + ori r4,r4,MSR_KERNEL@l + lis r3,start_secondary@h + ori r3,r3,start_secondary@l + mtspr SPRN_SRR0,r3 + mtspr SPRN_SRR1,r4 + sync + rfi + sync + + .globl __secondary_hold_acknowledge +__secondary_hold_acknowledge: + .long -1 +#endif + /* * We put a few things here that have to be page-aligned. This stuff * goes at the beginning of the data segment, which is page-aligned. diff --git a/arch/powerpc/kernel/ibmebus.c b/arch/powerpc/kernel/ibmebus.c index 64299d2..7c65377 100644 --- a/arch/powerpc/kernel/ibmebus.c +++ b/arch/powerpc/kernel/ibmebus.c @@ -231,6 +231,7 @@ void ibmebus_free_irq(u32 ist, void *dev_id) unsigned int irq = irq_find_mapping(NULL, ist); free_irq(irq, dev_id); + irq_dispose_mapping(irq); } EXPORT_SYMBOL(ibmebus_free_irq); diff --git a/arch/powerpc/kernel/paca.c b/arch/powerpc/kernel/paca.c index 48a3471..c744b32 100644 --- a/arch/powerpc/kernel/paca.c +++ b/arch/powerpc/kernel/paca.c @@ -37,6 +37,7 @@ struct lppaca lppaca[] = { .end_of_quantum = 0xfffffffffffffffful, .slb_count = 64, .vmxregs_in_use = 0, + .page_ins = 0, }, }; diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c index f36936d..91c3f52 100644 --- a/arch/powerpc/kernel/pci-common.c +++ b/arch/powerpc/kernel/pci-common.c @@ -37,13 +37,7 @@ #include <asm/machdep.h> #include <asm/ppc-pci.h> #include <asm/firmware.h> - -#ifdef DEBUG -#include <asm/udbg.h> -#define DBG(fmt...) printk(fmt) -#else -#define DBG(fmt...) -#endif +#include <asm/eeh.h> static DEFINE_SPINLOCK(hose_spinlock); @@ -53,8 +47,9 @@ static int global_phb_number; /* Global phb counter */ /* ISA Memory physical address */ resource_size_t isa_mem_base; -/* Default PCI flags is 0 */ -unsigned int ppc_pci_flags; +/* Default PCI flags is 0 on ppc32, modified at boot on ppc64 */ +unsigned int ppc_pci_flags = 0; + static struct dma_mapping_ops *pci_dma_ops; @@ -208,26 +203,6 @@ char __devinit *pcibios_setup(char *str) return str; } -void __devinit pcibios_setup_new_device(struct pci_dev *dev) -{ - struct dev_archdata *sd = &dev->dev.archdata; - - sd->of_node = pci_device_to_OF_node(dev); - - DBG("PCI: device %s OF node: %s\n", pci_name(dev), - sd->of_node ? sd->of_node->full_name : "<none>"); - - sd->dma_ops = pci_dma_ops; -#ifdef CONFIG_PPC32 - sd->dma_data = (void *)PCI_DRAM_OFFSET; -#endif - set_dev_node(&dev->dev, pcibus_to_node(dev->bus)); - - if (ppc_md.pci_dma_dev_setup) - ppc_md.pci_dma_dev_setup(dev); -} -EXPORT_SYMBOL(pcibios_setup_new_device); - /* * Reads the interrupt pin to determine if interrupt is use by card. * If the interrupt is used, then gets the interrupt line from the @@ -252,7 +227,7 @@ int pci_read_irq_line(struct pci_dev *pci_dev) return -1; #endif - DBG("Try to map irq for %s...\n", pci_name(pci_dev)); + pr_debug("PCI: Try to map irq for %s...\n", pci_name(pci_dev)); #ifdef DEBUG memset(&oirq, 0xff, sizeof(oirq)); @@ -276,26 +251,26 @@ int pci_read_irq_line(struct pci_dev *pci_dev) line == 0xff || line == 0) { return -1; } - DBG(" -> no map ! Using line %d (pin %d) from PCI config\n", - line, pin); + pr_debug(" No map ! Using line %d (pin %d) from PCI config\n", + line, pin); virq = irq_create_mapping(NULL, line); if (virq != NO_IRQ) set_irq_type(virq, IRQ_TYPE_LEVEL_LOW); } else { - DBG(" -> got one, spec %d cells (0x%08x 0x%08x...) on %s\n", - oirq.size, oirq.specifier[0], oirq.specifier[1], + pr_debug(" Got one, spec %d cells (0x%08x 0x%08x...) on %s\n", + oirq.size, oirq.specifier[0], oirq.specifier[1], oirq.controller->full_name); virq = irq_create_of_mapping(oirq.controller, oirq.specifier, oirq.size); } if(virq == NO_IRQ) { - DBG(" -> failed to map !\n"); + pr_debug(" Failed to map !\n"); return -1; } - DBG(" -> mapped to linux irq %d\n", virq); + pr_debug(" Mapped to linux irq %d\n", virq); pci_dev->irq = virq; @@ -451,8 +426,8 @@ pgprot_t pci_phys_mem_access_prot(struct file *file, pci_dev_put(pdev); } - DBG("non-PCI map for %llx, prot: %lx\n", - (unsigned long long)offset, prot); + pr_debug("PCI: Non-PCI map for %llx, prot: %lx\n", + (unsigned long long)offset, prot); return __pgprot(prot); } @@ -853,15 +828,12 @@ void __devinit pci_process_bridge_OF_ranges(struct pci_controller *hose, int pci_proc_domain(struct pci_bus *bus) { struct pci_controller *hose = pci_bus_to_host(bus); -#ifdef CONFIG_PPC64 - return hose->buid != 0; -#else + if (!(ppc_pci_flags & PPC_PCI_ENABLE_PROC_DOMAINS)) return 0; if (ppc_pci_flags & PPC_PCI_COMPAT_DOMAIN_0) return hose->global_number != 0; return 1; -#endif } void pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, @@ -1083,27 +1055,50 @@ static void __devinit pcibios_fixup_bridge(struct pci_bus *bus) } } -static void __devinit __pcibios_fixup_bus(struct pci_bus *bus) +void __devinit pcibios_setup_bus_self(struct pci_bus *bus) { - struct pci_dev *dev = bus->self; - - pr_debug("PCI: Fixup bus %d (%s)\n", bus->number, dev ? pci_name(dev) : "PHB"); - - /* Fixup PCI<->PCI bridges. Host bridges are handled separately, for - * now differently between 32 and 64 bits. - */ - if (dev != NULL) + /* Fix up the bus resources for P2P bridges */ + if (bus->self != NULL) pcibios_fixup_bridge(bus); - /* Additional setup that is different between 32 and 64 bits for now */ - pcibios_do_bus_setup(bus); - - /* Platform specific bus fixups */ + /* Platform specific bus fixups. This is currently only used + * by fsl_pci and I'm hoping to get rid of it at some point + */ if (ppc_md.pcibios_fixup_bus) ppc_md.pcibios_fixup_bus(bus); - /* Read default IRQs and fixup if necessary */ + /* Setup bus DMA mappings */ + if (ppc_md.pci_dma_bus_setup) + ppc_md.pci_dma_bus_setup(bus); +} + +void __devinit pcibios_setup_bus_devices(struct pci_bus *bus) +{ + struct pci_dev *dev; + + pr_debug("PCI: Fixup bus devices %d (%s)\n", + bus->number, bus->self ? pci_name(bus->self) : "PHB"); + list_for_each_entry(dev, &bus->devices, bus_list) { + struct dev_archdata *sd = &dev->dev.archdata; + + /* Setup OF node pointer in archdata */ + sd->of_node = pci_device_to_OF_node(dev); + + /* Fixup NUMA node as it may not be setup yet by the generic + * code and is needed by the DMA init + */ + set_dev_node(&dev->dev, pcibus_to_node(dev->bus)); + + /* Hook up default DMA ops */ + sd->dma_ops = pci_dma_ops; + sd->dma_data = (void *)PCI_DRAM_OFFSET; + + /* Additional platform DMA/iommu setup */ + if (ppc_md.pci_dma_dev_setup) + ppc_md.pci_dma_dev_setup(dev); + + /* Read default IRQs and fixup if necessary */ pci_read_irq_line(dev); if (ppc_md.pci_irq_fixup) ppc_md.pci_irq_fixup(dev); @@ -1113,22 +1108,19 @@ static void __devinit __pcibios_fixup_bus(struct pci_bus *bus) void __devinit pcibios_fixup_bus(struct pci_bus *bus) { /* When called from the generic PCI probe, read PCI<->PCI bridge - * bases before proceeding + * bases. This is -not- called when generating the PCI tree from + * the OF device-tree. */ if (bus->self != NULL) pci_read_bridge_bases(bus); - __pcibios_fixup_bus(bus); -} -EXPORT_SYMBOL(pcibios_fixup_bus); -/* When building a bus from the OF tree rather than probing, we need a - * slightly different version of the fixup which doesn't read the - * bridge bases using config space accesses - */ -void __devinit pcibios_fixup_of_probed_bus(struct pci_bus *bus) -{ - __pcibios_fixup_bus(bus); + /* Now fixup the bus bus */ + pcibios_setup_bus_self(bus); + + /* Now fixup devices on that bus */ + pcibios_setup_bus_devices(bus); } +EXPORT_SYMBOL(pcibios_fixup_bus); static int skip_isa_ioresource_align(struct pci_dev *dev) { @@ -1198,10 +1190,10 @@ static int __init reparent_resources(struct resource *parent, *pp = NULL; for (p = res->child; p != NULL; p = p->sibling) { p->parent = res; - DBG(KERN_INFO "PCI: reparented %s [%llx..%llx] under %s\n", - p->name, - (unsigned long long)p->start, - (unsigned long long)p->end, res->name); + pr_debug("PCI: Reparented %s [%llx..%llx] under %s\n", + p->name, + (unsigned long long)p->start, + (unsigned long long)p->end, res->name); } return 0; } @@ -1245,9 +1237,12 @@ void pcibios_allocate_bus_resources(struct pci_bus *bus) int i; struct resource *res, *pr; + pr_debug("PCI: Allocating bus resources for %04x:%02x...\n", + pci_domain_nr(bus), bus->number); + for (i = 0; i < PCI_BUS_NUM_RESOURCES; ++i) { if ((res = bus->resource[i]) == NULL || !res->flags - || res->start > res->end) + || res->start > res->end || res->parent) continue; if (bus->parent == NULL) pr = (res->flags & IORESOURCE_IO) ? @@ -1271,14 +1266,14 @@ void pcibios_allocate_bus_resources(struct pci_bus *bus) } } - DBG("PCI: %s (bus %d) bridge rsrc %d: %016llx-%016llx " - "[0x%x], parent %p (%s)\n", - bus->self ? pci_name(bus->self) : "PHB", - bus->number, i, - (unsigned long long)res->start, - (unsigned long long)res->end, - (unsigned int)res->flags, - pr, (pr && pr->name) ? pr->name : "nil"); + pr_debug("PCI: %s (bus %d) bridge rsrc %d: %016llx-%016llx " + "[0x%x], parent %p (%s)\n", + bus->self ? pci_name(bus->self) : "PHB", + bus->number, i, + (unsigned long long)res->start, + (unsigned long long)res->end, + (unsigned int)res->flags, + pr, (pr && pr->name) ? pr->name : "nil"); if (pr && !(pr->flags & IORESOURCE_UNSET)) { if (request_resource(pr, res) == 0) @@ -1305,11 +1300,11 @@ static inline void __devinit alloc_resource(struct pci_dev *dev, int idx) { struct resource *pr, *r = &dev->resource[idx]; - DBG("PCI: Allocating %s: Resource %d: %016llx..%016llx [%x]\n", - pci_name(dev), idx, - (unsigned long long)r->start, - (unsigned long long)r->end, - (unsigned int)r->flags); + pr_debug("PCI: Allocating %s: Resource %d: %016llx..%016llx [%x]\n", + pci_name(dev), idx, + (unsigned long long)r->start, + (unsigned long long)r->end, + (unsigned int)r->flags); pr = pci_find_parent_resource(dev, r); if (!pr || (pr->flags & IORESOURCE_UNSET) || @@ -1317,10 +1312,11 @@ static inline void __devinit alloc_resource(struct pci_dev *dev, int idx) printk(KERN_WARNING "PCI: Cannot allocate resource region %d" " of device %s, will remap\n", idx, pci_name(dev)); if (pr) - DBG("PCI: parent is %p: %016llx-%016llx [%x]\n", pr, - (unsigned long long)pr->start, - (unsigned long long)pr->end, - (unsigned int)pr->flags); + pr_debug("PCI: parent is %p: %016llx-%016llx [%x]\n", + pr, + (unsigned long long)pr->start, + (unsigned long long)pr->end, + (unsigned int)pr->flags); /* We'll assign a new address later */ r->flags |= IORESOURCE_UNSET; r->end -= r->start; @@ -1358,7 +1354,8 @@ static void __init pcibios_allocate_resources(int pass) * but keep it unregistered. */ u32 reg; - DBG("PCI: Switching off ROM of %s\n", pci_name(dev)); + pr_debug("PCI: Switching off ROM of %s\n", + pci_name(dev)); r->flags &= ~IORESOURCE_ROM_ENABLE; pci_read_config_dword(dev, dev->rom_base_reg, ®); pci_write_config_dword(dev, dev->rom_base_reg, @@ -1383,7 +1380,7 @@ void __init pcibios_resource_survey(void) } if (!(ppc_pci_flags & PPC_PCI_PROBE_ONLY)) { - DBG("PCI: Assigning unassigned resouces...\n"); + pr_debug("PCI: Assigning unassigned resouces...\n"); pci_assign_unassigned_resources(); } @@ -1393,9 +1390,11 @@ void __init pcibios_resource_survey(void) } #ifdef CONFIG_HOTPLUG -/* This is used by the pSeries hotplug driver to allocate resource + +/* This is used by the PCI hotplug driver to allocate resource * of newly plugged busses. We can try to consolidate with the - * rest of the code later, for now, keep it as-is + * rest of the code later, for now, keep it as-is as our main + * resource allocation function doesn't deal with sub-trees yet. */ void __devinit pcibios_claim_one_bus(struct pci_bus *bus) { @@ -1410,6 +1409,14 @@ void __devinit pcibios_claim_one_bus(struct pci_bus *bus) if (r->parent || !r->start || !r->flags) continue; + + pr_debug("PCI: Claiming %s: " + "Resource %d: %016llx..%016llx [%x]\n", + pci_name(dev), i, + (unsigned long long)r->start, + (unsigned long long)r->end, + (unsigned int)r->flags); + pci_claim_resource(dev, i); } } @@ -1418,6 +1425,31 @@ void __devinit pcibios_claim_one_bus(struct pci_bus *bus) pcibios_claim_one_bus(child_bus); } EXPORT_SYMBOL_GPL(pcibios_claim_one_bus); + + +/* pcibios_finish_adding_to_bus + * + * This is to be called by the hotplug code after devices have been + * added to a bus, this include calling it for a PHB that is just + * being added + */ +void pcibios_finish_adding_to_bus(struct pci_bus *bus) +{ + pr_debug("PCI: Finishing adding to hotplug bus %04x:%02x\n", + pci_domain_nr(bus), bus->number); + + /* Allocate bus and devices resources */ + pcibios_allocate_bus_resources(bus); + pcibios_claim_one_bus(bus); + + /* Add new devices to global lists. Register in proc, sysfs. */ + pci_bus_add_devices(bus); + + /* Fixup EEH */ + eeh_add_device_tree_late(bus); +} +EXPORT_SYMBOL_GPL(pcibios_finish_adding_to_bus); + #endif /* CONFIG_HOTPLUG */ int pcibios_enable_device(struct pci_dev *dev, int mask) @@ -1428,3 +1460,61 @@ int pcibios_enable_device(struct pci_dev *dev, int mask) return pci_enable_resources(dev, mask); } + +void __devinit pcibios_setup_phb_resources(struct pci_controller *hose) +{ + struct pci_bus *bus = hose->bus; + struct resource *res; + int i; + + /* Hookup PHB IO resource */ + bus->resource[0] = res = &hose->io_resource; + + if (!res->flags) { + printk(KERN_WARNING "PCI: I/O resource not set for host" + " bridge %s (domain %d)\n", + hose->dn->full_name, hose->global_number); +#ifdef CONFIG_PPC32 + /* Workaround for lack of IO resource only on 32-bit */ + res->start = (unsigned long)hose->io_base_virt - isa_io_base; + res->end = res->start + IO_SPACE_LIMIT; + res->flags = IORESOURCE_IO; +#endif /* CONFIG_PPC32 */ + } + + pr_debug("PCI: PHB IO resource = %016llx-%016llx [%lx]\n", + (unsigned long long)res->start, + (unsigned long long)res->end, + (unsigned long)res->flags); + + /* Hookup PHB Memory resources */ + for (i = 0; i < 3; ++i) { + res = &hose->mem_resources[i]; + if (!res->flags) { + if (i > 0) + continue; + printk(KERN_ERR "PCI: Memory resource 0 not set for " + "host bridge %s (domain %d)\n", + hose->dn->full_name, hose->global_number); +#ifdef CONFIG_PPC32 + /* Workaround for lack of MEM resource only on 32-bit */ + res->start = hose->pci_mem_offset; + res->end = (resource_size_t)-1LL; + res->flags = IORESOURCE_MEM; +#endif /* CONFIG_PPC32 */ + } + bus->resource[i+1] = res; + + pr_debug("PCI: PHB MEM resource %d = %016llx-%016llx [%lx]\n", i, + (unsigned long long)res->start, + (unsigned long long)res->end, + (unsigned long)res->flags); + } + + pr_debug("PCI: PHB MEM offset = %016llx\n", + (unsigned long long)hose->pci_mem_offset); + pr_debug("PCI: PHB IO offset = %08lx\n", + (unsigned long)hose->io_base_virt - _IO_BASE); + +} + diff --git a/arch/powerpc/kernel/pci_32.c b/arch/powerpc/kernel/pci_32.c index 131b1df..7ad11e5 100644 --- a/arch/powerpc/kernel/pci_32.c +++ b/arch/powerpc/kernel/pci_32.c @@ -26,12 +26,6 @@ #undef DEBUG -#ifdef DEBUG -#define DBG(x...) printk(x) -#else -#define DBG(x...) -#endif - unsigned long isa_io_base = 0; unsigned long pci_dram_offset = 0; int pcibios_assign_bus_offset = 1; @@ -275,14 +269,14 @@ pci_busdev_to_OF_node(struct pci_bus *bus, int devfn) if (!have_of) return NULL; - DBG("pci_busdev_to_OF_node(%d,0x%x)\n", bus->number, devfn); + pr_debug("pci_busdev_to_OF_node(%d,0x%x)\n", bus->number, devfn); parent = scan_OF_for_pci_bus(bus); if (parent == NULL) return NULL; - DBG(" parent is %s\n", parent ? parent->full_name : "<NULL>"); + pr_debug(" parent is %s\n", parent ? parent->full_name : "<NULL>"); np = scan_OF_for_pci_dev(parent, devfn); of_node_put(parent); - DBG(" result is %s\n", np ? np->full_name : "<NULL>"); + pr_debug(" result is %s\n", np ? np->full_name : "<NULL>"); /* XXX most callers don't release the returned node * mostly because ppc64 doesn't increase the refcount, @@ -379,10 +373,41 @@ void pcibios_make_OF_bus_map(void) } #endif /* CONFIG_PPC_OF */ +static void __devinit pcibios_scan_phb(struct pci_controller *hose) +{ + struct pci_bus *bus; + struct device_node *node = hose->dn; + unsigned long io_offset; + struct resource *res = &hose->io_resource; + + pr_debug("PCI: Scanning PHB %s\n", + node ? node->full_name : "<NO NAME>"); + + /* Create an empty bus for the toplevel */ + bus = pci_create_bus(hose->parent, hose->first_busno, hose->ops, hose); + if (bus == NULL) { + printk(KERN_ERR "Failed to create bus for PCI domain %04x\n", + hose->global_number); + return; + } + bus->secondary = hose->first_busno; + hose->bus = bus; + + /* Fixup IO space offset */ + io_offset = (unsigned long)hose->io_base_virt - isa_io_base; + res->start = (res->start + io_offset) & 0xffffffffu; + res->end = (res->end + io_offset) & 0xffffffffu; + + /* Wire up PHB bus resources */ + pcibios_setup_phb_resources(hose); + + /* Scan children */ + hose->last_busno = bus->subordinate = pci_scan_child_bus(bus); +} + static int __init pcibios_init(void) { struct pci_controller *hose, *tmp; - struct pci_bus *bus; int next_busno = 0; printk(KERN_INFO "PCI: Probing PCI hardware\n"); @@ -395,12 +420,8 @@ static int __init pcibios_init(void) if (pci_assign_all_buses) hose->first_busno = next_busno; hose->last_busno = 0xff; - bus = pci_scan_bus_parented(hose->parent, hose->first_busno, - hose->ops, hose); - if (bus) { - pci_bus_add_devices(bus); - hose->last_busno = bus->subordinate; - } + pcibios_scan_phb(hose); + pci_bus_add_devices(hose->bus); if (pci_assign_all_buses || next_busno <= hose->last_busno) next_busno = hose->last_busno + pcibios_assign_bus_offset; } @@ -425,54 +446,6 @@ static int __init pcibios_init(void) subsys_initcall(pcibios_init); -void __devinit pcibios_do_bus_setup(struct pci_bus *bus) -{ - struct pci_controller *hose = (struct pci_controller *) bus->sysdata; - unsigned long io_offset; - struct resource *res; - int i; - struct pci_dev *dev; - - /* Hookup PHB resources */ - io_offset = (unsigned long)hose->io_base_virt - isa_io_base; - if (bus->parent == NULL) { - /* This is a host bridge - fill in its resources */ - hose->bus = bus; - - bus->resource[0] = res = &hose->io_resource; - if (!res->flags) { - if (io_offset) - printk(KERN_ERR "I/O resource not set for host" - " bridge %d\n", hose->global_number); - res->start = 0; - res->end = IO_SPACE_LIMIT; - res->flags = IORESOURCE_IO; - } - res->start = (res->start + io_offset) & 0xffffffffu; - res->end = (res->end + io_offset) & 0xffffffffu; - - for (i = 0; i < 3; ++i) { - res = &hose->mem_resources[i]; - if (!res->flags) { - if (i > 0) - continue; - printk(KERN_ERR "Memory resource not set for " - "host bridge %d\n", hose->global_number); - res->start = hose->pci_mem_offset; - res->end = ~0U; - res->flags = IORESOURCE_MEM; - } - bus->resource[i+1] = res; - } - } - - if (ppc_md.pci_dma_bus_setup) - ppc_md.pci_dma_bus_setup(bus); - - list_for_each_entry(dev, &bus->devices, bus_list) - pcibios_setup_new_device(dev); -} - /* the next one is stolen from the alpha port... */ void __init pcibios_update_irq(struct pci_dev *dev, int irq) diff --git a/arch/powerpc/kernel/pci_64.c b/arch/powerpc/kernel/pci_64.c index 3502b91..39fadc6 100644 --- a/arch/powerpc/kernel/pci_64.c +++ b/arch/powerpc/kernel/pci_64.c @@ -32,13 +32,6 @@ #include <asm/machdep.h> #include <asm/ppc-pci.h> -#ifdef DEBUG -#include <asm/udbg.h> -#define DBG(fmt...) printk(fmt) -#else -#define DBG(fmt...) -#endif - unsigned long pci_probe_only = 1; /* pci_io_base -- the base address from which io bars are offsets. @@ -102,7 +95,7 @@ static void pci_parse_of_addrs(struct device_node *node, struct pci_dev *dev) addrs = of_get_property(node, "assigned-addresses", &proplen); if (!addrs) return; - DBG(" parse addresses (%d bytes) @ %p\n", proplen, addrs); + pr_debug(" parse addresses (%d bytes) @ %p\n", proplen, addrs); for (; proplen >= 20; proplen -= 20, addrs += 5) { flags = pci_parse_of_flags(addrs[0]); if (!flags) @@ -112,8 +105,9 @@ static void pci_parse_of_addrs(struct device_node *node, struct pci_dev *dev) if (!size) continue; i = addrs[0] & 0xff; - DBG(" base: %llx, size: %llx, i: %x\n", - (unsigned long long)base, (unsigned long long)size, i); + pr_debug(" base: %llx, size: %llx, i: %x\n", + (unsigned long long)base, + (unsigned long long)size, i); if (PCI_BASE_ADDRESS_0 <= i && i <= PCI_BASE_ADDRESS_5) { res = &dev->resource[(i - PCI_BASE_ADDRESS_0) >> 2]; @@ -144,7 +138,7 @@ struct pci_dev *of_create_pci_dev(struct device_node *node, if (type == NULL) type = ""; - DBG(" create device, devfn: %x, type: %s\n", devfn, type); + pr_debug(" create device, devfn: %x, type: %s\n", devfn, type); dev->bus = bus; dev->sysdata = node; @@ -165,8 +159,8 @@ struct pci_dev *of_create_pci_dev(struct device_node *node, dev->class = get_int_prop(node, "class-code", 0); dev->revision = get_int_prop(node, "revision-id", 0); - DBG(" class: 0x%x\n", dev->class); - DBG(" revision: 0x%x\n", dev->revision); + pr_debug(" class: 0x%x\n", dev->class); + pr_debug(" revision: 0x%x\n", dev->revision); dev->current_state = 4; /* unknown power state */ dev->error_state = pci_channel_io_normal; @@ -187,7 +181,7 @@ struct pci_dev *of_create_pci_dev(struct device_node *node, pci_parse_of_addrs(node, dev); - DBG(" adding to system ...\n"); + pr_debug(" adding to system ...\n"); pci_device_add(dev, bus); @@ -195,19 +189,20 @@ struct pci_dev *of_create_pci_dev(struct device_node *node, } EXPORT_SYMBOL(of_create_pci_dev); -void __devinit of_scan_bus(struct device_node *node, - struct pci_bus *bus) +static void __devinit __of_scan_bus(struct device_node *node, + struct pci_bus *bus, int rescan_existing) { struct device_node *child; const u32 *reg; int reglen, devfn; struct pci_dev *dev; - DBG("of_scan_bus(%s) bus no %d... \n", node->full_name, bus->number); + pr_debug("of_scan_bus(%s) bus no %d... \n", + node->full_name, bus->number); /* Scan direct children */ for_each_child_of_node(node, child) { - DBG(" * %s\n", child->full_name); + pr_debug(" * %s\n", child->full_name); reg = of_get_property(child, "reg", ®len); if (reg == NULL || reglen < 20) continue; @@ -217,11 +212,15 @@ void __devinit of_scan_bus(struct device_node *node, dev = of_create_pci_dev(child, bus, devfn); if (!dev) continue; - DBG(" dev header type: %x\n", dev->hdr_type); + pr_debug(" dev header type: %x\n", dev->hdr_type); } - /* Ally all fixups */ - pcibios_fixup_of_probed_bus(bus); + /* Apply all fixups necessary. We don't fixup the bus "self" + * for an existing bridge that is being rescanned + */ + if (!rescan_existing) + pcibios_setup_bus_self(bus); + pcibios_setup_bus_devices(bus); /* Now scan child busses */ list_for_each_entry(dev, &bus->devices, bus_list) { @@ -233,7 +232,20 @@ void __devinit of_scan_bus(struct device_node *node, } } } -EXPORT_SYMBOL(of_scan_bus); + +void __devinit of_scan_bus(struct device_node *node, + struct pci_bus *bus) +{ + __of_scan_bus(node, bus, 0); +} +EXPORT_SYMBOL_GPL(of_scan_bus); + +void __devinit of_rescan_bus(struct device_node *node, + struct pci_bus *bus) +{ + __of_scan_bus(node, bus, 1); +} +EXPORT_SYMBOL_GPL(of_rescan_bus); void __devinit of_scan_pci_bridge(struct device_node *node, struct pci_dev *dev) @@ -245,7 +257,7 @@ void __devinit of_scan_pci_bridge(struct device_node *node, unsigned int flags; u64 size; - DBG("of_scan_pci_bridge(%s)\n", node->full_name); + pr_debug("of_scan_pci_bridge(%s)\n", node->full_name); /* parse bus-range property */ busrange = of_get_property(node, "bus-range", &len); @@ -309,12 +321,12 @@ void __devinit of_scan_pci_bridge(struct device_node *node, } sprintf(bus->name, "PCI Bus %04x:%02x", pci_domain_nr(bus), bus->number); - DBG(" bus name: %s\n", bus->name); + pr_debug(" bus name: %s\n", bus->name); mode = PCI_PROBE_NORMAL; if (ppc_md.pci_probe_mode) mode = ppc_md.pci_probe_mode(bus); - DBG(" probe mode: %d\n", mode); + pr_debug(" probe mode: %d\n", mode); if (mode == PCI_PROBE_DEVTREE) of_scan_bus(node, bus); @@ -327,9 +339,10 @@ void __devinit scan_phb(struct pci_controller *hose) { struct pci_bus *bus; struct device_node *node = hose->dn; - int i, mode; + int mode; - DBG("PCI: Scanning PHB %s\n", node ? node->full_name : "<NO NAME>"); + pr_debug("PCI: Scanning PHB %s\n", + node ? node->full_name : "<NO NAME>"); /* Create an empty bus for the toplevel */ bus = pci_create_bus(hose->parent, hose->first_busno, hose->ops, node); @@ -345,26 +358,13 @@ void __devinit scan_phb(struct pci_controller *hose) pcibios_map_io_space(bus); /* Wire up PHB bus resources */ - DBG("PCI: PHB IO resource = %016lx-%016lx [%lx]\n", - hose->io_resource.start, hose->io_resource.end, - hose->io_resource.flags); - bus->resource[0] = &hose->io_resource; - for (i = 0; i < 3; ++i) { - DBG("PCI: PHB MEM resource %d = %016lx-%016lx [%lx]\n", i, - hose->mem_resources[i].start, - hose->mem_resources[i].end, - hose->mem_resources[i].flags); - bus->resource[i+1] = &hose->mem_resources[i]; - } - DBG("PCI: PHB MEM offset = %016lx\n", hose->pci_mem_offset); - DBG("PCI: PHB IO offset = %08lx\n", - (unsigned long)hose->io_base_virt - _IO_BASE); + pcibios_setup_phb_resources(hose); /* Get probe mode and perform scan */ mode = PCI_PROBE_NORMAL; if (node && ppc_md.pci_probe_mode) mode = ppc_md.pci_probe_mode(bus); - DBG(" probe mode: %d\n", mode); + pr_debug(" probe mode: %d\n", mode); if (mode == PCI_PROBE_DEVTREE) { bus->subordinate = hose->last_busno; of_scan_bus(node, bus); @@ -380,7 +380,7 @@ static int __init pcibios_init(void) printk(KERN_INFO "PCI: Probing PCI hardware\n"); - /* For now, override phys_mem_access_prot. If we need it, + /* For now, override phys_mem_access_prot. If we need it,g * later, we may move that initialization to each ppc_md */ ppc_md.phys_mem_access_prot = pci_phys_mem_access_prot; @@ -388,6 +388,11 @@ static int __init pcibios_init(void) if (pci_probe_only) ppc_pci_flags |= PPC_PCI_PROBE_ONLY; + /* On ppc64, we always enable PCI domains and we keep domain 0 + * backward compatible in /proc for video cards + */ + ppc_pci_flags |= PPC_PCI_ENABLE_PROC_DOMAINS | PPC_PCI_COMPAT_DOMAIN_0; + /* Scan all of the recorded PCI controllers. */ list_for_each_entry_safe(hose, tmp, &hose_list, list_node) { scan_phb(hose); @@ -422,8 +427,8 @@ int pcibios_unmap_io_space(struct pci_bus *bus) if (bus->self) { struct resource *res = bus->resource[0]; - DBG("IO unmapping for PCI-PCI bridge %s\n", - pci_name(bus->self)); + pr_debug("IO unmapping for PCI-PCI bridge %s\n", + pci_name(bus->self)); __flush_hash_table_range(&init_mm, res->start + _IO_BASE, res->end + _IO_BASE + 1); @@ -437,8 +442,8 @@ int pcibios_unmap_io_space(struct pci_bus *bus) if (hose->io_base_alloc == 0) return 0; - DBG("IO unmapping for PHB %s\n", hose->dn->full_name); - DBG(" alloc=0x%p\n", hose->io_base_alloc); + pr_debug("IO unmapping for PHB %s\n", hose->dn->full_name); + pr_debug(" alloc=0x%p\n", hose->io_base_alloc); /* This is a PHB, we fully unmap the IO area */ vunmap(hose->io_base_alloc); @@ -463,11 +468,11 @@ int __devinit pcibios_map_io_space(struct pci_bus *bus) * thus HPTEs will be faulted in when needed */ if (bus->self) { - DBG("IO mapping for PCI-PCI bridge %s\n", - pci_name(bus->self)); - DBG(" virt=0x%016lx...0x%016lx\n", - bus->resource[0]->start + _IO_BASE, - bus->resource[0]->end + _IO_BASE); + pr_debug("IO mapping for PCI-PCI bridge %s\n", + pci_name(bus->self)); + pr_debug(" virt=0x%016lx...0x%016lx\n", + bus->resource[0]->start + _IO_BASE, + bus->resource[0]->end + _IO_BASE); return 0; } @@ -496,11 +501,11 @@ int __devinit pcibios_map_io_space(struct pci_bus *bus) hose->io_base_virt = (void __iomem *)(area->addr + hose->io_base_phys - phys_page); - DBG("IO mapping for PHB %s\n", hose->dn->full_name); - DBG(" phys=0x%016lx, virt=0x%p (alloc=0x%p)\n", - hose->io_base_phys, hose->io_base_virt, hose->io_base_alloc); - DBG(" size=0x%016lx (alloc=0x%016lx)\n", - hose->pci_io_size, size_page); + pr_debug("IO mapping for PHB %s\n", hose->dn->full_name); + pr_debug(" phys=0x%016lx, virt=0x%p (alloc=0x%p)\n", + hose->io_base_phys, hose->io_base_virt, hose->io_base_alloc); + pr_debug(" size=0x%016lx (alloc=0x%016lx)\n", + hose->pci_io_size, size_page); /* Establish the mapping */ if (__ioremap_at(phys_page, area->addr, size_page, @@ -512,24 +517,13 @@ int __devinit pcibios_map_io_space(struct pci_bus *bus) hose->io_resource.start += io_virt_offset; hose->io_resource.end += io_virt_offset; - DBG(" hose->io_resource=0x%016lx...0x%016lx\n", - hose->io_resource.start, hose->io_resource.end); + pr_debug(" hose->io_resource=0x%016lx...0x%016lx\n", + hose->io_resource.start, hose->io_resource.end); return 0; } EXPORT_SYMBOL_GPL(pcibios_map_io_space); -void __devinit pcibios_do_bus_setup(struct pci_bus *bus) -{ - struct pci_dev *dev; - - if (ppc_md.pci_dma_bus_setup) - ppc_md.pci_dma_bus_setup(bus); - - list_for_each_entry(dev, &bus->devices, bus_list) - pcibios_setup_new_device(dev); -} - unsigned long pci_address_to_pio(phys_addr_t address) { struct pci_controller *hose, *tmp; diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c index 957bded..51b201d 100644 --- a/arch/powerpc/kernel/process.c +++ b/arch/powerpc/kernel/process.c @@ -467,6 +467,8 @@ static struct regbit { {MSR_VEC, "VEC"}, {MSR_VSX, "VSX"}, {MSR_ME, "ME"}, + {MSR_CE, "CE"}, + {MSR_DE, "DE"}, {MSR_IR, "IR"}, {MSR_DR, "DR"}, {0, NULL} @@ -998,7 +1000,7 @@ unsigned long get_wchan(struct task_struct *p) return 0; } -static int kstack_depth_to_print = 64; +static int kstack_depth_to_print = CONFIG_PRINT_STACK_DEPTH; void show_stack(struct task_struct *tsk, unsigned long *stack) { diff --git a/arch/powerpc/kernel/prom_parse.c b/arch/powerpc/kernel/prom_parse.c index a11d689..8c13355 100644 --- a/arch/powerpc/kernel/prom_parse.c +++ b/arch/powerpc/kernel/prom_parse.c @@ -734,10 +734,7 @@ void of_irq_map_init(unsigned int flags) if (flags & OF_IMAP_NO_PHANDLE) { struct device_node *np; - for(np = NULL; (np = of_find_all_nodes(np)) != NULL;) { - if (of_get_property(np, "interrupt-controller", NULL) - == NULL) - continue; + for_each_node_with_property(np, "interrupt-controller") { /* Skip /chosen/interrupt-controller */ if (strcmp(np->name, "chosen") == 0) continue; diff --git a/arch/powerpc/kernel/rtas_pci.c b/arch/powerpc/kernel/rtas_pci.c index 589a279..8869001 100644 --- a/arch/powerpc/kernel/rtas_pci.c +++ b/arch/powerpc/kernel/rtas_pci.c @@ -301,51 +301,3 @@ void __init find_and_init_phbs(void) #endif /* CONFIG_PPC32 */ } } - -/* RPA-specific bits for removing PHBs */ -int pcibios_remove_root_bus(struct pci_controller *phb) -{ - struct pci_bus *b = phb->bus; - struct resource *res; - int rc, i; - - res = b->resource[0]; - if (!res->flags) { - printk(KERN_ERR "%s: no IO resource for PHB %s\n", __func__, - b->name); - return 1; - } - - rc = pcibios_unmap_io_space(b); - if (rc) { - printk(KERN_ERR "%s: failed to unmap IO on bus %s\n", - __func__, b->name); - return 1; - } - - if (release_resource(res)) { - printk(KERN_ERR "%s: failed to release IO on bus %s\n", - __func__, b->name); - return 1; - } - - for (i = 1; i < 3; ++i) { - res = b->resource[i]; - if (!res->flags && i == 0) { - printk(KERN_ERR "%s: no MEM resource for PHB %s\n", - __func__, b->name); - return 1; - } - if (res->flags && release_resource(res)) { - printk(KERN_ERR - "%s: failed to release IO %d on bus %s\n", - __func__, i, b->name); - return 1; - } - } - - pcibios_free_controller(phb); - - return 0; -} -EXPORT_SYMBOL(pcibios_remove_root_bus); diff --git a/arch/powerpc/kernel/setup_64.c b/arch/powerpc/kernel/setup_64.c index 169d74c..93c875a 100644 --- a/arch/powerpc/kernel/setup_64.c +++ b/arch/powerpc/kernel/setup_64.c @@ -606,8 +606,6 @@ void __init setup_per_cpu_areas(void) for_each_possible_cpu(i) { ptr = alloc_bootmem_pages_node(NODE_DATA(cpu_to_node(i)), size); - if (!ptr) - panic("Cannot allocate cpu data for CPU %d\n", i); paca[i].data_offset = ptr - __per_cpu_start; memcpy(ptr, __per_cpu_start, __per_cpu_end - __per_cpu_start); diff --git a/arch/powerpc/kernel/smp-tbsync.c b/arch/powerpc/kernel/smp-tbsync.c index bc892e69..a5e5452 100644 --- a/arch/powerpc/kernel/smp-tbsync.c +++ b/arch/powerpc/kernel/smp-tbsync.c @@ -113,7 +113,7 @@ void __devinit smp_generic_give_timebase(void) { int i, score, score2, old, min=0, max=5000, offset=1000; - printk("Synchronizing timebase\n"); + pr_debug("Software timebase sync\n"); /* if this fails then this kernel won't work anyway... */ tbsync = kzalloc( sizeof(*tbsync), GFP_KERNEL ); @@ -123,13 +123,13 @@ void __devinit smp_generic_give_timebase(void) while (!tbsync->ack) barrier(); - printk("Got ack\n"); + pr_debug("Got ack\n"); /* binary search */ for (old = -1; old != offset ; offset = (min+max) / 2) { score = start_contest(kSetAndTest, offset, NUM_ITER); - printk("score %d, offset %d\n", score, offset ); + pr_debug("score %d, offset %d\n", score, offset ); if( score > 0 ) max = offset; @@ -140,8 +140,8 @@ void __devinit smp_generic_give_timebase(void) score = start_contest(kSetAndTest, min, NUM_ITER); score2 = start_contest(kSetAndTest, max, NUM_ITER); - printk("Min %d (score %d), Max %d (score %d)\n", - min, score, max, score2); + pr_debug("Min %d (score %d), Max %d (score %d)\n", + min, score, max, score2); score = abs(score); score2 = abs(score2); offset = (score < score2) ? min : max; @@ -155,7 +155,7 @@ void __devinit smp_generic_give_timebase(void) if (score2 <= score || score2 < 20) break; } - printk("Final offset: %d (%d/%d)\n", offset, score2, NUM_ITER ); + pr_debug("Final offset: %d (%d/%d)\n", offset, score2, NUM_ITER ); /* exiting */ tbsync->cmd = kExit; diff --git a/arch/powerpc/kernel/smp.c b/arch/powerpc/kernel/smp.c index ff9f701..a59d8d7 100644 --- a/arch/powerpc/kernel/smp.c +++ b/arch/powerpc/kernel/smp.c @@ -123,6 +123,65 @@ void smp_message_recv(int msg) } } +static irqreturn_t call_function_action(int irq, void *data) +{ + generic_smp_call_function_interrupt(); + return IRQ_HANDLED; +} + +static irqreturn_t reschedule_action(int irq, void *data) +{ + /* we just need the return path side effect of checking need_resched */ + return IRQ_HANDLED; +} + +static irqreturn_t call_function_single_action(int irq, void *data) +{ + generic_smp_call_function_single_interrupt(); + return IRQ_HANDLED; +} + +static irqreturn_t debug_ipi_action(int irq, void *data) +{ + smp_message_recv(PPC_MSG_DEBUGGER_BREAK); + return IRQ_HANDLED; +} + +static irq_handler_t smp_ipi_action[] = { + [PPC_MSG_CALL_FUNCTION] = call_function_action, + [PPC_MSG_RESCHEDULE] = reschedule_action, + [PPC_MSG_CALL_FUNC_SINGLE] = call_function_single_action, + [PPC_MSG_DEBUGGER_BREAK] = debug_ipi_action, +}; + +const char *smp_ipi_name[] = { + [PPC_MSG_CALL_FUNCTION] = "ipi call function", + [PPC_MSG_RESCHEDULE] = "ipi reschedule", + [PPC_MSG_CALL_FUNC_SINGLE] = "ipi call function single", + [PPC_MSG_DEBUGGER_BREAK] = "ipi debugger", +}; + +/* optional function to request ipi, for controllers with >= 4 ipis */ +int smp_request_message_ipi(int virq, int msg) +{ + int err; + + if (msg < 0 || msg > PPC_MSG_DEBUGGER_BREAK) { + return -EINVAL; + } +#if !defined(CONFIG_DEBUGGER) && !defined(CONFIG_KEXEC) + if (msg == PPC_MSG_DEBUGGER_BREAK) { + return 1; + } +#endif + err = request_irq(virq, smp_ipi_action[msg], IRQF_DISABLED|IRQF_PERCPU, + smp_ipi_name[msg], 0); + WARN(err < 0, "unable to request_irq %d for %s (rc %d)\n", + virq, smp_ipi_name[msg], err); + + return err; +} + void smp_send_reschedule(int cpu) { if (likely(smp_ops)) diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c index e2ee66b..e1f3a51 100644 --- a/arch/powerpc/kernel/time.c +++ b/arch/powerpc/kernel/time.c @@ -164,8 +164,6 @@ static u64 tb_to_ns_scale __read_mostly; static unsigned tb_to_ns_shift __read_mostly; static unsigned long boot_tb __read_mostly; -static struct gettimeofday_struct do_gtod; - extern struct timezone sys_tz; static long timezone_offset; @@ -415,31 +413,9 @@ void udelay(unsigned long usecs) } EXPORT_SYMBOL(udelay); - -/* - * There are two copies of tb_to_xs and stamp_xsec so that no - * lock is needed to access and use these values in - * do_gettimeofday. We alternate the copies and as long as a - * reasonable time elapses between changes, there will never - * be inconsistent values. ntpd has a minimum of one minute - * between updates. - */ static inline void update_gtod(u64 new_tb_stamp, u64 new_stamp_xsec, u64 new_tb_to_xs) { - unsigned temp_idx; - struct gettimeofday_vars *temp_varp; - - temp_idx = (do_gtod.var_idx == 0); - temp_varp = &do_gtod.vars[temp_idx]; - - temp_varp->tb_to_xs = new_tb_to_xs; - temp_varp->tb_orig_stamp = new_tb_stamp; - temp_varp->stamp_xsec = new_stamp_xsec; - smp_mb(); - do_gtod.varp = temp_varp; - do_gtod.var_idx = temp_idx; - /* * tb_update_count is used to allow the userspace gettimeofday code * to assure itself that it sees a consistent view of the tb_to_xs and @@ -456,6 +432,7 @@ static inline void update_gtod(u64 new_tb_stamp, u64 new_stamp_xsec, vdso_data->tb_to_xs = new_tb_to_xs; vdso_data->wtom_clock_sec = wall_to_monotonic.tv_sec; vdso_data->wtom_clock_nsec = wall_to_monotonic.tv_nsec; + vdso_data->stamp_xtime = xtime; smp_wmb(); ++(vdso_data->tb_update_count); } @@ -514,9 +491,7 @@ static int __init iSeries_tb_recal(void) tb_ticks_per_sec = new_tb_ticks_per_sec; calc_cputime_factors(); div128_by_32( XSEC_PER_SEC, 0, tb_ticks_per_sec, &divres ); - do_gtod.tb_ticks_per_sec = tb_ticks_per_sec; tb_to_xs = divres.result_low; - do_gtod.varp->tb_to_xs = tb_to_xs; vdso_data->tb_ticks_per_sec = tb_ticks_per_sec; vdso_data->tb_to_xs = tb_to_xs; } @@ -988,15 +963,6 @@ void __init time_init(void) sys_tz.tz_dsttime = 0; } - do_gtod.varp = &do_gtod.vars[0]; - do_gtod.var_idx = 0; - do_gtod.varp->tb_orig_stamp = tb_last_jiffy; - __get_cpu_var(last_jiffy) = tb_last_jiffy; - do_gtod.varp->stamp_xsec = (u64) xtime.tv_sec * XSEC_PER_SEC; - do_gtod.tb_ticks_per_sec = tb_ticks_per_sec; - do_gtod.varp->tb_to_xs = tb_to_xs; - do_gtod.tb_to_us = tb_to_us; - vdso_data->tb_orig_stamp = tb_last_jiffy; vdso_data->tb_update_count = 0; vdso_data->tb_ticks_per_sec = tb_ticks_per_sec; diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index f5def6c..5457e95 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c @@ -1160,37 +1160,85 @@ void CacheLockingException(struct pt_regs *regs, unsigned long address, #ifdef CONFIG_SPE void SPEFloatingPointException(struct pt_regs *regs) { + extern int do_spe_mathemu(struct pt_regs *regs); unsigned long spefscr; int fpexc_mode; int code = 0; + int err; + + preempt_disable(); + if (regs->msr & MSR_SPE) + giveup_spe(current); + preempt_enable(); spefscr = current->thread.spefscr; fpexc_mode = current->thread.fpexc_mode; - /* Hardware does not neccessarily set sticky - * underflow/overflow/invalid flags */ if ((spefscr & SPEFSCR_FOVF) && (fpexc_mode & PR_FP_EXC_OVF)) { code = FPE_FLTOVF; - spefscr |= SPEFSCR_FOVFS; } else if ((spefscr & SPEFSCR_FUNF) && (fpexc_mode & PR_FP_EXC_UND)) { code = FPE_FLTUND; - spefscr |= SPEFSCR_FUNFS; } else if ((spefscr & SPEFSCR_FDBZ) && (fpexc_mode & PR_FP_EXC_DIV)) code = FPE_FLTDIV; else if ((spefscr & SPEFSCR_FINV) && (fpexc_mode & PR_FP_EXC_INV)) { code = FPE_FLTINV; - spefscr |= SPEFSCR_FINVS; } else if ((spefscr & (SPEFSCR_FG | SPEFSCR_FX)) && (fpexc_mode & PR_FP_EXC_RES)) code = FPE_FLTRES; - current->thread.spefscr = spefscr; + err = do_spe_mathemu(regs); + if (err == 0) { + regs->nip += 4; /* skip emulated instruction */ + emulate_single_step(regs); + return; + } + + if (err == -EFAULT) { + /* got an error reading the instruction */ + _exception(SIGSEGV, regs, SEGV_ACCERR, regs->nip); + } else if (err == -EINVAL) { + /* didn't recognize the instruction */ + printk(KERN_ERR "unrecognized spe instruction " + "in %s at %lx\n", current->comm, regs->nip); + } else { + _exception(SIGFPE, regs, code, regs->nip); + } - _exception(SIGFPE, regs, code, regs->nip); return; } + +void SPEFloatingPointRoundException(struct pt_regs *regs) +{ + extern int speround_handler(struct pt_regs *regs); + int err; + + preempt_disable(); + if (regs->msr & MSR_SPE) + giveup_spe(current); + preempt_enable(); + + regs->nip -= 4; + err = speround_handler(regs); + if (err == 0) { + regs->nip += 4; /* skip emulated instruction */ + emulate_single_step(regs); + return; + } + + if (err == -EFAULT) { + /* got an error reading the instruction */ + _exception(SIGSEGV, regs, SEGV_ACCERR, regs->nip); + } else if (err == -EINVAL) { + /* didn't recognize the instruction */ + printk(KERN_ERR "unrecognized spe instruction " + "in %s at %lx\n", current->comm, regs->nip); + } else { + _exception(SIGFPE, regs, 0, regs->nip); + return; + } +} #endif /* diff --git a/arch/powerpc/kernel/vdso32/gettimeofday.S b/arch/powerpc/kernel/vdso32/gettimeofday.S index 72ca26d..ee038d4 100644 --- a/arch/powerpc/kernel/vdso32/gettimeofday.S +++ b/arch/powerpc/kernel/vdso32/gettimeofday.S @@ -16,6 +16,13 @@ #include <asm/asm-offsets.h> #include <asm/unistd.h> +/* Offset for the low 32-bit part of a field of long type */ +#ifdef CONFIG_PPC64 +#define LOPART 4 +#else +#define LOPART 0 +#endif + .text /* * Exact prototype of gettimeofday @@ -90,101 +97,53 @@ V_FUNCTION_BEGIN(__kernel_clock_gettime) mflr r12 /* r12 saves lr */ .cfi_register lr,r12 - mr r10,r3 /* r10 saves id */ mr r11,r4 /* r11 saves tp */ bl __get_datapage@local /* get data page */ mr r9,r3 /* datapage ptr in r9 */ - beq cr1,50f /* if monotonic -> jump there */ - - /* - * CLOCK_REALTIME - */ - - bl __do_get_xsec@local /* get xsec from tb & kernel */ - bne- 98f /* out of line -> do syscall */ - - /* seconds are xsec >> 20 */ - rlwinm r5,r4,12,20,31 - rlwimi r5,r3,12,0,19 - stw r5,TSPC32_TV_SEC(r11) - /* get remaining xsec and convert to nsec. we scale - * up remaining xsec by 12 bits and get the top 32 bits - * of the multiplication, then we multiply by 1000 - */ - rlwinm r5,r4,12,0,19 - lis r6,1000000@h - ori r6,r6,1000000@l - mulhwu r5,r5,r6 - mulli r5,r5,1000 - stw r5,TSPC32_TV_NSEC(r11) - mtlr r12 - crclr cr0*4+so - li r3,0 - blr +50: bl __do_get_tspec@local /* get sec/nsec from tb & kernel */ + bne cr1,80f /* not monotonic -> all done */ /* * CLOCK_MONOTONIC */ -50: bl __do_get_xsec@local /* get xsec from tb & kernel */ - bne- 98f /* out of line -> do syscall */ - - /* seconds are xsec >> 20 */ - rlwinm r6,r4,12,20,31 - rlwimi r6,r3,12,0,19 - - /* get remaining xsec and convert to nsec. we scale - * up remaining xsec by 12 bits and get the top 32 bits - * of the multiplication, then we multiply by 1000 - */ - rlwinm r7,r4,12,0,19 - lis r5,1000000@h - ori r5,r5,1000000@l - mulhwu r7,r7,r5 - mulli r7,r7,1000 - /* now we must fixup using wall to monotonic. We need to snapshot * that value and do the counter trick again. Fortunately, we still * have the counter value in r8 that was returned by __do_get_xsec. - * At this point, r6,r7 contain our sec/nsec values, r3,r4 and r5 - * can be used + * At this point, r3,r4 contain our sec/nsec values, r5 and r6 + * can be used, r7 contains NSEC_PER_SEC. */ - lwz r3,WTOM_CLOCK_SEC(r9) - lwz r4,WTOM_CLOCK_NSEC(r9) + lwz r5,WTOM_CLOCK_SEC(r9) + lwz r6,WTOM_CLOCK_NSEC(r9) - /* We now have our result in r3,r4. We create a fake dependency - * on that result and re-check the counter + /* We now have our offset in r5,r6. We create a fake dependency + * on that value and re-check the counter */ - or r5,r4,r3 - xor r0,r5,r5 + or r0,r6,r5 + xor r0,r0,r0 add r9,r9,r0 -#ifdef CONFIG_PPC64 - lwz r0,(CFG_TB_UPDATE_COUNT+4)(r9) -#else - lwz r0,(CFG_TB_UPDATE_COUNT)(r9) -#endif + lwz r0,(CFG_TB_UPDATE_COUNT+LOPART)(r9) cmpl cr0,r8,r0 /* check if updated */ bne- 50b - /* Calculate and store result. Note that this mimmics the C code, + /* Calculate and store result. Note that this mimics the C code, * which may cause funny results if nsec goes negative... is that * possible at all ? */ - add r3,r3,r6 - add r4,r4,r7 - lis r5,NSEC_PER_SEC@h - ori r5,r5,NSEC_PER_SEC@l - cmpl cr0,r4,r5 - cmpli cr1,r4,0 + add r3,r3,r5 + add r4,r4,r6 + cmpw cr0,r4,r7 + cmpwi cr1,r4,0 blt 1f - subf r4,r5,r4 + subf r4,r7,r4 addi r3,r3,1 -1: bge cr1,1f +1: bge cr1,80f addi r3,r3,-1 - add r4,r4,r5 -1: stw r3,TSPC32_TV_SEC(r11) + add r4,r4,r7 + +80: stw r3,TSPC32_TV_SEC(r11) stw r4,TSPC32_TV_NSEC(r11) mtlr r12 @@ -195,10 +154,6 @@ V_FUNCTION_BEGIN(__kernel_clock_gettime) /* * syscall fallback */ -98: - mtlr r12 - mr r3,r10 - mr r4,r11 99: li r0,__NR_clock_gettime sc @@ -254,11 +209,7 @@ __do_get_xsec: /* Check for update count & load values. We use the low * order 32 bits of the update count */ -#ifdef CONFIG_PPC64 -1: lwz r8,(CFG_TB_UPDATE_COUNT+4)(r9) -#else -1: lwz r8,(CFG_TB_UPDATE_COUNT)(r9) -#endif +1: lwz r8,(CFG_TB_UPDATE_COUNT+LOPART)(r9) andi. r0,r8,1 /* pending update ? loop */ bne- 1b xor r0,r8,r8 /* create dependency */ @@ -305,11 +256,7 @@ __do_get_xsec: or r6,r4,r3 xor r0,r6,r6 add r9,r9,r0 -#ifdef CONFIG_PPC64 - lwz r0,(CFG_TB_UPDATE_COUNT+4)(r9) -#else - lwz r0,(CFG_TB_UPDATE_COUNT)(r9) -#endif + lwz r0,(CFG_TB_UPDATE_COUNT+LOPART)(r9) cmpl cr0,r8,r0 /* check if updated */ bne- 1b @@ -322,3 +269,98 @@ __do_get_xsec: */ 3: blr .cfi_endproc + +/* + * This is the core of clock_gettime(), it returns the current + * time in seconds and nanoseconds in r3 and r4. + * It expects the datapage ptr in r9 and doesn't clobber it. + * It clobbers r0, r5, r6, r10 and returns NSEC_PER_SEC in r7. + * On return, r8 contains the counter value that can be reused. + * This clobbers cr0 but not any other cr field. + */ +__do_get_tspec: + .cfi_startproc + /* Check for update count & load values. We use the low + * order 32 bits of the update count + */ +1: lwz r8,(CFG_TB_UPDATE_COUNT+LOPART)(r9) + andi. r0,r8,1 /* pending update ? loop */ + bne- 1b + xor r0,r8,r8 /* create dependency */ + add r9,r9,r0 + + /* Load orig stamp (offset to TB) */ + lwz r5,CFG_TB_ORIG_STAMP(r9) + lwz r6,(CFG_TB_ORIG_STAMP+4)(r9) + + /* Get a stable TB value */ +2: mftbu r3 + mftbl r4 + mftbu r0 + cmpl cr0,r3,r0 + bne- 2b + + /* Subtract tb orig stamp and shift left 12 bits. + */ + subfc r7,r6,r4 + subfe r0,r5,r3 + slwi r0,r0,12 + rlwimi. r0,r7,12,20,31 + slwi r7,r7,12 + + /* Load scale factor & do multiplication */ + lwz r5,CFG_TB_TO_XS(r9) /* load values */ + lwz r6,(CFG_TB_TO_XS+4)(r9) + mulhwu r3,r7,r6 + mullw r10,r7,r5 + mulhwu r4,r7,r5 + addc r10,r3,r10 + li r3,0 + + beq+ 4f /* skip high part computation if 0 */ + mulhwu r3,r0,r5 + mullw r7,r0,r5 + mulhwu r5,r0,r6 + mullw r6,r0,r6 + adde r4,r4,r7 + addze r3,r3 + addc r4,r4,r5 + addze r3,r3 + addc r10,r10,r6 + +4: addze r4,r4 /* add in carry */ + lis r7,NSEC_PER_SEC@h + ori r7,r7,NSEC_PER_SEC@l + mulhwu r4,r4,r7 /* convert to nanoseconds */ + + /* At this point, we have seconds & nanoseconds since the xtime + * stamp in r3+CA and r4. Load & add the xtime stamp. + */ +#ifdef CONFIG_PPC64 + lwz r5,STAMP_XTIME+TSPC64_TV_SEC+LOPART(r9) + lwz r6,STAMP_XTIME+TSPC64_TV_NSEC+LOPART(r9) +#else + lwz r5,STAMP_XTIME+TSPC32_TV_SEC(r9) + lwz r6,STAMP_XTIME+TSPC32_TV_NSEC(r9) +#endif + add r4,r4,r6 + adde r3,r3,r5 + + /* We now have our result in r3,r4. We create a fake dependency + * on that result and re-check the counter + */ + or r6,r4,r3 + xor r0,r6,r6 + add r9,r9,r0 + lwz r0,(CFG_TB_UPDATE_COUNT+LOPART)(r9) + cmpl cr0,r8,r0 /* check if updated */ + bne- 1b + + /* check for nanosecond overflow and adjust if necessary */ + cmpw r4,r7 + bltlr /* all done if no overflow */ + subf r4,r7,r4 /* adjust if overflow */ + addi r3,r3,1 + + blr + .cfi_endproc diff --git a/arch/powerpc/kernel/vdso64/gettimeofday.S b/arch/powerpc/kernel/vdso64/gettimeofday.S index c6401f9..262cd58 100644 --- a/arch/powerpc/kernel/vdso64/gettimeofday.S +++ b/arch/powerpc/kernel/vdso64/gettimeofday.S @@ -75,90 +75,49 @@ V_FUNCTION_BEGIN(__kernel_clock_gettime) mflr r12 /* r12 saves lr */ .cfi_register lr,r12 - mr r10,r3 /* r10 saves id */ mr r11,r4 /* r11 saves tp */ bl V_LOCAL_FUNC(__get_datapage) /* get data page */ - beq cr1,50f /* if monotonic -> jump there */ - - /* - * CLOCK_REALTIME - */ - - bl V_LOCAL_FUNC(__do_get_xsec) /* get xsec from tb & kernel */ - - lis r7,15 /* r7 = 1000000 = USEC_PER_SEC */ - ori r7,r7,16960 - rldicl r5,r4,44,20 /* r5 = sec = xsec / XSEC_PER_SEC */ - rldicr r6,r5,20,43 /* r6 = sec * XSEC_PER_SEC */ - std r5,TSPC64_TV_SEC(r11) /* store sec in tv */ - subf r0,r6,r4 /* r0 = xsec = (xsec - r6) */ - mulld r0,r0,r7 /* usec = (xsec * USEC_PER_SEC) / - * XSEC_PER_SEC - */ - rldicl r0,r0,44,20 - mulli r0,r0,1000 /* nsec = usec * 1000 */ - std r0,TSPC64_TV_NSEC(r11) /* store nsec in tp */ - - mtlr r12 - crclr cr0*4+so - li r3,0 - blr +50: bl V_LOCAL_FUNC(__do_get_tspec) /* get time from tb & kernel */ + bne cr1,80f /* if not monotonic, all done */ /* * CLOCK_MONOTONIC */ -50: bl V_LOCAL_FUNC(__do_get_xsec) /* get xsec from tb & kernel */ - - lis r7,15 /* r7 = 1000000 = USEC_PER_SEC */ - ori r7,r7,16960 - rldicl r5,r4,44,20 /* r5 = sec = xsec / XSEC_PER_SEC */ - rldicr r6,r5,20,43 /* r6 = sec * XSEC_PER_SEC */ - subf r0,r6,r4 /* r0 = xsec = (xsec - r6) */ - mulld r0,r0,r7 /* usec = (xsec * USEC_PER_SEC) / - * XSEC_PER_SEC - */ - rldicl r6,r0,44,20 - mulli r6,r6,1000 /* nsec = usec * 1000 */ - /* now we must fixup using wall to monotonic. We need to snapshot * that value and do the counter trick again. Fortunately, we still - * have the counter value in r8 that was returned by __do_get_xsec. - * At this point, r5,r6 contain our sec/nsec values. - * can be used + * have the counter value in r8 that was returned by __do_get_tspec. + * At this point, r4,r5 contain our sec/nsec values. */ - lwa r4,WTOM_CLOCK_SEC(r3) - lwa r7,WTOM_CLOCK_NSEC(r3) + lwa r6,WTOM_CLOCK_SEC(r3) + lwa r9,WTOM_CLOCK_NSEC(r3) - /* We now have our result in r4,r7. We create a fake dependency + /* We now have our result in r6,r9. We create a fake dependency * on that result and re-check the counter */ - or r9,r4,r7 - xor r0,r9,r9 + or r0,r6,r9 + xor r0,r0,r0 add r3,r3,r0 ld r0,CFG_TB_UPDATE_COUNT(r3) cmpld cr0,r0,r8 /* check if updated */ bne- 50b - /* Calculate and store result. Note that this mimmics the C code, - * which may cause funny results if nsec goes negative... is that - * possible at all ? + /* Add wall->monotonic offset and check for overflow or underflow. */ - add r4,r4,r5 - add r7,r7,r6 - lis r9,NSEC_PER_SEC@h - ori r9,r9,NSEC_PER_SEC@l - cmpl cr0,r7,r9 - cmpli cr1,r7,0 + add r4,r4,r6 + add r5,r5,r9 + cmpd cr0,r5,r7 + cmpdi cr1,r5,0 blt 1f - subf r7,r9,r7 + subf r5,r7,r5 addi r4,r4,1 -1: bge cr1,1f +1: bge cr1,80f addi r4,r4,-1 - add r7,r7,r9 -1: std r4,TSPC64_TV_SEC(r11) - std r7,TSPC64_TV_NSEC(r11) + add r5,r5,r7 + +80: std r4,TSPC64_TV_SEC(r11) + std r5,TSPC64_TV_NSEC(r11) mtlr r12 crclr cr0*4+so @@ -168,10 +127,6 @@ V_FUNCTION_BEGIN(__kernel_clock_gettime) /* * syscall fallback */ -98: - mtlr r12 - mr r3,r10 - mr r4,r11 99: li r0,__NR_clock_gettime sc @@ -253,3 +208,59 @@ V_FUNCTION_BEGIN(__do_get_xsec) blr .cfi_endproc V_FUNCTION_END(__do_get_xsec) + +/* + * This is the core of clock_gettime(), it returns the current + * time in seconds and nanoseconds in r4 and r5. + * It expects the datapage ptr in r3 and doesn't clobber it. + * It clobbers r0 and r6 and returns NSEC_PER_SEC in r7. + * On return, r8 contains the counter value that can be reused. + * This clobbers cr0 but not any other cr field. + */ +V_FUNCTION_BEGIN(__do_get_tspec) + .cfi_startproc + /* check for update count & load values */ +1: ld r8,CFG_TB_UPDATE_COUNT(r3) + andi. r0,r8,1 /* pending update ? loop */ + bne- 1b + xor r0,r8,r8 /* create dependency */ + add r3,r3,r0 + + /* Get TB & offset it. We use the MFTB macro which will generate + * workaround code for Cell. + */ + MFTB(r7) + ld r9,CFG_TB_ORIG_STAMP(r3) + subf r7,r9,r7 + + /* Scale result */ + ld r5,CFG_TB_TO_XS(r3) + sldi r7,r7,12 /* compute time since stamp_xtime */ + mulhdu r6,r7,r5 /* in units of 2^-32 seconds */ + + /* Add stamp since epoch */ + ld r4,STAMP_XTIME+TSPC64_TV_SEC(r3) + ld r5,STAMP_XTIME+TSPC64_TV_NSEC(r3) + or r0,r4,r5 + or r0,r0,r6 + xor r0,r0,r0 + add r3,r3,r0 + ld r0,CFG_TB_UPDATE_COUNT(r3) + cmpld r0,r8 /* check if updated */ + bne- 1b /* reload if so */ + + /* convert to seconds & nanoseconds and add to stamp */ + lis r7,NSEC_PER_SEC@h + ori r7,r7,NSEC_PER_SEC@l + mulhwu r0,r6,r7 /* compute nanoseconds and */ + srdi r6,r6,32 /* seconds since stamp_xtime */ + clrldi r0,r0,32 + add r5,r5,r0 /* add nanoseconds together */ + cmpd r5,r7 /* overflow? */ + add r4,r4,r6 + bltlr /* all done if no overflow */ + subf r5,r7,r5 /* if overflow, adjust */ + addi r4,r4,1 + blr + .cfi_endproc +V_FUNCTION_END(__do_get_tspec) diff --git a/arch/powerpc/lib/copyuser_64.S b/arch/powerpc/lib/copyuser_64.S index 25ec537..70693a5 100644 --- a/arch/powerpc/lib/copyuser_64.S +++ b/arch/powerpc/lib/copyuser_64.S @@ -26,11 +26,24 @@ _GLOBAL(__copy_tofrom_user) andi. r6,r6,7 PPC_MTOCRF 0x01,r5 blt cr1,.Lshort_copy +/* Below we want to nop out the bne if we're on a CPU that has the + * CPU_FTR_UNALIGNED_LD_STD bit set and the CPU_FTR_CP_USE_DCBTZ bit + * cleared. + * At the time of writing the only CPU that has this combination of bits + * set is Power6. + */ +BEGIN_FTR_SECTION + nop +FTR_SECTION_ELSE bne .Ldst_unaligned +ALT_FTR_SECTION_END(CPU_FTR_UNALIGNED_LD_STD | CPU_FTR_CP_USE_DCBTZ, \ + CPU_FTR_UNALIGNED_LD_STD) .Ldst_aligned: - andi. r0,r4,7 addi r3,r3,-16 +BEGIN_FTR_SECTION + andi. r0,r4,7 bne .Lsrc_unaligned +END_FTR_SECTION_IFCLR(CPU_FTR_UNALIGNED_LD_STD) srdi r7,r5,4 20: ld r9,0(r4) addi r4,r4,-8 @@ -138,7 +151,7 @@ _GLOBAL(__copy_tofrom_user) PPC_MTOCRF 0x01,r6 /* put #bytes to 8B bdry into cr7 */ subf r5,r6,r5 li r7,0 - cmpldi r1,r5,16 + cmpldi cr1,r5,16 bf cr7*4+3,1f 35: lbz r0,0(r4) 81: stb r0,0(r3) diff --git a/arch/powerpc/lib/dma-noncoherent.c b/arch/powerpc/lib/dma-noncoherent.c index 31734c0..2b1ce18 100644 --- a/arch/powerpc/lib/dma-noncoherent.c +++ b/arch/powerpc/lib/dma-noncoherent.c @@ -320,7 +320,6 @@ static int __init dma_alloc_init(void) ret = -ENOMEM; break; } - WARN_ON(!pmd_none(*pmd)); pte = pte_alloc_kernel(pmd, CONSISTENT_BASE); if (!pte) { diff --git a/arch/powerpc/lib/memcpy_64.S b/arch/powerpc/lib/memcpy_64.S index 3f13112..fe2d34e 100644 --- a/arch/powerpc/lib/memcpy_64.S +++ b/arch/powerpc/lib/memcpy_64.S @@ -18,11 +18,23 @@ _GLOBAL(memcpy) andi. r6,r6,7 dcbt 0,r4 blt cr1,.Lshort_copy +/* Below we want to nop out the bne if we're on a CPU that has the + CPU_FTR_UNALIGNED_LD_STD bit set and the CPU_FTR_CP_USE_DCBTZ bit + cleared. + At the time of writing the only CPU that has this combination of bits + set is Power6. */ +BEGIN_FTR_SECTION + nop +FTR_SECTION_ELSE bne .Ldst_unaligned +ALT_FTR_SECTION_END(CPU_FTR_UNALIGNED_LD_STD | CPU_FTR_CP_USE_DCBTZ, \ + CPU_FTR_UNALIGNED_LD_STD) .Ldst_aligned: - andi. r0,r4,7 addi r3,r3,-16 +BEGIN_FTR_SECTION + andi. r0,r4,7 bne .Lsrc_unaligned +END_FTR_SECTION_IFCLR(CPU_FTR_UNALIGNED_LD_STD) srdi r7,r5,4 ld r9,0(r4) addi r4,r4,-8 @@ -131,7 +143,7 @@ _GLOBAL(memcpy) PPC_MTOCRF 0x01,r6 # put #bytes to 8B bdry into cr7 subf r5,r6,r5 li r7,0 - cmpldi r1,r5,16 + cmpldi cr1,r5,16 bf cr7*4+3,1f lbz r0,0(r4) stb r0,0(r3) diff --git a/arch/powerpc/math-emu/Makefile b/arch/powerpc/math-emu/Makefile index 03aa98d..f9e506a 100644 --- a/arch/powerpc/math-emu/Makefile +++ b/arch/powerpc/math-emu/Makefile @@ -11,6 +11,8 @@ obj-$(CONFIG_MATH_EMULATION) += fabs.o fadd.o fadds.o fcmpo.o fcmpu.o \ mcrfs.o mffs.o mtfsb0.o mtfsb1.o \ mtfsf.o mtfsfi.o stfiwx.o stfs.o +obj-$(CONFIG_SPE) += math_efp.o + CFLAGS_fabs.o = -fno-builtin-fabs CFLAGS_math.o = -fno-builtin-fabs diff --git a/arch/powerpc/math-emu/fadd.c b/arch/powerpc/math-emu/fadd.c index 04d3b4a..0158a16 100644 --- a/arch/powerpc/math-emu/fadd.c +++ b/arch/powerpc/math-emu/fadd.c @@ -13,7 +13,6 @@ fadd(void *frD, void *frA, void *frB) FP_DECL_D(B); FP_DECL_D(R); FP_DECL_EX; - int ret = 0; #ifdef DEBUG printk("%s: %p %p %p\n", __func__, frD, frA, frB); diff --git a/arch/powerpc/math-emu/fcmpo.c b/arch/powerpc/math-emu/fcmpo.c index b5dc449..5bce011 100644 --- a/arch/powerpc/math-emu/fcmpo.c +++ b/arch/powerpc/math-emu/fcmpo.c @@ -14,7 +14,6 @@ fcmpo(u32 *ccr, int crfD, void *frA, void *frB) FP_DECL_EX; int code[4] = { (1 << 3), (1 << 1), (1 << 2), (1 << 0) }; long cmp; - int ret = 0; #ifdef DEBUG printk("%s: %p (%08x) %d %p %p\n", __func__, ccr, *ccr, crfD, frA, frB); @@ -29,7 +28,7 @@ fcmpo(u32 *ccr, int crfD, void *frA, void *frB) #endif if (A_c == FP_CLS_NAN || B_c == FP_CLS_NAN) - ret |= EFLAG_VXVC; + FP_SET_EXCEPTION(EFLAG_VXVC); FP_CMP_D(cmp, A, B, 2); cmp = code[(cmp + 1) & 3]; @@ -44,5 +43,5 @@ fcmpo(u32 *ccr, int crfD, void *frA, void *frB) printk("CR: %08x\n", *ccr); #endif - return ret; + return FP_CUR_EXCEPTIONS; } diff --git a/arch/powerpc/math-emu/fdiv.c b/arch/powerpc/math-emu/fdiv.c index 2db1509..a29239c 100644 --- a/arch/powerpc/math-emu/fdiv.c +++ b/arch/powerpc/math-emu/fdiv.c @@ -13,7 +13,6 @@ fdiv(void *frD, void *frA, void *frB) FP_DECL_D(B); FP_DECL_D(R); FP_DECL_EX; - int ret = 0; #ifdef DEBUG printk("%s: %p %p %p\n", __func__, frD, frA, frB); @@ -28,22 +27,22 @@ fdiv(void *frD, void *frA, void *frB) #endif if (A_c == FP_CLS_ZERO && B_c == FP_CLS_ZERO) { - ret |= EFLAG_VXZDZ; + FP_SET_EXCEPTION(EFLAG_VXZDZ); #ifdef DEBUG printk("%s: FPSCR_VXZDZ raised\n", __func__); #endif } if (A_c == FP_CLS_INF && B_c == FP_CLS_INF) { - ret |= EFLAG_VXIDI; + FP_SET_EXCEPTION(EFLAG_VXIDI); #ifdef DEBUG printk("%s: FPSCR_VXIDI raised\n", __func__); #endif } if (B_c == FP_CLS_ZERO && A_c != FP_CLS_ZERO) { - ret |= EFLAG_DIVZERO; + FP_SET_EXCEPTION(EFLAG_DIVZERO); if (__FPU_TRAP_P(EFLAG_DIVZERO)) - return ret; + return FP_CUR_EXCEPTIONS; } FP_DIV_D(R, A, B); diff --git a/arch/powerpc/math-emu/fdivs.c b/arch/powerpc/math-emu/fdivs.c index 797f6a9..526bc26 100644 --- a/arch/powerpc/math-emu/fdivs.c +++ b/arch/powerpc/math-emu/fdivs.c @@ -14,7 +14,6 @@ fdivs(void *frD, void *frA, void *frB) FP_DECL_D(B); FP_DECL_D(R); FP_DECL_EX; - int ret = 0; #ifdef DEBUG printk("%s: %p %p %p\n", __func__, frD, frA, frB); @@ -29,22 +28,22 @@ fdivs(void *frD, void *frA, void *frB) #endif if (A_c == FP_CLS_ZERO && B_c == FP_CLS_ZERO) { - ret |= EFLAG_VXZDZ; + FP_SET_EXCEPTION(EFLAG_VXZDZ); #ifdef DEBUG printk("%s: FPSCR_VXZDZ raised\n", __func__); #endif } if (A_c == FP_CLS_INF && B_c == FP_CLS_INF) { - ret |= EFLAG_VXIDI; + FP_SET_EXCEPTION(EFLAG_VXIDI); #ifdef DEBUG printk("%s: FPSCR_VXIDI raised\n", __func__); #endif } if (B_c == FP_CLS_ZERO && A_c != FP_CLS_ZERO) { - ret |= EFLAG_DIVZERO; + FP_SET_EXCEPTION(EFLAG_DIVZERO); if (__FPU_TRAP_P(EFLAG_DIVZERO)) - return ret; + return FP_CUR_EXCEPTIONS; } FP_DIV_D(R, A, B); diff --git a/arch/powerpc/math-emu/fmadd.c b/arch/powerpc/math-emu/fmadd.c index 925313a..8c3f20a 100644 --- a/arch/powerpc/math-emu/fmadd.c +++ b/arch/powerpc/math-emu/fmadd.c @@ -15,7 +15,6 @@ fmadd(void *frD, void *frA, void *frB, void *frC) FP_DECL_D(C); FP_DECL_D(T); FP_DECL_EX; - int ret = 0; #ifdef DEBUG printk("%s: %p %p %p %p\n", __func__, frD, frA, frB, frC); @@ -33,12 +32,12 @@ fmadd(void *frD, void *frA, void *frB, void *frC) if ((A_c == FP_CLS_INF && C_c == FP_CLS_ZERO) || (A_c == FP_CLS_ZERO && C_c == FP_CLS_INF)) - ret |= EFLAG_VXIMZ; + FP_SET_EXCEPTION(EFLAG_VXIMZ); FP_MUL_D(T, A, C); if (T_s != B_s && T_c == FP_CLS_INF && B_c == FP_CLS_INF) - ret |= EFLAG_VXISI; + FP_SET_EXCEPTION(EFLAG_VXISI); FP_ADD_D(R, T, B); diff --git a/arch/powerpc/math-emu/fmadds.c b/arch/powerpc/math-emu/fmadds.c index aea80ef..794fb31 100644 --- a/arch/powerpc/math-emu/fmadds.c +++ b/arch/powerpc/math-emu/fmadds.c @@ -16,7 +16,6 @@ fmadds(void *frD, void *frA, void *frB, void *frC) FP_DECL_D(C); FP_DECL_D(T); FP_DECL_EX; - int ret = 0; #ifdef DEBUG printk("%s: %p %p %p %p\n", __func__, frD, frA, frB, frC); @@ -34,12 +33,12 @@ fmadds(void *frD, void *frA, void *frB, void *frC) if ((A_c == FP_CLS_INF && C_c == FP_CLS_ZERO) || (A_c == FP_CLS_ZERO && C_c == FP_CLS_INF)) - ret |= EFLAG_VXIMZ; + FP_SET_EXCEPTION(EFLAG_VXIMZ); FP_MUL_D(T, A, C); if (T_s != B_s && T_c == FP_CLS_INF && B_c == FP_CLS_INF) - ret |= EFLAG_VXISI; + FP_SET_EXCEPTION(EFLAG_VXISI); FP_ADD_D(R, T, B); diff --git a/arch/powerpc/math-emu/fmsub.c b/arch/powerpc/math-emu/fmsub.c index a644d52..626f6fe 100644 --- a/arch/powerpc/math-emu/fmsub.c +++ b/arch/powerpc/math-emu/fmsub.c @@ -15,7 +15,6 @@ fmsub(void *frD, void *frA, void *frB, void *frC) FP_DECL_D(C); FP_DECL_D(T); FP_DECL_EX; - int ret = 0; #ifdef DEBUG printk("%s: %p %p %p %p\n", __func__, frD, frA, frB, frC); @@ -33,7 +32,7 @@ fmsub(void *frD, void *frA, void *frB, void *frC) if ((A_c == FP_CLS_INF && C_c == FP_CLS_ZERO) || (A_c == FP_CLS_ZERO && C_c == FP_CLS_INF)) - ret |= EFLAG_VXIMZ; + FP_SET_EXCEPTION(EFLAG_VXIMZ); FP_MUL_D(T, A, C); @@ -41,7 +40,7 @@ fmsub(void *frD, void *frA, void *frB, void *frC) B_s ^= 1; if (T_s != B_s && T_c == FP_CLS_INF && B_c == FP_CLS_INF) - ret |= EFLAG_VXISI; + FP_SET_EXCEPTION(EFLAG_VXISI); FP_ADD_D(R, T, B); diff --git a/arch/powerpc/math-emu/fmsubs.c b/arch/powerpc/math-emu/fmsubs.c index 2fdeeb9..3425bc8 100644 --- a/arch/powerpc/math-emu/fmsubs.c +++ b/arch/powerpc/math-emu/fmsubs.c @@ -16,7 +16,6 @@ fmsubs(void *frD, void *frA, void *frB, void *frC) FP_DECL_D(C); FP_DECL_D(T); FP_DECL_EX; - int ret = 0; #ifdef DEBUG printk("%s: %p %p %p %p\n", __func__, frD, frA, frB, frC); @@ -34,7 +33,7 @@ fmsubs(void *frD, void *frA, void *frB, void *frC) if ((A_c == FP_CLS_INF && C_c == FP_CLS_ZERO) || (A_c == FP_CLS_ZERO && C_c == FP_CLS_INF)) - ret |= EFLAG_VXIMZ; + FP_SET_EXCEPTION(EFLAG_VXIMZ); FP_MUL_D(T, A, C); @@ -42,7 +41,7 @@ fmsubs(void *frD, void *frA, void *frB, void *frC) B_s ^= 1; if (T_s != B_s && T_c == FP_CLS_INF && B_c == FP_CLS_INF) - ret |= EFLAG_VXISI; + FP_SET_EXCEPTION(EFLAG_VXISI); FP_ADD_D(R, T, B); diff --git a/arch/powerpc/math-emu/fmul.c b/arch/powerpc/math-emu/fmul.c index 391fd17..2c19297 100644 --- a/arch/powerpc/math-emu/fmul.c +++ b/arch/powerpc/math-emu/fmul.c @@ -13,7 +13,6 @@ fmul(void *frD, void *frA, void *frB) FP_DECL_D(B); FP_DECL_D(R); FP_DECL_EX; - int ret = 0; #ifdef DEBUG printk("%s: %p %p %p\n", __func__, frD, frA, frB); @@ -31,7 +30,7 @@ fmul(void *frD, void *frA, void *frB) if ((A_c == FP_CLS_INF && B_c == FP_CLS_ZERO) || (A_c == FP_CLS_ZERO && B_c == FP_CLS_INF)) - ret |= EFLAG_VXIMZ; + FP_SET_EXCEPTION(EFLAG_VXIMZ); FP_MUL_D(R, A, B); diff --git a/arch/powerpc/math-emu/fmuls.c b/arch/powerpc/math-emu/fmuls.c index 2d3ec5f..f5ad5c9 100644 --- a/arch/powerpc/math-emu/fmuls.c +++ b/arch/powerpc/math-emu/fmuls.c @@ -14,7 +14,6 @@ fmuls(void *frD, void *frA, void *frB) FP_DECL_D(B); FP_DECL_D(R); FP_DECL_EX; - int ret = 0; #ifdef DEBUG printk("%s: %p %p %p\n", __func__, frD, frA, frB); @@ -32,7 +31,7 @@ fmuls(void *frD, void *frA, void *frB) if ((A_c == FP_CLS_INF && B_c == FP_CLS_ZERO) || (A_c == FP_CLS_ZERO && B_c == FP_CLS_INF)) - ret |= EFLAG_VXIMZ; + FP_SET_EXCEPTION(EFLAG_VXIMZ); FP_MUL_D(R, A, B); diff --git a/arch/powerpc/math-emu/fnmadd.c b/arch/powerpc/math-emu/fnmadd.c index 2497b86..e817bc5 100644 --- a/arch/powerpc/math-emu/fnmadd.c +++ b/arch/powerpc/math-emu/fnmadd.c @@ -15,7 +15,6 @@ fnmadd(void *frD, void *frA, void *frB, void *frC) FP_DECL_D(C); FP_DECL_D(T); FP_DECL_EX; - int ret = 0; #ifdef DEBUG printk("%s: %p %p %p %p\n", __func__, frD, frA, frB, frC); @@ -33,12 +32,12 @@ fnmadd(void *frD, void *frA, void *frB, void *frC) if ((A_c == FP_CLS_INF && C_c == FP_CLS_ZERO) || (A_c == FP_CLS_ZERO && C_c == FP_CLS_INF)) - ret |= EFLAG_VXIMZ; + FP_SET_EXCEPTION(EFLAG_VXIMZ); FP_MUL_D(T, A, C); if (T_s != B_s && T_c == FP_CLS_INF && B_c == FP_CLS_INF) - ret |= EFLAG_VXISI; + FP_SET_EXCEPTION(EFLAG_VXISI); FP_ADD_D(R, T, B); diff --git a/arch/powerpc/math-emu/fnmadds.c b/arch/powerpc/math-emu/fnmadds.c index ee9d71e..4db4b7d 100644 --- a/arch/powerpc/math-emu/fnmadds.c +++ b/arch/powerpc/math-emu/fnmadds.c @@ -16,7 +16,6 @@ fnmadds(void *frD, void *frA, void *frB, void *frC) FP_DECL_D(C); FP_DECL_D(T); FP_DECL_EX; - int ret = 0; #ifdef DEBUG printk("%s: %p %p %p %p\n", __func__, frD, frA, frB, frC); @@ -34,12 +33,12 @@ fnmadds(void *frD, void *frA, void *frB, void *frC) if ((A_c == FP_CLS_INF && C_c == FP_CLS_ZERO) || (A_c == FP_CLS_ZERO && C_c == FP_CLS_INF)) - ret |= EFLAG_VXIMZ; + FP_SET_EXCEPTION(EFLAG_VXIMZ); FP_MUL_D(T, A, C); if (T_s != B_s && T_c == FP_CLS_INF && B_c == FP_CLS_INF) - ret |= EFLAG_VXISI; + FP_SET_EXCEPTION(EFLAG_VXISI); FP_ADD_D(R, T, B); diff --git a/arch/powerpc/math-emu/fnmsub.c b/arch/powerpc/math-emu/fnmsub.c index 3885a77..f65979f 100644 --- a/arch/powerpc/math-emu/fnmsub.c +++ b/arch/powerpc/math-emu/fnmsub.c @@ -15,7 +15,6 @@ fnmsub(void *frD, void *frA, void *frB, void *frC) FP_DECL_D(C); FP_DECL_D(T); FP_DECL_EX; - int ret = 0; #ifdef DEBUG printk("%s: %p %p %p %p\n", __func__, frD, frA, frB, frC); @@ -33,7 +32,7 @@ fnmsub(void *frD, void *frA, void *frB, void *frC) if ((A_c == FP_CLS_INF && C_c == FP_CLS_ZERO) || (A_c == FP_CLS_ZERO && C_c == FP_CLS_INF)) - ret |= EFLAG_VXIMZ; + FP_SET_EXCEPTION(EFLAG_VXIMZ); FP_MUL_D(T, A, C); @@ -41,7 +40,7 @@ fnmsub(void *frD, void *frA, void *frB, void *frC) B_s ^= 1; if (T_s != B_s && T_c == FP_CLS_INF && B_c == FP_CLS_INF) - ret |= EFLAG_VXISI; + FP_SET_EXCEPTION(EFLAG_VXISI); FP_ADD_D(R, T, B); diff --git a/arch/powerpc/math-emu/fnmsubs.c b/arch/powerpc/math-emu/fnmsubs.c index f835dfe..9021dac 100644 --- a/arch/powerpc/math-emu/fnmsubs.c +++ b/arch/powerpc/math-emu/fnmsubs.c @@ -16,7 +16,6 @@ fnmsubs(void *frD, void *frA, void *frB, void *frC) FP_DECL_D(C); FP_DECL_D(T); FP_DECL_EX; - int ret = 0; #ifdef DEBUG printk("%s: %p %p %p %p\n", __func__, frD, frA, frB, frC); @@ -34,7 +33,7 @@ fnmsubs(void *frD, void *frA, void *frB, void *frC) if ((A_c == FP_CLS_INF && C_c == FP_CLS_ZERO) || (A_c == FP_CLS_ZERO && C_c == FP_CLS_INF)) - ret |= EFLAG_VXIMZ; + FP_SET_EXCEPTION(EFLAG_VXIMZ); FP_MUL_D(T, A, C); @@ -42,7 +41,7 @@ fnmsubs(void *frD, void *frA, void *frB, void *frC) B_s ^= 1; if (T_s != B_s && T_c == FP_CLS_INF && B_c == FP_CLS_INF) - ret |= EFLAG_VXISI; + FP_SET_EXCEPTION(EFLAG_VXISI); FP_ADD_D(R, T, B); diff --git a/arch/powerpc/math-emu/fsqrt.c b/arch/powerpc/math-emu/fsqrt.c index 3e90072..a55fc7d 100644 --- a/arch/powerpc/math-emu/fsqrt.c +++ b/arch/powerpc/math-emu/fsqrt.c @@ -12,7 +12,6 @@ fsqrt(void *frD, void *frB) FP_DECL_D(B); FP_DECL_D(R); FP_DECL_EX; - int ret = 0; #ifdef DEBUG printk("%s: %p %p %p %p\n", __func__, frD, frB); @@ -25,9 +24,9 @@ fsqrt(void *frD, void *frB) #endif if (B_s && B_c != FP_CLS_ZERO) - ret |= EFLAG_VXSQRT; + FP_SET_EXCEPTION(EFLAG_VXSQRT); if (B_c == FP_CLS_NAN) - ret |= EFLAG_VXSNAN; + FP_SET_EXCEPTION(EFLAG_VXSNAN); FP_SQRT_D(R, B); diff --git a/arch/powerpc/math-emu/fsqrts.c b/arch/powerpc/math-emu/fsqrts.c index 2843be9..31dccbf 100644 --- a/arch/powerpc/math-emu/fsqrts.c +++ b/arch/powerpc/math-emu/fsqrts.c @@ -13,7 +13,6 @@ fsqrts(void *frD, void *frB) FP_DECL_D(B); FP_DECL_D(R); FP_DECL_EX; - int ret = 0; #ifdef DEBUG printk("%s: %p %p %p %p\n", __func__, frD, frB); @@ -26,9 +25,9 @@ fsqrts(void *frD, void *frB) #endif if (B_s && B_c != FP_CLS_ZERO) - ret |= EFLAG_VXSQRT; + FP_SET_EXCEPTION(EFLAG_VXSQRT); if (B_c == FP_CLS_NAN) - ret |= EFLAG_VXSNAN; + FP_SET_EXCEPTION(EFLAG_VXSNAN); FP_SQRT_D(R, B); diff --git a/arch/powerpc/math-emu/fsub.c b/arch/powerpc/math-emu/fsub.c index 78b0944..02c5dff 100644 --- a/arch/powerpc/math-emu/fsub.c +++ b/arch/powerpc/math-emu/fsub.c @@ -13,7 +13,6 @@ fsub(void *frD, void *frA, void *frB) FP_DECL_D(B); FP_DECL_D(R); FP_DECL_EX; - int ret = 0; #ifdef DEBUG printk("%s: %p %p %p\n", __func__, frD, frA, frB); @@ -31,7 +30,7 @@ fsub(void *frD, void *frA, void *frB) B_s ^= 1; if (A_s != B_s && A_c == FP_CLS_INF && B_c == FP_CLS_INF) - ret |= EFLAG_VXISI; + FP_SET_EXCEPTION(EFLAG_VXISI); FP_ADD_D(R, A, B); diff --git a/arch/powerpc/math-emu/fsubs.c b/arch/powerpc/math-emu/fsubs.c index d3bf908..5d9b18c 100644 --- a/arch/powerpc/math-emu/fsubs.c +++ b/arch/powerpc/math-emu/fsubs.c @@ -14,7 +14,6 @@ fsubs(void *frD, void *frA, void *frB) FP_DECL_D(B); FP_DECL_D(R); FP_DECL_EX; - int ret = 0; #ifdef DEBUG printk("%s: %p %p %p\n", __func__, frD, frA, frB); @@ -32,7 +31,7 @@ fsubs(void *frD, void *frA, void *frB) B_s ^= 1; if (A_s != B_s && A_c == FP_CLS_INF && B_c == FP_CLS_INF) - ret |= EFLAG_VXISI; + FP_SET_EXCEPTION(EFLAG_VXISI); FP_ADD_D(R, A, B); diff --git a/arch/powerpc/math-emu/math_efp.c b/arch/powerpc/math-emu/math_efp.c new file mode 100644 index 0000000..41f4ef3 --- /dev/null +++ b/arch/powerpc/math-emu/math_efp.c @@ -0,0 +1,720 @@ +/* + * arch/powerpc/math-emu/math_efp.c + * + * Copyright (C) 2006-2008 Freescale Semiconductor, Inc. All rights reserved. + * + * Author: Ebony Zhu, <ebony.zhu@freescale.com> + * Yu Liu, <yu.liu@freescale.com> + * + * Derived from arch/alpha/math-emu/math.c + * arch/powerpc/math-emu/math.c + * + * Description: + * This file is the exception handler to make E500 SPE instructions + * fully comply with IEEE-754 floating point standard. + * + * 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/types.h> + +#include <asm/uaccess.h> +#include <asm/reg.h> + +#define FP_EX_BOOKE_E500_SPE +#include <asm/sfp-machine.h> + +#include <math-emu/soft-fp.h> +#include <math-emu/single.h> +#include <math-emu/double.h> + +#define EFAPU 0x4 + +#define VCT 0x4 +#define SPFP 0x6 +#define DPFP 0x7 + +#define EFSADD 0x2c0 +#define EFSSUB 0x2c1 +#define EFSABS 0x2c4 +#define EFSNABS 0x2c5 +#define EFSNEG 0x2c6 +#define EFSMUL 0x2c8 +#define EFSDIV 0x2c9 +#define EFSCMPGT 0x2cc +#define EFSCMPLT 0x2cd +#define EFSCMPEQ 0x2ce +#define EFSCFD 0x2cf +#define EFSCFSI 0x2d1 +#define EFSCTUI 0x2d4 +#define EFSCTSI 0x2d5 +#define EFSCTUF 0x2d6 +#define EFSCTSF 0x2d7 +#define EFSCTUIZ 0x2d8 +#define EFSCTSIZ 0x2da + +#define EVFSADD 0x280 +#define EVFSSUB 0x281 +#define EVFSABS 0x284 +#define EVFSNABS 0x285 +#define EVFSNEG 0x286 +#define EVFSMUL 0x288 +#define EVFSDIV 0x289 +#define EVFSCMPGT 0x28c +#define EVFSCMPLT 0x28d +#define EVFSCMPEQ 0x28e +#define EVFSCTUI 0x294 +#define EVFSCTSI 0x295 +#define EVFSCTUF 0x296 +#define EVFSCTSF 0x297 +#define EVFSCTUIZ 0x298 +#define EVFSCTSIZ 0x29a + +#define EFDADD 0x2e0 +#define EFDSUB 0x2e1 +#define EFDABS 0x2e4 +#define EFDNABS 0x2e5 +#define EFDNEG 0x2e6 +#define EFDMUL 0x2e8 +#define EFDDIV 0x2e9 +#define EFDCTUIDZ 0x2ea +#define EFDCTSIDZ 0x2eb +#define EFDCMPGT 0x2ec +#define EFDCMPLT 0x2ed +#define EFDCMPEQ 0x2ee +#define EFDCFS 0x2ef +#define EFDCTUI 0x2f4 +#define EFDCTSI 0x2f5 +#define EFDCTUF 0x2f6 +#define EFDCTSF 0x2f7 +#define EFDCTUIZ 0x2f8 +#define EFDCTSIZ 0x2fa + +#define AB 2 +#define XA 3 +#define XB 4 +#define XCR 5 +#define NOTYPE 0 + +#define SIGN_BIT_S (1UL << 31) +#define SIGN_BIT_D (1ULL << 63) +#define FP_EX_MASK (FP_EX_INEXACT | FP_EX_INVALID | FP_EX_DIVZERO | \ + FP_EX_UNDERFLOW | FP_EX_OVERFLOW) + +union dw_union { + u64 dp[1]; + u32 wp[2]; +}; + +static unsigned long insn_type(unsigned long speinsn) +{ + unsigned long ret = NOTYPE; + + switch (speinsn & 0x7ff) { + case EFSABS: ret = XA; break; + case EFSADD: ret = AB; break; + case EFSCFD: ret = XB; break; + case EFSCMPEQ: ret = XCR; break; + case EFSCMPGT: ret = XCR; break; + case EFSCMPLT: ret = XCR; break; + case EFSCTSF: ret = XB; break; + case EFSCTSI: ret = XB; break; + case EFSCTSIZ: ret = XB; break; + case EFSCTUF: ret = XB; break; + case EFSCTUI: ret = XB; break; + case EFSCTUIZ: ret = XB; break; + case EFSDIV: ret = AB; break; + case EFSMUL: ret = AB; break; + case EFSNABS: ret = XA; break; + case EFSNEG: ret = XA; break; + case EFSSUB: ret = AB; break; + case EFSCFSI: ret = XB; break; + + case EVFSABS: ret = XA; break; + case EVFSADD: ret = AB; break; + case EVFSCMPEQ: ret = XCR; break; + case EVFSCMPGT: ret = XCR; break; + case EVFSCMPLT: ret = XCR; break; + case EVFSCTSF: ret = XB; break; + case EVFSCTSI: ret = XB; break; + case EVFSCTSIZ: ret = XB; break; + case EVFSCTUF: ret = XB; break; + case EVFSCTUI: ret = XB; break; + case EVFSCTUIZ: ret = XB; break; + case EVFSDIV: ret = AB; break; + case EVFSMUL: ret = AB; break; + case EVFSNABS: ret = XA; break; + case EVFSNEG: ret = XA; break; + case EVFSSUB: ret = AB; break; + + case EFDABS: ret = XA; break; + case EFDADD: ret = AB; break; + case EFDCFS: ret = XB; break; + case EFDCMPEQ: ret = XCR; break; + case EFDCMPGT: ret = XCR; break; + case EFDCMPLT: ret = XCR; break; + case EFDCTSF: ret = XB; break; + case EFDCTSI: ret = XB; break; + case EFDCTSIDZ: ret = XB; break; + case EFDCTSIZ: ret = XB; break; + case EFDCTUF: ret = XB; break; + case EFDCTUI: ret = XB; break; + case EFDCTUIDZ: ret = XB; break; + case EFDCTUIZ: ret = XB; break; + case EFDDIV: ret = AB; break; + case EFDMUL: ret = AB; break; + case EFDNABS: ret = XA; break; + case EFDNEG: ret = XA; break; + case EFDSUB: ret = AB; break; + + default: + printk(KERN_ERR "\nOoops! SPE instruction no type found."); + printk(KERN_ERR "\ninst code: %08lx\n", speinsn); + } + + return ret; +} + +int do_spe_mathemu(struct pt_regs *regs) +{ + FP_DECL_EX; + int IR, cmp; + + unsigned long type, func, fc, fa, fb, src, speinsn; + union dw_union vc, va, vb; + + if (get_user(speinsn, (unsigned int __user *) regs->nip)) + return -EFAULT; + if ((speinsn >> 26) != EFAPU) + return -EINVAL; /* not an spe instruction */ + + type = insn_type(speinsn); + if (type == NOTYPE) + return -ENOSYS; + + func = speinsn & 0x7ff; + fc = (speinsn >> 21) & 0x1f; + fa = (speinsn >> 16) & 0x1f; + fb = (speinsn >> 11) & 0x1f; + src = (speinsn >> 5) & 0x7; + + vc.wp[0] = current->thread.evr[fc]; + vc.wp[1] = regs->gpr[fc]; + va.wp[0] = current->thread.evr[fa]; + va.wp[1] = regs->gpr[fa]; + vb.wp[0] = current->thread.evr[fb]; + vb.wp[1] = regs->gpr[fb]; + + __FPU_FPSCR = mfspr(SPRN_SPEFSCR); + +#ifdef DEBUG + printk("speinsn:%08lx spefscr:%08lx\n", speinsn, __FPU_FPSCR); + printk("vc: %08x %08x\n", vc.wp[0], vc.wp[1]); + printk("va: %08x %08x\n", va.wp[0], va.wp[1]); + printk("vb: %08x %08x\n", vb.wp[0], vb.wp[1]); +#endif + + switch (src) { + case SPFP: { + FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SR); + + switch (type) { + case AB: + case XCR: + FP_UNPACK_SP(SA, va.wp + 1); + case XB: + FP_UNPACK_SP(SB, vb.wp + 1); + break; + case XA: + FP_UNPACK_SP(SA, va.wp + 1); + break; + } + +#ifdef DEBUG + printk("SA: %ld %08lx %ld (%ld)\n", SA_s, SA_f, SA_e, SA_c); + printk("SB: %ld %08lx %ld (%ld)\n", SB_s, SB_f, SB_e, SB_c); +#endif + + switch (func) { + case EFSABS: + vc.wp[1] = va.wp[1] & ~SIGN_BIT_S; + goto update_regs; + + case EFSNABS: + vc.wp[1] = va.wp[1] | SIGN_BIT_S; + goto update_regs; + + case EFSNEG: + vc.wp[1] = va.wp[1] ^ SIGN_BIT_S; + goto update_regs; + + case EFSADD: + FP_ADD_S(SR, SA, SB); + goto pack_s; + + case EFSSUB: + FP_SUB_S(SR, SA, SB); + goto pack_s; + + case EFSMUL: + FP_MUL_S(SR, SA, SB); + goto pack_s; + + case EFSDIV: + FP_DIV_S(SR, SA, SB); + goto pack_s; + + case EFSCMPEQ: + cmp = 0; + goto cmp_s; + + case EFSCMPGT: + cmp = 1; + goto cmp_s; + + case EFSCMPLT: + cmp = -1; + goto cmp_s; + + case EFSCTSF: + case EFSCTUF: + if (!((vb.wp[1] >> 23) == 0xff && ((vb.wp[1] & 0x7fffff) > 0))) { + /* NaN */ + if (((vb.wp[1] >> 23) & 0xff) == 0) { + /* denorm */ + vc.wp[1] = 0x0; + } else if ((vb.wp[1] >> 31) == 0) { + /* positive normal */ + vc.wp[1] = (func == EFSCTSF) ? + 0x7fffffff : 0xffffffff; + } else { /* negative normal */ + vc.wp[1] = (func == EFSCTSF) ? + 0x80000000 : 0x0; + } + } else { /* rB is NaN */ + vc.wp[1] = 0x0; + } + goto update_regs; + + case EFSCFD: { + FP_DECL_D(DB); + FP_CLEAR_EXCEPTIONS; + FP_UNPACK_DP(DB, vb.dp); +#ifdef DEBUG + printk("DB: %ld %08lx %08lx %ld (%ld)\n", + DB_s, DB_f1, DB_f0, DB_e, DB_c); +#endif + FP_CONV(S, D, 1, 2, SR, DB); + goto pack_s; + } + + case EFSCTSI: + case EFSCTSIZ: + case EFSCTUI: + case EFSCTUIZ: + if (func & 0x4) { + _FP_ROUND(1, SB); + } else { + _FP_ROUND_ZERO(1, SB); + } + FP_TO_INT_S(vc.wp[1], SB, 32, ((func & 0x3) != 0)); + goto update_regs; + + default: + goto illegal; + } + break; + +pack_s: +#ifdef DEBUG + printk("SR: %ld %08lx %ld (%ld)\n", SR_s, SR_f, SR_e, SR_c); +#endif + FP_PACK_SP(vc.wp + 1, SR); + goto update_regs; + +cmp_s: + FP_CMP_S(IR, SA, SB, 3); + if (IR == 3 && (FP_ISSIGNAN_S(SA) || FP_ISSIGNAN_S(SB))) + FP_SET_EXCEPTION(FP_EX_INVALID); + if (IR == cmp) { + IR = 0x4; + } else { + IR = 0; + } + goto update_ccr; + } + + case DPFP: { + FP_DECL_D(DA); FP_DECL_D(DB); FP_DECL_D(DR); + + switch (type) { + case AB: + case XCR: + FP_UNPACK_DP(DA, va.dp); + case XB: + FP_UNPACK_DP(DB, vb.dp); + break; + case XA: + FP_UNPACK_DP(DA, va.dp); + break; + } + +#ifdef DEBUG + printk("DA: %ld %08lx %08lx %ld (%ld)\n", + DA_s, DA_f1, DA_f0, DA_e, DA_c); + printk("DB: %ld %08lx %08lx %ld (%ld)\n", + DB_s, DB_f1, DB_f0, DB_e, DB_c); +#endif + + switch (func) { + case EFDABS: + vc.dp[0] = va.dp[0] & ~SIGN_BIT_D; + goto update_regs; + + case EFDNABS: + vc.dp[0] = va.dp[0] | SIGN_BIT_D; + goto update_regs; + + case EFDNEG: + vc.dp[0] = va.dp[0] ^ SIGN_BIT_D; + goto update_regs; + + case EFDADD: + FP_ADD_D(DR, DA, DB); + goto pack_d; + + case EFDSUB: + FP_SUB_D(DR, DA, DB); + goto pack_d; + + case EFDMUL: + FP_MUL_D(DR, DA, DB); + goto pack_d; + + case EFDDIV: + FP_DIV_D(DR, DA, DB); + goto pack_d; + + case EFDCMPEQ: + cmp = 0; + goto cmp_d; + + case EFDCMPGT: + cmp = 1; + goto cmp_d; + + case EFDCMPLT: + cmp = -1; + goto cmp_d; + + case EFDCTSF: + case EFDCTUF: + if (!((vb.wp[0] >> 20) == 0x7ff && + ((vb.wp[0] & 0xfffff) > 0 || (vb.wp[1] > 0)))) { + /* not a NaN */ + if (((vb.wp[0] >> 20) & 0x7ff) == 0) { + /* denorm */ + vc.wp[1] = 0x0; + } else if ((vb.wp[0] >> 31) == 0) { + /* positive normal */ + vc.wp[1] = (func == EFDCTSF) ? + 0x7fffffff : 0xffffffff; + } else { /* negative normal */ + vc.wp[1] = (func == EFDCTSF) ? + 0x80000000 : 0x0; + } + } else { /* NaN */ + vc.wp[1] = 0x0; + } + goto update_regs; + + case EFDCFS: { + FP_DECL_S(SB); + FP_CLEAR_EXCEPTIONS; + FP_UNPACK_SP(SB, vb.wp + 1); +#ifdef DEBUG + printk("SB: %ld %08lx %ld (%ld)\n", + SB_s, SB_f, SB_e, SB_c); +#endif + FP_CONV(D, S, 2, 1, DR, SB); + goto pack_d; + } + + case EFDCTUIDZ: + case EFDCTSIDZ: + _FP_ROUND_ZERO(2, DB); + FP_TO_INT_D(vc.dp[0], DB, 64, ((func & 0x1) == 0)); + goto update_regs; + + case EFDCTUI: + case EFDCTSI: + case EFDCTUIZ: + case EFDCTSIZ: + if (func & 0x4) { + _FP_ROUND(2, DB); + } else { + _FP_ROUND_ZERO(2, DB); + } + FP_TO_INT_D(vc.wp[1], DB, 32, ((func & 0x3) != 0)); + goto update_regs; + + default: + goto illegal; + } + break; + +pack_d: +#ifdef DEBUG + printk("DR: %ld %08lx %08lx %ld (%ld)\n", + DR_s, DR_f1, DR_f0, DR_e, DR_c); +#endif + FP_PACK_DP(vc.dp, DR); + goto update_regs; + +cmp_d: + FP_CMP_D(IR, DA, DB, 3); + if (IR == 3 && (FP_ISSIGNAN_D(DA) || FP_ISSIGNAN_D(DB))) + FP_SET_EXCEPTION(FP_EX_INVALID); + if (IR == cmp) { + IR = 0x4; + } else { + IR = 0; + } + goto update_ccr; + + } + + case VCT: { + FP_DECL_S(SA0); FP_DECL_S(SB0); FP_DECL_S(SR0); + FP_DECL_S(SA1); FP_DECL_S(SB1); FP_DECL_S(SR1); + int IR0, IR1; + + switch (type) { + case AB: + case XCR: + FP_UNPACK_SP(SA0, va.wp); + FP_UNPACK_SP(SA1, va.wp + 1); + case XB: + FP_UNPACK_SP(SB0, vb.wp); + FP_UNPACK_SP(SB1, vb.wp + 1); + break; + case XA: + FP_UNPACK_SP(SA0, va.wp); + FP_UNPACK_SP(SA1, va.wp + 1); + break; + } + +#ifdef DEBUG + printk("SA0: %ld %08lx %ld (%ld)\n", SA0_s, SA0_f, SA0_e, SA0_c); + printk("SA1: %ld %08lx %ld (%ld)\n", SA1_s, SA1_f, SA1_e, SA1_c); + printk("SB0: %ld %08lx %ld (%ld)\n", SB0_s, SB0_f, SB0_e, SB0_c); + printk("SB1: %ld %08lx %ld (%ld)\n", SB1_s, SB1_f, SB1_e, SB1_c); +#endif + + switch (func) { + case EVFSABS: + vc.wp[0] = va.wp[0] & ~SIGN_BIT_S; + vc.wp[1] = va.wp[1] & ~SIGN_BIT_S; + goto update_regs; + + case EVFSNABS: + vc.wp[0] = va.wp[0] | SIGN_BIT_S; + vc.wp[1] = va.wp[1] | SIGN_BIT_S; + goto update_regs; + + case EVFSNEG: + vc.wp[0] = va.wp[0] ^ SIGN_BIT_S; + vc.wp[1] = va.wp[1] ^ SIGN_BIT_S; + goto update_regs; + + case EVFSADD: + FP_ADD_S(SR0, SA0, SB0); + FP_ADD_S(SR1, SA1, SB1); + goto pack_vs; + + case EVFSSUB: + FP_SUB_S(SR0, SA0, SB0); + FP_SUB_S(SR1, SA1, SB1); + goto pack_vs; + + case EVFSMUL: + FP_MUL_S(SR0, SA0, SB0); + FP_MUL_S(SR1, SA1, SB1); + goto pack_vs; + + case EVFSDIV: + FP_DIV_S(SR0, SA0, SB0); + FP_DIV_S(SR1, SA1, SB1); + goto pack_vs; + + case EVFSCMPEQ: + cmp = 0; + goto cmp_vs; + + case EVFSCMPGT: + cmp = 1; + goto cmp_vs; + + case EVFSCMPLT: + cmp = -1; + goto cmp_vs; + + case EVFSCTSF: + __asm__ __volatile__ ("mtspr 512, %4\n" + "efsctsf %0, %2\n" + "efsctsf %1, %3\n" + : "=r" (vc.wp[0]), "=r" (vc.wp[1]) + : "r" (vb.wp[0]), "r" (vb.wp[1]), "r" (0)); + goto update_regs; + + case EVFSCTUF: + __asm__ __volatile__ ("mtspr 512, %4\n" + "efsctuf %0, %2\n" + "efsctuf %1, %3\n" + : "=r" (vc.wp[0]), "=r" (vc.wp[1]) + : "r" (vb.wp[0]), "r" (vb.wp[1]), "r" (0)); + goto update_regs; + + case EVFSCTUI: + case EVFSCTSI: + case EVFSCTUIZ: + case EVFSCTSIZ: + if (func & 0x4) { + _FP_ROUND(1, SB0); + _FP_ROUND(1, SB1); + } else { + _FP_ROUND_ZERO(1, SB0); + _FP_ROUND_ZERO(1, SB1); + } + FP_TO_INT_S(vc.wp[0], SB0, 32, ((func & 0x3) != 0)); + FP_TO_INT_S(vc.wp[1], SB1, 32, ((func & 0x3) != 0)); + goto update_regs; + + default: + goto illegal; + } + break; + +pack_vs: +#ifdef DEBUG + printk("SR0: %ld %08lx %ld (%ld)\n", SR0_s, SR0_f, SR0_e, SR0_c); + printk("SR1: %ld %08lx %ld (%ld)\n", SR1_s, SR1_f, SR1_e, SR1_c); +#endif + FP_PACK_SP(vc.wp, SR0); + FP_PACK_SP(vc.wp + 1, SR1); + goto update_regs; + +cmp_vs: + { + int ch, cl; + + FP_CMP_S(IR0, SA0, SB0, 3); + FP_CMP_S(IR1, SA1, SB1, 3); + if (IR0 == 3 && (FP_ISSIGNAN_S(SA0) || FP_ISSIGNAN_S(SB0))) + FP_SET_EXCEPTION(FP_EX_INVALID); + if (IR1 == 3 && (FP_ISSIGNAN_S(SA1) || FP_ISSIGNAN_S(SB1))) + FP_SET_EXCEPTION(FP_EX_INVALID); + ch = (IR0 == cmp) ? 1 : 0; + cl = (IR1 == cmp) ? 1 : 0; + IR = (ch << 3) | (cl << 2) | ((ch | cl) << 1) | + ((ch & cl) << 0); + goto update_ccr; + } + } + default: + return -EINVAL; + } + +update_ccr: + regs->ccr &= ~(15 << ((7 - ((speinsn >> 23) & 0x7)) << 2)); + regs->ccr |= (IR << ((7 - ((speinsn >> 23) & 0x7)) << 2)); + +update_regs: + __FPU_FPSCR &= ~FP_EX_MASK; + __FPU_FPSCR |= (FP_CUR_EXCEPTIONS & FP_EX_MASK); + mtspr(SPRN_SPEFSCR, __FPU_FPSCR); + + current->thread.evr[fc] = vc.wp[0]; + regs->gpr[fc] = vc.wp[1]; + +#ifdef DEBUG + printk("ccr = %08lx\n", regs->ccr); + printk("cur exceptions = %08x spefscr = %08lx\n", + FP_CUR_EXCEPTIONS, __FPU_FPSCR); + printk("vc: %08x %08x\n", vc.wp[0], vc.wp[1]); + printk("va: %08x %08x\n", va.wp[0], va.wp[1]); + printk("vb: %08x %08x\n", vb.wp[0], vb.wp[1]); +#endif + + return 0; + +illegal: + printk(KERN_ERR "\nOoops! IEEE-754 compliance handler encountered un-supported instruction.\ninst code: %08lx\n", speinsn); + return -ENOSYS; +} + +int speround_handler(struct pt_regs *regs) +{ + union dw_union fgpr; + int s_lo, s_hi; + unsigned long speinsn, type, fc; + + if (get_user(speinsn, (unsigned int __user *) regs->nip)) + return -EFAULT; + if ((speinsn >> 26) != 4) + return -EINVAL; /* not an spe instruction */ + + type = insn_type(speinsn & 0x7ff); + if (type == XCR) return -ENOSYS; + + fc = (speinsn >> 21) & 0x1f; + s_lo = regs->gpr[fc] & SIGN_BIT_S; + s_hi = current->thread.evr[fc] & SIGN_BIT_S; + fgpr.wp[0] = current->thread.evr[fc]; + fgpr.wp[1] = regs->gpr[fc]; + + __FPU_FPSCR = mfspr(SPRN_SPEFSCR); + + switch ((speinsn >> 5) & 0x7) { + /* Since SPE instructions on E500 core can handle round to nearest + * and round toward zero with IEEE-754 complied, we just need + * to handle round toward +Inf and round toward -Inf by software. + */ + case SPFP: + if ((FP_ROUNDMODE) == FP_RND_PINF) { + if (!s_lo) fgpr.wp[1]++; /* Z > 0, choose Z1 */ + } else { /* round to -Inf */ + if (s_lo) fgpr.wp[1]++; /* Z < 0, choose Z2 */ + } + break; + + case DPFP: + if (FP_ROUNDMODE == FP_RND_PINF) { + if (!s_hi) fgpr.dp[0]++; /* Z > 0, choose Z1 */ + } else { /* round to -Inf */ + if (s_hi) fgpr.dp[0]++; /* Z < 0, choose Z2 */ + } + break; + + case VCT: + if (FP_ROUNDMODE == FP_RND_PINF) { + if (!s_lo) fgpr.wp[1]++; /* Z_low > 0, choose Z1 */ + if (!s_hi) fgpr.wp[0]++; /* Z_high word > 0, choose Z1 */ + } else { /* round to -Inf */ + if (s_lo) fgpr.wp[1]++; /* Z_low < 0, choose Z2 */ + if (s_hi) fgpr.wp[0]++; /* Z_high < 0, choose Z2 */ + } + break; + + default: + return -EINVAL; + } + + current->thread.evr[fc] = fgpr.wp[0]; + regs->gpr[fc] = fgpr.wp[1]; + + return 0; +} diff --git a/arch/powerpc/mm/Makefile b/arch/powerpc/mm/Makefile index e7392b4..86e657b 100644 --- a/arch/powerpc/mm/Makefile +++ b/arch/powerpc/mm/Makefile @@ -6,7 +6,7 @@ ifeq ($(CONFIG_PPC64),y) EXTRA_CFLAGS += -mno-minimal-toc endif -obj-y := fault.o mem.o \ +obj-y := fault.o mem.o pgtable.o \ init_$(CONFIG_WORD_SIZE).o \ pgtable_$(CONFIG_WORD_SIZE).o \ mmu_context_$(CONFIG_WORD_SIZE).o diff --git a/arch/powerpc/mm/fault.c b/arch/powerpc/mm/fault.c index 565b7a2..7df0409 100644 --- a/arch/powerpc/mm/fault.c +++ b/arch/powerpc/mm/fault.c @@ -30,6 +30,7 @@ #include <linux/kprobes.h> #include <linux/kdebug.h> +#include <asm/firmware.h> #include <asm/page.h> #include <asm/pgtable.h> #include <asm/mmu.h> @@ -318,9 +319,16 @@ good_area: goto do_sigbus; BUG(); } - if (ret & VM_FAULT_MAJOR) + if (ret & VM_FAULT_MAJOR) { current->maj_flt++; - else +#ifdef CONFIG_PPC_SMLPAR + if (firmware_has_feature(FW_FEATURE_CMO)) { + preempt_disable(); + get_lppaca()->page_ins += (1 << PAGE_FACTOR); + preempt_enable(); + } +#endif + } else current->min_flt++; up_read(&mm->mmap_sem); return 0; diff --git a/arch/powerpc/mm/hash_low_32.S b/arch/powerpc/mm/hash_low_32.S index 7bffb70..c5536b8 100644 --- a/arch/powerpc/mm/hash_low_32.S +++ b/arch/powerpc/mm/hash_low_32.S @@ -36,36 +36,6 @@ mmu_hash_lock: #endif /* CONFIG_SMP */ /* - * Sync CPUs with hash_page taking & releasing the hash - * table lock - */ -#ifdef CONFIG_SMP - .text -_GLOBAL(hash_page_sync) - mfmsr r10 - rlwinm r0,r10,0,17,15 /* clear bit 16 (MSR_EE) */ - mtmsr r0 - lis r8,mmu_hash_lock@h - ori r8,r8,mmu_hash_lock@l - lis r0,0x0fff - b 10f -11: lwz r6,0(r8) - cmpwi 0,r6,0 - bne 11b -10: lwarx r6,0,r8 - cmpwi 0,r6,0 - bne- 11b - stwcx. r0,0,r8 - bne- 10b - isync - eieio - li r0,0 - stw r0,0(r8) - mtmsr r10 - blr -#endif /* CONFIG_SMP */ - -/* * Load a PTE into the hash table, if possible. * The address is in r4, and r3 contains an access flag: * _PAGE_RW (0x400) if a write. diff --git a/arch/powerpc/mm/hugetlbpage.c b/arch/powerpc/mm/hugetlbpage.c index f0c3b88..201c7a5 100644 --- a/arch/powerpc/mm/hugetlbpage.c +++ b/arch/powerpc/mm/hugetlbpage.c @@ -53,8 +53,7 @@ unsigned int mmu_huge_psizes[MMU_PAGE_COUNT] = { }; /* initialize all to 0 */ /* Subtract one from array size because we don't need a cache for 4K since * is not a huge page size */ -#define huge_pgtable_cache(psize) (pgtable_cache[HUGEPTE_CACHE_NUM \ - + psize-1]) +#define HUGE_PGTABLE_INDEX(psize) (HUGEPTE_CACHE_NUM + psize - 1) #define HUGEPTE_CACHE_NAME(psize) (huge_pgtable_cache_name[psize]) static const char *huge_pgtable_cache_name[MMU_PAGE_COUNT] = { @@ -113,7 +112,7 @@ static inline pte_t *hugepte_offset(hugepd_t *hpdp, unsigned long addr, static int __hugepte_alloc(struct mm_struct *mm, hugepd_t *hpdp, unsigned long address, unsigned int psize) { - pte_t *new = kmem_cache_zalloc(huge_pgtable_cache(psize), + pte_t *new = kmem_cache_zalloc(pgtable_cache[HUGE_PGTABLE_INDEX(psize)], GFP_KERNEL|__GFP_REPEAT); if (! new) @@ -121,7 +120,7 @@ static int __hugepte_alloc(struct mm_struct *mm, hugepd_t *hpdp, spin_lock(&mm->page_table_lock); if (!hugepd_none(*hpdp)) - kmem_cache_free(huge_pgtable_cache(psize), new); + kmem_cache_free(pgtable_cache[HUGE_PGTABLE_INDEX(psize)], new); else hpdp->pd = (unsigned long)new | HUGEPD_OK; spin_unlock(&mm->page_table_lock); @@ -763,13 +762,14 @@ static int __init hugetlbpage_init(void) for (psize = 0; psize < MMU_PAGE_COUNT; ++psize) { if (mmu_huge_psizes[psize]) { - huge_pgtable_cache(psize) = kmem_cache_create( - HUGEPTE_CACHE_NAME(psize), - HUGEPTE_TABLE_SIZE(psize), - HUGEPTE_TABLE_SIZE(psize), - 0, - NULL); - if (!huge_pgtable_cache(psize)) + pgtable_cache[HUGE_PGTABLE_INDEX(psize)] = + kmem_cache_create( + HUGEPTE_CACHE_NAME(psize), + HUGEPTE_TABLE_SIZE(psize), + HUGEPTE_TABLE_SIZE(psize), + 0, + NULL); + if (!pgtable_cache[HUGE_PGTABLE_INDEX(psize)]) panic("hugetlbpage_init(): could not create %s"\ "\n", HUGEPTE_CACHE_NAME(psize)); } diff --git a/arch/powerpc/mm/pgtable.c b/arch/powerpc/mm/pgtable.c new file mode 100644 index 0000000..6d94116 --- /dev/null +++ b/arch/powerpc/mm/pgtable.c @@ -0,0 +1,117 @@ +/* + * This file contains common routines for dealing with free of page tables + * + * Derived from arch/powerpc/mm/tlb_64.c: + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au) + * and Cort Dougan (PReP) (cort@cs.nmt.edu) + * Copyright (C) 1996 Paul Mackerras + * + * Derived from "arch/i386/mm/init.c" + * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds + * + * Dave Engebretsen <engebret@us.ibm.com> + * Rework for PPC64 port. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/init.h> +#include <linux/percpu.h> +#include <linux/hardirq.h> +#include <asm/pgalloc.h> +#include <asm/tlbflush.h> +#include <asm/tlb.h> + +static DEFINE_PER_CPU(struct pte_freelist_batch *, pte_freelist_cur); +static unsigned long pte_freelist_forced_free; + +struct pte_freelist_batch +{ + struct rcu_head rcu; + unsigned int index; + pgtable_free_t tables[0]; +}; + +#define PTE_FREELIST_SIZE \ + ((PAGE_SIZE - sizeof(struct pte_freelist_batch)) \ + / sizeof(pgtable_free_t)) + +static void pte_free_smp_sync(void *arg) +{ + /* Do nothing, just ensure we sync with all CPUs */ +} + +/* This is only called when we are critically out of memory + * (and fail to get a page in pte_free_tlb). + */ +static void pgtable_free_now(pgtable_free_t pgf) +{ + pte_freelist_forced_free++; + + smp_call_function(pte_free_smp_sync, NULL, 1); + + pgtable_free(pgf); +} + +static void pte_free_rcu_callback(struct rcu_head *head) +{ + struct pte_freelist_batch *batch = + container_of(head, struct pte_freelist_batch, rcu); + unsigned int i; + + for (i = 0; i < batch->index; i++) + pgtable_free(batch->tables[i]); + + free_page((unsigned long)batch); +} + +static void pte_free_submit(struct pte_freelist_batch *batch) +{ + INIT_RCU_HEAD(&batch->rcu); + call_rcu(&batch->rcu, pte_free_rcu_callback); +} + +void pgtable_free_tlb(struct mmu_gather *tlb, pgtable_free_t pgf) +{ + /* This is safe since tlb_gather_mmu has disabled preemption */ + cpumask_t local_cpumask = cpumask_of_cpu(smp_processor_id()); + struct pte_freelist_batch **batchp = &__get_cpu_var(pte_freelist_cur); + + if (atomic_read(&tlb->mm->mm_users) < 2 || + cpus_equal(tlb->mm->cpu_vm_mask, local_cpumask)) { + pgtable_free(pgf); + return; + } + + if (*batchp == NULL) { + *batchp = (struct pte_freelist_batch *)__get_free_page(GFP_ATOMIC); + if (*batchp == NULL) { + pgtable_free_now(pgf); + return; + } + (*batchp)->index = 0; + } + (*batchp)->tables[(*batchp)->index++] = pgf; + if ((*batchp)->index == PTE_FREELIST_SIZE) { + pte_free_submit(*batchp); + *batchp = NULL; + } +} + +void pte_free_finish(void) +{ + /* This is safe since tlb_gather_mmu has disabled preemption */ + struct pte_freelist_batch **batchp = &__get_cpu_var(pte_freelist_cur); + + if (*batchp == NULL) + return; + pte_free_submit(*batchp); + *batchp = NULL; +} diff --git a/arch/powerpc/mm/pgtable_32.c b/arch/powerpc/mm/pgtable_32.c index c31d6d2..c7b755c 100644 --- a/arch/powerpc/mm/pgtable_32.c +++ b/arch/powerpc/mm/pgtable_32.c @@ -48,10 +48,6 @@ EXPORT_SYMBOL(ioremap_bot); /* aka VMALLOC_END */ extern char etext[], _stext[]; -#ifdef CONFIG_SMP -extern void hash_page_sync(void); -#endif - #ifdef HAVE_BATS extern phys_addr_t v_mapped_by_bats(unsigned long va); extern unsigned long p_mapped_by_bats(phys_addr_t pa); @@ -125,23 +121,6 @@ pgtable_t pte_alloc_one(struct mm_struct *mm, unsigned long address) return ptepage; } -void pte_free_kernel(struct mm_struct *mm, pte_t *pte) -{ -#ifdef CONFIG_SMP - hash_page_sync(); -#endif - free_page((unsigned long)pte); -} - -void pte_free(struct mm_struct *mm, pgtable_t ptepage) -{ -#ifdef CONFIG_SMP - hash_page_sync(); -#endif - pgtable_page_dtor(ptepage); - __free_page(ptepage); -} - void __iomem * ioremap(phys_addr_t addr, unsigned long size) { diff --git a/arch/powerpc/mm/tlb_64.c b/arch/powerpc/mm/tlb_64.c index be7dd42..c931bc7 100644 --- a/arch/powerpc/mm/tlb_64.c +++ b/arch/powerpc/mm/tlb_64.c @@ -37,81 +37,6 @@ DEFINE_PER_CPU(struct ppc64_tlb_batch, ppc64_tlb_batch); * arch/powerpc/include/asm/tlb.h file -- tgall */ DEFINE_PER_CPU(struct mmu_gather, mmu_gathers); -static DEFINE_PER_CPU(struct pte_freelist_batch *, pte_freelist_cur); -static unsigned long pte_freelist_forced_free; - -struct pte_freelist_batch -{ - struct rcu_head rcu; - unsigned int index; - pgtable_free_t tables[0]; -}; - -#define PTE_FREELIST_SIZE \ - ((PAGE_SIZE - sizeof(struct pte_freelist_batch)) \ - / sizeof(pgtable_free_t)) - -static void pte_free_smp_sync(void *arg) -{ - /* Do nothing, just ensure we sync with all CPUs */ -} - -/* This is only called when we are critically out of memory - * (and fail to get a page in pte_free_tlb). - */ -static void pgtable_free_now(pgtable_free_t pgf) -{ - pte_freelist_forced_free++; - - smp_call_function(pte_free_smp_sync, NULL, 1); - - pgtable_free(pgf); -} - -static void pte_free_rcu_callback(struct rcu_head *head) -{ - struct pte_freelist_batch *batch = - container_of(head, struct pte_freelist_batch, rcu); - unsigned int i; - - for (i = 0; i < batch->index; i++) - pgtable_free(batch->tables[i]); - - free_page((unsigned long)batch); -} - -static void pte_free_submit(struct pte_freelist_batch *batch) -{ - INIT_RCU_HEAD(&batch->rcu); - call_rcu(&batch->rcu, pte_free_rcu_callback); -} - -void pgtable_free_tlb(struct mmu_gather *tlb, pgtable_free_t pgf) -{ - /* This is safe since tlb_gather_mmu has disabled preemption */ - cpumask_t local_cpumask = cpumask_of_cpu(smp_processor_id()); - struct pte_freelist_batch **batchp = &__get_cpu_var(pte_freelist_cur); - - if (atomic_read(&tlb->mm->mm_users) < 2 || - cpus_equal(tlb->mm->cpu_vm_mask, local_cpumask)) { - pgtable_free(pgf); - return; - } - - if (*batchp == NULL) { - *batchp = (struct pte_freelist_batch *)__get_free_page(GFP_ATOMIC); - if (*batchp == NULL) { - pgtable_free_now(pgf); - return; - } - (*batchp)->index = 0; - } - (*batchp)->tables[(*batchp)->index++] = pgf; - if ((*batchp)->index == PTE_FREELIST_SIZE) { - pte_free_submit(*batchp); - *batchp = NULL; - } -} /* * A linux PTE was changed and the corresponding hash table entry @@ -229,17 +154,6 @@ void __flush_tlb_pending(struct ppc64_tlb_batch *batch) batch->index = 0; } -void pte_free_finish(void) -{ - /* This is safe since tlb_gather_mmu has disabled preemption */ - struct pte_freelist_batch **batchp = &__get_cpu_var(pte_freelist_cur); - - if (*batchp == NULL) - return; - pte_free_submit(*batchp); - *batchp = NULL; -} - /** * __flush_hash_table_range - Flush all HPTEs for a given address range * from the hash table (and the TLB). But keeps diff --git a/arch/powerpc/platforms/85xx/Makefile b/arch/powerpc/platforms/85xx/Makefile index cb3054e..f0798c0 100644 --- a/arch/powerpc/platforms/85xx/Makefile +++ b/arch/powerpc/platforms/85xx/Makefile @@ -1,6 +1,8 @@ # # Makefile for the PowerPC 85xx linux kernel. # +obj-$(CONFIG_SMP) += smp.o + obj-$(CONFIG_MPC8540_ADS) += mpc85xx_ads.o obj-$(CONFIG_MPC8560_ADS) += mpc85xx_ads.o obj-$(CONFIG_MPC85xx_CDS) += mpc85xx_cds.o diff --git a/arch/powerpc/platforms/85xx/mpc85xx_ds.c b/arch/powerpc/platforms/85xx/mpc85xx_ds.c index 613bf8c..a8301c8 100644 --- a/arch/powerpc/platforms/85xx/mpc85xx_ds.c +++ b/arch/powerpc/platforms/85xx/mpc85xx_ds.c @@ -63,6 +63,7 @@ void __init mpc85xx_ds_pic_init(void) struct device_node *cascade_node = NULL; int cascade_irq; #endif + unsigned long root = of_get_flat_dt_root(); np = of_find_node_by_type(NULL, "open-pic"); if (np == NULL) { @@ -76,11 +77,19 @@ void __init mpc85xx_ds_pic_init(void) return; } - mpic = mpic_alloc(np, r.start, + if (of_flat_dt_is_compatible(root, "fsl,MPC8572DS-CAMP")) { + mpic = mpic_alloc(np, r.start, + MPIC_PRIMARY | + MPIC_BIG_ENDIAN | MPIC_BROKEN_FRR_NIRQS, + 0, 256, " OpenPIC "); + } else { + mpic = mpic_alloc(np, r.start, MPIC_PRIMARY | MPIC_WANTS_RESET | MPIC_BIG_ENDIAN | MPIC_BROKEN_FRR_NIRQS | MPIC_SINGLE_DEST_CPU, 0, 256, " OpenPIC "); + } + BUG_ON(mpic == NULL); of_node_put(np); diff --git a/arch/powerpc/platforms/85xx/mpc85xx_mds.c b/arch/powerpc/platforms/85xx/mpc85xx_mds.c index 2494c51..b915bf5 100644 --- a/arch/powerpc/platforms/85xx/mpc85xx_mds.c +++ b/arch/powerpc/platforms/85xx/mpc85xx_mds.c @@ -241,13 +241,15 @@ static int __init board_fixups(void) mdio = of_find_compatible_node(NULL, NULL, compstrs[i]); of_address_to_resource(mdio, 0, &res); - snprintf(phy_id, BUS_ID_SIZE, "%x:%02x", res.start, 1); + snprintf(phy_id, BUS_ID_SIZE, "%llx:%02x", + (unsigned long long)res.start, 1); phy_register_fixup_for_id(phy_id, mpc8568_fixup_125_clock); phy_register_fixup_for_id(phy_id, mpc8568_mds_phy_fixups); /* Register a workaround for errata */ - snprintf(phy_id, BUS_ID_SIZE, "%x:%02x", res.start, 7); + snprintf(phy_id, BUS_ID_SIZE, "%llx:%02x", + (unsigned long long)res.start, 7); phy_register_fixup_for_id(phy_id, mpc8568_mds_phy_fixups); of_node_put(mdio); diff --git a/arch/powerpc/platforms/85xx/smp.c b/arch/powerpc/platforms/85xx/smp.c new file mode 100644 index 0000000..d652c71 --- /dev/null +++ b/arch/powerpc/platforms/85xx/smp.c @@ -0,0 +1,104 @@ +/* + * Author: Andy Fleming <afleming@freescale.com> + * Kumar Gala <galak@kernel.crashing.org> + * + * Copyright 2006-2008 Freescale Semiconductor Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/stddef.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/of.h> + +#include <asm/machdep.h> +#include <asm/pgtable.h> +#include <asm/page.h> +#include <asm/mpic.h> +#include <asm/cacheflush.h> + +#include <sysdev/fsl_soc.h> + +extern volatile unsigned long __secondary_hold_acknowledge; +extern void __early_start(void); + +#define BOOT_ENTRY_ADDR_UPPER 0 +#define BOOT_ENTRY_ADDR_LOWER 1 +#define BOOT_ENTRY_R3_UPPER 2 +#define BOOT_ENTRY_R3_LOWER 3 +#define BOOT_ENTRY_RESV 4 +#define BOOT_ENTRY_PIR 5 +#define BOOT_ENTRY_R6_UPPER 6 +#define BOOT_ENTRY_R6_LOWER 7 +#define NUM_BOOT_ENTRY 8 +#define SIZE_BOOT_ENTRY (NUM_BOOT_ENTRY * sizeof(u32)) + +static void __init +smp_85xx_kick_cpu(int nr) +{ + unsigned long flags; + const u64 *cpu_rel_addr; + __iomem u32 *bptr_vaddr; + struct device_node *np; + int n = 0; + + WARN_ON (nr < 0 || nr >= NR_CPUS); + + pr_debug("smp_85xx_kick_cpu: kick CPU #%d\n", nr); + + local_irq_save(flags); + + np = of_get_cpu_node(nr, NULL); + cpu_rel_addr = of_get_property(np, "cpu-release-addr", NULL); + + if (cpu_rel_addr == NULL) { + printk(KERN_ERR "No cpu-release-addr for cpu %d\n", nr); + return; + } + + /* Map the spin table */ + bptr_vaddr = ioremap(*cpu_rel_addr, SIZE_BOOT_ENTRY); + + out_be32(bptr_vaddr + BOOT_ENTRY_PIR, nr); + out_be32(bptr_vaddr + BOOT_ENTRY_ADDR_LOWER, __pa(__early_start)); + + /* Wait a bit for the CPU to ack. */ + while ((__secondary_hold_acknowledge != nr) && (++n < 1000)) + mdelay(1); + + iounmap(bptr_vaddr); + + local_irq_restore(flags); + + pr_debug("waited %d msecs for CPU #%d.\n", n, nr); +} + +static void __init +smp_85xx_setup_cpu(int cpu_nr) +{ + mpic_setup_this_cpu(); + + /* Clear any pending timer interrupts */ + mtspr(SPRN_TSR, TSR_ENW | TSR_WIS | TSR_DIS | TSR_FIS); + + /* Enable decrementer interrupt */ + mtspr(SPRN_TCR, TCR_DIE); +} + +struct smp_ops_t smp_85xx_ops = { + .message_pass = smp_mpic_message_pass, + .probe = smp_mpic_probe, + .kick_cpu = smp_85xx_kick_cpu, + .setup_cpu = smp_85xx_setup_cpu, +}; + +void __init +mpc85xx_smp_init(void) +{ + smp_ops = &smp_85xx_ops; +} diff --git a/arch/powerpc/platforms/86xx/Kconfig b/arch/powerpc/platforms/86xx/Kconfig index 77dd797..8e56939 100644 --- a/arch/powerpc/platforms/86xx/Kconfig +++ b/arch/powerpc/platforms/86xx/Kconfig @@ -34,6 +34,8 @@ config MPC8610_HPCD config GEF_SBC610 bool "GE Fanuc SBC610" select DEFAULT_UIMAGE + select GENERIC_GPIO + select ARCH_REQUIRE_GPIOLIB select HAS_RAPIDIO help This option enables support for GE Fanuc's SBC610. diff --git a/arch/powerpc/platforms/86xx/Makefile b/arch/powerpc/platforms/86xx/Makefile index 4a56ff6..31e540c 100644 --- a/arch/powerpc/platforms/86xx/Makefile +++ b/arch/powerpc/platforms/86xx/Makefile @@ -7,4 +7,5 @@ obj-$(CONFIG_SMP) += mpc86xx_smp.o obj-$(CONFIG_MPC8641_HPCN) += mpc86xx_hpcn.o obj-$(CONFIG_SBC8641D) += sbc8641d.o obj-$(CONFIG_MPC8610_HPCD) += mpc8610_hpcd.o -obj-$(CONFIG_GEF_SBC610) += gef_sbc610.o gef_pic.o +gef-gpio-$(CONFIG_GPIOLIB) += gef_gpio.o +obj-$(CONFIG_GEF_SBC610) += gef_sbc610.o gef_pic.o $(gef-gpio-y) diff --git a/arch/powerpc/platforms/86xx/gef_gpio.c b/arch/powerpc/platforms/86xx/gef_gpio.c new file mode 100644 index 0000000..85b2800 --- /dev/null +++ b/arch/powerpc/platforms/86xx/gef_gpio.c @@ -0,0 +1,143 @@ +/* + * Driver for GE Fanuc's FPGA based GPIO pins + * + * Author: Martyn Welch <martyn.welch@gefanuc.com> + * + * 2008 (c) GE Fanuc Intelligent Platforms Embedded Systems, Inc. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +/* TODO + * + * Configuration of output modes (totem-pole/open-drain) + * Interrupt configuration - interrupts are always generated the FPGA relies on + * the I/O interrupt controllers mask to stop them propergating + */ + +#include <linux/kernel.h> +#include <linux/compiler.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_platform.h> +#include <linux/of_gpio.h> +#include <linux/gpio.h> + +#define GEF_GPIO_DIRECT 0x00 +#define GEF_GPIO_IN 0x04 +#define GEF_GPIO_OUT 0x08 +#define GEF_GPIO_TRIG 0x0C +#define GEF_GPIO_POLAR_A 0x10 +#define GEF_GPIO_POLAR_B 0x14 +#define GEF_GPIO_INT_STAT 0x18 +#define GEF_GPIO_OVERRUN 0x1C +#define GEF_GPIO_MODE 0x20 + +#define NUM_GPIO 19 + +static void _gef_gpio_set(void __iomem *reg, unsigned int offset, int value) +{ + unsigned int data; + + data = ioread32be(reg); + /* value: 0=low; 1=high */ + if (value & 0x1) + data = data | (0x1 << offset); + else + data = data & ~(0x1 << offset); + + iowrite32be(data, reg); +} + + +static int gef_gpio_dir_in(struct gpio_chip *chip, unsigned offset) +{ + unsigned int data; + struct of_mm_gpio_chip *mmchip = to_of_mm_gpio_chip(chip); + + data = ioread32be(mmchip->regs + GEF_GPIO_DIRECT); + data = data | (0x1 << offset); + iowrite32be(data, mmchip->regs + GEF_GPIO_DIRECT); + + return 0; +} + +static int gef_gpio_dir_out(struct gpio_chip *chip, unsigned offset, int value) +{ + unsigned int data; + struct of_mm_gpio_chip *mmchip = to_of_mm_gpio_chip(chip); + + /* Set direction before switching to input */ + _gef_gpio_set(mmchip->regs + GEF_GPIO_OUT, offset, value); + + data = ioread32be(mmchip->regs + GEF_GPIO_DIRECT); + data = data & ~(0x1 << offset); + iowrite32be(data, mmchip->regs + GEF_GPIO_DIRECT); + + return 0; +} + +static int gef_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + unsigned int data; + int state = 0; + struct of_mm_gpio_chip *mmchip = to_of_mm_gpio_chip(chip); + + data = ioread32be(mmchip->regs + GEF_GPIO_IN); + state = (int)((data >> offset) & 0x1); + + return state; +} + +static void gef_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct of_mm_gpio_chip *mmchip = to_of_mm_gpio_chip(chip); + + _gef_gpio_set(mmchip->regs + GEF_GPIO_OUT, offset, value); +} + +static int __init gef_gpio_init(void) +{ + struct device_node *np; + + for_each_compatible_node(np, NULL, "gef,sbc610-gpio") { + int retval; + struct of_mm_gpio_chip *gef_gpio_chip; + + pr_debug("%s: Initialising GEF GPIO\n", np->full_name); + + /* Allocate chip structure */ + gef_gpio_chip = kzalloc(sizeof(*gef_gpio_chip), GFP_KERNEL); + if (!gef_gpio_chip) { + pr_err("%s: Unable to allocate structure\n", + np->full_name); + continue; + } + + /* Setup pointers to chip functions */ + gef_gpio_chip->of_gc.gpio_cells = 2; + gef_gpio_chip->of_gc.gc.ngpio = NUM_GPIO; + gef_gpio_chip->of_gc.gc.direction_input = gef_gpio_dir_in; + gef_gpio_chip->of_gc.gc.direction_output = gef_gpio_dir_out; + gef_gpio_chip->of_gc.gc.get = gef_gpio_get; + gef_gpio_chip->of_gc.gc.set = gef_gpio_set; + + /* This function adds a memory mapped GPIO chip */ + retval = of_mm_gpiochip_add(np, gef_gpio_chip); + if (retval) { + kfree(gef_gpio_chip); + pr_err("%s: Unable to add GPIO\n", np->full_name); + } + } + + return 0; +}; +arch_initcall(gef_gpio_init); + +MODULE_DESCRIPTION("GE Fanuc I/O FPGA GPIO driver"); +MODULE_AUTHOR("Martyn Welch <martyn.welch@gefanuc.com"); +MODULE_LICENSE("GPL"); diff --git a/arch/powerpc/platforms/cell/iommu.c b/arch/powerpc/platforms/cell/iommu.c index 3168272..86db4dd 100644 --- a/arch/powerpc/platforms/cell/iommu.c +++ b/arch/powerpc/platforms/cell/iommu.c @@ -1053,10 +1053,7 @@ static int __init cell_iommu_fixed_mapping_init(void) } /* We must have dma-ranges properties for fixed mapping to work */ - for (np = NULL; (np = of_find_all_nodes(np));) { - if (of_find_property(np, "dma-ranges", NULL)) - break; - } + np = of_find_node_with_property(NULL, "dma-ranges"); of_node_put(np); if (!np) { diff --git a/arch/powerpc/platforms/powermac/setup.c b/arch/powerpc/platforms/powermac/setup.c index 82c14d2..1293772 100644 --- a/arch/powerpc/platforms/powermac/setup.c +++ b/arch/powerpc/platforms/powermac/setup.c @@ -310,9 +310,7 @@ static void __init pmac_setup_arch(void) } /* See if newworld or oldworld */ - for (ic = NULL; (ic = of_find_all_nodes(ic)) != NULL; ) - if (of_get_property(ic, "interrupt-controller", NULL)) - break; + ic = of_find_node_with_property(NULL, "interrupt-controller"); if (ic) { pmac_newworld = 1; of_node_put(ic); diff --git a/arch/powerpc/platforms/ps3/device-init.c b/arch/powerpc/platforms/ps3/device-init.c index ffdd8e9..43816da 100644 --- a/arch/powerpc/platforms/ps3/device-init.c +++ b/arch/powerpc/platforms/ps3/device-init.c @@ -314,11 +314,17 @@ static int __init ps3_setup_vuart_device(enum ps3_match_id match_id, result = ps3_system_bus_device_register(&p->dev); - if (result) + if (result) { pr_debug("%s:%d ps3_system_bus_device_register failed\n", __func__, __LINE__); - + goto fail_device_register; + } pr_debug(" <- %s:%d\n", __func__, __LINE__); + return 0; + +fail_device_register: + kfree(p); + pr_debug(" <- %s:%d fail\n", __func__, __LINE__); return result; } @@ -463,11 +469,17 @@ static int __init ps3_register_sound_devices(void) result = ps3_system_bus_device_register(&p->dev); - if (result) + if (result) { pr_debug("%s:%d ps3_system_bus_device_register failed\n", __func__, __LINE__); - + goto fail_device_register; + } pr_debug(" <- %s:%d\n", __func__, __LINE__); + return 0; + +fail_device_register: + kfree(p); + pr_debug(" <- %s:%d failed\n", __func__, __LINE__); return result; } @@ -491,11 +503,18 @@ static int __init ps3_register_graphics_devices(void) result = ps3_system_bus_device_register(&p->dev); - if (result) + if (result) { pr_debug("%s:%d ps3_system_bus_device_register failed\n", __func__, __LINE__); + goto fail_device_register; + } pr_debug(" <- %s:%d\n", __func__, __LINE__); + return 0; + +fail_device_register: + kfree(p); + pr_debug(" <- %s:%d failed\n", __func__, __LINE__); return result; } diff --git a/arch/powerpc/platforms/ps3/setup.c b/arch/powerpc/platforms/ps3/setup.c index 77bc330..bfc33fb 100644 --- a/arch/powerpc/platforms/ps3/setup.c +++ b/arch/powerpc/platforms/ps3/setup.c @@ -42,6 +42,10 @@ #define DBG pr_debug #endif +/* mutex synchronizing GPU accesses and video mode changes */ +DEFINE_MUTEX(ps3_gpu_mutex); +EXPORT_SYMBOL_GPL(ps3_gpu_mutex); + #if !defined(CONFIG_SMP) static void smp_send_stop(void) {} #endif diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c index 54816d7..989d646 100644 --- a/arch/powerpc/platforms/pseries/eeh.c +++ b/arch/powerpc/platforms/pseries/eeh.c @@ -21,6 +21,8 @@ * Please address comments and feedback to Linas Vepstas <linas@austin.ibm.com> */ +#undef DEBUG + #include <linux/delay.h> #include <linux/init.h> #include <linux/list.h> @@ -488,10 +490,8 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev) if (!(pdn->eeh_mode & EEH_MODE_SUPPORTED) || pdn->eeh_mode & EEH_MODE_NOCHECK) { ignored_check++; -#ifdef DEBUG - printk ("EEH:ignored check (%x) for %s %s\n", - pdn->eeh_mode, pci_name (dev), dn->full_name); -#endif + pr_debug("EEH: Ignored check (%x) for %s %s\n", + pdn->eeh_mode, pci_name (dev), dn->full_name); return 0; } @@ -1014,10 +1014,9 @@ static void *early_enable_eeh(struct device_node *dn, void *data) eeh_subsystem_enabled = 1; pdn->eeh_mode |= EEH_MODE_SUPPORTED; -#ifdef DEBUG - printk(KERN_DEBUG "EEH: %s: eeh enabled, config=%x pe_config=%x\n", - dn->full_name, pdn->eeh_config_addr, pdn->eeh_pe_config_addr); -#endif + pr_debug("EEH: %s: eeh enabled, config=%x pe_config=%x\n", + dn->full_name, pdn->eeh_config_addr, + pdn->eeh_pe_config_addr); } else { /* This device doesn't support EEH, but it may have an @@ -1161,13 +1160,17 @@ static void eeh_add_device_late(struct pci_dev *dev) if (!dev || !eeh_subsystem_enabled) return; -#ifdef DEBUG - printk(KERN_DEBUG "EEH: adding device %s\n", pci_name(dev)); -#endif + pr_debug("EEH: Adding device %s\n", pci_name(dev)); - pci_dev_get (dev); dn = pci_device_to_OF_node(dev); pdn = PCI_DN(dn); + if (pdn->pcidev == dev) { + pr_debug("EEH: Already referenced !\n"); + return; + } + WARN_ON(pdn->pcidev); + + pci_dev_get (dev); pdn->pcidev = dev; pci_addr_cache_insert_device(dev); @@ -1206,17 +1209,18 @@ static void eeh_remove_device(struct pci_dev *dev) return; /* Unregister the device with the EEH/PCI address search system */ -#ifdef DEBUG - printk(KERN_DEBUG "EEH: remove device %s\n", pci_name(dev)); -#endif - pci_addr_cache_remove_device(dev); - eeh_sysfs_remove_device(dev); + pr_debug("EEH: Removing device %s\n", pci_name(dev)); dn = pci_device_to_OF_node(dev); - if (PCI_DN(dn)->pcidev) { - PCI_DN(dn)->pcidev = NULL; - pci_dev_put (dev); + if (PCI_DN(dn)->pcidev == NULL) { + pr_debug("EEH: Not referenced !\n"); + return; } + PCI_DN(dn)->pcidev = NULL; + pci_dev_put (dev); + + pci_addr_cache_remove_device(dev); + eeh_sysfs_remove_device(dev); } void eeh_remove_bus_device(struct pci_dev *dev) diff --git a/arch/powerpc/platforms/pseries/pci_dlpar.c b/arch/powerpc/platforms/pseries/pci_dlpar.c index 7190493..5e1ed3d 100644 --- a/arch/powerpc/platforms/pseries/pci_dlpar.c +++ b/arch/powerpc/platforms/pseries/pci_dlpar.c @@ -25,6 +25,8 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#undef DEBUG + #include <linux/pci.h> #include <asm/pci-bridge.h> #include <asm/ppc-pci.h> @@ -69,74 +71,25 @@ EXPORT_SYMBOL_GPL(pcibios_find_pci_bus); * Remove all of the PCI devices under this bus both from the * linux pci device tree, and from the powerpc EEH address cache. */ -void -pcibios_remove_pci_devices(struct pci_bus *bus) +void pcibios_remove_pci_devices(struct pci_bus *bus) { - struct pci_dev *dev, *tmp; + struct pci_dev *dev, *tmp; + struct pci_bus *child_bus; + + /* First go down child busses */ + list_for_each_entry(child_bus, &bus->children, node) + pcibios_remove_pci_devices(child_bus); + pr_debug("PCI: Removing devices on bus %04x:%02x\n", + pci_domain_nr(bus), bus->number); list_for_each_entry_safe(dev, tmp, &bus->devices, bus_list) { + pr_debug(" * Removing %s...\n", pci_name(dev)); eeh_remove_bus_device(dev); - pci_remove_bus_device(dev); - } + pci_remove_bus_device(dev); + } } EXPORT_SYMBOL_GPL(pcibios_remove_pci_devices); -/* Must be called before pci_bus_add_devices */ -void -pcibios_fixup_new_pci_devices(struct pci_bus *bus) -{ - struct pci_dev *dev; - - list_for_each_entry(dev, &bus->devices, bus_list) { - /* Skip already-added devices */ - if (!dev->is_added) { - int i; - - /* Fill device archdata and setup iommu table */ - pcibios_setup_new_device(dev); - - pci_read_irq_line(dev); - for (i = 0; i < PCI_NUM_RESOURCES; i++) { - struct resource *r = &dev->resource[i]; - - if (r->parent || !r->start || !r->flags) - continue; - pci_claim_resource(dev, i); - } - } - } -} -EXPORT_SYMBOL_GPL(pcibios_fixup_new_pci_devices); - -static int -pcibios_pci_config_bridge(struct pci_dev *dev) -{ - u8 sec_busno; - struct pci_bus *child_bus; - - /* Get busno of downstream bus */ - pci_read_config_byte(dev, PCI_SECONDARY_BUS, &sec_busno); - - /* Add to children of PCI bridge dev->bus */ - child_bus = pci_add_new_bus(dev->bus, dev, sec_busno); - if (!child_bus) { - printk (KERN_ERR "%s: could not add second bus\n", __func__); - return -EIO; - } - sprintf(child_bus->name, "PCI Bus #%02x", child_bus->number); - - pci_scan_child_bus(child_bus); - - /* Fixup new pci devices */ - pcibios_fixup_new_pci_devices(child_bus); - - /* Make the discovered devices available */ - pci_bus_add_devices(child_bus); - - eeh_add_device_tree_late(child_bus); - return 0; -} - /** * pcibios_add_pci_devices - adds new pci devices to bus * @@ -147,10 +100,9 @@ pcibios_pci_config_bridge(struct pci_dev *dev) * is how this routine differs from other, similar pcibios * routines.) */ -void -pcibios_add_pci_devices(struct pci_bus * bus) +void pcibios_add_pci_devices(struct pci_bus * bus) { - int slotno, num, mode; + int slotno, num, mode, pass, max; struct pci_dev *dev; struct device_node *dn = pci_bus_to_OF_node(bus); @@ -162,26 +114,23 @@ pcibios_add_pci_devices(struct pci_bus * bus) if (mode == PCI_PROBE_DEVTREE) { /* use ofdt-based probe */ - of_scan_bus(dn, bus); - if (!list_empty(&bus->devices)) { - pcibios_fixup_new_pci_devices(bus); - pci_bus_add_devices(bus); - eeh_add_device_tree_late(bus); - } + of_rescan_bus(dn, bus); } else if (mode == PCI_PROBE_NORMAL) { /* use legacy probe */ slotno = PCI_SLOT(PCI_DN(dn->child)->devfn); num = pci_scan_slot(bus, PCI_DEVFN(slotno, 0)); - if (num) { - pcibios_fixup_new_pci_devices(bus); - pci_bus_add_devices(bus); - eeh_add_device_tree_late(bus); + if (!num) + return; + pcibios_setup_bus_devices(bus); + max = bus->secondary; + for (pass=0; pass < 2; pass++) + list_for_each_entry(dev, &bus->devices, bus_list) { + if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE || + dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) + max = pci_scan_bridge(bus, dev, max, pass); } - - list_for_each_entry(dev, &bus->devices, bus_list) - if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) - pcibios_pci_config_bridge(dev); } + pcibios_finish_adding_to_bus(bus); } EXPORT_SYMBOL_GPL(pcibios_add_pci_devices); @@ -190,6 +139,8 @@ struct pci_controller * __devinit init_phb_dynamic(struct device_node *dn) struct pci_controller *phb; int primary; + pr_debug("PCI: Initializing new hotplug PHB %s\n", dn->full_name); + primary = list_empty(&hose_list); phb = pcibios_alloc_controller(dn); if (!phb) @@ -203,11 +154,59 @@ struct pci_controller * __devinit init_phb_dynamic(struct device_node *dn) eeh_add_device_tree_early(dn); scan_phb(phb); - pcibios_allocate_bus_resources(phb->bus); - pcibios_fixup_new_pci_devices(phb->bus); - pci_bus_add_devices(phb->bus); - eeh_add_device_tree_late(phb->bus); + pcibios_finish_adding_to_bus(phb->bus); return phb; } EXPORT_SYMBOL_GPL(init_phb_dynamic); + +/* RPA-specific bits for removing PHBs */ +int remove_phb_dynamic(struct pci_controller *phb) +{ + struct pci_bus *b = phb->bus; + struct resource *res; + int rc, i; + + pr_debug("PCI: Removing PHB %04x:%02x... \n", + pci_domain_nr(b), b->number); + + /* We cannot to remove a root bus that has children */ + if (!(list_empty(&b->children) && list_empty(&b->devices))) + return -EBUSY; + + /* We -know- there aren't any child devices anymore at this stage + * and thus, we can safely unmap the IO space as it's not in use + */ + res = &phb->io_resource; + if (res->flags & IORESOURCE_IO) { + rc = pcibios_unmap_io_space(b); + if (rc) { + printk(KERN_ERR "%s: failed to unmap IO on bus %s\n", + __func__, b->name); + return 1; + } + } + + /* Unregister the bridge device from sysfs and remove the PCI bus */ + device_unregister(b->bridge); + phb->bus = NULL; + pci_remove_bus(b); + + /* Now release the IO resource */ + if (res->flags & IORESOURCE_IO) + release_resource(res); + + /* Release memory resources */ + for (i = 0; i < 3; ++i) { + res = &phb->mem_resources[i]; + if (!(res->flags & IORESOURCE_MEM)) + continue; + release_resource(res); + } + + /* Free pci_controller data structure */ + pcibios_free_controller(phb); + + return 0; +} +EXPORT_SYMBOL_GPL(remove_phb_dynamic); diff --git a/arch/powerpc/platforms/pseries/xics.c b/arch/powerpc/platforms/pseries/xics.c index e190477..75a289b 100644 --- a/arch/powerpc/platforms/pseries/xics.c +++ b/arch/powerpc/platforms/pseries/xics.c @@ -579,7 +579,7 @@ static void xics_update_irq_servers(void) int i, j; struct device_node *np; u32 ilen; - const u32 *ireg, *isize; + const u32 *ireg; u32 hcpuid; /* Find the server numbers for the boot cpu. */ @@ -607,11 +607,6 @@ static void xics_update_irq_servers(void) } } - /* get the bit size of server numbers */ - isize = of_get_property(np, "ibm,interrupt-server#-size", NULL); - if (isize) - interrupt_server_size = *isize; - of_node_put(np); } @@ -682,6 +677,7 @@ void __init xics_init_IRQ(void) struct device_node *np; u32 indx = 0; int found = 0; + const u32 *isize; ppc64_boot_msg(0x20, "XICS Init"); @@ -701,6 +697,26 @@ void __init xics_init_IRQ(void) if (found == 0) return; + /* get the bit size of server numbers */ + found = 0; + + for_each_compatible_node(np, NULL, "ibm,ppc-xics") { + isize = of_get_property(np, "ibm,interrupt-server#-size", NULL); + + if (!isize) + continue; + + if (!found) { + interrupt_server_size = *isize; + found = 1; + } else if (*isize != interrupt_server_size) { + printk(KERN_WARNING "XICS: " + "mismatched ibm,interrupt-server#-size\n"); + interrupt_server_size = max(*isize, + interrupt_server_size); + } + } + xics_update_irq_servers(); xics_init_host(); diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c index 1890fb0..c82babb 100644 --- a/arch/powerpc/sysdev/mpic.c +++ b/arch/powerpc/sysdev/mpic.c @@ -661,17 +661,6 @@ static inline void mpic_eoi(struct mpic *mpic) (void)mpic_cpu_read(MPIC_INFO(CPU_WHOAMI)); } -#ifdef CONFIG_SMP -static irqreturn_t mpic_ipi_action(int irq, void *data) -{ - long ipi = (long)data; - - smp_message_recv(ipi); - - return IRQ_HANDLED; -} -#endif /* CONFIG_SMP */ - /* * Linux descriptor level callbacks */ @@ -1548,13 +1537,7 @@ unsigned int mpic_get_mcirq(void) void mpic_request_ipis(void) { struct mpic *mpic = mpic_primary; - long i, err; - static char *ipi_names[] = { - "IPI0 (call function)", - "IPI1 (reschedule)", - "IPI2 (call function single)", - "IPI3 (debugger break)", - }; + int i; BUG_ON(mpic == NULL); printk(KERN_INFO "mpic: requesting IPIs ... \n"); @@ -1563,17 +1546,10 @@ void mpic_request_ipis(void) unsigned int vipi = irq_create_mapping(mpic->irqhost, mpic->ipi_vecs[0] + i); if (vipi == NO_IRQ) { - printk(KERN_ERR "Failed to map IPI %ld\n", i); - break; - } - err = request_irq(vipi, mpic_ipi_action, - IRQF_DISABLED|IRQF_PERCPU, - ipi_names[i], (void *)i); - if (err) { - printk(KERN_ERR "Request of irq %d for IPI %ld failed\n", - vipi, i); - break; + printk(KERN_ERR "Failed to map %s\n", smp_ipi_name[i]); + continue; } + smp_request_message_ipi(vipi, i); } } diff --git a/arch/powerpc/sysdev/qe_lib/qe.c b/arch/powerpc/sysdev/qe_lib/qe.c index b3b73ae..01bce37 100644 --- a/arch/powerpc/sysdev/qe_lib/qe.c +++ b/arch/powerpc/sysdev/qe_lib/qe.c @@ -19,6 +19,7 @@ #include <linux/kernel.h> #include <linux/param.h> #include <linux/string.h> +#include <linux/spinlock.h> #include <linux/mm.h> #include <linux/interrupt.h> #include <linux/bootmem.h> @@ -38,6 +39,8 @@ static void qe_snums_init(void); static int qe_sdma_init(void); static DEFINE_SPINLOCK(qe_lock); +DEFINE_SPINLOCK(cmxgcr_lock); +EXPORT_SYMBOL(cmxgcr_lock); /* QE snum state */ enum qe_snum_state { diff --git a/arch/powerpc/sysdev/qe_lib/ucc.c b/arch/powerpc/sysdev/qe_lib/ucc.c index 1d78071..ebb442e 100644 --- a/arch/powerpc/sysdev/qe_lib/ucc.c +++ b/arch/powerpc/sysdev/qe_lib/ucc.c @@ -18,6 +18,7 @@ #include <linux/errno.h> #include <linux/slab.h> #include <linux/stddef.h> +#include <linux/spinlock.h> #include <linux/module.h> #include <asm/irq.h> @@ -26,9 +27,6 @@ #include <asm/qe.h> #include <asm/ucc.h> -DEFINE_SPINLOCK(cmxgcr_lock); -EXPORT_SYMBOL(cmxgcr_lock); - int ucc_set_qe_mux_mii_mng(unsigned int ucc_num) { unsigned long flags; diff --git a/arch/sparc/include/asm/device.h b/arch/sparc/include/asm/device.h index 19790eb..3702e08 100644 --- a/arch/sparc/include/asm/device.h +++ b/arch/sparc/include/asm/device.h @@ -20,4 +20,16 @@ struct dev_archdata { int numa_node; }; +static inline void dev_archdata_set_node(struct dev_archdata *ad, + struct device_node *np) +{ + ad->prom_node = np; +} + +static inline struct device_node * +dev_archdata_get_node(const struct dev_archdata *ad) +{ + return ad->prom_node; +} + #endif /* _ASM_SPARC_DEVICE_H */ diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 43d6ba8..f57907a 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -631,6 +631,12 @@ config HVC_XEN help Xen virtual console device driver +config HVC_UDBG + bool "udbg based fake hypervisor console" + depends on PPC && EXPERIMENTAL + select HVC_DRIVER + default n + config VIRTIO_CONSOLE tristate "Virtio console" depends on VIRTIO diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 438f713..52e1552 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -50,6 +50,7 @@ obj-$(CONFIG_HVC_BEAT) += hvc_beat.o obj-$(CONFIG_HVC_DRIVER) += hvc_console.o obj-$(CONFIG_HVC_IRQ) += hvc_irq.o obj-$(CONFIG_HVC_XEN) += hvc_xen.o +obj-$(CONFIG_HVC_UDBG) += hvc_udbg.o obj-$(CONFIG_VIRTIO_CONSOLE) += virtio_console.o obj-$(CONFIG_RAW_DRIVER) += raw.o obj-$(CONFIG_SGI_SNSC) += snsc.o snsc_event.o diff --git a/drivers/char/bsr.c b/drivers/char/bsr.c index 456f54d..977dfb1 100644 --- a/drivers/char/bsr.c +++ b/drivers/char/bsr.c @@ -60,6 +60,8 @@ struct bsr_dev { unsigned bsr_num; /* bsr id number for its type */ int bsr_minor; + struct list_head bsr_list; + dev_t bsr_dev; struct cdev bsr_cdev; struct device *bsr_device; @@ -67,8 +69,8 @@ struct bsr_dev { }; -static unsigned num_bsr_devs; -static struct bsr_dev *bsr_devs; +static unsigned total_bsr_devs; +static struct list_head bsr_devs = LIST_HEAD_INIT(bsr_devs); static struct class *bsr_class; static int bsr_major; @@ -146,24 +148,25 @@ const static struct file_operations bsr_fops = { static void bsr_cleanup_devs(void) { - int i; - for (i=0 ; i < num_bsr_devs; i++) { - struct bsr_dev *cur = bsr_devs + i; + struct bsr_dev *cur, *n; + + list_for_each_entry_safe(cur, n, &bsr_devs, bsr_list) { if (cur->bsr_device) { cdev_del(&cur->bsr_cdev); device_del(cur->bsr_device); } + list_del(&cur->bsr_list); + kfree(cur); } - - kfree(bsr_devs); } -static int bsr_create_devs(struct device_node *bn) +static int bsr_add_node(struct device_node *bn) { - int bsr_stride_len, bsr_bytes_len; + int bsr_stride_len, bsr_bytes_len, num_bsr_devs; const u32 *bsr_stride; const u32 *bsr_bytes; unsigned i; + int ret = -ENODEV; bsr_stride = of_get_property(bn, "ibm,lock-stride", &bsr_stride_len); bsr_bytes = of_get_property(bn, "ibm,#lock-bytes", &bsr_bytes_len); @@ -171,35 +174,36 @@ static int bsr_create_devs(struct device_node *bn) if (!bsr_stride || !bsr_bytes || (bsr_stride_len != bsr_bytes_len)) { printk(KERN_ERR "bsr of-node has missing/incorrect property\n"); - return -ENODEV; + return ret; } num_bsr_devs = bsr_bytes_len / sizeof(u32); - /* only a warning, its informational since we'll fail and exit */ - WARN_ON(num_bsr_devs > BSR_MAX_DEVS); - - bsr_devs = kzalloc(sizeof(struct bsr_dev) * num_bsr_devs, GFP_KERNEL); - if (!bsr_devs) - return -ENOMEM; - for (i = 0 ; i < num_bsr_devs; i++) { - struct bsr_dev *cur = bsr_devs + i; + struct bsr_dev *cur = kzalloc(sizeof(struct bsr_dev), + GFP_KERNEL); struct resource res; int result; + if (!cur) { + printk(KERN_ERR "Unable to alloc bsr dev\n"); + ret = -ENOMEM; + goto out_err; + } + result = of_address_to_resource(bn, i, &res); if (result < 0) { - printk(KERN_ERR "bsr of-node has invalid reg property\n"); - goto out_err; + printk(KERN_ERR "bsr of-node has invalid reg property, skipping\n"); + kfree(cur); + continue; } - cur->bsr_minor = i; + cur->bsr_minor = i + total_bsr_devs; cur->bsr_addr = res.start; cur->bsr_len = res.end - res.start + 1; cur->bsr_bytes = bsr_bytes[i]; cur->bsr_stride = bsr_stride[i]; - cur->bsr_dev = MKDEV(bsr_major, i); + cur->bsr_dev = MKDEV(bsr_major, i + total_bsr_devs); switch(cur->bsr_bytes) { case 8: @@ -220,14 +224,15 @@ static int bsr_create_devs(struct device_node *bn) } cur->bsr_num = bsr_types[cur->bsr_type]; - bsr_types[cur->bsr_type] = cur->bsr_num + 1; snprintf(cur->bsr_name, 32, "bsr%d_%d", cur->bsr_bytes, cur->bsr_num); cdev_init(&cur->bsr_cdev, &bsr_fops); result = cdev_add(&cur->bsr_cdev, cur->bsr_dev, 1); - if (result) + if (result) { + kfree(cur); goto out_err; + } cur->bsr_device = device_create(bsr_class, NULL, cur->bsr_dev, cur, cur->bsr_name); @@ -235,16 +240,37 @@ static int bsr_create_devs(struct device_node *bn) printk(KERN_ERR "device_create failed for %s\n", cur->bsr_name); cdev_del(&cur->bsr_cdev); + kfree(cur); goto out_err; } + + bsr_types[cur->bsr_type] = cur->bsr_num + 1; + list_add_tail(&cur->bsr_list, &bsr_devs); } + total_bsr_devs += num_bsr_devs; + return 0; out_err: bsr_cleanup_devs(); - return -ENODEV; + return ret; +} + +static int bsr_create_devs(struct device_node *bn) +{ + int ret; + + while (bn) { + ret = bsr_add_node(bn); + if (ret) { + of_node_put(bn); + return ret; + } + bn = of_find_compatible_node(bn, NULL, "ibm,bsr"); + } + return 0; } static int __init bsr_init(void) @@ -254,7 +280,7 @@ static int __init bsr_init(void) int ret = -ENODEV; int result; - np = of_find_compatible_node(NULL, "ibm,bsr", "ibm,bsr"); + np = of_find_compatible_node(NULL, NULL, "ibm,bsr"); if (!np) goto out_err; @@ -272,10 +298,10 @@ static int __init bsr_init(void) goto out_err_2; } - if ((ret = bsr_create_devs(np)) < 0) + if ((ret = bsr_create_devs(np)) < 0) { + np = NULL; goto out_err_3; - - of_node_put(np); + } return 0; diff --git a/drivers/char/hvc_console.c b/drivers/char/hvc_console.c index 5b819b1..74ecb5b 100644 --- a/drivers/char/hvc_console.c +++ b/drivers/char/hvc_console.c @@ -689,10 +689,8 @@ EXPORT_SYMBOL_GPL(hvc_poll); */ void hvc_resize(struct hvc_struct *hp, struct winsize ws) { - if ((hp->ws.ws_row != ws.ws_row) || (hp->ws.ws_col != ws.ws_col)) { - hp->ws = ws; - schedule_work(&hp->tty_resize); - } + hp->ws = ws; + schedule_work(&hp->tty_resize); } /* diff --git a/drivers/char/hvc_console.h b/drivers/char/hvc_console.h index 8297dbc..3c85d78 100644 --- a/drivers/char/hvc_console.h +++ b/drivers/char/hvc_console.h @@ -48,7 +48,7 @@ struct hvc_struct { spinlock_t lock; int index; struct tty_struct *tty; - unsigned int count; + int count; int do_wakeup; char *outbuf; int outbuf_size; diff --git a/drivers/char/hvc_iseries.c b/drivers/char/hvc_iseries.c index b74a2f8..449727b 100644 --- a/drivers/char/hvc_iseries.c +++ b/drivers/char/hvc_iseries.c @@ -575,8 +575,10 @@ static int __init hvc_find_vtys(void) * of console adapters. */ if ((num_found >= MAX_NR_HVC_CONSOLES) || - (num_found >= VTTY_PORTS)) + (num_found >= VTTY_PORTS)) { + of_node_put(vty); break; + } vtermno = of_get_property(vty, "reg", NULL); if (!vtermno) diff --git a/drivers/char/hvc_udbg.c b/drivers/char/hvc_udbg.c new file mode 100644 index 0000000..bd63ba8 --- /dev/null +++ b/drivers/char/hvc_udbg.c @@ -0,0 +1,96 @@ +/* + * udbg interface to hvc_console.c + * + * (C) Copyright David Gibson, IBM Corporation 2008. + * + * 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/console.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/moduleparam.h> +#include <linux/types.h> +#include <linux/irq.h> + +#include <asm/udbg.h> + +#include "hvc_console.h" + +struct hvc_struct *hvc_udbg_dev; + +static int hvc_udbg_put(uint32_t vtermno, const char *buf, int count) +{ + int i; + + for (i = 0; i < count; i++) + udbg_putc(buf[i]); + + return i; +} + +static int hvc_udbg_get(uint32_t vtermno, char *buf, int count) +{ + int i, c; + + if (!udbg_getc_poll) + return 0; + + for (i = 0; i < count; i++) { + if ((c = udbg_getc_poll()) == -1) + break; + buf[i] = c; + } + + return i; +} + +static struct hv_ops hvc_udbg_ops = { + .get_chars = hvc_udbg_get, + .put_chars = hvc_udbg_put, +}; + +static int __init hvc_udbg_init(void) +{ + struct hvc_struct *hp; + + BUG_ON(hvc_udbg_dev); + + hp = hvc_alloc(0, NO_IRQ, &hvc_udbg_ops, 16); + if (IS_ERR(hp)) + return PTR_ERR(hp); + + hvc_udbg_dev = hp; + + return 0; +} +module_init(hvc_udbg_init); + +static void __exit hvc_udbg_exit(void) +{ + if (hvc_udbg_dev) + hvc_remove(hvc_udbg_dev); +} +module_exit(hvc_udbg_exit); + +static int __init hvc_udbg_console_init(void) +{ + hvc_instantiate(0, 0, &hvc_udbg_ops); + add_preferred_console("hvc", 0, NULL); + + return 0; +} +console_initcall(hvc_udbg_console_init); diff --git a/drivers/char/hvc_vio.c b/drivers/char/hvc_vio.c index 019e0b5..bd62dc8 100644 --- a/drivers/char/hvc_vio.c +++ b/drivers/char/hvc_vio.c @@ -153,8 +153,10 @@ static int hvc_find_vtys(void) /* We have statically defined space for only a certain number * of console adapters. */ - if (num_found >= MAX_NR_HVC_CONSOLES) + if (num_found >= MAX_NR_HVC_CONSOLES) { + of_node_put(vty); break; + } vtermno = of_get_property(vty, "reg", NULL); if (!vtermno) diff --git a/drivers/char/hvcs.c b/drivers/char/hvcs.c index 473d9b1..6e6eb44 100644 --- a/drivers/char/hvcs.c +++ b/drivers/char/hvcs.c @@ -269,7 +269,7 @@ struct hvcs_struct { unsigned int index; struct tty_struct *tty; - unsigned int open_count; + int open_count; /* * Used to tell the driver kernel_thread what operations need to take diff --git a/drivers/char/hvsi.c b/drivers/char/hvsi.c index 59c6f9a..af05528 100644 --- a/drivers/char/hvsi.c +++ b/drivers/char/hvsi.c @@ -75,7 +75,7 @@ struct hvsi_struct { spinlock_t lock; int index; struct tty_struct *tty; - unsigned int count; + int count; uint8_t throttle_buf[128]; uint8_t outbuf[N_OUTBUF]; /* to implement write_room and chars_in_buffer */ /* inbuf is for packet reassembly. leave a little room for leftovers. */ diff --git a/drivers/macintosh/windfarm_smu_sat.c b/drivers/macintosh/windfarm_smu_sat.c index 7f2be4b..7847e98 100644 --- a/drivers/macintosh/windfarm_smu_sat.c +++ b/drivers/macintosh/windfarm_smu_sat.c @@ -87,11 +87,12 @@ struct smu_sdbp_header *smu_sat_get_sdb_partition(unsigned int sat_id, int id, return NULL; } - len = i2c_smbus_read_word_data(&sat->i2c, 9); - if (len < 0) { + err = i2c_smbus_read_word_data(&sat->i2c, 9); + if (err < 0) { printk(KERN_ERR "smu_sat_get_sdb_part rd len error\n"); return NULL; } + len = err; if (len == 0) { printk(KERN_ERR "smu_sat_get_sdb_part no partition %x\n", id); return NULL; diff --git a/drivers/of/base.c b/drivers/of/base.c index 7c79e94..4f884a3 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -329,6 +329,41 @@ struct device_node *of_find_compatible_node(struct device_node *from, EXPORT_SYMBOL(of_find_compatible_node); /** + * of_find_node_with_property - Find a node which has a property with + * the given name. + * @from: The node to start searching from or NULL, the node + * you pass will not be searched, only the next one + * will; typically, you pass what the previous call + * returned. of_node_put() will be called on it + * @prop_name: The name of the property to look for. + * + * Returns a node pointer with refcount incremented, use + * of_node_put() on it when done. + */ +struct device_node *of_find_node_with_property(struct device_node *from, + const char *prop_name) +{ + struct device_node *np; + struct property *pp; + + read_lock(&devtree_lock); + np = from ? from->allnext : allnodes; + for (; np; np = np->allnext) { + for (pp = np->properties; pp != 0; pp = pp->next) { + if (of_prop_cmp(pp->name, prop_name) == 0) { + of_node_get(np); + goto out; + } + } + } +out: + of_node_put(from); + read_unlock(&devtree_lock); + return np; +} +EXPORT_SYMBOL(of_find_node_with_property); + +/** * of_match_node - Tell if an device_node has a matching of_match structure * @matches: array of of device match structures to search in * @node: the of device structure to match against diff --git a/drivers/of/gpio.c b/drivers/of/gpio.c index 7cd7301..a4ba217 100644 --- a/drivers/of/gpio.c +++ b/drivers/of/gpio.c @@ -19,14 +19,17 @@ #include <asm/prom.h> /** - * of_get_gpio - Get a GPIO number from the device tree to use with GPIO API + * of_get_gpio_flags - Get a GPIO number and flags to use with GPIO API * @np: device node to get GPIO from * @index: index of the GPIO + * @flags: a flags pointer to fill in * * Returns GPIO number to use with Linux generic GPIO API, or one of the errno - * value on the error condition. + * value on the error condition. If @flags is not NULL the function also fills + * in flags for the GPIO. */ -int of_get_gpio(struct device_node *np, int index) +int of_get_gpio_flags(struct device_node *np, int index, + enum of_gpio_flags *flags) { int ret; struct device_node *gc; @@ -59,7 +62,11 @@ int of_get_gpio(struct device_node *np, int index) goto err1; } - ret = of_gc->xlate(of_gc, np, gpio_spec); + /* .xlate might decide to not fill in the flags, so clear it. */ + if (flags) + *flags = 0; + + ret = of_gc->xlate(of_gc, np, gpio_spec, flags); if (ret < 0) goto err1; @@ -70,26 +77,41 @@ err0: pr_debug("%s exited with status %d\n", __func__, ret); return ret; } -EXPORT_SYMBOL(of_get_gpio); +EXPORT_SYMBOL(of_get_gpio_flags); /** - * of_gpio_simple_xlate - translate gpio_spec to the GPIO number + * of_gpio_simple_xlate - translate gpio_spec to the GPIO number and flags * @of_gc: pointer to the of_gpio_chip structure * @np: device node of the GPIO chip * @gpio_spec: gpio specifier as found in the device tree + * @flags: a flags pointer to fill in * * This is simple translation function, suitable for the most 1:1 mapped * gpio chips. This function performs only one sanity check: whether gpio * is less than ngpios (that is specified in the gpio_chip). */ int of_gpio_simple_xlate(struct of_gpio_chip *of_gc, struct device_node *np, - const void *gpio_spec) + const void *gpio_spec, enum of_gpio_flags *flags) { const u32 *gpio = gpio_spec; + /* + * We're discouraging gpio_cells < 2, since that way you'll have to + * write your own xlate function (that will have to retrive the GPIO + * number and the flags from a single gpio cell -- this is possible, + * but not recommended). + */ + if (of_gc->gpio_cells < 2) { + WARN_ON(1); + return -EINVAL; + } + if (*gpio > of_gc->gc.ngpio) return -EINVAL; + if (flags) + *flags = gpio[1]; + return *gpio; } EXPORT_SYMBOL(of_gpio_simple_xlate); diff --git a/drivers/of/of_i2c.c b/drivers/of/of_i2c.c index 24bbef7..e1b0ad6 100644 --- a/drivers/of/of_i2c.c +++ b/drivers/of/of_i2c.c @@ -24,6 +24,7 @@ void of_register_i2c_devices(struct i2c_adapter *adap, for_each_child_of_node(adap_node, node) { struct i2c_board_info info = {}; + struct dev_archdata dev_ad = {}; const u32 *addr; int len; @@ -41,6 +42,9 @@ void of_register_i2c_devices(struct i2c_adapter *adap, info.addr = *addr; + dev_archdata_set_node(&dev_ad, node); + info.archdata = &dev_ad; + request_module("%s", info.type); result = i2c_new_device(adap, &info); @@ -51,6 +55,13 @@ void of_register_i2c_devices(struct i2c_adapter *adap, irq_dispose_mapping(info.irq); continue; } + + /* + * Get the node to not lose the dev_archdata->of_node. + * Currently there is no way to put it back, as well as no + * of_unregister_i2c_devices() call. + */ + of_node_get(node); } } EXPORT_SYMBOL(of_register_i2c_devices); diff --git a/drivers/pci/hotplug/rpadlpar_core.c b/drivers/pci/hotplug/rpadlpar_core.c index 9c2a22f..4e3e038 100644 --- a/drivers/pci/hotplug/rpadlpar_core.c +++ b/drivers/pci/hotplug/rpadlpar_core.c @@ -14,6 +14,9 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ + +#undef DEBUG + #include <linux/init.h> #include <linux/pci.h> #include <linux/string.h> @@ -151,20 +154,20 @@ static void dlpar_pci_add_bus(struct device_node *dn) return; } + /* Scan below the new bridge */ if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE || dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) of_scan_pci_bridge(dn, dev); - pcibios_fixup_new_pci_devices(dev->subordinate); - - /* Claim new bus resources */ - pcibios_claim_one_bus(dev->bus); - /* Map IO space for child bus, which may or may not succeed */ pcibios_map_io_space(dev->subordinate); - /* Add new devices to global lists. Register in proc, sysfs. */ - pci_bus_add_devices(phb->bus); + /* Finish adding it : resource allocation, adding devices, etc... + * Note that we need to perform the finish pass on the -parent- + * bus of the EADS bridge so the bridge device itself gets + * properly added + */ + pcibios_finish_adding_to_bus(phb->bus); } static int dlpar_add_pci_slot(char *drc_name, struct device_node *dn) @@ -203,27 +206,6 @@ static int dlpar_add_pci_slot(char *drc_name, struct device_node *dn) return 0; } -static int dlpar_remove_root_bus(struct pci_controller *phb) -{ - struct pci_bus *phb_bus; - int rc; - - phb_bus = phb->bus; - if (!(list_empty(&phb_bus->children) && - list_empty(&phb_bus->devices))) { - return -EBUSY; - } - - rc = pcibios_remove_root_bus(phb); - if (rc) - return -EIO; - - device_unregister(phb_bus->bridge); - pci_remove_bus(phb_bus); - - return 0; -} - static int dlpar_remove_phb(char *drc_name, struct device_node *dn) { struct slot *slot; @@ -235,18 +217,15 @@ static int dlpar_remove_phb(char *drc_name, struct device_node *dn) /* If pci slot is hotplugable, use hotplug to remove it */ slot = find_php_slot(dn); - if (slot) { - if (rpaphp_deregister_slot(slot)) { - printk(KERN_ERR - "%s: unable to remove hotplug slot %s\n", - __func__, drc_name); - return -EIO; - } + if (slot && rpaphp_deregister_slot(slot)) { + printk(KERN_ERR "%s: unable to remove hotplug slot %s\n", + __func__, drc_name); + return -EIO; } pdn = dn->data; BUG_ON(!pdn || !pdn->phb); - rc = dlpar_remove_root_bus(pdn->phb); + rc = remove_phb_dynamic(pdn->phb); if (rc < 0) return rc; @@ -378,26 +357,38 @@ int dlpar_remove_pci_slot(char *drc_name, struct device_node *dn) if (!bus) return -EINVAL; - /* If pci slot is hotplugable, use hotplug to remove it */ + pr_debug("PCI: Removing PCI slot below EADS bridge %s\n", + bus->self ? pci_name(bus->self) : "<!PHB!>"); + slot = find_php_slot(dn); if (slot) { + pr_debug("PCI: Removing hotplug slot for %04x:%02x...\n", + pci_domain_nr(bus), bus->number); + if (rpaphp_deregister_slot(slot)) { printk(KERN_ERR "%s: unable to remove hotplug slot %s\n", __func__, drc_name); return -EIO; } - } else - pcibios_remove_pci_devices(bus); + } + + /* Remove all devices below slot */ + pcibios_remove_pci_devices(bus); + /* Unmap PCI IO space */ if (pcibios_unmap_io_space(bus)) { printk(KERN_ERR "%s: failed to unmap bus range\n", __func__); return -ERANGE; } + /* Remove the EADS bridge device itself */ BUG_ON(!bus->self); + pr_debug("PCI: Now removing bridge device %s\n", pci_name(bus->self)); + eeh_remove_bus_device(bus->self); pci_remove_bus_device(bus->self); + return 0; } diff --git a/drivers/ps3/ps3av.c b/drivers/ps3/ps3av.c index 06848b2..5324978 100644 --- a/drivers/ps3/ps3av.c +++ b/drivers/ps3/ps3av.c @@ -59,8 +59,6 @@ static struct ps3av { struct ps3av_reply_hdr reply_hdr; u8 raw[PS3AV_BUF_SIZE]; } recv_buf; - void (*flip_ctl)(int on, void *data); - void *flip_data; } *ps3av; /* color space */ @@ -939,24 +937,6 @@ int ps3av_audio_mute(int mute) EXPORT_SYMBOL_GPL(ps3av_audio_mute); -void ps3av_register_flip_ctl(void (*flip_ctl)(int on, void *data), - void *flip_data) -{ - mutex_lock(&ps3av->mutex); - ps3av->flip_ctl = flip_ctl; - ps3av->flip_data = flip_data; - mutex_unlock(&ps3av->mutex); -} -EXPORT_SYMBOL_GPL(ps3av_register_flip_ctl); - -void ps3av_flip_ctl(int on) -{ - mutex_lock(&ps3av->mutex); - if (ps3av->flip_ctl) - ps3av->flip_ctl(on, ps3av->flip_data); - mutex_unlock(&ps3av->mutex); -} - static int ps3av_probe(struct ps3_system_bus_device *dev) { int res; diff --git a/drivers/ps3/ps3av_cmd.c b/drivers/ps3/ps3av_cmd.c index 11eb503..716596e 100644 --- a/drivers/ps3/ps3av_cmd.c +++ b/drivers/ps3/ps3av_cmd.c @@ -864,7 +864,7 @@ int ps3av_cmd_avb_param(struct ps3av_pkt_avb_param *avb, u32 send_len) { int res; - ps3av_flip_ctl(0); /* flip off */ + mutex_lock(&ps3_gpu_mutex); /* avb packet */ res = ps3av_do_pkt(PS3AV_CID_AVB_PARAM, send_len, sizeof(*avb), @@ -878,7 +878,7 @@ int ps3av_cmd_avb_param(struct ps3av_pkt_avb_param *avb, u32 send_len) res); out: - ps3av_flip_ctl(1); /* flip on */ + mutex_unlock(&ps3_gpu_mutex); return res; } diff --git a/drivers/serial/pmac_zilog.c b/drivers/serial/pmac_zilog.c index 317b061..ad34885 100644 --- a/drivers/serial/pmac_zilog.c +++ b/drivers/serial/pmac_zilog.c @@ -1383,6 +1383,29 @@ static int pmz_verify_port(struct uart_port *port, struct serial_struct *ser) return -EINVAL; } +#ifdef CONFIG_CONSOLE_POLL + +static int pmz_poll_get_char(struct uart_port *port) +{ + struct uart_pmac_port *uap = (struct uart_pmac_port *)port; + + while ((read_zsreg(uap, R0) & Rx_CH_AV) == 0) + udelay(5); + return read_zsdata(uap); +} + +static void pmz_poll_put_char(struct uart_port *port, unsigned char c) +{ + struct uart_pmac_port *uap = (struct uart_pmac_port *)port; + + /* Wait for the transmit buffer to empty. */ + while ((read_zsreg(uap, R0) & Tx_BUF_EMP) == 0) + udelay(5); + write_zsdata(uap, c); +} + +#endif + static struct uart_ops pmz_pops = { .tx_empty = pmz_tx_empty, .set_mctrl = pmz_set_mctrl, @@ -1400,6 +1423,10 @@ static struct uart_ops pmz_pops = { .request_port = pmz_request_port, .config_port = pmz_config_port, .verify_port = pmz_verify_port, +#ifdef CONFIG_CONSOLE_POLL + .poll_get_char = pmz_poll_get_char, + .poll_put_char = pmz_poll_put_char, +#endif }; /* diff --git a/drivers/video/ps3fb.c b/drivers/video/ps3fb.c index 4b5d807..bd3e39b 100644 --- a/drivers/video/ps3fb.c +++ b/drivers/video/ps3fb.c @@ -460,12 +460,16 @@ static void ps3fb_sync_image(struct device *dev, u64 frame_offset, line_length |= (u64)src_line_length << 32; src_offset += GPU_FB_START; + + mutex_lock(&ps3_gpu_mutex); status = lv1_gpu_context_attribute(ps3fb.context_handle, L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT, dst_offset, GPU_IOIF + src_offset, L1GPU_FB_BLIT_WAIT_FOR_COMPLETION | (width << 16) | height, line_length); + mutex_unlock(&ps3_gpu_mutex); + if (status) dev_err(dev, "%s: lv1_gpu_context_attribute FB_BLIT failed: %d\n", @@ -784,15 +788,6 @@ static int ps3fb_wait_for_vsync(u32 crtc) return 0; } -static void ps3fb_flip_ctl(int on, void *data) -{ - struct ps3fb_priv *priv = data; - if (on) - atomic_dec_if_positive(&priv->ext_flip); - else - atomic_inc(&priv->ext_flip); -} - /* * ioctl @@ -1228,7 +1223,6 @@ static int __devinit ps3fb_probe(struct ps3_system_bus_device *dev) } ps3fb.task = task; - ps3av_register_flip_ctl(ps3fb_flip_ctl, &ps3fb); return 0; @@ -1258,10 +1252,9 @@ static int ps3fb_shutdown(struct ps3_system_bus_device *dev) dev_dbg(&dev->core, " -> %s:%d\n", __func__, __LINE__); - ps3fb_flip_ctl(0, &ps3fb); /* flip off */ + atomic_inc(&ps3fb.ext_flip); /* flip off */ ps3fb.dinfo->irq.mask = 0; - ps3av_register_flip_ctl(NULL, NULL); if (ps3fb.task) { struct task_struct *task = ps3fb.task; ps3fb.task = NULL; diff --git a/include/linux/of.h b/include/linux/of.h index e2488f5..6a7efa2 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -57,6 +57,12 @@ extern struct device_node *of_get_next_child(const struct device_node *node, for (child = of_get_next_child(parent, NULL); child != NULL; \ child = of_get_next_child(parent, child)) +extern struct device_node *of_find_node_with_property( + struct device_node *from, const char *prop_name); +#define for_each_node_with_property(dn, prop_name) \ + for (dn = of_find_node_with_property(NULL, prop_name); dn; \ + dn = of_find_node_with_property(dn, prop_name)) + extern struct property *of_find_property(const struct device_node *np, const char *name, int *lenp); diff --git a/include/linux/of_gpio.h b/include/linux/of_gpio.h index 67db101..e25abf6 100644 --- a/include/linux/of_gpio.h +++ b/include/linux/of_gpio.h @@ -14,9 +14,22 @@ #ifndef __LINUX_OF_GPIO_H #define __LINUX_OF_GPIO_H +#include <linux/compiler.h> +#include <linux/kernel.h> #include <linux/errno.h> #include <linux/gpio.h> +struct device_node; + +/* + * This is Linux-specific flags. By default controllers' and Linux' mapping + * match, but GPIO controllers are free to translate their own flags to + * Linux-specific in their .xlate callback. Though, 1:1 mapping is recommended. + */ +enum of_gpio_flags { + OF_GPIO_ACTIVE_LOW = 0x1, +}; + #ifdef CONFIG_OF_GPIO /* @@ -26,7 +39,7 @@ struct of_gpio_chip { struct gpio_chip gc; int gpio_cells; int (*xlate)(struct of_gpio_chip *of_gc, struct device_node *np, - const void *gpio_spec); + const void *gpio_spec, enum of_gpio_flags *flags); }; static inline struct of_gpio_chip *to_of_gpio_chip(struct gpio_chip *gc) @@ -50,20 +63,37 @@ static inline struct of_mm_gpio_chip *to_of_mm_gpio_chip(struct gpio_chip *gc) return container_of(of_gc, struct of_mm_gpio_chip, of_gc); } -extern int of_get_gpio(struct device_node *np, int index); +extern int of_get_gpio_flags(struct device_node *np, int index, + enum of_gpio_flags *flags); + extern int of_mm_gpiochip_add(struct device_node *np, struct of_mm_gpio_chip *mm_gc); extern int of_gpio_simple_xlate(struct of_gpio_chip *of_gc, struct device_node *np, - const void *gpio_spec); + const void *gpio_spec, + enum of_gpio_flags *flags); #else /* Drivers may not strictly depend on the GPIO support, so let them link. */ -static inline int of_get_gpio(struct device_node *np, int index) +static inline int of_get_gpio_flags(struct device_node *np, int index, + enum of_gpio_flags *flags) { return -ENOSYS; } #endif /* CONFIG_OF_GPIO */ +/** + * of_get_gpio - Get a GPIO number to use with GPIO API + * @np: device node to get GPIO from + * @index: index of the GPIO + * + * Returns GPIO number to use with Linux generic GPIO API, or one of the errno + * value on the error condition. + */ +static inline int of_get_gpio(struct device_node *np, int index) +{ + return of_get_gpio_flags(np, index, NULL); +} + #endif /* __LINUX_OF_GPIO_H */ |