diff --git a/Documentation/cachetlb.txt b/Documentation/cachetlb.txt index debf6813934a..866b76139420 100644 --- a/Documentation/cachetlb.txt +++ b/Documentation/cachetlb.txt @@ -253,7 +253,7 @@ Here are the routines, one by one: The first of these two routines is invoked after map_vm_area() has installed the page table entries. The second is invoked - before unmap_vm_area() deletes the page table entries. + before unmap_kernel_range() deletes the page table entries. There exists another whole class of cpu cache issues which currently require a whole different set of interfaces to handle properly. diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 3a159dac04f5..55b5d6a210d8 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -330,3 +330,15 @@ Who: Tejun Heo --------------------------- +What: The arch/ppc and include/asm-ppc directories +When: Jun 2008 +Why: The arch/powerpc tree is the merged architecture for ppc32 and ppc64 + platforms. Currently there are efforts underway to port the remaining + arch/ppc platforms to the merged tree. New submissions to the arch/ppc + tree have been frozen with the 2.6.22 kernel release and that tree will + remain in bug-fix only mode until its scheduled removal. Platforms + that are not ported by June 2008 will be removed due to the lack of an + interested maintainer. +Who: linuxppc-dev@ozlabs.org + +--------------------------- diff --git a/Documentation/powerpc/booting-without-of.txt b/Documentation/powerpc/booting-without-of.txt index d42d98107d49..0c2434822094 100644 --- a/Documentation/powerpc/booting-without-of.txt +++ b/Documentation/powerpc/booting-without-of.txt @@ -42,15 +42,16 @@ Table of Contents 1) Defining child nodes of an SOC 2) Representing devices without a current OF specification a) MDIO IO device - c) PHY nodes b) Gianfar-compatible ethernet nodes + c) PHY nodes d) Interrupt controllers e) I2C f) Freescale SOC USB controllers g) Freescale SOC SEC Security Engines h) Board Control and Status (BCSR) i) Freescale QUICC Engine module (QE) - g) Flash chip nodes + j) Flash chip nodes + k) Global Utilities Block VII - Specifying interrupt information for devices 1) interrupts property @@ -626,6 +627,14 @@ So the node content can be summarized as a start token, a full path, a list of properties, a list of child nodes, and an end token. Every child node is a full node structure itself as defined above. +NOTE: The above definition requires that all property definitions for +a particular node MUST precede any subnode definitions for that node. +Although the structure would not be ambiguous if properties and +subnodes were intermingled, the kernel parser requires that the +properties come first (up until at least 2.6.22). Any tools +manipulating a flattened tree must take care to preserve this +constraint. + 4) Device tree "strings" block In order to save space, property names, which are generally redundant, @@ -1782,6 +1791,33 @@ platforms are moved over to use the flattened-device-tree model. partition-names = "fs\0firmware"; }; + k) Global Utilities Block + + The global utilities block controls power management, I/O device + enabling, power-on-reset configuration monitoring, general-purpose + I/O signal configuration, alternate function selection for multiplexed + signals, and clock control. + + Required properties: + + - compatible : Should define the compatible device type for + global-utilities. + - reg : Offset and length of the register set for the device. + + Recommended properties: + + - fsl,has-rstcr : Indicates that the global utilities register set + contains a functioning "reset control register" (i.e. the board + is wired to reset upon setting the HRESET_REQ bit in this register). + + Example: + + global-utilities@e0000 { /* global utilities block */ + compatible = "fsl,mpc8548-guts"; + reg = ; + fsl,has-rstcr; + }; + More devices will be defined as this spec matures. VII - Specifying interrupt information for devices diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 5eaeafd30bdf..cbfbd981cdcd 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -4,17 +4,7 @@ mainmenu "Linux/PowerPC Kernel Configuration" -config PPC64 - bool "64-bit kernel" - default n - help - This option selects whether a 32-bit or a 64-bit kernel - will be built. - -config PPC_PM_NEEDS_RTC_LIB - bool - select RTC_LIB - default y if PM +source "arch/powerpc/platforms/Kconfig.cputype" config PPC32 bool @@ -132,123 +122,6 @@ config PPC64_SWSUSP depends on PPC64 && (BROKEN || (PPC_PMAC64 && EXPERIMENTAL)) default y -menu "Processor support" -choice - prompt "Processor Type" - depends on PPC32 - default 6xx - -config CLASSIC32 - bool "52xx/6xx/7xx/74xx" - select PPC_FPU - select 6xx - help - There are four families of PowerPC chips supported. The more common - types (601, 603, 604, 740, 750, 7400), the Motorola embedded - versions (821, 823, 850, 855, 860, 52xx, 82xx, 83xx), the AMCC - embedded versions (403 and 405) and the high end 64 bit Power - processors (POWER 3, POWER4, and IBM PPC970 also known as G5). - - This option is the catch-all for 6xx types, including some of the - embedded versions. Unless there is see an option for the specific - chip family you are using, you want this option. - - You do not want this if you are building a kernel for a 64 bit - IBM RS/6000 or an Apple G5, choose 6xx. - - If unsure, select this option - - Note that the kernel runs in 32-bit mode even on 64-bit chips. - -config PPC_82xx - bool "Freescale 82xx" - select 6xx - select PPC_FPU - -config PPC_83xx - bool "Freescale 83xx" - select 6xx - select FSL_SOC - select 83xx - select PPC_FPU - select WANT_DEVICE_TREE - -config PPC_85xx - bool "Freescale 85xx" - select E500 - select FSL_SOC - select 85xx - select WANT_DEVICE_TREE - -config PPC_86xx - bool "Freescale 86xx" - select 6xx - select FSL_SOC - select FSL_PCIE - select PPC_FPU - select ALTIVEC - help - The Freescale E600 SoCs have 74xx cores. - -config PPC_8xx - bool "Freescale 8xx" - select FSL_SOC - select 8xx - -config 40x - bool "AMCC 40x" - select PPC_DCR_NATIVE - -config 44x - bool "AMCC 44x" - select PPC_DCR_NATIVE - select WANT_DEVICE_TREE - -config E200 - bool "Freescale e200" - -endchoice - -config POWER4_ONLY - bool "Optimize for POWER4" - depends on PPC64 - default n - ---help--- - Cause the compiler to optimize for POWER4/POWER5/PPC970 processors. - The resulting binary will not work on POWER3 or RS64 processors - when compiled with binutils 2.15 or later. - -config POWER3 - bool - depends on PPC64 - default y if !POWER4_ONLY - -config POWER4 - depends on PPC64 - def_bool y - -config 6xx - bool - -# this is temp to handle compat with arch=ppc -config 8xx - bool - -# this is temp to handle compat with arch=ppc -config 83xx - bool - -# this is temp to handle compat with arch=ppc -config 85xx - bool - -config E500 - bool - -config PPC_FPU - bool - default y if PPC64 - config PPC_DCR_NATIVE bool default n @@ -267,134 +140,6 @@ config PPC_OF_PLATFORM_PCI depends on PPC64 # not supported on 32 bits yet default n -config 4xx - bool - depends on 40x || 44x - default y - -config BOOKE - bool - depends on E200 || E500 || 44x - default y - -config FSL_BOOKE - bool - depends on E200 || E500 - default y - -config PTE_64BIT - bool - depends on 44x || E500 - default y if 44x - default y if E500 && PHYS_64BIT - -config PHYS_64BIT - bool 'Large physical address support' if E500 - depends on 44x || E500 - select RESOURCES_64BIT - default y if 44x - ---help--- - This option enables kernel support for larger than 32-bit physical - addresses. This features is not be available on all e500 cores. - - If in doubt, say N here. - -config ALTIVEC - bool "AltiVec Support" - depends on CLASSIC32 || POWER4 - ---help--- - This option enables kernel support for the Altivec extensions to the - PowerPC processor. The kernel currently supports saving and restoring - altivec registers, and turning on the 'altivec enable' bit so user - processes can execute altivec instructions. - - This option is only usefully if you have a processor that supports - altivec (G4, otherwise known as 74xx series), but does not have - any affect on a non-altivec cpu (it does, however add code to the - kernel). - - If in doubt, say Y here. - -config SPE - bool "SPE Support" - depends on E200 || E500 - default y - ---help--- - This option enables kernel support for the Signal Processing - Extensions (SPE) to the PowerPC processor. The kernel currently - supports saving and restoring SPE registers, and turning on the - 'spe enable' bit so user processes can execute SPE instructions. - - This option is only useful if you have a processor that supports - SPE (e500, otherwise known as 85xx series), but does not have any - effect on a non-spe cpu (it does, however add code to the kernel). - - If in doubt, say Y here. - -config PPC_STD_MMU - bool - depends on 6xx || POWER3 || POWER4 || PPC64 - default y - -config PPC_STD_MMU_32 - def_bool y - depends on PPC_STD_MMU && PPC32 - -config PPC_MM_SLICES - bool - default y if HUGETLB_PAGE - default n - -config VIRT_CPU_ACCOUNTING - bool "Deterministic task and CPU time accounting" - depends on PPC64 - default y - help - Select this option to enable more accurate task and CPU time - accounting. This is done by reading a CPU counter on each - kernel entry and exit and on transitions within the kernel - between system, softirq and hardirq state, so there is a - small performance impact. This also enables accounting of - stolen time on logically-partitioned systems running on - IBM POWER5-based machines. - - If in doubt, say Y here. - -config SMP - depends on PPC_STD_MMU - bool "Symmetric multi-processing support" - ---help--- - This enables support for systems with more than one CPU. If you have - a system with only one CPU, say N. If you have a system with more - than one CPU, say Y. Note that the kernel does not currently - support SMP machines with 603/603e/603ev or PPC750 ("G3") processors - since they have inadequate hardware support for multiprocessor - operation. - - If you say N here, the kernel will run on single and multiprocessor - machines, but will use only one CPU of a multiprocessor machine. If - you say Y here, the kernel will run on single-processor machines. - On a single-processor machine, the kernel will run faster if you say - N here. - - If you don't know what to do here, say N. - -config NR_CPUS - int "Maximum number of CPUs (2-128)" - range 2 128 - depends on SMP - default "32" if PPC64 - default "4" - -config NOT_COHERENT_CACHE - bool - depends on 4xx || 8xx || E200 - default y - -config CONFIG_CHECK_CACHE_COHERENCY - bool -endmenu - source "init/Kconfig" source "arch/powerpc/platforms/Kconfig" @@ -674,10 +419,6 @@ config SBUS config FSL_SOC bool -config FSL_PCIE - bool - depends on PPC_86xx - # Yes MCA RS/6000s exist but Linux-PPC does not currently support any config MCA bool @@ -685,10 +426,10 @@ config MCA config PCI bool "PCI support" if 40x || CPM2 || PPC_83xx || PPC_85xx || PPC_86xx \ || PPC_MPC52xx || (EMBEDDED && (PPC_PSERIES || PPC_ISERIES)) \ - || MPC7448HPC2 || PPC_PS3 || PPC_HOLLY - default y if !40x && !CPM2 && !8xx && !APUS && !PPC_83xx \ + || PPC_PS3 + default y if !40x && !CPM2 && !8xx && !PPC_83xx \ && !PPC_85xx && !PPC_86xx - default PCI_PERMEDIA if !4xx && !CPM2 && !8xx && APUS + default PCI_PERMEDIA if !4xx && !CPM2 && !8xx default PCI_QSPAN if !4xx && !CPM2 && 8xx select ARCH_SUPPORTS_MSI help diff --git a/arch/powerpc/Makefile b/arch/powerpc/Makefile index fbafd965dcd2..187a39af3e1c 100644 --- a/arch/powerpc/Makefile +++ b/arch/powerpc/Makefile @@ -148,7 +148,7 @@ all: $(KBUILD_IMAGE) CPPFLAGS_vmlinux.lds := -Upowerpc -BOOT_TARGETS = zImage zImage.initrd zImage.dts zImage.dts_initrd uImage +BOOT_TARGETS = zImage zImage.initrd uImage PHONY += $(BOOT_TARGETS) diff --git a/arch/powerpc/boot/44x.c b/arch/powerpc/boot/44x.c index d51377d9024f..9f64e840bef6 100644 --- a/arch/powerpc/boot/44x.c +++ b/arch/powerpc/boot/44x.c @@ -38,3 +38,48 @@ void ibm44x_fixup_memsize(void) dt_fixup_memory(0, memsize); } + +#define SPRN_DBCR0 0x134 +#define DBCR0_RST_SYSTEM 0x30000000 + +void ibm44x_dbcr_reset(void) +{ + unsigned long tmp; + + asm volatile ( + "mfspr %0,%1\n" + "oris %0,%0,%2@h\n" + "mtspr %1,%0" + : "=&r"(tmp) : "i"(SPRN_DBCR0), "i"(DBCR0_RST_SYSTEM) + ); + +} + +/* Read 4xx EBC bus bridge registers to get mappings of the peripheral + * banks into the OPB address space */ +void ibm4xx_fixup_ebc_ranges(const char *ebc) +{ + void *devp; + u32 bxcr; + u32 ranges[EBC_NUM_BANKS*4]; + u32 *p = ranges; + int i; + + for (i = 0; i < EBC_NUM_BANKS; i++) { + mtdcr(DCRN_EBC0_CFGADDR, EBC_BXCR(i)); + bxcr = mfdcr(DCRN_EBC0_CFGDATA); + + if ((bxcr & EBC_BXCR_BU) != EBC_BXCR_BU_OFF) { + *p++ = i; + *p++ = 0; + *p++ = bxcr & EBC_BXCR_BAS; + *p++ = EBC_BXCR_BANK_SIZE(bxcr); + } + } + + devp = finddevice(ebc); + if (! devp) + fatal("Couldn't locate EBC node %s\n\r", ebc); + + setprop(devp, "ranges", ranges, (p - ranges) * sizeof(u32)); +} diff --git a/arch/powerpc/boot/44x.h b/arch/powerpc/boot/44x.h index 7b129ad043e1..577982c9a3cd 100644 --- a/arch/powerpc/boot/44x.h +++ b/arch/powerpc/boot/44x.h @@ -11,6 +11,9 @@ #define _PPC_BOOT_44X_H_ void ibm44x_fixup_memsize(void); +void ibm4xx_fixup_ebc_ranges(const char *ebc); + +void ibm44x_dbcr_reset(void); void ebony_init(void *mac0, void *mac1); #endif /* _PPC_BOOT_44X_H_ */ diff --git a/arch/powerpc/boot/Makefile b/arch/powerpc/boot/Makefile index ff2701949ee1..61a6f34ca5ed 100644 --- a/arch/powerpc/boot/Makefile +++ b/arch/powerpc/boot/Makefile @@ -43,10 +43,11 @@ $(addprefix $(obj)/,$(zlib) gunzip_util.o main.o): \ src-wlib := string.S crt0.S stdio.c main.c flatdevtree.c flatdevtree_misc.c \ ns16550.c serial.c simple_alloc.c div64.S util.S \ - gunzip_util.c elf_util.c $(zlib) devtree.c \ - 44x.c ebony.c mv64x60.c mpsc.c mv64x60_i2c.c + gunzip_util.c elf_util.c $(zlib) devtree.c oflib.c ofconsole.c \ + 44x.c ebony.c mv64x60.c mpsc.c mv64x60_i2c.c cuboot.c src-plat := of.c cuboot-83xx.c cuboot-85xx.c holly.c \ - cuboot-ebony.c treeboot-ebony.c prpmc2800.c + cuboot-ebony.c treeboot-ebony.c prpmc2800.c \ + ps3-head.S ps3-hvcall.S ps3.c src-boot := $(src-wlib) $(src-plat) empty.c src-boot := $(addprefix $(obj)/, $(src-boot)) @@ -75,11 +76,11 @@ $(addprefix $(obj)/,$(zliblinuxheader)): $(obj)/%: $(srctree)/include/linux/% $(obj)/empty.c: @touch $@ -$(obj)/zImage.lds $(obj)/zImage.coff.lds: $(obj)/%: $(srctree)/$(src)/%.S +$(obj)/zImage.lds $(obj)/zImage.coff.lds $(obj)/zImage.ps3.lds: $(obj)/%: $(srctree)/$(src)/%.S @cp $< $@ clean-files := $(zlib) $(zlibheader) $(zliblinuxheader) \ - empty.c zImage.coff.lds zImage.lds + empty.c zImage zImage.coff.lds zImage.ps3.lds zImage.lds quiet_cmd_bootcc = BOOTCC $@ cmd_bootcc = $(CROSS32CC) -Wp,-MD,$(depfile) $(BOOTCFLAGS) -c -o $@ $< @@ -102,7 +103,7 @@ hostprogs-y := addnote addRamDisk hack-coff mktree targets += $(patsubst $(obj)/%,%,$(obj-boot) wrapper.a) extra-y := $(obj)/wrapper.a $(obj-plat) $(obj)/empty.o \ - $(obj)/zImage.lds $(obj)/zImage.coff.lds + $(obj)/zImage.lds $(obj)/zImage.coff.lds $(obj)/zImage.ps3.lds wrapper :=$(srctree)/$(src)/wrapper wrapperbits := $(extra-y) $(addprefix $(obj)/,addnote hack-coff mktree) \ @@ -132,7 +133,7 @@ image-$(CONFIG_PPC_CELLEB) += zImage.pseries image-$(CONFIG_PPC_CHRP) += zImage.chrp image-$(CONFIG_PPC_EFIKA) += zImage.chrp image-$(CONFIG_PPC_PMAC) += zImage.pmac -image-$(CONFIG_PPC_HOLLY) += zImage.holly-elf +image-$(CONFIG_PPC_HOLLY) += zImage.holly image-$(CONFIG_PPC_PRPMC2800) += zImage.prpmc2800 image-$(CONFIG_PPC_ISERIES) += zImage.iseries image-$(CONFIG_DEFAULT_UIMAGE) += uImage @@ -157,55 +158,43 @@ targets += $(image-y) $(initrd-y) $(addprefix $(obj)/, $(initrd-y)): $(obj)/ramdisk.image.gz -dts- := $(patsubst zImage%, zImage.dts%, $(image-n) $(image-)) -dts-y := $(patsubst zImage%, zImage.dts%, $(image-y)) -dts-y := $(filter-out $(image-y), $(dts-y)) -targets += $(image-y) $(dts-y) - -dts_initrd- := $(patsubst zImage%, zImage.dts_initrd%, $(image-n) $(image-)) -dts_initrd-y := $(patsubst zImage%, zImage.dts_initrd%, $(image-y)) -dts_initrd-y := $(filter-out $(image-y), $(dts_initrd-y)) -targets += $(image-y) $(dts_initrd-y) - -$(addprefix $(obj)/, $(dts_initrd-y)): $(obj)/ramdisk.image.gz +# If CONFIG_WANT_DEVICE_TREE is set and CONFIG_DEVICE_TREE isn't an +# empty string, define 'dts' to be path to the dts +# CONFIG_DEVICE_TREE will have "" around it, make sure to strip them +ifeq ($(CONFIG_WANT_DEVICE_TREE),y) +ifneq ($(CONFIG_DEVICE_TREE),"") +dts = $(if $(shell echo $(CONFIG_DEVICE_TREE) | grep '^/'),\ + ,$(srctree)/$(src)/dts/)$(CONFIG_DEVICE_TREE:"%"=%) +endif +endif # Don't put the ramdisk on the pattern rule; when its missing make will try # the pattern rule with less dependencies that also matches (even with the # hard dependency listed). -$(obj)/zImage.dts_initrd.%: vmlinux $(wrapperbits) $(dts) $(obj)/ramdisk.image.gz +$(obj)/zImage.initrd.%: vmlinux $(wrapperbits) $(dts) $(call if_changed,wrap,$*,$(dts),,$(obj)/ramdisk.image.gz) -$(obj)/zImage.dts.%: vmlinux $(wrapperbits) $(dts) +$(obj)/zImage.%: vmlinux $(wrapperbits) $(dts) $(call if_changed,wrap,$*,$(dts)) -$(obj)/zImage.initrd.%: vmlinux $(wrapperbits) - $(call if_changed,wrap,$*,,,$(obj)/ramdisk.image.gz) - -$(obj)/zImage.%: vmlinux $(wrapperbits) - $(call if_changed,wrap,$*) +# This cannot be in the root of $(src) as the zImage rule always adds a $(obj) +# prefix +$(obj)/vmlinux.strip: vmlinux + $(STRIP) -s -R .comment $< -o $@ $(obj)/zImage.iseries: vmlinux $(STRIP) -s -R .comment $< -o $@ -$(obj)/zImage.ps3: vmlinux - $(STRIP) -s -R .comment $< -o $@ +$(obj)/zImage.ps3: vmlinux $(wrapper) $(wrapperbits) $(srctree)/$(src)/dts/ps3.dts + $(STRIP) -s -R .comment $< -o vmlinux.strip + $(call cmd,wrap,ps3,$(srctree)/$(src)/dts/ps3.dts,,) -$(obj)/zImage.initrd.ps3: vmlinux - @echo " WARNING zImage.initrd.ps3 not supported (yet)" - -$(obj)/zImage.holly-elf: vmlinux $(wrapperbits) - $(call if_changed,wrap,holly,$(obj)/dts/holly.dts,,) - -$(obj)/zImage.initrd.holly-elf: vmlinux $(wrapperbits) $(obj)/ramdisk.image.gz - $(call if_changed,wrap,holly,$(obj)/dts/holly.dts,,$(obj)/ramdisk.image.gz) +$(obj)/zImage.initrd.ps3: vmlinux $(wrapper) $(wrapperbits) $(srctree)/$(src)/dts/ps3.dts $(obj)/ramdisk.image.gz + $(call cmd,wrap,ps3,$(srctree)/$(src)/dts/ps3.dts,,$(obj)/ramdisk.image.gz) $(obj)/uImage: vmlinux $(wrapperbits) $(call if_changed,wrap,uboot) -# CONFIG_DEVICE_TREE will have "" around it, make sure to strip them -dts = $(if $(shell echo $(CONFIG_DEVICE_TREE) | grep '^/'),\ - ,$(srctree)/$(src)/dts/)$(CONFIG_DEVICE_TREE:"%"=%) - $(obj)/cuImage.%: vmlinux $(dts) $(wrapperbits) $(call if_changed,wrap,cuboot-$*,$(dts)) @@ -215,22 +204,22 @@ $(obj)/treeImage.initrd.%: vmlinux $(dts) $(wrapperbits) $(obj)/treeImage.%: vmlinux $(dts) $(wrapperbits) $(call if_changed,wrap,treeboot-$*,$(dts)) +# If there isn't a platform selected then just strip the vmlinux. +ifeq (,$(image-y)) +image-y := vmlinux.strip +endif + $(obj)/zImage: $(addprefix $(obj)/, $(image-y)) @rm -f $@; ln $< $@ $(obj)/zImage.initrd: $(addprefix $(obj)/, $(initrd-y)) @rm -f $@; ln $< $@ -$(obj)/zImage.dts: $(addprefix $(obj)/, $(dts-y)) - @rm -f $@; ln $< $@ -$(obj)/zImage.dts_initrd: $(addprefix $(obj)/, $(dts_initrd-y)) - @rm -f $@; ln $< $@ - install: $(CONFIGURE) $(addprefix $(obj)/, $(image-y)) sh -x $(srctree)/$(src)/install.sh "$(KERNELRELEASE)" vmlinux System.map "$(INSTALL_PATH)" $< # anything not in $(targets) -clean-files += $(image-) $(initrd-) zImage zImage.initrd cuImage.* \ - treeImage.* zImage.dts zImage.dts_initrd +clean-files += $(image-) $(initrd-) zImage zImage.initrd cuImage.* treeImage.* \ + otheros.bld # clean up files cached by wrapper clean-kernel := vmlinux.strip vmlinux.bin diff --git a/arch/powerpc/boot/cuboot-83xx.c b/arch/powerpc/boot/cuboot-83xx.c index 9af554eea54b..296025d8b295 100644 --- a/arch/powerpc/boot/cuboot-83xx.c +++ b/arch/powerpc/boot/cuboot-83xx.c @@ -12,12 +12,12 @@ #include "ops.h" #include "stdio.h" +#include "cuboot.h" #define TARGET_83xx #include "ppcboot.h" static bd_t bd; -extern char _end[]; extern char _dtb_start[], _dtb_end[]; static void platform_fixups(void) @@ -52,16 +52,7 @@ static void platform_fixups(void) void platform_init(unsigned long r3, unsigned long r4, unsigned long r5, unsigned long r6, unsigned long r7) { - unsigned long end_of_ram = bd.bi_memstart + bd.bi_memsize; - unsigned long avail_ram = end_of_ram - (unsigned long)_end; - - memcpy(&bd, (bd_t *)r3, sizeof(bd)); - loader_info.initrd_addr = r4; - loader_info.initrd_size = r4 ? r5 - r4 : 0; - loader_info.cmdline = (char *)r6; - loader_info.cmdline_len = r7 - r6; - - simple_alloc_init(_end, avail_ram - 1024*1024, 32, 64); + CUBOOT_INIT(); ft_init(_dtb_start, _dtb_end - _dtb_start, 32); serial_console_init(); platform_ops.fixups = platform_fixups; diff --git a/arch/powerpc/boot/cuboot-85xx.c b/arch/powerpc/boot/cuboot-85xx.c index e2560317f278..10f0f697c935 100644 --- a/arch/powerpc/boot/cuboot-85xx.c +++ b/arch/powerpc/boot/cuboot-85xx.c @@ -12,12 +12,12 @@ #include "ops.h" #include "stdio.h" +#include "cuboot.h" #define TARGET_85xx #include "ppcboot.h" static bd_t bd; -extern char _end[]; extern char _dtb_start[], _dtb_end[]; static void platform_fixups(void) @@ -53,16 +53,7 @@ static void platform_fixups(void) void platform_init(unsigned long r3, unsigned long r4, unsigned long r5, unsigned long r6, unsigned long r7) { - unsigned long end_of_ram = bd.bi_memstart + bd.bi_memsize; - unsigned long avail_ram = end_of_ram - (unsigned long)_end; - - memcpy(&bd, (bd_t *)r3, sizeof(bd)); - loader_info.initrd_addr = r4; - loader_info.initrd_size = r4 ? r5 - r4 : 0; - loader_info.cmdline = (char *)r6; - loader_info.cmdline_len = r7 - r6; - - simple_alloc_init(_end, avail_ram - 1024*1024, 32, 64); + CUBOOT_INIT(); ft_init(_dtb_start, _dtb_end - _dtb_start, 32); serial_console_init(); platform_ops.fixups = platform_fixups; diff --git a/arch/powerpc/boot/cuboot-ebony.c b/arch/powerpc/boot/cuboot-ebony.c index 4464c5f67acb..c5f37ce172ea 100644 --- a/arch/powerpc/boot/cuboot-ebony.c +++ b/arch/powerpc/boot/cuboot-ebony.c @@ -15,28 +15,16 @@ #include "ops.h" #include "stdio.h" #include "44x.h" +#include "cuboot.h" #define TARGET_44x #include "ppcboot.h" static bd_t bd; -extern char _end[]; - -BSS_STACK(4096); void platform_init(unsigned long r3, unsigned long r4, unsigned long r5, unsigned long r6, unsigned long r7) { - unsigned long end_of_ram = bd.bi_memstart + bd.bi_memsize; - unsigned long avail_ram = end_of_ram - (unsigned long)_end; - - memcpy(&bd, (bd_t *)r3, sizeof(bd)); - loader_info.initrd_addr = r4; - loader_info.initrd_size = r4 ? r5 : 0; - loader_info.cmdline = (char *)r6; - loader_info.cmdline_len = r7 - r6; - - simple_alloc_init(_end, avail_ram, 32, 64); - + CUBOOT_INIT(); ebony_init(&bd.bi_enetaddr, &bd.bi_enet1addr); } diff --git a/arch/powerpc/boot/cuboot.c b/arch/powerpc/boot/cuboot.c new file mode 100644 index 000000000000..65795468ad6f --- /dev/null +++ b/arch/powerpc/boot/cuboot.c @@ -0,0 +1,35 @@ +/* + * Compatibility for old (not device tree aware) U-Boot versions + * + * Author: Scott Wood + * Consolidated using macros by David Gibson + * + * Copyright 2007 David Gibson, IBM Corporation. + * Copyright (c) 2007 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#include "ops.h" +#include "stdio.h" + +#include "ppcboot.h" + +extern char _end[]; +extern char _dtb_start[], _dtb_end[]; + +void cuboot_init(unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7, + unsigned long end_of_ram) +{ + unsigned long avail_ram = end_of_ram - (unsigned long)_end; + + loader_info.initrd_addr = r4; + loader_info.initrd_size = r4 ? r5 - r4 : 0; + loader_info.cmdline = (char *)r6; + loader_info.cmdline_len = r7 - r6; + + simple_alloc_init(_end, avail_ram - 1024*1024, 32, 64); +} diff --git a/arch/powerpc/boot/cuboot.h b/arch/powerpc/boot/cuboot.h new file mode 100644 index 000000000000..cd2aa7f348f3 --- /dev/null +++ b/arch/powerpc/boot/cuboot.h @@ -0,0 +1,14 @@ +#ifndef _PPC_BOOT_CUBOOT_H_ +#define _PPC_BOOT_CUBOOT_H_ + +void cuboot_init(unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7, + unsigned long end_of_ram); + +#define CUBOOT_INIT() \ + do { \ + memcpy(&bd, (bd_t *)r3, sizeof(bd)); \ + cuboot_init(r4, r5, r6, r7, bd.bi_memstart + bd.bi_memsize); \ + } while (0) + +#endif /* _PPC_BOOT_CUBOOT_H_ */ diff --git a/arch/powerpc/boot/dcr.h b/arch/powerpc/boot/dcr.h index 877bc97b1e97..14b44aa96fea 100644 --- a/arch/powerpc/boot/dcr.h +++ b/arch/powerpc/boot/dcr.h @@ -26,6 +26,43 @@ static const unsigned long sdram_bxcr[] = { SDRAM0_B0CR, SDRAM0_B1CR, SDRAM0_B2C #define SDRAM_CONFIG_BANK_SIZE(reg) \ (0x00400000 << ((reg & SDRAM_CONFIG_SIZE_MASK) >> 17)) +/* 440GP External Bus Controller (EBC) */ +#define DCRN_EBC0_CFGADDR 0x012 +#define DCRN_EBC0_CFGDATA 0x013 +#define EBC_NUM_BANKS 8 +#define EBC_B0CR 0x00 +#define EBC_B1CR 0x01 +#define EBC_B2CR 0x02 +#define EBC_B3CR 0x03 +#define EBC_B4CR 0x04 +#define EBC_B5CR 0x05 +#define EBC_B6CR 0x06 +#define EBC_B7CR 0x07 +#define EBC_BXCR(n) (n) +#define EBC_BXCR_BAS 0xfff00000 +#define EBC_BXCR_BS 0x000e0000 +#define EBC_BXCR_BANK_SIZE(reg) \ + (0x100000 << (((reg) & EBC_BXCR_BS) >> 17)) +#define EBC_BXCR_BU 0x00018000 +#define EBC_BXCR_BU_OFF 0x00000000 +#define EBC_BXCR_BU_RO 0x00008000 +#define EBC_BXCR_BU_WO 0x00010000 +#define EBC_BXCR_BU_RW 0x00018000 +#define EBC_BXCR_BW 0x00006000 +#define EBC_B0AP 0x10 +#define EBC_B1AP 0x11 +#define EBC_B2AP 0x12 +#define EBC_B3AP 0x13 +#define EBC_B4AP 0x14 +#define EBC_B5AP 0x15 +#define EBC_B6AP 0x16 +#define EBC_B7AP 0x17 +#define EBC_BXAP(n) (0x10+(n)) +#define EBC_BEAR 0x20 +#define EBC_BESR 0x21 +#define EBC_CFG 0x23 +#define EBC_CID 0x24 + /* 440GP Clock, PM, chip control */ #define DCRN_CPC0_SR 0x0b0 #define DCRN_CPC0_ER 0x0b1 diff --git a/arch/powerpc/boot/dts/ebony.dts b/arch/powerpc/boot/dts/ebony.dts index 0ec02f4726b5..c5f99613fc7b 100644 --- a/arch/powerpc/boot/dts/ebony.dts +++ b/arch/powerpc/boot/dts/ebony.dts @@ -31,8 +31,8 @@ reg = <0>; clock-frequency = <0>; // Filled in by zImage timebase-frequency = <0>; // Filled in by zImage - i-cache-line-size = <32>; - d-cache-line-size = <32>; + i-cache-line-size = <20>; + d-cache-line-size = <20>; i-cache-size = <8000>; /* 32 kB */ d-cache-size = <8000>; /* 32 kB */ dcr-controller; @@ -135,11 +135,9 @@ #address-cells = <2>; #size-cells = <1>; clock-frequency = <0>; // Filled in by zImage - ranges = <0 00000000 fff00000 100000 - 1 00000000 48000000 100000 - 2 00000000 ff800000 400000 - 3 00000000 48200000 100000 - 7 00000000 48300000 100000>; + // ranges property is supplied by zImage + // based on firmware's configuration of the + // EBC bridge interrupts = <5 4>; interrupt-parent = <&UIC1>; diff --git a/arch/powerpc/boot/dts/holly.dts b/arch/powerpc/boot/dts/holly.dts index 254499b107f4..80a4fab8ee37 100644 --- a/arch/powerpc/boot/dts/holly.dts +++ b/arch/powerpc/boot/dts/holly.dts @@ -46,7 +46,7 @@ tsi109@c0000000 { device_type = "tsi-bridge"; - compatible = "tsi-bridge"; + compatible = "tsi109-bridge", "tsi108-bridge"; #address-cells = <1>; #size-cells = <1>; ranges = <00000000 c0000000 00010000>; @@ -54,52 +54,55 @@ i2c@7000 { device_type = "i2c"; - compatible = "tsi-i2c"; - interrupt-parent = < &/tsi109@c0000000/pic@7400 >; + compatible = "tsi109-i2c", "tsi108-i2c"; + interrupt-parent = <&MPIC>; interrupts = ; reg = <7000 400>; }; - mdio@6000 { + MDIO: mdio@6000 { device_type = "mdio"; - compatible = "tsi-ethernet"; + compatible = "tsi109-mdio", "tsi108-mdio"; + reg = <6000 50>; + #address-cells = <1>; + #size-cells = <0>; - PHY1: ethernet-phy@6000 { - device_type = "ethernet-phy"; - compatible = "bcm54xx"; - reg = <6000 50>; - phy-id = <1>; + PHY1: ethernet-phy@1 { + compatible = "bcm5461a"; + reg = <1>; + txc-rxc-delay-disable; }; - PHY2: ethernet-phy@6400 { - device_type = "ethernet-phy"; - compatible = "bcm54xx"; - reg = <6000 50>; - phy-id = <2>; + PHY2: ethernet-phy@2 { + compatible = "bcm5461a"; + reg = <2>; + txc-rxc-delay-disable; }; }; ethernet@6200 { device_type = "network"; - compatible = "tsi-ethernet"; + compatible = "tsi109-ethernet", "tsi108-ethernet"; #address-cells = <1>; #size-cells = <0>; reg = <6000 200>; local-mac-address = [ 00 00 00 00 00 00 ]; - interrupt-parent = < &/tsi109@c0000000/pic@7400 >; + interrupt-parent = <&MPIC>; interrupts = <10 2>; + mdio-handle = <&MDIO>; phy-handle = <&PHY1>; }; ethernet@6600 { device_type = "network"; - compatible = "tsi-ethernet"; + compatible = "tsi109-ethernet", "tsi108-ethernet"; #address-cells = <1>; #size-cells = <0>; reg = <6400 200>; local-mac-address = [ 00 00 00 00 00 00 ]; - interrupt-parent = < &/tsi109@c0000000/pic@7400 >; + interrupt-parent = <&MPIC>; interrupts = <11 2>; + mdio-handle = <&MDIO>; phy-handle = <&PHY2>; }; @@ -110,7 +113,7 @@ virtual-reg = ; clock-frequency = <3F9C6000>; current-speed = <1c200>; - interrupt-parent = < &/tsi109@c0000000/pic@7400 >; + interrupt-parent = <&MPIC>; interrupts = ; }; @@ -121,7 +124,7 @@ virtual-reg = ; clock-frequency = <3F9C6000>; current-speed = <1c200>; - interrupt-parent = < &/tsi109@c0000000/pic@7400 >; + interrupt-parent = <&MPIC>; interrupts = ; }; @@ -136,7 +139,7 @@ pci@1000 { device_type = "pci"; - compatible = "tsi109"; + compatible = "tsi109-pci", "tsi108-pci"; #interrupt-cells = <1>; #size-cells = <2>; #address-cells = <3>; @@ -150,7 +153,7 @@ ranges = <02000000 0 40000000 40000000 0 10000000 01000000 0 00000000 7e000000 0 00010000>; clock-frequency = <7f28154>; - interrupt-parent = < &/tsi109@c0000000/pic@7400 >; + interrupt-parent = <&MPIC>; interrupts = <17 2>; interrupt-map-mask = ; /*----------------------------------------------------+ @@ -186,13 +189,12 @@ #address-cells = <0>; #interrupt-cells = <2>; interrupts = <17 2>; - interrupt-parent = < &/tsi109@c0000000/pic@7400 >; + interrupt-parent = <&MPIC>; }; }; }; chosen { linux,stdout-path = "/tsi109@c0000000/serial@7808"; - bootargs = "console=ttyS0,115200"; }; }; diff --git a/arch/powerpc/boot/dts/mpc7448hpc2.dts b/arch/powerpc/boot/dts/mpc7448hpc2.dts index 765c306ecf80..0e3d314a7158 100644 --- a/arch/powerpc/boot/dts/mpc7448hpc2.dts +++ b/arch/powerpc/boot/dts/mpc7448hpc2.dts @@ -45,7 +45,7 @@ #address-cells = <1>; #size-cells = <1>; #interrupt-cells = <2>; - device_type = "tsi-bridge"; + device_type = "tsi108-bridge"; ranges = <00000000 c0000000 00010000>; reg = ; bus-frequency = <0>; @@ -55,27 +55,26 @@ interrupts = ; reg = <7000 400>; device_type = "i2c"; - compatible = "tsi-i2c"; + compatible = "tsi108-i2c"; }; - mdio@6000 { + MDIO: mdio@6000 { device_type = "mdio"; - compatible = "tsi-ethernet"; + compatible = "tsi108-mdio"; + reg = <6000 50>; + #address-cells = <1>; + #size-cells = <0>; - phy8: ethernet-phy@6000 { + phy8: ethernet-phy@8 { interrupt-parent = <&mpic>; interrupts = <2 1>; - reg = <6000 50>; - phy-id = <8>; - device_type = "ethernet-phy"; + reg = <8>; }; - phy9: ethernet-phy@6400 { + phy9: ethernet-phy@9 { interrupt-parent = <&mpic>; interrupts = <2 1>; - reg = <6000 50>; - phy-id = <9>; - device_type = "ethernet-phy"; + reg = <9>; }; }; @@ -83,12 +82,12 @@ ethernet@6200 { #size-cells = <0>; device_type = "network"; - model = "TSI-ETH"; - compatible = "tsi-ethernet"; + compatible = "tsi108-ethernet"; reg = <6000 200>; address = [ 00 06 D2 00 00 01 ]; interrupts = <10 2>; interrupt-parent = <&mpic>; + mdio-handle = <&MDIO>; phy-handle = <&phy8>; }; @@ -96,12 +95,12 @@ #address-cells = <1>; #size-cells = <0>; device_type = "network"; - model = "TSI-ETH"; - compatible = "tsi-ethernet"; + compatible = "tsi108-ethernet"; reg = <6400 200>; address = [ 00 06 D2 00 00 02 ]; interrupts = <11 2>; interrupt-parent = <&mpic>; + mdio-handle = <&MDIO>; phy-handle = <&phy9>; }; @@ -135,7 +134,7 @@ big-endian; }; pci@1000 { - compatible = "tsi10x"; + compatible = "tsi108-pci"; device_type = "pci"; #interrupt-cells = <1>; #size-cells = <2>; diff --git a/arch/powerpc/boot/dts/mpc8272ads.dts b/arch/powerpc/boot/dts/mpc8272ads.dts index 423eedcf634f..1934b800278e 100644 --- a/arch/powerpc/boot/dts/mpc8272ads.dts +++ b/arch/powerpc/boot/dts/mpc8272ads.dts @@ -14,12 +14,10 @@ compatible = "MPC8260ADS"; #address-cells = <1>; #size-cells = <1>; - linux,phandle = <100>; cpus { #address-cells = <1>; #size-cells = <0>; - linux,phandle = <200>; PowerPC,8272@0 { device_type = "cpu"; @@ -32,12 +30,10 @@ bus-frequency = <0>; clock-frequency = <0>; 32-bit; - linux,phandle = <201>; }; }; - interrupt-controller@f8200000 { - linux,phandle = ; + pci_pic: interrupt-controller@f8200000 { #address-cells = <0>; #interrupt-cells = <2>; interrupt-controller; @@ -47,15 +43,13 @@ }; memory { device_type = "memory"; - linux,phandle = <300>; reg = <00000000 4000000 f4500000 00000020>; }; chosen { name = "chosen"; linux,platform = <0>; - interrupt-controller = <10c00>; - linux,phandle = <400>; + interrupt-controller = <&Cpm_pic>; }; soc8272@f0000000 { @@ -70,20 +64,17 @@ device_type = "mdio"; compatible = "fs_enet"; reg = <0 0>; - linux,phandle = <24520>; #address-cells = <1>; #size-cells = <0>; - ethernet-phy@0 { - linux,phandle = <2452000>; - interrupt-parent = <10c00>; + phy0:ethernet-phy@0 { + interrupt-parent = <&Cpm_pic>; interrupts = <17 4>; reg = <0>; bitbang = [ 12 12 13 02 02 01 ]; device_type = "ethernet-phy"; }; - ethernet-phy@1 { - linux,phandle = <2452001>; - interrupt-parent = <10c00>; + phy1:ethernet-phy@1 { + interrupt-parent = <&Cpm_pic>; interrupts = <17 4>; bitbang = [ 12 12 13 02 02 01 ]; reg = <3>; @@ -101,8 +92,8 @@ reg = <11300 20 8400 100 11380 30>; mac-address = [ 00 11 2F 99 43 54 ]; interrupts = <20 2>; - interrupt-parent = <10c00>; - phy-handle = <2452000>; + interrupt-parent = <&Cpm_pic>; + phy-handle = <&Phy0>; rx-clock = <13>; tx-clock = <12>; }; @@ -115,14 +106,13 @@ reg = <11320 20 8500 100 113b0 30>; mac-address = [ 00 11 2F 99 44 54 ]; interrupts = <21 2>; - interrupt-parent = <10c00>; - phy-handle = <2452001>; + interrupt-parent = <&Cpm_pic>; + phy-handle = <&Phy1>; rx-clock = <17>; tx-clock = <18>; }; cpm@f0000000 { - linux,phandle = ; #address-cells = <1>; #size-cells = <1>; #interrupt-cells = <2>; @@ -142,7 +132,7 @@ reg = <11a00 20 8000 100>; current-speed = <1c200>; interrupts = <28 2>; - interrupt-parent = <10c00>; + interrupt-parent = <&Cpm_pic>; clock-setup = <0 00ffffff>; rx-clock = <1>; tx-clock = <1>; @@ -156,15 +146,14 @@ reg = <11a60 20 8300 100>; current-speed = <1c200>; interrupts = <2b 2>; - interrupt-parent = <10c00>; + interrupt-parent = <&Cpm_pic>; clock-setup = <1b ffffff00>; rx-clock = <4>; tx-clock = <4>; }; }; - interrupt-controller@10c00 { - linux,phandle = <10c00>; + cpm_pic:interrupt-controller@10c00 { #address-cells = <0>; #interrupt-cells = <2>; interrupt-controller; @@ -174,7 +163,6 @@ compatible = "CPM2"; }; pci@0500 { - linux,phandle = <0500>; #interrupt-cells = <1>; #size-cells = <2>; #address-cells = <3>; @@ -202,7 +190,7 @@ c000 0 0 2 f8200000 43 8 c000 0 0 3 f8200000 40 8 c000 0 0 4 f8200000 41 8>; - interrupt-parent = <10c00>; + interrupt-parent = <&Cpm_pic>; interrupts = <14 8>; bus-range = <0 0>; ranges = <02000000 0 80000000 80000000 0 40000000 @@ -216,7 +204,7 @@ compatible = "talitos"; reg = <30000 10000>; interrupts = ; - interrupt-parent = <10c00>; + interrupt-parent = <&Cpm_pic>; num-channels = <4>; channel-fifo-len = <18>; exec-units-mask = <0000007e>; diff --git a/arch/powerpc/boot/dts/mpc832x_mds.dts b/arch/powerpc/boot/dts/mpc832x_mds.dts index 112dd5198fe2..4fc0c4d34aa8 100644 --- a/arch/powerpc/boot/dts/mpc832x_mds.dts +++ b/arch/powerpc/boot/dts/mpc832x_mds.dts @@ -272,7 +272,13 @@ reg = <2200 200>; interrupts = <22>; interrupt-parent = < &qeic >; - mac-address = [ 00 04 9f 00 23 23 ]; + /* + * mac-address is deprecated and will be removed + * in 2.6.25. Only recent versions of + * U-Boot support local-mac-address, however. + */ + mac-address = [ 00 00 00 00 00 00 ]; + local-mac-address = [ 00 00 00 00 00 00 ]; rx-clock = <19>; tx-clock = <1a>; phy-handle = < &phy3 >; @@ -287,7 +293,13 @@ reg = <3000 200>; interrupts = <23>; interrupt-parent = < &qeic >; - mac-address = [ 00 11 22 33 44 55 ]; + /* + * mac-address is deprecated and will be removed + * in 2.6.25. Only recent versions of + * U-Boot support local-mac-address, however. + */ + mac-address = [ 00 00 00 00 00 00 ]; + local-mac-address = [ 00 00 00 00 00 00 ]; rx-clock = <17>; tx-clock = <18>; phy-handle = < &phy4 >; diff --git a/arch/powerpc/boot/dts/mpc832x_rdb.dts b/arch/powerpc/boot/dts/mpc832x_rdb.dts index be4c35784e49..447c03ffabbc 100644 --- a/arch/powerpc/boot/dts/mpc832x_rdb.dts +++ b/arch/powerpc/boot/dts/mpc832x_rdb.dts @@ -231,7 +231,13 @@ reg = <3000 200>; interrupts = <21>; interrupt-parent = <&qeic>; - mac-address = [ 00 04 9f ef 03 02 ]; + /* + * mac-address is deprecated and will be removed + * in 2.6.25. Only recent versions of + * U-Boot support local-mac-address, however. + */ + mac-address = [ 00 00 00 00 00 00 ]; + local-mac-address = [ 00 00 00 00 00 00 ]; rx-clock = <20>; tx-clock = <13>; phy-handle = <&phy00>; @@ -246,7 +252,13 @@ reg = <2200 200>; interrupts = <22>; interrupt-parent = <&qeic>; - mac-address = [ 00 04 9f ef 03 01 ]; + /* + * mac-address is deprecated and will be removed + * in 2.6.25. Only recent versions of + * U-Boot support local-mac-address, however. + */ + mac-address = [ 00 00 00 00 00 00 ]; + local-mac-address = [ 00 00 00 00 00 00 ]; rx-clock = <19>; tx-clock = <1a>; phy-handle = <&phy04>; diff --git a/arch/powerpc/boot/dts/mpc8349emitx.dts b/arch/powerpc/boot/dts/mpc8349emitx.dts index db0d00303275..ae9bca575453 100644 --- a/arch/powerpc/boot/dts/mpc8349emitx.dts +++ b/arch/powerpc/boot/dts/mpc8349emitx.dts @@ -131,6 +131,11 @@ model = "TSEC"; compatible = "gianfar"; reg = <24000 1000>; + /* + * address is deprecated and will be removed + * in 2.6.25. Only recent versions of + * U-Boot support local-mac-address, however. + */ address = [ 00 00 00 00 00 00 ]; local-mac-address = [ 00 00 00 00 00 00 ]; interrupts = <20 8 21 8 22 8>; @@ -145,6 +150,11 @@ model = "TSEC"; compatible = "gianfar"; reg = <25000 1000>; + /* + * address is deprecated and will be removed + * in 2.6.25. Only recent versions of + * U-Boot support local-mac-address, however. + */ address = [ 00 00 00 00 00 00 ]; local-mac-address = [ 00 00 00 00 00 00 ]; interrupts = <23 8 24 8 25 8>; diff --git a/arch/powerpc/boot/dts/mpc834x_mds.dts b/arch/powerpc/boot/dts/mpc834x_mds.dts index df773fafe9d1..310e877826b4 100644 --- a/arch/powerpc/boot/dts/mpc834x_mds.dts +++ b/arch/powerpc/boot/dts/mpc834x_mds.dts @@ -136,6 +136,11 @@ model = "TSEC"; compatible = "gianfar"; reg = <24000 1000>; + /* + * address is deprecated and will be removed + * in 2.6.25. Only recent versions of + * U-Boot support local-mac-address, however. + */ address = [ 00 00 00 00 00 00 ]; local-mac-address = [ 00 00 00 00 00 00 ]; interrupts = <20 8 21 8 22 8>; @@ -150,6 +155,11 @@ model = "TSEC"; compatible = "gianfar"; reg = <25000 1000>; + /* + * address is deprecated and will be removed + * in 2.6.25. Only recent versions of + * U-Boot support local-mac-address, however. + */ address = [ 00 00 00 00 00 00 ]; local-mac-address = [ 00 00 00 00 00 00 ]; interrupts = <23 8 24 8 25 8>; diff --git a/arch/powerpc/boot/dts/mpc836x_mds.dts b/arch/powerpc/boot/dts/mpc836x_mds.dts index 38c8594df3a4..1e914f31dd92 100644 --- a/arch/powerpc/boot/dts/mpc836x_mds.dts +++ b/arch/powerpc/boot/dts/mpc836x_mds.dts @@ -301,7 +301,13 @@ reg = <2000 200>; interrupts = <20>; interrupt-parent = < &qeic >; - mac-address = [ 00 04 9f 00 23 23 ]; + /* + * mac-address is deprecated and will be removed + * in 2.6.25. Only recent versions of + * U-Boot support local-mac-address, however. + */ + mac-address = [ 00 00 00 00 00 00 ]; + local-mac-address = [ 00 00 00 00 00 00 ]; rx-clock = <0>; tx-clock = <19>; phy-handle = < &phy0 >; @@ -317,7 +323,13 @@ reg = <3000 200>; interrupts = <21>; interrupt-parent = < &qeic >; - mac-address = [ 00 11 22 33 44 55 ]; + /* + * mac-address is deprecated and will be removed + * in 2.6.25. Only recent versions of + * U-Boot support local-mac-address, however. + */ + mac-address = [ 00 00 00 00 00 00 ]; + local-mac-address = [ 00 00 00 00 00 00 ]; rx-clock = <0>; tx-clock = <14>; phy-handle = < &phy1 >; diff --git a/arch/powerpc/boot/dts/mpc8540ads.dts b/arch/powerpc/boot/dts/mpc8540ads.dts index d91e81c009f5..364a969f5c2f 100644 --- a/arch/powerpc/boot/dts/mpc8540ads.dts +++ b/arch/powerpc/boot/dts/mpc8540ads.dts @@ -52,7 +52,7 @@ compatible = "fsl,8540-memory-controller"; reg = <2000 1000>; interrupt-parent = <&mpic>; - interrupts = <2 2>; + interrupts = <12 2>; }; l2-cache-controller@20000 { @@ -61,14 +61,14 @@ cache-line-size = <20>; // 32 bytes cache-size = <40000>; // L2, 256K interrupt-parent = <&mpic>; - interrupts = <0 2>; + interrupts = <10 2>; }; i2c@3000 { device_type = "i2c"; compatible = "fsl-i2c"; reg = <3000 100>; - interrupts = <1b 2>; + interrupts = <2b 2>; interrupt-parent = <&mpic>; dfsrr; }; @@ -81,19 +81,19 @@ reg = <24520 20>; phy0: ethernet-phy@0 { interrupt-parent = <&mpic>; - interrupts = <35 1>; + interrupts = <5 1>; reg = <0>; device_type = "ethernet-phy"; }; phy1: ethernet-phy@1 { interrupt-parent = <&mpic>; - interrupts = <35 1>; + interrupts = <5 1>; reg = <1>; device_type = "ethernet-phy"; }; phy3: ethernet-phy@3 { interrupt-parent = <&mpic>; - interrupts = <37 1>; + interrupts = <7 1>; reg = <3>; device_type = "ethernet-phy"; }; @@ -106,9 +106,14 @@ model = "TSEC"; compatible = "gianfar"; reg = <24000 1000>; - address = [ 00 E0 0C 00 73 00 ]; - local-mac-address = [ 00 E0 0C 00 73 00 ]; - interrupts = ; + /* + * address is deprecated and will be removed + * in 2.6.25. Only recent versions of + * U-Boot support local-mac-address, however. + */ + address = [ 00 00 00 00 00 00 ]; + local-mac-address = [ 00 00 00 00 00 00 ]; + interrupts = <1d 2 1e 2 22 2>; interrupt-parent = <&mpic>; phy-handle = <&phy0>; }; @@ -120,9 +125,14 @@ model = "TSEC"; compatible = "gianfar"; reg = <25000 1000>; - address = [ 00 E0 0C 00 73 01 ]; - local-mac-address = [ 00 E0 0C 00 73 01 ]; - interrupts = <13 2 14 2 18 2>; + /* + * address is deprecated and will be removed + * in 2.6.25. Only recent versions of + * U-Boot support local-mac-address, however. + */ + address = [ 00 00 00 00 00 00 ]; + local-mac-address = [ 00 00 00 00 00 00 ]; + interrupts = <23 2 24 2 28 2>; interrupt-parent = <&mpic>; phy-handle = <&phy1>; }; @@ -134,9 +144,14 @@ model = "FEC"; compatible = "gianfar"; reg = <26000 1000>; - address = [ 00 E0 0C 00 73 02 ]; - local-mac-address = [ 00 E0 0C 00 73 02 ]; - interrupts = <19 2>; + /* + * address is deprecated and will be removed + * in 2.6.25. Only recent versions of + * U-Boot support local-mac-address, however. + */ + address = [ 00 00 00 00 00 00 ]; + local-mac-address = [ 00 00 00 00 00 00 ]; + interrupts = <29 2>; interrupt-parent = <&mpic>; phy-handle = <&phy3>; }; @@ -146,7 +161,7 @@ compatible = "ns16550"; reg = <4500 100>; // reg base, size clock-frequency = <0>; // should we fill in in uboot? - interrupts = <1a 2>; + interrupts = <2a 2>; interrupt-parent = <&mpic>; }; @@ -155,7 +170,7 @@ compatible = "ns16550"; reg = <4600 100>; // reg base, size clock-frequency = <0>; // should we fill in in uboot? - interrupts = <1a 2>; + interrupts = <2a 2>; interrupt-parent = <&mpic>; }; pci@8000 { @@ -163,78 +178,78 @@ interrupt-map = < /* IDSEL 0x02 */ - 1000 0 0 1 &mpic 31 1 - 1000 0 0 2 &mpic 32 1 - 1000 0 0 3 &mpic 33 1 - 1000 0 0 4 &mpic 34 1 + 1000 0 0 1 &mpic 1 1 + 1000 0 0 2 &mpic 2 1 + 1000 0 0 3 &mpic 3 1 + 1000 0 0 4 &mpic 4 1 /* IDSEL 0x03 */ - 1800 0 0 1 &mpic 34 1 - 1800 0 0 2 &mpic 31 1 - 1800 0 0 3 &mpic 32 1 - 1800 0 0 4 &mpic 33 1 + 1800 0 0 1 &mpic 4 1 + 1800 0 0 2 &mpic 1 1 + 1800 0 0 3 &mpic 2 1 + 1800 0 0 4 &mpic 3 1 /* IDSEL 0x04 */ - 2000 0 0 1 &mpic 33 1 - 2000 0 0 2 &mpic 34 1 - 2000 0 0 3 &mpic 31 1 - 2000 0 0 4 &mpic 32 1 + 2000 0 0 1 &mpic 3 1 + 2000 0 0 2 &mpic 4 1 + 2000 0 0 3 &mpic 1 1 + 2000 0 0 4 &mpic 2 1 /* IDSEL 0x05 */ - 2800 0 0 1 &mpic 32 1 - 2800 0 0 2 &mpic 33 1 - 2800 0 0 3 &mpic 34 1 - 2800 0 0 4 &mpic 31 1 + 2800 0 0 1 &mpic 2 1 + 2800 0 0 2 &mpic 3 1 + 2800 0 0 3 &mpic 4 1 + 2800 0 0 4 &mpic 1 1 /* IDSEL 0x0c */ - 6000 0 0 1 &mpic 31 1 - 6000 0 0 2 &mpic 32 1 - 6000 0 0 3 &mpic 33 1 - 6000 0 0 4 &mpic 34 1 + 6000 0 0 1 &mpic 1 1 + 6000 0 0 2 &mpic 2 1 + 6000 0 0 3 &mpic 3 1 + 6000 0 0 4 &mpic 4 1 /* IDSEL 0x0d */ - 6800 0 0 1 &mpic 34 1 - 6800 0 0 2 &mpic 31 1 - 6800 0 0 3 &mpic 32 1 - 6800 0 0 4 &mpic 33 1 + 6800 0 0 1 &mpic 4 1 + 6800 0 0 2 &mpic 1 1 + 6800 0 0 3 &mpic 2 1 + 6800 0 0 4 &mpic 3 1 /* IDSEL 0x0e */ - 7000 0 0 1 &mpic 33 1 - 7000 0 0 2 &mpic 34 1 - 7000 0 0 3 &mpic 31 1 - 7000 0 0 4 &mpic 32 1 + 7000 0 0 1 &mpic 3 1 + 7000 0 0 2 &mpic 4 1 + 7000 0 0 3 &mpic 1 1 + 7000 0 0 4 &mpic 2 1 /* IDSEL 0x0f */ - 7800 0 0 1 &mpic 32 1 - 7800 0 0 2 &mpic 33 1 - 7800 0 0 3 &mpic 34 1 - 7800 0 0 4 &mpic 31 1 + 7800 0 0 1 &mpic 2 1 + 7800 0 0 2 &mpic 3 1 + 7800 0 0 3 &mpic 4 1 + 7800 0 0 4 &mpic 1 1 /* IDSEL 0x12 */ - 9000 0 0 1 &mpic 31 1 - 9000 0 0 2 &mpic 32 1 - 9000 0 0 3 &mpic 33 1 - 9000 0 0 4 &mpic 34 1 + 9000 0 0 1 &mpic 1 1 + 9000 0 0 2 &mpic 2 1 + 9000 0 0 3 &mpic 3 1 + 9000 0 0 4 &mpic 4 1 /* IDSEL 0x13 */ - 9800 0 0 1 &mpic 34 1 - 9800 0 0 2 &mpic 31 1 - 9800 0 0 3 &mpic 32 1 - 9800 0 0 4 &mpic 33 1 + 9800 0 0 1 &mpic 4 1 + 9800 0 0 2 &mpic 1 1 + 9800 0 0 3 &mpic 2 1 + 9800 0 0 4 &mpic 3 1 /* IDSEL 0x14 */ - a000 0 0 1 &mpic 33 1 - a000 0 0 2 &mpic 34 1 - a000 0 0 3 &mpic 31 1 - a000 0 0 4 &mpic 32 1 + a000 0 0 1 &mpic 3 1 + a000 0 0 2 &mpic 4 1 + a000 0 0 3 &mpic 1 1 + a000 0 0 4 &mpic 2 1 /* IDSEL 0x15 */ - a800 0 0 1 &mpic 32 1 - a800 0 0 2 &mpic 33 1 - a800 0 0 3 &mpic 34 1 - a800 0 0 4 &mpic 31 1>; + a800 0 0 1 &mpic 2 1 + a800 0 0 2 &mpic 3 1 + a800 0 0 3 &mpic 4 1 + a800 0 0 4 &mpic 1 1>; interrupt-parent = <&mpic>; - interrupts = <08 2>; + interrupts = <18 2>; bus-range = <0 0>; ranges = <02000000 0 80000000 80000000 0 20000000 01000000 0 00000000 e2000000 0 00100000>; diff --git a/arch/powerpc/boot/dts/mpc8541cds.dts b/arch/powerpc/boot/dts/mpc8541cds.dts index 4f2c3af2e052..070206fffe88 100644 --- a/arch/powerpc/boot/dts/mpc8541cds.dts +++ b/arch/powerpc/boot/dts/mpc8541cds.dts @@ -52,7 +52,7 @@ compatible = "fsl,8541-memory-controller"; reg = <2000 1000>; interrupt-parent = <&mpic>; - interrupts = <2 2>; + interrupts = <12 2>; }; l2-cache-controller@20000 { @@ -61,14 +61,14 @@ cache-line-size = <20>; // 32 bytes cache-size = <40000>; // L2, 256K interrupt-parent = <&mpic>; - interrupts = <0 2>; + interrupts = <10 2>; }; i2c@3000 { device_type = "i2c"; compatible = "fsl-i2c"; reg = <3000 100>; - interrupts = <1b 2>; + interrupts = <2b 2>; interrupt-parent = <&mpic>; dfsrr; }; @@ -81,13 +81,13 @@ reg = <24520 20>; phy0: ethernet-phy@0 { interrupt-parent = <&mpic>; - interrupts = <35 0>; + interrupts = <5 1>; reg = <0>; device_type = "ethernet-phy"; }; phy1: ethernet-phy@1 { interrupt-parent = <&mpic>; - interrupts = <35 0>; + interrupts = <5 1>; reg = <1>; device_type = "ethernet-phy"; }; @@ -100,8 +100,8 @@ model = "TSEC"; compatible = "gianfar"; reg = <24000 1000>; - local-mac-address = [ 00 E0 0C 00 73 00 ]; - interrupts = ; + local-mac-address = [ 00 00 00 00 00 00 ]; + interrupts = <1d 2 1e 2 22 2>; interrupt-parent = <&mpic>; phy-handle = <&phy0>; }; @@ -113,8 +113,8 @@ model = "TSEC"; compatible = "gianfar"; reg = <25000 1000>; - local-mac-address = [ 00 E0 0C 00 73 01 ]; - interrupts = <13 2 14 2 18 2>; + local-mac-address = [ 00 00 00 00 00 00 ]; + interrupts = <23 2 24 2 28 2>; interrupt-parent = <&mpic>; phy-handle = <&phy1>; }; @@ -124,7 +124,7 @@ compatible = "ns16550"; reg = <4500 100>; // reg base, size clock-frequency = <0>; // should we fill in in uboot? - interrupts = <1a 2>; + interrupts = <2a 2>; interrupt-parent = <&mpic>; }; @@ -133,7 +133,7 @@ compatible = "ns16550"; reg = <4600 100>; // reg base, size clock-frequency = <0>; // should we fill in in uboot? - interrupts = <1a 2>; + interrupts = <2a 2>; interrupt-parent = <&mpic>; }; @@ -142,49 +142,49 @@ interrupt-map = < /* IDSEL 0x10 */ - 08000 0 0 1 &mpic 30 1 - 08000 0 0 2 &mpic 31 1 - 08000 0 0 3 &mpic 32 1 - 08000 0 0 4 &mpic 33 1 + 08000 0 0 1 &mpic 0 1 + 08000 0 0 2 &mpic 1 1 + 08000 0 0 3 &mpic 2 1 + 08000 0 0 4 &mpic 3 1 /* IDSEL 0x11 */ - 08800 0 0 1 &mpic 30 1 - 08800 0 0 2 &mpic 31 1 - 08800 0 0 3 &mpic 32 1 - 08800 0 0 4 &mpic 33 1 + 08800 0 0 1 &mpic 0 1 + 08800 0 0 2 &mpic 1 1 + 08800 0 0 3 &mpic 2 1 + 08800 0 0 4 &mpic 3 1 /* IDSEL 0x12 (Slot 1) */ - 09000 0 0 1 &mpic 30 1 - 09000 0 0 2 &mpic 31 1 - 09000 0 0 3 &mpic 32 1 - 09000 0 0 4 &mpic 33 1 + 09000 0 0 1 &mpic 0 1 + 09000 0 0 2 &mpic 1 1 + 09000 0 0 3 &mpic 2 1 + 09000 0 0 4 &mpic 3 1 /* IDSEL 0x13 (Slot 2) */ - 09800 0 0 1 &mpic 31 1 - 09800 0 0 2 &mpic 32 1 - 09800 0 0 3 &mpic 33 1 - 09800 0 0 4 &mpic 30 1 + 09800 0 0 1 &mpic 1 1 + 09800 0 0 2 &mpic 2 1 + 09800 0 0 3 &mpic 3 1 + 09800 0 0 4 &mpic 0 1 /* IDSEL 0x14 (Slot 3) */ - 0a000 0 0 1 &mpic 32 1 - 0a000 0 0 2 &mpic 33 1 - 0a000 0 0 3 &mpic 30 1 - 0a000 0 0 4 &mpic 31 1 + 0a000 0 0 1 &mpic 2 1 + 0a000 0 0 2 &mpic 3 1 + 0a000 0 0 3 &mpic 0 1 + 0a000 0 0 4 &mpic 1 1 /* IDSEL 0x15 (Slot 4) */ - 0a800 0 0 1 &mpic 33 1 - 0a800 0 0 2 &mpic 30 1 - 0a800 0 0 3 &mpic 31 1 - 0a800 0 0 4 &mpic 32 1 + 0a800 0 0 1 &mpic 3 1 + 0a800 0 0 2 &mpic 0 1 + 0a800 0 0 3 &mpic 1 1 + 0a800 0 0 4 &mpic 2 1 /* Bus 1 (Tundra Bridge) */ /* IDSEL 0x12 (ISA bridge) */ - 19000 0 0 1 &mpic 30 1 - 19000 0 0 2 &mpic 31 1 - 19000 0 0 3 &mpic 32 1 - 19000 0 0 4 &mpic 33 1>; + 19000 0 0 1 &mpic 0 1 + 19000 0 0 2 &mpic 1 1 + 19000 0 0 3 &mpic 2 1 + 19000 0 0 4 &mpic 3 1>; interrupt-parent = <&mpic>; - interrupts = <08 2>; + interrupts = <18 2>; bus-range = <0 0>; ranges = <02000000 0 80000000 80000000 0 20000000 01000000 0 00000000 e2000000 0 00100000>; @@ -216,12 +216,12 @@ interrupt-map = < /* IDSEL 0x15 */ - a800 0 0 1 &mpic 3b 1 - a800 0 0 2 &mpic 3b 1 - a800 0 0 3 &mpic 3b 1 - a800 0 0 4 &mpic 3b 1>; + a800 0 0 1 &mpic b 1 + a800 0 0 2 &mpic b 1 + a800 0 0 3 &mpic b 1 + a800 0 0 4 &mpic b 1>; interrupt-parent = <&mpic>; - interrupts = <09 2>; + interrupts = <19 2>; bus-range = <0 0>; ranges = <02000000 0 a0000000 a0000000 0 20000000 01000000 0 00000000 e3000000 0 00100000>; diff --git a/arch/powerpc/boot/dts/mpc8544ds.dts b/arch/powerpc/boot/dts/mpc8544ds.dts index 3033599e74e8..828592592460 100644 --- a/arch/powerpc/boot/dts/mpc8544ds.dts +++ b/arch/powerpc/boot/dts/mpc8544ds.dts @@ -52,7 +52,7 @@ compatible = "fsl,8544-memory-controller"; reg = <2000 1000>; interrupt-parent = <&mpic>; - interrupts = <2 2>; + interrupts = <12 2>; }; l2-cache-controller@20000 { @@ -61,14 +61,14 @@ cache-line-size = <20>; // 32 bytes cache-size = <40000>; // L2, 256K interrupt-parent = <&mpic>; - interrupts = <0 2>; + interrupts = <10 2>; }; i2c@3000 { device_type = "i2c"; compatible = "fsl-i2c"; reg = <3000 100>; - interrupts = <1b 2>; + interrupts = <2b 2>; interrupt-parent = <&mpic>; dfsrr; }; @@ -81,13 +81,13 @@ reg = <24520 20>; phy0: ethernet-phy@0 { interrupt-parent = <&mpic>; - interrupts = <3a 1>; + interrupts = ; reg = <0>; device_type = "ethernet-phy"; }; phy1: ethernet-phy@1 { interrupt-parent = <&mpic>; - interrupts = <3a 1>; + interrupts = ; reg = <1>; device_type = "ethernet-phy"; }; @@ -101,7 +101,7 @@ compatible = "gianfar"; reg = <24000 1000>; local-mac-address = [ 00 00 00 00 00 00 ]; - interrupts = ; + interrupts = <1d 2 1e 2 22 2>; interrupt-parent = <&mpic>; phy-handle = <&phy0>; }; @@ -114,7 +114,7 @@ compatible = "gianfar"; reg = <26000 1000>; local-mac-address = [ 00 00 00 00 00 00 ]; - interrupts = ; + interrupts = <1f 2 20 2 21 2>; interrupt-parent = <&mpic>; phy-handle = <&phy1>; }; @@ -124,7 +124,7 @@ compatible = "ns16550"; reg = <4500 100>; clock-frequency = <0>; - interrupts = <1a 2>; + interrupts = <2a 2>; interrupt-parent = <&mpic>; }; @@ -133,7 +133,7 @@ compatible = "ns16550"; reg = <4600 100>; clock-frequency = <0>; - interrupts = <1a 2>; + interrupts = <2a 2>; interrupt-parent = <&mpic>; }; diff --git a/arch/powerpc/boot/dts/mpc8548cds.dts b/arch/powerpc/boot/dts/mpc8548cds.dts index ad96381033c0..9d0b84b66cd4 100644 --- a/arch/powerpc/boot/dts/mpc8548cds.dts +++ b/arch/powerpc/boot/dts/mpc8548cds.dts @@ -52,7 +52,7 @@ compatible = "fsl,8548-memory-controller"; reg = <2000 1000>; interrupt-parent = <&mpic>; - interrupts = <2 2>; + interrupts = <12 2>; }; l2-cache-controller@20000 { @@ -61,14 +61,14 @@ cache-line-size = <20>; // 32 bytes cache-size = <80000>; // L2, 512K interrupt-parent = <&mpic>; - interrupts = <0 2>; + interrupts = <10 2>; }; i2c@3000 { device_type = "i2c"; compatible = "fsl-i2c"; reg = <3000 100>; - interrupts = <1b 2>; + interrupts = <2b 2>; interrupt-parent = <&mpic>; dfsrr; }; @@ -81,25 +81,25 @@ reg = <24520 20>; phy0: ethernet-phy@0 { interrupt-parent = <&mpic>; - interrupts = <35 0>; + interrupts = <5 1>; reg = <0>; device_type = "ethernet-phy"; }; phy1: ethernet-phy@1 { interrupt-parent = <&mpic>; - interrupts = <35 0>; + interrupts = <5 1>; reg = <1>; device_type = "ethernet-phy"; }; phy2: ethernet-phy@2 { interrupt-parent = <&mpic>; - interrupts = <35 0>; + interrupts = <5 1>; reg = <2>; device_type = "ethernet-phy"; }; phy3: ethernet-phy@3 { interrupt-parent = <&mpic>; - interrupts = <35 0>; + interrupts = <5 1>; reg = <3>; device_type = "ethernet-phy"; }; @@ -112,8 +112,8 @@ model = "eTSEC"; compatible = "gianfar"; reg = <24000 1000>; - local-mac-address = [ 00 E0 0C 00 73 00 ]; - interrupts = ; + local-mac-address = [ 00 00 00 00 00 00 ]; + interrupts = <1d 2 1e 2 22 2>; interrupt-parent = <&mpic>; phy-handle = <&phy0>; }; @@ -125,8 +125,8 @@ model = "eTSEC"; compatible = "gianfar"; reg = <25000 1000>; - local-mac-address = [ 00 E0 0C 00 73 01 ]; - interrupts = <13 2 14 2 18 2>; + local-mac-address = [ 00 00 00 00 00 00 ]; + interrupts = <23 2 24 2 28 2>; interrupt-parent = <&mpic>; phy-handle = <&phy1>; }; @@ -139,8 +139,8 @@ model = "eTSEC"; compatible = "gianfar"; reg = <26000 1000>; - local-mac-address = [ 00 E0 0C 00 73 02 ]; - interrupts = ; + local-mac-address = [ 00 00 00 00 00 00 ]; + interrupts = <1f 2 20 2 21 2>; interrupt-parent = <&mpic>; phy-handle = <&phy2>; }; @@ -152,8 +152,8 @@ model = "eTSEC"; compatible = "gianfar"; reg = <27000 1000>; - local-mac-address = [ 00 E0 0C 00 73 03 ]; - interrupts = <15 2 16 2 17 2>; + local-mac-address = [ 00 00 00 00 00 00 ]; + interrupts = <25 2 26 2 27 2>; interrupt-parent = <&mpic>; phy-handle = <&phy3>; }; @@ -164,7 +164,7 @@ compatible = "ns16550"; reg = <4500 100>; // reg base, size clock-frequency = <0>; // should we fill in in uboot? - interrupts = <1a 2>; + interrupts = <2a 2>; interrupt-parent = <&mpic>; }; @@ -173,58 +173,64 @@ compatible = "ns16550"; reg = <4600 100>; // reg base, size clock-frequency = <0>; // should we fill in in uboot? - interrupts = <1a 2>; + interrupts = <2a 2>; interrupt-parent = <&mpic>; }; + global-utilities@e0000 { //global utilities reg + compatible = "fsl,mpc8548-guts"; + reg = ; + fsl,has-rstcr; + }; + pci1: pci@8000 { interrupt-map-mask = <1f800 0 0 7>; interrupt-map = < /* IDSEL 0x10 */ - 08000 0 0 1 &mpic 30 1 - 08000 0 0 2 &mpic 31 1 - 08000 0 0 3 &mpic 32 1 - 08000 0 0 4 &mpic 33 1 + 08000 0 0 1 &mpic 0 1 + 08000 0 0 2 &mpic 1 1 + 08000 0 0 3 &mpic 2 1 + 08000 0 0 4 &mpic 3 1 /* IDSEL 0x11 */ - 08800 0 0 1 &mpic 30 1 - 08800 0 0 2 &mpic 31 1 - 08800 0 0 3 &mpic 32 1 - 08800 0 0 4 &mpic 33 1 + 08800 0 0 1 &mpic 0 1 + 08800 0 0 2 &mpic 1 1 + 08800 0 0 3 &mpic 2 1 + 08800 0 0 4 &mpic 3 1 /* IDSEL 0x12 (Slot 1) */ - 09000 0 0 1 &mpic 30 1 - 09000 0 0 2 &mpic 31 1 - 09000 0 0 3 &mpic 32 1 - 09000 0 0 4 &mpic 33 1 + 09000 0 0 1 &mpic 0 1 + 09000 0 0 2 &mpic 1 1 + 09000 0 0 3 &mpic 2 1 + 09000 0 0 4 &mpic 3 1 /* IDSEL 0x13 (Slot 2) */ - 09800 0 0 1 &mpic 31 1 - 09800 0 0 2 &mpic 32 1 - 09800 0 0 3 &mpic 33 1 - 09800 0 0 4 &mpic 30 1 + 09800 0 0 1 &mpic 1 1 + 09800 0 0 2 &mpic 2 1 + 09800 0 0 3 &mpic 3 1 + 09800 0 0 4 &mpic 0 1 /* IDSEL 0x14 (Slot 3) */ - 0a000 0 0 1 &mpic 32 1 - 0a000 0 0 2 &mpic 33 1 - 0a000 0 0 3 &mpic 30 1 - 0a000 0 0 4 &mpic 31 1 + 0a000 0 0 1 &mpic 2 1 + 0a000 0 0 2 &mpic 3 1 + 0a000 0 0 3 &mpic 0 1 + 0a000 0 0 4 &mpic 1 1 /* IDSEL 0x15 (Slot 4) */ - 0a800 0 0 1 &mpic 33 1 - 0a800 0 0 2 &mpic 30 1 - 0a800 0 0 3 &mpic 31 1 - 0a800 0 0 4 &mpic 32 1 + 0a800 0 0 1 &mpic 3 1 + 0a800 0 0 2 &mpic 0 1 + 0a800 0 0 3 &mpic 1 1 + 0a800 0 0 4 &mpic 2 1 /* Bus 1 (Tundra Bridge) */ /* IDSEL 0x12 (ISA bridge) */ - 19000 0 0 1 &mpic 30 1 - 19000 0 0 2 &mpic 31 1 - 19000 0 0 3 &mpic 32 1 - 19000 0 0 4 &mpic 33 1>; + 19000 0 0 1 &mpic 0 1 + 19000 0 0 2 &mpic 1 1 + 19000 0 0 3 &mpic 2 1 + 19000 0 0 4 &mpic 3 1>; interrupt-parent = <&mpic>; - interrupts = <08 2>; + interrupts = <18 2>; bus-range = <0 0>; ranges = <02000000 0 80000000 80000000 0 20000000 01000000 0 00000000 e2000000 0 00100000>; @@ -256,12 +262,12 @@ interrupt-map = < /* IDSEL 0x15 */ - a800 0 0 1 &mpic 3b 1 - a800 0 0 2 &mpic 3b 1 - a800 0 0 3 &mpic 3b 1 - a800 0 0 4 &mpic 3b 1>; + a800 0 0 1 &mpic b 1 + a800 0 0 2 &mpic b 1 + a800 0 0 3 &mpic b 1 + a800 0 0 4 &mpic b 1>; interrupt-parent = <&mpic>; - interrupts = <09 2>; + interrupts = <19 2>; bus-range = <0 0>; ranges = <02000000 0 a0000000 a0000000 0 20000000 01000000 0 00000000 e3000000 0 00100000>; diff --git a/arch/powerpc/boot/dts/mpc8555cds.dts b/arch/powerpc/boot/dts/mpc8555cds.dts index 951ed92f1154..17e45d9a382a 100644 --- a/arch/powerpc/boot/dts/mpc8555cds.dts +++ b/arch/powerpc/boot/dts/mpc8555cds.dts @@ -52,7 +52,7 @@ compatible = "fsl,8555-memory-controller"; reg = <2000 1000>; interrupt-parent = <&mpic>; - interrupts = <2 2>; + interrupts = <12 2>; }; l2-cache-controller@20000 { @@ -61,14 +61,14 @@ cache-line-size = <20>; // 32 bytes cache-size = <40000>; // L2, 256K interrupt-parent = <&mpic>; - interrupts = <0 2>; + interrupts = <10 2>; }; i2c@3000 { device_type = "i2c"; compatible = "fsl-i2c"; reg = <3000 100>; - interrupts = <1b 2>; + interrupts = <2b 2>; interrupt-parent = <&mpic>; dfsrr; }; @@ -81,13 +81,13 @@ reg = <24520 20>; phy0: ethernet-phy@0 { interrupt-parent = <&mpic>; - interrupts = <35 0>; + interrupts = <5 1>; reg = <0>; device_type = "ethernet-phy"; }; phy1: ethernet-phy@1 { interrupt-parent = <&mpic>; - interrupts = <35 0>; + interrupts = <5 1>; reg = <1>; device_type = "ethernet-phy"; }; @@ -100,8 +100,8 @@ model = "TSEC"; compatible = "gianfar"; reg = <24000 1000>; - local-mac-address = [ 00 E0 0C 00 73 00 ]; - interrupts = <0d 2 0e 2 12 2>; + local-mac-address = [ 00 00 00 00 00 00 ]; + interrupts = <1d 2 1e 2 22 2>; interrupt-parent = <&mpic>; phy-handle = <&phy0>; }; @@ -113,8 +113,8 @@ model = "TSEC"; compatible = "gianfar"; reg = <25000 1000>; - local-mac-address = [ 00 E0 0C 00 73 01 ]; - interrupts = <13 2 14 2 18 2>; + local-mac-address = [ 00 00 00 00 00 00 ]; + interrupts = <23 2 24 2 28 2>; interrupt-parent = <&mpic>; phy-handle = <&phy1>; }; @@ -124,7 +124,7 @@ compatible = "ns16550"; reg = <4500 100>; // reg base, size clock-frequency = <0>; // should we fill in in uboot? - interrupts = <1a 2>; + interrupts = <2a 2>; interrupt-parent = <&mpic>; }; @@ -133,7 +133,7 @@ compatible = "ns16550"; reg = <4600 100>; // reg base, size clock-frequency = <0>; // should we fill in in uboot? - interrupts = <1a 2>; + interrupts = <2a 2>; interrupt-parent = <&mpic>; }; @@ -142,49 +142,49 @@ interrupt-map = < /* IDSEL 0x10 */ - 08000 0 0 1 &mpic 30 1 - 08000 0 0 2 &mpic 31 1 - 08000 0 0 3 &mpic 32 1 - 08000 0 0 4 &mpic 33 1 + 08000 0 0 1 &mpic 0 1 + 08000 0 0 2 &mpic 1 1 + 08000 0 0 3 &mpic 2 1 + 08000 0 0 4 &mpic 3 1 /* IDSEL 0x11 */ - 08800 0 0 1 &mpic 30 1 - 08800 0 0 2 &mpic 31 1 - 08800 0 0 3 &mpic 32 1 - 08800 0 0 4 &mpic 33 1 + 08800 0 0 1 &mpic 0 1 + 08800 0 0 2 &mpic 1 1 + 08800 0 0 3 &mpic 2 1 + 08800 0 0 4 &mpic 3 1 /* IDSEL 0x12 (Slot 1) */ - 09000 0 0 1 &mpic 30 1 - 09000 0 0 2 &mpic 31 1 - 09000 0 0 3 &mpic 32 1 - 09000 0 0 4 &mpic 33 1 + 09000 0 0 1 &mpic 0 1 + 09000 0 0 2 &mpic 1 1 + 09000 0 0 3 &mpic 2 1 + 09000 0 0 4 &mpic 3 1 /* IDSEL 0x13 (Slot 2) */ - 09800 0 0 1 &mpic 31 1 - 09800 0 0 2 &mpic 32 1 - 09800 0 0 3 &mpic 33 1 - 09800 0 0 4 &mpic 30 1 + 09800 0 0 1 &mpic 1 1 + 09800 0 0 2 &mpic 2 1 + 09800 0 0 3 &mpic 3 1 + 09800 0 0 4 &mpic 0 1 /* IDSEL 0x14 (Slot 3) */ - 0a000 0 0 1 &mpic 32 1 - 0a000 0 0 2 &mpic 33 1 - 0a000 0 0 3 &mpic 30 1 - 0a000 0 0 4 &mpic 31 1 + 0a000 0 0 1 &mpic 2 1 + 0a000 0 0 2 &mpic 3 1 + 0a000 0 0 3 &mpic 0 1 + 0a000 0 0 4 &mpic 1 1 /* IDSEL 0x15 (Slot 4) */ - 0a800 0 0 1 &mpic 33 1 - 0a800 0 0 2 &mpic 30 1 - 0a800 0 0 3 &mpic 31 1 - 0a800 0 0 4 &mpic 32 1 + 0a800 0 0 1 &mpic 3 1 + 0a800 0 0 2 &mpic 0 1 + 0a800 0 0 3 &mpic 1 1 + 0a800 0 0 4 &mpic 2 1 /* Bus 1 (Tundra Bridge) */ /* IDSEL 0x12 (ISA bridge) */ - 19000 0 0 1 &mpic 30 1 - 19000 0 0 2 &mpic 31 1 - 19000 0 0 3 &mpic 32 1 - 19000 0 0 4 &mpic 33 1>; + 19000 0 0 1 &mpic 0 1 + 19000 0 0 2 &mpic 1 1 + 19000 0 0 3 &mpic 2 1 + 19000 0 0 4 &mpic 3 1>; interrupt-parent = <&mpic>; - interrupts = <08 2>; + interrupts = <18 2>; bus-range = <0 0>; ranges = <02000000 0 80000000 80000000 0 20000000 01000000 0 00000000 e2000000 0 00100000>; @@ -216,12 +216,12 @@ interrupt-map = < /* IDSEL 0x15 */ - a800 0 0 1 &mpic 3b 1 - a800 0 0 2 &mpic 3b 1 - a800 0 0 3 &mpic 3b 1 - a800 0 0 4 &mpic 3b 1>; + a800 0 0 1 &mpic b 1 + a800 0 0 2 &mpic b 1 + a800 0 0 3 &mpic b 1 + a800 0 0 4 &mpic b 1>; interrupt-parent = <&mpic>; - interrupts = <09 2>; + interrupts = <19 2>; bus-range = <0 0>; ranges = <02000000 0 a0000000 a0000000 0 20000000 01000000 0 00000000 e3000000 0 00100000>; diff --git a/arch/powerpc/boot/dts/mpc8560ads.dts b/arch/powerpc/boot/dts/mpc8560ads.dts index 80682152b0cf..21ccaaa27993 100644 --- a/arch/powerpc/boot/dts/mpc8560ads.dts +++ b/arch/powerpc/boot/dts/mpc8560ads.dts @@ -52,7 +52,7 @@ compatible = "fsl,8540-memory-controller"; reg = <2000 1000>; interrupt-parent = <&mpic>; - interrupts = <2 2>; + interrupts = <12 2>; }; l2-cache-controller@20000 { @@ -61,7 +61,7 @@ cache-line-size = <20>; // 32 bytes cache-size = <40000>; // L2, 256K interrupt-parent = <&mpic>; - interrupts = <0 2>; + interrupts = <10 2>; }; mdio@24520 { @@ -72,25 +72,25 @@ #size-cells = <0>; phy0: ethernet-phy@0 { interrupt-parent = <&mpic>; - interrupts = <35 1>; + interrupts = <5 1>; reg = <0>; device_type = "ethernet-phy"; }; phy1: ethernet-phy@1 { interrupt-parent = <&mpic>; - interrupts = <35 1>; + interrupts = <5 1>; reg = <1>; device_type = "ethernet-phy"; }; phy2: ethernet-phy@2 { interrupt-parent = <&mpic>; - interrupts = <37 1>; + interrupts = <7 1>; reg = <2>; device_type = "ethernet-phy"; }; phy3: ethernet-phy@3 { interrupt-parent = <&mpic>; - interrupts = <37 1>; + interrupts = <7 1>; reg = <3>; device_type = "ethernet-phy"; }; @@ -101,8 +101,14 @@ model = "TSEC"; compatible = "gianfar"; reg = <24000 1000>; - address = [ 00 00 0C 00 00 FD ]; - interrupts = ; + /* + * address is deprecated and will be removed + * in 2.6.25. Only recent versions of + * U-Boot support local-mac-address, however. + */ + address = [ 00 00 00 00 00 00 ]; + local-mac-address = [ 00 00 00 00 00 00 ]; + interrupts = <1d 2 1e 2 22 2>; interrupt-parent = <&mpic>; phy-handle = <&phy0>; }; @@ -114,8 +120,14 @@ model = "TSEC"; compatible = "gianfar"; reg = <25000 1000>; - address = [ 00 00 0C 00 01 FD ]; - interrupts = <13 2 14 2 18 2>; + /* + * address is deprecated and will be removed + * in 2.6.25. Only recent versions of + * U-Boot support local-mac-address, however. + */ + address = [ 00 00 00 00 00 00 ]; + local-mac-address = [ 00 00 00 00 00 00 ]; + interrupts = <23 2 24 2 28 2>; interrupt-parent = <&mpic>; phy-handle = <&phy1>; }; @@ -132,79 +144,79 @@ interrupt-map = < /* IDSEL 0x2 */ - 1000 0 0 1 &mpic 31 1 - 1000 0 0 2 &mpic 32 1 - 1000 0 0 3 &mpic 33 1 - 1000 0 0 4 &mpic 34 1 + 1000 0 0 1 &mpic 1 1 + 1000 0 0 2 &mpic 2 1 + 1000 0 0 3 &mpic 3 1 + 1000 0 0 4 &mpic 4 1 /* IDSEL 0x3 */ - 1800 0 0 1 &mpic 34 1 - 1800 0 0 2 &mpic 31 1 - 1800 0 0 3 &mpic 32 1 - 1800 0 0 4 &mpic 33 1 + 1800 0 0 1 &mpic 4 1 + 1800 0 0 2 &mpic 1 1 + 1800 0 0 3 &mpic 2 1 + 1800 0 0 4 &mpic 3 1 /* IDSEL 0x4 */ - 2000 0 0 1 &mpic 33 1 - 2000 0 0 2 &mpic 34 1 - 2000 0 0 3 &mpic 31 1 - 2000 0 0 4 &mpic 32 1 + 2000 0 0 1 &mpic 3 1 + 2000 0 0 2 &mpic 4 1 + 2000 0 0 3 &mpic 1 1 + 2000 0 0 4 &mpic 2 1 /* IDSEL 0x5 */ - 2800 0 0 1 &mpic 32 1 - 2800 0 0 2 &mpic 33 1 - 2800 0 0 3 &mpic 34 1 - 2800 0 0 4 &mpic 31 1 + 2800 0 0 1 &mpic 2 1 + 2800 0 0 2 &mpic 3 1 + 2800 0 0 3 &mpic 4 1 + 2800 0 0 4 &mpic 1 1 /* IDSEL 12 */ - 6000 0 0 1 &mpic 31 1 - 6000 0 0 2 &mpic 32 1 - 6000 0 0 3 &mpic 33 1 - 6000 0 0 4 &mpic 34 1 + 6000 0 0 1 &mpic 1 1 + 6000 0 0 2 &mpic 2 1 + 6000 0 0 3 &mpic 3 1 + 6000 0 0 4 &mpic 4 1 /* IDSEL 13 */ - 6800 0 0 1 &mpic 34 1 - 6800 0 0 2 &mpic 31 1 - 6800 0 0 3 &mpic 32 1 - 6800 0 0 4 &mpic 33 1 + 6800 0 0 1 &mpic 4 1 + 6800 0 0 2 &mpic 1 1 + 6800 0 0 3 &mpic 2 1 + 6800 0 0 4 &mpic 3 1 /* IDSEL 14*/ - 7000 0 0 1 &mpic 33 1 - 7000 0 0 2 &mpic 34 1 - 7000 0 0 3 &mpic 31 1 - 7000 0 0 4 &mpic 32 1 + 7000 0 0 1 &mpic 3 1 + 7000 0 0 2 &mpic 4 1 + 7000 0 0 3 &mpic 1 1 + 7000 0 0 4 &mpic 2 1 /* IDSEL 15 */ - 7800 0 0 1 &mpic 32 1 - 7800 0 0 2 &mpic 33 1 - 7800 0 0 3 &mpic 34 1 - 7800 0 0 4 &mpic 31 1 + 7800 0 0 1 &mpic 2 1 + 7800 0 0 2 &mpic 3 1 + 7800 0 0 3 &mpic 4 1 + 7800 0 0 4 &mpic 1 1 /* IDSEL 18 */ - 9000 0 0 1 &mpic 31 1 - 9000 0 0 2 &mpic 32 1 - 9000 0 0 3 &mpic 33 1 - 9000 0 0 4 &mpic 34 1 + 9000 0 0 1 &mpic 1 1 + 9000 0 0 2 &mpic 2 1 + 9000 0 0 3 &mpic 3 1 + 9000 0 0 4 &mpic 4 1 /* IDSEL 19 */ - 9800 0 0 1 &mpic 34 1 - 9800 0 0 2 &mpic 31 1 - 9800 0 0 3 &mpic 32 1 - 9800 0 0 4 &mpic 33 1 + 9800 0 0 1 &mpic 4 1 + 9800 0 0 2 &mpic 1 1 + 9800 0 0 3 &mpic 2 1 + 9800 0 0 4 &mpic 3 1 /* IDSEL 20 */ - a000 0 0 1 &mpic 33 1 - a000 0 0 2 &mpic 34 1 - a000 0 0 3 &mpic 31 1 - a000 0 0 4 &mpic 32 1 + a000 0 0 1 &mpic 3 1 + a000 0 0 2 &mpic 4 1 + a000 0 0 3 &mpic 1 1 + a000 0 0 4 &mpic 2 1 /* IDSEL 21 */ - a800 0 0 1 &mpic 32 1 - a800 0 0 2 &mpic 33 1 - a800 0 0 3 &mpic 34 1 - a800 0 0 4 &mpic 31 1>; + a800 0 0 1 &mpic 2 1 + a800 0 0 2 &mpic 3 1 + a800 0 0 3 &mpic 4 1 + a800 0 0 4 &mpic 1 1>; interrupt-parent = <&mpic>; - interrupts = <8 0>; + interrupts = <18 2>; bus-range = <0 0>; ranges = <02000000 0 80000000 80000000 0 20000000 01000000 0 00000000 e2000000 0 01000000>; @@ -234,7 +246,7 @@ interrupt-controller; #address-cells = <0>; #interrupt-cells = <2>; - interrupts = <1e 0>; + interrupts = <2e 2>; interrupt-parent = <&mpic>; reg = <90c00 80>; built-in; @@ -275,7 +287,13 @@ model = "FCC"; device-id = <2>; reg = <91320 20 88500 100 913a0 30>; - mac-address = [ 00 00 0C 00 02 FD ]; + /* + * mac-address is deprecated and will be removed + * in 2.6.25. Only recent versions of + * U-Boot support local-mac-address, however. + */ + mac-address = [ 00 00 00 00 00 00 ]; + local-mac-address = [ 00 00 00 00 00 00 ]; clock-setup = ; rx-clock = <15>; tx-clock = <16>; @@ -290,7 +308,13 @@ model = "FCC"; device-id = <3>; reg = <91340 20 88600 100 913d0 30>; - mac-address = [ 00 00 0C 00 03 FD ]; + /* + * mac-address is deprecated and will be removed + * in 2.6.25. Only recent versions of + * U-Boot support local-mac-address, however. + */ + mac-address = [ 00 00 00 00 00 00 ]; + local-mac-address = [ 00 00 00 00 00 00 ]; clock-setup = ; rx-clock = <17>; tx-clock = <18>; diff --git a/arch/powerpc/boot/dts/mpc8568mds.dts b/arch/powerpc/boot/dts/mpc8568mds.dts index a123ec9456bc..6bb18f2807a8 100644 --- a/arch/powerpc/boot/dts/mpc8568mds.dts +++ b/arch/powerpc/boot/dts/mpc8568mds.dts @@ -61,7 +61,7 @@ compatible = "fsl,8568-memory-controller"; reg = <2000 1000>; interrupt-parent = <&mpic>; - interrupts = <2 2>; + interrupts = <12 2>; }; l2-cache-controller@20000 { @@ -70,14 +70,14 @@ cache-line-size = <20>; // 32 bytes cache-size = <80000>; // L2, 512K interrupt-parent = <&mpic>; - interrupts = <0 2>; + interrupts = <10 2>; }; i2c@3000 { device_type = "i2c"; compatible = "fsl-i2c"; reg = <3000 100>; - interrupts = <1b 2>; + interrupts = <2b 2>; interrupt-parent = <&mpic>; dfsrr; }; @@ -86,7 +86,7 @@ device_type = "i2c"; compatible = "fsl-i2c"; reg = <3100 100>; - interrupts = <1b 2>; + interrupts = <2b 2>; interrupt-parent = <&mpic>; dfsrr; }; @@ -99,25 +99,25 @@ reg = <24520 20>; phy0: ethernet-phy@0 { interrupt-parent = <&mpic>; - interrupts = <31 1>; + interrupts = <1 1>; reg = <0>; device_type = "ethernet-phy"; }; phy1: ethernet-phy@1 { interrupt-parent = <&mpic>; - interrupts = <32 1>; + interrupts = <2 1>; reg = <1>; device_type = "ethernet-phy"; }; phy2: ethernet-phy@2 { interrupt-parent = <&mpic>; - interrupts = <31 1>; + interrupts = <1 1>; reg = <2>; device_type = "ethernet-phy"; }; phy3: ethernet-phy@3 { interrupt-parent = <&mpic>; - interrupts = <32 1>; + interrupts = <2 1>; reg = <3>; device_type = "ethernet-phy"; }; @@ -130,8 +130,14 @@ model = "eTSEC"; compatible = "gianfar"; reg = <24000 1000>; + /* + * mac-address is deprecated and will be removed + * in 2.6.25. Only recent versions of + * U-Boot support local-mac-address, however. + */ mac-address = [ 00 00 00 00 00 00 ]; - interrupts = ; + local-mac-address = [ 00 00 00 00 00 00 ]; + interrupts = <1d 2 1e 2 22 2>; interrupt-parent = <&mpic>; phy-handle = <&phy2>; }; @@ -143,8 +149,14 @@ model = "eTSEC"; compatible = "gianfar"; reg = <25000 1000>; - mac-address = [ 00 00 00 00 00 00]; - interrupts = <13 2 14 2 18 2>; + /* + * mac-address is deprecated and will be removed + * in 2.6.25. Only recent versions of + * U-Boot support local-mac-address, however. + */ + mac-address = [ 00 00 00 00 00 00 ]; + local-mac-address = [ 00 00 00 00 00 00 ]; + interrupts = <23 2 24 2 28 2>; interrupt-parent = <&mpic>; phy-handle = <&phy3>; }; @@ -154,7 +166,7 @@ compatible = "ns16550"; reg = <4500 100>; clock-frequency = <0>; - interrupts = <1a 2>; + interrupts = <2a 2>; interrupt-parent = <&mpic>; }; @@ -163,7 +175,7 @@ compatible = "ns16550"; reg = <4600 100>; clock-frequency = <0>; - interrupts = <1a 2>; + interrupts = <2a 2>; interrupt-parent = <&mpic>; }; @@ -172,7 +184,7 @@ model = "SEC2"; compatible = "talitos"; reg = <30000 f000>; - interrupts = <1d 2>; + interrupts = <2d 2>; interrupt-parent = <&mpic>; num-channels = <4>; channel-fifo-len = <18>; @@ -300,7 +312,13 @@ reg = <2000 200>; interrupts = <20>; interrupt-parent = <&qeic>; - mac-address = [ 00 04 9f 00 23 23 ]; + /* + * mac-address is deprecated and will be removed + * in 2.6.25. Only recent versions of + * U-Boot support local-mac-address, however. + */ + mac-address = [ 00 00 00 00 00 00 ]; + local-mac-address = [ 00 00 00 00 00 00 ]; rx-clock = <0>; tx-clock = <19>; phy-handle = <&qe_phy0>; @@ -316,7 +334,13 @@ reg = <3000 200>; interrupts = <21>; interrupt-parent = <&qeic>; - mac-address = [ 00 11 22 33 44 55 ]; + /* + * mac-address is deprecated and will be removed + * in 2.6.25. Only recent versions of + * U-Boot support local-mac-address, however. + */ + mac-address = [ 00 00 00 00 00 00 ]; + local-mac-address = [ 00 00 00 00 00 00 ]; rx-clock = <0>; tx-clock = <14>; phy-handle = <&qe_phy1>; @@ -335,25 +359,25 @@ * gianfar's MDIO bus */ qe_phy0: ethernet-phy@00 { interrupt-parent = <&mpic>; - interrupts = <31 1>; + interrupts = <1 1>; reg = <0>; device_type = "ethernet-phy"; }; qe_phy1: ethernet-phy@01 { interrupt-parent = <&mpic>; - interrupts = <32 1>; + interrupts = <2 1>; reg = <1>; device_type = "ethernet-phy"; }; qe_phy2: ethernet-phy@02 { interrupt-parent = <&mpic>; - interrupts = <31 1>; + interrupts = <1 1>; reg = <2>; device_type = "ethernet-phy"; }; qe_phy3: ethernet-phy@03 { interrupt-parent = <&mpic>; - interrupts = <32 1>; + interrupts = <2 1>; reg = <3>; device_type = "ethernet-phy"; }; @@ -367,7 +391,7 @@ reg = <80 80>; built-in; big-endian; - interrupts = <1e 2 1e 2>; //high:30 low:30 + interrupts = <2e 2 2e 2>; //high:30 low:30 interrupt-parent = <&mpic>; }; diff --git a/arch/powerpc/boot/dts/mpc8641_hpcn.dts b/arch/powerpc/boot/dts/mpc8641_hpcn.dts index 260b264c869e..db56a02b748f 100644 --- a/arch/powerpc/boot/dts/mpc8641_hpcn.dts +++ b/arch/powerpc/boot/dts/mpc8641_hpcn.dts @@ -56,8 +56,12 @@ #size-cells = <1>; #interrupt-cells = <2>; device_type = "soc"; - ranges = <0 f8000000 00100000>; - reg = ; // CCSRBAR 1M + ranges = <00001000 f8001000 000ff000 + 80000000 80000000 20000000 + e2000000 e2000000 00100000 + a0000000 a0000000 20000000 + e3000000 e3000000 00100000>; + reg = ; // CCSRBAR bus-frequency = <0>; i2c@3000 { @@ -86,25 +90,25 @@ reg = <24520 20>; phy0: ethernet-phy@0 { interrupt-parent = <&mpic>; - interrupts = <4a 1>; + interrupts = ; reg = <0>; device_type = "ethernet-phy"; }; phy1: ethernet-phy@1 { interrupt-parent = <&mpic>; - interrupts = <4a 1>; + interrupts = ; reg = <1>; device_type = "ethernet-phy"; }; phy2: ethernet-phy@2 { interrupt-parent = <&mpic>; - interrupts = <4a 1>; + interrupts = ; reg = <2>; device_type = "ethernet-phy"; }; phy3: ethernet-phy@3 { interrupt-parent = <&mpic>; - interrupts = <4a 1>; + interrupts = ; reg = <3>; device_type = "ethernet-phy"; }; @@ -117,7 +121,13 @@ model = "TSEC"; compatible = "gianfar"; reg = <24000 1000>; - mac-address = [ 00 E0 0C 00 73 00 ]; + /* + * mac-address is deprecated and will be removed + * in 2.6.25. Only recent versions of + * U-Boot support local-mac-address, however. + */ + mac-address = [ 00 00 00 00 00 00 ]; + local-mac-address = [ 00 00 00 00 00 00 ]; interrupts = <1d 2 1e 2 22 2>; interrupt-parent = <&mpic>; phy-handle = <&phy0>; @@ -130,7 +140,13 @@ model = "TSEC"; compatible = "gianfar"; reg = <25000 1000>; - mac-address = [ 00 E0 0C 00 73 01 ]; + /* + * mac-address is deprecated and will be removed + * in 2.6.25. Only recent versions of + * U-Boot support local-mac-address, however. + */ + mac-address = [ 00 00 00 00 00 00 ]; + local-mac-address = [ 00 00 00 00 00 00 ]; interrupts = <23 2 24 2 28 2>; interrupt-parent = <&mpic>; phy-handle = <&phy1>; @@ -143,7 +159,13 @@ model = "TSEC"; compatible = "gianfar"; reg = <26000 1000>; - mac-address = [ 00 E0 0C 00 02 FD ]; + /* + * mac-address is deprecated and will be removed + * in 2.6.25. Only recent versions of + * U-Boot support local-mac-address, however. + */ + mac-address = [ 00 00 00 00 00 00 ]; + local-mac-address = [ 00 00 00 00 00 00 ]; interrupts = <1F 2 20 2 21 2>; interrupt-parent = <&mpic>; phy-handle = <&phy2>; @@ -156,7 +178,13 @@ model = "TSEC"; compatible = "gianfar"; reg = <27000 1000>; - mac-address = [ 00 E0 0C 00 03 FD ]; + /* + * mac-address is deprecated and will be removed + * in 2.6.25. Only recent versions of + * U-Boot support local-mac-address, however. + */ + mac-address = [ 00 00 00 00 00 00 ]; + local-mac-address = [ 00 00 00 00 00 00 ]; interrupts = <25 2 26 2 27 2>; interrupt-parent = <&mpic>; phy-handle = <&phy3>; @@ -186,7 +214,7 @@ #size-cells = <2>; #address-cells = <3>; reg = <8000 1000>; - bus-range = <0 fe>; + bus-range = <0 ff>; ranges = <02000000 0 80000000 80000000 0 20000000 01000000 0 00000000 e2000000 0 00100000>; clock-frequency = <1fca055>; @@ -285,17 +313,84 @@ f800 0 0 3 &i8259 0 0 f800 0 0 4 &i8259 0 0 >; - i8259: i8259@4d0 { - clock-frequency = <0>; - interrupt-controller; - device_type = "interrupt-controller"; - #address-cells = <0>; - #interrupt-cells = <2>; - built-in; - compatible = "chrp,iic"; - big-endian; - interrupts = <49 2>; - interrupt-parent = <&mpic>; + uli1575@0 { + reg = <0 0 0 0 0>; + #size-cells = <2>; + #address-cells = <3>; + ranges = <02000000 0 80000000 + 02000000 0 80000000 + 0 20000000 + 01000000 0 00000000 + 01000000 0 00000000 + 0 00100000>; + + pci_bridge@0 { + reg = <0 0 0 0 0>; + #size-cells = <2>; + #address-cells = <3>; + ranges = <02000000 0 80000000 + 02000000 0 80000000 + 0 20000000 + 01000000 0 00000000 + 01000000 0 00000000 + 0 00100000>; + + isa@1e { + device_type = "isa"; + #interrupt-cells = <2>; + #size-cells = <1>; + #address-cells = <2>; + reg = ; + ranges = <1 0 01000000 0 0 + 00001000>; + interrupt-parent = <&i8259>; + + i8259: interrupt-controller@20 { + reg = <1 20 2 + 1 a0 2 + 1 4d0 2>; + clock-frequency = <0>; + interrupt-controller; + device_type = "interrupt-controller"; + #address-cells = <0>; + #interrupt-cells = <2>; + built-in; + compatible = "chrp,iic"; + interrupts = <9 2>; + interrupt-parent = + <&mpic>; + }; + + i8042@60 { + #size-cells = <0>; + #address-cells = <1>; + reg = <1 60 1 1 64 1>; + interrupts = <1 3 c 3>; + interrupt-parent = + <&i8259>; + + keyboard@0 { + reg = <0>; + compatible = "pnpPNP,303"; + }; + + mouse@1 { + reg = <1>; + compatible = "pnpPNP,f03"; + }; + }; + + rtc@70 { + compatible = + "pnpPNP,b00"; + reg = <1 70 2>; + }; + + gpio@400 { + reg = <1 400 80>; + }; + }; + }; }; }; @@ -316,10 +411,10 @@ interrupt-map-mask = ; interrupt-map = < /* IDSEL 0x0 */ - 0000 0 0 1 &mpic 44 1 - 0000 0 0 2 &mpic 45 1 - 0000 0 0 3 &mpic 46 1 - 0000 0 0 4 &mpic 47 1 + 0000 0 0 1 &mpic 4 1 + 0000 0 0 2 &mpic 5 1 + 0000 0 0 3 &mpic 6 1 + 0000 0 0 4 &mpic 7 1 >; }; diff --git a/arch/powerpc/boot/dts/mpc866ads.dts b/arch/powerpc/boot/dts/mpc866ads.dts index c0d06fd12927..e5e7726ddb03 100644 --- a/arch/powerpc/boot/dts/mpc866ads.dts +++ b/arch/powerpc/boot/dts/mpc866ads.dts @@ -15,12 +15,10 @@ compatible = "mpc8xx"; #address-cells = <1>; #size-cells = <1>; - linux,phandle = <100>; cpus { #address-cells = <1>; #size-cells = <0>; - linux,phandle = <200>; PowerPC,866@0 { device_type = "cpu"; @@ -34,14 +32,12 @@ clock-frequency = <0>; 32-bit; interrupts = ; // decrementer interrupt - interrupt-parent = ; - linux,phandle = <201>; + interrupt-parent = <&Mpc8xx_pic>; }; }; memory { device_type = "memory"; - linux,phandle = <300>; reg = <00000000 800000>; }; @@ -57,11 +53,9 @@ device_type = "mdio"; compatible = "fs_enet"; reg = ; - linux,phandle = ; #address-cells = <1>; #size-cells = <0>; - ethernet-phy@f { - linux,phandle = ; + phy: ethernet-phy@f { reg = ; device_type = "ethernet-phy"; }; @@ -75,12 +69,11 @@ reg = ; mac-address = [ 00 00 0C 00 01 FD ]; interrupts = <3 1>; - interrupt-parent = ; - phy-handle = ; + interrupt-parent = <&Mpc8xx_pic>; + phy-handle = <&Phy>; }; - pic@ff000000 { - linux,phandle = ; + mpc8xx_pic: pic@ff000000 { interrupt-controller; #address-cells = <0>; #interrupt-cells = <2>; @@ -91,7 +84,6 @@ }; cpm@ff000000 { - linux,phandle = ; #address-cells = <1>; #size-cells = <1>; #interrupt-cells = <2>; @@ -102,15 +94,14 @@ command-proc = <9c0>; brg-frequency = <0>; interrupts = <0 2>; // cpm error interrupt - interrupt-parent = <930>; + interrupt-parent = <&Cpm_pic>; - pic@930 { - linux,phandle = <930>; + cpm_pic: pic@930 { interrupt-controller; #address-cells = <0>; #interrupt-cells = <2>; interrupts = <5 2 0 2>; - interrupt-parent = ; + interrupt-parent = <&Mpc8xx_pic>; reg = <930 20>; built-in; device_type = "cpm-pic"; @@ -128,7 +119,7 @@ tx-clock = <1>; current-speed = <0>; interrupts = <4 3>; - interrupt-parent = <930>; + interrupt-parent = <&Cpm_pic>; }; smc@a90 { @@ -142,7 +133,7 @@ tx-clock = <2>; current-speed = <0>; interrupts = <3 3>; - interrupt-parent = <930>; + interrupt-parent = <&Cpm_pic>; }; scc@a00 { @@ -153,7 +144,7 @@ reg = ; mac-address = [ 00 00 0C 00 03 FD ]; interrupts = <1e 3>; - interrupt-parent = <930>; + interrupt-parent = <&Cpm_pic>; }; }; }; diff --git a/arch/powerpc/boot/dts/mpc885ads.dts b/arch/powerpc/boot/dts/mpc885ads.dts index 110bf6170603..dc7ab9c80611 100644 --- a/arch/powerpc/boot/dts/mpc885ads.dts +++ b/arch/powerpc/boot/dts/mpc885ads.dts @@ -15,12 +15,10 @@ compatible = "mpc8xx"; #address-cells = <1>; #size-cells = <1>; - linux,phandle = <100>; cpus { #address-cells = <1>; #size-cells = <0>; - linux,phandle = <200>; PowerPC,885@0 { device_type = "cpu"; @@ -34,14 +32,12 @@ clock-frequency = <0>; 32-bit; interrupts = ; // decrementer interrupt - interrupt-parent = ; - linux,phandle = <201>; + interrupt-parent = <&Mpc8xx_pic>; }; }; memory { device_type = "memory"; - linux,phandle = <300>; reg = <00000000 800000>; }; @@ -57,21 +53,17 @@ device_type = "mdio"; compatible = "fs_enet"; reg = ; - linux,phandle = ; #address-cells = <1>; #size-cells = <0>; - ethernet-phy@0 { - linux,phandle = ; + Phy0: ethernet-phy@0 { reg = <0>; device_type = "ethernet-phy"; }; - ethernet-phy@1 { - linux,phandle = ; + Phy1: ethernet-phy@1 { reg = <1>; device_type = "ethernet-phy"; }; - ethernet-phy@2 { - linux,phandle = ; + Phy2: ethernet-phy@2 { reg = <2>; device_type = "ethernet-phy"; }; @@ -85,8 +77,8 @@ reg = ; mac-address = [ 00 00 0C 00 01 FD ]; interrupts = <3 1>; - interrupt-parent = ; - phy-handle = ; + interrupt-parent = <&Mpc8xx_pic>; + phy-handle = <&Phy1>; }; fec@1e00 { @@ -97,12 +89,11 @@ reg = <1e00 188>; mac-address = [ 00 00 0C 00 02 FD ]; interrupts = <7 1>; - interrupt-parent = ; - phy-handle = ; + interrupt-parent = <&Mpc8xx_pic>; + phy-handle = <&Phy2>; }; - pic@ff000000 { - linux,phandle = ; + Mpc8xx_pic: pic@ff000000 { interrupt-controller; #address-cells = <0>; #interrupt-cells = <2>; @@ -112,8 +103,18 @@ compatible = "CPM"; }; + pcmcia@0080 { + #address-cells = <3>; + #interrupt-cells = <1>; + #size-cells = <2>; + compatible = "fsl,pq-pcmcia"; + device_type = "pcmcia"; + reg = <80 80>; + interrupt-parent = <&Mpc8xx_pic>; + interrupts = ; + }; + cpm@ff000000 { - linux,phandle = ; #address-cells = <1>; #size-cells = <1>; #interrupt-cells = <2>; @@ -124,15 +125,14 @@ command-proc = <9c0>; brg-frequency = <0>; interrupts = <0 2>; // cpm error interrupt - interrupt-parent = <930>; + interrupt-parent = <&Cpm_pic>; - pic@930 { - linux,phandle = <930>; + Cpm_pic: pic@930 { interrupt-controller; #address-cells = <0>; #interrupt-cells = <2>; interrupts = <5 2 0 2>; - interrupt-parent = ; + interrupt-parent = <&Mpc8xx_pic>; reg = <930 20>; built-in; device_type = "cpm-pic"; @@ -150,7 +150,7 @@ tx-clock = <1>; current-speed = <0>; interrupts = <4 3>; - interrupt-parent = <930>; + interrupt-parent = <&Cpm_pic>; }; smc@a90 { @@ -164,7 +164,7 @@ tx-clock = <2>; current-speed = <0>; interrupts = <3 3>; - interrupt-parent = <930>; + interrupt-parent = <&Cpm_pic>; }; scc@a40 { @@ -175,8 +175,8 @@ reg = ; mac-address = [ 00 00 0C 00 03 FD ]; interrupts = <1c 3>; - interrupt-parent = <930>; - phy-handle = ; + interrupt-parent = <&Cpm_pic>; + phy-handle = <&Phy2>; }; }; }; diff --git a/arch/powerpc/boot/dts/prpmc2800.dts b/arch/powerpc/boot/dts/prpmc2800.dts index 568965a022b9..699d0df574d5 100644 --- a/arch/powerpc/boot/dts/prpmc2800.dts +++ b/arch/powerpc/boot/dts/prpmc2800.dts @@ -309,7 +309,7 @@ }; chosen { - bootargs = "ip=on console=ttyMM0"; + bootargs = "ip=on"; linux,stdout-path = "/mv64x60@f1000000/mpsc@8000"; }; }; diff --git a/arch/powerpc/boot/dts/ps3.dts b/arch/powerpc/boot/dts/ps3.dts new file mode 100644 index 000000000000..379ded282d5e --- /dev/null +++ b/arch/powerpc/boot/dts/ps3.dts @@ -0,0 +1,68 @@ +/* + * PS3 Game Console device tree. + * + * Copyright (C) 2007 Sony Computer Entertainment Inc. + * Copyright 2007 Sony Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/ { + model = "SonyPS3"; + compatible = "sony,ps3"; + #size-cells = <2>; + #address-cells = <2>; + + chosen { + }; + + /* + * We'll get the size of the bootmem block from lv1 after startup, + * so we'll put a null entry here. + */ + + memory { + device_type = "memory"; + reg = <0 0 0 0>; + }; + + /* + * The boot cpu is always zero for PS3. + * + * dtc expects a clock-frequency and timebase-frequency entries, so + * we'll put a null entries here. These will be initialized after + * startup with data from lv1. + * + * Seems the only way currently to indicate a processor has multiple + * threads is with an ibm,ppc-interrupt-server#s entry. We'll put one + * here so we can bring up both of ours. See smp_setup_cpu_maps(). + */ + + cpus { + #size-cells = <0>; + #address-cells = <1>; + + cpu@0 { + device_type = "cpu"; + reg = <0>; + ibm,ppc-interrupt-server#s = <0 1>; + clock-frequency = <0>; + timebase-frequency = <0>; + i-cache-size = <8000>; + d-cache-size = <8000>; + i-cache-line-size = <80>; + d-cache-line-size = <80>; + }; + }; +}; diff --git a/arch/powerpc/boot/ebony.c b/arch/powerpc/boot/ebony.c index b1251ee7a102..75daedafd0a4 100644 --- a/arch/powerpc/boot/ebony.c +++ b/arch/powerpc/boot/ebony.c @@ -100,28 +100,13 @@ static void ebony_fixups(void) ibm440gp_fixup_clocks(sysclk, 6 * 1843200); ibm44x_fixup_memsize(); dt_fixup_mac_addresses(ebony_mac0, ebony_mac1); -} - -#define SPRN_DBCR0 0x134 -#define DBCR0_RST_SYSTEM 0x30000000 - -static void ebony_exit(void) -{ - unsigned long tmp; - - asm volatile ( - "mfspr %0,%1\n" - "oris %0,%0,%2@h\n" - "mtspr %1,%0" - : "=&r"(tmp) : "i"(SPRN_DBCR0), "i"(DBCR0_RST_SYSTEM) - ); - + ibm4xx_fixup_ebc_ranges("/plb/opb/ebc"); } void ebony_init(void *mac0, void *mac1) { platform_ops.fixups = ebony_fixups; - platform_ops.exit = ebony_exit; + platform_ops.exit = ibm44x_dbcr_reset; ebony_mac0 = mac0; ebony_mac1 = mac1; ft_init(_dtb_start, _dtb_end - _dtb_start, 32); diff --git a/arch/powerpc/boot/main.c b/arch/powerpc/boot/main.c index 56b56a8d4b23..416dc3857bfe 100644 --- a/arch/powerpc/boot/main.c +++ b/arch/powerpc/boot/main.c @@ -36,8 +36,6 @@ struct addr_range { unsigned long size; }; -typedef void (*kernel_entry_t)(unsigned long, unsigned long, void *); - #undef DEBUG static struct addr_range prep_kernel(void) diff --git a/arch/powerpc/boot/of.c b/arch/powerpc/boot/of.c index d16ee3e3f868..385e08b83b7e 100644 --- a/arch/powerpc/boot/of.c +++ b/arch/powerpc/boot/of.c @@ -15,8 +15,7 @@ #include "page.h" #include "ops.h" -typedef void *ihandle; -typedef void *phandle; +#include "of.h" extern char _end[]; @@ -25,154 +24,10 @@ extern char _end[]; #define RAM_END (512<<20) /* Fixme: use OF */ #define ONE_MB 0x100000 -int (*prom) (void *); static unsigned long claim_base; -static int call_prom(const char *service, int nargs, int nret, ...) -{ - int i; - struct prom_args { - const char *service; - int nargs; - int nret; - unsigned int args[12]; - } args; - va_list list; - - args.service = service; - args.nargs = nargs; - args.nret = nret; - - va_start(list, nret); - for (i = 0; i < nargs; i++) - args.args[i] = va_arg(list, unsigned int); - va_end(list); - - for (i = 0; i < nret; i++) - args.args[nargs+i] = 0; - - if (prom(&args) < 0) - return -1; - - return (nret > 0)? args.args[nargs]: 0; -} - -static int call_prom_ret(const char *service, int nargs, int nret, - unsigned int *rets, ...) -{ - int i; - struct prom_args { - const char *service; - int nargs; - int nret; - unsigned int args[12]; - } args; - va_list list; - - args.service = service; - args.nargs = nargs; - args.nret = nret; - - va_start(list, rets); - for (i = 0; i < nargs; i++) - args.args[i] = va_arg(list, unsigned int); - va_end(list); - - for (i = 0; i < nret; i++) - args.args[nargs+i] = 0; - - if (prom(&args) < 0) - return -1; - - if (rets != (void *) 0) - for (i = 1; i < nret; ++i) - rets[i-1] = args.args[nargs+i]; - - return (nret > 0)? args.args[nargs]: 0; -} - -/* - * Older OF's require that when claiming a specific range of addresses, - * we claim the physical space in the /memory node and the virtual - * space in the chosen mmu node, and then do a map operation to - * map virtual to physical. - */ -static int need_map = -1; -static ihandle chosen_mmu; -static phandle memory; - -/* returns true if s2 is a prefix of s1 */ -static int string_match(const char *s1, const char *s2) -{ - for (; *s2; ++s2) - if (*s1++ != *s2) - return 0; - return 1; -} - -static int check_of_version(void) -{ - phandle oprom, chosen; - char version[64]; - - oprom = finddevice("/openprom"); - if (oprom == (phandle) -1) - return 0; - if (getprop(oprom, "model", version, sizeof(version)) <= 0) - return 0; - version[sizeof(version)-1] = 0; - printf("OF version = '%s'\r\n", version); - if (!string_match(version, "Open Firmware, 1.") - && !string_match(version, "FirmWorks,3.")) - return 0; - chosen = finddevice("/chosen"); - if (chosen == (phandle) -1) { - chosen = finddevice("/chosen@0"); - if (chosen == (phandle) -1) { - printf("no chosen\n"); - return 0; - } - } - if (getprop(chosen, "mmu", &chosen_mmu, sizeof(chosen_mmu)) <= 0) { - printf("no mmu\n"); - return 0; - } - memory = (ihandle) call_prom("open", 1, 1, "/memory"); - if (memory == (ihandle) -1) { - memory = (ihandle) call_prom("open", 1, 1, "/memory@0"); - if (memory == (ihandle) -1) { - printf("no memory node\n"); - return 0; - } - } - printf("old OF detected\r\n"); - return 1; -} - -static void *claim(unsigned long virt, unsigned long size, unsigned long align) -{ - int ret; - unsigned int result; - - if (need_map < 0) - need_map = check_of_version(); - if (align || !need_map) - return (void *) call_prom("claim", 3, 1, virt, size, align); - - ret = call_prom_ret("call-method", 5, 2, &result, "claim", memory, - align, size, virt); - if (ret != 0 || result == -1) - return (void *) -1; - ret = call_prom_ret("call-method", 5, 2, &result, "claim", chosen_mmu, - align, size, virt); - /* 0x12 == coherent + read/write */ - ret = call_prom("call-method", 6, 1, "map", chosen_mmu, - 0x12, size, virt, virt); - return (void *) virt; -} - static void *of_try_claim(unsigned long size) { unsigned long addr = 0; @@ -184,7 +39,7 @@ static void *of_try_claim(unsigned long size) #ifdef DEBUG printf(" trying: 0x%08lx\n\r", claim_base); #endif - addr = (unsigned long)claim(claim_base, size, 0); + addr = (unsigned long)of_claim(claim_base, size, 0); if ((void *)addr != (void *)-1) break; } @@ -208,64 +63,6 @@ static void of_image_hdr(const void *hdr) } } -static void *of_vmlinux_alloc(unsigned long size) -{ - void *p = malloc(size); - - if (!p) - fatal("Can't allocate memory for kernel image!\n\r"); - - return p; -} - -static void of_exit(void) -{ - call_prom("exit", 0, 0); -} - -/* - * OF device tree routines - */ -static void *of_finddevice(const char *name) -{ - return (phandle) call_prom("finddevice", 1, 1, name); -} - -static int of_getprop(const void *phandle, const char *name, void *buf, - const int buflen) -{ - return call_prom("getprop", 4, 1, phandle, name, buf, buflen); -} - -static int of_setprop(const void *phandle, const char *name, const void *buf, - const int buflen) -{ - return call_prom("setprop", 4, 1, phandle, name, buf, buflen); -} - -/* - * OF console routines - */ -static void *of_stdout_handle; - -static int of_console_open(void) -{ - void *devp; - - if (((devp = finddevice("/chosen")) != NULL) - && (getprop(devp, "stdout", &of_stdout_handle, - sizeof(of_stdout_handle)) - == sizeof(of_stdout_handle))) - return 0; - - return -1; -} - -static void of_console_write(char *buf, int len) -{ - call_prom("write", 3, 1, of_stdout_handle, buf, len); -} - void platform_init(unsigned long a1, unsigned long a2, void *promptr) { platform_ops.image_hdr = of_image_hdr; @@ -277,10 +74,9 @@ void platform_init(unsigned long a1, unsigned long a2, void *promptr) dt_ops.getprop = of_getprop; dt_ops.setprop = of_setprop; - console_ops.open = of_console_open; - console_ops.write = of_console_write; + of_console_init(); - prom = (int (*)(void *))promptr; + of_init(promptr); loader_info.promptr = promptr; if (a1 && a2 && a2 != 0xdeadbeef) { loader_info.initrd_addr = a1; diff --git a/arch/powerpc/boot/of.h b/arch/powerpc/boot/of.h new file mode 100644 index 000000000000..e4c68f7391c5 --- /dev/null +++ b/arch/powerpc/boot/of.h @@ -0,0 +1,21 @@ +#ifndef _PPC_BOOT_OF_H_ +#define _PPC_BOOT_OF_H_ + +typedef void *phandle; +typedef void *ihandle; + +void of_init(void *promptr); +int of_call_prom(const char *service, int nargs, int nret, ...); +void *of_claim(unsigned long virt, unsigned long size, unsigned long align); +void *of_vmlinux_alloc(unsigned long size); +void of_exit(void); +void *of_finddevice(const char *name); +int of_getprop(const void *phandle, const char *name, void *buf, + const int buflen); +int of_setprop(const void *phandle, const char *name, const void *buf, + const int buflen); + +/* Console functions */ +void of_console_init(void); + +#endif /* _PPC_BOOT_OF_H_ */ diff --git a/arch/powerpc/boot/ofconsole.c b/arch/powerpc/boot/ofconsole.c new file mode 100644 index 000000000000..ce0e02424453 --- /dev/null +++ b/arch/powerpc/boot/ofconsole.c @@ -0,0 +1,45 @@ +/* + * OF console routines + * + * Copyright (C) Paul Mackerras 1997. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include +#include "types.h" +#include "elf.h" +#include "string.h" +#include "stdio.h" +#include "page.h" +#include "ops.h" + +#include "of.h" + +static void *of_stdout_handle; + +static int of_console_open(void) +{ + void *devp; + + if (((devp = of_finddevice("/chosen")) != NULL) + && (of_getprop(devp, "stdout", &of_stdout_handle, + sizeof(of_stdout_handle)) + == sizeof(of_stdout_handle))) + return 0; + + return -1; +} + +static void of_console_write(const char *buf, int len) +{ + of_call_prom("write", 3, 1, of_stdout_handle, buf, len); +} + +void of_console_init(void) +{ + console_ops.open = of_console_open; + console_ops.write = of_console_write; +} diff --git a/arch/powerpc/boot/oflib.c b/arch/powerpc/boot/oflib.c new file mode 100644 index 000000000000..95b8fd69a403 --- /dev/null +++ b/arch/powerpc/boot/oflib.c @@ -0,0 +1,202 @@ +/* + * Copyright (C) Paul Mackerras 1997. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include +#include "types.h" +#include "elf.h" +#include "string.h" +#include "stdio.h" +#include "page.h" +#include "ops.h" + +#include "of.h" + +static int (*prom) (void *); + +void of_init(void *promptr) +{ + prom = (int (*)(void *))promptr; +} + +int of_call_prom(const char *service, int nargs, int nret, ...) +{ + int i; + struct prom_args { + const char *service; + int nargs; + int nret; + unsigned int args[12]; + } args; + va_list list; + + args.service = service; + args.nargs = nargs; + args.nret = nret; + + va_start(list, nret); + for (i = 0; i < nargs; i++) + args.args[i] = va_arg(list, unsigned int); + va_end(list); + + for (i = 0; i < nret; i++) + args.args[nargs+i] = 0; + + if (prom(&args) < 0) + return -1; + + return (nret > 0)? args.args[nargs]: 0; +} + +static int of_call_prom_ret(const char *service, int nargs, int nret, + unsigned int *rets, ...) +{ + int i; + struct prom_args { + const char *service; + int nargs; + int nret; + unsigned int args[12]; + } args; + va_list list; + + args.service = service; + args.nargs = nargs; + args.nret = nret; + + va_start(list, rets); + for (i = 0; i < nargs; i++) + args.args[i] = va_arg(list, unsigned int); + va_end(list); + + for (i = 0; i < nret; i++) + args.args[nargs+i] = 0; + + if (prom(&args) < 0) + return -1; + + if (rets != (void *) 0) + for (i = 1; i < nret; ++i) + rets[i-1] = args.args[nargs+i]; + + return (nret > 0)? args.args[nargs]: 0; +} + +/* returns true if s2 is a prefix of s1 */ +static int string_match(const char *s1, const char *s2) +{ + for (; *s2; ++s2) + if (*s1++ != *s2) + return 0; + return 1; +} + +/* + * Older OF's require that when claiming a specific range of addresses, + * we claim the physical space in the /memory node and the virtual + * space in the chosen mmu node, and then do a map operation to + * map virtual to physical. + */ +static int need_map = -1; +static ihandle chosen_mmu; +static phandle memory; + +static int check_of_version(void) +{ + phandle oprom, chosen; + char version[64]; + + oprom = of_finddevice("/openprom"); + if (oprom == (phandle) -1) + return 0; + if (of_getprop(oprom, "model", version, sizeof(version)) <= 0) + return 0; + version[sizeof(version)-1] = 0; + printf("OF version = '%s'\r\n", version); + if (!string_match(version, "Open Firmware, 1.") + && !string_match(version, "FirmWorks,3.")) + return 0; + chosen = of_finddevice("/chosen"); + if (chosen == (phandle) -1) { + chosen = of_finddevice("/chosen@0"); + if (chosen == (phandle) -1) { + printf("no chosen\n"); + return 0; + } + } + if (of_getprop(chosen, "mmu", &chosen_mmu, sizeof(chosen_mmu)) <= 0) { + printf("no mmu\n"); + return 0; + } + memory = (ihandle) of_call_prom("open", 1, 1, "/memory"); + if (memory == (ihandle) -1) { + memory = (ihandle) of_call_prom("open", 1, 1, "/memory@0"); + if (memory == (ihandle) -1) { + printf("no memory node\n"); + return 0; + } + } + printf("old OF detected\r\n"); + return 1; +} + +void *of_claim(unsigned long virt, unsigned long size, unsigned long align) +{ + int ret; + unsigned int result; + + if (need_map < 0) + need_map = check_of_version(); + if (align || !need_map) + return (void *) of_call_prom("claim", 3, 1, virt, size, align); + + ret = of_call_prom_ret("call-method", 5, 2, &result, "claim", memory, + align, size, virt); + if (ret != 0 || result == -1) + return (void *) -1; + ret = of_call_prom_ret("call-method", 5, 2, &result, "claim", chosen_mmu, + align, size, virt); + /* 0x12 == coherent + read/write */ + ret = of_call_prom("call-method", 6, 1, "map", chosen_mmu, + 0x12, size, virt, virt); + return (void *) virt; +} + +void *of_vmlinux_alloc(unsigned long size) +{ + void *p = malloc(size); + + if (!p) + fatal("Can't allocate memory for kernel image!\n\r"); + + return p; +} + +void of_exit(void) +{ + of_call_prom("exit", 0, 0); +} + +/* + * OF device tree routines + */ +void *of_finddevice(const char *name) +{ + return (phandle) of_call_prom("finddevice", 1, 1, name); +} + +int of_getprop(const void *phandle, const char *name, void *buf, + const int buflen) +{ + return of_call_prom("getprop", 4, 1, phandle, name, buf, buflen); +} + +int of_setprop(const void *phandle, const char *name, const void *buf, + const int buflen) +{ + return of_call_prom("setprop", 4, 1, phandle, name, buf, buflen); +} diff --git a/arch/powerpc/boot/ops.h b/arch/powerpc/boot/ops.h index 959124f3f9af..86077066cd7c 100644 --- a/arch/powerpc/boot/ops.h +++ b/arch/powerpc/boot/ops.h @@ -19,6 +19,8 @@ #define MAX_PATH_LEN 256 #define MAX_PROP_LEN 256 /* What should this be? */ +typedef void (*kernel_entry_t)(unsigned long r3, unsigned long r4, void *r5); + /* Platform specific operations */ struct platform_ops { void (*fixups)(void); @@ -51,7 +53,7 @@ extern struct dt_ops dt_ops; /* Console operations */ struct console_ops { int (*open)(void); - void (*write)(char *buf, int len); + void (*write)(const char *buf, int len); void (*edit_cmdline)(char *buf, int len); void (*close)(void); void *data; diff --git a/arch/powerpc/boot/ps3-head.S b/arch/powerpc/boot/ps3-head.S new file mode 100644 index 000000000000..1a6d64a68df5 --- /dev/null +++ b/arch/powerpc/boot/ps3-head.S @@ -0,0 +1,80 @@ +/* + * PS3 bootwrapper entry. + * + * Copyright (C) 2007 Sony Computer Entertainment Inc. + * Copyright 2007 Sony Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ppc_asm.h" + + .text + +/* + * __system_reset_overlay - The PS3 first stage entry. + * + * The bootwraper build script copies the 0x100 bytes at symbol + * __system_reset_overlay to offset 0x100 of the rom image. + * + * The PS3 has a single processor with two threads. + */ + + .globl __system_reset_overlay +__system_reset_overlay: + + /* Switch to 32-bit mode. */ + + mfmsr r9 + clrldi r9,r9,1 + mtmsrd r9 + nop + + /* Get thread number in r3 and branch. */ + + mfspr r3, 0x88 + cntlzw. r3, r3 + li r4, 0 + li r5, 0 + beq 1f + + /* Secondary goes to __secondary_hold in kernel. */ + + li r4, 0x60 + mtctr r4 + bctr + + /* Primary delays then goes to _zimage_start in wrapper. */ +1: + or 31, 31, 31 /* db16cyc */ + or 31, 31, 31 /* db16cyc */ + + lis r4, _zimage_start@ha + addi r4, r4, _zimage_start@l + mtctr r4 + bctr + +/* + * __system_reset_kernel - Place holder for the kernel reset vector. + * + * The bootwrapper build script copies 0x100 bytes from offset 0x100 + * of the rom image to the symbol __system_reset_kernel. At runtime + * the bootwrapper program copies the 0x100 bytes at __system_reset_kernel + * to ram address 0x100. This symbol must occupy 0x100 bytes. + */ + + .globl __system_reset_kernel +__system_reset_kernel: + + . = __system_reset_kernel + 0x100 diff --git a/arch/powerpc/boot/ps3-hvcall.S b/arch/powerpc/boot/ps3-hvcall.S new file mode 100644 index 000000000000..c8b7df3210d1 --- /dev/null +++ b/arch/powerpc/boot/ps3-hvcall.S @@ -0,0 +1,184 @@ +/* + * PS3 bootwrapper hvcalls. + * + * Copyright (C) 2007 Sony Computer Entertainment Inc. + * Copyright 2007 Sony Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ppc_asm.h" + +/* + * The PS3 hypervisor uses a 64 bit "C" language calling convention. + * The routines here marshal arguments between the 32 bit wrapper + * program and the 64 bit hvcalls. + * + * wrapper lv1 + * 32-bit (h,l) 64-bit + * + * 1: r3,r4 <-> r3 + * 2: r5,r6 <-> r4 + * 3: r7,r8 <-> r5 + * 4: r9,r10 <-> r6 + * 5: 8(r1),12(r1) <-> r7 + * 6: 16(r1),20(r1) <-> r8 + * 7: 24(r1),28(r1) <-> r9 + * 8: 32(r1),36(r1) <-> r10 + * + */ + +.macro GLOBAL name + .section ".text" + .balign 4 + .globl \name +\name: +.endm + +.macro NO_SUPPORT name + GLOBAL \name + b ps3_no_support +.endm + +.macro HVCALL num + li r11, \num + .long 0x44000022 + extsw r3, r3 +.endm + +.macro SAVE_LR offset=4 + mflr r0 + stw r0, \offset(r1) +.endm + +.macro LOAD_LR offset=4 + lwz r0, \offset(r1) + mtlr r0 +.endm + +.macro LOAD_64_REG target,high,low + sldi r11, \high, 32 + or \target, r11, \low +.endm + +.macro LOAD_64_STACK target,offset + ld \target, \offset(r1) +.endm + +.macro LOAD_R3 + LOAD_64_REG r3,r3,r4 +.endm + +.macro LOAD_R4 + LOAD_64_REG r4,r5,r6 +.endm + +.macro LOAD_R5 + LOAD_64_REG r5,r7,r8 +.endm + +.macro LOAD_R6 + LOAD_64_REG r6,r9,r10 +.endm + +.macro LOAD_R7 + LOAD_64_STACK r7,8 +.endm + +.macro LOAD_R8 + LOAD_64_STACK r8,16 +.endm + +.macro LOAD_R9 + LOAD_64_STACK r9,24 +.endm + +.macro LOAD_R10 + LOAD_64_STACK r10,32 +.endm + +.macro LOAD_REGS_0 + stwu 1,-16(1) + stw 3, 8(1) +.endm + +.macro LOAD_REGS_5 + LOAD_R3 + LOAD_R4 + LOAD_R5 + LOAD_R6 + LOAD_R7 +.endm + +.macro LOAD_REGS_6 + LOAD_REGS_5 + LOAD_R8 +.endm + +.macro LOAD_REGS_8 + LOAD_REGS_6 + LOAD_R9 + LOAD_R10 +.endm + +.macro STORE_REGS_0_1 + lwz r11, 8(r1) + std r4, 0(r11) + mr r4, r3 + li r3, 0 + addi r1,r1,16 +.endm + +.macro STORE_REGS_5_2 + lwz r11, 16(r1) + std r4, 0(r11) + lwz r11, 24(r1) + std r5, 0(r11) +.endm + +.macro STORE_REGS_6_1 + lwz r11, 24(r1) + std r4, 0(r11) +.endm + +GLOBAL lv1_get_logical_ppe_id + SAVE_LR + LOAD_REGS_0 + HVCALL 69 + STORE_REGS_0_1 + LOAD_LR + blr + +GLOBAL lv1_get_logical_partition_id + SAVE_LR + LOAD_REGS_0 + HVCALL 74 + STORE_REGS_0_1 + LOAD_LR + blr + +GLOBAL lv1_get_repository_node_value + SAVE_LR + LOAD_REGS_5 + HVCALL 91 + STORE_REGS_5_2 + LOAD_LR + blr + +GLOBAL lv1_panic + SAVE_LR + LOAD_REGS_8 + HVCALL 255 + LOAD_LR + blr diff --git a/arch/powerpc/boot/ps3.c b/arch/powerpc/boot/ps3.c new file mode 100644 index 000000000000..893d59339c26 --- /dev/null +++ b/arch/powerpc/boot/ps3.c @@ -0,0 +1,161 @@ +/* + * PS3 bootwrapper support. + * + * Copyright (C) 2007 Sony Computer Entertainment Inc. + * Copyright 2007 Sony Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include "types.h" +#include "elf.h" +#include "string.h" +#include "stdio.h" +#include "page.h" +#include "ops.h" + +extern s64 lv1_panic(u64 in_1); +extern s64 lv1_get_logical_partition_id(u64 *out_1); +extern s64 lv1_get_logical_ppe_id(u64 *out_1); +extern s64 lv1_get_repository_node_value(u64 in_1, u64 in_2, u64 in_3, + u64 in_4, u64 in_5, u64 *out_1, u64 *out_2); + +#ifdef DEBUG +#define DBG(fmt...) printf(fmt) +#else +static inline int __attribute__ ((format (printf, 1, 2))) DBG( + const char *fmt, ...) {return 0;} +#endif + +BSS_STACK(4096); + +/* A buffer that may be edited by tools operating on a zImage binary so as to + * edit the command line passed to vmlinux (by setting /chosen/bootargs). + * The buffer is put in it's own section so that tools may locate it easier. + */ +static char cmdline[COMMAND_LINE_SIZE] + __attribute__((__section__("__builtin_cmdline"))); + +static void prep_cmdline(void *chosen) +{ + if (cmdline[0] == '\0') + getprop(chosen, "bootargs", cmdline, COMMAND_LINE_SIZE-1); + else + setprop_str(chosen, "bootargs", cmdline); + + printf("cmdline: '%s'\n", cmdline); +} + +static void ps3_console_write(const char *buf, int len) +{ +} + +static void ps3_exit(void) +{ + printf("ps3_exit\n"); + + /* lv1_panic will shutdown the lpar. */ + + lv1_panic(0); /* zero = do not reboot */ + while (1); +} + +static int ps3_repository_read_rm_size(u64 *rm_size) +{ + s64 result; + u64 lpar_id; + u64 ppe_id; + u64 v2; + + result = lv1_get_logical_partition_id(&lpar_id); + + if (result) + return -1; + + result = lv1_get_logical_ppe_id(&ppe_id); + + if (result) + return -1; + + /* + * n1: 0000000062690000 : ....bi.. + * n2: 7075000000000000 : pu...... + * n3: 0000000000000001 : ........ + * n4: 726d5f73697a6500 : rm_size. + */ + + result = lv1_get_repository_node_value(lpar_id, 0x0000000062690000ULL, + 0x7075000000000000ULL, ppe_id, 0x726d5f73697a6500ULL, rm_size, + &v2); + + printf("%s:%d: ppe_id %lu \n", __func__, __LINE__, + (unsigned long)ppe_id); + printf("%s:%d: lpar_id %lu \n", __func__, __LINE__, + (unsigned long)lpar_id); + printf("%s:%d: rm_size %llxh \n", __func__, __LINE__, *rm_size); + + return result ? -1 : 0; +} + +void ps3_copy_vectors(void) +{ + extern char __system_reset_kernel[]; + + memcpy((void *)0x100, __system_reset_kernel, 0x100); + flush_cache((void *)0x100, 0x100); +} + +void platform_init(void) +{ + extern char _end[]; + extern char _dtb_start[]; + extern char _initrd_start[]; + extern char _initrd_end[]; + const u32 heapsize = 0x1000000 - (u32)_end; /* 16MiB */ + void *chosen; + unsigned long ft_addr; + u64 rm_size; + + console_ops.write = ps3_console_write; + platform_ops.exit = ps3_exit; + + printf("\n-- PS3 bootwrapper --\n"); + + simple_alloc_init(_end, heapsize, 32, 64); + ft_init(_dtb_start, 0, 4); + + chosen = finddevice("/chosen"); + + ps3_repository_read_rm_size(&rm_size); + dt_fixup_memory(0, rm_size); + + if (_initrd_end > _initrd_start) { + setprop_val(chosen, "linux,initrd-start", (u32)(_initrd_start)); + setprop_val(chosen, "linux,initrd-end", (u32)(_initrd_end)); + } + + prep_cmdline(chosen); + + ft_addr = dt_ops.finalize(); + + ps3_copy_vectors(); + + printf(" flat tree at 0x%lx\n\r", ft_addr); + + ((kernel_entry_t)0)(ft_addr, 0, NULL); + + ps3_exit(); +} diff --git a/arch/powerpc/boot/serial.c b/arch/powerpc/boot/serial.c index 7fd32330a9a5..eaa0d3ae3518 100644 --- a/arch/powerpc/boot/serial.c +++ b/arch/powerpc/boot/serial.c @@ -27,7 +27,7 @@ static int serial_open(void) return scdp->open(); } -static void serial_write(char *buf, int len) +static void serial_write(const char *buf, int len) { struct serial_console_data *scdp = console_ops.data; diff --git a/arch/powerpc/boot/stdio.c b/arch/powerpc/boot/stdio.c index 0a9feeb98342..5b57800bbc67 100644 --- a/arch/powerpc/boot/stdio.c +++ b/arch/powerpc/boot/stdio.c @@ -190,7 +190,11 @@ int vsprintf(char *buf, const char *fmt, va_list args) /* get the conversion qualifier */ qualifier = -1; - if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || *fmt =='Z') { + if (*fmt == 'l' && *(fmt + 1) == 'l') { + qualifier = 'q'; + fmt += 2; + } else if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' + || *fmt == 'Z') { qualifier = *fmt; ++fmt; } @@ -281,6 +285,10 @@ int vsprintf(char *buf, const char *fmt, va_list args) num = va_arg(args, unsigned long); if (flags & SIGN) num = (signed long) num; + } else if (qualifier == 'q') { + num = va_arg(args, unsigned long long); + if (flags & SIGN) + num = (signed long long) num; } else if (qualifier == 'Z') { num = va_arg(args, size_t); } else if (qualifier == 'h') { diff --git a/arch/powerpc/boot/types.h b/arch/powerpc/boot/types.h index 79d26e708677..31393d17a9c1 100644 --- a/arch/powerpc/boot/types.h +++ b/arch/powerpc/boot/types.h @@ -7,6 +7,10 @@ typedef unsigned char u8; typedef unsigned short u16; typedef unsigned int u32; typedef unsigned long long u64; +typedef signed char s8; +typedef short s16; +typedef int s32; +typedef long long s64; #define min(x,y) ({ \ typeof(x) _x = (x); \ diff --git a/arch/powerpc/boot/wrapper b/arch/powerpc/boot/wrapper index da77adc73078..65f685479175 100755 --- a/arch/powerpc/boot/wrapper +++ b/arch/powerpc/boot/wrapper @@ -144,6 +144,15 @@ miboot|uboot) cuboot*) gzip= ;; +ps3) + platformo="$object/ps3-head.o $object/ps3-hvcall.o $object/ps3.o" + lds=$object/zImage.ps3.lds + gzip= + ext=bin + objflags="-O binary --set-section-flags=.bss=contents,alloc,load,data" + ksection=.kernel:vmlinux.bin + isection=.kernel:initrd + ;; esac vmz="$tmpdir/`basename \"$kernel\"`.$ext" @@ -239,4 +248,50 @@ treeboot*) fi exit 0 ;; +ps3) + # The ps3's loader supports loading gzipped binary images from flash + # rom to addr zero. The loader enters the image at addr 0x100. A + # bootwrapper overlay is use to arrange for the kernel to be loaded + # to addr zero and to have a suitable bootwrapper entry at 0x100. + # To construct the rom image, 0x100 bytes from offset 0x100 in the + # kernel is copied to the bootwrapper symbol __system_reset_kernel. + # The 0x100 bytes at the bootwrapper symbol __system_reset_overlay is + # then copied to offset 0x100. At runtime the bootwrapper program + # copies the 0x100 bytes at __system_reset_kernel to addr 0x100. + + system_reset_overlay=0x`${CROSS}nm "$ofile" \ + | grep ' __system_reset_overlay$' \ + | cut -d' ' -f1` + system_reset_overlay=`printf "%d" $system_reset_overlay` + system_reset_kernel=0x`${CROSS}nm "$ofile" \ + | grep ' __system_reset_kernel$' \ + | cut -d' ' -f1` + system_reset_kernel=`printf "%d" $system_reset_kernel` + overlay_dest="256" + overlay_size="256" + + rm -f "$object/otheros.bld" + + ${CROSS}objcopy -O binary "$ofile" "$ofile.bin" + + msg=$(dd if="$ofile.bin" of="$ofile.bin" conv=notrunc \ + skip=$overlay_dest seek=$system_reset_kernel \ + count=$overlay_size bs=1 2>&1) + + if [ $? -ne "0" ]; then + echo $msg + exit 1 + fi + + msg=$(dd if="$ofile.bin" of="$ofile.bin" conv=notrunc \ + skip=$system_reset_overlay seek=$overlay_dest \ + count=$overlay_size bs=1 2>&1) + + if [ $? -ne "0" ]; then + echo $msg + exit 2 + fi + + gzip --force -9 --stdout "$ofile.bin" > "$object/otheros.bld" + ;; esac diff --git a/arch/powerpc/boot/zImage.ps3.lds.S b/arch/powerpc/boot/zImage.ps3.lds.S new file mode 100644 index 000000000000..aaa469c1e60d --- /dev/null +++ b/arch/powerpc/boot/zImage.ps3.lds.S @@ -0,0 +1,50 @@ +OUTPUT_ARCH(powerpc:common) +ENTRY(_zimage_start) +EXTERN(_zimage_start) +SECTIONS +{ + _vmlinux_start = .; + .kernel:vmlinux.bin : { *(.kernel:vmlinux.bin) } + _vmlinux_end = .; + + . = ALIGN(4096); + _dtb_start = .; + .kernel:dtb : { *(.kernel:dtb) } + _dtb_end = .; + + . = ALIGN(4096); + _initrd_start = .; + .kernel:initrd : { *(.kernel:initrd) } + _initrd_end = .; + + _start = .; + .text : + { + *(.text) + *(.fixup) + } + _etext = .; + . = ALIGN(4096); + .data : + { + *(.rodata*) + *(.data*) + *(.sdata*) + __got2_start = .; + *(.got2) + __got2_end = .; + } + + . = ALIGN(4096); + _edata = .; + + . = ALIGN(4096); + __bss_start = .; + .bss : + { + *(.sbss) + *(.bss) + } + . = ALIGN(4096); + _end = . ; +} diff --git a/arch/powerpc/configs/holly_defconfig b/arch/powerpc/configs/holly_defconfig index 32781849ad4c..04b94f884aae 100644 --- a/arch/powerpc/configs/holly_defconfig +++ b/arch/powerpc/configs/holly_defconfig @@ -190,10 +190,12 @@ CONFIG_SPLIT_PTLOCK_CPUS=4 # CONFIG_RESOURCES_64BIT is not set CONFIG_ZONE_DMA_FLAG=1 CONFIG_PROC_DEVICETREE=y -# CONFIG_CMDLINE_BOOL is not set +CONFIG_CMDLINE_BOOL=y +CONFIG_CMDLINE="console=ttyS0,115200" # CONFIG_PM is not set # CONFIG_SECCOMP is not set -# CONFIG_WANT_DEVICE_TREE is not set +CONFIG_WANT_DEVICE_TREE=y +CONFIG_DEVICE_TREE="holly.dts" CONFIG_ISA_DMA_API=y # diff --git a/arch/powerpc/configs/ps3_defconfig b/arch/powerpc/configs/ps3_defconfig index 956d1df61e0f..d0b43df44426 100644 --- a/arch/powerpc/configs/ps3_defconfig +++ b/arch/powerpc/configs/ps3_defconfig @@ -156,7 +156,11 @@ CONFIG_PS3_HTAB_SIZE=20 CONFIG_PS3_USE_LPAR_ADDR=y CONFIG_PS3_VUART=y CONFIG_PS3_PS3AV=y -CONFIG_PS3_SYS_MANAGER=y +CONFIG_PS3_SYS_MANAGER=m +CONFIG_PS3_STORAGE=y +CONFIG_PS3_DISK=y +CONFIG_PS3_ROM=y +CONFIG_PS3_FLASH=y CONFIG_PPC_CELL=y # CONFIG_PPC_CELL_NATIVE is not set # CONFIG_PPC_IBM_CELL_BLADE is not set @@ -335,7 +339,7 @@ CONFIG_BT=m CONFIG_BT_L2CAP=m CONFIG_BT_SCO=m CONFIG_BT_RFCOMM=m -# CONFIG_BT_RFCOMM_TTY is not set +CONFIG_BT_RFCOMM_TTY=y # CONFIG_BT_BNEP is not set CONFIG_BT_HIDP=m @@ -344,7 +348,9 @@ CONFIG_BT_HIDP=m # CONFIG_BT_HCIUSB=m CONFIG_BT_HCIUSB_SCO=y -# CONFIG_BT_HCIUART is not set +CONFIG_BT_HCIUART=m +CONFIG_BT_HCIUART_H4=y +CONFIG_BT_HCIUART_BCSP=y # CONFIG_BT_HCIBCM203X is not set # CONFIG_BT_HCIBPA10X is not set # CONFIG_BT_HCIBFUSB is not set @@ -435,7 +441,7 @@ CONFIG_CHR_DEV_SG=m # # Some SCSI devices (e.g. CD jukebox) support multiple LUNs # -# CONFIG_SCSI_MULTI_LUN is not set +CONFIG_SCSI_MULTI_LUN=y # CONFIG_SCSI_CONSTANTS is not set # CONFIG_SCSI_LOGGING is not set # CONFIG_SCSI_SCAN_ASYNC is not set @@ -479,6 +485,7 @@ CONFIG_NETDEVICES=y CONFIG_MII=m CONFIG_NETDEV_1000=y CONFIG_NETDEV_10000=y +CONFIG_GELIC_NET=y # # Wireless LAN @@ -546,7 +553,27 @@ CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 # # CONFIG_INPUT_KEYBOARD is not set # CONFIG_INPUT_MOUSE is not set -# CONFIG_INPUT_JOYSTICK is not set +CONFIG_INPUT_JOYSTICK=y +# CONFIG_JOYSTICK_ANALOG is not set +# CONFIG_JOYSTICK_A3D is not set +# CONFIG_JOYSTICK_ADI is not set +# CONFIG_JOYSTICK_COBRA is not set +# CONFIG_JOYSTICK_GF2K is not set +# CONFIG_JOYSTICK_GRIP is not set +# CONFIG_JOYSTICK_GRIP_MP is not set +# CONFIG_JOYSTICK_GUILLEMOT is not set +# CONFIG_JOYSTICK_INTERACT is not set +# CONFIG_JOYSTICK_SIDEWINDER is not set +# CONFIG_JOYSTICK_TMDC is not set +# CONFIG_JOYSTICK_IFORCE is not set +# CONFIG_JOYSTICK_WARRIOR is not set +# CONFIG_JOYSTICK_MAGELLAN is not set +# CONFIG_JOYSTICK_SPACEORB is not set +# CONFIG_JOYSTICK_SPACEBALL is not set +# CONFIG_JOYSTICK_STINGER is not set +# CONFIG_JOYSTICK_TWIDJOY is not set +# CONFIG_JOYSTICK_JOYDUMP is not set +# CONFIG_JOYSTICK_XPAD is not set # CONFIG_INPUT_TABLET is not set # CONFIG_INPUT_TOUCHSCREEN is not set # CONFIG_INPUT_MISC is not set @@ -563,7 +590,7 @@ CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 CONFIG_VT=y CONFIG_VT_CONSOLE=y CONFIG_HW_CONSOLE=y -# CONFIG_VT_HW_CONSOLE_BINDING is not set +CONFIG_VT_HW_CONSOLE_BINDING=y # CONFIG_SERIAL_NONSTANDARD is not set # @@ -1086,7 +1113,7 @@ CONFIG_HAS_DMA=y # # CONFIG_PRINTK_TIME is not set CONFIG_ENABLE_MUST_CHECK=y -# CONFIG_MAGIC_SYSRQ is not set +CONFIG_MAGIC_SYSRQ=y # CONFIG_UNUSED_SYMBOLS is not set # CONFIG_DEBUG_FS is not set # CONFIG_HEADERS_CHECK is not set @@ -1116,16 +1143,7 @@ CONFIG_DEBUG_STACKOVERFLOW=y # CONFIG_DEBUGGER is not set CONFIG_IRQSTACKS=y # CONFIG_BOOTX_TEXT is not set -CONFIG_PPC_EARLY_DEBUG=y -# CONFIG_PPC_EARLY_DEBUG_LPAR is not set -# CONFIG_PPC_EARLY_DEBUG_G5 is not set -# CONFIG_PPC_EARLY_DEBUG_RTAS_PANEL is not set -# CONFIG_PPC_EARLY_DEBUG_RTAS_CONSOLE is not set -# CONFIG_PPC_EARLY_DEBUG_MAPLE is not set -# CONFIG_PPC_EARLY_DEBUG_ISERIES is not set -# CONFIG_PPC_EARLY_DEBUG_PAS_REALMODE is not set -# CONFIG_PPC_EARLY_DEBUG_BEAT is not set -# CONFIG_PPC_EARLY_DEBUG_44x is not set +# CONFIG_PPC_EARLY_DEBUG is not set # # Security options diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile index 3e779f07f21b..42c42ecad00c 100644 --- a/arch/powerpc/kernel/Makefile +++ b/arch/powerpc/kernel/Makefile @@ -12,7 +12,8 @@ endif obj-y := semaphore.o cputable.o ptrace.o syscalls.o \ irq.o align.o signal_32.o pmc.o vdso.o \ - init_task.o process.o systbl.o idle.o + init_task.o process.o systbl.o idle.o \ + signal.o obj-y += vdso32/ obj-$(CONFIG_PPC64) += setup_64.o binfmt_elf32.o sys_ppc32.o \ signal_64.o ptrace32.o \ @@ -65,9 +66,9 @@ obj-$(CONFIG_PPC_UDBG_16550) += legacy_serial.o udbg_16550.o module-$(CONFIG_PPC64) += module_64.o obj-$(CONFIG_MODULES) += $(module-y) -pci64-$(CONFIG_PPC64) += pci_64.o pci_dn.o +pci64-$(CONFIG_PPC64) += pci_64.o pci_dn.o isa-bridge.o pci32-$(CONFIG_PPC32) := pci_32.o -obj-$(CONFIG_PCI) += $(pci64-y) $(pci32-y) +obj-$(CONFIG_PCI) += $(pci64-y) $(pci32-y) pci-common.o obj-$(CONFIG_PCI_MSI) += msi.o kexec-$(CONFIG_PPC64) := machine_kexec_64.o kexec-$(CONFIG_PPC32) := machine_kexec_32.o diff --git a/arch/powerpc/kernel/cputable.c b/arch/powerpc/kernel/cputable.c index b2b5d664d328..b1f8000952f3 100644 --- a/arch/powerpc/kernel/cputable.c +++ b/arch/powerpc/kernel/cputable.c @@ -294,6 +294,21 @@ static struct cpu_spec cpu_specs[] = { .oprofile_mmcra_sipr = MMCRA_SIPR, .platform = "power5", }, + { /* Power5++ */ + .pvr_mask = 0xffffff00, + .pvr_value = 0x003b0300, + .cpu_name = "POWER5+ (gs)", + .cpu_features = CPU_FTRS_POWER5, + .cpu_user_features = COMMON_USER_POWER5_PLUS, + .icache_bsize = 128, + .dcache_bsize = 128, + .num_pmcs = 6, + .oprofile_cpu_type = "ppc64/power5++", + .oprofile_type = PPC_OPROFILE_POWER4, + .oprofile_mmcra_sihv = MMCRA_SIHV, + .oprofile_mmcra_sipr = MMCRA_SIPR, + .platform = "power5+", + }, { /* Power5 GS */ .pvr_mask = 0xffff0000, .pvr_value = 0x003b0000, @@ -1178,8 +1193,8 @@ static struct cpu_spec cpu_specs[] = { .platform = "ppc440", }, { /* 440SP Rev. A */ - .pvr_mask = 0xff000fff, - .pvr_value = 0x53000891, + .pvr_mask = 0xfff00fff, + .pvr_value = 0x53200891, .cpu_name = "440SP Rev. A", .cpu_features = CPU_FTRS_44X, .cpu_user_features = COMMON_USER_BOOKE, @@ -1188,9 +1203,19 @@ static struct cpu_spec cpu_specs[] = { .platform = "ppc440", }, { /* 440SPe Rev. A */ - .pvr_mask = 0xff000fff, - .pvr_value = 0x53000890, - .cpu_name = "440SPe Rev. A", + .pvr_mask = 0xfff00fff, + .pvr_value = 0x53400890, + .cpu_name = "440SPe Rev. A", + .cpu_features = CPU_FTRS_44X, + .cpu_user_features = COMMON_USER_BOOKE, + .icache_bsize = 32, + .dcache_bsize = 32, + .platform = "ppc440", + }, + { /* 440SPe Rev. B */ + .pvr_mask = 0xfff00fff, + .pvr_value = 0x53400891, + .cpu_name = "440SPe Rev. B", .cpu_features = CPU_FTRS_44X, .cpu_user_features = COMMON_USER_BOOKE, .icache_bsize = 32, diff --git a/arch/powerpc/kernel/head_32.S b/arch/powerpc/kernel/head_32.S index c897203198b1..7d73a13450b0 100644 --- a/arch/powerpc/kernel/head_32.S +++ b/arch/powerpc/kernel/head_32.S @@ -9,7 +9,6 @@ * rewritten by Paul Mackerras. * Copyright (C) 1996 Paul Mackerras. * MPC8xx modifications Copyright (C) 1997 Dan Malek (dmalek@jlc.net). - * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). * * This file contains the low-level support and setup for the * PowerPC platform, including trap and interrupt dispatch. @@ -32,10 +31,6 @@ #include #include -#ifdef CONFIG_APUS -#include -#endif - /* 601 only have IBAT; cr0.eq is set on 601 when using this macro */ #define LOAD_BAT(n, reg, RA, RB) \ /* see the comment for clear_bats() -- Cort */ \ @@ -92,11 +87,6 @@ _start: * r4: virtual address of boot_infos_t * r5: 0 * - * APUS - * r3: 'APUS' - * r4: physical address of memory base - * Linux/m68k style BootInfo structure at &_end. - * * PREP * This is jumped to on prep systems right after the kernel is relocated * to its proper place in memory by the boot loader. The expected layout @@ -150,14 +140,6 @@ __start: */ bl early_init -#ifdef CONFIG_APUS -/* On APUS the __va/__pa constants need to be set to the correct - * values before continuing. - */ - mr r4,r30 - bl fix_mem_constants -#endif /* CONFIG_APUS */ - /* Switch MMU off, clear BATs and flush TLB. At this point, r3 contains * the physical address we are running at, returned by early_init() */ @@ -167,7 +149,7 @@ __after_mmu_off: bl flush_tlbs bl initial_bats -#if !defined(CONFIG_APUS) && defined(CONFIG_BOOTX_TEXT) +#if defined(CONFIG_BOOTX_TEXT) bl setup_disp_bat #endif @@ -183,7 +165,6 @@ __after_mmu_off: #endif /* CONFIG_6xx */ -#ifndef CONFIG_APUS /* * We need to run with _start at physical address 0. * On CHRP, we are loaded at 0x10000 since OF on CHRP uses @@ -196,7 +177,6 @@ __after_mmu_off: addis r4,r3,KERNELBASE@h /* current address of _start */ cmpwi 0,r4,0 /* are we already running at 0? */ bne relocate_kernel -#endif /* CONFIG_APUS */ /* * we now have the 1st 16M of ram mapped with the bats. * prep needs the mmu to be turned on here, but pmac already has it on. @@ -881,85 +861,6 @@ _GLOBAL(copy_and_flush) addi r6,r6,4 blr -#ifdef CONFIG_APUS -/* - * On APUS the physical base address of the kernel is not known at compile - * time, which means the __pa/__va constants used are incorrect. In the - * __init section is recorded the virtual addresses of instructions using - * these constants, so all that has to be done is fix these before - * continuing the kernel boot. - * - * r4 = The physical address of the kernel base. - */ -fix_mem_constants: - mr r10,r4 - addis r10,r10,-KERNELBASE@h /* virt_to_phys constant */ - neg r11,r10 /* phys_to_virt constant */ - - lis r12,__vtop_table_begin@h - ori r12,r12,__vtop_table_begin@l - add r12,r12,r10 /* table begin phys address */ - lis r13,__vtop_table_end@h - ori r13,r13,__vtop_table_end@l - add r13,r13,r10 /* table end phys address */ - subi r12,r12,4 - subi r13,r13,4 -1: lwzu r14,4(r12) /* virt address of instruction */ - add r14,r14,r10 /* phys address of instruction */ - lwz r15,0(r14) /* instruction, now insert top */ - rlwimi r15,r10,16,16,31 /* half of vp const in low half */ - stw r15,0(r14) /* of instruction and restore. */ - dcbst r0,r14 /* write it to memory */ - sync - icbi r0,r14 /* flush the icache line */ - cmpw r12,r13 - bne 1b - sync /* additional sync needed on g4 */ - isync - -/* - * Map the memory where the exception handlers will - * be copied to when hash constants have been patched. - */ -#ifdef CONFIG_APUS_FAST_EXCEPT - lis r8,0xfff0 -#else - lis r8,0 -#endif - ori r8,r8,0x2 /* 128KB, supervisor */ - mtspr SPRN_DBAT3U,r8 - mtspr SPRN_DBAT3L,r8 - - lis r12,__ptov_table_begin@h - ori r12,r12,__ptov_table_begin@l - add r12,r12,r10 /* table begin phys address */ - lis r13,__ptov_table_end@h - ori r13,r13,__ptov_table_end@l - add r13,r13,r10 /* table end phys address */ - subi r12,r12,4 - subi r13,r13,4 -1: lwzu r14,4(r12) /* virt address of instruction */ - add r14,r14,r10 /* phys address of instruction */ - lwz r15,0(r14) /* instruction, now insert top */ - rlwimi r15,r11,16,16,31 /* half of pv const in low half*/ - stw r15,0(r14) /* of instruction and restore. */ - dcbst r0,r14 /* write it to memory */ - sync - icbi r0,r14 /* flush the icache line */ - cmpw r12,r13 - bne 1b - - sync /* additional sync needed on g4 */ - isync /* No speculative loading until now */ - blr - -/*********************************************************************** - * Please note that on APUS the exception handlers are located at the - * physical address 0xfff0000. For this reason, the exception handlers - * cannot use relative branches to access the code below. - ***********************************************************************/ -#endif /* CONFIG_APUS */ - #ifdef CONFIG_SMP #ifdef CONFIG_GEMINI .globl __secondary_start_gemini @@ -1135,19 +1036,6 @@ start_here: bl __save_cpu_setup bl MMU_init -#ifdef CONFIG_APUS - /* Copy exception code to exception vector base on APUS. */ - lis r4,KERNELBASE@h -#ifdef CONFIG_APUS_FAST_EXCEPT - lis r3,0xfff0 /* Copy to 0xfff00000 */ -#else - lis r3,0 /* Copy to 0x00000000 */ -#endif - li r5,0x4000 /* # bytes of memory to copy */ - li r6,0 - bl copy_and_flush /* copy the first 0x4000 bytes */ -#endif /* CONFIG_APUS */ - /* * Go back to running unmapped so we can load up new values * for SDR1 (hash table pointer) and the segment registers @@ -1324,11 +1212,7 @@ initial_bats: #else ori r8,r8,2 /* R/W access */ #endif /* CONFIG_SMP */ -#ifdef CONFIG_APUS - ori r11,r11,BL_8M<<2|0x2 /* set up 8MB BAT registers for 604 */ -#else ori r11,r11,BL_256M<<2|0x2 /* set up BAT registers for 604 */ -#endif /* CONFIG_APUS */ mtspr SPRN_DBAT0L,r8 /* N.B. 6xx (not 601) have valid */ mtspr SPRN_DBAT0U,r11 /* bit in upper BAT register */ @@ -1338,7 +1222,7 @@ initial_bats: blr -#if !defined(CONFIG_APUS) && defined(CONFIG_BOOTX_TEXT) +#ifdef CONFIG_BOOTX_TEXT setup_disp_bat: /* * setup the display bat prepared for us in prom.c @@ -1362,7 +1246,7 @@ setup_disp_bat: 1: mtspr SPRN_IBAT3L,r8 mtspr SPRN_IBAT3U,r11 blr -#endif /* !defined(CONFIG_APUS) && defined(CONFIG_BOOTX_TEXT) */ +#endif /* CONFIG_BOOTX_TEXT */ #ifdef CONFIG_8260 /* Jump into the system reset for the rom. diff --git a/arch/powerpc/kernel/head_64.S b/arch/powerpc/kernel/head_64.S index 1111fcec7673..8cdd48ea4391 100644 --- a/arch/powerpc/kernel/head_64.S +++ b/arch/powerpc/kernel/head_64.S @@ -103,8 +103,8 @@ __secondary_hold_acknowledge: . = 0x60 /* - * The following code is used on pSeries to hold secondary processors - * in a spin loop after they have been freed from OpenFirmware, but + * The following code is used to hold secondary processors + * in a spin loop after they have entered the kernel, but * before the bulk of the kernel has been relocated. This code * is relocated to physical address 0x60 before prom_init is run. * All of it must fit below the first exception vector at 0x100. diff --git a/arch/powerpc/kernel/io.c b/arch/powerpc/kernel/io.c index 34ae11494ddc..e31aca9208eb 100644 --- a/arch/powerpc/kernel/io.c +++ b/arch/powerpc/kernel/io.c @@ -35,7 +35,7 @@ void _insb(const volatile u8 __iomem *port, void *buf, long count) asm volatile("sync"); do { tmp = *port; - asm volatile("eieio"); + eieio(); *tbuf++ = tmp; } while (--count != 0); asm volatile("twi 0,%0,0; isync" : : "r" (tmp)); @@ -66,7 +66,7 @@ void _insw_ns(const volatile u16 __iomem *port, void *buf, long count) asm volatile("sync"); do { tmp = *port; - asm volatile("eieio"); + eieio(); *tbuf++ = tmp; } while (--count != 0); asm volatile("twi 0,%0,0; isync" : : "r" (tmp)); @@ -97,7 +97,7 @@ void _insl_ns(const volatile u32 __iomem *port, void *buf, long count) asm volatile("sync"); do { tmp = *port; - asm volatile("eieio"); + eieio(); *tbuf++ = tmp; } while (--count != 0); asm volatile("twi 0,%0,0; isync" : : "r" (tmp)); @@ -155,21 +155,21 @@ void _memcpy_fromio(void *dest, const volatile void __iomem *src, __asm__ __volatile__ ("sync" : : : "memory"); while(n && (!IO_CHECK_ALIGN(vsrc, 4) || !IO_CHECK_ALIGN(dest, 4))) { *((u8 *)dest) = *((volatile u8 *)vsrc); - __asm__ __volatile__ ("eieio" : : : "memory"); + eieio(); vsrc++; dest++; n--; } while(n > 4) { *((u32 *)dest) = *((volatile u32 *)vsrc); - __asm__ __volatile__ ("eieio" : : : "memory"); + eieio(); vsrc += 4; dest += 4; n -= 4; } while(n) { *((u8 *)dest) = *((volatile u8 *)vsrc); - __asm__ __volatile__ ("eieio" : : : "memory"); + eieio(); vsrc++; dest++; n--; diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c index c2b84c64db20..2fc87862146c 100644 --- a/arch/powerpc/kernel/irq.c +++ b/arch/powerpc/kernel/irq.c @@ -7,7 +7,6 @@ * Copyright (C) 1996-2001 Cort Dougan * Adapted for Power Macintosh by Paul Mackerras * Copyright (C) 1996 Paul Mackerras (paulus@cs.anu.edu.au) - * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -337,7 +336,8 @@ void do_IRQ(struct pt_regs *regs) void __init init_IRQ(void) { - ppc_md.init_IRQ(); + if (ppc_md.init_IRQ) + ppc_md.init_IRQ(); #ifdef CONFIG_PPC64 irq_ctx_init(); #endif @@ -597,6 +597,49 @@ static void irq_radix_rdunlock(unsigned long flags) local_irq_restore(flags); } +static int irq_setup_virq(struct irq_host *host, unsigned int virq, + irq_hw_number_t hwirq) +{ + /* Clear IRQ_NOREQUEST flag */ + get_irq_desc(virq)->status &= ~IRQ_NOREQUEST; + + /* map it */ + smp_wmb(); + irq_map[virq].hwirq = hwirq; + smp_mb(); + + if (host->ops->map(host, virq, hwirq)) { + pr_debug("irq: -> mapping failed, freeing\n"); + irq_free_virt(virq, 1); + return -1; + } + + return 0; +} + +unsigned int irq_create_direct_mapping(struct irq_host *host) +{ + unsigned int virq; + + if (host == NULL) + host = irq_default_host; + + BUG_ON(host == NULL); + WARN_ON(host->revmap_type != IRQ_HOST_MAP_NOMAP); + + virq = irq_alloc_virt(host, 1, 0); + if (virq == NO_IRQ) { + pr_debug("irq: create_direct virq allocation failed\n"); + return NO_IRQ; + } + + pr_debug("irq: create_direct obtained virq %d\n", virq); + + if (irq_setup_virq(host, virq, virq)) + return NO_IRQ; + + return virq; +} unsigned int irq_create_mapping(struct irq_host *host, irq_hw_number_t hwirq) @@ -645,18 +688,9 @@ unsigned int irq_create_mapping(struct irq_host *host, } pr_debug("irq: -> obtained virq %d\n", virq); - /* Clear IRQ_NOREQUEST flag */ - get_irq_desc(virq)->status &= ~IRQ_NOREQUEST; - - /* map it */ - smp_wmb(); - irq_map[virq].hwirq = hwirq; - smp_mb(); - if (host->ops->map(host, virq, hwirq)) { - pr_debug("irq: -> mapping failed, freeing\n"); - irq_free_virt(virq, 1); + if (irq_setup_virq(host, virq, hwirq)) return NO_IRQ; - } + return virq; } EXPORT_SYMBOL_GPL(irq_create_mapping); diff --git a/arch/powerpc/kernel/isa-bridge.c b/arch/powerpc/kernel/isa-bridge.c new file mode 100644 index 000000000000..f0f49d1be3d5 --- /dev/null +++ b/arch/powerpc/kernel/isa-bridge.c @@ -0,0 +1,271 @@ +/* + * Routines for tracking a legacy ISA bridge + * + * Copyrigh 2007 Benjamin Herrenschmidt , IBM Corp. + * + * Some bits and pieces moved over from pci_64.c + * + * Copyrigh 2003 Anton Blanchard , IBM Corp. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#define DEBUG + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +unsigned long isa_io_base; /* NULL if no ISA bus */ +EXPORT_SYMBOL(isa_io_base); + +/* Cached ISA bridge dev. */ +static struct device_node *isa_bridge_devnode; +struct pci_dev *isa_bridge_pcidev; +EXPORT_SYMBOL_GPL(isa_bridge_pcidev); + +#define ISA_SPACE_MASK 0x1 +#define ISA_SPACE_IO 0x1 + +static void __devinit pci_process_ISA_OF_ranges(struct device_node *isa_node, + unsigned long phb_io_base_phys) +{ + /* We should get some saner parsing here and remove these structs */ + struct pci_address { + u32 a_hi; + u32 a_mid; + u32 a_lo; + }; + + struct isa_address { + u32 a_hi; + u32 a_lo; + }; + + struct isa_range { + struct isa_address isa_addr; + struct pci_address pci_addr; + unsigned int size; + }; + + const struct isa_range *range; + unsigned long pci_addr; + unsigned int isa_addr; + unsigned int size; + int rlen = 0; + + range = of_get_property(isa_node, "ranges", &rlen); + if (range == NULL || (rlen < sizeof(struct isa_range))) + goto inval_range; + + /* From "ISA Binding to 1275" + * The ranges property is laid out as an array of elements, + * each of which comprises: + * cells 0 - 1: an ISA address + * cells 2 - 4: a PCI address + * (size depending on dev->n_addr_cells) + * cell 5: the size of the range + */ + if ((range->isa_addr.a_hi && ISA_SPACE_MASK) != ISA_SPACE_IO) { + range++; + rlen -= sizeof(struct isa_range); + if (rlen < sizeof(struct isa_range)) + goto inval_range; + } + if ((range->isa_addr.a_hi && ISA_SPACE_MASK) != ISA_SPACE_IO) + goto inval_range; + + isa_addr = range->isa_addr.a_lo; + pci_addr = (unsigned long) range->pci_addr.a_mid << 32 | + range->pci_addr.a_lo; + + /* Assume these are both zero. Note: We could fix that and + * do a proper parsing instead ... oh well, that will do for + * now as nobody uses fancy mappings for ISA bridges + */ + if ((pci_addr != 0) || (isa_addr != 0)) { + printk(KERN_ERR "unexpected isa to pci mapping: %s\n", + __FUNCTION__); + return; + } + + /* Align size and make sure it's cropped to 64K */ + size = PAGE_ALIGN(range->size); + if (size > 0x10000) + size = 0x10000; + + printk(KERN_ERR "no ISA IO ranges or unexpected isa range," + "mapping 64k\n"); + + __ioremap_at(phb_io_base_phys, (void *)ISA_IO_BASE, + size, _PAGE_NO_CACHE|_PAGE_GUARDED); + return; + +inval_range: + printk(KERN_ERR "no ISA IO ranges or unexpected isa range," + "mapping 64k\n"); + __ioremap_at(phb_io_base_phys, (void *)ISA_IO_BASE, + 0x10000, _PAGE_NO_CACHE|_PAGE_GUARDED); +} + + +/** + * isa_bridge_find_early - Find and map the ISA IO space early before + * main PCI discovery. This is optionally called by + * the arch code when adding PCI PHBs to get early + * access to ISA IO ports + */ +void __init isa_bridge_find_early(struct pci_controller *hose) +{ + struct device_node *np, *parent = NULL, *tmp; + + /* If we already have an ISA bridge, bail off */ + if (isa_bridge_devnode != NULL) + return; + + /* For each "isa" node in the system. Note : we do a search by + * type and not by name. It might be better to do by name but that's + * what the code used to do and I don't want to break too much at + * once. We can look into changing that separately + */ + for_each_node_by_type(np, "isa") { + /* Look for our hose being a parent */ + for (parent = of_get_parent(np); parent;) { + if (parent == hose->arch_data) { + of_node_put(parent); + break; + } + tmp = parent; + parent = of_get_parent(parent); + of_node_put(tmp); + } + if (parent != NULL) + break; + } + if (np == NULL) + return; + isa_bridge_devnode = np; + + /* Now parse the "ranges" property and setup the ISA mapping */ + pci_process_ISA_OF_ranges(np, hose->io_base_phys); + + /* Set the global ISA io base to indicate we have an ISA bridge */ + isa_io_base = ISA_IO_BASE; + + pr_debug("ISA bridge (early) is %s\n", np->full_name); +} + +/** + * isa_bridge_find_late - Find and map the ISA IO space upon discovery of + * a new ISA bridge + */ +static void __devinit isa_bridge_find_late(struct pci_dev *pdev, + struct device_node *devnode) +{ + struct pci_controller *hose = pci_bus_to_host(pdev->bus); + + /* Store ISA device node and PCI device */ + isa_bridge_devnode = of_node_get(devnode); + isa_bridge_pcidev = pdev; + + /* Now parse the "ranges" property and setup the ISA mapping */ + pci_process_ISA_OF_ranges(devnode, hose->io_base_phys); + + /* Set the global ISA io base to indicate we have an ISA bridge */ + isa_io_base = ISA_IO_BASE; + + pr_debug("ISA bridge (late) is %s on %s\n", + devnode->full_name, pci_name(pdev)); +} + +/** + * isa_bridge_remove - Remove/unmap an ISA bridge + */ +static void isa_bridge_remove(void) +{ + pr_debug("ISA bridge removed !\n"); + + /* Clear the global ISA io base to indicate that we have no more + * ISA bridge. Note that drivers don't quite handle that, though + * we should probably do something about it. But do we ever really + * have ISA bridges being removed on machines using legacy devices ? + */ + isa_io_base = ISA_IO_BASE; + + /* Clear references to the bridge */ + of_node_put(isa_bridge_devnode); + isa_bridge_devnode = NULL; + isa_bridge_pcidev = NULL; + + /* Unmap the ISA area */ + __iounmap_at((void *)ISA_IO_BASE, 0x10000); +} + +/** + * isa_bridge_notify - Get notified of PCI devices addition/removal + */ +static int __devinit isa_bridge_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct device *dev = data; + struct pci_dev *pdev = to_pci_dev(dev); + struct device_node *devnode = pci_device_to_OF_node(pdev); + + switch(action) { + case BUS_NOTIFY_ADD_DEVICE: + /* Check if we have an early ISA device, without PCI dev */ + if (isa_bridge_devnode && isa_bridge_devnode == devnode && + !isa_bridge_pcidev) { + pr_debug("ISA bridge PCI attached: %s\n", + pci_name(pdev)); + isa_bridge_pcidev = pdev; + } + + /* Check if we have no ISA device, and this happens to be one, + * register it as such if it has an OF device + */ + if (!isa_bridge_devnode && devnode && devnode->type && + !strcmp(devnode->type, "isa")) + isa_bridge_find_late(pdev, devnode); + + return 0; + case BUS_NOTIFY_DEL_DEVICE: + /* Check if this our existing ISA device */ + if (pdev == isa_bridge_pcidev || + (devnode && devnode == isa_bridge_devnode)) + isa_bridge_remove(); + return 0; + } + return 0; +} + +static struct notifier_block isa_bridge_notifier = { + .notifier_call = isa_bridge_notify +}; + +/** + * isa_bridge_init - register to be notified of ISA bridge addition/removal + * + */ +static int __init isa_bridge_init(void) +{ + if (firmware_has_feature(FW_FEATURE_ISERIES)) + return 0; + bus_register_notifier(&pci_bus_type, &isa_bridge_notifier); + return 0; +} +arch_initcall(isa_bridge_init); diff --git a/arch/powerpc/kernel/misc_32.S b/arch/powerpc/kernel/misc_32.S index 98decf8ebff4..e708ab7ca9e8 100644 --- a/arch/powerpc/kernel/misc_32.S +++ b/arch/powerpc/kernel/misc_32.S @@ -392,7 +392,7 @@ BEGIN_FTR_SECTION mtspr SPRN_L1CSR0,r3 isync blr -END_FTR_SECTION_IFCLR(CPU_FTR_SPLIT_ID_CACHE) +END_FTR_SECTION_IFSET(CPU_FTR_UNIFIED_ID_CACHE) mfspr r3,SPRN_L1CSR1 ori r3,r3,L1CSR1_ICFI|L1CSR1_ICLFR mtspr SPRN_L1CSR1,r3 @@ -419,7 +419,7 @@ END_FTR_SECTION_IFCLR(CPU_FTR_SPLIT_ID_CACHE) _GLOBAL(__flush_icache_range) BEGIN_FTR_SECTION blr /* for 601, do nothing */ -END_FTR_SECTION_IFCLR(CPU_FTR_SPLIT_ID_CACHE) +END_FTR_SECTION_IFSET(CPU_FTR_COHERENT_ICACHE) li r5,L1_CACHE_BYTES-1 andc r3,r3,r5 subf r4,r3,r4 @@ -514,8 +514,8 @@ _GLOBAL(invalidate_dcache_range) */ _GLOBAL(__flush_dcache_icache) BEGIN_FTR_SECTION - blr /* for 601, do nothing */ -END_FTR_SECTION_IFCLR(CPU_FTR_SPLIT_ID_CACHE) + blr +END_FTR_SECTION_IFSET(CPU_FTR_COHERENT_ICACHE) rlwinm r3,r3,0,0,19 /* Get page base address */ li r4,4096/L1_CACHE_BYTES /* Number of lines in a page */ mtctr r4 @@ -543,7 +543,7 @@ END_FTR_SECTION_IFCLR(CPU_FTR_SPLIT_ID_CACHE) _GLOBAL(__flush_dcache_icache_phys) BEGIN_FTR_SECTION blr /* for 601, do nothing */ -END_FTR_SECTION_IFCLR(CPU_FTR_SPLIT_ID_CACHE) +END_FTR_SECTION_IFSET(CPU_FTR_COHERENT_ICACHE) mfmsr r10 rlwinm r0,r10,0,28,26 /* clear DR */ mtmsr r0 diff --git a/arch/powerpc/kernel/misc_64.S b/arch/powerpc/kernel/misc_64.S index 519861da0423..bbb3ba54c51c 100644 --- a/arch/powerpc/kernel/misc_64.S +++ b/arch/powerpc/kernel/misc_64.S @@ -646,6 +646,19 @@ _GLOBAL(kexec_sequence) /* turn off mmu */ bl real_mode + /* copy 0x100 bytes starting at start to 0 */ + li r3,0 + mr r4,r30 /* start, aka phys mem offset */ + li r5,0x100 + li r6,0 + bl .copy_and_flush /* (dest, src, copy limit, start offset) */ +1: /* assume normal blr return */ + + /* release other cpus to the new kernel secondary start at 0x60 */ + mflr r5 + li r6,1 + stw r6,kexec_flag-1b(5) + /* clear out hardware hash page table and tlb */ ld r5,0(r27) /* deref function descriptor */ mtctr r5 @@ -676,19 +689,6 @@ _GLOBAL(kexec_sequence) * are the boot cpu ????? * other device tree differences (prop sizes, va vs pa, etc)... */ - - /* copy 0x100 bytes starting at start to 0 */ - li r3,0 - mr r4,r30 - li r5,0x100 - li r6,0 - bl .copy_and_flush /* (dest, src, copy limit, start offset) */ -1: /* assume normal blr return */ - - /* release other cpus to the new kernel secondary start at 0x60 */ - mflr r5 - li r6,1 - stw r6,kexec_flag-1b(5) mr r3,r25 # my phys cpu mr r4,r30 # start, aka phys mem offset mtlr 4 diff --git a/arch/powerpc/kernel/of_platform.c b/arch/powerpc/kernel/of_platform.c index d454f61c9c7c..9536ed7f247c 100644 --- a/arch/powerpc/kernel/of_platform.c +++ b/arch/powerpc/kernel/of_platform.c @@ -427,14 +427,6 @@ static int __devinit of_pci_phb_probe(struct of_device *dev, /* Process "ranges" property */ pci_process_bridge_OF_ranges(phb, dev->node, 0); - /* Setup IO space. We use the non-dynamic version of that code here, - * which doesn't quite support unplugging. Next kernel release will - * have a better fix for this. - * Note also that we don't do ISA, this will also be fixed with a - * more massive rework. - */ - pci_setup_phb_io(phb, pci_io_base == 0); - /* Init pci_dn data structures */ pci_devs_phb_init_dynamic(phb); diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c new file mode 100644 index 000000000000..faf5ef3e90d0 --- /dev/null +++ b/arch/powerpc/kernel/pci-common.c @@ -0,0 +1,454 @@ +/* + * Contains common pci routines for ALL ppc platform + * (based on pci_32.c and pci_64.c) + * + * Port for PPC64 David Engebretsen, IBM Corp. + * Contains common pci routines for ppc64 platform, pSeries and iSeries brands. + * + * Copyright (C) 2003 Anton Blanchard , IBM + * Rework, based on alpha PCI code. + * + * Common pmac/prep/chrp pci routines. -- Cort + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#undef DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef DEBUG +#include +#define DBG(fmt...) printk(fmt) +#else +#define DBG(fmt...) +#endif + +static DEFINE_SPINLOCK(hose_spinlock); + +/* XXX kill that some day ... */ +int global_phb_number; /* Global phb counter */ + +extern struct list_head hose_list; + +/* + * pci_controller(phb) initialized common variables. + */ +static void __devinit pci_setup_pci_controller(struct pci_controller *hose) +{ + memset(hose, 0, sizeof(struct pci_controller)); + + spin_lock(&hose_spinlock); + hose->global_number = global_phb_number++; + list_add_tail(&hose->list_node, &hose_list); + spin_unlock(&hose_spinlock); +} + +struct pci_controller * pcibios_alloc_controller(struct device_node *dev) +{ + struct pci_controller *phb; + + if (mem_init_done) + phb = kmalloc(sizeof(struct pci_controller), GFP_KERNEL); + else + phb = alloc_bootmem(sizeof (struct pci_controller)); + if (phb == NULL) + return NULL; + pci_setup_pci_controller(phb); + phb->arch_data = dev; + phb->is_dynamic = mem_init_done; +#ifdef CONFIG_PPC64 + if (dev) { + int nid = of_node_to_nid(dev); + + if (nid < 0 || !node_online(nid)) + nid = -1; + + PHB_SET_NODE(phb, nid); + } +#endif + return phb; +} + +void pcibios_free_controller(struct pci_controller *phb) +{ + spin_lock(&hose_spinlock); + list_del(&phb->list_node); + spin_unlock(&hose_spinlock); + + if (phb->is_dynamic) + kfree(phb); +} + +/* + * Return the domain number for this bus. + */ +int pci_domain_nr(struct pci_bus *bus) +{ + if (firmware_has_feature(FW_FEATURE_ISERIES)) + return 0; + else { + struct pci_controller *hose = pci_bus_to_host(bus); + + return hose->global_number; + } +} + +EXPORT_SYMBOL(pci_domain_nr); + +#ifdef CONFIG_PPC_OF + +/* This routine is meant to be used early during boot, when the + * PCI bus numbers have not yet been assigned, and you need to + * issue PCI config cycles to an OF device. + * It could also be used to "fix" RTAS config cycles if you want + * to set pci_assign_all_buses to 1 and still use RTAS for PCI + * config cycles. + */ +struct pci_controller* pci_find_hose_for_OF_device(struct device_node* node) +{ + if (!have_of) + return NULL; + while(node) { + struct pci_controller *hose, *tmp; + list_for_each_entry_safe(hose, tmp, &hose_list, list_node) + if (hose->arch_data == node) + return hose; + node = node->parent; + } + return NULL; +} + +static ssize_t pci_show_devspec(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pci_dev *pdev; + struct device_node *np; + + pdev = to_pci_dev (dev); + np = pci_device_to_OF_node(pdev); + if (np == NULL || np->full_name == NULL) + return 0; + return sprintf(buf, "%s", np->full_name); +} +static DEVICE_ATTR(devspec, S_IRUGO, pci_show_devspec, NULL); +#endif /* CONFIG_PPC_OF */ + +/* Add sysfs properties */ +void pcibios_add_platform_entries(struct pci_dev *pdev) +{ +#ifdef CONFIG_PPC_OF + device_create_file(&pdev->dev, &dev_attr_devspec); +#endif /* CONFIG_PPC_OF */ +} + +char __init *pcibios_setup(char *str) +{ + return str; +} + +/* + * Reads the interrupt pin to determine if interrupt is use by card. + * If the interrupt is used, then gets the interrupt line from the + * openfirmware and sets it in the pci_dev and pci_config line. + */ +int pci_read_irq_line(struct pci_dev *pci_dev) +{ + struct of_irq oirq; + unsigned int virq; + + DBG("Try to map irq for %s...\n", pci_name(pci_dev)); + +#ifdef DEBUG + memset(&oirq, 0xff, sizeof(oirq)); +#endif + /* Try to get a mapping from the device-tree */ + if (of_irq_map_pci(pci_dev, &oirq)) { + u8 line, pin; + + /* If that fails, lets fallback to what is in the config + * space and map that through the default controller. We + * also set the type to level low since that's what PCI + * interrupts are. If your platform does differently, then + * either provide a proper interrupt tree or don't use this + * function. + */ + if (pci_read_config_byte(pci_dev, PCI_INTERRUPT_PIN, &pin)) + return -1; + if (pin == 0) + return -1; + if (pci_read_config_byte(pci_dev, PCI_INTERRUPT_LINE, &line) || + line == 0xff) { + return -1; + } + DBG(" -> no map ! Using irq line %d from PCI config\n", line); + + virq = irq_create_mapping(NULL, line); + if (virq != NO_IRQ) + set_irq_type(virq, IRQ_TYPE_LEVEL_LOW); + } else { + DBG(" -> got one, spec %d cells (0x%08x 0x%08x...) on %s\n", + oirq.size, oirq.specifier[0], oirq.specifier[1], + oirq.controller->full_name); + + virq = irq_create_of_mapping(oirq.controller, oirq.specifier, + oirq.size); + } + if(virq == NO_IRQ) { + DBG(" -> failed to map !\n"); + return -1; + } + + DBG(" -> mapped to linux irq %d\n", virq); + + pci_dev->irq = virq; + + return 0; +} +EXPORT_SYMBOL(pci_read_irq_line); + +/* + * Platform support for /proc/bus/pci/X/Y mmap()s, + * modelled on the sparc64 implementation by Dave Miller. + * -- paulus. + */ + +/* + * Adjust vm_pgoff of VMA such that it is the physical page offset + * corresponding to the 32-bit pci bus offset for DEV requested by the user. + * + * Basically, the user finds the base address for his device which he wishes + * to mmap. They read the 32-bit value from the config space base register, + * add whatever PAGE_SIZE multiple offset they wish, and feed this into the + * offset parameter of mmap on /proc/bus/pci/XXX for that device. + * + * Returns negative error code on failure, zero on success. + */ +static struct resource *__pci_mmap_make_offset(struct pci_dev *dev, + resource_size_t *offset, + enum pci_mmap_state mmap_state) +{ + struct pci_controller *hose = pci_bus_to_host(dev->bus); + unsigned long io_offset = 0; + int i, res_bit; + + if (hose == 0) + return NULL; /* should never happen */ + + /* If memory, add on the PCI bridge address offset */ + if (mmap_state == pci_mmap_mem) { +#if 0 /* See comment in pci_resource_to_user() for why this is disabled */ + *offset += hose->pci_mem_offset; +#endif + res_bit = IORESOURCE_MEM; + } else { + io_offset = (unsigned long)hose->io_base_virt - _IO_BASE; + *offset += io_offset; + res_bit = IORESOURCE_IO; + } + + /* + * Check that the offset requested corresponds to one of the + * resources of the device. + */ + for (i = 0; i <= PCI_ROM_RESOURCE; i++) { + struct resource *rp = &dev->resource[i]; + int flags = rp->flags; + + /* treat ROM as memory (should be already) */ + if (i == PCI_ROM_RESOURCE) + flags |= IORESOURCE_MEM; + + /* Active and same type? */ + if ((flags & res_bit) == 0) + continue; + + /* In the range of this resource? */ + if (*offset < (rp->start & PAGE_MASK) || *offset > rp->end) + continue; + + /* found it! construct the final physical address */ + if (mmap_state == pci_mmap_io) + *offset += hose->io_base_phys - io_offset; + return rp; + } + + return NULL; +} + +/* + * Set vm_page_prot of VMA, as appropriate for this architecture, for a pci + * device mapping. + */ +static pgprot_t __pci_mmap_set_pgprot(struct pci_dev *dev, struct resource *rp, + pgprot_t protection, + enum pci_mmap_state mmap_state, + int write_combine) +{ + unsigned long prot = pgprot_val(protection); + + /* Write combine is always 0 on non-memory space mappings. On + * memory space, if the user didn't pass 1, we check for a + * "prefetchable" resource. This is a bit hackish, but we use + * this to workaround the inability of /sysfs to provide a write + * combine bit + */ + if (mmap_state != pci_mmap_mem) + write_combine = 0; + else if (write_combine == 0) { + if (rp->flags & IORESOURCE_PREFETCH) + write_combine = 1; + } + + /* XXX would be nice to have a way to ask for write-through */ + prot |= _PAGE_NO_CACHE; + if (write_combine) + prot &= ~_PAGE_GUARDED; + else + prot |= _PAGE_GUARDED; + + return __pgprot(prot); +} + +/* + * This one is used by /dev/mem and fbdev who have no clue about the + * PCI device, it tries to find the PCI device first and calls the + * above routine + */ +pgprot_t pci_phys_mem_access_prot(struct file *file, + unsigned long pfn, + unsigned long size, + pgprot_t protection) +{ + struct pci_dev *pdev = NULL; + struct resource *found = NULL; + unsigned long prot = pgprot_val(protection); + unsigned long offset = pfn << PAGE_SHIFT; + int i; + + if (page_is_ram(pfn)) + return __pgprot(prot); + + prot |= _PAGE_NO_CACHE | _PAGE_GUARDED; + + for_each_pci_dev(pdev) { + for (i = 0; i <= PCI_ROM_RESOURCE; i++) { + struct resource *rp = &pdev->resource[i]; + int flags = rp->flags; + + /* Active and same type? */ + if ((flags & IORESOURCE_MEM) == 0) + continue; + /* In the range of this resource? */ + if (offset < (rp->start & PAGE_MASK) || + offset > rp->end) + continue; + found = rp; + break; + } + if (found) + break; + } + if (found) { + if (found->flags & IORESOURCE_PREFETCH) + prot &= ~_PAGE_GUARDED; + pci_dev_put(pdev); + } + + DBG("non-PCI map for %lx, prot: %lx\n", offset, prot); + + return __pgprot(prot); +} + + +/* + * Perform the actual remap of the pages for a PCI device mapping, as + * appropriate for this architecture. The region in the process to map + * is described by vm_start and vm_end members of VMA, the base physical + * address is found in vm_pgoff. + * The pci device structure is provided so that architectures may make mapping + * decisions on a per-device or per-bus basis. + * + * Returns a negative error code on failure, zero on success. + */ +int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, + enum pci_mmap_state mmap_state, int write_combine) +{ + resource_size_t offset = vma->vm_pgoff << PAGE_SHIFT; + struct resource *rp; + int ret; + + rp = __pci_mmap_make_offset(dev, &offset, mmap_state); + if (rp == NULL) + return -EINVAL; + + vma->vm_pgoff = offset >> PAGE_SHIFT; + vma->vm_page_prot = __pci_mmap_set_pgprot(dev, rp, + vma->vm_page_prot, + mmap_state, write_combine); + + ret = remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, + vma->vm_end - vma->vm_start, vma->vm_page_prot); + + return ret; +} + +void pci_resource_to_user(const struct pci_dev *dev, int bar, + const struct resource *rsrc, + resource_size_t *start, resource_size_t *end) +{ + struct pci_controller *hose = pci_bus_to_host(dev->bus); + resource_size_t offset = 0; + + if (hose == NULL) + return; + + if (rsrc->flags & IORESOURCE_IO) + offset = (unsigned long)hose->io_base_virt - _IO_BASE; + + /* We pass a fully fixed up address to userland for MMIO instead of + * a BAR value because X is lame and expects to be able to use that + * to pass to /dev/mem ! + * + * That means that we'll have potentially 64 bits values where some + * userland apps only expect 32 (like X itself since it thinks only + * Sparc has 64 bits MMIO) but if we don't do that, we break it on + * 32 bits CHRPs :-( + * + * Hopefully, the sysfs insterface is immune to that gunk. Once X + * has been fixed (and the fix spread enough), we can re-enable the + * 2 lines below and pass down a BAR value to userland. In that case + * we'll also have to re-enable the matching code in + * __pci_mmap_make_offset(). + * + * BenH. + */ +#if 0 + else if (rsrc->flags & IORESOURCE_MEM) + offset = hose->pci_mem_offset; +#endif + + *start = rsrc->start - offset; + *end = rsrc->end - offset; +} diff --git a/arch/powerpc/kernel/pci_32.c b/arch/powerpc/kernel/pci_32.c index e66064b5093a..0adf077f3f3a 100644 --- a/arch/powerpc/kernel/pci_32.c +++ b/arch/powerpc/kernel/pci_32.c @@ -55,8 +55,7 @@ static u8* pci_to_OF_bus_map; */ int pci_assign_all_buses; -struct pci_controller* hose_head; -struct pci_controller** hose_tail = &hose_head; +LIST_HEAD(hose_list); static int pci_bus_count; @@ -573,58 +572,6 @@ pcibios_assign_resources(void) } } - -int -pcibios_enable_resources(struct pci_dev *dev, int mask) -{ - u16 cmd, old_cmd; - int idx; - struct resource *r; - - pci_read_config_word(dev, PCI_COMMAND, &cmd); - old_cmd = cmd; - for (idx=0; idx<6; idx++) { - /* Only set up the requested stuff */ - if (!(mask & (1<resource[idx]; - if (r->flags & IORESOURCE_UNSET) { - printk(KERN_ERR "PCI: Device %s not available because of resource collisions\n", pci_name(dev)); - return -EINVAL; - } - if (r->flags & IORESOURCE_IO) - cmd |= PCI_COMMAND_IO; - if (r->flags & IORESOURCE_MEM) - cmd |= PCI_COMMAND_MEMORY; - } - if (dev->resource[PCI_ROM_RESOURCE].start) - cmd |= PCI_COMMAND_MEMORY; - if (cmd != old_cmd) { - printk("PCI: Enabling device %s (%04x -> %04x)\n", pci_name(dev), old_cmd, cmd); - pci_write_config_word(dev, PCI_COMMAND, cmd); - } - return 0; -} - -static int next_controller_index; - -struct pci_controller * __init -pcibios_alloc_controller(void) -{ - struct pci_controller *hose; - - hose = (struct pci_controller *)alloc_bootmem(sizeof(*hose)); - memset(hose, 0, sizeof(struct pci_controller)); - - *hose_tail = hose; - hose_tail = &hose->next; - - hose->index = next_controller_index++; - - return hose; -} - #ifdef CONFIG_PPC_OF /* * Functions below are used on OpenFirmware machines. @@ -670,7 +617,7 @@ void pcibios_make_OF_bus_map(void) { int i; - struct pci_controller* hose; + struct pci_controller *hose, *tmp; struct property *map_prop; struct device_node *dn; @@ -687,7 +634,7 @@ pcibios_make_OF_bus_map(void) pci_to_OF_bus_map[i] = 0xff; /* For each hose, we begin searching bridges */ - for(hose=hose_head; hose; hose=hose->next) { + list_for_each_entry_safe(hose, tmp, &hose_list, list_node) { struct device_node* node; node = (struct device_node *)hose->arch_data; if (!node) @@ -765,7 +712,7 @@ static struct device_node *scan_OF_for_pci_bus(struct pci_bus *bus) /* Are we a root bus ? */ if (bus->self == NULL || bus->parent == NULL) { - struct pci_controller *hose = pci_bus_to_hose(bus->number); + struct pci_controller *hose = pci_bus_to_host(bus); if (hose == NULL) return NULL; return of_node_get(hose->arch_data); @@ -818,27 +765,6 @@ pci_device_to_OF_node(struct pci_dev *dev) } EXPORT_SYMBOL(pci_device_to_OF_node); -/* This routine is meant to be used early during boot, when the - * PCI bus numbers have not yet been assigned, and you need to - * issue PCI config cycles to an OF device. - * It could also be used to "fix" RTAS config cycles if you want - * to set pci_assign_all_buses to 1 and still use RTAS for PCI - * config cycles. - */ -struct pci_controller* pci_find_hose_for_OF_device(struct device_node* node) -{ - if (!have_of) - return NULL; - while(node) { - struct pci_controller* hose; - for (hose=hose_head;hose;hose=hose->next) - if (hose->arch_data == node) - return hose; - node=node->parent; - } - return NULL; -} - static int find_OF_pci_device_filter(struct device_node* node, void* data) { @@ -1027,34 +953,12 @@ pci_create_OF_bus_map(void) } } -static ssize_t pci_show_devspec(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct pci_dev *pdev; - struct device_node *np; - - pdev = to_pci_dev (dev); - np = pci_device_to_OF_node(pdev); - if (np == NULL || np->full_name == NULL) - return 0; - return sprintf(buf, "%s", np->full_name); -} -static DEVICE_ATTR(devspec, S_IRUGO, pci_show_devspec, NULL); - #else /* CONFIG_PPC_OF */ void pcibios_make_OF_bus_map(void) { } #endif /* CONFIG_PPC_OF */ -/* Add sysfs properties */ -void pcibios_add_platform_entries(struct pci_dev *pdev) -{ -#ifdef CONFIG_PPC_OF - device_create_file(&pdev->dev, &dev_attr_devspec); -#endif /* CONFIG_PPC_OF */ -} - - #ifdef CONFIG_PPC_PMAC /* * This set of routines checks for PCI<->PCI bridges that have closed @@ -1269,14 +1173,14 @@ pcibios_fixup_p2p_bridges(void) static int __init pcibios_init(void) { - struct pci_controller *hose; + struct pci_controller *hose, *tmp; struct pci_bus *bus; - int next_busno; + int next_busno = 0; printk(KERN_INFO "PCI: Probing PCI hardware\n"); /* Scan all of the recorded PCI controllers. */ - for (next_busno = 0, hose = hose_head; hose; hose = hose->next) { + list_for_each_entry_safe(hose, tmp, &hose_list, list_node) { if (pci_assign_all_buses) hose->first_busno = next_busno; hose->last_busno = 0xff; @@ -1319,12 +1223,6 @@ pcibios_init(void) subsys_initcall(pcibios_init); -unsigned long resource_fixup(struct pci_dev * dev, struct resource * res, - unsigned long start, unsigned long size) -{ - return start; -} - void __init pcibios_fixup_bus(struct pci_bus *bus) { struct pci_controller *hose = (struct pci_controller *) bus->sysdata; @@ -1342,7 +1240,7 @@ void __init pcibios_fixup_bus(struct pci_bus *bus) if (!res->flags) { if (io_offset) printk(KERN_ERR "I/O resource not set for host" - " bridge %d\n", hose->index); + " bridge %d\n", hose->global_number); res->start = 0; res->end = IO_SPACE_LIMIT; res->flags = IORESOURCE_IO; @@ -1356,7 +1254,7 @@ void __init pcibios_fixup_bus(struct pci_bus *bus) if (i > 0) continue; printk(KERN_ERR "Memory resource not set for " - "host bridge %d\n", hose->index); + "host bridge %d\n", hose->global_number); res->start = hose->pci_mem_offset; res->end = ~0U; res->flags = IORESOURCE_MEM; @@ -1370,7 +1268,7 @@ void __init pcibios_fixup_bus(struct pci_bus *bus) for (i = 0; i < 4; ++i) { if ((res = bus->resource[i]) == NULL) continue; - if (!res->flags) + if (!res->flags || bus->self->transparent) continue; if (io_offset && (res->flags & IORESOURCE_IO)) { res->start += io_offset; @@ -1395,11 +1293,6 @@ void __init pcibios_fixup_bus(struct pci_bus *bus) } } -char __init *pcibios_setup(char *str) -{ - return str; -} - /* the next one is stolen from the alpha port... */ void __init pcibios_update_irq(struct pci_dev *dev, int irq) @@ -1408,64 +1301,6 @@ pcibios_update_irq(struct pci_dev *dev, int irq) /* XXX FIXME - update OF device tree node interrupt property */ } -#ifdef CONFIG_PPC_MERGE -/* XXX This is a copy of the ppc64 version. This is temporary until we start - * merging the 2 PCI layers - */ -/* - * Reads the interrupt pin to determine if interrupt is use by card. - * If the interrupt is used, then gets the interrupt line from the - * openfirmware and sets it in the pci_dev and pci_config line. - */ -int pci_read_irq_line(struct pci_dev *pci_dev) -{ - struct of_irq oirq; - unsigned int virq; - - DBG("Try to map irq for %s...\n", pci_name(pci_dev)); - - /* Try to get a mapping from the device-tree */ - if (of_irq_map_pci(pci_dev, &oirq)) { - u8 line, pin; - - /* If that fails, lets fallback to what is in the config - * space and map that through the default controller. We - * also set the type to level low since that's what PCI - * interrupts are. If your platform does differently, then - * either provide a proper interrupt tree or don't use this - * function. - */ - if (pci_read_config_byte(pci_dev, PCI_INTERRUPT_PIN, &pin)) - return -1; - if (pin == 0) - return -1; - if (pci_read_config_byte(pci_dev, PCI_INTERRUPT_LINE, &line) || - line == 0xff) { - return -1; - } - DBG(" -> no map ! Using irq line %d from PCI config\n", line); - - virq = irq_create_mapping(NULL, line); - if (virq != NO_IRQ) - set_irq_type(virq, IRQ_TYPE_LEVEL_LOW); - } else { - DBG(" -> got one, spec %d cells (0x%08x...) on %s\n", - oirq.size, oirq.specifier[0], oirq.controller->full_name); - - virq = irq_create_of_mapping(oirq.controller, oirq.specifier, - oirq.size); - } - if(virq == NO_IRQ) { - DBG(" -> failed to map !\n"); - return -1; - } - pci_dev->irq = virq; - - return 0; -} -EXPORT_SYMBOL(pci_read_irq_line); -#endif /* CONFIG_PPC_MERGE */ - int pcibios_enable_device(struct pci_dev *dev, int mask) { u16 cmd, old_cmd; @@ -1497,281 +1332,17 @@ int pcibios_enable_device(struct pci_dev *dev, int mask) return 0; } -struct pci_controller* +static struct pci_controller* pci_bus_to_hose(int bus) { - struct pci_controller* hose = hose_head; + struct pci_controller *hose, *tmp; - for (; hose; hose = hose->next) + list_for_each_entry_safe(hose, tmp, &hose_list, list_node) if (bus >= hose->first_busno && bus <= hose->last_busno) return hose; return NULL; } -void __iomem * -pci_bus_io_base(unsigned int bus) -{ - struct pci_controller *hose; - - hose = pci_bus_to_hose(bus); - if (!hose) - return NULL; - return hose->io_base_virt; -} - -unsigned long -pci_bus_io_base_phys(unsigned int bus) -{ - struct pci_controller *hose; - - hose = pci_bus_to_hose(bus); - if (!hose) - return 0; - return hose->io_base_phys; -} - -unsigned long -pci_bus_mem_base_phys(unsigned int bus) -{ - struct pci_controller *hose; - - hose = pci_bus_to_hose(bus); - if (!hose) - return 0; - return hose->pci_mem_offset; -} - -unsigned long -pci_resource_to_bus(struct pci_dev *pdev, struct resource *res) -{ - /* Hack alert again ! See comments in chrp_pci.c - */ - struct pci_controller* hose = - (struct pci_controller *)pdev->sysdata; - if (hose && res->flags & IORESOURCE_MEM) - return res->start - hose->pci_mem_offset; - /* We may want to do something with IOs here... */ - return res->start; -} - - -static struct resource *__pci_mmap_make_offset(struct pci_dev *dev, - resource_size_t *offset, - enum pci_mmap_state mmap_state) -{ - struct pci_controller *hose = pci_bus_to_hose(dev->bus->number); - unsigned long io_offset = 0; - int i, res_bit; - - if (hose == 0) - return NULL; /* should never happen */ - - /* If memory, add on the PCI bridge address offset */ - if (mmap_state == pci_mmap_mem) { -#if 0 /* See comment in pci_resource_to_user() for why this is disabled */ - *offset += hose->pci_mem_offset; -#endif - res_bit = IORESOURCE_MEM; - } else { - io_offset = hose->io_base_virt - (void __iomem *)_IO_BASE; - *offset += io_offset; - res_bit = IORESOURCE_IO; - } - - /* - * Check that the offset requested corresponds to one of the - * resources of the device. - */ - for (i = 0; i <= PCI_ROM_RESOURCE; i++) { - struct resource *rp = &dev->resource[i]; - int flags = rp->flags; - - /* treat ROM as memory (should be already) */ - if (i == PCI_ROM_RESOURCE) - flags |= IORESOURCE_MEM; - - /* Active and same type? */ - if ((flags & res_bit) == 0) - continue; - - /* In the range of this resource? */ - if (*offset < (rp->start & PAGE_MASK) || *offset > rp->end) - continue; - - /* found it! construct the final physical address */ - if (mmap_state == pci_mmap_io) - *offset += hose->io_base_phys - io_offset; - return rp; - } - - return NULL; -} - -/* - * Set vm_page_prot of VMA, as appropriate for this architecture, for a pci - * device mapping. - */ -static pgprot_t __pci_mmap_set_pgprot(struct pci_dev *dev, struct resource *rp, - pgprot_t protection, - enum pci_mmap_state mmap_state, - int write_combine) -{ - unsigned long prot = pgprot_val(protection); - - /* Write combine is always 0 on non-memory space mappings. On - * memory space, if the user didn't pass 1, we check for a - * "prefetchable" resource. This is a bit hackish, but we use - * this to workaround the inability of /sysfs to provide a write - * combine bit - */ - if (mmap_state != pci_mmap_mem) - write_combine = 0; - else if (write_combine == 0) { - if (rp->flags & IORESOURCE_PREFETCH) - write_combine = 1; - } - - /* XXX would be nice to have a way to ask for write-through */ - prot |= _PAGE_NO_CACHE; - if (write_combine) - prot &= ~_PAGE_GUARDED; - else - prot |= _PAGE_GUARDED; - - return __pgprot(prot); -} - -/* - * This one is used by /dev/mem and fbdev who have no clue about the - * PCI device, it tries to find the PCI device first and calls the - * above routine - */ -pgprot_t pci_phys_mem_access_prot(struct file *file, - unsigned long pfn, - unsigned long size, - pgprot_t protection) -{ - struct pci_dev *pdev = NULL; - struct resource *found = NULL; - unsigned long prot = pgprot_val(protection); - unsigned long offset = pfn << PAGE_SHIFT; - int i; - - if (page_is_ram(pfn)) - return __pgprot(prot); - - prot |= _PAGE_NO_CACHE | _PAGE_GUARDED; - - for_each_pci_dev(pdev) { - for (i = 0; i <= PCI_ROM_RESOURCE; i++) { - struct resource *rp = &pdev->resource[i]; - int flags = rp->flags; - - /* Active and same type? */ - if ((flags & IORESOURCE_MEM) == 0) - continue; - /* In the range of this resource? */ - if (offset < (rp->start & PAGE_MASK) || - offset > rp->end) - continue; - found = rp; - break; - } - if (found) - break; - } - if (found) { - if (found->flags & IORESOURCE_PREFETCH) - prot &= ~_PAGE_GUARDED; - pci_dev_put(pdev); - } - - DBG("non-PCI map for %lx, prot: %lx\n", offset, prot); - - return __pgprot(prot); -} - - -/* - * Perform the actual remap of the pages for a PCI device mapping, as - * appropriate for this architecture. The region in the process to map - * is described by vm_start and vm_end members of VMA, the base physical - * address is found in vm_pgoff. - * The pci device structure is provided so that architectures may make mapping - * decisions on a per-device or per-bus basis. - * - * Returns a negative error code on failure, zero on success. - */ -int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, - enum pci_mmap_state mmap_state, - int write_combine) -{ - resource_size_t offset = vma->vm_pgoff << PAGE_SHIFT; - struct resource *rp; - int ret; - - rp = __pci_mmap_make_offset(dev, &offset, mmap_state); - if (rp == NULL) - return -EINVAL; - - vma->vm_pgoff = offset >> PAGE_SHIFT; - vma->vm_page_prot = __pci_mmap_set_pgprot(dev, rp, - vma->vm_page_prot, - mmap_state, write_combine); - - ret = remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, - vma->vm_end - vma->vm_start, vma->vm_page_prot); - - return ret; -} - -/* Obsolete functions. Should be removed once the symbios driver - * is fixed - */ -unsigned long -phys_to_bus(unsigned long pa) -{ - struct pci_controller *hose; - int i; - - for (hose = hose_head; hose; hose = hose->next) { - for (i = 0; i < 3; ++i) { - if (pa >= hose->mem_resources[i].start - && pa <= hose->mem_resources[i].end) { - /* - * XXX the hose->pci_mem_offset really - * only applies to mem_resources[0]. - * We need a way to store an offset for - * the others. -- paulus - */ - if (i == 0) - pa -= hose->pci_mem_offset; - return pa; - } - } - } - /* hmmm, didn't find it */ - return 0; -} - -unsigned long -pci_phys_to_bus(unsigned long pa, int busnr) -{ - struct pci_controller* hose = pci_bus_to_hose(busnr); - if (!hose) - return pa; - return pa - hose->pci_mem_offset; -} - -unsigned long -pci_bus_to_phys(unsigned int ba, int busnr) -{ - struct pci_controller* hose = pci_bus_to_hose(busnr); - if (!hose) - return ba; - return ba + hose->pci_mem_offset; -} - /* Provide information on locations of various I/O regions in physical * memory. Do this on a per-card basis so that we choose the right * root bridge. @@ -1814,62 +1385,11 @@ long sys_pciconfig_iobase(long which, unsigned long bus, unsigned long devfn) return result; } -void pci_resource_to_user(const struct pci_dev *dev, int bar, - const struct resource *rsrc, - resource_size_t *start, resource_size_t *end) -{ - struct pci_controller *hose = pci_bus_to_hose(dev->bus->number); - resource_size_t offset = 0; - - if (hose == NULL) - return; - - if (rsrc->flags & IORESOURCE_IO) - offset = (unsigned long)hose->io_base_virt - _IO_BASE; - - /* We pass a fully fixed up address to userland for MMIO instead of - * a BAR value because X is lame and expects to be able to use that - * to pass to /dev/mem ! - * - * That means that we'll have potentially 64 bits values where some - * userland apps only expect 32 (like X itself since it thinks only - * Sparc has 64 bits MMIO) but if we don't do that, we break it on - * 32 bits CHRPs :-( - * - * Hopefully, the sysfs insterface is immune to that gunk. Once X - * has been fixed (and the fix spread enough), we can re-enable the - * 2 lines below and pass down a BAR value to userland. In that case - * we'll also have to re-enable the matching code in - * __pci_mmap_make_offset(). - * - * BenH. - */ -#if 0 - else if (rsrc->flags & IORESOURCE_MEM) - offset = hose->pci_mem_offset; -#endif - - *start = rsrc->start - offset; - *end = rsrc->end - offset; -} - -void __init pci_init_resource(struct resource *res, resource_size_t start, - resource_size_t end, int flags, char *name) -{ - res->start = start; - res->end = end; - res->flags = flags; - res->name = name; - res->parent = NULL; - res->sibling = NULL; - res->child = NULL; -} - unsigned long pci_address_to_pio(phys_addr_t address) { - struct pci_controller* hose = hose_head; + struct pci_controller *hose, *tmp; - for (; hose; hose = hose->next) { + list_for_each_entry_safe(hose, tmp, &hose_list, list_node) { unsigned int size = hose->io_resource.end - hose->io_resource.start + 1; if (address >= hose->io_base_phys && diff --git a/arch/powerpc/kernel/pci_64.c b/arch/powerpc/kernel/pci_64.c index 249cca27a9b8..93b2920effc5 100644 --- a/arch/powerpc/kernel/pci_64.c +++ b/arch/powerpc/kernel/pci_64.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -41,35 +42,23 @@ unsigned long pci_probe_only = 1; int pci_assign_all_buses = 0; -static int pci_initial_scan_done; static void fixup_resource(struct resource *res, struct pci_dev *dev); static void do_bus_setup(struct pci_bus *bus); -static void phbs_remap_io(void); /* pci_io_base -- the base address from which io bars are offsets. * This is the lowest I/O base address (so bar values are always positive), * and it *must* be the start of ISA space if an ISA bus exists because - * ISA drivers use hard coded offsets. If no ISA bus exists a dummy - * page is mapped and isa_io_limit prevents access to it. + * ISA drivers use hard coded offsets. If no ISA bus exists nothing + * is mapped on the first 64K of IO space */ -unsigned long isa_io_base; /* NULL if no ISA bus */ -EXPORT_SYMBOL(isa_io_base); -unsigned long pci_io_base; +unsigned long pci_io_base = ISA_IO_BASE; EXPORT_SYMBOL(pci_io_base); -void iSeries_pcibios_init(void); - LIST_HEAD(hose_list); static struct dma_mapping_ops *pci_dma_ops; -int global_phb_number; /* Global phb counter */ - -/* Cached ISA bridge dev. */ -struct pci_dev *ppc64_isabridge_dev = NULL; -EXPORT_SYMBOL_GPL(ppc64_isabridge_dev); - void set_pci_dma_ops(struct dma_mapping_ops *dma_ops) { pci_dma_ops = dma_ops; @@ -100,7 +89,7 @@ void pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region return; if (res->flags & IORESOURCE_IO) - offset = (unsigned long)hose->io_base_virt - pci_io_base; + offset = (unsigned long)hose->io_base_virt - _IO_BASE; if (res->flags & IORESOURCE_MEM) offset = hose->pci_mem_offset; @@ -119,7 +108,7 @@ void pcibios_bus_to_resource(struct pci_dev *dev, struct resource *res, return; if (res->flags & IORESOURCE_IO) - offset = (unsigned long)hose->io_base_virt - pci_io_base; + offset = (unsigned long)hose->io_base_virt - _IO_BASE; if (res->flags & IORESOURCE_MEM) offset = hose->pci_mem_offset; @@ -156,7 +145,7 @@ void pcibios_align_resource(void *data, struct resource *res, if (res->flags & IORESOURCE_IO) { unsigned long offset = (unsigned long)hose->io_base_virt - - pci_io_base; + _IO_BASE; /* Make sure we start at our min on all hoses */ if (start - offset < PCIBIOS_MIN_IO) start = PCIBIOS_MIN_IO + offset; @@ -180,55 +169,6 @@ void pcibios_align_resource(void *data, struct resource *res, res->start = start; } -static DEFINE_SPINLOCK(hose_spinlock); - -/* - * pci_controller(phb) initialized common variables. - */ -static void __devinit pci_setup_pci_controller(struct pci_controller *hose) -{ - memset(hose, 0, sizeof(struct pci_controller)); - - spin_lock(&hose_spinlock); - hose->global_number = global_phb_number++; - list_add_tail(&hose->list_node, &hose_list); - spin_unlock(&hose_spinlock); -} - -struct pci_controller * pcibios_alloc_controller(struct device_node *dev) -{ - struct pci_controller *phb; - - if (mem_init_done) - phb = kmalloc(sizeof(struct pci_controller), GFP_KERNEL); - else - phb = alloc_bootmem(sizeof (struct pci_controller)); - if (phb == NULL) - return NULL; - pci_setup_pci_controller(phb); - phb->arch_data = dev; - phb->is_dynamic = mem_init_done; - if (dev) { - int nid = of_node_to_nid(dev); - - if (nid < 0 || !node_online(nid)) - nid = -1; - - PHB_SET_NODE(phb, nid); - } - return phb; -} - -void pcibios_free_controller(struct pci_controller *phb) -{ - spin_lock(&hose_spinlock); - list_del(&phb->list_node); - spin_unlock(&hose_spinlock); - - if (phb->is_dynamic) - kfree(phb); -} - void __devinit pcibios_claim_one_bus(struct pci_bus *b) { struct pci_dev *dev; @@ -291,7 +231,6 @@ static unsigned int pci_parse_of_flags(u32 addr0) return flags; } -#define GET_64BIT(prop, i) ((((u64) (prop)[(i)]) << 32) | (prop)[(i)+1]) static void pci_parse_of_addrs(struct device_node *node, struct pci_dev *dev) { @@ -310,8 +249,8 @@ static void pci_parse_of_addrs(struct device_node *node, struct pci_dev *dev) flags = pci_parse_of_flags(addrs[0]); if (!flags) continue; - base = GET_64BIT(addrs, 1); - size = GET_64BIT(addrs, 3); + base = of_read_number(&addrs[1], 2); + size = of_read_number(&addrs[3], 2); if (!size) continue; i = addrs[0] & 0xff; @@ -477,7 +416,7 @@ void __devinit of_scan_pci_bridge(struct device_node *node, i = 1; for (; len >= 32; len -= 32, ranges += 8) { flags = pci_parse_of_flags(ranges[0]); - size = GET_64BIT(ranges, 6); + size = of_read_number(&ranges[6], 2); if (flags == 0 || size == 0) continue; if (flags & IORESOURCE_IO) { @@ -496,7 +435,7 @@ void __devinit of_scan_pci_bridge(struct device_node *node, res = bus->resource[i]; ++i; } - res->start = GET_64BIT(ranges, 1); + res->start = of_read_number(&ranges[1], 2); res->end = res->start + size - 1; res->flags = flags; fixup_resource(res, dev); @@ -535,10 +474,16 @@ void __devinit scan_phb(struct pci_controller *hose) bus->secondary = hose->first_busno; hose->bus = bus; + if (!firmware_has_feature(FW_FEATURE_ISERIES)) + pcibios_map_io_space(bus); + bus->resource[0] = res = &hose->io_resource; - if (res->flags && request_resource(&ioport_resource, res)) + if (res->flags && request_resource(&ioport_resource, res)) { printk(KERN_ERR "Failed to request PCI IO region " "on PCI domain %04x\n", hose->global_number); + DBG("res->start = 0x%016lx, res->end = 0x%016lx\n", + res->start, res->end); + } for (i = 0; i < 3; ++i) { res = &hose->mem_resources[i]; @@ -596,17 +541,6 @@ static int __init pcibios_init(void) if (ppc_md.pcibios_fixup) ppc_md.pcibios_fixup(); - /* Cache the location of the ISA bridge (if we have one) */ - ppc64_isabridge_dev = pci_get_class(PCI_CLASS_BRIDGE_ISA << 8, NULL); - if (ppc64_isabridge_dev != NULL) - printk(KERN_DEBUG "ISA bridge at %s\n", pci_name(ppc64_isabridge_dev)); - - if (!firmware_has_feature(FW_FEATURE_ISERIES)) - /* map in PCI I/O space */ - phbs_remap_io(); - - pci_initial_scan_done = 1; - printk(KERN_DEBUG "PCI: Probing PCI hardware done\n"); return 0; @@ -614,11 +548,6 @@ static int __init pcibios_init(void) subsys_initcall(pcibios_init); -char __init *pcibios_setup(char *str) -{ - return str; -} - int pcibios_enable_device(struct pci_dev *dev, int mask) { u16 cmd, oldcmd; @@ -649,22 +578,6 @@ int pcibios_enable_device(struct pci_dev *dev, int mask) return 0; } -/* - * Return the domain number for this bus. - */ -int pci_domain_nr(struct pci_bus *bus) -{ - if (firmware_has_feature(FW_FEATURE_ISERIES)) - return 0; - else { - struct pci_controller *hose = pci_bus_to_host(bus); - - return hose->global_number; - } -} - -EXPORT_SYMBOL(pci_domain_nr); - /* Decide whether to display the domain number in /proc */ int pci_proc_domain(struct pci_bus *bus) { @@ -676,281 +589,6 @@ int pci_proc_domain(struct pci_bus *bus) } } -/* - * Platform support for /proc/bus/pci/X/Y mmap()s, - * modelled on the sparc64 implementation by Dave Miller. - * -- paulus. - */ - -/* - * Adjust vm_pgoff of VMA such that it is the physical page offset - * corresponding to the 32-bit pci bus offset for DEV requested by the user. - * - * Basically, the user finds the base address for his device which he wishes - * to mmap. They read the 32-bit value from the config space base register, - * add whatever PAGE_SIZE multiple offset they wish, and feed this into the - * offset parameter of mmap on /proc/bus/pci/XXX for that device. - * - * Returns negative error code on failure, zero on success. - */ -static struct resource *__pci_mmap_make_offset(struct pci_dev *dev, - resource_size_t *offset, - enum pci_mmap_state mmap_state) -{ - struct pci_controller *hose = pci_bus_to_host(dev->bus); - unsigned long io_offset = 0; - int i, res_bit; - - if (hose == 0) - return NULL; /* should never happen */ - - /* If memory, add on the PCI bridge address offset */ - if (mmap_state == pci_mmap_mem) { -#if 0 /* See comment in pci_resource_to_user() for why this is disabled */ - *offset += hose->pci_mem_offset; -#endif - res_bit = IORESOURCE_MEM; - } else { - io_offset = (unsigned long)hose->io_base_virt - pci_io_base; - *offset += io_offset; - res_bit = IORESOURCE_IO; - } - - /* - * Check that the offset requested corresponds to one of the - * resources of the device. - */ - for (i = 0; i <= PCI_ROM_RESOURCE; i++) { - struct resource *rp = &dev->resource[i]; - int flags = rp->flags; - - /* treat ROM as memory (should be already) */ - if (i == PCI_ROM_RESOURCE) - flags |= IORESOURCE_MEM; - - /* Active and same type? */ - if ((flags & res_bit) == 0) - continue; - - /* In the range of this resource? */ - if (*offset < (rp->start & PAGE_MASK) || *offset > rp->end) - continue; - - /* found it! construct the final physical address */ - if (mmap_state == pci_mmap_io) - *offset += hose->io_base_phys - io_offset; - return rp; - } - - return NULL; -} - -/* - * Set vm_page_prot of VMA, as appropriate for this architecture, for a pci - * device mapping. - */ -static pgprot_t __pci_mmap_set_pgprot(struct pci_dev *dev, struct resource *rp, - pgprot_t protection, - enum pci_mmap_state mmap_state, - int write_combine) -{ - unsigned long prot = pgprot_val(protection); - - /* Write combine is always 0 on non-memory space mappings. On - * memory space, if the user didn't pass 1, we check for a - * "prefetchable" resource. This is a bit hackish, but we use - * this to workaround the inability of /sysfs to provide a write - * combine bit - */ - if (mmap_state != pci_mmap_mem) - write_combine = 0; - else if (write_combine == 0) { - if (rp->flags & IORESOURCE_PREFETCH) - write_combine = 1; - } - - /* XXX would be nice to have a way to ask for write-through */ - prot |= _PAGE_NO_CACHE; - if (write_combine) - prot &= ~_PAGE_GUARDED; - else - prot |= _PAGE_GUARDED; - - return __pgprot(prot); -} - -/* - * This one is used by /dev/mem and fbdev who have no clue about the - * PCI device, it tries to find the PCI device first and calls the - * above routine - */ -pgprot_t pci_phys_mem_access_prot(struct file *file, - unsigned long pfn, - unsigned long size, - pgprot_t protection) -{ - struct pci_dev *pdev = NULL; - struct resource *found = NULL; - unsigned long prot = pgprot_val(protection); - unsigned long offset = pfn << PAGE_SHIFT; - int i; - - if (page_is_ram(pfn)) - return __pgprot(prot); - - prot |= _PAGE_NO_CACHE | _PAGE_GUARDED; - - for_each_pci_dev(pdev) { - for (i = 0; i <= PCI_ROM_RESOURCE; i++) { - struct resource *rp = &pdev->resource[i]; - int flags = rp->flags; - - /* Active and same type? */ - if ((flags & IORESOURCE_MEM) == 0) - continue; - /* In the range of this resource? */ - if (offset < (rp->start & PAGE_MASK) || - offset > rp->end) - continue; - found = rp; - break; - } - if (found) - break; - } - if (found) { - if (found->flags & IORESOURCE_PREFETCH) - prot &= ~_PAGE_GUARDED; - pci_dev_put(pdev); - } - - DBG("non-PCI map for %lx, prot: %lx\n", offset, prot); - - return __pgprot(prot); -} - - -/* - * Perform the actual remap of the pages for a PCI device mapping, as - * appropriate for this architecture. The region in the process to map - * is described by vm_start and vm_end members of VMA, the base physical - * address is found in vm_pgoff. - * The pci device structure is provided so that architectures may make mapping - * decisions on a per-device or per-bus basis. - * - * Returns a negative error code on failure, zero on success. - */ -int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, - enum pci_mmap_state mmap_state, int write_combine) -{ - resource_size_t offset = vma->vm_pgoff << PAGE_SHIFT; - struct resource *rp; - int ret; - - rp = __pci_mmap_make_offset(dev, &offset, mmap_state); - if (rp == NULL) - return -EINVAL; - - vma->vm_pgoff = offset >> PAGE_SHIFT; - vma->vm_page_prot = __pci_mmap_set_pgprot(dev, rp, - vma->vm_page_prot, - mmap_state, write_combine); - - ret = remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, - vma->vm_end - vma->vm_start, vma->vm_page_prot); - - return ret; -} - -static ssize_t pci_show_devspec(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct pci_dev *pdev; - struct device_node *np; - - pdev = to_pci_dev (dev); - np = pci_device_to_OF_node(pdev); - if (np == NULL || np->full_name == NULL) - return 0; - return sprintf(buf, "%s", np->full_name); -} -static DEVICE_ATTR(devspec, S_IRUGO, pci_show_devspec, NULL); - -void pcibios_add_platform_entries(struct pci_dev *pdev) -{ - device_create_file(&pdev->dev, &dev_attr_devspec); -} - -#define ISA_SPACE_MASK 0x1 -#define ISA_SPACE_IO 0x1 - -static void __devinit pci_process_ISA_OF_ranges(struct device_node *isa_node, - unsigned long phb_io_base_phys, - void __iomem * phb_io_base_virt) -{ - /* Remove these asap */ - - struct pci_address { - u32 a_hi; - u32 a_mid; - u32 a_lo; - }; - - struct isa_address { - u32 a_hi; - u32 a_lo; - }; - - struct isa_range { - struct isa_address isa_addr; - struct pci_address pci_addr; - unsigned int size; - }; - - const struct isa_range *range; - unsigned long pci_addr; - unsigned int isa_addr; - unsigned int size; - int rlen = 0; - - range = of_get_property(isa_node, "ranges", &rlen); - if (range == NULL || (rlen < sizeof(struct isa_range))) { - printk(KERN_ERR "no ISA ranges or unexpected isa range size," - "mapping 64k\n"); - __ioremap_explicit(phb_io_base_phys, - (unsigned long)phb_io_base_virt, - 0x10000, _PAGE_NO_CACHE | _PAGE_GUARDED); - return; - } - - /* From "ISA Binding to 1275" - * The ranges property is laid out as an array of elements, - * each of which comprises: - * cells 0 - 1: an ISA address - * cells 2 - 4: a PCI address - * (size depending on dev->n_addr_cells) - * cell 5: the size of the range - */ - if ((range->isa_addr.a_hi && ISA_SPACE_MASK) == ISA_SPACE_IO) { - isa_addr = range->isa_addr.a_lo; - pci_addr = (unsigned long) range->pci_addr.a_mid << 32 | - range->pci_addr.a_lo; - - /* Assume these are both zero */ - if ((pci_addr != 0) || (isa_addr != 0)) { - printk(KERN_ERR "unexpected isa to pci mapping: %s\n", - __FUNCTION__); - return; - } - - size = PAGE_ALIGN(range->size); - - __ioremap_explicit(phb_io_base_phys, - (unsigned long) phb_io_base_virt, - size, _PAGE_NO_CACHE | _PAGE_GUARDED); - } -} - void __devinit pci_process_bridge_OF_ranges(struct pci_controller *hose, struct device_node *dev, int prim) { @@ -1045,155 +683,122 @@ void __devinit pci_process_bridge_OF_ranges(struct pci_controller *hose, } } -void __devinit pci_setup_phb_io(struct pci_controller *hose, int primary) +#ifdef CONFIG_HOTPLUG + +int pcibios_unmap_io_space(struct pci_bus *bus) { - unsigned long size = hose->pci_io_size; - unsigned long io_virt_offset; - struct resource *res; - struct device_node *isa_dn; + struct pci_controller *hose; - if (size == 0) - return; + WARN_ON(bus == NULL); - hose->io_base_virt = reserve_phb_iospace(size); - DBG("phb%d io_base_phys 0x%lx io_base_virt 0x%lx\n", - hose->global_number, hose->io_base_phys, - (unsigned long) hose->io_base_virt); - - if (primary) { - pci_io_base = (unsigned long)hose->io_base_virt; - isa_dn = of_find_node_by_type(NULL, "isa"); - if (isa_dn) { - isa_io_base = pci_io_base; - pci_process_ISA_OF_ranges(isa_dn, hose->io_base_phys, - hose->io_base_virt); - of_node_put(isa_dn); - } - } - - io_virt_offset = (unsigned long)hose->io_base_virt - pci_io_base; - res = &hose->io_resource; - res->start += io_virt_offset; - res->end += io_virt_offset; - - /* If this is called after the initial PCI scan, then we need to - * proceed to IO mappings now + /* If this is not a PHB, we only flush the hash table over + * the area mapped by this bridge. We don't play with the PTE + * mappings since we might have to deal with sub-page alignemnts + * so flushing the hash table is the only sane way to make sure + * that no hash entries are covering that removed bridge area + * while still allowing other busses overlapping those pages */ - if (pci_initial_scan_done) - __ioremap_explicit(hose->io_base_phys, - (unsigned long)hose->io_base_virt, - hose->pci_io_size, - _PAGE_NO_CACHE | _PAGE_GUARDED); -} + if (bus->self) { + struct resource *res = bus->resource[0]; -void __devinit pci_setup_phb_io_dynamic(struct pci_controller *hose, - int primary) + DBG("IO unmapping for PCI-PCI bridge %s\n", + pci_name(bus->self)); + + __flush_hash_table_range(&init_mm, res->start + _IO_BASE, + res->end - res->start + 1); + return 0; + } + + /* Get the host bridge */ + hose = pci_bus_to_host(bus); + + /* Check if we have IOs allocated */ + if (hose->io_base_alloc == 0) + return 0; + + DBG("IO unmapping for PHB %s\n", + ((struct device_node *)hose->arch_data)->full_name); + DBG(" alloc=0x%p\n", hose->io_base_alloc); + + /* This is a PHB, we fully unmap the IO area */ + vunmap(hose->io_base_alloc); + + return 0; +} +EXPORT_SYMBOL_GPL(pcibios_unmap_io_space); + +#endif /* CONFIG_HOTPLUG */ + +int __devinit pcibios_map_io_space(struct pci_bus *bus) { - unsigned long size = hose->pci_io_size; + struct vm_struct *area; + unsigned long phys_page; + unsigned long size_page; unsigned long io_virt_offset; - struct resource *res; + struct pci_controller *hose; - if (size == 0) - return; + WARN_ON(bus == NULL); - hose->io_base_virt = __ioremap(hose->io_base_phys, size, - _PAGE_NO_CACHE | _PAGE_GUARDED); - DBG("phb%d io_base_phys 0x%lx io_base_virt 0x%lx\n", - hose->global_number, hose->io_base_phys, - (unsigned long) hose->io_base_virt); - - if (primary) - pci_io_base = (unsigned long)hose->io_base_virt; - - io_virt_offset = (unsigned long)hose->io_base_virt - pci_io_base; - res = &hose->io_resource; - res->start += io_virt_offset; - res->end += io_virt_offset; -} - - -static int get_bus_io_range(struct pci_bus *bus, unsigned long *start_phys, - unsigned long *start_virt, unsigned long *size) -{ - struct pci_controller *hose = pci_bus_to_host(bus); - struct resource *res; - - if (bus->self) - res = bus->resource[0]; - else - /* Root Bus */ - res = &hose->io_resource; - - if (res->end == 0 && res->start == 0) - return 1; - - *start_virt = pci_io_base + res->start; - *start_phys = *start_virt + hose->io_base_phys - - (unsigned long) hose->io_base_virt; - - if (res->end > res->start) - *size = res->end - res->start + 1; - else { - printk("%s(): unexpected region 0x%lx->0x%lx\n", - __FUNCTION__, res->start, res->end); - return 1; + /* If this not a PHB, nothing to do, page tables still exist and + * thus HPTEs will be faulted in when needed + */ + if (bus->self) { + DBG("IO mapping for PCI-PCI bridge %s\n", + pci_name(bus->self)); + DBG(" virt=0x%016lx...0x%016lx\n", + bus->resource[0]->start + _IO_BASE, + bus->resource[0]->end + _IO_BASE); + return 0; } + /* Get the host bridge */ + hose = pci_bus_to_host(bus); + phys_page = _ALIGN_DOWN(hose->io_base_phys, PAGE_SIZE); + size_page = _ALIGN_UP(hose->pci_io_size, PAGE_SIZE); + + /* Make sure IO area address is clear */ + hose->io_base_alloc = NULL; + + /* If there's no IO to map on that bus, get away too */ + if (hose->pci_io_size == 0 || hose->io_base_phys == 0) + return 0; + + /* Let's allocate some IO space for that guy. We don't pass + * VM_IOREMAP because we don't care about alignment tricks that + * the core does in that case. Maybe we should due to stupid card + * with incomplete address decoding but I'd rather not deal with + * those outside of the reserved 64K legacy region. + */ + area = __get_vm_area(size_page, 0, PHB_IO_BASE, PHB_IO_END); + if (area == NULL) + return -ENOMEM; + hose->io_base_alloc = area->addr; + hose->io_base_virt = (void __iomem *)(area->addr + + hose->io_base_phys - phys_page); + + DBG("IO mapping for PHB %s\n", + ((struct device_node *)hose->arch_data)->full_name); + DBG(" phys=0x%016lx, virt=0x%p (alloc=0x%p)\n", + hose->io_base_phys, hose->io_base_virt, hose->io_base_alloc); + DBG(" size=0x%016lx (alloc=0x%016lx)\n", + hose->pci_io_size, size_page); + + /* Establish the mapping */ + if (__ioremap_at(phys_page, area->addr, size_page, + _PAGE_NO_CACHE | _PAGE_GUARDED) == NULL) + return -ENOMEM; + + /* Fixup hose IO resource */ + io_virt_offset = (unsigned long)hose->io_base_virt - _IO_BASE; + hose->io_resource.start += io_virt_offset; + hose->io_resource.end += io_virt_offset; + + DBG(" hose->io_resource=0x%016lx...0x%016lx\n", + hose->io_resource.start, hose->io_resource.end); + return 0; } - -int unmap_bus_range(struct pci_bus *bus) -{ - unsigned long start_phys; - unsigned long start_virt; - unsigned long size; - - if (!bus) { - printk(KERN_ERR "%s() expected bus\n", __FUNCTION__); - return 1; - } - - if (get_bus_io_range(bus, &start_phys, &start_virt, &size)) - return 1; - if (__iounmap_explicit((void __iomem *) start_virt, size)) - return 1; - - return 0; -} -EXPORT_SYMBOL(unmap_bus_range); - -int remap_bus_range(struct pci_bus *bus) -{ - unsigned long start_phys; - unsigned long start_virt; - unsigned long size; - - if (!bus) { - printk(KERN_ERR "%s() expected bus\n", __FUNCTION__); - return 1; - } - - - if (get_bus_io_range(bus, &start_phys, &start_virt, &size)) - return 1; - if (start_phys == 0) - return 1; - printk(KERN_DEBUG "mapping IO %lx -> %lx, size: %lx\n", start_phys, start_virt, size); - if (__ioremap_explicit(start_phys, start_virt, size, - _PAGE_NO_CACHE | _PAGE_GUARDED)) - return 1; - - return 0; -} -EXPORT_SYMBOL(remap_bus_range); - -static void phbs_remap_io(void) -{ - struct pci_controller *hose, *tmp; - - list_for_each_entry_safe(hose, tmp, &hose_list, list_node) - remap_bus_range(hose->bus); -} +EXPORT_SYMBOL_GPL(pcibios_map_io_space); static void __devinit fixup_resource(struct resource *res, struct pci_dev *dev) { @@ -1201,8 +806,7 @@ static void __devinit fixup_resource(struct resource *res, struct pci_dev *dev) unsigned long offset; if (res->flags & IORESOURCE_IO) { - offset = (unsigned long)hose->io_base_virt - pci_io_base; - + offset = (unsigned long)hose->io_base_virt - _IO_BASE; res->start += offset; res->end += offset; } else if (res->flags & IORESOURCE_MEM) { @@ -1217,9 +821,20 @@ void __devinit pcibios_fixup_device_resources(struct pci_dev *dev, /* Update device resources. */ int i; - for (i = 0; i < PCI_NUM_RESOURCES; i++) - if (dev->resource[i].flags) - fixup_resource(&dev->resource[i], dev); + DBG("%s: Fixup resources:\n", pci_name(dev)); + for (i = 0; i < PCI_NUM_RESOURCES; i++) { + struct resource *res = &dev->resource[i]; + if (!res->flags) + continue; + + DBG(" 0x%02x < %08lx:0x%016lx...0x%016lx\n", + i, res->flags, res->start, res->end); + + fixup_resource(res, dev); + + DBG(" > %08lx:0x%016lx...0x%016lx\n", + res->flags, res->start, res->end); + } } EXPORT_SYMBOL(pcibios_fixup_device_resources); @@ -1289,119 +904,6 @@ void __devinit pcibios_fixup_bus(struct pci_bus *bus) } EXPORT_SYMBOL(pcibios_fixup_bus); -/* - * Reads the interrupt pin to determine if interrupt is use by card. - * If the interrupt is used, then gets the interrupt line from the - * openfirmware and sets it in the pci_dev and pci_config line. - */ -int pci_read_irq_line(struct pci_dev *pci_dev) -{ - struct of_irq oirq; - unsigned int virq; - - DBG("Try to map irq for %s...\n", pci_name(pci_dev)); - -#ifdef DEBUG - memset(&oirq, 0xff, sizeof(oirq)); -#endif - /* Try to get a mapping from the device-tree */ - if (of_irq_map_pci(pci_dev, &oirq)) { - u8 line, pin; - - /* If that fails, lets fallback to what is in the config - * space and map that through the default controller. We - * also set the type to level low since that's what PCI - * interrupts are. If your platform does differently, then - * either provide a proper interrupt tree or don't use this - * function. - */ - if (pci_read_config_byte(pci_dev, PCI_INTERRUPT_PIN, &pin)) - return -1; - if (pin == 0) - return -1; - if (pci_read_config_byte(pci_dev, PCI_INTERRUPT_LINE, &line) || - line == 0xff) { - return -1; - } - DBG(" -> no map ! Using irq line %d from PCI config\n", line); - - virq = irq_create_mapping(NULL, line); - if (virq != NO_IRQ) - set_irq_type(virq, IRQ_TYPE_LEVEL_LOW); - } else { - DBG(" -> got one, spec %d cells (0x%08x 0x%08x...) on %s\n", - oirq.size, oirq.specifier[0], oirq.specifier[1], - oirq.controller->full_name); - - virq = irq_create_of_mapping(oirq.controller, oirq.specifier, - oirq.size); - } - if(virq == NO_IRQ) { - DBG(" -> failed to map !\n"); - return -1; - } - - DBG(" -> mapped to linux irq %d\n", virq); - - pci_dev->irq = virq; - - return 0; -} -EXPORT_SYMBOL(pci_read_irq_line); - -void pci_resource_to_user(const struct pci_dev *dev, int bar, - const struct resource *rsrc, - resource_size_t *start, resource_size_t *end) -{ - struct pci_controller *hose = pci_bus_to_host(dev->bus); - resource_size_t offset = 0; - - if (hose == NULL) - return; - - if (rsrc->flags & IORESOURCE_IO) - offset = (unsigned long)hose->io_base_virt - pci_io_base; - - /* We pass a fully fixed up address to userland for MMIO instead of - * a BAR value because X is lame and expects to be able to use that - * to pass to /dev/mem ! - * - * That means that we'll have potentially 64 bits values where some - * userland apps only expect 32 (like X itself since it thinks only - * Sparc has 64 bits MMIO) but if we don't do that, we break it on - * 32 bits CHRPs :-( - * - * Hopefully, the sysfs insterface is immune to that gunk. Once X - * has been fixed (and the fix spread enough), we can re-enable the - * 2 lines below and pass down a BAR value to userland. In that case - * we'll also have to re-enable the matching code in - * __pci_mmap_make_offset(). - * - * BenH. - */ -#if 0 - else if (rsrc->flags & IORESOURCE_MEM) - offset = hose->pci_mem_offset; -#endif - - *start = rsrc->start - offset; - *end = rsrc->end - offset; -} - -struct pci_controller* pci_find_hose_for_OF_device(struct device_node* node) -{ - if (!have_of) - return NULL; - while(node) { - struct pci_controller *hose, *tmp; - list_for_each_entry_safe(hose, tmp, &hose_list, list_node) - if (hose->arch_data == node) - return hose; - node = node->parent; - } - return NULL; -} - unsigned long pci_address_to_pio(phys_addr_t address) { struct pci_controller *hose, *tmp; @@ -1410,7 +912,7 @@ unsigned long pci_address_to_pio(phys_addr_t address) if (address >= hose->io_base_phys && address < (hose->io_base_phys + hose->pci_io_size)) { unsigned long base = - (unsigned long)hose->io_base_virt - pci_io_base; + (unsigned long)hose->io_base_virt - _IO_BASE; return base + (address - hose->io_base_phys); } } diff --git a/arch/powerpc/kernel/ppc_ksyms.c b/arch/powerpc/kernel/ppc_ksyms.c index c96fa9bd35a4..a20f1951a5ce 100644 --- a/arch/powerpc/kernel/ppc_ksyms.c +++ b/arch/powerpc/kernel/ppc_ksyms.c @@ -67,7 +67,6 @@ EXPORT_SYMBOL(ISA_DMA_THRESHOLD); EXPORT_SYMBOL(DMA_MODE_READ); EXPORT_SYMBOL(DMA_MODE_WRITE); -EXPORT_SYMBOL(do_signal); EXPORT_SYMBOL(transfer_to_handler); EXPORT_SYMBOL(do_IRQ); EXPORT_SYMBOL(machine_check_exception); @@ -106,10 +105,6 @@ EXPORT_SYMBOL(isa_mem_base); EXPORT_SYMBOL(pci_dram_offset); EXPORT_SYMBOL(pci_alloc_consistent); EXPORT_SYMBOL(pci_free_consistent); -EXPORT_SYMBOL(pci_bus_io_base); -EXPORT_SYMBOL(pci_bus_io_base_phys); -EXPORT_SYMBOL(pci_bus_mem_base_phys); -EXPORT_SYMBOL(pci_bus_to_hose); #endif /* CONFIG_PCI */ EXPORT_SYMBOL(start_thread); diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c index 6e2f03566b0d..84f000a45e36 100644 --- a/arch/powerpc/kernel/process.c +++ b/arch/powerpc/kernel/process.c @@ -219,22 +219,26 @@ void discard_lazy_cpu_state(void) } #endif /* CONFIG_SMP */ -#ifdef CONFIG_PPC_MERGE /* XXX for now */ int set_dabr(unsigned long dabr) { +#ifdef CONFIG_PPC_MERGE /* XXX for now */ if (ppc_md.set_dabr) return ppc_md.set_dabr(dabr); +#endif + /* XXX should we have a CPU_FTR_HAS_DABR ? */ +#if defined(CONFIG_PPC64) || defined(CONFIG_6xx) mtspr(SPRN_DABR, dabr); +#endif return 0; } -#endif #ifdef CONFIG_PPC64 DEFINE_PER_CPU(struct cpu_usage, cpu_usage_array); -static DEFINE_PER_CPU(unsigned long, current_dabr); #endif +static DEFINE_PER_CPU(unsigned long, current_dabr); + struct task_struct *__switch_to(struct task_struct *prev, struct task_struct *new) { @@ -299,12 +303,10 @@ struct task_struct *__switch_to(struct task_struct *prev, #endif /* CONFIG_SMP */ -#ifdef CONFIG_PPC64 /* for now */ if (unlikely(__get_cpu_var(current_dabr) != new->thread.dabr)) { set_dabr(new->thread.dabr); __get_cpu_var(current_dabr) = new->thread.dabr; } -#endif /* CONFIG_PPC64 */ new_thread = &new->thread; old_thread = ¤t->thread; @@ -473,12 +475,10 @@ void flush_thread(void) discard_lazy_cpu_state(); -#ifdef CONFIG_PPC64 /* for now */ if (current->thread.dabr) { current->thread.dabr = 0; set_dabr(0); } -#endif } void diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c index af42ddab3ab4..37ff99bd98b4 100644 --- a/arch/powerpc/kernel/prom.c +++ b/arch/powerpc/kernel/prom.c @@ -52,6 +52,7 @@ #include #include #include +#include #ifdef DEBUG #define DBG(fmt...) printk(KERN_ERR fmt) @@ -1005,7 +1006,7 @@ static void __init early_reserve_mem(void) void __init early_init_devtree(void *params) { - DBG(" -> early_init_devtree()\n"); + DBG(" -> early_init_devtree(%p)\n", params); /* Setup flat device-tree pointer */ initial_boot_params = params; @@ -1055,8 +1056,6 @@ void __init early_init_devtree(void *params) DBG(" <- early_init_devtree()\n"); } -#undef printk - int of_n_addr_cells(struct device_node* np) { const int *ip; @@ -1375,8 +1374,17 @@ static void of_node_release(struct kref *kref) struct device_node *node = kref_to_device_node(kref); struct property *prop = node->properties; - if (!OF_IS_DYNAMIC(node)) + /* We should never be releasing nodes that haven't been detached. */ + if (!of_node_check_flag(node, OF_DETACHED)) { + printk("WARNING: Bad of_node_put() on %s\n", node->full_name); + dump_stack(); + kref_init(&node->kref); return; + } + + if (!of_node_check_flag(node, OF_DYNAMIC)) + return; + while (prop) { struct property *next = prop->next; kfree(prop->name); @@ -1432,6 +1440,8 @@ void of_detach_node(const struct device_node *np) write_lock(&devtree_lock); parent = np->parent; + if (!parent) + goto out_unlock; if (allnodes == np) allnodes = np->allnext; @@ -1455,6 +1465,9 @@ void of_detach_node(const struct device_node *np) prevsib->sibling = np->sibling; } + of_node_set_flag(np, OF_DETACHED); + +out_unlock: write_unlock(&devtree_lock); } @@ -1716,22 +1729,18 @@ struct device_node *of_get_cpu_node(int cpu, unsigned int *thread) } EXPORT_SYMBOL(of_get_cpu_node); -#ifdef DEBUG +#if defined(CONFIG_DEBUG_FS) && defined(DEBUG) static struct debugfs_blob_wrapper flat_dt_blob; static int __init export_flat_device_tree(void) { struct dentry *d; - d = debugfs_create_dir("powerpc", NULL); - if (!d) - return 1; - flat_dt_blob.data = initial_boot_params; flat_dt_blob.size = initial_boot_params->totalsize; d = debugfs_create_blob("flat-device-tree", S_IFREG | S_IRUSR, - d, &flat_dt_blob); + powerpc_debugfs_root, &flat_dt_blob); if (!d) return 1; diff --git a/arch/powerpc/kernel/prom_init.c b/arch/powerpc/kernel/prom_init.c index d6047c441034..a1d582e38627 100644 --- a/arch/powerpc/kernel/prom_init.c +++ b/arch/powerpc/kernel/prom_init.c @@ -635,6 +635,7 @@ static void __init early_cmdline_parse(void) /* ibm,dynamic-reconfiguration-memory property supported */ #define OV5_DRCONF_MEMORY 0x20 #define OV5_LARGE_PAGES 0x10 /* large pages supported */ +#define OV5_DONATE_DEDICATE_CPU 0x02 /* donate dedicated CPU support */ /* PCIe/MSI support. Without MSI full PCIe is not supported */ #ifdef CONFIG_PCI_MSI #define OV5_MSI 0x01 /* PCIe/MSI support */ @@ -685,7 +686,8 @@ static unsigned char ibm_architecture_vec[] = { /* option vector 5: PAPR/OF options */ 3 - 2, /* length */ 0, /* don't ignore, don't halt */ - OV5_LPAR | OV5_SPLPAR | OV5_LARGE_PAGES | OV5_DRCONF_MEMORY | OV5_MSI, + OV5_LPAR | OV5_SPLPAR | OV5_LARGE_PAGES | OV5_DRCONF_MEMORY | + OV5_DONATE_DEDICATE_CPU | OV5_MSI, }; /* Old method - ELF header with PT_NOTE sections */ diff --git a/arch/powerpc/kernel/ptrace-common.h b/arch/powerpc/kernel/ptrace-common.h deleted file mode 100644 index 8797ae737a7b..000000000000 --- a/arch/powerpc/kernel/ptrace-common.h +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright (c) 2002 Stephen Rothwell, IBM Coproration - * Extracted from ptrace.c and ptrace32.c - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file README.legal in the main directory of - * this archive for more details. - */ - -#ifndef _PPC64_PTRACE_COMMON_H -#define _PPC64_PTRACE_COMMON_H - -#include - -/* - * Set of msr bits that gdb can change on behalf of a process. - */ -#define MSR_DEBUGCHANGE (MSR_FE0 | MSR_SE | MSR_BE | MSR_FE1) - -/* - * Get contents of register REGNO in task TASK. - */ -static inline unsigned long get_reg(struct task_struct *task, int regno) -{ - unsigned long tmp = 0; - - /* - * Put the correct FP bits in, they might be wrong as a result - * of our lazy FP restore. - */ - if (regno == PT_MSR) { - tmp = ((unsigned long *)task->thread.regs)[PT_MSR]; - tmp |= task->thread.fpexc_mode; - } else if (regno < (sizeof(struct pt_regs) / sizeof(unsigned long))) { - tmp = ((unsigned long *)task->thread.regs)[regno]; - } - - return tmp; -} - -/* - * Write contents of register REGNO in task TASK. - */ -static inline int put_reg(struct task_struct *task, int regno, - unsigned long data) -{ - if (regno < PT_SOFTE) { - if (regno == PT_MSR) - data = (data & MSR_DEBUGCHANGE) - | (task->thread.regs->msr & ~MSR_DEBUGCHANGE); - ((unsigned long *)task->thread.regs)[regno] = data; - return 0; - } - return -EIO; -} - -static inline void set_single_step(struct task_struct *task) -{ - struct pt_regs *regs = task->thread.regs; - if (regs != NULL) - regs->msr |= MSR_SE; - set_tsk_thread_flag(task, TIF_SINGLESTEP); -} - -static inline void clear_single_step(struct task_struct *task) -{ - struct pt_regs *regs = task->thread.regs; - if (regs != NULL) - regs->msr &= ~MSR_SE; - clear_tsk_thread_flag(task, TIF_SINGLESTEP); -} - -#ifdef CONFIG_ALTIVEC -/* - * Get/set all the altivec registers vr0..vr31, vscr, vrsave, in one go. - * The transfer totals 34 quadword. Quadwords 0-31 contain the - * corresponding vector registers. Quadword 32 contains the vscr as the - * last word (offset 12) within that quadword. Quadword 33 contains the - * vrsave as the first word (offset 0) within the quadword. - * - * This definition of the VMX state is compatible with the current PPC32 - * ptrace interface. This allows signal handling and ptrace to use the - * same structures. This also simplifies the implementation of a bi-arch - * (combined (32- and 64-bit) gdb. - */ - -/* - * Get contents of AltiVec register state in task TASK - */ -static inline int get_vrregs(unsigned long __user *data, - struct task_struct *task) -{ - unsigned long regsize; - - /* copy AltiVec registers VR[0] .. VR[31] */ - regsize = 32 * sizeof(vector128); - if (copy_to_user(data, task->thread.vr, regsize)) - return -EFAULT; - data += (regsize / sizeof(unsigned long)); - - /* copy VSCR */ - regsize = 1 * sizeof(vector128); - if (copy_to_user(data, &task->thread.vscr, regsize)) - return -EFAULT; - data += (regsize / sizeof(unsigned long)); - - /* copy VRSAVE */ - if (put_user(task->thread.vrsave, (u32 __user *)data)) - return -EFAULT; - - return 0; -} - -/* - * Write contents of AltiVec register state into task TASK. - */ -static inline int set_vrregs(struct task_struct *task, - unsigned long __user *data) -{ - unsigned long regsize; - - /* copy AltiVec registers VR[0] .. VR[31] */ - regsize = 32 * sizeof(vector128); - if (copy_from_user(task->thread.vr, data, regsize)) - return -EFAULT; - data += (regsize / sizeof(unsigned long)); - - /* copy VSCR */ - regsize = 1 * sizeof(vector128); - if (copy_from_user(&task->thread.vscr, data, regsize)) - return -EFAULT; - data += (regsize / sizeof(unsigned long)); - - /* copy VRSAVE */ - if (get_user(task->thread.vrsave, (u32 __user *)data)) - return -EFAULT; - - return 0; -} -#endif - -static inline int ptrace_set_debugreg(struct task_struct *task, - unsigned long addr, unsigned long data) -{ - /* We only support one DABR and no IABRS at the moment */ - if (addr > 0) - return -EINVAL; - - /* The bottom 3 bits are flags */ - if ((data & ~0x7UL) >= TASK_SIZE) - return -EIO; - - /* Ensure translation is on */ - if (data && !(data & DABR_TRANSLATION)) - return -EIO; - - task->thread.dabr = data; - return 0; -} - -#endif /* _PPC64_PTRACE_COMMON_H */ diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c index bf76562167c3..0fb53950da43 100644 --- a/arch/powerpc/kernel/ptrace.c +++ b/arch/powerpc/kernel/ptrace.c @@ -35,11 +35,11 @@ #include #include -#ifdef CONFIG_PPC64 -#include "ptrace-common.h" -#endif +/* + * does not yet catch signals sent when the child dies. + * in exit.c or in signal.c. + */ -#ifdef CONFIG_PPC32 /* * Set of msr bits that gdb can change on behalf of a process. */ @@ -48,65 +48,117 @@ #else #define MSR_DEBUGCHANGE (MSR_SE | MSR_BE) #endif -#endif /* CONFIG_PPC32 */ /* - * does not yet catch signals sent when the child dies. - * in exit.c or in signal.c. + * Max register writeable via put_reg */ - #ifdef CONFIG_PPC32 +#define PT_MAX_PUT_REG PT_MQ +#else +#define PT_MAX_PUT_REG PT_CCR +#endif + /* * Get contents of register REGNO in task TASK. */ -static inline unsigned long get_reg(struct task_struct *task, int regno) +unsigned long ptrace_get_reg(struct task_struct *task, int regno) { - if (regno < sizeof(struct pt_regs) / sizeof(unsigned long) - && task->thread.regs != NULL) + unsigned long tmp = 0; + + if (task->thread.regs == NULL) + return -EIO; + + if (regno == PT_MSR) { + tmp = ((unsigned long *)task->thread.regs)[PT_MSR]; + return tmp | task->thread.fpexc_mode; + } + + if (regno < (sizeof(struct pt_regs) / sizeof(unsigned long))) return ((unsigned long *)task->thread.regs)[regno]; - return (0); + + return -EIO; } /* * Write contents of register REGNO in task TASK. */ -static inline int put_reg(struct task_struct *task, int regno, - unsigned long data) +int ptrace_put_reg(struct task_struct *task, int regno, unsigned long data) { - if (regno <= PT_MQ && task->thread.regs != NULL) { + if (task->thread.regs == NULL) + return -EIO; + + if (regno <= PT_MAX_PUT_REG || regno == PT_TRAP) { if (regno == PT_MSR) data = (data & MSR_DEBUGCHANGE) | (task->thread.regs->msr & ~MSR_DEBUGCHANGE); + /* We prevent mucking around with the reserved area of trap + * which are used internally by the kernel + */ + if (regno == PT_TRAP) + data &= 0xfff0; ((unsigned long *)task->thread.regs)[regno] = data; return 0; } return -EIO; } + +static int get_fpregs(void __user *data, struct task_struct *task, + int has_fpscr) +{ + unsigned int count = has_fpscr ? 33 : 32; + + if (copy_to_user(data, task->thread.fpr, count * sizeof(double))) + return -EFAULT; + return 0; +} + +static int set_fpregs(void __user *data, struct task_struct *task, + int has_fpscr) +{ + unsigned int count = has_fpscr ? 33 : 32; + + if (copy_from_user(task->thread.fpr, data, count * sizeof(double))) + return -EFAULT; + return 0; +} + + #ifdef CONFIG_ALTIVEC +/* + * Get/set all the altivec registers vr0..vr31, vscr, vrsave, in one go. + * The transfer totals 34 quadword. Quadwords 0-31 contain the + * corresponding vector registers. Quadword 32 contains the vscr as the + * last word (offset 12) within that quadword. Quadword 33 contains the + * vrsave as the first word (offset 0) within the quadword. + * + * This definition of the VMX state is compatible with the current PPC32 + * ptrace interface. This allows signal handling and ptrace to use the + * same structures. This also simplifies the implementation of a bi-arch + * (combined (32- and 64-bit) gdb. + */ + /* * Get contents of AltiVec register state in task TASK */ -static inline int get_vrregs(unsigned long __user *data, struct task_struct *task) +static int get_vrregs(unsigned long __user *data, struct task_struct *task) { - int i, j; - - if (!access_ok(VERIFY_WRITE, data, 133 * sizeof(unsigned long))) - return -EFAULT; + unsigned long regsize; /* copy AltiVec registers VR[0] .. VR[31] */ - for (i = 0; i < 32; i++) - for (j = 0; j < 4; j++, data++) - if (__put_user(task->thread.vr[i].u[j], data)) - return -EFAULT; + regsize = 32 * sizeof(vector128); + if (copy_to_user(data, task->thread.vr, regsize)) + return -EFAULT; + data += (regsize / sizeof(unsigned long)); /* copy VSCR */ - for (i = 0; i < 4; i++, data++) - if (__put_user(task->thread.vscr.u[i], data)) - return -EFAULT; + regsize = 1 * sizeof(vector128); + if (copy_to_user(data, &task->thread.vscr, regsize)) + return -EFAULT; + data += (regsize / sizeof(unsigned long)); - /* copy VRSAVE */ - if (__put_user(task->thread.vrsave, data)) + /* copy VRSAVE */ + if (put_user(task->thread.vrsave, (u32 __user *)data)) return -EFAULT; return 0; @@ -115,31 +167,29 @@ static inline int get_vrregs(unsigned long __user *data, struct task_struct *tas /* * Write contents of AltiVec register state into task TASK. */ -static inline int set_vrregs(struct task_struct *task, unsigned long __user *data) +static int set_vrregs(struct task_struct *task, unsigned long __user *data) { - int i, j; - - if (!access_ok(VERIFY_READ, data, 133 * sizeof(unsigned long))) - return -EFAULT; + unsigned long regsize; /* copy AltiVec registers VR[0] .. VR[31] */ - for (i = 0; i < 32; i++) - for (j = 0; j < 4; j++, data++) - if (__get_user(task->thread.vr[i].u[j], data)) - return -EFAULT; + regsize = 32 * sizeof(vector128); + if (copy_from_user(task->thread.vr, data, regsize)) + return -EFAULT; + data += (regsize / sizeof(unsigned long)); /* copy VSCR */ - for (i = 0; i < 4; i++, data++) - if (__get_user(task->thread.vscr.u[i], data)) - return -EFAULT; + regsize = 1 * sizeof(vector128); + if (copy_from_user(&task->thread.vscr, data, regsize)) + return -EFAULT; + data += (regsize / sizeof(unsigned long)); /* copy VRSAVE */ - if (__get_user(task->thread.vrsave, data)) + if (get_user(task->thread.vrsave, (u32 __user *)data)) return -EFAULT; return 0; } -#endif +#endif /* CONFIG_ALTIVEC */ #ifdef CONFIG_SPE @@ -156,7 +206,7 @@ static inline int set_vrregs(struct task_struct *task, unsigned long __user *dat /* * Get contents of SPE register state in task TASK. */ -static inline int get_evrregs(unsigned long *data, struct task_struct *task) +static int get_evrregs(unsigned long *data, struct task_struct *task) { int i; @@ -182,7 +232,7 @@ static inline int get_evrregs(unsigned long *data, struct task_struct *task) /* * Write contents of SPE register state into task TASK. */ -static inline int set_evrregs(struct task_struct *task, unsigned long *data) +static int set_evrregs(struct task_struct *task, unsigned long *data) { int i; @@ -205,8 +255,8 @@ static inline int set_evrregs(struct task_struct *task, unsigned long *data) } #endif /* CONFIG_SPE */ -static inline void -set_single_step(struct task_struct *task) + +static void set_single_step(struct task_struct *task) { struct pt_regs *regs = task->thread.regs; @@ -221,8 +271,7 @@ set_single_step(struct task_struct *task) set_tsk_thread_flag(task, TIF_SINGLESTEP); } -static inline void -clear_single_step(struct task_struct *task) +static void clear_single_step(struct task_struct *task) { struct pt_regs *regs = task->thread.regs; @@ -236,7 +285,25 @@ clear_single_step(struct task_struct *task) } clear_tsk_thread_flag(task, TIF_SINGLESTEP); } -#endif /* CONFIG_PPC32 */ + +static int ptrace_set_debugreg(struct task_struct *task, unsigned long addr, + unsigned long data) +{ + /* We only support one DABR and no IABRS at the moment */ + if (addr > 0) + return -EINVAL; + + /* The bottom 3 bits are flags */ + if ((data & ~0x7UL) >= TASK_SIZE) + return -EIO; + + /* Ensure translation is on */ + if (data && !(data & DABR_TRANSLATION)) + return -EIO; + + task->thread.dabr = data; + return 0; +} /* * Called by kernel/ptrace.c when detaching.. @@ -249,6 +316,62 @@ void ptrace_disable(struct task_struct *child) clear_single_step(child); } +/* + * Here are the old "legacy" powerpc specific getregs/setregs ptrace calls, + * we mark them as obsolete now, they will be removed in a future version + */ +static long arch_ptrace_old(struct task_struct *child, long request, long addr, + long data) +{ + int ret = -EPERM; + + switch(request) { + case PPC_PTRACE_GETREGS: { /* Get GPRs 0 - 31. */ + int i; + unsigned long *reg = &((unsigned long *)child->thread.regs)[0]; + unsigned long __user *tmp = (unsigned long __user *)addr; + + for (i = 0; i < 32; i++) { + ret = put_user(*reg, tmp); + if (ret) + break; + reg++; + tmp++; + } + break; + } + + case PPC_PTRACE_SETREGS: { /* Set GPRs 0 - 31. */ + int i; + unsigned long *reg = &((unsigned long *)child->thread.regs)[0]; + unsigned long __user *tmp = (unsigned long __user *)addr; + + for (i = 0; i < 32; i++) { + ret = get_user(*reg, tmp); + if (ret) + break; + reg++; + tmp++; + } + break; + } + + case PPC_PTRACE_GETFPREGS: { /* Get FPRs 0 - 31. */ + flush_fp_to_thread(child); + ret = get_fpregs((void __user *)addr, child, 0); + break; + } + + case PPC_PTRACE_SETFPREGS: { /* Get FPRs 0 - 31. */ + flush_fp_to_thread(child); + ret = set_fpregs((void __user *)addr, child, 0); + break; + } + + } + return ret; +} + long arch_ptrace(struct task_struct *child, long request, long addr, long data) { int ret = -EPERM; @@ -284,11 +407,9 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) #endif break; -#ifdef CONFIG_PPC32 CHECK_FULL_REGS(child->thread.regs); -#endif if (index < PT_FPR0) { - tmp = get_reg(child, (int) index); + tmp = ptrace_get_reg(child, (int) index); } else { flush_fp_to_thread(child); tmp = ((unsigned long *)child->thread.fpr)[index - PT_FPR0]; @@ -323,13 +444,9 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) #endif break; -#ifdef CONFIG_PPC32 CHECK_FULL_REGS(child->thread.regs); -#endif - if (index == PT_ORIG_R3) - break; if (index < PT_FPR0) { - ret = put_reg(child, index, data); + ret = ptrace_put_reg(child, index, data); } else { flush_fp_to_thread(child); ((unsigned long *)child->thread.fpr)[index - PT_FPR0] = data; @@ -384,7 +501,6 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) break; } -#ifdef CONFIG_PPC64 case PTRACE_GET_DEBUGREG: { ret = -EINVAL; /* We only support one DABR and no IABRS at the moment */ @@ -398,73 +514,61 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) case PTRACE_SET_DEBUGREG: ret = ptrace_set_debugreg(child, addr, data); break; -#endif case PTRACE_DETACH: ret = ptrace_detach(child, data); break; - case PPC_PTRACE_GETREGS: { /* Get GPRs 0 - 31. */ - int i; - unsigned long *reg = &((unsigned long *)child->thread.regs)[0]; - unsigned long __user *tmp = (unsigned long __user *)addr; - - for (i = 0; i < 32; i++) { - ret = put_user(*reg, tmp); - if (ret) - break; - reg++; - tmp++; +#ifdef CONFIG_PPC64 + case PTRACE_GETREGS64: +#endif + case PTRACE_GETREGS: { /* Get all pt_regs from the child. */ + int ui; + if (!access_ok(VERIFY_WRITE, (void __user *)data, + sizeof(struct pt_regs))) { + ret = -EIO; + break; + } + ret = 0; + for (ui = 0; ui < PT_REGS_COUNT; ui ++) { + ret |= __put_user(ptrace_get_reg(child, ui), + (unsigned long __user *) data); + data += sizeof(long); } break; } - case PPC_PTRACE_SETREGS: { /* Set GPRs 0 - 31. */ - int i; - unsigned long *reg = &((unsigned long *)child->thread.regs)[0]; - unsigned long __user *tmp = (unsigned long __user *)addr; - - for (i = 0; i < 32; i++) { - ret = get_user(*reg, tmp); +#ifdef CONFIG_PPC64 + case PTRACE_SETREGS64: +#endif + case PTRACE_SETREGS: { /* Set all gp regs in the child. */ + unsigned long tmp; + int ui; + if (!access_ok(VERIFY_READ, (void __user *)data, + sizeof(struct pt_regs))) { + ret = -EIO; + break; + } + ret = 0; + for (ui = 0; ui < PT_REGS_COUNT; ui ++) { + ret = __get_user(tmp, (unsigned long __user *) data); if (ret) break; - reg++; - tmp++; + ptrace_put_reg(child, ui, tmp); + data += sizeof(long); } break; } - case PPC_PTRACE_GETFPREGS: { /* Get FPRs 0 - 31. */ - int i; - unsigned long *reg = &((unsigned long *)child->thread.fpr)[0]; - unsigned long __user *tmp = (unsigned long __user *)addr; - + case PTRACE_GETFPREGS: { /* Get the child FPU state (FPR0...31 + FPSCR) */ flush_fp_to_thread(child); - - for (i = 0; i < 32; i++) { - ret = put_user(*reg, tmp); - if (ret) - break; - reg++; - tmp++; - } + ret = get_fpregs((void __user *)data, child, 1); break; } - case PPC_PTRACE_SETFPREGS: { /* Get FPRs 0 - 31. */ - int i; - unsigned long *reg = &((unsigned long *)child->thread.fpr)[0]; - unsigned long __user *tmp = (unsigned long __user *)addr; - + case PTRACE_SETFPREGS: { /* Set the child FPU state (FPR0...31 + FPSCR) */ flush_fp_to_thread(child); - - for (i = 0; i < 32; i++) { - ret = get_user(*reg, tmp); - if (ret) - break; - reg++; - tmp++; - } + ret = set_fpregs((void __user *)data, child, 1); break; } @@ -499,11 +603,18 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) break; #endif + /* Old reverse args ptrace callss */ + case PPC_PTRACE_GETREGS: /* Get GPRs 0 - 31. */ + case PPC_PTRACE_SETREGS: /* Set GPRs 0 - 31. */ + case PPC_PTRACE_GETFPREGS: /* Get FPRs 0 - 31. */ + case PPC_PTRACE_SETFPREGS: /* Get FPRs 0 - 31. */ + ret = arch_ptrace_old(child, request, addr, data); + break; + default: ret = ptrace_request(child, request, addr, data); break; } - return ret; } diff --git a/arch/powerpc/kernel/ptrace32.c b/arch/powerpc/kernel/ptrace32.c index 9b9a230349bc..9e6baeac0fb1 100644 --- a/arch/powerpc/kernel/ptrace32.c +++ b/arch/powerpc/kernel/ptrace32.c @@ -33,13 +33,55 @@ #include #include -#include "ptrace-common.h" - /* * does not yet catch signals sent when the child dies. * in exit.c or in signal.c. */ +/* + * Here are the old "legacy" powerpc specific getregs/setregs ptrace calls, + * we mark them as obsolete now, they will be removed in a future version + */ +static long compat_ptrace_old(struct task_struct *child, long request, + long addr, long data) +{ + int ret = -EPERM; + + switch(request) { + case PPC_PTRACE_GETREGS: { /* Get GPRs 0 - 31. */ + int i; + unsigned long *reg = &((unsigned long *)child->thread.regs)[0]; + unsigned int __user *tmp = (unsigned int __user *)addr; + + for (i = 0; i < 32; i++) { + ret = put_user(*reg, tmp); + if (ret) + break; + reg++; + tmp++; + } + break; + } + + case PPC_PTRACE_SETREGS: { /* Set GPRs 0 - 31. */ + int i; + unsigned long *reg = &((unsigned long *)child->thread.regs)[0]; + unsigned int __user *tmp = (unsigned int __user *)addr; + + for (i = 0; i < 32; i++) { + ret = get_user(*reg, tmp); + if (ret) + break; + reg++; + tmp++; + } + break; + } + + } + return ret; +} + long compat_sys_ptrace(int request, int pid, unsigned long addr, unsigned long data) { @@ -123,7 +165,7 @@ long compat_sys_ptrace(int request, int pid, unsigned long addr, break; if (index < PT_FPR0) { - tmp = get_reg(child, index); + tmp = ptrace_get_reg(child, index); } else { flush_fp_to_thread(child); /* @@ -162,7 +204,9 @@ long compat_sys_ptrace(int request, int pid, unsigned long addr, else part = 0; /* want the 1st half of the register (left-most). */ - /* Validate the input - check to see if address is on the wrong boundary or beyond the end of the user area */ + /* Validate the input - check to see if address is on the wrong boundary + * or beyond the end of the user area + */ if ((addr & 3) || numReg > PT_FPSCR) break; @@ -170,7 +214,7 @@ long compat_sys_ptrace(int request, int pid, unsigned long addr, flush_fp_to_thread(child); tmp = ((unsigned long int *)child->thread.fpr)[numReg - PT_FPR0]; } else { /* register within PT_REGS struct */ - tmp = get_reg(child, numReg); + tmp = ptrace_get_reg(child, numReg); } reg32bits = ((u32*)&tmp)[part]; ret = put_user(reg32bits, (u32 __user *)data); @@ -226,10 +270,8 @@ long compat_sys_ptrace(int request, int pid, unsigned long addr, if ((addr & 3) || (index > PT_FPSCR32)) break; - if (index == PT_ORIG_R3) - break; if (index < PT_FPR0) { - ret = put_reg(child, index, data); + ret = ptrace_put_reg(child, index, data); } else { flush_fp_to_thread(child); /* @@ -258,70 +300,25 @@ long compat_sys_ptrace(int request, int pid, unsigned long addr, /* Determine which register the user wants */ index = (u64)addr >> 2; numReg = index / 2; + /* * Validate the input - check to see if address is on the * wrong boundary or beyond the end of the user area */ if ((addr & 3) || (numReg > PT_FPSCR)) break; - /* Insure it is a register we let them change */ - if ((numReg == PT_ORIG_R3) - || ((numReg > PT_CCR) && (numReg < PT_FPR0))) - break; - if (numReg >= PT_FPR0) { + if (numReg < PT_FPR0) { + unsigned long freg = ptrace_get_reg(child, numReg); + if (index % 2) + freg = (freg & ~0xfffffffful) | (data & 0xfffffffful); + else + freg = (freg & 0xfffffffful) | (data << 32); + ret = ptrace_put_reg(child, numReg, freg); + } else { flush_fp_to_thread(child); + ((unsigned int *)child->thread.regs)[index] = data; + ret = 0; } - if (numReg == PT_MSR) - data = (data & MSR_DEBUGCHANGE) - | (child->thread.regs->msr & ~MSR_DEBUGCHANGE); - ((u32*)child->thread.regs)[index] = data; - ret = 0; - break; - } - - case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ - case PTRACE_CONT: { /* restart after signal. */ - ret = -EIO; - if (!valid_signal(data)) - break; - if (request == PTRACE_SYSCALL) - set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); - else - clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); - child->exit_code = data; - /* make sure the single step bit is not set. */ - clear_single_step(child); - wake_up_process(child); - ret = 0; - break; - } - - /* - * make the child exit. Best I can do is send it a sigkill. - * perhaps it should be put in the status that it wants to - * exit. - */ - case PTRACE_KILL: { - ret = 0; - if (child->exit_state == EXIT_ZOMBIE) /* already dead */ - break; - child->exit_code = SIGKILL; - /* make sure the single step bit is not set. */ - clear_single_step(child); - wake_up_process(child); - break; - } - - case PTRACE_SINGLESTEP: { /* set the trap flag. */ - ret = -EIO; - if (!valid_signal(data)) - break; - clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); - set_single_step(child); - child->exit_code = data; - /* give it a chance to run. */ - wake_up_process(child); - ret = 0; break; } @@ -334,95 +331,67 @@ long compat_sys_ptrace(int request, int pid, unsigned long addr, break; } - case PTRACE_SET_DEBUGREG: - ret = ptrace_set_debugreg(child, addr, data); - break; - - case PTRACE_DETACH: - ret = ptrace_detach(child, data); - break; - - case PPC_PTRACE_GETREGS: { /* Get GPRs 0 - 31. */ - int i; - unsigned long *reg = &((unsigned long *)child->thread.regs)[0]; - unsigned int __user *tmp = (unsigned int __user *)addr; - - for (i = 0; i < 32; i++) { - ret = put_user(*reg, tmp); - if (ret) - break; - reg++; - tmp++; - } - break; - } - - case PPC_PTRACE_SETREGS: { /* Set GPRs 0 - 31. */ - int i; - unsigned long *reg = &((unsigned long *)child->thread.regs)[0]; - unsigned int __user *tmp = (unsigned int __user *)addr; - - for (i = 0; i < 32; i++) { - ret = get_user(*reg, tmp); - if (ret) - break; - reg++; - tmp++; - } - break; - } - - case PPC_PTRACE_GETFPREGS: { /* Get FPRs 0 - 31. */ - int i; - unsigned long *reg = &((unsigned long *)child->thread.fpr)[0]; - unsigned int __user *tmp = (unsigned int __user *)addr; - - flush_fp_to_thread(child); - - for (i = 0; i < 32; i++) { - ret = put_user(*reg, tmp); - if (ret) - break; - reg++; - tmp++; - } - break; - } - - case PPC_PTRACE_SETFPREGS: { /* Get FPRs 0 - 31. */ - int i; - unsigned long *reg = &((unsigned long *)child->thread.fpr)[0]; - unsigned int __user *tmp = (unsigned int __user *)addr; - - flush_fp_to_thread(child); - - for (i = 0; i < 32; i++) { - ret = get_user(*reg, tmp); - if (ret) - break; - reg++; - tmp++; - } - break; - } - case PTRACE_GETEVENTMSG: ret = put_user(child->ptrace_message, (unsigned int __user *) data); break; -#ifdef CONFIG_ALTIVEC + case PTRACE_GETREGS: { /* Get all pt_regs from the child. */ + int ui; + if (!access_ok(VERIFY_WRITE, (void __user *)data, + PT_REGS_COUNT * sizeof(int))) { + ret = -EIO; + break; + } + ret = 0; + for (ui = 0; ui < PT_REGS_COUNT; ui ++) { + ret |= __put_user(ptrace_get_reg(child, ui), + (unsigned int __user *) data); + data += sizeof(int); + } + break; + } + + case PTRACE_SETREGS: { /* Set all gp regs in the child. */ + unsigned long tmp; + int ui; + if (!access_ok(VERIFY_READ, (void __user *)data, + PT_REGS_COUNT * sizeof(int))) { + ret = -EIO; + break; + } + ret = 0; + for (ui = 0; ui < PT_REGS_COUNT; ui ++) { + ret = __get_user(tmp, (unsigned int __user *) data); + if (ret) + break; + ptrace_put_reg(child, ui, tmp); + data += sizeof(int); + } + break; + } + + case PTRACE_GETFPREGS: + case PTRACE_SETFPREGS: case PTRACE_GETVRREGS: - /* Get the child altivec register state. */ - flush_altivec_to_thread(child); - ret = get_vrregs((unsigned long __user *)data, child); + case PTRACE_SETVRREGS: + case PTRACE_GETREGS64: + case PTRACE_SETREGS64: + case PPC_PTRACE_GETFPREGS: + case PPC_PTRACE_SETFPREGS: + case PTRACE_KILL: + case PTRACE_SINGLESTEP: + case PTRACE_DETACH: + case PTRACE_SET_DEBUGREG: + case PTRACE_SYSCALL: + case PTRACE_CONT: + ret = arch_ptrace(child, request, addr, data); break; - case PTRACE_SETVRREGS: - /* Set the child altivec register state. */ - flush_altivec_to_thread(child); - ret = set_vrregs(child, (unsigned long __user *)data); + /* Old reverse args ptrace callss */ + case PPC_PTRACE_GETREGS: /* Get GPRs 0 - 31. */ + case PPC_PTRACE_SETREGS: /* Set GPRs 0 - 31. */ + ret = compat_ptrace_old(child, request, addr, data); break; -#endif default: ret = ptrace_request(child, request, addr, data); diff --git a/arch/powerpc/kernel/rtas_pci.c b/arch/powerpc/kernel/rtas_pci.c index f2286822be09..a5de6211b97a 100644 --- a/arch/powerpc/kernel/rtas_pci.c +++ b/arch/powerpc/kernel/rtas_pci.c @@ -278,10 +278,8 @@ void __init find_and_init_phbs(void) { struct device_node *node; struct pci_controller *phb; - unsigned int index; struct device_node *root = of_find_node_by_path("/"); - index = 0; for (node = of_get_next_child(root, NULL); node != NULL; node = of_get_next_child(root, node)) { @@ -295,8 +293,7 @@ void __init find_and_init_phbs(void) continue; rtas_setup_phb(phb); pci_process_bridge_OF_ranges(phb, node, 0); - pci_setup_phb_io(phb, index == 0); - index++; + isa_bridge_find_early(phb); } of_node_put(root); @@ -335,7 +332,7 @@ int pcibios_remove_root_bus(struct pci_controller *phb) return 1; } - rc = unmap_bus_range(b); + rc = pcibios_unmap_io_space(b); if (rc) { printk(KERN_ERR "%s: failed to unmap IO on bus %s\n", __FUNCTION__, b->name); diff --git a/arch/powerpc/kernel/setup-common.c b/arch/powerpc/kernel/setup-common.c index ed07a198f8d6..4924c48cb1ff 100644 --- a/arch/powerpc/kernel/setup-common.c +++ b/arch/powerpc/kernel/setup-common.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -486,6 +487,14 @@ int check_legacy_ioport(unsigned long base_port) switch(base_port) { case I8042_DATA_REG: + if (!(np = of_find_compatible_node(NULL, NULL, "pnpPNP,303"))) + np = of_find_compatible_node(NULL, NULL, "pnpPNP,f03"); + if (np) { + parent = of_get_parent(np); + of_node_put(np); + np = parent; + break; + } np = of_find_node_by_type(NULL, "8042"); break; case FDC_BASE: /* FDC1 */ @@ -571,3 +580,15 @@ static int __init check_cache_coherency(void) late_initcall(check_cache_coherency); #endif /* CONFIG_CHECK_CACHE_COHERENCY */ + +#ifdef CONFIG_DEBUG_FS +struct dentry *powerpc_debugfs_root; + +static int powerpc_debugfs_init(void) +{ + powerpc_debugfs_root = debugfs_create_dir("powerpc", NULL); + + return powerpc_debugfs_root == NULL; +} +arch_initcall(powerpc_debugfs_init); +#endif diff --git a/arch/powerpc/kernel/setup_32.c b/arch/powerpc/kernel/setup_32.c index 35f8f443c14f..7ec6ba56d83d 100644 --- a/arch/powerpc/kernel/setup_32.c +++ b/arch/powerpc/kernel/setup_32.c @@ -262,13 +262,11 @@ void __init setup_arch(char **cmdline_p) * Systems with OF can look in the properties on the cpu node(s) * for a possibly more accurate value. */ - if (cpu_has_feature(CPU_FTR_SPLIT_ID_CACHE)) { - dcache_bsize = cur_cpu_spec->dcache_bsize; - icache_bsize = cur_cpu_spec->icache_bsize; - ucache_bsize = 0; - } else - ucache_bsize = dcache_bsize = icache_bsize - = cur_cpu_spec->dcache_bsize; + dcache_bsize = cur_cpu_spec->dcache_bsize; + icache_bsize = cur_cpu_spec->icache_bsize; + ucache_bsize = 0; + if (cpu_has_feature(CPU_FTR_UNIFIED_ID_CACHE)) + ucache_bsize = icache_bsize = dcache_bsize; /* reboot on panic */ panic_timeout = 180; diff --git a/arch/powerpc/kernel/setup_64.c b/arch/powerpc/kernel/setup_64.c index 6018178708a5..bc43bba05cf8 100644 --- a/arch/powerpc/kernel/setup_64.c +++ b/arch/powerpc/kernel/setup_64.c @@ -350,13 +350,11 @@ void __init setup_system(void) { DBG(" -> setup_system()\n"); - /* Apply the CPUs-specific and firmware specific fixups to kernel - * text (nop out sections not relevant to this CPU or this firmware) + /* Apply CPUs-specific fixups to kernel text (nop out sections + * not relevant to this CPU) */ do_feature_fixups(cur_cpu_spec->cpu_features, &__start___ftr_fixup, &__stop___ftr_fixup); - do_feature_fixups(powerpc_firmware_features, - &__start___fw_ftr_fixup, &__stop___fw_ftr_fixup); /* * Unflatten the device-tree passed by prom_init or kexec @@ -394,6 +392,12 @@ void __init setup_system(void) if (ppc_md.init_early) ppc_md.init_early(); + /* Apply firmware specific fixups to kernel text (nop out + * sections not relevant to this firmware) + */ + do_feature_fixups(powerpc_firmware_features, + &__start___fw_ftr_fixup, &__stop___fw_ftr_fixup); + /* * We can discover serial ports now since the above did setup the * hash table management for us, thus ioremap works. We do that early diff --git a/arch/powerpc/kernel/signal.c b/arch/powerpc/kernel/signal.c new file mode 100644 index 000000000000..c434d6c4e4e6 --- /dev/null +++ b/arch/powerpc/kernel/signal.c @@ -0,0 +1,180 @@ +/* + * Common signal handling code for both 32 and 64 bits + * + * Copyright (c) 2007 Benjamin Herrenschmidt, IBM Coproration + * Extracted from signal_32.c and signal_64.c + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file README.legal in the main directory of + * this archive for more details. + */ + +#include +#include +#include +#include + +#include "signal.h" + +/* + * Allocate space for the signal frame + */ +void __user * get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, + size_t frame_size) +{ + unsigned long oldsp, newsp; + + /* Default to using normal stack */ + oldsp = regs->gpr[1]; + + /* Check for alt stack */ + if ((ka->sa.sa_flags & SA_ONSTACK) && + current->sas_ss_size && !on_sig_stack(oldsp)) + oldsp = (current->sas_ss_sp + current->sas_ss_size); + + /* Get aligned frame */ + newsp = (oldsp - frame_size) & ~0xFUL; + + /* Check access */ + if (!access_ok(VERIFY_WRITE, (void __user *)newsp, oldsp - newsp)) + return NULL; + + return (void __user *)newsp; +} + + +/* + * Restore the user process's signal mask + */ +void restore_sigmask(sigset_t *set) +{ + sigdelsetmask(set, ~_BLOCKABLE); + spin_lock_irq(¤t->sighand->siglock); + current->blocked = *set; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); +} + +static void check_syscall_restart(struct pt_regs *regs, struct k_sigaction *ka, + int has_handler) +{ + unsigned long ret = regs->gpr[3]; + int restart = 1; + + /* syscall ? */ + if (TRAP(regs) != 0x0C00) + return; + + /* error signalled ? */ + if (!(regs->ccr & 0x10000000)) + return; + + switch (ret) { + case ERESTART_RESTARTBLOCK: + case ERESTARTNOHAND: + /* ERESTARTNOHAND means that the syscall should only be + * restarted if there was no handler for the signal, and since + * we only get here if there is a handler, we dont restart. + */ + restart = !has_handler; + break; + case ERESTARTSYS: + /* ERESTARTSYS means to restart the syscall if there is no + * handler or the handler was registered with SA_RESTART + */ + restart = !has_handler || (ka->sa.sa_flags & SA_RESTART) != 0; + break; + case ERESTARTNOINTR: + /* ERESTARTNOINTR means that the syscall should be + * called again after the signal handler returns. + */ + break; + default: + return; + } + if (restart) { + if (ret == ERESTART_RESTARTBLOCK) + regs->gpr[0] = __NR_restart_syscall; + else + regs->gpr[3] = regs->orig_gpr3; + regs->nip -= 4; + regs->result = 0; + } else { + regs->result = -EINTR; + regs->gpr[3] = EINTR; + regs->ccr |= 0x10000000; + } +} + +int do_signal(sigset_t *oldset, struct pt_regs *regs) +{ + siginfo_t info; + int signr; + struct k_sigaction ka; + int ret; + int is32 = is_32bit_task(); + + if (test_thread_flag(TIF_RESTORE_SIGMASK)) + oldset = ¤t->saved_sigmask; + else if (!oldset) + oldset = ¤t->blocked; + + signr = get_signal_to_deliver(&info, &ka, regs, NULL); + + /* Is there any syscall restart business here ? */ + check_syscall_restart(regs, &ka, signr > 0); + + if (signr <= 0) { + /* No signal to deliver -- put the saved sigmask back */ + if (test_thread_flag(TIF_RESTORE_SIGMASK)) { + clear_thread_flag(TIF_RESTORE_SIGMASK); + sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL); + } + return 0; /* no signals delivered */ + } + + /* + * Reenable the DABR before delivering the signal to + * user space. The DABR will have been cleared if it + * triggered inside the kernel. + */ + if (current->thread.dabr) + set_dabr(current->thread.dabr); + + if (is32) { + if (ka.sa.sa_flags & SA_SIGINFO) + ret = handle_rt_signal32(signr, &ka, &info, oldset, + regs); + else + ret = handle_signal32(signr, &ka, &info, oldset, + regs); + } else { + ret = handle_rt_signal64(signr, &ka, &info, oldset, regs); + } + + if (ret) { + spin_lock_irq(¤t->sighand->siglock); + sigorsets(¤t->blocked, ¤t->blocked, + &ka.sa.sa_mask); + if (!(ka.sa.sa_flags & SA_NODEFER)) + sigaddset(¤t->blocked, signr); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + /* + * A signal was successfully delivered; the saved sigmask is in + * its frame, and we can clear the TIF_RESTORE_SIGMASK flag. + */ + if (test_thread_flag(TIF_RESTORE_SIGMASK)) + clear_thread_flag(TIF_RESTORE_SIGMASK); + } + + return ret; +} + +long sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss, + unsigned long r5, unsigned long r6, unsigned long r7, + unsigned long r8, struct pt_regs *regs) +{ + return do_sigaltstack(uss, uoss, regs->gpr[1]); +} diff --git a/arch/powerpc/kernel/signal.h b/arch/powerpc/kernel/signal.h new file mode 100644 index 000000000000..77efb3d5465a --- /dev/null +++ b/arch/powerpc/kernel/signal.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2007 Benjamin Herrenschmidt, IBM Coproration + * Extracted from signal_32.c and signal_64.c + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file README.legal in the main directory of + * this archive for more details. + */ + +#ifndef _POWERPC_ARCH_SIGNAL_H +#define _POWERPC_ARCH_SIGNAL_H + +#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) + +extern void __user * get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, + size_t frame_size); +extern void restore_sigmask(sigset_t *set); + +extern int handle_signal32(unsigned long sig, struct k_sigaction *ka, + siginfo_t *info, sigset_t *oldset, + struct pt_regs *regs); + +extern int handle_rt_signal32(unsigned long sig, struct k_sigaction *ka, + siginfo_t *info, sigset_t *oldset, + struct pt_regs *regs); + + +#ifdef CONFIG_PPC64 + +static inline int is_32bit_task(void) +{ + return test_thread_flag(TIF_32BIT); +} + +extern int handle_rt_signal64(int signr, struct k_sigaction *ka, + siginfo_t *info, sigset_t *set, + struct pt_regs *regs); + +#else /* CONFIG_PPC64 */ + +static inline int is_32bit_task(void) +{ + return 1; +} + +static inline int handle_rt_signal64(int signr, struct k_sigaction *ka, + siginfo_t *info, sigset_t *set, + struct pt_regs *regs) +{ + return -EFAULT; +} + +#endif /* !defined(CONFIG_PPC64) */ + +#endif /* _POWERPC_ARCH_SIGNAL_H */ diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c index dd1dca5bfa81..590057e9e987 100644 --- a/arch/powerpc/kernel/signal_32.c +++ b/arch/powerpc/kernel/signal_32.c @@ -51,12 +51,11 @@ #include #endif +#include "signal.h" + #undef DEBUG_SIG -#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) - #ifdef CONFIG_PPC64 -#define do_signal do_signal32 #define sys_sigsuspend compat_sys_sigsuspend #define sys_rt_sigsuspend compat_sys_rt_sigsuspend #define sys_rt_sigreturn compat_sys_rt_sigreturn @@ -231,8 +230,6 @@ static inline int restore_general_regs(struct pt_regs *regs, #endif /* CONFIG_PPC64 */ -int do_signal(sigset_t *oldset, struct pt_regs *regs); - /* * Atomically swap in the new signal mask, and wait for a signal. */ @@ -251,14 +248,6 @@ long sys_sigsuspend(old_sigset_t mask) return -ERESTARTNOHAND; } -#ifdef CONFIG_PPC32 -long sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss, int r5, - int r6, int r7, int r8, struct pt_regs *regs) -{ - return do_sigaltstack(uss, uoss, regs->gpr[1]); -} -#endif - long sys_sigaction(int sig, struct old_sigaction __user *act, struct old_sigaction __user *oact) { @@ -293,14 +282,17 @@ long sys_sigaction(int sig, struct old_sigaction __user *act, /* * When we have signals to deliver, we set up on the * user stack, going down from the original stack pointer: - * a sigregs struct + * an ABI gap of 56 words + * an mcontext struct * a sigcontext struct * a gap of __SIGNAL_FRAMESIZE bytes * - * Each of these things must be a multiple of 16 bytes in size. + * Each of these things must be a multiple of 16 bytes in size. The following + * structure represent all of this except the __SIGNAL_FRAMESIZE gap * */ -struct sigregs { +struct sigframe { + struct sigcontext sctx; /* the sigcontext */ struct mcontext mctx; /* all the register values */ /* * Programs using the rs6000/xcoff abi can save up to 19 gp @@ -703,44 +695,22 @@ int compat_sys_sigaltstack(u32 __new, u32 __old, int r5, } #endif /* CONFIG_PPC64 */ - -/* - * Restore the user process's signal mask - */ -#ifdef CONFIG_PPC64 -extern void restore_sigmask(sigset_t *set); -#else /* CONFIG_PPC64 */ -static void restore_sigmask(sigset_t *set) -{ - sigdelsetmask(set, ~_BLOCKABLE); - spin_lock_irq(¤t->sighand->siglock); - current->blocked = *set; - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); -} -#endif - /* * Set up a signal frame for a "real-time" signal handler * (one which gets siginfo). */ -static int handle_rt_signal(unsigned long sig, struct k_sigaction *ka, +int handle_rt_signal32(unsigned long sig, struct k_sigaction *ka, siginfo_t *info, sigset_t *oldset, - struct pt_regs *regs, unsigned long newsp) + struct pt_regs *regs) { struct rt_sigframe __user *rt_sf; struct mcontext __user *frame; - unsigned long origsp = newsp; + unsigned long newsp = 0; /* Set up Signal Frame */ /* Put a Real Time Context onto stack */ - newsp -= sizeof(*rt_sf); - rt_sf = (struct rt_sigframe __user *)newsp; - - /* create a stack frame for the caller of the handler */ - newsp -= __SIGNAL_FRAMESIZE + 16; - - if (!access_ok(VERIFY_WRITE, (void __user *)newsp, origsp - newsp)) + rt_sf = get_sigframe(ka, regs, sizeof(*rt_sf)); + if (unlikely(rt_sf == NULL)) goto badframe; /* Put the siginfo & fill in most of the ucontext */ @@ -770,8 +740,12 @@ static int handle_rt_signal(unsigned long sig, struct k_sigaction *ka, current->thread.fpscr.val = 0; /* turn off all fp exceptions */ + /* create a stack frame for the caller of the handler */ + newsp = ((unsigned long)rt_sf) - (__SIGNAL_FRAMESIZE + 16); if (put_user(regs->gpr[1], (u32 __user *)newsp)) goto badframe; + + /* Fill registers for signal handler */ regs->gpr[1] = newsp; regs->gpr[3] = sig; regs->gpr[4] = (unsigned long) &rt_sf->info; @@ -1015,27 +989,18 @@ int sys_debug_setcontext(struct ucontext __user *ctx, /* * OK, we're invoking a handler */ -static int handle_signal(unsigned long sig, struct k_sigaction *ka, - siginfo_t *info, sigset_t *oldset, struct pt_regs *regs, - unsigned long newsp) +int handle_signal32(unsigned long sig, struct k_sigaction *ka, + siginfo_t *info, sigset_t *oldset, struct pt_regs *regs) { struct sigcontext __user *sc; - struct sigregs __user *frame; - unsigned long origsp = newsp; + struct sigframe __user *frame; + unsigned long newsp = 0; /* Set up Signal Frame */ - newsp -= sizeof(struct sigregs); - frame = (struct sigregs __user *) newsp; - - /* Put a sigcontext on the stack */ - newsp -= sizeof(*sc); - sc = (struct sigcontext __user *) newsp; - - /* create a stack frame for the caller of the handler */ - newsp -= __SIGNAL_FRAMESIZE; - - if (!access_ok(VERIFY_WRITE, (void __user *) newsp, origsp - newsp)) + frame = get_sigframe(ka, regs, sizeof(*frame)); + if (unlikely(frame == NULL)) goto badframe; + sc = (struct sigcontext __user *) &frame->sctx; #if _NSIG != 64 #error "Please adjust handle_signal()" @@ -1047,7 +1012,7 @@ static int handle_signal(unsigned long sig, struct k_sigaction *ka, #else || __put_user(oldset->sig[1], &sc->_unused[3]) #endif - || __put_user(to_user_ptr(frame), &sc->regs) + || __put_user(to_user_ptr(&frame->mctx), &sc->regs) || __put_user(sig, &sc->signal)) goto badframe; @@ -1063,8 +1028,11 @@ static int handle_signal(unsigned long sig, struct k_sigaction *ka, current->thread.fpscr.val = 0; /* turn off all fp exceptions */ + /* create a stack frame for the caller of the handler */ + newsp = ((unsigned long)frame) - __SIGNAL_FRAMESIZE; if (put_user(regs->gpr[1], (u32 __user *)newsp)) goto badframe; + regs->gpr[1] = newsp; regs->gpr[3] = sig; regs->gpr[4] = (unsigned long) sc; @@ -1126,106 +1094,3 @@ badframe: force_sig(SIGSEGV, current); return 0; } - -/* - * Note that 'init' is a special process: it doesn't get signals it doesn't - * want to handle. Thus you cannot kill init even with a SIGKILL even by - * mistake. - */ -int do_signal(sigset_t *oldset, struct pt_regs *regs) -{ - siginfo_t info; - struct k_sigaction ka; - unsigned int newsp; - int signr, ret; - -#ifdef CONFIG_PPC32 - if (try_to_freeze()) { - signr = 0; - if (!signal_pending(current)) - goto no_signal; - } -#endif - - if (test_thread_flag(TIF_RESTORE_SIGMASK)) - oldset = ¤t->saved_sigmask; - else if (!oldset) - oldset = ¤t->blocked; - - signr = get_signal_to_deliver(&info, &ka, regs, NULL); -#ifdef CONFIG_PPC32 -no_signal: -#endif - if (TRAP(regs) == 0x0C00 /* System Call! */ - && regs->ccr & 0x10000000 /* error signalled */ - && ((ret = regs->gpr[3]) == ERESTARTSYS - || ret == ERESTARTNOHAND || ret == ERESTARTNOINTR - || ret == ERESTART_RESTARTBLOCK)) { - - if (signr > 0 - && (ret == ERESTARTNOHAND || ret == ERESTART_RESTARTBLOCK - || (ret == ERESTARTSYS - && !(ka.sa.sa_flags & SA_RESTART)))) { - /* make the system call return an EINTR error */ - regs->result = -EINTR; - regs->gpr[3] = EINTR; - /* note that the cr0.SO bit is already set */ - } else { - regs->nip -= 4; /* Back up & retry system call */ - regs->result = 0; - regs->trap = 0; - if (ret == ERESTART_RESTARTBLOCK) - regs->gpr[0] = __NR_restart_syscall; - else - regs->gpr[3] = regs->orig_gpr3; - } - } - - if (signr == 0) { - /* No signal to deliver -- put the saved sigmask back */ - if (test_thread_flag(TIF_RESTORE_SIGMASK)) { - clear_thread_flag(TIF_RESTORE_SIGMASK); - sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL); - } - return 0; /* no signals delivered */ - } - - if ((ka.sa.sa_flags & SA_ONSTACK) && current->sas_ss_size - && !on_sig_stack(regs->gpr[1])) - newsp = current->sas_ss_sp + current->sas_ss_size; - else - newsp = regs->gpr[1]; - newsp &= ~0xfUL; - -#ifdef CONFIG_PPC64 - /* - * Reenable the DABR before delivering the signal to - * user space. The DABR will have been cleared if it - * triggered inside the kernel. - */ - if (current->thread.dabr) - set_dabr(current->thread.dabr); -#endif - - /* Whee! Actually deliver the signal. */ - if (ka.sa.sa_flags & SA_SIGINFO) - ret = handle_rt_signal(signr, &ka, &info, oldset, regs, newsp); - else - ret = handle_signal(signr, &ka, &info, oldset, regs, newsp); - - if (ret) { - spin_lock_irq(¤t->sighand->siglock); - sigorsets(¤t->blocked, ¤t->blocked, - &ka.sa.sa_mask); - if (!(ka.sa.sa_flags & SA_NODEFER)) - sigaddset(¤t->blocked, signr); - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); - /* A signal was successfully delivered; the saved sigmask is in - its frame, and we can clear the TIF_RESTORE_SIGMASK flag */ - if (test_thread_flag(TIF_RESTORE_SIGMASK)) - clear_thread_flag(TIF_RESTORE_SIGMASK); - } - - return ret; -} diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c index b27e26852fdb..de895e6d8c62 100644 --- a/arch/powerpc/kernel/signal_64.c +++ b/arch/powerpc/kernel/signal_64.c @@ -34,9 +34,9 @@ #include #include -#define DEBUG_SIG 0 +#include "signal.h" -#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) +#define DEBUG_SIG 0 #define GP_REGS_SIZE min(sizeof(elf_gregset_t), sizeof(struct pt_regs)) #define FP_REGS_SIZE sizeof(elf_fpregset_t) @@ -64,14 +64,6 @@ struct rt_sigframe { char abigap[288]; } __attribute__ ((aligned (16))); -long sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss, unsigned long r5, - unsigned long r6, unsigned long r7, unsigned long r8, - struct pt_regs *regs) -{ - return do_sigaltstack(uss, uoss, regs->gpr[1]); -} - - /* * Set up the sigcontext for the signal frame. */ @@ -207,25 +199,6 @@ static long restore_sigcontext(struct pt_regs *regs, sigset_t *set, int sig, return err; } -/* - * Allocate space for the signal frame - */ -static inline void __user * get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, - size_t frame_size) -{ - unsigned long newsp; - - /* Default to using normal stack */ - newsp = regs->gpr[1]; - - if ((ka->sa.sa_flags & SA_ONSTACK) && current->sas_ss_size) { - if (! on_sig_stack(regs->gpr[1])) - newsp = (current->sas_ss_sp + current->sas_ss_size); - } - - return (void __user *)((newsp - frame_size) & -16ul); -} - /* * Setup the trampoline code on the stack */ @@ -252,19 +225,6 @@ static long setup_trampoline(unsigned int syscall, unsigned int __user *tramp) return err; } -/* - * Restore the user process's signal mask (also used by signal32.c) - */ -void restore_sigmask(sigset_t *set) -{ - sigdelsetmask(set, ~_BLOCKABLE); - spin_lock_irq(¤t->sighand->siglock); - current->blocked = *set; - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); -} - - /* * Handle {get,set,swap}_context operations */ @@ -359,7 +319,7 @@ badframe: return 0; } -static int setup_rt_frame(int signr, struct k_sigaction *ka, siginfo_t *info, +int handle_rt_signal64(int signr, struct k_sigaction *ka, siginfo_t *info, sigset_t *set, struct pt_regs *regs) { /* Handler is *really* a pointer to the function descriptor for @@ -373,8 +333,7 @@ static int setup_rt_frame(int signr, struct k_sigaction *ka, siginfo_t *info, long err = 0; frame = get_sigframe(ka, regs, sizeof(*frame)); - - if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) + if (unlikely(frame == NULL)) goto badframe; err |= __put_user(&frame->info, &frame->pinfo); @@ -411,7 +370,7 @@ static int setup_rt_frame(int signr, struct k_sigaction *ka, siginfo_t *info, funct_desc_ptr = (func_descr_t __user *) ka->sa.sa_handler; /* Allocate a dummy caller frame for the signal handler. */ - newsp = (unsigned long)frame - __SIGNAL_FRAMESIZE; + newsp = ((unsigned long)frame) - __SIGNAL_FRAMESIZE; err |= put_user(regs->gpr[1], (unsigned long __user *)newsp); /* Set up "regs" so we "return" to the signal handler. */ @@ -442,134 +401,3 @@ badframe: force_sigsegv(signr, current); return 0; } - - -/* - * OK, we're invoking a handler - */ -static int handle_signal(unsigned long sig, struct k_sigaction *ka, - siginfo_t *info, sigset_t *oldset, struct pt_regs *regs) -{ - int ret; - - /* Set up Signal Frame */ - ret = setup_rt_frame(sig, ka, info, oldset, regs); - - if (ret) { - spin_lock_irq(¤t->sighand->siglock); - sigorsets(¤t->blocked, ¤t->blocked, &ka->sa.sa_mask); - if (!(ka->sa.sa_flags & SA_NODEFER)) - sigaddset(¤t->blocked,sig); - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); - } - - return ret; -} - -static inline void syscall_restart(struct pt_regs *regs, struct k_sigaction *ka) -{ - switch ((int)regs->result) { - case -ERESTART_RESTARTBLOCK: - case -ERESTARTNOHAND: - /* ERESTARTNOHAND means that the syscall should only be - * restarted if there was no handler for the signal, and since - * we only get here if there is a handler, we dont restart. - */ - regs->result = -EINTR; - regs->gpr[3] = EINTR; - regs->ccr |= 0x10000000; - break; - case -ERESTARTSYS: - /* ERESTARTSYS means to restart the syscall if there is no - * handler or the handler was registered with SA_RESTART - */ - if (!(ka->sa.sa_flags & SA_RESTART)) { - regs->result = -EINTR; - regs->gpr[3] = EINTR; - regs->ccr |= 0x10000000; - break; - } - /* fallthrough */ - case -ERESTARTNOINTR: - /* ERESTARTNOINTR means that the syscall should be - * called again after the signal handler returns. - */ - regs->gpr[3] = regs->orig_gpr3; - regs->nip -= 4; - regs->result = 0; - break; - } -} - -/* - * Note that 'init' is a special process: it doesn't get signals it doesn't - * want to handle. Thus you cannot kill init even with a SIGKILL even by - * mistake. - */ -int do_signal(sigset_t *oldset, struct pt_regs *regs) -{ - siginfo_t info; - int signr; - struct k_sigaction ka; - - /* - * If the current thread is 32 bit - invoke the - * 32 bit signal handling code - */ - if (test_thread_flag(TIF_32BIT)) - return do_signal32(oldset, regs); - - if (test_thread_flag(TIF_RESTORE_SIGMASK)) - oldset = ¤t->saved_sigmask; - else if (!oldset) - oldset = ¤t->blocked; - - signr = get_signal_to_deliver(&info, &ka, regs, NULL); - if (signr > 0) { - int ret; - - /* Whee! Actually deliver the signal. */ - if (TRAP(regs) == 0x0C00) - syscall_restart(regs, &ka); - - /* - * Reenable the DABR before delivering the signal to - * user space. The DABR will have been cleared if it - * triggered inside the kernel. - */ - if (current->thread.dabr) - set_dabr(current->thread.dabr); - - ret = handle_signal(signr, &ka, &info, oldset, regs); - - /* If a signal was successfully delivered, the saved sigmask is in - its frame, and we can clear the TIF_RESTORE_SIGMASK flag */ - if (ret && test_thread_flag(TIF_RESTORE_SIGMASK)) - clear_thread_flag(TIF_RESTORE_SIGMASK); - - return ret; - } - - if (TRAP(regs) == 0x0C00) { /* System Call! */ - if ((int)regs->result == -ERESTARTNOHAND || - (int)regs->result == -ERESTARTSYS || - (int)regs->result == -ERESTARTNOINTR) { - regs->gpr[3] = regs->orig_gpr3; - regs->nip -= 4; /* Back up & retry system call */ - regs->result = 0; - } else if ((int)regs->result == -ERESTART_RESTARTBLOCK) { - regs->gpr[0] = __NR_restart_syscall; - regs->nip -= 4; - regs->result = 0; - } - } - /* No signal to deliver -- put the saved sigmask back */ - if (test_thread_flag(TIF_RESTORE_SIGMASK)) { - clear_thread_flag(TIF_RESTORE_SIGMASK); - sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL); - } - - return 0; -} -EXPORT_SYMBOL(do_signal); diff --git a/arch/powerpc/kernel/sysfs.c b/arch/powerpc/kernel/sysfs.c index 68991c2d4a1b..55d29ed4b7a0 100644 --- a/arch/powerpc/kernel/sysfs.c +++ b/arch/powerpc/kernel/sysfs.c @@ -442,12 +442,14 @@ int sysfs_add_device_to_node(struct sys_device *dev, int nid) return sysfs_create_link(&node->sysdev.kobj, &dev->kobj, kobject_name(&dev->kobj)); } +EXPORT_SYMBOL_GPL(sysfs_add_device_to_node); void sysfs_remove_device_from_node(struct sys_device *dev, int nid) { struct node *node = &node_devices[nid]; sysfs_remove_link(&node->sysdev.kobj, kobject_name(&dev->kobj)); } +EXPORT_SYMBOL_GPL(sysfs_remove_device_from_node); #else static void register_nodes(void) @@ -457,9 +459,6 @@ static void register_nodes(void) #endif -EXPORT_SYMBOL_GPL(sysfs_add_device_to_node); -EXPORT_SYMBOL_GPL(sysfs_remove_device_from_node); - /* Only valid if CPU is present. */ static ssize_t show_physical_id(struct sys_device *dev, char *buf) { diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c index 2c8564d54e4d..e5df167f7824 100644 --- a/arch/powerpc/kernel/time.c +++ b/arch/powerpc/kernel/time.c @@ -77,9 +77,8 @@ /* keep track of when we need to update the rtc */ time_t last_rtc_update; #ifdef CONFIG_PPC_ISERIES -unsigned long iSeries_recal_titan = 0; -unsigned long iSeries_recal_tb = 0; -static unsigned long first_settimeofday = 1; +static unsigned long __initdata iSeries_recal_titan; +static signed long __initdata iSeries_recal_tb; #endif /* The decrementer counts down by 128 every 128ns on a 601. */ @@ -113,8 +112,9 @@ u64 ticklen_to_xs; /* 0.64 fraction */ DEFINE_SPINLOCK(rtc_lock); EXPORT_SYMBOL_GPL(rtc_lock); -u64 tb_to_ns_scale; -unsigned tb_to_ns_shift; +static u64 tb_to_ns_scale __read_mostly; +static unsigned tb_to_ns_shift __read_mostly; +static unsigned long boot_tb __read_mostly; struct gettimeofday_struct do_gtod; @@ -214,7 +214,6 @@ static void account_process_time(struct pt_regs *regs) run_posix_cpu_timers(current); } -#ifdef CONFIG_PPC_SPLPAR /* * Stuff for accounting stolen time. */ @@ -222,19 +221,28 @@ struct cpu_purr_data { int initialized; /* thread is running */ u64 tb; /* last TB value read */ u64 purr; /* last PURR value read */ - spinlock_t lock; }; +/* + * Each entry in the cpu_purr_data array is manipulated only by its + * "owner" cpu -- usually in the timer interrupt but also occasionally + * in process context for cpu online. As long as cpus do not touch + * each others' cpu_purr_data, disabling local interrupts is + * sufficient to serialize accesses. + */ static DEFINE_PER_CPU(struct cpu_purr_data, cpu_purr_data); static void snapshot_tb_and_purr(void *data) { + unsigned long flags; struct cpu_purr_data *p = &__get_cpu_var(cpu_purr_data); + local_irq_save(flags); p->tb = mftb(); p->purr = mfspr(SPRN_PURR); wmb(); p->initialized = 1; + local_irq_restore(flags); } /* @@ -242,15 +250,14 @@ static void snapshot_tb_and_purr(void *data) */ void snapshot_timebases(void) { - int cpu; - if (!cpu_has_feature(CPU_FTR_PURR)) return; - for_each_possible_cpu(cpu) - spin_lock_init(&per_cpu(cpu_purr_data, cpu).lock); on_each_cpu(snapshot_tb_and_purr, NULL, 0, 1); } +/* + * Must be called with interrupts disabled. + */ void calculate_steal_time(void) { u64 tb, purr; @@ -262,7 +269,6 @@ void calculate_steal_time(void) pme = &per_cpu(cpu_purr_data, smp_processor_id()); if (!pme->initialized) return; /* this can happen in early boot */ - spin_lock(&pme->lock); tb = mftb(); purr = mfspr(SPRN_PURR); stolen = (tb - pme->tb) - (purr - pme->purr); @@ -270,9 +276,9 @@ void calculate_steal_time(void) account_steal_time(current, stolen); pme->tb = tb; pme->purr = purr; - spin_unlock(&pme->lock); } +#ifdef CONFIG_PPC_SPLPAR /* * Must be called before the cpu is added to the online map when * a cpu is being brought up at runtime. @@ -284,12 +290,12 @@ static void snapshot_purr(void) if (!cpu_has_feature(CPU_FTR_PURR)) return; + local_irq_save(flags); pme = &per_cpu(cpu_purr_data, smp_processor_id()); - spin_lock_irqsave(&pme->lock, flags); pme->tb = mftb(); pme->purr = mfspr(SPRN_PURR); pme->initialized = 1; - spin_unlock_irqrestore(&pme->lock, flags); + local_irq_restore(flags); } #endif /* CONFIG_PPC_SPLPAR */ @@ -550,10 +556,15 @@ EXPORT_SYMBOL(profile_pc); * returned by the service processor for the timebase frequency. */ -static void iSeries_tb_recal(void) +static int __init iSeries_tb_recal(void) { struct div_result divres; unsigned long titan, tb; + + /* Make sure we only run on iSeries */ + if (!firmware_has_feature(FW_FEATURE_ISERIES)) + return -ENODEV; + tb = get_tb(); titan = HvCallXm_loadTod(); if ( iSeries_recal_titan ) { @@ -594,8 +605,18 @@ static void iSeries_tb_recal(void) } iSeries_recal_titan = titan; iSeries_recal_tb = tb; + + return 0; } -#endif +late_initcall(iSeries_tb_recal); + +/* Called from platform early init */ +void __init iSeries_time_init_early(void) +{ + iSeries_recal_tb = get_tb(); + iSeries_recal_titan = HvCallXm_loadTod(); +} +#endif /* CONFIG_PPC_ISERIES */ /* * For iSeries shared processors, we have to let the hypervisor @@ -735,7 +756,7 @@ unsigned long long sched_clock(void) { if (__USE_RTC()) return get_rtc(); - return mulhdu(get_tb(), tb_to_ns_scale) << tb_to_ns_shift; + return mulhdu(get_tb() - boot_tb, tb_to_ns_scale) << tb_to_ns_shift; } int do_settimeofday(struct timespec *tv) @@ -759,12 +780,6 @@ int do_settimeofday(struct timespec *tv) * to the RTC again, or write to the RTC but then they don't call * settimeofday to perform this operation. */ -#ifdef CONFIG_PPC_ISERIES - if (firmware_has_feature(FW_FEATURE_ISERIES) && first_settimeofday) { - iSeries_tb_recal(); - first_settimeofday = 0; - } -#endif /* Make userspace gettimeofday spin until we're done. */ ++vdso_data->tb_update_count; @@ -960,6 +975,8 @@ void __init time_init(void) } tb_to_ns_scale = scale; tb_to_ns_shift = shift; + /* Save the current timebase to pretty up CONFIG_PRINTK_TIME */ + boot_tb = get_tb(); tm = get_boot_time(); diff --git a/arch/powerpc/kernel/vdso.c b/arch/powerpc/kernel/vdso.c index 4245579edb4e..cef01e4e8989 100644 --- a/arch/powerpc/kernel/vdso.c +++ b/arch/powerpc/kernel/vdso.c @@ -670,7 +670,7 @@ static int __init vdso_init(void) /* * Fill up the "systemcfg" stuff for backward compatiblity */ - strcpy(vdso_data->eye_catcher, "SYSTEMCFG:PPC64"); + strcpy((char *)vdso_data->eye_catcher, "SYSTEMCFG:PPC64"); vdso_data->version.major = SYSTEMCFG_MAJOR; vdso_data->version.minor = SYSTEMCFG_MINOR; vdso_data->processor = mfspr(SPRN_PVR); diff --git a/arch/powerpc/kernel/vmlinux.lds.S b/arch/powerpc/kernel/vmlinux.lds.S index 21c39ff2dc39..ae4acd84143d 100644 --- a/arch/powerpc/kernel/vmlinux.lds.S +++ b/arch/powerpc/kernel/vmlinux.lds.S @@ -7,6 +7,7 @@ #define PROVIDE32(x) PROVIDE(x) #endif #include +#include ENTRY(_stext) @@ -211,6 +212,11 @@ SECTIONS *(.data.cacheline_aligned) } + . = ALIGN(L1_CACHE_BYTES); + .data.read_mostly : { + *(.data.read_mostly) + } + . = ALIGN(PAGE_SIZE); __data_nosave : { __nosave_begin = .; diff --git a/arch/powerpc/mm/44x_mmu.c b/arch/powerpc/mm/44x_mmu.c index ca4dcb07a939..c3df50476539 100644 --- a/arch/powerpc/mm/44x_mmu.c +++ b/arch/powerpc/mm/44x_mmu.c @@ -12,7 +12,6 @@ * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au) * and Cort Dougan (PReP) (cort@cs.nmt.edu) * Copyright (C) 1996 Paul Mackerras - * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). * * Derived from "arch/i386/mm/init.c" * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds diff --git a/arch/powerpc/mm/4xx_mmu.c b/arch/powerpc/mm/4xx_mmu.c index 838e09db71d9..7ff2609b64d1 100644 --- a/arch/powerpc/mm/4xx_mmu.c +++ b/arch/powerpc/mm/4xx_mmu.c @@ -9,7 +9,6 @@ * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au) * and Cort Dougan (PReP) (cort@cs.nmt.edu) * Copyright (C) 1996 Paul Mackerras - * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). * * Derived from "arch/i386/mm/init.c" * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds diff --git a/arch/powerpc/mm/Makefile b/arch/powerpc/mm/Makefile index 4f839c6a9768..7e4d27ad3dee 100644 --- a/arch/powerpc/mm/Makefile +++ b/arch/powerpc/mm/Makefile @@ -11,8 +11,7 @@ obj-$(CONFIG_PPC32) += init_32.o pgtable_32.o mmu_context_32.o hash-$(CONFIG_PPC_NATIVE) := hash_native_64.o obj-$(CONFIG_PPC64) += init_64.o pgtable_64.o mmu_context_64.o \ hash_utils_64.o hash_low_64.o tlb_64.o \ - slb_low.o slb.o stab.o mmap.o imalloc.o \ - $(hash-y) + slb_low.o slb.o stab.o mmap.o $(hash-y) obj-$(CONFIG_PPC_STD_MMU_32) += ppc_mmu_32.o hash_low_32.o tlb_32.o obj-$(CONFIG_40x) += 4xx_mmu.o obj-$(CONFIG_44x) += 44x_mmu.o diff --git a/arch/powerpc/mm/fault.c b/arch/powerpc/mm/fault.c index 115b25f50bf8..0ece51310bfe 100644 --- a/arch/powerpc/mm/fault.c +++ b/arch/powerpc/mm/fault.c @@ -380,7 +380,7 @@ out_of_memory: } printk("VM: killing process %s\n", current->comm); if (user_mode(regs)) - do_exit(SIGKILL); + do_group_exit(SIGKILL); return SIGKILL; do_sigbus: diff --git a/arch/powerpc/mm/fsl_booke_mmu.c b/arch/powerpc/mm/fsl_booke_mmu.c index 123da03ab118..afab247d472f 100644 --- a/arch/powerpc/mm/fsl_booke_mmu.c +++ b/arch/powerpc/mm/fsl_booke_mmu.c @@ -14,7 +14,6 @@ * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au) * and Cort Dougan (PReP) (cort@cs.nmt.edu) * Copyright (C) 1996 Paul Mackerras - * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). * * Derived from "arch/i386/mm/init.c" * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds diff --git a/arch/powerpc/mm/hash_native_64.c b/arch/powerpc/mm/hash_native_64.c index 4a20d890e2f4..6ba9b47e55af 100644 --- a/arch/powerpc/mm/hash_native_64.c +++ b/arch/powerpc/mm/hash_native_64.c @@ -104,7 +104,7 @@ static inline void tlbie(unsigned long va, int psize, int local) spin_unlock(&native_tlbie_lock); } -static inline void native_lock_hpte(hpte_t *hptep) +static inline void native_lock_hpte(struct hash_pte *hptep) { unsigned long *word = &hptep->v; @@ -116,7 +116,7 @@ static inline void native_lock_hpte(hpte_t *hptep) } } -static inline void native_unlock_hpte(hpte_t *hptep) +static inline void native_unlock_hpte(struct hash_pte *hptep) { unsigned long *word = &hptep->v; @@ -128,7 +128,7 @@ static long native_hpte_insert(unsigned long hpte_group, unsigned long va, unsigned long pa, unsigned long rflags, unsigned long vflags, int psize) { - hpte_t *hptep = htab_address + hpte_group; + struct hash_pte *hptep = htab_address + hpte_group; unsigned long hpte_v, hpte_r; int i; @@ -163,7 +163,7 @@ static long native_hpte_insert(unsigned long hpte_group, unsigned long va, hptep->r = hpte_r; /* Guarantee the second dword is visible before the valid bit */ - __asm__ __volatile__ ("eieio" : : : "memory"); + eieio(); /* * Now set the first dword including the valid bit * NOTE: this also unlocks the hpte @@ -177,7 +177,7 @@ static long native_hpte_insert(unsigned long hpte_group, unsigned long va, static long native_hpte_remove(unsigned long hpte_group) { - hpte_t *hptep; + struct hash_pte *hptep; int i; int slot_offset; unsigned long hpte_v; @@ -217,7 +217,7 @@ static long native_hpte_remove(unsigned long hpte_group) static long native_hpte_updatepp(unsigned long slot, unsigned long newpp, unsigned long va, int psize, int local) { - hpte_t *hptep = htab_address + slot; + struct hash_pte *hptep = htab_address + slot; unsigned long hpte_v, want_v; int ret = 0; @@ -233,15 +233,14 @@ static long native_hpte_updatepp(unsigned long slot, unsigned long newpp, /* Even if we miss, we need to invalidate the TLB */ if (!HPTE_V_COMPARE(hpte_v, want_v) || !(hpte_v & HPTE_V_VALID)) { DBG_LOW(" -> miss\n"); - native_unlock_hpte(hptep); ret = -1; } else { DBG_LOW(" -> hit\n"); /* Update the HPTE */ hptep->r = (hptep->r & ~(HPTE_R_PP | HPTE_R_N)) | (newpp & (HPTE_R_PP | HPTE_R_N | HPTE_R_C)); - native_unlock_hpte(hptep); } + native_unlock_hpte(hptep); /* Ensure it is out of the tlb too. */ tlbie(va, psize, local); @@ -251,7 +250,7 @@ static long native_hpte_updatepp(unsigned long slot, unsigned long newpp, static long native_hpte_find(unsigned long va, int psize) { - hpte_t *hptep; + struct hash_pte *hptep; unsigned long hash; unsigned long i, j; long slot; @@ -294,7 +293,7 @@ static void native_hpte_updateboltedpp(unsigned long newpp, unsigned long ea, { unsigned long vsid, va; long slot; - hpte_t *hptep; + struct hash_pte *hptep; vsid = get_kernel_vsid(ea); va = (vsid << 28) | (ea & 0x0fffffff); @@ -315,7 +314,7 @@ static void native_hpte_updateboltedpp(unsigned long newpp, unsigned long ea, static void native_hpte_invalidate(unsigned long slot, unsigned long va, int psize, int local) { - hpte_t *hptep = htab_address + slot; + struct hash_pte *hptep = htab_address + slot; unsigned long hpte_v; unsigned long want_v; unsigned long flags; @@ -345,7 +344,7 @@ static void native_hpte_invalidate(unsigned long slot, unsigned long va, #define LP_BITS 8 #define LP_MASK(i) ((0xFF >> (i)) << LP_SHIFT) -static void hpte_decode(hpte_t *hpte, unsigned long slot, +static void hpte_decode(struct hash_pte *hpte, unsigned long slot, int *psize, unsigned long *va) { unsigned long hpte_r = hpte->r; @@ -415,7 +414,7 @@ static void hpte_decode(hpte_t *hpte, unsigned long slot, static void native_hpte_clear(void) { unsigned long slot, slots, flags; - hpte_t *hptep = htab_address; + struct hash_pte *hptep = htab_address; unsigned long hpte_v, va; unsigned long pteg_count; int psize; @@ -462,7 +461,7 @@ static void native_hpte_clear(void) static void native_flush_hash_range(unsigned long number, int local) { unsigned long va, hash, index, hidx, shift, slot; - hpte_t *hptep; + struct hash_pte *hptep; unsigned long hpte_v; unsigned long want_v; unsigned long flags; diff --git a/arch/powerpc/mm/hash_utils_64.c b/arch/powerpc/mm/hash_utils_64.c index 4f2f4534a9d8..2ce9491b48d4 100644 --- a/arch/powerpc/mm/hash_utils_64.c +++ b/arch/powerpc/mm/hash_utils_64.c @@ -87,7 +87,7 @@ extern unsigned long dart_tablebase; static unsigned long _SDR1; struct mmu_psize_def mmu_psize_defs[MMU_PAGE_COUNT]; -hpte_t *htab_address; +struct hash_pte *htab_address; unsigned long htab_size_bytes; unsigned long htab_hash_mask; int mmu_linear_psize = MMU_PAGE_4K; diff --git a/arch/powerpc/mm/imalloc.c b/arch/powerpc/mm/imalloc.c deleted file mode 100644 index c831815c31f0..000000000000 --- a/arch/powerpc/mm/imalloc.c +++ /dev/null @@ -1,313 +0,0 @@ -/* - * c 2001 PPC 64 Team, IBM Corp - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - */ - -#include -#include - -#include -#include -#include -#include -#include - -#include "mmu_decl.h" - -static DEFINE_MUTEX(imlist_mutex); -struct vm_struct * imlist = NULL; - -static int get_free_im_addr(unsigned long size, unsigned long *im_addr) -{ - unsigned long addr; - struct vm_struct **p, *tmp; - - addr = ioremap_bot; - for (p = &imlist; (tmp = *p) ; p = &tmp->next) { - if (size + addr < (unsigned long) tmp->addr) - break; - if ((unsigned long)tmp->addr >= ioremap_bot) - addr = tmp->size + (unsigned long) tmp->addr; - if (addr >= IMALLOC_END-size) - return 1; - } - *im_addr = addr; - - return 0; -} - -/* Return whether the region described by v_addr and size is a subset - * of the region described by parent - */ -static inline int im_region_is_subset(unsigned long v_addr, unsigned long size, - struct vm_struct *parent) -{ - return (int) (v_addr >= (unsigned long) parent->addr && - v_addr < (unsigned long) parent->addr + parent->size && - size < parent->size); -} - -/* Return whether the region described by v_addr and size is a superset - * of the region described by child - */ -static int im_region_is_superset(unsigned long v_addr, unsigned long size, - struct vm_struct *child) -{ - struct vm_struct parent; - - parent.addr = (void *) v_addr; - parent.size = size; - - return im_region_is_subset((unsigned long) child->addr, child->size, - &parent); -} - -/* Return whether the region described by v_addr and size overlaps - * the region described by vm. Overlapping regions meet the - * following conditions: - * 1) The regions share some part of the address space - * 2) The regions aren't identical - * 3) Neither region is a subset of the other - */ -static int im_region_overlaps(unsigned long v_addr, unsigned long size, - struct vm_struct *vm) -{ - if (im_region_is_superset(v_addr, size, vm)) - return 0; - - return (v_addr + size > (unsigned long) vm->addr + vm->size && - v_addr < (unsigned long) vm->addr + vm->size) || - (v_addr < (unsigned long) vm->addr && - v_addr + size > (unsigned long) vm->addr); -} - -/* Determine imalloc status of region described by v_addr and size. - * Can return one of the following: - * IM_REGION_UNUSED - Entire region is unallocated in imalloc space. - * IM_REGION_SUBSET - Region is a subset of a region that is already - * allocated in imalloc space. - * vm will be assigned to a ptr to the parent region. - * IM_REGION_EXISTS - Exact region already allocated in imalloc space. - * vm will be assigned to a ptr to the existing imlist - * member. - * IM_REGION_OVERLAPS - Region overlaps an allocated region in imalloc space. - * IM_REGION_SUPERSET - Region is a superset of a region that is already - * allocated in imalloc space. - */ -static int im_region_status(unsigned long v_addr, unsigned long size, - struct vm_struct **vm) -{ - struct vm_struct *tmp; - - for (tmp = imlist; tmp; tmp = tmp->next) - if (v_addr < (unsigned long) tmp->addr + tmp->size) - break; - - *vm = NULL; - if (tmp) { - if (im_region_overlaps(v_addr, size, tmp)) - return IM_REGION_OVERLAP; - - *vm = tmp; - if (im_region_is_subset(v_addr, size, tmp)) { - /* Return with tmp pointing to superset */ - return IM_REGION_SUBSET; - } - if (im_region_is_superset(v_addr, size, tmp)) { - /* Return with tmp pointing to first subset */ - return IM_REGION_SUPERSET; - } - else if (v_addr == (unsigned long) tmp->addr && - size == tmp->size) { - /* Return with tmp pointing to exact region */ - return IM_REGION_EXISTS; - } - } - - return IM_REGION_UNUSED; -} - -static struct vm_struct * split_im_region(unsigned long v_addr, - unsigned long size, struct vm_struct *parent) -{ - struct vm_struct *vm1 = NULL; - struct vm_struct *vm2 = NULL; - struct vm_struct *new_vm = NULL; - - vm1 = kmalloc(sizeof(*vm1), GFP_KERNEL); - if (vm1 == NULL) { - printk(KERN_ERR "%s() out of memory\n", __FUNCTION__); - return NULL; - } - - if (v_addr == (unsigned long) parent->addr) { - /* Use existing parent vm_struct to represent child, allocate - * new one for the remainder of parent range - */ - vm1->size = parent->size - size; - vm1->addr = (void *) (v_addr + size); - vm1->next = parent->next; - - parent->size = size; - parent->next = vm1; - new_vm = parent; - } else if (v_addr + size == (unsigned long) parent->addr + - parent->size) { - /* Allocate new vm_struct to represent child, use existing - * parent one for remainder of parent range - */ - vm1->size = size; - vm1->addr = (void *) v_addr; - vm1->next = parent->next; - new_vm = vm1; - - parent->size -= size; - parent->next = vm1; - } else { - /* Allocate two new vm_structs for the new child and - * uppermost remainder, and use existing parent one for the - * lower remainder of parent range - */ - vm2 = kmalloc(sizeof(*vm2), GFP_KERNEL); - if (vm2 == NULL) { - printk(KERN_ERR "%s() out of memory\n", __FUNCTION__); - kfree(vm1); - return NULL; - } - - vm1->size = size; - vm1->addr = (void *) v_addr; - vm1->next = vm2; - new_vm = vm1; - - vm2->size = ((unsigned long) parent->addr + parent->size) - - (v_addr + size); - vm2->addr = (void *) v_addr + size; - vm2->next = parent->next; - - parent->size = v_addr - (unsigned long) parent->addr; - parent->next = vm1; - } - - return new_vm; -} - -static struct vm_struct * __add_new_im_area(unsigned long req_addr, - unsigned long size) -{ - struct vm_struct **p, *tmp, *area; - - for (p = &imlist; (tmp = *p) ; p = &tmp->next) { - if (req_addr + size <= (unsigned long)tmp->addr) - break; - } - - area = kmalloc(sizeof(*area), GFP_KERNEL); - if (!area) - return NULL; - area->flags = 0; - area->addr = (void *)req_addr; - area->size = size; - area->next = *p; - *p = area; - - return area; -} - -static struct vm_struct * __im_get_area(unsigned long req_addr, - unsigned long size, - int criteria) -{ - struct vm_struct *tmp; - int status; - - status = im_region_status(req_addr, size, &tmp); - if ((criteria & status) == 0) { - return NULL; - } - - switch (status) { - case IM_REGION_UNUSED: - tmp = __add_new_im_area(req_addr, size); - break; - case IM_REGION_SUBSET: - tmp = split_im_region(req_addr, size, tmp); - break; - case IM_REGION_EXISTS: - /* Return requested region */ - break; - case IM_REGION_SUPERSET: - /* Return first existing subset of requested region */ - break; - default: - printk(KERN_ERR "%s() unexpected imalloc region status\n", - __FUNCTION__); - tmp = NULL; - } - - return tmp; -} - -struct vm_struct * im_get_free_area(unsigned long size) -{ - struct vm_struct *area; - unsigned long addr; - - mutex_lock(&imlist_mutex); - if (get_free_im_addr(size, &addr)) { - printk(KERN_ERR "%s() cannot obtain addr for size 0x%lx\n", - __FUNCTION__, size); - area = NULL; - goto next_im_done; - } - - area = __im_get_area(addr, size, IM_REGION_UNUSED); - if (area == NULL) { - printk(KERN_ERR - "%s() cannot obtain area for addr 0x%lx size 0x%lx\n", - __FUNCTION__, addr, size); - } -next_im_done: - mutex_unlock(&imlist_mutex); - return area; -} - -struct vm_struct * im_get_area(unsigned long v_addr, unsigned long size, - int criteria) -{ - struct vm_struct *area; - - mutex_lock(&imlist_mutex); - area = __im_get_area(v_addr, size, criteria); - mutex_unlock(&imlist_mutex); - return area; -} - -void im_free(void * addr) -{ - struct vm_struct **p, *tmp; - - if (!addr) - return; - if ((unsigned long) addr & ~PAGE_MASK) { - printk(KERN_ERR "Trying to %s bad address (%p)\n", __FUNCTION__, addr); - return; - } - mutex_lock(&imlist_mutex); - for (p = &imlist ; (tmp = *p) ; p = &tmp->next) { - if (tmp->addr == addr) { - *p = tmp->next; - unmap_vm_area(tmp); - kfree(tmp); - mutex_unlock(&imlist_mutex); - return; - } - } - mutex_unlock(&imlist_mutex); - printk(KERN_ERR "Trying to %s nonexistent area (%p)\n", __FUNCTION__, - addr); -} diff --git a/arch/powerpc/mm/init_32.c b/arch/powerpc/mm/init_32.c index 5fce6ccecb8d..e1f5ded851f6 100644 --- a/arch/powerpc/mm/init_32.c +++ b/arch/powerpc/mm/init_32.c @@ -5,7 +5,6 @@ * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au) * and Cort Dougan (PReP) (cort@cs.nmt.edu) * Copyright (C) 1996 Paul Mackerras - * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). * PPC44x/36-bit changes by Matt Porter (mporter@mvista.com) * * Derived from "arch/i386/mm/init.c" diff --git a/arch/powerpc/mm/init_64.c b/arch/powerpc/mm/init_64.c index 7312a265545f..1d6edf724c85 100644 --- a/arch/powerpc/mm/init_64.c +++ b/arch/powerpc/mm/init_64.c @@ -5,7 +5,6 @@ * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au) * and Cort Dougan (PReP) (cort@cs.nmt.edu) * Copyright (C) 1996 Paul Mackerras - * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). * * Derived from "arch/i386/mm/init.c" * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds diff --git a/arch/powerpc/mm/mem.c b/arch/powerpc/mm/mem.c index 0266a94d83b6..f0e7eedb1ba3 100644 --- a/arch/powerpc/mm/mem.c +++ b/arch/powerpc/mm/mem.c @@ -5,7 +5,6 @@ * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au) * and Cort Dougan (PReP) (cort@cs.nmt.edu) * Copyright (C) 1996 Paul Mackerras - * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). * PPC44x/36-bit changes by Matt Porter (mporter@mvista.com) * * Derived from "arch/i386/mm/init.c" @@ -129,8 +128,6 @@ int __devinit arch_add_memory(int nid, u64 start, u64 size) zone = pgdata->node_zones; return __add_pages(zone, start_pfn, nr_pages); - - return 0; } /* diff --git a/arch/powerpc/mm/mmu_context_32.c b/arch/powerpc/mm/mmu_context_32.c index 792086b01000..cc32ba41d900 100644 --- a/arch/powerpc/mm/mmu_context_32.c +++ b/arch/powerpc/mm/mmu_context_32.c @@ -11,7 +11,6 @@ * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au) * and Cort Dougan (PReP) (cort@cs.nmt.edu) * Copyright (C) 1996 Paul Mackerras - * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). * * Derived from "arch/i386/mm/init.c" * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds diff --git a/arch/powerpc/mm/mmu_decl.h b/arch/powerpc/mm/mmu_decl.h index 2558c34eedaa..c94a64fd3c01 100644 --- a/arch/powerpc/mm/mmu_decl.h +++ b/arch/powerpc/mm/mmu_decl.h @@ -8,7 +8,6 @@ * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au) * and Cort Dougan (PReP) (cort@cs.nmt.edu) * Copyright (C) 1996 Paul Mackerras - * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). * * Derived from "arch/i386/mm/init.c" * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds @@ -40,8 +39,8 @@ extern int __map_without_bats; extern unsigned long ioremap_base; extern unsigned int rtas_data, rtas_size; -struct _PTE; -extern struct _PTE *Hash, *Hash_end; +struct hash_pte; +extern struct hash_pte *Hash, *Hash_end; extern unsigned long Hash_size, Hash_mask; extern unsigned int num_tlbcam_entries; @@ -90,16 +89,4 @@ static inline void flush_HPTE(unsigned context, unsigned long va, else _tlbie(va); } -#else /* CONFIG_PPC64 */ -/* imalloc region types */ -#define IM_REGION_UNUSED 0x1 -#define IM_REGION_SUBSET 0x2 -#define IM_REGION_EXISTS 0x4 -#define IM_REGION_OVERLAP 0x8 -#define IM_REGION_SUPERSET 0x10 - -extern struct vm_struct * im_get_free_area(unsigned long size); -extern struct vm_struct * im_get_area(unsigned long v_addr, unsigned long size, - int region_type); -extern void im_free(void *addr); #endif diff --git a/arch/powerpc/mm/pgtable_32.c b/arch/powerpc/mm/pgtable_32.c index f6ae1a57d652..64488723162a 100644 --- a/arch/powerpc/mm/pgtable_32.c +++ b/arch/powerpc/mm/pgtable_32.c @@ -8,7 +8,6 @@ * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au) * and Cort Dougan (PReP) (cort@cs.nmt.edu) * Copyright (C) 1996 Paul Mackerras - * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). * * Derived from "arch/i386/mm/init.c" * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds @@ -37,7 +36,6 @@ unsigned long ioremap_base; unsigned long ioremap_bot; EXPORT_SYMBOL(ioremap_bot); /* aka VMALLOC_END */ -int io_bat_index; #if defined(CONFIG_6xx) || defined(CONFIG_POWER3) #define HAVE_BATS 1 @@ -300,51 +298,6 @@ void __init mapin_ram(void) } } -/* is x a power of 4? */ -#define is_power_of_4(x) is_power_of_2(x) && (ffs(x) & 1) - -/* - * Set up a mapping for a block of I/O. - * virt, phys, size must all be page-aligned. - * This should only be called before ioremap is called. - */ -void __init io_block_mapping(unsigned long virt, phys_addr_t phys, - unsigned int size, int flags) -{ - int i; - - if (virt > KERNELBASE && virt < ioremap_bot) - ioremap_bot = ioremap_base = virt; - -#ifdef HAVE_BATS - /* - * Use a BAT for this if possible... - */ - if (io_bat_index < 2 && is_power_of_2(size) - && (virt & (size - 1)) == 0 && (phys & (size - 1)) == 0) { - setbat(io_bat_index, virt, phys, size, flags); - ++io_bat_index; - return; - } -#endif /* HAVE_BATS */ - -#ifdef HAVE_TLBCAM - /* - * Use a CAM for this if possible... - */ - if (tlbcam_index < num_tlbcam_entries && is_power_of_4(size) - && (virt & (size - 1)) == 0 && (phys & (size - 1)) == 0) { - settlbcam(tlbcam_index, virt, phys, size, flags, 0); - ++tlbcam_index; - return; - } -#endif /* HAVE_TLBCAM */ - - /* No BATs available, put it in the page tables. */ - for (i = 0; i < size; i += PAGE_SIZE) - map_page(virt + i, phys + i, flags); -} - /* Scan the real Linux page tables and return a PTE pointer for * a virtual address in a context. * Returns true (1) if PTE was found, zero otherwise. The pointer to @@ -379,82 +332,6 @@ get_pteptr(struct mm_struct *mm, unsigned long addr, pte_t **ptep, pmd_t **pmdp) return(retval); } -/* Find physical address for this virtual address. Normally used by - * I/O functions, but anyone can call it. - */ -unsigned long iopa(unsigned long addr) -{ - unsigned long pa; - - /* I don't know why this won't work on PMacs or CHRP. It - * appears there is some bug, or there is some implicit - * mapping done not properly represented by BATs or in page - * tables.......I am actively working on resolving this, but - * can't hold up other stuff. -- Dan - */ - pte_t *pte; - struct mm_struct *mm; - - /* Check the BATs */ - pa = v_mapped_by_bats(addr); - if (pa) - return pa; - - /* Allow mapping of user addresses (within the thread) - * for DMA if necessary. - */ - if (addr < TASK_SIZE) - mm = current->mm; - else - mm = &init_mm; - - pa = 0; - if (get_pteptr(mm, addr, &pte, NULL)) { - pa = (pte_val(*pte) & PAGE_MASK) | (addr & ~PAGE_MASK); - pte_unmap(pte); - } - - return(pa); -} - -/* This is will find the virtual address for a physical one.... - * Swiped from APUS, could be dangerous :-). - * This is only a placeholder until I really find a way to make this - * work. -- Dan - */ -unsigned long -mm_ptov (unsigned long paddr) -{ - unsigned long ret; -#if 0 - if (paddr < 16*1024*1024) - ret = ZTWO_VADDR(paddr); - else { - int i; - - for (i = 0; i < kmap_chunk_count;){ - unsigned long phys = kmap_chunks[i++]; - unsigned long size = kmap_chunks[i++]; - unsigned long virt = kmap_chunks[i++]; - if (paddr >= phys - && paddr < (phys + size)){ - ret = virt + paddr - phys; - goto exit; - } - } - - ret = (unsigned long) __va(paddr); - } -exit: -#ifdef DEBUGPV - printk ("PTOV(%lx)=%lx\n", paddr, ret); -#endif -#else - ret = (unsigned long)paddr + KERNELBASE; -#endif - return ret; -} - #ifdef CONFIG_DEBUG_PAGEALLOC static int __change_page_attr(struct page *page, pgprot_t prot) diff --git a/arch/powerpc/mm/pgtable_64.c b/arch/powerpc/mm/pgtable_64.c index ad6e135bf212..3dfd10db931a 100644 --- a/arch/powerpc/mm/pgtable_64.c +++ b/arch/powerpc/mm/pgtable_64.c @@ -7,7 +7,6 @@ * Modifications by Paul Mackerras (PowerMac) (paulus@samba.org) * and Cort Dougan (PReP) (cort@cs.nmt.edu) * Copyright (C) 1996 Paul Mackerras - * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). * * Derived from "arch/i386/mm/init.c" * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds @@ -34,41 +33,27 @@ #include #include #include -#include -#include -#include -#include -#include -#include #include #include #include -#include -#include #include #include #include #include -#include #include #include #include -#include #include -#include #include #include #include -#include #include -#include #include #include "mmu_decl.h" -unsigned long ioremap_bot = IMALLOC_BASE; -static unsigned long phbs_io_bot = PHBS_IO_BASE; +unsigned long ioremap_bot = IOREMAP_BASE; /* * map_io_page currently only called by __ioremap @@ -102,8 +87,8 @@ static int map_io_page(unsigned long ea, unsigned long pa, int flags) * entry in the hardware page table. * */ - if (htab_bolt_mapping(ea, ea + PAGE_SIZE, pa, flags, - mmu_io_psize)) { + if (htab_bolt_mapping(ea, (unsigned long)ea + PAGE_SIZE, + pa, flags, mmu_io_psize)) { printk(KERN_ERR "Failed to do bolted mapping IO " "memory at %016lx !\n", pa); return -ENOMEM; @@ -113,8 +98,11 @@ static int map_io_page(unsigned long ea, unsigned long pa, int flags) } -static void __iomem * __ioremap_com(phys_addr_t addr, unsigned long pa, - unsigned long ea, unsigned long size, +/** + * __ioremap_at - Low level function to establish the page tables + * for an IO mapping + */ +void __iomem * __ioremap_at(phys_addr_t pa, void *ea, unsigned long size, unsigned long flags) { unsigned long i; @@ -122,17 +110,35 @@ static void __iomem * __ioremap_com(phys_addr_t addr, unsigned long pa, if ((flags & _PAGE_PRESENT) == 0) flags |= pgprot_val(PAGE_KERNEL); + WARN_ON(pa & ~PAGE_MASK); + WARN_ON(((unsigned long)ea) & ~PAGE_MASK); + WARN_ON(size & ~PAGE_MASK); + for (i = 0; i < size; i += PAGE_SIZE) - if (map_io_page(ea+i, pa+i, flags)) + if (map_io_page((unsigned long)ea+i, pa+i, flags)) return NULL; - return (void __iomem *) (ea + (addr & ~PAGE_MASK)); + return (void __iomem *)ea; +} + +/** + * __iounmap_from - Low level function to tear down the page tables + * for an IO mapping. This is used for mappings that + * are manipulated manually, like partial unmapping of + * PCI IOs or ISA space. + */ +void __iounmap_at(void *ea, unsigned long size) +{ + WARN_ON(((unsigned long)ea) & ~PAGE_MASK); + WARN_ON(size & ~PAGE_MASK); + + unmap_kernel_range((unsigned long)ea, size); } void __iomem * __ioremap(phys_addr_t addr, unsigned long size, unsigned long flags) { - unsigned long pa, ea; + phys_addr_t paligned; void __iomem *ret; /* @@ -144,27 +150,30 @@ void __iomem * __ioremap(phys_addr_t addr, unsigned long size, * IMALLOC_END * */ - pa = addr & PAGE_MASK; - size = PAGE_ALIGN(addr + size) - pa; + paligned = addr & PAGE_MASK; + size = PAGE_ALIGN(addr + size) - paligned; - if ((size == 0) || (pa == 0)) + if ((size == 0) || (paligned == 0)) return NULL; if (mem_init_done) { struct vm_struct *area; - area = im_get_free_area(size); + + area = __get_vm_area(size, VM_IOREMAP, + ioremap_bot, IOREMAP_END); if (area == NULL) return NULL; - ea = (unsigned long)(area->addr); - ret = __ioremap_com(addr, pa, ea, size, flags); + ret = __ioremap_at(paligned, area->addr, size, flags); if (!ret) - im_free(area->addr); + vunmap(area->addr); } else { - ea = ioremap_bot; - ret = __ioremap_com(addr, pa, ea, size, flags); + ret = __ioremap_at(paligned, (void *)ioremap_bot, size, flags); if (ret) ioremap_bot += size; } + + if (ret) + ret += addr & ~PAGE_MASK; return ret; } @@ -187,62 +196,9 @@ void __iomem * ioremap_flags(phys_addr_t addr, unsigned long size, } -#define IS_PAGE_ALIGNED(_val) ((_val) == ((_val) & PAGE_MASK)) - -int __ioremap_explicit(phys_addr_t pa, unsigned long ea, - unsigned long size, unsigned long flags) -{ - struct vm_struct *area; - void __iomem *ret; - - /* For now, require page-aligned values for pa, ea, and size */ - if (!IS_PAGE_ALIGNED(pa) || !IS_PAGE_ALIGNED(ea) || - !IS_PAGE_ALIGNED(size)) { - printk(KERN_ERR "unaligned value in %s\n", __FUNCTION__); - return 1; - } - - if (!mem_init_done) { - /* Two things to consider in this case: - * 1) No records will be kept (imalloc, etc) that the region - * has been remapped - * 2) It won't be easy to iounmap() the region later (because - * of 1) - */ - ; - } else { - area = im_get_area(ea, size, - IM_REGION_UNUSED|IM_REGION_SUBSET|IM_REGION_EXISTS); - if (area == NULL) { - /* Expected when PHB-dlpar is in play */ - return 1; - } - if (ea != (unsigned long) area->addr) { - printk(KERN_ERR "unexpected addr return from " - "im_get_area\n"); - return 1; - } - } - - ret = __ioremap_com(pa, pa, ea, size, flags); - if (ret == NULL) { - printk(KERN_ERR "ioremap_explicit() allocation failure !\n"); - return 1; - } - if (ret != (void *) ea) { - printk(KERN_ERR "__ioremap_com() returned unexpected addr\n"); - return 1; - } - - return 0; -} - /* * Unmap an IO region and remove it from imalloc'd list. * Access to IO memory should be serialized by driver. - * This code is modeled after vmalloc code - unmap_vm_area() - * - * XXX what about calls before mem_init_done (ie python_countermeasures()) */ void __iounmap(volatile void __iomem *token) { @@ -251,9 +207,14 @@ void __iounmap(volatile void __iomem *token) if (!mem_init_done) return; - addr = (void *) ((unsigned long __force) token & PAGE_MASK); - - im_free(addr); + addr = (void *) ((unsigned long __force) + PCI_FIX_ADDR(token) & PAGE_MASK); + if ((unsigned long)addr < ioremap_bot) { + printk(KERN_WARNING "Attempt to iounmap early bolted mapping" + " at 0x%p\n", addr); + return; + } + vunmap(addr); } void iounmap(volatile void __iomem *token) @@ -264,77 +225,8 @@ void iounmap(volatile void __iomem *token) __iounmap(token); } -static int iounmap_subset_regions(unsigned long addr, unsigned long size) -{ - struct vm_struct *area; - - /* Check whether subsets of this region exist */ - area = im_get_area(addr, size, IM_REGION_SUPERSET); - if (area == NULL) - return 1; - - while (area) { - iounmap((void __iomem *) area->addr); - area = im_get_area(addr, size, - IM_REGION_SUPERSET); - } - - return 0; -} - -int __iounmap_explicit(volatile void __iomem *start, unsigned long size) -{ - struct vm_struct *area; - unsigned long addr; - int rc; - - addr = (unsigned long __force) start & PAGE_MASK; - - /* Verify that the region either exists or is a subset of an existing - * region. In the latter case, split the parent region to create - * the exact region - */ - area = im_get_area(addr, size, - IM_REGION_EXISTS | IM_REGION_SUBSET); - if (area == NULL) { - /* Determine whether subset regions exist. If so, unmap */ - rc = iounmap_subset_regions(addr, size); - if (rc) { - printk(KERN_ERR - "%s() cannot unmap nonexistent range 0x%lx\n", - __FUNCTION__, addr); - return 1; - } - } else { - iounmap((void __iomem *) area->addr); - } - /* - * FIXME! This can't be right: - iounmap(area->addr); - * Maybe it should be "iounmap(area);" - */ - return 0; -} - EXPORT_SYMBOL(ioremap); EXPORT_SYMBOL(ioremap_flags); EXPORT_SYMBOL(__ioremap); EXPORT_SYMBOL(iounmap); EXPORT_SYMBOL(__iounmap); - -static DEFINE_SPINLOCK(phb_io_lock); - -void __iomem * reserve_phb_iospace(unsigned long size) -{ - void __iomem *virt_addr; - - if (phbs_io_bot >= IMALLOC_BASE) - panic("reserve_phb_iospace(): phb io space overflow\n"); - - spin_lock(&phb_io_lock); - virt_addr = (void __iomem *) phbs_io_bot; - phbs_io_bot += size; - spin_unlock(&phb_io_lock); - - return virt_addr; -} diff --git a/arch/powerpc/mm/ppc_mmu_32.c b/arch/powerpc/mm/ppc_mmu_32.c index ec1421a20aaa..5c45d474cfcc 100644 --- a/arch/powerpc/mm/ppc_mmu_32.c +++ b/arch/powerpc/mm/ppc_mmu_32.c @@ -11,7 +11,6 @@ * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au) * and Cort Dougan (PReP) (cort@cs.nmt.edu) * Copyright (C) 1996 Paul Mackerras - * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). * * Derived from "arch/i386/mm/init.c" * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds @@ -35,12 +34,12 @@ #include "mmu_decl.h" -PTE *Hash, *Hash_end; +struct hash_pte *Hash, *Hash_end; unsigned long Hash_size, Hash_mask; unsigned long _SDR1; union ubat { /* BAT register values to be loaded */ - BAT bat; + struct ppc_bat bat; u32 word[2]; } BATS[8][2]; /* 8 pairs of IBAT, DBAT */ @@ -245,7 +244,7 @@ void __init MMU_init_hw(void) cacheable_memzero(Hash, Hash_size); _SDR1 = __pa(Hash) | SDR1_LOW_BITS; - Hash_end = (PTE *) ((unsigned long)Hash + Hash_size); + Hash_end = (struct hash_pte *) ((unsigned long)Hash + Hash_size); printk("Total memory = %ldMB; using %ldkB for hash table (at %p)\n", total_memory >> 20, Hash_size >> 10, Hash); diff --git a/arch/powerpc/mm/stab.c b/arch/powerpc/mm/stab.c index 132c6bc66ce1..28492bbdee8e 100644 --- a/arch/powerpc/mm/stab.c +++ b/arch/powerpc/mm/stab.c @@ -55,7 +55,7 @@ static int make_ste(unsigned long stab, unsigned long esid, unsigned long vsid) for (entry = 0; entry < 8; entry++, ste++) { if (!(ste->esid_data & STE_ESID_V)) { ste->vsid_data = vsid_data; - asm volatile("eieio":::"memory"); + eieio(); ste->esid_data = esid_data; return (global_entry | entry); } @@ -101,7 +101,7 @@ static int make_ste(unsigned long stab, unsigned long esid, unsigned long vsid) asm volatile("sync" : : : "memory"); /* Order update */ castout_ste->vsid_data = vsid_data; - asm volatile("eieio" : : : "memory"); /* Order update */ + eieio(); /* Order update */ castout_ste->esid_data = esid_data; asm volatile("slbie %0" : : "r" (old_esid << SID_SHIFT)); diff --git a/arch/powerpc/mm/tlb_32.c b/arch/powerpc/mm/tlb_32.c index 6a69417cbc0e..06c7e77e097a 100644 --- a/arch/powerpc/mm/tlb_32.c +++ b/arch/powerpc/mm/tlb_32.c @@ -11,7 +11,6 @@ * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au) * and Cort Dougan (PReP) (cort@cs.nmt.edu) * Copyright (C) 1996 Paul Mackerras - * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). * * Derived from "arch/i386/mm/init.c" * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds diff --git a/arch/powerpc/mm/tlb_64.c b/arch/powerpc/mm/tlb_64.c index 2bfc4d7e1aa2..cbd34fc813ee 100644 --- a/arch/powerpc/mm/tlb_64.c +++ b/arch/powerpc/mm/tlb_64.c @@ -8,7 +8,6 @@ * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au) * and Cort Dougan (PReP) (cort@cs.nmt.edu) * Copyright (C) 1996 Paul Mackerras - * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). * * Derived from "arch/i386/mm/init.c" * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds @@ -239,3 +238,59 @@ void pte_free_finish(void) pte_free_submit(*batchp); *batchp = NULL; } + +/** + * __flush_hash_table_range - Flush all HPTEs for a given address range + * from the hash table (and the TLB). But keeps + * the linux PTEs intact. + * + * @mm : mm_struct of the target address space (generally init_mm) + * @start : starting address + * @end : ending address (not included in the flush) + * + * This function is mostly to be used by some IO hotplug code in order + * to remove all hash entries from a given address range used to map IO + * space on a removed PCI-PCI bidge without tearing down the full mapping + * since 64K pages may overlap with other bridges when using 64K pages + * with 4K HW pages on IO space. + * + * Because of that usage pattern, it's only available with CONFIG_HOTPLUG + * and is implemented for small size rather than speed. + */ +#ifdef CONFIG_HOTPLUG + +void __flush_hash_table_range(struct mm_struct *mm, unsigned long start, + unsigned long end) +{ + unsigned long flags; + + start = _ALIGN_DOWN(start, PAGE_SIZE); + end = _ALIGN_UP(end, PAGE_SIZE); + + BUG_ON(!mm->pgd); + + /* Note: Normally, we should only ever use a batch within a + * PTE locked section. This violates the rule, but will work + * since we don't actually modify the PTEs, we just flush the + * hash while leaving the PTEs intact (including their reference + * to being hashed). This is not the most performance oriented + * way to do things but is fine for our needs here. + */ + local_irq_save(flags); + arch_enter_lazy_mmu_mode(); + for (; start < end; start += PAGE_SIZE) { + pte_t *ptep = find_linux_pte(mm->pgd, start); + unsigned long pte; + + if (ptep == NULL) + continue; + pte = pte_val(*ptep); + if (!(pte & _PAGE_HASHPTE)) + continue; + hpte_need_flush(mm, start, ptep, pte, 0); + } + arch_leave_lazy_mmu_mode(); + local_irq_restore(flags); +} + +#endif /* CONFIG_HOTPLUG */ diff --git a/arch/powerpc/oprofile/op_model_power4.c b/arch/powerpc/oprofile/op_model_power4.c index fe597a154d4f..a7c206b665af 100644 --- a/arch/powerpc/oprofile/op_model_power4.c +++ b/arch/powerpc/oprofile/op_model_power4.c @@ -1,5 +1,7 @@ /* * Copyright (C) 2004 Anton Blanchard , IBM + * Added mmcra[slot] support: + * Copyright (C) 2006-2007 Will Schmidt , IBM * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -181,11 +183,17 @@ static void __attribute_used__ kernel_unknown_bucket(void) * On GQ and newer the MMCRA stores the HV and PR bits at the time * the SIAR was sampled. We use that to work out if the SIAR was sampled in * the hypervisor, our exception vectors or RTAS. + * If the MMCRA_SAMPLE_ENABLE bit is set, we can use the MMCRA[slot] bits + * to more accurately identify the address of the sampled instruction. The + * mmcra[slot] bits represent the slot number of a sampled instruction + * within an instruction group. The slot will contain a value between 1 + * and 5 if MMCRA_SAMPLE_ENABLE is set, otherwise 0. */ static unsigned long get_pc(struct pt_regs *regs) { unsigned long pc = mfspr(SPRN_SIAR); unsigned long mmcra; + unsigned long slot; /* Cant do much about it */ if (!cur_cpu_spec->oprofile_mmcra_sihv) @@ -193,6 +201,12 @@ static unsigned long get_pc(struct pt_regs *regs) mmcra = mfspr(SPRN_MMCRA); + if (mmcra & MMCRA_SAMPLE_ENABLE) { + slot = ((mmcra & MMCRA_SLOT) >> MMCRA_SLOT_SHIFT); + if (slot > 1) + pc += 4 * (slot - 1); + } + /* Were we in the hypervisor? */ if (firmware_has_feature(FW_FEATURE_LPAR) && (mmcra & cur_cpu_spec->oprofile_mmcra_sihv)) diff --git a/arch/powerpc/platforms/52xx/efika.c b/arch/powerpc/platforms/52xx/efika.c index f591a9fc19b9..4be6e7a17b66 100644 --- a/arch/powerpc/platforms/52xx/efika.c +++ b/arch/powerpc/platforms/52xx/efika.c @@ -54,7 +54,7 @@ static int rtas_read_config(struct pci_bus *bus, unsigned int devfn, int offset, struct pci_controller *hose = bus->sysdata; unsigned long addr = (offset & 0xff) | ((devfn & 0xff) << 8) | (((bus->number - hose->first_busno) & 0xff) << 16) - | (hose->index << 24); + | (hose->global_number << 24); int ret = -1; int rval; @@ -69,7 +69,7 @@ static int rtas_write_config(struct pci_bus *bus, unsigned int devfn, struct pci_controller *hose = bus->sysdata; unsigned long addr = (offset & 0xff) | ((devfn & 0xff) << 8) | (((bus->number - hose->first_busno) & 0xff) << 16) - | (hose->index << 24); + | (hose->global_number << 24); int rval; rval = rtas_call(rtas_token("write-pci-config"), 3, 1, NULL, @@ -83,7 +83,7 @@ static struct pci_ops rtas_pci_ops = { }; -void __init efika_pcisetup(void) +static void __init efika_pcisetup(void) { const int *bus_range; int len; @@ -128,7 +128,7 @@ void __init efika_pcisetup(void) printk(" controlled by %s\n", pcictrl->full_name); printk("\n"); - hose = pcibios_alloc_controller(); + hose = pcibios_alloc_controller(of_node_get(pcictrl)); if (!hose) { printk(KERN_WARNING EFIKA_PLATFORM_NAME ": Can't allocate PCI controller structure for %s\n", @@ -136,7 +136,6 @@ void __init efika_pcisetup(void) return; } - hose->arch_data = of_node_get(pcictrl); hose->first_busno = bus_range[0]; hose->last_busno = bus_range[1]; hose->ops = &rtas_pci_ops; @@ -145,7 +144,7 @@ void __init efika_pcisetup(void) } #else -void __init efika_pcisetup(void) +static void __init efika_pcisetup(void) {} #endif @@ -252,6 +251,8 @@ define_machine(efika) .progress = rtas_progress, .get_boot_time = rtas_get_boot_time, .calibrate_decr = generic_calibrate_decr, +#ifdef CONFIG_PCI .phys_mem_access_prot = pci_phys_mem_access_prot, +#endif }; diff --git a/arch/powerpc/platforms/52xx/lite5200.c b/arch/powerpc/platforms/52xx/lite5200.c index 1cfc00dfb99a..5c46e898fd45 100644 --- a/arch/powerpc/platforms/52xx/lite5200.c +++ b/arch/powerpc/platforms/52xx/lite5200.c @@ -156,7 +156,7 @@ static void __init lite5200_setup_arch(void) } -void lite5200_show_cpuinfo(struct seq_file *m) +static void lite5200_show_cpuinfo(struct seq_file *m) { struct device_node* np = of_find_all_nodes(NULL); const char *model = NULL; diff --git a/arch/powerpc/platforms/52xx/mpc52xx_pci.c b/arch/powerpc/platforms/52xx/mpc52xx_pci.c index 34d34a26d305..4c6c82a684b1 100644 --- a/arch/powerpc/platforms/52xx/mpc52xx_pci.c +++ b/arch/powerpc/platforms/52xx/mpc52xx_pci.c @@ -112,18 +112,18 @@ mpc52xx_pci_read_config(struct pci_bus *bus, unsigned int devfn, u32 value; if (ppc_md.pci_exclude_device) - if (ppc_md.pci_exclude_device(bus->number, devfn)) + if (ppc_md.pci_exclude_device(hose, bus->number, devfn)) return PCIBIOS_DEVICE_NOT_FOUND; out_be32(hose->cfg_addr, (1 << 31) | - ((bus->number - hose->bus_offset) << 16) | + (bus->number << 16) | (devfn << 8) | (offset & 0xfc)); mb(); #if defined(CONFIG_PPC_MPC5200_BUGFIX) - if (bus->number != hose->bus_offset) { + if (bus->number) { /* workaround for the bug 435 of the MPC5200 (L25R); * Don't do 32 bits config access during type-1 cycles */ switch (len) { @@ -169,18 +169,18 @@ mpc52xx_pci_write_config(struct pci_bus *bus, unsigned int devfn, u32 value, mask; if (ppc_md.pci_exclude_device) - if (ppc_md.pci_exclude_device(bus->number, devfn)) + if (ppc_md.pci_exclude_device(hose, bus->number, devfn)) return PCIBIOS_DEVICE_NOT_FOUND; out_be32(hose->cfg_addr, (1 << 31) | - ((bus->number - hose->bus_offset) << 16) | + (bus->number << 16) | (devfn << 8) | (offset & 0xfc)); mb(); #if defined(CONFIG_PPC_MPC5200_BUGFIX) - if (bus->number != hose->bus_offset) { + if (bus->number) { /* workaround for the bug 435 of the MPC5200 (L25R); * Don't do 32 bits config access during type-1 cycles */ switch (len) { @@ -385,17 +385,13 @@ mpc52xx_add_bridge(struct device_node *node) * tree are needed to configure the 52xx PCI controller. Rather * than parse the tree here, let pci_process_bridge_OF_ranges() * do it for us and extract the values after the fact */ - hose = pcibios_alloc_controller(); + hose = pcibios_alloc_controller(node); if (!hose) return -ENOMEM; - hose->arch_data = node; - hose->set_cfg_type = 1; - hose->first_busno = bus_range ? bus_range[0] : 0; hose->last_busno = bus_range ? bus_range[1] : 0xff; - hose->bus_offset = 0; hose->ops = &mpc52xx_pci_ops; pci_regs = ioremap(rsrc.start, rsrc.end - rsrc.start + 1); diff --git a/arch/powerpc/platforms/52xx/mpc52xx_pm.c b/arch/powerpc/platforms/52xx/mpc52xx_pm.c index fd40044d16cd..ee2e7639c63e 100644 --- a/arch/powerpc/platforms/52xx/mpc52xx_pm.c +++ b/arch/powerpc/platforms/52xx/mpc52xx_pm.c @@ -9,8 +9,8 @@ /* these are defined in mpc52xx_sleep.S, and only used here */ -extern void mpc52xx_deep_sleep(void *sram, void *sdram_regs, - struct mpc52xx_cdm *, struct mpc52xx_intr *); +extern void mpc52xx_deep_sleep(void __iomem *sram, void __iomem *sdram_regs, + struct mpc52xx_cdm __iomem *, struct mpc52xx_intr __iomem*); extern void mpc52xx_ds_sram(void); extern const long mpc52xx_ds_sram_size; extern void mpc52xx_ds_cached(void); @@ -21,7 +21,7 @@ static void __iomem *sdram; static struct mpc52xx_cdm __iomem *cdm; static struct mpc52xx_intr __iomem *intr; static struct mpc52xx_gpio_wkup __iomem *gpiow; -static void *sram; +static void __iomem *sram; static int sram_size; struct mpc52xx_suspend mpc52xx_suspend; @@ -100,7 +100,7 @@ int mpc52xx_pm_enter(suspend_state_t state) u32 clk_enables; u32 msr, hid0; u32 intr_main_mask; - void __iomem * irq_0x500 = (void *)CONFIG_KERNEL_START + 0x500; + void __iomem * irq_0x500 = (void __iomem *)CONFIG_KERNEL_START + 0x500; unsigned long irq_0x500_stop = (unsigned long)irq_0x500 + mpc52xx_ds_cached_size; char saved_0x500[mpc52xx_ds_cached_size]; diff --git a/arch/powerpc/platforms/82xx/Kconfig b/arch/powerpc/platforms/82xx/Kconfig index de7fce9cb6eb..89fde43895c5 100644 --- a/arch/powerpc/platforms/82xx/Kconfig +++ b/arch/powerpc/platforms/82xx/Kconfig @@ -1,5 +1,5 @@ choice - prompt "Machine Type" + prompt "82xx Board Type" depends on PPC_82xx default MPC82xx_ADS diff --git a/arch/powerpc/platforms/82xx/mpc82xx_ads.c b/arch/powerpc/platforms/82xx/mpc82xx_ads.c index 47cb09f08052..da20832b27f1 100644 --- a/arch/powerpc/platforms/82xx/mpc82xx_ads.c +++ b/arch/powerpc/platforms/82xx/mpc82xx_ads.c @@ -49,7 +49,7 @@ #include #include -#include <../sysdev/cpm2_pic.h> +#include #include "pq2ads.h" @@ -507,7 +507,8 @@ void m82xx_pci_init_irq(void) return; } -static int m82xx_pci_exclude_device(u_char bus, u_char devfn) +static int m82xx_pci_exclude_device(struct pci_controller *hose, + u_char bus, u_char devfn) { if (bus == 0 && PCI_SLOT(devfn) == 0) return PCIBIOS_DEVICE_NOT_FOUND; @@ -515,7 +516,7 @@ static int m82xx_pci_exclude_device(u_char bus, u_char devfn) return PCIBIOS_SUCCESSFUL; } -void __init add_bridge(struct device_node *np) +static void __init mpc82xx_add_bridge(struct device_node *np) { int len; struct pci_controller *hose; @@ -542,19 +543,13 @@ void __init add_bridge(struct device_node *np) pci_assign_all_buses = 1; - hose = pcibios_alloc_controller(); + hose = pcibios_alloc_controller(np); if (!hose) return; - hose->arch_data = np; - hose->set_cfg_type = 1; - hose->first_busno = bus_range ? bus_range[0] : 0; hose->last_busno = bus_range ? bus_range[1] : 0xff; - hose->bus_offset = 0; - - hose->set_cfg_type = 1; setup_indirect_pci(hose, r.start + offsetof(pci_cpm2_t, pci_cfg_addr), @@ -584,7 +579,7 @@ static void __init mpc82xx_ads_setup_arch(void) #ifdef CONFIG_PCI ppc_md.pci_exclude_device = m82xx_pci_exclude_device; for (np = NULL; (np = of_find_node_by_type(np, "pci")) != NULL;) - add_bridge(np); + mpc82xx_add_bridge(np); of_node_put(np); #endif diff --git a/arch/powerpc/platforms/83xx/Kconfig b/arch/powerpc/platforms/83xx/Kconfig index 19cafdf6df93..ec305f18abd8 100644 --- a/arch/powerpc/platforms/83xx/Kconfig +++ b/arch/powerpc/platforms/83xx/Kconfig @@ -1,5 +1,5 @@ choice - prompt "Machine Type" + prompt "83xx Board Type" depends on PPC_83xx default MPC834x_MDS diff --git a/arch/powerpc/platforms/83xx/Makefile b/arch/powerpc/platforms/83xx/Makefile index 31a91b53f528..5a98f885779f 100644 --- a/arch/powerpc/platforms/83xx/Makefile +++ b/arch/powerpc/platforms/83xx/Makefile @@ -1,7 +1,7 @@ # # Makefile for the PowerPC 83xx linux kernel. # -obj-y := misc.o +obj-y := misc.o usb.o obj-$(CONFIG_PCI) += pci.o obj-$(CONFIG_MPC8313_RDB) += mpc8313_rdb.o obj-$(CONFIG_MPC832x_RDB) += mpc832x_rdb.o diff --git a/arch/powerpc/platforms/83xx/mpc8313_rdb.c b/arch/powerpc/platforms/83xx/mpc8313_rdb.c index 96970ac887ee..3edfe170a03b 100644 --- a/arch/powerpc/platforms/83xx/mpc8313_rdb.c +++ b/arch/powerpc/platforms/83xx/mpc8313_rdb.c @@ -28,11 +28,6 @@ #define DBG(fmt...) #endif -#ifndef CONFIG_PCI -unsigned long isa_io_base = 0; -unsigned long isa_mem_base = 0; -#endif - /* ************************************************************************ * * Setup the architecture @@ -49,10 +44,11 @@ static void __init mpc8313_rdb_setup_arch(void) #ifdef CONFIG_PCI for (np = NULL; (np = of_find_node_by_type(np, "pci")) != NULL;) - add_bridge(np); + mpc83xx_add_bridge(np); ppc_md.pci_exclude_device = mpc83xx_exclude_device; #endif + mpc831x_usb_cfg(); } void __init mpc8313_rdb_init_IRQ(void) diff --git a/arch/powerpc/platforms/83xx/mpc832x_mds.c b/arch/powerpc/platforms/83xx/mpc832x_mds.c index 94843ed52a93..b39cb52c6fb9 100644 --- a/arch/powerpc/platforms/83xx/mpc832x_mds.c +++ b/arch/powerpc/platforms/83xx/mpc832x_mds.c @@ -49,11 +49,6 @@ #define DBG(fmt...) #endif -#ifndef CONFIG_PCI -unsigned long isa_io_base = 0; -unsigned long isa_mem_base = 0; -#endif - static u8 *bcsr_regs = NULL; /* ************************************************************************ @@ -80,7 +75,7 @@ static void __init mpc832x_sys_setup_arch(void) #ifdef CONFIG_PCI for (np = NULL; (np = of_find_node_by_type(np, "pci")) != NULL;) - add_bridge(np); + mpc83xx_add_bridge(np); ppc_md.pci_exclude_device = mpc83xx_exclude_device; #endif diff --git a/arch/powerpc/platforms/83xx/mpc832x_rdb.c b/arch/powerpc/platforms/83xx/mpc832x_rdb.c index 3db68b73fc32..b2b28a44738c 100644 --- a/arch/powerpc/platforms/83xx/mpc832x_rdb.c +++ b/arch/powerpc/platforms/83xx/mpc832x_rdb.c @@ -32,11 +32,6 @@ #define DBG(fmt...) #endif -#ifndef CONFIG_PCI -unsigned long isa_io_base = 0; -unsigned long isa_mem_base = 0; -#endif - /* ************************************************************************ * * Setup the architecture @@ -53,7 +48,7 @@ static void __init mpc832x_rdb_setup_arch(void) #ifdef CONFIG_PCI for (np = NULL; (np = of_find_node_by_type(np, "pci")) != NULL;) - add_bridge(np); + mpc83xx_add_bridge(np); ppc_md.pci_exclude_device = mpc83xx_exclude_device; #endif diff --git a/arch/powerpc/platforms/83xx/mpc834x_itx.c b/arch/powerpc/platforms/83xx/mpc834x_itx.c index 40a01947d684..47ba5446f63c 100644 --- a/arch/powerpc/platforms/83xx/mpc834x_itx.c +++ b/arch/powerpc/platforms/83xx/mpc834x_itx.c @@ -38,11 +38,6 @@ #include "mpc83xx.h" -#ifndef CONFIG_PCI -unsigned long isa_io_base = 0; -unsigned long isa_mem_base = 0; -#endif - /* ************************************************************************ * * Setup the architecture @@ -59,10 +54,12 @@ static void __init mpc834x_itx_setup_arch(void) #ifdef CONFIG_PCI for (np = NULL; (np = of_find_node_by_type(np, "pci")) != NULL;) - add_bridge(np); + mpc83xx_add_bridge(np); ppc_md.pci_exclude_device = mpc83xx_exclude_device; #endif + + mpc834x_usb_cfg(); } static void __init mpc834x_itx_init_IRQ(void) diff --git a/arch/powerpc/platforms/83xx/mpc834x_mds.c b/arch/powerpc/platforms/83xx/mpc834x_mds.c index 10394b2d7e7a..4c9ff9cadfe4 100644 --- a/arch/powerpc/platforms/83xx/mpc834x_mds.c +++ b/arch/powerpc/platforms/83xx/mpc834x_mds.c @@ -38,61 +38,17 @@ #include "mpc83xx.h" -#ifndef CONFIG_PCI -unsigned long isa_io_base = 0; -unsigned long isa_mem_base = 0; -#endif - #define BCSR5_INT_USB 0x02 -/* Note: This is only for PB, not for PB+PIB - * On PB only port0 is connected using ULPI */ -static int mpc834x_usb_cfg(void) +static int mpc834xemds_usb_cfg(void) { - unsigned long sccr, sicrl; - void __iomem *immap; + struct device_node *np; void __iomem *bcsr_regs = NULL; u8 bcsr5; - struct device_node *np = NULL; - int port0_is_dr = 0; - - if ((np = of_find_compatible_node(NULL, "usb", "fsl-usb2-dr")) != NULL) - port0_is_dr = 1; - if ((np = of_find_compatible_node(NULL, "usb", "fsl-usb2-mph")) != NULL){ - if (port0_is_dr) { - printk(KERN_WARNING - "There is only one USB port on PB board! \n"); - return -1; - } else if (!port0_is_dr) - /* No usb port enabled */ - return -1; - } - - immap = ioremap(get_immrbase(), 0x1000); - if (!immap) - return -1; - - /* Configure clock */ - sccr = in_be32(immap + MPC83XX_SCCR_OFFS); - if (port0_is_dr) - sccr |= MPC83XX_SCCR_USB_DRCM_11; /* 1:3 */ - else - sccr |= MPC83XX_SCCR_USB_MPHCM_11; /* 1:3 */ - out_be32(immap + MPC83XX_SCCR_OFFS, sccr); - - /* Configure Pin */ - sicrl = in_be32(immap + MPC83XX_SICRL_OFFS); - /* set port0 only */ - if (port0_is_dr) - sicrl |= MPC83XX_SICRL_USB0; - else - sicrl &= ~(MPC83XX_SICRL_USB0); - out_be32(immap + MPC83XX_SICRL_OFFS, sicrl); - - iounmap(immap); + mpc834x_usb_cfg(); /* Map BCSR area */ np = of_find_node_by_name(NULL, "bcsr"); - if (np != 0) { + if (np) { struct resource res; of_address_to_resource(np, 0, &res); @@ -129,12 +85,12 @@ static void __init mpc834x_mds_setup_arch(void) #ifdef CONFIG_PCI for (np = NULL; (np = of_find_node_by_type(np, "pci")) != NULL;) - add_bridge(np); + mpc83xx_add_bridge(np); ppc_md.pci_exclude_device = mpc83xx_exclude_device; #endif - mpc834x_usb_cfg(); + mpc834xemds_usb_cfg(); } static void __init mpc834x_mds_init_IRQ(void) diff --git a/arch/powerpc/platforms/83xx/mpc836x_mds.c b/arch/powerpc/platforms/83xx/mpc836x_mds.c index bceeff8bbfd2..0e615fd65c1f 100644 --- a/arch/powerpc/platforms/83xx/mpc836x_mds.c +++ b/arch/powerpc/platforms/83xx/mpc836x_mds.c @@ -55,11 +55,6 @@ #define DBG(fmt...) #endif -#ifndef CONFIG_PCI -unsigned long isa_io_base = 0; -unsigned long isa_mem_base = 0; -#endif - static u8 *bcsr_regs = NULL; /* ************************************************************************ @@ -86,7 +81,7 @@ static void __init mpc836x_mds_setup_arch(void) #ifdef CONFIG_PCI for (np = NULL; (np = of_find_node_by_type(np, "pci")) != NULL;) - add_bridge(np); + mpc83xx_add_bridge(np); ppc_md.pci_exclude_device = mpc83xx_exclude_device; #endif diff --git a/arch/powerpc/platforms/83xx/mpc83xx.h b/arch/powerpc/platforms/83xx/mpc83xx.h index 9cd03b59c8f4..589ee55730f3 100644 --- a/arch/powerpc/platforms/83xx/mpc83xx.h +++ b/arch/powerpc/platforms/83xx/mpc83xx.h @@ -3,9 +3,11 @@ #include #include +#include /* System Clock Control Register */ #define MPC83XX_SCCR_OFFS 0xA08 +#define MPC83XX_SCCR_USB_MASK 0x00f00000 #define MPC83XX_SCCR_USB_MPHCM_11 0x00c00000 #define MPC83XX_SCCR_USB_MPHCM_01 0x00400000 #define MPC83XX_SCCR_USB_MPHCM_10 0x00800000 @@ -15,21 +17,43 @@ /* system i/o configuration register low */ #define MPC83XX_SICRL_OFFS 0x114 -#define MPC83XX_SICRL_USB0 0x40000000 -#define MPC83XX_SICRL_USB1 0x20000000 +#define MPC834X_SICRL_USB_MASK 0x60000000 +#define MPC834X_SICRL_USB0 0x40000000 +#define MPC834X_SICRL_USB1 0x20000000 +#define MPC831X_SICRL_USB_MASK 0x00000c00 +#define MPC831X_SICRL_USB_ULPI 0x00000800 /* system i/o configuration register high */ #define MPC83XX_SICRH_OFFS 0x118 -#define MPC83XX_SICRH_USB_UTMI 0x00020000 +#define MPC834X_SICRH_USB_UTMI 0x00020000 +#define MPC831X_SICRH_USB_MASK 0x000000e0 +#define MPC831X_SICRH_USB_ULPI 0x000000a0 + +/* USB Control Register */ +#define FSL_USB2_CONTROL_OFFS 0x500 +#define CONTROL_UTMI_PHY_EN 0x00000200 +#define CONTROL_REFSEL_48MHZ 0x00000080 +#define CONTROL_PHY_CLK_SEL_ULPI 0x00000400 +#define CONTROL_OTG_PORT 0x00000020 + +/* USB PORTSC Registers */ +#define FSL_USB2_PORTSC1_OFFS 0x184 +#define FSL_USB2_PORTSC2_OFFS 0x188 +#define PORTSCX_PTW_16BIT 0x10000000 +#define PORTSCX_PTS_UTMI 0x00000000 +#define PORTSCX_PTS_ULPI 0x80000000 /* * Declaration for the various functions exported by the * mpc83xx_* files. Mostly for use by mpc83xx_setup */ -extern int add_bridge(struct device_node *dev); -extern int mpc83xx_exclude_device(u_char bus, u_char devfn); +extern int mpc83xx_add_bridge(struct device_node *dev); +extern int mpc83xx_exclude_device(struct pci_controller *hose, + u_char bus, u_char devfn); extern void mpc83xx_restart(char *cmd); extern long mpc83xx_time_init(void); +extern int mpc834x_usb_cfg(void); +extern int mpc831x_usb_cfg(void); #endif /* __MPC83XX_H__ */ diff --git a/arch/powerpc/platforms/83xx/pci.c b/arch/powerpc/platforms/83xx/pci.c index 774457d09e94..c0e2b89154e5 100644 --- a/arch/powerpc/platforms/83xx/pci.c +++ b/arch/powerpc/platforms/83xx/pci.c @@ -33,19 +33,14 @@ #define DBG(x...) #endif -int mpc83xx_pci2_busno; - -int mpc83xx_exclude_device(u_char bus, u_char devfn) +int mpc83xx_exclude_device(struct pci_controller *hose, u_char bus, u_char devfn) { - if (bus == 0 && PCI_SLOT(devfn) == 0) + if ((bus == hose->first_busno) && PCI_SLOT(devfn) == 0) return PCIBIOS_DEVICE_NOT_FOUND; - if (mpc83xx_pci2_busno) - if (bus == (mpc83xx_pci2_busno) && PCI_SLOT(devfn) == 0) - return PCIBIOS_DEVICE_NOT_FOUND; return PCIBIOS_SUCCESSFUL; } -int __init add_bridge(struct device_node *dev) +int __init mpc83xx_add_bridge(struct device_node *dev) { int len; struct pci_controller *hose; @@ -66,11 +61,10 @@ int __init add_bridge(struct device_node *dev) " bus 0\n", dev->full_name); } - hose = pcibios_alloc_controller(); + pci_assign_all_buses = 1; + hose = pcibios_alloc_controller(dev); if (!hose) return -ENOMEM; - hose->arch_data = dev; - hose->set_cfg_type = 1; hose->first_busno = bus_range ? bus_range[0] : 0; hose->last_busno = bus_range ? bus_range[1] : 0xff; @@ -86,8 +80,6 @@ int __init add_bridge(struct device_node *dev) if ((rsrc.start & 0xfffff) == 0x8600) { setup_indirect_pci(hose, immr + 0x8380, immr + 0x8384); primary = 0; - hose->bus_offset = hose->first_busno; - mpc83xx_pci2_busno = hose->first_busno; } printk(KERN_INFO "Found MPC83xx PCI host bridge at 0x%016llx. " diff --git a/arch/powerpc/platforms/83xx/usb.c b/arch/powerpc/platforms/83xx/usb.c new file mode 100644 index 000000000000..e7fdf013cd39 --- /dev/null +++ b/arch/powerpc/platforms/83xx/usb.c @@ -0,0 +1,181 @@ +/* + * Freescale 83xx USB SOC setup code + * + * Copyright (C) 2007 Freescale Semiconductor, Inc. + * Author: Li Yang + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + + +#include +#include +#include + +#include +#include +#include + +#include "mpc83xx.h" + + +#ifdef CONFIG_MPC834x +int mpc834x_usb_cfg(void) +{ + unsigned long sccr, sicrl, sicrh; + void __iomem *immap; + struct device_node *np = NULL; + int port0_is_dr = 0, port1_is_dr = 0; + const void *prop, *dr_mode; + + immap = ioremap(get_immrbase(), 0x1000); + if (!immap) + return -ENOMEM; + + /* Read registers */ + /* Note: DR and MPH must use the same clock setting in SCCR */ + sccr = in_be32(immap + MPC83XX_SCCR_OFFS) & ~MPC83XX_SCCR_USB_MASK; + sicrl = in_be32(immap + MPC83XX_SICRL_OFFS) & ~MPC834X_SICRL_USB_MASK; + sicrh = in_be32(immap + MPC83XX_SICRH_OFFS) & ~MPC834X_SICRH_USB_UTMI; + + np = of_find_compatible_node(NULL, "usb", "fsl-usb2-dr"); + if (np) { + sccr |= MPC83XX_SCCR_USB_DRCM_11; /* 1:3 */ + + prop = of_get_property(np, "phy_type", NULL); + if (prop && (!strcmp(prop, "utmi") || + !strcmp(prop, "utmi_wide"))) { + sicrl |= MPC834X_SICRL_USB0 | MPC834X_SICRL_USB1; + sicrh |= MPC834X_SICRH_USB_UTMI; + port1_is_dr = 1; + } else if (prop && !strcmp(prop, "serial")) { + dr_mode = of_get_property(np, "dr_mode", NULL); + if (dr_mode && !strcmp(dr_mode, "otg")) { + sicrl |= MPC834X_SICRL_USB0 | MPC834X_SICRL_USB1; + port1_is_dr = 1; + } else { + sicrl |= MPC834X_SICRL_USB0; + } + } else if (prop && !strcmp(prop, "ulpi")) { + sicrl |= MPC834X_SICRL_USB0; + } else { + printk(KERN_WARNING "834x USB PHY type not supported\n"); + } + port0_is_dr = 1; + of_node_put(np); + } + np = of_find_compatible_node(NULL, "usb", "fsl-usb2-mph"); + if (np) { + sccr |= MPC83XX_SCCR_USB_MPHCM_11; /* 1:3 */ + + prop = of_get_property(np, "port0", NULL); + if (prop) { + if (port0_is_dr) + printk(KERN_WARNING + "834x USB port0 can't be used by both DR and MPH!\n"); + sicrl |= MPC834X_SICRL_USB0; + } + prop = of_get_property(np, "port1", NULL); + if (prop) { + if (port1_is_dr) + printk(KERN_WARNING + "834x USB port1 can't be used by both DR and MPH!\n"); + sicrl |= MPC834X_SICRL_USB1; + } + of_node_put(np); + } + + /* Write back */ + out_be32(immap + MPC83XX_SCCR_OFFS, sccr); + out_be32(immap + MPC83XX_SICRL_OFFS, sicrl); + out_be32(immap + MPC83XX_SICRH_OFFS, sicrh); + + iounmap(immap); + return 0; +} +#endif /* CONFIG_MPC834x */ + +#ifdef CONFIG_PPC_MPC831x +int mpc831x_usb_cfg(void) +{ + u32 temp; + void __iomem *immap, *usb_regs; + struct device_node *np = NULL; + const void *prop; + struct resource res; + int ret = 0; +#ifdef CONFIG_USB_OTG + const void *dr_mode; +#endif + + np = of_find_compatible_node(NULL, "usb", "fsl-usb2-dr"); + if (!np) + return -ENODEV; + prop = of_get_property(np, "phy_type", NULL); + + /* Map IMMR space for pin and clock settings */ + immap = ioremap(get_immrbase(), 0x1000); + if (!immap) { + of_node_put(np); + return -ENOMEM; + } + + /* Configure clock */ + temp = in_be32(immap + MPC83XX_SCCR_OFFS); + temp &= ~MPC83XX_SCCR_USB_MASK; + temp |= MPC83XX_SCCR_USB_DRCM_11; /* 1:3 */ + out_be32(immap + MPC83XX_SCCR_OFFS, temp); + + /* Configure pin mux for ULPI. There is no pin mux for UTMI */ + if (!strcmp(prop, "ulpi")) { + temp = in_be32(immap + MPC83XX_SICRL_OFFS); + temp &= ~MPC831X_SICRL_USB_MASK; + temp |= MPC831X_SICRL_USB_ULPI; + out_be32(immap + MPC83XX_SICRL_OFFS, temp); + + temp = in_be32(immap + MPC83XX_SICRH_OFFS); + temp &= ~MPC831X_SICRH_USB_MASK; + temp |= MPC831X_SICRH_USB_ULPI; + out_be32(immap + MPC83XX_SICRH_OFFS, temp); + } + + iounmap(immap); + + /* Map USB SOC space */ + ret = of_address_to_resource(np, 0, &res); + if (ret) { + of_node_put(np); + return ret; + } + usb_regs = ioremap(res.start, res.end - res.start + 1); + + /* Using on-chip PHY */ + if (!strcmp(prop, "utmi_wide") || + !strcmp(prop, "utmi")) { + /* Set UTMI_PHY_EN, REFSEL to 48MHZ */ + out_be32(usb_regs + FSL_USB2_CONTROL_OFFS, + CONTROL_UTMI_PHY_EN | CONTROL_REFSEL_48MHZ); + /* Using external UPLI PHY */ + } else if (!strcmp(prop, "ulpi")) { + /* Set PHY_CLK_SEL to ULPI */ + temp = CONTROL_PHY_CLK_SEL_ULPI; +#ifdef CONFIG_USB_OTG + /* Set OTG_PORT */ + dr_mode = of_get_property(np, "dr_mode", NULL); + if (dr_mode && !strcmp(dr_mode, "otg")) + temp |= CONTROL_OTG_PORT; +#endif /* CONFIG_USB_OTG */ + out_be32(usb_regs + FSL_USB2_CONTROL_OFFS, temp); + } else { + printk(KERN_WARNING "831x USB PHY type not supported\n"); + ret = -EINVAL; + } + + iounmap(usb_regs); + of_node_put(np); + return ret; +} +#endif /* CONFIG_PPC_MPC831x */ diff --git a/arch/powerpc/platforms/85xx/misc.c b/arch/powerpc/platforms/85xx/misc.c index 3e62fcb04c1c..4fe376e9c3b6 100644 --- a/arch/powerpc/platforms/85xx/misc.c +++ b/arch/powerpc/platforms/85xx/misc.c @@ -13,11 +13,43 @@ #include #include #include +#include +#include +#include + +static __be32 __iomem *rstcr; extern void abort(void); +static int __init mpc85xx_rstcr(void) +{ + struct device_node *np; + np = of_find_node_by_name(NULL, "global-utilities"); + if ((np && of_get_property(np, "fsl,has-rstcr", NULL))) { + const u32 *prop = of_get_property(np, "reg", NULL); + if (prop) { + /* map reset control register + * 0xE00B0 is offset of reset control register + */ + rstcr = ioremap(get_immrbase() + *prop + 0xB0, 0xff); + if (!rstcr) + printk (KERN_EMERG "Error: reset control " + "register not mapped!\n"); + } + } else + printk (KERN_INFO "rstcr compatible register does not exist!\n"); + if (np) + of_node_put(np); + return 0; +} + +arch_initcall(mpc85xx_rstcr); + void mpc85xx_restart(char *cmd) { local_irq_disable(); + if (rstcr) + /* set reset control register */ + out_be32(rstcr, 0x2); /* HRESET_REQ */ abort(); } diff --git a/arch/powerpc/platforms/85xx/mpc8544_ds.c b/arch/powerpc/platforms/85xx/mpc8544_ds.c index bec84ffe708e..6fb90aab879f 100644 --- a/arch/powerpc/platforms/85xx/mpc8544_ds.c +++ b/arch/powerpc/platforms/85xx/mpc8544_ds.c @@ -61,24 +61,11 @@ void __init mpc8544_ds_pic_init(void) return; } - /* Alloc mpic structure and per isu has 16 INT entries. */ mpic = mpic_alloc(np, r.start, MPIC_PRIMARY | MPIC_WANTS_RESET | MPIC_BIG_ENDIAN, - 16, 64, " OPENPIC "); + 0, 256, " OpenPIC "); BUG_ON(mpic == NULL); - /* - * 48 Internal Interrupts - */ - mpic_assign_isu(mpic, 0, r.start + 0x10200); - mpic_assign_isu(mpic, 1, r.start + 0x10400); - mpic_assign_isu(mpic, 2, r.start + 0x10600); - - /* - * 16 External interrupts - */ - mpic_assign_isu(mpic, 3, r.start + 0x10000); - mpic_init(mpic); #ifdef CONFIG_PPC_I8259 diff --git a/arch/powerpc/platforms/85xx/mpc85xx.h b/arch/powerpc/platforms/85xx/mpc85xx.h index 83415db33378..7286ffac2c1d 100644 --- a/arch/powerpc/platforms/85xx/mpc85xx.h +++ b/arch/powerpc/platforms/85xx/mpc85xx.h @@ -15,4 +15,4 @@ */ extern void mpc85xx_restart(char *); -extern int add_bridge(struct device_node *dev); +extern int mpc85xx_add_bridge(struct device_node *dev); diff --git a/arch/powerpc/platforms/85xx/mpc85xx_ads.c b/arch/powerpc/platforms/85xx/mpc85xx_ads.c index 5d27621f0927..7235f702394c 100644 --- a/arch/powerpc/platforms/85xx/mpc85xx_ads.c +++ b/arch/powerpc/platforms/85xx/mpc85xx_ads.c @@ -38,13 +38,9 @@ #include #endif -#ifndef CONFIG_PCI -unsigned long isa_io_base = 0; -unsigned long isa_mem_base = 0; -#endif - #ifdef CONFIG_PCI -static int mpc85xx_exclude_device(u_char bus, u_char devfn) +static int mpc85xx_exclude_device(struct pci_controller *hose, + u_char bus, u_char devfn) { if (bus == 0 && PCI_SLOT(devfn) == 0) return PCIBIOS_DEVICE_NOT_FOUND; @@ -91,30 +87,10 @@ static void __init mpc85xx_ads_pic_init(void) mpic = mpic_alloc(np, r.start, MPIC_PRIMARY | MPIC_WANTS_RESET | MPIC_BIG_ENDIAN, - 4, 0, " OpenPIC "); + 0, 256, " OpenPIC "); BUG_ON(mpic == NULL); of_node_put(np); - mpic_assign_isu(mpic, 0, r.start + 0x10200); - mpic_assign_isu(mpic, 1, r.start + 0x10280); - mpic_assign_isu(mpic, 2, r.start + 0x10300); - mpic_assign_isu(mpic, 3, r.start + 0x10380); - mpic_assign_isu(mpic, 4, r.start + 0x10400); - mpic_assign_isu(mpic, 5, r.start + 0x10480); - mpic_assign_isu(mpic, 6, r.start + 0x10500); - mpic_assign_isu(mpic, 7, r.start + 0x10580); - - /* Unused on this platform (leave room for 8548) */ - mpic_assign_isu(mpic, 8, r.start + 0x10600); - mpic_assign_isu(mpic, 9, r.start + 0x10680); - mpic_assign_isu(mpic, 10, r.start + 0x10700); - mpic_assign_isu(mpic, 11, r.start + 0x10780); - - /* External Interrupts */ - mpic_assign_isu(mpic, 12, r.start + 0x10000); - mpic_assign_isu(mpic, 13, r.start + 0x10080); - mpic_assign_isu(mpic, 14, r.start + 0x10100); - mpic_init(mpic); #ifdef CONFIG_CPM2 @@ -241,7 +217,7 @@ static void __init mpc85xx_ads_setup_arch(void) #ifdef CONFIG_PCI for (np = NULL; (np = of_find_node_by_type(np, "pci")) != NULL;) - add_bridge(np); + mpc85xx_add_bridge(np); ppc_md.pci_exclude_device = mpc85xx_exclude_device; #endif } diff --git a/arch/powerpc/platforms/85xx/mpc85xx_cds.c b/arch/powerpc/platforms/85xx/mpc85xx_cds.c index 1490eb3ce0d3..50c8d6458362 100644 --- a/arch/powerpc/platforms/85xx/mpc85xx_cds.c +++ b/arch/powerpc/platforms/85xx/mpc85xx_cds.c @@ -47,11 +47,6 @@ #include #include "mpc85xx.h" -#ifndef CONFIG_PCI -unsigned long isa_io_base = 0; -unsigned long isa_mem_base = 0; -#endif - static int cds_pci_slot = 2; static volatile u8 *cadmus; @@ -60,15 +55,11 @@ static volatile u8 *cadmus; #define ARCADIA_HOST_BRIDGE_IDSEL 17 #define ARCADIA_2ND_BRIDGE_IDSEL 3 -extern int mpc85xx_pci2_busno; - -static int mpc85xx_exclude_device(u_char bus, u_char devfn) +static int mpc85xx_exclude_device(struct pci_controller *hose, + u_char bus, u_char devfn) { - if (bus == 0 && PCI_SLOT(devfn) == 0) + if ((bus == hose->first_busno) && PCI_SLOT(devfn) == 0) return PCIBIOS_DEVICE_NOT_FOUND; - if (mpc85xx_pci2_busno) - if (bus == (mpc85xx_pci2_busno) && PCI_SLOT(devfn) == 0) - return PCIBIOS_DEVICE_NOT_FOUND; /* We explicitly do not go past the Tundra 320 Bridge */ if ((bus == 1) && (PCI_SLOT(devfn) == ARCADIA_2ND_BRIDGE_IDSEL)) return PCIBIOS_DEVICE_NOT_FOUND; @@ -78,52 +69,44 @@ static int mpc85xx_exclude_device(u_char bus, u_char devfn) return PCIBIOS_SUCCESSFUL; } -static void __init mpc85xx_cds_pcibios_fixup(void) +static void __init mpc85xx_cds_pci_irq_fixup(struct pci_dev *dev) { - struct pci_dev *dev; - u_char c; + u_char c; + if (dev->vendor == PCI_VENDOR_ID_VIA) { + switch (dev->device) { + case PCI_DEVICE_ID_VIA_82C586_1: + /* + * U-Boot does not set the enable bits + * for the IDE device. Force them on here. + */ + pci_read_config_byte(dev, 0x40, &c); + c |= 0x03; /* IDE: Chip Enable Bits */ + pci_write_config_byte(dev, 0x40, c); - if ((dev = pci_get_device(PCI_VENDOR_ID_VIA, - PCI_DEVICE_ID_VIA_82C586_1, NULL))) { + /* + * Since only primary interface works, force the + * IDE function to standard primary IDE interrupt + * w/ 8259 offset + */ + dev->irq = 14; + pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); + break; /* - * U-Boot does not set the enable bits - * for the IDE device. Force them on here. + * Force legacy USB interrupt routing */ - pci_read_config_byte(dev, 0x40, &c); - c |= 0x03; /* IDE: Chip Enable Bits */ - pci_write_config_byte(dev, 0x40, c); - - /* - * Since only primary interface works, force the - * IDE function to standard primary IDE interrupt - * w/ 8259 offset + case PCI_DEVICE_ID_VIA_82C586_2: + /* There are two USB controllers. + * Identify them by functon number */ - dev->irq = 14; - pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); - pci_dev_put(dev); + if (PCI_FUNC(dev->devfn)) + dev->irq = 11; + else + dev->irq = 10; + pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); + default: + break; + } } - - /* - * Force legacy USB interrupt routing - */ - if ((dev = pci_get_device(PCI_VENDOR_ID_VIA, - PCI_DEVICE_ID_VIA_82C586_2, NULL))) { - dev->irq = 10; - pci_write_config_byte(dev, PCI_INTERRUPT_LINE, 10); - pci_dev_put(dev); - } - - if ((dev = pci_get_device(PCI_VENDOR_ID_VIA, - PCI_DEVICE_ID_VIA_82C586_2, dev))) { - dev->irq = 11; - pci_write_config_byte(dev, PCI_INTERRUPT_LINE, 11); - pci_dev_put(dev); - } - - /* Now map all the PCI irqs */ - dev = NULL; - for_each_pci_dev(dev) - pci_read_irq_line(dev); } #ifdef CONFIG_PPC_I8259 @@ -165,33 +148,12 @@ static void __init mpc85xx_cds_pic_init(void) mpic = mpic_alloc(np, r.start, MPIC_PRIMARY | MPIC_WANTS_RESET | MPIC_BIG_ENDIAN, - 4, 0, " OpenPIC "); + 0, 256, " OpenPIC "); BUG_ON(mpic == NULL); /* Return the mpic node */ of_node_put(np); - mpic_assign_isu(mpic, 0, r.start + 0x10200); - mpic_assign_isu(mpic, 1, r.start + 0x10280); - mpic_assign_isu(mpic, 2, r.start + 0x10300); - mpic_assign_isu(mpic, 3, r.start + 0x10380); - mpic_assign_isu(mpic, 4, r.start + 0x10400); - mpic_assign_isu(mpic, 5, r.start + 0x10480); - mpic_assign_isu(mpic, 6, r.start + 0x10500); - mpic_assign_isu(mpic, 7, r.start + 0x10580); - - /* Used only for 8548 so far, but no harm in - * allocating them for everyone */ - mpic_assign_isu(mpic, 8, r.start + 0x10600); - mpic_assign_isu(mpic, 9, r.start + 0x10680); - mpic_assign_isu(mpic, 10, r.start + 0x10700); - mpic_assign_isu(mpic, 11, r.start + 0x10780); - - /* External Interrupts */ - mpic_assign_isu(mpic, 12, r.start + 0x10000); - mpic_assign_isu(mpic, 13, r.start + 0x10080); - mpic_assign_isu(mpic, 14, r.start + 0x10100); - mpic_init(mpic); #ifdef CONFIG_PPC_I8259 @@ -257,9 +219,9 @@ static void __init mpc85xx_cds_setup_arch(void) #ifdef CONFIG_PCI for (np = NULL; (np = of_find_node_by_type(np, "pci")) != NULL;) - add_bridge(np); + mpc85xx_add_bridge(np); - ppc_md.pcibios_fixup = mpc85xx_cds_pcibios_fixup; + ppc_md.pci_irq_fixup = mpc85xx_cds_pci_irq_fixup; ppc_md.pci_exclude_device = mpc85xx_exclude_device; #endif } diff --git a/arch/powerpc/platforms/85xx/mpc85xx_mds.c b/arch/powerpc/platforms/85xx/mpc85xx_mds.c index e3dddbfe66ff..004b80bd0b84 100644 --- a/arch/powerpc/platforms/85xx/mpc85xx_mds.c +++ b/arch/powerpc/platforms/85xx/mpc85xx_mds.c @@ -59,11 +59,6 @@ #define DBG(fmt...) #endif -#ifndef CONFIG_PCI -unsigned long isa_io_base = 0; -unsigned long isa_mem_base = 0; -#endif - /* ************************************************************************ * * Setup the architecture @@ -100,7 +95,7 @@ static void __init mpc85xx_mds_setup_arch(void) #ifdef CONFIG_PCI for (np = NULL; (np = of_find_node_by_type(np, "pci")) != NULL;) { - add_bridge(np); + mpc85xx_add_bridge(np); } of_node_put(np); #endif @@ -181,29 +176,10 @@ static void __init mpc85xx_mds_pic_init(void) mpic = mpic_alloc(np, r.start, MPIC_PRIMARY | MPIC_WANTS_RESET | MPIC_BIG_ENDIAN, - 4, 0, " OpenPIC "); + 0, 256, " OpenPIC "); BUG_ON(mpic == NULL); of_node_put(np); - /* Internal Interrupts */ - mpic_assign_isu(mpic, 0, r.start + 0x10200); - mpic_assign_isu(mpic, 1, r.start + 0x10280); - mpic_assign_isu(mpic, 2, r.start + 0x10300); - mpic_assign_isu(mpic, 3, r.start + 0x10380); - mpic_assign_isu(mpic, 4, r.start + 0x10400); - mpic_assign_isu(mpic, 5, r.start + 0x10480); - mpic_assign_isu(mpic, 6, r.start + 0x10500); - mpic_assign_isu(mpic, 7, r.start + 0x10580); - mpic_assign_isu(mpic, 8, r.start + 0x10600); - mpic_assign_isu(mpic, 9, r.start + 0x10680); - mpic_assign_isu(mpic, 10, r.start + 0x10700); - mpic_assign_isu(mpic, 11, r.start + 0x10780); - - /* External Interrupts */ - mpic_assign_isu(mpic, 12, r.start + 0x10000); - mpic_assign_isu(mpic, 13, r.start + 0x10080); - mpic_assign_isu(mpic, 14, r.start + 0x10100); - mpic_init(mpic); #ifdef CONFIG_QUICC_ENGINE diff --git a/arch/powerpc/platforms/85xx/pci.c b/arch/powerpc/platforms/85xx/pci.c index 48f17e23d771..8118417b7364 100644 --- a/arch/powerpc/platforms/85xx/pci.c +++ b/arch/powerpc/platforms/85xx/pci.c @@ -33,10 +33,8 @@ #define DBG(x...) #endif -int mpc85xx_pci2_busno = 0; - #ifdef CONFIG_PCI -int __init add_bridge(struct device_node *dev) +int __init mpc85xx_add_bridge(struct device_node *dev) { int len; struct pci_controller *hose; @@ -57,11 +55,10 @@ int __init add_bridge(struct device_node *dev) " bus 0\n", dev->full_name); } - hose = pcibios_alloc_controller(); + pci_assign_all_buses = 1; + hose = pcibios_alloc_controller(dev); if (!hose) return -ENOMEM; - hose->arch_data = dev; - hose->set_cfg_type = 1; hose->first_busno = bus_range ? bus_range[0] : 0; hose->last_busno = bus_range ? bus_range[1] : 0xff; @@ -74,8 +71,6 @@ int __init add_bridge(struct device_node *dev) if ((rsrc.start & 0xfffff) == 0x9000) { setup_indirect_pci(hose, immr + 0x9000, immr + 0x9004); primary = 0; - hose->bus_offset = hose->first_busno; - mpc85xx_pci2_busno = hose->first_busno; } printk(KERN_INFO "Found MPC85xx PCI host bridge at 0x%016llx. " diff --git a/arch/powerpc/platforms/86xx/Kconfig b/arch/powerpc/platforms/86xx/Kconfig index d1bcff500464..0faebfdc1596 100644 --- a/arch/powerpc/platforms/86xx/Kconfig +++ b/arch/powerpc/platforms/86xx/Kconfig @@ -1,5 +1,5 @@ choice - prompt "Machine Type" + prompt "86xx Board Type" depends on PPC_86xx default MPC8641_HPCN diff --git a/arch/powerpc/platforms/86xx/mpc86xx.h b/arch/powerpc/platforms/86xx/mpc86xx.h index 2834462590b8..23f7ed2a7f88 100644 --- a/arch/powerpc/platforms/86xx/mpc86xx.h +++ b/arch/powerpc/platforms/86xx/mpc86xx.h @@ -15,15 +15,10 @@ * mpc86xx_* files. Mostly for use by mpc86xx_setup(). */ -extern int add_bridge(struct device_node *dev); +extern int mpc86xx_add_bridge(struct device_node *dev); -extern int mpc86xx_exclude_device(u_char bus, u_char devfn); - -extern void setup_indirect_pcie(struct pci_controller *hose, - u32 cfg_addr, u32 cfg_data); -extern void setup_indirect_pcie_nomap(struct pci_controller *hose, - void __iomem *cfg_addr, - void __iomem *cfg_data); +extern int mpc86xx_exclude_device(struct pci_controller *hose, + u_char bus, u_char devfn); extern void __init mpc86xx_smp_init(void); diff --git a/arch/powerpc/platforms/86xx/mpc86xx_hpcn.c b/arch/powerpc/platforms/86xx/mpc86xx_hpcn.c index 1051702c8d4f..5b01ec7c13dc 100644 --- a/arch/powerpc/platforms/86xx/mpc86xx_hpcn.c +++ b/arch/powerpc/platforms/86xx/mpc86xx_hpcn.c @@ -44,13 +44,6 @@ #define DBG(fmt...) do { } while(0) #endif -#ifndef CONFIG_PCI -unsigned long isa_io_base = 0; -unsigned long isa_mem_base = 0; -unsigned long pci_dram_offset = 0; -#endif - - #ifdef CONFIG_PCI static void mpc86xx_8259_cascade(unsigned int irq, struct irq_desc *desc) { @@ -81,22 +74,9 @@ mpc86xx_hpcn_init_irq(void) /* Alloc mpic structure and per isu has 16 INT entries. */ mpic1 = mpic_alloc(np, res.start, MPIC_PRIMARY | MPIC_WANTS_RESET | MPIC_BIG_ENDIAN, - 16, NR_IRQS - 4, - " MPIC "); + 0, 256, " MPIC "); BUG_ON(mpic1 == NULL); - mpic_assign_isu(mpic1, 0, res.start + 0x10000); - - /* 48 Internal Interrupts */ - mpic_assign_isu(mpic1, 1, res.start + 0x10200); - mpic_assign_isu(mpic1, 2, res.start + 0x10400); - mpic_assign_isu(mpic1, 3, res.start + 0x10600); - - /* 16 External interrupts - * Moving them from [0 - 15] to [64 - 79] - */ - mpic_assign_isu(mpic1, 4, res.start + 0x10000); - mpic_init(mpic1); #ifdef CONFIG_PCI @@ -319,6 +299,7 @@ static void __devinit quirk_uli5229(struct pci_dev *dev) { unsigned short temp; pci_write_config_word(dev, 0x04, 0x0405); + dev->class &= ~0x5; pci_read_config_word(dev, 0x4a, &temp); temp |= 0x1000; pci_write_config_word(dev, 0x4a, temp); @@ -364,9 +345,7 @@ mpc86xx_hpcn_setup_arch(void) #ifdef CONFIG_PCI for (np = NULL; (np = of_find_node_by_type(np, "pci")) != NULL;) - add_bridge(np); - - ppc_md.pci_exclude_device = mpc86xx_exclude_device; + mpc86xx_add_bridge(np); #endif printk("MPC86xx HPCN board from Freescale Semiconductor\n"); diff --git a/arch/powerpc/platforms/86xx/pci.c b/arch/powerpc/platforms/86xx/pci.c index 8235c562661f..73cd5b05a84e 100644 --- a/arch/powerpc/platforms/86xx/pci.c +++ b/arch/powerpc/platforms/86xx/pci.c @@ -122,7 +122,6 @@ static void __init mpc86xx_setup_pcie(struct pci_controller *hose, u32 pcie_offset, u32 pcie_size) { u16 cmd; - unsigned int temps; DBG("PCIE host controller register offset 0x%08x, size 0x%08x.\n", pcie_offset, pcie_size); @@ -133,22 +132,49 @@ mpc86xx_setup_pcie(struct pci_controller *hose, u32 pcie_offset, u32 pcie_size) early_write_config_word(hose, 0, 0, PCI_COMMAND, cmd); early_write_config_byte(hose, 0, 0, PCI_LATENCY_TIMER, 0x80); - - /* PCIE Bus, Fix the MPC8641D host bridge's location to bus 0xFF. */ - early_read_config_dword(hose, 0, 0, PCI_PRIMARY_BUS, &temps); - temps = (temps & 0xff000000) | (0xff) | (0x0 << 8) | (0xfe << 16); - early_write_config_dword(hose, 0, 0, PCI_PRIMARY_BUS, temps); } -int mpc86xx_exclude_device(u_char bus, u_char devfn) +static void __devinit quirk_fsl_pcie_transparent(struct pci_dev *dev) { - if (bus == 0 && PCI_SLOT(devfn) == 0) - return PCIBIOS_DEVICE_NOT_FOUND; + struct resource *res; + int i, res_idx = PCI_BRIDGE_RESOURCES; + struct pci_controller *hose; - return PCIBIOS_SUCCESSFUL; + /* + * Make the bridge be transparent. + */ + dev->transparent = 1; + + hose = pci_bus_to_host(dev->bus); + if (!hose) { + printk(KERN_ERR "Can't find hose for bus %d\n", + dev->bus->number); + return; + } + + if (hose->io_resource.flags) { + res = &dev->resource[res_idx++]; + res->start = hose->io_resource.start; + res->end = hose->io_resource.end; + res->flags = hose->io_resource.flags; + } + + for (i = 0; i < 3; i++) { + res = &dev->resource[res_idx + i]; + res->start = hose->mem_resources[i].start; + res->end = hose->mem_resources[i].end; + res->flags = hose->mem_resources[i].flags; + } } -int __init add_bridge(struct device_node *dev) + +DECLARE_PCI_FIXUP_EARLY(0x1957, 0x7010, quirk_fsl_pcie_transparent); +DECLARE_PCI_FIXUP_EARLY(0x1957, 0x7011, quirk_fsl_pcie_transparent); + +#define PCIE_LTSSM 0x404 /* PCIe Link Training and Status */ +#define PCIE_LTSSM_L0 0x16 /* L0 state */ + +int __init mpc86xx_add_bridge(struct device_node *dev) { int len; struct pci_controller *hose; @@ -156,6 +182,7 @@ int __init add_bridge(struct device_node *dev) const int *bus_range; int has_address = 0; int primary = 0; + u16 val; DBG("Adding PCIE host bridge %s\n", dev->full_name); @@ -168,17 +195,23 @@ int __init add_bridge(struct device_node *dev) printk(KERN_WARNING "Can't get bus-range for %s, assume" " bus 0\n", dev->full_name); - hose = pcibios_alloc_controller(); + pci_assign_all_buses = 1; + hose = pcibios_alloc_controller(dev); if (!hose) return -ENOMEM; - hose->arch_data = dev; - hose->set_cfg_type = 1; - /* last_busno = 0xfe cause by MPC8641 PCIE bug */ + hose->indirect_type = PPC_INDIRECT_TYPE_EXT_REG | + PPC_INDIRECT_TYPE_SURPRESS_PRIMARY_BUS; + hose->first_busno = bus_range ? bus_range[0] : 0x0; - hose->last_busno = bus_range ? bus_range[1] : 0xfe; + hose->last_busno = bus_range ? bus_range[1] : 0xff; - setup_indirect_pcie(hose, rsrc.start, rsrc.start + 0x4); + setup_indirect_pci(hose, rsrc.start, rsrc.start + 0x4); + + /* Probe the hose link training status */ + early_read_config_word(hose, 0, 0, PCIE_LTSSM, &val); + if (val < PCIE_LTSSM_L0) + return -ENXIO; /* Setup the PCIE host controller. */ mpc86xx_setup_pcie(hose, rsrc.start, rsrc.end - rsrc.start + 1); diff --git a/arch/powerpc/platforms/8xx/m8xx_setup.c b/arch/powerpc/platforms/8xx/m8xx_setup.c index 0901dbada350..f1693550c70c 100644 --- a/arch/powerpc/platforms/8xx/m8xx_setup.c +++ b/arch/powerpc/platforms/8xx/m8xx_setup.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -49,6 +50,10 @@ #include "sysdev/mpc8xx_pic.h" +#ifdef CONFIG_PCMCIA_M8XX +struct mpc8xx_pcmcia_ops m8xx_pcmcia_ops; +#endif + void m8xx_calibrate_decr(void); extern void m8xx_wdt_handler_install(bd_t *bp); extern int cpm_pic_init(void); diff --git a/arch/powerpc/platforms/8xx/mpc885ads_setup.c b/arch/powerpc/platforms/8xx/mpc885ads_setup.c index c36e475d93dc..dc27dab48df0 100644 --- a/arch/powerpc/platforms/8xx/mpc885ads_setup.c +++ b/arch/powerpc/platforms/8xx/mpc885ads_setup.c @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -51,6 +52,70 @@ static void init_smc1_uart_ioports(struct fs_uart_platform_info* fpi); static void init_smc2_uart_ioports(struct fs_uart_platform_info* fpi); static void init_scc3_ioports(struct fs_platform_info* ptr); +#ifdef CONFIG_PCMCIA_M8XX +static void pcmcia_hw_setup(int slot, int enable) +{ + unsigned *bcsr_io; + + bcsr_io = ioremap(BCSR1, sizeof(unsigned long)); + if (enable) + clrbits32(bcsr_io, BCSR1_PCCEN); + else + setbits32(bcsr_io, BCSR1_PCCEN); + + iounmap(bcsr_io); +} + +static int pcmcia_set_voltage(int slot, int vcc, int vpp) +{ + u32 reg = 0; + unsigned *bcsr_io; + + bcsr_io = ioremap(BCSR1, sizeof(unsigned long)); + + switch(vcc) { + case 0: + break; + case 33: + reg |= BCSR1_PCCVCC0; + break; + case 50: + reg |= BCSR1_PCCVCC1; + break; + default: + return 1; + } + + switch(vpp) { + case 0: + break; + case 33: + case 50: + if(vcc == vpp) + reg |= BCSR1_PCCVPP1; + else + return 1; + break; + case 120: + if ((vcc == 33) || (vcc == 50)) + reg |= BCSR1_PCCVPP0; + else + return 1; + default: + return 1; + } + + /* first, turn off all power */ + clrbits32(bcsr_io, 0x00610000); + + /* enable new powersettings */ + setbits32(bcsr_io, reg); + + iounmap(bcsr_io); + return 0; +} +#endif + void __init mpc885ads_board_setup(void) { cpm8xx_t *cp; @@ -115,6 +180,12 @@ void __init mpc885ads_board_setup(void) immr_unmap(io_port); #endif + +#ifdef CONFIG_PCMCIA_M8XX + /*Set up board specific hook-ups*/ + m8xx_pcmcia_ops.hw_ctrl = pcmcia_hw_setup; + m8xx_pcmcia_ops.voltage_set = pcmcia_set_voltage; +#endif } diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig index 361acfa2894c..33545d352e92 100644 --- a/arch/powerpc/platforms/Kconfig +++ b/arch/powerpc/platforms/Kconfig @@ -2,7 +2,7 @@ menu "Platform support" choice prompt "Machine type" - depends on PPC64 || CLASSIC32 + depends on PPC64 || 6xx default PPC_MULTIPLATFORM config PPC_MULTIPLATFORM @@ -16,15 +16,30 @@ config EMBEDDED6xx bool "Embedded 6xx/7xx/7xxx-based board" depends on PPC32 && (BROKEN||BROKEN_ON_SMP) -config APUS - bool "Amiga-APUS" - depends on PPC32 && BROKEN +config PPC_82xx + bool "Freescale 82xx" + depends on 6xx + +config PPC_83xx + bool "Freescale 83xx" + depends on 6xx + select FSL_SOC + select 83xx + select WANT_DEVICE_TREE + +config PPC_86xx + bool "Freescale 86xx" + depends on 6xx + select FSL_SOC + select ALTIVEC help - Select APUS if configuring for a PowerUP Amiga. - More information is available at: - . + The Freescale E600 SoCs have 74xx cores. endchoice +config CLASSIC32 + def_bool y + depends on 6xx && PPC_MULTIPLATFORM + source "arch/powerpc/platforms/pseries/Kconfig" source "arch/powerpc/platforms/iseries/Kconfig" source "arch/powerpc/platforms/chrp/Kconfig" diff --git a/arch/powerpc/platforms/Kconfig.cputype b/arch/powerpc/platforms/Kconfig.cputype new file mode 100644 index 000000000000..b8b5fde94668 --- /dev/null +++ b/arch/powerpc/platforms/Kconfig.cputype @@ -0,0 +1,221 @@ +config PPC64 + bool "64-bit kernel" + default n + help + This option selects whether a 32-bit or a 64-bit kernel + will be built. + +menu "Processor support" +choice + prompt "Processor Type" + depends on PPC32 + default 6xx + help + There are five families of 32 bit PowerPC chips supported. + The most common ones are the desktop and server CPUs (601, 603, + 604, 740, 750, 74xx) CPUs from Freescale and IBM, with their + embedded 52xx/82xx/83xx/86xx counterparts. + The other embeeded parts, namely 4xx, 8xx, e200 (55xx) and e500 + (85xx) each form a family of their own that is not compatible + with the others. + + If unsure, select 52xx/6xx/7xx/74xx/82xx/83xx/86xx. + +config 6xx + bool "52xx/6xx/7xx/74xx/82xx/83xx/86xx" + select PPC_FPU + +config PPC_85xx + bool "Freescale 85xx" + select E500 + select FSL_SOC + select 85xx + select WANT_DEVICE_TREE + +config PPC_8xx + bool "Freescale 8xx" + select FSL_SOC + select 8xx + +config 40x + bool "AMCC 40x" + select PPC_DCR_NATIVE + +config 44x + bool "AMCC 44x" + select PPC_DCR_NATIVE + select WANT_DEVICE_TREE + +config E200 + bool "Freescale e200" + +endchoice + +config POWER4_ONLY + bool "Optimize for POWER4" + depends on PPC64 + default n + ---help--- + Cause the compiler to optimize for POWER4/POWER5/PPC970 processors. + The resulting binary will not work on POWER3 or RS64 processors + when compiled with binutils 2.15 or later. + +config POWER3 + bool + depends on PPC64 + default y if !POWER4_ONLY + +config POWER4 + depends on PPC64 + def_bool y + +config 6xx + bool + +# this is temp to handle compat with arch=ppc +config 8xx + bool + +# this is temp to handle compat with arch=ppc +config 83xx + bool + +# this is temp to handle compat with arch=ppc +config 85xx + bool + +config E500 + bool + +config PPC_FPU + bool + default y if PPC64 + +config 4xx + bool + depends on 40x || 44x + default y + +config BOOKE + bool + depends on E200 || E500 || 44x + default y + +config FSL_BOOKE + bool + depends on E200 || E500 + default y + +config PTE_64BIT + bool + depends on 44x || E500 + default y if 44x + default y if E500 && PHYS_64BIT + +config PHYS_64BIT + bool 'Large physical address support' if E500 + depends on 44x || E500 + select RESOURCES_64BIT + default y if 44x + ---help--- + This option enables kernel support for larger than 32-bit physical + addresses. This features is not be available on all e500 cores. + + If in doubt, say N here. + +config ALTIVEC + bool "AltiVec Support" + depends on CLASSIC32 || POWER4 + ---help--- + This option enables kernel support for the Altivec extensions to the + PowerPC processor. The kernel currently supports saving and restoring + altivec registers, and turning on the 'altivec enable' bit so user + processes can execute altivec instructions. + + This option is only usefully if you have a processor that supports + altivec (G4, otherwise known as 74xx series), but does not have + any affect on a non-altivec cpu (it does, however add code to the + kernel). + + If in doubt, say Y here. + +config SPE + bool "SPE Support" + depends on E200 || E500 + default y + ---help--- + This option enables kernel support for the Signal Processing + Extensions (SPE) to the PowerPC processor. The kernel currently + supports saving and restoring SPE registers, and turning on the + 'spe enable' bit so user processes can execute SPE instructions. + + This option is only useful if you have a processor that supports + SPE (e500, otherwise known as 85xx series), but does not have any + effect on a non-spe cpu (it does, however add code to the kernel). + + If in doubt, say Y here. + +config PPC_STD_MMU + bool + depends on 6xx || POWER3 || POWER4 || PPC64 + default y + +config PPC_STD_MMU_32 + def_bool y + depends on PPC_STD_MMU && PPC32 + +config PPC_MM_SLICES + bool + default y if HUGETLB_PAGE + default n + +config VIRT_CPU_ACCOUNTING + bool "Deterministic task and CPU time accounting" + depends on PPC64 + default y + help + Select this option to enable more accurate task and CPU time + accounting. This is done by reading a CPU counter on each + kernel entry and exit and on transitions within the kernel + between system, softirq and hardirq state, so there is a + small performance impact. This also enables accounting of + stolen time on logically-partitioned systems running on + IBM POWER5-based machines. + + If in doubt, say Y here. + +config SMP + depends on PPC_STD_MMU + bool "Symmetric multi-processing support" + ---help--- + This enables support for systems with more than one CPU. If you have + a system with only one CPU, say N. If you have a system with more + than one CPU, say Y. Note that the kernel does not currently + support SMP machines with 603/603e/603ev or PPC750 ("G3") processors + since they have inadequate hardware support for multiprocessor + operation. + + If you say N here, the kernel will run on single and multiprocessor + machines, but will use only one CPU of a multiprocessor machine. If + you say Y here, the kernel will run on single-processor machines. + On a single-processor machine, the kernel will run faster if you say + N here. + + If you don't know what to do here, say N. + +config NR_CPUS + int "Maximum number of CPUs (2-128)" + range 2 128 + depends on SMP + default "32" if PPC64 + default "4" + +config NOT_COHERENT_CACHE + bool + depends on 4xx || 8xx || E200 + default y + +config CONFIG_CHECK_CACHE_COHERENCY + bool + +endmenu diff --git a/arch/powerpc/platforms/apus/Kconfig b/arch/powerpc/platforms/apus/Kconfig deleted file mode 100644 index 6bde3bffed86..000000000000 --- a/arch/powerpc/platforms/apus/Kconfig +++ /dev/null @@ -1,130 +0,0 @@ - -config AMIGA - bool - depends on APUS - default y - help - This option enables support for the Amiga series of computers. - -config ZORRO - bool - depends on APUS - default y - help - This enables support for the Zorro bus in the Amiga. If you have - expansion cards in your Amiga that conform to the Amiga - AutoConfig(tm) specification, say Y, otherwise N. Note that even - expansion cards that do not fit in the Zorro slots but fit in e.g. - the CPU slot may fall in this category, so you have to say Y to let - Linux use these. - -config ABSTRACT_CONSOLE - bool - depends on APUS - default y - -config APUS_FAST_EXCEPT - bool - depends on APUS - default y - -config AMIGA_PCMCIA - bool "Amiga 1200/600 PCMCIA support" - depends on APUS && EXPERIMENTAL - help - Include support in the kernel for pcmcia on Amiga 1200 and Amiga - 600. If you intend to use pcmcia cards say Y; otherwise say N. - -config AMIGA_BUILTIN_SERIAL - tristate "Amiga builtin serial support" - depends on APUS - help - If you want to use your Amiga's built-in serial port in Linux, - answer Y. - - To compile this driver as a module, choose M here. - -config GVPIOEXT - tristate "GVP IO-Extender support" - depends on APUS - help - If you want to use a GVP IO-Extender serial card in Linux, say Y. - Otherwise, say N. - -config GVPIOEXT_LP - tristate "GVP IO-Extender parallel printer support" - depends on GVPIOEXT - help - Say Y to enable driving a printer from the parallel port on your - GVP IO-Extender card, N otherwise. - -config GVPIOEXT_PLIP - tristate "GVP IO-Extender PLIP support" - depends on GVPIOEXT - help - Say Y to enable doing IP over the parallel port on your GVP - IO-Extender card, N otherwise. - -config MULTIFACE_III_TTY - tristate "Multiface Card III serial support" - depends on APUS - help - If you want to use a Multiface III card's serial port in Linux, - answer Y. - - To compile this driver as a module, choose M here. - -config A2232 - tristate "Commodore A2232 serial support (EXPERIMENTAL)" - depends on EXPERIMENTAL && APUS - ---help--- - This option supports the 2232 7-port serial card shipped with the - Amiga 2000 and other Zorro-bus machines, dating from 1989. At - a max of 19,200 bps, the ports are served by a 6551 ACIA UART chip - each, plus a 8520 CIA, and a master 6502 CPU and buffer as well. The - ports were connected with 8 pin DIN connectors on the card bracket, - for which 8 pin to DB25 adapters were supplied. The card also had - jumpers internally to toggle various pinning configurations. - - This driver can be built as a module; but then "generic_serial" - will also be built as a module. This has to be loaded before - "ser_a2232". If you want to do this, answer M here. - -config WHIPPET_SERIAL - tristate "Hisoft Whippet PCMCIA serial support" - depends on AMIGA_PCMCIA - help - HiSoft has a web page at , but there - is no listing for the Whippet in their Amiga section. - -config APNE - tristate "PCMCIA NE2000 support" - depends on AMIGA_PCMCIA - help - If you have a PCMCIA NE2000 compatible adapter, say Y. Otherwise, - say N. - - To compile this driver as a module, choose M here: the - module will be called apne. - -config SERIAL_CONSOLE - bool "Support for serial port console" - depends on APUS && (AMIGA_BUILTIN_SERIAL=y || GVPIOEXT=y || MULTIFACE_III_TTY=y) - -config HEARTBEAT - bool "Use power LED as a heartbeat" - depends on APUS - help - Use the power-on LED on your machine as a load meter. The exact - behavior is platform-dependent, but normally the flash frequency is - a hyperbolic function of the 5-minute load average. - -config PROC_HARDWARE - bool "/proc/hardware support" - depends on APUS - -source "drivers/zorro/Kconfig" - -config PCI_PERMEDIA - bool "PCI for Permedia2" - depends on !4xx && !8xx && APUS diff --git a/arch/powerpc/platforms/cell/io-workarounds.c b/arch/powerpc/platforms/cell/io-workarounds.c index 7fb92f23f380..9d7c2ef940a8 100644 --- a/arch/powerpc/platforms/cell/io-workarounds.c +++ b/arch/powerpc/platforms/cell/io-workarounds.c @@ -102,7 +102,7 @@ static void spider_io_flush(const volatile void __iomem *addr) vaddr = (unsigned long)PCI_FIX_ADDR(addr); /* Check if it's in allowed range for PIO */ - if (vaddr < PHBS_IO_BASE || vaddr >= IMALLOC_BASE) + if (vaddr < PHB_IO_BASE || vaddr > PHB_IO_END) return; /* Try to find a PTE. If not, clear the paddr, we'll do diff --git a/arch/powerpc/platforms/cell/spu_base.c b/arch/powerpc/platforms/cell/spu_base.c index a7f5a7653c62..e4d0c9f42abd 100644 --- a/arch/powerpc/platforms/cell/spu_base.c +++ b/arch/powerpc/platforms/cell/spu_base.c @@ -183,7 +183,7 @@ static int __spu_trap_data_seg(struct spu *spu, unsigned long ea) spu->slb_replace = 0; spu_restart_dma(spu); - + spu->stats.slb_flt++; return 0; } @@ -332,6 +332,7 @@ spu_irq_class_2(int irq, void *data) if (stat & 0x10) /* SPU mailbox threshold */ spu->wbox_callback(spu); + spu->stats.class2_intr++; return stat ? IRQ_HANDLED : IRQ_NONE; } @@ -462,8 +463,18 @@ void spu_free(struct spu *spu) } EXPORT_SYMBOL_GPL(spu_free); +static int spu_shutdown(struct sys_device *sysdev) +{ + struct spu *spu = container_of(sysdev, struct spu, sysdev); + + spu_free_irqs(spu); + spu_destroy_spu(spu); + return 0; +} + struct sysdev_class spu_sysdev_class = { - set_kset_name("spu") + set_kset_name("spu"), + .shutdown = spu_shutdown, }; int spu_add_sysdev_attr(struct sysdev_attribute *attr) @@ -574,6 +585,9 @@ static int __init create_spu(void *data) spin_unlock_irqrestore(&spu_list_lock, flags); mutex_unlock(&spu_mutex); + spu->stats.utilization_state = SPU_UTIL_IDLE; + spu->stats.tstamp = jiffies; + goto out; out_free_irqs: @@ -586,6 +600,45 @@ out: return ret; } +static const char *spu_state_names[] = { + "user", "system", "iowait", "idle" +}; + +static unsigned long long spu_acct_time(struct spu *spu, + enum spu_utilization_state state) +{ + unsigned long long time = spu->stats.times[state]; + + if (spu->stats.utilization_state == state) + time += jiffies - spu->stats.tstamp; + + return jiffies_to_msecs(time); +} + + +static ssize_t spu_stat_show(struct sys_device *sysdev, char *buf) +{ + struct spu *spu = container_of(sysdev, struct spu, sysdev); + + return sprintf(buf, "%s %llu %llu %llu %llu " + "%llu %llu %llu %llu %llu %llu %llu %llu\n", + spu_state_names[spu->stats.utilization_state], + spu_acct_time(spu, SPU_UTIL_USER), + spu_acct_time(spu, SPU_UTIL_SYSTEM), + spu_acct_time(spu, SPU_UTIL_IOWAIT), + spu_acct_time(spu, SPU_UTIL_IDLE), + spu->stats.vol_ctx_switch, + spu->stats.invol_ctx_switch, + spu->stats.slb_flt, + spu->stats.hash_flt, + spu->stats.min_flt, + spu->stats.maj_flt, + spu->stats.class2_intr, + spu->stats.libassist); +} + +static SYSDEV_ATTR(stat, 0644, spu_stat_show, NULL); + static int __init init_spu_base(void) { int i, ret = 0; @@ -611,6 +664,8 @@ static int __init init_spu_base(void) xmon_register_spus(&spu_full_list); + spu_add_sysdev_attr(&attr_stat); + return 0; out_unregister_sysdev_class: diff --git a/arch/powerpc/platforms/cell/spufs/backing_ops.c b/arch/powerpc/platforms/cell/spufs/backing_ops.c index d32db9ffc6eb..07a0e815abf5 100644 --- a/arch/powerpc/platforms/cell/spufs/backing_ops.c +++ b/arch/powerpc/platforms/cell/spufs/backing_ops.c @@ -320,6 +320,12 @@ static int spu_backing_set_mfc_query(struct spu_context * ctx, u32 mask, /* FIXME: what are the side-effects of this? */ prob->dma_querymask_RW = mask; prob->dma_querytype_RW = mode; + /* In the current implementation, the SPU context is always + * acquired in runnable state when new bits are added to the + * mask (tagwait), so it's sufficient just to mask + * dma_tagstatus_R with the 'mask' parameter here. + */ + ctx->csa.prob.dma_tagstatus_R &= mask; out: spin_unlock(&ctx->csa.register_lock); diff --git a/arch/powerpc/platforms/cell/spufs/context.c b/arch/powerpc/platforms/cell/spufs/context.c index 7c51cb54bca1..6d7bd60f5380 100644 --- a/arch/powerpc/platforms/cell/spufs/context.c +++ b/arch/powerpc/platforms/cell/spufs/context.c @@ -23,10 +23,14 @@ #include #include #include +#include #include #include #include "spufs.h" + +atomic_t nr_spu_contexts = ATOMIC_INIT(0); + struct spu_context *alloc_spu_context(struct spu_gang *gang) { struct spu_context *ctx; @@ -53,10 +57,12 @@ struct spu_context *alloc_spu_context(struct spu_gang *gang) INIT_LIST_HEAD(&ctx->rq); if (gang) spu_gang_add_ctx(gang, ctx); - ctx->rt_priority = current->rt_priority; - ctx->policy = current->policy; - ctx->prio = current->prio; - INIT_DELAYED_WORK(&ctx->sched_work, spu_sched_tick); + ctx->cpus_allowed = current->cpus_allowed; + spu_set_timeslice(ctx); + ctx->stats.execution_state = SPUCTX_UTIL_USER; + ctx->stats.tstamp = jiffies; + + atomic_inc(&nr_spu_contexts); goto out; out_free: kfree(ctx); @@ -76,6 +82,7 @@ void destroy_spu_context(struct kref *kref) if (ctx->gang) spu_gang_remove_ctx(ctx->gang, ctx); BUG_ON(!list_empty(&ctx->rq)); + atomic_dec(&nr_spu_contexts); kfree(ctx); } diff --git a/arch/powerpc/platforms/cell/spufs/fault.c b/arch/powerpc/platforms/cell/spufs/fault.c index 0f75c07e29d8..e064d0c0d80e 100644 --- a/arch/powerpc/platforms/cell/spufs/fault.c +++ b/arch/powerpc/platforms/cell/spufs/fault.c @@ -33,7 +33,8 @@ * function. Currently, there are a few corner cases that we haven't had * to handle fortunately. */ -static int spu_handle_mm_fault(struct mm_struct *mm, unsigned long ea, unsigned long dsisr) +static int spu_handle_mm_fault(struct mm_struct *mm, unsigned long ea, + unsigned long dsisr, unsigned *flt) { struct vm_area_struct *vma; unsigned long is_write; @@ -73,7 +74,8 @@ good_area: goto bad_area; } ret = 0; - switch (handle_mm_fault(mm, vma, ea, is_write)) { + *flt = handle_mm_fault(mm, vma, ea, is_write); + switch (*flt) { case VM_FAULT_MINOR: current->min_flt++; break; @@ -153,6 +155,7 @@ int spufs_handle_class1(struct spu_context *ctx) { u64 ea, dsisr, access; unsigned long flags; + unsigned flt = 0; int ret; /* @@ -178,9 +181,17 @@ int spufs_handle_class1(struct spu_context *ctx) if (!(dsisr & (MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED))) return 0; + spuctx_switch_state(ctx, SPUCTX_UTIL_IOWAIT); + pr_debug("ctx %p: ea %016lx, dsisr %016lx state %d\n", ctx, ea, dsisr, ctx->state); + ctx->stats.hash_flt++; + if (ctx->state == SPU_STATE_RUNNABLE) { + ctx->spu->stats.hash_flt++; + spu_switch_state(ctx->spu, SPU_UTIL_IOWAIT); + } + /* we must not hold the lock when entering spu_handle_mm_fault */ spu_release(ctx); @@ -192,7 +203,7 @@ int spufs_handle_class1(struct spu_context *ctx) /* hashing failed, so try the actual fault handler */ if (ret) - ret = spu_handle_mm_fault(current->mm, ea, dsisr); + ret = spu_handle_mm_fault(current->mm, ea, dsisr, &flt); spu_acquire(ctx); /* @@ -201,11 +212,23 @@ int spufs_handle_class1(struct spu_context *ctx) * In case of unhandled error report the problem to user space. */ if (!ret) { + if (flt == VM_FAULT_MINOR) + ctx->stats.min_flt++; + else + ctx->stats.maj_flt++; + if (ctx->state == SPU_STATE_RUNNABLE) { + if (flt == VM_FAULT_MINOR) + ctx->spu->stats.min_flt++; + else + ctx->spu->stats.maj_flt++; + } + if (ctx->spu) ctx->ops->restart_dma(ctx); } else spufs_handle_dma_error(ctx, ea, SPE_EVENT_SPE_DATA_STORAGE); + spuctx_switch_state(ctx, SPUCTX_UTIL_SYSTEM); return ret; } EXPORT_SYMBOL_GPL(spufs_handle_class1); diff --git a/arch/powerpc/platforms/cell/spufs/file.c b/arch/powerpc/platforms/cell/spufs/file.c index b1e7e2f8a2e9..c2814ea96af2 100644 --- a/arch/powerpc/platforms/cell/spufs/file.c +++ b/arch/powerpc/platforms/cell/spufs/file.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -39,6 +40,7 @@ #define SPUFS_MMAP_4K (PAGE_SIZE == 0x1000) + static int spufs_mem_open(struct inode *inode, struct file *file) { @@ -216,12 +218,12 @@ unsigned long spufs_get_unmapped_area(struct file *file, unsigned long addr, #endif /* CONFIG_SPU_FS_64K_LS */ static const struct file_operations spufs_mem_fops = { - .open = spufs_mem_open, - .release = spufs_mem_release, - .read = spufs_mem_read, - .write = spufs_mem_write, - .llseek = generic_file_llseek, - .mmap = spufs_mem_mmap, + .open = spufs_mem_open, + .release = spufs_mem_release, + .read = spufs_mem_read, + .write = spufs_mem_write, + .llseek = generic_file_llseek, + .mmap = spufs_mem_mmap, #ifdef CONFIG_SPU_FS_64K_LS .get_unmapped_area = spufs_get_unmapped_area, #endif @@ -1497,14 +1499,15 @@ static ssize_t spufs_mfc_write(struct file *file, const char __user *buffer, if (status) ret = status; } - spu_release(ctx); if (ret) - goto out; + goto out_unlock; ctx->tagwait |= 1 << cmd.tag; ret = size; +out_unlock: + spu_release(ctx); out: return ret; } @@ -1515,14 +1518,14 @@ static unsigned int spufs_mfc_poll(struct file *file,poll_table *wait) u32 free_elements, tagstatus; unsigned int mask; + poll_wait(file, &ctx->mfc_wq, wait); + spu_acquire(ctx); ctx->ops->set_mfc_query(ctx, ctx->tagwait, 2); free_elements = ctx->ops->get_mfc_free_elements(ctx); tagstatus = ctx->ops->read_mfc_tagstatus(ctx); spu_release(ctx); - poll_wait(file, &ctx->mfc_wq, wait); - mask = 0; if (free_elements & 0xffff) mask |= POLLOUT | POLLWRNORM; @@ -1797,6 +1800,29 @@ static int spufs_info_open(struct inode *inode, struct file *file) return 0; } +static int spufs_caps_show(struct seq_file *s, void *private) +{ + struct spu_context *ctx = s->private; + + if (!(ctx->flags & SPU_CREATE_NOSCHED)) + seq_puts(s, "sched\n"); + if (!(ctx->flags & SPU_CREATE_ISOLATE)) + seq_puts(s, "step\n"); + return 0; +} + +static int spufs_caps_open(struct inode *inode, struct file *file) +{ + return single_open(file, spufs_caps_show, SPUFS_I(inode)->i_ctx); +} + +static const struct file_operations spufs_caps_fops = { + .open = spufs_caps_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + static ssize_t __spufs_mbox_info_read(struct spu_context *ctx, char __user *buf, size_t len, loff_t *pos) { @@ -2014,7 +2040,105 @@ static const struct file_operations spufs_proxydma_info_fops = { .read = spufs_proxydma_info_read, }; +static int spufs_show_tid(struct seq_file *s, void *private) +{ + struct spu_context *ctx = s->private; + + seq_printf(s, "%d\n", ctx->tid); + return 0; +} + +static int spufs_tid_open(struct inode *inode, struct file *file) +{ + return single_open(file, spufs_show_tid, SPUFS_I(inode)->i_ctx); +} + +static const struct file_operations spufs_tid_fops = { + .open = spufs_tid_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static const char *ctx_state_names[] = { + "user", "system", "iowait", "loaded" +}; + +static unsigned long long spufs_acct_time(struct spu_context *ctx, + enum spuctx_execution_state state) +{ + unsigned long time = ctx->stats.times[state]; + + if (ctx->stats.execution_state == state) + time += jiffies - ctx->stats.tstamp; + + return jiffies_to_msecs(time); +} + +static unsigned long long spufs_slb_flts(struct spu_context *ctx) +{ + unsigned long long slb_flts = ctx->stats.slb_flt; + + if (ctx->state == SPU_STATE_RUNNABLE) { + slb_flts += (ctx->spu->stats.slb_flt - + ctx->stats.slb_flt_base); + } + + return slb_flts; +} + +static unsigned long long spufs_class2_intrs(struct spu_context *ctx) +{ + unsigned long long class2_intrs = ctx->stats.class2_intr; + + if (ctx->state == SPU_STATE_RUNNABLE) { + class2_intrs += (ctx->spu->stats.class2_intr - + ctx->stats.class2_intr_base); + } + + return class2_intrs; +} + + +static int spufs_show_stat(struct seq_file *s, void *private) +{ + struct spu_context *ctx = s->private; + + spu_acquire(ctx); + seq_printf(s, "%s %llu %llu %llu %llu " + "%llu %llu %llu %llu %llu %llu %llu %llu\n", + ctx_state_names[ctx->stats.execution_state], + spufs_acct_time(ctx, SPUCTX_UTIL_USER), + spufs_acct_time(ctx, SPUCTX_UTIL_SYSTEM), + spufs_acct_time(ctx, SPUCTX_UTIL_IOWAIT), + spufs_acct_time(ctx, SPUCTX_UTIL_LOADED), + ctx->stats.vol_ctx_switch, + ctx->stats.invol_ctx_switch, + spufs_slb_flts(ctx), + ctx->stats.hash_flt, + ctx->stats.min_flt, + ctx->stats.maj_flt, + spufs_class2_intrs(ctx), + ctx->stats.libassist); + spu_release(ctx); + return 0; +} + +static int spufs_stat_open(struct inode *inode, struct file *file) +{ + return single_open(file, spufs_show_stat, SPUFS_I(inode)->i_ctx); +} + +static const struct file_operations spufs_stat_fops = { + .open = spufs_stat_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + + struct tree_descr spufs_dir_contents[] = { + { "capabilities", &spufs_caps_fops, 0444, }, { "mem", &spufs_mem_fops, 0666, }, { "regs", &spufs_regs_fops, 0666, }, { "mbox", &spufs_mbox_fops, 0444, }, @@ -2046,10 +2170,13 @@ struct tree_descr spufs_dir_contents[] = { { "wbox_info", &spufs_wbox_info_fops, 0444, }, { "dma_info", &spufs_dma_info_fops, 0444, }, { "proxydma_info", &spufs_proxydma_info_fops, 0444, }, + { "tid", &spufs_tid_fops, 0444, }, + { "stat", &spufs_stat_fops, 0444, }, {}, }; struct tree_descr spufs_dir_nosched_contents[] = { + { "capabilities", &spufs_caps_fops, 0444, }, { "mem", &spufs_mem_fops, 0666, }, { "mbox", &spufs_mbox_fops, 0444, }, { "ibox", &spufs_ibox_fops, 0444, }, @@ -2068,6 +2195,8 @@ struct tree_descr spufs_dir_nosched_contents[] = { { "psmap", &spufs_psmap_fops, 0666, }, { "phys-id", &spufs_id_ops, 0666, }, { "object-id", &spufs_object_id_ops, 0666, }, + { "tid", &spufs_tid_fops, 0444, }, + { "stat", &spufs_stat_fops, 0444, }, {}, }; diff --git a/arch/powerpc/platforms/cell/spufs/inode.c b/arch/powerpc/platforms/cell/spufs/inode.c index 9807206e0219..f37460e5bfd2 100644 --- a/arch/powerpc/platforms/cell/spufs/inode.c +++ b/arch/powerpc/platforms/cell/spufs/inode.c @@ -232,10 +232,6 @@ static int spufs_dir_close(struct inode *inode, struct file *file) return dcache_dir_close(inode, file); } -const struct inode_operations spufs_dir_inode_operations = { - .lookup = simple_lookup, -}; - const struct file_operations spufs_context_fops = { .open = dcache_dir_open, .release = spufs_dir_close, @@ -269,7 +265,7 @@ spufs_mkdir(struct inode *dir, struct dentry *dentry, unsigned int flags, goto out_iput; ctx->flags = flags; - inode->i_op = &spufs_dir_inode_operations; + inode->i_op = &simple_dir_inode_operations; inode->i_fop = &simple_dir_operations; if (flags & SPU_CREATE_NOSCHED) ret = spufs_fill_dir(dentry, spufs_dir_nosched_contents, @@ -386,7 +382,7 @@ spufs_mkgang(struct inode *dir, struct dentry *dentry, int mode) if (!gang) goto out_iput; - inode->i_op = &spufs_dir_inode_operations; + inode->i_op = &simple_dir_inode_operations; inode->i_fop = &simple_dir_operations; d_instantiate(dentry, inode); @@ -593,7 +589,7 @@ spufs_create_root(struct super_block *sb, void *data) if (!inode) goto out; - inode->i_op = &spufs_dir_inode_operations; + inode->i_op = &simple_dir_inode_operations; inode->i_fop = &simple_dir_operations; SPUFS_I(inode)->i_ctx = NULL; diff --git a/arch/powerpc/platforms/cell/spufs/run.c b/arch/powerpc/platforms/cell/spufs/run.c index 57626600b1a4..58ae13b7de84 100644 --- a/arch/powerpc/platforms/cell/spufs/run.c +++ b/arch/powerpc/platforms/cell/spufs/run.c @@ -29,7 +29,8 @@ static inline int spu_stopped(struct spu_context *ctx, u32 * stat) spu = ctx->spu; pte_fault = spu->dsisr & (MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED); - return (!(*stat & 0x1) || pte_fault || spu->class_0_pending) ? 1 : 0; + return (!(*stat & SPU_STATUS_RUNNING) || pte_fault || spu->class_0_pending) ? + 1 : 0; } static int spu_setup_isolated(struct spu_context *ctx) @@ -142,8 +143,11 @@ static int spu_run_init(struct spu_context *ctx, u32 * npc) runcntl = SPU_RUNCNTL_RUNNABLE; ctx->ops->runcntl_write(ctx, runcntl); } else { - spu_start_tick(ctx); + unsigned long mode = SPU_PRIVCNTL_MODE_NORMAL; ctx->ops->npc_write(ctx, *npc); + if (test_thread_flag(TIF_SINGLESTEP)) + mode = SPU_PRIVCNTL_MODE_SINGLE_STEP; + out_be64(&ctx->spu->priv2->spu_privcntl_RW, mode); ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_RUNNABLE); } @@ -155,7 +159,6 @@ static int spu_run_fini(struct spu_context *ctx, u32 * npc, { int ret = 0; - spu_stop_tick(ctx); *status = ctx->ops->status_read(ctx); *npc = ctx->ops->npc_read(ctx); spu_release(ctx); @@ -298,9 +301,22 @@ long spufs_run_spu(struct file *file, struct spu_context *ctx, ctx->ops->master_start(ctx); ctx->event_return = 0; - ret = spu_acquire_runnable(ctx, 0); - if (ret) - return ret; + spu_acquire(ctx); + if (ctx->state == SPU_STATE_SAVED) { + __spu_update_sched_info(ctx); + + ret = spu_activate(ctx, 0); + if (ret) { + spu_release(ctx); + goto out; + } + } else { + /* + * We have to update the scheduling priority under active_mutex + * to protect against find_victim(). + */ + spu_update_sched_info(ctx); + } ret = spu_run_init(ctx, npc); if (ret) { @@ -325,16 +341,20 @@ long spufs_run_spu(struct file *file, struct spu_context *ctx, if (unlikely(ctx->state != SPU_STATE_RUNNABLE)) { ret = spu_reacquire_runnable(ctx, npc, &status); - if (ret) { - spu_stop_tick(ctx); + if (ret) goto out2; - } continue; } ret = spu_process_events(ctx); } while (!ret && !(status & (SPU_STATUS_STOPPED_BY_STOP | - SPU_STATUS_STOPPED_BY_HALT))); + SPU_STATUS_STOPPED_BY_HALT | + SPU_STATUS_SINGLE_STEP))); + + if ((status & SPU_STATUS_STOPPED_BY_STOP) && + (((status >> SPU_STOP_STATUS_SHIFT) & 0x3f00) == 0x2100) && + (ctx->state == SPU_STATE_RUNNABLE)) + ctx->stats.libassist++; ctx->ops->master_stop(ctx); ret = spu_run_fini(ctx, npc, &status); @@ -344,10 +364,15 @@ out2: if ((ret == 0) || ((ret == -ERESTARTSYS) && ((status & SPU_STATUS_STOPPED_BY_HALT) || + (status & SPU_STATUS_SINGLE_STEP) || ((status & SPU_STATUS_STOPPED_BY_STOP) && (status >> SPU_STOP_STATUS_SHIFT != 0x2104))))) ret = status; + /* Note: we don't need to force_sig SIGTRAP on single-step + * since we have TIF_SINGLESTEP set, thus the kernel will do + * it upon return from the syscall anyawy + */ if ((status & SPU_STATUS_STOPPED_BY_STOP) && (status >> SPU_STOP_STATUS_SHIFT) == 0x3fff) { force_sig(SIGTRAP, current); diff --git a/arch/powerpc/platforms/cell/spufs/sched.c b/arch/powerpc/platforms/cell/spufs/sched.c index 3b831e07f1ed..e5b4dd1db286 100644 --- a/arch/powerpc/platforms/cell/spufs/sched.c +++ b/arch/powerpc/platforms/cell/spufs/sched.c @@ -35,6 +35,10 @@ #include #include #include +#include +#include +#include +#include #include #include @@ -43,54 +47,126 @@ #include #include "spufs.h" -#define SPU_TIMESLICE (HZ) - struct spu_prio_array { DECLARE_BITMAP(bitmap, MAX_PRIO); struct list_head runq[MAX_PRIO]; spinlock_t runq_lock; struct list_head active_list[MAX_NUMNODES]; struct mutex active_mutex[MAX_NUMNODES]; + int nr_active[MAX_NUMNODES]; + int nr_waiting; }; +static unsigned long spu_avenrun[3]; static struct spu_prio_array *spu_prio; -static struct workqueue_struct *spu_sched_wq; +static struct task_struct *spusched_task; +static struct timer_list spusched_timer; -static inline int node_allowed(int node) +/* + * Priority of a normal, non-rt, non-niced'd process (aka nice level 0). + */ +#define NORMAL_PRIO 120 + +/* + * Frequency of the spu scheduler tick. By default we do one SPU scheduler + * tick for every 10 CPU scheduler ticks. + */ +#define SPUSCHED_TICK (10) + +/* + * These are the 'tuning knobs' of the scheduler: + * + * Minimum timeslice is 5 msecs (or 1 spu scheduler tick, whichever is + * larger), default timeslice is 100 msecs, maximum timeslice is 800 msecs. + */ +#define MIN_SPU_TIMESLICE max(5 * HZ / (1000 * SPUSCHED_TICK), 1) +#define DEF_SPU_TIMESLICE (100 * HZ / (1000 * SPUSCHED_TICK)) + +#define MAX_USER_PRIO (MAX_PRIO - MAX_RT_PRIO) +#define SCALE_PRIO(x, prio) \ + max(x * (MAX_PRIO - prio) / (MAX_USER_PRIO / 2), MIN_SPU_TIMESLICE) + +/* + * scale user-nice values [ -20 ... 0 ... 19 ] to time slice values: + * [800ms ... 100ms ... 5ms] + * + * The higher a thread's priority, the bigger timeslices + * it gets during one round of execution. But even the lowest + * priority thread gets MIN_TIMESLICE worth of execution time. + */ +void spu_set_timeslice(struct spu_context *ctx) { - cpumask_t mask; - - if (!nr_cpus_node(node)) - return 0; - mask = node_to_cpumask(node); - if (!cpus_intersects(mask, current->cpus_allowed)) - return 0; - return 1; + if (ctx->prio < NORMAL_PRIO) + ctx->time_slice = SCALE_PRIO(DEF_SPU_TIMESLICE * 4, ctx->prio); + else + ctx->time_slice = SCALE_PRIO(DEF_SPU_TIMESLICE, ctx->prio); } -void spu_start_tick(struct spu_context *ctx) +/* + * Update scheduling information from the owning thread. + */ +void __spu_update_sched_info(struct spu_context *ctx) { - if (ctx->policy == SCHED_RR) { - /* - * Make sure the exiting bit is cleared. - */ - clear_bit(SPU_SCHED_EXITING, &ctx->sched_flags); - mb(); - queue_delayed_work(spu_sched_wq, &ctx->sched_work, SPU_TIMESLICE); - } + /* + * 32-Bit assignment are atomic on powerpc, and we don't care about + * memory ordering here because retriving the controlling thread is + * per defintion racy. + */ + ctx->tid = current->pid; + + /* + * We do our own priority calculations, so we normally want + * ->static_prio to start with. Unfortunately thies field + * contains junk for threads with a realtime scheduling + * policy so we have to look at ->prio in this case. + */ + if (rt_prio(current->prio)) + ctx->prio = current->prio; + else + ctx->prio = current->static_prio; + ctx->policy = current->policy; + + /* + * A lot of places that don't hold active_mutex poke into + * cpus_allowed, including grab_runnable_context which + * already holds the runq_lock. So abuse runq_lock + * to protect this field aswell. + */ + spin_lock(&spu_prio->runq_lock); + ctx->cpus_allowed = current->cpus_allowed; + spin_unlock(&spu_prio->runq_lock); } -void spu_stop_tick(struct spu_context *ctx) +void spu_update_sched_info(struct spu_context *ctx) { - if (ctx->policy == SCHED_RR) { - /* - * While the work can be rearming normally setting this flag - * makes sure it does not rearm itself anymore. - */ - set_bit(SPU_SCHED_EXITING, &ctx->sched_flags); - mb(); - cancel_delayed_work(&ctx->sched_work); + int node = ctx->spu->node; + + mutex_lock(&spu_prio->active_mutex[node]); + __spu_update_sched_info(ctx); + mutex_unlock(&spu_prio->active_mutex[node]); +} + +static int __node_allowed(struct spu_context *ctx, int node) +{ + if (nr_cpus_node(node)) { + cpumask_t mask = node_to_cpumask(node); + + if (cpus_intersects(mask, ctx->cpus_allowed)) + return 1; } + + return 0; +} + +static int node_allowed(struct spu_context *ctx, int node) +{ + int rval; + + spin_lock(&spu_prio->runq_lock); + rval = __node_allowed(ctx, node); + spin_unlock(&spu_prio->runq_lock); + + return rval; } /** @@ -99,9 +175,18 @@ void spu_stop_tick(struct spu_context *ctx) */ static void spu_add_to_active_list(struct spu *spu) { - mutex_lock(&spu_prio->active_mutex[spu->node]); - list_add_tail(&spu->list, &spu_prio->active_list[spu->node]); - mutex_unlock(&spu_prio->active_mutex[spu->node]); + int node = spu->node; + + mutex_lock(&spu_prio->active_mutex[node]); + spu_prio->nr_active[node]++; + list_add_tail(&spu->list, &spu_prio->active_list[node]); + mutex_unlock(&spu_prio->active_mutex[node]); +} + +static void __spu_remove_from_active_list(struct spu *spu) +{ + list_del_init(&spu->list); + spu_prio->nr_active[spu->node]--; } /** @@ -113,7 +198,7 @@ static void spu_remove_from_active_list(struct spu *spu) int node = spu->node; mutex_lock(&spu_prio->active_mutex[node]); - list_del_init(&spu->list); + __spu_remove_from_active_list(spu); mutex_unlock(&spu_prio->active_mutex[node]); } @@ -144,6 +229,10 @@ static void spu_bind_context(struct spu *spu, struct spu_context *ctx) { pr_debug("%s: pid=%d SPU=%d NODE=%d\n", __FUNCTION__, current->pid, spu->number, spu->node); + + ctx->stats.slb_flt_base = spu->stats.slb_flt; + ctx->stats.class2_intr_base = spu->stats.class2_intr; + spu->ctx = ctx; spu->flags = 0; ctx->spu = spu; @@ -161,8 +250,8 @@ static void spu_bind_context(struct spu *spu, struct spu_context *ctx) spu->timestamp = jiffies; spu_cpu_affinity_set(spu, raw_smp_processor_id()); spu_switch_notify(spu, ctx); - spu_add_to_active_list(spu); ctx->state = SPU_STATE_RUNNABLE; + spu_switch_state(spu, SPU_UTIL_SYSTEM); } /** @@ -175,7 +264,8 @@ static void spu_unbind_context(struct spu *spu, struct spu_context *ctx) pr_debug("%s: unbind pid=%d SPU=%d NODE=%d\n", __FUNCTION__, spu->pid, spu->number, spu->node); - spu_remove_from_active_list(spu); + spu_switch_state(spu, SPU_UTIL_IDLE); + spu_switch_notify(spu, NULL); spu_unmap_mappings(ctx); spu_save(&ctx->csa, spu); @@ -192,6 +282,11 @@ static void spu_unbind_context(struct spu *spu, struct spu_context *ctx) ctx->spu = NULL; spu->flags = 0; spu->ctx = NULL; + + ctx->stats.slb_flt += + (spu->stats.slb_flt - ctx->stats.slb_flt_base); + ctx->stats.class2_intr += + (spu->stats.class2_intr - ctx->stats.class2_intr_base); } /** @@ -200,20 +295,39 @@ static void spu_unbind_context(struct spu *spu, struct spu_context *ctx) */ static void __spu_add_to_rq(struct spu_context *ctx) { - int prio = ctx->prio; - - list_add_tail(&ctx->rq, &spu_prio->runq[prio]); - set_bit(prio, spu_prio->bitmap); + /* + * Unfortunately this code path can be called from multiple threads + * on behalf of a single context due to the way the problem state + * mmap support works. + * + * Fortunately we need to wake up all these threads at the same time + * and can simply skip the runqueue addition for every but the first + * thread getting into this codepath. + * + * It's still quite hacky, and long-term we should proxy all other + * threads through the owner thread so that spu_run is in control + * of all the scheduling activity for a given context. + */ + if (list_empty(&ctx->rq)) { + list_add_tail(&ctx->rq, &spu_prio->runq[ctx->prio]); + set_bit(ctx->prio, spu_prio->bitmap); + if (!spu_prio->nr_waiting++) + __mod_timer(&spusched_timer, jiffies + SPUSCHED_TICK); + } } static void __spu_del_from_rq(struct spu_context *ctx) { int prio = ctx->prio; - if (!list_empty(&ctx->rq)) + if (!list_empty(&ctx->rq)) { + if (!--spu_prio->nr_waiting) + del_timer(&spusched_timer); list_del_init(&ctx->rq); - if (list_empty(&spu_prio->runq[prio])) - clear_bit(prio, spu_prio->bitmap); + + if (list_empty(&spu_prio->runq[prio])) + clear_bit(prio, spu_prio->bitmap); + } } static void spu_prio_wait(struct spu_context *ctx) @@ -244,7 +358,7 @@ static struct spu *spu_get_idle(struct spu_context *ctx) for (n = 0; n < MAX_NUMNODES; n++, node++) { node = (node < MAX_NUMNODES) ? node : 0; - if (!node_allowed(node)) + if (!node_allowed(ctx, node)) continue; spu = spu_alloc_node(node); if (spu) @@ -276,15 +390,15 @@ static struct spu *find_victim(struct spu_context *ctx) node = cpu_to_node(raw_smp_processor_id()); for (n = 0; n < MAX_NUMNODES; n++, node++) { node = (node < MAX_NUMNODES) ? node : 0; - if (!node_allowed(node)) + if (!node_allowed(ctx, node)) continue; mutex_lock(&spu_prio->active_mutex[node]); list_for_each_entry(spu, &spu_prio->active_list[node], list) { struct spu_context *tmp = spu->ctx; - if (tmp->rt_priority < ctx->rt_priority && - (!victim || tmp->rt_priority < victim->rt_priority)) + if (tmp->prio > ctx->prio && + (!victim || tmp->prio > victim->prio)) victim = spu->ctx; } mutex_unlock(&spu_prio->active_mutex[node]); @@ -312,7 +426,10 @@ static struct spu *find_victim(struct spu_context *ctx) victim = NULL; goto restart; } + spu_remove_from_active_list(spu); spu_unbind_context(spu, victim); + victim->stats.invol_ctx_switch++; + spu->stats.invol_ctx_switch++; mutex_unlock(&victim->state_mutex); /* * We need to break out of the wait loop in spu_run @@ -338,22 +455,30 @@ static struct spu *find_victim(struct spu_context *ctx) */ int spu_activate(struct spu_context *ctx, unsigned long flags) { - - if (ctx->spu) - return 0; + spuctx_switch_state(ctx, SPUCTX_UTIL_SYSTEM); do { struct spu *spu; + /* + * If there are multiple threads waiting for a single context + * only one actually binds the context while the others will + * only be able to acquire the state_mutex once the context + * already is in runnable state. + */ + if (ctx->spu) + return 0; + spu = spu_get_idle(ctx); /* * If this is a realtime thread we try to get it running by * preempting a lower priority thread. */ - if (!spu && ctx->rt_priority) + if (!spu && rt_prio(ctx->prio)) spu = find_victim(ctx); if (spu) { spu_bind_context(spu, ctx); + spu_add_to_active_list(spu); return 0; } @@ -369,23 +494,28 @@ int spu_activate(struct spu_context *ctx, unsigned long flags) * Remove the highest priority context on the runqueue and return it * to the caller. Returns %NULL if no runnable context was found. */ -static struct spu_context *grab_runnable_context(int prio) +static struct spu_context *grab_runnable_context(int prio, int node) { - struct spu_context *ctx = NULL; + struct spu_context *ctx; int best; spin_lock(&spu_prio->runq_lock); best = sched_find_first_bit(spu_prio->bitmap); - if (best < prio) { + while (best < prio) { struct list_head *rq = &spu_prio->runq[best]; - BUG_ON(list_empty(rq)); - - ctx = list_entry(rq->next, struct spu_context, rq); - __spu_del_from_rq(ctx); + list_for_each_entry(ctx, rq, rq) { + /* XXX(hch): check for affinity here aswell */ + if (__node_allowed(ctx, node)) { + __spu_del_from_rq(ctx); + goto found; + } + } + best++; } + ctx = NULL; + found: spin_unlock(&spu_prio->runq_lock); - return ctx; } @@ -395,9 +525,12 @@ static int __spu_deactivate(struct spu_context *ctx, int force, int max_prio) struct spu_context *new = NULL; if (spu) { - new = grab_runnable_context(max_prio); + new = grab_runnable_context(max_prio, spu->node); if (new || force) { + spu_remove_from_active_list(spu); spu_unbind_context(spu, ctx); + ctx->stats.vol_ctx_switch++; + spu->stats.vol_ctx_switch++; spu_free(spu); if (new) wake_up(&new->stop_wq); @@ -417,7 +550,17 @@ static int __spu_deactivate(struct spu_context *ctx, int force, int max_prio) */ void spu_deactivate(struct spu_context *ctx) { + /* + * We must never reach this for a nosched context, + * but handle the case gracefull instead of panicing. + */ + if (ctx->flags & SPU_CREATE_NOSCHED) { + WARN_ON(1); + return; + } + __spu_deactivate(ctx, 1, MAX_PRIO); + spuctx_switch_state(ctx, SPUCTX_UTIL_USER); } /** @@ -432,56 +575,178 @@ void spu_yield(struct spu_context *ctx) { if (!(ctx->flags & SPU_CREATE_NOSCHED)) { mutex_lock(&ctx->state_mutex); - __spu_deactivate(ctx, 0, MAX_PRIO); + if (__spu_deactivate(ctx, 0, MAX_PRIO)) + spuctx_switch_state(ctx, SPUCTX_UTIL_USER); + else { + spuctx_switch_state(ctx, SPUCTX_UTIL_LOADED); + spu_switch_state(ctx->spu, SPU_UTIL_USER); + } mutex_unlock(&ctx->state_mutex); } } -void spu_sched_tick(struct work_struct *work) +static void spusched_tick(struct spu_context *ctx) { - struct spu_context *ctx = - container_of(work, struct spu_context, sched_work.work); - int preempted; - - /* - * If this context is being stopped avoid rescheduling from the - * scheduler tick because we would block on the state_mutex. - * The caller will yield the spu later on anyway. - */ - if (test_bit(SPU_SCHED_EXITING, &ctx->sched_flags)) + if (ctx->flags & SPU_CREATE_NOSCHED) + return; + if (ctx->policy == SCHED_FIFO) return; - mutex_lock(&ctx->state_mutex); - preempted = __spu_deactivate(ctx, 0, ctx->prio + 1); - mutex_unlock(&ctx->state_mutex); + if (--ctx->time_slice) + return; - if (preempted) { - /* - * We need to break out of the wait loop in spu_run manually - * to ensure this context gets put on the runqueue again - * ASAP. - */ - wake_up(&ctx->stop_wq); + /* + * Unfortunately active_mutex ranks outside of state_mutex, so + * we have to trylock here. If we fail give the context another + * tick and try again. + */ + if (mutex_trylock(&ctx->state_mutex)) { + struct spu *spu = ctx->spu; + struct spu_context *new; + + new = grab_runnable_context(ctx->prio + 1, spu->node); + if (new) { + + __spu_remove_from_active_list(spu); + spu_unbind_context(spu, ctx); + ctx->stats.invol_ctx_switch++; + spu->stats.invol_ctx_switch++; + spu_free(spu); + wake_up(&new->stop_wq); + /* + * We need to break out of the wait loop in + * spu_run manually to ensure this context + * gets put on the runqueue again ASAP. + */ + wake_up(&ctx->stop_wq); + } + spu_set_timeslice(ctx); + mutex_unlock(&ctx->state_mutex); } else { - spu_start_tick(ctx); + ctx->time_slice++; } } +/** + * count_active_contexts - count nr of active tasks + * + * Return the number of tasks currently running or waiting to run. + * + * Note that we don't take runq_lock / active_mutex here. Reading + * a single 32bit value is atomic on powerpc, and we don't care + * about memory ordering issues here. + */ +static unsigned long count_active_contexts(void) +{ + int nr_active = 0, node; + + for (node = 0; node < MAX_NUMNODES; node++) + nr_active += spu_prio->nr_active[node]; + nr_active += spu_prio->nr_waiting; + + return nr_active; +} + +/** + * spu_calc_load - given tick count, update the avenrun load estimates. + * @tick: tick count + * + * No locking against reading these values from userspace, as for + * the CPU loadavg code. + */ +static void spu_calc_load(unsigned long ticks) +{ + unsigned long active_tasks; /* fixed-point */ + static int count = LOAD_FREQ; + + count -= ticks; + + if (unlikely(count < 0)) { + active_tasks = count_active_contexts() * FIXED_1; + do { + CALC_LOAD(spu_avenrun[0], EXP_1, active_tasks); + CALC_LOAD(spu_avenrun[1], EXP_5, active_tasks); + CALC_LOAD(spu_avenrun[2], EXP_15, active_tasks); + count += LOAD_FREQ; + } while (count < 0); + } +} + +static void spusched_wake(unsigned long data) +{ + mod_timer(&spusched_timer, jiffies + SPUSCHED_TICK); + wake_up_process(spusched_task); + spu_calc_load(SPUSCHED_TICK); +} + +static int spusched_thread(void *unused) +{ + struct spu *spu, *next; + int node; + + while (!kthread_should_stop()) { + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + for (node = 0; node < MAX_NUMNODES; node++) { + mutex_lock(&spu_prio->active_mutex[node]); + list_for_each_entry_safe(spu, next, + &spu_prio->active_list[node], + list) + spusched_tick(spu->ctx); + mutex_unlock(&spu_prio->active_mutex[node]); + } + } + + return 0; +} + +#define LOAD_INT(x) ((x) >> FSHIFT) +#define LOAD_FRAC(x) LOAD_INT(((x) & (FIXED_1-1)) * 100) + +static int show_spu_loadavg(struct seq_file *s, void *private) +{ + int a, b, c; + + a = spu_avenrun[0] + (FIXED_1/200); + b = spu_avenrun[1] + (FIXED_1/200); + c = spu_avenrun[2] + (FIXED_1/200); + + /* + * Note that last_pid doesn't really make much sense for the + * SPU loadavg (it even seems very odd on the CPU side..), + * but we include it here to have a 100% compatible interface. + */ + seq_printf(s, "%d.%02d %d.%02d %d.%02d %ld/%d %d\n", + LOAD_INT(a), LOAD_FRAC(a), + LOAD_INT(b), LOAD_FRAC(b), + LOAD_INT(c), LOAD_FRAC(c), + count_active_contexts(), + atomic_read(&nr_spu_contexts), + current->nsproxy->pid_ns->last_pid); + return 0; +} + +static int spu_loadavg_open(struct inode *inode, struct file *file) +{ + return single_open(file, show_spu_loadavg, NULL); +} + +static const struct file_operations spu_loadavg_fops = { + .open = spu_loadavg_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + int __init spu_sched_init(void) { - int i; - - spu_sched_wq = create_singlethread_workqueue("spusched"); - if (!spu_sched_wq) - return 1; + struct proc_dir_entry *entry; + int err = -ENOMEM, i; spu_prio = kzalloc(sizeof(struct spu_prio_array), GFP_KERNEL); - if (!spu_prio) { - printk(KERN_WARNING "%s: Unable to allocate priority queue.\n", - __FUNCTION__); - destroy_workqueue(spu_sched_wq); - return 1; - } + if (!spu_prio) + goto out; + for (i = 0; i < MAX_PRIO; i++) { INIT_LIST_HEAD(&spu_prio->runq[i]); __clear_bit(i, spu_prio->bitmap); @@ -492,7 +757,30 @@ int __init spu_sched_init(void) INIT_LIST_HEAD(&spu_prio->active_list[i]); } spin_lock_init(&spu_prio->runq_lock); + + setup_timer(&spusched_timer, spusched_wake, 0); + + spusched_task = kthread_run(spusched_thread, NULL, "spusched"); + if (IS_ERR(spusched_task)) { + err = PTR_ERR(spusched_task); + goto out_free_spu_prio; + } + + entry = create_proc_entry("spu_loadavg", 0, NULL); + if (!entry) + goto out_stop_kthread; + entry->proc_fops = &spu_loadavg_fops; + + pr_debug("spusched: tick: %d, min ticks: %d, default ticks: %d\n", + SPUSCHED_TICK, MIN_SPU_TIMESLICE, DEF_SPU_TIMESLICE); return 0; + + out_stop_kthread: + kthread_stop(spusched_task); + out_free_spu_prio: + kfree(spu_prio); + out: + return err; } void __exit spu_sched_exit(void) @@ -500,6 +788,11 @@ void __exit spu_sched_exit(void) struct spu *spu, *tmp; int node; + remove_proc_entry("spu_loadavg", NULL); + + del_timer_sync(&spusched_timer); + kthread_stop(spusched_task); + for (node = 0; node < MAX_NUMNODES; node++) { mutex_lock(&spu_prio->active_mutex[node]); list_for_each_entry_safe(spu, tmp, &spu_prio->active_list[node], @@ -510,5 +803,4 @@ void __exit spu_sched_exit(void) mutex_unlock(&spu_prio->active_mutex[node]); } kfree(spu_prio); - destroy_workqueue(spu_sched_wq); } diff --git a/arch/powerpc/platforms/cell/spufs/spu_restore.c b/arch/powerpc/platforms/cell/spufs/spu_restore.c index 0bf723dcd677..4e19ed7a0756 100644 --- a/arch/powerpc/platforms/cell/spufs/spu_restore.c +++ b/arch/powerpc/platforms/cell/spufs/spu_restore.c @@ -296,7 +296,7 @@ static inline void restore_complete(void) * This code deviates from the documented sequence in the * following aspects: * - * 1. The EA for LSCSA is passed from PPE in the + * 1. The EA for LSCSA is passed from PPE in the * signal notification channels. * 2. The register spill area is pulled by SPU * into LS, rather than pushed by PPE. diff --git a/arch/powerpc/platforms/cell/spufs/spu_save.c b/arch/powerpc/platforms/cell/spufs/spu_save.c index 196033b8a579..ae95cc1701e9 100644 --- a/arch/powerpc/platforms/cell/spufs/spu_save.c +++ b/arch/powerpc/platforms/cell/spufs/spu_save.c @@ -44,7 +44,7 @@ static inline void save_event_mask(void) * Read the SPU_RdEventMsk channel and save to the LSCSA. */ offset = LSCSA_QW_OFFSET(event_mask); - regs_spill[offset].slot[0] = spu_readch(SPU_RdEventStatMask); + regs_spill[offset].slot[0] = spu_readch(SPU_RdEventMask); } static inline void save_tag_mask(void) diff --git a/arch/powerpc/platforms/cell/spufs/spufs.h b/arch/powerpc/platforms/cell/spufs/spufs.h index 47617e8014a5..08b3530288ac 100644 --- a/arch/powerpc/platforms/cell/spufs/spufs.h +++ b/arch/powerpc/platforms/cell/spufs/spufs.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -39,9 +40,17 @@ enum { struct spu_context_ops; struct spu_gang; -/* ctx->sched_flags */ -enum { - SPU_SCHED_EXITING = 0, +/* + * This is the state for spu utilization reporting to userspace. + * Because this state is visible to userspace it must never change and needs + * to be kept strictly separate from any internal state kept by the kernel. + */ +enum spuctx_execution_state { + SPUCTX_UTIL_USER = 0, + SPUCTX_UTIL_SYSTEM, + SPUCTX_UTIL_IOWAIT, + SPUCTX_UTIL_LOADED, + SPUCTX_UTIL_MAX }; struct spu_context { @@ -81,13 +90,34 @@ struct spu_context { struct list_head gang_list; struct spu_gang *gang; + /* owner thread */ + pid_t tid; + /* scheduler fields */ - struct list_head rq; - struct delayed_work sched_work; + struct list_head rq; + unsigned int time_slice; unsigned long sched_flags; - unsigned long rt_priority; + cpumask_t cpus_allowed; int policy; int prio; + + /* statistics */ + struct { + /* updates protected by ctx->state_mutex */ + enum spuctx_execution_state execution_state; + unsigned long tstamp; /* time of last ctx switch */ + unsigned long times[SPUCTX_UTIL_MAX]; + unsigned long long vol_ctx_switch; + unsigned long long invol_ctx_switch; + unsigned long long min_flt; + unsigned long long maj_flt; + unsigned long long hash_flt; + unsigned long long slb_flt; + unsigned long long slb_flt_base; /* # at last ctx switch */ + unsigned long long class2_intr; + unsigned long long class2_intr_base; /* # at last ctx switch */ + unsigned long long libassist; + } stats; }; struct spu_gang { @@ -177,6 +207,7 @@ void spu_gang_add_ctx(struct spu_gang *gang, struct spu_context *ctx); int spufs_handle_class1(struct spu_context *ctx); /* context management */ +extern atomic_t nr_spu_contexts; static inline void spu_acquire(struct spu_context *ctx) { mutex_lock(&ctx->state_mutex); @@ -200,9 +231,9 @@ void spu_acquire_saved(struct spu_context *ctx); int spu_activate(struct spu_context *ctx, unsigned long flags); void spu_deactivate(struct spu_context *ctx); void spu_yield(struct spu_context *ctx); -void spu_start_tick(struct spu_context *ctx); -void spu_stop_tick(struct spu_context *ctx); -void spu_sched_tick(struct work_struct *work); +void spu_set_timeslice(struct spu_context *ctx); +void spu_update_sched_info(struct spu_context *ctx); +void __spu_update_sched_info(struct spu_context *ctx); int __init spu_sched_init(void); void __exit spu_sched_exit(void); @@ -210,7 +241,7 @@ extern char *isolated_loader; /* * spufs_wait - * Same as wait_event_interruptible(), except that here + * Same as wait_event_interruptible(), except that here * we need to call spu_release(ctx) before sleeping, and * then spu_acquire(ctx) when awoken. */ @@ -256,4 +287,37 @@ struct spufs_coredump_reader { extern struct spufs_coredump_reader spufs_coredump_read[]; extern int spufs_coredump_num_notes; +/* + * This function is a little bit too large for an inline, but + * as fault.c is built into the kernel we can't move it out of + * line. + */ +static inline void spuctx_switch_state(struct spu_context *ctx, + enum spuctx_execution_state new_state) +{ + WARN_ON(!mutex_is_locked(&ctx->state_mutex)); + + if (ctx->stats.execution_state != new_state) { + unsigned long curtime = jiffies; + + ctx->stats.times[ctx->stats.execution_state] += + curtime - ctx->stats.tstamp; + ctx->stats.tstamp = curtime; + ctx->stats.execution_state = new_state; + } +} + +static inline void spu_switch_state(struct spu *spu, + enum spuctx_execution_state new_state) +{ + if (spu->stats.utilization_state != new_state) { + unsigned long curtime = jiffies; + + spu->stats.times[spu->stats.utilization_state] += + curtime - spu->stats.tstamp; + spu->stats.tstamp = curtime; + spu->stats.utilization_state = new_state; + } +} + #endif diff --git a/arch/powerpc/platforms/cell/spufs/switch.c b/arch/powerpc/platforms/cell/spufs/switch.c index 71a0b41adb8c..9c506ba08cdc 100644 --- a/arch/powerpc/platforms/cell/spufs/switch.c +++ b/arch/powerpc/platforms/cell/spufs/switch.c @@ -70,7 +70,7 @@ } #endif /* debug */ -#define POLL_WHILE_FALSE(_c) POLL_WHILE_TRUE(!(_c)) +#define POLL_WHILE_FALSE(_c) POLL_WHILE_TRUE(!(_c)) static inline void acquire_spu_lock(struct spu *spu) { @@ -387,6 +387,19 @@ static inline void save_ppu_querytype(struct spu_state *csa, struct spu *spu) csa->prob.dma_querytype_RW = in_be32(&prob->dma_querytype_RW); } +static inline void save_ppu_tagstatus(struct spu_state *csa, struct spu *spu) +{ + struct spu_problem __iomem *prob = spu->problem; + + /* Save the Prxy_TagStatus register in the CSA. + * + * It is unnecessary to restore dma_tagstatus_R, however, + * dma_tagstatus_R in the CSA is accessed via backing_ops, so + * we must save it. + */ + csa->prob.dma_tagstatus_R = in_be32(&prob->dma_tagstatus_R); +} + static inline void save_mfc_csr_tsq(struct spu_state *csa, struct spu *spu) { struct spu_priv2 __iomem *priv2 = spu->priv2; @@ -1812,6 +1825,7 @@ static void save_csa(struct spu_state *prev, struct spu *spu) save_mfc_queues(prev, spu); /* Step 19. */ save_ppu_querymask(prev, spu); /* Step 20. */ save_ppu_querytype(prev, spu); /* Step 21. */ + save_ppu_tagstatus(prev, spu); /* NEW. */ save_mfc_csr_tsq(prev, spu); /* Step 22. */ save_mfc_csr_cmd(prev, spu); /* Step 23. */ save_mfc_csr_ato(prev, spu); /* Step 24. */ @@ -1930,7 +1944,7 @@ static void harvest(struct spu_state *prev, struct spu *spu) reset_spu_privcntl(prev, spu); /* Step 16. */ reset_spu_lslr(prev, spu); /* Step 17. */ setup_mfc_sr1(prev, spu); /* Step 18. */ - spu_invalidate_slbs(spu); /* Step 19. */ + spu_invalidate_slbs(spu); /* Step 19. */ reset_ch_part1(prev, spu); /* Step 20. */ reset_ch_part2(prev, spu); /* Step 21. */ enable_interrupts(prev, spu); /* Step 22. */ diff --git a/arch/powerpc/platforms/chrp/Kconfig b/arch/powerpc/platforms/chrp/Kconfig index d2c690531963..22b4b4e3b6f0 100644 --- a/arch/powerpc/platforms/chrp/Kconfig +++ b/arch/powerpc/platforms/chrp/Kconfig @@ -8,4 +8,5 @@ config PPC_CHRP select PPC_MPC106 select PPC_UDBG_16550 select PPC_NATIVE + select PCI default y diff --git a/arch/powerpc/platforms/chrp/Makefile b/arch/powerpc/platforms/chrp/Makefile index 902feb1ac431..4b3bfadc70fa 100644 --- a/arch/powerpc/platforms/chrp/Makefile +++ b/arch/powerpc/platforms/chrp/Makefile @@ -1,4 +1,3 @@ -obj-y += setup.o time.o pegasos_eth.o -obj-$(CONFIG_PCI) += pci.o +obj-y += setup.o time.o pegasos_eth.o pci.o obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_NVRAM) += nvram.o diff --git a/arch/powerpc/platforms/chrp/pci.c b/arch/powerpc/platforms/chrp/pci.c index d32fedc991d3..3690624e49d4 100644 --- a/arch/powerpc/platforms/chrp/pci.c +++ b/arch/powerpc/platforms/chrp/pci.c @@ -99,7 +99,7 @@ int rtas_read_config(struct pci_bus *bus, unsigned int devfn, int offset, struct pci_controller *hose = bus->sysdata; unsigned long addr = (offset & 0xff) | ((devfn & 0xff) << 8) | (((bus->number - hose->first_busno) & 0xff) << 16) - | (hose->index << 24); + | (hose->global_number << 24); int ret = -1; int rval; @@ -114,7 +114,7 @@ int rtas_write_config(struct pci_bus *bus, unsigned int devfn, int offset, struct pci_controller *hose = bus->sysdata; unsigned long addr = (offset & 0xff) | ((devfn & 0xff) << 8) | (((bus->number - hose->first_busno) & 0xff) << 16) - | (hose->index << 24); + | (hose->global_number << 24); int rval; rval = rtas_call(rtas_token("write-pci-config"), 3, 1, NULL, @@ -254,13 +254,12 @@ chrp_find_bridges(void) printk(" at %llx", (unsigned long long)r.start); printk("\n"); - hose = pcibios_alloc_controller(); + hose = pcibios_alloc_controller(dev); if (!hose) { printk("Can't allocate PCI controller structure for %s\n", dev->full_name); continue; } - hose->arch_data = dev; hose->first_busno = bus_range[0]; hose->last_busno = bus_range[1]; diff --git a/arch/powerpc/platforms/embedded6xx/Kconfig b/arch/powerpc/platforms/embedded6xx/Kconfig index f2d26268ca6f..bec772674e40 100644 --- a/arch/powerpc/platforms/embedded6xx/Kconfig +++ b/arch/powerpc/platforms/embedded6xx/Kconfig @@ -28,6 +28,7 @@ config PPC_HOLLY bool "PPC750GX/CL with TSI10x bridge (Hickory/Holly)" select TSI108_BRIDGE select PPC_UDBG_16550 + select WANT_DEVICE_TREE help Select PPC_HOLLY if configuring for an IBM 750GX/CL Eval Board with TSI108/9 bridge (Hickory/Holly) @@ -44,6 +45,7 @@ endchoice config TSI108_BRIDGE bool depends on MPC7448HPC2 || PPC_HOLLY + select PCI select MPIC select MPIC_WEIRD default y diff --git a/arch/powerpc/platforms/embedded6xx/holly.c b/arch/powerpc/platforms/embedded6xx/holly.c index 3a0b4a01401c..6292e36dc577 100644 --- a/arch/powerpc/platforms/embedded6xx/holly.c +++ b/arch/powerpc/platforms/embedded6xx/holly.c @@ -45,7 +45,7 @@ #define HOLLY_PCI_CFG_PHYS 0x7c000000 -int holly_exclude_device(u_char bus, u_char devfn) +int holly_exclude_device(struct pci_controller *hose, u_char bus, u_char devfn) { if (bus == 0 && PCI_SLOT(devfn) == 0) return PCIBIOS_DEVICE_NOT_FOUND; diff --git a/arch/powerpc/platforms/embedded6xx/linkstation.c b/arch/powerpc/platforms/embedded6xx/linkstation.c index b412f006a9c5..f4d0a7a603f5 100644 --- a/arch/powerpc/platforms/embedded6xx/linkstation.c +++ b/arch/powerpc/platforms/embedded6xx/linkstation.c @@ -54,8 +54,9 @@ static struct mtd_partition linkstation_physmap_partitions[] = { }, }; -static int __init add_bridge(struct device_node *dev) +static int __init linkstation_add_bridge(struct device_node *dev) { +#ifdef CONFIG_PCI int len; struct pci_controller *hose; const int *bus_range; @@ -67,18 +68,17 @@ static int __init add_bridge(struct device_node *dev) printk(KERN_WARNING "Can't get bus-range for %s, assume" " bus 0\n", dev->full_name); - hose = pcibios_alloc_controller(); + hose = pcibios_alloc_controller(dev); if (hose == NULL) return -ENOMEM; hose->first_busno = bus_range ? bus_range[0] : 0; hose->last_busno = bus_range ? bus_range[1] : 0xff; - hose->arch_data = dev; setup_indirect_pci(hose, 0xfec00000, 0xfee00000); /* Interpret the "ranges" property */ /* This also maps the I/O region and sets isa_io/mem_base */ pci_process_bridge_OF_ranges(hose, dev, 1); - +#endif return 0; } @@ -92,7 +92,7 @@ static void __init linkstation_setup_arch(void) /* Lookup PCI host bridges */ for (np = NULL; (np = of_find_node_by_type(np, "pci")) != NULL;) - add_bridge(np); + linkstation_add_bridge(np); printk(KERN_INFO "BUFFALO Network Attached Storage Series\n"); printk(KERN_INFO "(C) 2002-2005 BUFFALO INC.\n"); diff --git a/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c b/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c index 4542e0c837c0..1e3cc69487b5 100644 --- a/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c +++ b/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c @@ -54,15 +54,10 @@ #define MPC7448HPC2_PCI_CFG_PHYS 0xfb000000 -#ifndef CONFIG_PCI -isa_io_base = MPC7448_HPC2_ISA_IO_BASE; -isa_mem_base = MPC7448_HPC2_ISA_MEM_BASE; -pci_dram_offset = MPC7448_HPC2_PCI_MEM_OFFSET; -#endif - extern void _nmask_and_or_msr(unsigned long nmask, unsigned long or_val); -int mpc7448_hpc2_exclude_device(u_char bus, u_char devfn) +int mpc7448_hpc2_exclude_device(struct pci_controller *hose, + u_char bus, u_char devfn) { if (bus == 0 && PCI_SLOT(devfn) == 0) return PCIBIOS_DEVICE_NOT_FOUND; diff --git a/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.h b/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.h index a543a5242e34..f7e0e0c7f8d8 100644 --- a/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.h +++ b/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.h @@ -18,9 +18,4 @@ #include -/* Base Addresses for the PCI bus - */ -#define MPC7448_HPC2_PCI_MEM_OFFSET (0x00000000) -#define MPC7448_HPC2_ISA_IO_BASE (0x00000000) -#define MPC7448_HPC2_ISA_MEM_BASE (0x00000000) #endif /* __PPC_PLATFORMS_MPC7448_HPC2_H */ diff --git a/arch/powerpc/platforms/iseries/call_hpt.h b/arch/powerpc/platforms/iseries/call_hpt.h index a843b0f87b72..8d95fe4b554e 100644 --- a/arch/powerpc/platforms/iseries/call_hpt.h +++ b/arch/powerpc/platforms/iseries/call_hpt.h @@ -76,24 +76,25 @@ static inline u64 HvCallHpt_invalidateSetSwBitsGet(u32 hpteIndex, u8 bitson, return compressedStatus; } -static inline u64 HvCallHpt_findValid(hpte_t *hpte, u64 vpn) +static inline u64 HvCallHpt_findValid(struct hash_pte *hpte, u64 vpn) { return HvCall3Ret16(HvCallHptFindValid, hpte, vpn, 0, 0); } -static inline u64 HvCallHpt_findNextValid(hpte_t *hpte, u32 hpteIndex, +static inline u64 HvCallHpt_findNextValid(struct hash_pte *hpte, u32 hpteIndex, u8 bitson, u8 bitsoff) { return HvCall3Ret16(HvCallHptFindNextValid, hpte, hpteIndex, bitson, bitsoff); } -static inline void HvCallHpt_get(hpte_t *hpte, u32 hpteIndex) +static inline void HvCallHpt_get(struct hash_pte *hpte, u32 hpteIndex) { HvCall2Ret16(HvCallHptGet, hpte, hpteIndex, 0); } -static inline void HvCallHpt_addValidate(u32 hpteIndex, u32 hBit, hpte_t *hpte) +static inline void HvCallHpt_addValidate(u32 hpteIndex, u32 hBit, + struct hash_pte *hpte) { HvCall4(HvCallHptAddValidate, hpteIndex, hBit, hpte->v, hpte->r); } diff --git a/arch/powerpc/platforms/iseries/htab.c b/arch/powerpc/platforms/iseries/htab.c index ed44dfceaa45..b4e2c7a038e1 100644 --- a/arch/powerpc/platforms/iseries/htab.c +++ b/arch/powerpc/platforms/iseries/htab.c @@ -44,7 +44,7 @@ long iSeries_hpte_insert(unsigned long hpte_group, unsigned long va, unsigned long vflags, int psize) { long slot; - hpte_t lhpte; + struct hash_pte lhpte; int secondary = 0; BUG_ON(psize != MMU_PAGE_4K); @@ -99,7 +99,7 @@ long iSeries_hpte_insert(unsigned long hpte_group, unsigned long va, static unsigned long iSeries_hpte_getword0(unsigned long slot) { - hpte_t hpte; + struct hash_pte hpte; HvCallHpt_get(&hpte, slot); return hpte.v; @@ -144,7 +144,7 @@ static long iSeries_hpte_remove(unsigned long hpte_group) static long iSeries_hpte_updatepp(unsigned long slot, unsigned long newpp, unsigned long va, int psize, int local) { - hpte_t hpte; + struct hash_pte hpte; unsigned long want_v; iSeries_hlock(slot); @@ -176,7 +176,7 @@ static long iSeries_hpte_updatepp(unsigned long slot, unsigned long newpp, */ static long iSeries_hpte_find(unsigned long vpn) { - hpte_t hpte; + struct hash_pte hpte; long slot; /* diff --git a/arch/powerpc/platforms/iseries/pci.c b/arch/powerpc/platforms/iseries/pci.c index 9c974227155e..da87162000f0 100644 --- a/arch/powerpc/platforms/iseries/pci.c +++ b/arch/powerpc/platforms/iseries/pci.c @@ -742,6 +742,11 @@ void __init iSeries_pcibios_init(void) /* Install IO hooks */ ppc_pci_io = iseries_pci_io; + /* iSeries has no IO space in the common sense, it needs to set + * the IO base to 0 + */ + pci_io_base = 0; + if (root == NULL) { printk(KERN_CRIT "iSeries_pcibios_init: can't find root " "of device tree\n"); @@ -763,7 +768,7 @@ void __init iSeries_pcibios_init(void) if (phb == NULL) continue; - phb->pci_mem_offset = phb->local_number = bus; + phb->pci_mem_offset = bus; phb->first_busno = bus; phb->last_busno = bus; phb->ops = &iSeries_pci_ops; diff --git a/arch/powerpc/platforms/iseries/setup.c b/arch/powerpc/platforms/iseries/setup.c index 7f5dcee814d4..13a8b1908ded 100644 --- a/arch/powerpc/platforms/iseries/setup.c +++ b/arch/powerpc/platforms/iseries/setup.c @@ -79,8 +79,6 @@ extern void iSeries_pci_final_fixup(void); static void iSeries_pci_final_fixup(void) { } #endif -extern unsigned long iSeries_recal_tb; -extern unsigned long iSeries_recal_titan; struct MemoryBlock { unsigned long absStart; @@ -292,8 +290,8 @@ static void __init iSeries_init_early(void) { DBG(" -> iSeries_init_early()\n"); - iSeries_recal_tb = get_tb(); - iSeries_recal_titan = HvCallXm_loadTod(); + /* Snapshot the timebase, for use in later recalibration */ + iSeries_time_init_early(); /* * Initialize the DMA/TCE management diff --git a/arch/powerpc/platforms/maple/pci.c b/arch/powerpc/platforms/maple/pci.c index 7aaa5bbc9363..fceaae40fe70 100644 --- a/arch/powerpc/platforms/maple/pci.c +++ b/arch/powerpc/platforms/maple/pci.c @@ -444,7 +444,7 @@ static void __init setup_u3_ht(struct pci_controller* hose) u3_ht = hose; } -static int __init add_bridge(struct device_node *dev) +static int __init maple_add_bridge(struct device_node *dev) { int len; struct pci_controller *hose; @@ -519,23 +519,6 @@ void __devinit maple_pci_irq_fixup(struct pci_dev *dev) DBG(" <- maple_pci_irq_fixup\n"); } -static void __init maple_fixup_phb_resources(void) -{ - struct pci_controller *hose, *tmp; - - list_for_each_entry_safe(hose, tmp, &hose_list, list_node) { - unsigned long offset = (unsigned long)hose->io_base_virt - pci_io_base; - - hose->io_resource.start += offset; - hose->io_resource.end += offset; - - printk(KERN_INFO "PCI Host %d, io start: %llx; io end: %llx\n", - hose->global_number, - (unsigned long long)hose->io_resource.start, - (unsigned long long)hose->io_resource.end); - } -} - void __init maple_pci_init(void) { struct device_node *np, *root; @@ -558,7 +541,7 @@ void __init maple_pci_init(void) continue; if ((of_device_is_compatible(np, "u4-pcie") || of_device_is_compatible(np, "u3-agp")) && - add_bridge(np) == 0) + maple_add_bridge(np) == 0) of_node_get(np); if (of_device_is_compatible(np, "u3-ht")) { @@ -570,27 +553,9 @@ void __init maple_pci_init(void) /* Now setup the HyperTransport host if we found any */ - if (ht && add_bridge(ht) != 0) + if (ht && maple_add_bridge(ht) != 0) of_node_put(ht); - /* - * We need to call pci_setup_phb_io for the HT bridge first - * so it gets the I/O port numbers starting at 0, and we - * need to call it for the AGP bridge after that so it gets - * small positive I/O port numbers. - */ - if (u3_ht) - pci_setup_phb_io(u3_ht, 1); - if (u3_agp) - pci_setup_phb_io(u3_agp, 0); - if (u4_pcie) - pci_setup_phb_io(u4_pcie, 0); - - /* Fixup the IO resources on our host bridges as the common code - * does it only for childs of the host bridges - */ - maple_fixup_phb_resources(); - /* Setup the linkage between OF nodes and PHBs */ pci_devs_phb_init(); diff --git a/arch/powerpc/platforms/pasemi/Kconfig b/arch/powerpc/platforms/pasemi/Kconfig index 7c5076e38ea1..95cd90fd81c7 100644 --- a/arch/powerpc/platforms/pasemi/Kconfig +++ b/arch/powerpc/platforms/pasemi/Kconfig @@ -25,4 +25,13 @@ config PPC_PASEMI_MDIO help Driver for MDIO via GPIO on PWRficient platforms +config ELECTRA_IDE + tristate "Electra IDE driver" + default y + depends on PPC_PASEMI && ATA + select PATA_PLATFORM + help + This includes driver support for the Electra on-board IDE + interface. + endmenu diff --git a/arch/powerpc/platforms/pasemi/Makefile b/arch/powerpc/platforms/pasemi/Makefile index 2cd2a4f26a48..f47fcac7e581 100644 --- a/arch/powerpc/platforms/pasemi/Makefile +++ b/arch/powerpc/platforms/pasemi/Makefile @@ -1,3 +1,4 @@ obj-y += setup.o pci.o time.o idle.o powersave.o iommu.o obj-$(CONFIG_PPC_PASEMI_MDIO) += gpio_mdio.o +obj-$(CONFIG_ELECTRA_IDE) += electra_ide.o obj-$(CONFIG_PPC_PASEMI_CPUFREQ) += cpufreq.o diff --git a/arch/powerpc/platforms/pasemi/electra_ide.c b/arch/powerpc/platforms/pasemi/electra_ide.c new file mode 100644 index 000000000000..12fb0c949263 --- /dev/null +++ b/arch/powerpc/platforms/pasemi/electra_ide.c @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2007 PA Semi, Inc + * + * Maintained by: Olof Johansson + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#include +#include + +/* The electra IDE interface is incredibly simple: Just a device on the localbus + * with interrupts hooked up to one of the GPIOs. The device tree contains the + * address window and interrupt mappings already, and the pata_platform driver handles + * the rest. We just need to hook the two up. + */ + +#define MAX_IFS 4 /* really, we have only one */ + +static struct platform_device *pdevs[MAX_IFS]; + +static int __devinit electra_ide_init(void) +{ + struct device_node *np; + struct resource r[3]; + int ret = 0; + int i; + + np = of_find_compatible_node(NULL, "ide", "electra-ide"); + i = 0; + + while (np && i < MAX_IFS) { + memset(r, 0, sizeof(r)); + + /* pata_platform wants two address ranges: one for the base registers, + * another for the control (altstatus). It's located at offset 0x3f6 in + * the window, but the device tree only has one large register window + * that covers both ranges. So we need to split it up by hand here: + */ + + ret = of_address_to_resource(np, 0, &r[0]); + if (ret) + goto out; + ret = of_address_to_resource(np, 0, &r[1]); + if (ret) + goto out; + + r[1].start += 0x3f6; + r[0].end = r[1].start-1; + + r[2].start = irq_of_parse_and_map(np, 0); + r[2].end = irq_of_parse_and_map(np, 0); + r[2].flags = IORESOURCE_IRQ; + + pr_debug("registering platform device at 0x%lx/0x%lx, irq is %ld\n", + r[0].start, r[1].start, r[2].start); + pdevs[i] = platform_device_register_simple("pata_platform", i, r, 3); + if (IS_ERR(pdevs[i])) { + ret = PTR_ERR(pdevs[i]); + pdevs[i] = NULL; + goto out; + } + np = of_find_compatible_node(np, "ide", "electra-ide"); + } +out: + return ret; +} +module_init(electra_ide_init); + +static void __devexit electra_ide_exit(void) +{ + int i; + + for (i = 0; i < MAX_IFS; i++) + if (pdevs[i]) + platform_device_unregister(pdevs[i]); +} +module_exit(electra_ide_exit); + + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR ("Olof Johansson "); +MODULE_DESCRIPTION("PA Semi Electra IDE driver"); diff --git a/arch/powerpc/platforms/pasemi/pci.c b/arch/powerpc/platforms/pasemi/pci.c index bbc6dfcfaa91..ab1f5f62bcd8 100644 --- a/arch/powerpc/platforms/pasemi/pci.c +++ b/arch/powerpc/platforms/pasemi/pci.c @@ -132,7 +132,7 @@ static void __init setup_pa_pxp(struct pci_controller *hose) hose->cfg_data = ioremap(0xe0000000, 0x10000000); } -static int __init add_bridge(struct device_node *dev) +static int __init pas_add_bridge(struct device_node *dev) { struct pci_controller *hose; @@ -150,29 +150,11 @@ static int __init add_bridge(struct device_node *dev) printk(KERN_INFO "Found PA-PXP PCI host bridge.\n"); /* Interpret the "ranges" property */ - /* This also maps the I/O region and sets isa_io/mem_base */ pci_process_bridge_OF_ranges(hose, dev, 1); - pci_setup_phb_io(hose, 1); return 0; } - -static void __init pas_fixup_phb_resources(void) -{ - struct pci_controller *hose, *tmp; - - list_for_each_entry_safe(hose, tmp, &hose_list, list_node) { - unsigned long offset = (unsigned long)hose->io_base_virt - pci_io_base; - hose->io_resource.start += offset; - hose->io_resource.end += offset; - printk(KERN_INFO "PCI Host %d, io start: %lx; io end: %lx\n", - hose->global_number, - hose->io_resource.start, hose->io_resource.end); - } -} - - void __init pas_pci_init(void) { struct device_node *np, *root; @@ -185,13 +167,11 @@ void __init pas_pci_init(void) } for (np = NULL; (np = of_get_next_child(root, np)) != NULL;) - if (np->name && !strcmp(np->name, "pxp") && !add_bridge(np)) + if (np->name && !strcmp(np->name, "pxp") && !pas_add_bridge(np)) of_node_get(np); of_node_put(root); - pas_fixup_phb_resources(); - /* Setup the linkage between OF nodes and PHBs */ pci_devs_phb_init(); diff --git a/arch/powerpc/platforms/pasemi/setup.c b/arch/powerpc/platforms/pasemi/setup.c index c5a3f61f8d85..ffe6528048b5 100644 --- a/arch/powerpc/platforms/pasemi/setup.c +++ b/arch/powerpc/platforms/pasemi/setup.c @@ -239,7 +239,7 @@ static int __init pas_probe(void) return 1; } -define_machine(pas) { +define_machine(pasemi) { .name = "PA Semi PA6T-1682M", .probe = pas_probe, .setup_arch = pas_setup_arch, diff --git a/arch/powerpc/platforms/powermac/Kconfig b/arch/powerpc/platforms/powermac/Kconfig index 5b7afe50039a..055990ca8ce6 100644 --- a/arch/powerpc/platforms/powermac/Kconfig +++ b/arch/powerpc/platforms/powermac/Kconfig @@ -2,6 +2,7 @@ config PPC_PMAC bool "Apple PowerMac based machines" depends on PPC_MULTIPLATFORM select MPIC + select PCI select PPC_INDIRECT_PCI if PPC32 select PPC_MPC106 if PPC32 select PPC_NATIVE diff --git a/arch/powerpc/platforms/powermac/low_i2c.c b/arch/powerpc/platforms/powermac/low_i2c.c index 3f507ab9c5e5..efdf5eb81ecc 100644 --- a/arch/powerpc/platforms/powermac/low_i2c.c +++ b/arch/powerpc/platforms/powermac/low_i2c.c @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -84,7 +85,7 @@ struct pmac_i2c_bus void *hostdata; int channel; /* some hosts have multiple */ int mode; /* current mode */ - struct semaphore sem; + struct mutex mutex; int opened; int polled; /* open mode */ struct platform_device *platform_dev; @@ -104,7 +105,7 @@ static LIST_HEAD(pmac_i2c_busses); struct pmac_i2c_host_kw { - struct semaphore mutex; /* Access mutex for use by + struct mutex mutex; /* Access mutex for use by * i2c-keywest */ void __iomem *base; /* register base address */ int bsteps; /* register stepping */ @@ -375,14 +376,14 @@ static void kw_i2c_timeout(unsigned long data) static int kw_i2c_open(struct pmac_i2c_bus *bus) { struct pmac_i2c_host_kw *host = bus->hostdata; - down(&host->mutex); + mutex_lock(&host->mutex); return 0; } static void kw_i2c_close(struct pmac_i2c_bus *bus) { struct pmac_i2c_host_kw *host = bus->hostdata; - up(&host->mutex); + mutex_unlock(&host->mutex); } static int kw_i2c_xfer(struct pmac_i2c_bus *bus, u8 addrdir, int subsize, @@ -498,7 +499,7 @@ static struct pmac_i2c_host_kw *__init kw_i2c_host_init(struct device_node *np) kfree(host); return NULL; } - init_MUTEX(&host->mutex); + mutex_init(&host->mutex); init_completion(&host->complete); spin_lock_init(&host->lock); init_timer(&host->timeout_timer); @@ -571,7 +572,7 @@ static void __init kw_i2c_add(struct pmac_i2c_host_kw *host, bus->open = kw_i2c_open; bus->close = kw_i2c_close; bus->xfer = kw_i2c_xfer; - init_MUTEX(&bus->sem); + mutex_init(&bus->mutex); if (controller == busnode) bus->flags = pmac_i2c_multibus; list_add(&bus->link, &pmac_i2c_busses); @@ -798,7 +799,7 @@ static void __init pmu_i2c_probe(void) bus->mode = pmac_i2c_mode_std; bus->hostdata = bus + 1; bus->xfer = pmu_i2c_xfer; - init_MUTEX(&bus->sem); + mutex_init(&bus->mutex); bus->flags = pmac_i2c_multibus; list_add(&bus->link, &pmac_i2c_busses); @@ -921,7 +922,7 @@ static void __init smu_i2c_probe(void) bus->mode = pmac_i2c_mode_std; bus->hostdata = bus + 1; bus->xfer = smu_i2c_xfer; - init_MUTEX(&bus->sem); + mutex_init(&bus->mutex); bus->flags = 0; list_add(&bus->link, &pmac_i2c_busses); @@ -1093,13 +1094,13 @@ int pmac_i2c_open(struct pmac_i2c_bus *bus, int polled) { int rc; - down(&bus->sem); + mutex_lock(&bus->mutex); bus->polled = polled || pmac_i2c_force_poll; bus->opened = 1; bus->mode = pmac_i2c_mode_std; if (bus->open && (rc = bus->open(bus)) != 0) { bus->opened = 0; - up(&bus->sem); + mutex_unlock(&bus->mutex); return rc; } return 0; @@ -1112,7 +1113,7 @@ void pmac_i2c_close(struct pmac_i2c_bus *bus) if (bus->close) bus->close(bus); bus->opened = 0; - up(&bus->sem); + mutex_unlock(&bus->mutex); } EXPORT_SYMBOL_GPL(pmac_i2c_close); diff --git a/arch/powerpc/platforms/powermac/pci.c b/arch/powerpc/platforms/powermac/pci.c index c4af9e21ac93..92586db19754 100644 --- a/arch/powerpc/platforms/powermac/pci.c +++ b/arch/powerpc/platforms/powermac/pci.c @@ -35,8 +35,6 @@ #define DBG(x...) #endif -static int add_bridge(struct device_node *dev); - /* XXX Could be per-controller, but I don't think we risk anything by * assuming we won't have both UniNorth and Bandit */ static int has_uninorth; @@ -897,7 +895,7 @@ static void __init setup_u3_ht(struct pci_controller* hose) * "pci" (a MPC106) and no bandit or chaos bridges, and contrariwise, * if we have one or more bandit or chaos bridges, we don't have a MPC106. */ -static int __init add_bridge(struct device_node *dev) +static int __init pmac_add_bridge(struct device_node *dev) { int len; struct pci_controller *hose; @@ -918,15 +916,9 @@ static int __init add_bridge(struct device_node *dev) " bus 0\n", dev->full_name); } - /* XXX Different prototypes, to be merged */ -#ifdef CONFIG_PPC64 hose = pcibios_alloc_controller(dev); -#else - hose = pcibios_alloc_controller(); -#endif if (!hose) return -ENOMEM; - hose->arch_data = dev; hose->first_busno = bus_range ? bus_range[0] : 0; hose->last_busno = bus_range ? bus_range[1] : 0xff; @@ -1006,19 +998,6 @@ void __devinit pmac_pci_irq_fixup(struct pci_dev *dev) #endif /* CONFIG_PPC32 */ } -#ifdef CONFIG_PPC64 -static void __init pmac_fixup_phb_resources(void) -{ - struct pci_controller *hose, *tmp; - - list_for_each_entry_safe(hose, tmp, &hose_list, list_node) { - printk(KERN_INFO "PCI Host %d, io start: %lx; io end: %lx\n", - hose->global_number, - hose->io_resource.start, hose->io_resource.end); - } -} -#endif - void __init pmac_pci_init(void) { struct device_node *np, *root; @@ -1036,7 +1015,7 @@ void __init pmac_pci_init(void) if (strcmp(np->name, "bandit") == 0 || strcmp(np->name, "chaos") == 0 || strcmp(np->name, "pci") == 0) { - if (add_bridge(np) == 0) + if (pmac_add_bridge(np) == 0) of_node_get(np); } if (strcmp(np->name, "ht") == 0) { @@ -1050,28 +1029,9 @@ void __init pmac_pci_init(void) /* Probe HT last as it relies on the agp resources to be already * setup */ - if (ht && add_bridge(ht) != 0) + if (ht && pmac_add_bridge(ht) != 0) of_node_put(ht); - /* - * We need to call pci_setup_phb_io for the HT bridge first - * so it gets the I/O port numbers starting at 0, and we - * need to call it for the AGP bridge after that so it gets - * small positive I/O port numbers. - */ - if (u3_ht) - pci_setup_phb_io(u3_ht, 1); - if (u3_agp) - pci_setup_phb_io(u3_agp, 0); - if (u4_pcie) - pci_setup_phb_io(u4_pcie, 0); - - /* - * On ppc64, fixup the IO resources on our host bridges as - * the common code does it only for children of the host bridges - */ - pmac_fixup_phb_resources(); - /* Setup the linkage between OF nodes and PHBs */ pci_devs_phb_init(); diff --git a/arch/powerpc/platforms/ps3/Kconfig b/arch/powerpc/platforms/ps3/Kconfig index 40f0008af4d1..a05079b07696 100644 --- a/arch/powerpc/platforms/ps3/Kconfig +++ b/arch/powerpc/platforms/ps3/Kconfig @@ -7,6 +7,7 @@ config PPC_PS3 select USB_OHCI_BIG_ENDIAN_MMIO select USB_ARCH_HAS_EHCI select USB_EHCI_BIG_ENDIAN_MMIO + select MEMORY_HOTPLUG help This option enables support for the Sony PS3 game console and other platforms using the PS3 hypervisor. @@ -73,18 +74,12 @@ config PS3_USE_LPAR_ADDR config PS3_VUART depends on PPC_PS3 - bool "PS3 Virtual UART support" if PS3_ADVANCED - default y - help - Include support for the PS3 Virtual UART. - - This support is required for several system services - including the System Manager and AV Settings. In - general, all users will say Y. + tristate config PS3_PS3AV + depends on PPC_PS3 tristate "PS3 AV settings driver" if PS3_ADVANCED - depends on PS3_VUART + select PS3_VUART default y help Include support for the PS3 AV Settings driver. @@ -93,13 +88,18 @@ config PS3_PS3AV general, all users will say Y or M. config PS3_SYS_MANAGER - bool "PS3 System Manager driver" if PS3_ADVANCED - depends on PS3_VUART - default y + depends on PPC_PS3 + tristate "PS3 System Manager driver" if PS3_ADVANCED + select PS3_VUART + default m help Include support for the PS3 System Manager. This support is required for system control. In - general, all users will say Y. + general, all users will say Y or M. + +config PS3_STORAGE + depends on PPC_PS3 + tristate endmenu diff --git a/arch/powerpc/platforms/ps3/Makefile b/arch/powerpc/platforms/ps3/Makefile index a0048fcf0866..ac1bdf844eca 100644 --- a/arch/powerpc/platforms/ps3/Makefile +++ b/arch/powerpc/platforms/ps3/Makefile @@ -4,3 +4,4 @@ obj-y += system-bus.o obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_SPU_BASE) += spu.o +obj-y += device-init.o diff --git a/arch/powerpc/platforms/ps3/device-init.c b/arch/powerpc/platforms/ps3/device-init.c new file mode 100644 index 000000000000..825ebb2cbc2a --- /dev/null +++ b/arch/powerpc/platforms/ps3/device-init.c @@ -0,0 +1,785 @@ +/* + * PS3 device registration routines. + * + * Copyright (C) 2007 Sony Computer Entertainment Inc. + * Copyright 2007 Sony Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "platform.h" + +/** + * ps3_setup_gelic_device - Setup and register a gelic device instance. + * + * Allocates memory for a struct ps3_system_bus_device instance, initialises the + * structure members, and registers the device instance with the system bus. + */ + +static int __init ps3_setup_gelic_device( + const struct ps3_repository_device *repo) +{ + int result; + struct layout { + struct ps3_system_bus_device dev; + struct ps3_dma_region d_region; + } *p; + + pr_debug(" -> %s:%d\n", __func__, __LINE__); + + BUG_ON(repo->bus_type != PS3_BUS_TYPE_SB); + BUG_ON(repo->dev_type != PS3_DEV_TYPE_SB_GELIC); + + p = kzalloc(sizeof(struct layout), GFP_KERNEL); + + if (!p) { + result = -ENOMEM; + goto fail_malloc; + } + + p->dev.match_id = PS3_MATCH_ID_GELIC; + p->dev.dev_type = PS3_DEVICE_TYPE_SB; + p->dev.bus_id = repo->bus_id; + p->dev.dev_id = repo->dev_id; + p->dev.d_region = &p->d_region; + + result = ps3_repository_find_interrupt(repo, + PS3_INTERRUPT_TYPE_EVENT_PORT, &p->dev.interrupt_id); + + if (result) { + pr_debug("%s:%d ps3_repository_find_interrupt failed\n", + __func__, __LINE__); + goto fail_find_interrupt; + } + + BUG_ON(p->dev.interrupt_id != 0); + + result = ps3_dma_region_init(&p->dev, p->dev.d_region, PS3_DMA_64K, + PS3_DMA_OTHER, NULL, 0); + + if (result) { + pr_debug("%s:%d ps3_dma_region_init failed\n", + __func__, __LINE__); + goto fail_dma_init; + } + + result = ps3_system_bus_device_register(&p->dev); + + if (result) { + pr_debug("%s:%d ps3_system_bus_device_register failed\n", + __func__, __LINE__); + goto fail_device_register; + } + + pr_debug(" <- %s:%d\n", __func__, __LINE__); + return result; + +fail_device_register: +fail_dma_init: +fail_find_interrupt: + kfree(p); +fail_malloc: + pr_debug(" <- %s:%d: fail.\n", __func__, __LINE__); + return result; +} + +static int __init_refok ps3_setup_uhc_device( + const struct ps3_repository_device *repo, enum ps3_match_id match_id, + enum ps3_interrupt_type interrupt_type, enum ps3_reg_type reg_type) +{ + int result; + struct layout { + struct ps3_system_bus_device dev; + struct ps3_dma_region d_region; + struct ps3_mmio_region m_region; + } *p; + u64 bus_addr; + u64 len; + + pr_debug(" -> %s:%d\n", __func__, __LINE__); + + BUG_ON(repo->bus_type != PS3_BUS_TYPE_SB); + BUG_ON(repo->dev_type != PS3_DEV_TYPE_SB_USB); + + p = kzalloc(sizeof(struct layout), GFP_KERNEL); + + if (!p) { + result = -ENOMEM; + goto fail_malloc; + } + + p->dev.match_id = match_id; + p->dev.dev_type = PS3_DEVICE_TYPE_SB; + p->dev.bus_id = repo->bus_id; + p->dev.dev_id = repo->dev_id; + p->dev.d_region = &p->d_region; + p->dev.m_region = &p->m_region; + + result = ps3_repository_find_interrupt(repo, + interrupt_type, &p->dev.interrupt_id); + + if (result) { + pr_debug("%s:%d ps3_repository_find_interrupt failed\n", + __func__, __LINE__); + goto fail_find_interrupt; + } + + result = ps3_repository_find_reg(repo, reg_type, + &bus_addr, &len); + + if (result) { + pr_debug("%s:%d ps3_repository_find_reg failed\n", + __func__, __LINE__); + goto fail_find_reg; + } + + result = ps3_dma_region_init(&p->dev, p->dev.d_region, PS3_DMA_64K, + PS3_DMA_INTERNAL, NULL, 0); + + if (result) { + pr_debug("%s:%d ps3_dma_region_init failed\n", + __func__, __LINE__); + goto fail_dma_init; + } + + result = ps3_mmio_region_init(&p->dev, p->dev.m_region, bus_addr, len, + PS3_MMIO_4K); + + if (result) { + pr_debug("%s:%d ps3_mmio_region_init failed\n", + __func__, __LINE__); + goto fail_mmio_init; + } + + result = ps3_system_bus_device_register(&p->dev); + + if (result) { + pr_debug("%s:%d ps3_system_bus_device_register failed\n", + __func__, __LINE__); + goto fail_device_register; + } + + pr_debug(" <- %s:%d\n", __func__, __LINE__); + return result; + +fail_device_register: +fail_mmio_init: +fail_dma_init: +fail_find_reg: +fail_find_interrupt: + kfree(p); +fail_malloc: + pr_debug(" <- %s:%d: fail.\n", __func__, __LINE__); + return result; +} + +static int __init ps3_setup_ehci_device( + const struct ps3_repository_device *repo) +{ + return ps3_setup_uhc_device(repo, PS3_MATCH_ID_EHCI, + PS3_INTERRUPT_TYPE_SB_EHCI, PS3_REG_TYPE_SB_EHCI); +} + +static int __init ps3_setup_ohci_device( + const struct ps3_repository_device *repo) +{ + return ps3_setup_uhc_device(repo, PS3_MATCH_ID_OHCI, + PS3_INTERRUPT_TYPE_SB_OHCI, PS3_REG_TYPE_SB_OHCI); +} + +static int __init ps3_setup_vuart_device(enum ps3_match_id match_id, + unsigned int port_number) +{ + int result; + struct layout { + struct ps3_system_bus_device dev; + } *p; + + pr_debug(" -> %s:%d: match_id %u, port %u\n", __func__, __LINE__, + match_id, port_number); + + p = kzalloc(sizeof(struct layout), GFP_KERNEL); + + if (!p) + return -ENOMEM; + + p->dev.match_id = match_id; + p->dev.dev_type = PS3_DEVICE_TYPE_VUART; + p->dev.port_number = port_number; + + result = ps3_system_bus_device_register(&p->dev); + + if (result) + pr_debug("%s:%d ps3_system_bus_device_register failed\n", + __func__, __LINE__); + + pr_debug(" <- %s:%d\n", __func__, __LINE__); + return result; +} + +static int ps3stor_wait_for_completion(u64 dev_id, u64 tag, + unsigned int timeout) +{ + int result = -1; + unsigned int retries = 0; + u64 status; + + for (retries = 0; retries < timeout; retries++) { + result = lv1_storage_check_async_status(dev_id, tag, &status); + if (!result) + break; + + msleep(1); + } + + if (result) + pr_debug("%s:%u: check_async_status: %s, status %lx\n", + __func__, __LINE__, ps3_result(result), status); + + return result; +} + +/** + * ps3_storage_wait_for_device - Wait for a storage device to become ready. + * @repo: The repository device to wait for. + * + * Uses the hypervisor's storage device notification mechanism to wait until + * a storage device is ready. The device notification mechanism uses a + * psuedo device (id = -1) to asynchronously notify the guest when storage + * devices become ready. The notification device has a block size of 512 + * bytes. + */ + +static int ps3_storage_wait_for_device(const struct ps3_repository_device *repo) +{ + int result; + const u64 notification_dev_id = (u64)-1LL; + const unsigned int timeout = HZ; + u64 lpar; + u64 tag; + struct { + u64 operation_code; /* must be zero */ + u64 event_mask; /* 1 = device ready */ + } *notify_cmd; + struct { + u64 event_type; /* notify_device_ready */ + u64 bus_id; + u64 dev_id; + u64 dev_type; + u64 dev_port; + } *notify_event; + enum { + notify_device_ready = 1 + }; + + pr_debug(" -> %s:%u: bus_id %u, dev_id %u, dev_type %u\n", __func__, + __LINE__, repo->bus_id, repo->dev_id, repo->dev_type); + + notify_cmd = kzalloc(512, GFP_KERNEL); + notify_event = (void *)notify_cmd; + if (!notify_cmd) + return -ENOMEM; + + lpar = ps3_mm_phys_to_lpar(__pa(notify_cmd)); + + result = lv1_open_device(repo->bus_id, notification_dev_id, 0); + if (result) { + printk(KERN_ERR "%s:%u: lv1_open_device %s\n", __func__, + __LINE__, ps3_result(result)); + result = -ENODEV; + goto fail_free; + } + + /* Setup and write the request for device notification. */ + + notify_cmd->operation_code = 0; /* must be zero */ + notify_cmd->event_mask = 0x01; /* device ready */ + + result = lv1_storage_write(notification_dev_id, 0, 0, 1, 0, lpar, + &tag); + if (result) { + printk(KERN_ERR "%s:%u: write failed %s\n", __func__, __LINE__, + ps3_result(result)); + result = -ENODEV; + goto fail_close; + } + + /* Wait for the write completion */ + + result = ps3stor_wait_for_completion(notification_dev_id, tag, + timeout); + if (result) { + printk(KERN_ERR "%s:%u: write not completed %s\n", __func__, + __LINE__, ps3_result(result)); + result = -ENODEV; + goto fail_close; + } + + /* Loop here processing the requested notification events. */ + + result = -ENODEV; + while (1) { + memset(notify_event, 0, sizeof(*notify_event)); + + result = lv1_storage_read(notification_dev_id, 0, 0, 1, 0, + lpar, &tag); + if (result) { + printk(KERN_ERR "%s:%u: write failed %s\n", __func__, + __LINE__, ps3_result(result)); + break; + } + + result = ps3stor_wait_for_completion(notification_dev_id, tag, + timeout); + if (result) { + printk(KERN_ERR "%s:%u: read not completed %s\n", + __func__, __LINE__, ps3_result(result)); + break; + } + + if (notify_event->event_type != notify_device_ready || + notify_event->bus_id != repo->bus_id) { + pr_debug("%s:%u: bad notify_event: event %lu, " + "dev_id %lu, dev_type %lu\n", + __func__, __LINE__, notify_event->event_type, + notify_event->dev_id, notify_event->dev_type); + break; + } + + if (notify_event->dev_id == repo->dev_id && + notify_event->dev_type == repo->dev_type) { + pr_debug("%s:%u: device ready: dev_id %u\n", __func__, + __LINE__, repo->dev_id); + result = 0; + break; + } + + if (notify_event->dev_id == repo->dev_id && + notify_event->dev_type == PS3_DEV_TYPE_NOACCESS) { + pr_debug("%s:%u: no access: dev_id %u\n", __func__, + __LINE__, repo->dev_id); + break; + } + } + +fail_close: + lv1_close_device(repo->bus_id, notification_dev_id); +fail_free: + kfree(notify_cmd); + pr_debug(" <- %s:%u\n", __func__, __LINE__); + return result; +} + +static int ps3_setup_storage_dev(const struct ps3_repository_device *repo, + enum ps3_match_id match_id) +{ + int result; + struct ps3_storage_device *p; + u64 port, blk_size, num_blocks; + unsigned int num_regions, i; + + pr_debug(" -> %s:%u: match_id %u\n", __func__, __LINE__, match_id); + + result = ps3_repository_read_stor_dev_info(repo->bus_index, + repo->dev_index, &port, + &blk_size, &num_blocks, + &num_regions); + if (result) { + printk(KERN_ERR "%s:%u: _read_stor_dev_info failed %d\n", + __func__, __LINE__, result); + return -ENODEV; + } + + pr_debug("%s:%u: index %u:%u: port %lu blk_size %lu num_blocks %lu " + "num_regions %u\n", __func__, __LINE__, repo->bus_index, + repo->dev_index, port, blk_size, num_blocks, num_regions); + + p = kzalloc(sizeof(struct ps3_storage_device) + + num_regions * sizeof(struct ps3_storage_region), + GFP_KERNEL); + if (!p) { + result = -ENOMEM; + goto fail_malloc; + } + + p->sbd.match_id = match_id; + p->sbd.dev_type = PS3_DEVICE_TYPE_SB; + p->sbd.bus_id = repo->bus_id; + p->sbd.dev_id = repo->dev_id; + p->sbd.d_region = &p->dma_region; + p->blk_size = blk_size; + p->num_regions = num_regions; + + result = ps3_repository_find_interrupt(repo, + PS3_INTERRUPT_TYPE_EVENT_PORT, + &p->sbd.interrupt_id); + if (result) { + printk(KERN_ERR "%s:%u: find_interrupt failed %d\n", __func__, + __LINE__, result); + result = -ENODEV; + goto fail_find_interrupt; + } + + /* FIXME: Arrange to only do this on a 'cold' boot */ + + result = ps3_storage_wait_for_device(repo); + if (result) { + printk(KERN_ERR "%s:%u: storage_notification failed %d\n", + __func__, __LINE__, result); + result = -ENODEV; + goto fail_probe_notification; + } + + for (i = 0; i < num_regions; i++) { + unsigned int id; + u64 start, size; + + result = ps3_repository_read_stor_dev_region(repo->bus_index, + repo->dev_index, + i, &id, &start, + &size); + if (result) { + printk(KERN_ERR + "%s:%u: read_stor_dev_region failed %d\n", + __func__, __LINE__, result); + result = -ENODEV; + goto fail_read_region; + } + pr_debug("%s:%u: region %u: id %u start %lu size %lu\n", + __func__, __LINE__, i, id, start, size); + + p->regions[i].id = id; + p->regions[i].start = start; + p->regions[i].size = size; + } + + result = ps3_system_bus_device_register(&p->sbd); + if (result) { + pr_debug("%s:%u ps3_system_bus_device_register failed\n", + __func__, __LINE__); + goto fail_device_register; + } + + pr_debug(" <- %s:%u\n", __func__, __LINE__); + return 0; + +fail_device_register: +fail_read_region: +fail_probe_notification: +fail_find_interrupt: + kfree(p); +fail_malloc: + pr_debug(" <- %s:%u: fail.\n", __func__, __LINE__); + return result; +} + +static int __init ps3_register_vuart_devices(void) +{ + int result; + unsigned int port_number; + + pr_debug(" -> %s:%d\n", __func__, __LINE__); + + result = ps3_repository_read_vuart_av_port(&port_number); + if (result) + port_number = 0; /* av default */ + + result = ps3_setup_vuart_device(PS3_MATCH_ID_AV_SETTINGS, port_number); + WARN_ON(result); + + result = ps3_repository_read_vuart_sysmgr_port(&port_number); + if (result) + port_number = 2; /* sysmgr default */ + + result = ps3_setup_vuart_device(PS3_MATCH_ID_SYSTEM_MANAGER, + port_number); + WARN_ON(result); + + pr_debug(" <- %s:%d\n", __func__, __LINE__); + return result; +} + +static int __init ps3_register_sound_devices(void) +{ + int result; + struct layout { + struct ps3_system_bus_device dev; + struct ps3_dma_region d_region; + struct ps3_mmio_region m_region; + } *p; + + pr_debug(" -> %s:%d\n", __func__, __LINE__); + + p = kzalloc(sizeof(*p), GFP_KERNEL); + if (!p) + return -ENOMEM; + + p->dev.match_id = PS3_MATCH_ID_SOUND; + p->dev.dev_type = PS3_DEVICE_TYPE_IOC0; + p->dev.d_region = &p->d_region; + p->dev.m_region = &p->m_region; + + result = ps3_system_bus_device_register(&p->dev); + + if (result) + pr_debug("%s:%d ps3_system_bus_device_register failed\n", + __func__, __LINE__); + + pr_debug(" <- %s:%d\n", __func__, __LINE__); + return result; +} + +static int __init ps3_register_graphics_devices(void) +{ + int result; + struct layout { + struct ps3_system_bus_device dev; + } *p; + + pr_debug(" -> %s:%d\n", __func__, __LINE__); + + p = kzalloc(sizeof(struct layout), GFP_KERNEL); + + if (!p) + return -ENOMEM; + + p->dev.match_id = PS3_MATCH_ID_GRAPHICS; + p->dev.dev_type = PS3_DEVICE_TYPE_IOC0; + + result = ps3_system_bus_device_register(&p->dev); + + if (result) + pr_debug("%s:%d ps3_system_bus_device_register failed\n", + __func__, __LINE__); + + pr_debug(" <- %s:%d\n", __func__, __LINE__); + return result; +} + +/** + * ps3_register_repository_device - Register a device from the repositiory info. + * + */ + +static int ps3_register_repository_device( + const struct ps3_repository_device *repo) +{ + int result; + + switch (repo->dev_type) { + case PS3_DEV_TYPE_SB_GELIC: + result = ps3_setup_gelic_device(repo); + if (result) { + pr_debug("%s:%d ps3_setup_gelic_device failed\n", + __func__, __LINE__); + } + break; + case PS3_DEV_TYPE_SB_USB: + + /* Each USB device has both an EHCI and an OHCI HC */ + + result = ps3_setup_ehci_device(repo); + + if (result) { + pr_debug("%s:%d ps3_setup_ehci_device failed\n", + __func__, __LINE__); + } + + result = ps3_setup_ohci_device(repo); + + if (result) { + pr_debug("%s:%d ps3_setup_ohci_device failed\n", + __func__, __LINE__); + } + break; + case PS3_DEV_TYPE_STOR_DISK: + result = ps3_setup_storage_dev(repo, PS3_MATCH_ID_STOR_DISK); + + /* Some devices are not accessable from the Other OS lpar. */ + if (result == -ENODEV) { + result = 0; + pr_debug("%s:%u: not accessable\n", __func__, + __LINE__); + } + + if (result) + pr_debug("%s:%u ps3_setup_storage_dev failed\n", + __func__, __LINE__); + break; + + case PS3_DEV_TYPE_STOR_ROM: + result = ps3_setup_storage_dev(repo, PS3_MATCH_ID_STOR_ROM); + if (result) + pr_debug("%s:%u ps3_setup_storage_dev failed\n", + __func__, __LINE__); + break; + + case PS3_DEV_TYPE_STOR_FLASH: + result = ps3_setup_storage_dev(repo, PS3_MATCH_ID_STOR_FLASH); + if (result) + pr_debug("%s:%u ps3_setup_storage_dev failed\n", + __func__, __LINE__); + break; + + default: + result = 0; + pr_debug("%s:%u: unsupported dev_type %u\n", __func__, __LINE__, + repo->dev_type); + } + + return result; +} + +/** + * ps3_probe_thread - Background repository probing at system startup. + * + * This implementation only supports background probing on a single bus. + */ + +static int ps3_probe_thread(void *data) +{ + struct ps3_repository_device *repo = data; + int result; + unsigned int ms = 250; + + pr_debug(" -> %s:%u: kthread started\n", __func__, __LINE__); + + do { + try_to_freeze(); + + pr_debug("%s:%u: probing...\n", __func__, __LINE__); + + do { + result = ps3_repository_find_device(repo); + + if (result == -ENODEV) + pr_debug("%s:%u: nothing new\n", __func__, + __LINE__); + else if (result) + pr_debug("%s:%u: find device error.\n", + __func__, __LINE__); + else { + pr_debug("%s:%u: found device\n", __func__, + __LINE__); + ps3_register_repository_device(repo); + ps3_repository_bump_device(repo); + ms = 250; + } + } while (!result); + + pr_debug("%s:%u: ms %u\n", __func__, __LINE__, ms); + + if ( ms > 60000) + break; + + msleep_interruptible(ms); + + /* An exponential backoff. */ + ms <<= 1; + + } while (!kthread_should_stop()); + + pr_debug(" <- %s:%u: kthread finished\n", __func__, __LINE__); + + return 0; +} + +/** + * ps3_start_probe_thread - Starts the background probe thread. + * + */ + +static int __init ps3_start_probe_thread(enum ps3_bus_type bus_type) +{ + int result; + struct task_struct *task; + static struct ps3_repository_device repo; /* must be static */ + + pr_debug(" -> %s:%d\n", __func__, __LINE__); + + memset(&repo, 0, sizeof(repo)); + + repo.bus_type = bus_type; + + result = ps3_repository_find_bus(repo.bus_type, 0, &repo.bus_index); + + if (result) { + printk(KERN_ERR "%s: Cannot find bus (%d)\n", __func__, result); + return -ENODEV; + } + + result = ps3_repository_read_bus_id(repo.bus_index, &repo.bus_id); + + if (result) { + printk(KERN_ERR "%s: read_bus_id failed %d\n", __func__, + result); + return -ENODEV; + } + + task = kthread_run(ps3_probe_thread, &repo, "ps3-probe-%u", bus_type); + + if (IS_ERR(task)) { + result = PTR_ERR(task); + printk(KERN_ERR "%s: kthread_run failed %d\n", __func__, + result); + return result; + } + + pr_debug(" <- %s:%d\n", __func__, __LINE__); + return 0; +} + +/** + * ps3_register_devices - Probe the system and register devices found. + * + * A device_initcall() routine. + */ + +static int __init ps3_register_devices(void) +{ + int result; + + if (!firmware_has_feature(FW_FEATURE_PS3_LV1)) + return -ENODEV; + + pr_debug(" -> %s:%d\n", __func__, __LINE__); + + /* ps3_repository_dump_bus_info(); */ + + result = ps3_start_probe_thread(PS3_BUS_TYPE_STORAGE); + + ps3_register_vuart_devices(); + + ps3_register_graphics_devices(); + + ps3_repository_find_devices(PS3_BUS_TYPE_SB, + ps3_register_repository_device); + + ps3_register_sound_devices(); + + pr_debug(" <- %s:%d\n", __func__, __LINE__); + return 0; +} + +device_initcall(ps3_register_devices); diff --git a/arch/powerpc/platforms/ps3/htab.c b/arch/powerpc/platforms/ps3/htab.c index a1409e450c70..5d2e176a1b18 100644 --- a/arch/powerpc/platforms/ps3/htab.c +++ b/arch/powerpc/platforms/ps3/htab.c @@ -29,12 +29,12 @@ #include "platform.h" #if defined(DEBUG) -#define DBG(fmt...) udbg_printf(fmt) +#define DBG udbg_printf #else -#define DBG(fmt...) do{if(0)printk(fmt);}while(0) +#define DBG pr_debug #endif -static hpte_t *htab; +static struct hash_pte *htab; static unsigned long htab_addr; static unsigned char *bolttab; static unsigned char *inusetab; @@ -44,8 +44,8 @@ static DEFINE_SPINLOCK(ps3_bolttab_lock); #define debug_dump_hpte(_a, _b, _c, _d, _e, _f, _g) \ _debug_dump_hpte(_a, _b, _c, _d, _e, _f, _g, __func__, __LINE__) static void _debug_dump_hpte(unsigned long pa, unsigned long va, - unsigned long group, unsigned long bitmap, hpte_t lhpte, int psize, - unsigned long slot, const char* func, int line) + unsigned long group, unsigned long bitmap, struct hash_pte lhpte, + int psize, unsigned long slot, const char* func, int line) { DBG("%s:%d: pa = %lxh\n", func, line, pa); DBG("%s:%d: lpar = %lxh\n", func, line, @@ -63,7 +63,7 @@ static long ps3_hpte_insert(unsigned long hpte_group, unsigned long va, unsigned long pa, unsigned long rflags, unsigned long vflags, int psize) { unsigned long slot; - hpte_t lhpte; + struct hash_pte lhpte; int secondary = 0; unsigned long result; unsigned long bitmap; @@ -234,10 +234,17 @@ static void ps3_hpte_invalidate(unsigned long slot, unsigned long va, static void ps3_hpte_clear(void) { - /* Make sure to clean up the frame buffer device first */ - ps3fb_cleanup(); + int result; - lv1_unmap_htab(htab_addr); + DBG(" -> %s:%d\n", __func__, __LINE__); + + result = lv1_unmap_htab(htab_addr); + BUG_ON(result); + + ps3_mm_shutdown(); + ps3_mm_vas_destroy(); + + DBG(" <- %s:%d\n", __func__, __LINE__); } void __init ps3_hpte_init(unsigned long htab_size) @@ -255,7 +262,7 @@ void __init ps3_hpte_init(unsigned long htab_size) ppc64_pft_size = __ilog2(htab_size); - bitmap_size = htab_size / sizeof(hpte_t) / 8; + bitmap_size = htab_size / sizeof(struct hash_pte) / 8; bolttab = __va(lmb_alloc(bitmap_size, 1)); inusetab = __va(lmb_alloc(bitmap_size, 1)); @@ -273,8 +280,8 @@ void __init ps3_map_htab(void) result = lv1_map_htab(0, &htab_addr); - htab = (hpte_t *)__ioremap(htab_addr, htab_size, - pgprot_val(PAGE_READONLY_X)); + htab = (__force struct hash_pte *)ioremap_flags(htab_addr, htab_size, + pgprot_val(PAGE_READONLY_X)); DBG("%s:%d: lpar %016lxh, virt %016lxh\n", __func__, __LINE__, htab_addr, (unsigned long)htab); diff --git a/arch/powerpc/platforms/ps3/interrupt.c b/arch/powerpc/platforms/ps3/interrupt.c index ec9030dbb5f1..67e32ec9b37e 100644 --- a/arch/powerpc/platforms/ps3/interrupt.c +++ b/arch/powerpc/platforms/ps3/interrupt.c @@ -30,9 +30,9 @@ #include "platform.h" #if defined(DEBUG) -#define DBG(fmt...) udbg_printf(fmt) +#define DBG udbg_printf #else -#define DBG(fmt...) do{if(0)printk(fmt);}while(0) +#define DBG pr_debug #endif /** @@ -78,18 +78,84 @@ struct ps3_bmp { /** * struct ps3_private - a per cpu data structure * @bmp: ps3_bmp structure - * @node: HV logical_ppe_id - * @cpu: HV thread_id + * @ppe_id: HV logical_ppe_id + * @thread_id: HV thread_id */ struct ps3_private { struct ps3_bmp bmp __attribute__ ((aligned (PS3_BMP_MINALIGN))); - u64 node; - unsigned int cpu; + u64 ppe_id; + u64 thread_id; }; static DEFINE_PER_CPU(struct ps3_private, ps3_private); +/** + * ps3_chip_mask - Set an interrupt mask bit in ps3_bmp. + * @virq: The assigned Linux virq. + * + * Sets ps3_bmp.mask and calls lv1_did_update_interrupt_mask(). + */ + +static void ps3_chip_mask(unsigned int virq) +{ + struct ps3_private *pd = get_irq_chip_data(virq); + unsigned long flags; + + pr_debug("%s:%d: thread_id %lu, virq %d\n", __func__, __LINE__, + pd->thread_id, virq); + + local_irq_save(flags); + clear_bit(63 - virq, &pd->bmp.mask); + lv1_did_update_interrupt_mask(pd->ppe_id, pd->thread_id); + local_irq_restore(flags); +} + +/** + * ps3_chip_unmask - Clear an interrupt mask bit in ps3_bmp. + * @virq: The assigned Linux virq. + * + * Clears ps3_bmp.mask and calls lv1_did_update_interrupt_mask(). + */ + +static void ps3_chip_unmask(unsigned int virq) +{ + struct ps3_private *pd = get_irq_chip_data(virq); + unsigned long flags; + + pr_debug("%s:%d: thread_id %lu, virq %d\n", __func__, __LINE__, + pd->thread_id, virq); + + local_irq_save(flags); + set_bit(63 - virq, &pd->bmp.mask); + lv1_did_update_interrupt_mask(pd->ppe_id, pd->thread_id); + local_irq_restore(flags); +} + +/** + * ps3_chip_eoi - HV end-of-interrupt. + * @virq: The assigned Linux virq. + * + * Calls lv1_end_of_interrupt_ext(). + */ + +static void ps3_chip_eoi(unsigned int virq) +{ + const struct ps3_private *pd = get_irq_chip_data(virq); + lv1_end_of_interrupt_ext(pd->ppe_id, pd->thread_id, virq); +} + +/** + * ps3_irq_chip - Represents the ps3_bmp as a Linux struct irq_chip. + */ + +static struct irq_chip ps3_irq_chip = { + .typename = "ps3", + .mask = ps3_chip_mask, + .unmask = ps3_chip_unmask, + .eoi = ps3_chip_eoi, +}; + /** * ps3_virq_setup - virq related setup. * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be @@ -134,6 +200,8 @@ int ps3_virq_setup(enum ps3_cpu_binding cpu, unsigned long outlet, goto fail_set; } + ps3_chip_mask(*virq); + return result; fail_set: @@ -153,8 +221,8 @@ int ps3_virq_destroy(unsigned int virq) { const struct ps3_private *pd = get_irq_chip_data(virq); - pr_debug("%s:%d: node %lu, cpu %d, virq %u\n", __func__, __LINE__, - pd->node, pd->cpu, virq); + pr_debug("%s:%d: ppe_id %lu, thread_id %lu, virq %u\n", __func__, + __LINE__, pd->ppe_id, pd->thread_id, virq); set_irq_chip_data(virq, NULL); irq_dispose_mapping(virq); @@ -190,7 +258,8 @@ int ps3_irq_plug_setup(enum ps3_cpu_binding cpu, unsigned long outlet, /* Binds outlet to cpu + virq. */ - result = lv1_connect_irq_plug_ext(pd->node, pd->cpu, *virq, outlet, 0); + result = lv1_connect_irq_plug_ext(pd->ppe_id, pd->thread_id, *virq, + outlet, 0); if (result) { pr_info("%s:%d: lv1_connect_irq_plug_ext failed: %s\n", @@ -222,10 +291,12 @@ int ps3_irq_plug_destroy(unsigned int virq) int result; const struct ps3_private *pd = get_irq_chip_data(virq); - pr_debug("%s:%d: node %lu, cpu %d, virq %u\n", __func__, __LINE__, - pd->node, pd->cpu, virq); + pr_debug("%s:%d: ppe_id %lu, thread_id %lu, virq %u\n", __func__, + __LINE__, pd->ppe_id, pd->thread_id, virq); - result = lv1_disconnect_irq_plug_ext(pd->node, pd->cpu, virq); + ps3_chip_mask(virq); + + result = lv1_disconnect_irq_plug_ext(pd->ppe_id, pd->thread_id, virq); if (result) pr_info("%s:%d: lv1_disconnect_irq_plug_ext failed: %s\n", @@ -282,7 +353,9 @@ int ps3_event_receive_port_destroy(unsigned int virq) { int result; - pr_debug(" -> %s:%d virq: %u\n", __func__, __LINE__, virq); + pr_debug(" -> %s:%d virq %u\n", __func__, __LINE__, virq); + + ps3_chip_mask(virq); result = lv1_destruct_event_receive_port(virq_to_hw(virq)); @@ -290,17 +363,14 @@ int ps3_event_receive_port_destroy(unsigned int virq) pr_debug("%s:%d: lv1_destruct_event_receive_port failed: %s\n", __func__, __LINE__, ps3_result(result)); - /* lv1_destruct_event_receive_port() destroys the IRQ plug, - * so don't call ps3_irq_plug_destroy() here. + /* + * Don't call ps3_virq_destroy() here since ps3_smp_cleanup_cpu() + * calls from interrupt context (smp_call_function) when kexecing. */ - result = ps3_virq_destroy(virq); - BUG_ON(result); - pr_debug(" <- %s:%d\n", __func__, __LINE__); return result; } -EXPORT_SYMBOL_GPL(ps3_event_receive_port_destroy); int ps3_send_event_locally(unsigned int virq) { @@ -311,17 +381,15 @@ int ps3_send_event_locally(unsigned int virq) * ps3_sb_event_receive_port_setup - Setup a system bus event receive port. * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be * serviced on. - * @did: The HV device identifier read from the system repository. - * @interrupt_id: The device interrupt id read from the system repository. + * @dev: The system bus device instance. * @virq: The assigned Linux virq. * * An event irq represents a virtual device interrupt. The interrupt_id * coresponds to the software interrupt number. */ -int ps3_sb_event_receive_port_setup(enum ps3_cpu_binding cpu, - const struct ps3_device_id *did, unsigned int interrupt_id, - unsigned int *virq) +int ps3_sb_event_receive_port_setup(struct ps3_system_bus_device *dev, + enum ps3_cpu_binding cpu, unsigned int *virq) { /* this should go in system-bus.c */ @@ -332,8 +400,8 @@ int ps3_sb_event_receive_port_setup(enum ps3_cpu_binding cpu, if (result) return result; - result = lv1_connect_interrupt_event_receive_port(did->bus_id, - did->dev_id, virq_to_hw(*virq), interrupt_id); + result = lv1_connect_interrupt_event_receive_port(dev->bus_id, + dev->dev_id, virq_to_hw(*virq), dev->interrupt_id); if (result) { pr_debug("%s:%d: lv1_connect_interrupt_event_receive_port" @@ -345,24 +413,24 @@ int ps3_sb_event_receive_port_setup(enum ps3_cpu_binding cpu, } pr_debug("%s:%d: interrupt_id %u, virq %u\n", __func__, __LINE__, - interrupt_id, *virq); + dev->interrupt_id, *virq); return 0; } EXPORT_SYMBOL(ps3_sb_event_receive_port_setup); -int ps3_sb_event_receive_port_destroy(const struct ps3_device_id *did, - unsigned int interrupt_id, unsigned int virq) +int ps3_sb_event_receive_port_destroy(struct ps3_system_bus_device *dev, + unsigned int virq) { /* this should go in system-bus.c */ int result; pr_debug(" -> %s:%d: interrupt_id %u, virq %u\n", __func__, __LINE__, - interrupt_id, virq); + dev->interrupt_id, virq); - result = lv1_disconnect_interrupt_event_receive_port(did->bus_id, - did->dev_id, virq_to_hw(virq), interrupt_id); + result = lv1_disconnect_interrupt_event_receive_port(dev->bus_id, + dev->dev_id, virq_to_hw(virq), dev->interrupt_id); if (result) pr_debug("%s:%d: lv1_disconnect_interrupt_event_receive_port" @@ -372,6 +440,14 @@ int ps3_sb_event_receive_port_destroy(const struct ps3_device_id *did, result = ps3_event_receive_port_destroy(virq); BUG_ON(result); + /* + * ps3_event_receive_port_destroy() destroys the IRQ plug, + * so don't call ps3_irq_plug_destroy() here. + */ + + result = ps3_virq_destroy(virq); + BUG_ON(result); + pr_debug(" <- %s:%d\n", __func__, __LINE__); return result; } @@ -412,16 +488,24 @@ EXPORT_SYMBOL_GPL(ps3_io_irq_setup); int ps3_io_irq_destroy(unsigned int virq) { int result; + unsigned long outlet = virq_to_hw(virq); - result = lv1_destruct_io_irq_outlet(virq_to_hw(virq)); + ps3_chip_mask(virq); + + /* + * lv1_destruct_io_irq_outlet() will destroy the IRQ plug, + * so call ps3_irq_plug_destroy() first. + */ + + result = ps3_irq_plug_destroy(virq); + BUG_ON(result); + + result = lv1_destruct_io_irq_outlet(outlet); if (result) pr_debug("%s:%d: lv1_destruct_io_irq_outlet failed: %s\n", __func__, __LINE__, ps3_result(result)); - result = ps3_irq_plug_destroy(virq); - BUG_ON(result); - return result; } EXPORT_SYMBOL_GPL(ps3_io_irq_destroy); @@ -461,11 +545,13 @@ int ps3_vuart_irq_setup(enum ps3_cpu_binding cpu, void* virt_addr_bmp, return result; } +EXPORT_SYMBOL_GPL(ps3_vuart_irq_setup); int ps3_vuart_irq_destroy(unsigned int virq) { int result; + ps3_chip_mask(virq); result = lv1_deconfigure_virtual_uart_irq(); if (result) { @@ -479,6 +565,7 @@ int ps3_vuart_irq_destroy(unsigned int virq) return result; } +EXPORT_SYMBOL_GPL(ps3_vuart_irq_destroy); /** * ps3_spe_irq_setup - Setup an spe virq. @@ -514,9 +601,14 @@ int ps3_spe_irq_setup(enum ps3_cpu_binding cpu, unsigned long spe_id, int ps3_spe_irq_destroy(unsigned int virq) { - int result = ps3_irq_plug_destroy(virq); + int result; + + ps3_chip_mask(virq); + + result = ps3_irq_plug_destroy(virq); BUG_ON(result); - return 0; + + return result; } @@ -533,7 +625,7 @@ static void _dump_64_bmp(const char *header, const u64 *p, unsigned cpu, *p & 0xffff); } -static void __attribute__ ((unused)) _dump_256_bmp(const char *header, +static void __maybe_unused _dump_256_bmp(const char *header, const u64 *p, unsigned cpu, const char* func, int line) { pr_debug("%s:%d: %s %u {%016lx:%016lx:%016lx:%016lx}\n", @@ -546,86 +638,25 @@ static void _dump_bmp(struct ps3_private* pd, const char* func, int line) unsigned long flags; spin_lock_irqsave(&pd->bmp.lock, flags); - _dump_64_bmp("stat", &pd->bmp.status, pd->cpu, func, line); - _dump_64_bmp("mask", &pd->bmp.mask, pd->cpu, func, line); + _dump_64_bmp("stat", &pd->bmp.status, pd->thread_id, func, line); + _dump_64_bmp("mask", &pd->bmp.mask, pd->thread_id, func, line); spin_unlock_irqrestore(&pd->bmp.lock, flags); } #define dump_mask(_x) _dump_mask(_x, __func__, __LINE__) -static void __attribute__ ((unused)) _dump_mask(struct ps3_private* pd, +static void __maybe_unused _dump_mask(struct ps3_private *pd, const char* func, int line) { unsigned long flags; spin_lock_irqsave(&pd->bmp.lock, flags); - _dump_64_bmp("mask", &pd->bmp.mask, pd->cpu, func, line); + _dump_64_bmp("mask", &pd->bmp.mask, pd->thread_id, func, line); spin_unlock_irqrestore(&pd->bmp.lock, flags); } #else static void dump_bmp(struct ps3_private* pd) {}; #endif /* defined(DEBUG) */ -static void ps3_chip_mask(unsigned int virq) -{ - struct ps3_private *pd = get_irq_chip_data(virq); - u64 bit = 0x8000000000000000UL >> virq; - u64 *p = &pd->bmp.mask; - u64 old; - unsigned long flags; - - pr_debug("%s:%d: cpu %u, virq %d\n", __func__, __LINE__, pd->cpu, virq); - - local_irq_save(flags); - asm volatile( - "1: ldarx %0,0,%3\n" - "andc %0,%0,%2\n" - "stdcx. %0,0,%3\n" - "bne- 1b" - : "=&r" (old), "+m" (*p) - : "r" (bit), "r" (p) - : "cc" ); - - lv1_did_update_interrupt_mask(pd->node, pd->cpu); - local_irq_restore(flags); -} - -static void ps3_chip_unmask(unsigned int virq) -{ - struct ps3_private *pd = get_irq_chip_data(virq); - u64 bit = 0x8000000000000000UL >> virq; - u64 *p = &pd->bmp.mask; - u64 old; - unsigned long flags; - - pr_debug("%s:%d: cpu %u, virq %d\n", __func__, __LINE__, pd->cpu, virq); - - local_irq_save(flags); - asm volatile( - "1: ldarx %0,0,%3\n" - "or %0,%0,%2\n" - "stdcx. %0,0,%3\n" - "bne- 1b" - : "=&r" (old), "+m" (*p) - : "r" (bit), "r" (p) - : "cc" ); - - lv1_did_update_interrupt_mask(pd->node, pd->cpu); - local_irq_restore(flags); -} - -static void ps3_chip_eoi(unsigned int virq) -{ - const struct ps3_private *pd = get_irq_chip_data(virq); - lv1_end_of_interrupt_ext(pd->node, pd->cpu, virq); -} - -static struct irq_chip irq_chip = { - .typename = "ps3", - .mask = ps3_chip_mask, - .unmask = ps3_chip_unmask, - .eoi = ps3_chip_eoi, -}; - static void ps3_host_unmap(struct irq_host *h, unsigned int virq) { set_irq_chip_data(virq, NULL); @@ -637,7 +668,7 @@ static int ps3_host_map(struct irq_host *h, unsigned int virq, pr_debug("%s:%d: hwirq %lu, virq %u\n", __func__, __LINE__, hwirq, virq); - set_irq_chip_and_handler(virq, &irq_chip, handle_fasteoi_irq); + set_irq_chip_and_handler(virq, &ps3_irq_chip, handle_fasteoi_irq); return 0; } @@ -657,7 +688,7 @@ void __init ps3_register_ipi_debug_brk(unsigned int cpu, unsigned int virq) cpu, virq, pd->bmp.ipi_debug_brk_mask); } -unsigned int ps3_get_irq(void) +static unsigned int ps3_get_irq(void) { struct ps3_private *pd = &__get_cpu_var(ps3_private); u64 x = (pd->bmp.status & pd->bmp.mask); @@ -672,8 +703,8 @@ unsigned int ps3_get_irq(void) plug &= 0x3f; if (unlikely(plug) == NO_IRQ) { - pr_debug("%s:%d: no plug found: cpu %u\n", __func__, __LINE__, - pd->cpu); + pr_debug("%s:%d: no plug found: thread_id %lu\n", __func__, + __LINE__, pd->thread_id); dump_bmp(&per_cpu(ps3_private, 0)); dump_bmp(&per_cpu(ps3_private, 1)); return NO_IRQ; @@ -703,16 +734,16 @@ void __init ps3_init_IRQ(void) for_each_possible_cpu(cpu) { struct ps3_private *pd = &per_cpu(ps3_private, cpu); - lv1_get_logical_ppe_id(&pd->node); - pd->cpu = get_hard_smp_processor_id(cpu); + lv1_get_logical_ppe_id(&pd->ppe_id); + pd->thread_id = get_hard_smp_processor_id(cpu); spin_lock_init(&pd->bmp.lock); - pr_debug("%s:%d: node %lu, cpu %d, bmp %lxh\n", __func__, - __LINE__, pd->node, pd->cpu, + pr_debug("%s:%d: ppe_id %lu, thread_id %lu, bmp %lxh\n", + __func__, __LINE__, pd->ppe_id, pd->thread_id, ps3_mm_phys_to_lpar(__pa(&pd->bmp))); - result = lv1_configure_irq_state_bitmap(pd->node, pd->cpu, - ps3_mm_phys_to_lpar(__pa(&pd->bmp))); + result = lv1_configure_irq_state_bitmap(pd->ppe_id, + pd->thread_id, ps3_mm_phys_to_lpar(__pa(&pd->bmp))); if (result) pr_debug("%s:%d: lv1_configure_irq_state_bitmap failed:" @@ -722,3 +753,16 @@ void __init ps3_init_IRQ(void) ppc_md.get_irq = ps3_get_irq; } + +void ps3_shutdown_IRQ(int cpu) +{ + int result; + u64 ppe_id; + u64 thread_id = get_hard_smp_processor_id(cpu); + + lv1_get_logical_ppe_id(&ppe_id); + result = lv1_configure_irq_state_bitmap(ppe_id, thread_id, 0); + + DBG("%s:%d: lv1_configure_irq_state_bitmap (%lu:%lu/%d) %s\n", __func__, + __LINE__, ppe_id, thread_id, cpu, ps3_result(result)); +} diff --git a/arch/powerpc/platforms/ps3/mm.c b/arch/powerpc/platforms/ps3/mm.c index f8a3e206c584..7bb3e1620974 100644 --- a/arch/powerpc/platforms/ps3/mm.c +++ b/arch/powerpc/platforms/ps3/mm.c @@ -30,9 +30,9 @@ #include "platform.h" #if defined(DEBUG) -#define DBG(fmt...) udbg_printf(fmt) +#define DBG udbg_printf #else -#define DBG(fmt...) do{if(0)printk(fmt);}while(0) +#define DBG pr_debug #endif enum { @@ -115,7 +115,8 @@ struct map { }; #define debug_dump_map(x) _debug_dump_map(x, __func__, __LINE__) -static void _debug_dump_map(const struct map* m, const char* func, int line) +static void __maybe_unused _debug_dump_map(const struct map *m, + const char *func, int line) { DBG("%s:%d: map.total = %lxh\n", func, line, m->total); DBG("%s:%d: map.rm.size = %lxh\n", func, line, m->rm.size); @@ -212,9 +213,15 @@ fail: void ps3_mm_vas_destroy(void) { + int result; + + DBG("%s:%d: map.vas_id = %lu\n", __func__, __LINE__, map.vas_id); + if (map.vas_id) { - lv1_select_virtual_address_space(0); - lv1_destruct_virtual_address_space(map.vas_id); + result = lv1_select_virtual_address_space(0); + BUG_ON(result); + result = lv1_destruct_virtual_address_space(map.vas_id); + BUG_ON(result); map.vas_id = 0; } } @@ -232,7 +239,7 @@ void ps3_mm_vas_destroy(void) * @size is rounded down to a multiple of the vas large page size. */ -int ps3_mm_region_create(struct mem_region *r, unsigned long size) +static int ps3_mm_region_create(struct mem_region *r, unsigned long size) { int result; unsigned long muid; @@ -273,10 +280,14 @@ zero_region: * @r: pointer to struct mem_region */ -void ps3_mm_region_destroy(struct mem_region *r) +static void ps3_mm_region_destroy(struct mem_region *r) { + int result; + + DBG("%s:%d: r->base = %lxh\n", __func__, __LINE__, r->base); if (r->base) { - lv1_release_memory(r->base); + result = lv1_release_memory(r->base); + BUG_ON(result); r->size = r->base = r->offset = 0; map.total = map.rm.size; } @@ -329,31 +340,34 @@ core_initcall(ps3_mm_add_memory); /*============================================================================*/ /** - * dma_lpar_to_bus - Translate an lpar address to ioc mapped bus address. + * dma_sb_lpar_to_bus - Translate an lpar address to ioc mapped bus address. * @r: pointer to dma region structure * @lpar_addr: HV lpar address */ -static unsigned long dma_lpar_to_bus(struct ps3_dma_region *r, +static unsigned long dma_sb_lpar_to_bus(struct ps3_dma_region *r, unsigned long lpar_addr) { - BUG_ON(lpar_addr >= map.r1.base + map.r1.size); - return r->bus_addr + (lpar_addr <= map.rm.size ? lpar_addr - : lpar_addr - map.r1.offset); + if (lpar_addr >= map.rm.size) + lpar_addr -= map.r1.offset; + BUG_ON(lpar_addr < r->offset); + BUG_ON(lpar_addr >= r->offset + r->len); + return r->bus_addr + lpar_addr - r->offset; } #define dma_dump_region(_a) _dma_dump_region(_a, __func__, __LINE__) -static void _dma_dump_region(const struct ps3_dma_region *r, const char* func, - int line) +static void __maybe_unused _dma_dump_region(const struct ps3_dma_region *r, + const char *func, int line) { - DBG("%s:%d: dev %u:%u\n", func, line, r->did.bus_id, - r->did.dev_id); + DBG("%s:%d: dev %u:%u\n", func, line, r->dev->bus_id, + r->dev->dev_id); DBG("%s:%d: page_size %u\n", func, line, r->page_size); DBG("%s:%d: bus_addr %lxh\n", func, line, r->bus_addr); DBG("%s:%d: len %lxh\n", func, line, r->len); + DBG("%s:%d: offset %lxh\n", func, line, r->offset); } -/** + /** * dma_chunk - A chunk of dma pages mapped by the io controller. * @region - The dma region that owns this chunk. * @lpar_addr: Starting lpar address of the area to map. @@ -381,10 +395,11 @@ static void _dma_dump_chunk (const struct dma_chunk* c, const char* func, int line) { DBG("%s:%d: r.dev %u:%u\n", func, line, - c->region->did.bus_id, c->region->did.dev_id); + c->region->dev->bus_id, c->region->dev->dev_id); DBG("%s:%d: r.bus_addr %lxh\n", func, line, c->region->bus_addr); DBG("%s:%d: r.page_size %u\n", func, line, c->region->page_size); DBG("%s:%d: r.len %lxh\n", func, line, c->region->len); + DBG("%s:%d: r.offset %lxh\n", func, line, c->region->offset); DBG("%s:%d: c.lpar_addr %lxh\n", func, line, c->lpar_addr); DBG("%s:%d: c.bus_addr %lxh\n", func, line, c->bus_addr); DBG("%s:%d: c.len %lxh\n", func, line, c->len); @@ -395,39 +410,68 @@ static struct dma_chunk * dma_find_chunk(struct ps3_dma_region *r, { struct dma_chunk *c; unsigned long aligned_bus = _ALIGN_DOWN(bus_addr, 1 << r->page_size); - unsigned long aligned_len = _ALIGN_UP(len, 1 << r->page_size); + unsigned long aligned_len = _ALIGN_UP(len+bus_addr-aligned_bus, + 1 << r->page_size); list_for_each_entry(c, &r->chunk_list.head, link) { /* intersection */ - if (aligned_bus >= c->bus_addr - && aligned_bus < c->bus_addr + c->len - && aligned_bus + aligned_len <= c->bus_addr + c->len) { + if (aligned_bus >= c->bus_addr && + aligned_bus + aligned_len <= c->bus_addr + c->len) return c; - } + /* below */ - if (aligned_bus + aligned_len <= c->bus_addr) { + if (aligned_bus + aligned_len <= c->bus_addr) continue; - } + /* above */ - if (aligned_bus >= c->bus_addr + c->len) { + if (aligned_bus >= c->bus_addr + c->len) continue; - } /* we don't handle the multi-chunk case for now */ - dma_dump_chunk(c); BUG(); } return NULL; } -static int dma_free_chunk(struct dma_chunk *c) +static struct dma_chunk *dma_find_chunk_lpar(struct ps3_dma_region *r, + unsigned long lpar_addr, unsigned long len) +{ + struct dma_chunk *c; + unsigned long aligned_lpar = _ALIGN_DOWN(lpar_addr, 1 << r->page_size); + unsigned long aligned_len = _ALIGN_UP(len + lpar_addr - aligned_lpar, + 1 << r->page_size); + + list_for_each_entry(c, &r->chunk_list.head, link) { + /* intersection */ + if (c->lpar_addr <= aligned_lpar && + aligned_lpar < c->lpar_addr + c->len) { + if (aligned_lpar + aligned_len <= c->lpar_addr + c->len) + return c; + else { + dma_dump_chunk(c); + BUG(); + } + } + /* below */ + if (aligned_lpar + aligned_len <= c->lpar_addr) { + continue; + } + /* above */ + if (c->lpar_addr + c->len <= aligned_lpar) { + continue; + } + } + return NULL; +} + +static int dma_sb_free_chunk(struct dma_chunk *c) { int result = 0; if (c->bus_addr) { - result = lv1_unmap_device_dma_region(c->region->did.bus_id, - c->region->did.dev_id, c->bus_addr, c->len); + result = lv1_unmap_device_dma_region(c->region->dev->bus_id, + c->region->dev->dev_id, c->bus_addr, c->len); BUG_ON(result); } @@ -435,8 +479,39 @@ static int dma_free_chunk(struct dma_chunk *c) return result; } +static int dma_ioc0_free_chunk(struct dma_chunk *c) +{ + int result = 0; + int iopage; + unsigned long offset; + struct ps3_dma_region *r = c->region; + + DBG("%s:start\n", __func__); + for (iopage = 0; iopage < (c->len >> r->page_size); iopage++) { + offset = (1 << r->page_size) * iopage; + /* put INVALID entry */ + result = lv1_put_iopte(0, + c->bus_addr + offset, + c->lpar_addr + offset, + r->ioid, + 0); + DBG("%s: bus=%#lx, lpar=%#lx, ioid=%d\n", __func__, + c->bus_addr + offset, + c->lpar_addr + offset, + r->ioid); + + if (result) { + DBG("%s:%d: lv1_put_iopte failed: %s\n", __func__, + __LINE__, ps3_result(result)); + } + } + kfree(c); + DBG("%s:end\n", __func__); + return result; +} + /** - * dma_map_pages - Maps dma pages into the io controller bus address space. + * dma_sb_map_pages - Maps dma pages into the io controller bus address space. * @r: Pointer to a struct ps3_dma_region. * @phys_addr: Starting physical address of the area to map. * @len: Length in bytes of the area to map. @@ -446,8 +521,8 @@ static int dma_free_chunk(struct dma_chunk *c) * make the HV call to add the pages into the io controller address space. */ -static int dma_map_pages(struct ps3_dma_region *r, unsigned long phys_addr, - unsigned long len, struct dma_chunk **c_out) +static int dma_sb_map_pages(struct ps3_dma_region *r, unsigned long phys_addr, + unsigned long len, struct dma_chunk **c_out, u64 iopte_flag) { int result; struct dma_chunk *c; @@ -461,13 +536,13 @@ static int dma_map_pages(struct ps3_dma_region *r, unsigned long phys_addr, c->region = r; c->lpar_addr = ps3_mm_phys_to_lpar(phys_addr); - c->bus_addr = dma_lpar_to_bus(r, c->lpar_addr); + c->bus_addr = dma_sb_lpar_to_bus(r, c->lpar_addr); c->len = len; - result = lv1_map_device_dma_region(c->region->did.bus_id, - c->region->did.dev_id, c->lpar_addr, c->bus_addr, c->len, - 0xf800000000000000UL); - + BUG_ON(iopte_flag != 0xf800000000000000UL); + result = lv1_map_device_dma_region(c->region->dev->bus_id, + c->region->dev->dev_id, c->lpar_addr, + c->bus_addr, c->len, iopte_flag); if (result) { DBG("%s:%d: lv1_map_device_dma_region failed: %s\n", __func__, __LINE__, ps3_result(result)); @@ -487,26 +562,120 @@ fail_alloc: return result; } +static int dma_ioc0_map_pages(struct ps3_dma_region *r, unsigned long phys_addr, + unsigned long len, struct dma_chunk **c_out, + u64 iopte_flag) +{ + int result; + struct dma_chunk *c, *last; + int iopage, pages; + unsigned long offset; + + DBG(KERN_ERR "%s: phy=%#lx, lpar%#lx, len=%#lx\n", __func__, + phys_addr, ps3_mm_phys_to_lpar(phys_addr), len); + c = kzalloc(sizeof(struct dma_chunk), GFP_ATOMIC); + + if (!c) { + result = -ENOMEM; + goto fail_alloc; + } + + c->region = r; + c->len = len; + c->lpar_addr = ps3_mm_phys_to_lpar(phys_addr); + /* allocate IO address */ + if (list_empty(&r->chunk_list.head)) { + /* first one */ + c->bus_addr = r->bus_addr; + } else { + /* derive from last bus addr*/ + last = list_entry(r->chunk_list.head.next, + struct dma_chunk, link); + c->bus_addr = last->bus_addr + last->len; + DBG("%s: last bus=%#lx, len=%#lx\n", __func__, + last->bus_addr, last->len); + } + + /* FIXME: check whether length exceeds region size */ + + /* build ioptes for the area */ + pages = len >> r->page_size; + DBG("%s: pgsize=%#x len=%#lx pages=%#x iopteflag=%#lx\n", __func__, + r->page_size, r->len, pages, iopte_flag); + for (iopage = 0; iopage < pages; iopage++) { + offset = (1 << r->page_size) * iopage; + result = lv1_put_iopte(0, + c->bus_addr + offset, + c->lpar_addr + offset, + r->ioid, + iopte_flag); + if (result) { + printk(KERN_WARNING "%s:%d: lv1_map_device_dma_region " + "failed: %s\n", __func__, __LINE__, + ps3_result(result)); + goto fail_map; + } + DBG("%s: pg=%d bus=%#lx, lpar=%#lx, ioid=%#x\n", __func__, + iopage, c->bus_addr + offset, c->lpar_addr + offset, + r->ioid); + } + + /* be sure that last allocated one is inserted at head */ + list_add(&c->link, &r->chunk_list.head); + + *c_out = c; + DBG("%s: end\n", __func__); + return 0; + +fail_map: + for (iopage--; 0 <= iopage; iopage--) { + lv1_put_iopte(0, + c->bus_addr + offset, + c->lpar_addr + offset, + r->ioid, + 0); + } + kfree(c); +fail_alloc: + *c_out = NULL; + return result; +} + /** - * dma_region_create - Create a device dma region. + * dma_sb_region_create - Create a device dma region. * @r: Pointer to a struct ps3_dma_region. * * This is the lowest level dma region create routine, and is the one that * will make the HV call to create the region. */ -static int dma_region_create(struct ps3_dma_region* r) +static int dma_sb_region_create(struct ps3_dma_region *r) { int result; - r->len = _ALIGN_UP(map.total, 1 << r->page_size); + pr_info(" -> %s:%d:\n", __func__, __LINE__); + + BUG_ON(!r); + + if (!r->dev->bus_id) { + pr_info("%s:%d: %u:%u no dma\n", __func__, __LINE__, + r->dev->bus_id, r->dev->dev_id); + return 0; + } + + DBG("%s:%u: len = 0x%lx, page_size = %u, offset = 0x%lx\n", __func__, + __LINE__, r->len, r->page_size, r->offset); + + BUG_ON(!r->len); + BUG_ON(!r->page_size); + BUG_ON(!r->region_ops); + INIT_LIST_HEAD(&r->chunk_list.head); spin_lock_init(&r->chunk_list.lock); - result = lv1_allocate_device_dma_region(r->did.bus_id, r->did.dev_id, - r->len, r->page_size, r->region_type, &r->bus_addr); - - dma_dump_region(r); + result = lv1_allocate_device_dma_region(r->dev->bus_id, r->dev->dev_id, + roundup_pow_of_two(r->len), r->page_size, r->region_type, + &r->bus_addr); if (result) { DBG("%s:%d: lv1_allocate_device_dma_region failed: %s\n", @@ -517,6 +686,27 @@ static int dma_region_create(struct ps3_dma_region* r) return result; } +static int dma_ioc0_region_create(struct ps3_dma_region *r) +{ + int result; + + INIT_LIST_HEAD(&r->chunk_list.head); + spin_lock_init(&r->chunk_list.lock); + + result = lv1_allocate_io_segment(0, + r->len, + r->page_size, + &r->bus_addr); + if (result) { + DBG("%s:%d: lv1_allocate_io_segment failed: %s\n", + __func__, __LINE__, ps3_result(result)); + r->len = r->bus_addr = 0; + } + DBG("%s: len=%#lx, pg=%d, bus=%#lx\n", __func__, + r->len, r->page_size, r->bus_addr); + return result; +} + /** * dma_region_free - Free a device dma region. * @r: Pointer to a struct ps3_dma_region. @@ -525,31 +715,62 @@ static int dma_region_create(struct ps3_dma_region* r) * will make the HV call to free the region. */ -static int dma_region_free(struct ps3_dma_region* r) +static int dma_sb_region_free(struct ps3_dma_region *r) { int result; struct dma_chunk *c; struct dma_chunk *tmp; - list_for_each_entry_safe(c, tmp, &r->chunk_list.head, link) { - list_del(&c->link); - dma_free_chunk(c); + BUG_ON(!r); + + if (!r->dev->bus_id) { + pr_info("%s:%d: %u:%u no dma\n", __func__, __LINE__, + r->dev->bus_id, r->dev->dev_id); + return 0; } - result = lv1_free_device_dma_region(r->did.bus_id, r->did.dev_id, + list_for_each_entry_safe(c, tmp, &r->chunk_list.head, link) { + list_del(&c->link); + dma_sb_free_chunk(c); + } + + result = lv1_free_device_dma_region(r->dev->bus_id, r->dev->dev_id, r->bus_addr); if (result) DBG("%s:%d: lv1_free_device_dma_region failed: %s\n", __func__, __LINE__, ps3_result(result)); - r->len = r->bus_addr = 0; + r->bus_addr = 0; + + return result; +} + +static int dma_ioc0_region_free(struct ps3_dma_region *r) +{ + int result; + struct dma_chunk *c, *n; + + DBG("%s: start\n", __func__); + list_for_each_entry_safe(c, n, &r->chunk_list.head, link) { + list_del(&c->link); + dma_ioc0_free_chunk(c); + } + + result = lv1_release_io_segment(0, r->bus_addr); + + if (result) + DBG("%s:%d: lv1_free_device_dma_region failed: %s\n", + __func__, __LINE__, ps3_result(result)); + + r->bus_addr = 0; + DBG("%s: end\n", __func__); return result; } /** - * dma_map_area - Map an area of memory into a device dma region. + * dma_sb_map_area - Map an area of memory into a device dma region. * @r: Pointer to a struct ps3_dma_region. * @virt_addr: Starting virtual address of the area to map. * @len: Length in bytes of the area to map. @@ -559,16 +780,19 @@ static int dma_region_free(struct ps3_dma_region* r) * This is the common dma mapping routine. */ -static int dma_map_area(struct ps3_dma_region *r, unsigned long virt_addr, - unsigned long len, unsigned long *bus_addr) +static int dma_sb_map_area(struct ps3_dma_region *r, unsigned long virt_addr, + unsigned long len, unsigned long *bus_addr, + u64 iopte_flag) { int result; unsigned long flags; struct dma_chunk *c; unsigned long phys_addr = is_kernel_addr(virt_addr) ? __pa(virt_addr) : virt_addr; - - *bus_addr = dma_lpar_to_bus(r, ps3_mm_phys_to_lpar(phys_addr)); + unsigned long aligned_phys = _ALIGN_DOWN(phys_addr, 1 << r->page_size); + unsigned long aligned_len = _ALIGN_UP(len + phys_addr - aligned_phys, + 1 << r->page_size); + *bus_addr = dma_sb_lpar_to_bus(r, ps3_mm_phys_to_lpar(phys_addr)); if (!USE_DYNAMIC_DMA) { unsigned long lpar_addr = ps3_mm_phys_to_lpar(phys_addr); @@ -588,17 +812,18 @@ static int dma_map_area(struct ps3_dma_region *r, unsigned long virt_addr, c = dma_find_chunk(r, *bus_addr, len); if (c) { + DBG("%s:%d: reusing mapped chunk", __func__, __LINE__); + dma_dump_chunk(c); c->usage_count++; spin_unlock_irqrestore(&r->chunk_list.lock, flags); return 0; } - result = dma_map_pages(r, _ALIGN_DOWN(phys_addr, 1 << r->page_size), - _ALIGN_UP(len, 1 << r->page_size), &c); + result = dma_sb_map_pages(r, aligned_phys, aligned_len, &c, iopte_flag); if (result) { *bus_addr = 0; - DBG("%s:%d: dma_map_pages failed (%d)\n", + DBG("%s:%d: dma_sb_map_pages failed (%d)\n", __func__, __LINE__, result); spin_unlock_irqrestore(&r->chunk_list.lock, flags); return result; @@ -610,8 +835,57 @@ static int dma_map_area(struct ps3_dma_region *r, unsigned long virt_addr, return result; } +static int dma_ioc0_map_area(struct ps3_dma_region *r, unsigned long virt_addr, + unsigned long len, unsigned long *bus_addr, + u64 iopte_flag) +{ + int result; + unsigned long flags; + struct dma_chunk *c; + unsigned long phys_addr = is_kernel_addr(virt_addr) ? __pa(virt_addr) + : virt_addr; + unsigned long aligned_phys = _ALIGN_DOWN(phys_addr, 1 << r->page_size); + unsigned long aligned_len = _ALIGN_UP(len + phys_addr - aligned_phys, + 1 << r->page_size); + + DBG(KERN_ERR "%s: vaddr=%#lx, len=%#lx\n", __func__, + virt_addr, len); + DBG(KERN_ERR "%s: ph=%#lx a_ph=%#lx a_l=%#lx\n", __func__, + phys_addr, aligned_phys, aligned_len); + + spin_lock_irqsave(&r->chunk_list.lock, flags); + c = dma_find_chunk_lpar(r, ps3_mm_phys_to_lpar(phys_addr), len); + + if (c) { + /* FIXME */ + BUG(); + *bus_addr = c->bus_addr + phys_addr - aligned_phys; + c->usage_count++; + spin_unlock_irqrestore(&r->chunk_list.lock, flags); + return 0; + } + + result = dma_ioc0_map_pages(r, aligned_phys, aligned_len, &c, + iopte_flag); + + if (result) { + *bus_addr = 0; + DBG("%s:%d: dma_ioc0_map_pages failed (%d)\n", + __func__, __LINE__, result); + spin_unlock_irqrestore(&r->chunk_list.lock, flags); + return result; + } + *bus_addr = c->bus_addr + phys_addr - aligned_phys; + DBG("%s: va=%#lx pa=%#lx a_pa=%#lx bus=%#lx\n", __func__, + virt_addr, phys_addr, aligned_phys, *bus_addr); + c->usage_count = 1; + + spin_unlock_irqrestore(&r->chunk_list.lock, flags); + return result; +} + /** - * dma_unmap_area - Unmap an area of memory from a device dma region. + * dma_sb_unmap_area - Unmap an area of memory from a device dma region. * @r: Pointer to a struct ps3_dma_region. * @bus_addr: The starting ioc bus address of the area to unmap. * @len: Length in bytes of the area to unmap. @@ -619,7 +893,7 @@ static int dma_map_area(struct ps3_dma_region *r, unsigned long virt_addr, * This is the common dma unmap routine. */ -int dma_unmap_area(struct ps3_dma_region *r, unsigned long bus_addr, +static int dma_sb_unmap_area(struct ps3_dma_region *r, unsigned long bus_addr, unsigned long len) { unsigned long flags; @@ -631,7 +905,8 @@ int dma_unmap_area(struct ps3_dma_region *r, unsigned long bus_addr, if (!c) { unsigned long aligned_bus = _ALIGN_DOWN(bus_addr, 1 << r->page_size); - unsigned long aligned_len = _ALIGN_UP(len, 1 << r->page_size); + unsigned long aligned_len = _ALIGN_UP(len + bus_addr + - aligned_bus, 1 << r->page_size); DBG("%s:%d: not found: bus_addr %lxh\n", __func__, __LINE__, bus_addr); DBG("%s:%d: not found: len %lxh\n", @@ -647,94 +922,166 @@ int dma_unmap_area(struct ps3_dma_region *r, unsigned long bus_addr, if (!c->usage_count) { list_del(&c->link); - dma_free_chunk(c); + dma_sb_free_chunk(c); } spin_unlock_irqrestore(&r->chunk_list.lock, flags); return 0; } +static int dma_ioc0_unmap_area(struct ps3_dma_region *r, + unsigned long bus_addr, unsigned long len) +{ + unsigned long flags; + struct dma_chunk *c; + + DBG("%s: start a=%#lx l=%#lx\n", __func__, bus_addr, len); + spin_lock_irqsave(&r->chunk_list.lock, flags); + c = dma_find_chunk(r, bus_addr, len); + + if (!c) { + unsigned long aligned_bus = _ALIGN_DOWN(bus_addr, + 1 << r->page_size); + unsigned long aligned_len = _ALIGN_UP(len + bus_addr + - aligned_bus, + 1 << r->page_size); + DBG("%s:%d: not found: bus_addr %lxh\n", + __func__, __LINE__, bus_addr); + DBG("%s:%d: not found: len %lxh\n", + __func__, __LINE__, len); + DBG("%s:%d: not found: aligned_bus %lxh\n", + __func__, __LINE__, aligned_bus); + DBG("%s:%d: not found: aligned_len %lxh\n", + __func__, __LINE__, aligned_len); + BUG(); + } + + c->usage_count--; + + if (!c->usage_count) { + list_del(&c->link); + dma_ioc0_free_chunk(c); + } + + spin_unlock_irqrestore(&r->chunk_list.lock, flags); + DBG("%s: end\n", __func__); + return 0; +} + /** - * dma_region_create_linear - Setup a linear dma maping for a device. + * dma_sb_region_create_linear - Setup a linear dma mapping for a device. * @r: Pointer to a struct ps3_dma_region. * * This routine creates an HV dma region for the device and maps all available * ram into the io controller bus address space. */ -static int dma_region_create_linear(struct ps3_dma_region *r) +static int dma_sb_region_create_linear(struct ps3_dma_region *r) { int result; - unsigned long tmp; + unsigned long virt_addr, len, tmp; - /* force 16M dma pages for linear mapping */ - - if (r->page_size != PS3_DMA_16M) { - pr_info("%s:%d: forcing 16M pages for linear map\n", - __func__, __LINE__); - r->page_size = PS3_DMA_16M; + if (r->len > 16*1024*1024) { /* FIXME: need proper fix */ + /* force 16M dma pages for linear mapping */ + if (r->page_size != PS3_DMA_16M) { + pr_info("%s:%d: forcing 16M pages for linear map\n", + __func__, __LINE__); + r->page_size = PS3_DMA_16M; + r->len = _ALIGN_UP(r->len, 1 << r->page_size); + } } - result = dma_region_create(r); + result = dma_sb_region_create(r); BUG_ON(result); - result = dma_map_area(r, map.rm.base, map.rm.size, &tmp); - BUG_ON(result); + if (r->offset < map.rm.size) { + /* Map (part of) 1st RAM chunk */ + virt_addr = map.rm.base + r->offset; + len = map.rm.size - r->offset; + if (len > r->len) + len = r->len; + result = dma_sb_map_area(r, virt_addr, len, &tmp, + IOPTE_PP_W | IOPTE_PP_R | IOPTE_SO_RW | IOPTE_M); + BUG_ON(result); + } - if (USE_LPAR_ADDR) - result = dma_map_area(r, map.r1.base, map.r1.size, - &tmp); - else - result = dma_map_area(r, map.rm.size, map.r1.size, - &tmp); - - BUG_ON(result); + if (r->offset + r->len > map.rm.size) { + /* Map (part of) 2nd RAM chunk */ + virt_addr = USE_LPAR_ADDR ? map.r1.base : map.rm.size; + len = r->len; + if (r->offset >= map.rm.size) + virt_addr += r->offset - map.rm.size; + else + len -= map.rm.size - r->offset; + result = dma_sb_map_area(r, virt_addr, len, &tmp, + IOPTE_PP_W | IOPTE_PP_R | IOPTE_SO_RW | IOPTE_M); + BUG_ON(result); + } return result; } /** - * dma_region_free_linear - Free a linear dma mapping for a device. + * dma_sb_region_free_linear - Free a linear dma mapping for a device. * @r: Pointer to a struct ps3_dma_region. * * This routine will unmap all mapped areas and free the HV dma region. */ -static int dma_region_free_linear(struct ps3_dma_region *r) +static int dma_sb_region_free_linear(struct ps3_dma_region *r) { int result; + unsigned long bus_addr, len, lpar_addr; - result = dma_unmap_area(r, dma_lpar_to_bus(r, 0), map.rm.size); - BUG_ON(result); + if (r->offset < map.rm.size) { + /* Unmap (part of) 1st RAM chunk */ + lpar_addr = map.rm.base + r->offset; + len = map.rm.size - r->offset; + if (len > r->len) + len = r->len; + bus_addr = dma_sb_lpar_to_bus(r, lpar_addr); + result = dma_sb_unmap_area(r, bus_addr, len); + BUG_ON(result); + } - result = dma_unmap_area(r, dma_lpar_to_bus(r, map.r1.base), - map.r1.size); - BUG_ON(result); + if (r->offset + r->len > map.rm.size) { + /* Unmap (part of) 2nd RAM chunk */ + lpar_addr = map.r1.base; + len = r->len; + if (r->offset >= map.rm.size) + lpar_addr += r->offset - map.rm.size; + else + len -= map.rm.size - r->offset; + bus_addr = dma_sb_lpar_to_bus(r, lpar_addr); + result = dma_sb_unmap_area(r, bus_addr, len); + BUG_ON(result); + } - result = dma_region_free(r); + result = dma_sb_region_free(r); BUG_ON(result); return result; } /** - * dma_map_area_linear - Map an area of memory into a device dma region. + * dma_sb_map_area_linear - Map an area of memory into a device dma region. * @r: Pointer to a struct ps3_dma_region. * @virt_addr: Starting virtual address of the area to map. * @len: Length in bytes of the area to map. * @bus_addr: A pointer to return the starting ioc bus address of the area to * map. * - * This routine just returns the coresponding bus address. Actual mapping + * This routine just returns the corresponding bus address. Actual mapping * occurs in dma_region_create_linear(). */ -static int dma_map_area_linear(struct ps3_dma_region *r, - unsigned long virt_addr, unsigned long len, unsigned long *bus_addr) +static int dma_sb_map_area_linear(struct ps3_dma_region *r, + unsigned long virt_addr, unsigned long len, unsigned long *bus_addr, + u64 iopte_flag) { unsigned long phys_addr = is_kernel_addr(virt_addr) ? __pa(virt_addr) : virt_addr; - *bus_addr = dma_lpar_to_bus(r, ps3_mm_phys_to_lpar(phys_addr)); + *bus_addr = dma_sb_lpar_to_bus(r, ps3_mm_phys_to_lpar(phys_addr)); return 0; } @@ -744,42 +1091,98 @@ static int dma_map_area_linear(struct ps3_dma_region *r, * @bus_addr: The starting ioc bus address of the area to unmap. * @len: Length in bytes of the area to unmap. * - * This routine does nothing. Unmapping occurs in dma_region_free_linear(). + * This routine does nothing. Unmapping occurs in dma_sb_region_free_linear(). */ -static int dma_unmap_area_linear(struct ps3_dma_region *r, +static int dma_sb_unmap_area_linear(struct ps3_dma_region *r, unsigned long bus_addr, unsigned long len) { return 0; +}; + +static const struct ps3_dma_region_ops ps3_dma_sb_region_ops = { + .create = dma_sb_region_create, + .free = dma_sb_region_free, + .map = dma_sb_map_area, + .unmap = dma_sb_unmap_area +}; + +static const struct ps3_dma_region_ops ps3_dma_sb_region_linear_ops = { + .create = dma_sb_region_create_linear, + .free = dma_sb_region_free_linear, + .map = dma_sb_map_area_linear, + .unmap = dma_sb_unmap_area_linear +}; + +static const struct ps3_dma_region_ops ps3_dma_ioc0_region_ops = { + .create = dma_ioc0_region_create, + .free = dma_ioc0_region_free, + .map = dma_ioc0_map_area, + .unmap = dma_ioc0_unmap_area +}; + +int ps3_dma_region_init(struct ps3_system_bus_device *dev, + struct ps3_dma_region *r, enum ps3_dma_page_size page_size, + enum ps3_dma_region_type region_type, void *addr, unsigned long len) +{ + unsigned long lpar_addr; + + lpar_addr = addr ? ps3_mm_phys_to_lpar(__pa(addr)) : 0; + + r->dev = dev; + r->page_size = page_size; + r->region_type = region_type; + r->offset = lpar_addr; + if (r->offset >= map.rm.size) + r->offset -= map.r1.offset; + r->len = len ? len : _ALIGN_UP(map.total, 1 << r->page_size); + + switch (dev->dev_type) { + case PS3_DEVICE_TYPE_SB: + r->region_ops = (USE_DYNAMIC_DMA) + ? &ps3_dma_sb_region_ops + : &ps3_dma_sb_region_linear_ops; + break; + case PS3_DEVICE_TYPE_IOC0: + r->region_ops = &ps3_dma_ioc0_region_ops; + break; + default: + BUG(); + return -EINVAL; + } + return 0; } +EXPORT_SYMBOL(ps3_dma_region_init); int ps3_dma_region_create(struct ps3_dma_region *r) { - return (USE_DYNAMIC_DMA) - ? dma_region_create(r) - : dma_region_create_linear(r); + BUG_ON(!r); + BUG_ON(!r->region_ops); + BUG_ON(!r->region_ops->create); + return r->region_ops->create(r); } +EXPORT_SYMBOL(ps3_dma_region_create); int ps3_dma_region_free(struct ps3_dma_region *r) { - return (USE_DYNAMIC_DMA) - ? dma_region_free(r) - : dma_region_free_linear(r); + BUG_ON(!r); + BUG_ON(!r->region_ops); + BUG_ON(!r->region_ops->free); + return r->region_ops->free(r); } +EXPORT_SYMBOL(ps3_dma_region_free); int ps3_dma_map(struct ps3_dma_region *r, unsigned long virt_addr, - unsigned long len, unsigned long *bus_addr) + unsigned long len, unsigned long *bus_addr, + u64 iopte_flag) { - return (USE_DYNAMIC_DMA) - ? dma_map_area(r, virt_addr, len, bus_addr) - : dma_map_area_linear(r, virt_addr, len, bus_addr); + return r->region_ops->map(r, virt_addr, len, bus_addr, iopte_flag); } int ps3_dma_unmap(struct ps3_dma_region *r, unsigned long bus_addr, unsigned long len) { - return (USE_DYNAMIC_DMA) ? dma_unmap_area(r, bus_addr, len) - : dma_unmap_area_linear(r, bus_addr, len); + return r->region_ops->unmap(r, bus_addr, len); } /*============================================================================*/ @@ -810,12 +1213,13 @@ void __init ps3_mm_init(void) BUG_ON(map.rm.base); BUG_ON(!map.rm.size); - lmb_add(map.rm.base, map.rm.size); - lmb_analyze(); /* arrange to do this in ps3_mm_add_memory */ ps3_mm_region_create(&map.r1, map.total - map.rm.size); + /* correct map.total for the real total amount of memory we use */ + map.total = map.rm.size + map.r1.size; + DBG(" <- %s:%d\n", __func__, __LINE__); } diff --git a/arch/powerpc/platforms/ps3/os-area.c b/arch/powerpc/platforms/ps3/os-area.c index 5c3da08bc0c4..b70e474014f0 100644 --- a/arch/powerpc/platforms/ps3/os-area.c +++ b/arch/powerpc/platforms/ps3/os-area.c @@ -133,7 +133,7 @@ struct saved_params { } static saved_params; #define dump_header(_a) _dump_header(_a, __func__, __LINE__) -static void _dump_header(const struct os_area_header __iomem *h, const char* func, +static void _dump_header(const struct os_area_header *h, const char *func, int line) { pr_debug("%s:%d: h.magic_num: '%s'\n", func, line, @@ -151,7 +151,7 @@ static void _dump_header(const struct os_area_header __iomem *h, const char* fun } #define dump_params(_a) _dump_params(_a, __func__, __LINE__) -static void _dump_params(const struct os_area_params __iomem *p, const char* func, +static void _dump_params(const struct os_area_params *p, const char *func, int line) { pr_debug("%s:%d: p.boot_flag: %u\n", func, line, p->boot_flag); diff --git a/arch/powerpc/platforms/ps3/platform.h b/arch/powerpc/platforms/ps3/platform.h index ca04f03305c7..87d52060fec0 100644 --- a/arch/powerpc/platforms/ps3/platform.h +++ b/arch/powerpc/platforms/ps3/platform.h @@ -41,6 +41,7 @@ void ps3_mm_shutdown(void); /* irq */ void ps3_init_IRQ(void); +void ps3_shutdown_IRQ(int cpu); void __init ps3_register_ipi_debug_brk(unsigned int cpu, unsigned int virq); /* smp */ @@ -82,6 +83,7 @@ enum ps3_dev_type { PS3_DEV_TYPE_STOR_ROM = TYPE_ROM, /* 5 */ PS3_DEV_TYPE_SB_GPIO = 6, PS3_DEV_TYPE_STOR_FLASH = TYPE_RBC, /* 14 */ + PS3_DEV_TYPE_NOACCESS = 255, }; int ps3_repository_read_bus_str(unsigned int bus_index, const char *bus_str, @@ -129,24 +131,28 @@ int ps3_repository_read_dev_reg(unsigned int bus_index, /* repository bus enumerators */ struct ps3_repository_device { + enum ps3_bus_type bus_type; unsigned int bus_index; + unsigned int bus_id; + enum ps3_dev_type dev_type; unsigned int dev_index; - struct ps3_device_id did; + unsigned int dev_id; }; -int ps3_repository_find_device(enum ps3_bus_type bus_type, - enum ps3_dev_type dev_type, - const struct ps3_repository_device *start_dev, - struct ps3_repository_device *dev); -static inline int ps3_repository_find_first_device( - enum ps3_bus_type bus_type, enum ps3_dev_type dev_type, - struct ps3_repository_device *dev) +static inline struct ps3_repository_device *ps3_repository_bump_device( + struct ps3_repository_device *repo) { - return ps3_repository_find_device(bus_type, dev_type, NULL, dev); + repo->dev_index++; + return repo; } -int ps3_repository_find_interrupt(const struct ps3_repository_device *dev, +int ps3_repository_find_device(struct ps3_repository_device *repo); +int ps3_repository_find_devices(enum ps3_bus_type bus_type, + int (*callback)(const struct ps3_repository_device *repo)); +int ps3_repository_find_bus(enum ps3_bus_type bus_type, unsigned int from, + unsigned int *bus_index); +int ps3_repository_find_interrupt(const struct ps3_repository_device *repo, enum ps3_interrupt_type intr_type, unsigned int *interrupt_id); -int ps3_repository_find_reg(const struct ps3_repository_device *dev, +int ps3_repository_find_reg(const struct ps3_repository_device *repo, enum ps3_reg_type reg_type, u64 *bus_addr, u64 *len); /* repository block device info */ @@ -216,4 +222,19 @@ int ps3_repository_read_num_spu_resource_id(unsigned int *num_resource_id); int ps3_repository_read_spu_resource_id(unsigned int res_index, enum ps3_spu_resource_type* resource_type, unsigned int *resource_id); +/* repository vuart info */ + +int ps3_repository_read_vuart_av_port(unsigned int *port); +int ps3_repository_read_vuart_sysmgr_port(unsigned int *port); + +/* Page table entries */ +#define IOPTE_PP_W 0x8000000000000000ul /* protection: write */ +#define IOPTE_PP_R 0x4000000000000000ul /* protection: read */ +#define IOPTE_M 0x2000000000000000ul /* coherency required */ +#define IOPTE_SO_R 0x1000000000000000ul /* ordering: writes */ +#define IOPTE_SO_RW 0x1800000000000000ul /* ordering: r & w */ +#define IOPTE_RPN_Mask 0x07fffffffffff000ul /* RPN */ +#define IOPTE_H 0x0000000000000800ul /* cache hint */ +#define IOPTE_IOID_Mask 0x00000000000007fful /* ioid */ + #endif diff --git a/arch/powerpc/platforms/ps3/repository.c b/arch/powerpc/platforms/ps3/repository.c index ae586a0e5d3f..8cc37cfea0f2 100644 --- a/arch/powerpc/platforms/ps3/repository.c +++ b/arch/powerpc/platforms/ps3/repository.c @@ -138,7 +138,7 @@ static int read_node(unsigned int lpar_id, u64 n1, u64 n2, u64 n3, u64 n4, pr_debug("%s:%d: lv1_get_repository_node_value failed: %s\n", __func__, __LINE__, ps3_result(result)); dump_node_name(lpar_id, n1, n2, n3, n4); - return result; + return -ENOENT; } dump_node(lpar_id, n1, n2, n3, n4, v1, v2); @@ -155,7 +155,7 @@ static int read_node(unsigned int lpar_id, u64 n1, u64 n2, u64 n3, u64 n4, pr_debug("%s:%d: warning: discarding non-zero v2: %016lx\n", __func__, __LINE__, v2); - return result; + return 0; } int ps3_repository_read_bus_str(unsigned int bus_index, const char *bus_str, @@ -314,324 +314,140 @@ int ps3_repository_read_dev_reg(unsigned int bus_index, reg_index, bus_addr, len); } -#if defined(DEBUG) -int ps3_repository_dump_resource_info(unsigned int bus_index, - unsigned int dev_index) + + +int ps3_repository_find_device(struct ps3_repository_device *repo) { - int result = 0; - unsigned int res_index; + int result; + struct ps3_repository_device tmp = *repo; + unsigned int num_dev; - pr_debug(" -> %s:%d: (%u:%u)\n", __func__, __LINE__, - bus_index, dev_index); + BUG_ON(repo->bus_index > 10); + BUG_ON(repo->dev_index > 10); - for (res_index = 0; res_index < 10; res_index++) { - enum ps3_interrupt_type intr_type; - unsigned int interrupt_id; + result = ps3_repository_read_bus_num_dev(tmp.bus_index, &num_dev); - result = ps3_repository_read_dev_intr(bus_index, dev_index, - res_index, &intr_type, &interrupt_id); - - if (result) { - if (result != LV1_NO_ENTRY) - pr_debug("%s:%d ps3_repository_read_dev_intr" - " (%u:%u) failed\n", __func__, __LINE__, - bus_index, dev_index); - break; - } - - pr_debug("%s:%d (%u:%u) intr_type %u, interrupt_id %u\n", - __func__, __LINE__, bus_index, dev_index, intr_type, - interrupt_id); - } - - for (res_index = 0; res_index < 10; res_index++) { - enum ps3_reg_type reg_type; - u64 bus_addr; - u64 len; - - result = ps3_repository_read_dev_reg(bus_index, dev_index, - res_index, ®_type, &bus_addr, &len); - - if (result) { - if (result != LV1_NO_ENTRY) - pr_debug("%s:%d ps3_repository_read_dev_reg" - " (%u:%u) failed\n", __func__, __LINE__, - bus_index, dev_index); - break; - } - - pr_debug("%s:%d (%u:%u) reg_type %u, bus_addr %lxh, len %lxh\n", - __func__, __LINE__, bus_index, dev_index, reg_type, - bus_addr, len); - } - - pr_debug(" <- %s:%d\n", __func__, __LINE__); - return result; -} - -static int dump_stor_dev_info(unsigned int bus_index, unsigned int dev_index) -{ - int result = 0; - unsigned int num_regions, region_index; - u64 port, blk_size, num_blocks; - - pr_debug(" -> %s:%d: (%u:%u)\n", __func__, __LINE__, - bus_index, dev_index); - - result = ps3_repository_read_stor_dev_info(bus_index, dev_index, &port, - &blk_size, &num_blocks, &num_regions); if (result) { - pr_debug("%s:%d ps3_repository_read_stor_dev_info" - " (%u:%u) failed\n", __func__, __LINE__, - bus_index, dev_index); - goto out; + pr_debug("%s:%d read_bus_num_dev failed\n", __func__, __LINE__); + return result; } - pr_debug("%s:%d (%u:%u): port %lu, blk_size %lu, num_blocks " - "%lu, num_regions %u\n", - __func__, __LINE__, bus_index, dev_index, port, - blk_size, num_blocks, num_regions); + pr_debug("%s:%d: bus_type %u, bus_index %u, bus_id %u, num_dev %u\n", + __func__, __LINE__, tmp.bus_type, tmp.bus_index, tmp.bus_id, + num_dev); - for (region_index = 0; region_index < num_regions; region_index++) { - unsigned int region_id; - u64 region_start, region_size; - - result = ps3_repository_read_stor_dev_region(bus_index, - dev_index, region_index, ®ion_id, ®ion_start, - ®ion_size); - if (result) { - pr_debug("%s:%d ps3_repository_read_stor_dev_region" - " (%u:%u) failed\n", __func__, __LINE__, - bus_index, dev_index); - break; - } - - pr_debug("%s:%d (%u:%u) region_id %u, start %lxh, size %lxh\n", - __func__, __LINE__, bus_index, dev_index, region_id, - region_start, region_size); + if (tmp.dev_index >= num_dev) { + pr_debug("%s:%d: no device found\n", __func__, __LINE__); + return -ENODEV; } -out: - pr_debug(" <- %s:%d\n", __func__, __LINE__); - return result; + result = ps3_repository_read_dev_type(tmp.bus_index, tmp.dev_index, + &tmp.dev_type); + + if (result) { + pr_debug("%s:%d read_dev_type failed\n", __func__, __LINE__); + return result; + } + + result = ps3_repository_read_dev_id(tmp.bus_index, tmp.dev_index, + &tmp.dev_id); + + if (result) { + pr_debug("%s:%d ps3_repository_read_dev_id failed\n", __func__, + __LINE__); + return result; + } + + pr_debug("%s:%d: found: dev_type %u, dev_index %u, dev_id %u\n", + __func__, __LINE__, tmp.dev_type, tmp.dev_index, tmp.dev_id); + + *repo = tmp; + return 0; } -static int dump_device_info(unsigned int bus_index, enum ps3_bus_type bus_type, - unsigned int num_dev) +int __devinit ps3_repository_find_devices(enum ps3_bus_type bus_type, + int (*callback)(const struct ps3_repository_device *repo)) { int result = 0; - unsigned int dev_index; + struct ps3_repository_device repo; - pr_debug(" -> %s:%d: bus_%u\n", __func__, __LINE__, bus_index); + pr_debug(" -> %s:%d: find bus_type %u\n", __func__, __LINE__, bus_type); - for (dev_index = 0; dev_index < num_dev; dev_index++) { - enum ps3_dev_type dev_type; - unsigned int dev_id; + for (repo.bus_index = 0; repo.bus_index < 10; repo.bus_index++) { - result = ps3_repository_read_dev_type(bus_index, dev_index, - &dev_type); - - if (result) { - pr_debug("%s:%d ps3_repository_read_dev_type" - " (%u:%u) failed\n", __func__, __LINE__, - bus_index, dev_index); - break; - } - - result = ps3_repository_read_dev_id(bus_index, dev_index, - &dev_id); - - if (result) { - pr_debug("%s:%d ps3_repository_read_dev_id" - " (%u:%u) failed\n", __func__, __LINE__, - bus_index, dev_index); - continue; - } - - pr_debug("%s:%d (%u:%u): dev_type %u, dev_id %u\n", __func__, - __LINE__, bus_index, dev_index, dev_type, dev_id); - - ps3_repository_dump_resource_info(bus_index, dev_index); - - if (bus_type == PS3_BUS_TYPE_STORAGE) - dump_stor_dev_info(bus_index, dev_index); - } - - pr_debug(" <- %s:%d\n", __func__, __LINE__); - return result; -} - -int ps3_repository_dump_bus_info(void) -{ - int result = 0; - unsigned int bus_index; - - pr_debug(" -> %s:%d\n", __func__, __LINE__); - - for (bus_index = 0; bus_index < 10; bus_index++) { - enum ps3_bus_type bus_type; - unsigned int bus_id; - unsigned int num_dev; - - result = ps3_repository_read_bus_type(bus_index, &bus_type); + result = ps3_repository_read_bus_type(repo.bus_index, + &repo.bus_type); if (result) { pr_debug("%s:%d read_bus_type(%u) failed\n", - __func__, __LINE__, bus_index); + __func__, __LINE__, repo.bus_index); break; } - result = ps3_repository_read_bus_id(bus_index, &bus_id); + if (repo.bus_type != bus_type) { + pr_debug("%s:%d: skip, bus_type %u\n", __func__, + __LINE__, repo.bus_type); + continue; + } + + result = ps3_repository_read_bus_id(repo.bus_index, + &repo.bus_id); if (result) { pr_debug("%s:%d read_bus_id(%u) failed\n", - __func__, __LINE__, bus_index); + __func__, __LINE__, repo.bus_index); continue; } - if (bus_index != bus_id) - pr_debug("%s:%d bus_index != bus_id\n", - __func__, __LINE__); + for (repo.dev_index = 0; ; repo.dev_index++) { + result = ps3_repository_find_device(&repo); - result = ps3_repository_read_bus_num_dev(bus_index, &num_dev); + if (result == -ENODEV) { + result = 0; + break; + } else if (result) + break; - if (result) { - pr_debug("%s:%d read_bus_num_dev(%u) failed\n", - __func__, __LINE__, bus_index); - continue; + result = callback(&repo); + + if (result) { + pr_debug("%s:%d: abort at callback\n", __func__, + __LINE__); + break; + } } - - pr_debug("%s:%d bus_%u: bus_type %u, bus_id %u, num_dev %u\n", - __func__, __LINE__, bus_index, bus_type, bus_id, - num_dev); - - dump_device_info(bus_index, bus_type, num_dev); + break; } pr_debug(" <- %s:%d\n", __func__, __LINE__); return result; } -#endif /* defined(DEBUG) */ -static int find_device(unsigned int bus_index, unsigned int num_dev, - unsigned int start_dev_index, enum ps3_dev_type dev_type, - struct ps3_repository_device *dev) +int ps3_repository_find_bus(enum ps3_bus_type bus_type, unsigned int from, + unsigned int *bus_index) { - int result = 0; - unsigned int dev_index; + unsigned int i; + enum ps3_bus_type type; + int error; - pr_debug("%s:%d: find dev_type %u\n", __func__, __LINE__, dev_type); - - dev->dev_index = UINT_MAX; - - for (dev_index = start_dev_index; dev_index < num_dev; dev_index++) { - enum ps3_dev_type x; - - result = ps3_repository_read_dev_type(bus_index, dev_index, - &x); - - if (result) { - pr_debug("%s:%d read_dev_type failed\n", - __func__, __LINE__); - return result; - } - - if (x == dev_type) - break; - } - - if (dev_index == num_dev) - return -1; - - pr_debug("%s:%d: found dev_type %u at dev_index %u\n", - __func__, __LINE__, dev_type, dev_index); - - result = ps3_repository_read_dev_id(bus_index, dev_index, - &dev->did.dev_id); - - if (result) { - pr_debug("%s:%d read_dev_id failed\n", - __func__, __LINE__); - return result; - } - - dev->dev_index = dev_index; - - pr_debug("%s:%d found: dev_id %u\n", __func__, __LINE__, - dev->did.dev_id); - - return result; -} - -int ps3_repository_find_device (enum ps3_bus_type bus_type, - enum ps3_dev_type dev_type, - const struct ps3_repository_device *start_dev, - struct ps3_repository_device *dev) -{ - int result = 0; - unsigned int bus_index; - unsigned int num_dev; - - pr_debug("%s:%d: find bus_type %u, dev_type %u\n", __func__, __LINE__, - bus_type, dev_type); - - BUG_ON(start_dev && start_dev->bus_index > 10); - - for (bus_index = start_dev ? start_dev->bus_index : 0; bus_index < 10; - bus_index++) { - enum ps3_bus_type x; - - result = ps3_repository_read_bus_type(bus_index, &x); - - if (result) { + for (i = from; i < 10; i++) { + error = ps3_repository_read_bus_type(i, &type); + if (error) { pr_debug("%s:%d read_bus_type failed\n", __func__, __LINE__); - dev->bus_index = UINT_MAX; - return result; + *bus_index = UINT_MAX; + return error; + } + if (type == bus_type) { + *bus_index = i; + return 0; } - if (x == bus_type) - break; } - - if (bus_index >= 10) - return -ENODEV; - - pr_debug("%s:%d: found bus_type %u at bus_index %u\n", - __func__, __LINE__, bus_type, bus_index); - - result = ps3_repository_read_bus_num_dev(bus_index, &num_dev); - - if (result) { - pr_debug("%s:%d read_bus_num_dev failed\n", - __func__, __LINE__); - return result; - } - - result = find_device(bus_index, num_dev, start_dev - ? start_dev->dev_index + 1 : 0, dev_type, dev); - - if (result) { - pr_debug("%s:%d get_did failed\n", __func__, __LINE__); - return result; - } - - result = ps3_repository_read_bus_id(bus_index, &dev->did.bus_id); - - if (result) { - pr_debug("%s:%d read_bus_id failed\n", - __func__, __LINE__); - return result; - } - - dev->bus_index = bus_index; - - pr_debug("%s:%d found: bus_id %u, dev_id %u\n", - __func__, __LINE__, dev->did.bus_id, dev->did.dev_id); - - return result; + *bus_index = UINT_MAX; + return -ENODEV; } -int ps3_repository_find_interrupt(const struct ps3_repository_device *dev, +int ps3_repository_find_interrupt(const struct ps3_repository_device *repo, enum ps3_interrupt_type intr_type, unsigned int *interrupt_id) { int result = 0; @@ -645,8 +461,8 @@ int ps3_repository_find_interrupt(const struct ps3_repository_device *dev, enum ps3_interrupt_type t; unsigned int id; - result = ps3_repository_read_dev_intr(dev->bus_index, - dev->dev_index, res_index, &t, &id); + result = ps3_repository_read_dev_intr(repo->bus_index, + repo->dev_index, res_index, &t, &id); if (result) { pr_debug("%s:%d read_dev_intr failed\n", @@ -669,7 +485,7 @@ int ps3_repository_find_interrupt(const struct ps3_repository_device *dev, return result; } -int ps3_repository_find_reg(const struct ps3_repository_device *dev, +int ps3_repository_find_reg(const struct ps3_repository_device *repo, enum ps3_reg_type reg_type, u64 *bus_addr, u64 *len) { int result = 0; @@ -684,8 +500,8 @@ int ps3_repository_find_reg(const struct ps3_repository_device *dev, u64 a; u64 l; - result = ps3_repository_read_dev_reg(dev->bus_index, - dev->dev_index, res_index, &t, &a, &l); + result = ps3_repository_read_dev_reg(repo->bus_index, + repo->dev_index, res_index, &t, &a, &l); if (result) { pr_debug("%s:%d read_dev_reg failed\n", @@ -965,6 +781,36 @@ int ps3_repository_read_boot_dat_size(unsigned int *size) return result; } +int ps3_repository_read_vuart_av_port(unsigned int *port) +{ + int result; + u64 v1; + + result = read_node(PS3_LPAR_ID_CURRENT, + make_first_field("bi", 0), + make_field("vir_uart", 0), + make_field("port", 0), + make_field("avset", 0), + &v1, 0); + *port = v1; + return result; +} + +int ps3_repository_read_vuart_sysmgr_port(unsigned int *port) +{ + int result; + u64 v1; + + result = read_node(PS3_LPAR_ID_CURRENT, + make_first_field("bi", 0), + make_field("vir_uart", 0), + make_field("port", 0), + make_field("sysmgr", 0), + &v1, 0); + *port = v1; + return result; +} + /** * ps3_repository_read_boot_dat_info - Get address and size of cell_ext_os_area. * address: lpar address of cell_ext_os_area @@ -1026,3 +872,205 @@ int ps3_repository_read_be_tb_freq(unsigned int be_index, u64 *tb_freq) return result ? result : ps3_repository_read_tb_freq(node_id, tb_freq); } + +#if defined(DEBUG) + +int ps3_repository_dump_resource_info(const struct ps3_repository_device *repo) +{ + int result = 0; + unsigned int res_index; + + pr_debug(" -> %s:%d: (%u:%u)\n", __func__, __LINE__, + repo->bus_index, repo->dev_index); + + for (res_index = 0; res_index < 10; res_index++) { + enum ps3_interrupt_type intr_type; + unsigned int interrupt_id; + + result = ps3_repository_read_dev_intr(repo->bus_index, + repo->dev_index, res_index, &intr_type, &interrupt_id); + + if (result) { + if (result != LV1_NO_ENTRY) + pr_debug("%s:%d ps3_repository_read_dev_intr" + " (%u:%u) failed\n", __func__, __LINE__, + repo->bus_index, repo->dev_index); + break; + } + + pr_debug("%s:%d (%u:%u) intr_type %u, interrupt_id %u\n", + __func__, __LINE__, repo->bus_index, repo->dev_index, + intr_type, interrupt_id); + } + + for (res_index = 0; res_index < 10; res_index++) { + enum ps3_reg_type reg_type; + u64 bus_addr; + u64 len; + + result = ps3_repository_read_dev_reg(repo->bus_index, + repo->dev_index, res_index, ®_type, &bus_addr, &len); + + if (result) { + if (result != LV1_NO_ENTRY) + pr_debug("%s:%d ps3_repository_read_dev_reg" + " (%u:%u) failed\n", __func__, __LINE__, + repo->bus_index, repo->dev_index); + break; + } + + pr_debug("%s:%d (%u:%u) reg_type %u, bus_addr %lxh, len %lxh\n", + __func__, __LINE__, repo->bus_index, repo->dev_index, + reg_type, bus_addr, len); + } + + pr_debug(" <- %s:%d\n", __func__, __LINE__); + return result; +} + +static int dump_stor_dev_info(struct ps3_repository_device *repo) +{ + int result = 0; + unsigned int num_regions, region_index; + u64 port, blk_size, num_blocks; + + pr_debug(" -> %s:%d: (%u:%u)\n", __func__, __LINE__, + repo->bus_index, repo->dev_index); + + result = ps3_repository_read_stor_dev_info(repo->bus_index, + repo->dev_index, &port, &blk_size, &num_blocks, &num_regions); + if (result) { + pr_debug("%s:%d ps3_repository_read_stor_dev_info" + " (%u:%u) failed\n", __func__, __LINE__, + repo->bus_index, repo->dev_index); + goto out; + } + + pr_debug("%s:%d (%u:%u): port %lu, blk_size %lu, num_blocks " + "%lu, num_regions %u\n", + __func__, __LINE__, repo->bus_index, repo->dev_index, port, + blk_size, num_blocks, num_regions); + + for (region_index = 0; region_index < num_regions; region_index++) { + unsigned int region_id; + u64 region_start, region_size; + + result = ps3_repository_read_stor_dev_region(repo->bus_index, + repo->dev_index, region_index, ®ion_id, + ®ion_start, ®ion_size); + if (result) { + pr_debug("%s:%d ps3_repository_read_stor_dev_region" + " (%u:%u) failed\n", __func__, __LINE__, + repo->bus_index, repo->dev_index); + break; + } + + pr_debug("%s:%d (%u:%u) region_id %u, start %lxh, size %lxh\n", + __func__, __LINE__, repo->bus_index, repo->dev_index, + region_id, region_start, region_size); + } + +out: + pr_debug(" <- %s:%d\n", __func__, __LINE__); + return result; +} + +static int dump_device_info(struct ps3_repository_device *repo, + unsigned int num_dev) +{ + int result = 0; + + pr_debug(" -> %s:%d: bus_%u\n", __func__, __LINE__, repo->bus_index); + + for (repo->dev_index = 0; repo->dev_index < num_dev; + repo->dev_index++) { + + result = ps3_repository_read_dev_type(repo->bus_index, + repo->dev_index, &repo->dev_type); + + if (result) { + pr_debug("%s:%d ps3_repository_read_dev_type" + " (%u:%u) failed\n", __func__, __LINE__, + repo->bus_index, repo->dev_index); + break; + } + + result = ps3_repository_read_dev_id(repo->bus_index, + repo->dev_index, &repo->dev_id); + + if (result) { + pr_debug("%s:%d ps3_repository_read_dev_id" + " (%u:%u) failed\n", __func__, __LINE__, + repo->bus_index, repo->dev_index); + continue; + } + + pr_debug("%s:%d (%u:%u): dev_type %u, dev_id %u\n", __func__, + __LINE__, repo->bus_index, repo->dev_index, + repo->dev_type, repo->dev_id); + + ps3_repository_dump_resource_info(repo); + + if (repo->bus_type == PS3_BUS_TYPE_STORAGE) + dump_stor_dev_info(repo); + } + + pr_debug(" <- %s:%d\n", __func__, __LINE__); + return result; +} + +int ps3_repository_dump_bus_info(void) +{ + int result = 0; + struct ps3_repository_device repo; + + pr_debug(" -> %s:%d\n", __func__, __LINE__); + + memset(&repo, 0, sizeof(repo)); + + for (repo.bus_index = 0; repo.bus_index < 10; repo.bus_index++) { + unsigned int num_dev; + + result = ps3_repository_read_bus_type(repo.bus_index, + &repo.bus_type); + + if (result) { + pr_debug("%s:%d read_bus_type(%u) failed\n", + __func__, __LINE__, repo.bus_index); + break; + } + + result = ps3_repository_read_bus_id(repo.bus_index, + &repo.bus_id); + + if (result) { + pr_debug("%s:%d read_bus_id(%u) failed\n", + __func__, __LINE__, repo.bus_index); + continue; + } + + if (repo.bus_index != repo.bus_id) + pr_debug("%s:%d bus_index != bus_id\n", + __func__, __LINE__); + + result = ps3_repository_read_bus_num_dev(repo.bus_index, + &num_dev); + + if (result) { + pr_debug("%s:%d read_bus_num_dev(%u) failed\n", + __func__, __LINE__, repo.bus_index); + continue; + } + + pr_debug("%s:%d bus_%u: bus_type %u, bus_id %u, num_dev %u\n", + __func__, __LINE__, repo.bus_index, repo.bus_type, + repo.bus_id, num_dev); + + dump_device_info(&repo, num_dev); + } + + pr_debug(" <- %s:%d\n", __func__, __LINE__); + return result; +} + +#endif /* defined(DEBUG) */ diff --git a/arch/powerpc/platforms/ps3/setup.c b/arch/powerpc/platforms/ps3/setup.c index 935396766621..aa05288de64e 100644 --- a/arch/powerpc/platforms/ps3/setup.c +++ b/arch/powerpc/platforms/ps3/setup.c @@ -37,28 +37,36 @@ #include "platform.h" #if defined(DEBUG) -#define DBG(fmt...) udbg_printf(fmt) +#define DBG udbg_printf #else -#define DBG(fmt...) do{if(0)printk(fmt);}while(0) +#define DBG pr_debug #endif #if !defined(CONFIG_SMP) static void smp_send_stop(void) {} #endif -int ps3_get_firmware_version(union ps3_firmware_version *v) +static union ps3_firmware_version ps3_firmware_version; + +void ps3_get_firmware_version(union ps3_firmware_version *v) { - int result = lv1_get_version_info(&v->raw); - - if (result) { - v->raw = 0; - return -1; - } - - return result; + *v = ps3_firmware_version; } EXPORT_SYMBOL_GPL(ps3_get_firmware_version); +int ps3_compare_firmware_version(u16 major, u16 minor, u16 rev) +{ + union ps3_firmware_version x; + + x.pad = 0; + x.major = major; + x.minor = minor; + x.rev = rev; + + return (ps3_firmware_version.raw - x.raw); +} +EXPORT_SYMBOL_GPL(ps3_compare_firmware_version); + static void ps3_power_save(void) { /* @@ -99,7 +107,8 @@ static void ps3_panic(char *str) while(1); } -#ifdef CONFIG_FB_PS3 +#if defined(CONFIG_FB_PS3) || defined(CONFIG_FB_PS3_MODULE) || \ + defined(CONFIG_PS3_FLASH) || defined(CONFIG_PS3_FLASH_MODULE) static void prealloc(struct ps3_prealloc *p) { if (!p->size) @@ -115,12 +124,15 @@ static void prealloc(struct ps3_prealloc *p) printk(KERN_INFO "%s: %lu bytes at %p\n", p->name, p->size, p->address); } +#endif +#if defined(CONFIG_FB_PS3) || defined(CONFIG_FB_PS3_MODULE) struct ps3_prealloc ps3fb_videomemory = { - .name = "ps3fb videomemory", - .size = CONFIG_FB_PS3_DEFAULT_SIZE_M*1024*1024, - .align = 1024*1024 /* the GPU requires 1 MiB alignment */ + .name = "ps3fb videomemory", + .size = CONFIG_FB_PS3_DEFAULT_SIZE_M*1024*1024, + .align = 1024*1024 /* the GPU requires 1 MiB alignment */ }; +EXPORT_SYMBOL_GPL(ps3fb_videomemory); #define prealloc_ps3fb_videomemory() prealloc(&ps3fb_videomemory) static int __init early_parse_ps3fb(char *p) @@ -137,6 +149,30 @@ early_param("ps3fb", early_parse_ps3fb); #define prealloc_ps3fb_videomemory() do { } while (0) #endif +#if defined(CONFIG_PS3_FLASH) || defined(CONFIG_PS3_FLASH_MODULE) +struct ps3_prealloc ps3flash_bounce_buffer = { + .name = "ps3flash bounce buffer", + .size = 256*1024, + .align = 256*1024 +}; +EXPORT_SYMBOL_GPL(ps3flash_bounce_buffer); +#define prealloc_ps3flash_bounce_buffer() prealloc(&ps3flash_bounce_buffer) + +static int __init early_parse_ps3flash(char *p) +{ + if (!p) + return 1; + + if (!strcmp(p, "off")) + ps3flash_bounce_buffer.size = 0; + + return 0; +} +early_param("ps3flash", early_parse_ps3flash); +#else +#define prealloc_ps3flash_bounce_buffer() do { } while (0) +#endif + static int ps3_set_dabr(u64 dabr) { enum {DABR_USER = 1, DABR_KERNEL = 2,}; @@ -146,13 +182,13 @@ static int ps3_set_dabr(u64 dabr) static void __init ps3_setup_arch(void) { - union ps3_firmware_version v; DBG(" -> %s:%d\n", __func__, __LINE__); - ps3_get_firmware_version(&v); - printk(KERN_INFO "PS3 firmware version %u.%u.%u\n", v.major, v.minor, - v.rev); + lv1_get_version_info(&ps3_firmware_version.raw); + printk(KERN_INFO "PS3 firmware version %u.%u.%u\n", + ps3_firmware_version.major, ps3_firmware_version.minor, + ps3_firmware_version.rev); ps3_spu_set_platform(); ps3_map_htab(); @@ -166,6 +202,8 @@ static void __init ps3_setup_arch(void) #endif prealloc_ps3fb_videomemory(); + prealloc_ps3flash_bounce_buffer(); + ppc_md.power_save = ps3_power_save; DBG(" <- %s:%d\n", __func__, __LINE__); @@ -184,7 +222,7 @@ static int __init ps3_probe(void) DBG(" -> %s:%d\n", __func__, __LINE__); dt_root = of_get_flat_dt_root(); - if (!of_flat_dt_is_compatible(dt_root, "PS3")) + if (!of_flat_dt_is_compatible(dt_root, "sony,ps3")) return 0; powerpc_firmware_features |= FW_FEATURE_PS3_POSSIBLE; @@ -201,31 +239,12 @@ static int __init ps3_probe(void) #if defined(CONFIG_KEXEC) static void ps3_kexec_cpu_down(int crash_shutdown, int secondary) { - DBG(" -> %s:%d\n", __func__, __LINE__); + int cpu = smp_processor_id(); - if (secondary) { - int cpu; - for_each_online_cpu(cpu) - if (cpu) - ps3_smp_cleanup_cpu(cpu); - } else - ps3_smp_cleanup_cpu(0); + DBG(" -> %s:%d: (%d)\n", __func__, __LINE__, cpu); - DBG(" <- %s:%d\n", __func__, __LINE__); -} - -static void ps3_machine_kexec(struct kimage *image) -{ - unsigned long ppe_id; - - DBG(" -> %s:%d\n", __func__, __LINE__); - - lv1_get_logical_ppe_id(&ppe_id); - lv1_configure_irq_state_bitmap(ppe_id, 0, 0); - ps3_mm_shutdown(); - ps3_mm_vas_destroy(); - - default_machine_kexec(image); + ps3_smp_cleanup_cpu(cpu); + ps3_shutdown_IRQ(cpu); DBG(" <- %s:%d\n", __func__, __LINE__); } @@ -247,7 +266,7 @@ define_machine(ps3) { .power_off = ps3_power_off, #if defined(CONFIG_KEXEC) .kexec_cpu_down = ps3_kexec_cpu_down, - .machine_kexec = ps3_machine_kexec, + .machine_kexec = default_machine_kexec, .machine_kexec_prepare = default_machine_kexec_prepare, .machine_crash_shutdown = default_machine_crash_shutdown, #endif diff --git a/arch/powerpc/platforms/ps3/smp.c b/arch/powerpc/platforms/ps3/smp.c index 53416ec5198b..f0b12f212363 100644 --- a/arch/powerpc/platforms/ps3/smp.c +++ b/arch/powerpc/platforms/ps3/smp.c @@ -27,9 +27,9 @@ #include "platform.h" #if defined(DEBUG) -#define DBG(fmt...) udbg_printf(fmt) +#define DBG udbg_printf #else -#define DBG(fmt...) do{if(0)printk(fmt);}while(0) +#define DBG pr_debug #endif static irqreturn_t ipi_function_handler(int irq, void *msg) @@ -39,11 +39,11 @@ static irqreturn_t ipi_function_handler(int irq, void *msg) } /** - * virqs - a per cpu array of virqs for ipi use + * ps3_ipi_virqs - a per cpu array of virqs for ipi use */ #define MSG_COUNT 4 -static DEFINE_PER_CPU(unsigned int, virqs[MSG_COUNT]); +static DEFINE_PER_CPU(unsigned int, ps3_ipi_virqs[MSG_COUNT]); static const char *names[MSG_COUNT] = { "ipi call", @@ -62,7 +62,7 @@ static void do_message_pass(int target, int msg) return; } - virq = per_cpu(virqs, target)[msg]; + virq = per_cpu(ps3_ipi_virqs, target)[msg]; result = ps3_send_event_locally(virq); if (result) @@ -94,13 +94,13 @@ static int ps3_smp_probe(void) static void __init ps3_smp_setup_cpu(int cpu) { int result; - unsigned int *virqs = per_cpu(virqs, cpu); + unsigned int *virqs = per_cpu(ps3_ipi_virqs, cpu); int i; DBG(" -> %s:%d: (%d)\n", __func__, __LINE__, cpu); /* - * Check assumptions on virqs[] indexing. If this + * Check assumptions on ps3_ipi_virqs[] indexing. If this * check fails, then a different mapping of PPC_MSG_ * to index needs to be setup. */ @@ -132,13 +132,13 @@ static void __init ps3_smp_setup_cpu(int cpu) void ps3_smp_cleanup_cpu(int cpu) { - unsigned int *virqs = per_cpu(virqs, cpu); + unsigned int *virqs = per_cpu(ps3_ipi_virqs, cpu); int i; DBG(" -> %s:%d: (%d)\n", __func__, __LINE__, cpu); for (i = 0; i < MSG_COUNT; i++) { - free_irq(virqs[i], (void*)(long)i); + /* Can't call free_irq from interrupt context. */ ps3_event_receive_port_destroy(virqs[i]); virqs[i] = NO_IRQ; } diff --git a/arch/powerpc/platforms/ps3/spu.c b/arch/powerpc/platforms/ps3/spu.c index 651437cb2c18..c7f734c89462 100644 --- a/arch/powerpc/platforms/ps3/spu.c +++ b/arch/powerpc/platforms/ps3/spu.c @@ -182,15 +182,18 @@ static int __init setup_areas(struct spu *spu) { struct table {char* name; unsigned long addr; unsigned long size;}; - spu_pdata(spu)->shadow = __ioremap( - spu_pdata(spu)->shadow_addr, sizeof(struct spe_shadow), - pgprot_val(PAGE_READONLY) | _PAGE_NO_CACHE | _PAGE_GUARDED); + spu_pdata(spu)->shadow = ioremap_flags(spu_pdata(spu)->shadow_addr, + sizeof(struct spe_shadow), + pgprot_val(PAGE_READONLY) | + _PAGE_NO_CACHE); if (!spu_pdata(spu)->shadow) { pr_debug("%s:%d: ioremap shadow failed\n", __func__, __LINE__); goto fail_ioremap; } - spu->local_store = ioremap(spu->local_store_phys, LS_SIZE); + spu->local_store = (__force void *)ioremap_flags(spu->local_store_phys, + LS_SIZE, _PAGE_NO_CACHE); + if (!spu->local_store) { pr_debug("%s:%d: ioremap local_store failed\n", __func__, __LINE__); @@ -199,6 +202,7 @@ static int __init setup_areas(struct spu *spu) spu->problem = ioremap(spu->problem_phys, sizeof(struct spu_problem)); + if (!spu->problem) { pr_debug("%s:%d: ioremap problem failed\n", __func__, __LINE__); goto fail_ioremap; @@ -206,6 +210,7 @@ static int __init setup_areas(struct spu *spu) spu->priv2 = ioremap(spu_pdata(spu)->priv2_addr, sizeof(struct spu_priv2)); + if (!spu->priv2) { pr_debug("%s:%d: ioremap priv2 failed\n", __func__, __LINE__); goto fail_ioremap; diff --git a/arch/powerpc/platforms/ps3/system-bus.c b/arch/powerpc/platforms/ps3/system-bus.c index 6bda51027cc6..4bb634a17e43 100644 --- a/arch/powerpc/platforms/ps3/system-bus.c +++ b/arch/powerpc/platforms/ps3/system-bus.c @@ -30,22 +30,228 @@ #include "platform.h" +static struct device ps3_system_bus = { + .bus_id = "ps3_system", +}; + +/* FIXME: need device usage counters! */ +struct { + struct mutex mutex; + int sb_11; /* usb 0 */ + int sb_12; /* usb 0 */ + int gpu; +} static usage_hack; + +static int ps3_is_device(struct ps3_system_bus_device *dev, + unsigned int bus_id, unsigned int dev_id) +{ + return dev->bus_id == bus_id && dev->dev_id == dev_id; +} + +static int ps3_open_hv_device_sb(struct ps3_system_bus_device *dev) +{ + int result; + + BUG_ON(!dev->bus_id); + mutex_lock(&usage_hack.mutex); + + if (ps3_is_device(dev, 1, 1)) { + usage_hack.sb_11++; + if (usage_hack.sb_11 > 1) { + result = 0; + goto done; + } + } + + if (ps3_is_device(dev, 1, 2)) { + usage_hack.sb_12++; + if (usage_hack.sb_12 > 1) { + result = 0; + goto done; + } + } + + result = lv1_open_device(dev->bus_id, dev->dev_id, 0); + + if (result) { + pr_debug("%s:%d: lv1_open_device failed: %s\n", __func__, + __LINE__, ps3_result(result)); + result = -EPERM; + } + +done: + mutex_unlock(&usage_hack.mutex); + return result; +} + +static int ps3_close_hv_device_sb(struct ps3_system_bus_device *dev) +{ + int result; + + BUG_ON(!dev->bus_id); + mutex_lock(&usage_hack.mutex); + + if (ps3_is_device(dev, 1, 1)) { + usage_hack.sb_11--; + if (usage_hack.sb_11) { + result = 0; + goto done; + } + } + + if (ps3_is_device(dev, 1, 2)) { + usage_hack.sb_12--; + if (usage_hack.sb_12) { + result = 0; + goto done; + } + } + + result = lv1_close_device(dev->bus_id, dev->dev_id); + BUG_ON(result); + +done: + mutex_unlock(&usage_hack.mutex); + return result; +} + +static int ps3_open_hv_device_gpu(struct ps3_system_bus_device *dev) +{ + int result; + + mutex_lock(&usage_hack.mutex); + + usage_hack.gpu++; + if (usage_hack.gpu > 1) { + result = 0; + goto done; + } + + result = lv1_gpu_open(0); + + if (result) { + pr_debug("%s:%d: lv1_gpu_open failed: %s\n", __func__, + __LINE__, ps3_result(result)); + result = -EPERM; + } + +done: + mutex_unlock(&usage_hack.mutex); + return result; +} + +static int ps3_close_hv_device_gpu(struct ps3_system_bus_device *dev) +{ + int result; + + mutex_lock(&usage_hack.mutex); + + usage_hack.gpu--; + if (usage_hack.gpu) { + result = 0; + goto done; + } + + result = lv1_gpu_close(); + BUG_ON(result); + +done: + mutex_unlock(&usage_hack.mutex); + return result; +} + +int ps3_open_hv_device(struct ps3_system_bus_device *dev) +{ + BUG_ON(!dev); + pr_debug("%s:%d: match_id: %u\n", __func__, __LINE__, dev->match_id); + + switch (dev->match_id) { + case PS3_MATCH_ID_EHCI: + case PS3_MATCH_ID_OHCI: + case PS3_MATCH_ID_GELIC: + case PS3_MATCH_ID_STOR_DISK: + case PS3_MATCH_ID_STOR_ROM: + case PS3_MATCH_ID_STOR_FLASH: + return ps3_open_hv_device_sb(dev); + + case PS3_MATCH_ID_SOUND: + case PS3_MATCH_ID_GRAPHICS: + return ps3_open_hv_device_gpu(dev); + + case PS3_MATCH_ID_AV_SETTINGS: + case PS3_MATCH_ID_SYSTEM_MANAGER: + pr_debug("%s:%d: unsupported match_id: %u\n", __func__, + __LINE__, dev->match_id); + pr_debug("%s:%d: bus_id: %u\n", __func__, + __LINE__, dev->bus_id); + BUG(); + return -EINVAL; + + default: + break; + } + + pr_debug("%s:%d: unknown match_id: %u\n", __func__, __LINE__, + dev->match_id); + BUG(); + return -ENODEV; +} +EXPORT_SYMBOL_GPL(ps3_open_hv_device); + +int ps3_close_hv_device(struct ps3_system_bus_device *dev) +{ + BUG_ON(!dev); + pr_debug("%s:%d: match_id: %u\n", __func__, __LINE__, dev->match_id); + + switch (dev->match_id) { + case PS3_MATCH_ID_EHCI: + case PS3_MATCH_ID_OHCI: + case PS3_MATCH_ID_GELIC: + case PS3_MATCH_ID_STOR_DISK: + case PS3_MATCH_ID_STOR_ROM: + case PS3_MATCH_ID_STOR_FLASH: + return ps3_close_hv_device_sb(dev); + + case PS3_MATCH_ID_SOUND: + case PS3_MATCH_ID_GRAPHICS: + return ps3_close_hv_device_gpu(dev); + + case PS3_MATCH_ID_AV_SETTINGS: + case PS3_MATCH_ID_SYSTEM_MANAGER: + pr_debug("%s:%d: unsupported match_id: %u\n", __func__, + __LINE__, dev->match_id); + pr_debug("%s:%d: bus_id: %u\n", __func__, + __LINE__, dev->bus_id); + BUG(); + return -EINVAL; + + default: + break; + } + + pr_debug("%s:%d: unknown match_id: %u\n", __func__, __LINE__, + dev->match_id); + BUG(); + return -ENODEV; +} +EXPORT_SYMBOL_GPL(ps3_close_hv_device); + #define dump_mmio_region(_a) _dump_mmio_region(_a, __func__, __LINE__) static void _dump_mmio_region(const struct ps3_mmio_region* r, const char* func, int line) { - pr_debug("%s:%d: dev %u:%u\n", func, line, r->did.bus_id, - r->did.dev_id); + pr_debug("%s:%d: dev %u:%u\n", func, line, r->dev->bus_id, + r->dev->dev_id); pr_debug("%s:%d: bus_addr %lxh\n", func, line, r->bus_addr); pr_debug("%s:%d: len %lxh\n", func, line, r->len); pr_debug("%s:%d: lpar_addr %lxh\n", func, line, r->lpar_addr); } -int ps3_mmio_region_create(struct ps3_mmio_region *r) +static int ps3_sb_mmio_region_create(struct ps3_mmio_region *r) { int result; - result = lv1_map_device_mmio_region(r->did.bus_id, r->did.dev_id, + result = lv1_map_device_mmio_region(r->dev->bus_id, r->dev->dev_id, r->bus_addr, r->len, r->page_size, &r->lpar_addr); if (result) { @@ -57,13 +263,26 @@ int ps3_mmio_region_create(struct ps3_mmio_region *r) dump_mmio_region(r); return result; } + +static int ps3_ioc0_mmio_region_create(struct ps3_mmio_region *r) +{ + /* device specific; do nothing currently */ + return 0; +} + +int ps3_mmio_region_create(struct ps3_mmio_region *r) +{ + return r->mmio_ops->create(r); +} EXPORT_SYMBOL_GPL(ps3_mmio_region_create); -int ps3_free_mmio_region(struct ps3_mmio_region *r) +static int ps3_sb_free_mmio_region(struct ps3_mmio_region *r) { int result; - result = lv1_unmap_device_mmio_region(r->did.bus_id, r->did.dev_id, + dump_mmio_region(r); +; + result = lv1_unmap_device_mmio_region(r->dev->bus_id, r->dev->dev_id, r->lpar_addr); if (result) @@ -73,14 +292,60 @@ int ps3_free_mmio_region(struct ps3_mmio_region *r) r->lpar_addr = 0; return result; } + +static int ps3_ioc0_free_mmio_region(struct ps3_mmio_region *r) +{ + /* device specific; do nothing currently */ + return 0; +} + + +int ps3_free_mmio_region(struct ps3_mmio_region *r) +{ + return r->mmio_ops->free(r); +} + EXPORT_SYMBOL_GPL(ps3_free_mmio_region); +static const struct ps3_mmio_region_ops ps3_mmio_sb_region_ops = { + .create = ps3_sb_mmio_region_create, + .free = ps3_sb_free_mmio_region +}; + +static const struct ps3_mmio_region_ops ps3_mmio_ioc0_region_ops = { + .create = ps3_ioc0_mmio_region_create, + .free = ps3_ioc0_free_mmio_region +}; + +int ps3_mmio_region_init(struct ps3_system_bus_device *dev, + struct ps3_mmio_region *r, unsigned long bus_addr, unsigned long len, + enum ps3_mmio_page_size page_size) +{ + r->dev = dev; + r->bus_addr = bus_addr; + r->len = len; + r->page_size = page_size; + switch (dev->dev_type) { + case PS3_DEVICE_TYPE_SB: + r->mmio_ops = &ps3_mmio_sb_region_ops; + break; + case PS3_DEVICE_TYPE_IOC0: + r->mmio_ops = &ps3_mmio_ioc0_region_ops; + break; + default: + BUG(); + return -EINVAL; + } + return 0; +} +EXPORT_SYMBOL_GPL(ps3_mmio_region_init); + static int ps3_system_bus_match(struct device *_dev, struct device_driver *_drv) { int result; - struct ps3_system_bus_driver *drv = to_ps3_system_bus_driver(_drv); - struct ps3_system_bus_device *dev = to_ps3_system_bus_device(_dev); + struct ps3_system_bus_driver *drv = ps3_drv_to_system_bus_drv(_drv); + struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); result = dev->match_id == drv->match_id; @@ -92,32 +357,14 @@ static int ps3_system_bus_match(struct device *_dev, static int ps3_system_bus_probe(struct device *_dev) { - int result; - struct ps3_system_bus_device *dev = to_ps3_system_bus_device(_dev); - struct ps3_system_bus_driver *drv = - to_ps3_system_bus_driver(_dev->driver); + int result = 0; + struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); + struct ps3_system_bus_driver *drv; - result = lv1_open_device(dev->did.bus_id, dev->did.dev_id, 0); - - if (result) { - pr_debug("%s:%d: lv1_open_device failed (%d)\n", - __func__, __LINE__, result); - result = -EACCES; - goto clean_none; - } - - if (dev->d_region->did.bus_id) { - result = ps3_dma_region_create(dev->d_region); - - if (result) { - pr_debug("%s:%d: ps3_dma_region_create failed (%d)\n", - __func__, __LINE__, result); - BUG_ON("check region type"); - result = -EINVAL; - goto clean_device; - } - } + BUG_ON(!dev); + pr_info(" -> %s:%d: %s\n", __func__, __LINE__, _dev->bus_id); + drv = ps3_system_bus_dev_to_system_bus_drv(dev); BUG_ON(!drv); if (drv->probe) @@ -126,56 +373,127 @@ static int ps3_system_bus_probe(struct device *_dev) pr_info("%s:%d: %s no probe method\n", __func__, __LINE__, dev->core.bus_id); - if (result) { - pr_debug("%s:%d: drv->probe failed\n", __func__, __LINE__); - goto clean_dma; - } - - return result; - -clean_dma: - ps3_dma_region_free(dev->d_region); -clean_device: - lv1_close_device(dev->did.bus_id, dev->did.dev_id); -clean_none: + pr_info(" <- %s:%d: %s\n", __func__, __LINE__, dev->core.bus_id); return result; } static int ps3_system_bus_remove(struct device *_dev) { - struct ps3_system_bus_device *dev = to_ps3_system_bus_device(_dev); - struct ps3_system_bus_driver *drv = - to_ps3_system_bus_driver(_dev->driver); + int result = 0; + struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); + struct ps3_system_bus_driver *drv; + + BUG_ON(!dev); + pr_info(" -> %s:%d: %s\n", __func__, __LINE__, _dev->bus_id); + + drv = ps3_system_bus_dev_to_system_bus_drv(dev); + BUG_ON(!drv); if (drv->remove) - drv->remove(dev); + result = drv->remove(dev); else - pr_info("%s:%d: %s no remove method\n", __func__, __LINE__, - dev->core.bus_id); + dev_dbg(&dev->core, "%s:%d %s: no remove method\n", + __func__, __LINE__, drv->core.name); - ps3_dma_region_free(dev->d_region); - ps3_free_mmio_region(dev->m_region); - lv1_close_device(dev->did.bus_id, dev->did.dev_id); + pr_info(" <- %s:%d: %s\n", __func__, __LINE__, dev->core.bus_id); + return result; +} +static void ps3_system_bus_shutdown(struct device *_dev) +{ + struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); + struct ps3_system_bus_driver *drv; + + BUG_ON(!dev); + + dev_dbg(&dev->core, " -> %s:%d: match_id %d\n", __func__, __LINE__, + dev->match_id); + + if (!dev->core.driver) { + dev_dbg(&dev->core, "%s:%d: no driver bound\n", __func__, + __LINE__); + return; + } + + drv = ps3_system_bus_dev_to_system_bus_drv(dev); + + BUG_ON(!drv); + + dev_dbg(&dev->core, "%s:%d: %s -> %s\n", __func__, __LINE__, + dev->core.bus_id, drv->core.name); + + if (drv->shutdown) + drv->shutdown(dev); + else if (drv->remove) { + dev_dbg(&dev->core, "%s:%d %s: no shutdown, calling remove\n", + __func__, __LINE__, drv->core.name); + drv->remove(dev); + } else { + dev_dbg(&dev->core, "%s:%d %s: no shutdown method\n", + __func__, __LINE__, drv->core.name); + BUG(); + } + + dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__); +} + +static int ps3_system_bus_uevent(struct device *_dev, char **envp, + int num_envp, char *buffer, int buffer_size) +{ + struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); + int i = 0, length = 0; + + if (add_uevent_var(envp, num_envp, &i, buffer, buffer_size, + &length, "MODALIAS=ps3:%d", + dev->match_id)) + return -ENOMEM; + + envp[i] = NULL; return 0; } +static ssize_t modalias_show(struct device *_dev, struct device_attribute *a, + char *buf) +{ + struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); + int len = snprintf(buf, PAGE_SIZE, "ps3:%d\n", dev->match_id); + + return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len; +} + +static struct device_attribute ps3_system_bus_dev_attrs[] = { + __ATTR_RO(modalias), + __ATTR_NULL, +}; + struct bus_type ps3_system_bus_type = { .name = "ps3_system_bus", .match = ps3_system_bus_match, + .uevent = ps3_system_bus_uevent, .probe = ps3_system_bus_probe, .remove = ps3_system_bus_remove, + .shutdown = ps3_system_bus_shutdown, + .dev_attrs = ps3_system_bus_dev_attrs, }; -int __init ps3_system_bus_init(void) +static int __init ps3_system_bus_init(void) { int result; if (!firmware_has_feature(FW_FEATURE_PS3_LV1)) return -ENODEV; + pr_debug(" -> %s:%d\n", __func__, __LINE__); + + mutex_init(&usage_hack.mutex); + + result = device_register(&ps3_system_bus); + BUG_ON(result); + result = bus_register(&ps3_system_bus_type); BUG_ON(result); + + pr_debug(" <- %s:%d\n", __func__, __LINE__); return result; } @@ -185,16 +503,13 @@ core_initcall(ps3_system_bus_init); * Returns the virtual address of the buffer and sets dma_handle * to the dma address (mapping) of the first page. */ - static void * ps3_alloc_coherent(struct device *_dev, size_t size, - dma_addr_t *dma_handle, gfp_t flag) + dma_addr_t *dma_handle, gfp_t flag) { int result; - struct ps3_system_bus_device *dev = to_ps3_system_bus_device(_dev); + struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); unsigned long virt_addr; - BUG_ON(!dev->d_region->bus_addr); - flag &= ~(__GFP_DMA | __GFP_HIGHMEM); flag |= __GFP_ZERO; @@ -205,7 +520,8 @@ static void * ps3_alloc_coherent(struct device *_dev, size_t size, goto clean_none; } - result = ps3_dma_map(dev->d_region, virt_addr, size, dma_handle); + result = ps3_dma_map(dev->d_region, virt_addr, size, dma_handle, + IOPTE_PP_W | IOPTE_PP_R | IOPTE_SO_RW | IOPTE_M); if (result) { pr_debug("%s:%d: ps3_dma_map failed (%d)\n", @@ -226,7 +542,7 @@ clean_none: static void ps3_free_coherent(struct device *_dev, size_t size, void *vaddr, dma_addr_t dma_handle) { - struct ps3_system_bus_device *dev = to_ps3_system_bus_device(_dev); + struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); ps3_dma_unmap(dev->d_region, dma_handle, size); free_pages((unsigned long)vaddr, get_order(size)); @@ -239,15 +555,16 @@ static void ps3_free_coherent(struct device *_dev, size_t size, void *vaddr, * byte within the page as vaddr. */ -static dma_addr_t ps3_map_single(struct device *_dev, void *ptr, size_t size, +static dma_addr_t ps3_sb_map_single(struct device *_dev, void *ptr, size_t size, enum dma_data_direction direction) { - struct ps3_system_bus_device *dev = to_ps3_system_bus_device(_dev); + struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); int result; unsigned long bus_addr; result = ps3_dma_map(dev->d_region, (unsigned long)ptr, size, - &bus_addr); + &bus_addr, + IOPTE_PP_R | IOPTE_PP_W | IOPTE_SO_RW | IOPTE_M); if (result) { pr_debug("%s:%d: ps3_dma_map failed (%d)\n", @@ -257,10 +574,44 @@ static dma_addr_t ps3_map_single(struct device *_dev, void *ptr, size_t size, return bus_addr; } +static dma_addr_t ps3_ioc0_map_single(struct device *_dev, void *ptr, + size_t size, + enum dma_data_direction direction) +{ + struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); + int result; + unsigned long bus_addr; + u64 iopte_flag; + + iopte_flag = IOPTE_M; + switch (direction) { + case DMA_BIDIRECTIONAL: + iopte_flag |= IOPTE_PP_R | IOPTE_PP_W | IOPTE_SO_RW; + break; + case DMA_TO_DEVICE: + iopte_flag |= IOPTE_PP_R | IOPTE_SO_R; + break; + case DMA_FROM_DEVICE: + iopte_flag |= IOPTE_PP_W | IOPTE_SO_RW; + break; + default: + /* not happned */ + BUG(); + }; + result = ps3_dma_map(dev->d_region, (unsigned long)ptr, size, + &bus_addr, iopte_flag); + + if (result) { + pr_debug("%s:%d: ps3_dma_map failed (%d)\n", + __func__, __LINE__, result); + } + return bus_addr; +} + static void ps3_unmap_single(struct device *_dev, dma_addr_t dma_addr, size_t size, enum dma_data_direction direction) { - struct ps3_system_bus_device *dev = to_ps3_system_bus_device(_dev); + struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); int result; result = ps3_dma_unmap(dev->d_region, dma_addr, size); @@ -271,20 +622,20 @@ static void ps3_unmap_single(struct device *_dev, dma_addr_t dma_addr, } } -static int ps3_map_sg(struct device *_dev, struct scatterlist *sg, int nents, +static int ps3_sb_map_sg(struct device *_dev, struct scatterlist *sg, int nents, enum dma_data_direction direction) { #if defined(CONFIG_PS3_DYNAMIC_DMA) BUG_ON("do"); return -EPERM; #else - struct ps3_system_bus_device *dev = to_ps3_system_bus_device(_dev); + struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); int i; for (i = 0; i < nents; i++, sg++) { int result = ps3_dma_map(dev->d_region, page_to_phys(sg->page) + sg->offset, sg->length, - &sg->dma_address); + &sg->dma_address, 0); if (result) { pr_debug("%s:%d: ps3_dma_map failed (%d)\n", @@ -299,7 +650,15 @@ static int ps3_map_sg(struct device *_dev, struct scatterlist *sg, int nents, #endif } -static void ps3_unmap_sg(struct device *_dev, struct scatterlist *sg, +static int ps3_ioc0_map_sg(struct device *_dev, struct scatterlist *sg, + int nents, + enum dma_data_direction direction) +{ + BUG(); + return 0; +} + +static void ps3_sb_unmap_sg(struct device *_dev, struct scatterlist *sg, int nents, enum dma_data_direction direction) { #if defined(CONFIG_PS3_DYNAMIC_DMA) @@ -307,18 +666,34 @@ static void ps3_unmap_sg(struct device *_dev, struct scatterlist *sg, #endif } +static void ps3_ioc0_unmap_sg(struct device *_dev, struct scatterlist *sg, + int nents, enum dma_data_direction direction) +{ + BUG(); +} + static int ps3_dma_supported(struct device *_dev, u64 mask) { return mask >= DMA_32BIT_MASK; } -static struct dma_mapping_ops ps3_dma_ops = { +static struct dma_mapping_ops ps3_sb_dma_ops = { .alloc_coherent = ps3_alloc_coherent, .free_coherent = ps3_free_coherent, - .map_single = ps3_map_single, + .map_single = ps3_sb_map_single, .unmap_single = ps3_unmap_single, - .map_sg = ps3_map_sg, - .unmap_sg = ps3_unmap_sg, + .map_sg = ps3_sb_map_sg, + .unmap_sg = ps3_sb_unmap_sg, + .dma_supported = ps3_dma_supported +}; + +static struct dma_mapping_ops ps3_ioc0_dma_ops = { + .alloc_coherent = ps3_alloc_coherent, + .free_coherent = ps3_free_coherent, + .map_single = ps3_ioc0_map_single, + .unmap_single = ps3_unmap_single, + .map_sg = ps3_ioc0_map_sg, + .unmap_sg = ps3_ioc0_unmap_sg, .dma_supported = ps3_dma_supported }; @@ -328,7 +703,7 @@ static struct dma_mapping_ops ps3_dma_ops = { static void ps3_system_bus_release_device(struct device *_dev) { - struct ps3_system_bus_device *dev = to_ps3_system_bus_device(_dev); + struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); kfree(dev); } @@ -343,18 +718,37 @@ static void ps3_system_bus_release_device(struct device *_dev) int ps3_system_bus_device_register(struct ps3_system_bus_device *dev) { int result; - static unsigned int dev_count = 1; + static unsigned int dev_ioc0_count; + static unsigned int dev_sb_count; + static unsigned int dev_vuart_count; - dev->core.parent = NULL; + if (!dev->core.parent) + dev->core.parent = &ps3_system_bus; dev->core.bus = &ps3_system_bus_type; dev->core.release = ps3_system_bus_release_device; - dev->core.archdata.of_node = NULL; - dev->core.archdata.dma_ops = &ps3_dma_ops; - dev->core.archdata.numa_node = 0; + switch (dev->dev_type) { + case PS3_DEVICE_TYPE_IOC0: + dev->core.archdata.dma_ops = &ps3_ioc0_dma_ops; + snprintf(dev->core.bus_id, sizeof(dev->core.bus_id), + "ioc0_%02x", ++dev_ioc0_count); + break; + case PS3_DEVICE_TYPE_SB: + dev->core.archdata.dma_ops = &ps3_sb_dma_ops; + snprintf(dev->core.bus_id, sizeof(dev->core.bus_id), + "sb_%02x", ++dev_sb_count); - snprintf(dev->core.bus_id, sizeof(dev->core.bus_id), "sb_%02x", - dev_count++); + break; + case PS3_DEVICE_TYPE_VUART: + snprintf(dev->core.bus_id, sizeof(dev->core.bus_id), + "vuart_%02x", ++dev_vuart_count); + break; + default: + BUG(); + }; + + dev->core.archdata.of_node = NULL; + dev->core.archdata.numa_node = 0; pr_debug("%s:%d add %s\n", __func__, __LINE__, dev->core.bus_id); @@ -368,9 +762,15 @@ int ps3_system_bus_driver_register(struct ps3_system_bus_driver *drv) { int result; + pr_debug(" -> %s:%d: %s\n", __func__, __LINE__, drv->core.name); + + if (!firmware_has_feature(FW_FEATURE_PS3_LV1)) + return -ENODEV; + drv->core.bus = &ps3_system_bus_type; result = driver_register(&drv->core); + pr_debug(" <- %s:%d: %s\n", __func__, __LINE__, drv->core.name); return result; } @@ -378,7 +778,9 @@ EXPORT_SYMBOL_GPL(ps3_system_bus_driver_register); void ps3_system_bus_driver_unregister(struct ps3_system_bus_driver *drv) { + pr_debug(" -> %s:%d: %s\n", __func__, __LINE__, drv->core.name); driver_unregister(&drv->core); + pr_debug(" <- %s:%d: %s\n", __func__, __LINE__, drv->core.name); } EXPORT_SYMBOL_GPL(ps3_system_bus_driver_unregister); diff --git a/arch/powerpc/platforms/ps3/time.c b/arch/powerpc/platforms/ps3/time.c index 1bae8b19b363..802a9ccacb5e 100644 --- a/arch/powerpc/platforms/ps3/time.c +++ b/arch/powerpc/platforms/ps3/time.c @@ -39,7 +39,7 @@ static void _dump_tm(const struct rtc_time *tm, const char* func, int line) } #define dump_time(_a) _dump_time(_a, __func__, __LINE__) -static void __attribute__ ((unused)) _dump_time(int time, const char* func, +static void __maybe_unused _dump_time(int time, const char *func, int line) { struct rtc_time tm; diff --git a/arch/powerpc/platforms/pseries/Makefile b/arch/powerpc/platforms/pseries/Makefile index ae1fc92dc1c9..992ba6753cf2 100644 --- a/arch/powerpc/platforms/pseries/Makefile +++ b/arch/powerpc/platforms/pseries/Makefile @@ -8,7 +8,7 @@ obj-y := lpar.o hvCall.o nvram.o reconfig.o \ obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_XICS) += xics.o obj-$(CONFIG_SCANLOG) += scanlog.o -obj-$(CONFIG_EEH) += eeh.o eeh_cache.o eeh_driver.o eeh_event.o +obj-$(CONFIG_EEH) += eeh.o eeh_cache.o eeh_driver.o eeh_event.o eeh_sysfs.o obj-$(CONFIG_KEXEC) += kexec.o obj-$(CONFIG_PCI) += pci.o pci_dlpar.o obj-$(CONFIG_PCI_MSI) += msi.o diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c index 5f3e6d8659fe..b8770395013d 100644 --- a/arch/powerpc/platforms/pseries/eeh.c +++ b/arch/powerpc/platforms/pseries/eeh.c @@ -1,6 +1,8 @@ /* * eeh.c - * Copyright (C) 2001 Dave Engebretsen & Todd Inglett IBM Corporation + * Copyright IBM Corporation 2001, 2005, 2006 + * Copyright Dave Engebretsen & Todd Inglett 2001 + * Copyright Linas Vepstas 2005, 2006 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,6 +17,8 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Please address comments and feedback to Linas Vepstas */ #include @@ -117,7 +121,6 @@ static unsigned long no_cfg_addr; static unsigned long ignored_check; static unsigned long total_mmio_ffs; static unsigned long false_positives; -static unsigned long ignored_failures; static unsigned long slot_resets; #define IS_BRIDGE(class_code) (((class_code)<<16) == PCI_BASE_CLASS_BRIDGE) @@ -505,6 +508,7 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev) printk(KERN_WARNING "EEH: read_slot_reset_state() failed; rc=%d dn=%s\n", ret, dn->full_name); false_positives++; + pdn->eeh_false_positives ++; rc = 0; goto dn_unlock; } @@ -513,6 +517,7 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev) * they are empty when they don't have children. */ if ((rets[0] == 5) && (dn->child == NULL)) { false_positives++; + pdn->eeh_false_positives ++; rc = 0; goto dn_unlock; } @@ -522,6 +527,7 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev) printk(KERN_WARNING "EEH: event on unsupported device, rc=%d dn=%s\n", ret, dn->full_name); false_positives++; + pdn->eeh_false_positives ++; rc = 0; goto dn_unlock; } @@ -529,6 +535,7 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev) /* If not the kind of error we know about, punt. */ if (rets[0] != 1 && rets[0] != 2 && rets[0] != 4 && rets[0] != 5) { false_positives++; + pdn->eeh_false_positives ++; rc = 0; goto dn_unlock; } @@ -921,6 +928,7 @@ static void *early_enable_eeh(struct device_node *dn, void *data) pdn->eeh_mode = 0; pdn->eeh_check_count = 0; pdn->eeh_freeze_count = 0; + pdn->eeh_false_positives = 0; if (status && strcmp(status, "ok") != 0) return NULL; /* ignore devices with bad status */ @@ -1139,7 +1147,8 @@ static void eeh_add_device_late(struct pci_dev *dev) pdn = PCI_DN(dn); pdn->pcidev = dev; - pci_addr_cache_insert_device (dev); + pci_addr_cache_insert_device(dev); + eeh_sysfs_add_device(dev); } void eeh_add_device_tree_late(struct pci_bus *bus) @@ -1178,6 +1187,7 @@ static void eeh_remove_device(struct pci_dev *dev) printk(KERN_DEBUG "EEH: remove device %s\n", pci_name(dev)); #endif pci_addr_cache_remove_device(dev); + eeh_sysfs_remove_device(dev); dn = pci_device_to_OF_node(dev); if (PCI_DN(dn)->pcidev) { @@ -1214,11 +1224,10 @@ static int proc_eeh_show(struct seq_file *m, void *v) "check not wanted=%ld\n" "eeh_total_mmio_ffs=%ld\n" "eeh_false_positives=%ld\n" - "eeh_ignored_failures=%ld\n" "eeh_slot_resets=%ld\n", no_device, no_dn, no_cfg_addr, ignored_check, total_mmio_ffs, - false_positives, ignored_failures, + false_positives, slot_resets); } diff --git a/arch/powerpc/platforms/pseries/eeh_cache.c b/arch/powerpc/platforms/pseries/eeh_cache.c index f2bae04424f8..e49c815eae23 100644 --- a/arch/powerpc/platforms/pseries/eeh_cache.c +++ b/arch/powerpc/platforms/pseries/eeh_cache.c @@ -2,7 +2,8 @@ * eeh_cache.c * PCI address cache; allows the lookup of PCI devices based on I/O address * - * Copyright (C) 2004 Linas Vepstas IBM Corporation + * Copyright IBM Corporation 2004 + * Copyright Linas Vepstas 2004 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -295,6 +296,8 @@ void __init pci_addr_cache_build(void) continue; pci_dev_get (dev); /* matching put is in eeh_remove_device() */ PCI_DN(dn)->pcidev = dev; + + eeh_sysfs_add_device(dev); } #ifdef DEBUG diff --git a/arch/powerpc/platforms/pseries/eeh_driver.c b/arch/powerpc/platforms/pseries/eeh_driver.c index 161a5844ab6c..15e015ef6865 100644 --- a/arch/powerpc/platforms/pseries/eeh_driver.c +++ b/arch/powerpc/platforms/pseries/eeh_driver.c @@ -1,6 +1,7 @@ /* * PCI Error Recovery Driver for RPA-compliant PPC64 platform. - * Copyright (C) 2004, 2005 Linas Vepstas + * Copyright IBM Corp. 2004 2005 + * Copyright Linas Vepstas 2004, 2005 * * All rights reserved. * @@ -19,8 +20,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * - * Send feedback to - * + * Send comments and feedback to Linas Vepstas */ #include #include diff --git a/arch/powerpc/platforms/pseries/eeh_sysfs.c b/arch/powerpc/platforms/pseries/eeh_sysfs.c new file mode 100644 index 000000000000..15e13b568904 --- /dev/null +++ b/arch/powerpc/platforms/pseries/eeh_sysfs.c @@ -0,0 +1,87 @@ +/* + * Sysfs entries for PCI Error Recovery for PAPR-compliant platform. + * Copyright IBM Corporation 2007 + * Copyright Linas Vepstas 2007 + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Send comments and feedback to Linas Vepstas + */ +#include +#include +#include +#include + +/** + * EEH_SHOW_ATTR -- create sysfs entry for eeh statistic + * @_name: name of file in sysfs directory + * @_memb: name of member in struct pci_dn to access + * @_format: printf format for display + * + * All of the attributes look very similar, so just + * auto-gen a cut-n-paste routine to display them. + */ +#define EEH_SHOW_ATTR(_name,_memb,_format) \ +static ssize_t eeh_show_##_name(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct pci_dev *pdev = to_pci_dev(dev); \ + struct device_node *dn = pci_device_to_OF_node(pdev); \ + struct pci_dn *pdn; \ + \ + if (!dn || PCI_DN(dn) == NULL) \ + return 0; \ + \ + pdn = PCI_DN(dn); \ + return sprintf(buf, _format "\n", pdn->_memb); \ +} \ +static DEVICE_ATTR(_name, S_IRUGO, eeh_show_##_name, NULL); + + +EEH_SHOW_ATTR(eeh_mode, eeh_mode, "0x%x"); +EEH_SHOW_ATTR(eeh_config_addr, eeh_config_addr, "0x%x"); +EEH_SHOW_ATTR(eeh_pe_config_addr, eeh_pe_config_addr, "0x%x"); +EEH_SHOW_ATTR(eeh_check_count, eeh_check_count, "%d"); +EEH_SHOW_ATTR(eeh_freeze_count, eeh_freeze_count, "%d"); +EEH_SHOW_ATTR(eeh_false_positives, eeh_false_positives, "%d"); + +void eeh_sysfs_add_device(struct pci_dev *pdev) +{ + int rc=0; + + rc += device_create_file(&pdev->dev, &dev_attr_eeh_mode); + rc += device_create_file(&pdev->dev, &dev_attr_eeh_config_addr); + rc += device_create_file(&pdev->dev, &dev_attr_eeh_pe_config_addr); + rc += device_create_file(&pdev->dev, &dev_attr_eeh_check_count); + rc += device_create_file(&pdev->dev, &dev_attr_eeh_false_positives); + rc += device_create_file(&pdev->dev, &dev_attr_eeh_freeze_count); + + if (rc) + printk(KERN_WARNING "EEH: Unable to create sysfs entries\n"); +} + +void eeh_sysfs_remove_device(struct pci_dev *pdev) +{ + device_remove_file(&pdev->dev, &dev_attr_eeh_mode); + device_remove_file(&pdev->dev, &dev_attr_eeh_config_addr); + device_remove_file(&pdev->dev, &dev_attr_eeh_pe_config_addr); + device_remove_file(&pdev->dev, &dev_attr_eeh_check_count); + device_remove_file(&pdev->dev, &dev_attr_eeh_false_positives); + device_remove_file(&pdev->dev, &dev_attr_eeh_freeze_count); +} + diff --git a/arch/powerpc/platforms/pseries/lpar.c b/arch/powerpc/platforms/pseries/lpar.c index 362dfbc260a6..8cc6eeeaae2f 100644 --- a/arch/powerpc/platforms/pseries/lpar.c +++ b/arch/powerpc/platforms/pseries/lpar.c @@ -373,12 +373,23 @@ static void pSeries_lpar_hptab_clear(void) { unsigned long size_bytes = 1UL << ppc64_pft_size; unsigned long hpte_count = size_bytes >> 4; - unsigned long dummy1, dummy2; + unsigned long dummy1, dummy2, dword0; + long lpar_rc; int i; /* TODO: Use bulk call */ - for (i = 0; i < hpte_count; i++) - plpar_pte_remove_raw(0, i, 0, &dummy1, &dummy2); + for (i = 0; i < hpte_count; i++) { + /* dont remove HPTEs with VRMA mappings */ + lpar_rc = plpar_pte_remove_raw(H_ANDCOND, i, HPTE_V_1TB_SEG, + &dummy1, &dummy2); + if (lpar_rc == H_NOT_FOUND) { + lpar_rc = plpar_pte_read_raw(0, i, &dword0, &dummy1); + if (!lpar_rc && ((dword0 & HPTE_V_VRMA_MASK) + != HPTE_V_VRMA_MASK)) + /* Can be hpte for 1TB Seg. So remove it */ + plpar_pte_remove_raw(0, i, 0, &dummy1, &dummy2); + } + } } /* diff --git a/arch/powerpc/platforms/pseries/pci_dlpar.c b/arch/powerpc/platforms/pseries/pci_dlpar.c index ffaf6c5c517b..47f0e0857f0e 100644 --- a/arch/powerpc/platforms/pseries/pci_dlpar.c +++ b/arch/powerpc/platforms/pseries/pci_dlpar.c @@ -110,8 +110,6 @@ pcibios_fixup_new_pci_devices(struct pci_bus *bus, int fix_bus) } } } - - eeh_add_device_tree_late(bus); } EXPORT_SYMBOL_GPL(pcibios_fixup_new_pci_devices); @@ -139,6 +137,8 @@ pcibios_pci_config_bridge(struct pci_dev *dev) /* Make the discovered devices available */ pci_bus_add_devices(child_bus); + + eeh_add_device_tree_late(child_bus); return 0; } @@ -171,6 +171,7 @@ pcibios_add_pci_devices(struct pci_bus * bus) if (!list_empty(&bus->devices)) { pcibios_fixup_new_pci_devices(bus, 0); pci_bus_add_devices(bus); + eeh_add_device_tree_late(bus); } } else if (mode == PCI_PROBE_NORMAL) { /* use legacy probe */ @@ -179,6 +180,7 @@ pcibios_add_pci_devices(struct pci_bus * bus) if (num) { pcibios_fixup_new_pci_devices(bus, 1); pci_bus_add_devices(bus); + eeh_add_device_tree_late(bus); } list_for_each_entry(dev, &bus->devices, bus_list) @@ -200,8 +202,6 @@ struct pci_controller * __devinit init_phb_dynamic(struct device_node *dn) rtas_setup_phb(phb); pci_process_bridge_OF_ranges(phb, dn, 0); - pci_setup_phb_io_dynamic(phb, primary); - pci_devs_phb_init_dynamic(phb); if (dn->child) @@ -210,6 +210,7 @@ struct pci_controller * __devinit init_phb_dynamic(struct device_node *dn) scan_phb(phb); pcibios_fixup_new_pci_devices(phb->bus, 0); pci_bus_add_devices(phb->bus); + eeh_add_device_tree_late(phb->bus); return phb; } diff --git a/arch/powerpc/platforms/pseries/plpar_wrappers.h b/arch/powerpc/platforms/pseries/plpar_wrappers.h index 2e4d10c9eea8..d003c80fa31d 100644 --- a/arch/powerpc/platforms/pseries/plpar_wrappers.h +++ b/arch/powerpc/platforms/pseries/plpar_wrappers.h @@ -108,6 +108,21 @@ static inline long plpar_pte_read(unsigned long flags, unsigned long ptex, return rc; } +/* plpar_pte_read_raw can be called in real mode. It calls plpar_hcall_raw */ +static inline long plpar_pte_read_raw(unsigned long flags, unsigned long ptex, + unsigned long *old_pteh_ret, unsigned long *old_ptel_ret) +{ + long rc; + unsigned long retbuf[PLPAR_HCALL_BUFSIZE]; + + rc = plpar_hcall_raw(H_READ, retbuf, flags, ptex); + + *old_pteh_ret = retbuf[0]; + *old_ptel_ret = retbuf[1]; + + return rc; +} + static inline long plpar_pte_protect(unsigned long flags, unsigned long ptex, unsigned long avpn) { diff --git a/arch/powerpc/platforms/pseries/pseries.h b/arch/powerpc/platforms/pseries/pseries.h index 2729d559fd91..61e19f78b923 100644 --- a/arch/powerpc/platforms/pseries/pseries.h +++ b/arch/powerpc/platforms/pseries/pseries.h @@ -33,6 +33,8 @@ static inline void setup_kexec_cpu_down_xics(void) { } static inline void setup_kexec_cpu_down_mpic(void) { } #endif +extern void pSeries_final_fixup(void); + /* Poweron flag used for enabling auto ups restart */ extern unsigned long rtas_poweron_auto; diff --git a/arch/powerpc/platforms/pseries/reconfig.c b/arch/powerpc/platforms/pseries/reconfig.c index 5aa97aff3391..c02f8742c54d 100644 --- a/arch/powerpc/platforms/pseries/reconfig.c +++ b/arch/powerpc/platforms/pseries/reconfig.c @@ -123,7 +123,7 @@ static int pSeries_reconfig_add_node(const char *path, struct property *proplist strcpy(np->full_name, path); np->properties = proplist; - OF_MARK_DYNAMIC(np); + of_node_set_flag(np, OF_DYNAMIC); kref_init(&np->kref); np->parent = derive_parent(path); diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c index 470db6efaeb6..de6c2efd0479 100644 --- a/arch/powerpc/platforms/pseries/setup.c +++ b/arch/powerpc/platforms/pseries/setup.c @@ -399,6 +399,7 @@ static void pseries_dedicated_idle_sleep(void) * a good time to find other work to dispatch. */ get_lppaca()->idle = 1; + get_lppaca()->donate_dedicated_cpu = 1; /* * We come in with interrupts disabled, and need_resched() @@ -431,6 +432,7 @@ static void pseries_dedicated_idle_sleep(void) out: HMT_medium(); + get_lppaca()->donate_dedicated_cpu = 0; get_lppaca()->idle = 0; } diff --git a/arch/powerpc/platforms/pseries/xics.c b/arch/powerpc/platforms/pseries/xics.c index f1df942072bb..5bd90a7eb763 100644 --- a/arch/powerpc/platforms/pseries/xics.c +++ b/arch/powerpc/platforms/pseries/xics.c @@ -156,9 +156,9 @@ static inline void lpar_qirr_info(int n_cpu , u8 value) #ifdef CONFIG_SMP -static int get_irq_server(unsigned int virq) +static int get_irq_server(unsigned int virq, unsigned int strict_check) { - unsigned int server; + int server; /* For the moment only implement delivery to all cpus or one cpu */ cpumask_t cpumask = irq_desc[virq].affinity; cpumask_t tmp = CPU_MASK_NONE; @@ -166,22 +166,25 @@ static int get_irq_server(unsigned int virq) if (!distribute_irqs) return default_server; - if (cpus_equal(cpumask, CPU_MASK_ALL)) { - server = default_distrib_server; - } else { + if (!cpus_equal(cpumask, CPU_MASK_ALL)) { cpus_and(tmp, cpu_online_map, cpumask); - if (cpus_empty(tmp)) - server = default_distrib_server; - else - server = get_hard_smp_processor_id(first_cpu(tmp)); + server = first_cpu(tmp); + + if (server < NR_CPUS) + return get_hard_smp_processor_id(server); + + if (strict_check) + return -1; } - return server; + if (cpus_equal(cpu_online_map, cpu_present_map)) + return default_distrib_server; + return default_server; } #else -static int get_irq_server(unsigned int virq) +static int get_irq_server(unsigned int virq, unsigned int strict_check) { return default_server; } @@ -192,7 +195,7 @@ static void xics_unmask_irq(unsigned int virq) { unsigned int irq; int call_status; - unsigned int server; + int server; pr_debug("xics: unmask virq %d\n", virq); @@ -201,7 +204,7 @@ static void xics_unmask_irq(unsigned int virq) if (irq == XICS_IPI || irq == XICS_IRQ_SPURIOUS) return; - server = get_irq_server(virq); + server = get_irq_server(virq, 0); call_status = rtas_call(ibm_set_xive, 3, 1, NULL, irq, server, DEFAULT_PRIORITY); @@ -398,8 +401,7 @@ static void xics_set_affinity(unsigned int virq, cpumask_t cpumask) unsigned int irq; int status; int xics_status[2]; - unsigned long newmask; - cpumask_t tmp = CPU_MASK_NONE; + int irq_server; irq = (unsigned int)irq_map[virq].hwirq; if (irq == XICS_IPI || irq == XICS_IRQ_SPURIOUS) @@ -413,18 +415,21 @@ static void xics_set_affinity(unsigned int virq, cpumask_t cpumask) return; } - /* For the moment only implement delivery to all cpus or one cpu */ - if (cpus_equal(cpumask, CPU_MASK_ALL)) { - newmask = default_distrib_server; - } else { - cpus_and(tmp, cpu_online_map, cpumask); - if (cpus_empty(tmp)) - return; - newmask = get_hard_smp_processor_id(first_cpu(tmp)); + /* + * For the moment only implement delivery to all cpus or one cpu. + * Get current irq_server for the given irq + */ + irq_server = get_irq_server(irq, 1); + if (irq_server == -1) { + char cpulist[128]; + cpumask_scnprintf(cpulist, sizeof(cpulist), cpumask); + printk(KERN_WARNING "xics_set_affinity: No online cpus in " + "the mask %s for irq %d\n", cpulist, virq); + return; } status = rtas_call(ibm_set_xive, 3, 1, NULL, - irq, newmask, xics_status[1]); + irq, irq_server, xics_status[1]); if (status) { printk(KERN_ERR "xics_set_affinity: irq=%u ibm,set-xive " diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile index c3ce0bd12c0b..f65078c3d3b3 100644 --- a/arch/powerpc/sysdev/Makefile +++ b/arch/powerpc/sysdev/Makefile @@ -5,7 +5,6 @@ endif mpic-msi-obj-$(CONFIG_PCI_MSI) += mpic_msi.o mpic_u3msi.o obj-$(CONFIG_MPIC) += mpic.o $(mpic-msi-obj-y) -obj-$(CONFIG_PPC_INDIRECT_PCI) += indirect_pci.o obj-$(CONFIG_PPC_MPC106) += grackle.o obj-$(CONFIG_PPC_DCR) += dcr.o obj-$(CONFIG_PPC_DCR_NATIVE) += dcr-low.o @@ -13,16 +12,19 @@ obj-$(CONFIG_PPC_PMI) += pmi.o obj-$(CONFIG_U3_DART) += dart_iommu.o obj-$(CONFIG_MMIO_NVRAM) += mmio_nvram.o obj-$(CONFIG_FSL_SOC) += fsl_soc.o -obj-$(CONFIG_FSL_PCIE) += fsl_pcie.o obj-$(CONFIG_TSI108_BRIDGE) += tsi108_pci.o tsi108_dev.o obj-$(CONFIG_QUICC_ENGINE) += qe_lib/ mv64x60-$(CONFIG_PCI) += mv64x60_pci.o obj-$(CONFIG_MV64X60) += $(mv64x60-y) mv64x60_pic.o mv64x60_dev.o +obj-$(CONFIG_RTC_DRV_CMOS) += rtc_cmos_setup.o # contains only the suspend handler for time +ifeq ($(CONFIG_RTC_CLASS),) obj-$(CONFIG_PM) += timer.o +endif ifeq ($(CONFIG_PPC_MERGE),y) +obj-$(CONFIG_PPC_INDIRECT_PCI) += indirect_pci.o obj-$(CONFIG_PPC_I8259) += i8259.o obj-$(CONFIG_PPC_83xx) += ipic.o obj-$(CONFIG_4xx) += uic.o diff --git a/arch/powerpc/sysdev/fsl_pcie.c b/arch/powerpc/sysdev/fsl_pcie.c deleted file mode 100644 index 041c07e8b665..000000000000 --- a/arch/powerpc/sysdev/fsl_pcie.c +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Support for indirect PCI bridges. - * - * Copyright (C) 1998 Gabriel Paubert. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * "Temporary" MPC8548 Errata file - - * The standard indirect_pci code should work with future silicon versions. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#define PCI_CFG_OUT out_be32 - -/* ERRATA PCI-Ex 14 PCIE Controller timeout */ -#define PCIE_FIX out_be32(hose->cfg_addr+0x4, 0x0400ffff) - - -static int -indirect_read_config_pcie(struct pci_bus *bus, unsigned int devfn, int offset, - int len, u32 *val) -{ - struct pci_controller *hose = bus->sysdata; - volatile void __iomem *cfg_data; - u32 temp; - - if (ppc_md.pci_exclude_device) - if (ppc_md.pci_exclude_device(bus->number, devfn)) - return PCIBIOS_DEVICE_NOT_FOUND; - - /* Possible artifact of CDCpp50937 needs further investigation */ - if (devfn != 0x0 && bus->number == 0xff) - return PCIBIOS_DEVICE_NOT_FOUND; - - PCIE_FIX; - if (bus->number == 0xff) { - PCI_CFG_OUT(hose->cfg_addr, - (0x80000000 | ((offset & 0xf00) << 16) | - ((bus->number - hose->bus_offset) << 16) - | (devfn << 8) | ((offset & 0xfc) ))); - } else { - PCI_CFG_OUT(hose->cfg_addr, - (0x80000001 | ((offset & 0xf00) << 16) | - ((bus->number - hose->bus_offset) << 16) - | (devfn << 8) | ((offset & 0xfc) ))); - } - - /* - * Note: the caller has already checked that offset is - * suitably aligned and that len is 1, 2 or 4. - */ - /* ERRATA PCI-Ex 12 - Configuration Address/Data Alignment */ - cfg_data = hose->cfg_data; - PCIE_FIX; - temp = in_le32(cfg_data); - switch (len) { - case 1: - *val = (temp >> (((offset & 3))*8)) & 0xff; - break; - case 2: - *val = (temp >> (((offset & 3))*8)) & 0xffff; - break; - default: - *val = temp; - break; - } - return PCIBIOS_SUCCESSFUL; -} - -static int -indirect_write_config_pcie(struct pci_bus *bus, unsigned int devfn, int offset, - int len, u32 val) -{ - struct pci_controller *hose = bus->sysdata; - volatile void __iomem *cfg_data; - u32 temp; - - if (ppc_md.pci_exclude_device) - if (ppc_md.pci_exclude_device(bus->number, devfn)) - return PCIBIOS_DEVICE_NOT_FOUND; - - /* Possible artifact of CDCpp50937 needs further investigation */ - if (devfn != 0x0 && bus->number == 0xff) - return PCIBIOS_DEVICE_NOT_FOUND; - - PCIE_FIX; - if (bus->number == 0xff) { - PCI_CFG_OUT(hose->cfg_addr, - (0x80000000 | ((offset & 0xf00) << 16) | - ((bus->number - hose->bus_offset) << 16) - | (devfn << 8) | ((offset & 0xfc) ))); - } else { - PCI_CFG_OUT(hose->cfg_addr, - (0x80000001 | ((offset & 0xf00) << 16) | - ((bus->number - hose->bus_offset) << 16) - | (devfn << 8) | ((offset & 0xfc) ))); - } - - /* - * Note: the caller has already checked that offset is - * suitably aligned and that len is 1, 2 or 4. - */ - /* ERRATA PCI-Ex 12 - Configuration Address/Data Alignment */ - cfg_data = hose->cfg_data; - switch (len) { - case 1: - PCIE_FIX; - temp = in_le32(cfg_data); - temp = (temp & ~(0xff << ((offset & 3) * 8))) | - (val << ((offset & 3) * 8)); - PCIE_FIX; - out_le32(cfg_data, temp); - break; - case 2: - PCIE_FIX; - temp = in_le32(cfg_data); - temp = (temp & ~(0xffff << ((offset & 3) * 8))); - temp |= (val << ((offset & 3) * 8)) ; - PCIE_FIX; - out_le32(cfg_data, temp); - break; - default: - PCIE_FIX; - out_le32(cfg_data, val); - break; - } - PCIE_FIX; - return PCIBIOS_SUCCESSFUL; -} - -static struct pci_ops indirect_pcie_ops = { - indirect_read_config_pcie, - indirect_write_config_pcie -}; - -void __init -setup_indirect_pcie_nomap(struct pci_controller* hose, void __iomem * cfg_addr, - void __iomem * cfg_data) -{ - hose->cfg_addr = cfg_addr; - hose->cfg_data = cfg_data; - hose->ops = &indirect_pcie_ops; -} - -void __init -setup_indirect_pcie(struct pci_controller* hose, u32 cfg_addr, u32 cfg_data) -{ - unsigned long base = cfg_addr & PAGE_MASK; - void __iomem *mbase, *addr, *data; - - mbase = ioremap(base, PAGE_SIZE); - addr = mbase + (cfg_addr & ~PAGE_MASK); - if ((cfg_data & PAGE_MASK) != base) - mbase = ioremap(cfg_data & PAGE_MASK, PAGE_SIZE); - data = mbase + (cfg_data & ~PAGE_MASK); - setup_indirect_pcie_nomap(hose, addr, data); -} diff --git a/arch/powerpc/sysdev/fsl_soc.c b/arch/powerpc/sysdev/fsl_soc.c index cad175724359..c0ddc80d8160 100644 --- a/arch/powerpc/sysdev/fsl_soc.c +++ b/arch/powerpc/sysdev/fsl_soc.c @@ -1028,6 +1028,19 @@ err: arch_initcall(fs_enet_of_init); +static int __init fsl_pcmcia_of_init(void) +{ + struct device_node *np = NULL; + /* + * Register all the devices which type is "pcmcia" + */ + while ((np = of_find_compatible_node(np, + "pcmcia", "fsl,pq-pcmcia")) != NULL) + of_platform_device_create(np, "m8xx-pcmcia", NULL); + return 0; +} + +arch_initcall(fsl_pcmcia_of_init); static const char *smc_regs = "regs"; static const char *smc_pram = "pram"; diff --git a/arch/powerpc/sysdev/indirect_pci.c b/arch/powerpc/sysdev/indirect_pci.c index e71488469704..c7e6e859b393 100644 --- a/arch/powerpc/sysdev/indirect_pci.c +++ b/arch/powerpc/sysdev/indirect_pci.c @@ -33,18 +33,27 @@ indirect_read_config(struct pci_bus *bus, unsigned int devfn, int offset, struct pci_controller *hose = bus->sysdata; volatile void __iomem *cfg_data; u8 cfg_type = 0; + u32 bus_no, reg; if (ppc_md.pci_exclude_device) - if (ppc_md.pci_exclude_device(bus->number, devfn)) + if (ppc_md.pci_exclude_device(hose, bus->number, devfn)) return PCIBIOS_DEVICE_NOT_FOUND; - if (hose->set_cfg_type) + if (hose->indirect_type & PPC_INDIRECT_TYPE_SET_CFG_TYPE) if (bus->number != hose->first_busno) cfg_type = 1; - PCI_CFG_OUT(hose->cfg_addr, - (0x80000000 | ((bus->number - hose->bus_offset) << 16) - | (devfn << 8) | ((offset & 0xfc) | cfg_type))); + bus_no = (bus->number == hose->first_busno) ? + hose->self_busno : bus->number; + + if (hose->indirect_type & PPC_INDIRECT_TYPE_EXT_REG) + reg = ((offset & 0xf00) << 16) | (offset & 0xfc); + else + reg = offset & 0xfc; + + PCI_CFG_OUT(hose->cfg_addr, + (0x80000000 | (bus_no << 16) + | (devfn << 8) | reg | cfg_type)); /* * Note: the caller has already checked that offset is @@ -72,18 +81,33 @@ indirect_write_config(struct pci_bus *bus, unsigned int devfn, int offset, struct pci_controller *hose = bus->sysdata; volatile void __iomem *cfg_data; u8 cfg_type = 0; + u32 bus_no, reg; if (ppc_md.pci_exclude_device) - if (ppc_md.pci_exclude_device(bus->number, devfn)) + if (ppc_md.pci_exclude_device(hose, bus->number, devfn)) return PCIBIOS_DEVICE_NOT_FOUND; - if (hose->set_cfg_type) + if (hose->indirect_type & PPC_INDIRECT_TYPE_SET_CFG_TYPE) if (bus->number != hose->first_busno) cfg_type = 1; - PCI_CFG_OUT(hose->cfg_addr, - (0x80000000 | ((bus->number - hose->bus_offset) << 16) - | (devfn << 8) | ((offset & 0xfc) | cfg_type))); + bus_no = (bus->number == hose->first_busno) ? + hose->self_busno : bus->number; + + if (hose->indirect_type & PPC_INDIRECT_TYPE_EXT_REG) + reg = ((offset & 0xf00) << 16) | (offset & 0xfc); + else + reg = offset & 0xfc; + + PCI_CFG_OUT(hose->cfg_addr, + (0x80000000 | (bus_no << 16) + | (devfn << 8) | reg | cfg_type)); + + /* surpress setting of PCI_PRIMARY_BUS */ + if (hose->indirect_type & PPC_INDIRECT_TYPE_SURPRESS_PRIMARY_BUS) + if ((offset == PCI_PRIMARY_BUS) && + (bus->number == hose->first_busno)) + val &= 0xffffff00; /* * Note: the caller has already checked that offset is diff --git a/arch/powerpc/sysdev/mpc8xx_pic.h b/arch/powerpc/sysdev/mpc8xx_pic.h index afa2ee6717c1..9fe00eebdc8b 100644 --- a/arch/powerpc/sysdev/mpc8xx_pic.h +++ b/arch/powerpc/sysdev/mpc8xx_pic.h @@ -4,9 +4,16 @@ #include #include -extern struct hw_interrupt_type mpc8xx_pic; - int mpc8xx_pic_init(void); unsigned int mpc8xx_get_irq(void); +/* + * Some internal interrupt registers use an 8-bit mask for the interrupt + * level instead of a number. + */ +static inline uint mk_int_int_mask(uint mask) +{ + return (1 << (7 - (mask/2))); +} + #endif /* _PPC_KERNEL_PPC8xx_H */ diff --git a/arch/powerpc/sysdev/mv64x60_dev.c b/arch/powerpc/sysdev/mv64x60_dev.c index 4b0a9c88eeb3..b618fa60aef3 100644 --- a/arch/powerpc/sysdev/mv64x60_dev.c +++ b/arch/powerpc/sysdev/mv64x60_dev.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -420,3 +421,30 @@ error: return err; } arch_initcall(mv64x60_device_setup); + +static int __init mv64x60_add_mpsc_console(void) +{ + struct device_node *np = NULL; + const char *prop; + + prop = of_get_property(of_chosen, "linux,stdout-path", NULL); + if (prop == NULL) + goto not_mpsc; + + np = of_find_node_by_path(prop); + if (!np) + goto not_mpsc; + + if (!of_device_is_compatible(np, "marvell,mpsc")) + goto not_mpsc; + + prop = of_get_property(np, "block-index", NULL); + if (!prop) + goto not_mpsc; + + add_preferred_console("ttyMM", *(int *)prop, NULL); + +not_mpsc: + return 0; +} +console_initcall(mv64x60_add_mpsc_console); diff --git a/arch/powerpc/sysdev/mv64x60_pci.c b/arch/powerpc/sysdev/mv64x60_pci.c index b5aef4cbc8d2..45db86c2363c 100644 --- a/arch/powerpc/sysdev/mv64x60_pci.c +++ b/arch/powerpc/sysdev/mv64x60_pci.c @@ -137,18 +137,15 @@ static int __init mv64x60_add_bridge(struct device_node *dev) printk(KERN_WARNING "Can't get bus-range for %s, assume" " bus 0\n", dev->full_name); - hose = pcibios_alloc_controller(); + hose = pcibios_alloc_controller(dev); if (!hose) return -ENOMEM; - hose->arch_data = dev; - hose->set_cfg_type = 1; - hose->first_busno = bus_range ? bus_range[0] : 0; hose->last_busno = bus_range ? bus_range[1] : 0xff; setup_indirect_pci(hose, rsrc.start, rsrc.start + 4); - hose->bus_offset = hose->first_busno; + hose->self_busno = hose->first_busno; printk(KERN_INFO "Found MV64x60 PCI host bridge at 0x%016llx. " "Firmware bus number: %d->%d\n", diff --git a/arch/powerpc/sysdev/qe_lib/ucc.c b/arch/powerpc/sysdev/qe_lib/ucc.c index ac12a44d516f..f970e5415ac0 100644 --- a/arch/powerpc/sysdev/qe_lib/ucc.c +++ b/arch/powerpc/sysdev/qe_lib/ucc.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -40,6 +41,7 @@ int ucc_set_qe_mux_mii_mng(int ucc_num) return 0; } +EXPORT_SYMBOL(ucc_set_qe_mux_mii_mng); int ucc_set_type(int ucc_num, struct ucc_common *regs, enum ucc_speed_type speed) diff --git a/arch/powerpc/sysdev/qe_lib/ucc_fast.c b/arch/powerpc/sysdev/qe_lib/ucc_fast.c index 9143236853fc..3df202e8d332 100644 --- a/arch/powerpc/sysdev/qe_lib/ucc_fast.c +++ b/arch/powerpc/sysdev/qe_lib/ucc_fast.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -70,6 +71,7 @@ void ucc_fast_dump_regs(struct ucc_fast_private * uccf) printk(KERN_INFO "guemr : addr - 0x%08x, val - 0x%02x", (u32) & uccf->uf_regs->guemr, uccf->uf_regs->guemr); } +EXPORT_SYMBOL(ucc_fast_dump_regs); u32 ucc_fast_get_qe_cr_subblock(int uccf_num) { @@ -85,11 +87,13 @@ u32 ucc_fast_get_qe_cr_subblock(int uccf_num) default: return QE_CR_SUBBLOCK_INVALID; } } +EXPORT_SYMBOL(ucc_fast_get_qe_cr_subblock); void ucc_fast_transmit_on_demand(struct ucc_fast_private * uccf) { out_be16(&uccf->uf_regs->utodr, UCC_FAST_TOD); } +EXPORT_SYMBOL(ucc_fast_transmit_on_demand); void ucc_fast_enable(struct ucc_fast_private * uccf, enum comm_dir mode) { @@ -110,6 +114,7 @@ void ucc_fast_enable(struct ucc_fast_private * uccf, enum comm_dir mode) } out_be32(&uf_regs->gumr, gumr); } +EXPORT_SYMBOL(ucc_fast_enable); void ucc_fast_disable(struct ucc_fast_private * uccf, enum comm_dir mode) { @@ -130,6 +135,7 @@ void ucc_fast_disable(struct ucc_fast_private * uccf, enum comm_dir mode) } out_be32(&uf_regs->gumr, gumr); } +EXPORT_SYMBOL(ucc_fast_disable); int ucc_fast_init(struct ucc_fast_info * uf_info, struct ucc_fast_private ** uccf_ret) { @@ -341,6 +347,7 @@ int ucc_fast_init(struct ucc_fast_info * uf_info, struct ucc_fast_private ** ucc *uccf_ret = uccf; return 0; } +EXPORT_SYMBOL(ucc_fast_init); void ucc_fast_free(struct ucc_fast_private * uccf) { @@ -355,3 +362,4 @@ void ucc_fast_free(struct ucc_fast_private * uccf) kfree(uccf); } +EXPORT_SYMBOL(ucc_fast_free); diff --git a/arch/powerpc/sysdev/rtc_cmos_setup.c b/arch/powerpc/sysdev/rtc_cmos_setup.c new file mode 100644 index 000000000000..e276048b8c5f --- /dev/null +++ b/arch/powerpc/sysdev/rtc_cmos_setup.c @@ -0,0 +1,49 @@ +/* + * Setup code for PC-style Real-Time Clock. + * + * Author: Wade Farnsworth + * + * 2007 (c) MontaVista Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include +#include +#include + +#include + +static int __init add_rtc(void) +{ + struct device_node *np; + struct platform_device *pd; + struct resource res; + int ret; + + np = of_find_compatible_node(NULL, NULL, "pnpPNP,b00"); + if (!np) + return -ENODEV; + + ret = of_address_to_resource(np, 0, &res); + of_node_put(np); + if (ret) + return ret; + + /* + * RTC_PORT(x) is hardcoded in asm/mc146818rtc.h. Verify that the + * address provided by the device node matches. + */ + if (res.start != RTC_PORT(0)) + return -EINVAL; + + pd = platform_device_register_simple("rtc_cmos", -1, + &res, 1); + if (IS_ERR(pd)) + return PTR_ERR(pd); + + return 0; +} +fs_initcall(add_rtc); diff --git a/arch/powerpc/sysdev/timer.c b/arch/powerpc/sysdev/timer.c index 4a01748b4217..e81e7ec2e799 100644 --- a/arch/powerpc/sysdev/timer.c +++ b/arch/powerpc/sysdev/timer.c @@ -24,7 +24,12 @@ static int timer_resume(struct sys_device *dev) /* get current RTC time and convert to seconds */ get_rtc_time(&cur_rtc_tm); - rtc_tm_to_time(&cur_rtc_tm, &cur_rtc_time); + cur_rtc_time = mktime(cur_rtc_tm.tm_year + 1900, + cur_rtc_tm.tm_mon + 1, + cur_rtc_tm.tm_mday, + cur_rtc_tm.tm_hour, + cur_rtc_tm.tm_min, + cur_rtc_tm.tm_sec); diff = cur_rtc_time - suspend_rtc_time; @@ -44,7 +49,12 @@ static int timer_suspend(struct sys_device *dev, pm_message_t state) WARN_ON(!ppc_md.get_rtc_time); get_rtc_time(&suspend_rtc_tm); - rtc_tm_to_time(&suspend_rtc_tm, &suspend_rtc_time); + suspend_rtc_time = mktime(suspend_rtc_tm.tm_year + 1900, + suspend_rtc_tm.tm_mon + 1, + suspend_rtc_tm.tm_mday, + suspend_rtc_tm.tm_hour, + suspend_rtc_tm.tm_min, + suspend_rtc_tm.tm_sec); return 0; } diff --git a/arch/powerpc/sysdev/tsi108_dev.c b/arch/powerpc/sysdev/tsi108_dev.c index 7d3b09b7d544..a113d800cbf0 100644 --- a/arch/powerpc/sysdev/tsi108_dev.c +++ b/arch/powerpc/sysdev/tsi108_dev.c @@ -72,12 +72,11 @@ static int __init tsi108_eth_of_init(void) int ret; for (np = NULL, i = 0; - (np = of_find_compatible_node(np, "network", "tsi-ethernet")) != NULL; + (np = of_find_compatible_node(np, "network", "tsi108-ethernet")) != NULL; i++) { struct resource r[2]; - struct device_node *phy; + struct device_node *phy, *mdio; hw_info tsi_eth_data; - const unsigned int *id; const unsigned int *phy_id; const void *mac_addr; const phandle *ph; @@ -111,6 +110,13 @@ static int __init tsi108_eth_of_init(void) if (mac_addr) memcpy(tsi_eth_data.mac_addr, mac_addr, 6); + ph = of_get_property(np, "mdio-handle", NULL); + mdio = of_find_node_by_phandle(*ph); + ret = of_address_to_resource(mdio, 0, &res); + of_node_put(mdio); + if (ret) + goto unreg; + ph = of_get_property(np, "phy-handle", NULL); phy = of_find_node_by_phandle(*ph); @@ -119,20 +125,25 @@ static int __init tsi108_eth_of_init(void) goto unreg; } - id = of_get_property(phy, "reg", NULL); - phy_id = of_get_property(phy, "phy-id", NULL); - ret = of_address_to_resource(phy, 0, &res); - if (ret) { - of_node_put(phy); - goto unreg; - } + phy_id = of_get_property(phy, "reg", NULL); + tsi_eth_data.regs = r[0].start; tsi_eth_data.phyregs = res.start; tsi_eth_data.phy = *phy_id; tsi_eth_data.irq_num = irq_of_parse_and_map(np, 0); - if (of_device_is_compatible(phy, "bcm54xx")) + + /* Some boards with the TSI108 bridge (e.g. Holly) + * have a miswiring of the ethernet PHYs which + * requires a workaround. The special + * "txc-rxc-delay-disable" property enables this + * workaround. FIXME: Need to port the tsi108_eth + * driver itself to phylib and use a non-misleading + * name for the workaround flag - it's not actually to + * do with the model of PHY in use */ + if (of_get_property(phy, "txc-rxc-delay-disable", NULL)) tsi_eth_data.phy_type = TSI108_PHY_BCM54XX; of_node_put(phy); + ret = platform_device_add_data(tsi_eth_dev, &tsi_eth_data, sizeof(hw_info)); diff --git a/arch/powerpc/sysdev/tsi108_pci.c b/arch/powerpc/sysdev/tsi108_pci.c index 2153163fa593..90db8a720fed 100644 --- a/arch/powerpc/sysdev/tsi108_pci.c +++ b/arch/powerpc/sysdev/tsi108_pci.c @@ -64,9 +64,10 @@ tsi108_direct_write_config(struct pci_bus *bus, unsigned int devfunc, int offset, int len, u32 val) { volatile unsigned char *cfg_addr; + struct pci_controller *hose = bus->sysdata; if (ppc_md.pci_exclude_device) - if (ppc_md.pci_exclude_device(bus->number, devfunc)) + if (ppc_md.pci_exclude_device(hose, bus->number, devfunc)) return PCIBIOS_DEVICE_NOT_FOUND; cfg_addr = (unsigned char *)(tsi_mk_config_addr(bus->number, @@ -149,10 +150,11 @@ tsi108_direct_read_config(struct pci_bus *bus, unsigned int devfn, int offset, int len, u32 * val) { volatile unsigned char *cfg_addr; + struct pci_controller *hose = bus->sysdata; u32 temp; if (ppc_md.pci_exclude_device) - if (ppc_md.pci_exclude_device(bus->number, devfn)) + if (ppc_md.pci_exclude_device(hose, bus->number, devfn)) return PCIBIOS_DEVICE_NOT_FOUND; cfg_addr = (unsigned char *)(tsi_mk_config_addr(bus->number, @@ -219,14 +221,12 @@ int __init tsi108_setup_pci(struct device_node *dev, u32 cfg_phys, int primary) " bus 0\n", dev->full_name); } - hose = pcibios_alloc_controller(); + hose = pcibios_alloc_controller(dev); if (!hose) { printk("PCI Host bridge init failed\n"); return -ENOMEM; } - hose->arch_data = dev; - hose->set_cfg_type = 1; hose->first_busno = bus_range ? bus_range[0] : 0; hose->last_busno = bus_range ? bus_range[1] : 0xff; diff --git a/arch/powerpc/xmon/xmon.c b/arch/powerpc/xmon/xmon.c index 28fdf4f50c27..669e6566ad70 100644 --- a/arch/powerpc/xmon/xmon.c +++ b/arch/powerpc/xmon/xmon.c @@ -2634,7 +2634,7 @@ static int __init setup_xmon_sysrq(void) __initcall(setup_xmon_sysrq); #endif /* CONFIG_MAGIC_SYSRQ */ -int __initdata xmon_early, xmon_off; +static int __initdata xmon_early, xmon_off; static int __init early_parse_xmon(char *p) { diff --git a/arch/ppc/kernel/misc.S b/arch/ppc/kernel/misc.S index d319f9ba2379..0da55368655c 100644 --- a/arch/ppc/kernel/misc.S +++ b/arch/ppc/kernel/misc.S @@ -328,7 +328,7 @@ BEGIN_FTR_SECTION mtspr SPRN_L1CSR0,r3 isync blr -END_FTR_SECTION_IFCLR(CPU_FTR_SPLIT_ID_CACHE) +END_FTR_SECTION_IFSET(CPU_FTR_UNIFIED_ID_CACHE) mfspr r3,SPRN_L1CSR1 ori r3,r3,L1CSR1_ICFI|L1CSR1_ICLFR mtspr SPRN_L1CSR1,r3 @@ -355,7 +355,7 @@ END_FTR_SECTION_IFCLR(CPU_FTR_SPLIT_ID_CACHE) _GLOBAL(__flush_icache_range) BEGIN_FTR_SECTION blr /* for 601, do nothing */ -END_FTR_SECTION_IFCLR(CPU_FTR_SPLIT_ID_CACHE) +END_FTR_SECTION_IFSET(CPU_FTR_COHERENT_ICACHE) li r5,L1_CACHE_BYTES-1 andc r3,r3,r5 subf r4,r3,r4 @@ -472,7 +472,7 @@ _GLOBAL(flush_dcache_all) _GLOBAL(__flush_dcache_icache) BEGIN_FTR_SECTION blr /* for 601, do nothing */ -END_FTR_SECTION_IFCLR(CPU_FTR_SPLIT_ID_CACHE) +END_FTR_SECTION_IFSET(CPU_FTR_COHERENT_ICACHE) rlwinm r3,r3,0,0,19 /* Get page base address */ li r4,4096/L1_CACHE_BYTES /* Number of lines in a page */ mtctr r4 @@ -500,7 +500,7 @@ END_FTR_SECTION_IFCLR(CPU_FTR_SPLIT_ID_CACHE) _GLOBAL(__flush_dcache_icache_phys) BEGIN_FTR_SECTION blr /* for 601, do nothing */ -END_FTR_SECTION_IFCLR(CPU_FTR_SPLIT_ID_CACHE) +END_FTR_SECTION_IFSET(CPU_FTR_COHERENT_ICACHE) mfmsr r10 rlwinm r0,r10,0,28,26 /* clear DR */ mtmsr r0 diff --git a/arch/ppc/kernel/ppc_ksyms.c b/arch/ppc/kernel/ppc_ksyms.c index a4165209ac7c..63f0a987139b 100644 --- a/arch/ppc/kernel/ppc_ksyms.c +++ b/arch/ppc/kernel/ppc_ksyms.c @@ -64,7 +64,6 @@ extern unsigned long mm_ptov (unsigned long paddr); EXPORT_SYMBOL(clear_pages); EXPORT_SYMBOL(clear_user_page); -EXPORT_SYMBOL(do_signal); EXPORT_SYMBOL(transfer_to_handler); EXPORT_SYMBOL(do_IRQ); EXPORT_SYMBOL(machine_check_exception); diff --git a/arch/ppc/kernel/setup.c b/arch/ppc/kernel/setup.c index c79704f5409c..967c1ef59a6b 100644 --- a/arch/ppc/kernel/setup.c +++ b/arch/ppc/kernel/setup.c @@ -526,7 +526,7 @@ void __init setup_arch(char **cmdline_p) * Systems with OF can look in the properties on the cpu node(s) * for a possibly more accurate value. */ - if (cpu_has_feature(CPU_FTR_SPLIT_ID_CACHE)) { + if (! cpu_has_feature(CPU_FTR_UNIFIED_ID_CACHE)) { dcache_bsize = cur_cpu_spec->dcache_bsize; icache_bsize = cur_cpu_spec->icache_bsize; ucache_bsize = 0; diff --git a/arch/ppc/mm/tlb.c b/arch/ppc/mm/tlb.c index fa29740a28f5..4ff260bc9dd1 100644 --- a/arch/ppc/mm/tlb.c +++ b/arch/ppc/mm/tlb.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include diff --git a/arch/ppc/platforms/prep_setup.c b/arch/ppc/platforms/prep_setup.c index 6f21110a9747..3c56654bfc6f 100644 --- a/arch/ppc/platforms/prep_setup.c +++ b/arch/ppc/platforms/prep_setup.c @@ -69,9 +69,6 @@ TODC_ALLOC(); -unsigned char ucBoardRev; -unsigned char ucBoardRevMaj, ucBoardRevMin; - extern unsigned char prep_nvram_read_val(int addr); extern void prep_nvram_write_val(int addr, unsigned char val); diff --git a/arch/ppc/syslib/Makefile b/arch/ppc/syslib/Makefile index 95694159b226..543795be58c8 100644 --- a/arch/ppc/syslib/Makefile +++ b/arch/ppc/syslib/Makefile @@ -7,6 +7,7 @@ CFLAGS_btext.o += -fPIC wdt-mpc8xx-$(CONFIG_8xx_WDT) += m8xx_wdt.o +obj-$(CONFIG_PPC_INDIRECT_PCI) += indirect_pci.o obj-$(CONFIG_PPCBUG_NVRAM) += prep_nvram.o obj-$(CONFIG_PPC_OCP) += ocp.o obj-$(CONFIG_IBM_OCP) += ibm_ocp.o diff --git a/arch/ppc/syslib/indirect_pci.c b/arch/ppc/syslib/indirect_pci.c new file mode 100644 index 000000000000..83b323a7d029 --- /dev/null +++ b/arch/ppc/syslib/indirect_pci.c @@ -0,0 +1,134 @@ +/* + * Support for indirect PCI bridges. + * + * Copyright (C) 1998 Gabriel Paubert. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef CONFIG_PPC_INDIRECT_PCI_BE +#define PCI_CFG_OUT out_be32 +#else +#define PCI_CFG_OUT out_le32 +#endif + +static int +indirect_read_config(struct pci_bus *bus, unsigned int devfn, int offset, + int len, u32 *val) +{ + struct pci_controller *hose = bus->sysdata; + volatile void __iomem *cfg_data; + u8 cfg_type = 0; + + if (ppc_md.pci_exclude_device) + if (ppc_md.pci_exclude_device(bus->number, devfn)) + return PCIBIOS_DEVICE_NOT_FOUND; + + if (hose->set_cfg_type) + if (bus->number != hose->first_busno) + cfg_type = 1; + + PCI_CFG_OUT(hose->cfg_addr, + (0x80000000 | ((bus->number - hose->bus_offset) << 16) + | (devfn << 8) | ((offset & 0xfc) | cfg_type))); + + /* + * Note: the caller has already checked that offset is + * suitably aligned and that len is 1, 2 or 4. + */ + cfg_data = hose->cfg_data + (offset & 3); + switch (len) { + case 1: + *val = in_8(cfg_data); + break; + case 2: + *val = in_le16(cfg_data); + break; + default: + *val = in_le32(cfg_data); + break; + } + return PCIBIOS_SUCCESSFUL; +} + +static int +indirect_write_config(struct pci_bus *bus, unsigned int devfn, int offset, + int len, u32 val) +{ + struct pci_controller *hose = bus->sysdata; + volatile void __iomem *cfg_data; + u8 cfg_type = 0; + + if (ppc_md.pci_exclude_device) + if (ppc_md.pci_exclude_device(bus->number, devfn)) + return PCIBIOS_DEVICE_NOT_FOUND; + + if (hose->set_cfg_type) + if (bus->number != hose->first_busno) + cfg_type = 1; + + PCI_CFG_OUT(hose->cfg_addr, + (0x80000000 | ((bus->number - hose->bus_offset) << 16) + | (devfn << 8) | ((offset & 0xfc) | cfg_type))); + + /* + * Note: the caller has already checked that offset is + * suitably aligned and that len is 1, 2 or 4. + */ + cfg_data = hose->cfg_data + (offset & 3); + switch (len) { + case 1: + out_8(cfg_data, val); + break; + case 2: + out_le16(cfg_data, val); + break; + default: + out_le32(cfg_data, val); + break; + } + return PCIBIOS_SUCCESSFUL; +} + +static struct pci_ops indirect_pci_ops = +{ + indirect_read_config, + indirect_write_config +}; + +void __init +setup_indirect_pci_nomap(struct pci_controller* hose, void __iomem * cfg_addr, + void __iomem * cfg_data) +{ + hose->cfg_addr = cfg_addr; + hose->cfg_data = cfg_data; + hose->ops = &indirect_pci_ops; +} + +void __init +setup_indirect_pci(struct pci_controller* hose, u32 cfg_addr, u32 cfg_data) +{ + unsigned long base = cfg_addr & PAGE_MASK; + void __iomem *mbase, *addr, *data; + + mbase = ioremap(base, PAGE_SIZE); + addr = mbase + (cfg_addr & ~PAGE_MASK); + if ((cfg_data & PAGE_MASK) != base) + mbase = ioremap(cfg_data & PAGE_MASK, PAGE_SIZE); + data = mbase + (cfg_data & ~PAGE_MASK); + setup_indirect_pci_nomap(hose, addr, data); +} diff --git a/arch/ppc/syslib/virtex_devices.c b/arch/ppc/syslib/virtex_devices.c index 16546788e23b..ace4ec08de51 100644 --- a/arch/ppc/syslib/virtex_devices.c +++ b/arch/ppc/syslib/virtex_devices.c @@ -71,6 +71,21 @@ }, \ } +/* + * ML300/ML403 Video Device: shortcut macro for single instance + */ +#define XPAR_TFT(num) { \ + .name = "xilinxfb", \ + .id = num, \ + .num_resources = 1, \ + .resource = (struct resource[]) { \ + { \ + .start = XPAR_TFT_##num##_BASEADDR, \ + .end = XPAR_TFT_##num##_BASEADDR+7, \ + .flags = IORESOURCE_IO, \ + }, \ + }, \ +} /* UART 8250 driver platform data table */ struct plat_serial8250_port virtex_serial_platform_data[] = { @@ -146,20 +161,17 @@ struct platform_device virtex_platform_devices[] = { XPAR_SYSACE(1), #endif - /* ML300/403 reference design framebuffer */ #if defined(XPAR_TFT_0_BASEADDR) - { - .name = "xilinxfb", - .id = 0, - .num_resources = 1, - .resource = (struct resource[]) { - { - .start = XPAR_TFT_0_BASEADDR, - .end = XPAR_TFT_0_BASEADDR+7, - .flags = IORESOURCE_IO, - }, - }, - }, + XPAR_TFT(0), +#endif +#if defined(XPAR_TFT_1_BASEADDR) + XPAR_TFT(1), +#endif +#if defined(XPAR_TFT_2_BASEADDR) + XPAR_TFT(2), +#endif +#if defined(XPAR_TFT_3_BASEADDR) + XPAR_TFT(3), #endif }; diff --git a/drivers/block/viodasd.c b/drivers/block/viodasd.c index 68592c336011..dae39911a11d 100644 --- a/drivers/block/viodasd.c +++ b/drivers/block/viodasd.c @@ -252,10 +252,10 @@ static int viodasd_getgeo(struct block_device *bdev, struct hd_geometry *geo) struct gendisk *disk = bdev->bd_disk; struct viodasd_device *d = disk->private_data; - geo->sectors = d->sectors ? d->sectors : 0; + geo->sectors = d->sectors ? d->sectors : 32; geo->heads = d->tracks ? d->tracks : 64; geo->cylinders = d->cylinders ? d->cylinders : - get_capacity(disk) / (geo->cylinders * geo->heads); + get_capacity(disk) / (geo->sectors * geo->heads); return 0; } diff --git a/drivers/char/briq_panel.c b/drivers/char/briq_panel.c index ed53f541d9e8..b6f2639f903d 100644 --- a/drivers/char/briq_panel.c +++ b/drivers/char/briq_panel.c @@ -91,11 +91,6 @@ static ssize_t briq_panel_read(struct file *file, char __user *buf, size_t count unsigned short c; unsigned char cp; -#if 0 /* Can't seek (pread) on this device */ - if (ppos != &file->f_pos) - return -ESPIPE; -#endif - if (!vfd_is_open) return -ENODEV; @@ -139,11 +134,6 @@ static ssize_t briq_panel_write(struct file *file, const char __user *buf, size_ size_t indx = len; int i, esc = 0; -#if 0 /* Can't seek (pwrite) on this device */ - if (ppos != &file->f_pos) - return -ESPIPE; -#endif - if (!vfd_is_open) return -EBUSY; diff --git a/drivers/char/hvc_console.c b/drivers/char/hvc_console.c index 322bc5f7d86b..b3ab42e0dd4a 100644 --- a/drivers/char/hvc_console.c +++ b/drivers/char/hvc_console.c @@ -674,7 +674,7 @@ static const cpumask_t cpus_in_xmon = CPU_MASK_NONE; * calling hvc_poll() who determines whether a console adapter support * interrupts. */ -int khvcd(void *unused) +static int khvcd(void *unused) { int poll_mask; struct hvc_struct *hp; diff --git a/drivers/char/viotape.c b/drivers/char/viotape.c index 13faf8d17482..db57277117bb 100644 --- a/drivers/char/viotape.c +++ b/drivers/char/viotape.c @@ -873,12 +873,12 @@ free_op: } const struct file_operations viotap_fops = { - owner: THIS_MODULE, - read: viotap_read, - write: viotap_write, - ioctl: viotap_ioctl, - open: viotap_open, - release: viotap_release, + .owner = THIS_MODULE, + .read = viotap_read, + .write = viotap_write, + .ioctl = viotap_ioctl, + .open = viotap_open, + .release = viotap_release, }; /* Handle interrupt events for tape */ diff --git a/drivers/pci/hotplug/rpadlpar_core.c b/drivers/pci/hotplug/rpadlpar_core.c index bb3c101c2c5a..deb6b5e35feb 100644 --- a/drivers/pci/hotplug/rpadlpar_core.c +++ b/drivers/pci/hotplug/rpadlpar_core.c @@ -159,8 +159,8 @@ static void dlpar_pci_add_bus(struct device_node *dn) /* Claim new bus resources */ pcibios_claim_one_bus(dev->bus); - /* ioremap() for child bus, which may or may not succeed */ - remap_bus_range(dev->subordinate); + /* 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); @@ -390,7 +390,7 @@ int dlpar_remove_pci_slot(char *drc_name, struct device_node *dn) } else pcibios_remove_pci_devices(bus); - if (unmap_bus_range(bus)) { + if (pcibios_unmap_io_space(bus)) { printk(KERN_ERR "%s: failed to unmap bus range\n", __FUNCTION__); return -ERANGE; diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig index 35f88649d3b7..c0c77f82d051 100644 --- a/drivers/pcmcia/Kconfig +++ b/drivers/pcmcia/Kconfig @@ -180,14 +180,15 @@ config TCIC PCMCIA cards are plugged into. If unsure, say N. config PCMCIA_M8XX - tristate "MPC8xx PCMCIA support" - depends on PCMCIA && PPC && 8xx - select PCCARD_IODYN - help - Say Y here to include support for PowerPC 8xx series PCMCIA - controller. + tristate "MPC8xx PCMCIA support" + depends on PCMCIA && PPC && 8xx + select PCCARD_IODYN + select PCCARD_NONSTATIC + help + Say Y here to include support for PowerPC 8xx series PCMCIA + controller. - This driver is also available as a module called m8xx_pcmcia. + This driver is also available as a module called m8xx_pcmcia. config HD64465_PCMCIA tristate "HD64465 host bridge support" diff --git a/drivers/pcmcia/m8xx_pcmcia.c b/drivers/pcmcia/m8xx_pcmcia.c index 9721ed7bf502..3b40f9623cc9 100644 --- a/drivers/pcmcia/m8xx_pcmcia.c +++ b/drivers/pcmcia/m8xx_pcmcia.c @@ -10,7 +10,7 @@ * Further fixes, v2.6 kernel port * * - * Some fixes, additions (C) 2005 Montavista Software, Inc. + * Some fixes, additions (C) 2005-2007 Montavista Software, Inc. * * * "The ExCA standard specifies that socket controllers should provide @@ -40,10 +40,6 @@ #include #include -#include -#include -#include - #include #include #include @@ -51,11 +47,18 @@ #include #include #include -#include +#include +#include +#include +#include +#include #include #include #include +#include +#include +#include #include #include @@ -146,27 +149,17 @@ MODULE_LICENSE("Dual MPL/GPL"); #define PCMCIA_MEM_WIN_BASE 0xe0000000 /* base address for memory window 0 */ #define PCMCIA_MEM_WIN_SIZE 0x04000000 /* each memory window is 64 MByte */ #define PCMCIA_IO_WIN_BASE _IO_BASE /* base address for io window 0 */ - -#define PCMCIA_SCHLVL PCMCIA_INTERRUPT /* Status Change Interrupt Level */ - /* ------------------------------------------------------------------------- */ -/* 2.4.x and newer has this always in HZ */ -#define M8XX_BUSFREQ ((((bd_t *)&(__res))->bi_busfreq)) - -static int pcmcia_schlvl = PCMCIA_SCHLVL; +static int pcmcia_schlvl; static DEFINE_SPINLOCK(events_lock); - #define PCMCIA_SOCKET_KEY_5V 1 #define PCMCIA_SOCKET_KEY_LV 2 /* look up table for pgcrx registers */ -static u32 *m8xx_pgcrx[2] = { - &((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_pgcra, - &((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_pgcrb -}; +static u32 *m8xx_pgcrx[2]; /* * This structure is used to address each window in the PCMCIA controller. @@ -228,11 +221,16 @@ struct event_table { u32 eventbit; }; +static const char driver_name[] = "m8xx-pcmcia"; + struct socket_info { void (*handler)(void *info, u32 events); void *info; u32 slot; + pcmconf8xx_t *pcmcia; + u32 bus_freq; + int hwirq; socket_state_t state; struct pccard_mem_map mem_win[PCMCIA_MEM_WIN_NO]; @@ -408,78 +406,21 @@ static void hardware_disable(int slot) #if defined(CONFIG_MPC885ADS) #define PCMCIA_BOARD_MSG "MPC885ADS" - -static int voltage_set(int slot, int vcc, int vpp) -{ - u32 reg = 0; - unsigned *bcsr_io; - - bcsr_io = ioremap(BCSR1, sizeof(unsigned long)); - - switch(vcc) { - case 0: - break; - case 33: - reg |= BCSR1_PCCVCC0; - break; - case 50: - reg |= BCSR1_PCCVCC1; - break; - default: - goto out_unmap; - } - - switch(vpp) { - case 0: - break; - case 33: - case 50: - if(vcc == vpp) - reg |= BCSR1_PCCVPP1; - else - goto out_unmap; - break; - case 120: - if ((vcc == 33) || (vcc == 50)) - reg |= BCSR1_PCCVPP0; - else - goto out_unmap; - default: - goto out_unmap; - } - - /* first, turn off all power */ - out_be32(bcsr_io, in_be32(bcsr_io) & ~(BCSR1_PCCVCC_MASK | BCSR1_PCCVPP_MASK)); - - /* enable new powersettings */ - out_be32(bcsr_io, in_be32(bcsr_io) | reg); - - iounmap(bcsr_io); - return 0; - -out_unmap: - iounmap(bcsr_io); - return 1; -} - #define socket_get(_slot_) PCMCIA_SOCKET_KEY_5V -static void hardware_enable(int slot) +static inline void hardware_enable(int slot) { - unsigned *bcsr_io; - - bcsr_io = ioremap(BCSR1, sizeof(unsigned long)); - out_be32(bcsr_io, in_be32(bcsr_io) & ~BCSR1_PCCEN); - iounmap(bcsr_io); + m8xx_pcmcia_ops.hw_ctrl(slot, 1); } -static void hardware_disable(int slot) +static inline void hardware_disable(int slot) { - unsigned *bcsr_io; + m8xx_pcmcia_ops.hw_ctrl(slot, 0); +} - bcsr_io = ioremap(BCSR1, sizeof(unsigned long)); - out_be32(bcsr_io, in_be32(bcsr_io) | BCSR1_PCCEN); - iounmap(bcsr_io); +static inline int voltage_set(int slot, int vcc, int vpp) +{ + return m8xx_pcmcia_ops.voltage_set(slot, vcc, vpp); } #endif @@ -604,48 +545,6 @@ static int voltage_set(int slot, int vcc, int vpp) #endif /* CONFIG_PRxK */ -static void m8xx_shutdown(void) -{ - u32 m, i; - struct pcmcia_win *w; - - for(i = 0; i < PCMCIA_SOCKETS_NO; i++){ - w = (void *) &((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_pbr0; - - out_be32(&((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_pscr, M8XX_PCMCIA_MASK(i)); - out_be32(&((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_per, in_be32(&((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_per) & ~M8XX_PCMCIA_MASK(i)); - - /* turn off interrupt and disable CxOE */ - out_be32(M8XX_PGCRX(i), M8XX_PGCRX_CXOE); - - /* turn off memory windows */ - for(m = 0; m < PCMCIA_MEM_WIN_NO; m++) { - out_be32(&w->or, 0); /* set to not valid */ - w++; - } - - /* turn off voltage */ - voltage_set(i, 0, 0); - - /* disable external hardware */ - hardware_disable(i); - } - - free_irq(pcmcia_schlvl, NULL); -} - -static struct device_driver m8xx_driver = { - .name = "m8xx-pcmcia", - .bus = &platform_bus_type, - .suspend = pcmcia_socket_dev_suspend, - .resume = pcmcia_socket_dev_resume, -}; - -static struct platform_device m8xx_device = { - .name = "m8xx-pcmcia", - .id = 0, -}; - static u32 pending_events[PCMCIA_SOCKETS_NO]; static DEFINE_SPINLOCK(pending_event_lock); @@ -654,13 +553,14 @@ static irqreturn_t m8xx_interrupt(int irq, void *dev) struct socket_info *s; struct event_table *e; unsigned int i, events, pscr, pipr, per; + pcmconf8xx_t *pcmcia = socket[0].pcmcia; dprintk("Interrupt!\n"); /* get interrupt sources */ - pscr = in_be32(&((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_pscr); - pipr = in_be32(&((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_pipr); - per = in_be32(&((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_per); + pscr = in_be32(&pcmcia->pcmc_pscr); + pipr = in_be32(&pcmcia->pcmc_pipr); + per = in_be32(&pcmcia->pcmc_per); for(i = 0; i < PCMCIA_SOCKETS_NO; i++) { s = &socket[i]; @@ -724,7 +624,7 @@ static irqreturn_t m8xx_interrupt(int irq, void *dev) per &= ~M8XX_PCMCIA_RDY_L(0); per &= ~M8XX_PCMCIA_RDY_L(1); - out_be32(&((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_per, per); + out_be32(&pcmcia->pcmc_per, per); if (events) pcmcia_parse_events(&socket[i].socket, events); @@ -732,7 +632,7 @@ static irqreturn_t m8xx_interrupt(int irq, void *dev) } /* clear the interrupt sources */ - out_be32(&((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_pscr, pscr); + out_be32(&pcmcia->pcmc_pscr, pscr); dprintk("Interrupt done.\n"); @@ -753,7 +653,7 @@ static u32 m8xx_get_graycode(u32 size) return k; } -static u32 m8xx_get_speed(u32 ns, u32 is_io) +static u32 m8xx_get_speed(u32 ns, u32 is_io, u32 bus_freq) { u32 reg, clocks, psst, psl, psht; @@ -781,7 +681,7 @@ static u32 m8xx_get_speed(u32 ns, u32 is_io) #define ADJ 180 /* 80 % longer accesstime - to be sure */ - clocks = ((M8XX_BUSFREQ / 1000) * ns) / 1000; + clocks = ((bus_freq / 1000) * ns) / 1000; clocks = (clocks * ADJ) / (100*1000); if(clocks >= PCMCIA_BMT_LIMIT) { printk( "Max access time limit reached\n"); @@ -806,8 +706,9 @@ static int m8xx_get_status(struct pcmcia_socket *sock, unsigned int *value) int lsock = container_of(sock, struct socket_info, socket)->slot; struct socket_info *s = &socket[lsock]; unsigned int pipr, reg; + pcmconf8xx_t *pcmcia = s->pcmcia; - pipr = in_be32(&((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_pipr); + pipr = in_be32(&pcmcia->pcmc_pipr); *value = ((pipr & (M8XX_PCMCIA_CD1(lsock) | M8XX_PCMCIA_CD2(lsock))) == 0) ? SS_DETECT : 0; @@ -918,6 +819,7 @@ static int m8xx_set_socket(struct pcmcia_socket *sock, socket_state_t *state) struct event_table *e; unsigned int reg; unsigned long flags; + pcmconf8xx_t *pcmcia = socket[0].pcmcia; dprintk( "SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, " "io_irq %d, csc_mask %#2.2x)\n", lsock, state->flags, @@ -927,6 +829,7 @@ static int m8xx_set_socket(struct pcmcia_socket *sock, socket_state_t *state) if(voltage_set(lsock, state->Vcc, state->Vpp)) return -EINVAL; + /* Take care of reset... */ if(state->flags & SS_RESET) out_be32(M8XX_PGCRX(lsock), in_be32(M8XX_PGCRX(lsock)) | M8XX_PGCRX_CXRESET); /* active high */ @@ -982,7 +885,8 @@ static int m8xx_set_socket(struct pcmcia_socket *sock, socket_state_t *state) * If io_irq is non-zero we should enable irq. */ if(state->io_irq) { - out_be32(M8XX_PGCRX(lsock), in_be32(M8XX_PGCRX(lsock)) | mk_int_int_mask(state->io_irq) << 24); + out_be32(M8XX_PGCRX(lsock), + in_be32(M8XX_PGCRX(lsock)) | mk_int_int_mask(s->hwirq) << 24); /* * Strange thing here: * The manual does not tell us which interrupt @@ -1027,7 +931,7 @@ static int m8xx_set_socket(struct pcmcia_socket *sock, socket_state_t *state) * Writing ones will clear the bits. */ - out_be32(&((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_pscr, reg); + out_be32(&pcmcia->pcmc_pscr, reg); /* * Write the mask. @@ -1036,15 +940,8 @@ static int m8xx_set_socket(struct pcmcia_socket *sock, socket_state_t *state) * Ones will enable the interrupt. */ - /* - reg |= ((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_per - & M8XX_PCMCIA_MASK(lsock); - */ - - reg |= in_be32(&((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_per) & - (M8XX_PCMCIA_MASK(0) | M8XX_PCMCIA_MASK(1)); - - out_be32(&((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_per, reg); + reg |= in_be32(&pcmcia->pcmc_per) & (M8XX_PCMCIA_MASK(0) | M8XX_PCMCIA_MASK(1)); + out_be32(&pcmcia->pcmc_per, reg); spin_unlock_irqrestore(&events_lock, flags); @@ -1062,6 +959,8 @@ static int m8xx_set_io_map(struct pcmcia_socket *sock, struct pccard_io_map *io) struct socket_info *s = &socket[lsock]; struct pcmcia_win *w; unsigned int reg, winnr; + pcmconf8xx_t *pcmcia = s->pcmcia; + #define M8XX_SIZE (io->stop - io->start + 1) #define M8XX_BASE (PCMCIA_IO_WIN_BASE + io->start) @@ -1086,7 +985,7 @@ static int m8xx_set_io_map(struct pcmcia_socket *sock, struct pccard_io_map *io) /* setup registers */ - w = (void *) &((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_pbr0; + w = (void *) &pcmcia->pcmc_pbr0; w += winnr; out_be32(&w->or, 0); /* turn off window first */ @@ -1095,12 +994,13 @@ static int m8xx_set_io_map(struct pcmcia_socket *sock, struct pccard_io_map *io) reg <<= 27; reg |= M8XX_PCMCIA_POR_IO |(lsock << 2); - reg |= m8xx_get_speed(io->speed, 1); + reg |= m8xx_get_speed(io->speed, 1, s->bus_freq); if(io->flags & MAP_WRPROT) reg |= M8XX_PCMCIA_POR_WRPROT; - if(io->flags & (MAP_16BIT | MAP_AUTOSZ)) + /*if(io->flags & (MAP_16BIT | MAP_AUTOSZ))*/ + if(io->flags & MAP_16BIT) reg |= M8XX_PCMCIA_POR_16BIT; if(io->flags & MAP_ACTIVE) @@ -1117,7 +1017,7 @@ static int m8xx_set_io_map(struct pcmcia_socket *sock, struct pccard_io_map *io) /* setup registers */ - w = (void *) &((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_pbr0; + w = (void *) &pcmcia->pcmc_pbr0; w += winnr; out_be32(&w->or, 0); /* turn off window */ @@ -1144,6 +1044,7 @@ static int m8xx_set_mem_map(struct pcmcia_socket *sock, struct pccard_mem_map *m struct pcmcia_win *w; struct pccard_mem_map *old; unsigned int reg, winnr; + pcmconf8xx_t *pcmcia = s->pcmcia; dprintk( "SetMemMap(%d, %d, %#2.2x, %d ns, " "%#5.5lx, %#5.5x)\n", lsock, mem->map, mem->flags, @@ -1166,12 +1067,12 @@ static int m8xx_set_mem_map(struct pcmcia_socket *sock, struct pccard_mem_map *m /* Setup the window in the pcmcia controller */ - w = (void *) &((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_pbr0; + w = (void *) &pcmcia->pcmc_pbr0; w += winnr; reg |= lsock << 2; - reg |= m8xx_get_speed(mem->speed, 0); + reg |= m8xx_get_speed(mem->speed, 0, s->bus_freq); if(mem->flags & MAP_ATTRIB) reg |= M8XX_PCMCIA_POR_ATTRMEM; @@ -1236,60 +1137,69 @@ static int m8xx_sock_init(struct pcmcia_socket *sock) } -static int m8xx_suspend(struct pcmcia_socket *sock) +static int m8xx_sock_suspend(struct pcmcia_socket *sock) { return m8xx_set_socket(sock, &dead_socket); } static struct pccard_operations m8xx_services = { .init = m8xx_sock_init, - .suspend = m8xx_suspend, + .suspend = m8xx_sock_suspend, .get_status = m8xx_get_status, .set_socket = m8xx_set_socket, .set_io_map = m8xx_set_io_map, .set_mem_map = m8xx_set_mem_map, }; -static int __init m8xx_init(void) +static int __init m8xx_probe(struct of_device *ofdev, const struct of_device_id *match) { struct pcmcia_win *w; - unsigned int i,m; + unsigned int i, m, hwirq; + pcmconf8xx_t *pcmcia; + int status; + struct device_node *np = ofdev->node; pcmcia_info("%s\n", version); - if (driver_register(&m8xx_driver)) - return -1; + pcmcia = of_iomap(np, 0); + if(pcmcia == NULL) + return -EINVAL; + + pcmcia_schlvl = irq_of_parse_and_map(np, 0); + hwirq = irq_map[pcmcia_schlvl].hwirq; + if (pcmcia_schlvl < 0) + return -EINVAL; + + m8xx_pgcrx[0] = &pcmcia->pcmc_pgcra; + m8xx_pgcrx[1] = &pcmcia->pcmc_pgcrb; + pcmcia_info(PCMCIA_BOARD_MSG " using " PCMCIA_SLOT_MSG - " with IRQ %u.\n", pcmcia_schlvl); + " with IRQ %u (%d). \n", pcmcia_schlvl, hwirq); /* Configure Status change interrupt */ - if(request_irq(pcmcia_schlvl, m8xx_interrupt, 0, - "m8xx_pcmcia", NULL)) { + if(request_irq(pcmcia_schlvl, m8xx_interrupt, IRQF_SHARED, + driver_name, socket)) { pcmcia_error("Cannot allocate IRQ %u for SCHLVL!\n", pcmcia_schlvl); return -1; } - w = (void *) &((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_pbr0; + w = (void *) &pcmcia->pcmc_pbr0; - out_be32(&((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_pscr, - M8XX_PCMCIA_MASK(0)| M8XX_PCMCIA_MASK(1)); + out_be32(&pcmcia->pcmc_pscr, M8XX_PCMCIA_MASK(0)| M8XX_PCMCIA_MASK(1)); + clrbits32(&pcmcia->pcmc_per, M8XX_PCMCIA_MASK(0) | M8XX_PCMCIA_MASK(1)); - out_be32(&((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_per, - in_be32(&((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_per) & - ~(M8XX_PCMCIA_MASK(0)| M8XX_PCMCIA_MASK(1))); + /* connect interrupt and disable CxOE */ -/* connect interrupt and disable CxOE */ + out_be32(M8XX_PGCRX(0), M8XX_PGCRX_CXOE | (mk_int_int_mask(hwirq) << 16)); + out_be32(M8XX_PGCRX(1), M8XX_PGCRX_CXOE | (mk_int_int_mask(hwirq) << 16)); - out_be32(M8XX_PGCRX(0), M8XX_PGCRX_CXOE | (mk_int_int_mask(pcmcia_schlvl) << 16)); - out_be32(M8XX_PGCRX(1), M8XX_PGCRX_CXOE | (mk_int_int_mask(pcmcia_schlvl) << 16)); - -/* intialize the fixed memory windows */ + /* intialize the fixed memory windows */ for(i = 0; i < PCMCIA_SOCKETS_NO; i++){ - for(m = 0; m < PCMCIA_MEM_WIN_NO; m++) { + for (m = 0; m < PCMCIA_MEM_WIN_NO; m++) { out_be32(&w->br, PCMCIA_MEM_WIN_BASE + (PCMCIA_MEM_WIN_SIZE * (m + i * PCMCIA_MEM_WIN_NO))); @@ -1300,16 +1210,14 @@ static int __init m8xx_init(void) } } -/* turn off voltage */ + /* turn off voltage */ voltage_set(0, 0, 0); voltage_set(1, 0, 0); -/* Enable external hardware */ + /* Enable external hardware */ hardware_enable(0); hardware_enable(1); - platform_device_register(&m8xx_device); - for (i = 0 ; i < PCMCIA_SOCKETS_NO; i++) { socket[i].slot = i; socket[i].socket.owner = THIS_MODULE; @@ -1317,30 +1225,105 @@ static int __init m8xx_init(void) socket[i].socket.irq_mask = 0x000; socket[i].socket.map_size = 0x1000; socket[i].socket.io_offset = 0; - socket[i].socket.pci_irq = i ? 7 : 9; + socket[i].socket.pci_irq = pcmcia_schlvl; socket[i].socket.ops = &m8xx_services; - socket[i].socket.resource_ops = &pccard_iodyn_ops; + socket[i].socket.resource_ops = &pccard_nonstatic_ops; socket[i].socket.cb_dev = NULL; - socket[i].socket.dev.parent = &m8xx_device.dev; + socket[i].socket.dev.parent = &ofdev->dev; + socket[i].pcmcia = pcmcia; + socket[i].bus_freq = ppc_proc_freq; + socket[i].hwirq = hwirq; + + } - for (i = 0; i < PCMCIA_SOCKETS_NO; i++) - pcmcia_register_socket(&socket[i].socket); + for (i = 0; i < PCMCIA_SOCKETS_NO; i++) { + status = pcmcia_register_socket(&socket[i].socket); + if (status < 0) + pcmcia_error("Socket register failed\n"); + } return 0; } -static void __exit m8xx_exit(void) +static int m8xx_remove(struct of_device* ofdev) { - int i; + u32 m, i; + struct pcmcia_win *w; + pcmconf8xx_t *pcmcia = socket[0].pcmcia; + for (i = 0; i < PCMCIA_SOCKETS_NO; i++) { + w = (void *) &pcmcia->pcmc_pbr0; + + out_be32(&pcmcia->pcmc_pscr, M8XX_PCMCIA_MASK(i)); + out_be32(&pcmcia->pcmc_per, + in_be32(&pcmcia->pcmc_per) & ~M8XX_PCMCIA_MASK(i)); + + /* turn off interrupt and disable CxOE */ + out_be32(M8XX_PGCRX(i), M8XX_PGCRX_CXOE); + + /* turn off memory windows */ + for (m = 0; m < PCMCIA_MEM_WIN_NO; m++) { + out_be32(&w->or, 0); /* set to not valid */ + w++; + } + + /* turn off voltage */ + voltage_set(i, 0, 0); + + /* disable external hardware */ + hardware_disable(i); + } for (i = 0; i < PCMCIA_SOCKETS_NO; i++) pcmcia_unregister_socket(&socket[i].socket); - m8xx_shutdown(); + free_irq(pcmcia_schlvl, NULL); - platform_device_unregister(&m8xx_device); - driver_unregister(&m8xx_driver); + return 0; +} + +#ifdef CONFIG_PM +static int m8xx_suspend(struct platform_device *pdev, pm_message_t state) +{ + return pcmcia_socket_dev_suspend(&pdev->dev, state); +} + +static int m8xx_resume(struct platform_device *pdev) +{ + return pcmcia_socket_dev_resume(&pdev->dev); +} +#else +#define m8xx_suspend NULL +#define m8xx_resume NULL +#endif + +static struct of_device_id m8xx_pcmcia_match[] = { + { + .type = "pcmcia", + .compatible = "fsl,pq-pcmcia", + }, + {}, +}; + +MODULE_DEVICE_TABLE(of, m8xx_pcmcia_match); + +static struct of_platform_driver m8xx_pcmcia_driver = { + .name = (char *) driver_name, + .match_table = m8xx_pcmcia_match, + .probe = m8xx_probe, + .remove = m8xx_remove, + .suspend = m8xx_suspend, + .resume = m8xx_resume, +}; + +static int __init m8xx_init(void) +{ + return of_register_platform_driver(&m8xx_pcmcia_driver); +} + +static void __exit m8xx_exit(void) +{ + of_unregister_platform_driver(&m8xx_pcmcia_driver); } module_init(m8xx_init); diff --git a/drivers/ps3/Makefile b/drivers/ps3/Makefile index e251d1c1171c..746031de2195 100644 --- a/drivers/ps3/Makefile +++ b/drivers/ps3/Makefile @@ -1,3 +1,6 @@ obj-$(CONFIG_PS3_VUART) += vuart.o -obj-$(CONFIG_PS3_PS3AV) += ps3av.o ps3av_cmd.o +obj-$(CONFIG_PS3_PS3AV) += ps3av_mod.o +ps3av_mod-objs += ps3av.o ps3av_cmd.o +obj-$(CONFIG_PPC_PS3) += sys-manager-core.o obj-$(CONFIG_PS3_SYS_MANAGER) += sys-manager.o +obj-$(CONFIG_PS3_STORAGE) += ps3stor_lib.o diff --git a/drivers/ps3/ps3av.c b/drivers/ps3/ps3av.c index 1393e64335f9..85e21614f868 100644 --- a/drivers/ps3/ps3av.c +++ b/drivers/ps3/ps3av.c @@ -1,32 +1,30 @@ /* - * Copyright (C) 2006 Sony Computer Entertainment Inc. - * Copyright 2006, 2007 Sony Corporation + * PS3 AV backend support. * - * AV backend support for PS3 + * Copyright (C) 2007 Sony Computer Entertainment Inc. + * Copyright 2007 Sony Corp. * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published - * by the Free Software Foundation; version 2 of the License. + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. + * 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., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * 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 #include #include #include -#include -#include #include #include -#include #include #include @@ -39,13 +37,12 @@ static int timeout = 5000; /* in msec ( 5 sec ) */ module_param(timeout, int, 0644); static struct ps3av { - int available; struct mutex mutex; struct work_struct work; struct completion done; struct workqueue_struct *wq; int open_count; - struct ps3_vuart_port_device *dev; + struct ps3_system_bus_device *dev; int region; struct ps3av_pkt_av_get_hw_conf av_hw_conf; @@ -55,11 +52,13 @@ static struct ps3av { u32 audio_port; int ps3av_mode; int ps3av_mode_old; -} ps3av; - -static struct ps3_vuart_port_device ps3av_dev = { - .match_id = PS3_MATCH_ID_AV_SETTINGS -}; + union { + 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 */ #define YUV444 PS3AV_CMD_VIDEO_CS_YUV444_8 @@ -169,7 +168,7 @@ static int ps3av_parse_event_packet(const struct ps3av_reply_hdr *hdr) if (hdr->cid & PS3AV_EVENT_CMD_MASK) { table = ps3av_search_cmd_table(hdr->cid, PS3AV_EVENT_CMD_MASK); if (table) - dev_dbg(&ps3av_dev.core, + dev_dbg(&ps3av->dev->core, "recv event packet cid:%08x port:0x%x size:%d\n", hdr->cid, ps3av_event_get_port_id(hdr->cid), hdr->size); @@ -182,6 +181,41 @@ static int ps3av_parse_event_packet(const struct ps3av_reply_hdr *hdr) return 0; } + +#define POLLING_INTERVAL 25 /* in msec */ + +static int ps3av_vuart_write(struct ps3_system_bus_device *dev, + const void *buf, unsigned long size) +{ + int error; + dev_dbg(&dev->core, " -> %s:%d\n", __func__, __LINE__); + error = ps3_vuart_write(dev, buf, size); + dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__); + return error ? error : size; +} + +static int ps3av_vuart_read(struct ps3_system_bus_device *dev, void *buf, + unsigned long size, int timeout) +{ + int error; + int loopcnt = 0; + + dev_dbg(&dev->core, " -> %s:%d\n", __func__, __LINE__); + timeout = (timeout + POLLING_INTERVAL - 1) / POLLING_INTERVAL; + while (loopcnt++ <= timeout) { + error = ps3_vuart_read(dev, buf, size); + if (!error) + return size; + if (error != -EAGAIN) { + printk(KERN_ERR "%s: ps3_vuart_read failed %d\n", + __func__, error); + return error; + } + msleep(POLLING_INTERVAL); + } + return -EWOULDBLOCK; +} + static int ps3av_send_cmd_pkt(const struct ps3av_send_hdr *send_buf, struct ps3av_reply_hdr *recv_buf, int write_len, int read_len) @@ -190,13 +224,13 @@ static int ps3av_send_cmd_pkt(const struct ps3av_send_hdr *send_buf, u32 cmd; int event; - if (!ps3av.available) + if (!ps3av) return -ENODEV; /* send pkt */ - res = ps3av_vuart_write(ps3av.dev, send_buf, write_len); + res = ps3av_vuart_write(ps3av->dev, send_buf, write_len); if (res < 0) { - dev_dbg(&ps3av_dev.core, + dev_dbg(&ps3av->dev->core, "%s: ps3av_vuart_write() failed (result=%d)\n", __func__, res); return res; @@ -206,20 +240,20 @@ static int ps3av_send_cmd_pkt(const struct ps3av_send_hdr *send_buf, cmd = send_buf->cid; do { /* read header */ - res = ps3av_vuart_read(ps3av.dev, recv_buf, PS3AV_HDR_SIZE, + res = ps3av_vuart_read(ps3av->dev, recv_buf, PS3AV_HDR_SIZE, timeout); if (res != PS3AV_HDR_SIZE) { - dev_dbg(&ps3av_dev.core, + dev_dbg(&ps3av->dev->core, "%s: ps3av_vuart_read() failed (result=%d)\n", __func__, res); return res; } /* read body */ - res = ps3av_vuart_read(ps3av.dev, &recv_buf->cid, + res = ps3av_vuart_read(ps3av->dev, &recv_buf->cid, recv_buf->size, timeout); if (res < 0) { - dev_dbg(&ps3av_dev.core, + dev_dbg(&ps3av->dev->core, "%s: ps3av_vuart_read() failed (result=%d)\n", __func__, res); return res; @@ -230,7 +264,7 @@ static int ps3av_send_cmd_pkt(const struct ps3av_send_hdr *send_buf, } while (event); if ((cmd | PS3AV_REPLY_BIT) != recv_buf->cid) { - dev_dbg(&ps3av_dev.core, "%s: reply err (result=%x)\n", + dev_dbg(&ps3av->dev->core, "%s: reply err (result=%x)\n", __func__, recv_buf->cid); return -EINVAL; } @@ -245,7 +279,7 @@ static int ps3av_process_reply_packet(struct ps3av_send_hdr *cmd_buf, int return_len; if (recv_buf->version != PS3AV_VERSION) { - dev_dbg(&ps3av_dev.core, "reply_packet invalid version:%x\n", + dev_dbg(&ps3av->dev->core, "reply_packet invalid version:%x\n", recv_buf->version); return -EFAULT; } @@ -267,16 +301,11 @@ int ps3av_do_pkt(u32 cid, u16 send_len, size_t usr_buf_size, struct ps3av_send_hdr *buf) { int res = 0; - static union { - struct ps3av_reply_hdr reply_hdr; - u8 raw[PS3AV_BUF_SIZE]; - } recv_buf; - u32 *table; - BUG_ON(!ps3av.available); + BUG_ON(!ps3av); - mutex_lock(&ps3av.mutex); + mutex_lock(&ps3av->mutex); table = ps3av_search_cmd_table(cid, PS3AV_CID_MASK); BUG_ON(!table); @@ -288,7 +317,7 @@ int ps3av_do_pkt(u32 cid, u16 send_len, size_t usr_buf_size, ps3av_set_hdr(cid, send_len, buf); /* send packet via vuart */ - res = ps3av_send_cmd_pkt(buf, &recv_buf.reply_hdr, send_len, + res = ps3av_send_cmd_pkt(buf, &ps3av->recv_buf.reply_hdr, send_len, usr_buf_size); if (res < 0) { printk(KERN_ERR @@ -298,7 +327,7 @@ int ps3av_do_pkt(u32 cid, u16 send_len, size_t usr_buf_size, } /* process reply packet */ - res = ps3av_process_reply_packet(buf, &recv_buf.reply_hdr, + res = ps3av_process_reply_packet(buf, &ps3av->recv_buf.reply_hdr, usr_buf_size); if (res < 0) { printk(KERN_ERR "%s: put_return_status() failed (result=%d)\n", @@ -306,11 +335,11 @@ int ps3av_do_pkt(u32 cid, u16 send_len, size_t usr_buf_size, goto err; } - mutex_unlock(&ps3av.mutex); + mutex_unlock(&ps3av->mutex); return 0; err: - mutex_unlock(&ps3av.mutex); + mutex_unlock(&ps3av->mutex); printk(KERN_ERR "%s: failed cid:%x res:%d\n", __func__, cid, res); return res; } @@ -319,11 +348,11 @@ static int ps3av_set_av_video_mute(u32 mute) { int i, num_of_av_port, res; - num_of_av_port = ps3av.av_hw_conf.num_of_hdmi + - ps3av.av_hw_conf.num_of_avmulti; + num_of_av_port = ps3av->av_hw_conf.num_of_hdmi + + ps3av->av_hw_conf.num_of_avmulti; /* video mute on */ for (i = 0; i < num_of_av_port; i++) { - res = ps3av_cmd_av_video_mute(1, &ps3av.av_port[i], mute); + res = ps3av_cmd_av_video_mute(1, &ps3av->av_port[i], mute); if (res < 0) return -1; } @@ -335,13 +364,13 @@ static int ps3av_set_video_disable_sig(void) { int i, num_of_hdmi_port, num_of_av_port, res; - num_of_hdmi_port = ps3av.av_hw_conf.num_of_hdmi; - num_of_av_port = ps3av.av_hw_conf.num_of_hdmi + - ps3av.av_hw_conf.num_of_avmulti; + num_of_hdmi_port = ps3av->av_hw_conf.num_of_hdmi; + num_of_av_port = ps3av->av_hw_conf.num_of_hdmi + + ps3av->av_hw_conf.num_of_avmulti; /* tv mute */ for (i = 0; i < num_of_hdmi_port; i++) { - res = ps3av_cmd_av_tv_mute(ps3av.av_port[i], + res = ps3av_cmd_av_tv_mute(ps3av->av_port[i], PS3AV_CMD_MUTE_ON); if (res < 0) return -1; @@ -350,11 +379,11 @@ static int ps3av_set_video_disable_sig(void) /* video mute on */ for (i = 0; i < num_of_av_port; i++) { - res = ps3av_cmd_av_video_disable_sig(ps3av.av_port[i]); + res = ps3av_cmd_av_video_disable_sig(ps3av->av_port[i]); if (res < 0) return -1; if (i < num_of_hdmi_port) { - res = ps3av_cmd_av_tv_mute(ps3av.av_port[i], + res = ps3av_cmd_av_tv_mute(ps3av->av_port[i], PS3AV_CMD_MUTE_OFF); if (res < 0) return -1; @@ -369,17 +398,17 @@ static int ps3av_set_audio_mute(u32 mute) { int i, num_of_av_port, num_of_opt_port, res; - num_of_av_port = ps3av.av_hw_conf.num_of_hdmi + - ps3av.av_hw_conf.num_of_avmulti; - num_of_opt_port = ps3av.av_hw_conf.num_of_spdif; + num_of_av_port = ps3av->av_hw_conf.num_of_hdmi + + ps3av->av_hw_conf.num_of_avmulti; + num_of_opt_port = ps3av->av_hw_conf.num_of_spdif; for (i = 0; i < num_of_av_port; i++) { - res = ps3av_cmd_av_audio_mute(1, &ps3av.av_port[i], mute); + res = ps3av_cmd_av_audio_mute(1, &ps3av->av_port[i], mute); if (res < 0) return -1; } for (i = 0; i < num_of_opt_port; i++) { - res = ps3av_cmd_audio_mute(1, &ps3av.opt_port[i], mute); + res = ps3av_cmd_audio_mute(1, &ps3av->opt_port[i], mute); if (res < 0) return -1; } @@ -394,40 +423,40 @@ int ps3av_set_audio_mode(u32 ch, u32 fs, u32 word_bits, u32 format, u32 source) struct ps3av_pkt_audio_mode audio_mode; u32 len = 0; - num_of_audio = ps3av.av_hw_conf.num_of_hdmi + - ps3av.av_hw_conf.num_of_avmulti + - ps3av.av_hw_conf.num_of_spdif; + num_of_audio = ps3av->av_hw_conf.num_of_hdmi + + ps3av->av_hw_conf.num_of_avmulti + + ps3av->av_hw_conf.num_of_spdif; avb_param.num_of_video_pkt = 0; avb_param.num_of_audio_pkt = PS3AV_AVB_NUM_AUDIO; /* always 0 */ avb_param.num_of_av_video_pkt = 0; - avb_param.num_of_av_audio_pkt = ps3av.av_hw_conf.num_of_hdmi; + avb_param.num_of_av_audio_pkt = ps3av->av_hw_conf.num_of_hdmi; - vid = video_mode_table[ps3av.ps3av_mode].vid; + vid = video_mode_table[ps3av->ps3av_mode].vid; /* audio mute */ ps3av_set_audio_mute(PS3AV_CMD_MUTE_ON); /* audio inactive */ - res = ps3av_cmd_audio_active(0, ps3av.audio_port); + res = ps3av_cmd_audio_active(0, ps3av->audio_port); if (res < 0) - dev_dbg(&ps3av_dev.core, + dev_dbg(&ps3av->dev->core, "ps3av_cmd_audio_active OFF failed\n"); /* audio_pkt */ for (i = 0; i < num_of_audio; i++) { - ps3av_cmd_set_audio_mode(&audio_mode, ps3av.av_port[i], ch, fs, - word_bits, format, source); - if (i < ps3av.av_hw_conf.num_of_hdmi) { + ps3av_cmd_set_audio_mode(&audio_mode, ps3av->av_port[i], ch, + fs, word_bits, format, source); + if (i < ps3av->av_hw_conf.num_of_hdmi) { /* hdmi only */ len += ps3av_cmd_set_av_audio_param(&avb_param.buf[len], - ps3av.av_port[i], + ps3av->av_port[i], &audio_mode, vid); } /* audio_mode pkt should be sent separately */ res = ps3av_cmd_audio_mode(&audio_mode); if (res < 0) - dev_dbg(&ps3av_dev.core, + dev_dbg(&ps3av->dev->core, "ps3av_cmd_audio_mode failed, port:%x\n", i); } @@ -435,15 +464,16 @@ int ps3av_set_audio_mode(u32 ch, u32 fs, u32 word_bits, u32 format, u32 source) len += offsetof(struct ps3av_pkt_avb_param, buf); res = ps3av_cmd_avb_param(&avb_param, len); if (res < 0) - dev_dbg(&ps3av_dev.core, "ps3av_cmd_avb_param failed\n"); + dev_dbg(&ps3av->dev->core, "ps3av_cmd_avb_param failed\n"); /* audio mute */ ps3av_set_audio_mute(PS3AV_CMD_MUTE_OFF); /* audio active */ - res = ps3av_cmd_audio_active(1, ps3av.audio_port); + res = ps3av_cmd_audio_active(1, ps3av->audio_port); if (res < 0) - dev_dbg(&ps3av_dev.core, "ps3av_cmd_audio_active ON failed\n"); + dev_dbg(&ps3av->dev->core, + "ps3av_cmd_audio_active ON failed\n"); return 0; } @@ -456,7 +486,7 @@ static int ps3av_set_videomode(void) ps3av_set_av_video_mute(PS3AV_CMD_MUTE_ON); /* wake up ps3avd to do the actual video mode setting */ - queue_work(ps3av.wq, &ps3av.work); + queue_work(ps3av->wq, &ps3av->work); return 0; } @@ -473,8 +503,8 @@ static void ps3av_set_videomode_cont(u32 id, u32 old_id) avb_param.num_of_video_pkt = PS3AV_AVB_NUM_VIDEO; /* num of head */ avb_param.num_of_audio_pkt = 0; - avb_param.num_of_av_video_pkt = ps3av.av_hw_conf.num_of_hdmi + - ps3av.av_hw_conf.num_of_avmulti; + avb_param.num_of_av_video_pkt = ps3av->av_hw_conf.num_of_hdmi + + ps3av->av_hw_conf.num_of_avmulti; avb_param.num_of_av_audio_pkt = 0; /* video signal off */ @@ -484,21 +514,21 @@ static void ps3av_set_videomode_cont(u32 id, u32 old_id) if (id & PS3AV_MODE_HDCP_OFF) { res = ps3av_cmd_av_hdmi_mode(PS3AV_CMD_AV_HDMI_HDCP_OFF); if (res == PS3AV_STATUS_UNSUPPORTED_HDMI_MODE) - dev_dbg(&ps3av_dev.core, "Not supported\n"); + dev_dbg(&ps3av->dev->core, "Not supported\n"); else if (res) - dev_dbg(&ps3av_dev.core, + dev_dbg(&ps3av->dev->core, "ps3av_cmd_av_hdmi_mode failed\n"); } else if (old_id & PS3AV_MODE_HDCP_OFF) { res = ps3av_cmd_av_hdmi_mode(PS3AV_CMD_AV_HDMI_MODE_NORMAL); if (res < 0 && res != PS3AV_STATUS_UNSUPPORTED_HDMI_MODE) - dev_dbg(&ps3av_dev.core, + dev_dbg(&ps3av->dev->core, "ps3av_cmd_av_hdmi_mode failed\n"); } /* video_pkt */ for (i = 0; i < avb_param.num_of_video_pkt; i++) len += ps3av_cmd_set_video_mode(&avb_param.buf[len], - ps3av.head[i], video_mode->vid, + ps3av->head[i], video_mode->vid, video_mode->fmt, id); /* av_video_pkt */ for (i = 0; i < avb_param.num_of_av_video_pkt; i++) { @@ -507,12 +537,12 @@ static void ps3av_set_videomode_cont(u32 id, u32 old_id) else av_video_cs = video_mode->cs; #ifndef PS3AV_HDMI_YUV - if (ps3av.av_port[i] == PS3AV_CMD_AVPORT_HDMI_0 || - ps3av.av_port[i] == PS3AV_CMD_AVPORT_HDMI_1) + if (ps3av->av_port[i] == PS3AV_CMD_AVPORT_HDMI_0 || + ps3av->av_port[i] == PS3AV_CMD_AVPORT_HDMI_1) av_video_cs = RGB8; /* use RGB for HDMI */ #endif len += ps3av_cmd_set_av_video_cs(&avb_param.buf[len], - ps3av.av_port[i], + ps3av->av_port[i], video_mode->vid, av_video_cs, video_mode->aspect, id); } @@ -524,7 +554,7 @@ static void ps3av_set_videomode_cont(u32 id, u32 old_id) "%s: Command failed. Please try your request again. \n", __func__); else if (res) - dev_dbg(&ps3av_dev.core, "ps3av_cmd_avb_param failed\n"); + dev_dbg(&ps3av->dev->core, "ps3av_cmd_avb_param failed\n"); msleep(1500); /* av video mute */ @@ -533,8 +563,8 @@ static void ps3av_set_videomode_cont(u32 id, u32 old_id) static void ps3avd(struct work_struct *work) { - ps3av_set_videomode_cont(ps3av.ps3av_mode, ps3av.ps3av_mode_old); - complete(&ps3av.done); + ps3av_set_videomode_cont(ps3av->ps3av_mode, ps3av->ps3av_mode_old); + complete(&ps3av->done); } static int ps3av_vid2table_id(int vid) @@ -601,7 +631,7 @@ static int ps3av_hdmi_get_vid(struct ps3av_info_monitor *info) return vid; } - if (ps3av.region & PS3AV_REGION_60) + if (ps3av->region & PS3AV_REGION_60) vid = PS3AV_DEFAULT_HDMI_VID_REG_60; else vid = PS3AV_DEFAULT_HDMI_VID_REG_50; @@ -643,16 +673,16 @@ static int ps3av_auto_videomode(struct ps3av_pkt_av_get_hw_conf *av_hw_conf, vid = PS3AV_DEFAULT_DVI_VID; } else if (vid == -1) { /* no HDMI interface or HDMI is off */ - if (ps3av.region & PS3AV_REGION_60) + if (ps3av->region & PS3AV_REGION_60) vid = PS3AV_DEFAULT_AVMULTI_VID_REG_60; else vid = PS3AV_DEFAULT_AVMULTI_VID_REG_50; - if (ps3av.region & PS3AV_REGION_RGB) + if (ps3av->region & PS3AV_REGION_RGB) rgb = PS3AV_MODE_RGB; } else if (boot) { /* HDMI: using DEFAULT HDMI_VID while booting up */ info = &monitor_info.info; - if (ps3av.region & PS3AV_REGION_60) { + if (ps3av->region & PS3AV_REGION_60) { if (info->res_60.res_bits & PS3AV_RESBIT_720x480P) vid = PS3AV_DEFAULT_HDMI_VID_REG_60; else if (info->res_50.res_bits & PS3AV_RESBIT_720x576P) @@ -715,14 +745,14 @@ int ps3av_set_video_mode(u32 id, int boot) size = ARRAY_SIZE(video_mode_table); if ((id & PS3AV_MODE_MASK) > size - 1 || id < 0) { - dev_dbg(&ps3av_dev.core, "%s: error id :%d\n", __func__, id); + dev_dbg(&ps3av->dev->core, "%s: error id :%d\n", __func__, id); return -EINVAL; } /* auto mode */ option = id & ~PS3AV_MODE_MASK; if ((id & PS3AV_MODE_MASK) == 0) { - id = ps3av_auto_videomode(&ps3av.av_hw_conf, boot); + id = ps3av_auto_videomode(&ps3av->av_hw_conf, boot); if (id < 1) { printk(KERN_ERR "%s: invalid id :%d\n", __func__, id); return -EINVAL; @@ -731,11 +761,11 @@ int ps3av_set_video_mode(u32 id, int boot) } /* set videomode */ - wait_for_completion(&ps3av.done); - ps3av.ps3av_mode_old = ps3av.ps3av_mode; - ps3av.ps3av_mode = id; + wait_for_completion(&ps3av->done); + ps3av->ps3av_mode_old = ps3av->ps3av_mode; + ps3av->ps3av_mode = id; if (ps3av_set_videomode()) - ps3av.ps3av_mode = ps3av.ps3av_mode_old; + ps3av->ps3av_mode = ps3av->ps3av_mode_old; return 0; } @@ -744,7 +774,7 @@ EXPORT_SYMBOL_GPL(ps3av_set_video_mode); int ps3av_get_auto_mode(int boot) { - return ps3av_auto_videomode(&ps3av.av_hw_conf, boot); + return ps3av_auto_videomode(&ps3av->av_hw_conf, boot); } EXPORT_SYMBOL_GPL(ps3av_get_auto_mode); @@ -772,7 +802,7 @@ EXPORT_SYMBOL_GPL(ps3av_set_mode); int ps3av_get_mode(void) { - return ps3av.ps3av_mode; + return ps3av ? ps3av->ps3av_mode : 0; } EXPORT_SYMBOL_GPL(ps3av_get_mode); @@ -842,82 +872,65 @@ int ps3av_audio_mute(int mute) EXPORT_SYMBOL_GPL(ps3av_audio_mute); -int ps3av_dev_open(void) +void ps3av_register_flip_ctl(void (*flip_ctl)(int on, void *data), + void *flip_data) { - int status = 0; + 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); - mutex_lock(&ps3av.mutex); - if (!ps3av.open_count++) { - status = lv1_gpu_open(0); - if (status) { - printk(KERN_ERR "%s: lv1_gpu_open failed %d\n", - __func__, status); - ps3av.open_count--; - } - } - mutex_unlock(&ps3av.mutex); - - return status; +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); } -EXPORT_SYMBOL_GPL(ps3av_dev_open); - -int ps3av_dev_close(void) -{ - int status = 0; - - mutex_lock(&ps3av.mutex); - if (ps3av.open_count <= 0) { - printk(KERN_ERR "%s: GPU already closed\n", __func__); - status = -1; - } else if (!--ps3av.open_count) { - status = lv1_gpu_close(); - if (status) - printk(KERN_WARNING "%s: lv1_gpu_close failed %d\n", - __func__, status); - } - mutex_unlock(&ps3av.mutex); - - return status; -} - -EXPORT_SYMBOL_GPL(ps3av_dev_close); - -static int ps3av_probe(struct ps3_vuart_port_device *dev) +static int ps3av_probe(struct ps3_system_bus_device *dev) { int res; u32 id; - dev_dbg(&ps3av_dev.core, "init ...\n"); - dev_dbg(&ps3av_dev.core, " timeout=%d\n", timeout); + dev_dbg(&dev->core, " -> %s:%d\n", __func__, __LINE__); + dev_dbg(&dev->core, " timeout=%d\n", timeout); - memset(&ps3av, 0, sizeof(ps3av)); + if (ps3av) { + dev_err(&dev->core, "Only one ps3av device is supported\n"); + return -EBUSY; + } - mutex_init(&ps3av.mutex); - ps3av.ps3av_mode = 0; - ps3av.dev = dev; - - INIT_WORK(&ps3av.work, ps3avd); - init_completion(&ps3av.done); - complete(&ps3av.done); - ps3av.wq = create_singlethread_workqueue("ps3avd"); - if (!ps3av.wq) + ps3av = kzalloc(sizeof(*ps3av), GFP_KERNEL); + if (!ps3av) return -ENOMEM; - ps3av.available = 1; + mutex_init(&ps3av->mutex); + ps3av->ps3av_mode = 0; + ps3av->dev = dev; + + INIT_WORK(&ps3av->work, ps3avd); + init_completion(&ps3av->done); + complete(&ps3av->done); + ps3av->wq = create_singlethread_workqueue("ps3avd"); + if (!ps3av->wq) + goto fail; + switch (ps3_os_area_get_av_multi_out()) { case PS3_PARAM_AV_MULTI_OUT_NTSC: - ps3av.region = PS3AV_REGION_60; + ps3av->region = PS3AV_REGION_60; break; case PS3_PARAM_AV_MULTI_OUT_PAL_YCBCR: case PS3_PARAM_AV_MULTI_OUT_SECAM: - ps3av.region = PS3AV_REGION_50; + ps3av->region = PS3AV_REGION_50; break; case PS3_PARAM_AV_MULTI_OUT_PAL_RGB: - ps3av.region = PS3AV_REGION_50 | PS3AV_REGION_RGB; + ps3av->region = PS3AV_REGION_50 | PS3AV_REGION_RGB; break; default: - ps3av.region = PS3AV_REGION_60; + ps3av->region = PS3AV_REGION_60; break; } @@ -927,39 +940,47 @@ static int ps3av_probe(struct ps3_vuart_port_device *dev) printk(KERN_ERR "%s: ps3av_cmd_init failed %d\n", __func__, res); - ps3av_get_hw_conf(&ps3av); - id = ps3av_auto_videomode(&ps3av.av_hw_conf, 1); - mutex_lock(&ps3av.mutex); - ps3av.ps3av_mode = id; - mutex_unlock(&ps3av.mutex); + ps3av_get_hw_conf(ps3av); + id = ps3av_auto_videomode(&ps3av->av_hw_conf, 1); + mutex_lock(&ps3av->mutex); + ps3av->ps3av_mode = id; + mutex_unlock(&ps3av->mutex); - dev_dbg(&ps3av_dev.core, "init...done\n"); + dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__); return 0; + +fail: + kfree(ps3av); + ps3av = NULL; + return -ENOMEM; } -static int ps3av_remove(struct ps3_vuart_port_device *dev) +static int ps3av_remove(struct ps3_system_bus_device *dev) { - if (ps3av.available) { + dev_dbg(&dev->core, " -> %s:%d\n", __func__, __LINE__); + if (ps3av) { ps3av_cmd_fin(); - if (ps3av.wq) - destroy_workqueue(ps3av.wq); - ps3av.available = 0; + if (ps3av->wq) + destroy_workqueue(ps3av->wq); + kfree(ps3av); + ps3av = NULL; } + dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__); return 0; } -static void ps3av_shutdown(struct ps3_vuart_port_device *dev) +static void ps3av_shutdown(struct ps3_system_bus_device *dev) { + dev_dbg(&dev->core, " -> %s:%d\n", __func__, __LINE__); ps3av_remove(dev); + dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__); } static struct ps3_vuart_port_driver ps3av_driver = { - .match_id = PS3_MATCH_ID_AV_SETTINGS, - .core = { - .name = "ps3_av", - }, + .core.match_id = PS3_MATCH_ID_AV_SETTINGS, + .core.core.name = "ps3_av", .probe = ps3av_probe, .remove = ps3av_remove, .shutdown = ps3av_shutdown, @@ -972,6 +993,8 @@ static int ps3av_module_init(void) if (!firmware_has_feature(FW_FEATURE_PS3_LV1)) return -ENODEV; + pr_debug(" -> %s:%d\n", __func__, __LINE__); + error = ps3_vuart_port_driver_register(&ps3av_driver); if (error) { printk(KERN_ERR @@ -980,20 +1003,21 @@ static int ps3av_module_init(void) return error; } - error = ps3_vuart_port_device_register(&ps3av_dev); - if (error) - printk(KERN_ERR - "%s: ps3_vuart_port_device_register failed %d\n", - __func__, error); - + pr_debug(" <- %s:%d\n", __func__, __LINE__); return error; } static void __exit ps3av_module_exit(void) { - device_unregister(&ps3av_dev.core); + pr_debug(" -> %s:%d\n", __func__, __LINE__); ps3_vuart_port_driver_unregister(&ps3av_driver); + pr_debug(" <- %s:%d\n", __func__, __LINE__); } subsys_initcall(ps3av_module_init); module_exit(ps3av_module_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("PS3 AV Settings Driver"); +MODULE_AUTHOR("Sony Computer Entertainment Inc."); +MODULE_ALIAS(PS3_MODULE_ALIAS_AV_SETTINGS); diff --git a/drivers/ps3/ps3av_cmd.c b/drivers/ps3/ps3av_cmd.c index 0145ea173c42..f72f5ddf18e4 100644 --- a/drivers/ps3/ps3av_cmd.c +++ b/drivers/ps3/ps3av_cmd.c @@ -143,6 +143,14 @@ static u32 ps3av_vid_video2av(int vid) return PS3AV_CMD_AV_VID_480P; } +static int ps3av_hdmi_range(void) +{ + if (ps3_compare_firmware_version(1, 8, 0) < 0) + return 0; + else + return 1; /* supported */ +} + int ps3av_cmd_init(void) { int res; @@ -350,6 +358,10 @@ u32 ps3av_cmd_set_av_video_cs(void *p, u32 avport, int video_vid, int cs_out, /* should be same as video_mode.video_cs_out */ av_video_cs->av_cs_in = ps3av_cs_video2av(PS3AV_CMD_VIDEO_CS_RGB_8); av_video_cs->bitlen_out = ps3av_cs_video2av_bitlen(cs_out); + if ((id & PS3AV_MODE_WHITE) && ps3av_hdmi_range()) + av_video_cs->super_white = PS3AV_CMD_AV_SUPER_WHITE_ON; + else /* default off */ + av_video_cs->super_white = PS3AV_CMD_AV_SUPER_WHITE_OFF; av_video_cs->aspect = aspect; if (id & PS3AV_MODE_DITHER) { av_video_cs->dither = PS3AV_CMD_AV_DITHER_ON @@ -392,6 +404,10 @@ u32 ps3av_cmd_set_video_mode(void *p, u32 head, int video_vid, int video_fmt, video_mode->pitch = video_mode->width * 4; /* line_length */ video_mode->video_out_format = PS3AV_CMD_VIDEO_OUT_FORMAT_RGB_12BIT; video_mode->video_format = ps3av_video_fmt_table[video_fmt].format; + if ((id & PS3AV_MODE_COLOR) && ps3av_hdmi_range()) + video_mode->video_cl_cnv = PS3AV_CMD_VIDEO_CL_CNV_DISABLE_LUT; + else /* default enable */ + video_mode->video_cl_cnv = PS3AV_CMD_VIDEO_CL_CNV_ENABLE_LUT; video_mode->video_order = ps3av_video_fmt_table[video_fmt].order; pr_debug("%s: video_mode:vid:%x width:%d height:%d pitch:%d out_format:%d format:%x order:%x\n", @@ -852,7 +868,7 @@ int ps3av_cmd_avb_param(struct ps3av_pkt_avb_param *avb, u32 send_len) { int res; - ps3fb_flip_ctl(0); /* flip off */ + ps3av_flip_ctl(0); /* flip off */ /* avb packet */ res = ps3av_do_pkt(PS3AV_CID_AVB_PARAM, send_len, sizeof(*avb), @@ -866,7 +882,7 @@ int ps3av_cmd_avb_param(struct ps3av_pkt_avb_param *avb, u32 send_len) res); out: - ps3fb_flip_ctl(1); /* flip on */ + ps3av_flip_ctl(1); /* flip on */ return res; } @@ -987,34 +1003,3 @@ void ps3av_cmd_av_monitor_info_dump(const struct ps3av_pkt_av_get_monitor_info * | PS3AV_CMD_AV_LAYOUT_176 \ | PS3AV_CMD_AV_LAYOUT_192) -/************************* vuart ***************************/ - -#define POLLING_INTERVAL 25 /* in msec */ - -int ps3av_vuart_write(struct ps3_vuart_port_device *dev, const void *buf, - unsigned long size) -{ - int error = ps3_vuart_write(dev, buf, size); - return error ? error : size; -} - -int ps3av_vuart_read(struct ps3_vuart_port_device *dev, void *buf, - unsigned long size, int timeout) -{ - int error; - int loopcnt = 0; - - timeout = (timeout + POLLING_INTERVAL - 1) / POLLING_INTERVAL; - while (loopcnt++ <= timeout) { - error = ps3_vuart_read(dev, buf, size); - if (!error) - return size; - if (error != -EAGAIN) { - printk(KERN_ERR "%s: ps3_vuart_read failed %d\n", - __func__, error); - return error; - } - msleep(POLLING_INTERVAL); - } - return -EWOULDBLOCK; -} diff --git a/drivers/ps3/ps3stor_lib.c b/drivers/ps3/ps3stor_lib.c new file mode 100644 index 000000000000..3a9824e3b251 --- /dev/null +++ b/drivers/ps3/ps3stor_lib.c @@ -0,0 +1,302 @@ +/* + * PS3 Storage Library + * + * Copyright (C) 2007 Sony Computer Entertainment Inc. + * Copyright 2007 Sony Corp. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published + * by the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include + +#include +#include + + +static int ps3stor_probe_access(struct ps3_storage_device *dev) +{ + int res, error; + unsigned int i; + unsigned long n; + + if (dev->sbd.match_id == PS3_MATCH_ID_STOR_ROM) { + /* special case: CD-ROM is assumed always accessible */ + dev->accessible_regions = 1; + return 0; + } + + error = -EPERM; + for (i = 0; i < dev->num_regions; i++) { + dev_dbg(&dev->sbd.core, + "%s:%u: checking accessibility of region %u\n", + __func__, __LINE__, i); + + dev->region_idx = i; + res = ps3stor_read_write_sectors(dev, dev->bounce_lpar, 0, 1, + 0); + if (res) { + dev_dbg(&dev->sbd.core, "%s:%u: read failed, " + "region %u is not accessible\n", __func__, + __LINE__, i); + continue; + } + + dev_dbg(&dev->sbd.core, "%s:%u: region %u is accessible\n", + __func__, __LINE__, i); + set_bit(i, &dev->accessible_regions); + + /* We can access at least one region */ + error = 0; + } + if (error) + return error; + + n = hweight_long(dev->accessible_regions); + if (n > 1) + dev_info(&dev->sbd.core, + "%s:%u: %lu accessible regions found. Only the first " + "one will be used", + __func__, __LINE__, n); + dev->region_idx = __ffs(dev->accessible_regions); + dev_info(&dev->sbd.core, + "First accessible region has index %u start %lu size %lu\n", + dev->region_idx, dev->regions[dev->region_idx].start, + dev->regions[dev->region_idx].size); + + return 0; +} + + +/** + * ps3stor_setup - Setup a storage device before use + * @dev: Pointer to a struct ps3_storage_device + * @handler: Pointer to an interrupt handler + * + * Returns 0 for success, or an error code + */ +int ps3stor_setup(struct ps3_storage_device *dev, irq_handler_t handler) +{ + int error, res, alignment; + enum ps3_dma_page_size page_size; + + error = ps3_open_hv_device(&dev->sbd); + if (error) { + dev_err(&dev->sbd.core, + "%s:%u: ps3_open_hv_device failed %d\n", __func__, + __LINE__, error); + goto fail; + } + + error = ps3_sb_event_receive_port_setup(&dev->sbd, PS3_BINDING_CPU_ANY, + &dev->irq); + if (error) { + dev_err(&dev->sbd.core, + "%s:%u: ps3_sb_event_receive_port_setup failed %d\n", + __func__, __LINE__, error); + goto fail_close_device; + } + + error = request_irq(dev->irq, handler, IRQF_DISABLED, + dev->sbd.core.driver->name, dev); + if (error) { + dev_err(&dev->sbd.core, "%s:%u: request_irq failed %d\n", + __func__, __LINE__, error); + goto fail_sb_event_receive_port_destroy; + } + + alignment = min(__ffs(dev->bounce_size), + __ffs((unsigned long)dev->bounce_buf)); + if (alignment < 12) { + dev_err(&dev->sbd.core, + "%s:%u: bounce buffer not aligned (%lx at 0x%p)\n", + __func__, __LINE__, dev->bounce_size, dev->bounce_buf); + error = -EINVAL; + goto fail_free_irq; + } else if (alignment < 16) + page_size = PS3_DMA_4K; + else + page_size = PS3_DMA_64K; + dev->sbd.d_region = &dev->dma_region; + ps3_dma_region_init(&dev->sbd, &dev->dma_region, page_size, + PS3_DMA_OTHER, dev->bounce_buf, dev->bounce_size); + res = ps3_dma_region_create(&dev->dma_region); + if (res) { + dev_err(&dev->sbd.core, "%s:%u: cannot create DMA region\n", + __func__, __LINE__); + error = -ENOMEM; + goto fail_free_irq; + } + + dev->bounce_lpar = ps3_mm_phys_to_lpar(__pa(dev->bounce_buf)); + dev->bounce_dma = dma_map_single(&dev->sbd.core, dev->bounce_buf, + dev->bounce_size, DMA_BIDIRECTIONAL); + if (!dev->bounce_dma) { + dev_err(&dev->sbd.core, "%s:%u: map DMA region failed\n", + __func__, __LINE__); + error = -ENODEV; + goto fail_free_dma; + } + + error = ps3stor_probe_access(dev); + if (error) { + dev_err(&dev->sbd.core, "%s:%u: No accessible regions found\n", + __func__, __LINE__); + goto fail_unmap_dma; + } + return 0; + +fail_unmap_dma: + dma_unmap_single(&dev->sbd.core, dev->bounce_dma, dev->bounce_size, + DMA_BIDIRECTIONAL); +fail_free_dma: + ps3_dma_region_free(&dev->dma_region); +fail_free_irq: + free_irq(dev->irq, dev); +fail_sb_event_receive_port_destroy: + ps3_sb_event_receive_port_destroy(&dev->sbd, dev->irq); +fail_close_device: + ps3_close_hv_device(&dev->sbd); +fail: + return error; +} +EXPORT_SYMBOL_GPL(ps3stor_setup); + + +/** + * ps3stor_teardown - Tear down a storage device after use + * @dev: Pointer to a struct ps3_storage_device + */ +void ps3stor_teardown(struct ps3_storage_device *dev) +{ + int error; + + dma_unmap_single(&dev->sbd.core, dev->bounce_dma, dev->bounce_size, + DMA_BIDIRECTIONAL); + ps3_dma_region_free(&dev->dma_region); + + free_irq(dev->irq, dev); + + error = ps3_sb_event_receive_port_destroy(&dev->sbd, dev->irq); + if (error) + dev_err(&dev->sbd.core, + "%s:%u: destroy event receive port failed %d\n", + __func__, __LINE__, error); + + error = ps3_close_hv_device(&dev->sbd); + if (error) + dev_err(&dev->sbd.core, + "%s:%u: ps3_close_hv_device failed %d\n", __func__, + __LINE__, error); +} +EXPORT_SYMBOL_GPL(ps3stor_teardown); + + +/** + * ps3stor_read_write_sectors - read/write from/to a storage device + * @dev: Pointer to a struct ps3_storage_device + * @lpar: HV logical partition address + * @start_sector: First sector to read/write + * @sectors: Number of sectors to read/write + * @write: Flag indicating write (non-zero) or read (zero) + * + * Returns 0 for success, -1 in case of failure to submit the command, or + * an LV1 status value in case of other errors + */ +u64 ps3stor_read_write_sectors(struct ps3_storage_device *dev, u64 lpar, + u64 start_sector, u64 sectors, int write) +{ + unsigned int region_id = dev->regions[dev->region_idx].id; + const char *op = write ? "write" : "read"; + int res; + + dev_dbg(&dev->sbd.core, "%s:%u: %s %lu sectors starting at %lu\n", + __func__, __LINE__, op, sectors, start_sector); + + init_completion(&dev->done); + res = write ? lv1_storage_write(dev->sbd.dev_id, region_id, + start_sector, sectors, 0, lpar, + &dev->tag) + : lv1_storage_read(dev->sbd.dev_id, region_id, + start_sector, sectors, 0, lpar, + &dev->tag); + if (res) { + dev_dbg(&dev->sbd.core, "%s:%u: %s failed %d\n", __func__, + __LINE__, op, res); + return -1; + } + + wait_for_completion(&dev->done); + if (dev->lv1_status) { + dev_dbg(&dev->sbd.core, "%s:%u: %s failed 0x%lx\n", __func__, + __LINE__, op, dev->lv1_status); + return dev->lv1_status; + } + + dev_dbg(&dev->sbd.core, "%s:%u: %s completed\n", __func__, __LINE__, + op); + + return 0; +} +EXPORT_SYMBOL_GPL(ps3stor_read_write_sectors); + + +/** + * ps3stor_send_command - send a device command to a storage device + * @dev: Pointer to a struct ps3_storage_device + * @cmd: Command number + * @arg1: First command argument + * @arg2: Second command argument + * @arg3: Third command argument + * @arg4: Fourth command argument + * + * Returns 0 for success, -1 in case of failure to submit the command, or + * an LV1 status value in case of other errors + */ +u64 ps3stor_send_command(struct ps3_storage_device *dev, u64 cmd, u64 arg1, + u64 arg2, u64 arg3, u64 arg4) +{ + int res; + + dev_dbg(&dev->sbd.core, "%s:%u: send device command 0x%lx\n", __func__, + __LINE__, cmd); + + init_completion(&dev->done); + + res = lv1_storage_send_device_command(dev->sbd.dev_id, cmd, arg1, + arg2, arg3, arg4, &dev->tag); + if (res) { + dev_err(&dev->sbd.core, + "%s:%u: send_device_command 0x%lx failed %d\n", + __func__, __LINE__, cmd, res); + return -1; + } + + wait_for_completion(&dev->done); + if (dev->lv1_status) { + dev_dbg(&dev->sbd.core, "%s:%u: command 0x%lx failed 0x%lx\n", + __func__, __LINE__, cmd, dev->lv1_status); + return dev->lv1_status; + } + + dev_dbg(&dev->sbd.core, "%s:%u: command 0x%lx completed\n", __func__, + __LINE__, cmd); + + return 0; +} +EXPORT_SYMBOL_GPL(ps3stor_send_command); + + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("PS3 Storage Bus Library"); +MODULE_AUTHOR("Sony Corporation"); diff --git a/drivers/ps3/sys-manager-core.c b/drivers/ps3/sys-manager-core.c new file mode 100644 index 000000000000..31648f7d9ae1 --- /dev/null +++ b/drivers/ps3/sys-manager-core.c @@ -0,0 +1,68 @@ +/* + * PS3 System Manager core. + * + * Copyright (C) 2007 Sony Computer Entertainment Inc. + * Copyright 2007 Sony Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include + +/** + * Staticly linked routines that allow late binding of a loaded sys-manager + * module. + */ + +static struct ps3_sys_manager_ops ps3_sys_manager_ops; + +/** + * ps3_register_sys_manager_ops - Bind ps3_sys_manager_ops to a module. + * @ops: struct ps3_sys_manager_ops. + * + * To be called from ps3_sys_manager_probe() and ps3_sys_manager_remove() to + * register call back ops for power control. Copies data to the static + * variable ps3_sys_manager_ops. + */ + +void ps3_sys_manager_register_ops(const struct ps3_sys_manager_ops *ops) +{ + BUG_ON(!ops); + BUG_ON(!ops->dev); + ps3_sys_manager_ops = ops ? *ops : ps3_sys_manager_ops; +} +EXPORT_SYMBOL_GPL(ps3_sys_manager_register_ops); + +void ps3_sys_manager_power_off(void) +{ + if (ps3_sys_manager_ops.power_off) + ps3_sys_manager_ops.power_off(ps3_sys_manager_ops.dev); + + printk(KERN_EMERG "System Halted, OK to turn off power\n"); + local_irq_disable(); + while (1) + (void)0; +} + +void ps3_sys_manager_restart(void) +{ + if (ps3_sys_manager_ops.restart) + ps3_sys_manager_ops.restart(ps3_sys_manager_ops.dev); + + printk(KERN_EMERG "System Halted, OK to turn off power\n"); + local_irq_disable(); + while (1) + (void)0; +} diff --git a/drivers/ps3/sys-manager.c b/drivers/ps3/sys-manager.c index 3aa2b0dcc369..8461b08ab9fb 100644 --- a/drivers/ps3/sys-manager.c +++ b/drivers/ps3/sys-manager.c @@ -35,7 +35,7 @@ MODULE_DESCRIPTION("PS3 System Manager"); /** * ps3_sys_manager - PS3 system manager driver. * - * The system manager provides an asyncronous system event notification + * The system manager provides an asynchronous system event notification * mechanism for reporting events like thermal alert and button presses to * guests. It also provides support to control system shutdown and startup. * @@ -52,6 +52,7 @@ MODULE_DESCRIPTION("PS3 System Manager"); * @size: Header size in bytes, curently 16. * @payload_size: Message payload size in bytes. * @service_id: Message type, one of enum ps3_sys_manager_service_id. + * @request_tag: Unique number to identify reply. */ struct ps3_sys_manager_header { @@ -61,29 +62,49 @@ struct ps3_sys_manager_header { u16 reserved_1; u32 payload_size; u16 service_id; - u16 reserved_2[3]; + u16 reserved_2; + u32 request_tag; }; +#define dump_sm_header(_h) _dump_sm_header(_h, __func__, __LINE__) +static void __maybe_unused _dump_sm_header( + const struct ps3_sys_manager_header *h, const char *func, int line) +{ + pr_debug("%s:%d: version: %xh\n", func, line, h->version); + pr_debug("%s:%d: size: %xh\n", func, line, h->size); + pr_debug("%s:%d: payload_size: %xh\n", func, line, h->payload_size); + pr_debug("%s:%d: service_id: %xh\n", func, line, h->service_id); + pr_debug("%s:%d: request_tag: %xh\n", func, line, h->request_tag); +} + /** - * @PS3_SM_RX_MSG_LEN - System manager received message length. + * @PS3_SM_RX_MSG_LEN_MIN - Shortest received message length. + * @PS3_SM_RX_MSG_LEN_MAX - Longest received message length. * - * Currently all messages received from the system manager are the same length - * (16 bytes header + 16 bytes payload = 32 bytes). This knowlege is used to - * simplify the logic. + * Currently all messages received from the system manager are either + * (16 bytes header + 8 bytes payload = 24 bytes) or (16 bytes header + * + 16 bytes payload = 32 bytes). This knowlege is used to simplify + * the logic. */ enum { - PS3_SM_RX_MSG_LEN = 32, + PS3_SM_RX_MSG_LEN_MIN = 24, + PS3_SM_RX_MSG_LEN_MAX = 32, }; /** * enum ps3_sys_manager_service_id - Message header service_id. - * @PS3_SM_SERVICE_ID_REQUEST: guest --> sys_manager. - * @PS3_SM_SERVICE_ID_COMMAND: guest <-- sys_manager. - * @PS3_SM_SERVICE_ID_RESPONSE: guest --> sys_manager. - * @PS3_SM_SERVICE_ID_SET_ATTR: guest --> sys_manager. - * @PS3_SM_SERVICE_ID_EXTERN_EVENT: guest <-- sys_manager. - * @PS3_SM_SERVICE_ID_SET_NEXT_OP: guest --> sys_manager. + * @PS3_SM_SERVICE_ID_REQUEST: guest --> sys_manager. + * @PS3_SM_SERVICE_ID_REQUEST_ERROR: guest <-- sys_manager. + * @PS3_SM_SERVICE_ID_COMMAND: guest <-- sys_manager. + * @PS3_SM_SERVICE_ID_RESPONSE: guest --> sys_manager. + * @PS3_SM_SERVICE_ID_SET_ATTR: guest --> sys_manager. + * @PS3_SM_SERVICE_ID_EXTERN_EVENT: guest <-- sys_manager. + * @PS3_SM_SERVICE_ID_SET_NEXT_OP: guest --> sys_manager. + * + * PS3_SM_SERVICE_ID_REQUEST_ERROR is returned for invalid data values in a + * a PS3_SM_SERVICE_ID_REQUEST message. It also seems to be returned when + * a REQUEST message is sent at the wrong time. */ enum ps3_sys_manager_service_id { @@ -93,6 +114,7 @@ enum ps3_sys_manager_service_id { PS3_SM_SERVICE_ID_COMMAND = 3, PS3_SM_SERVICE_ID_EXTERN_EVENT = 4, PS3_SM_SERVICE_ID_SET_NEXT_OP = 5, + PS3_SM_SERVICE_ID_REQUEST_ERROR = 6, PS3_SM_SERVICE_ID_SET_ATTR = 8, }; @@ -184,12 +206,22 @@ enum ps3_sys_manager_cmd { PS3_SM_CMD_SHUTDOWN = 1, /* shutdown guest OS */ }; +/** + * ps3_sm_force_power_off - Poweroff helper. + * + * A global variable used to force a poweroff when the power button has + * been pressed irrespective of how init handles the ctrl_alt_del signal. + * + */ + +static unsigned int ps3_sm_force_power_off; + /** * ps3_sys_manager_write - Helper to write a two part message to the vuart. * */ -static int ps3_sys_manager_write(struct ps3_vuart_port_device *dev, +static int ps3_sys_manager_write(struct ps3_system_bus_device *dev, const struct ps3_sys_manager_header *header, const void *payload) { int result; @@ -213,15 +245,10 @@ static int ps3_sys_manager_write(struct ps3_vuart_port_device *dev, * */ -static int ps3_sys_manager_send_attr(struct ps3_vuart_port_device *dev, +static int ps3_sys_manager_send_attr(struct ps3_system_bus_device *dev, enum ps3_sys_manager_attr attr) { - static const struct ps3_sys_manager_header header = { - .version = 1, - .size = 16, - .payload_size = 16, - .service_id = PS3_SM_SERVICE_ID_SET_ATTR, - }; + struct ps3_sys_manager_header header; struct { u8 version; u8 reserved_1[3]; @@ -232,6 +259,12 @@ static int ps3_sys_manager_send_attr(struct ps3_vuart_port_device *dev, dev_dbg(&dev->core, "%s:%d: %xh\n", __func__, __LINE__, attr); + memset(&header, 0, sizeof(header)); + header.version = 1; + header.size = 16; + header.payload_size = 16; + header.service_id = PS3_SM_SERVICE_ID_SET_ATTR; + memset(&payload, 0, sizeof(payload)); payload.version = 1; payload.attribute = attr; @@ -245,16 +278,11 @@ static int ps3_sys_manager_send_attr(struct ps3_vuart_port_device *dev, * Tell the system manager what to do after this lpar is destroyed. */ -static int ps3_sys_manager_send_next_op(struct ps3_vuart_port_device *dev, +static int ps3_sys_manager_send_next_op(struct ps3_system_bus_device *dev, enum ps3_sys_manager_next_op op, enum ps3_sys_manager_wake_source wake_source) { - static const struct ps3_sys_manager_header header = { - .version = 1, - .size = 16, - .payload_size = 16, - .service_id = PS3_SM_SERVICE_ID_SET_NEXT_OP, - }; + struct ps3_sys_manager_header header; struct { u8 version; u8 type; @@ -268,6 +296,12 @@ static int ps3_sys_manager_send_next_op(struct ps3_vuart_port_device *dev, dev_dbg(&dev->core, "%s:%d: (%xh)\n", __func__, __LINE__, op); + memset(&header, 0, sizeof(header)); + header.version = 1; + header.size = 16; + header.payload_size = 16; + header.service_id = PS3_SM_SERVICE_ID_SET_NEXT_OP; + memset(&payload, 0, sizeof(payload)); payload.version = 3; payload.type = op; @@ -286,32 +320,35 @@ static int ps3_sys_manager_send_next_op(struct ps3_vuart_port_device *dev, * the command is then communicated back to the system manager with a response * message. * - * Currently, the only supported request it the 'shutdown self' request. + * Currently, the only supported request is the 'shutdown self' request. */ -static int ps3_sys_manager_send_request_shutdown(struct ps3_vuart_port_device *dev) +static int ps3_sys_manager_send_request_shutdown( + struct ps3_system_bus_device *dev) { - static const struct ps3_sys_manager_header header = { - .version = 1, - .size = 16, - .payload_size = 16, - .service_id = PS3_SM_SERVICE_ID_REQUEST, - }; + struct ps3_sys_manager_header header; struct { u8 version; u8 type; u8 gos_id; u8 reserved_1[13]; - } static const payload = { - .version = 1, - .type = 1, /* shutdown */ - .gos_id = 0, /* self */ - }; + } payload; BUILD_BUG_ON(sizeof(payload) != 16); dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); + memset(&header, 0, sizeof(header)); + header.version = 1; + header.size = 16; + header.payload_size = 16; + header.service_id = PS3_SM_SERVICE_ID_REQUEST; + + memset(&payload, 0, sizeof(payload)); + payload.version = 1; + payload.type = 1; /* shutdown */ + payload.gos_id = 0; /* self */ + return ps3_sys_manager_write(dev, &header, &payload); } @@ -323,15 +360,10 @@ static int ps3_sys_manager_send_request_shutdown(struct ps3_vuart_port_device *d * failure of a command sent by the system manager. */ -static int ps3_sys_manager_send_response(struct ps3_vuart_port_device *dev, +static int ps3_sys_manager_send_response(struct ps3_system_bus_device *dev, u64 status) { - static const struct ps3_sys_manager_header header = { - .version = 1, - .size = 16, - .payload_size = 16, - .service_id = PS3_SM_SERVICE_ID_RESPONSE, - }; + struct ps3_sys_manager_header header; struct { u8 version; u8 reserved_1[3]; @@ -344,6 +376,12 @@ static int ps3_sys_manager_send_response(struct ps3_vuart_port_device *dev, dev_dbg(&dev->core, "%s:%d: (%s)\n", __func__, __LINE__, (status ? "nak" : "ack")); + memset(&header, 0, sizeof(header)); + header.version = 1; + header.size = 16; + header.payload_size = 16; + header.service_id = PS3_SM_SERVICE_ID_RESPONSE; + memset(&payload, 0, sizeof(payload)); payload.version = 1; payload.status = status; @@ -356,7 +394,7 @@ static int ps3_sys_manager_send_response(struct ps3_vuart_port_device *dev, * */ -static int ps3_sys_manager_handle_event(struct ps3_vuart_port_device *dev) +static int ps3_sys_manager_handle_event(struct ps3_system_bus_device *dev) { int result; struct { @@ -370,7 +408,7 @@ static int ps3_sys_manager_handle_event(struct ps3_vuart_port_device *dev) BUILD_BUG_ON(sizeof(event) != 16); result = ps3_vuart_read(dev, &event, sizeof(event)); - BUG_ON(result); + BUG_ON(result && "need to retry here"); if (event.version != 1) { dev_dbg(&dev->core, "%s:%d: unsupported event version (%u)\n", @@ -382,11 +420,34 @@ static int ps3_sys_manager_handle_event(struct ps3_vuart_port_device *dev) case PS3_SM_EVENT_POWER_PRESSED: dev_dbg(&dev->core, "%s:%d: POWER_PRESSED\n", __func__, __LINE__); + ps3_sm_force_power_off = 1; + /* + * A memory barrier is use here to sync memory since + * ps3_sys_manager_final_restart() could be called on + * another cpu. + */ + wmb(); + kill_cad_pid(SIGINT, 1); /* ctrl_alt_del */ break; case PS3_SM_EVENT_POWER_RELEASED: dev_dbg(&dev->core, "%s:%d: POWER_RELEASED (%u ms)\n", __func__, __LINE__, event.value); - kill_cad_pid(SIGINT, 1); + break; + case PS3_SM_EVENT_RESET_PRESSED: + dev_dbg(&dev->core, "%s:%d: RESET_PRESSED\n", + __func__, __LINE__); + ps3_sm_force_power_off = 0; + /* + * A memory barrier is use here to sync memory since + * ps3_sys_manager_final_restart() could be called on + * another cpu. + */ + wmb(); + kill_cad_pid(SIGINT, 1); /* ctrl_alt_del */ + break; + case PS3_SM_EVENT_RESET_RELEASED: + dev_dbg(&dev->core, "%s:%d: RESET_RELEASED (%u ms)\n", + __func__, __LINE__, event.value); break; case PS3_SM_EVENT_THERMAL_ALERT: dev_dbg(&dev->core, "%s:%d: THERMAL_ALERT (zone %u)\n", @@ -411,7 +472,7 @@ static int ps3_sys_manager_handle_event(struct ps3_vuart_port_device *dev) * The system manager sends this in reply to a 'request' message from the guest. */ -static int ps3_sys_manager_handle_cmd(struct ps3_vuart_port_device *dev) +static int ps3_sys_manager_handle_cmd(struct ps3_system_bus_device *dev) { int result; struct { @@ -425,6 +486,7 @@ static int ps3_sys_manager_handle_cmd(struct ps3_vuart_port_device *dev) dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); result = ps3_vuart_read(dev, &cmd, sizeof(cmd)); + BUG_ON(result && "need to retry here"); if(result) return result; @@ -448,9 +510,10 @@ static int ps3_sys_manager_handle_cmd(struct ps3_vuart_port_device *dev) /** * ps3_sys_manager_handle_msg - First stage msg handler. * + * Can be called directly to manually poll vuart and pump message handler. */ -static int ps3_sys_manager_handle_msg(struct ps3_vuart_port_device *dev) +static int ps3_sys_manager_handle_msg(struct ps3_system_bus_device *dev) { int result; struct ps3_sys_manager_header header; @@ -464,12 +527,17 @@ static int ps3_sys_manager_handle_msg(struct ps3_vuart_port_device *dev) if (header.version != 1) { dev_dbg(&dev->core, "%s:%d: unsupported header version (%u)\n", __func__, __LINE__, header.version); + dump_sm_header(&header); goto fail_header; } BUILD_BUG_ON(sizeof(header) != 16); - BUG_ON(header.size != 16); - BUG_ON(header.payload_size != 16); + + if (header.size != 16 || (header.payload_size != 8 + && header.payload_size != 16)) { + dump_sm_header(&header); + BUG(); + } switch (header.service_id) { case PS3_SM_SERVICE_ID_EXTERN_EVENT: @@ -478,6 +546,11 @@ static int ps3_sys_manager_handle_msg(struct ps3_vuart_port_device *dev) case PS3_SM_SERVICE_ID_COMMAND: dev_dbg(&dev->core, "%s:%d: COMMAND\n", __func__, __LINE__); return ps3_sys_manager_handle_cmd(dev); + case PS3_SM_SERVICE_ID_REQUEST_ERROR: + dev_dbg(&dev->core, "%s:%d: REQUEST_ERROR\n", __func__, + __LINE__); + dump_sm_header(&header); + break; default: dev_dbg(&dev->core, "%s:%d: unknown service_id (%u)\n", __func__, __LINE__, header.service_id); @@ -494,69 +567,19 @@ fail_id: } /** - * ps3_sys_manager_work - Asyncronous read handler. + * ps3_sys_manager_final_power_off - The final platform machine_power_off routine. * - * Signaled when a complete message arrives at the vuart port. - */ - -static void ps3_sys_manager_work(struct work_struct *work) -{ - struct ps3_vuart_port_device *dev = ps3_vuart_work_to_port_device(work); - - ps3_sys_manager_handle_msg(dev); - ps3_vuart_read_async(dev, ps3_sys_manager_work, PS3_SM_RX_MSG_LEN); -} - -struct { - struct ps3_vuart_port_device *dev; -} static drv_priv; - -/** - * ps3_sys_manager_restart - The final platform machine_restart routine. - * - * This routine never returns. The routine disables asyncronous vuart reads + * This routine never returns. The routine disables asynchronous vuart reads * then spins calling ps3_sys_manager_handle_msg() to receive and acknowledge * the shutdown command sent from the system manager. Soon after the * acknowledgement is sent the lpar is destroyed by the HV. This routine - * should only be called from ps3_restart(). + * should only be called from ps3_power_off() through + * ps3_sys_manager_ops.power_off. */ -void ps3_sys_manager_restart(void) +static void ps3_sys_manager_final_power_off(struct ps3_system_bus_device *dev) { - struct ps3_vuart_port_device *dev = drv_priv.dev; - - BUG_ON(!drv_priv.dev); - - dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); - - ps3_vuart_cancel_async(dev); - - ps3_sys_manager_send_attr(dev, 0); - ps3_sys_manager_send_next_op(dev, PS3_SM_NEXT_OP_LPAR_REBOOT, - PS3_SM_WAKE_DEFAULT); - ps3_sys_manager_send_request_shutdown(dev); - - printk(KERN_EMERG "System Halted, OK to turn off power\n"); - - while(1) - ps3_sys_manager_handle_msg(dev); -} - -/** - * ps3_sys_manager_power_off - The final platform machine_power_off routine. - * - * This routine never returns. The routine disables asyncronous vuart reads - * then spins calling ps3_sys_manager_handle_msg() to receive and acknowledge - * the shutdown command sent from the system manager. Soon after the - * acknowledgement is sent the lpar is destroyed by the HV. This routine - * should only be called from ps3_power_off(). - */ - -void ps3_sys_manager_power_off(void) -{ - struct ps3_vuart_port_device *dev = drv_priv.dev; - - BUG_ON(!drv_priv.dev); + BUG_ON(!dev); dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); @@ -572,31 +595,97 @@ void ps3_sys_manager_power_off(void) ps3_sys_manager_handle_msg(dev); } -static int ps3_sys_manager_probe(struct ps3_vuart_port_device *dev) +/** + * ps3_sys_manager_final_restart - The final platform machine_restart routine. + * + * This routine never returns. The routine disables asynchronous vuart reads + * then spins calling ps3_sys_manager_handle_msg() to receive and acknowledge + * the shutdown command sent from the system manager. Soon after the + * acknowledgement is sent the lpar is destroyed by the HV. This routine + * should only be called from ps3_restart() through ps3_sys_manager_ops.restart. + */ + +static void ps3_sys_manager_final_restart(struct ps3_system_bus_device *dev) { - int result; + BUG_ON(!dev); dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); - BUG_ON(drv_priv.dev); - drv_priv.dev = dev; + /* Check if we got here via a power button event. */ + + if (ps3_sm_force_power_off) { + dev_dbg(&dev->core, "%s:%d: forcing poweroff\n", + __func__, __LINE__); + ps3_sys_manager_final_power_off(dev); + } + + ps3_vuart_cancel_async(dev); + + ps3_sys_manager_send_attr(dev, 0); + ps3_sys_manager_send_next_op(dev, PS3_SM_NEXT_OP_LPAR_REBOOT, + PS3_SM_WAKE_DEFAULT); + ps3_sys_manager_send_request_shutdown(dev); + + printk(KERN_EMERG "System Halted, OK to turn off power\n"); + + while(1) + ps3_sys_manager_handle_msg(dev); +} + +/** + * ps3_sys_manager_work - Asynchronous read handler. + * + * Signaled when PS3_SM_RX_MSG_LEN_MIN bytes arrive at the vuart port. + */ + +static void ps3_sys_manager_work(struct ps3_system_bus_device *dev) +{ + ps3_sys_manager_handle_msg(dev); + ps3_vuart_read_async(dev, PS3_SM_RX_MSG_LEN_MIN); +} + +static int ps3_sys_manager_probe(struct ps3_system_bus_device *dev) +{ + int result; + struct ps3_sys_manager_ops ops; + + dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); + + ops.power_off = ps3_sys_manager_final_power_off; + ops.restart = ps3_sys_manager_final_restart; + ops.dev = dev; + + /* ps3_sys_manager_register_ops copies ops. */ + + ps3_sys_manager_register_ops(&ops); result = ps3_sys_manager_send_attr(dev, PS3_SM_ATTR_ALL); BUG_ON(result); - result = ps3_vuart_read_async(dev, ps3_sys_manager_work, - PS3_SM_RX_MSG_LEN); + result = ps3_vuart_read_async(dev, PS3_SM_RX_MSG_LEN_MIN); BUG_ON(result); return result; } +static int ps3_sys_manager_remove(struct ps3_system_bus_device *dev) +{ + dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); + return 0; +} + +static void ps3_sys_manager_shutdown(struct ps3_system_bus_device *dev) +{ + dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); +} + static struct ps3_vuart_port_driver ps3_sys_manager = { - .match_id = PS3_MATCH_ID_SYSTEM_MANAGER, - .core = { - .name = "ps3_sys_manager", - }, + .core.match_id = PS3_MATCH_ID_SYSTEM_MANAGER, + .core.core.name = "ps3_sys_manager", .probe = ps3_sys_manager_probe, + .remove = ps3_sys_manager_remove, + .shutdown = ps3_sys_manager_shutdown, + .work = ps3_sys_manager_work, }; static int __init ps3_sys_manager_init(void) @@ -608,3 +697,6 @@ static int __init ps3_sys_manager_init(void) } module_init(ps3_sys_manager_init); +/* Module remove not supported. */ + +MODULE_ALIAS(PS3_MODULE_ALIAS_SYSTEM_MANAGER); diff --git a/drivers/ps3/vuart.c b/drivers/ps3/vuart.c index ec2d36a1bc67..bea25a1391ee 100644 --- a/drivers/ps3/vuart.c +++ b/drivers/ps3/vuart.c @@ -70,6 +70,34 @@ enum vuart_interrupt_mask { INTERRUPT_MASK_DISCONNECT = 4, }; +/** + * struct ps3_vuart_port_priv - private vuart device data. + */ + +struct ps3_vuart_port_priv { + u64 interrupt_mask; + + struct { + spinlock_t lock; + struct list_head head; + } tx_list; + struct { + struct ps3_vuart_work work; + unsigned long bytes_held; + spinlock_t lock; + struct list_head head; + } rx_list; + struct ps3_vuart_stats stats; +}; + +static struct ps3_vuart_port_priv *to_port_priv( + struct ps3_system_bus_device *dev) +{ + BUG_ON(!dev); + BUG_ON(!dev->driver_priv); + return (struct ps3_vuart_port_priv *)dev->driver_priv; +} + /** * struct ports_bmp - bitmap indicating ports needing service. * @@ -83,31 +111,14 @@ struct ports_bmp { } __attribute__ ((aligned (32))); #define dump_ports_bmp(_b) _dump_ports_bmp(_b, __func__, __LINE__) -static void __attribute__ ((unused)) _dump_ports_bmp( +static void __maybe_unused _dump_ports_bmp( const struct ports_bmp* bmp, const char* func, int line) { pr_debug("%s:%d: ports_bmp: %016lxh\n", func, line, bmp->status); } -static int ps3_vuart_match_id_to_port(enum ps3_match_id match_id, - unsigned int *port_number) -{ - switch(match_id) { - case PS3_MATCH_ID_AV_SETTINGS: - *port_number = 0; - return 0; - case PS3_MATCH_ID_SYSTEM_MANAGER: - *port_number = 2; - return 0; - default: - WARN_ON(1); - *port_number = UINT_MAX; - return -EINVAL; - }; -} - #define dump_port_params(_b) _dump_port_params(_b, __func__, __LINE__) -static void __attribute__ ((unused)) _dump_port_params(unsigned int port_number, +static void __maybe_unused _dump_port_params(unsigned int port_number, const char* func, int line) { #if defined(DEBUG) @@ -144,14 +155,14 @@ struct vuart_triggers { unsigned long tx; }; -int ps3_vuart_get_triggers(struct ps3_vuart_port_device *dev, +int ps3_vuart_get_triggers(struct ps3_system_bus_device *dev, struct vuart_triggers *trig) { int result; unsigned long size; unsigned long val; - result = lv1_get_virtual_uart_param(dev->priv->port_number, + result = lv1_get_virtual_uart_param(dev->port_number, PARAM_TX_TRIGGER, &trig->tx); if (result) { @@ -160,7 +171,7 @@ int ps3_vuart_get_triggers(struct ps3_vuart_port_device *dev, return result; } - result = lv1_get_virtual_uart_param(dev->priv->port_number, + result = lv1_get_virtual_uart_param(dev->port_number, PARAM_RX_BUF_SIZE, &size); if (result) { @@ -169,7 +180,7 @@ int ps3_vuart_get_triggers(struct ps3_vuart_port_device *dev, return result; } - result = lv1_get_virtual_uart_param(dev->priv->port_number, + result = lv1_get_virtual_uart_param(dev->port_number, PARAM_RX_TRIGGER, &val); if (result) { @@ -186,13 +197,13 @@ int ps3_vuart_get_triggers(struct ps3_vuart_port_device *dev, return result; } -int ps3_vuart_set_triggers(struct ps3_vuart_port_device *dev, unsigned int tx, +int ps3_vuart_set_triggers(struct ps3_system_bus_device *dev, unsigned int tx, unsigned int rx) { int result; unsigned long size; - result = lv1_set_virtual_uart_param(dev->priv->port_number, + result = lv1_set_virtual_uart_param(dev->port_number, PARAM_TX_TRIGGER, tx); if (result) { @@ -201,7 +212,7 @@ int ps3_vuart_set_triggers(struct ps3_vuart_port_device *dev, unsigned int tx, return result; } - result = lv1_get_virtual_uart_param(dev->priv->port_number, + result = lv1_get_virtual_uart_param(dev->port_number, PARAM_RX_BUF_SIZE, &size); if (result) { @@ -210,7 +221,7 @@ int ps3_vuart_set_triggers(struct ps3_vuart_port_device *dev, unsigned int tx, return result; } - result = lv1_set_virtual_uart_param(dev->priv->port_number, + result = lv1_set_virtual_uart_param(dev->port_number, PARAM_RX_TRIGGER, size - rx); if (result) { @@ -225,10 +236,12 @@ int ps3_vuart_set_triggers(struct ps3_vuart_port_device *dev, unsigned int tx, return result; } -static int ps3_vuart_get_rx_bytes_waiting(struct ps3_vuart_port_device *dev, +static int ps3_vuart_get_rx_bytes_waiting(struct ps3_system_bus_device *dev, u64 *bytes_waiting) { - int result = lv1_get_virtual_uart_param(dev->priv->port_number, + int result; + + result = lv1_get_virtual_uart_param(dev->port_number, PARAM_RX_BYTES, bytes_waiting); if (result) @@ -240,17 +253,24 @@ static int ps3_vuart_get_rx_bytes_waiting(struct ps3_vuart_port_device *dev, return result; } -static int ps3_vuart_set_interrupt_mask(struct ps3_vuart_port_device *dev, +/** + * ps3_vuart_set_interrupt_mask - Enable/disable the port interrupt sources. + * @dev: The struct ps3_system_bus_device instance. + * @bmp: Logical OR of enum vuart_interrupt_mask values. A zero bit disables. + */ + +static int ps3_vuart_set_interrupt_mask(struct ps3_system_bus_device *dev, unsigned long mask) { int result; + struct ps3_vuart_port_priv *priv = to_port_priv(dev); dev_dbg(&dev->core, "%s:%d: %lxh\n", __func__, __LINE__, mask); - dev->priv->interrupt_mask = mask; + priv->interrupt_mask = mask; - result = lv1_set_virtual_uart_param(dev->priv->port_number, - PARAM_INTERRUPT_MASK, dev->priv->interrupt_mask); + result = lv1_set_virtual_uart_param(dev->port_number, + PARAM_INTERRUPT_MASK, priv->interrupt_mask); if (result) dev_dbg(&dev->core, "%s:%d: interrupt_mask failed: %s\n", @@ -259,79 +279,96 @@ static int ps3_vuart_set_interrupt_mask(struct ps3_vuart_port_device *dev, return result; } -static int ps3_vuart_get_interrupt_status(struct ps3_vuart_port_device *dev, +static int ps3_vuart_get_interrupt_status(struct ps3_system_bus_device *dev, unsigned long *status) { + int result; + struct ps3_vuart_port_priv *priv = to_port_priv(dev); u64 tmp; - int result = lv1_get_virtual_uart_param(dev->priv->port_number, + + result = lv1_get_virtual_uart_param(dev->port_number, PARAM_INTERRUPT_STATUS, &tmp); if (result) dev_dbg(&dev->core, "%s:%d: interrupt_status failed: %s\n", __func__, __LINE__, ps3_result(result)); - *status = tmp & dev->priv->interrupt_mask; + *status = tmp & priv->interrupt_mask; dev_dbg(&dev->core, "%s:%d: m %lxh, s %lxh, m&s %lxh\n", - __func__, __LINE__, dev->priv->interrupt_mask, tmp, *status); + __func__, __LINE__, priv->interrupt_mask, tmp, *status); return result; } -int ps3_vuart_enable_interrupt_tx(struct ps3_vuart_port_device *dev) +int ps3_vuart_enable_interrupt_tx(struct ps3_system_bus_device *dev) { - return (dev->priv->interrupt_mask & INTERRUPT_MASK_TX) ? 0 - : ps3_vuart_set_interrupt_mask(dev, dev->priv->interrupt_mask + struct ps3_vuart_port_priv *priv = to_port_priv(dev); + + return (priv->interrupt_mask & INTERRUPT_MASK_TX) ? 0 + : ps3_vuart_set_interrupt_mask(dev, priv->interrupt_mask | INTERRUPT_MASK_TX); } -int ps3_vuart_enable_interrupt_rx(struct ps3_vuart_port_device *dev) +int ps3_vuart_enable_interrupt_rx(struct ps3_system_bus_device *dev) { - return (dev->priv->interrupt_mask & INTERRUPT_MASK_RX) ? 0 - : ps3_vuart_set_interrupt_mask(dev, dev->priv->interrupt_mask + struct ps3_vuart_port_priv *priv = to_port_priv(dev); + + return (priv->interrupt_mask & INTERRUPT_MASK_RX) ? 0 + : ps3_vuart_set_interrupt_mask(dev, priv->interrupt_mask | INTERRUPT_MASK_RX); } -int ps3_vuart_enable_interrupt_disconnect(struct ps3_vuart_port_device *dev) +int ps3_vuart_enable_interrupt_disconnect(struct ps3_system_bus_device *dev) { - return (dev->priv->interrupt_mask & INTERRUPT_MASK_DISCONNECT) ? 0 - : ps3_vuart_set_interrupt_mask(dev, dev->priv->interrupt_mask + struct ps3_vuart_port_priv *priv = to_port_priv(dev); + + return (priv->interrupt_mask & INTERRUPT_MASK_DISCONNECT) ? 0 + : ps3_vuart_set_interrupt_mask(dev, priv->interrupt_mask | INTERRUPT_MASK_DISCONNECT); } -int ps3_vuart_disable_interrupt_tx(struct ps3_vuart_port_device *dev) +int ps3_vuart_disable_interrupt_tx(struct ps3_system_bus_device *dev) { - return (dev->priv->interrupt_mask & INTERRUPT_MASK_TX) - ? ps3_vuart_set_interrupt_mask(dev, dev->priv->interrupt_mask + struct ps3_vuart_port_priv *priv = to_port_priv(dev); + + return (priv->interrupt_mask & INTERRUPT_MASK_TX) + ? ps3_vuart_set_interrupt_mask(dev, priv->interrupt_mask & ~INTERRUPT_MASK_TX) : 0; } -int ps3_vuart_disable_interrupt_rx(struct ps3_vuart_port_device *dev) +int ps3_vuart_disable_interrupt_rx(struct ps3_system_bus_device *dev) { - return (dev->priv->interrupt_mask & INTERRUPT_MASK_RX) - ? ps3_vuart_set_interrupt_mask(dev, dev->priv->interrupt_mask + struct ps3_vuart_port_priv *priv = to_port_priv(dev); + + return (priv->interrupt_mask & INTERRUPT_MASK_RX) + ? ps3_vuart_set_interrupt_mask(dev, priv->interrupt_mask & ~INTERRUPT_MASK_RX) : 0; } -int ps3_vuart_disable_interrupt_disconnect(struct ps3_vuart_port_device *dev) +int ps3_vuart_disable_interrupt_disconnect(struct ps3_system_bus_device *dev) { - return (dev->priv->interrupt_mask & INTERRUPT_MASK_DISCONNECT) - ? ps3_vuart_set_interrupt_mask(dev, dev->priv->interrupt_mask + struct ps3_vuart_port_priv *priv = to_port_priv(dev); + + return (priv->interrupt_mask & INTERRUPT_MASK_DISCONNECT) + ? ps3_vuart_set_interrupt_mask(dev, priv->interrupt_mask & ~INTERRUPT_MASK_DISCONNECT) : 0; } /** * ps3_vuart_raw_write - Low level write helper. + * @dev: The struct ps3_system_bus_device instance. * * Do not call ps3_vuart_raw_write directly, use ps3_vuart_write. */ -static int ps3_vuart_raw_write(struct ps3_vuart_port_device *dev, +static int ps3_vuart_raw_write(struct ps3_system_bus_device *dev, const void* buf, unsigned int bytes, unsigned long *bytes_written) { int result; + struct ps3_vuart_port_priv *priv = to_port_priv(dev); - result = lv1_write_virtual_uart(dev->priv->port_number, + result = lv1_write_virtual_uart(dev->port_number, ps3_mm_phys_to_lpar(__pa(buf)), bytes, bytes_written); if (result) { @@ -340,28 +377,30 @@ static int ps3_vuart_raw_write(struct ps3_vuart_port_device *dev, return result; } - dev->priv->stats.bytes_written += *bytes_written; + priv->stats.bytes_written += *bytes_written; dev_dbg(&dev->core, "%s:%d: wrote %lxh/%xh=>%lxh\n", __func__, __LINE__, - *bytes_written, bytes, dev->priv->stats.bytes_written); + *bytes_written, bytes, priv->stats.bytes_written); return result; } /** * ps3_vuart_raw_read - Low level read helper. + * @dev: The struct ps3_system_bus_device instance. * * Do not call ps3_vuart_raw_read directly, use ps3_vuart_read. */ -static int ps3_vuart_raw_read(struct ps3_vuart_port_device *dev, void* buf, +static int ps3_vuart_raw_read(struct ps3_system_bus_device *dev, void *buf, unsigned int bytes, unsigned long *bytes_read) { int result; + struct ps3_vuart_port_priv *priv = to_port_priv(dev); dev_dbg(&dev->core, "%s:%d: %xh\n", __func__, __LINE__, bytes); - result = lv1_read_virtual_uart(dev->priv->port_number, + result = lv1_read_virtual_uart(dev->port_number, ps3_mm_phys_to_lpar(__pa(buf)), bytes, bytes_read); if (result) { @@ -370,25 +409,27 @@ static int ps3_vuart_raw_read(struct ps3_vuart_port_device *dev, void* buf, return result; } - dev->priv->stats.bytes_read += *bytes_read; + priv->stats.bytes_read += *bytes_read; dev_dbg(&dev->core, "%s:%d: read %lxh/%xh=>%lxh\n", __func__, __LINE__, - *bytes_read, bytes, dev->priv->stats.bytes_read); + *bytes_read, bytes, priv->stats.bytes_read); return result; } /** * ps3_vuart_clear_rx_bytes - Discard bytes received. + * @dev: The struct ps3_system_bus_device instance. * @bytes: Max byte count to discard, zero = all pending. * * Used to clear pending rx interrupt source. Will not block. */ -void ps3_vuart_clear_rx_bytes(struct ps3_vuart_port_device *dev, +void ps3_vuart_clear_rx_bytes(struct ps3_system_bus_device *dev, unsigned int bytes) { int result; + struct ps3_vuart_port_priv *priv = to_port_priv(dev); u64 bytes_waiting; void* tmp; @@ -418,8 +459,9 @@ void ps3_vuart_clear_rx_bytes(struct ps3_vuart_port_device *dev, /* Don't include these bytes in the stats. */ - dev->priv->stats.bytes_read -= bytes_waiting; + priv->stats.bytes_read -= bytes_waiting; } +EXPORT_SYMBOL_GPL(ps3_vuart_clear_rx_bytes); /** * struct list_buffer - An element for a port device fifo buffer list. @@ -435,6 +477,7 @@ struct list_buffer { /** * ps3_vuart_write - the entry point for writing data to a port + * @dev: The struct ps3_system_bus_device instance. * * If the port is idle on entry as much of the incoming data is written to * the port as the port will accept. Otherwise a list buffer is created @@ -442,25 +485,26 @@ struct list_buffer { * then enqueued for transmision via the transmit interrupt. */ -int ps3_vuart_write(struct ps3_vuart_port_device *dev, const void* buf, +int ps3_vuart_write(struct ps3_system_bus_device *dev, const void *buf, unsigned int bytes) { static unsigned long dbg_number; int result; + struct ps3_vuart_port_priv *priv = to_port_priv(dev); unsigned long flags; struct list_buffer *lb; dev_dbg(&dev->core, "%s:%d: %u(%xh) bytes\n", __func__, __LINE__, bytes, bytes); - spin_lock_irqsave(&dev->priv->tx_list.lock, flags); + spin_lock_irqsave(&priv->tx_list.lock, flags); - if (list_empty(&dev->priv->tx_list.head)) { + if (list_empty(&priv->tx_list.head)) { unsigned long bytes_written; result = ps3_vuart_raw_write(dev, buf, bytes, &bytes_written); - spin_unlock_irqrestore(&dev->priv->tx_list.lock, flags); + spin_unlock_irqrestore(&priv->tx_list.lock, flags); if (result) { dev_dbg(&dev->core, @@ -478,7 +522,7 @@ int ps3_vuart_write(struct ps3_vuart_port_device *dev, const void* buf, bytes -= bytes_written; buf += bytes_written; } else - spin_unlock_irqrestore(&dev->priv->tx_list.lock, flags); + spin_unlock_irqrestore(&priv->tx_list.lock, flags); lb = kmalloc(sizeof(struct list_buffer) + bytes, GFP_KERNEL); @@ -491,29 +535,86 @@ int ps3_vuart_write(struct ps3_vuart_port_device *dev, const void* buf, lb->tail = lb->data + bytes; lb->dbg_number = ++dbg_number; - spin_lock_irqsave(&dev->priv->tx_list.lock, flags); - list_add_tail(&lb->link, &dev->priv->tx_list.head); + spin_lock_irqsave(&priv->tx_list.lock, flags); + list_add_tail(&lb->link, &priv->tx_list.head); ps3_vuart_enable_interrupt_tx(dev); - spin_unlock_irqrestore(&dev->priv->tx_list.lock, flags); + spin_unlock_irqrestore(&priv->tx_list.lock, flags); dev_dbg(&dev->core, "%s:%d: queued buf_%lu, %xh bytes\n", __func__, __LINE__, lb->dbg_number, bytes); return 0; } +EXPORT_SYMBOL_GPL(ps3_vuart_write); /** - * ps3_vuart_read - the entry point for reading data from a port + * ps3_vuart_queue_rx_bytes - Queue waiting bytes into the buffer list. + * @dev: The struct ps3_system_bus_device instance. + * @bytes_queued: Number of bytes queued to the buffer list. * - * If enough bytes to satisfy the request are held in the buffer list those - * bytes are dequeued and copied to the caller's buffer. Emptied list buffers - * are retiered. If the request cannot be statified by bytes held in the list - * buffers -EAGAIN is returned. + * Must be called with priv->rx_list.lock held. */ -int ps3_vuart_read(struct ps3_vuart_port_device *dev, void* buf, +static int ps3_vuart_queue_rx_bytes(struct ps3_system_bus_device *dev, + u64 *bytes_queued) +{ + static unsigned long dbg_number; + int result; + struct ps3_vuart_port_priv *priv = to_port_priv(dev); + struct list_buffer *lb; + u64 bytes; + + *bytes_queued = 0; + + result = ps3_vuart_get_rx_bytes_waiting(dev, &bytes); + BUG_ON(result); + + if (result) + return -EIO; + + if (!bytes) + return 0; + + /* Add some extra space for recently arrived data. */ + + bytes += 128; + + lb = kmalloc(sizeof(struct list_buffer) + bytes, GFP_ATOMIC); + + if (!lb) + return -ENOMEM; + + ps3_vuart_raw_read(dev, lb->data, bytes, &bytes); + + lb->head = lb->data; + lb->tail = lb->data + bytes; + lb->dbg_number = ++dbg_number; + + list_add_tail(&lb->link, &priv->rx_list.head); + priv->rx_list.bytes_held += bytes; + + dev_dbg(&dev->core, "%s:%d: buf_%lu: queued %lxh bytes\n", + __func__, __LINE__, lb->dbg_number, bytes); + + *bytes_queued = bytes; + + return 0; +} + +/** + * ps3_vuart_read - The entry point for reading data from a port. + * + * Queue data waiting at the port, and if enough bytes to satisfy the request + * are held in the buffer list those bytes are dequeued and copied to the + * caller's buffer. Emptied list buffers are retiered. If the request cannot + * be statified by bytes held in the list buffers -EAGAIN is returned. + */ + +int ps3_vuart_read(struct ps3_system_bus_device *dev, void *buf, unsigned int bytes) { + int result; + struct ps3_vuart_port_priv *priv = to_port_priv(dev); unsigned long flags; struct list_buffer *lb, *n; unsigned long bytes_read; @@ -521,30 +622,37 @@ int ps3_vuart_read(struct ps3_vuart_port_device *dev, void* buf, dev_dbg(&dev->core, "%s:%d: %u(%xh) bytes\n", __func__, __LINE__, bytes, bytes); - spin_lock_irqsave(&dev->priv->rx_list.lock, flags); + spin_lock_irqsave(&priv->rx_list.lock, flags); - if (dev->priv->rx_list.bytes_held < bytes) { - spin_unlock_irqrestore(&dev->priv->rx_list.lock, flags); - dev_dbg(&dev->core, "%s:%d: starved for %lxh bytes\n", - __func__, __LINE__, - bytes - dev->priv->rx_list.bytes_held); - return -EAGAIN; + /* Queue rx bytes here for polled reads. */ + + while (priv->rx_list.bytes_held < bytes) { + u64 tmp; + + result = ps3_vuart_queue_rx_bytes(dev, &tmp); + if (result || !tmp) { + dev_dbg(&dev->core, "%s:%d: starved for %lxh bytes\n", + __func__, __LINE__, + bytes - priv->rx_list.bytes_held); + spin_unlock_irqrestore(&priv->rx_list.lock, flags); + return -EAGAIN; + } } - list_for_each_entry_safe(lb, n, &dev->priv->rx_list.head, link) { + list_for_each_entry_safe(lb, n, &priv->rx_list.head, link) { bytes_read = min((unsigned int)(lb->tail - lb->head), bytes); memcpy(buf, lb->head, bytes_read); buf += bytes_read; bytes -= bytes_read; - dev->priv->rx_list.bytes_held -= bytes_read; + priv->rx_list.bytes_held -= bytes_read; if (bytes_read < lb->tail - lb->head) { lb->head += bytes_read; dev_dbg(&dev->core, "%s:%d: buf_%lu: dequeued %lxh " "bytes\n", __func__, __LINE__, lb->dbg_number, bytes_read); - spin_unlock_irqrestore(&dev->priv->rx_list.lock, flags); + spin_unlock_irqrestore(&priv->rx_list.lock, flags); return 0; } @@ -556,16 +664,32 @@ int ps3_vuart_read(struct ps3_vuart_port_device *dev, void* buf, kfree(lb); } - spin_unlock_irqrestore(&dev->priv->rx_list.lock, flags); + spin_unlock_irqrestore(&priv->rx_list.lock, flags); return 0; } +EXPORT_SYMBOL_GPL(ps3_vuart_read); -int ps3_vuart_read_async(struct ps3_vuart_port_device *dev, work_func_t func, - unsigned int bytes) +/** + * ps3_vuart_work - Asynchronous read handler. + */ + +static void ps3_vuart_work(struct work_struct *work) { + struct ps3_system_bus_device *dev = + ps3_vuart_work_to_system_bus_dev(work); + struct ps3_vuart_port_driver *drv = + ps3_system_bus_dev_to_vuart_drv(dev); + + BUG_ON(!drv); + drv->work(dev); +} + +int ps3_vuart_read_async(struct ps3_system_bus_device *dev, unsigned int bytes) +{ + struct ps3_vuart_port_priv *priv = to_port_priv(dev); unsigned long flags; - if(dev->priv->work.trigger) { + if (priv->rx_list.work.trigger) { dev_dbg(&dev->core, "%s:%d: warning, multiple calls\n", __func__, __LINE__); return -EAGAIN; @@ -573,30 +697,32 @@ int ps3_vuart_read_async(struct ps3_vuart_port_device *dev, work_func_t func, BUG_ON(!bytes); - PREPARE_WORK(&dev->priv->work.work, func); + PREPARE_WORK(&priv->rx_list.work.work, ps3_vuart_work); - spin_lock_irqsave(&dev->priv->work.lock, flags); - if(dev->priv->rx_list.bytes_held >= bytes) { + spin_lock_irqsave(&priv->rx_list.lock, flags); + if (priv->rx_list.bytes_held >= bytes) { dev_dbg(&dev->core, "%s:%d: schedule_work %xh bytes\n", __func__, __LINE__, bytes); - schedule_work(&dev->priv->work.work); - spin_unlock_irqrestore(&dev->priv->work.lock, flags); + schedule_work(&priv->rx_list.work.work); + spin_unlock_irqrestore(&priv->rx_list.lock, flags); return 0; } - dev->priv->work.trigger = bytes; - spin_unlock_irqrestore(&dev->priv->work.lock, flags); + priv->rx_list.work.trigger = bytes; + spin_unlock_irqrestore(&priv->rx_list.lock, flags); dev_dbg(&dev->core, "%s:%d: waiting for %u(%xh) bytes\n", __func__, __LINE__, bytes, bytes); return 0; } +EXPORT_SYMBOL_GPL(ps3_vuart_read_async); -void ps3_vuart_cancel_async(struct ps3_vuart_port_device *dev) +void ps3_vuart_cancel_async(struct ps3_system_bus_device *dev) { - dev->priv->work.trigger = 0; + to_port_priv(dev)->rx_list.work.trigger = 0; } +EXPORT_SYMBOL_GPL(ps3_vuart_cancel_async); /** * ps3_vuart_handle_interrupt_tx - third stage transmit interrupt handler @@ -606,18 +732,19 @@ void ps3_vuart_cancel_async(struct ps3_vuart_port_device *dev) * adjusts the final list buffer state for a partial write. */ -static int ps3_vuart_handle_interrupt_tx(struct ps3_vuart_port_device *dev) +static int ps3_vuart_handle_interrupt_tx(struct ps3_system_bus_device *dev) { int result = 0; + struct ps3_vuart_port_priv *priv = to_port_priv(dev); unsigned long flags; struct list_buffer *lb, *n; unsigned long bytes_total = 0; dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); - spin_lock_irqsave(&dev->priv->tx_list.lock, flags); + spin_lock_irqsave(&priv->tx_list.lock, flags); - list_for_each_entry_safe(lb, n, &dev->priv->tx_list.head, link) { + list_for_each_entry_safe(lb, n, &priv->tx_list.head, link) { unsigned long bytes_written; @@ -651,7 +778,7 @@ static int ps3_vuart_handle_interrupt_tx(struct ps3_vuart_port_device *dev) ps3_vuart_disable_interrupt_tx(dev); port_full: - spin_unlock_irqrestore(&dev->priv->tx_list.lock, flags); + spin_unlock_irqrestore(&priv->tx_list.lock, flags); dev_dbg(&dev->core, "%s:%d wrote %lxh bytes total\n", __func__, __LINE__, bytes_total); return result; @@ -665,60 +792,37 @@ port_full: * buffer list. Buffer list data is dequeued via ps3_vuart_read. */ -static int ps3_vuart_handle_interrupt_rx(struct ps3_vuart_port_device *dev) +static int ps3_vuart_handle_interrupt_rx(struct ps3_system_bus_device *dev) { - static unsigned long dbg_number; - int result = 0; + int result; + struct ps3_vuart_port_priv *priv = to_port_priv(dev); unsigned long flags; - struct list_buffer *lb; - unsigned long bytes; + u64 bytes; dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); - result = ps3_vuart_get_rx_bytes_waiting(dev, &bytes); + spin_lock_irqsave(&priv->rx_list.lock, flags); + result = ps3_vuart_queue_rx_bytes(dev, &bytes); - if (result) - return -EIO; - - BUG_ON(!bytes); - - /* Add some extra space for recently arrived data. */ - - bytes += 128; - - lb = kmalloc(sizeof(struct list_buffer) + bytes, GFP_ATOMIC); - - if (!lb) - return -ENOMEM; - - ps3_vuart_raw_read(dev, lb->data, bytes, &bytes); - - lb->head = lb->data; - lb->tail = lb->data + bytes; - lb->dbg_number = ++dbg_number; - - spin_lock_irqsave(&dev->priv->rx_list.lock, flags); - list_add_tail(&lb->link, &dev->priv->rx_list.head); - dev->priv->rx_list.bytes_held += bytes; - spin_unlock_irqrestore(&dev->priv->rx_list.lock, flags); - - dev_dbg(&dev->core, "%s:%d: buf_%lu: queued %lxh bytes\n", - __func__, __LINE__, lb->dbg_number, bytes); - - spin_lock_irqsave(&dev->priv->work.lock, flags); - if(dev->priv->work.trigger - && dev->priv->rx_list.bytes_held >= dev->priv->work.trigger) { - dev_dbg(&dev->core, "%s:%d: schedule_work %lxh bytes\n", - __func__, __LINE__, dev->priv->work.trigger); - dev->priv->work.trigger = 0; - schedule_work(&dev->priv->work.work); + if (result) { + spin_unlock_irqrestore(&priv->rx_list.lock, flags); + return result; } - spin_unlock_irqrestore(&dev->priv->work.lock, flags); - return 0; + + if (priv->rx_list.work.trigger && priv->rx_list.bytes_held + >= priv->rx_list.work.trigger) { + dev_dbg(&dev->core, "%s:%d: schedule_work %lxh bytes\n", + __func__, __LINE__, priv->rx_list.work.trigger); + priv->rx_list.work.trigger = 0; + schedule_work(&priv->rx_list.work.work); + } + + spin_unlock_irqrestore(&priv->rx_list.lock, flags); + return result; } static int ps3_vuart_handle_interrupt_disconnect( - struct ps3_vuart_port_device *dev) + struct ps3_system_bus_device *dev) { dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); BUG_ON("no support"); @@ -733,9 +837,10 @@ static int ps3_vuart_handle_interrupt_disconnect( * stage handler after one iteration. */ -static int ps3_vuart_handle_port_interrupt(struct ps3_vuart_port_device *dev) +static int ps3_vuart_handle_port_interrupt(struct ps3_system_bus_device *dev) { int result; + struct ps3_vuart_port_priv *priv = to_port_priv(dev); unsigned long status; result = ps3_vuart_get_interrupt_status(dev, &status); @@ -747,21 +852,21 @@ static int ps3_vuart_handle_port_interrupt(struct ps3_vuart_port_device *dev) status); if (status & INTERRUPT_MASK_DISCONNECT) { - dev->priv->stats.disconnect_interrupts++; + priv->stats.disconnect_interrupts++; result = ps3_vuart_handle_interrupt_disconnect(dev); if (result) ps3_vuart_disable_interrupt_disconnect(dev); } if (status & INTERRUPT_MASK_TX) { - dev->priv->stats.tx_interrupts++; + priv->stats.tx_interrupts++; result = ps3_vuart_handle_interrupt_tx(dev); if (result) ps3_vuart_disable_interrupt_tx(dev); } if (status & INTERRUPT_MASK_RX) { - dev->priv->stats.rx_interrupts++; + priv->stats.rx_interrupts++; result = ps3_vuart_handle_interrupt_rx(dev); if (result) ps3_vuart_disable_interrupt_rx(dev); @@ -771,11 +876,11 @@ static int ps3_vuart_handle_port_interrupt(struct ps3_vuart_port_device *dev) } struct vuart_bus_priv { - const struct ports_bmp bmp; + struct ports_bmp *bmp; unsigned int virq; struct semaphore probe_mutex; int use_count; - struct ps3_vuart_port_device *devices[PORT_COUNT]; + struct ps3_system_bus_device *devices[PORT_COUNT]; } static vuart_bus_priv; /** @@ -788,17 +893,16 @@ struct vuart_bus_priv { static irqreturn_t ps3_vuart_irq_handler(int irq, void *_private) { - struct vuart_bus_priv *bus_priv; + struct vuart_bus_priv *bus_priv = _private; - BUG_ON(!_private); - bus_priv = (struct vuart_bus_priv *)_private; + BUG_ON(!bus_priv); while (1) { unsigned int port; - dump_ports_bmp(&bus_priv->bmp); + dump_ports_bmp(bus_priv->bmp); - port = (BITS_PER_LONG - 1) - __ilog2(bus_priv->bmp.status); + port = (BITS_PER_LONG - 1) - __ilog2(bus_priv->bmp->status); if (port == BITS_PER_LONG) break; @@ -812,100 +916,144 @@ static irqreturn_t ps3_vuart_irq_handler(int irq, void *_private) return IRQ_HANDLED; } -static int ps3_vuart_match(struct device *_dev, struct device_driver *_drv) +static int ps3_vuart_bus_interrupt_get(void) { int result; - struct ps3_vuart_port_driver *drv = to_ps3_vuart_port_driver(_drv); - struct ps3_vuart_port_device *dev = to_ps3_vuart_port_device(_dev); - result = dev->match_id == drv->match_id; + pr_debug(" -> %s:%d\n", __func__, __LINE__); - dev_info(&dev->core, "%s:%d: dev=%u(%s), drv=%u(%s): %s\n", __func__, - __LINE__, dev->match_id, dev->core.bus_id, drv->match_id, - drv->core.name, (result ? "match" : "miss")); + vuart_bus_priv.use_count++; + BUG_ON(vuart_bus_priv.use_count > 2); + + if (vuart_bus_priv.use_count != 1) { + return 0; + } + + BUG_ON(vuart_bus_priv.bmp); + + vuart_bus_priv.bmp = kzalloc(sizeof(struct ports_bmp), GFP_KERNEL); + + if (!vuart_bus_priv.bmp) { + pr_debug("%s:%d: kzalloc failed.\n", __func__, __LINE__); + result = -ENOMEM; + goto fail_bmp_malloc; + } + + result = ps3_vuart_irq_setup(PS3_BINDING_CPU_ANY, vuart_bus_priv.bmp, + &vuart_bus_priv.virq); + + if (result) { + pr_debug("%s:%d: ps3_vuart_irq_setup failed (%d)\n", + __func__, __LINE__, result); + result = -EPERM; + goto fail_alloc_irq; + } + + result = request_irq(vuart_bus_priv.virq, ps3_vuart_irq_handler, + IRQF_DISABLED, "vuart", &vuart_bus_priv); + + if (result) { + pr_debug("%s:%d: request_irq failed (%d)\n", + __func__, __LINE__, result); + goto fail_request_irq; + } + + pr_debug(" <- %s:%d: ok\n", __func__, __LINE__); + return result; + +fail_request_irq: + ps3_vuart_irq_destroy(vuart_bus_priv.virq); + vuart_bus_priv.virq = NO_IRQ; +fail_alloc_irq: + kfree(vuart_bus_priv.bmp); + vuart_bus_priv.bmp = NULL; +fail_bmp_malloc: + vuart_bus_priv.use_count--; + pr_debug(" <- %s:%d: failed\n", __func__, __LINE__); return result; } -static int ps3_vuart_probe(struct device *_dev) +static int ps3_vuart_bus_interrupt_put(void) +{ + pr_debug(" -> %s:%d\n", __func__, __LINE__); + + vuart_bus_priv.use_count--; + + BUG_ON(vuart_bus_priv.use_count < 0); + + if (vuart_bus_priv.use_count != 0) + return 0; + + free_irq(vuart_bus_priv.virq, &vuart_bus_priv); + + ps3_vuart_irq_destroy(vuart_bus_priv.virq); + vuart_bus_priv.virq = NO_IRQ; + + kfree(vuart_bus_priv.bmp); + vuart_bus_priv.bmp = NULL; + + pr_debug(" <- %s:%d\n", __func__, __LINE__); + return 0; +} + +static int ps3_vuart_probe(struct ps3_system_bus_device *dev) { int result; - unsigned int port_number; - struct ps3_vuart_port_device *dev = to_ps3_vuart_port_device(_dev); - struct ps3_vuart_port_driver *drv = - to_ps3_vuart_port_driver(_dev->driver); + struct ps3_vuart_port_driver *drv; + struct ps3_vuart_port_priv *priv = NULL; dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); + drv = ps3_system_bus_dev_to_vuart_drv(dev); + + dev_dbg(&dev->core, "%s:%d: (%s)\n", __func__, __LINE__, + drv->core.core.name); + BUG_ON(!drv); + if (dev->port_number >= PORT_COUNT) { + BUG(); + return -EINVAL; + } + down(&vuart_bus_priv.probe_mutex); - /* Setup vuart_bus_priv.devices[]. */ + result = ps3_vuart_bus_interrupt_get(); - result = ps3_vuart_match_id_to_port(dev->match_id, - &port_number); + if (result) + goto fail_setup_interrupt; - if (result) { - dev_dbg(&dev->core, "%s:%d: unknown match_id (%d)\n", - __func__, __LINE__, dev->match_id); - result = -EINVAL; - goto fail_match; - } - - if (vuart_bus_priv.devices[port_number]) { + if (vuart_bus_priv.devices[dev->port_number]) { dev_dbg(&dev->core, "%s:%d: port busy (%d)\n", __func__, - __LINE__, port_number); + __LINE__, dev->port_number); result = -EBUSY; - goto fail_match; + goto fail_busy; } - vuart_bus_priv.devices[port_number] = dev; + vuart_bus_priv.devices[dev->port_number] = dev; - /* Setup dev->priv. */ + /* Setup dev->driver_priv. */ - dev->priv = kzalloc(sizeof(struct ps3_vuart_port_priv), GFP_KERNEL); + dev->driver_priv = kzalloc(sizeof(struct ps3_vuart_port_priv), + GFP_KERNEL); - if (!dev->priv) { + if (!dev->driver_priv) { result = -ENOMEM; - goto fail_alloc; + goto fail_dev_malloc; } - dev->priv->port_number = port_number; + priv = to_port_priv(dev); - INIT_LIST_HEAD(&dev->priv->tx_list.head); - spin_lock_init(&dev->priv->tx_list.lock); + INIT_LIST_HEAD(&priv->tx_list.head); + spin_lock_init(&priv->tx_list.lock); - INIT_LIST_HEAD(&dev->priv->rx_list.head); - spin_lock_init(&dev->priv->rx_list.lock); + INIT_LIST_HEAD(&priv->rx_list.head); + spin_lock_init(&priv->rx_list.lock); - INIT_WORK(&dev->priv->work.work, NULL); - spin_lock_init(&dev->priv->work.lock); - dev->priv->work.trigger = 0; - dev->priv->work.dev = dev; - - if (++vuart_bus_priv.use_count == 1) { - - result = ps3_vuart_irq_setup(PS3_BINDING_CPU_ANY, - (void*)&vuart_bus_priv.bmp.status, &vuart_bus_priv.virq); - - if (result) { - dev_dbg(&dev->core, - "%s:%d: ps3_vuart_irq_setup failed (%d)\n", - __func__, __LINE__, result); - result = -EPERM; - goto fail_alloc_irq; - } - - result = request_irq(vuart_bus_priv.virq, ps3_vuart_irq_handler, - IRQF_DISABLED, "vuart", &vuart_bus_priv); - - if (result) { - dev_info(&dev->core, "%s:%d: request_irq failed (%d)\n", - __func__, __LINE__, result); - goto fail_request_irq; - } - } + INIT_WORK(&priv->rx_list.work.work, NULL); + priv->rx_list.work.trigger = 0; + priv->rx_list.work.dev = dev; /* clear stale pending interrupts */ @@ -936,151 +1084,159 @@ static int ps3_vuart_probe(struct device *_dev) fail_probe: ps3_vuart_set_interrupt_mask(dev, 0); -fail_request_irq: - ps3_vuart_irq_destroy(vuart_bus_priv.virq); - vuart_bus_priv.virq = NO_IRQ; -fail_alloc_irq: - --vuart_bus_priv.use_count; - kfree(dev->priv); - dev->priv = NULL; -fail_alloc: - vuart_bus_priv.devices[port_number] = NULL; -fail_match: + kfree(dev->driver_priv); + dev->driver_priv = NULL; +fail_dev_malloc: + vuart_bus_priv.devices[dev->port_number] = NULL; +fail_busy: + ps3_vuart_bus_interrupt_put(); +fail_setup_interrupt: up(&vuart_bus_priv.probe_mutex); - dev_dbg(&dev->core, "%s:%d failed\n", __func__, __LINE__); + dev_dbg(&dev->core, "%s:%d: failed\n", __func__, __LINE__); return result; } -static int ps3_vuart_remove(struct device *_dev) +/** + * ps3_vuart_cleanup - common cleanup helper. + * @dev: The struct ps3_system_bus_device instance. + * + * Cleans interrupts and HV resources. Must be called with + * vuart_bus_priv.probe_mutex held. Used by ps3_vuart_remove and + * ps3_vuart_shutdown. After this call, polled reading will still work. + */ + +static int ps3_vuart_cleanup(struct ps3_system_bus_device *dev) { - struct ps3_vuart_port_device *dev = to_ps3_vuart_port_device(_dev); - struct ps3_vuart_port_driver *drv = - to_ps3_vuart_port_driver(_dev->driver); + dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); + + ps3_vuart_cancel_async(dev); + ps3_vuart_set_interrupt_mask(dev, 0); + ps3_vuart_bus_interrupt_put(); + return 0; +} + +/** + * ps3_vuart_remove - Completely clean the device instance. + * @dev: The struct ps3_system_bus_device instance. + * + * Cleans all memory, interrupts and HV resources. After this call the + * device can no longer be used. + */ + +static int ps3_vuart_remove(struct ps3_system_bus_device *dev) +{ + struct ps3_vuart_port_priv *priv = to_port_priv(dev); + struct ps3_vuart_port_driver *drv; + + BUG_ON(!dev); down(&vuart_bus_priv.probe_mutex); - dev_dbg(&dev->core, "%s:%d: %s\n", __func__, __LINE__, - dev->core.bus_id); + dev_dbg(&dev->core, " -> %s:%d: match_id %d\n", __func__, __LINE__, + dev->match_id); - BUG_ON(vuart_bus_priv.use_count < 1); - - if (drv->remove) - drv->remove(dev); - else - dev_dbg(&dev->core, "%s:%d: %s no remove method\n", __func__, - __LINE__, dev->core.bus_id); - - vuart_bus_priv.devices[dev->priv->port_number] = NULL; - - if (--vuart_bus_priv.use_count == 0) { - BUG(); - free_irq(vuart_bus_priv.virq, &vuart_bus_priv); - ps3_vuart_irq_destroy(vuart_bus_priv.virq); - vuart_bus_priv.virq = NO_IRQ; + if (!dev->core.driver) { + dev_dbg(&dev->core, "%s:%d: no driver bound\n", __func__, + __LINE__); + up(&vuart_bus_priv.probe_mutex); + return 0; } - kfree(dev->priv); - dev->priv = NULL; + drv = ps3_system_bus_dev_to_vuart_drv(dev); + + BUG_ON(!drv); + + if (drv->remove) { + drv->remove(dev); + } else { + dev_dbg(&dev->core, "%s:%d: no remove method\n", __func__, + __LINE__); + BUG(); + } + + ps3_vuart_cleanup(dev); + + vuart_bus_priv.devices[dev->port_number] = NULL; + kfree(priv); + priv = NULL; + + dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__); + up(&vuart_bus_priv.probe_mutex); + return 0; +} + +/** + * ps3_vuart_shutdown - Cleans interrupts and HV resources. + * @dev: The struct ps3_system_bus_device instance. + * + * Cleans interrupts and HV resources. After this call the + * device can still be used in polling mode. This behavior required + * by sys-manager to be able to complete the device power operation + * sequence. + */ + +static int ps3_vuart_shutdown(struct ps3_system_bus_device *dev) +{ + struct ps3_vuart_port_driver *drv; + + BUG_ON(!dev); + + down(&vuart_bus_priv.probe_mutex); + + dev_dbg(&dev->core, " -> %s:%d: match_id %d\n", __func__, __LINE__, + dev->match_id); + + if (!dev->core.driver) { + dev_dbg(&dev->core, "%s:%d: no driver bound\n", __func__, + __LINE__); + up(&vuart_bus_priv.probe_mutex); + return 0; + } + + drv = ps3_system_bus_dev_to_vuart_drv(dev); + + BUG_ON(!drv); + + if (drv->shutdown) + drv->shutdown(dev); + else if (drv->remove) { + dev_dbg(&dev->core, "%s:%d: no shutdown, calling remove\n", + __func__, __LINE__); + drv->remove(dev); + } else { + dev_dbg(&dev->core, "%s:%d: no shutdown method\n", __func__, + __LINE__); + BUG(); + } + + ps3_vuart_cleanup(dev); + + dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__); up(&vuart_bus_priv.probe_mutex); return 0; } -static void ps3_vuart_shutdown(struct device *_dev) +static int __init ps3_vuart_bus_init(void) { - struct ps3_vuart_port_device *dev = to_ps3_vuart_port_device(_dev); - struct ps3_vuart_port_driver *drv = - to_ps3_vuart_port_driver(_dev->driver); - - dev_dbg(&dev->core, "%s:%d: %s\n", __func__, __LINE__, - dev->core.bus_id); - - if (drv->shutdown) - drv->shutdown(dev); - else - dev_dbg(&dev->core, "%s:%d: %s no shutdown method\n", __func__, - __LINE__, dev->core.bus_id); -} - -/** - * ps3_vuart_bus - The vuart bus instance. - * - * The vuart is managed as a bus that port devices connect to. - */ - -struct bus_type ps3_vuart_bus = { - .name = "ps3_vuart", - .match = ps3_vuart_match, - .probe = ps3_vuart_probe, - .remove = ps3_vuart_remove, - .shutdown = ps3_vuart_shutdown, -}; - -int __init ps3_vuart_bus_init(void) -{ - int result; - pr_debug("%s:%d:\n", __func__, __LINE__); if (!firmware_has_feature(FW_FEATURE_PS3_LV1)) return -ENODEV; init_MUTEX(&vuart_bus_priv.probe_mutex); - result = bus_register(&ps3_vuart_bus); - BUG_ON(result); - return result; + return 0; } -void __exit ps3_vuart_bus_exit(void) +static void __exit ps3_vuart_bus_exit(void) { pr_debug("%s:%d:\n", __func__, __LINE__); - bus_unregister(&ps3_vuart_bus); } core_initcall(ps3_vuart_bus_init); module_exit(ps3_vuart_bus_exit); -/** - * ps3_vuart_port_release_device - Remove a vuart port device. - */ - -static void ps3_vuart_port_release_device(struct device *_dev) -{ -#if defined(DEBUG) - struct ps3_vuart_port_device *dev = to_ps3_vuart_port_device(_dev); - - dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); - - BUG_ON(dev->priv && "forgot to free"); - memset(&dev->core, 0, sizeof(dev->core)); -#endif -} - -/** - * ps3_vuart_port_device_register - Add a vuart port device. - */ - -int ps3_vuart_port_device_register(struct ps3_vuart_port_device *dev) -{ - static unsigned int dev_count = 1; - - BUG_ON(dev->priv && "forgot to free"); - - dev->core.parent = NULL; - dev->core.bus = &ps3_vuart_bus; - dev->core.release = ps3_vuart_port_release_device; - - snprintf(dev->core.bus_id, sizeof(dev->core.bus_id), "vuart_%02x", - dev_count++); - - dev_dbg(&dev->core, "%s:%d register\n", __func__, __LINE__); - - return device_register(&dev->core); -} - -EXPORT_SYMBOL_GPL(ps3_vuart_port_device_register); - /** * ps3_vuart_port_driver_register - Add a vuart port device driver. */ @@ -1089,12 +1245,18 @@ int ps3_vuart_port_driver_register(struct ps3_vuart_port_driver *drv) { int result; - pr_debug("%s:%d: (%s)\n", __func__, __LINE__, drv->core.name); - drv->core.bus = &ps3_vuart_bus; - result = driver_register(&drv->core); + pr_debug("%s:%d: (%s)\n", __func__, __LINE__, drv->core.core.name); + + BUG_ON(!drv->core.match_id); + BUG_ON(!drv->core.core.name); + + drv->core.probe = ps3_vuart_probe; + drv->core.remove = ps3_vuart_remove; + drv->core.shutdown = ps3_vuart_shutdown; + + result = ps3_system_bus_driver_register(&drv->core); return result; } - EXPORT_SYMBOL_GPL(ps3_vuart_port_driver_register); /** @@ -1103,8 +1265,7 @@ EXPORT_SYMBOL_GPL(ps3_vuart_port_driver_register); void ps3_vuart_port_driver_unregister(struct ps3_vuart_port_driver *drv) { - pr_debug("%s:%d: (%s)\n", __func__, __LINE__, drv->core.name); - driver_unregister(&drv->core); + pr_debug("%s:%d: (%s)\n", __func__, __LINE__, drv->core.core.name); + ps3_system_bus_driver_unregister(&drv->core); } - EXPORT_SYMBOL_GPL(ps3_vuart_port_driver_unregister); diff --git a/drivers/ps3/vuart.h b/drivers/ps3/vuart.h index 1be992d568c8..eb7f6d94a890 100644 --- a/drivers/ps3/vuart.h +++ b/drivers/ps3/vuart.h @@ -34,29 +34,7 @@ struct ps3_vuart_stats { struct ps3_vuart_work { struct work_struct work; unsigned long trigger; - spinlock_t lock; - struct ps3_vuart_port_device* dev; /* to convert work to device */ -}; - -/** - * struct ps3_vuart_port_priv - private vuart device data. - */ - -struct ps3_vuart_port_priv { - unsigned int port_number; - u64 interrupt_mask; - - struct { - spinlock_t lock; - struct list_head head; - } tx_list; - struct { - unsigned long bytes_held; - spinlock_t lock; - struct list_head head; - } rx_list; - struct ps3_vuart_stats stats; - struct ps3_vuart_work work; + struct ps3_system_bus_device *dev; /* to convert work to device */ }; /** @@ -64,32 +42,30 @@ struct ps3_vuart_port_priv { */ struct ps3_vuart_port_driver { - enum ps3_match_id match_id; - struct device_driver core; - int (*probe)(struct ps3_vuart_port_device *); - int (*remove)(struct ps3_vuart_port_device *); - void (*shutdown)(struct ps3_vuart_port_device *); - int (*tx_event)(struct ps3_vuart_port_device *dev); - int (*rx_event)(struct ps3_vuart_port_device *dev); - int (*disconnect_event)(struct ps3_vuart_port_device *dev); - /* int (*suspend)(struct ps3_vuart_port_device *, pm_message_t); */ - /* int (*resume)(struct ps3_vuart_port_device *); */ + struct ps3_system_bus_driver core; + int (*probe)(struct ps3_system_bus_device *); + int (*remove)(struct ps3_system_bus_device *); + void (*shutdown)(struct ps3_system_bus_device *); + void (*work)(struct ps3_system_bus_device *); + /* int (*tx_event)(struct ps3_system_bus_device *dev); */ + /* int (*rx_event)(struct ps3_system_bus_device *dev); */ + /* int (*disconnect_event)(struct ps3_system_bus_device *dev); */ + /* int (*suspend)(struct ps3_system_bus_device *, pm_message_t); */ + /* int (*resume)(struct ps3_system_bus_device *); */ }; int ps3_vuart_port_driver_register(struct ps3_vuart_port_driver *drv); void ps3_vuart_port_driver_unregister(struct ps3_vuart_port_driver *drv); -static inline struct ps3_vuart_port_driver *to_ps3_vuart_port_driver( - struct device_driver *_drv) +static inline struct ps3_vuart_port_driver * + ps3_system_bus_dev_to_vuart_drv(struct ps3_system_bus_device *_dev) { - return container_of(_drv, struct ps3_vuart_port_driver, core); + struct ps3_system_bus_driver *sbd = + ps3_system_bus_dev_to_system_bus_drv(_dev); + BUG_ON(!sbd); + return container_of(sbd, struct ps3_vuart_port_driver, core); } -static inline struct ps3_vuart_port_device *to_ps3_vuart_port_device( - struct device *_dev) -{ - return container_of(_dev, struct ps3_vuart_port_device, core); -} -static inline struct ps3_vuart_port_device *ps3_vuart_work_to_port_device( +static inline struct ps3_system_bus_device *ps3_vuart_work_to_system_bus_dev( struct work_struct *_work) { struct ps3_vuart_work *vw = container_of(_work, struct ps3_vuart_work, @@ -97,14 +73,13 @@ static inline struct ps3_vuart_port_device *ps3_vuart_work_to_port_device( return vw->dev; } -int ps3_vuart_write(struct ps3_vuart_port_device *dev, const void* buf, +int ps3_vuart_write(struct ps3_system_bus_device *dev, const void *buf, unsigned int bytes); -int ps3_vuart_read(struct ps3_vuart_port_device *dev, void* buf, +int ps3_vuart_read(struct ps3_system_bus_device *dev, void *buf, unsigned int bytes); -int ps3_vuart_read_async(struct ps3_vuart_port_device *dev, work_func_t func, - unsigned int bytes); -void ps3_vuart_cancel_async(struct ps3_vuart_port_device *dev); -void ps3_vuart_clear_rx_bytes(struct ps3_vuart_port_device *dev, +int ps3_vuart_read_async(struct ps3_system_bus_device *dev, unsigned int bytes); +void ps3_vuart_cancel_async(struct ps3_system_bus_device *dev); +void ps3_vuart_clear_rx_bytes(struct ps3_system_bus_device *dev, unsigned int bytes); #endif diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 4e4c10a7fd3a..83b071b6ece4 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -246,7 +246,7 @@ comment "Platform RTC drivers" config RTC_DRV_CMOS tristate "PC-style 'CMOS'" depends on RTC_CLASS && (X86 || ALPHA || ARM26 || ARM \ - || M32R || ATARI || POWERPC || MIPS) + || M32R || ATARI || PPC || MIPS) help Say "yes" here to get direct support for the real time clock found in every PC or ACPI-based system, and some other boards. diff --git a/drivers/serial/cpm_uart/cpm_uart_core.c b/drivers/serial/cpm_uart/cpm_uart_core.c index b63ff8dd7304..cefde58dbad2 100644 --- a/drivers/serial/cpm_uart/cpm_uart_core.c +++ b/drivers/serial/cpm_uart/cpm_uart_core.c @@ -678,7 +678,7 @@ static int cpm_uart_tx_pump(struct uart_port *port) } bdp->cbd_datlen = count; bdp->cbd_sc |= BD_SC_READY; - __asm__("eieio"); + eieio(); /* Get next BD. */ if (bdp->cbd_sc & BD_SC_WRAP) bdp = pinfo->tx_bd_base; diff --git a/drivers/serial/of_serial.c b/drivers/serial/of_serial.c index 7ffdaeaf0545..a64d85821996 100644 --- a/drivers/serial/of_serial.c +++ b/drivers/serial/of_serial.c @@ -17,6 +17,11 @@ #include #include +struct of_serial_info { + int type; + int line; +}; + /* * Fill a struct uart_port for a given device node */ @@ -62,6 +67,7 @@ static int __devinit of_platform_serial_setup(struct of_device *ofdev, static int __devinit of_platform_serial_probe(struct of_device *ofdev, const struct of_device_id *id) { + struct of_serial_info *info; struct uart_port port; int port_type; int ret; @@ -69,30 +75,35 @@ static int __devinit of_platform_serial_probe(struct of_device *ofdev, if (of_find_property(ofdev->node, "used-by-rtas", NULL)) return -EBUSY; + info = kmalloc(sizeof(*info), GFP_KERNEL); + if (info == NULL) + return -ENOMEM; + port_type = (unsigned long)id->data; ret = of_platform_serial_setup(ofdev, port_type, &port); if (ret) goto out; switch (port_type) { - case PORT_UNKNOWN: - dev_info(&ofdev->dev, "Unknown serial port found, " - "attempting to use 8250 driver\n"); - /* fallthrough */ case PORT_8250 ... PORT_MAX_8250: ret = serial8250_register_port(&port); break; default: /* need to add code for these */ + case PORT_UNKNOWN: + dev_info(&ofdev->dev, "Unknown serial port found, ignored\n"); ret = -ENODEV; break; } if (ret < 0) goto out; - ofdev->dev.driver_data = (void *)(unsigned long)ret; + info->type = port_type; + info->line = ret; + ofdev->dev.driver_data = info; return 0; out: + kfree(info); irq_dispose_mapping(port.irq); return ret; } @@ -102,8 +113,16 @@ out: */ static int of_platform_serial_remove(struct of_device *ofdev) { - int line = (unsigned long)ofdev->dev.driver_data; - serial8250_unregister_port(line); + struct of_serial_info *info = ofdev->dev.driver_data; + switch (info->type) { + case PORT_8250 ... PORT_MAX_8250: + serial8250_unregister_port(info->line); + break; + default: + /* need to add code for these */ + break; + } + kfree(info); return 0; } diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 403dac787ebf..9b7a76be36a0 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -1790,8 +1790,8 @@ config FB_IBM_GXT4500 adaptor, found on some IBM System P (pSeries) machines. config FB_PS3 - bool "PS3 GPU framebuffer driver" - depends on (FB = y) && PS3_PS3AV + tristate "PS3 GPU framebuffer driver" + depends on FB && PS3_PS3AV select FB_SYS_FILLRECT select FB_SYS_COPYAREA select FB_SYS_IMAGEBLIT diff --git a/drivers/video/ps3fb.c b/drivers/video/ps3fb.c index 9cf92ba5d6e3..08b7ffbbbbd8 100644 --- a/drivers/video/ps3fb.c +++ b/drivers/video/ps3fb.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #include #include @@ -46,6 +45,9 @@ #include #include + +#define DEVICE_NAME "ps3fb" + #ifdef PS3FB_DEBUG #define DPRINTK(fmt, args...) printk("%s: " fmt, __func__ , ##args) #else @@ -126,7 +128,6 @@ struct gpu_driver_info { struct ps3fb_priv { unsigned int irq_no; - void *dev; u64 context_handle, memory_handle; void *xdr_ea; @@ -171,7 +172,7 @@ static const struct ps3fb_res_table ps3fb_res[] = { { 0, 0, 0, 0 , 0} }; /* default resolution */ -#define GPU_RES_INDEX 0 /* 720 x 480 */ +#define GPU_RES_INDEX 0 /* 720 x 480 */ static const struct fb_videomode ps3fb_modedb[] = { /* 60 Hz broadcast modes (modes "1" to "5") */ @@ -298,10 +299,9 @@ static const struct fb_videomode ps3fb_modedb[] = { #define FB_OFF(i) (GPU_OFFSET - VP_OFF(i) % GPU_OFFSET) static int ps3fb_mode; -module_param(ps3fb_mode, bool, 0); - -static char *mode_option __initdata; +module_param(ps3fb_mode, int, 0); +static char *mode_option __devinitdata; static int ps3fb_get_res_table(u32 xres, u32 yres) { @@ -681,15 +681,15 @@ int ps3fb_wait_for_vsync(u32 crtc) EXPORT_SYMBOL_GPL(ps3fb_wait_for_vsync); -void ps3fb_flip_ctl(int on) +void ps3fb_flip_ctl(int on, void *data) { + struct ps3fb_priv *priv = data; if (on) - atomic_dec_if_positive(&ps3fb.ext_flip); + atomic_dec_if_positive(&priv->ext_flip); else - atomic_inc(&ps3fb.ext_flip); + atomic_inc(&priv->ext_flip); } -EXPORT_SYMBOL_GPL(ps3fb_flip_ctl); /* * ioctl @@ -851,37 +851,9 @@ static irqreturn_t ps3fb_vsync_interrupt(int irq, void *ptr) return IRQ_HANDLED; } -#ifndef MODULE -static int __init ps3fb_setup(char *options) -{ - char *this_opt; - int mode = 0; - if (!options || !*options) - return 0; /* no options */ - - while ((this_opt = strsep(&options, ",")) != NULL) { - if (!*this_opt) - continue; - if (!strncmp(this_opt, "mode:", 5)) - mode = simple_strtoul(this_opt + 5, NULL, 0); - else - mode_option = this_opt; - } - return mode; -} -#endif /* MODULE */ - - /* - * Initialisation - */ - -static void ps3fb_platform_release(struct device *device) -{ - /* This is called when the reference count goes to zero. */ -} - -static int ps3fb_vsync_settings(struct gpu_driver_info *dinfo, void *dev) +static int ps3fb_vsync_settings(struct gpu_driver_info *dinfo, + struct ps3_system_bus_device *dev) { int error; @@ -897,7 +869,6 @@ static int ps3fb_vsync_settings(struct gpu_driver_info *dinfo, void *dev) return -EINVAL; } - ps3fb.dev = dev; error = ps3_irq_plug_setup(PS3_BINDING_CPU_ANY, dinfo->irq.irq_outlet, &ps3fb.irq_no); if (error) { @@ -907,7 +878,7 @@ static int ps3fb_vsync_settings(struct gpu_driver_info *dinfo, void *dev) } error = request_irq(ps3fb.irq_no, ps3fb_vsync_interrupt, IRQF_DISABLED, - "ps3fb vsync", ps3fb.dev); + DEVICE_NAME, dev); if (error) { printk(KERN_ERR "%s: request_irq failed %d\n", __func__, error); @@ -966,16 +937,45 @@ static struct fb_ops ps3fb_ops = { }; static struct fb_fix_screeninfo ps3fb_fix __initdata = { - .id = "PS3 FB", + .id = DEVICE_NAME, .type = FB_TYPE_PACKED_PIXELS, .visual = FB_VISUAL_TRUECOLOR, .accel = FB_ACCEL_NONE, }; -static int __init ps3fb_probe(struct platform_device *dev) +static int ps3fb_set_sync(void) +{ + int status; + +#ifdef HEAD_A + status = lv1_gpu_context_attribute(0x0, + L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_SYNC, + 0, L1GPU_DISPLAY_SYNC_VSYNC, 0, 0); + if (status) { + printk(KERN_ERR "%s: lv1_gpu_context_attribute DISPLAY_SYNC " + "failed: %d\n", __func__, status); + return -1; + } +#endif +#ifdef HEAD_B + status = lv1_gpu_context_attribute(0x0, + L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_SYNC, + 1, L1GPU_DISPLAY_SYNC_VSYNC, 0, 0); + + if (status) { + printk(KERN_ERR "%s: lv1_gpu_context_attribute DISPLAY_MODE " + "failed: %d\n", __func__, status); + return -1; + } +#endif + return 0; +} + +static int __devinit ps3fb_probe(struct ps3_system_bus_device *dev) { struct fb_info *info; int retval = -ENOMEM; + u32 xres, yres; u64 ddr_lpar = 0; u64 lpar_dma_control = 0; u64 lpar_driver_info = 0; @@ -986,6 +986,30 @@ static int __init ps3fb_probe(struct platform_device *dev) unsigned long offset; struct task_struct *task; + status = ps3_open_hv_device(dev); + if (status) { + printk(KERN_ERR "%s: ps3_open_hv_device failed\n", __func__); + goto err; + } + + if (!ps3fb_mode) + ps3fb_mode = ps3av_get_mode(); + DPRINTK("ps3av_mode:%d\n", ps3fb_mode); + + if (ps3fb_mode > 0 && + !ps3av_video_mode2res(ps3fb_mode, &xres, &yres)) { + ps3fb.res_index = ps3fb_get_res_table(xres, yres); + DPRINTK("res_index:%d\n", ps3fb.res_index); + } else + ps3fb.res_index = GPU_RES_INDEX; + + atomic_set(&ps3fb.f_count, -1); /* fbcon opens ps3fb */ + atomic_set(&ps3fb.ext_flip, 0); /* for flip with vsync */ + init_waitqueue_head(&ps3fb.wait_vsync); + ps3fb.num_frames = 1; + + ps3fb_set_sync(); + /* get gpu context handle */ status = lv1_gpu_memory_allocate(DDR_SIZE, 0, 0, 0, 0, &ps3fb.memory_handle, &ddr_lpar); @@ -1029,7 +1053,7 @@ static int __init ps3fb_probe(struct platform_device *dev) * leakage into userspace */ memset(ps3fb.xdr_ea, 0, ps3fb_videomemory.size); - info = framebuffer_alloc(sizeof(u32) * 16, &dev->dev); + info = framebuffer_alloc(sizeof(u32) * 16, &dev->core); if (!info) goto err_free_irq; @@ -1061,19 +1085,20 @@ static int __init ps3fb_probe(struct platform_device *dev) if (retval < 0) goto err_fb_dealloc; - platform_set_drvdata(dev, info); + dev->core.driver_data = info; printk(KERN_INFO "fb%d: PS3 frame buffer device, using %ld KiB of video memory\n", info->node, ps3fb_videomemory.size >> 10); - task = kthread_run(ps3fbd, info, "ps3fbd"); + task = kthread_run(ps3fbd, info, DEVICE_NAME); if (IS_ERR(task)) { retval = PTR_ERR(task); goto err_unregister_framebuffer; } ps3fb.task = task; + ps3av_register_flip_ctl(ps3fb_flip_ctl, &ps3fb); return 0; @@ -1084,7 +1109,7 @@ err_fb_dealloc: err_framebuffer_release: framebuffer_release(info); err_free_irq: - free_irq(ps3fb.irq_no, ps3fb.dev); + free_irq(ps3fb.irq_no, dev); ps3_irq_plug_destroy(ps3fb.irq_no); err_iounmap_dinfo: iounmap((u8 __iomem *)ps3fb.dinfo); @@ -1096,26 +1121,30 @@ err: return retval; } -static void ps3fb_shutdown(struct platform_device *dev) -{ - ps3fb_flip_ctl(0); /* flip off */ - ps3fb.dinfo->irq.mask = 0; - free_irq(ps3fb.irq_no, ps3fb.dev); - ps3_irq_plug_destroy(ps3fb.irq_no); - iounmap((u8 __iomem *)ps3fb.dinfo); -} - -void ps3fb_cleanup(void) +static int ps3fb_shutdown(struct ps3_system_bus_device *dev) { int status; + struct fb_info *info = dev->core.driver_data; + DPRINTK(" -> %s:%d\n", __func__, __LINE__); + + ps3fb_flip_ctl(0, &ps3fb); /* flip off */ + ps3fb.dinfo->irq.mask = 0; + + if (info) { + unregister_framebuffer(info); + fb_dealloc_cmap(&info->cmap); + framebuffer_release(info); + } + + ps3av_register_flip_ctl(NULL, NULL); if (ps3fb.task) { struct task_struct *task = ps3fb.task; ps3fb.task = NULL; kthread_stop(task); } if (ps3fb.irq_no) { - free_irq(ps3fb.irq_no, ps3fb.dev); + free_irq(ps3fb.irq_no, dev); ps3_irq_plug_destroy(ps3fb.irq_no); } iounmap((u8 __iomem *)ps3fb.dinfo); @@ -1128,134 +1157,69 @@ void ps3fb_cleanup(void) if (status) DPRINTK("lv1_gpu_memory_free failed: %d\n", status); - ps3av_dev_close(); -} + ps3_close_hv_device(dev); + DPRINTK(" <- %s:%d\n", __func__, __LINE__); -EXPORT_SYMBOL_GPL(ps3fb_cleanup); - -static int ps3fb_remove(struct platform_device *dev) -{ - struct fb_info *info = platform_get_drvdata(dev); - - if (info) { - unregister_framebuffer(info); - fb_dealloc_cmap(&info->cmap); - framebuffer_release(info); - } - ps3fb_cleanup(); return 0; } -static struct platform_driver ps3fb_driver = { - .probe = ps3fb_probe, - .remove = ps3fb_remove, - .shutdown = ps3fb_shutdown, - .driver = { .name = "ps3fb" } +static struct ps3_system_bus_driver ps3fb_driver = { + .match_id = PS3_MATCH_ID_GRAPHICS, + .core.name = DEVICE_NAME, + .core.owner = THIS_MODULE, + .probe = ps3fb_probe, + .remove = ps3fb_shutdown, + .shutdown = ps3fb_shutdown, }; -static struct platform_device ps3fb_device = { - .name = "ps3fb", - .id = 0, - .dev = { .release = ps3fb_platform_release } -}; - -int ps3fb_set_sync(void) +static int __init ps3fb_setup(void) { - int status; + char *options; -#ifdef HEAD_A - status = lv1_gpu_context_attribute(0x0, - L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_SYNC, - 0, L1GPU_DISPLAY_SYNC_VSYNC, 0, 0); - if (status) { - printk(KERN_ERR - "%s: lv1_gpu_context_attribute DISPLAY_SYNC failed: %d\n", - __func__, status); - return -1; - } +#ifdef MODULE + return 0; #endif -#ifdef HEAD_B - status = lv1_gpu_context_attribute(0x0, - L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_SYNC, - 1, L1GPU_DISPLAY_SYNC_VSYNC, 0, 0); - if (status) { - printk(KERN_ERR - "%s: lv1_gpu_context_attribute DISPLAY_MODE failed: %d\n", - __func__, status); - return -1; + if (fb_get_options(DEVICE_NAME, &options)) + return -ENXIO; + + if (!options || !*options) + return 0; + + while (1) { + char *this_opt = strsep(&options, ","); + + if (!this_opt) + break; + if (!*this_opt) + continue; + if (!strncmp(this_opt, "mode:", 5)) + ps3fb_mode = simple_strtoul(this_opt + 5, NULL, 0); + else + mode_option = this_opt; } -#endif return 0; } -EXPORT_SYMBOL_GPL(ps3fb_set_sync); - static int __init ps3fb_init(void) { - int error; -#ifndef MODULE - int mode; - char *option = NULL; + if (!ps3fb_videomemory.address || ps3fb_setup()) + return -ENXIO; - if (fb_get_options("ps3fb", &option)) - goto err; -#endif + return ps3_system_bus_driver_register(&ps3fb_driver); +} - if (!ps3fb_videomemory.address) - goto err; - - error = ps3av_dev_open(); - if (error) { - printk(KERN_ERR "%s: ps3av_dev_open failed\n", __func__); - goto err; - } - - ps3fb_mode = ps3av_get_mode(); - DPRINTK("ps3av_mode:%d\n", ps3fb_mode); -#ifndef MODULE - mode = ps3fb_setup(option); /* check boot option */ - if (mode) - ps3fb_mode = mode; -#endif - if (ps3fb_mode > 0) { - u32 xres, yres; - ps3av_video_mode2res(ps3fb_mode, &xres, &yres); - ps3fb.res_index = ps3fb_get_res_table(xres, yres); - DPRINTK("res_index:%d\n", ps3fb.res_index); - } else - ps3fb.res_index = GPU_RES_INDEX; - - atomic_set(&ps3fb.f_count, -1); /* fbcon opens ps3fb */ - atomic_set(&ps3fb.ext_flip, 0); /* for flip with vsync */ - init_waitqueue_head(&ps3fb.wait_vsync); - ps3fb.num_frames = 1; - - error = platform_driver_register(&ps3fb_driver); - if (!error) { - error = platform_device_register(&ps3fb_device); - if (error) - platform_driver_unregister(&ps3fb_driver); - } - - ps3fb_set_sync(); - - return error; - -err: - return -ENXIO; +static void __exit ps3fb_exit(void) +{ + DPRINTK(" -> %s:%d\n", __func__, __LINE__); + ps3_system_bus_driver_unregister(&ps3fb_driver); + DPRINTK(" <- %s:%d\n", __func__, __LINE__); } module_init(ps3fb_init); - -#ifdef MODULE -static void __exit ps3fb_exit(void) -{ - platform_device_unregister(&ps3fb_device); - platform_driver_unregister(&ps3fb_driver); -} - module_exit(ps3fb_exit); MODULE_LICENSE("GPL"); -#endif /* MODULE */ +MODULE_DESCRIPTION("PS3 GPU Frame Buffer Driver"); +MODULE_AUTHOR("Sony Computer Entertainment Inc."); +MODULE_ALIAS(PS3_MODULE_ALIAS_GRAPHICS); diff --git a/include/asm-powerpc/cache.h b/include/asm-powerpc/cache.h index 642be62cf393..53507046a1b1 100644 --- a/include/asm-powerpc/cache.h +++ b/include/asm-powerpc/cache.h @@ -34,5 +34,9 @@ struct ppc64_caches { extern struct ppc64_caches ppc64_caches; #endif /* __powerpc64__ && ! __ASSEMBLY__ */ +#if !defined(__ASSEMBLY__) +#define __read_mostly __attribute__((__section__(".data.read_mostly"))) +#endif + #endif /* __KERNEL__ */ #endif /* _ASM_POWERPC_CACHE_H */ diff --git a/include/asm-powerpc/cputable.h b/include/asm-powerpc/cputable.h index 82d595a52109..3dc8e2dfca84 100644 --- a/include/asm-powerpc/cputable.h +++ b/include/asm-powerpc/cputable.h @@ -111,7 +111,7 @@ extern void do_feature_fixups(unsigned long value, void *fixup_start, /* CPU kernel features */ /* Retain the 32b definitions all use bottom half of word */ -#define CPU_FTR_SPLIT_ID_CACHE ASM_CONST(0x0000000000000001) +#define CPU_FTR_COHERENT_ICACHE ASM_CONST(0x0000000000000001) #define CPU_FTR_L2CR ASM_CONST(0x0000000000000002) #define CPU_FTR_SPEC7450 ASM_CONST(0x0000000000000004) #define CPU_FTR_ALTIVEC ASM_CONST(0x0000000000000008) @@ -135,6 +135,7 @@ extern void do_feature_fixups(unsigned long value, void *fixup_start, #define CPU_FTR_PPC_LE ASM_CONST(0x0000000000200000) #define CPU_FTR_REAL_LE ASM_CONST(0x0000000000400000) #define CPU_FTR_FPU_UNAVAILABLE ASM_CONST(0x0000000000800000) +#define CPU_FTR_UNIFIED_ID_CACHE ASM_CONST(0x0000000001000000) /* * Add the 64-bit processor unique features in the top half of the word; @@ -154,7 +155,6 @@ extern void do_feature_fixups(unsigned long value, void *fixup_start, #define CPU_FTR_MMCRA LONG_ASM_CONST(0x0000004000000000) #define CPU_FTR_CTRL LONG_ASM_CONST(0x0000008000000000) #define CPU_FTR_SMT LONG_ASM_CONST(0x0000010000000000) -#define CPU_FTR_COHERENT_ICACHE LONG_ASM_CONST(0x0000020000000000) #define CPU_FTR_LOCKLESS_TLBIE LONG_ASM_CONST(0x0000040000000000) #define CPU_FTR_CI_LARGE_PAGE LONG_ASM_CONST(0x0000100000000000) #define CPU_FTR_PAUSE_ZERO LONG_ASM_CONST(0x0000200000000000) @@ -206,164 +206,149 @@ extern void do_feature_fixups(unsigned long value, void *fixup_start, !defined(CONFIG_POWER3) && !defined(CONFIG_POWER4) && \ !defined(CONFIG_BOOKE)) -#define CPU_FTRS_PPC601 (CPU_FTR_COMMON | CPU_FTR_601 | CPU_FTR_HPTE_TABLE) -#define CPU_FTRS_603 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ +#define CPU_FTRS_PPC601 (CPU_FTR_COMMON | CPU_FTR_601 | CPU_FTR_HPTE_TABLE | \ + CPU_FTR_COHERENT_ICACHE | CPU_FTR_UNIFIED_ID_CACHE) +#define CPU_FTRS_603 (CPU_FTR_COMMON | \ CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | \ CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_PPC_LE) -#define CPU_FTRS_604 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ +#define CPU_FTRS_604 (CPU_FTR_COMMON | \ CPU_FTR_USE_TB | CPU_FTR_604_PERF_MON | CPU_FTR_HPTE_TABLE | \ CPU_FTR_PPC_LE) -#define CPU_FTRS_740_NOTAU (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ +#define CPU_FTRS_740_NOTAU (CPU_FTR_COMMON | \ CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | CPU_FTR_L2CR | \ CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_PPC_LE) -#define CPU_FTRS_740 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ +#define CPU_FTRS_740 (CPU_FTR_COMMON | \ CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | CPU_FTR_L2CR | \ CPU_FTR_TAU | CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP | \ CPU_FTR_PPC_LE) -#define CPU_FTRS_750 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ +#define CPU_FTRS_750 (CPU_FTR_COMMON | \ CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | CPU_FTR_L2CR | \ CPU_FTR_TAU | CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP | \ CPU_FTR_PPC_LE) -#define CPU_FTRS_750CL (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ - CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | CPU_FTR_L2CR | \ - CPU_FTR_TAU | CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP | \ - CPU_FTR_HAS_HIGH_BATS | CPU_FTR_PPC_LE) -#define CPU_FTRS_750FX1 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ - CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | CPU_FTR_L2CR | \ - CPU_FTR_TAU | CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP | \ - CPU_FTR_DUAL_PLL_750FX | CPU_FTR_NO_DPM | CPU_FTR_PPC_LE) -#define CPU_FTRS_750FX2 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ - CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | CPU_FTR_L2CR | \ - CPU_FTR_TAU | CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP | \ - CPU_FTR_NO_DPM | CPU_FTR_PPC_LE) -#define CPU_FTRS_750FX (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ - CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | CPU_FTR_L2CR | \ - CPU_FTR_TAU | CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP | \ - CPU_FTR_DUAL_PLL_750FX | CPU_FTR_HAS_HIGH_BATS | CPU_FTR_PPC_LE) -#define CPU_FTRS_750GX (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ - CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | CPU_FTR_L2CR | \ - CPU_FTR_TAU | CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP | \ - CPU_FTR_DUAL_PLL_750FX | CPU_FTR_HAS_HIGH_BATS | CPU_FTR_PPC_LE) -#define CPU_FTRS_7400_NOTAU (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ +#define CPU_FTRS_750CL (CPU_FTRS_750 | CPU_FTR_HAS_HIGH_BATS) +#define CPU_FTRS_750FX1 (CPU_FTRS_750 | CPU_FTR_DUAL_PLL_750FX | CPU_FTR_NO_DPM) +#define CPU_FTRS_750FX2 (CPU_FTRS_750 | CPU_FTR_NO_DPM) +#define CPU_FTRS_750FX (CPU_FTRS_750 | CPU_FTR_DUAL_PLL_750FX | \ + CPU_FTR_HAS_HIGH_BATS) +#define CPU_FTRS_750GX (CPU_FTRS_750FX) +#define CPU_FTRS_7400_NOTAU (CPU_FTR_COMMON | \ CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | CPU_FTR_L2CR | \ CPU_FTR_ALTIVEC_COMP | CPU_FTR_HPTE_TABLE | \ CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_PPC_LE) -#define CPU_FTRS_7400 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ +#define CPU_FTRS_7400 (CPU_FTR_COMMON | \ CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | CPU_FTR_L2CR | \ CPU_FTR_TAU | CPU_FTR_ALTIVEC_COMP | CPU_FTR_HPTE_TABLE | \ CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_PPC_LE) -#define CPU_FTRS_7450_20 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ +#define CPU_FTRS_7450_20 (CPU_FTR_COMMON | \ CPU_FTR_USE_TB | CPU_FTR_L2CR | CPU_FTR_ALTIVEC_COMP | \ CPU_FTR_L3CR | CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | \ CPU_FTR_NEED_COHERENT | CPU_FTR_PPC_LE) -#define CPU_FTRS_7450_21 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ +#define CPU_FTRS_7450_21 (CPU_FTR_COMMON | \ CPU_FTR_USE_TB | \ CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_L2CR | CPU_FTR_ALTIVEC_COMP | \ CPU_FTR_L3CR | CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | \ CPU_FTR_NAP_DISABLE_L2_PR | CPU_FTR_L3_DISABLE_NAP | \ CPU_FTR_NEED_COHERENT | CPU_FTR_PPC_LE) -#define CPU_FTRS_7450_23 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ +#define CPU_FTRS_7450_23 (CPU_FTR_COMMON | \ CPU_FTR_USE_TB | \ CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_L2CR | CPU_FTR_ALTIVEC_COMP | \ CPU_FTR_L3CR | CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | \ CPU_FTR_NAP_DISABLE_L2_PR | CPU_FTR_NEED_COHERENT | CPU_FTR_PPC_LE) -#define CPU_FTRS_7455_1 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ +#define CPU_FTRS_7455_1 (CPU_FTR_COMMON | \ CPU_FTR_USE_TB | \ CPU_FTR_L2CR | CPU_FTR_ALTIVEC_COMP | CPU_FTR_L3CR | \ CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | CPU_FTR_HAS_HIGH_BATS | \ CPU_FTR_NEED_COHERENT | CPU_FTR_PPC_LE) -#define CPU_FTRS_7455_20 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ +#define CPU_FTRS_7455_20 (CPU_FTR_COMMON | \ CPU_FTR_USE_TB | \ CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_L2CR | CPU_FTR_ALTIVEC_COMP | \ CPU_FTR_L3CR | CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | \ CPU_FTR_NAP_DISABLE_L2_PR | CPU_FTR_L3_DISABLE_NAP | \ CPU_FTR_NEED_COHERENT | CPU_FTR_HAS_HIGH_BATS | CPU_FTR_PPC_LE) -#define CPU_FTRS_7455 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ +#define CPU_FTRS_7455 (CPU_FTR_COMMON | \ CPU_FTR_USE_TB | \ CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_L2CR | CPU_FTR_ALTIVEC_COMP | \ CPU_FTR_L3CR | CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | \ CPU_FTR_NAP_DISABLE_L2_PR | CPU_FTR_HAS_HIGH_BATS | \ CPU_FTR_NEED_COHERENT | CPU_FTR_PPC_LE) -#define CPU_FTRS_7447_10 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ +#define CPU_FTRS_7447_10 (CPU_FTR_COMMON | \ CPU_FTR_USE_TB | \ CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_L2CR | CPU_FTR_ALTIVEC_COMP | \ CPU_FTR_L3CR | CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | \ CPU_FTR_NAP_DISABLE_L2_PR | CPU_FTR_HAS_HIGH_BATS | \ CPU_FTR_NEED_COHERENT | CPU_FTR_NO_BTIC | CPU_FTR_PPC_LE) -#define CPU_FTRS_7447 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ +#define CPU_FTRS_7447 (CPU_FTR_COMMON | \ CPU_FTR_USE_TB | \ CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_L2CR | CPU_FTR_ALTIVEC_COMP | \ CPU_FTR_L3CR | CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | \ CPU_FTR_NAP_DISABLE_L2_PR | CPU_FTR_HAS_HIGH_BATS | \ CPU_FTR_NEED_COHERENT | CPU_FTR_PPC_LE) -#define CPU_FTRS_7447A (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ +#define CPU_FTRS_7447A (CPU_FTR_COMMON | \ CPU_FTR_USE_TB | \ CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_L2CR | CPU_FTR_ALTIVEC_COMP | \ CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | \ CPU_FTR_NAP_DISABLE_L2_PR | CPU_FTR_HAS_HIGH_BATS | \ CPU_FTR_NEED_COHERENT | CPU_FTR_PPC_LE) -#define CPU_FTRS_7448 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ +#define CPU_FTRS_7448 (CPU_FTR_COMMON | \ CPU_FTR_USE_TB | \ CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_L2CR | CPU_FTR_ALTIVEC_COMP | \ CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | \ CPU_FTR_NAP_DISABLE_L2_PR | CPU_FTR_HAS_HIGH_BATS | \ CPU_FTR_PPC_LE) -#define CPU_FTRS_82XX (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ +#define CPU_FTRS_82XX (CPU_FTR_COMMON | \ CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB) -#define CPU_FTRS_G2_LE (CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_MAYBE_CAN_DOZE | \ +#define CPU_FTRS_G2_LE (CPU_FTR_MAYBE_CAN_DOZE | \ CPU_FTR_USE_TB | CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_HAS_HIGH_BATS) -#define CPU_FTRS_E300 (CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_MAYBE_CAN_DOZE | \ +#define CPU_FTRS_E300 (CPU_FTR_MAYBE_CAN_DOZE | \ CPU_FTR_USE_TB | CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_HAS_HIGH_BATS | \ CPU_FTR_COMMON) -#define CPU_FTRS_E300C2 (CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_MAYBE_CAN_DOZE | \ +#define CPU_FTRS_E300C2 (CPU_FTR_MAYBE_CAN_DOZE | \ CPU_FTR_USE_TB | CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_HAS_HIGH_BATS | \ CPU_FTR_COMMON | CPU_FTR_FPU_UNAVAILABLE) -#define CPU_FTRS_CLASSIC32 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ +#define CPU_FTRS_CLASSIC32 (CPU_FTR_COMMON | \ CPU_FTR_USE_TB | CPU_FTR_HPTE_TABLE) -#define CPU_FTRS_8XX (CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB) -#define CPU_FTRS_40X (CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | \ - CPU_FTR_NODSISRALIGN) -#define CPU_FTRS_44X (CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | \ - CPU_FTR_NODSISRALIGN) -#define CPU_FTRS_E200 (CPU_FTR_USE_TB | CPU_FTR_NODSISRALIGN) -#define CPU_FTRS_E500 (CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | \ - CPU_FTR_NODSISRALIGN) -#define CPU_FTRS_E500_2 (CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | \ +#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_E200 (CPU_FTR_USE_TB | CPU_FTR_NODSISRALIGN | \ + CPU_FTR_COHERENT_ICACHE | CPU_FTR_UNIFIED_ID_CACHE) +#define CPU_FTRS_E500 (CPU_FTR_USE_TB | CPU_FTR_NODSISRALIGN) +#define CPU_FTRS_E500_2 (CPU_FTR_USE_TB | \ CPU_FTR_BIG_PHYS | CPU_FTR_NODSISRALIGN) #define CPU_FTRS_GENERIC_32 (CPU_FTR_COMMON | CPU_FTR_NODSISRALIGN) /* 64-bit CPUs */ -#define CPU_FTRS_POWER3 (CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | \ +#define CPU_FTRS_POWER3 (CPU_FTR_USE_TB | \ CPU_FTR_HPTE_TABLE | CPU_FTR_IABR | CPU_FTR_PPC_LE) -#define CPU_FTRS_RS64 (CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | \ +#define CPU_FTRS_RS64 (CPU_FTR_USE_TB | \ CPU_FTR_HPTE_TABLE | CPU_FTR_IABR | \ CPU_FTR_MMCRA | CPU_FTR_CTRL) -#define CPU_FTRS_POWER4 (CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | \ +#define CPU_FTRS_POWER4 (CPU_FTR_USE_TB | \ CPU_FTR_HPTE_TABLE | CPU_FTR_PPCAS_ARCH_V2 | CPU_FTR_CTRL | \ CPU_FTR_MMCRA) -#define CPU_FTRS_PPC970 (CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | \ +#define CPU_FTRS_PPC970 (CPU_FTR_USE_TB | \ CPU_FTR_HPTE_TABLE | CPU_FTR_PPCAS_ARCH_V2 | CPU_FTR_CTRL | \ CPU_FTR_ALTIVEC_COMP | CPU_FTR_CAN_NAP | CPU_FTR_MMCRA) -#define CPU_FTRS_POWER5 (CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | \ +#define CPU_FTRS_POWER5 (CPU_FTR_USE_TB | \ CPU_FTR_HPTE_TABLE | CPU_FTR_PPCAS_ARCH_V2 | CPU_FTR_CTRL | \ CPU_FTR_MMCRA | CPU_FTR_SMT | \ CPU_FTR_COHERENT_ICACHE | CPU_FTR_LOCKLESS_TLBIE | \ CPU_FTR_PURR) -#define CPU_FTRS_POWER6 (CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | \ +#define CPU_FTRS_POWER6 (CPU_FTR_USE_TB | \ CPU_FTR_HPTE_TABLE | CPU_FTR_PPCAS_ARCH_V2 | CPU_FTR_CTRL | \ 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) -#define CPU_FTRS_CELL (CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | \ +#define CPU_FTRS_CELL (CPU_FTR_USE_TB | \ 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) -#define CPU_FTRS_PA6T (CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | \ +#define CPU_FTRS_PA6T (CPU_FTR_USE_TB | \ CPU_FTR_HPTE_TABLE | CPU_FTR_PPCAS_ARCH_V2 | \ CPU_FTR_ALTIVEC_COMP | CPU_FTR_CI_LARGE_PAGE | \ CPU_FTR_PURR | CPU_FTR_REAL_LE) -#define CPU_FTRS_COMPATIBLE (CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | \ +#define CPU_FTRS_COMPATIBLE (CPU_FTR_USE_TB | \ CPU_FTR_HPTE_TABLE | CPU_FTR_PPCAS_ARCH_V2) #ifdef __powerpc64__ diff --git a/include/asm-powerpc/floppy.h b/include/asm-powerpc/floppy.h index afa700ded877..34146f0eea63 100644 --- a/include/asm-powerpc/floppy.h +++ b/include/asm-powerpc/floppy.h @@ -29,7 +29,7 @@ #define fd_free_irq() free_irq(FLOPPY_IRQ, NULL); #include -#include /* for ppc64_isabridge_dev */ +#include /* for isa_bridge_pcidev */ #define fd_dma_setup(addr,size,mode,io) fd_ops->_dma_setup(addr,size,mode,io) @@ -139,12 +139,12 @@ static int hard_dma_setup(char *addr, unsigned long size, int mode, int io) if (bus_addr && (addr != prev_addr || size != prev_size || dir != prev_dir)) { /* different from last time -- unmap prev */ - pci_unmap_single(ppc64_isabridge_dev, bus_addr, prev_size, prev_dir); + pci_unmap_single(isa_bridge_pcidev, bus_addr, prev_size, prev_dir); bus_addr = 0; } if (!bus_addr) /* need to map it */ - bus_addr = pci_map_single(ppc64_isabridge_dev, addr, size, dir); + bus_addr = pci_map_single(isa_bridge_pcidev, addr, size, dir); /* remember this one as prev */ prev_addr = addr; diff --git a/include/asm-powerpc/hvcall.h b/include/asm-powerpc/hvcall.h index 62efd9d7a43d..bf6cd7cb996c 100644 --- a/include/asm-powerpc/hvcall.h +++ b/include/asm-powerpc/hvcall.h @@ -206,6 +206,7 @@ #define H_FREE_LOGICAL_LAN_BUFFER 0x1D4 #define H_QUERY_INT_STATE 0x1E4 #define H_POLL_PENDING 0x1D8 +#define H_ILLAN_ATTRIBUTES 0x244 #define H_JOIN 0x298 #define H_VASI_STATE 0x2A4 #define H_ENABLE_CRQ 0x2B0 diff --git a/include/asm-powerpc/io.h b/include/asm-powerpc/io.h index 350c9bdb31dc..bb8d965f96c6 100644 --- a/include/asm-powerpc/io.h +++ b/include/asm-powerpc/io.h @@ -607,9 +607,9 @@ static inline void iosync(void) * * * iounmap undoes such a mapping and can be hooked * - * * __ioremap_explicit (and the pending __iounmap_explicit) are low level - * functions to create hand-made mappings for use only by the PCI code - * and cannot currently be hooked. + * * __ioremap_at (and the pending __iounmap_at) are low level functions to + * create hand-made mappings for use only by the PCI code and cannot + * currently be hooked. Must be page aligned. * * * __ioremap is the low level implementation used by ioremap and * ioremap_flags and cannot be hooked (but can be used by a hook on one @@ -629,19 +629,9 @@ extern void __iomem *__ioremap(phys_addr_t, unsigned long size, unsigned long flags); extern void __iounmap(volatile void __iomem *addr); -extern int __ioremap_explicit(phys_addr_t p_addr, unsigned long v_addr, - unsigned long size, unsigned long flags); -extern int __iounmap_explicit(volatile void __iomem *start, - unsigned long size); - -extern void __iomem * reserve_phb_iospace(unsigned long size); - -/* Those are more 32 bits only functions */ -extern unsigned long iopa(unsigned long addr); -extern unsigned long mm_ptov(unsigned long addr) __attribute_const__; -extern void io_block_mapping(unsigned long virt, phys_addr_t phys, - unsigned int size, int flags); - +extern void __iomem * __ioremap_at(phys_addr_t pa, void *ea, + unsigned long size, unsigned long flags); +extern void __iounmap_at(void *ea, unsigned long size); /* * When CONFIG_PPC_INDIRECT_IO is set, we use the generic iomap implementation @@ -651,8 +641,8 @@ extern void io_block_mapping(unsigned long virt, phys_addr_t phys, */ #define HAVE_ARCH_PIO_SIZE 1 #define PIO_OFFSET 0x00000000UL -#define PIO_MASK 0x3fffffffUL -#define PIO_RESERVED 0x40000000UL +#define PIO_MASK (FULL_IO_SIZE - 1) +#define PIO_RESERVED (FULL_IO_SIZE) #define mmio_read16be(addr) readw_be(addr) #define mmio_read32be(addr) readl_be(addr) diff --git a/include/asm-powerpc/irq.h b/include/asm-powerpc/irq.h index 05dd5a3eb3aa..0485c53db2b5 100644 --- a/include/asm-powerpc/irq.h +++ b/include/asm-powerpc/irq.h @@ -223,6 +223,15 @@ extern void irq_dispose_mapping(unsigned int virq); extern unsigned int irq_find_mapping(struct irq_host *host, irq_hw_number_t hwirq); +/** + * irq_create_direct_mapping - Allocate a virq for direct mapping + * @host: host to allocate the virq for or NULL for default host + * + * This routine is used for irq controllers which can choose the hardware + * interrupt numbers they generate. In such a case it's simplest to use + * the linux virq as the hardware interrupt number. + */ +extern unsigned int irq_create_direct_mapping(struct irq_host *host); /** * irq_radix_revmap - Find a linux virq from a hw irq number. diff --git a/include/asm-powerpc/lppaca.h b/include/asm-powerpc/lppaca.h index 821ea0c512b4..567ed92cd91f 100644 --- a/include/asm-powerpc/lppaca.h +++ b/include/asm-powerpc/lppaca.h @@ -98,7 +98,7 @@ struct lppaca { u64 saved_gpr5; // Saved GPR5 x30-x37 u8 reserved4; // Reserved x38-x38 - u8 cpuctls_task_attrs; // Task attributes for cpuctls x39-x39 + u8 donate_dedicated_cpu; // Donate dedicated CPU cycles x39-x39 u8 fpregs_in_use; // FP regs in use x3A-x3A u8 pmcregs_in_use; // PMC regs in use x3B-x3B volatile u32 saved_decr; // Saved Decr Value x3C-x3F diff --git a/include/asm-powerpc/lv1call.h b/include/asm-powerpc/lv1call.h index f733beeea63a..81713acf7529 100644 --- a/include/asm-powerpc/lv1call.h +++ b/include/asm-powerpc/lv1call.h @@ -238,6 +238,7 @@ LV1_CALL(destruct_virtual_address_space, 1, 0, 10 ) LV1_CALL(configure_irq_state_bitmap, 3, 0, 11 ) LV1_CALL(connect_irq_plug_ext, 5, 0, 12 ) LV1_CALL(release_memory, 1, 0, 13 ) +LV1_CALL(put_iopte, 5, 0, 15 ) LV1_CALL(disconnect_irq_plug_ext, 3, 0, 17 ) LV1_CALL(construct_event_receive_port, 0, 1, 18 ) LV1_CALL(destruct_event_receive_port, 1, 0, 19 ) @@ -268,6 +269,8 @@ LV1_CALL(remove_repository_node, 4, 0, 93 ) LV1_CALL(read_htab_entries, 2, 5, 95 ) LV1_CALL(set_dabr, 2, 0, 96 ) LV1_CALL(get_total_execution_time, 2, 1, 103 ) +LV1_CALL(allocate_io_segment, 3, 1, 116 ) +LV1_CALL(release_io_segment, 2, 0, 117 ) LV1_CALL(construct_io_irq_outlet, 1, 1, 120 ) LV1_CALL(destruct_io_irq_outlet, 1, 0, 121 ) LV1_CALL(map_htab, 1, 1, 122 ) diff --git a/include/asm-powerpc/machdep.h b/include/asm-powerpc/machdep.h index 6cf1a831f550..71c6e7eb2a26 100644 --- a/include/asm-powerpc/machdep.h +++ b/include/asm-powerpc/machdep.h @@ -218,7 +218,7 @@ struct machdep_calls { int (*pcibios_enable_device_hook)(struct pci_dev *, int initial); /* Called in indirect_* to avoid touching devices */ - int (*pci_exclude_device)(unsigned char, unsigned char); + int (*pci_exclude_device)(struct pci_controller *, unsigned char, unsigned char); /* Called at then very end of pcibios_init() */ void (*pcibios_after_init)(void); diff --git a/include/asm-powerpc/mmu-8xx.h b/include/asm-powerpc/mmu-8xx.h new file mode 100644 index 000000000000..952bd8899f2f --- /dev/null +++ b/include/asm-powerpc/mmu-8xx.h @@ -0,0 +1,147 @@ +#ifndef _ASM_POWERPC_MMU_8XX_H_ +#define _ASM_POWERPC_MMU_8XX_H_ +/* + * PPC8xx support + */ + +/* Control/status registers for the MPC8xx. + * A write operation to these registers causes serialized access. + * During software tablewalk, the registers used perform mask/shift-add + * operations when written/read. A TLB entry is created when the Mx_RPN + * is written, and the contents of several registers are used to + * create the entry. + */ +#define SPRN_MI_CTR 784 /* Instruction TLB control register */ +#define MI_GPM 0x80000000 /* Set domain manager mode */ +#define MI_PPM 0x40000000 /* Set subpage protection */ +#define MI_CIDEF 0x20000000 /* Set cache inhibit when MMU dis */ +#define MI_RSV4I 0x08000000 /* Reserve 4 TLB entries */ +#define MI_PPCS 0x02000000 /* Use MI_RPN prob/priv state */ +#define MI_IDXMASK 0x00001f00 /* TLB index to be loaded */ +#define MI_RESETVAL 0x00000000 /* Value of register at reset */ + +/* These are the Ks and Kp from the PowerPC books. For proper operation, + * Ks = 0, Kp = 1. + */ +#define SPRN_MI_AP 786 +#define MI_Ks 0x80000000 /* Should not be set */ +#define MI_Kp 0x40000000 /* Should always be set */ + +/* The effective page number register. When read, contains the information + * about the last instruction TLB miss. When MI_RPN is written, bits in + * this register are used to create the TLB entry. + */ +#define SPRN_MI_EPN 787 +#define MI_EPNMASK 0xfffff000 /* Effective page number for entry */ +#define MI_EVALID 0x00000200 /* Entry is valid */ +#define MI_ASIDMASK 0x0000000f /* ASID match value */ + /* Reset value is undefined */ + +/* A "level 1" or "segment" or whatever you want to call it register. + * For the instruction TLB, it contains bits that get loaded into the + * TLB entry when the MI_RPN is written. + */ +#define SPRN_MI_TWC 789 +#define MI_APG 0x000001e0 /* Access protection group (0) */ +#define MI_GUARDED 0x00000010 /* Guarded storage */ +#define MI_PSMASK 0x0000000c /* Mask of page size bits */ +#define MI_PS8MEG 0x0000000c /* 8M page size */ +#define MI_PS512K 0x00000004 /* 512K page size */ +#define MI_PS4K_16K 0x00000000 /* 4K or 16K page size */ +#define MI_SVALID 0x00000001 /* Segment entry is valid */ + /* Reset value is undefined */ + +/* Real page number. Defined by the pte. Writing this register + * causes a TLB entry to be created for the instruction TLB, using + * additional information from the MI_EPN, and MI_TWC registers. + */ +#define SPRN_MI_RPN 790 + +/* Define an RPN value for mapping kernel memory to large virtual + * pages for boot initialization. This has real page number of 0, + * large page size, shared page, cache enabled, and valid. + * Also mark all subpages valid and write access. + */ +#define MI_BOOTINIT 0x000001fd + +#define SPRN_MD_CTR 792 /* Data TLB control register */ +#define MD_GPM 0x80000000 /* Set domain manager mode */ +#define MD_PPM 0x40000000 /* Set subpage protection */ +#define MD_CIDEF 0x20000000 /* Set cache inhibit when MMU dis */ +#define MD_WTDEF 0x10000000 /* Set writethrough when MMU dis */ +#define MD_RSV4I 0x08000000 /* Reserve 4 TLB entries */ +#define MD_TWAM 0x04000000 /* Use 4K page hardware assist */ +#define MD_PPCS 0x02000000 /* Use MI_RPN prob/priv state */ +#define MD_IDXMASK 0x00001f00 /* TLB index to be loaded */ +#define MD_RESETVAL 0x04000000 /* Value of register at reset */ + +#define SPRN_M_CASID 793 /* Address space ID (context) to match */ +#define MC_ASIDMASK 0x0000000f /* Bits used for ASID value */ + + +/* These are the Ks and Kp from the PowerPC books. For proper operation, + * Ks = 0, Kp = 1. + */ +#define SPRN_MD_AP 794 +#define MD_Ks 0x80000000 /* Should not be set */ +#define MD_Kp 0x40000000 /* Should always be set */ + +/* The effective page number register. When read, contains the information + * about the last instruction TLB miss. When MD_RPN is written, bits in + * this register are used to create the TLB entry. + */ +#define SPRN_MD_EPN 795 +#define MD_EPNMASK 0xfffff000 /* Effective page number for entry */ +#define MD_EVALID 0x00000200 /* Entry is valid */ +#define MD_ASIDMASK 0x0000000f /* ASID match value */ + /* Reset value is undefined */ + +/* The pointer to the base address of the first level page table. + * During a software tablewalk, reading this register provides the address + * of the entry associated with MD_EPN. + */ +#define SPRN_M_TWB 796 +#define M_L1TB 0xfffff000 /* Level 1 table base address */ +#define M_L1INDX 0x00000ffc /* Level 1 index, when read */ + /* Reset value is undefined */ + +/* A "level 1" or "segment" or whatever you want to call it register. + * For the data TLB, it contains bits that get loaded into the TLB entry + * when the MD_RPN is written. It is also provides the hardware assist + * for finding the PTE address during software tablewalk. + */ +#define SPRN_MD_TWC 797 +#define MD_L2TB 0xfffff000 /* Level 2 table base address */ +#define MD_L2INDX 0xfffffe00 /* Level 2 index (*pte), when read */ +#define MD_APG 0x000001e0 /* Access protection group (0) */ +#define MD_GUARDED 0x00000010 /* Guarded storage */ +#define MD_PSMASK 0x0000000c /* Mask of page size bits */ +#define MD_PS8MEG 0x0000000c /* 8M page size */ +#define MD_PS512K 0x00000004 /* 512K page size */ +#define MD_PS4K_16K 0x00000000 /* 4K or 16K page size */ +#define MD_WT 0x00000002 /* Use writethrough page attribute */ +#define MD_SVALID 0x00000001 /* Segment entry is valid */ + /* Reset value is undefined */ + + +/* Real page number. Defined by the pte. Writing this register + * causes a TLB entry to be created for the data TLB, using + * additional information from the MD_EPN, and MD_TWC registers. + */ +#define SPRN_MD_RPN 798 + +/* This is a temporary storage register that could be used to save + * a processor working register during a tablewalk. + */ +#define SPRN_M_TW 799 + +#ifndef __ASSEMBLY__ +typedef unsigned long phys_addr_t; + +typedef struct { + unsigned long id; + unsigned long vdso_base; +} mm_context_t; +#endif /* !__ASSEMBLY__ */ + +#endif /* _ASM_POWERPC_MMU_8XX_H_ */ diff --git a/include/asm-powerpc/mmu-fsl-booke.h b/include/asm-powerpc/mmu-fsl-booke.h new file mode 100644 index 000000000000..37580004cd7a --- /dev/null +++ b/include/asm-powerpc/mmu-fsl-booke.h @@ -0,0 +1,88 @@ +#ifndef _ASM_POWERPC_MMU_FSL_BOOKE_H_ +#define _ASM_POWERPC_MMU_FSL_BOOKE_H_ +/* + * Freescale Book-E MMU support + */ + +/* Book-E defined page sizes */ +#define BOOKE_PAGESZ_1K 0 +#define BOOKE_PAGESZ_4K 1 +#define BOOKE_PAGESZ_16K 2 +#define BOOKE_PAGESZ_64K 3 +#define BOOKE_PAGESZ_256K 4 +#define BOOKE_PAGESZ_1M 5 +#define BOOKE_PAGESZ_4M 6 +#define BOOKE_PAGESZ_16M 7 +#define BOOKE_PAGESZ_64M 8 +#define BOOKE_PAGESZ_256M 9 +#define BOOKE_PAGESZ_1GB 10 +#define BOOKE_PAGESZ_4GB 11 +#define BOOKE_PAGESZ_16GB 12 +#define BOOKE_PAGESZ_64GB 13 +#define BOOKE_PAGESZ_256GB 14 +#define BOOKE_PAGESZ_1TB 15 + +#define MAS0_TLBSEL(x) ((x << 28) & 0x30000000) +#define MAS0_ESEL(x) ((x << 16) & 0x0FFF0000) +#define MAS0_NV(x) ((x) & 0x00000FFF) + +#define MAS1_VALID 0x80000000 +#define MAS1_IPROT 0x40000000 +#define MAS1_TID(x) ((x << 16) & 0x3FFF0000) +#define MAS1_TS 0x00001000 +#define MAS1_TSIZE(x) ((x << 8) & 0x00000F00) + +#define MAS2_EPN 0xFFFFF000 +#define MAS2_X0 0x00000040 +#define MAS2_X1 0x00000020 +#define MAS2_W 0x00000010 +#define MAS2_I 0x00000008 +#define MAS2_M 0x00000004 +#define MAS2_G 0x00000002 +#define MAS2_E 0x00000001 + +#define MAS3_RPN 0xFFFFF000 +#define MAS3_U0 0x00000200 +#define MAS3_U1 0x00000100 +#define MAS3_U2 0x00000080 +#define MAS3_U3 0x00000040 +#define MAS3_UX 0x00000020 +#define MAS3_SX 0x00000010 +#define MAS3_UW 0x00000008 +#define MAS3_SW 0x00000004 +#define MAS3_UR 0x00000002 +#define MAS3_SR 0x00000001 + +#define MAS4_TLBSELD(x) MAS0_TLBSEL(x) +#define MAS4_TIDDSEL 0x000F0000 +#define MAS4_TSIZED(x) MAS1_TSIZE(x) +#define MAS4_X0D 0x00000040 +#define MAS4_X1D 0x00000020 +#define MAS4_WD 0x00000010 +#define MAS4_ID 0x00000008 +#define MAS4_MD 0x00000004 +#define MAS4_GD 0x00000002 +#define MAS4_ED 0x00000001 + +#define MAS6_SPID0 0x3FFF0000 +#define MAS6_SPID1 0x00007FFE +#define MAS6_SAS 0x00000001 +#define MAS6_SPID MAS6_SPID0 + +#define MAS7_RPN 0xFFFFFFFF + +#ifndef __ASSEMBLY__ + +#ifndef CONFIG_PHYS_64BIT +typedef unsigned long phys_addr_t; +#else +typedef unsigned long long phys_addr_t; +#endif + +typedef struct { + unsigned long id; + unsigned long vdso_base; +} mm_context_t; +#endif /* !__ASSEMBLY__ */ + +#endif /* _ASM_POWERPC_MMU_FSL_BOOKE_H_ */ diff --git a/include/asm-powerpc/mmu-hash32.h b/include/asm-powerpc/mmu-hash32.h new file mode 100644 index 000000000000..4bd735be3833 --- /dev/null +++ b/include/asm-powerpc/mmu-hash32.h @@ -0,0 +1,91 @@ +#ifndef _ASM_POWERPC_MMU_HASH32_H_ +#define _ASM_POWERPC_MMU_HASH32_H_ +/* + * 32-bit hash table MMU support + */ + +/* + * BATs + */ + +/* Block size masks */ +#define BL_128K 0x000 +#define BL_256K 0x001 +#define BL_512K 0x003 +#define BL_1M 0x007 +#define BL_2M 0x00F +#define BL_4M 0x01F +#define BL_8M 0x03F +#define BL_16M 0x07F +#define BL_32M 0x0FF +#define BL_64M 0x1FF +#define BL_128M 0x3FF +#define BL_256M 0x7FF + +/* BAT Access Protection */ +#define BPP_XX 0x00 /* No access */ +#define BPP_RX 0x01 /* Read only */ +#define BPP_RW 0x02 /* Read/write */ + +#ifndef __ASSEMBLY__ +struct ppc_bat { + struct { + unsigned long bepi:15; /* Effective page index (virtual address) */ + unsigned long :4; /* Unused */ + unsigned long bl:11; /* Block size mask */ + unsigned long vs:1; /* Supervisor valid */ + unsigned long vp:1; /* User valid */ + } batu; /* Upper register */ + struct { + unsigned long brpn:15; /* Real page index (physical address) */ + unsigned long :10; /* Unused */ + unsigned long w:1; /* Write-thru cache */ + unsigned long i:1; /* Cache inhibit */ + unsigned long m:1; /* Memory coherence */ + unsigned long g:1; /* Guarded (MBZ in IBAT) */ + unsigned long :1; /* Unused */ + unsigned long pp:2; /* Page access protections */ + } batl; /* Lower register */ +}; +#endif /* !__ASSEMBLY__ */ + +/* + * Hash table + */ + +/* Values for PP (assumes Ks=0, Kp=1) */ +#define PP_RWXX 0 /* Supervisor read/write, User none */ +#define PP_RWRX 1 /* Supervisor read/write, User read */ +#define PP_RWRW 2 /* Supervisor read/write, User read/write */ +#define PP_RXRX 3 /* Supervisor read, User read */ + +#ifndef __ASSEMBLY__ + +/* Hardware Page Table Entry */ +struct hash_pte { + unsigned long v:1; /* Entry is valid */ + unsigned long vsid:24; /* Virtual segment identifier */ + unsigned long h:1; /* Hash algorithm indicator */ + unsigned long api:6; /* Abbreviated page index */ + unsigned long rpn:20; /* Real (physical) page number */ + unsigned long :3; /* Unused */ + unsigned long r:1; /* Referenced */ + unsigned long c:1; /* Changed */ + unsigned long w:1; /* Write-thru cache mode */ + unsigned long i:1; /* Cache inhibited */ + unsigned long m:1; /* Memory coherence */ + unsigned long g:1; /* Guarded */ + unsigned long :1; /* Unused */ + unsigned long pp:2; /* Page protection */ +}; + +typedef struct { + unsigned long id; + unsigned long vdso_base; +} mm_context_t; + +typedef unsigned long phys_addr_t; + +#endif /* !__ASSEMBLY__ */ + +#endif /* _ASM_POWERPC_MMU_HASH32_H_ */ diff --git a/include/asm-powerpc/mmu-hash64.h b/include/asm-powerpc/mmu-hash64.h index b8dca30bd0b5..695962f02059 100644 --- a/include/asm-powerpc/mmu-hash64.h +++ b/include/asm-powerpc/mmu-hash64.h @@ -94,6 +94,9 @@ extern char initial_stab[]; #define HPTE_R_C ASM_CONST(0x0000000000000080) #define HPTE_R_R ASM_CONST(0x0000000000000100) +#define HPTE_V_1TB_SEG ASM_CONST(0x4000000000000000) +#define HPTE_V_VRMA_MASK ASM_CONST(0x4001ffffff000000) + /* Values for PP (assumes Ks=0, Kp=1) */ /* pp0 will always be 0 for linux */ #define PP_RWXX 0 /* Supervisor read/write, User none */ @@ -103,12 +106,12 @@ extern char initial_stab[]; #ifndef __ASSEMBLY__ -typedef struct { +struct hash_pte { unsigned long v; unsigned long r; -} hpte_t; +}; -extern hpte_t *htab_address; +extern struct hash_pte *htab_address; extern unsigned long htab_size_bytes; extern unsigned long htab_hash_mask; diff --git a/include/asm-powerpc/mmu.h b/include/asm-powerpc/mmu.h index fe510fff8907..d44d211e7588 100644 --- a/include/asm-powerpc/mmu.h +++ b/include/asm-powerpc/mmu.h @@ -5,13 +5,18 @@ #ifdef CONFIG_PPC64 /* 64-bit classic hash table MMU */ # include +#elif defined(CONFIG_PPC_STD_MMU) +/* 32-bit classic hash table MMU */ +# include #elif defined(CONFIG_44x) /* 44x-style software loaded TLB */ # include -#else -/* Other 32-bit. FIXME: split up the other 32-bit MMU types, and - * revise for arch/powerpc */ -# include +#elif defined(CONFIG_FSL_BOOKE) +/* Freescale Book-E software loaded TLB */ +# include +#elif defined (CONFIG_PPC_8xx) +/* Motorola/Freescale 8xx software loaded TLB */ +# include #endif #endif /* __KERNEL__ */ diff --git a/include/asm-powerpc/mmu_context.h b/include/asm-powerpc/mmu_context.h index 40c9e5a13ff1..f863ac21409e 100644 --- a/include/asm-powerpc/mmu_context.h +++ b/include/asm-powerpc/mmu_context.h @@ -2,16 +2,210 @@ #define __ASM_POWERPC_MMU_CONTEXT_H #ifdef __KERNEL__ +#include +#include +#include + #ifndef CONFIG_PPC64 -#include +#include +#include + +/* + * On 32-bit PowerPC 6xx/7xx/7xxx CPUs, we use a set of 16 VSIDs + * (virtual segment identifiers) for each context. Although the + * hardware supports 24-bit VSIDs, and thus >1 million contexts, + * we only use 32,768 of them. That is ample, since there can be + * at most around 30,000 tasks in the system anyway, and it means + * that we can use a bitmap to indicate which contexts are in use. + * Using a bitmap means that we entirely avoid all of the problems + * that we used to have when the context number overflowed, + * particularly on SMP systems. + * -- paulus. + */ + +/* + * This function defines the mapping from contexts to VSIDs (virtual + * segment IDs). We use a skew on both the context and the high 4 bits + * of the 32-bit virtual address (the "effective segment ID") in order + * to spread out the entries in the MMU hash table. Note, if this + * function is changed then arch/ppc/mm/hashtable.S will have to be + * changed to correspond. + */ +#define CTX_TO_VSID(ctx, va) (((ctx) * (897 * 16) + ((va) >> 28) * 0x111) \ + & 0xffffff) + +/* + The MPC8xx has only 16 contexts. We rotate through them on each + task switch. A better way would be to keep track of tasks that + own contexts, and implement an LRU usage. That way very active + tasks don't always have to pay the TLB reload overhead. The + kernel pages are mapped shared, so the kernel can run on behalf + of any task that makes a kernel entry. Shared does not mean they + are not protected, just that the ASID comparison is not performed. + -- Dan + + The IBM4xx has 256 contexts, so we can just rotate through these + as a way of "switching" contexts. If the TID of the TLB is zero, + the PID/TID comparison is disabled, so we can use a TID of zero + to represent all kernel pages as shared among all contexts. + -- Dan + */ + +static inline void enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk) +{ +} + +#ifdef CONFIG_8xx +#define NO_CONTEXT 16 +#define LAST_CONTEXT 15 +#define FIRST_CONTEXT 0 + +#elif defined(CONFIG_4xx) +#define NO_CONTEXT 256 +#define LAST_CONTEXT 255 +#define FIRST_CONTEXT 1 + +#elif defined(CONFIG_E200) || defined(CONFIG_E500) +#define NO_CONTEXT 256 +#define LAST_CONTEXT 255 +#define FIRST_CONTEXT 1 + +#else + +/* PPC 6xx, 7xx CPUs */ +#define NO_CONTEXT ((unsigned long) -1) +#define LAST_CONTEXT 32767 +#define FIRST_CONTEXT 1 +#endif + +/* + * Set the current MMU context. + * On 32-bit PowerPCs (other than the 8xx embedded chips), this is done by + * loading up the segment registers for the user part of the address space. + * + * Since the PGD is immediately available, it is much faster to simply + * pass this along as a second parameter, which is required for 8xx and + * can be used for debugging on all processors (if you happen to have + * an Abatron). + */ +extern void set_context(unsigned long contextid, pgd_t *pgd); + +/* + * Bitmap of contexts in use. + * The size of this bitmap is LAST_CONTEXT + 1 bits. + */ +extern unsigned long context_map[]; + +/* + * This caches the next context number that we expect to be free. + * Its use is an optimization only, we can't rely on this context + * number to be free, but it usually will be. + */ +extern unsigned long next_mmu_context; + +/* + * If we don't have sufficient contexts to give one to every task + * that could be in the system, we need to be able to steal contexts. + * These variables support that. + */ +#if LAST_CONTEXT < 30000 +#define FEW_CONTEXTS 1 +extern atomic_t nr_free_contexts; +extern struct mm_struct *context_mm[LAST_CONTEXT+1]; +extern void steal_context(void); +#endif + +/* + * Get a new mmu context for the address space described by `mm'. + */ +static inline void get_mmu_context(struct mm_struct *mm) +{ + unsigned long ctx; + + if (mm->context.id != NO_CONTEXT) + return; +#ifdef FEW_CONTEXTS + while (atomic_dec_if_positive(&nr_free_contexts) < 0) + steal_context(); +#endif + ctx = next_mmu_context; + while (test_and_set_bit(ctx, context_map)) { + ctx = find_next_zero_bit(context_map, LAST_CONTEXT+1, ctx); + if (ctx > LAST_CONTEXT) + ctx = 0; + } + next_mmu_context = (ctx + 1) & LAST_CONTEXT; + mm->context.id = ctx; +#ifdef FEW_CONTEXTS + context_mm[ctx] = mm; +#endif +} + +/* + * Set up the context for a new address space. + */ +static inline int init_new_context(struct task_struct *t, struct mm_struct *mm) +{ + mm->context.id = NO_CONTEXT; + mm->context.vdso_base = 0; + return 0; +} + +/* + * We're finished using the context for an address space. + */ +static inline void destroy_context(struct mm_struct *mm) +{ + preempt_disable(); + if (mm->context.id != NO_CONTEXT) { + clear_bit(mm->context.id, context_map); + mm->context.id = NO_CONTEXT; +#ifdef FEW_CONTEXTS + atomic_inc(&nr_free_contexts); +#endif + } + preempt_enable(); +} + +static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next, + struct task_struct *tsk) +{ +#ifdef CONFIG_ALTIVEC + if (cpu_has_feature(CPU_FTR_ALTIVEC)) + asm volatile ("dssall;\n" +#ifndef CONFIG_POWER4 + "sync;\n" /* G4 needs a sync here, G5 apparently not */ +#endif + : : ); +#endif /* CONFIG_ALTIVEC */ + + tsk->thread.pgdir = next->pgd; + + /* No need to flush userspace segments if the mm doesnt change */ + if (prev == next) + return; + + /* Setup new userspace context */ + get_mmu_context(next); + set_context(next->context.id, next->pgd); +} + +#define deactivate_mm(tsk,mm) do { } while (0) + +/* + * After we have set current->mm to a new value, this activates + * the context for the new mm so we see the new mappings. + */ +#define activate_mm(active_mm, mm) switch_mm(active_mm, mm, current) + +extern void mmu_context_init(void); + + #else #include #include #include -#include -#include -#include /* * Copyright (C) 2001 PPC 64 Team, IBM Corp diff --git a/include/asm-powerpc/mpc86xx.h b/include/asm-powerpc/mpc86xx.h index b85df45b1a84..15f650f987e7 100644 --- a/include/asm-powerpc/mpc86xx.h +++ b/include/asm-powerpc/mpc86xx.h @@ -19,12 +19,6 @@ #ifdef CONFIG_PPC_86xx -#define _IO_BASE isa_io_base -#define _ISA_MEM_BASE isa_mem_base -#ifdef CONFIG_PCI -#define PCI_DRAM_OFFSET pci_dram_offset -#endif - #define CPU0_BOOT_RELEASE 0x01000000 #define CPU1_BOOT_RELEASE 0x02000000 #define CPU_ALL_RELEASED (CPU0_BOOT_RELEASE | CPU1_BOOT_RELEASE) diff --git a/include/asm-powerpc/mpc8xx.h b/include/asm-powerpc/mpc8xx.h index 580371120e1a..2be014b6f57c 100644 --- a/include/asm-powerpc/mpc8xx.h +++ b/include/asm-powerpc/mpc8xx.h @@ -23,6 +23,10 @@ #include #endif +#ifdef CONFIG_PCMCIA_M8XX +extern struct mpc8xx_pcmcia_ops m8xx_pcmcia_ops; +#endif + #endif /* CONFIG_8xx */ #endif /* __CONFIG_8xx_DEFS */ #endif /* __KERNEL__ */ diff --git a/include/asm-powerpc/pci-bridge.h b/include/asm-powerpc/pci-bridge.h index d9bf5aba96cb..e72c2a60853c 100644 --- a/include/asm-powerpc/pci-bridge.h +++ b/include/asm-powerpc/pci-bridge.h @@ -2,12 +2,91 @@ #define _ASM_POWERPC_PCI_BRIDGE_H #ifdef __KERNEL__ -#ifndef CONFIG_PPC64 -#include -#else - #include #include +#include + +#ifndef CONFIG_PPC64 + +struct device_node; +struct pci_controller; + +/* + * Structure of a PCI controller (host bridge) + */ +struct pci_controller { + struct pci_bus *bus; + char is_dynamic; + void *arch_data; + struct list_head list_node; + struct device *parent; + + int first_busno; + int last_busno; + int self_busno; + + void __iomem *io_base_virt; + resource_size_t io_base_phys; + + /* Some machines (PReP) have a non 1:1 mapping of + * the PCI memory space in the CPU bus space + */ + resource_size_t pci_mem_offset; + + struct pci_ops *ops; + volatile unsigned int __iomem *cfg_addr; + volatile void __iomem *cfg_data; + + /* + * Used for variants of PCI indirect handling and possible quirks: + * SET_CFG_TYPE - used on 4xx or any PHB that does explicit type0/1 + * EXT_REG - provides access to PCI-e extended registers + * SURPRESS_PRIMARY_BUS - we surpress the setting of PCI_PRIMARY_BUS + * on Freescale PCI-e controllers since they used the PCI_PRIMARY_BUS + * to determine which bus number to match on when generating type0 + * config cycles + */ +#define PPC_INDIRECT_TYPE_SET_CFG_TYPE (0x00000001) +#define PPC_INDIRECT_TYPE_EXT_REG (0x00000002) +#define PPC_INDIRECT_TYPE_SURPRESS_PRIMARY_BUS (0x00000004) + u32 indirect_type; + + /* Currently, we limit ourselves to 1 IO range and 3 mem + * ranges since the common pci_bus structure can't handle more + */ + struct resource io_resource; + struct resource mem_resources[3]; + int global_number; /* PCI domain number */ +}; + +static inline struct pci_controller *pci_bus_to_host(struct pci_bus *bus) +{ + return bus->sysdata; +} + +/* These are used for config access before all the PCI probing + has been done. */ +int early_read_config_byte(struct pci_controller *hose, int bus, int dev_fn, + int where, u8 *val); +int early_read_config_word(struct pci_controller *hose, int bus, int dev_fn, + int where, u16 *val); +int early_read_config_dword(struct pci_controller *hose, int bus, int dev_fn, + int where, u32 *val); +int early_write_config_byte(struct pci_controller *hose, int bus, int dev_fn, + int where, u8 val); +int early_write_config_word(struct pci_controller *hose, int bus, int dev_fn, + int where, u16 val); +int early_write_config_dword(struct pci_controller *hose, int bus, int dev_fn, + int where, u32 val); + +extern void setup_indirect_pci_nomap(struct pci_controller* hose, + void __iomem *cfg_addr, void __iomem *cfg_data); +extern void setup_indirect_pci(struct pci_controller* hose, + u32 cfg_addr, u32 cfg_data); +extern void setup_grackle(struct pci_controller *hose); + +#else + /* * This program is free software; you can redistribute it and/or @@ -31,6 +110,7 @@ struct pci_controller { int last_busno; void __iomem *io_base_virt; + void *io_base_alloc; resource_size_t io_base_phys; /* Some machines have a non 1:1 mapping of @@ -48,8 +128,7 @@ struct pci_controller { */ struct resource io_resource; struct resource mem_resources[3]; - int global_number; - int local_number; + int global_number; unsigned long buid; unsigned long dma_window_base_cur; unsigned long dma_window_size; @@ -70,19 +149,22 @@ struct pci_dn { int devfn; /* pci device and function number */ int class_code; /* pci device class */ -#ifdef CONFIG_PPC_PSERIES + struct pci_controller *phb; /* for pci devices */ + struct iommu_table *iommu_table; /* for phb's or bridges */ + struct pci_dev *pcidev; /* back-pointer to the pci device */ + struct device_node *node; /* back-pointer to the device_node */ + + int pci_ext_config_space; /* for pci devices */ + +#ifdef CONFIG_EEH int eeh_mode; /* See eeh.h for possible EEH_MODEs */ int eeh_config_addr; int eeh_pe_config_addr; /* new-style partition endpoint address */ int eeh_check_count; /* # times driver ignored error */ int eeh_freeze_count; /* # times this device froze up. */ -#endif - int pci_ext_config_space; /* for pci devices */ - struct pci_controller *phb; /* for pci devices */ - struct iommu_table *iommu_table; /* for phb's or bridges */ - struct pci_dev *pcidev; /* back-pointer to the pci device */ - struct device_node *node; /* back-pointer to the device_node */ + int eeh_false_positives; /* # times this device reported #ff's */ u32 config_space[16]; /* saved PCI config space */ +#endif }; /* Get the pointer to a device_node's pci_dn */ @@ -128,9 +210,6 @@ static inline struct device_node *pci_bus_to_OF_node(struct pci_bus *bus) /** Find the bus corresponding to the indicated device node */ struct pci_bus * pcibios_find_pci_bus(struct device_node *dn); -extern void pci_process_bridge_OF_ranges(struct pci_controller *hose, - struct device_node *dev, int primary); - /** Remove all of the PCI devices under this bus */ void pcibios_remove_pci_devices(struct pci_bus *bus); @@ -148,21 +227,12 @@ static inline struct pci_controller *pci_bus_to_host(struct pci_bus *bus) return PCI_DN(busdn)->phb; } -extern struct pci_controller* -pci_find_hose_for_OF_device(struct device_node* node); - -extern struct pci_controller * -pcibios_alloc_controller(struct device_node *dev); extern void pcibios_free_controller(struct pci_controller *phb); -#ifdef CONFIG_PCI -extern unsigned long pci_address_to_pio(phys_addr_t address); -#else -static inline unsigned long pci_address_to_pio(phys_addr_t address) -{ - return (unsigned long)-1; -} -#endif +extern void isa_bridge_find_early(struct pci_controller *hose); + +extern int pcibios_unmap_io_space(struct pci_bus *bus); +extern int pcibios_map_io_space(struct pci_bus *bus); /* Return values for ppc_md.pci_probe_mode function */ #define PCI_PROBE_NONE -1 /* Don't look at this bus at all */ @@ -176,5 +246,29 @@ static inline unsigned long pci_address_to_pio(phys_addr_t address) #endif #endif /* CONFIG_PPC64 */ + +/* Get the PCI host controller for an OF device */ +extern struct pci_controller* +pci_find_hose_for_OF_device(struct device_node* node); + +/* Fill up host controller resources from the OF node */ +extern void +pci_process_bridge_OF_ranges(struct pci_controller *hose, + struct device_node *dev, int primary); + +/* Allocate a new PCI host bridge structure */ +extern struct pci_controller * +pcibios_alloc_controller(struct device_node *dev); +#ifdef CONFIG_PCI +extern unsigned long pci_address_to_pio(phys_addr_t address); +#else +static inline unsigned long pci_address_to_pio(phys_addr_t address) +{ + return (unsigned long)-1; +} +#endif + + + #endif /* __KERNEL__ */ #endif diff --git a/include/asm-powerpc/pci.h b/include/asm-powerpc/pci.h index ce0f13e8eb14..0cd3e77efd20 100644 --- a/include/asm-powerpc/pci.h +++ b/include/asm-powerpc/pci.h @@ -107,8 +107,6 @@ static inline void pci_dma_burst_advice(struct pci_dev *pdev, #define get_pci_dma_ops() NULL #endif -extern int pci_domain_nr(struct pci_bus *bus); - /* Decide whether to display the domain number in /proc */ extern int pci_proc_domain(struct pci_bus *bus); @@ -130,9 +128,6 @@ static inline void pci_dma_burst_advice(struct pci_dev *pdev, */ #define pci_dac_dma_supported(pci_dev, mask) (0) -/* Return the index of the PCI controller for device PDEV. */ -#define pci_domain_nr(bus) ((struct pci_controller *)(bus)->sysdata)->index - /* Set the name of the bus as it appears in /proc/bus/pci */ static inline int pci_proc_domain(struct pci_bus *bus) { @@ -141,6 +136,8 @@ static inline int pci_proc_domain(struct pci_bus *bus) #endif /* CONFIG_PPC64 */ +extern int pci_domain_nr(struct pci_bus *bus); + struct vm_area_struct; /* Map a range of PCI memory or I/O space for a device into user space */ int pci_mmap_page_range(struct pci_dev *pdev, struct vm_area_struct *vma, @@ -220,10 +217,6 @@ static inline struct resource *pcibios_select_root(struct pci_dev *pdev, return root; } -extern int unmap_bus_range(struct pci_bus *bus); - -extern int remap_bus_range(struct pci_bus *bus); - extern void pcibios_fixup_device_resources(struct pci_dev *dev, struct pci_bus *bus); diff --git a/include/asm-powerpc/pgtable-ppc32.h b/include/asm-powerpc/pgtable-ppc32.h index 7fb730c62f83..b2b880a6f631 100644 --- a/include/asm-powerpc/pgtable-ppc32.h +++ b/include/asm-powerpc/pgtable-ppc32.h @@ -6,11 +6,7 @@ #ifndef __ASSEMBLY__ #include #include -#include /* For TASK_SIZE */ -#include -#include #include /* For sub-arch specific PPC_PIN_SIZE */ -struct mm_struct; extern unsigned long va_to_phys(unsigned long address); extern pte_t *va_to_pte(unsigned long address); @@ -488,14 +484,6 @@ extern unsigned long bad_call_to_PMD_PAGE_SIZE(void); #define pfn_pte(pfn, prot) __pte(((pte_basic_t)(pfn) << PFN_SHIFT_OFFSET) |\ pgprot_val(prot)) #define mk_pte(page, prot) pfn_pte(page_to_pfn(page), prot) - -/* - * ZERO_PAGE is a global shared page that is always zero: used - * for zero-mapped memory areas etc.. - */ -extern unsigned long empty_zero_page[1024]; -#define ZERO_PAGE(vaddr) (virt_to_page(empty_zero_page)) - #endif /* __ASSEMBLY__ */ #define pte_none(pte) ((pte_val(pte) & ~_PTE_NONE_MASK) == 0) @@ -734,10 +722,6 @@ extern pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn, #define pte_unmap(pte) kunmap_atomic(pte, KM_PTE0) #define pte_unmap_nested(pte) kunmap_atomic(pte, KM_PTE1) -extern pgd_t swapper_pg_dir[PTRS_PER_PGD]; - -extern void paging_init(void); - /* * Encode and decode a swap entry. * Note that the bits we use in a PTE for representing a swap entry @@ -755,40 +739,6 @@ extern void paging_init(void); #define pte_to_pgoff(pte) (pte_val(pte) >> 3) #define pgoff_to_pte(off) ((pte_t) { ((off) << 3) | _PAGE_FILE }) -/* CONFIG_APUS */ -/* For virtual address to physical address conversion */ -extern void cache_clear(__u32 addr, int length); -extern void cache_push(__u32 addr, int length); -extern int mm_end_of_chunk (unsigned long addr, int len); -extern unsigned long iopa(unsigned long addr); -extern unsigned long mm_ptov(unsigned long addr) __attribute_const__; - -/* Values for nocacheflag and cmode */ -/* These are not used by the APUS kernel_map, but prevents - compilation errors. */ -#define KERNELMAP_FULL_CACHING 0 -#define KERNELMAP_NOCACHE_SER 1 -#define KERNELMAP_NOCACHE_NONSER 2 -#define KERNELMAP_NO_COPYBACK 3 - -/* - * Map some physical address range into the kernel address space. - */ -extern unsigned long kernel_map(unsigned long paddr, unsigned long size, - int nocacheflag, unsigned long *memavailp ); - -/* - * Set cache mode of (kernel space) address range. - */ -extern void kernel_set_cachemode (unsigned long address, unsigned long size, - unsigned int cmode); - -/* Needs to be defined here and not in linux/mm.h, as it is arch dependent */ -#define kern_addr_valid(addr) (1) - -#define io_remap_pfn_range(vma, vaddr, pfn, size, prot) \ - remap_pfn_range(vma, vaddr, pfn, size, prot) - /* * No page table caches to initialise */ diff --git a/include/asm-powerpc/pgtable-ppc64.h b/include/asm-powerpc/pgtable-ppc64.h index 3cfd98f44bfe..4ea99922a75c 100644 --- a/include/asm-powerpc/pgtable-ppc64.h +++ b/include/asm-powerpc/pgtable-ppc64.h @@ -7,11 +7,7 @@ #ifndef __ASSEMBLY__ #include -#include /* For TASK_SIZE */ -#include -#include #include -struct mm_struct; #endif /* __ASSEMBLY__ */ #ifdef CONFIG_PPC_64K_PAGES @@ -27,7 +23,7 @@ struct mm_struct; */ #define PGTABLE_EADDR_SIZE (PTE_INDEX_SIZE + PMD_INDEX_SIZE + \ PUD_INDEX_SIZE + PGD_INDEX_SIZE + PAGE_SHIFT) -#define PGTABLE_RANGE (1UL << PGTABLE_EADDR_SIZE) +#define PGTABLE_RANGE (ASM_CONST(1) << PGTABLE_EADDR_SIZE) #if TASK_SIZE_USER64 > PGTABLE_RANGE #error TASK_SIZE_USER64 exceeds pagetable range @@ -37,19 +33,28 @@ struct mm_struct; #error TASK_SIZE_USER64 exceeds user VSID range #endif + /* * Define the address range of the vmalloc VM area. */ #define VMALLOC_START ASM_CONST(0xD000000000000000) -#define VMALLOC_SIZE ASM_CONST(0x80000000000) +#define VMALLOC_SIZE (PGTABLE_RANGE >> 1) #define VMALLOC_END (VMALLOC_START + VMALLOC_SIZE) /* - * Define the address range of the imalloc VM area. + * Define the address ranges for MMIO and IO space : + * + * ISA_IO_BASE = VMALLOC_END, 64K reserved area + * PHB_IO_BASE = ISA_IO_BASE + 64K to ISA_IO_BASE + 2G, PHB IO spaces + * IOREMAP_BASE = ISA_IO_BASE + 2G to VMALLOC_START + PGTABLE_RANGE */ -#define PHBS_IO_BASE VMALLOC_END -#define IMALLOC_BASE (PHBS_IO_BASE + 0x80000000ul) /* Reserve 2 gigs for PHBs */ -#define IMALLOC_END (VMALLOC_START + PGTABLE_RANGE) +#define FULL_IO_SIZE 0x80000000ul +#define ISA_IO_BASE (VMALLOC_END) +#define ISA_IO_END (VMALLOC_END + 0x10000ul) +#define PHB_IO_BASE (ISA_IO_END) +#define PHB_IO_END (VMALLOC_END + FULL_IO_SIZE) +#define IOREMAP_BASE (PHB_IO_END) +#define IOREMAP_END (VMALLOC_START + PGTABLE_RANGE) /* * Region IDs @@ -134,16 +139,6 @@ struct mm_struct; #define __S110 PAGE_SHARED_X #define __S111 PAGE_SHARED_X -#ifndef __ASSEMBLY__ - -/* - * ZERO_PAGE is a global shared page that is always zero: used - * for zero-mapped memory areas etc.. - */ -extern unsigned long empty_zero_page[PAGE_SIZE/sizeof(unsigned long)]; -#define ZERO_PAGE(vaddr) (virt_to_page(empty_zero_page)) -#endif /* __ASSEMBLY__ */ - #ifdef CONFIG_HUGETLB_PAGE #define HAVE_ARCH_UNMAPPED_AREA @@ -442,10 +437,6 @@ extern pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn, #define pgd_ERROR(e) \ printk("%s:%d: bad pgd %08lx.\n", __FILE__, __LINE__, pgd_val(e)) -extern pgd_t swapper_pg_dir[]; - -extern void paging_init(void); - /* Encode and de-code a swap entry */ #define __swp_type(entry) (((entry).val >> 1) & 0x3f) #define __swp_offset(entry) ((entry).val >> 8) @@ -456,17 +447,6 @@ extern void paging_init(void); #define pgoff_to_pte(off) ((pte_t) {((off) << PTE_RPN_SHIFT)|_PAGE_FILE}) #define PTE_FILE_MAX_BITS (BITS_PER_LONG - PTE_RPN_SHIFT) -/* - * kern_addr_valid is intended to indicate whether an address is a valid - * kernel address. Most 32-bit archs define it as always true (like this) - * but most 64-bit archs actually perform a test. What should we do here? - * The only use is in fs/ncpfs/dir.c - */ -#define kern_addr_valid(addr) (1) - -#define io_remap_pfn_range(vma, vaddr, pfn, size, prot) \ - remap_pfn_range(vma, vaddr, pfn, size, prot) - void pgtable_cache_init(void); /* diff --git a/include/asm-powerpc/pgtable.h b/include/asm-powerpc/pgtable.h index 78bf4ae712a6..d18ffe7bc7c4 100644 --- a/include/asm-powerpc/pgtable.h +++ b/include/asm-powerpc/pgtable.h @@ -2,6 +2,13 @@ #define _ASM_POWERPC_PGTABLE_H #ifdef __KERNEL__ +#ifndef __ASSEMBLY__ +#include /* For TASK_SIZE */ +#include +#include +struct mm_struct; +#endif /* !__ASSEMBLY__ */ + #if defined(CONFIG_PPC64) # include #else @@ -9,6 +16,27 @@ #endif #ifndef __ASSEMBLY__ +/* + * ZERO_PAGE is a global shared page that is always zero: used + * for zero-mapped memory areas etc.. + */ +extern unsigned long empty_zero_page[]; +#define ZERO_PAGE(vaddr) (virt_to_page(empty_zero_page)) + +extern pgd_t swapper_pg_dir[]; + +extern void paging_init(void); + +/* + * kern_addr_valid is intended to indicate whether an address is a valid + * kernel address. Most 32-bit archs define it as always true (like this) + * but most 64-bit archs actually perform a test. What should we do here? + */ +#define kern_addr_valid(addr) (1) + +#define io_remap_pfn_range(vma, vaddr, pfn, size, prot) \ + remap_pfn_range(vma, vaddr, pfn, size, prot) + #include #endif /* __ASSEMBLY__ */ diff --git a/include/asm-powerpc/ppc-pci.h b/include/asm-powerpc/ppc-pci.h index 8e2005159ffd..b847aa10074b 100644 --- a/include/asm-powerpc/ppc-pci.h +++ b/include/asm-powerpc/ppc-pci.h @@ -26,7 +26,7 @@ extern int global_phb_number; extern void find_and_init_phbs(void); -extern struct pci_dev *ppc64_isabridge_dev; /* may be NULL if no ISA bus */ +extern struct pci_dev *isa_bridge_pcidev; /* may be NULL if no ISA bus */ /** Bus Unit ID macros; get low and hi 32-bits of the 64-bit BUID */ #define BUID_HI(buid) ((buid) >> 32) @@ -47,8 +47,8 @@ extern void init_pci_config_tokens (void); extern unsigned long get_phb_buid (struct device_node *); extern int rtas_setup_phb(struct pci_controller *phb); -/* From pSeries_pci.h */ -extern void pSeries_final_fixup(void); +/* From iSeries PCI */ +extern void iSeries_pcibios_init(void); extern unsigned long pci_probe_only; @@ -139,6 +139,9 @@ void eeh_clear_slot (struct device_node *dn, int mode_flag); */ struct device_node * find_device_pe(struct device_node *dn); +void eeh_sysfs_add_device(struct pci_dev *pdev); +void eeh_sysfs_remove_device(struct pci_dev *pdev); + #endif /* CONFIG_EEH */ #else /* CONFIG_PCI */ diff --git a/include/asm-powerpc/processor.h b/include/asm-powerpc/processor.h index d947b1609491..e28b10805159 100644 --- a/include/asm-powerpc/processor.h +++ b/include/asm-powerpc/processor.h @@ -43,14 +43,6 @@ extern int _chrp_type; /* what kind of prep workstation we are */ extern int _prep_type; -/* - * This is used to identify the board type from a given PReP board - * vendor. Board revision is also made available. This will be moved - * elsewhere soon - */ -extern unsigned char ucBoardRev; -extern unsigned char ucBoardRevMaj, ucBoardRevMin; - #endif /* CONFIG_PPC_PREP */ #endif /* defined(__KERNEL__) && defined(CONFIG_PPC32) */ diff --git a/include/asm-powerpc/prom.h b/include/asm-powerpc/prom.h index 6845af93ba91..1632baa17dc6 100644 --- a/include/asm-powerpc/prom.h +++ b/include/asm-powerpc/prom.h @@ -98,10 +98,19 @@ struct device_node { extern struct device_node *of_chosen; /* flag descriptions */ -#define OF_DYNAMIC 1 /* node and properties were allocated via kmalloc */ +#define OF_DYNAMIC 1 /* node and properties were allocated via kmalloc */ +#define OF_DETACHED 2 /* node has been detached from the device tree */ + +static inline int of_node_check_flag(struct device_node *n, unsigned long flag) +{ + return test_bit(flag, &n->_flags); +} + +static inline void of_node_set_flag(struct device_node *n, unsigned long flag) +{ + set_bit(flag, &n->_flags); +} -#define OF_IS_DYNAMIC(x) test_bit(OF_DYNAMIC, &x->_flags) -#define OF_MARK_DYNAMIC(x) set_bit(OF_DYNAMIC, &x->_flags) #define HAVE_ARCH_DEVTREE_FIXUPS @@ -124,6 +133,9 @@ extern struct device_node *of_find_node_by_type(struct device_node *from, dn = of_find_node_by_type(dn, type)) extern struct device_node *of_find_compatible_node(struct device_node *from, const char *type, const char *compat); +#define for_each_compatible_node(dn, type, compatible) \ + for (dn = of_find_compatible_node(NULL, type, compatible); dn; \ + dn = of_find_compatible_node(dn, type, compatible)) extern struct device_node *of_find_node_by_path(const char *path); extern struct device_node *of_find_node_by_phandle(phandle handle); extern struct device_node *of_find_all_nodes(struct device_node *prev); diff --git a/include/asm-powerpc/ps3.h b/include/asm-powerpc/ps3.h index 1e04651eedc4..a6f3f5ee7ca7 100644 --- a/include/asm-powerpc/ps3.h +++ b/include/asm-powerpc/ps3.h @@ -35,7 +35,8 @@ union ps3_firmware_version { }; }; -int ps3_get_firmware_version(union ps3_firmware_version *v); +void ps3_get_firmware_version(union ps3_firmware_version *v); +int ps3_compare_firmware_version(u16 major, u16 minor, u16 rev); /* 'Other OS' area */ @@ -48,18 +49,6 @@ enum ps3_param_av_multi_out { enum ps3_param_av_multi_out ps3_os_area_get_av_multi_out(void); -/** - * struct ps3_device_id - HV bus device identifier from the system repository - * @bus_id: HV bus id, {1..} (zero invalid) - * @dev_id: HV device id, {0..} - */ - -struct ps3_device_id { - unsigned int bus_id; - unsigned int dev_id; -}; - - /* dma routines */ enum ps3_dma_page_size { @@ -74,6 +63,8 @@ enum ps3_dma_region_type { PS3_DMA_INTERNAL = 2, }; +struct ps3_dma_region_ops; + /** * struct ps3_dma_region - A per device dma state variables structure * @did: The HV device id. @@ -81,21 +72,42 @@ enum ps3_dma_region_type { * @region_type: The HV region type. * @bus_addr: The 'translated' bus address of the region. * @len: The length in bytes of the region. + * @offset: The offset from the start of memory of the region. + * @ioid: The IOID of the device who owns this region * @chunk_list: Opaque variable used by the ioc page manager. + * @region_ops: struct ps3_dma_region_ops - dma region operations */ struct ps3_dma_region { - struct ps3_device_id did; + struct ps3_system_bus_device *dev; + /* device variables */ + const struct ps3_dma_region_ops *region_ops; + unsigned char ioid; enum ps3_dma_page_size page_size; enum ps3_dma_region_type region_type; - unsigned long bus_addr; unsigned long len; + unsigned long offset; + + /* driver variables (set by ps3_dma_region_create) */ + unsigned long bus_addr; struct { spinlock_t lock; struct list_head head; } chunk_list; }; +struct ps3_dma_region_ops { + int (*create)(struct ps3_dma_region *); + int (*free)(struct ps3_dma_region *); + int (*map)(struct ps3_dma_region *, + unsigned long virt_addr, + unsigned long len, + unsigned long *bus_addr, + u64 iopte_pp); + int (*unmap)(struct ps3_dma_region *, + unsigned long bus_addr, + unsigned long len); +}; /** * struct ps3_dma_region_init - Helper to initialize structure variables * @@ -103,18 +115,16 @@ struct ps3_dma_region { * ps3_system_bus_device_register. */ -static inline void ps3_dma_region_init(struct ps3_dma_region *r, - const struct ps3_device_id* did, enum ps3_dma_page_size page_size, - enum ps3_dma_region_type region_type) -{ - r->did = *did; - r->page_size = page_size; - r->region_type = region_type; -} +struct ps3_system_bus_device; + +int ps3_dma_region_init(struct ps3_system_bus_device *dev, + struct ps3_dma_region *r, enum ps3_dma_page_size page_size, + enum ps3_dma_region_type region_type, void *addr, unsigned long len); int ps3_dma_region_create(struct ps3_dma_region *r); int ps3_dma_region_free(struct ps3_dma_region *r); int ps3_dma_map(struct ps3_dma_region *r, unsigned long virt_addr, - unsigned long len, unsigned long *bus_addr); + unsigned long len, unsigned long *bus_addr, + u64 iopte_pp); int ps3_dma_unmap(struct ps3_dma_region *r, unsigned long bus_addr, unsigned long len); @@ -125,6 +135,7 @@ enum ps3_mmio_page_size { PS3_MMIO_64K = 16U }; +struct ps3_mmio_region_ops; /** * struct ps3_mmio_region - a per device mmio state variables structure * @@ -132,13 +143,18 @@ enum ps3_mmio_page_size { */ struct ps3_mmio_region { - struct ps3_device_id did; + struct ps3_system_bus_device *dev; + const struct ps3_mmio_region_ops *mmio_ops; unsigned long bus_addr; unsigned long len; enum ps3_mmio_page_size page_size; unsigned long lpar_addr; }; +struct ps3_mmio_region_ops { + int (*create)(struct ps3_mmio_region *); + int (*free)(struct ps3_mmio_region *); +}; /** * struct ps3_mmio_region_init - Helper to initialize structure variables * @@ -146,15 +162,9 @@ struct ps3_mmio_region { * ps3_system_bus_device_register. */ -static inline void ps3_mmio_region_init(struct ps3_mmio_region *r, - const struct ps3_device_id* did, unsigned long bus_addr, - unsigned long len, enum ps3_mmio_page_size page_size) -{ - r->did = *did; - r->bus_addr = bus_addr; - r->len = len; - r->page_size = page_size; -} +int ps3_mmio_region_init(struct ps3_system_bus_device *dev, + struct ps3_mmio_region *r, unsigned long bus_addr, unsigned long len, + enum ps3_mmio_page_size page_size); int ps3_mmio_region_create(struct ps3_mmio_region *r); int ps3_free_mmio_region(struct ps3_mmio_region *r); unsigned long ps3_mm_phys_to_lpar(unsigned long phys_addr); @@ -187,11 +197,10 @@ int ps3_spe_irq_setup(enum ps3_cpu_binding cpu, unsigned long spe_id, unsigned int class, unsigned int *virq); int ps3_spe_irq_destroy(unsigned int virq); -int ps3_sb_event_receive_port_setup(enum ps3_cpu_binding cpu, - const struct ps3_device_id *did, unsigned int interrupt_id, - unsigned int *virq); -int ps3_sb_event_receive_port_destroy(const struct ps3_device_id *did, - unsigned int interrupt_id, unsigned int virq); +int ps3_sb_event_receive_port_setup(struct ps3_system_bus_device *dev, + enum ps3_cpu_binding cpu, unsigned int *virq); +int ps3_sb_event_receive_port_destroy(struct ps3_system_bus_device *dev, + unsigned int virq); /* lv1 result codes */ @@ -289,11 +298,33 @@ static inline const char* ps3_result(int result) /* system bus routines */ enum ps3_match_id { - PS3_MATCH_ID_EHCI = 1, - PS3_MATCH_ID_OHCI, - PS3_MATCH_ID_GELIC, - PS3_MATCH_ID_AV_SETTINGS, - PS3_MATCH_ID_SYSTEM_MANAGER, + PS3_MATCH_ID_EHCI = 1, + PS3_MATCH_ID_OHCI = 2, + PS3_MATCH_ID_GELIC = 3, + PS3_MATCH_ID_AV_SETTINGS = 4, + PS3_MATCH_ID_SYSTEM_MANAGER = 5, + PS3_MATCH_ID_STOR_DISK = 6, + PS3_MATCH_ID_STOR_ROM = 7, + PS3_MATCH_ID_STOR_FLASH = 8, + PS3_MATCH_ID_SOUND = 9, + PS3_MATCH_ID_GRAPHICS = 10, +}; + +#define PS3_MODULE_ALIAS_EHCI "ps3:1" +#define PS3_MODULE_ALIAS_OHCI "ps3:2" +#define PS3_MODULE_ALIAS_GELIC "ps3:3" +#define PS3_MODULE_ALIAS_AV_SETTINGS "ps3:4" +#define PS3_MODULE_ALIAS_SYSTEM_MANAGER "ps3:5" +#define PS3_MODULE_ALIAS_STOR_DISK "ps3:6" +#define PS3_MODULE_ALIAS_STOR_ROM "ps3:7" +#define PS3_MODULE_ALIAS_STOR_FLASH "ps3:8" +#define PS3_MODULE_ALIAS_SOUND "ps3:9" +#define PS3_MODULE_ALIAS_GRAPHICS "ps3:10" + +enum ps3_system_bus_device_type { + PS3_DEVICE_TYPE_IOC0 = 1, + PS3_DEVICE_TYPE_SB, + PS3_DEVICE_TYPE_VUART, }; /** @@ -302,14 +333,23 @@ enum ps3_match_id { struct ps3_system_bus_device { enum ps3_match_id match_id; - struct ps3_device_id did; - unsigned int interrupt_id; -/* struct iommu_table *iommu_table; -- waiting for Ben's cleanups */ - struct ps3_dma_region *d_region; - struct ps3_mmio_region *m_region; + enum ps3_system_bus_device_type dev_type; + + unsigned int bus_id; /* SB */ + unsigned int dev_id; /* SB */ + unsigned int interrupt_id; /* SB */ + struct ps3_dma_region *d_region; /* SB, IOC0 */ + struct ps3_mmio_region *m_region; /* SB, IOC0*/ + unsigned int port_number; /* VUART */ + +/* struct iommu_table *iommu_table; -- waiting for BenH's cleanups */ struct device core; + void *driver_priv; /* private driver variables */ }; +int ps3_open_hv_device(struct ps3_system_bus_device *dev); +int ps3_close_hv_device(struct ps3_system_bus_device *dev); + /** * struct ps3_system_bus_driver - a driver for a device on the system bus */ @@ -319,6 +359,7 @@ struct ps3_system_bus_driver { struct device_driver core; int (*probe)(struct ps3_system_bus_device *); int (*remove)(struct ps3_system_bus_device *); + int (*shutdown)(struct ps3_system_bus_device *); /* int (*suspend)(struct ps3_system_bus_device *, pm_message_t); */ /* int (*resume)(struct ps3_system_bus_device *); */ }; @@ -326,16 +367,24 @@ struct ps3_system_bus_driver { int ps3_system_bus_device_register(struct ps3_system_bus_device *dev); int ps3_system_bus_driver_register(struct ps3_system_bus_driver *drv); void ps3_system_bus_driver_unregister(struct ps3_system_bus_driver *drv); -static inline struct ps3_system_bus_driver *to_ps3_system_bus_driver( + +static inline struct ps3_system_bus_driver *ps3_drv_to_system_bus_drv( struct device_driver *_drv) { return container_of(_drv, struct ps3_system_bus_driver, core); } -static inline struct ps3_system_bus_device *to_ps3_system_bus_device( +static inline struct ps3_system_bus_device *ps3_dev_to_system_bus_dev( struct device *_dev) { return container_of(_dev, struct ps3_system_bus_device, core); } +static inline struct ps3_system_bus_driver * + ps3_system_bus_dev_to_system_bus_drv(struct ps3_system_bus_device *_dev) +{ + BUG_ON(!_dev); + BUG_ON(!_dev->core.driver); + return ps3_drv_to_system_bus_drv(_dev->core.driver); +} /** * ps3_system_bus_set_drvdata - @@ -358,32 +407,17 @@ static inline void *ps3_system_bus_get_driver_data( extern struct bus_type ps3_system_bus_type; -/* vuart routines */ - -struct ps3_vuart_port_priv; - -/** - * struct ps3_vuart_port_device - a device on a vuart port - */ - -struct ps3_vuart_port_device { - enum ps3_match_id match_id; - struct device core; - struct ps3_vuart_port_priv* priv; /* private driver variables */ - -}; - -int ps3_vuart_port_device_register(struct ps3_vuart_port_device *dev); - /* system manager */ -#ifdef CONFIG_PS3_SYS_MANAGER -void ps3_sys_manager_restart(void); +struct ps3_sys_manager_ops { + struct ps3_system_bus_device *dev; + void (*power_off)(struct ps3_system_bus_device *dev); + void (*restart)(struct ps3_system_bus_device *dev); +}; + +void ps3_sys_manager_register_ops(const struct ps3_sys_manager_ops *ops); void ps3_sys_manager_power_off(void); -#else -static inline void ps3_sys_manager_restart(void) {} -static inline void ps3_sys_manager_power_off(void) {} -#endif +void ps3_sys_manager_restart(void); struct ps3_prealloc { const char *name; @@ -393,5 +427,7 @@ struct ps3_prealloc { }; extern struct ps3_prealloc ps3fb_videomemory; +extern struct ps3_prealloc ps3flash_bounce_buffer; + #endif diff --git a/include/asm-powerpc/ps3av.h b/include/asm-powerpc/ps3av.h index 9efc40f1c778..7df4250802de 100644 --- a/include/asm-powerpc/ps3av.h +++ b/include/asm-powerpc/ps3av.h @@ -1,20 +1,23 @@ /* - * Copyright (C) 2006 Sony Computer Entertainment Inc. - * Copyright 2006, 2007 Sony Corporation + * PS3 AV backend support. * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published - * by the Free Software Foundation; version 2 of the License. + * Copyright (C) 2007 Sony Computer Entertainment Inc. + * Copyright 2007 Sony Corp. * - * 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. + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. * - * 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., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * 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 */ + #ifndef _ASM_POWERPC_PS3AV_H_ #define _ASM_POWERPC_PS3AV_H_ @@ -159,6 +162,9 @@ #define PS3AV_CMD_VIDEO_FMT_X8R8G8B8 0x0000 /* video_out_format */ #define PS3AV_CMD_VIDEO_OUT_FORMAT_RGB_12BIT 0x0000 +/* video_cl_cnv */ +#define PS3AV_CMD_VIDEO_CL_CNV_ENABLE_LUT 0x0000 +#define PS3AV_CMD_VIDEO_CL_CNV_DISABLE_LUT 0x0010 /* video_sync */ #define PS3AV_CMD_VIDEO_SYNC_VSYNC 0x0001 #define PS3AV_CMD_VIDEO_SYNC_CSYNC 0x0004 @@ -311,6 +317,8 @@ #define PS3AV_MODE_MASK 0x000F #define PS3AV_MODE_HDCP_OFF 0x1000 /* Retail PS3 product doesn't support this */ #define PS3AV_MODE_DITHER 0x0800 +#define PS3AV_MODE_COLOR 0x0400 +#define PS3AV_MODE_WHITE 0x0200 #define PS3AV_MODE_FULL 0x0080 #define PS3AV_MODE_DVI 0x0040 #define PS3AV_MODE_RGB 0x0020 @@ -529,9 +537,9 @@ struct ps3av_pkt_video_mode { u32 video_out_format; /* in: out format */ u32 video_format; /* in: input frame buffer format */ u8 reserved3; - u8 reserved4; + u8 video_cl_cnv; /* in: color conversion */ u16 video_order; /* in: input RGB order */ - u32 reserved5; + u32 reserved4; }; /* video: format */ @@ -539,7 +547,8 @@ struct ps3av_pkt_video_format { struct ps3av_send_hdr send_hdr; u32 video_head; /* in: head */ u32 video_format; /* in: frame buffer format */ - u16 reserved; + u8 reserved; + u8 video_cl_cnv; /* in: color conversion */ u16 video_order; /* in: input RGB order */ }; @@ -698,12 +707,6 @@ static inline void ps3av_cmd_av_monitor_info_dump(const struct ps3av_pkt_av_get_ extern int ps3av_cmd_video_get_monitor_info(struct ps3av_pkt_av_get_monitor_info *, u32); -struct ps3_vuart_port_device; -extern int ps3av_vuart_write(struct ps3_vuart_port_device *dev, - const void *buf, unsigned long size); -extern int ps3av_vuart_read(struct ps3_vuart_port_device *dev, void *buf, - unsigned long size, int timeout); - extern int ps3av_set_video_mode(u32, int); extern int ps3av_set_audio_mode(u32, u32, u32, u32, u32); extern int ps3av_get_auto_mode(int); @@ -716,5 +719,8 @@ extern int ps3av_video_mute(int); extern int ps3av_audio_mute(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/include/asm-powerpc/ps3fb.h b/include/asm-powerpc/ps3fb.h index ad81cf431964..3f121fe4010d 100644 --- a/include/asm-powerpc/ps3fb.h +++ b/include/asm-powerpc/ps3fb.h @@ -41,16 +41,4 @@ struct ps3fb_ioctl_res { __u32 num_frames; /* num of frame buffers */ }; -#ifdef __KERNEL__ - -#ifdef CONFIG_FB_PS3 -extern void ps3fb_flip_ctl(int on); -extern void ps3fb_cleanup(void); -#else -static inline void ps3fb_flip_ctl(int on) {} -static inline void ps3fb_cleanup(void) {} -#endif - -#endif /* __KERNEL__ */ - #endif /* _ASM_POWERPC_PS3FB_H_ */ diff --git a/include/asm-powerpc/ps3stor.h b/include/asm-powerpc/ps3stor.h new file mode 100644 index 000000000000..6fcaf714fa50 --- /dev/null +++ b/include/asm-powerpc/ps3stor.h @@ -0,0 +1,71 @@ +/* + * PS3 Storage Devices + * + * Copyright (C) 2007 Sony Computer Entertainment Inc. + * Copyright 2007 Sony Corp. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published + * by the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef _ASM_POWERPC_PS3STOR_H_ +#define _ASM_POWERPC_PS3STOR_H_ + +#include + +#include + + +struct ps3_storage_region { + unsigned int id; + u64 start; + u64 size; +}; + +struct ps3_storage_device { + struct ps3_system_bus_device sbd; + + struct ps3_dma_region dma_region; + unsigned int irq; + u64 blk_size; + + u64 tag; + u64 lv1_status; + struct completion done; + + unsigned long bounce_size; + void *bounce_buf; + u64 bounce_lpar; + dma_addr_t bounce_dma; + + unsigned int num_regions; + unsigned long accessible_regions; + unsigned int region_idx; /* first accessible region */ + struct ps3_storage_region regions[0]; /* Must be last */ +}; + +static inline struct ps3_storage_device *to_ps3_storage_device(struct device *dev) +{ + return container_of(dev, struct ps3_storage_device, sbd.core); +} + +extern int ps3stor_setup(struct ps3_storage_device *dev, + irq_handler_t handler); +extern void ps3stor_teardown(struct ps3_storage_device *dev); +extern u64 ps3stor_read_write_sectors(struct ps3_storage_device *dev, u64 lpar, + u64 start_sector, u64 sectors, + int write); +extern u64 ps3stor_send_command(struct ps3_storage_device *dev, u64 cmd, + u64 arg1, u64 arg2, u64 arg3, u64 arg4); + +#endif /* _ASM_POWERPC_PS3STOR_H_ */ diff --git a/include/asm-powerpc/ptrace.h b/include/asm-powerpc/ptrace.h index 4ad77a13f865..13fccc5a4119 100644 --- a/include/asm-powerpc/ptrace.h +++ b/include/asm-powerpc/ptrace.h @@ -92,6 +92,11 @@ extern unsigned long profile_pc(struct pt_regs *regs); set_thread_flag(TIF_NOERROR); \ } while(0) +struct task_struct; +extern unsigned long ptrace_get_reg(struct task_struct *task, int regno); +extern int ptrace_put_reg(struct task_struct *task, int regno, + unsigned long data); + /* * We use the least-significant bit of the trap field to indicate * whether we have saved the full set of registers, or only a @@ -158,9 +163,7 @@ do { \ #define PT_NIP 32 #define PT_MSR 33 -#ifdef __KERNEL__ #define PT_ORIG_R3 34 -#endif #define PT_CTR 35 #define PT_LNK 36 #define PT_XER 37 @@ -169,11 +172,12 @@ do { \ #define PT_MQ 39 #else #define PT_SOFTE 39 +#endif #define PT_TRAP 40 #define PT_DAR 41 #define PT_DSISR 42 #define PT_RESULT 43 -#endif +#define PT_REGS_COUNT 44 #define PT_FPR0 48 /* each FP reg occupies 2 slots in this space */ @@ -229,7 +233,17 @@ do { \ #define PTRACE_GET_DEBUGREG 25 #define PTRACE_SET_DEBUGREG 26 -/* Additional PTRACE requests implemented on PowerPC. */ +/* (new) PTRACE requests using the same numbers as x86 and the same + * argument ordering. Additionally, they support more registers too + */ +#define PTRACE_GETREGS 12 +#define PTRACE_SETREGS 13 +#define PTRACE_GETFPREGS 14 +#define PTRACE_SETFPREGS 15 +#define PTRACE_GETREGS64 22 +#define PTRACE_SETREGS64 23 + +/* (old) PTRACE requests with inverted arguments */ #define PPC_PTRACE_GETREGS 0x99 /* Get GPRs 0 - 31 */ #define PPC_PTRACE_SETREGS 0x98 /* Set GPRs 0 - 31 */ #define PPC_PTRACE_GETFPREGS 0x97 /* Get FPRs 0 - 31 */ diff --git a/include/asm-powerpc/reg.h b/include/asm-powerpc/reg.h index 749c7f953b58..281011e953ec 100644 --- a/include/asm-powerpc/reg.h +++ b/include/asm-powerpc/reg.h @@ -453,6 +453,8 @@ #define SPRN_MMCRA 0x312 #define MMCRA_SIHV 0x10000000UL /* state of MSR HV when SIAR set */ #define MMCRA_SIPR 0x08000000UL /* state of MSR PR when SIAR set */ +#define MMCRA_SLOT 0x07000000UL /* SLOT bits (37-39) */ +#define MMCRA_SLOT_SHIFT 24 #define MMCRA_SAMPLE_ENABLE 0x00000001UL /* enable sampling */ #define POWER6_MMCRA_SIHV 0x0000040000000000ULL #define POWER6_MMCRA_SIPR 0x0000020000000000ULL diff --git a/include/asm-powerpc/spu.h b/include/asm-powerpc/spu.h index 31d5054be20f..eedc828cef2d 100644 --- a/include/asm-powerpc/spu.h +++ b/include/asm-powerpc/spu.h @@ -106,6 +106,14 @@ struct spu_context; struct spu_runqueue; struct device_node; +enum spu_utilization_state { + SPU_UTIL_SYSTEM, + SPU_UTIL_USER, + SPU_UTIL_IOWAIT, + SPU_UTIL_IDLE, + SPU_UTIL_MAX +}; + struct spu { const char *name; unsigned long local_store_phys; @@ -156,6 +164,21 @@ struct spu { u64 shadow_int_mask_RW[3]; struct sys_device sysdev; + + struct { + /* protected by interrupt reentrancy */ + enum spu_utilization_state utilization_state; + unsigned long tstamp; /* time of last ctx switch */ + unsigned long times[SPU_UTIL_MAX]; + unsigned long long vol_ctx_switch; + unsigned long long invol_ctx_switch; + unsigned long long min_flt; + unsigned long long maj_flt; + unsigned long long hash_flt; + unsigned long long slb_flt; + unsigned long long class2_intr; + unsigned long long libassist; + } stats; }; struct spu *spu_alloc(void); @@ -448,6 +471,7 @@ struct spu_priv1 { #define MFC_STATE1_PROBLEM_STATE_MASK 0x08ull #define MFC_STATE1_RELOCATE_MASK 0x10ull #define MFC_STATE1_MASTER_RUN_CONTROL_MASK 0x20ull +#define MFC_STATE1_TABLE_SEARCH_MASK 0x40ull u64 mfc_lpid_RW; /* 0x008 */ u64 spu_idr_RW; /* 0x010 */ u64 mfc_vr_RO; /* 0x018 */ diff --git a/include/asm-powerpc/syscalls.h b/include/asm-powerpc/syscalls.h index c2fe79d4f90f..b3ca41fc8bb1 100644 --- a/include/asm-powerpc/syscalls.h +++ b/include/asm-powerpc/syscalls.h @@ -43,16 +43,9 @@ asmlinkage long ppc_newuname(struct new_utsname __user * name); asmlinkage long sys_rt_sigsuspend(sigset_t __user *unewset, size_t sigsetsize); - -#ifndef __powerpc64__ -asmlinkage long sys_sigaltstack(const stack_t __user *uss, - stack_t __user *uoss, int r5, int r6, int r7, int r8, - struct pt_regs *regs); -#else /* __powerpc64__ */ asmlinkage long sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss, unsigned long r5, unsigned long r6, unsigned long r7, unsigned long r8, struct pt_regs *regs); -#endif /* __powerpc64__ */ #endif /* __KERNEL__ */ #endif /* __ASM_POWERPC_SYSCALLS_H */ diff --git a/include/asm-powerpc/system.h b/include/asm-powerpc/system.h index 09621f611dbc..32aa42b748be 100644 --- a/include/asm-powerpc/system.h +++ b/include/asm-powerpc/system.h @@ -43,7 +43,7 @@ #ifdef CONFIG_SMP #define smp_mb() mb() #define smp_rmb() rmb() -#define smp_wmb() __asm__ __volatile__ ("eieio" : : : "memory") +#define smp_wmb() eieio() #define smp_read_barrier_depends() read_barrier_depends() #else #define smp_mb() barrier() @@ -559,5 +559,7 @@ static inline void create_function_call(unsigned long addr, void * func) extern void account_system_vtime(struct task_struct *); #endif +extern struct dentry *powerpc_debugfs_root; + #endif /* __KERNEL__ */ #endif /* _ASM_POWERPC_SYSTEM_H */ diff --git a/include/asm-powerpc/termbits.h b/include/asm-powerpc/termbits.h index 5e79198f7d18..6698188ca550 100644 --- a/include/asm-powerpc/termbits.h +++ b/include/asm-powerpc/termbits.h @@ -152,6 +152,10 @@ struct ktermios { #define B3000000 00034 #define B3500000 00035 #define B4000000 00036 +#define BOTHER 00037 + +#define CIBAUD 077600000 +#define IBSHIFT 16 /* Shift from CBAUD to CIBAUD */ #define CSIZE 00001400 #define CS5 00000000 diff --git a/include/asm-powerpc/thread_info.h b/include/asm-powerpc/thread_info.h index 3f32ca8bfec9..9d9aeca8ad22 100644 --- a/include/asm-powerpc/thread_info.h +++ b/include/asm-powerpc/thread_info.h @@ -113,8 +113,8 @@ static inline struct thread_info *current_thread_info(void) #define TIF_POLLING_NRFLAG 4 /* true if poll_idle() is polling TIF_NEED_RESCHED */ #define TIF_32BIT 5 /* 32 bit binary */ -#define TIF_RUNLATCH 6 /* Is the runlatch enabled? */ -#define TIF_ABI_PENDING 7 /* 32/64 bit switch needed */ +#define TIF_PERFMON_WORK 6 /* work for pfm_handle_work() */ +#define TIF_PERFMON_CTXSW 7 /* perfmon needs ctxsw calls */ #define TIF_SYSCALL_AUDIT 8 /* syscall auditing active */ #define TIF_SINGLESTEP 9 /* singlestepping active */ #define TIF_MEMDIE 10 @@ -123,6 +123,8 @@ static inline struct thread_info *current_thread_info(void) #define TIF_NOERROR 14 /* Force successful syscall return */ #define TIF_RESTORE_SIGMASK 15 /* Restore signal mask in do_signal */ #define TIF_FREEZE 16 /* Freezing for suspend */ +#define TIF_RUNLATCH 17 /* Is the runlatch enabled? */ +#define TIF_ABI_PENDING 18 /* 32/64 bit switch needed */ /* as above, but as bit values */ #define _TIF_SYSCALL_TRACE (1<addr; - unsigned long end = addr + area->size; + unsigned long start = addr; + unsigned long end = addr + size; BUG_ON(addr >= end); pgd = pgd_offset_k(addr); @@ -84,7 +84,12 @@ void unmap_vm_area(struct vm_struct *area) continue; vunmap_pud_range(pgd, addr, next); } while (pgd++, addr = next, addr != end); - flush_tlb_kernel_range((unsigned long) area->addr, end); + flush_tlb_kernel_range(start, end); +} + +static void unmap_vm_area(struct vm_struct *area) +{ + unmap_kernel_range((unsigned long)area->addr, area->size); } static int vmap_pte_range(pmd_t *pmd, unsigned long addr,