diff options
author | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2008-02-07 09:02:26 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2008-02-07 09:02:26 -0800 |
commit | 37969581301e50872a1ae84dc73962b5f7ee6b76 (patch) | |
tree | 35bbd6a07f903f248f4bd209b8d194a922415fe0 | |
parent | 80ff8a805113850a3ffafcc8e6eaa1fdd51b02f3 (diff) | |
parent | 24f1a849614ba1805e26a05da7cc8c6bd67490ea (diff) |
Merge branch 'for-2.6.25' of git://git.kernel.org/pub/scm/linux/kernel/git/paulus/powerpc
* 'for-2.6.25' of git://git.kernel.org/pub/scm/linux/kernel/git/paulus/powerpc: (69 commits)
[POWERPC] Add SPE registers to core dumps
[POWERPC] Use regset code for compat PTRACE_*REGS* calls
[POWERPC] Use generic compat_sys_ptrace
[POWERPC] Use generic compat_ptrace_request
[POWERPC] Use generic ptrace peekdata/pokedata
[POWERPC] Use regset code for PTRACE_*REGS* requests
[POWERPC] Switch to generic compat_binfmt_elf code
[POWERPC] Switch to using user_regset-based core dumps
[POWERPC] Add user_regset compat support
[POWERPC] Add user_regset_view definitions
[POWERPC] Use user_regset accessors for GPRs
[POWERPC] ptrace accessors for special regs MSR and TRAP
[POWERPC] Use user_regset accessors for SPE regs
[POWERPC] Use user_regset accessors for altivec regs
[POWERPC] Use user_regset accessors for FP regs
[POWERPC] mpc52xx: fix compile error introduce when rebasing patch
[POWERPC] 4xx: PCIe indirect DCR spinlock fix.
[POWERPC] Add missing native dcr dcr_ind_lock spinlock
[POWERPC] 4xx: Fix offset value on Warp board
[POWERPC] 4xx: Add 440EPx Sequoia ehci dts entry
...
102 files changed, 4173 insertions, 969 deletions
diff --git a/Documentation/powerpc/booting-without-of.txt b/Documentation/powerpc/booting-without-of.txt index b5e46efeba8..7b4e8a70882 100644 --- a/Documentation/powerpc/booting-without-of.txt +++ b/Documentation/powerpc/booting-without-of.txt @@ -57,6 +57,7 @@ Table of Contents n) 4xx/Axon EMAC ethernet nodes o) Xilinx IP cores p) Freescale Synchronous Serial Interface + q) USB EHCI controllers VII - Specifying interrupt information for devices 1) interrupts property @@ -2577,6 +2578,20 @@ platforms are moved over to use the flattened-device-tree model. Requred properties: - current-speed : Baud rate of uartlite + v) Xilinx hwicap + + Xilinx hwicap devices provide access to the configuration logic + of the FPGA through the Internal Configuration Access Port + (ICAP). The ICAP enables partial reconfiguration of the FPGA, + readback of the configuration information, and some control over + 'warm boots' of the FPGA fabric. + + Required properties: + - xlnx,family : The family of the FPGA, necessary since the + capabilities of the underlying ICAP hardware + differ between different families. May be + 'virtex2p', 'virtex4', or 'virtex5'. + p) Freescale Synchronous Serial Interface The SSI is a serial device that communicates with audio codecs. It can @@ -2775,6 +2790,33 @@ platforms are moved over to use the flattened-device-tree model. interrupt-parent = < &ipic >; }; + q) USB EHCI controllers + + Required properties: + - compatible : should be "usb-ehci". + - reg : should contain at least address and length of the standard EHCI + register set for the device. Optional platform-dependent registers + (debug-port or other) can be also specified here, but only after + definition of standard EHCI registers. + - interrupts : one EHCI interrupt should be described here. + If device registers are implemented in big endian mode, the device + node should have "big-endian-regs" property. + If controller implementation operates with big endian descriptors, + "big-endian-desc" property should be specified. + If both big endian registers and descriptors are used by the controller + implementation, "big-endian" property can be specified instead of having + both "big-endian-regs" and "big-endian-desc". + + Example (Sequoia 440EPx): + ehci@e0000300 { + compatible = "ibm,usb-ehci-440epx", "usb-ehci"; + interrupt-parent = <&UIC0>; + interrupts = <1a 4>; + reg = <0 e0000300 90 0 e0000390 70>; + big-endian; + }; + + 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 cf030b00441..8dcac0b22d6 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -97,6 +97,7 @@ config EARLY_PRINTK config COMPAT bool default y if PPC64 + select COMPAT_BINFMT_ELF config SYSVIPC_COMPAT bool @@ -438,25 +439,6 @@ config WANT_DEVICE_TREE bool default n -config DEVICE_TREE - string "Static device tree source file" - depends on WANT_DEVICE_TREE - help - This specifies the device tree source (.dts) file to be - compiled and included when building the bootwrapper. If a - relative filename is given, then it will be relative to - arch/powerpc/boot/dts. If you are not using the bootwrapper, - or do not need to build a dts into the bootwrapper, this - field is ignored. - - For example, this is required when building a cuImage target - for an older U-Boot, which cannot pass a device tree itself. - Such a kernel will not work with a newer U-Boot that tries to - pass a device tree (unless you tell it not to). If your U-Boot - does not mention a device tree in "help bootm", then use the - cuImage target and specify a device tree here. Otherwise, use - the uImage target and leave this field blank. - endmenu config ISA_DMA_API @@ -512,7 +494,7 @@ config PCI bool "PCI support" if 40x || CPM2 || PPC_83xx || PPC_85xx || PPC_86xx \ || PPC_MPC52xx || (EMBEDDED && (PPC_PSERIES || PPC_ISERIES)) \ || PPC_PS3 || 44x - default y if !40x && !CPM2 && !8xx && !PPC_83xx \ + default y if !40x && !CPM2 && !8xx && !PPC_MPC512x && !PPC_83xx \ && !PPC_85xx && !PPC_86xx default PCI_PERMEDIA if !4xx && !CPM2 && !8xx default PCI_QSPAN if !4xx && !CPM2 && 8xx diff --git a/arch/powerpc/Makefile b/arch/powerpc/Makefile index f70df9b64f8..6845482f009 100644 --- a/arch/powerpc/Makefile +++ b/arch/powerpc/Makefile @@ -151,14 +151,11 @@ core-$(CONFIG_XMON) += arch/powerpc/xmon/ drivers-$(CONFIG_OPROFILE) += arch/powerpc/oprofile/ # Default to zImage, override when needed -defaultimage-y := zImage -defaultimage-$(CONFIG_DEFAULT_UIMAGE) := uImage -KBUILD_IMAGE := $(defaultimage-y) -all: $(KBUILD_IMAGE) +all: zImage CPPFLAGS_vmlinux.lds := -Upowerpc -BOOT_TARGETS = zImage zImage.initrd uImage +BOOT_TARGETS = zImage zImage.initrd uImage treeImage.% cuImage.% PHONY += $(BOOT_TARGETS) @@ -180,7 +177,7 @@ define archhelp endef install: vdso_install - $(Q)$(MAKE) $(build)=$(boot) BOOTIMAGE=$(KBUILD_IMAGE) install + $(Q)$(MAKE) $(build)=$(boot) install vdso_install: ifeq ($(CONFIG_PPC64),y) diff --git a/arch/powerpc/boot/Makefile b/arch/powerpc/boot/Makefile index 122a2707899..49797a45416 100644 --- a/arch/powerpc/boot/Makefile +++ b/arch/powerpc/boot/Makefile @@ -60,8 +60,9 @@ src-wlib := string.S crt0.S stdio.c main.c \ src-plat := of.c cuboot-52xx.c cuboot-824x.c cuboot-83xx.c cuboot-85xx.c holly.c \ cuboot-ebony.c treeboot-ebony.c prpmc2800.c \ ps3-head.S ps3-hvcall.S ps3.c treeboot-bamboo.c cuboot-8xx.c \ - cuboot-pq2.c cuboot-sequoia.c treeboot-walnut.c cuboot-bamboo.c \ - fixed-head.S ep88xc.c cuboot-hpc2.c ep405.c cuboot-taishan.c \ + cuboot-pq2.c cuboot-sequoia.c treeboot-walnut.c \ + cuboot-bamboo.c cuboot-mpc7448hpc2.c cuboot-taishan.c \ + fixed-head.S ep88xc.c ep405.c \ cuboot-katmai.c cuboot-rainier.c redboot-8xx.c ep8248e.c \ cuboot-warp.c cuboot-85xx-cpm2.c src-boot := $(src-wlib) $(src-plat) empty.c @@ -123,6 +124,8 @@ 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.ps3.lds +dtstree := $(srctree)/$(src)/dts + wrapper :=$(srctree)/$(src)/wrapper wrapperbits := $(extra-y) $(addprefix $(obj)/,addnote hack-coff mktree dtc) \ $(wrapper) FORCE @@ -181,7 +184,7 @@ quiet_cmd_wrap = WRAP $@ image-$(CONFIG_PPC_PSERIES) += zImage.pseries image-$(CONFIG_PPC_MAPLE) += zImage.pseries image-$(CONFIG_PPC_IBM_CELL_BLADE) += zImage.pseries -image-$(CONFIG_PPC_PS3) += zImage.ps3 +image-$(CONFIG_PPC_PS3) += zImage-dtb.ps3 image-$(CONFIG_PPC_CELLEB) += zImage.pseries image-$(CONFIG_PPC_CHRP) += zImage.chrp image-$(CONFIG_PPC_EFIKA) += zImage.chrp @@ -191,33 +194,69 @@ image-$(CONFIG_PPC_PRPMC2800) += zImage.prpmc2800 image-$(CONFIG_PPC_ISERIES) += zImage.iseries image-$(CONFIG_DEFAULT_UIMAGE) += uImage -ifneq ($(CONFIG_DEVICE_TREE),"") -image-$(CONFIG_PPC_8xx) += cuImage.8xx -image-$(CONFIG_PPC_EP88XC) += zImage.ep88xc -image-$(CONFIG_EP405) += zImage.ep405 -image-$(CONFIG_8260) += cuImage.pq2 -image-$(CONFIG_EP8248E) += zImage.ep8248e -image-$(CONFIG_PPC_MPC52xx) += cuImage.52xx -image-$(CONFIG_STORCENTER) += cuImage.824x -image-$(CONFIG_PPC_83xx) += cuImage.83xx -image-$(CONFIG_PPC_85xx) += cuImage.85xx -ifeq ($(CONFIG_CPM2),y) -image-$(CONFIG_PPC_85xx) += cuImage.85xx-cpm2 -endif -image-$(CONFIG_MPC7448HPC2) += cuImage.hpc2 +# +# Targets which embed a device tree blob +# +# Theses are default targets to build images which embed device tree blobs. +# They are only required on boards which do not have FDT support in firmware. +# Boards with newish u-boot firmare can use the uImage target above +# + +# Board ports in arch/powerpc/platform/40x/Kconfig +image-$(CONFIG_EP405) += zImage-dtb.ep405 +image-$(CONFIG_WALNUT) += treeImage.walnut + +# Board ports in arch/powerpc/platform/44x/Kconfig image-$(CONFIG_EBONY) += treeImage.ebony cuImage.ebony image-$(CONFIG_BAMBOO) += treeImage.bamboo cuImage.bamboo image-$(CONFIG_SEQUOIA) += cuImage.sequoia image-$(CONFIG_RAINIER) += cuImage.rainier -image-$(CONFIG_WALNUT) += treeImage.walnut image-$(CONFIG_TAISHAN) += cuImage.taishan image-$(CONFIG_KATMAI) += cuImage.katmai image-$(CONFIG_WARP) += cuImage.warp -endif -ifneq ($(CONFIG_REDBOOT),"") -image-$(CONFIG_PPC_8xx) += zImage.redboot-8xx -endif +# Board ports in arch/powerpc/platform/8xx/Kconfig +image-$(CONFIG_PPC_MPC86XADS) += cuImage.mpc866ads +image-$(CONFIG_PPC_MPC885ADS) += cuImage.mpc885ads +image-$(CONFIG_PPC_EP88XC) += zImage-dtb.ep88xc +image-$(CONFIG_PPC_ADDER875) += cuImage.adder875-uboot \ + zImage-dtb.adder875-redboot + +# Board ports in arch/powerpc/platform/52xx/Kconfig +image-$(CONFIG_PPC_LITE5200) += cuImage.lite5200 cuImage.lite5200b + +# Board ports in arch/powerpc/platform/82xx/Kconfig +image-$(CONFIG_MPC8272_ADS) += cuImage.mpc8272ads +image-$(CONFIG_PQ2FADS) += cuImage.pq2fads +image-$(CONFIG_EP8248E) += zImage-dtb.ep8248e + +# Board ports in arch/powerpc/platform/83xx/Kconfig +image-$(CONFIG_MPC832x_MDS) += cuImage.mpc832x_mds +image-$(CONFIG_MPC832x_RDB) += cuImage.mpc832x_rdb +image-$(CONFIG_MPC834x_ITX) += cuImage.mpc8349emitx \ + cuImage.mpc8349emitxgp +image-$(CONFIG_MPC834x_MDS) += cuImage.mpc834x_mds +image-$(CONFIG_MPC836x_MDS) += cuImage.mpc836x_mds + +# Board ports in arch/powerpc/platform/85xx/Kconfig +image-$(CONFIG_MPC8540_ADS) += cuImage.mpc8540ads +image-$(CONFIG_MPC8560_ADS) += cuImage.mpc8560ads +image-$(CONFIG_MPC85xx_CDS) += cuImage.mpc8541cds \ + cuImage.mpc8548cds \ + cuImage.mpc8555cds +image-$(CONFIG_MPC85xx_MDS) += cuImage.mpc8568mds +image-$(CONFIG_MPC85xx_DS) += cuImage.mpc8544ds \ + cuImage.mpc8572ds +image-$(CONFIG_TQM8540) += cuImage.tqm8540 +image-$(CONFIG_TQM8541) += cuImage.tqm8541 +image-$(CONFIG_TQM8555) += cuImage.tqm8555 +image-$(CONFIG_TQM8560) += cuImage.tqm8560 +image-$(CONFIG_SBC8548) += cuImage.tqm8548 +image-$(CONFIG_SBC8560) += cuImage.tqm8560 + +# Board ports in arch/powerpc/platform/embedded6xx/Kconfig +image-$(CONFIG_STORCENTER) += cuImage.storcenter +image-$(CONFIG_MPC7448HPC2) += cuImage.mpc7448hpc2 # For 32-bit powermacs, build the COFF and miboot images # as well as the ELF images. @@ -233,24 +272,20 @@ targets += $(image-y) $(initrd-y) $(addprefix $(obj)/, $(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.initrd.%: vmlinux $(wrapperbits) $(dts) - $(call if_changed,wrap,$*,$(dts),,$(obj)/ramdisk.image.gz) +$(obj)/zImage.initrd.%: vmlinux $(wrapperbits) + $(call if_changed,wrap,$*,,,$(obj)/ramdisk.image.gz) -$(obj)/zImage.%: vmlinux $(wrapperbits) $(dts) - $(call if_changed,wrap,$*,$(dts)) +$(obj)/zImage.%: vmlinux $(wrapperbits) + $(call if_changed,wrap,$*) + +$(obj)/zImage-dtb.initrd.%: vmlinux $(wrapperbits) $(dtstree)/%.dts + $(call if_changed,wrap,$*,$(dtstree)/$*.dts,,$(obj)/ramdisk.image.gz) + +$(obj)/zImage-dtb.%: vmlinux $(wrapperbits) $(dtstree)/%.dts + $(call if_changed,wrap,$*,$(dtstree)/$*.dts) # This cannot be in the root of $(src) as the zImage rule always adds a $(obj) # prefix @@ -260,24 +295,17 @@ $(obj)/vmlinux.strip: vmlinux $(obj)/zImage.iseries: 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 $(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) -$(obj)/cuImage.%: vmlinux $(dts) $(wrapperbits) - $(call if_changed,wrap,cuboot-$*,$(dts)) +$(obj)/cuImage.%: vmlinux $(dtstree)/%.dts $(wrapperbits) + $(call if_changed,wrap,cuboot-$*,$(dtstree)/$*.dts) -$(obj)/treeImage.initrd.%: vmlinux $(dts) $(wrapperbits) - $(call if_changed,wrap,treeboot-$*,$(dts),,$(obj)/ramdisk.image.gz) +$(obj)/treeImage.initrd.%: vmlinux $(dtstree)/%.dts $(wrapperbits) + $(call if_changed,wrap,treeboot-$*,$(dtstree)/$*.dts,,$(obj)/ramdisk.image.gz) -$(obj)/treeImage.%: vmlinux $(dts) $(wrapperbits) - $(call if_changed,wrap,treeboot-$*,$(dts)) +$(obj)/treeImage.%: vmlinux $(dtstree)/%.dts $(wrapperbits) + $(call if_changed,wrap,treeboot-$*,$(dtstree)/$*.dts) # If there isn't a platform selected then just strip the vmlinux. ifeq (,$(image-y)) diff --git a/arch/powerpc/boot/cuboot-hpc2.c b/arch/powerpc/boot/cuboot-mpc7448hpc2.c index 1b8953259d7..1b8953259d7 100644 --- a/arch/powerpc/boot/cuboot-hpc2.c +++ b/arch/powerpc/boot/cuboot-mpc7448hpc2.c diff --git a/arch/powerpc/boot/dts/adder875-redboot.dts b/arch/powerpc/boot/dts/adder875-redboot.dts index 930bfb3894e..28e9cd3d7a2 100644 --- a/arch/powerpc/boot/dts/adder875-redboot.dts +++ b/arch/powerpc/boot/dts/adder875-redboot.dts @@ -151,6 +151,7 @@ compatible = "fsl,mpc875-brg", "fsl,cpm1-brg", "fsl,cpm-brg"; + clock-frequency = <50000000>; reg = <0x9f0 0x10>; }; diff --git a/arch/powerpc/boot/dts/adder875-uboot.dts b/arch/powerpc/boot/dts/adder875-uboot.dts index 0197242dacf..54fb60ec03e 100644 --- a/arch/powerpc/boot/dts/adder875-uboot.dts +++ b/arch/powerpc/boot/dts/adder875-uboot.dts @@ -150,6 +150,7 @@ compatible = "fsl,mpc875-brg", "fsl,cpm1-brg", "fsl,cpm-brg"; + clock-frequency = <50000000>; reg = <0x9f0 0x10>; }; diff --git a/arch/powerpc/boot/dts/mpc5121ads.dts b/arch/powerpc/boot/dts/mpc5121ads.dts new file mode 100644 index 00000000000..94ad7b2b241 --- /dev/null +++ b/arch/powerpc/boot/dts/mpc5121ads.dts @@ -0,0 +1,122 @@ +/* + * MPC5121E MDS Device Tree Source + * + * Copyright 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 as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +/dts-v1/; + +/ { + model = "mpc5121ads"; + compatible = "fsl,mpc5121ads"; + #address-cells = <1>; + #size-cells = <1>; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + PowerPC,5121@0 { + device_type = "cpu"; + reg = <0>; + d-cache-line-size = <0x20>; // 32 bytes + i-cache-line-size = <0x20>; // 32 bytes + d-cache-size = <0x8000>; // L1, 32K + i-cache-size = <0x8000>; // L1, 32K + timebase-frequency = <49500000>;// 49.5 MHz (csb/4) + bus-frequency = <198000000>; // 198 MHz csb bus + clock-frequency = <396000000>; // 396 MHz ppc core + }; + }; + + memory { + device_type = "memory"; + reg = <0x00000000 0x10000000>; // 256MB at 0 + }; + + localbus@80000020 { + compatible = "fsl,mpc5121ads-localbus"; + #address-cells = <2>; + #size-cells = <1>; + reg = <0x80000020 0x40>; + + ranges = <0x0 0x0 0xfc000000 0x04000000 + 0x2 0x0 0x82000000 0x00008000>; + + flash@0,0 { + compatible = "cfi-flash"; + reg = <0 0x0 0x4000000>; + bank-width = <4>; + device-width = <1>; + }; + + board-control@2,0 { + compatible = "fsl,mpc5121ads-cpld"; + reg = <0x2 0x0 0x8000>; + }; + }; + + soc@80000000 { + compatible = "fsl,mpc5121-immr"; + #address-cells = <1>; + #size-cells = <1>; + #interrupt-cells = <2>; + ranges = <0x0 0x80000000 0x400000>; + reg = <0x80000000 0x400000>; + bus-frequency = <66000000>; // 66 MHz ips bus + + + // IPIC + // interrupts cell = <intr #, sense> + // sense values match linux IORESOURCE_IRQ_* defines: + // sense == 8: Level, low assertion + // sense == 2: Edge, high-to-low change + // + ipic: interrupt-controller@c00 { + compatible = "fsl,mpc5121-ipic", "fsl,ipic"; + interrupt-controller; + #address-cells = <0>; + #interrupt-cells = <2>; + reg = <0xc00 0x100>; + }; + + // 512x PSCs are not 52xx PSCs compatible + // PSC3 serial port A aka ttyPSC0 + serial@11300 { + device_type = "serial"; + compatible = "fsl,mpc5121-psc-uart"; + // Logical port assignment needed until driver + // learns to use aliases + port-number = <0>; + cell-index = <3>; + reg = <0x11300 0x100>; + interrupts = <0x28 0x8>; // actually the fifo irq + interrupt-parent = < &ipic >; + }; + + // PSC4 serial port B aka ttyPSC1 + serial@11400 { + device_type = "serial"; + compatible = "fsl,mpc5121-psc-uart"; + // Logical port assignment needed until driver + // learns to use aliases + port-number = <1>; + cell-index = <4>; + reg = <0x11400 0x100>; + interrupts = <0x28 0x8>; // actually the fifo irq + interrupt-parent = < &ipic >; + }; + + pscsfifo@11f00 { + compatible = "fsl,mpc5121-psc-fifo"; + reg = <0x11f00 0x100>; + interrupts = <0x28 0x8>; + interrupt-parent = < &ipic >; + }; + }; +}; diff --git a/arch/powerpc/boot/dts/mpc8313erdb.dts b/arch/powerpc/boot/dts/mpc8313erdb.dts index 2d6653fe72f..e1f0dca8ac3 100644 --- a/arch/powerpc/boot/dts/mpc8313erdb.dts +++ b/arch/powerpc/boot/dts/mpc8313erdb.dts @@ -118,6 +118,10 @@ interrupts = <14 0x8>; interrupt-parent = <&ipic>; dfsrr; + rtc@68 { + compatible = "dallas,ds1339"; + reg = <0x68>; + }; }; i2c@3100 { diff --git a/arch/powerpc/boot/dts/mpc8315erdb.dts b/arch/powerpc/boot/dts/mpc8315erdb.dts index b582032ba3d..d7a1ececa30 100644 --- a/arch/powerpc/boot/dts/mpc8315erdb.dts +++ b/arch/powerpc/boot/dts/mpc8315erdb.dts @@ -96,7 +96,7 @@ #address-cells = <1>; #size-cells = <1>; device_type = "soc"; - compatible = "simple-bus"; + compatible = "fsl,mpc8315-immr", "simple-bus"; ranges = <0 0xe0000000 0x00100000>; reg = <0xe0000000 0x00000200>; bus-frequency = <0>; diff --git a/arch/powerpc/boot/dts/mpc834x_mds.dts b/arch/powerpc/boot/dts/mpc834x_mds.dts index 7480edae85e..0199c5c548d 100644 --- a/arch/powerpc/boot/dts/mpc834x_mds.dts +++ b/arch/powerpc/boot/dts/mpc834x_mds.dts @@ -332,7 +332,7 @@ 0xc000 0x0 0x0 0x3 &ipic 23 0x8 0xc000 0x0 0x0 0x4 &ipic 20 0x8>; interrupt-parent = <&ipic>; - interrupts = <66 0x8>; + interrupts = <67 0x8>; bus-range = <0 0>; ranges = <0x02000000 0x0 0xb0000000 0xb0000000 0x0 0x10000000 0x42000000 0x0 0xa0000000 0xa0000000 0x0 0x10000000 diff --git a/arch/powerpc/boot/dts/mpc8572ds.dts b/arch/powerpc/boot/dts/mpc8572ds.dts index 813c259abbe..db37214aee3 100644 --- a/arch/powerpc/boot/dts/mpc8572ds.dts +++ b/arch/powerpc/boot/dts/mpc8572ds.dts @@ -42,6 +42,18 @@ bus-frequency = <0>; clock-frequency = <0>; }; + + PowerPC,8572@1 { + device_type = "cpu"; + reg = <1>; + d-cache-line-size = <20>; // 32 bytes + i-cache-line-size = <20>; // 32 bytes + d-cache-size = <8000>; // L1, 32K + i-cache-size = <8000>; // L1, 32K + timebase-frequency = <0>; + bus-frequency = <0>; + clock-frequency = <0>; + }; }; memory { diff --git a/arch/powerpc/boot/dts/mpc885ads.dts b/arch/powerpc/boot/dts/mpc885ads.dts index 8848e637293..d84a012c2aa 100644 --- a/arch/powerpc/boot/dts/mpc885ads.dts +++ b/arch/powerpc/boot/dts/mpc885ads.dts @@ -166,6 +166,7 @@ compatible = "fsl,mpc885-brg", "fsl,cpm1-brg", "fsl,cpm-brg"; + clock-frequency = <0>; reg = <9f0 10>; }; diff --git a/arch/powerpc/boot/dts/sequoia.dts b/arch/powerpc/boot/dts/sequoia.dts index d9046c1adcb..5c13d46f441 100644 --- a/arch/powerpc/boot/dts/sequoia.dts +++ b/arch/powerpc/boot/dts/sequoia.dts @@ -138,6 +138,14 @@ interrupts = <15 8>; }; + USB0: ehci@e0000300 { + compatible = "ibm,usb-ehci-440epx", "usb-ehci"; + interrupt-parent = <&UIC0>; + interrupts = <1a 4>; + reg = <0 e0000300 90 0 e0000390 70>; + big-endian; + }; + POB0: opb { compatible = "ibm,opb-440epx", "ibm,opb"; #address-cells = <1>; diff --git a/arch/powerpc/boot/dts/storcenter.dts b/arch/powerpc/boot/dts/storcenter.dts index 2204874ac5f..5893816c0bc 100644 --- a/arch/powerpc/boot/dts/storcenter.dts +++ b/arch/powerpc/boot/dts/storcenter.dts @@ -15,7 +15,7 @@ / { model = "StorCenter"; - compatible = "storcenter"; + compatible = "iomega,storcenter"; #address-cells = <1>; #size-cells = <1>; @@ -62,12 +62,12 @@ #size-cells = <0>; compatible = "fsl-i2c"; reg = <0x3000 0x100>; - interrupts = <5 2>; + interrupts = <17 2>; interrupt-parent = <&mpic>; rtc@68 { compatible = "dallas,ds1337"; - reg = <68>; + reg = <0x68>; }; }; @@ -78,7 +78,7 @@ reg = <0x4500 0x20>; clock-frequency = <97553800>; /* Hz */ current-speed = <115200>; - interrupts = <9 2>; + interrupts = <25 2>; interrupt-parent = <&mpic>; }; @@ -89,7 +89,7 @@ reg = <0x4600 0x20>; clock-frequency = <97553800>; /* Hz */ current-speed = <9600>; - interrupts = <10 2>; + interrupts = <26 2>; interrupt-parent = <&mpic>; }; @@ -136,6 +136,6 @@ }; chosen { - linux,stdout-path = "/soc/serial@4500"; + linux,stdout-path = &serial0; }; }; diff --git a/arch/powerpc/boot/wrapper b/arch/powerpc/boot/wrapper index 763a0c46f44..c3178155311 100755 --- a/arch/powerpc/boot/wrapper +++ b/arch/powerpc/boot/wrapper @@ -158,6 +158,29 @@ miboot|uboot) cuboot*) binary=y gzip= + case "$platform" in + *-mpc885ads|*-adder875*|*-ep88xc) + platformo=$object/cuboot-8xx.o + ;; + *5200*|*-motionpro) + platformo=$object/cuboot-52xx.o + ;; + *-pq2fads|*-ep8248e|*-mpc8272*|*-storcenter) + platformo=$object/cuboot-pq2.o + ;; + *-mpc824*) + platformo=$object/cuboot-824x.o + ;; + *-mpc83*) + platformo=$object/cuboot-83xx.o + ;; + *-tqm8541|*-mpc8560*|*-tqm8560|*-tqm8555*) + platformo=$object/cuboot-85xx-cpm2.o + ;; + *-mpc85*) + platformo=$object/cuboot-85xx.o + ;; + esac ;; ps3) platformo="$object/ps3-head.o $object/ps3-hvcall.o $object/ps3.o" diff --git a/arch/powerpc/configs/mpc83xx_defconfig b/arch/powerpc/configs/mpc83xx_defconfig index 31bdbf3f756..a9807f083bc 100644 --- a/arch/powerpc/configs/mpc83xx_defconfig +++ b/arch/powerpc/configs/mpc83xx_defconfig @@ -186,7 +186,7 @@ CONFIG_PREEMPT_NONE=y # CONFIG_PREEMPT is not set CONFIG_BINFMT_ELF=y # CONFIG_BINFMT_MISC is not set -# CONFIG_MATH_EMULATION is not set +CONFIG_MATH_EMULATION=y CONFIG_ARCH_ENABLE_MEMORY_HOTPLUG=y CONFIG_ARCH_FLATMEM_ENABLE=y CONFIG_ARCH_POPULATES_NODE_MAP=y @@ -416,14 +416,14 @@ CONFIG_PHYLIB=y # MII PHY device drivers # CONFIG_MARVELL_PHY=y -# CONFIG_DAVICOM_PHY is not set +CONFIG_DAVICOM_PHY=y # CONFIG_QSEMI_PHY is not set # CONFIG_LXT_PHY is not set # CONFIG_CICADA_PHY is not set -# CONFIG_VITESSE_PHY is not set +CONFIG_VITESSE_PHY=y # CONFIG_SMSC_PHY is not set # CONFIG_BROADCOM_PHY is not set -# CONFIG_ICPLUS_PHY is not set +CONFIG_ICPLUS_PHY=y # CONFIG_FIXED_PHY is not set # CONFIG_MDIO_BITBANG is not set CONFIG_NET_ETHERNET=y @@ -436,7 +436,7 @@ CONFIG_MII=y CONFIG_NETDEV_1000=y CONFIG_GIANFAR=y # CONFIG_GFAR_NAPI is not set -# CONFIG_UCC_GETH is not set +CONFIG_UCC_GETH=y CONFIG_NETDEV_10000=y # diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile index 58dbfeff9b4..0662ae46f72 100644 --- a/arch/powerpc/kernel/Makefile +++ b/arch/powerpc/kernel/Makefile @@ -2,6 +2,8 @@ # Makefile for the linux kernel. # +CFLAGS_ptrace.o += -DUTS_MACHINE='"$(UTS_MACHINE)"' + ifeq ($(CONFIG_PPC64),y) CFLAGS_prom_init.o += -mno-minimal-toc endif @@ -15,7 +17,7 @@ obj-y := semaphore.o cputable.o ptrace.o syscalls.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 \ +obj-$(CONFIG_PPC64) += setup_64.o sys_ppc32.o \ signal_64.o ptrace32.o \ paca.o cpu_setup_ppc970.o \ cpu_setup_pa6t.o \ diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c index ed083feaf6f..e6e49289f78 100644 --- a/arch/powerpc/kernel/asm-offsets.c +++ b/arch/powerpc/kernel/asm-offsets.c @@ -22,6 +22,7 @@ #include <linux/mman.h> #include <linux/mm.h> #include <linux/suspend.h> +#include <linux/hrtimer.h> #ifdef CONFIG_PPC64 #include <linux/time.h> #include <linux/hardirq.h> @@ -312,7 +313,7 @@ int main(void) DEFINE(CLOCK_REALTIME, CLOCK_REALTIME); DEFINE(CLOCK_MONOTONIC, CLOCK_MONOTONIC); DEFINE(NSEC_PER_SEC, NSEC_PER_SEC); - DEFINE(CLOCK_REALTIME_RES, TICK_NSEC); + DEFINE(CLOCK_REALTIME_RES, (KTIME_MONOTONIC_RES).tv64); #ifdef CONFIG_BUG DEFINE(BUG_ENTRY_SIZE, sizeof(struct bug_entry)); diff --git a/arch/powerpc/kernel/binfmt_elf32.c b/arch/powerpc/kernel/binfmt_elf32.c deleted file mode 100644 index 1d45d7782d4..00000000000 --- a/arch/powerpc/kernel/binfmt_elf32.c +++ /dev/null @@ -1,69 +0,0 @@ -/* - * binfmt_elf32.c: Support 32-bit PPC ELF binaries on Power3 and followons. - * based on the SPARC64 version. - * Copyright (C) 1995, 1996, 1997, 1998 David S. Miller (davem@redhat.com) - * Copyright (C) 1995, 1996, 1997, 1998 Jakub Jelinek (jj@ultra.linux.cz) - * - * Copyright (C) 2000,2001 Ken Aaker (kdaaker@rchland.vnet.ibm.com), IBM Corp - * Copyright (C) 2001 Anton Blanchard (anton@au.ibm.com), IBM - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - */ - -#include <asm/processor.h> -#include <linux/module.h> -#include <linux/compat.h> -#include <linux/elfcore-compat.h> - -#undef ELF_ARCH -#undef ELF_CLASS -#define ELF_CLASS ELFCLASS32 -#define ELF_ARCH EM_PPC - -#undef elfhdr -#undef elf_phdr -#undef elf_note -#undef elf_addr_t -#define elfhdr elf32_hdr -#define elf_phdr elf32_phdr -#define elf_note elf32_note -#define elf_addr_t Elf32_Off - -#define elf_prstatus compat_elf_prstatus -#define elf_prpsinfo compat_elf_prpsinfo - -#define elf_core_copy_regs compat_elf_core_copy_regs -static inline void compat_elf_core_copy_regs(compat_elf_gregset_t *elf_regs, - struct pt_regs *regs) -{ - PPC_ELF_CORE_COPY_REGS((*elf_regs), regs); -} - -#define elf_core_copy_task_regs compat_elf_core_copy_task_regs -static int compat_elf_core_copy_task_regs(struct task_struct *tsk, - compat_elf_gregset_t *elf_regs) -{ - struct pt_regs *regs = tsk->thread.regs; - if (regs) - compat_elf_core_copy_regs(elf_regs, regs); - return 1; -} - -#include <linux/time.h> - -#undef cputime_to_timeval -#define cputime_to_timeval cputime_to_compat_timeval -static __inline__ void -cputime_to_compat_timeval(const cputime_t cputime, struct compat_timeval *value) -{ - unsigned long jiffies = cputime_to_jiffies(cputime); - value->tv_usec = (jiffies % HZ) * (1000000L / HZ); - value->tv_sec = jiffies / HZ; -} - -#define init_elf_binfmt init_elf32_binfmt - -#include "../../../fs/binfmt_elf.c" diff --git a/arch/powerpc/kernel/cputable.c b/arch/powerpc/kernel/cputable.c index a4c2771b5e6..2a8f5cc5184 100644 --- a/arch/powerpc/kernel/cputable.c +++ b/arch/powerpc/kernel/cputable.c @@ -959,6 +959,9 @@ static struct cpu_spec __initdata cpu_specs[] = { .icache_bsize = 32, .dcache_bsize = 32, .cpu_setup = __setup_cpu_603, + .num_pmcs = 4, + .oprofile_cpu_type = "ppc/e300", + .oprofile_type = PPC_OPROFILE_FSL_EMB, .platform = "ppc603", }, { /* e300c4 (e300c1, plus one IU) */ @@ -971,6 +974,9 @@ static struct cpu_spec __initdata cpu_specs[] = { .dcache_bsize = 32, .cpu_setup = __setup_cpu_603, .machine_check = machine_check_generic, + .num_pmcs = 4, + .oprofile_cpu_type = "ppc/e300", + .oprofile_type = PPC_OPROFILE_FSL_EMB, .platform = "ppc603", }, { /* default match, we assume split I/D cache & TB (non-601)... */ @@ -1435,7 +1441,7 @@ static struct cpu_spec __initdata cpu_specs[] = { .dcache_bsize = 32, .num_pmcs = 4, .oprofile_cpu_type = "ppc/e500", - .oprofile_type = PPC_OPROFILE_BOOKE, + .oprofile_type = PPC_OPROFILE_FSL_EMB, .machine_check = machine_check_e500, .platform = "ppc8540", }, @@ -1453,7 +1459,7 @@ static struct cpu_spec __initdata cpu_specs[] = { .dcache_bsize = 32, .num_pmcs = 4, .oprofile_cpu_type = "ppc/e500", - .oprofile_type = PPC_OPROFILE_BOOKE, + .oprofile_type = PPC_OPROFILE_FSL_EMB, .machine_check = machine_check_e500, .platform = "ppc8548", }, diff --git a/arch/powerpc/kernel/legacy_serial.c b/arch/powerpc/kernel/legacy_serial.c index 76b862bd1fe..61dd17449dd 100644 --- a/arch/powerpc/kernel/legacy_serial.c +++ b/arch/powerpc/kernel/legacy_serial.c @@ -36,7 +36,8 @@ static struct legacy_serial_info { static struct __initdata of_device_id parents[] = { {.type = "soc",}, {.type = "tsi-bridge",}, - {.type = "opb", .compatible = "ibm,opb",}, + {.type = "opb", }, + {.compatible = "ibm,opb",}, {.compatible = "simple-bus",}, {.compatible = "wrs,epld-localbus",}, }; diff --git a/arch/powerpc/kernel/pmc.c b/arch/powerpc/kernel/pmc.c index ea04e0ab3f2..0516e2d3e02 100644 --- a/arch/powerpc/kernel/pmc.c +++ b/arch/powerpc/kernel/pmc.c @@ -26,7 +26,7 @@ static void dummy_perf(struct pt_regs *regs) { -#if defined(CONFIG_FSL_BOOKE) && !defined(CONFIG_E200) +#if defined(CONFIG_FSL_EMB_PERFMON) mtpmr(PMRN_PMGC0, mfpmr(PMRN_PMGC0) & ~PMGC0_PMIE); #elif defined(CONFIG_PPC64) || defined(CONFIG_6xx) if (cur_cpu_spec->pmc_type == PPC_PMC_IBM) diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c index 8b056d2295c..7673e986573 100644 --- a/arch/powerpc/kernel/ptrace.c +++ b/arch/powerpc/kernel/ptrace.c @@ -21,6 +21,8 @@ #include <linux/smp.h> #include <linux/errno.h> #include <linux/ptrace.h> +#include <linux/regset.h> +#include <linux/elf.h> #include <linux/user.h> #include <linux/security.h> #include <linux/signal.h> @@ -58,20 +60,38 @@ #define PT_MAX_PUT_REG PT_CCR #endif +static unsigned long get_user_msr(struct task_struct *task) +{ + return task->thread.regs->msr | task->thread.fpexc_mode; +} + +static int set_user_msr(struct task_struct *task, unsigned long msr) +{ + task->thread.regs->msr &= ~MSR_DEBUGCHANGE; + task->thread.regs->msr |= msr & MSR_DEBUGCHANGE; + return 0; +} + +/* + * We prevent mucking around with the reserved area of trap + * which are used internally by the kernel. + */ +static int set_user_trap(struct task_struct *task, unsigned long trap) +{ + task->thread.regs->trap = trap & 0xfff0; + return 0; +} + /* * Get contents of register REGNO in task TASK. */ unsigned long ptrace_get_reg(struct task_struct *task, int regno) { - 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 == PT_MSR) + return get_user_msr(task); if (regno < (sizeof(struct pt_regs) / sizeof(unsigned long))) return ((unsigned long *)task->thread.regs)[regno]; @@ -87,40 +107,134 @@ int ptrace_put_reg(struct task_struct *task, int regno, unsigned long data) 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; + if (regno == PT_MSR) + return set_user_msr(task, data); + if (regno == PT_TRAP) + return set_user_trap(task, data); + + if (regno <= PT_MAX_PUT_REG) { ((unsigned long *)task->thread.regs)[regno] = data; return 0; } return -EIO; } +static int gpr_get(struct task_struct *target, const struct user_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) +{ + int ret; + + if (target->thread.regs == NULL) + return -EIO; + + CHECK_FULL_REGS(target->thread.regs); + + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + target->thread.regs, + 0, offsetof(struct pt_regs, msr)); + if (!ret) { + unsigned long msr = get_user_msr(target); + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, &msr, + offsetof(struct pt_regs, msr), + offsetof(struct pt_regs, msr) + + sizeof(msr)); + } + + BUILD_BUG_ON(offsetof(struct pt_regs, orig_gpr3) != + offsetof(struct pt_regs, msr) + sizeof(long)); + + if (!ret) + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + &target->thread.regs->orig_gpr3, + offsetof(struct pt_regs, orig_gpr3), + sizeof(struct pt_regs)); + if (!ret) + ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, + sizeof(struct pt_regs), -1); + + return ret; +} + +static int gpr_set(struct task_struct *target, const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + unsigned long reg; + int ret; + + if (target->thread.regs == NULL) + return -EIO; + + CHECK_FULL_REGS(target->thread.regs); + + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + target->thread.regs, + 0, PT_MSR * sizeof(reg)); -static int get_fpregs(void __user *data, struct task_struct *task, - int has_fpscr) + if (!ret && count > 0) { + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, ®, + PT_MSR * sizeof(reg), + (PT_MSR + 1) * sizeof(reg)); + if (!ret) + ret = set_user_msr(target, reg); + } + + BUILD_BUG_ON(offsetof(struct pt_regs, orig_gpr3) != + offsetof(struct pt_regs, msr) + sizeof(long)); + + if (!ret) + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + &target->thread.regs->orig_gpr3, + PT_ORIG_R3 * sizeof(reg), + (PT_MAX_PUT_REG + 1) * sizeof(reg)); + + if (PT_MAX_PUT_REG + 1 < PT_TRAP && !ret) + ret = user_regset_copyin_ignore( + &pos, &count, &kbuf, &ubuf, + (PT_MAX_PUT_REG + 1) * sizeof(reg), + PT_TRAP * sizeof(reg)); + + if (!ret && count > 0) { + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, ®, + PT_TRAP * sizeof(reg), + (PT_TRAP + 1) * sizeof(reg)); + if (!ret) + ret = set_user_trap(target, reg); + } + + if (!ret) + ret = user_regset_copyin_ignore( + &pos, &count, &kbuf, &ubuf, + (PT_TRAP + 1) * sizeof(reg), -1); + + return ret; +} + +static int fpr_get(struct task_struct *target, const struct user_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) { - unsigned int count = has_fpscr ? 33 : 32; + flush_fp_to_thread(target); - if (copy_to_user(data, task->thread.fpr, count * sizeof(double))) - return -EFAULT; - return 0; + BUILD_BUG_ON(offsetof(struct thread_struct, fpscr) != + offsetof(struct thread_struct, fpr[32])); + + return user_regset_copyout(&pos, &count, &kbuf, &ubuf, + &target->thread.fpr, 0, -1); } -static int set_fpregs(void __user *data, struct task_struct *task, - int has_fpscr) +static int fpr_set(struct task_struct *target, const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) { - unsigned int count = has_fpscr ? 33 : 32; + flush_fp_to_thread(target); - if (copy_from_user(task->thread.fpr, data, count * sizeof(double))) - return -EFAULT; - return 0; + BUILD_BUG_ON(offsetof(struct thread_struct, fpscr) != + offsetof(struct thread_struct, fpr[32])); + + return user_regset_copyin(&pos, &count, &kbuf, &ubuf, + &target->thread.fpr, 0, -1); } @@ -138,56 +252,74 @@ static int set_fpregs(void __user *data, struct task_struct *task, * (combined (32- and 64-bit) gdb. */ -/* - * Get contents of AltiVec register state in task TASK - */ -static int get_vrregs(unsigned long __user *data, struct task_struct *task) +static int vr_active(struct task_struct *target, + const struct user_regset *regset) { - unsigned long regsize; + flush_altivec_to_thread(target); + return target->thread.used_vr ? regset->n : 0; +} - /* 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)); +static int vr_get(struct task_struct *target, const struct user_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) +{ + int ret; - /* copy VSCR */ - regsize = 1 * sizeof(vector128); - if (copy_to_user(data, &task->thread.vscr, regsize)) - return -EFAULT; - data += (regsize / sizeof(unsigned long)); + flush_altivec_to_thread(target); - /* copy VRSAVE */ - if (put_user(task->thread.vrsave, (u32 __user *)data)) - return -EFAULT; + BUILD_BUG_ON(offsetof(struct thread_struct, vscr) != + offsetof(struct thread_struct, vr[32])); - return 0; + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + &target->thread.vr, 0, + 33 * sizeof(vector128)); + if (!ret) { + /* + * Copy out only the low-order word of vrsave. + */ + union { + elf_vrreg_t reg; + u32 word; + } vrsave; + memset(&vrsave, 0, sizeof(vrsave)); + vrsave.word = target->thread.vrsave; + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, &vrsave, + 33 * sizeof(vector128), -1); + } + + return ret; } -/* - * Write contents of AltiVec register state into task TASK. - */ -static int set_vrregs(struct task_struct *task, unsigned long __user *data) +static int vr_set(struct task_struct *target, const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) { - unsigned long regsize; + int ret; - /* 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)); + flush_altivec_to_thread(target); - /* copy VSCR */ - regsize = 1 * sizeof(vector128); - if (copy_from_user(&task->thread.vscr, data, regsize)) - return -EFAULT; - data += (regsize / sizeof(unsigned long)); + BUILD_BUG_ON(offsetof(struct thread_struct, vscr) != + offsetof(struct thread_struct, vr[32])); - /* copy VRSAVE */ - if (get_user(task->thread.vrsave, (u32 __user *)data)) - return -EFAULT; + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + &target->thread.vr, 0, 33 * sizeof(vector128)); + if (!ret && count > 0) { + /* + * We use only the first word of vrsave. + */ + union { + elf_vrreg_t reg; + u32 word; + } vrsave; + memset(&vrsave, 0, sizeof(vrsave)); + vrsave.word = target->thread.vrsave; + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &vrsave, + 33 * sizeof(vector128), -1); + if (!ret) + target->thread.vrsave = vrsave.word; + } - return 0; + return ret; } #endif /* CONFIG_ALTIVEC */ @@ -203,57 +335,273 @@ static int set_vrregs(struct task_struct *task, unsigned long __user *data) * } */ -/* - * Get contents of SPE register state in task TASK. - */ -static int get_evrregs(unsigned long *data, struct task_struct *task) +static int evr_active(struct task_struct *target, + const struct user_regset *regset) { - int i; + flush_spe_to_thread(target); + return target->thread.used_spe ? regset->n : 0; +} - if (!access_ok(VERIFY_WRITE, data, 35 * sizeof(unsigned long))) - return -EFAULT; +static int evr_get(struct task_struct *target, const struct user_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) +{ + int ret; - /* copy SPEFSCR */ - if (__put_user(task->thread.spefscr, &data[34])) - return -EFAULT; + flush_spe_to_thread(target); - /* copy SPE registers EVR[0] .. EVR[31] */ - for (i = 0; i < 32; i++, data++) - if (__put_user(task->thread.evr[i], data)) - return -EFAULT; + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + &target->thread.evr, + 0, sizeof(target->thread.evr)); - /* copy ACC */ - if (__put_user64(task->thread.acc, (unsigned long long *)data)) - return -EFAULT; + BUILD_BUG_ON(offsetof(struct thread_struct, acc) + sizeof(u64) != + offsetof(struct thread_struct, spefscr)); - return 0; + if (!ret) + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + &target->thread.acc, + sizeof(target->thread.evr), -1); + + return ret; +} + +static int evr_set(struct task_struct *target, const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + int ret; + + flush_spe_to_thread(target); + + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + &target->thread.evr, + 0, sizeof(target->thread.evr)); + + BUILD_BUG_ON(offsetof(struct thread_struct, acc) + sizeof(u64) != + offsetof(struct thread_struct, spefscr)); + + if (!ret) + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + &target->thread.acc, + sizeof(target->thread.evr), -1); + + return ret; } +#endif /* CONFIG_SPE */ + /* - * Write contents of SPE register state into task TASK. + * These are our native regset flavors. */ -static int set_evrregs(struct task_struct *task, unsigned long *data) +enum powerpc_regset { + REGSET_GPR, + REGSET_FPR, +#ifdef CONFIG_ALTIVEC + REGSET_VMX, +#endif +#ifdef CONFIG_SPE + REGSET_SPE, +#endif +}; + +static const struct user_regset native_regsets[] = { + [REGSET_GPR] = { + .core_note_type = NT_PRSTATUS, .n = ELF_NGREG, + .size = sizeof(long), .align = sizeof(long), + .get = gpr_get, .set = gpr_set + }, + [REGSET_FPR] = { + .core_note_type = NT_PRFPREG, .n = ELF_NFPREG, + .size = sizeof(double), .align = sizeof(double), + .get = fpr_get, .set = fpr_set + }, +#ifdef CONFIG_ALTIVEC + [REGSET_VMX] = { + .core_note_type = NT_PPC_VMX, .n = 34, + .size = sizeof(vector128), .align = sizeof(vector128), + .active = vr_active, .get = vr_get, .set = vr_set + }, +#endif +#ifdef CONFIG_SPE + [REGSET_SPE] = { + .n = 35, + .size = sizeof(u32), .align = sizeof(u32), + .active = evr_active, .get = evr_get, .set = evr_set + }, +#endif +}; + +static const struct user_regset_view user_ppc_native_view = { + .name = UTS_MACHINE, .e_machine = ELF_ARCH, .ei_osabi = ELF_OSABI, + .regsets = native_regsets, .n = ARRAY_SIZE(native_regsets) +}; + +#ifdef CONFIG_PPC64 +#include <linux/compat.h> + +static int gpr32_get(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) { - int i; + const unsigned long *regs = &target->thread.regs->gpr[0]; + compat_ulong_t *k = kbuf; + compat_ulong_t __user *u = ubuf; + compat_ulong_t reg; + + if (target->thread.regs == NULL) + return -EIO; + + CHECK_FULL_REGS(target->thread.regs); - if (!access_ok(VERIFY_READ, data, 35 * sizeof(unsigned long))) - return -EFAULT; + pos /= sizeof(reg); + count /= sizeof(reg); - /* copy SPEFSCR */ - if (__get_user(task->thread.spefscr, &data[34])) - return -EFAULT; + if (kbuf) + for (; count > 0 && pos < PT_MSR; --count) + *k++ = regs[pos++]; + else + for (; count > 0 && pos < PT_MSR; --count) + if (__put_user((compat_ulong_t) regs[pos++], u++)) + return -EFAULT; - /* copy SPE registers EVR[0] .. EVR[31] */ - for (i = 0; i < 32; i++, data++) - if (__get_user(task->thread.evr[i], data)) + if (count > 0 && pos == PT_MSR) { + reg = get_user_msr(target); + if (kbuf) + *k++ = reg; + else if (__put_user(reg, u++)) return -EFAULT; - /* copy ACC */ - if (__get_user64(task->thread.acc, (unsigned long long*)data)) - return -EFAULT; + ++pos; + --count; + } - return 0; + if (kbuf) + for (; count > 0 && pos < PT_REGS_COUNT; --count) + *k++ = regs[pos++]; + else + for (; count > 0 && pos < PT_REGS_COUNT; --count) + if (__put_user((compat_ulong_t) regs[pos++], u++)) + return -EFAULT; + + kbuf = k; + ubuf = u; + pos *= sizeof(reg); + count *= sizeof(reg); + return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, + PT_REGS_COUNT * sizeof(reg), -1); +} + +static int gpr32_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + unsigned long *regs = &target->thread.regs->gpr[0]; + const compat_ulong_t *k = kbuf; + const compat_ulong_t __user *u = ubuf; + compat_ulong_t reg; + + if (target->thread.regs == NULL) + return -EIO; + + CHECK_FULL_REGS(target->thread.regs); + + pos /= sizeof(reg); + count /= sizeof(reg); + + if (kbuf) + for (; count > 0 && pos < PT_MSR; --count) + regs[pos++] = *k++; + else + for (; count > 0 && pos < PT_MSR; --count) { + if (__get_user(reg, u++)) + return -EFAULT; + regs[pos++] = reg; + } + + + if (count > 0 && pos == PT_MSR) { + if (kbuf) + reg = *k++; + else if (__get_user(reg, u++)) + return -EFAULT; + set_user_msr(target, reg); + ++pos; + --count; + } + + if (kbuf) + for (; count > 0 && pos <= PT_MAX_PUT_REG; --count) + regs[pos++] = *k++; + else + for (; count > 0 && pos <= PT_MAX_PUT_REG; --count) { + if (__get_user(reg, u++)) + return -EFAULT; + regs[pos++] = reg; + } + + if (count > 0 && pos == PT_TRAP) { + if (kbuf) + reg = *k++; + else if (__get_user(reg, u++)) + return -EFAULT; + set_user_trap(target, reg); + ++pos; + --count; + } + + kbuf = k; + ubuf = u; + pos *= sizeof(reg); + count *= sizeof(reg); + return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, + (PT_TRAP + 1) * sizeof(reg), -1); +} + +/* + * These are the regset flavors matching the CONFIG_PPC32 native set. + */ +static const struct user_regset compat_regsets[] = { + [REGSET_GPR] = { + .core_note_type = NT_PRSTATUS, .n = ELF_NGREG, + .size = sizeof(compat_long_t), .align = sizeof(compat_long_t), + .get = gpr32_get, .set = gpr32_set + }, + [REGSET_FPR] = { + .core_note_type = NT_PRFPREG, .n = ELF_NFPREG, + .size = sizeof(double), .align = sizeof(double), + .get = fpr_get, .set = fpr_set + }, +#ifdef CONFIG_ALTIVEC + [REGSET_VMX] = { + .core_note_type = NT_PPC_VMX, .n = 34, + .size = sizeof(vector128), .align = sizeof(vector128), + .active = vr_active, .get = vr_get, .set = vr_set + }, +#endif +#ifdef CONFIG_SPE + [REGSET_SPE] = { + .core_note_type = NT_PPC_SPE, .n = 35, + .size = sizeof(u32), .align = sizeof(u32), + .active = evr_active, .get = evr_get, .set = evr_set + }, +#endif +}; + +static const struct user_regset_view user_ppc_compat_view = { + .name = "ppc", .e_machine = EM_PPC, .ei_osabi = ELF_OSABI, + .regsets = compat_regsets, .n = ARRAY_SIZE(compat_regsets) +}; +#endif /* CONFIG_PPC64 */ + +const struct user_regset_view *task_user_regset_view(struct task_struct *task) +{ +#ifdef CONFIG_PPC64 + if (test_tsk_thread_flag(task, TIF_32BIT)) + return &user_ppc_compat_view; +#endif + return &user_ppc_native_view; } -#endif /* CONFIG_SPE */ void user_enable_single_step(struct task_struct *task) @@ -323,55 +671,29 @@ void ptrace_disable(struct task_struct *child) 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; - - CHECK_FULL_REGS(child->thread.regs); - 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; - - CHECK_FULL_REGS(child->thread.regs); - 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; + switch (request) { + case PPC_PTRACE_GETREGS: /* Get GPRs 0 - 31. */ + return copy_regset_to_user(child, &user_ppc_native_view, + REGSET_GPR, 0, 32 * sizeof(long), + (void __user *) data); + + case PPC_PTRACE_SETREGS: /* Set GPRs 0 - 31. */ + return copy_regset_from_user(child, &user_ppc_native_view, + REGSET_GPR, 0, 32 * sizeof(long), + (const void __user *) data); + + case PPC_PTRACE_GETFPREGS: /* Get FPRs 0 - 31. */ + return copy_regset_to_user(child, &user_ppc_native_view, + REGSET_FPR, 0, 32 * sizeof(double), + (void __user *) data); + + case PPC_PTRACE_SETFPREGS: /* Set FPRs 0 - 31. */ + return copy_regset_from_user(child, &user_ppc_native_view, + REGSET_FPR, 0, 32 * sizeof(double), + (const void __user *) data); } - } - return ret; + return -EPERM; } long arch_ptrace(struct task_struct *child, long request, long addr, long data) @@ -379,12 +701,6 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) int ret = -EPERM; switch (request) { - /* when I and D space are separate, these will need to be fixed. */ - case PTRACE_PEEKTEXT: /* read word at location addr. */ - case PTRACE_PEEKDATA: - ret = generic_ptrace_peekdata(child, addr, data); - break; - /* read the word at location addr in the USER area. */ case PTRACE_PEEKUSR: { unsigned long index, tmp; @@ -412,12 +728,6 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) break; } - /* If I and D space are separate, this will have to be fixed. */ - case PTRACE_POKETEXT: /* write the word at location addr. */ - case PTRACE_POKEDATA: - ret = generic_ptrace_pokedata(child, addr, data); - break; - /* write the word at location addr in the USER area */ case PTRACE_POKEUSR: { unsigned long index; @@ -462,85 +772,60 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) #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; - } - CHECK_FULL_REGS(child->thread.regs); - 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 PTRACE_GETREGS: /* Get all pt_regs from the child. */ + return copy_regset_to_user(child, &user_ppc_native_view, + REGSET_GPR, + 0, sizeof(struct pt_regs), + (void __user *) data); #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; - } - CHECK_FULL_REGS(child->thread.regs); - ret = 0; - for (ui = 0; ui < PT_REGS_COUNT; ui ++) { - ret = __get_user(tmp, (unsigned long __user *) data); - if (ret) - break; - ptrace_put_reg(child, ui, tmp); - data += sizeof(long); - } - break; - } - - case PTRACE_GETFPREGS: { /* Get the child FPU state (FPR0...31 + FPSCR) */ - flush_fp_to_thread(child); - ret = get_fpregs((void __user *)data, child, 1); - break; - } - - case PTRACE_SETFPREGS: { /* Set the child FPU state (FPR0...31 + FPSCR) */ - flush_fp_to_thread(child); - ret = set_fpregs((void __user *)data, child, 1); - break; - } + case PTRACE_SETREGS: /* Set all gp regs in the child. */ + return copy_regset_from_user(child, &user_ppc_native_view, + REGSET_GPR, + 0, sizeof(struct pt_regs), + (const void __user *) data); + + case PTRACE_GETFPREGS: /* Get the child FPU state (FPR0...31 + FPSCR) */ + return copy_regset_to_user(child, &user_ppc_native_view, + REGSET_FPR, + 0, sizeof(elf_fpregset_t), + (void __user *) data); + + case PTRACE_SETFPREGS: /* Set the child FPU state (FPR0...31 + FPSCR) */ + return copy_regset_from_user(child, &user_ppc_native_view, + REGSET_FPR, + 0, sizeof(elf_fpregset_t), + (const void __user *) data); #ifdef CONFIG_ALTIVEC case PTRACE_GETVRREGS: - /* Get the child altivec register state. */ - flush_altivec_to_thread(child); - ret = get_vrregs((unsigned long __user *)data, child); - break; + return copy_regset_to_user(child, &user_ppc_native_view, + REGSET_VMX, + 0, (33 * sizeof(vector128) + + sizeof(u32)), + (void __user *) data); case PTRACE_SETVRREGS: - /* Set the child altivec register state. */ - flush_altivec_to_thread(child); - ret = set_vrregs(child, (unsigned long __user *)data); - break; + return copy_regset_from_user(child, &user_ppc_native_view, + REGSET_VMX, + 0, (33 * sizeof(vector128) + + sizeof(u32)), + (const void __user *) data); #endif #ifdef CONFIG_SPE case PTRACE_GETEVRREGS: /* Get the child spe register state. */ - flush_spe_to_thread(child); - ret = get_evrregs((unsigned long __user *)data, child); - break; + return copy_regset_to_user(child, &user_ppc_native_view, + REGSET_SPE, 0, 35 * sizeof(u32), + (void __user *) data); case PTRACE_SETEVRREGS: /* Set the child spe register state. */ - /* this is to clear the MSR_SPE bit to force a reload - * of register state from memory */ - flush_spe_to_thread(child); - ret = set_evrregs(child, (unsigned long __user *)data); - break; + return copy_regset_from_user(child, &user_ppc_native_view, + REGSET_SPE, 0, 35 * sizeof(u32), + (const void __user *) data); #endif /* Old reverse args ptrace callss */ diff --git a/arch/powerpc/kernel/ptrace32.c b/arch/powerpc/kernel/ptrace32.c index fea6206ff90..4c1de6af4c0 100644 --- a/arch/powerpc/kernel/ptrace32.c +++ b/arch/powerpc/kernel/ptrace32.c @@ -24,9 +24,11 @@ #include <linux/smp_lock.h> #include <linux/errno.h> #include <linux/ptrace.h> +#include <linux/regset.h> #include <linux/user.h> #include <linux/security.h> #include <linux/signal.h> +#include <linux/compat.h> #include <asm/uaccess.h> #include <asm/page.h> @@ -45,87 +47,31 @@ 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; - - CHECK_FULL_REGS(child->thread.regs); - 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; - - CHECK_FULL_REGS(child->thread.regs); - for (i = 0; i < 32; i++) { - ret = get_user(*reg, tmp); - if (ret) - break; - reg++; - tmp++; - } - break; + switch (request) { + case PPC_PTRACE_GETREGS: /* Get GPRs 0 - 31. */ + return copy_regset_to_user(child, + task_user_regset_view(current), 0, + 0, 32 * sizeof(compat_long_t), + compat_ptr(data)); + + case PPC_PTRACE_SETREGS: /* Set GPRs 0 - 31. */ + return copy_regset_from_user(child, + task_user_regset_view(current), 0, + 0, 32 * sizeof(compat_long_t), + compat_ptr(data)); } - } - return ret; + return -EPERM; } -long compat_sys_ptrace(int request, int pid, unsigned long addr, - unsigned long data) +long compat_arch_ptrace(struct task_struct *child, compat_long_t request, + compat_ulong_t caddr, compat_ulong_t cdata) { - struct task_struct *child; + unsigned long addr = caddr; + unsigned long data = cdata; int ret; - lock_kernel(); - if (request == PTRACE_TRACEME) { - ret = ptrace_traceme(); - goto out; - } - - child = ptrace_get_task_struct(pid); - if (IS_ERR(child)) { - ret = PTR_ERR(child); - goto out; - } - - if (request == PTRACE_ATTACH) { - ret = ptrace_attach(child); - goto out_tsk; - } - - ret = ptrace_check_attach(child, request == PTRACE_KILL); - if (ret < 0) - goto out_tsk; - switch (request) { - /* when I and D space are separate, these will need to be fixed. */ - case PTRACE_PEEKTEXT: /* read word at location addr. */ - case PTRACE_PEEKDATA: { - unsigned int tmp; - int copied; - - copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); - ret = -EIO; - if (copied != sizeof(tmp)) - break; - ret = put_user(tmp, (u32 __user *)data); - break; - } - /* * Read 4 bytes of the other process' storage * data is a pointer specifying where the user wants the @@ -225,19 +171,6 @@ long compat_sys_ptrace(int request, int pid, unsigned long addr, break; } - /* If I and D space are separate, this will have to be fixed. */ - case PTRACE_POKETEXT: /* write the word at location addr. */ - case PTRACE_POKEDATA: { - unsigned int tmp; - tmp = data; - ret = 0; - if (access_process_vm(child, addr, &tmp, sizeof(tmp), 1) - == sizeof(tmp)) - break; - ret = -EIO; - break; - } - /* * Write 4 bytes into the other process' storage * data is the 4 bytes that the user wants written @@ -337,46 +270,17 @@ long compat_sys_ptrace(int request, int pid, unsigned long addr, break; } - case PTRACE_GETEVENTMSG: - ret = put_user(child->ptrace_message, (unsigned int __user *) data); - break; + case PTRACE_GETREGS: /* Get all pt_regs from the child. */ + return copy_regset_to_user( + child, task_user_regset_view(current), 0, + 0, PT_REGS_COUNT * sizeof(compat_long_t), + compat_ptr(data)); - 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; - } - CHECK_FULL_REGS(child->thread.regs); - 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; - } - CHECK_FULL_REGS(child->thread.regs); - 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_SETREGS: /* Set all gp regs in the child. */ + return copy_regset_from_user( + child, task_user_regset_view(current), 0, + 0, PT_REGS_COUNT * sizeof(compat_long_t), + compat_ptr(data)); case PTRACE_GETFPREGS: case PTRACE_SETFPREGS: @@ -402,12 +306,9 @@ long compat_sys_ptrace(int request, int pid, unsigned long addr, break; default: - ret = ptrace_request(child, request, addr, data); + ret = compat_ptrace_request(child, request, addr, data); break; } -out_tsk: - put_task_struct(child); -out: - unlock_kernel(); + return ret; } diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index 848a20475db..4b5b7ff4f78 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c @@ -54,7 +54,7 @@ #endif #include <asm/kexec.h> -#ifdef CONFIG_DEBUGGER +#if defined(CONFIG_DEBUGGER) || defined(CONFIG_KEXEC) int (*__debugger)(struct pt_regs *regs); int (*__debugger_ipi)(struct pt_regs *regs); int (*__debugger_bpt)(struct pt_regs *regs); diff --git a/arch/powerpc/kernel/vio.c b/arch/powerpc/kernel/vio.c index f0bad7070fb..f98867252ee 100644 --- a/arch/powerpc/kernel/vio.c +++ b/arch/powerpc/kernel/vio.c @@ -176,7 +176,7 @@ static void __devinit vio_dev_release(struct device *dev) * Returns a pointer to the created vio_dev or NULL if node has * NULL device_type or compatible fields. */ -struct vio_dev * __devinit vio_register_device_node(struct device_node *of_node) +struct vio_dev *vio_register_device_node(struct device_node *of_node) { struct vio_dev *viodev; const unsigned int *unit_address; diff --git a/arch/powerpc/mm/mem.c b/arch/powerpc/mm/mem.c index ff5debf5eed..93a5c53e342 100644 --- a/arch/powerpc/mm/mem.c +++ b/arch/powerpc/mm/mem.c @@ -485,7 +485,12 @@ void update_mmu_cache(struct vm_area_struct *vma, unsigned long address, */ _tlbie(address, 0 /* 8xx doesn't care about PID */); #endif - if (!PageReserved(page) + /* The _PAGE_USER test should really be _PAGE_EXEC, but + * older glibc versions execute some code from no-exec + * pages, which for now we are supporting. If exec-only + * pages are ever implemented, this will have to change. + */ + if (!PageReserved(page) && (pte_val(pte) & _PAGE_USER) && !test_bit(PG_arch_1, &page->flags)) { if (vma->vm_mm == current->active_mm) { __flush_dcache_icache((void *) address); diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c index bc60322d243..a300d254aac 100644 --- a/arch/powerpc/mm/numa.c +++ b/arch/powerpc/mm/numa.c @@ -24,6 +24,8 @@ static int numa_enabled = 1; +static char *cmdline __initdata; + static int numa_debug; #define dbg(args...) if (numa_debug) { printk(KERN_INFO args); } @@ -39,6 +41,53 @@ static bootmem_data_t __initdata plat_node_bdata[MAX_NUMNODES]; static int min_common_depth; static int n_mem_addr_cells, n_mem_size_cells; +static int __cpuinit fake_numa_create_new_node(unsigned long end_pfn, + unsigned int *nid) +{ + unsigned long long mem; + char *p = cmdline; + static unsigned int fake_nid; + static unsigned long long curr_boundary; + + /* + * Modify node id, iff we started creating NUMA nodes + * We want to continue from where we left of the last time + */ + if (fake_nid) + *nid = fake_nid; + /* + * In case there are no more arguments to parse, the + * node_id should be the same as the last fake node id + * (we've handled this above). + */ + if (!p) + return 0; + + mem = memparse(p, &p); + if (!mem) + return 0; + + if (mem < curr_boundary) + return 0; + + curr_boundary = mem; + + if ((end_pfn << PAGE_SHIFT) > mem) { + /* + * Skip commas and spaces + */ + while (*p == ',' || *p == ' ' || *p == '\t') + p++; + + cmdline = p; + fake_nid++; + *nid = fake_nid; + dbg("created new fake_node with id %d\n", fake_nid); + return 1; + } + return 0; +} + static void __cpuinit map_cpu_to_node(int cpu, int node) { numa_cpu_lookup_table[cpu] = node; @@ -344,6 +393,9 @@ static void __init parse_drconf_memory(struct device_node *memory) if (nid == 0xffff || nid >= MAX_NUMNODES) nid = default_nid; } + + fake_numa_create_new_node(((start + lmb_size) >> PAGE_SHIFT), + &nid); node_set_online(nid); size = numa_enforce_memory_limit(start, lmb_size); @@ -429,6 +481,8 @@ new_range: nid = of_node_to_nid_single(memory); if (nid < 0) nid = default_nid; + + fake_numa_create_new_node(((start + size) >> PAGE_SHIFT), &nid); node_set_online(nid); if (!(size = numa_enforce_memory_limit(start, size))) { @@ -461,7 +515,7 @@ static void __init setup_nonnuma(void) unsigned long top_of_ram = lmb_end_of_DRAM(); unsigned long total_ram = lmb_phys_mem_size(); unsigned long start_pfn, end_pfn; - unsigned int i; + unsigned int i, nid = 0; printk(KERN_DEBUG "Top of RAM: 0x%lx, Total RAM: 0x%lx\n", top_of_ram, total_ram); @@ -471,9 +525,11 @@ static void __init setup_nonnuma(void) for (i = 0; i < lmb.memory.cnt; ++i) { start_pfn = lmb.memory.region[i].base >> PAGE_SHIFT; end_pfn = start_pfn + lmb_size_pages(&lmb.memory, i); - add_active_range(0, start_pfn, end_pfn); + + fake_numa_create_new_node(end_pfn, &nid); + add_active_range(nid, start_pfn, end_pfn); + node_set_online(nid); } - node_set_online(0); } void __init dump_numa_cpu_topology(void) @@ -702,6 +758,10 @@ static int __init early_numa(char *p) if (strstr(p, "debug")) numa_debug = 1; + p = strstr(p, "fake="); + if (p) + cmdline = p + strlen("fake="); + return 0; } early_param("numa", early_numa); diff --git a/arch/powerpc/oprofile/Makefile b/arch/powerpc/oprofile/Makefile index c5f64c3bd66..2ef6b0dddd8 100644 --- a/arch/powerpc/oprofile/Makefile +++ b/arch/powerpc/oprofile/Makefile @@ -15,5 +15,5 @@ oprofile-$(CONFIG_OPROFILE_CELL) += op_model_cell.o \ cell/spu_profiler.o cell/vma_map.o \ cell/spu_task_sync.o oprofile-$(CONFIG_PPC64) += op_model_rs64.o op_model_power4.o op_model_pa6t.o -oprofile-$(CONFIG_FSL_BOOKE) += op_model_fsl_booke.o +oprofile-$(CONFIG_FSL_EMB_PERFMON) += op_model_fsl_emb.o oprofile-$(CONFIG_6xx) += op_model_7450.o diff --git a/arch/powerpc/oprofile/common.c b/arch/powerpc/oprofile/common.c index a28cce1d6c2..4908dc98f9c 100644 --- a/arch/powerpc/oprofile/common.c +++ b/arch/powerpc/oprofile/common.c @@ -202,9 +202,9 @@ int __init oprofile_arch_init(struct oprofile_operations *ops) model = &op_model_7450; break; #endif -#ifdef CONFIG_FSL_BOOKE - case PPC_OPROFILE_BOOKE: - model = &op_model_fsl_booke; +#if defined(CONFIG_FSL_EMB_PERFMON) + case PPC_OPROFILE_FSL_EMB: + model = &op_model_fsl_emb; break; #endif default: diff --git a/arch/powerpc/oprofile/op_model_fsl_booke.c b/arch/powerpc/oprofile/op_model_fsl_emb.c index 183a28bb181..91596f6ba1f 100644 --- a/arch/powerpc/oprofile/op_model_fsl_booke.c +++ b/arch/powerpc/oprofile/op_model_fsl_emb.c @@ -1,7 +1,5 @@ /* - * arch/powerpc/oprofile/op_model_fsl_booke.c - * - * Freescale Book-E oprofile support, based on ppc64 oprofile support + * Freescale Embedded oprofile support, based on ppc64 oprofile support * Copyright (C) 2004 Anton Blanchard <anton@au.ibm.com>, IBM * * Copyright (c) 2004 Freescale Semiconductor, Inc @@ -22,7 +20,7 @@ #include <asm/system.h> #include <asm/processor.h> #include <asm/cputable.h> -#include <asm/reg_booke.h> +#include <asm/reg_fsl_emb.h> #include <asm/page.h> #include <asm/pmc.h> #include <asm/oprofile_impl.h> @@ -244,7 +242,7 @@ static void dump_pmcs(void) mfpmr(PMRN_PMLCA3), mfpmr(PMRN_PMLCB3)); } -static int fsl_booke_cpu_setup(struct op_counter_config *ctr) +static int fsl_emb_cpu_setup(struct op_counter_config *ctr) { int i; @@ -262,7 +260,7 @@ static int fsl_booke_cpu_setup(struct op_counter_config *ctr) return 0; } -static int fsl_booke_reg_setup(struct op_counter_config *ctr, +static int fsl_emb_reg_setup(struct op_counter_config *ctr, struct op_system_config *sys, int num_ctrs) { @@ -281,7 +279,7 @@ static int fsl_booke_reg_setup(struct op_counter_config *ctr, return 0; } -static int fsl_booke_start(struct op_counter_config *ctr) +static int fsl_emb_start(struct op_counter_config *ctr) { int i; @@ -315,7 +313,7 @@ static int fsl_booke_start(struct op_counter_config *ctr) return 0; } -static void fsl_booke_stop(void) +static void fsl_emb_stop(void) { /* freeze counters */ pmc_stop_ctrs(); @@ -329,7 +327,7 @@ static void fsl_booke_stop(void) } -static void fsl_booke_handle_interrupt(struct pt_regs *regs, +static void fsl_emb_handle_interrupt(struct pt_regs *regs, struct op_counter_config *ctr) { unsigned long pc; @@ -362,10 +360,10 @@ static void fsl_booke_handle_interrupt(struct pt_regs *regs, pmc_start_ctrs(1); } -struct op_powerpc_model op_model_fsl_booke = { - .reg_setup = fsl_booke_reg_setup, - .cpu_setup = fsl_booke_cpu_setup, - .start = fsl_booke_start, - .stop = fsl_booke_stop, - .handle_interrupt = fsl_booke_handle_interrupt, +struct op_powerpc_model op_model_fsl_emb = { + .reg_setup = fsl_emb_reg_setup, + .cpu_setup = fsl_emb_cpu_setup, + .start = fsl_emb_start, + .stop = fsl_emb_stop, + .handle_interrupt = fsl_emb_handle_interrupt, }; diff --git a/arch/powerpc/platforms/40x/Kconfig b/arch/powerpc/platforms/40x/Kconfig index 74f31177e47..a9260e21451 100644 --- a/arch/powerpc/platforms/40x/Kconfig +++ b/arch/powerpc/platforms/40x/Kconfig @@ -72,6 +72,7 @@ config WALNUT default y select 405GP select PCI + select OF_RTC help This option enables support for the IBM PPC405GP evaluation board. diff --git a/arch/powerpc/platforms/40x/virtex.c b/arch/powerpc/platforms/40x/virtex.c index 88b66444dfb..0422590040d 100644 --- a/arch/powerpc/platforms/40x/virtex.c +++ b/arch/powerpc/platforms/40x/virtex.c @@ -37,7 +37,7 @@ static int __init virtex_probe(void) { unsigned long root = of_get_flat_dt_root(); - if (!of_flat_dt_is_compatible(root, "xilinx,virtex")) + if (!of_flat_dt_is_compatible(root, "xlnx,virtex")) return 0; return 1; diff --git a/arch/powerpc/platforms/40x/walnut.c b/arch/powerpc/platforms/40x/walnut.c index 5d9edd917f9..b8b257efeb7 100644 --- a/arch/powerpc/platforms/40x/walnut.c +++ b/arch/powerpc/platforms/40x/walnut.c @@ -18,6 +18,7 @@ #include <linux/init.h> #include <linux/of_platform.h> +#include <linux/rtc.h> #include <asm/machdep.h> #include <asm/prom.h> diff --git a/arch/powerpc/platforms/44x/warp.c b/arch/powerpc/platforms/44x/warp.c index 8f01563dbd2..da5b7b7599d 100644 --- a/arch/powerpc/platforms/44x/warp.c +++ b/arch/powerpc/platforms/44x/warp.c @@ -137,7 +137,7 @@ static int __init pika_dtm_start(void) } of_node_put(np); - fpga = ioremap(res.start + 0x20, 4); + fpga = ioremap(res.start, 0x24); if (fpga == NULL) return -ENOENT; diff --git a/arch/powerpc/platforms/512x/Kconfig b/arch/powerpc/platforms/512x/Kconfig new file mode 100644 index 00000000000..c6fa49e23dc --- /dev/null +++ b/arch/powerpc/platforms/512x/Kconfig @@ -0,0 +1,20 @@ +config PPC_MPC512x + bool + select FSL_SOC + select IPIC + default n + +config PPC_MPC5121 + bool + select PPC_MPC512x + default n + +config MPC5121_ADS + bool "Freescale MPC5121E ADS" + depends on PPC_MULTIPLATFORM && PPC32 + select DEFAULT_UIMAGE + select WANT_DEVICE_TREE + select PPC_MPC5121 + help + This option enables support for the MPC5121E ADS board. + default n diff --git a/arch/powerpc/platforms/512x/Makefile b/arch/powerpc/platforms/512x/Makefile new file mode 100644 index 00000000000..232c89f2039 --- /dev/null +++ b/arch/powerpc/platforms/512x/Makefile @@ -0,0 +1,4 @@ +# +# Makefile for the Freescale PowerPC 512x linux kernel. +# +obj-$(CONFIG_MPC5121_ADS) += mpc5121_ads.o diff --git a/arch/powerpc/platforms/512x/mpc5121_ads.c b/arch/powerpc/platforms/512x/mpc5121_ads.c new file mode 100644 index 00000000000..50bd3a31902 --- /dev/null +++ b/arch/powerpc/platforms/512x/mpc5121_ads.c @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2007 Freescale Semiconductor, Inc. All rights reserved. + * + * Author: John Rigby, <jrigby@freescale.com>, Thur Mar 29 2007 + * + * Description: + * MPC5121 ADS board setup + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include <linux/kernel.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/of_platform.h> + +#include <asm/machdep.h> +#include <asm/ipic.h> +#include <asm/prom.h> +#include <asm/time.h> + +/** + * mpc512x_find_ips_freq - Find the IPS bus frequency for a device + * @node: device node + * + * Returns IPS bus frequency, or 0 if the bus frequency cannot be found. + */ +unsigned long +mpc512x_find_ips_freq(struct device_node *node) +{ + struct device_node *np; + const unsigned int *p_ips_freq = NULL; + + of_node_get(node); + while (node) { + p_ips_freq = of_get_property(node, "bus-frequency", NULL); + if (p_ips_freq) + break; + + np = of_get_parent(node); + of_node_put(node); + node = np; + } + if (node) + of_node_put(node); + + return p_ips_freq ? *p_ips_freq : 0; +} +EXPORT_SYMBOL(mpc512x_find_ips_freq); + +static struct of_device_id __initdata of_bus_ids[] = { + { .name = "soc", }, + { .name = "localbus", }, + {}, +}; + +static void __init mpc5121_ads_declare_of_platform_devices(void) +{ + /* Find every child of the SOC node and add it to of_platform */ + if (of_platform_bus_probe(NULL, of_bus_ids, NULL)) + printk(KERN_ERR __FILE__ ": " + "Error while probing of_platform bus\n"); +} + +static void __init mpc5121_ads_init_IRQ(void) +{ + struct device_node *np; + + np = of_find_compatible_node(NULL, NULL, "fsl,ipic"); + if (!np) + return; + + ipic_init(np, 0); + of_node_put(np); + + /* + * Initialize the default interrupt mapping priorities, + * in case the boot rom changed something on us. + */ + ipic_set_default_priority(); +} + +/* + * Called very early, MMU is off, device-tree isn't unflattened + */ +static int __init mpc5121_ads_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + + return of_flat_dt_is_compatible(root, "fsl,mpc5121ads"); +} + +define_machine(mpc5121_ads) { + .name = "MPC5121 ADS", + .probe = mpc5121_ads_probe, + .init = mpc5121_ads_declare_of_platform_devices, + .init_IRQ = mpc5121_ads_init_IRQ, + .get_irq = ipic_get_irq, + .calibrate_decr = generic_calibrate_decr, +}; diff --git a/arch/powerpc/platforms/82xx/mpc8272_ads.c b/arch/powerpc/platforms/82xx/mpc8272_ads.c index 3fce6b375db..7d301875198 100644 --- a/arch/powerpc/platforms/82xx/mpc8272_ads.c +++ b/arch/powerpc/platforms/82xx/mpc8272_ads.c @@ -134,13 +134,12 @@ static void __init mpc8272_ads_setup_arch(void) } bcsr = of_iomap(np, 0); + of_node_put(np); if (!bcsr) { printk(KERN_ERR "Cannot map BCSR registers\n"); return; } - of_node_put(np); - clrbits32(&bcsr[1], BCSR1_RS232_EN1 | BCSR1_RS232_EN2 | BCSR1_FETHIEN); setbits32(&bcsr[1], BCSR1_FETH_RST); diff --git a/arch/powerpc/platforms/82xx/pq2fads.c b/arch/powerpc/platforms/82xx/pq2fads.c index 68196e34999..e1dceeec499 100644 --- a/arch/powerpc/platforms/82xx/pq2fads.c +++ b/arch/powerpc/platforms/82xx/pq2fads.c @@ -130,13 +130,12 @@ static void __init pq2fads_setup_arch(void) } bcsr = of_iomap(np, 0); + of_node_put(np); if (!bcsr) { printk(KERN_ERR "Cannot map BCSR registers\n"); return; } - of_node_put(np); - /* Enable the serial and ethernet ports */ clrbits32(&bcsr[1], BCSR1_RS232_EN1 | BCSR1_RS232_EN2 | BCSR1_FETHIEN); diff --git a/arch/powerpc/platforms/83xx/mpc832x_rdb.c b/arch/powerpc/platforms/83xx/mpc832x_rdb.c index 9f0fd88b2b1..e7f706b624f 100644 --- a/arch/powerpc/platforms/83xx/mpc832x_rdb.c +++ b/arch/powerpc/platforms/83xx/mpc832x_rdb.c @@ -101,7 +101,7 @@ static void __init mpc832x_rdb_setup_arch(void) #ifdef CONFIG_QUICC_ENGINE qe_reset(); - if ((np = of_find_node_by_name(np, "par_io")) != NULL) { + if ((np = of_find_node_by_name(NULL, "par_io")) != NULL) { par_io_init(np); of_node_put(np); diff --git a/arch/powerpc/platforms/83xx/mpc83xx.h b/arch/powerpc/platforms/83xx/mpc83xx.h index 88bb748aff0..68065e62fc3 100644 --- a/arch/powerpc/platforms/83xx/mpc83xx.h +++ b/arch/powerpc/platforms/83xx/mpc83xx.h @@ -14,6 +14,8 @@ #define MPC83XX_SCCR_USB_DRCM_11 0x00300000 #define MPC83XX_SCCR_USB_DRCM_01 0x00100000 #define MPC83XX_SCCR_USB_DRCM_10 0x00200000 +#define MPC8315_SCCR_USB_MASK 0x00c00000 +#define MPC8315_SCCR_USB_DRCM_11 0x00c00000 #define MPC837X_SCCR_USB_DRCM_11 0x00c00000 /* system i/o configuration register low */ diff --git a/arch/powerpc/platforms/83xx/usb.c b/arch/powerpc/platforms/83xx/usb.c index 681230a30ac..471fdd8f410 100644 --- a/arch/powerpc/platforms/83xx/usb.c +++ b/arch/powerpc/platforms/83xx/usb.c @@ -104,6 +104,7 @@ int mpc831x_usb_cfg(void) u32 temp; void __iomem *immap, *usb_regs; struct device_node *np = NULL; + struct device_node *immr_node = NULL; const void *prop; struct resource res; int ret = 0; @@ -124,10 +125,15 @@ int mpc831x_usb_cfg(void) } /* 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); + immr_node = of_get_parent(np); + if (immr_node && of_device_is_compatible(immr_node, "fsl,mpc8315-immr")) + clrsetbits_be32(immap + MPC83XX_SCCR_OFFS, + MPC8315_SCCR_USB_MASK, + MPC8315_SCCR_USB_DRCM_11); + else + clrsetbits_be32(immap + MPC83XX_SCCR_OFFS, + MPC83XX_SCCR_USB_MASK, + MPC83XX_SCCR_USB_DRCM_11); /* Configure pin mux for ULPI. There is no pin mux for UTMI */ if (prop && !strcmp(prop, "ulpi")) { @@ -144,6 +150,9 @@ int mpc831x_usb_cfg(void) iounmap(immap); + if (immr_node) + of_node_put(immr_node); + /* Map USB SOC space */ ret = of_address_to_resource(np, 0, &res); if (ret) { diff --git a/arch/powerpc/platforms/8xx/adder875.c b/arch/powerpc/platforms/8xx/adder875.c index c6bc0783c3b..82363e98f50 100644 --- a/arch/powerpc/platforms/8xx/adder875.c +++ b/arch/powerpc/platforms/8xx/adder875.c @@ -15,12 +15,12 @@ #include <asm/time.h> #include <asm/machdep.h> -#include <asm/commproc.h> +#include <asm/cpm1.h> #include <asm/fs_pd.h> #include <asm/udbg.h> #include <asm/prom.h> -#include <sysdev/commproc.h> +#include "mpc8xx.h" struct cpm_pin { int port, pin, flags; @@ -108,7 +108,7 @@ define_machine(adder875) { .name = "Adder MPC875", .probe = adder875_probe, .setup_arch = adder875_setup, - .init_IRQ = m8xx_pic_init, + .init_IRQ = mpc8xx_pics_init, .get_irq = mpc8xx_get_irq, .restart = mpc8xx_restart, .calibrate_decr = generic_calibrate_decr, diff --git a/arch/powerpc/platforms/8xx/ep88xc.c b/arch/powerpc/platforms/8xx/ep88xc.c index a8dffa00577..7d9ac6040d6 100644 --- a/arch/powerpc/platforms/8xx/ep88xc.c +++ b/arch/powerpc/platforms/8xx/ep88xc.c @@ -15,7 +15,6 @@ #include <asm/machdep.h> #include <asm/io.h> #include <asm/udbg.h> -#include <asm/commproc.h> #include <asm/cpm1.h> #include "mpc8xx.h" diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig index fdce10c4f07..fcedbec07f9 100644 --- a/arch/powerpc/platforms/Kconfig +++ b/arch/powerpc/platforms/Kconfig @@ -24,6 +24,7 @@ config PPC_83xx select MPC83xx select IPIC select WANT_DEVICE_TREE + select FSL_EMB_PERFMON config PPC_86xx bool "Freescale 86xx" @@ -41,6 +42,7 @@ config CLASSIC32 source "arch/powerpc/platforms/pseries/Kconfig" source "arch/powerpc/platforms/iseries/Kconfig" source "arch/powerpc/platforms/chrp/Kconfig" +source "arch/powerpc/platforms/512x/Kconfig" source "arch/powerpc/platforms/52xx/Kconfig" source "arch/powerpc/platforms/powermac/Kconfig" source "arch/powerpc/platforms/prep/Kconfig" diff --git a/arch/powerpc/platforms/Kconfig.cputype b/arch/powerpc/platforms/Kconfig.cputype index 7fc41104d53..69941ba7097 100644 --- a/arch/powerpc/platforms/Kconfig.cputype +++ b/arch/powerpc/platforms/Kconfig.cputype @@ -14,7 +14,7 @@ choice 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. + embedded 512x/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. @@ -22,7 +22,7 @@ choice If unsure, select 52xx/6xx/7xx/74xx/82xx/83xx/86xx. config 6xx - bool "52xx/6xx/7xx/74xx/82xx/83xx/86xx" + bool "512x/52xx/6xx/7xx/74xx/82xx/83xx/86xx" select PPC_FPU config PPC_85xx @@ -94,6 +94,7 @@ config 8xx bool config E500 + select FSL_EMB_PERFMON bool config PPC_FPU @@ -115,6 +116,9 @@ config FSL_BOOKE depends on E200 || E500 default y +config FSL_EMB_PERFMON + bool + config PTE_64BIT bool depends on 44x || E500 @@ -221,7 +225,7 @@ config NR_CPUS config NOT_COHERENT_CACHE bool - depends on 4xx || 8xx || E200 + depends on 4xx || 8xx || E200 || PPC_MPC512x default y config CHECK_CACHE_COHERENCY diff --git a/arch/powerpc/platforms/Makefile b/arch/powerpc/platforms/Makefile index 6d9079da5f5..a984894466d 100644 --- a/arch/powerpc/platforms/Makefile +++ b/arch/powerpc/platforms/Makefile @@ -11,6 +11,7 @@ endif obj-$(CONFIG_PPC_CHRP) += chrp/ obj-$(CONFIG_40x) += 40x/ obj-$(CONFIG_44x) += 44x/ +obj-$(CONFIG_PPC_MPC512x) += 512x/ obj-$(CONFIG_PPC_MPC52xx) += 52xx/ obj-$(CONFIG_PPC_8xx) += 8xx/ obj-$(CONFIG_PPC_82xx) += 82xx/ diff --git a/arch/powerpc/platforms/cell/Kconfig b/arch/powerpc/platforms/cell/Kconfig index 3a963b4a9be..2f169991896 100644 --- a/arch/powerpc/platforms/cell/Kconfig +++ b/arch/powerpc/platforms/cell/Kconfig @@ -54,6 +54,13 @@ config SPU_FS_64K_LS uses 4K pages. This can improve performances of applications using multiple SPEs by lowering the TLB pressure on them. +config SPU_TRACE + tristate "SPU event tracing support" + depends on SPU_FS && MARKERS + help + This option allows reading a trace of spu-related events through + the sputrace file in procfs. + config SPU_BASE bool default n diff --git a/arch/powerpc/platforms/cell/axon_msi.c b/arch/powerpc/platforms/cell/axon_msi.c index 095988f13bf..d95e71dee91 100644 --- a/arch/powerpc/platforms/cell/axon_msi.c +++ b/arch/powerpc/platforms/cell/axon_msi.c @@ -13,7 +13,7 @@ #include <linux/kernel.h> #include <linux/pci.h> #include <linux/msi.h> -#include <linux/reboot.h> +#include <linux/of_platform.h> #include <asm/dcr.h> #include <asm/machdep.h> @@ -65,14 +65,12 @@ struct axon_msic { struct irq_host *irq_host; - __le32 *fifo; + __le32 *fifo_virt; + dma_addr_t fifo_phys; dcr_host_t dcr_host; - struct list_head list; u32 read_offset; }; -static LIST_HEAD(axon_msic_list); - static void msic_dcr_write(struct axon_msic *msic, unsigned int dcr_n, u32 val) { pr_debug("axon_msi: dcr_write(0x%x, 0x%x)\n", val, dcr_n); @@ -94,7 +92,7 @@ static void axon_msi_cascade(unsigned int irq, struct irq_desc *desc) while (msic->read_offset != write_offset) { idx = msic->read_offset / sizeof(__le32); - msi = le32_to_cpu(msic->fifo[idx]); + msi = le32_to_cpu(msic->fifo_virt[idx]); msi &= 0xFFFF; pr_debug("axon_msi: woff %x roff %x msi %x\n", @@ -139,6 +137,7 @@ static struct axon_msic *find_msi_translator(struct pci_dev *dev) tmp = dn; dn = of_find_node_by_phandle(*ph); + of_node_put(tmp); if (!dn) { dev_dbg(&dev->dev, "axon_msi: msi-translator doesn't point to a node\n"); @@ -156,7 +155,6 @@ static struct axon_msic *find_msi_translator(struct pci_dev *dev) out_error: of_node_put(dn); - of_node_put(tmp); return msic; } @@ -292,30 +290,24 @@ static struct irq_host_ops msic_host_ops = { .map = msic_host_map, }; -static int axon_msi_notify_reboot(struct notifier_block *nb, - unsigned long code, void *data) +static int axon_msi_shutdown(struct of_device *device) { - struct axon_msic *msic; + struct axon_msic *msic = device->dev.platform_data; u32 tmp; - list_for_each_entry(msic, &axon_msic_list, list) { - pr_debug("axon_msi: disabling %s\n", - msic->irq_host->of_node->full_name); - tmp = dcr_read(msic->dcr_host, MSIC_CTRL_REG); - tmp &= ~MSIC_CTRL_ENABLE & ~MSIC_CTRL_IRQ_ENABLE; - msic_dcr_write(msic, MSIC_CTRL_REG, tmp); - } + pr_debug("axon_msi: disabling %s\n", + msic->irq_host->of_node->full_name); + tmp = dcr_read(msic->dcr_host, MSIC_CTRL_REG); + tmp &= ~MSIC_CTRL_ENABLE & ~MSIC_CTRL_IRQ_ENABLE; + msic_dcr_write(msic, MSIC_CTRL_REG, tmp); return 0; } -static struct notifier_block axon_msi_reboot_notifier = { - .notifier_call = axon_msi_notify_reboot -}; - -static int axon_msi_setup_one(struct device_node *dn) +static int axon_msi_probe(struct of_device *device, + const struct of_device_id *device_id) { - struct page *page; + struct device_node *dn = device->node; struct axon_msic *msic; unsigned int virq; int dcr_base, dcr_len; @@ -346,16 +338,14 @@ static int axon_msi_setup_one(struct device_node *dn) goto out_free_msic; } - page = alloc_pages_node(of_node_to_nid(dn), GFP_KERNEL, - get_order(MSIC_FIFO_SIZE_BYTES)); - if (!page) { + msic->fifo_virt = dma_alloc_coherent(&device->dev, MSIC_FIFO_SIZE_BYTES, + &msic->fifo_phys, GFP_KERNEL); + if (!msic->fifo_virt) { printk(KERN_ERR "axon_msi: couldn't allocate fifo for %s\n", dn->full_name); goto out_free_msic; } - msic->fifo = page_address(page); - msic->irq_host = irq_alloc_host(of_node_get(dn), IRQ_HOST_MAP_NOMAP, NR_IRQS, &msic_host_ops, 0); if (!msic->irq_host) { @@ -378,14 +368,18 @@ static int axon_msi_setup_one(struct device_node *dn) pr_debug("axon_msi: irq 0x%x setup for axon_msi\n", virq); /* Enable the MSIC hardware */ - msic_dcr_write(msic, MSIC_BASE_ADDR_HI_REG, (u64)msic->fifo >> 32); + msic_dcr_write(msic, MSIC_BASE_ADDR_HI_REG, msic->fifo_phys >> 32); msic_dcr_write(msic, MSIC_BASE_ADDR_LO_REG, - (u64)msic->fifo & 0xFFFFFFFF); + msic->fifo_phys & 0xFFFFFFFF); msic_dcr_write(msic, MSIC_CTRL_REG, MSIC_CTRL_IRQ_ENABLE | MSIC_CTRL_ENABLE | MSIC_CTRL_FIFO_SIZE); - list_add(&msic->list, &axon_msic_list); + device->dev.platform_data = msic; + + ppc_md.setup_msi_irqs = axon_msi_setup_msi_irqs; + ppc_md.teardown_msi_irqs = axon_msi_teardown_msi_irqs; + ppc_md.msi_check_device = axon_msi_check_device; printk(KERN_DEBUG "axon_msi: setup MSIC on %s\n", dn->full_name); @@ -394,7 +388,8 @@ static int axon_msi_setup_one(struct device_node *dn) out_free_host: kfree(msic->irq_host); out_free_fifo: - __free_pages(virt_to_page(msic->fifo), get_order(MSIC_FIFO_SIZE_BYTES)); + dma_free_coherent(&device->dev, MSIC_FIFO_SIZE_BYTES, msic->fifo_virt, + msic->fifo_phys); out_free_msic: kfree(msic); out: @@ -402,28 +397,24 @@ out: return -1; } -static int axon_msi_init(void) -{ - struct device_node *dn; - int found = 0; - - pr_debug("axon_msi: initialising ...\n"); - - for_each_compatible_node(dn, NULL, "ibm,axon-msic") { - if (axon_msi_setup_one(dn) == 0) - found++; - } - - if (found) { - ppc_md.setup_msi_irqs = axon_msi_setup_msi_irqs; - ppc_md.teardown_msi_irqs = axon_msi_teardown_msi_irqs; - ppc_md.msi_check_device = axon_msi_check_device; - - register_reboot_notifier(&axon_msi_reboot_notifier); +static const struct of_device_id axon_msi_device_id[] = { + { + .compatible = "ibm,axon-msic" + }, + {} +}; - pr_debug("axon_msi: registered callbacks!\n"); - } +static struct of_platform_driver axon_msi_driver = { + .match_table = axon_msi_device_id, + .probe = axon_msi_probe, + .shutdown = axon_msi_shutdown, + .driver = { + .name = "axon-msi" + }, +}; - return 0; +static int __init axon_msi_init(void) +{ + return of_register_platform_driver(&axon_msi_driver); } -arch_initcall(axon_msi_init); +subsys_initcall(axon_msi_init); diff --git a/arch/powerpc/platforms/cell/setup.c b/arch/powerpc/platforms/cell/setup.c index e6534b519c9..a7f609b3b87 100644 --- a/arch/powerpc/platforms/cell/setup.c +++ b/arch/powerpc/platforms/cell/setup.c @@ -98,7 +98,7 @@ static int __init cell_publish_devices(void) } return 0; } -machine_device_initcall(cell, cell_publish_devices); +machine_subsys_initcall(cell, cell_publish_devices); static void cell_mpic_cascade(unsigned int irq, struct irq_desc *desc) { diff --git a/arch/powerpc/platforms/cell/spufs/Makefile b/arch/powerpc/platforms/cell/spufs/Makefile index d3a349fb42e..99610a6361f 100644 --- a/arch/powerpc/platforms/cell/spufs/Makefile +++ b/arch/powerpc/platforms/cell/spufs/Makefile @@ -4,6 +4,8 @@ spufs-y += inode.o file.o context.o syscalls.o coredump.o spufs-y += sched.o backing_ops.o hw_ops.o run.o gang.o spufs-y += switch.o fault.o lscsa_alloc.o +obj-$(CONFIG_SPU_TRACE) += sputrace.o + # Rules to build switch.o with the help of SPU tool chain SPU_CROSS := spu- SPU_CC := $(SPU_CROSS)gcc diff --git a/arch/powerpc/platforms/cell/spufs/file.c b/arch/powerpc/platforms/cell/spufs/file.c index 3fcd06418b0..1018acd1746 100644 --- a/arch/powerpc/platforms/cell/spufs/file.c +++ b/arch/powerpc/platforms/cell/spufs/file.c @@ -29,6 +29,7 @@ #include <linux/poll.h> #include <linux/ptrace.h> #include <linux/seq_file.h> +#include <linux/marker.h> #include <asm/io.h> #include <asm/semaphore.h> @@ -358,6 +359,8 @@ static unsigned long spufs_ps_nopfn(struct vm_area_struct *vma, struct spu_context *ctx = vma->vm_file->private_data; unsigned long area, offset = address - vma->vm_start; + spu_context_nospu_trace(spufs_ps_nopfn__enter, ctx); + offset += vma->vm_pgoff << PAGE_SHIFT; if (offset >= ps_size) return NOPFN_SIGBUS; @@ -375,11 +378,14 @@ static unsigned long spufs_ps_nopfn(struct vm_area_struct *vma, if (ctx->state == SPU_STATE_SAVED) { up_read(¤t->mm->mmap_sem); + spu_context_nospu_trace(spufs_ps_nopfn__sleep, ctx); spufs_wait(ctx->run_wq, ctx->state == SPU_STATE_RUNNABLE); + spu_context_trace(spufs_ps_nopfn__wake, ctx, ctx->spu); down_read(¤t->mm->mmap_sem); } else { area = ctx->spu->problem_phys + ps_offs; vm_insert_pfn(vma, address, (area + offset) >> PAGE_SHIFT); + spu_context_trace(spufs_ps_nopfn__insert, ctx, ctx->spu); } spu_release(ctx); diff --git a/arch/powerpc/platforms/cell/spufs/inode.c b/arch/powerpc/platforms/cell/spufs/inode.c index c0e968a4c21..90784c029f2 100644 --- a/arch/powerpc/platforms/cell/spufs/inode.c +++ b/arch/powerpc/platforms/cell/spufs/inode.c @@ -322,7 +322,7 @@ static struct spu_context * spufs_assert_affinity(unsigned int flags, struct spu_gang *gang, struct file *filp) { - struct spu_context *tmp, *neighbor; + struct spu_context *tmp, *neighbor, *err; int count, node; int aff_supp; @@ -354,11 +354,15 @@ spufs_assert_affinity(unsigned int flags, struct spu_gang *gang, if (!list_empty(&neighbor->aff_list) && !(neighbor->aff_head) && !list_is_last(&neighbor->aff_list, &gang->aff_list_head) && !list_entry(neighbor->aff_list.next, struct spu_context, - aff_list)->aff_head) - return ERR_PTR(-EEXIST); + aff_list)->aff_head) { + err = ERR_PTR(-EEXIST); + goto out_put_neighbor; + } - if (gang != neighbor->gang) - return ERR_PTR(-EINVAL); + if (gang != neighbor->gang) { + err = ERR_PTR(-EINVAL); + goto out_put_neighbor; + } count = 1; list_for_each_entry(tmp, &gang->aff_list_head, aff_list) @@ -372,11 +376,17 @@ spufs_assert_affinity(unsigned int flags, struct spu_gang *gang, break; } - if (node == MAX_NUMNODES) - return ERR_PTR(-EEXIST); + if (node == MAX_NUMNODES) { + err = ERR_PTR(-EEXIST); + goto out_put_neighbor; + } } return neighbor; + +out_put_neighbor: + put_spu_context(neighbor); + return err; } static void @@ -454,9 +464,12 @@ spufs_create_context(struct inode *inode, struct dentry *dentry, if (ret) goto out_aff_unlock; - if (affinity) + if (affinity) { spufs_set_affinity(flags, SPUFS_I(dentry->d_inode)->i_ctx, neighbor); + if (neighbor) + put_spu_context(neighbor); + } /* * get references for dget and mntget, will be released diff --git a/arch/powerpc/platforms/cell/spufs/run.c b/arch/powerpc/platforms/cell/spufs/run.c index c01a09da1e5..b4814c740d8 100644 --- a/arch/powerpc/platforms/cell/spufs/run.c +++ b/arch/powerpc/platforms/cell/spufs/run.c @@ -410,8 +410,11 @@ long spufs_run_spu(struct spu_context *ctx, u32 *npc, u32 *event) * 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) { + if (unlikely(status & SPU_STATUS_SINGLE_STEP)) + ret = -ERESTARTSYS; + + else if (unlikely((status & SPU_STATUS_STOPPED_BY_STOP) + && (status >> SPU_STOP_STATUS_SHIFT) == 0x3fff)) { force_sig(SIGTRAP, current); ret = -ERESTARTSYS; } diff --git a/arch/powerpc/platforms/cell/spufs/sched.c b/arch/powerpc/platforms/cell/spufs/sched.c index 00d914232af..5915343e259 100644 --- a/arch/powerpc/platforms/cell/spufs/sched.c +++ b/arch/powerpc/platforms/cell/spufs/sched.c @@ -39,6 +39,7 @@ #include <linux/pid_namespace.h> #include <linux/proc_fs.h> #include <linux/seq_file.h> +#include <linux/marker.h> #include <asm/io.h> #include <asm/mmu_context.h> @@ -216,8 +217,8 @@ void do_notify_spus_active(void) */ 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); + spu_context_trace(spu_bind_context__enter, ctx, spu); + spuctx_switch_state(ctx, SPU_UTIL_SYSTEM); if (ctx->flags & SPU_CREATE_NOSCHED) @@ -399,8 +400,8 @@ static int has_affinity(struct spu_context *ctx) */ 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_context_trace(spu_unbind_context__enter, ctx, spu); + spuctx_switch_state(ctx, SPU_UTIL_SYSTEM); if (spu->ctx->flags & SPU_CREATE_NOSCHED) @@ -528,6 +529,8 @@ static struct spu *spu_get_idle(struct spu_context *ctx) struct spu *spu, *aff_ref_spu; int node, n; + spu_context_nospu_trace(spu_get_idle__enter, ctx); + if (ctx->gang) { mutex_lock(&ctx->gang->aff_mutex); if (has_affinity(ctx)) { @@ -546,8 +549,7 @@ static struct spu *spu_get_idle(struct spu_context *ctx) if (atomic_dec_and_test(&ctx->gang->aff_sched_count)) ctx->gang->aff_ref_spu = NULL; mutex_unlock(&ctx->gang->aff_mutex); - - return NULL; + goto not_found; } mutex_unlock(&ctx->gang->aff_mutex); } @@ -565,12 +567,14 @@ static struct spu *spu_get_idle(struct spu_context *ctx) mutex_unlock(&cbe_spu_info[node].list_mutex); } + not_found: + spu_context_nospu_trace(spu_get_idle__not_found, ctx); return NULL; found: spu->alloc_state = SPU_USED; mutex_unlock(&cbe_spu_info[node].list_mutex); - pr_debug("Got SPU %d %d\n", spu->number, spu->node); + spu_context_trace(spu_get_idle__found, ctx, spu); spu_init_channels(spu); return spu; } @@ -587,6 +591,8 @@ static struct spu *find_victim(struct spu_context *ctx) struct spu *spu; int node, n; + spu_context_nospu_trace(spu_find_vitim__enter, ctx); + /* * Look for a possible preemption candidate on the local node first. * If there is no candidate look at the other nodes. This isn't @@ -640,6 +646,8 @@ static struct spu *find_victim(struct spu_context *ctx) goto restart; } + spu_context_trace(__spu_deactivate__unload, ctx, spu); + mutex_lock(&cbe_spu_info[node].list_mutex); cbe_spu_info[node].nr_active--; spu_unbind_context(spu, victim); @@ -822,6 +830,7 @@ static int __spu_deactivate(struct spu_context *ctx, int force, int max_prio) */ void spu_deactivate(struct spu_context *ctx) { + spu_context_nospu_trace(spu_deactivate__enter, ctx); __spu_deactivate(ctx, 1, MAX_PRIO); } @@ -835,6 +844,7 @@ void spu_deactivate(struct spu_context *ctx) */ void spu_yield(struct spu_context *ctx) { + spu_context_nospu_trace(spu_yield__enter, ctx); if (!(ctx->flags & SPU_CREATE_NOSCHED)) { mutex_lock(&ctx->state_mutex); __spu_deactivate(ctx, 0, MAX_PRIO); @@ -864,11 +874,15 @@ static noinline void spusched_tick(struct spu_context *ctx) goto out; spu = ctx->spu; + + spu_context_trace(spusched_tick__preempt, ctx, spu); + new = grab_runnable_context(ctx->prio + 1, spu->node); if (new) { spu_unschedule(spu, ctx); spu_add_to_rq(ctx); } else { + spu_context_nospu_trace(spusched_tick__newslice, ctx); ctx->time_slice++; } out: diff --git a/arch/powerpc/platforms/cell/spufs/spufs.h b/arch/powerpc/platforms/cell/spufs/spufs.h index 0e114038ea6..795a1b52538 100644 --- a/arch/powerpc/platforms/cell/spufs/spufs.h +++ b/arch/powerpc/platforms/cell/spufs/spufs.h @@ -325,4 +325,9 @@ extern void spu_free_lscsa(struct spu_state *csa); extern void spuctx_switch_state(struct spu_context *ctx, enum spu_utilization_state new_state); +#define spu_context_trace(name, ctx, spu) \ + trace_mark(name, "%p %p", ctx, spu); +#define spu_context_nospu_trace(name, ctx) \ + trace_mark(name, "%p", ctx); + #endif diff --git a/arch/powerpc/platforms/cell/spufs/sputrace.c b/arch/powerpc/platforms/cell/spufs/sputrace.c new file mode 100644 index 00000000000..2b1953f6f12 --- /dev/null +++ b/arch/powerpc/platforms/cell/spufs/sputrace.c @@ -0,0 +1,250 @@ +/* + * Copyright (C) 2007 IBM Deutschland Entwicklung GmbH + * Released under GPL v2. + * + * Partially based on net/ipv4/tcp_probe.c. + * + * Simple tracing facility for spu contexts. + */ +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/marker.h> +#include <linux/proc_fs.h> +#include <linux/wait.h> +#include <asm/atomic.h> +#include <asm/uaccess.h> +#include "spufs.h" + +struct spu_probe { + const char *name; + const char *format; + marker_probe_func *probe_func; +}; + +struct sputrace { + ktime_t tstamp; + int owner_tid; /* owner */ + int curr_tid; + const char *name; + int number; +}; + +static int bufsize __read_mostly = 16384; +MODULE_PARM_DESC(bufsize, "Log buffer size (number of records)"); +module_param(bufsize, int, 0); + + +static DEFINE_SPINLOCK(sputrace_lock); +static DECLARE_WAIT_QUEUE_HEAD(sputrace_wait); +static ktime_t sputrace_start; +static unsigned long sputrace_head, sputrace_tail; +static struct sputrace *sputrace_log; + +static int sputrace_used(void) +{ + return (sputrace_head - sputrace_tail) % bufsize; +} + +static inline int sputrace_avail(void) +{ + return bufsize - sputrace_used(); +} + +static int sputrace_sprint(char *tbuf, int n) +{ + const struct sputrace *t = sputrace_log + sputrace_tail % bufsize; + struct timespec tv = + ktime_to_timespec(ktime_sub(t->tstamp, sputrace_start)); + + return snprintf(tbuf, n, + "[%lu.%09lu] %d: %s (thread = %d, spu = %d)\n", + (unsigned long) tv.tv_sec, + (unsigned long) tv.tv_nsec, + t->owner_tid, + t->name, + t->curr_tid, + t->number); +} + +static ssize_t sputrace_read(struct file *file, char __user *buf, + size_t len, loff_t *ppos) +{ + int error = 0, cnt = 0; + + if (!buf || len < 0) + return -EINVAL; + + while (cnt < len) { + char tbuf[128]; + int width; + + error = wait_event_interruptible(sputrace_wait, + sputrace_used() > 0); + if (error) + break; + + spin_lock(&sputrace_lock); + if (sputrace_head == sputrace_tail) { + spin_unlock(&sputrace_lock); + continue; + } + + width = sputrace_sprint(tbuf, sizeof(tbuf)); + if (width < len) + sputrace_tail = (sputrace_tail + 1) % bufsize; + spin_unlock(&sputrace_lock); + + if (width >= len) + break; + + error = copy_to_user(buf + cnt, tbuf, width); + if (error) + break; + cnt += width; + } + + return cnt == 0 ? error : cnt; +} + +static int sputrace_open(struct inode *inode, struct file *file) +{ + spin_lock(&sputrace_lock); + sputrace_head = sputrace_tail = 0; + sputrace_start = ktime_get(); + spin_unlock(&sputrace_lock); + + return 0; +} + +static const struct file_operations sputrace_fops = { + .owner = THIS_MODULE, + .open = sputrace_open, + .read = sputrace_read, +}; + +static void sputrace_log_item(const char *name, struct spu_context *ctx, + struct spu *spu) +{ + spin_lock(&sputrace_lock); + if (sputrace_avail() > 1) { + struct sputrace *t = sputrace_log + sputrace_head; + + t->tstamp = ktime_get(); + t->owner_tid = ctx->tid; + t->name = name; + t->curr_tid = current->pid; + t->number = spu ? spu->number : -1; + + sputrace_head = (sputrace_head + 1) % bufsize; + } else { + printk(KERN_WARNING + "sputrace: lost samples due to full buffer.\n"); + } + spin_unlock(&sputrace_lock); + + wake_up(&sputrace_wait); +} + +static void spu_context_event(const struct marker *mdata, + void *private, const char *format, ...) +{ + struct spu_probe *p = mdata->private; + va_list ap; + struct spu_context *ctx; + struct spu *spu; + + va_start(ap, format); + ctx = va_arg(ap, struct spu_context *); + spu = va_arg(ap, struct spu *); + + sputrace_log_item(p->name, ctx, spu); + va_end(ap); +} + +static void spu_context_nospu_event(const struct marker *mdata, + void *private, const char *format, ...) +{ + struct spu_probe *p = mdata->private; + va_list ap; + struct spu_context *ctx; + + va_start(ap, format); + ctx = va_arg(ap, struct spu_context *); + + sputrace_log_item(p->name, ctx, NULL); + va_end(ap); +} + +struct spu_probe spu_probes[] = { + { "spu_bind_context__enter", "%p %p", spu_context_event }, + { "spu_unbind_context__enter", "%p %p", spu_context_event }, + { "spu_get_idle__enter", "%p", spu_context_nospu_event }, + { "spu_get_idle__found", "%p %p", spu_context_event }, + { "spu_get_idle__not_found", "%p", spu_context_nospu_event }, + { "spu_find_victim__enter", "%p", spu_context_nospu_event }, + { "spusched_tick__preempt", "%p %p", spu_context_event }, + { "spusched_tick__newslice", "%p", spu_context_nospu_event }, + { "spu_yield__enter", "%p", spu_context_nospu_event }, + { "spu_deactivate__enter", "%p", spu_context_nospu_event }, + { "__spu_deactivate__unload", "%p %p", spu_context_event }, + { "spufs_ps_nopfn__enter", "%p", spu_context_nospu_event }, + { "spufs_ps_nopfn__sleep", "%p", spu_context_nospu_event }, + { "spufs_ps_nopfn__wake", "%p %p", spu_context_event }, + { "spufs_ps_nopfn__insert", "%p %p", spu_context_event }, + { "spu_acquire_saved__enter", "%p", spu_context_nospu_event }, + { "destroy_spu_context__enter", "%p", spu_context_nospu_event }, +}; + +static int __init sputrace_init(void) +{ + struct proc_dir_entry *entry; + int i, error = -ENOMEM; + + sputrace_log = kcalloc(sizeof(struct sputrace), + bufsize, GFP_KERNEL); + if (!sputrace_log) + goto out; + + entry = create_proc_entry("sputrace", S_IRUSR, NULL); + if (!entry) + goto out_free_log; + entry->proc_fops = &sputrace_fops; + + for (i = 0; i < ARRAY_SIZE(spu_probes); i++) { + struct spu_probe *p = &spu_probes[i]; + + error = marker_probe_register(p->name, p->format, + p->probe_func, p); + if (error) + printk(KERN_INFO "Unable to register probe %s\n", + p->name); + + error = marker_arm(p->name); + if (error) + printk(KERN_INFO "Unable to arm probe %s\n", p->name); + } + + return 0; + +out_free_log: + kfree(sputrace_log); +out: + return -ENOMEM; +} + +static void __exit sputrace_exit(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(spu_probes); i++) + marker_probe_unregister(spu_probes[i].name); + + remove_proc_entry("sputrace", NULL); + kfree(sputrace_log); +} + +module_init(sputrace_init); +module_exit(sputrace_exit); + +MODULE_LICENSE("GPL"); diff --git a/arch/powerpc/platforms/embedded6xx/storcenter.c b/arch/powerpc/platforms/embedded6xx/storcenter.c index e12e9d29871..8864e488498 100644 --- a/arch/powerpc/platforms/embedded6xx/storcenter.c +++ b/arch/powerpc/platforms/embedded6xx/storcenter.c @@ -132,33 +132,18 @@ static void __init storcenter_init_IRQ(void) paddr = (phys_addr_t)of_translate_address(dnp, prop); mpic = mpic_alloc(dnp, paddr, MPIC_PRIMARY | MPIC_WANTS_RESET, - 4, 32, " EPIC "); + 16, 32, " OpenPIC "); of_node_put(dnp); BUG_ON(mpic == NULL); - /* PCI IRQs */ /* - * 2.6.12 patch: - * openpic_set_sources(0, 5, OpenPIC_Addr + 0x10200); - * openpic_set_sources(5, 2, OpenPIC_Addr + 0x11120); - * first_irq, num_irqs, __iomem first_ISR - * o_ss: i, src: 0, fdf50200 - * o_ss: i, src: 1, fdf50220 - * o_ss: i, src: 2, fdf50240 - * o_ss: i, src: 3, fdf50260 - * o_ss: i, src: 4, fdf50280 - * o_ss: i, src: 5, fdf51120 - * o_ss: i, src: 6, fdf51140 + * 16 Serial Interrupts followed by 16 Internal Interrupts. + * I2C is the second internal, so it is at 17, 0x11020. */ mpic_assign_isu(mpic, 0, paddr + 0x10200); - mpic_assign_isu(mpic, 1, paddr + 0x10220); - mpic_assign_isu(mpic, 2, paddr + 0x10240); - mpic_assign_isu(mpic, 3, paddr + 0x10260); - mpic_assign_isu(mpic, 4, paddr + 0x10280); - mpic_assign_isu(mpic, 5, paddr + 0x11120); - mpic_assign_isu(mpic, 6, paddr + 0x11140); + mpic_assign_isu(mpic, 1, paddr + 0x11000); mpic_init(mpic); } @@ -178,7 +163,7 @@ static int __init storcenter_probe(void) { unsigned long root = of_get_flat_dt_root(); - return of_flat_dt_is_compatible(root, "storcenter"); + return of_flat_dt_is_compatible(root, "iomega,storcenter"); } define_machine(storcenter){ diff --git a/arch/powerpc/platforms/pseries/hotplug-cpu.c b/arch/powerpc/platforms/pseries/hotplug-cpu.c index c4ad54e0f28..1f032483c02 100644 --- a/arch/powerpc/platforms/pseries/hotplug-cpu.c +++ b/arch/powerpc/platforms/pseries/hotplug-cpu.c @@ -58,7 +58,7 @@ static void pseries_mach_cpu_die(void) { local_irq_disable(); idle_task_exit(); - xics_teardown_cpu(0); + xics_teardown_cpu(); unregister_slb_shadow(hard_smp_processor_id(), __pa(get_slb_shadow())); rtas_stop_self(); /* Should never get here... */ diff --git a/arch/powerpc/platforms/pseries/kexec.c b/arch/powerpc/platforms/pseries/kexec.c index 412a5e7aff2..e9dd5fe081c 100644 --- a/arch/powerpc/platforms/pseries/kexec.c +++ b/arch/powerpc/platforms/pseries/kexec.c @@ -54,7 +54,7 @@ void __init setup_kexec_cpu_down_mpic(void) static void pseries_kexec_cpu_down_xics(int crash_shutdown, int secondary) { pseries_kexec_cpu_down(crash_shutdown, secondary); - xics_teardown_cpu(secondary); + xics_kexec_teardown_cpu(secondary); } void __init setup_kexec_cpu_down_xics(void) diff --git a/arch/powerpc/platforms/pseries/reconfig.c b/arch/powerpc/platforms/pseries/reconfig.c index c02f8742c54..2800fced8c7 100644 --- a/arch/powerpc/platforms/pseries/reconfig.c +++ b/arch/powerpc/platforms/pseries/reconfig.c @@ -167,6 +167,7 @@ static int pSeries_reconfig_remove_node(struct device_node *np) if ((child = of_get_next_child(np, NULL))) { of_node_put(child); + of_node_put(parent); return -EBUSY; } diff --git a/arch/powerpc/platforms/pseries/xics.c b/arch/powerpc/platforms/pseries/xics.c index 8f8dd9c3ca6..ca52b587166 100644 --- a/arch/powerpc/platforms/pseries/xics.c +++ b/arch/powerpc/platforms/pseries/xics.c @@ -160,6 +160,46 @@ static inline void lpar_qirr_info(int n_cpu , u8 value) /* High level handlers and init code */ +static void xics_update_irq_servers(void) +{ + int i, j; + struct device_node *np; + u32 ilen; + const u32 *ireg, *isize; + u32 hcpuid; + + /* Find the server numbers for the boot cpu. */ + np = of_get_cpu_node(boot_cpuid, NULL); + BUG_ON(!np); + + ireg = of_get_property(np, "ibm,ppc-interrupt-gserver#s", &ilen); + if (!ireg) { + of_node_put(np); + return; + } + + i = ilen / sizeof(int); + hcpuid = get_hard_smp_processor_id(boot_cpuid); + + /* Global interrupt distribution server is specified in the last + * entry of "ibm,ppc-interrupt-gserver#s" property. Get the last + * entry fom this property for current boot cpu id and use it as + * default distribution server + */ + for (j = 0; j < i; j += 2) { + if (ireg[j] == hcpuid) { + default_server = hcpuid; + default_distrib_server = ireg[j+1]; + + isize = of_get_property(np, + "ibm,interrupt-server#-size", NULL); + if (isize) + interrupt_server_size = *isize; + } + } + + of_node_put(np); +} #ifdef CONFIG_SMP static int get_irq_server(unsigned int virq, unsigned int strict_check) @@ -169,6 +209,9 @@ static int get_irq_server(unsigned int virq, unsigned int strict_check) cpumask_t cpumask = irq_desc[virq].affinity; cpumask_t tmp = CPU_MASK_NONE; + if (! cpu_isset(default_server, cpu_online_map)) + xics_update_irq_servers(); + if (!distribute_irqs) return default_server; @@ -658,39 +701,11 @@ static void __init xics_setup_8259_cascade(void) set_irq_chained_handler(cascade, pseries_8259_cascade); } -static struct device_node *cpuid_to_of_node(int cpu) -{ - struct device_node *np; - u32 hcpuid = get_hard_smp_processor_id(cpu); - - for_each_node_by_type(np, "cpu") { - int i, len; - const u32 *intserv; - - intserv = of_get_property(np, "ibm,ppc-interrupt-server#s", - &len); - - if (!intserv) - intserv = of_get_property(np, "reg", &len); - - i = len / sizeof(u32); - - while (i--) - if (intserv[i] == hcpuid) - return np; - } - - return NULL; -} - void __init xics_init_IRQ(void) { - int i, j; struct device_node *np; - u32 ilen, indx = 0; - const u32 *ireg, *isize; + u32 indx = 0; int found = 0; - u32 hcpuid; ppc64_boot_msg(0x20, "XICS Init"); @@ -709,34 +724,7 @@ void __init xics_init_IRQ(void) return; xics_init_host(); - - /* Find the server numbers for the boot cpu. */ - np = cpuid_to_of_node(boot_cpuid); - BUG_ON(!np); - ireg = of_get_property(np, "ibm,ppc-interrupt-gserver#s", &ilen); - if (!ireg) - goto skip_gserver_check; - i = ilen / sizeof(int); - hcpuid = get_hard_smp_processor_id(boot_cpuid); - - /* Global interrupt distribution server is specified in the last - * entry of "ibm,ppc-interrupt-gserver#s" property. Get the last - * entry fom this property for current boot cpu id and use it as - * default distribution server - */ - for (j = 0; j < i; j += 2) { - if (ireg[j] == hcpuid) { - default_server = hcpuid; - default_distrib_server = ireg[j+1]; - - isize = of_get_property(np, - "ibm,interrupt-server#-size", NULL); - if (isize) - interrupt_server_size = *isize; - } - } -skip_gserver_check: - of_node_put(np); + xics_update_irq_servers(); if (firmware_has_feature(FW_FEATURE_LPAR)) ppc_md.get_irq = xics_get_irq_lpar; @@ -775,11 +763,9 @@ void xics_request_IPIs(void) } #endif /* CONFIG_SMP */ -void xics_teardown_cpu(int secondary) +void xics_teardown_cpu() { int cpu = smp_processor_id(); - unsigned int ipi; - struct irq_desc *desc; xics_set_cpu_priority(0); @@ -790,9 +776,17 @@ void xics_teardown_cpu(int secondary) lpar_qirr_info(cpu, 0xff); else direct_qirr_info(cpu, 0xff); +} + +void xics_kexec_teardown_cpu(int secondary) +{ + unsigned int ipi; + struct irq_desc *desc; + + xics_teardown_cpu(); /* - * we need to EOI the IPI if we got here from kexec down IPI + * we need to EOI the IPI * * probably need to check all the other interrupts too * should we be flagging idle loop instead? @@ -880,8 +874,8 @@ void xics_migrate_irqs_away(void) virq, cpu); /* Reset affinity to all cpus */ + irq_desc[virq].affinity = CPU_MASK_ALL; desc->chip->set_affinity(virq, CPU_MASK_ALL); - irq_desc[irq].affinity = CPU_MASK_ALL; unlock: spin_unlock_irqrestore(&desc->lock, flags); } diff --git a/arch/powerpc/platforms/pseries/xics.h b/arch/powerpc/platforms/pseries/xics.h index 9ffd809d29e..c26bcff47b6 100644 --- a/arch/powerpc/platforms/pseries/xics.h +++ b/arch/powerpc/platforms/pseries/xics.h @@ -16,7 +16,8 @@ extern void xics_init_IRQ(void); extern void xics_setup_cpu(void); -extern void xics_teardown_cpu(int secondary); +extern void xics_teardown_cpu(void); +extern void xics_kexec_teardown_cpu(int secondary); extern void xics_cause_IPI(int cpu); extern void xics_request_IPIs(void); extern void xics_migrate_irqs_away(void); diff --git a/arch/powerpc/sysdev/dcr.c b/arch/powerpc/sysdev/dcr.c index 427027c7ea0..437e48d3ae3 100644 --- a/arch/powerpc/sysdev/dcr.c +++ b/arch/powerpc/sysdev/dcr.c @@ -137,5 +137,6 @@ void dcr_unmap(dcr_host_t host, unsigned int dcr_c) h.token = NULL; } EXPORT_SYMBOL_GPL(dcr_unmap); - -#endif /* !defined(CONFIG_PPC_DCR_NATIVE) */ +#else /* defined(CONFIG_PPC_DCR_NATIVE) */ +DEFINE_SPINLOCK(dcr_ind_lock); +#endif /* !defined(CONFIG_PPC_DCR_NATIVE) */ diff --git a/arch/powerpc/sysdev/fsl_soc.c b/arch/powerpc/sysdev/fsl_soc.c index e48b20e934c..2c5388ce902 100644 --- a/arch/powerpc/sysdev/fsl_soc.c +++ b/arch/powerpc/sysdev/fsl_soc.c @@ -1342,7 +1342,7 @@ static int __init of_fsl_spi_probe(char *type, char *compatible, u32 sysclk, if (ret) goto unreg; - ret = platform_device_register(pdev); + ret = platform_device_add(pdev); if (ret) goto unreg; diff --git a/arch/powerpc/sysdev/mpc8xx_pic.c b/arch/powerpc/sysdev/mpc8xx_pic.c index 0e74a4bd982..5d2d5522ef4 100644 --- a/arch/powerpc/sysdev/mpc8xx_pic.c +++ b/arch/powerpc/sysdev/mpc8xx_pic.c @@ -174,15 +174,19 @@ int mpc8xx_pic_init(void) goto out; siu_reg = ioremap(res.start, res.end - res.start + 1); - if (siu_reg == NULL) - return -EINVAL; + if (siu_reg == NULL) { + ret = -EINVAL; + goto out; + } - mpc8xx_pic_host = irq_alloc_host(of_node_get(np), IRQ_HOST_MAP_LINEAR, + mpc8xx_pic_host = irq_alloc_host(np, IRQ_HOST_MAP_LINEAR, 64, &mpc8xx_pic_host_ops, 64); if (mpc8xx_pic_host == NULL) { printk(KERN_ERR "MPC8xx PIC: failed to allocate irq host!\n"); ret = -ENOMEM; + goto out; } + return 0; out: of_node_put(np); diff --git a/arch/powerpc/sysdev/qe_lib/qe.c b/arch/powerpc/sysdev/qe_lib/qe.c index 5ef844da935..6efbd5e5bb1 100644 --- a/arch/powerpc/sysdev/qe_lib/qe.c +++ b/arch/powerpc/sysdev/qe_lib/qe.c @@ -66,7 +66,7 @@ phys_addr_t get_qe_base(void) { struct device_node *qe; unsigned int size; - const void *prop; + const u32 *prop; if (qebase != -1) return qebase; @@ -79,7 +79,8 @@ phys_addr_t get_qe_base(void) } prop = of_get_property(qe, "reg", &size); - qebase = of_translate_address(qe, prop); + if (prop && size >= sizeof(*prop)) + qebase = of_translate_address(qe, prop); of_node_put(qe); return qebase; @@ -172,10 +173,9 @@ unsigned int get_brg_clk(void) } prop = of_get_property(qe, "brg-frequency", &size); - if (!prop || size != sizeof(*prop)) - return brg_clk; + if (prop && size == sizeof(*prop)) + brg_clk = *prop; - brg_clk = *prop; of_node_put(qe); return brg_clk; diff --git a/drivers/block/xsysace.c b/drivers/block/xsysace.c index 78ebfffc77e..4a7a059ebaf 100644 --- a/drivers/block/xsysace.c +++ b/drivers/block/xsysace.c @@ -1202,8 +1202,10 @@ static int __devexit ace_of_remove(struct of_device *op) } /* Match table for of_platform binding */ -static struct of_device_id __devinit ace_of_match[] = { - { .compatible = "xilinx,xsysace", }, +static struct of_device_id ace_of_match[] __devinitdata = { + { .compatible = "xlnx,opb-sysace-1.00.b", }, + { .compatible = "xlnx,opb-sysace-1.00.c", }, + { .compatible = "xlnx,xps-sysace-1.00.a", }, {}, }; MODULE_DEVICE_TABLE(of, ace_of_match); diff --git a/drivers/cdrom/viocd.c b/drivers/cdrom/viocd.c index 8473b9f1da9..cac06bc1754 100644 --- a/drivers/cdrom/viocd.c +++ b/drivers/cdrom/viocd.c @@ -558,7 +558,7 @@ static struct cdrom_device_ops viocd_dops = { .capability = CDC_CLOSE_TRAY | CDC_OPEN_TRAY | CDC_LOCK | CDC_SELECT_SPEED | CDC_SELECT_DISC | CDC_MULTI_SESSION | CDC_MCN | CDC_MEDIA_CHANGED | CDC_PLAY_AUDIO | CDC_RESET | CDC_DRIVE_STATUS | CDC_GENERIC_PACKET | CDC_CD_R | CDC_CD_RW | CDC_DVD | CDC_DVD_R | CDC_DVD_RAM | CDC_RAM }; -static int __init find_capability(const char *type) +static int find_capability(const char *type) { struct capability_entry *entry; diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 7927fd0faca..f01ac9a07bf 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -830,6 +830,16 @@ config DTLK To compile this driver as a module, choose M here: the module will be called dtlk. +config XILINX_HWICAP + tristate "Xilinx HWICAP Support" + depends on XILINX_VIRTEX + help + This option enables support for Xilinx Internal Configuration + Access Port (ICAP) driver. The ICAP is used on Xilinx Virtex + FPGA platforms to partially reconfigure the FPGA at runtime. + + If unsure, say N. + config R3964 tristate "Siemens R3964 line discipline" ---help--- diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 4396e37b3d0..5407b761561 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -76,6 +76,7 @@ obj-$(CONFIG_EFI_RTC) += efirtc.o obj-$(CONFIG_SGI_DS1286) += ds1286.o obj-$(CONFIG_SGI_IP27_RTC) += ip27-rtc.o obj-$(CONFIG_DS1302) += ds1302.o +obj-$(CONFIG_XILINX_HWICAP) += xilinx_hwicap/ ifeq ($(CONFIG_GENERIC_NVRAM),y) obj-$(CONFIG_NVRAM) += generic_nvram.o else diff --git a/drivers/char/xilinx_hwicap/Makefile b/drivers/char/xilinx_hwicap/Makefile new file mode 100644 index 00000000000..5491cbc42f4 --- /dev/null +++ b/drivers/char/xilinx_hwicap/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for the Xilinx OPB hwicap driver +# + +obj-$(CONFIG_XILINX_HWICAP) += xilinx_hwicap_m.o + +xilinx_hwicap_m-y := xilinx_hwicap.o fifo_icap.o buffer_icap.o diff --git a/drivers/char/xilinx_hwicap/buffer_icap.c b/drivers/char/xilinx_hwicap/buffer_icap.c new file mode 100644 index 00000000000..dfea2bde162 --- /dev/null +++ b/drivers/char/xilinx_hwicap/buffer_icap.c @@ -0,0 +1,380 @@ +/***************************************************************************** + * + * Author: Xilinx, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" + * AS A COURTESY TO YOU, SOLELY FOR USE IN DEVELOPING PROGRAMS AND + * SOLUTIONS FOR XILINX DEVICES. BY PROVIDING THIS DESIGN, CODE, + * OR INFORMATION AS ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, + * APPLICATION OR STANDARD, XILINX IS MAKING NO REPRESENTATION + * THAT THIS IMPLEMENTATION IS FREE FROM ANY CLAIMS OF INFRINGEMENT, + * AND YOU ARE RESPONSIBLE FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE + * FOR YOUR IMPLEMENTATION. XILINX EXPRESSLY DISCLAIMS ANY + * WARRANTY WHATSOEVER WITH RESPECT TO THE ADEQUACY OF THE + * IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OR + * REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM CLAIMS OF + * INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE. + * + * Xilinx products are not intended for use in life support appliances, + * devices, or systems. Use in such applications is expressly prohibited. + * + * (c) Copyright 2003-2008 Xilinx Inc. + * All rights reserved. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + *****************************************************************************/ + +#include "buffer_icap.h" + +/* Indicates how many bytes will fit in a buffer. (1 BRAM) */ +#define XHI_MAX_BUFFER_BYTES 2048 +#define XHI_MAX_BUFFER_INTS (XHI_MAX_BUFFER_BYTES >> 2) + +/* File access and error constants */ +#define XHI_DEVICE_READ_ERROR -1 +#define XHI_DEVICE_WRITE_ERROR -2 +#define XHI_BUFFER_OVERFLOW_ERROR -3 + +#define XHI_DEVICE_READ 0x1 +#define XHI_DEVICE_WRITE 0x0 + +/* Constants for checking transfer status */ +#define XHI_CYCLE_DONE 0 +#define XHI_CYCLE_EXECUTING 1 + +/* buffer_icap register offsets */ + +/* Size of transfer, read & write */ +#define XHI_SIZE_REG_OFFSET 0x800L +/* offset into bram, read & write */ +#define XHI_BRAM_OFFSET_REG_OFFSET 0x804L +/* Read not Configure, direction of transfer. Write only */ +#define XHI_RNC_REG_OFFSET 0x808L +/* Indicates transfer complete. Read only */ +#define XHI_STATUS_REG_OFFSET 0x80CL + +/* Constants for setting the RNC register */ +#define XHI_CONFIGURE 0x0UL +#define XHI_READBACK 0x1UL + +/* Constants for the Done register */ +#define XHI_NOT_FINISHED 0x0UL +#define XHI_FINISHED 0x1UL + +#define XHI_BUFFER_START 0 + +/** + * buffer_icap_get_status: Get the contents of the status register. + * @parameter base_address: is the base address of the device + * + * The status register contains the ICAP status and the done bit. + * + * D8 - cfgerr + * D7 - dalign + * D6 - rip + * D5 - in_abort_l + * D4 - Always 1 + * D3 - Always 1 + * D2 - Always 1 + * D1 - Always 1 + * D0 - Done bit + **/ +static inline u32 buffer_icap_get_status(void __iomem *base_address) +{ + return in_be32(base_address + XHI_STATUS_REG_OFFSET); +} + +/** + * buffer_icap_get_bram: Reads data from the storage buffer bram. + * @parameter base_address: contains the base address of the component. + * @parameter offset: The word offset from which the data should be read. + * + * A bram is used as a configuration memory cache. One frame of data can + * be stored in this "storage buffer". + **/ +static inline u32 buffer_icap_get_bram(void __iomem *base_address, + u32 offset) +{ + return in_be32(base_address + (offset << 2)); +} + +/** + * buffer_icap_busy: Return true if the icap device is busy + * @parameter base_address: is the base address of the device + * + * The queries the low order bit of the status register, which + * indicates whether the current configuration or readback operation + * has completed. + **/ +static inline bool buffer_icap_busy(void __iomem *base_address) +{ + return (buffer_icap_get_status(base_address) & 1) == XHI_NOT_FINISHED; +} + +/** + * buffer_icap_busy: Return true if the icap device is not busy + * @parameter base_address: is the base address of the device + * + * The queries the low order bit of the status register, which + * indicates whether the current configuration or readback operation + * has completed. + **/ +static inline bool buffer_icap_done(void __iomem *base_address) +{ + return (buffer_icap_get_status(base_address) & 1) == XHI_FINISHED; +} + +/** + * buffer_icap_set_size: Set the size register. + * @parameter base_address: is the base address of the device + * @parameter data: The size in bytes. + * + * The size register holds the number of 8 bit bytes to transfer between + * bram and the icap (or icap to bram). + **/ +static inline void buffer_icap_set_size(void __iomem *base_address, + u32 data) +{ + out_be32(base_address + XHI_SIZE_REG_OFFSET, data); +} + +/** + * buffer_icap_mSetoffsetReg: Set the bram offset register. + * @parameter base_address: contains the base address of the device. + * @parameter data: is the value to be written to the data register. + * + * The bram offset register holds the starting bram address to transfer + * data from during configuration or write data to during readback. + **/ +static inline void buffer_icap_set_offset(void __iomem *base_address, + u32 data) +{ + out_be32(base_address + XHI_BRAM_OFFSET_REG_OFFSET, data); +} + +/** + * buffer_icap_set_rnc: Set the RNC (Readback not Configure) register. + * @parameter base_address: contains the base address of the device. + * @parameter data: is the value to be written to the data register. + * + * The RNC register determines the direction of the data transfer. It + * controls whether a configuration or readback take place. Writing to + * this register initiates the transfer. A value of 1 initiates a + * readback while writing a value of 0 initiates a configuration. + **/ +static inline void buffer_icap_set_rnc(void __iomem *base_address, + u32 data) +{ + out_be32(base_address + XHI_RNC_REG_OFFSET, data); +} + +/** + * buffer_icap_set_bram: Write data to the storage buffer bram. + * @parameter base_address: contains the base address of the component. + * @parameter offset: The word offset at which the data should be written. + * @parameter data: The value to be written to the bram offset. + * + * A bram is used as a configuration memory cache. One frame of data can + * be stored in this "storage buffer". + **/ +static inline void buffer_icap_set_bram(void __iomem *base_address, + u32 offset, u32 data) +{ + out_be32(base_address + (offset << 2), data); +} + +/** + * buffer_icap_device_read: Transfer bytes from ICAP to the storage buffer. + * @parameter drvdata: a pointer to the drvdata. + * @parameter offset: The storage buffer start address. + * @parameter count: The number of words (32 bit) to read from the + * device (ICAP). + **/ +static int buffer_icap_device_read(struct hwicap_drvdata *drvdata, + u32 offset, u32 count) +{ + + s32 retries = 0; + void __iomem *base_address = drvdata->base_address; + + if (buffer_icap_busy(base_address)) + return -EBUSY; + + if ((offset + count) > XHI_MAX_BUFFER_INTS) + return -EINVAL; + + /* setSize count*4 to get bytes. */ + buffer_icap_set_size(base_address, (count << 2)); + buffer_icap_set_offset(base_address, offset); + buffer_icap_set_rnc(base_address, XHI_READBACK); + + while (buffer_icap_busy(base_address)) { + retries++; + if (retries > XHI_MAX_RETRIES) + return -EBUSY; + } + return 0; + +}; + +/** + * buffer_icap_device_write: Transfer bytes from ICAP to the storage buffer. + * @parameter drvdata: a pointer to the drvdata. + * @parameter offset: The storage buffer start address. + * @parameter count: The number of words (32 bit) to read from the + * device (ICAP). + **/ +static int buffer_icap_device_write(struct hwicap_drvdata *drvdata, + u32 offset, u32 count) +{ + + s32 retries = 0; + void __iomem *base_address = drvdata->base_address; + + if (buffer_icap_busy(base_address)) + return -EBUSY; + + if ((offset + count) > XHI_MAX_BUFFER_INTS) + return -EINVAL; + + /* setSize count*4 to get bytes. */ + buffer_icap_set_size(base_address, count << 2); + buffer_icap_set_offset(base_address, offset); + buffer_icap_set_rnc(base_address, XHI_CONFIGURE); + + while (buffer_icap_busy(base_address)) { + retries++; + if (retries > XHI_MAX_RETRIES) + return -EBUSY; + } + return 0; + +}; + +/** + * buffer_icap_reset: Reset the logic of the icap device. + * @parameter drvdata: a pointer to the drvdata. + * + * Writing to the status register resets the ICAP logic in an internal + * version of the core. For the version of the core published in EDK, + * this is a noop. + **/ +void buffer_icap_reset(struct hwicap_drvdata *drvdata) +{ + out_be32(drvdata->base_address + XHI_STATUS_REG_OFFSET, 0xFEFE); +} + +/** + * buffer_icap_set_configuration: Load a partial bitstream from system memory. + * @parameter drvdata: a pointer to the drvdata. + * @parameter data: Kernel address of the partial bitstream. + * @parameter size: the size of the partial bitstream in 32 bit words. + **/ +int buffer_icap_set_configuration(struct hwicap_drvdata *drvdata, u32 *data, + u32 size) +{ + int status; + s32 buffer_count = 0; + s32 num_writes = 0; + bool dirty = 0; + u32 i; + void __iomem *base_address = drvdata->base_address; + + /* Loop through all the data */ + for (i = 0, buffer_count = 0; i < size; i++) { + + /* Copy data to bram */ + buffer_icap_set_bram(base_address, buffer_count, data[i]); + dirty = 1; + + if (buffer_count < XHI_MAX_BUFFER_INTS - 1) { + buffer_count++; + continue; + } + + /* Write data to ICAP */ + status = buffer_icap_device_write( + drvdata, + XHI_BUFFER_START, + XHI_MAX_BUFFER_INTS); + if (status != 0) { + /* abort. */ + buffer_icap_reset(drvdata); + return status; + } + + buffer_count = 0; + num_writes++; + dirty = 0; + } + + /* Write unwritten data to ICAP */ + if (dirty) { + /* Write data to ICAP */ + status = buffer_icap_device_write(drvdata, XHI_BUFFER_START, + buffer_count); + if (status != 0) { + /* abort. */ + buffer_icap_reset(drvdata); + } + return status; + } + + return 0; +}; + +/** + * buffer_icap_get_configuration: Read configuration data from the device. + * @parameter drvdata: a pointer to the drvdata. + * @parameter data: Address of the data representing the partial bitstream + * @parameter size: the size of the partial bitstream in 32 bit words. + **/ +int buffer_icap_get_configuration(struct hwicap_drvdata *drvdata, u32 *data, + u32 size) +{ + int status; + s32 buffer_count = 0; + s32 read_count = 0; + u32 i; + void __iomem *base_address = drvdata->base_address; + + /* Loop through all the data */ + for (i = 0, buffer_count = XHI_MAX_BUFFER_INTS; i < size; i++) { + if (buffer_count == XHI_MAX_BUFFER_INTS) { + u32 words_remaining = size - i; + u32 words_to_read = + words_remaining < + XHI_MAX_BUFFER_INTS ? words_remaining : + XHI_MAX_BUFFER_INTS; + + /* Read data from ICAP */ + status = buffer_icap_device_read( + drvdata, + XHI_BUFFER_START, + words_to_read); + if (status != 0) { + /* abort. */ + buffer_icap_reset(drvdata); + return status; + } + + buffer_count = 0; + read_count++; + } + + /* Copy data from bram */ + data[i] = buffer_icap_get_bram(base_address, buffer_count); + buffer_count++; + } + + return 0; +}; diff --git a/drivers/char/xilinx_hwicap/buffer_icap.h b/drivers/char/xilinx_hwicap/buffer_icap.h new file mode 100644 index 00000000000..03184959fa0 --- /dev/null +++ b/drivers/char/xilinx_hwicap/buffer_icap.h @@ -0,0 +1,57 @@ +/***************************************************************************** + * + * Author: Xilinx, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" + * AS A COURTESY TO YOU, SOLELY FOR USE IN DEVELOPING PROGRAMS AND + * SOLUTIONS FOR XILINX DEVICES. BY PROVIDING THIS DESIGN, CODE, + * OR INFORMATION AS ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, + * APPLICATION OR STANDARD, XILINX IS MAKING NO REPRESENTATION + * THAT THIS IMPLEMENTATION IS FREE FROM ANY CLAIMS OF INFRINGEMENT, + * AND YOU ARE RESPONSIBLE FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE + * FOR YOUR IMPLEMENTATION. XILINX EXPRESSLY DISCLAIMS ANY + * WARRANTY WHATSOEVER WITH RESPECT TO THE ADEQUACY OF THE + * IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OR + * REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM CLAIMS OF + * INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE. + * + * Xilinx products are not intended for use in life support appliances, + * devices, or systems. Use in such applications is expressly prohibited. + * + * (c) Copyright 2003-2008 Xilinx Inc. + * All rights reserved. + * + * 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. + * + *****************************************************************************/ + +#ifndef XILINX_BUFFER_ICAP_H_ /* prevent circular inclusions */ +#define XILINX_BUFFER_ICAP_H_ /* by using protection macros */ + +#include <linux/types.h> +#include <linux/cdev.h> +#include <linux/version.h> +#include <linux/platform_device.h> + +#include <asm/io.h> +#include "xilinx_hwicap.h" + +void buffer_icap_reset(struct hwicap_drvdata *drvdata); + +/* Loads a partial bitstream from system memory. */ +int buffer_icap_set_configuration(struct hwicap_drvdata *drvdata, u32 *data, + u32 Size); + +/* Loads a partial bitstream from system memory. */ +int buffer_icap_get_configuration(struct hwicap_drvdata *drvdata, u32 *data, + u32 Size); + +#endif diff --git a/drivers/char/xilinx_hwicap/fifo_icap.c b/drivers/char/xilinx_hwicap/fifo_icap.c new file mode 100644 index 00000000000..0988314694a --- /dev/null +++ b/drivers/char/xilinx_hwicap/fifo_icap.c @@ -0,0 +1,381 @@ +/***************************************************************************** + * + * Author: Xilinx, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" + * AS A COURTESY TO YOU, SOLELY FOR USE IN DEVELOPING PROGRAMS AND + * SOLUTIONS FOR XILINX DEVICES. BY PROVIDING THIS DESIGN, CODE, + * OR INFORMATION AS ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, + * APPLICATION OR STANDARD, XILINX IS MAKING NO REPRESENTATION + * THAT THIS IMPLEMENTATION IS FREE FROM ANY CLAIMS OF INFRINGEMENT, + * AND YOU ARE RESPONSIBLE FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE + * FOR YOUR IMPLEMENTATION. XILINX EXPRESSLY DISCLAIMS ANY + * WARRANTY WHATSOEVER WITH RESPECT TO THE ADEQUACY OF THE + * IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OR + * REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM CLAIMS OF + * INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE. + * + * Xilinx products are not intended for use in life support appliances, + * devices, or systems. Use in such applications is expressly prohibited. + * + * (c) Copyright 2007-2008 Xilinx Inc. + * All rights reserved. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + *****************************************************************************/ + +#include "fifo_icap.h" + +/* Register offsets for the XHwIcap device. */ +#define XHI_GIER_OFFSET 0x1C /* Device Global Interrupt Enable Reg */ +#define XHI_IPISR_OFFSET 0x20 /* Interrupt Status Register */ +#define XHI_IPIER_OFFSET 0x28 /* Interrupt Enable Register */ +#define XHI_WF_OFFSET 0x100 /* Write FIFO */ +#define XHI_RF_OFFSET 0x104 /* Read FIFO */ +#define XHI_SZ_OFFSET 0x108 /* Size Register */ +#define XHI_CR_OFFSET 0x10C /* Control Register */ +#define XHI_SR_OFFSET 0x110 /* Status Register */ +#define XHI_WFV_OFFSET 0x114 /* Write FIFO Vacancy Register */ +#define XHI_RFO_OFFSET 0x118 /* Read FIFO Occupancy Register */ + +/* Device Global Interrupt Enable Register (GIER) bit definitions */ + +#define XHI_GIER_GIE_MASK 0x80000000 /* Global Interrupt enable Mask */ + +/** + * HwIcap Device Interrupt Status/Enable Registers + * + * Interrupt Status Register (IPISR) : This register holds the + * interrupt status flags for the device. These bits are toggle on + * write. + * + * Interrupt Enable Register (IPIER) : This register is used to enable + * interrupt sources for the device. + * Writing a '1' to a bit enables the corresponding interrupt. + * Writing a '0' to a bit disables the corresponding interrupt. + * + * IPISR/IPIER registers have the same bit definitions and are only defined + * once. + */ +#define XHI_IPIXR_RFULL_MASK 0x00000008 /* Read FIFO Full */ +#define XHI_IPIXR_WEMPTY_MASK 0x00000004 /* Write FIFO Empty */ +#define XHI_IPIXR_RDP_MASK 0x00000002 /* Read FIFO half full */ +#define XHI_IPIXR_WRP_MASK 0x00000001 /* Write FIFO half full */ +#define XHI_IPIXR_ALL_MASK 0x0000000F /* Mask of all interrupts */ + +/* Control Register (CR) */ +#define XHI_CR_SW_RESET_MASK 0x00000008 /* SW Reset Mask */ +#define XHI_CR_FIFO_CLR_MASK 0x00000004 /* FIFO Clear Mask */ +#define XHI_CR_READ_MASK 0x00000002 /* Read from ICAP to FIFO */ +#define XHI_CR_WRITE_MASK 0x00000001 /* Write from FIFO to ICAP */ + +/* Status Register (SR) */ +#define XHI_SR_CFGERR_N_MASK 0x00000100 /* Config Error Mask */ +#define XHI_SR_DALIGN_MASK 0x00000080 /* Data Alignment Mask */ +#define XHI_SR_RIP_MASK 0x00000040 /* Read back Mask */ +#define XHI_SR_IN_ABORT_N_MASK 0x00000020 /* Select Map Abort Mask */ +#define XHI_SR_DONE_MASK 0x00000001 /* Done bit Mask */ + + +#define XHI_WFO_MAX_VACANCY 1024 /* Max Write FIFO Vacancy, in words */ +#define XHI_RFO_MAX_OCCUPANCY 256 /* Max Read FIFO Occupancy, in words */ +/* The maximum amount we can request from fifo_icap_get_configuration + at once, in bytes. */ +#define XHI_MAX_READ_TRANSACTION_WORDS 0xFFF + + +/** + * fifo_icap_fifo_write: Write data to the write FIFO. + * @parameter drvdata: a pointer to the drvdata. + * @parameter data: the 32-bit value to be written to the FIFO. + * + * This function will silently fail if the fifo is full. + **/ +static inline void fifo_icap_fifo_write(struct hwicap_drvdata *drvdata, + u32 data) +{ + dev_dbg(drvdata->dev, "fifo_write: %x\n", data); + out_be32(drvdata->base_address + XHI_WF_OFFSET, data); +} + +/** + * fifo_icap_fifo_read: Read data from the Read FIFO. + * @parameter drvdata: a pointer to the drvdata. + * + * This function will silently fail if the fifo is empty. + **/ +static inline u32 fifo_icap_fifo_read(struct hwicap_drvdata *drvdata) +{ + u32 data = in_be32(drvdata->base_address + XHI_RF_OFFSET); + dev_dbg(drvdata->dev, "fifo_read: %x\n", data); + return data; +} + +/** + * fifo_icap_set_read_size: Set the the size register. + * @parameter drvdata: a pointer to the drvdata. + * @parameter data: the size of the following read transaction, in words. + **/ +static inline void fifo_icap_set_read_size(struct hwicap_drvdata *drvdata, + u32 data) +{ + out_be32(drvdata->base_address + XHI_SZ_OFFSET, data); +} + +/** + * fifo_icap_start_config: Initiate a configuration (write) to the device. + * @parameter drvdata: a pointer to the drvdata. + **/ +static inline void fifo_icap_start_config(struct hwicap_drvdata *drvdata) +{ + out_be32(drvdata->base_address + XHI_CR_OFFSET, XHI_CR_WRITE_MASK); + dev_dbg(drvdata->dev, "configuration started\n"); +} + +/** + * fifo_icap_start_readback: Initiate a readback from the device. + * @parameter drvdata: a pointer to the drvdata. + **/ +static inline void fifo_icap_start_readback(struct hwicap_drvdata *drvdata) +{ + out_be32(drvdata->base_address + XHI_CR_OFFSET, XHI_CR_READ_MASK); + dev_dbg(drvdata->dev, "readback started\n"); +} + +/** + * fifo_icap_busy: Return true if the ICAP is still processing a transaction. + * @parameter drvdata: a pointer to the drvdata. + **/ +static inline u32 fifo_icap_busy(struct hwicap_drvdata *drvdata) +{ + u32 status = in_be32(drvdata->base_address + XHI_SR_OFFSET); + dev_dbg(drvdata->dev, "Getting status = %x\n", status); + return (status & XHI_SR_DONE_MASK) ? 0 : 1; +} + +/** + * fifo_icap_write_fifo_vacancy: Query the write fifo available space. + * @parameter drvdata: a pointer to the drvdata. + * + * Return the number of words that can be safely pushed into the write fifo. + **/ +static inline u32 fifo_icap_write_fifo_vacancy( + struct hwicap_drvdata *drvdata) +{ + return in_be32(drvdata->base_address + XHI_WFV_OFFSET); +} + +/** + * fifo_icap_read_fifo_occupancy: Query the read fifo available data. + * @parameter drvdata: a pointer to the drvdata. + * + * Return the number of words that can be safely read from the read fifo. + **/ +static inline u32 fifo_icap_read_fifo_occupancy( + struct hwicap_drvdata *drvdata) +{ + return in_be32(drvdata->base_address + XHI_RFO_OFFSET); +} + +/** + * fifo_icap_set_configuration: Send configuration data to the ICAP. + * @parameter drvdata: a pointer to the drvdata. + * @parameter frame_buffer: a pointer to the data to be written to the + * ICAP device. + * @parameter num_words: the number of words (32 bit) to write to the ICAP + * device. + + * This function writes the given user data to the Write FIFO in + * polled mode and starts the transfer of the data to + * the ICAP device. + **/ +int fifo_icap_set_configuration(struct hwicap_drvdata *drvdata, + u32 *frame_buffer, u32 num_words) +{ + + u32 write_fifo_vacancy = 0; + u32 retries = 0; + u32 remaining_words; + + dev_dbg(drvdata->dev, "fifo_set_configuration\n"); + + /* + * Check if the ICAP device is Busy with the last Read/Write + */ + if (fifo_icap_busy(drvdata)) + return -EBUSY; + + /* + * Set up the buffer pointer and the words to be transferred. + */ + remaining_words = num_words; + + while (remaining_words > 0) { + /* + * Wait until we have some data in the fifo. + */ + while (write_fifo_vacancy == 0) { + write_fifo_vacancy = + fifo_icap_write_fifo_vacancy(drvdata); + retries++; + if (retries > XHI_MAX_RETRIES) + return -EIO; + } + + /* + * Write data into the Write FIFO. + */ + while ((write_fifo_vacancy != 0) && + (remaining_words > 0)) { + fifo_icap_fifo_write(drvdata, *frame_buffer); + + remaining_words--; + write_fifo_vacancy--; + frame_buffer++; + } + /* Start pushing whatever is in the FIFO into the ICAP. */ + fifo_icap_start_config(drvdata); + } + + /* Wait until the write has finished. */ + while (fifo_icap_busy(drvdata)) { + retries++; + if (retries > XHI_MAX_RETRIES) + break; + } + + dev_dbg(drvdata->dev, "done fifo_set_configuration\n"); + + /* + * If the requested number of words have not been read from + * the device then indicate failure. + */ + if (remaining_words != 0) + return -EIO; + + return 0; +} + +/** + * fifo_icap_get_configuration: Read configuration data from the device. + * @parameter drvdata: a pointer to the drvdata. + * @parameter data: Address of the data representing the partial bitstream + * @parameter size: the size of the partial bitstream in 32 bit words. + * + * This function reads the specified number of words from the ICAP device in + * the polled mode. + */ +int fifo_icap_get_configuration(struct hwicap_drvdata *drvdata, + u32 *frame_buffer, u32 num_words) +{ + + u32 read_fifo_occupancy = 0; + u32 retries = 0; + u32 *data = frame_buffer; + u32 remaining_words; + u32 words_to_read; + + dev_dbg(drvdata->dev, "fifo_get_configuration\n"); + + /* + * Check if the ICAP device is Busy with the last Write/Read + */ + if (fifo_icap_busy(drvdata)) + return -EBUSY; + + remaining_words = num_words; + + while (remaining_words > 0) { + words_to_read = remaining_words; + /* The hardware has a limit on the number of words + that can be read at one time. */ + if (words_to_read > XHI_MAX_READ_TRANSACTION_WORDS) + words_to_read = XHI_MAX_READ_TRANSACTION_WORDS; + + remaining_words -= words_to_read; + + fifo_icap_set_read_size(drvdata, words_to_read); + fifo_icap_start_readback(drvdata); + + while (words_to_read > 0) { + /* Wait until we have some data in the fifo. */ + while (read_fifo_occupancy == 0) { + read_fifo_occupancy = + fifo_icap_read_fifo_occupancy(drvdata); + retries++; + if (retries > XHI_MAX_RETRIES) + return -EIO; + } + + if (read_fifo_occupancy > words_to_read) + read_fifo_occupancy = words_to_read; + + words_to_read -= read_fifo_occupancy; + + /* Read the data from the Read FIFO. */ + while (read_fifo_occupancy != 0) { + *data++ = fifo_icap_fifo_read(drvdata); + read_fifo_occupancy--; + } + } + } + + dev_dbg(drvdata->dev, "done fifo_get_configuration\n"); + + return 0; +} + +/** + * buffer_icap_reset: Reset the logic of the icap device. + * @parameter drvdata: a pointer to the drvdata. + * + * This function forces the software reset of the complete HWICAP device. + * All the registers will return to the default value and the FIFO is also + * flushed as a part of this software reset. + */ +void fifo_icap_reset(struct hwicap_drvdata *drvdata) +{ + u32 reg_data; + /* + * Reset the device by setting/clearing the RESET bit in the + * Control Register. + */ + reg_data = in_be32(drvdata->base_address + XHI_CR_OFFSET); + + out_be32(drvdata->base_address + XHI_CR_OFFSET, + reg_data | XHI_CR_SW_RESET_MASK); + + out_be32(drvdata->base_address + XHI_CR_OFFSET, + reg_data & (~XHI_CR_SW_RESET_MASK)); + +} + +/** + * fifo_icap_flush_fifo: This function flushes the FIFOs in the device. + * @parameter drvdata: a pointer to the drvdata. + */ +void fifo_icap_flush_fifo(struct hwicap_drvdata *drvdata) +{ + u32 reg_data; + /* + * Flush the FIFO by setting/clearing the FIFO Clear bit in the + * Control Register. + */ + reg_data = in_be32(drvdata->base_address + XHI_CR_OFFSET); + + out_be32(drvdata->base_address + XHI_CR_OFFSET, + reg_data | XHI_CR_FIFO_CLR_MASK); + + out_be32(drvdata->base_address + XHI_CR_OFFSET, + reg_data & (~XHI_CR_FIFO_CLR_MASK)); +} + diff --git a/drivers/char/xilinx_hwicap/fifo_icap.h b/drivers/char/xilinx_hwicap/fifo_icap.h new file mode 100644 index 00000000000..4d3068dd040 --- /dev/null +++ b/drivers/char/xilinx_hwicap/fifo_icap.h @@ -0,0 +1,62 @@ +/***************************************************************************** + * + * Author: Xilinx, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" + * AS A COURTESY TO YOU, SOLELY FOR USE IN DEVELOPING PROGRAMS AND + * SOLUTIONS FOR XILINX DEVICES. BY PROVIDING THIS DESIGN, CODE, + * OR INFORMATION AS ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, + * APPLICATION OR STANDARD, XILINX IS MAKING NO REPRESENTATION + * THAT THIS IMPLEMENTATION IS FREE FROM ANY CLAIMS OF INFRINGEMENT, + * AND YOU ARE RESPONSIBLE FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE + * FOR YOUR IMPLEMENTATION. XILINX EXPRESSLY DISCLAIMS ANY + * WARRANTY WHATSOEVER WITH RESPECT TO THE ADEQUACY OF THE + * IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OR + * REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM CLAIMS OF + * INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE. + * + * Xilinx products are not intended for use in life support appliances, + * devices, or systems. Use in such applications is expressly prohibited. + * + * (c) Copyright 2007-2008 Xilinx Inc. + * All rights reserved. + * + * 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. + * + *****************************************************************************/ + +#ifndef XILINX_FIFO_ICAP_H_ /* prevent circular inclusions */ +#define XILINX_FIFO_ICAP_H_ /* by using protection macros */ + +#include <linux/types.h> +#include <linux/cdev.h> +#include <linux/version.h> +#include <linux/platform_device.h> + +#include <asm/io.h> +#include "xilinx_hwicap.h" + +/* Reads integers from the device into the storage buffer. */ +int fifo_icap_get_configuration( + struct hwicap_drvdata *drvdata, + u32 *FrameBuffer, + u32 NumWords); + +/* Writes integers to the device from the storage buffer. */ +int fifo_icap_set_configuration( + struct hwicap_drvdata *drvdata, + u32 *FrameBuffer, + u32 NumWords); + +void fifo_icap_reset(struct hwicap_drvdata *drvdata); +void fifo_icap_flush_fifo(struct hwicap_drvdata *drvdata); + +#endif diff --git a/drivers/char/xilinx_hwicap/xilinx_hwicap.c b/drivers/char/xilinx_hwicap/xilinx_hwicap.c new file mode 100644 index 00000000000..24f6aef0fd3 --- /dev/null +++ b/drivers/char/xilinx_hwicap/xilinx_hwicap.c @@ -0,0 +1,904 @@ +/***************************************************************************** + * + * Author: Xilinx, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" + * AS A COURTESY TO YOU, SOLELY FOR USE IN DEVELOPING PROGRAMS AND + * SOLUTIONS FOR XILINX DEVICES. BY PROVIDING THIS DESIGN, CODE, + * OR INFORMATION AS ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, + * APPLICATION OR STANDARD, XILINX IS MAKING NO REPRESENTATION + * THAT THIS IMPLEMENTATION IS FREE FROM ANY CLAIMS OF INFRINGEMENT, + * AND YOU ARE RESPONSIBLE FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE + * FOR YOUR IMPLEMENTATION. XILINX EXPRESSLY DISCLAIMS ANY + * WARRANTY WHATSOEVER WITH RESPECT TO THE ADEQUACY OF THE + * IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OR + * REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM CLAIMS OF + * INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE. + * + * Xilinx products are not intended for use in life support appliances, + * devices, or systems. Use in such applications is expressly prohibited. + * + * (c) Copyright 2002 Xilinx Inc., Systems Engineering Group + * (c) Copyright 2004 Xilinx Inc., Systems Engineering Group + * (c) Copyright 2007-2008 Xilinx Inc. + * All rights reserved. + * + * 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. + * + *****************************************************************************/ + +/* + * This is the code behind /dev/xilinx_icap -- it allows a user-space + * application to use the Xilinx ICAP subsystem. + * + * The following operations are possible: + * + * open open the port and initialize for access. + * release release port + * write Write a bitstream to the configuration processor. + * read Read a data stream from the configuration processor. + * + * After being opened, the port is initialized and accessed to avoid a + * corrupted first read which may occur with some hardware. The port + * is left in a desynched state, requiring that a synch sequence be + * transmitted before any valid configuration data. A user will have + * exclusive access to the device while it remains open, and the state + * of the ICAP cannot be guaranteed after the device is closed. Note + * that a complete reset of the core and the state of the ICAP cannot + * be performed on many versions of the cores, hence users of this + * device should avoid making inconsistent accesses to the device. In + * particular, accessing the read interface, without first generating + * a write containing a readback packet can leave the ICAP in an + * inaccessible state. + * + * Note that in order to use the read interface, it is first necessary + * to write a request packet to the write interface. i.e., it is not + * possible to simply readback the bitstream (or any configuration + * bits) from a device without specifically requesting them first. + * The code to craft such packets is intended to be part of the + * user-space application code that uses this device. The simplest + * way to use this interface is simply: + * + * cp foo.bit /dev/xilinx_icap + * + * Note that unless foo.bit is an appropriately constructed partial + * bitstream, this has a high likelyhood of overwriting the design + * currently programmed in the FPGA. + */ + +#include <linux/version.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/ioport.h> +#include <linux/interrupt.h> +#include <linux/fcntl.h> +#include <linux/init.h> +#include <linux/poll.h> +#include <linux/proc_fs.h> +#include <asm/semaphore.h> +#include <linux/sysctl.h> +#include <linux/version.h> +#include <linux/fs.h> +#include <linux/cdev.h> +#include <linux/platform_device.h> + +#include <asm/io.h> +#include <asm/uaccess.h> +#include <asm/system.h> + +#ifdef CONFIG_OF +/* For open firmware. */ +#include <linux/of_device.h> +#include <linux/of_platform.h> +#endif + +#include "xilinx_hwicap.h" +#include "buffer_icap.h" +#include "fifo_icap.h" + +#define DRIVER_NAME "xilinx_icap" + +#define HWICAP_REGS (0x10000) + +/* dynamically allocate device number */ +static int xhwicap_major; +static int xhwicap_minor; +#define HWICAP_DEVICES 1 + +module_param(xhwicap_major, int, S_IRUGO); +module_param(xhwicap_minor, int, S_IRUGO); + +/* An array, which is set to true when the device is registered. */ +static bool probed_devices[HWICAP_DEVICES]; + +static struct class *icap_class; + +#define UNIMPLEMENTED 0xFFFF + +static const struct config_registers v2_config_registers = { + .CRC = 0, + .FAR = 1, + .FDRI = 2, + .FDRO = 3, + .CMD = 4, + .CTL = 5, + .MASK = 6, + .STAT = 7, + .LOUT = 8, + .COR = 9, + .MFWR = 10, + .FLR = 11, + .KEY = 12, + .CBC = 13, + .IDCODE = 14, + .AXSS = UNIMPLEMENTED, + .C0R_1 = UNIMPLEMENTED, + .CSOB = UNIMPLEMENTED, + .WBSTAR = UNIMPLEMENTED, + .TIMER = UNIMPLEMENTED, + .BOOTSTS = UNIMPLEMENTED, + .CTL_1 = UNIMPLEMENTED, +}; + +static const struct config_registers v4_config_registers = { + .CRC = 0, + .FAR = 1, + .FDRI = 2, + .FDRO = 3, + .CMD = 4, + .CTL = 5, + .MASK = 6, + .STAT = 7, + .LOUT = 8, + .COR = 9, + .MFWR = 10, + .FLR = UNIMPLEMENTED, + .KEY = UNIMPLEMENTED, + .CBC = 11, + .IDCODE = 12, + .AXSS = 13, + .C0R_1 = UNIMPLEMENTED, + .CSOB = UNIMPLEMENTED, + .WBSTAR = UNIMPLEMENTED, + .TIMER = UNIMPLEMENTED, + .BOOTSTS = UNIMPLEMENTED, + .CTL_1 = UNIMPLEMENTED, +}; +static const struct config_registers v5_config_registers = { + .CRC = 0, + .FAR = 1, + .FDRI = 2, + .FDRO = 3, + .CMD = 4, + .CTL = 5, + .MASK = 6, + .STAT = 7, + .LOUT = 8, + .COR = 9, + .MFWR = 10, + .FLR = UNIMPLEMENTED, + .KEY = UNIMPLEMENTED, + .CBC = 11, + .IDCODE = 12, + .AXSS = 13, + .C0R_1 = 14, + .CSOB = 15, + .WBSTAR = 16, + .TIMER = 17, + .BOOTSTS = 18, + .CTL_1 = 19, +}; + +/** + * hwicap_command_desync: Send a DESYNC command to the ICAP port. + * @parameter drvdata: a pointer to the drvdata. + * + * This command desynchronizes the ICAP After this command, a + * bitstream containing a NULL packet, followed by a SYNCH packet is + * required before the ICAP will recognize commands. + */ +int hwicap_command_desync(struct hwicap_drvdata *drvdata) +{ + u32 buffer[4]; + u32 index = 0; + + /* + * Create the data to be written to the ICAP. + */ + buffer[index++] = hwicap_type_1_write(drvdata->config_regs->CMD) | 1; + buffer[index++] = XHI_CMD_DESYNCH; + buffer[index++] = XHI_NOOP_PACKET; + buffer[index++] = XHI_NOOP_PACKET; + + /* + * Write the data to the FIFO and intiate the transfer of data present + * in the FIFO to the ICAP device. + */ + return drvdata->config->set_configuration(drvdata, + &buffer[0], index); +} + +/** + * hwicap_command_capture: Send a CAPTURE command to the ICAP port. + * @parameter drvdata: a pointer to the drvdata. + * + * This command captures all of the flip flop states so they will be + * available during readback. One can use this command instead of + * enabling the CAPTURE block in the design. + */ +int hwicap_command_capture(struct hwicap_drvdata *drvdata) +{ + u32 buffer[7]; + u32 index = 0; + + /* + * Create the data to be written to the ICAP. + */ + buffer[index++] = XHI_DUMMY_PACKET; + buffer[index++] = XHI_SYNC_PACKET; + buffer[index++] = XHI_NOOP_PACKET; + buffer[index++] = hwicap_type_1_write(drvdata->config_regs->CMD) | 1; + buffer[index++] = XHI_CMD_GCAPTURE; + buffer[index++] = XHI_DUMMY_PACKET; + buffer[index++] = XHI_DUMMY_PACKET; + + /* + * Write the data to the FIFO and intiate the transfer of data + * present in the FIFO to the ICAP device. + */ + return drvdata->config->set_configuration(drvdata, + &buffer[0], index); + +} + +/** + * hwicap_get_configuration_register: Query a configuration register. + * @parameter drvdata: a pointer to the drvdata. + * @parameter reg: a constant which represents the configuration + * register value to be returned. + * Examples: XHI_IDCODE, XHI_FLR. + * @parameter RegData: returns the value of the register. + * + * Sends a query packet to the ICAP and then receives the response. + * The icap is left in Synched state. + */ +int hwicap_get_configuration_register(struct hwicap_drvdata *drvdata, + u32 reg, u32 *RegData) +{ + int status; + u32 buffer[6]; + u32 index = 0; + + /* + * Create the data to be written to the ICAP. + */ + buffer[index++] = XHI_DUMMY_PACKET; + buffer[index++] = XHI_SYNC_PACKET; + buffer[index++] = XHI_NOOP_PACKET; + buffer[index++] = hwicap_type_1_read(reg) | 1; + buffer[index++] = XHI_NOOP_PACKET; + buffer[index++] = XHI_NOOP_PACKET; + + /* + * Write the data to the FIFO and intiate the transfer of data present + * in the FIFO to the ICAP device. + */ + status = drvdata->config->set_configuration(drvdata, + &buffer[0], index); + if (status) + return status; + + /* + * Read the configuration register + */ + status = drvdata->config->get_configuration(drvdata, RegData, 1); + if (status) + return status; + + return 0; +} + +int hwicap_initialize_hwicap(struct hwicap_drvdata *drvdata) +{ + int status; + u32 idcode; + + dev_dbg(drvdata->dev, "initializing\n"); + + /* Abort any current transaction, to make sure we have the + * ICAP in a good state. */ + dev_dbg(drvdata->dev, "Reset...\n"); + drvdata->config->reset(drvdata); + + dev_dbg(drvdata->dev, "Desync...\n"); + status = hwicap_command_desync(drvdata); + if (status) + return status; + + /* Attempt to read the IDCODE from ICAP. This + * may not be returned correctly, due to the design of the + * hardware. + */ + dev_dbg(drvdata->dev, "Reading IDCODE...\n"); + status = hwicap_get_configuration_register( + drvdata, drvdata->config_regs->IDCODE, &idcode); + dev_dbg(drvdata->dev, "IDCODE = %x\n", idcode); + if (status) + return status; + + dev_dbg(drvdata->dev, "Desync...\n"); + status = hwicap_command_desync(drvdata); + if (status) + return status; + + return 0; +} + +static ssize_t +hwicap_read(struct file *file, char *buf, size_t count, loff_t *ppos) +{ + struct hwicap_drvdata *drvdata = file->private_data; + ssize_t bytes_to_read = 0; + u32 *kbuf; + u32 words; + u32 bytes_remaining; + int status; + + if (down_interruptible(&drvdata->sem)) + return -ERESTARTSYS; + + if (drvdata->read_buffer_in_use) { + /* If there are leftover bytes in the buffer, just */ + /* return them and don't try to read more from the */ + /* ICAP device. */ + bytes_to_read = + (count < drvdata->read_buffer_in_use) ? count : + drvdata->read_buffer_in_use; + + /* Return the data currently in the read buffer. */ + if (copy_to_user(buf, drvdata->read_buffer, bytes_to_read)) { + status = -EFAULT; + goto error; + } + drvdata->read_buffer_in_use -= bytes_to_read; + memcpy(drvdata->read_buffer + bytes_to_read, + drvdata->read_buffer, 4 - bytes_to_read); + } else { + /* Get new data from the ICAP, and return was was requested. */ + kbuf = (u32 *) get_zeroed_page(GFP_KERNEL); + if (!kbuf) { + status = -ENOMEM; + goto error; + } + + /* The ICAP device is only able to read complete */ + /* words. If a number of bytes that do not correspond */ + /* to complete words is requested, then we read enough */ + /* words to get the required number of bytes, and then */ + /* save the remaining bytes for the next read. */ + + /* Determine the number of words to read, rounding up */ + /* if necessary. */ + words = ((count + 3) >> 2); + bytes_to_read = words << 2; + + if (bytes_to_read > PAGE_SIZE) + bytes_to_read = PAGE_SIZE; + + /* Ensure we only read a complete number of words. */ + bytes_remaining = bytes_to_read & 3; + bytes_to_read &= ~3; + words = bytes_to_read >> 2; + + status = drvdata->config->get_configuration(drvdata, + kbuf, words); + + /* If we didn't read correctly, then bail out. */ + if (status) { + free_page((unsigned long)kbuf); + goto error; + } + + /* If we fail to return the data to the user, then bail out. */ + if (copy_to_user(buf, kbuf, bytes_to_read)) { + free_page((unsigned long)kbuf); + status = -EFAULT; + goto error; + } + memcpy(kbuf, drvdata->read_buffer, bytes_remaining); + drvdata->read_buffer_in_use = bytes_remaining; + free_page((unsigned long)kbuf); + } + status = bytes_to_read; + error: + up(&drvdata->sem); + return status; +} + +static ssize_t +hwicap_write(struct file *file, const char *buf, + size_t count, loff_t *ppos) +{ + struct hwicap_drvdata *drvdata = file->private_data; + ssize_t written = 0; + ssize_t left = count; + u32 *kbuf; + ssize_t len; + ssize_t status; + + if (down_interruptible(&drvdata->sem)) + return -ERESTARTSYS; + + left += drvdata->write_buffer_in_use; + + /* Only write multiples of 4 bytes. */ + if (left < 4) { + status = 0; + goto error; + } + + kbuf = (u32 *) __get_free_page(GFP_KERNEL); + if (!kbuf) { + status = -ENOMEM; + goto error; + } + + while (left > 3) { + /* only write multiples of 4 bytes, so there might */ + /* be as many as 3 bytes left (at the end). */ + len = left; + + if (len > PAGE_SIZE) + len = PAGE_SIZE; + len &= ~3; + + if (drvdata->write_buffer_in_use) { + memcpy(kbuf, drvdata->write_buffer, + drvdata->write_buffer_in_use); + if (copy_from_user( + (((char *)kbuf) + (drvdata->write_buffer_in_use)), + buf + written, + len - (drvdata->write_buffer_in_use))) { + free_page((unsigned long)kbuf); + status = -EFAULT; + goto error; + } + } else { + if (copy_from_user(kbuf, buf + written, len)) { + free_page((unsigned long)kbuf); + status = -EFAULT; + goto error; + } + } + + status = drvdata->config->set_configuration(drvdata, + kbuf, len >> 2); + + if (status) { + free_page((unsigned long)kbuf); + status = -EFAULT; + goto error; + } + if (drvdata->write_buffer_in_use) { + len -= drvdata->write_buffer_in_use; + left -= drvdata->write_buffer_in_use; + drvdata->write_buffer_in_use = 0; + } + written += len; + left -= len; + } + if ((left > 0) && (left < 4)) { + if (!copy_from_user(drvdata->write_buffer, + buf + written, left)) { + drvdata->write_buffer_in_use = left; + written += left; + left = 0; + } + } + + free_page((unsigned long)kbuf); + status = written; + error: + up(&drvdata->sem); + return status; +} + +static int hwicap_open(struct inode *inode, struct file *file) +{ + struct hwicap_drvdata *drvdata; + int status; + + drvdata = container_of(inode->i_cdev, struct hwicap_drvdata, cdev); + + if (down_interruptible(&drvdata->sem)) + return -ERESTARTSYS; + + if (drvdata->is_open) { + status = -EBUSY; + goto error; + } + + status = hwicap_initialize_hwicap(drvdata); + if (status) { + dev_err(drvdata->dev, "Failed to open file"); + goto error; + } + + file->private_data = drvdata; + drvdata->write_buffer_in_use = 0; + drvdata->read_buffer_in_use = 0; + drvdata->is_open = 1; + + error: + up(&drvdata->sem); + return status; +} + +static int hwicap_release(struct inode *inode, struct file *file) +{ + struct hwicap_drvdata *drvdata = file->private_data; + int i; + int status = 0; + + if (down_interruptible(&drvdata->sem)) + return -ERESTARTSYS; + + if (drvdata->write_buffer_in_use) { + /* Flush write buffer. */ + for (i = drvdata->write_buffer_in_use; i < 4; i++) + drvdata->write_buffer[i] = 0; + + status = drvdata->config->set_configuration(drvdata, + (u32 *) drvdata->write_buffer, 1); + if (status) + goto error; + } + + status = hwicap_command_desync(drvdata); + if (status) + goto error; + + error: + drvdata->is_open = 0; + up(&drvdata->sem); + return status; +} + +static struct file_operations hwicap_fops = { + .owner = THIS_MODULE, + .write = hwicap_write, + .read = hwicap_read, + .open = hwicap_open, + .release = hwicap_release, +}; + +static int __devinit hwicap_setup(struct device *dev, int id, + const struct resource *regs_res, + const struct hwicap_driver_config *config, + const struct config_registers *config_regs) +{ + dev_t devt; + struct hwicap_drvdata *drvdata = NULL; + int retval = 0; + + dev_info(dev, "Xilinx icap port driver\n"); + + if (id < 0) { + for (id = 0; id < HWICAP_DEVICES; id++) + if (!probed_devices[id]) + break; + } + if (id < 0 || id >= HWICAP_DEVICES) { + dev_err(dev, "%s%i too large\n", DRIVER_NAME, id); + return -EINVAL; + } + if (probed_devices[id]) { + dev_err(dev, "cannot assign to %s%i; it is already in use\n", + DRIVER_NAME, id); + return -EBUSY; + } + + probed_devices[id] = 1; + + devt = MKDEV(xhwicap_major, xhwicap_minor + id); + + drvdata = kmalloc(sizeof(struct hwicap_drvdata), GFP_KERNEL); + if (!drvdata) { + dev_err(dev, "Couldn't allocate device private record\n"); + return -ENOMEM; + } + memset((void *)drvdata, 0, sizeof(struct hwicap_drvdata)); + dev_set_drvdata(dev, (void *)drvdata); + + if (!regs_res) { + dev_err(dev, "Couldn't get registers resource\n"); + retval = -EFAULT; + goto failed1; + } + + drvdata->mem_start = regs_res->start; + drvdata->mem_end = regs_res->end; + drvdata->mem_size = regs_res->end - regs_res->start + 1; + + if (!request_mem_region(drvdata->mem_start, + drvdata->mem_size, DRIVER_NAME)) { + dev_err(dev, "Couldn't lock memory region at %p\n", + (void *)regs_res->start); + retval = -EBUSY; + goto failed1; + } + + drvdata->devt = devt; + drvdata->dev = dev; + drvdata->base_address = ioremap(drvdata->mem_start, drvdata->mem_size); + if (!drvdata->base_address) { + dev_err(dev, "ioremap() failed\n"); + goto failed2; + } + + drvdata->config = config; + drvdata->config_regs = config_regs; + + init_MUTEX(&drvdata->sem); + drvdata->is_open = 0; + + dev_info(dev, "ioremap %lx to %p with size %x\n", + (unsigned long int)drvdata->mem_start, + drvdata->base_address, drvdata->mem_size); + + cdev_init(&drvdata->cdev, &hwicap_fops); + drvdata->cdev.owner = THIS_MODULE; + retval = cdev_add(&drvdata->cdev, devt, 1); + if (retval) { + dev_err(dev, "cdev_add() failed\n"); + goto failed3; + } + /* devfs_mk_cdev(devt, S_IFCHR|S_IRUGO|S_IWUGO, DRIVER_NAME); */ + class_device_create(icap_class, NULL, devt, NULL, DRIVER_NAME); + return 0; /* success */ + + failed3: + iounmap(drvdata->base_address); + + failed2: + release_mem_region(regs_res->start, drvdata->mem_size); + + failed1: + kfree(drvdata); + + return retval; +} + +static struct hwicap_driver_config buffer_icap_config = { + .get_configuration = buffer_icap_get_configuration, + .set_configuration = buffer_icap_set_configuration, + .reset = buffer_icap_reset, +}; + +static struct hwicap_driver_config fifo_icap_config = { + .get_configuration = fifo_icap_get_configuration, + .set_configuration = fifo_icap_set_configuration, + .reset = fifo_icap_reset, +}; + +static int __devexit hwicap_remove(struct device *dev) +{ + struct hwicap_drvdata *drvdata; + + drvdata = (struct hwicap_drvdata *)dev_get_drvdata(dev); + + if (!drvdata) + return 0; + + class_device_destroy(icap_class, drvdata->devt); + cdev_del(&drvdata->cdev); + iounmap(drvdata->base_address); + release_mem_region(drvdata->mem_start, drvdata->mem_size); + kfree(drvdata); + dev_set_drvdata(dev, NULL); + probed_devices[MINOR(dev->devt)-xhwicap_minor] = 0; + + return 0; /* success */ +} + +static int __devinit hwicap_drv_probe(struct platform_device *pdev) +{ + struct resource *res; + const struct config_registers *regs; + const char *family; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + /* It's most likely that we're using V4, if the family is not + specified */ + regs = &v4_config_registers; + family = pdev->dev.platform_data; + + if (family) { + if (!strcmp(family, "virtex2p")) { + regs = &v2_config_registers; + } else if (!strcmp(family, "virtex4")) { + regs = &v4_config_registers; + } else if (!strcmp(family, "virtex5")) { + regs = &v5_config_registers; + } + } + + return hwicap_setup(&pdev->dev, pdev->id, res, + &buffer_icap_config, regs); +} + +static int __devexit hwicap_drv_remove(struct platform_device *pdev) +{ + return hwicap_remove(&pdev->dev); +} + +static struct platform_driver hwicap_platform_driver = { + .probe = hwicap_drv_probe, + .remove = hwicap_drv_remove, + .driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME, + }, +}; + +/* --------------------------------------------------------------------- + * OF bus binding + */ + +#if defined(CONFIG_OF) +static int __devinit +hwicap_of_probe(struct of_device *op, const struct of_device_id *match) +{ + struct resource res; + const unsigned int *id; + const char *family; + int rc; + const struct hwicap_driver_config *config = match->data; + const struct config_registers *regs; + + dev_dbg(&op->dev, "hwicap_of_probe(%p, %p)\n", op, match); + + rc = of_address_to_resource(op->node, 0, &res); + if (rc) { + dev_err(&op->dev, "invalid address\n"); + return rc; + } + + id = of_get_property(op->node, "port-number", NULL); + + /* It's most likely that we're using V4, if the family is not + specified */ + regs = &v4_config_registers; + family = of_get_property(op->node, "xlnx,family", NULL); + + if (family) { + if (!strcmp(family, "virtex2p")) { + regs = &v2_config_registers; + } else if (!strcmp(family, "virtex4")) { + regs = &v4_config_registers; + } else if (!strcmp(family, "virtex5")) { + regs = &v5_config_registers; + } + } + return hwicap_setup(&op->dev, id ? *id : -1, &res, config, + regs); +} + +static int __devexit hwicap_of_remove(struct of_device *op) +{ + return hwicap_remove(&op->dev); +} + +/* Match table for of_platform binding */ +static const struct of_device_id __devinit hwicap_of_match[] = { + { .compatible = "xlnx,opb-hwicap-1.00.b", .data = &buffer_icap_config}, + { .compatible = "xlnx,xps-hwicap-1.00.a", .data = &fifo_icap_config}, + {}, +}; +MODULE_DEVICE_TABLE(of, hwicap_of_match); + +static struct of_platform_driver hwicap_of_driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME, + .match_table = hwicap_of_match, + .probe = hwicap_of_probe, + .remove = __devexit_p(hwicap_of_remove), + .driver = { + .name = DRIVER_NAME, + }, +}; + +/* Registration helpers to keep the number of #ifdefs to a minimum */ +static inline int __devinit hwicap_of_register(void) +{ + pr_debug("hwicap: calling of_register_platform_driver()\n"); + return of_register_platform_driver(&hwicap_of_driver); +} + +static inline void __devexit hwicap_of_unregister(void) +{ + of_unregister_platform_driver(&hwicap_of_driver); +} +#else /* CONFIG_OF */ +/* CONFIG_OF not enabled; do nothing helpers */ +static inline int __devinit hwicap_of_register(void) { return 0; } +static inline void __devexit hwicap_of_unregister(void) { } +#endif /* CONFIG_OF */ + +static int __devinit hwicap_module_init(void) +{ + dev_t devt; + int retval; + + icap_class = class_create(THIS_MODULE, "xilinx_config"); + + if (xhwicap_major) { + devt = MKDEV(xhwicap_major, xhwicap_minor); + retval = register_chrdev_region( + devt, + HWICAP_DEVICES, + DRIVER_NAME); + if (retval < 0) + return retval; + } else { + retval = alloc_chrdev_region(&devt, + xhwicap_minor, + HWICAP_DEVICES, + DRIVER_NAME); + if (retval < 0) + return retval; + xhwicap_major = MAJOR(devt); + } + + retval = platform_driver_register(&hwicap_platform_driver); + + if (retval) + goto failed1; + + retval = hwicap_of_register(); + + if (retval) + goto failed2; + + return retval; + + failed2: + platform_driver_unregister(&hwicap_platform_driver); + + failed1: + unregister_chrdev_region(devt, HWICAP_DEVICES); + + return retval; +} + +static void __devexit hwicap_module_cleanup(void) +{ + dev_t devt = MKDEV(xhwicap_major, xhwicap_minor); + + class_destroy(icap_class); + + platform_driver_unregister(&hwicap_platform_driver); + + hwicap_of_unregister(); + + unregister_chrdev_region(devt, HWICAP_DEVICES); +} + +module_init(hwicap_module_init); +module_exit(hwicap_module_cleanup); + +MODULE_AUTHOR("Xilinx, Inc; Xilinx Research Labs Group"); +MODULE_DESCRIPTION("Xilinx ICAP Port Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/char/xilinx_hwicap/xilinx_hwicap.h b/drivers/char/xilinx_hwicap/xilinx_hwicap.h new file mode 100644 index 00000000000..ae771cac162 --- /dev/null +++ b/drivers/char/xilinx_hwicap/xilinx_hwicap.h @@ -0,0 +1,193 @@ +/***************************************************************************** + * + * Author: Xilinx, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" + * AS A COURTESY TO YOU, SOLELY FOR USE IN DEVELOPING PROGRAMS AND + * SOLUTIONS FOR XILINX DEVICES. BY PROVIDING THIS DESIGN, CODE, + * OR INFORMATION AS ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, + * APPLICATION OR STANDARD, XILINX IS MAKING NO REPRESENTATION + * THAT THIS IMPLEMENTATION IS FREE FROM ANY CLAIMS OF INFRINGEMENT, + * AND YOU ARE RESPONSIBLE FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE + * FOR YOUR IMPLEMENTATION. XILINX EXPRESSLY DISCLAIMS ANY + * WARRANTY WHATSOEVER WITH RESPECT TO THE ADEQUACY OF THE + * IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OR + * REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM CLAIMS OF + * INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE. + * + * Xilinx products are not intended for use in life support appliances, + * devices, or systems. Use in such applications is expressly prohibited. + * + * (c) Copyright 2003-2007 Xilinx Inc. + * All rights reserved. + * + * 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. + * + *****************************************************************************/ + +#ifndef XILINX_HWICAP_H_ /* prevent circular inclusions */ +#define XILINX_HWICAP_H_ /* by using protection macros */ + +#include <linux/types.h> +#include <linux/cdev.h> +#include <linux/version.h> +#include <linux/platform_device.h> + +#include <asm/io.h> + +struct hwicap_drvdata { + u32 write_buffer_in_use; /* Always in [0,3] */ + u8 write_buffer[4]; + u32 read_buffer_in_use; /* Always in [0,3] */ + u8 read_buffer[4]; + u32 mem_start; /* phys. address of the control registers */ + u32 mem_end; /* phys. address of the control registers */ + u32 mem_size; + void __iomem *base_address;/* virt. address of the control registers */ + + struct device *dev; + struct cdev cdev; /* Char device structure */ + dev_t devt; + + const struct hwicap_driver_config *config; + const struct config_registers *config_regs; + void *private_data; + bool is_open; + struct semaphore sem; +}; + +struct hwicap_driver_config { + int (*get_configuration)(struct hwicap_drvdata *drvdata, u32 *data, + u32 size); + int (*set_configuration)(struct hwicap_drvdata *drvdata, u32 *data, + u32 size); + void (*reset)(struct hwicap_drvdata *drvdata); +}; + +/* Number of times to poll the done regsiter */ +#define XHI_MAX_RETRIES 10 + +/************ Constant Definitions *************/ + +#define XHI_PAD_FRAMES 0x1 + +/* Mask for calculating configuration packet headers */ +#define XHI_WORD_COUNT_MASK_TYPE_1 0x7FFUL +#define XHI_WORD_COUNT_MASK_TYPE_2 0x1FFFFFUL +#define XHI_TYPE_MASK 0x7 +#define XHI_REGISTER_MASK 0xF +#define XHI_OP_MASK 0x3 + +#define XHI_TYPE_SHIFT 29 +#define XHI_REGISTER_SHIFT 13 +#define XHI_OP_SHIFT 27 + +#define XHI_TYPE_1 1 +#define XHI_TYPE_2 2 +#define XHI_OP_WRITE 2 +#define XHI_OP_READ 1 + +/* Address Block Types */ +#define XHI_FAR_CLB_BLOCK 0 +#define XHI_FAR_BRAM_BLOCK 1 +#define XHI_FAR_BRAM_INT_BLOCK 2 + +struct config_registers { + u32 CRC; + u32 FAR; + u32 FDRI; + u32 FDRO; + u32 CMD; + u32 CTL; + u32 MASK; + u32 STAT; + u32 LOUT; + u32 COR; + u32 MFWR; + u32 FLR; + u32 KEY; + u32 CBC; + u32 IDCODE; + u32 AXSS; + u32 C0R_1; + u32 CSOB; + u32 WBSTAR; + u32 TIMER; + u32 BOOTSTS; + u32 CTL_1; +}; + +/* Configuration Commands */ +#define XHI_CMD_NULL 0 +#define XHI_CMD_WCFG 1 +#define XHI_CMD_MFW 2 +#define XHI_CMD_DGHIGH 3 +#define XHI_CMD_RCFG 4 +#define XHI_CMD_START 5 +#define XHI_CMD_RCAP 6 +#define XHI_CMD_RCRC 7 +#define XHI_CMD_AGHIGH 8 +#define XHI_CMD_SWITCH 9 +#define XHI_CMD_GRESTORE 10 +#define XHI_CMD_SHUTDOWN 11 +#define XHI_CMD_GCAPTURE 12 +#define XHI_CMD_DESYNCH 13 +#define XHI_CMD_IPROG 15 /* Only in Virtex5 */ +#define XHI_CMD_CRCC 16 /* Only in Virtex5 */ +#define XHI_CMD_LTIMER 17 /* Only in Virtex5 */ + +/* Packet constants */ +#define XHI_SYNC_PACKET 0xAA995566UL +#define XHI_DUMMY_PACKET 0xFFFFFFFFUL +#define XHI_NOOP_PACKET (XHI_TYPE_1 << XHI_TYPE_SHIFT) +#define XHI_TYPE_2_READ ((XHI_TYPE_2 << XHI_TYPE_SHIFT) | \ + (XHI_OP_READ << XHI_OP_SHIFT)) + +#define XHI_TYPE_2_WRITE ((XHI_TYPE_2 << XHI_TYPE_SHIFT) | \ + (XHI_OP_WRITE << XHI_OP_SHIFT)) + +#define XHI_TYPE2_CNT_MASK 0x07FFFFFF + +#define XHI_TYPE_1_PACKET_MAX_WORDS 2047UL +#define XHI_TYPE_1_HEADER_BYTES 4 +#define XHI_TYPE_2_HEADER_BYTES 8 + +/* Constant to use for CRC check when CRC has been disabled */ +#define XHI_DISABLED_AUTO_CRC 0x0000DEFCUL + +/** + * hwicap_type_1_read: Generates a Type 1 read packet header. + * @parameter: Register is the address of the register to be read back. + * + * Generates a Type 1 read packet header, which is used to indirectly + * read registers in the configuration logic. This packet must then + * be sent through the icap device, and a return packet received with + * the information. + **/ +static inline u32 hwicap_type_1_read(u32 Register) +{ + return (XHI_TYPE_1 << XHI_TYPE_SHIFT) | + (Register << XHI_REGISTER_SHIFT) | + (XHI_OP_READ << XHI_OP_SHIFT); +} + +/** + * hwicap_type_1_write: Generates a Type 1 write packet header + * @parameter: Register is the address of the register to be read back. + **/ +static inline u32 hwicap_type_1_write(u32 Register) +{ + return (XHI_TYPE_1 << XHI_TYPE_SHIFT) | + (Register << XHI_REGISTER_SHIFT) | + (XHI_OP_WRITE << XHI_OP_SHIFT); +} + +#endif diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 7d170cd381c..9cc25fd80b6 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -1737,10 +1737,8 @@ config SC92031 config CPMAC tristate "TI AR7 CPMAC Ethernet support (EXPERIMENTAL)" - depends on NET_ETHERNET && EXPERIMENTAL && AR7 + depends on NET_ETHERNET && EXPERIMENTAL && AR7 && BROKEN select PHYLIB - select FIXED_PHY - select FIXED_MII_100_FDX help TI AR7 CPMAC Ethernet support diff --git a/drivers/net/cpmac.c b/drivers/net/cpmac.c index 6ccebb830ff..c85194f2cd2 100644 --- a/drivers/net/cpmac.c +++ b/drivers/net/cpmac.c @@ -845,15 +845,6 @@ static void cpmac_adjust_link(struct net_device *dev) spin_unlock(&priv->lock); } -static int cpmac_link_update(struct net_device *dev, - struct fixed_phy_status *status) -{ - status->link = 1; - status->speed = 100; - status->duplex = 1; - return 0; -} - static int cpmac_open(struct net_device *dev) { int i, size, res; @@ -996,11 +987,11 @@ static int external_switch; static int __devinit cpmac_probe(struct platform_device *pdev) { int rc, phy_id, i; + int mdio_bus_id = cpmac_mii.id; struct resource *mem; struct cpmac_priv *priv; struct net_device *dev; struct plat_cpmac_data *pdata; - struct fixed_info *fixed_phy; DECLARE_MAC_BUF(mac); pdata = pdev->dev.platform_data; @@ -1014,9 +1005,23 @@ static int __devinit cpmac_probe(struct platform_device *pdev) } if (phy_id == PHY_MAX_ADDR) { - if (external_switch || dumb_switch) + if (external_switch || dumb_switch) { + struct fixed_phy_status status = {}; + + mdio_bus_id = 0; + + /* + * FIXME: this should be in the platform code! + * Since there is not platform code at all (that is, + * no mainline users of that driver), place it here + * for now. + */ phy_id = 0; - else { + status.link = 1; + status.duplex = 1; + status.speed = 100; + fixed_phy_add(PHY_POLL, phy_id, &status); + } else { printk(KERN_ERR "cpmac: no PHY present\n"); return -ENODEV; } @@ -1060,32 +1065,8 @@ static int __devinit cpmac_probe(struct platform_device *pdev) priv->msg_enable = netif_msg_init(debug_level, 0xff); memcpy(dev->dev_addr, pdata->dev_addr, sizeof(dev->dev_addr)); - if (phy_id == 31) { - snprintf(priv->phy_name, BUS_ID_SIZE, PHY_ID_FMT, cpmac_mii.id, - phy_id); - } else { - /* Let's try to get a free fixed phy... */ - for (i = 0; i < MAX_PHY_AMNT; i++) { - fixed_phy = fixed_mdio_get_phydev(i); - if (!fixed_phy) - continue; - if (!fixed_phy->phydev->attached_dev) { - strncpy(priv->phy_name, - fixed_phy->phydev->dev.bus_id, - BUS_ID_SIZE); - fixed_mdio_set_link_update(fixed_phy->phydev, - &cpmac_link_update); - goto phy_found; - } - } - if (netif_msg_drv(priv)) - printk(KERN_ERR "%s: Could not find fixed PHY\n", - dev->name); - rc = -ENODEV; - goto fail; - } + snprintf(priv->phy_name, BUS_ID_SIZE, PHY_ID_FMT, mdio_bus_id, phy_id); -phy_found: priv->phy = phy_connect(dev, priv->phy_name, &cpmac_adjust_link, 0, PHY_INTERFACE_MODE_MII); if (IS_ERR(priv->phy)) { diff --git a/drivers/of/base.c b/drivers/of/base.c index b306fef1ac4..80c9deca5f3 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -138,6 +138,31 @@ struct device_node *of_get_parent(const struct device_node *node) EXPORT_SYMBOL(of_get_parent); /** + * of_get_next_parent - Iterate to a node's parent + * @node: Node to get parent of + * + * This is like of_get_parent() except that it drops the + * refcount on the passed node, making it suitable for iterating + * through a node's parents. + * + * Returns a node pointer with refcount incremented, use + * of_node_put() on it when done. + */ +struct device_node *of_get_next_parent(struct device_node *node) +{ + struct device_node *parent; + + if (!node) + return NULL; + + read_lock(&devtree_lock); + parent = of_node_get(node->parent); + of_node_put(node); + read_unlock(&devtree_lock); + return parent; +} + +/** * of_get_next_child - Iterate a node childs * @node: parent node * @prev: previous child of the parent node, or NULL to get first diff --git a/drivers/of/platform.c b/drivers/of/platform.c index b47bb2d7476..ca09a63a64d 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -85,6 +85,15 @@ static int of_platform_device_resume(struct device * dev) return error; } +static void of_platform_device_shutdown(struct device *dev) +{ + struct of_device *of_dev = to_of_device(dev); + struct of_platform_driver *drv = to_of_platform_driver(dev->driver); + + if (dev->driver && drv->shutdown) + drv->shutdown(of_dev); +} + int of_bus_type_init(struct bus_type *bus, const char *name) { bus->name = name; @@ -93,6 +102,7 @@ int of_bus_type_init(struct bus_type *bus, const char *name) bus->remove = of_platform_device_remove; bus->suspend = of_platform_device_suspend; bus->resume = of_platform_device_resume; + bus->shutdown = of_platform_device_shutdown; return bus_register(bus); } diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 50d6e2214dd..84a054d7e98 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -1142,17 +1142,17 @@ config SERIAL_SGI_L1_CONSOLE say Y. Otherwise, say N. config SERIAL_MPC52xx - tristate "Freescale MPC52xx family PSC serial support" - depends on PPC_MPC52xx + tristate "Freescale MPC52xx/MPC512x family PSC serial support" + depends on PPC_MPC52xx || PPC_MPC512x select SERIAL_CORE help - This drivers support the MPC52xx PSC serial ports. If you would - like to use them, you must answer Y or M to this option. Not that + This driver supports MPC52xx and MPC512x PSC serial ports. If you would + like to use them, you must answer Y or M to this option. Note that for use as console, it must be included in kernel and not as a module. config SERIAL_MPC52xx_CONSOLE - bool "Console on a Freescale MPC52xx family PSC serial port" + bool "Console on a Freescale MPC52xx/MPC512x family PSC serial port" depends on SERIAL_MPC52xx=y select SERIAL_CORE_CONSOLE help @@ -1160,7 +1160,7 @@ config SERIAL_MPC52xx_CONSOLE of the Freescale MPC52xx family as a console. config SERIAL_MPC52xx_CONSOLE_BAUD - int "Freescale MPC52xx family PSC serial port baud" + int "Freescale MPC52xx/MPC512x family PSC serial port baud" depends on SERIAL_MPC52xx_CONSOLE=y default "9600" help diff --git a/drivers/serial/mpc52xx_uart.c b/drivers/serial/mpc52xx_uart.c index 3c4d29e59b2..a638f23c6c6 100644 --- a/drivers/serial/mpc52xx_uart.c +++ b/drivers/serial/mpc52xx_uart.c @@ -16,6 +16,9 @@ * Some of the code has been inspired/copied from the 2.4 code written * by Dale Farnsworth <dfarnsworth@mvista.com>. * + * Copyright (C) 2008 Freescale Semiconductor Inc. + * John Rigby <jrigby@gmail.com> + * Added support for MPC5121 * Copyright (C) 2006 Secret Lab Technologies Ltd. * Grant Likely <grant.likely@secretlab.ca> * Copyright (C) 2004-2006 Sylvain Munaut <tnt@246tNt.com> @@ -67,7 +70,6 @@ #include <linux/serial.h> #include <linux/sysrq.h> #include <linux/console.h> - #include <linux/delay.h> #include <linux/io.h> @@ -79,6 +81,7 @@ #endif #include <asm/mpc52xx.h> +#include <asm/mpc512x.h> #include <asm/mpc52xx_psc.h> #if defined(CONFIG_SERIAL_MPC52xx_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) @@ -111,8 +114,8 @@ static struct device_node *mpc52xx_uart_nodes[MPC52xx_PSC_MAXNUM]; static void mpc52xx_uart_of_enumerate(void); #endif + #define PSC(port) ((struct mpc52xx_psc __iomem *)((port)->membase)) -#define FIFO(port) ((struct mpc52xx_psc_fifo __iomem *)(PSC(port)+1)) /* Forward declaration of the interruption handling routine */ @@ -128,15 +131,301 @@ static irqreturn_t mpc52xx_uart_int(int irq, void *dev_id); #define uart_console(port) (0) #endif +/* ======================================================================== */ +/* PSC fifo operations for isolating differences between 52xx and 512x */ +/* ======================================================================== */ + +struct psc_ops { + void (*fifo_init)(struct uart_port *port); + int (*raw_rx_rdy)(struct uart_port *port); + int (*raw_tx_rdy)(struct uart_port *port); + int (*rx_rdy)(struct uart_port *port); + int (*tx_rdy)(struct uart_port *port); + int (*tx_empty)(struct uart_port *port); + void (*stop_rx)(struct uart_port *port); + void (*start_tx)(struct uart_port *port); + void (*stop_tx)(struct uart_port *port); + void (*rx_clr_irq)(struct uart_port *port); + void (*tx_clr_irq)(struct uart_port *port); + void (*write_char)(struct uart_port *port, unsigned char c); + unsigned char (*read_char)(struct uart_port *port); + void (*cw_disable_ints)(struct uart_port *port); + void (*cw_restore_ints)(struct uart_port *port); + unsigned long (*getuartclk)(void *p); +}; + +#ifdef CONFIG_PPC_MPC52xx +#define FIFO_52xx(port) ((struct mpc52xx_psc_fifo __iomem *)(PSC(port)+1)) +static void mpc52xx_psc_fifo_init(struct uart_port *port) +{ + struct mpc52xx_psc __iomem *psc = PSC(port); + struct mpc52xx_psc_fifo __iomem *fifo = FIFO_52xx(port); + + /* /32 prescaler */ + out_be16(&psc->mpc52xx_psc_clock_select, 0xdd00); + + out_8(&fifo->rfcntl, 0x00); + out_be16(&fifo->rfalarm, 0x1ff); + out_8(&fifo->tfcntl, 0x07); + out_be16(&fifo->tfalarm, 0x80); + + port->read_status_mask |= MPC52xx_PSC_IMR_RXRDY | MPC52xx_PSC_IMR_TXRDY; + out_be16(&psc->mpc52xx_psc_imr, port->read_status_mask); +} + +static int mpc52xx_psc_raw_rx_rdy(struct uart_port *port) +{ + return in_be16(&PSC(port)->mpc52xx_psc_status) + & MPC52xx_PSC_SR_RXRDY; +} + +static int mpc52xx_psc_raw_tx_rdy(struct uart_port *port) +{ + return in_be16(&PSC(port)->mpc52xx_psc_status) + & MPC52xx_PSC_SR_TXRDY; +} + + +static int mpc52xx_psc_rx_rdy(struct uart_port *port) +{ + return in_be16(&PSC(port)->mpc52xx_psc_isr) + & port->read_status_mask + & MPC52xx_PSC_IMR_RXRDY; +} + +static int mpc52xx_psc_tx_rdy(struct uart_port *port) +{ + return in_be16(&PSC(port)->mpc52xx_psc_isr) + & port->read_status_mask + & MPC52xx_PSC_IMR_TXRDY; +} + +static int mpc52xx_psc_tx_empty(struct uart_port *port) +{ + return in_be16(&PSC(port)->mpc52xx_psc_status) + & MPC52xx_PSC_SR_TXEMP; +} + +static void mpc52xx_psc_start_tx(struct uart_port *port) +{ + port->read_status_mask |= MPC52xx_PSC_IMR_TXRDY; + out_be16(&PSC(port)->mpc52xx_psc_imr, port->read_status_mask); +} + +static void mpc52xx_psc_stop_tx(struct uart_port *port) +{ + port->read_status_mask &= ~MPC52xx_PSC_IMR_TXRDY; + out_be16(&PSC(port)->mpc52xx_psc_imr, port->read_status_mask); +} + +static void mpc52xx_psc_stop_rx(struct uart_port *port) +{ + port->read_status_mask &= ~MPC52xx_PSC_IMR_RXRDY; + out_be16(&PSC(port)->mpc52xx_psc_imr, port->read_status_mask); +} + +static void mpc52xx_psc_rx_clr_irq(struct uart_port *port) +{ +} + +static void mpc52xx_psc_tx_clr_irq(struct uart_port *port) +{ +} + +static void mpc52xx_psc_write_char(struct uart_port *port, unsigned char c) +{ + out_8(&PSC(port)->mpc52xx_psc_buffer_8, c); +} + +static unsigned char mpc52xx_psc_read_char(struct uart_port *port) +{ + return in_8(&PSC(port)->mpc52xx_psc_buffer_8); +} + +static void mpc52xx_psc_cw_disable_ints(struct uart_port *port) +{ + out_be16(&PSC(port)->mpc52xx_psc_imr, 0); +} + +static void mpc52xx_psc_cw_restore_ints(struct uart_port *port) +{ + out_be16(&PSC(port)->mpc52xx_psc_imr, port->read_status_mask); +} + +/* Search for bus-frequency property in this node or a parent */ +static unsigned long mpc52xx_getuartclk(void *p) +{ #if defined(CONFIG_PPC_MERGE) -static struct of_device_id mpc52xx_uart_of_match[] = { - { .type = "serial", .compatible = "fsl,mpc5200-psc-uart", }, - { .type = "serial", .compatible = "mpc5200-psc-uart", }, /* lite5200 */ - { .type = "serial", .compatible = "mpc5200-serial", }, /* efika */ - {}, + /* + * 5200 UARTs have a / 32 prescaler + * but the generic serial code assumes 16 + * so return ipb freq / 2 + */ + return mpc52xx_find_ipb_freq(p) / 2; +#else + pr_debug("unexpected call to mpc52xx_getuartclk with arch/ppc\n"); + return NULL; +#endif +} + +static struct psc_ops mpc52xx_psc_ops = { + .fifo_init = mpc52xx_psc_fifo_init, + .raw_rx_rdy = mpc52xx_psc_raw_rx_rdy, + .raw_tx_rdy = mpc52xx_psc_raw_tx_rdy, + .rx_rdy = mpc52xx_psc_rx_rdy, + .tx_rdy = mpc52xx_psc_tx_rdy, + .tx_empty = mpc52xx_psc_tx_empty, + .stop_rx = mpc52xx_psc_stop_rx, + .start_tx = mpc52xx_psc_start_tx, + .stop_tx = mpc52xx_psc_stop_tx, + .rx_clr_irq = mpc52xx_psc_rx_clr_irq, + .tx_clr_irq = mpc52xx_psc_tx_clr_irq, + .write_char = mpc52xx_psc_write_char, + .read_char = mpc52xx_psc_read_char, + .cw_disable_ints = mpc52xx_psc_cw_disable_ints, + .cw_restore_ints = mpc52xx_psc_cw_restore_ints, + .getuartclk = mpc52xx_getuartclk, +}; + +#endif /* CONFIG_MPC52xx */ + +#ifdef CONFIG_PPC_MPC512x +#define FIFO_512x(port) ((struct mpc512x_psc_fifo __iomem *)(PSC(port)+1)) +static void mpc512x_psc_fifo_init(struct uart_port *port) +{ + out_be32(&FIFO_512x(port)->txcmd, MPC512x_PSC_FIFO_RESET_SLICE); + out_be32(&FIFO_512x(port)->txcmd, MPC512x_PSC_FIFO_ENABLE_SLICE); + out_be32(&FIFO_512x(port)->txalarm, 1); + out_be32(&FIFO_512x(port)->tximr, 0); + + out_be32(&FIFO_512x(port)->rxcmd, MPC512x_PSC_FIFO_RESET_SLICE); + out_be32(&FIFO_512x(port)->rxcmd, MPC512x_PSC_FIFO_ENABLE_SLICE); + out_be32(&FIFO_512x(port)->rxalarm, 1); + out_be32(&FIFO_512x(port)->rximr, 0); + + out_be32(&FIFO_512x(port)->tximr, MPC512x_PSC_FIFO_ALARM); + out_be32(&FIFO_512x(port)->rximr, MPC512x_PSC_FIFO_ALARM); +} + +static int mpc512x_psc_raw_rx_rdy(struct uart_port *port) +{ + return !(in_be32(&FIFO_512x(port)->rxsr) & MPC512x_PSC_FIFO_EMPTY); +} + +static int mpc512x_psc_raw_tx_rdy(struct uart_port *port) +{ + return !(in_be32(&FIFO_512x(port)->txsr) & MPC512x_PSC_FIFO_FULL); +} + +static int mpc512x_psc_rx_rdy(struct uart_port *port) +{ + return in_be32(&FIFO_512x(port)->rxsr) + & in_be32(&FIFO_512x(port)->rximr) + & MPC512x_PSC_FIFO_ALARM; +} + +static int mpc512x_psc_tx_rdy(struct uart_port *port) +{ + return in_be32(&FIFO_512x(port)->txsr) + & in_be32(&FIFO_512x(port)->tximr) + & MPC512x_PSC_FIFO_ALARM; +} + +static int mpc512x_psc_tx_empty(struct uart_port *port) +{ + return in_be32(&FIFO_512x(port)->txsr) + & MPC512x_PSC_FIFO_EMPTY; +} + +static void mpc512x_psc_stop_rx(struct uart_port *port) +{ + unsigned long rx_fifo_imr; + + rx_fifo_imr = in_be32(&FIFO_512x(port)->rximr); + rx_fifo_imr &= ~MPC512x_PSC_FIFO_ALARM; + out_be32(&FIFO_512x(port)->rximr, rx_fifo_imr); +} + +static void mpc512x_psc_start_tx(struct uart_port *port) +{ + unsigned long tx_fifo_imr; + + tx_fifo_imr = in_be32(&FIFO_512x(port)->tximr); + tx_fifo_imr |= MPC512x_PSC_FIFO_ALARM; + out_be32(&FIFO_512x(port)->tximr, tx_fifo_imr); +} + +static void mpc512x_psc_stop_tx(struct uart_port *port) +{ + unsigned long tx_fifo_imr; + + tx_fifo_imr = in_be32(&FIFO_512x(port)->tximr); + tx_fifo_imr &= ~MPC512x_PSC_FIFO_ALARM; + out_be32(&FIFO_512x(port)->tximr, tx_fifo_imr); +} + +static void mpc512x_psc_rx_clr_irq(struct uart_port *port) +{ + out_be32(&FIFO_512x(port)->rxisr, in_be32(&FIFO_512x(port)->rxisr)); +} + +static void mpc512x_psc_tx_clr_irq(struct uart_port *port) +{ + out_be32(&FIFO_512x(port)->txisr, in_be32(&FIFO_512x(port)->txisr)); +} + +static void mpc512x_psc_write_char(struct uart_port *port, unsigned char c) +{ + out_8(&FIFO_512x(port)->txdata_8, c); +} + +static unsigned char mpc512x_psc_read_char(struct uart_port *port) +{ + return in_8(&FIFO_512x(port)->rxdata_8); +} + +static void mpc512x_psc_cw_disable_ints(struct uart_port *port) +{ + port->read_status_mask = + in_be32(&FIFO_512x(port)->tximr) << 16 | + in_be32(&FIFO_512x(port)->rximr); + out_be32(&FIFO_512x(port)->tximr, 0); + out_be32(&FIFO_512x(port)->rximr, 0); +} + +static void mpc512x_psc_cw_restore_ints(struct uart_port *port) +{ + out_be32(&FIFO_512x(port)->tximr, + (port->read_status_mask >> 16) & 0x7f); + out_be32(&FIFO_512x(port)->rximr, port->read_status_mask & 0x7f); +} + +static unsigned long mpc512x_getuartclk(void *p) +{ + return mpc512x_find_ips_freq(p); +} + +static struct psc_ops mpc512x_psc_ops = { + .fifo_init = mpc512x_psc_fifo_init, + .raw_rx_rdy = mpc512x_psc_raw_rx_rdy, + .raw_tx_rdy = mpc512x_psc_raw_tx_rdy, + .rx_rdy = mpc512x_psc_rx_rdy, + .tx_rdy = mpc512x_psc_tx_rdy, + .tx_empty = mpc512x_psc_tx_empty, + .stop_rx = mpc512x_psc_stop_rx, + .start_tx = mpc512x_psc_start_tx, + .stop_tx = mpc512x_psc_stop_tx, + .rx_clr_irq = mpc512x_psc_rx_clr_irq, + .tx_clr_irq = mpc512x_psc_tx_clr_irq, + .write_char = mpc512x_psc_write_char, + .read_char = mpc512x_psc_read_char, + .cw_disable_ints = mpc512x_psc_cw_disable_ints, + .cw_restore_ints = mpc512x_psc_cw_restore_ints, + .getuartclk = mpc512x_getuartclk, }; #endif +static struct psc_ops *psc_ops; /* ======================================================================== */ /* UART operations */ @@ -145,8 +434,7 @@ static struct of_device_id mpc52xx_uart_of_match[] = { static unsigned int mpc52xx_uart_tx_empty(struct uart_port *port) { - int status = in_be16(&PSC(port)->mpc52xx_psc_status); - return (status & MPC52xx_PSC_SR_TXEMP) ? TIOCSER_TEMT : 0; + return psc_ops->tx_empty(port) ? TIOCSER_TEMT : 0; } static void @@ -166,16 +454,14 @@ static void mpc52xx_uart_stop_tx(struct uart_port *port) { /* port->lock taken by caller */ - port->read_status_mask &= ~MPC52xx_PSC_IMR_TXRDY; - out_be16(&PSC(port)->mpc52xx_psc_imr, port->read_status_mask); + psc_ops->stop_tx(port); } static void mpc52xx_uart_start_tx(struct uart_port *port) { /* port->lock taken by caller */ - port->read_status_mask |= MPC52xx_PSC_IMR_TXRDY; - out_be16(&PSC(port)->mpc52xx_psc_imr, port->read_status_mask); + psc_ops->start_tx(port); } static void @@ -188,8 +474,7 @@ mpc52xx_uart_send_xchar(struct uart_port *port, char ch) if (ch) { /* Make sure tx interrupts are on */ /* Truly necessary ??? They should be anyway */ - port->read_status_mask |= MPC52xx_PSC_IMR_TXRDY; - out_be16(&PSC(port)->mpc52xx_psc_imr, port->read_status_mask); + psc_ops->start_tx(port); } spin_unlock_irqrestore(&port->lock, flags); @@ -199,8 +484,7 @@ static void mpc52xx_uart_stop_rx(struct uart_port *port) { /* port->lock taken by caller */ - port->read_status_mask &= ~MPC52xx_PSC_IMR_RXRDY; - out_be16(&PSC(port)->mpc52xx_psc_imr, port->read_status_mask); + psc_ops->stop_rx(port); } static void @@ -227,12 +511,12 @@ static int mpc52xx_uart_startup(struct uart_port *port) { struct mpc52xx_psc __iomem *psc = PSC(port); - struct mpc52xx_psc_fifo __iomem *fifo = FIFO(port); int ret; /* Request IRQ */ ret = request_irq(port->irq, mpc52xx_uart_int, - IRQF_DISABLED | IRQF_SAMPLE_RANDOM, "mpc52xx_psc_uart", port); + IRQF_DISABLED | IRQF_SAMPLE_RANDOM | IRQF_SHARED, + "mpc52xx_psc_uart", port); if (ret) return ret; @@ -242,15 +526,7 @@ mpc52xx_uart_startup(struct uart_port *port) out_be32(&psc->sicr, 0); /* UART mode DCD ignored */ - out_be16(&psc->mpc52xx_psc_clock_select, 0xdd00); /* /16 prescaler on */ - - out_8(&fifo->rfcntl, 0x00); - out_be16(&fifo->rfalarm, 0x1ff); - out_8(&fifo->tfcntl, 0x07); - out_be16(&fifo->tfalarm, 0x80); - - port->read_status_mask |= MPC52xx_PSC_IMR_RXRDY | MPC52xx_PSC_IMR_TXRDY; - out_be16(&psc->mpc52xx_psc_imr, port->read_status_mask); + psc_ops->fifo_init(port); out_8(&psc->command, MPC52xx_PSC_TX_ENABLE); out_8(&psc->command, MPC52xx_PSC_RX_ENABLE); @@ -333,8 +609,7 @@ mpc52xx_uart_set_termios(struct uart_port *port, struct ktermios *new, * boot for the console, all stuff is not yet ready to receive at that * time and that just makes the kernel oops */ /* while (j-- && mpc52xx_uart_int_rx_chars(port)); */ - while (!(in_be16(&psc->mpc52xx_psc_status) & MPC52xx_PSC_SR_TXEMP) && - --j) + while (!mpc52xx_uart_tx_empty(port) && --j) udelay(1); if (!j) @@ -462,11 +737,9 @@ mpc52xx_uart_int_rx_chars(struct uart_port *port) unsigned short status; /* While we can read, do so ! */ - while ((status = in_be16(&PSC(port)->mpc52xx_psc_status)) & - MPC52xx_PSC_SR_RXRDY) { - + while (psc_ops->raw_rx_rdy(port)) { /* Get the char */ - ch = in_8(&PSC(port)->mpc52xx_psc_buffer_8); + ch = psc_ops->read_char(port); /* Handle sysreq char */ #ifdef SUPPORT_SYSRQ @@ -481,6 +754,8 @@ mpc52xx_uart_int_rx_chars(struct uart_port *port) flag = TTY_NORMAL; port->icount.rx++; + status = in_be16(&PSC(port)->mpc52xx_psc_status); + if (status & (MPC52xx_PSC_SR_PE | MPC52xx_PSC_SR_FE | MPC52xx_PSC_SR_RB)) { @@ -510,7 +785,7 @@ mpc52xx_uart_int_rx_chars(struct uart_port *port) tty_flip_buffer_push(tty); - return in_be16(&PSC(port)->mpc52xx_psc_status) & MPC52xx_PSC_SR_RXRDY; + return psc_ops->raw_rx_rdy(port); } static inline int @@ -520,7 +795,7 @@ mpc52xx_uart_int_tx_chars(struct uart_port *port) /* Process out of band chars */ if (port->x_char) { - out_8(&PSC(port)->mpc52xx_psc_buffer_8, port->x_char); + psc_ops->write_char(port, port->x_char); port->icount.tx++; port->x_char = 0; return 1; @@ -533,8 +808,8 @@ mpc52xx_uart_int_tx_chars(struct uart_port *port) } /* Send chars */ - while (in_be16(&PSC(port)->mpc52xx_psc_status) & MPC52xx_PSC_SR_TXRDY) { - out_8(&PSC(port)->mpc52xx_psc_buffer_8, xmit->buf[xmit->tail]); + while (psc_ops->raw_tx_rdy(port)) { + psc_ops->write_char(port, xmit->buf[xmit->tail]); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); port->icount.tx++; if (uart_circ_empty(xmit)) @@ -560,7 +835,6 @@ mpc52xx_uart_int(int irq, void *dev_id) struct uart_port *port = dev_id; unsigned long pass = ISR_PASS_LIMIT; unsigned int keepgoing; - unsigned short status; spin_lock(&port->lock); @@ -569,18 +843,12 @@ mpc52xx_uart_int(int irq, void *dev_id) /* If we don't find anything to do, we stop */ keepgoing = 0; - /* Read status */ - status = in_be16(&PSC(port)->mpc52xx_psc_isr); - status &= port->read_status_mask; - - /* Do we need to receive chars ? */ - /* For this RX interrupts must be on and some chars waiting */ - if (status & MPC52xx_PSC_IMR_RXRDY) + psc_ops->rx_clr_irq(port); + if (psc_ops->rx_rdy(port)) keepgoing |= mpc52xx_uart_int_rx_chars(port); - /* Do we need to send chars ? */ - /* For this, TX must be ready and TX interrupt enabled */ - if (status & MPC52xx_PSC_IMR_TXRDY) + psc_ops->tx_clr_irq(port); + if (psc_ops->tx_rdy(port)) keepgoing |= mpc52xx_uart_int_tx_chars(port); /* Limit number of iteration */ @@ -647,36 +915,33 @@ static void mpc52xx_console_write(struct console *co, const char *s, unsigned int count) { struct uart_port *port = &mpc52xx_uart_ports[co->index]; - struct mpc52xx_psc __iomem *psc = PSC(port); unsigned int i, j; /* Disable interrupts */ - out_be16(&psc->mpc52xx_psc_imr, 0); + psc_ops->cw_disable_ints(port); /* Wait the TX buffer to be empty */ j = 5000000; /* Maximum wait */ - while (!(in_be16(&psc->mpc52xx_psc_status) & MPC52xx_PSC_SR_TXEMP) && - --j) + while (!mpc52xx_uart_tx_empty(port) && --j) udelay(1); /* Write all the chars */ for (i = 0; i < count; i++, s++) { /* Line return handling */ if (*s == '\n') - out_8(&psc->mpc52xx_psc_buffer_8, '\r'); + psc_ops->write_char(port, '\r'); /* Send the char */ - out_8(&psc->mpc52xx_psc_buffer_8, *s); + psc_ops->write_char(port, *s); /* Wait the TX buffer to be empty */ j = 20000; /* Maximum wait */ - while (!(in_be16(&psc->mpc52xx_psc_status) & - MPC52xx_PSC_SR_TXEMP) && --j) + while (!mpc52xx_uart_tx_empty(port) && --j) udelay(1); } /* Restore interrupt state */ - out_be16(&psc->mpc52xx_psc_imr, port->read_status_mask); + psc_ops->cw_restore_ints(port); } #if !defined(CONFIG_PPC_MERGE) @@ -721,7 +986,7 @@ mpc52xx_console_setup(struct console *co, char *options) { struct uart_port *port = &mpc52xx_uart_ports[co->index]; struct device_node *np = mpc52xx_uart_nodes[co->index]; - unsigned int ipb_freq; + unsigned int uartclk; struct resource res; int ret; @@ -753,17 +1018,16 @@ mpc52xx_console_setup(struct console *co, char *options) return ret; } - /* Search for bus-frequency property in this node or a parent */ - ipb_freq = mpc52xx_find_ipb_freq(np); - if (ipb_freq == 0) { - pr_debug("Could not find IPB bus frequency!\n"); + uartclk = psc_ops->getuartclk(np); + if (uartclk == 0) { + pr_debug("Could not find uart clock frequency!\n"); return -EINVAL; } /* Basic port init. Needed since we use some uart_??? func before * real init for early access */ spin_lock_init(&port->lock); - port->uartclk = ipb_freq / 2; + port->uartclk = uartclk; port->ops = &mpc52xx_uart_ops; port->mapbase = res.start; port->membase = ioremap(res.start, sizeof(struct mpc52xx_psc)); @@ -945,11 +1209,25 @@ static struct platform_driver mpc52xx_uart_platform_driver = { /* OF Platform Driver */ /* ======================================================================== */ +static struct of_device_id mpc52xx_uart_of_match[] = { +#ifdef CONFIG_PPC_MPC52xx + { .compatible = "fsl,mpc5200-psc-uart", .data = &mpc52xx_psc_ops, }, + /* binding used by old lite5200 device trees: */ + { .compatible = "mpc5200-psc-uart", .data = &mpc52xx_psc_ops, }, + /* binding used by efika: */ + { .compatible = "mpc5200-serial", .data = &mpc52xx_psc_ops, }, +#endif +#ifdef CONFIG_PPC_MPC512x + { .compatible = "fsl,mpc5121-psc-uart", .data = &mpc512x_psc_ops, }, + {}, +#endif +}; + static int __devinit mpc52xx_uart_of_probe(struct of_device *op, const struct of_device_id *match) { int idx = -1; - unsigned int ipb_freq; + unsigned int uartclk; struct uart_port *port = NULL; struct resource res; int ret; @@ -965,10 +1243,9 @@ mpc52xx_uart_of_probe(struct of_device *op, const struct of_device_id *match) pr_debug("Found %s assigned to ttyPSC%x\n", mpc52xx_uart_nodes[idx]->full_name, idx); - /* Search for bus-frequency property in this node or a parent */ - ipb_freq = mpc52xx_find_ipb_freq(op->node); - if (ipb_freq == 0) { - dev_dbg(&op->dev, "Could not find IPB bus frequency!\n"); + uartclk = psc_ops->getuartclk(op->node); + if (uartclk == 0) { + dev_dbg(&op->dev, "Could not find uart clock frequency!\n"); return -EINVAL; } @@ -976,7 +1253,7 @@ mpc52xx_uart_of_probe(struct of_device *op, const struct of_device_id *match) port = &mpc52xx_uart_ports[idx]; spin_lock_init(&port->lock); - port->uartclk = ipb_freq / 2; + port->uartclk = uartclk; port->fifosize = 512; port->iotype = UPIO_MEM; port->flags = UPF_BOOT_AUTOCONF | @@ -1080,15 +1357,19 @@ mpc52xx_uart_of_enumerate(void) static int enum_done; struct device_node *np; const unsigned int *devno; + const struct of_device_id *match; int i; if (enum_done) return; for_each_node_by_type(np, "serial") { - if (!of_match_node(mpc52xx_uart_of_match, np)) + match = of_match_node(mpc52xx_uart_of_match, np); + if (!match) continue; + psc_ops = match->data; + /* Is a particular device number requested? */ devno = of_get_property(np, "port-number", NULL); mpc52xx_uart_of_assign(np, devno ? *devno : -1); @@ -1149,6 +1430,7 @@ mpc52xx_uart_init(void) return ret; } #else + psc_ops = &mpc52xx_psc_ops; ret = platform_driver_register(&mpc52xx_uart_platform_driver); if (ret) { printk(KERN_ERR "%s: platform_driver_register failed (%i)\n", diff --git a/drivers/serial/uartlite.c b/drivers/serial/uartlite.c index bacf68dca01..4e06ab6bcb6 100644 --- a/drivers/serial/uartlite.c +++ b/drivers/serial/uartlite.c @@ -17,10 +17,21 @@ #include <linux/tty.h> #include <linux/delay.h> #include <linux/interrupt.h> +#include <linux/init.h> #include <asm/io.h> #if defined(CONFIG_OF) +#include <linux/of.h> #include <linux/of_device.h> #include <linux/of_platform.h> + +/* Match table for of_platform binding */ +static struct of_device_id ulite_of_match[] __devinitdata = { + { .compatible = "xlnx,opb-uartlite-1.00.b", }, + { .compatible = "xlnx,xps-uartlite-1.00.a", }, + {} +}; +MODULE_DEVICE_TABLE(of, ulite_of_match); + #endif #define ULITE_NAME "ttyUL" @@ -275,6 +286,9 @@ static void ulite_release_port(struct uart_port *port) static int ulite_request_port(struct uart_port *port) { + pr_debug("ulite console: port=%p; port->mapbase=%x\n", + port, port->mapbase); + if (!request_mem_region(port->mapbase, ULITE_REGION, "uartlite")) { dev_err(port->dev, "Memory region busy\n"); return -EBUSY; @@ -375,32 +389,6 @@ static void ulite_console_write(struct console *co, const char *s, spin_unlock_irqrestore(&port->lock, flags); } -#if defined(CONFIG_OF) -static inline void __init ulite_console_of_find_device(int id) -{ - struct device_node *np; - struct resource res; - const unsigned int *of_id; - int rc; - - for_each_compatible_node(np, NULL, "xilinx,uartlite") { - of_id = of_get_property(np, "port-number", NULL); - if ((!of_id) || (*of_id != id)) - continue; - - rc = of_address_to_resource(np, 0, &res); - if (rc) - continue; - - ulite_ports[id].mapbase = res.start; - of_node_put(np); - return; - } -} -#else /* CONFIG_OF */ -static inline void __init ulite_console_of_find_device(int id) { /* do nothing */ } -#endif /* CONFIG_OF */ - static int __init ulite_console_setup(struct console *co, char *options) { struct uart_port *port; @@ -414,11 +402,7 @@ static int __init ulite_console_setup(struct console *co, char *options) port = &ulite_ports[co->index]; - /* Check if it is an OF device */ - if (!port->mapbase) - ulite_console_of_find_device(co->index); - - /* Do we have a device now? */ + /* Has the device been initialized yet? */ if (!port->mapbase) { pr_debug("console on ttyUL%i not present\n", co->index); return -ENODEV; @@ -617,13 +601,6 @@ static int __devexit ulite_of_remove(struct of_device *op) return ulite_release(&op->dev); } -/* Match table for of_platform binding */ -static struct of_device_id __devinit ulite_of_match[] = { - { .type = "serial", .compatible = "xilinx,uartlite", }, - {}, -}; -MODULE_DEVICE_TABLE(of, ulite_of_match); - static struct of_platform_driver ulite_of_driver = { .owner = THIS_MODULE, .name = "uartlite", diff --git a/drivers/video/xilinxfb.c b/drivers/video/xilinxfb.c index e38d3b7c3ad..7b3a8423f48 100644 --- a/drivers/video/xilinxfb.c +++ b/drivers/video/xilinxfb.c @@ -459,8 +459,8 @@ static int __devexit xilinxfb_of_remove(struct of_device *op) } /* Match table for of_platform binding */ -static struct of_device_id __devinit xilinxfb_of_match[] = { - { .compatible = "xilinx,ml300-fb", }, +static struct of_device_id xilinxfb_of_match[] __devinitdata = { + { .compatible = "xlnx,plb-tft-cntlr-ref-1.00.a", }, {}, }; MODULE_DEVICE_TABLE(of, xilinxfb_of_match); diff --git a/include/asm-powerpc/cputable.h b/include/asm-powerpc/cputable.h index 528ef183c22..1e79673b731 100644 --- a/include/asm-powerpc/cputable.h +++ b/include/asm-powerpc/cputable.h @@ -46,7 +46,7 @@ enum powerpc_oprofile_type { PPC_OPROFILE_RS64 = 1, PPC_OPROFILE_POWER4 = 2, PPC_OPROFILE_G4 = 3, - PPC_OPROFILE_BOOKE = 4, + PPC_OPROFILE_FSL_EMB = 4, PPC_OPROFILE_CELL = 5, PPC_OPROFILE_PA6T = 6, }; diff --git a/include/asm-powerpc/dcr-native.h b/include/asm-powerpc/dcr-native.h index af5fb31af55..be6c879e876 100644 --- a/include/asm-powerpc/dcr-native.h +++ b/include/asm-powerpc/dcr-native.h @@ -59,25 +59,36 @@ do { \ /* R/W of indirect DCRs make use of standard naming conventions for DCRs */ extern spinlock_t dcr_ind_lock; -#define mfdcri(base, reg) \ -({ \ - unsigned long flags; \ - unsigned int val; \ - spin_lock_irqsave(&dcr_ind_lock, flags); \ - mtdcr(DCRN_ ## base ## _CONFIG_ADDR, reg); \ - val = mfdcr(DCRN_ ## base ## _CONFIG_DATA); \ - spin_unlock_irqrestore(&dcr_ind_lock, flags); \ - val; \ -}) +static inline unsigned __mfdcri(int base_addr, int base_data, int reg) +{ + unsigned long flags; + unsigned int val; -#define mtdcri(base, reg, data) \ -do { \ - unsigned long flags; \ - spin_lock_irqsave(&dcr_ind_lock, flags); \ - mtdcr(DCRN_ ## base ## _CONFIG_ADDR, reg); \ - mtdcr(DCRN_ ## base ## _CONFIG_DATA, data); \ - spin_unlock_irqrestore(&dcr_ind_lock, flags); \ -} while (0) + spin_lock_irqsave(&dcr_ind_lock, flags); + __mtdcr(base_addr, reg); + val = __mfdcr(base_data); + spin_unlock_irqrestore(&dcr_ind_lock, flags); + return val; +} + +static inline void __mtdcri(int base_addr, int base_data, int reg, + unsigned val) +{ + unsigned long flags; + + spin_lock_irqsave(&dcr_ind_lock, flags); + __mtdcr(base_addr, reg); + __mtdcr(base_data, val); + spin_unlock_irqrestore(&dcr_ind_lock, flags); +} + +#define mfdcri(base, reg) __mfdcri(DCRN_ ## base ## _CONFIG_ADDR, \ + DCRN_ ## base ## _CONFIG_DATA, \ + reg) + +#define mtdcri(base, reg, data) __mtdcri(DCRN_ ## base ## _CONFIG_ADDR, \ + DCRN_ ## base ## _CONFIG_DATA, \ + reg, data) #endif /* __ASSEMBLY__ */ #endif /* __KERNEL__ */ diff --git a/include/asm-powerpc/elf.h b/include/asm-powerpc/elf.h index 6bd07ef78ac..9080d85cb9d 100644 --- a/include/asm-powerpc/elf.h +++ b/include/asm-powerpc/elf.h @@ -165,8 +165,10 @@ typedef elf_vrreg_t elf_vrregset_t32[ELF_NVRREG32]; * This is used to ensure we don't load something for the wrong architecture. */ #define elf_check_arch(x) ((x)->e_machine == ELF_ARCH) +#define compat_elf_check_arch(x) ((x)->e_machine == EM_PPC) #define USE_ELF_CORE_DUMP +#define CORE_DUMP_USE_REGSET #define ELF_EXEC_PAGESIZE PAGE_SIZE /* This is the location that an ET_DYN program is loaded if exec'ed. Typical diff --git a/include/asm-powerpc/mpc512x.h b/include/asm-powerpc/mpc512x.h new file mode 100644 index 00000000000..c48a1658eea --- /dev/null +++ b/include/asm-powerpc/mpc512x.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2007 Freescale Semiconductor, Inc. All rights reserved. + * + * Author: John Rigby, <jrigby@freescale.com>, Friday Apr 13 2007 + * + * Description: + * MPC5121 Prototypes and definitions + * + * This 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. + * + */ + +#ifndef __ASM_POWERPC_MPC512x_H__ +#define __ASM_POWERPC_MPC512x_H__ + +extern unsigned long mpc512x_find_ips_freq(struct device_node *node); + +#endif /* __ASM_POWERPC_MPC512x_H__ */ + diff --git a/include/asm-powerpc/mpc52xx_psc.h b/include/asm-powerpc/mpc52xx_psc.h index bea42b95390..710c5d36efa 100644 --- a/include/asm-powerpc/mpc52xx_psc.h +++ b/include/asm-powerpc/mpc52xx_psc.h @@ -190,5 +190,53 @@ struct mpc52xx_psc_fifo { u16 tflwfptr; /* PSC + 0x9e */ }; +#define MPC512x_PSC_FIFO_RESET_SLICE 0x80 +#define MPC512x_PSC_FIFO_ENABLE_SLICE 0x01 +#define MPC512x_PSC_FIFO_ENABLE_DMA 0x04 + +#define MPC512x_PSC_FIFO_EMPTY 0x1 +#define MPC512x_PSC_FIFO_FULL 0x2 +#define MPC512x_PSC_FIFO_ALARM 0x4 +#define MPC512x_PSC_FIFO_URERR 0x8 +#define MPC512x_PSC_FIFO_ORERR 0x01 +#define MPC512x_PSC_FIFO_MEMERROR 0x02 + +struct mpc512x_psc_fifo { + u32 reserved1[10]; + u32 txcmd; /* PSC + 0x80 */ + u32 txalarm; /* PSC + 0x84 */ + u32 txsr; /* PSC + 0x88 */ + u32 txisr; /* PSC + 0x8c */ + u32 tximr; /* PSC + 0x90 */ + u32 txcnt; /* PSC + 0x94 */ + u32 txptr; /* PSC + 0x98 */ + u32 txsz; /* PSC + 0x9c */ + u32 reserved2[7]; + union { + u8 txdata_8; + u16 txdata_16; + u32 txdata_32; + } txdata; /* PSC + 0xbc */ +#define txdata_8 txdata.txdata_8 +#define txdata_16 txdata.txdata_16 +#define txdata_32 txdata.txdata_32 + u32 rxcmd; /* PSC + 0xc0 */ + u32 rxalarm; /* PSC + 0xc4 */ + u32 rxsr; /* PSC + 0xc8 */ + u32 rxisr; /* PSC + 0xcc */ + u32 rximr; /* PSC + 0xd0 */ + u32 rxcnt; /* PSC + 0xd4 */ + u32 rxptr; /* PSC + 0xd8 */ + u32 rxsz; /* PSC + 0xdc */ + u32 reserved3[7]; + union { + u8 rxdata_8; + u16 rxdata_16; + u32 rxdata_32; + } rxdata; /* PSC + 0xfc */ +#define rxdata_8 rxdata.rxdata_8 +#define rxdata_16 rxdata.rxdata_16 +#define rxdata_32 rxdata.rxdata_32 +}; #endif /* __ASM_MPC52xx_PSC_H__ */ diff --git a/include/asm-powerpc/oprofile_impl.h b/include/asm-powerpc/oprofile_impl.h index 938fefb4c4b..95035c602ba 100644 --- a/include/asm-powerpc/oprofile_impl.h +++ b/include/asm-powerpc/oprofile_impl.h @@ -54,7 +54,7 @@ struct op_powerpc_model { int num_counters; }; -extern struct op_powerpc_model op_model_fsl_booke; +extern struct op_powerpc_model op_model_fsl_emb; extern struct op_powerpc_model op_model_rs64; extern struct op_powerpc_model op_model_power4; extern struct op_powerpc_model op_model_7450; diff --git a/include/asm-powerpc/ptrace.h b/include/asm-powerpc/ptrace.h index ffc150f602b..891d68932f3 100644 --- a/include/asm-powerpc/ptrace.h +++ b/include/asm-powerpc/ptrace.h @@ -55,6 +55,8 @@ struct pt_regs { #ifdef __powerpc64__ +#define __ARCH_WANT_COMPAT_SYS_PTRACE + #define STACK_FRAME_OVERHEAD 112 /* size of minimum stack frame */ /* Size of dummy stack frame allocated when calling signal handler. */ diff --git a/include/asm-powerpc/reg.h b/include/asm-powerpc/reg.h index 2408a29507e..0d6238987df 100644 --- a/include/asm-powerpc/reg.h +++ b/include/asm-powerpc/reg.h @@ -18,6 +18,10 @@ #include <asm/reg_booke.h> #endif /* CONFIG_BOOKE || CONFIG_40x */ +#ifdef CONFIG_FSL_EMB_PERFMON +#include <asm/reg_fsl_emb.h> +#endif + #ifdef CONFIG_8xx #include <asm/reg_8xx.h> #endif /* CONFIG_8xx */ diff --git a/include/asm-powerpc/reg_booke.h b/include/asm-powerpc/reg_booke.h index 0405ef47981..cf54a3f3175 100644 --- a/include/asm-powerpc/reg_booke.h +++ b/include/asm-powerpc/reg_booke.h @@ -9,68 +9,6 @@ #ifndef __ASM_POWERPC_REG_BOOKE_H__ #define __ASM_POWERPC_REG_BOOKE_H__ -#ifndef __ASSEMBLY__ -/* Performance Monitor Registers */ -#define mfpmr(rn) ({unsigned int rval; \ - asm volatile("mfpmr %0," __stringify(rn) \ - : "=r" (rval)); rval;}) -#define mtpmr(rn, v) asm volatile("mtpmr " __stringify(rn) ",%0" : : "r" (v)) -#endif /* __ASSEMBLY__ */ - -/* Freescale Book E Performance Monitor APU Registers */ -#define PMRN_PMC0 0x010 /* Performance Monitor Counter 0 */ -#define PMRN_PMC1 0x011 /* Performance Monitor Counter 1 */ -#define PMRN_PMC2 0x012 /* Performance Monitor Counter 1 */ -#define PMRN_PMC3 0x013 /* Performance Monitor Counter 1 */ -#define PMRN_PMLCA0 0x090 /* PM Local Control A0 */ -#define PMRN_PMLCA1 0x091 /* PM Local Control A1 */ -#define PMRN_PMLCA2 0x092 /* PM Local Control A2 */ -#define PMRN_PMLCA3 0x093 /* PM Local Control A3 */ - -#define PMLCA_FC 0x80000000 /* Freeze Counter */ -#define PMLCA_FCS 0x40000000 /* Freeze in Supervisor */ -#define PMLCA_FCU 0x20000000 /* Freeze in User */ -#define PMLCA_FCM1 0x10000000 /* Freeze when PMM==1 */ -#define PMLCA_FCM0 0x08000000 /* Freeze when PMM==0 */ -#define PMLCA_CE 0x04000000 /* Condition Enable */ - -#define PMLCA_EVENT_MASK 0x007f0000 /* Event field */ -#define PMLCA_EVENT_SHIFT 16 - -#define PMRN_PMLCB0 0x110 /* PM Local Control B0 */ -#define PMRN_PMLCB1 0x111 /* PM Local Control B1 */ -#define PMRN_PMLCB2 0x112 /* PM Local Control B2 */ -#define PMRN_PMLCB3 0x113 /* PM Local Control B3 */ - -#define PMLCB_THRESHMUL_MASK 0x0700 /* Threshhold Multiple Field */ -#define PMLCB_THRESHMUL_SHIFT 8 - -#define PMLCB_THRESHOLD_MASK 0x003f /* Threshold Field */ -#define PMLCB_THRESHOLD_SHIFT 0 - -#define PMRN_PMGC0 0x190 /* PM Global Control 0 */ - -#define PMGC0_FAC 0x80000000 /* Freeze all Counters */ -#define PMGC0_PMIE 0x40000000 /* Interrupt Enable */ -#define PMGC0_FCECE 0x20000000 /* Freeze countes on - Enabled Condition or - Event */ - -#define PMRN_UPMC0 0x000 /* User Performance Monitor Counter 0 */ -#define PMRN_UPMC1 0x001 /* User Performance Monitor Counter 1 */ -#define PMRN_UPMC2 0x002 /* User Performance Monitor Counter 1 */ -#define PMRN_UPMC3 0x003 /* User Performance Monitor Counter 1 */ -#define PMRN_UPMLCA0 0x080 /* User PM Local Control A0 */ -#define PMRN_UPMLCA1 0x081 /* User PM Local Control A1 */ -#define PMRN_UPMLCA2 0x082 /* User PM Local Control A2 */ -#define PMRN_UPMLCA3 0x083 /* User PM Local Control A3 */ -#define PMRN_UPMLCB0 0x100 /* User PM Local Control B0 */ -#define PMRN_UPMLCB1 0x101 /* User PM Local Control B1 */ -#define PMRN_UPMLCB2 0x102 /* User PM Local Control B2 */ -#define PMRN_UPMLCB3 0x103 /* User PM Local Control B3 */ -#define PMRN_UPMGC0 0x180 /* User PM Global Control 0 */ - - /* Machine State Register (MSR) Fields */ #define MSR_UCLE (1<<26) /* User-mode cache lock enable */ #define MSR_SPE (1<<25) /* Enable SPE */ diff --git a/include/asm-powerpc/reg_fsl_emb.h b/include/asm-powerpc/reg_fsl_emb.h new file mode 100644 index 00000000000..1e180a59458 --- /dev/null +++ b/include/asm-powerpc/reg_fsl_emb.h @@ -0,0 +1,72 @@ +/* + * Contains register definitions for the Freescale Embedded Performance + * Monitor. + */ +#ifdef __KERNEL__ +#ifndef __ASM_POWERPC_REG_FSL_EMB_H__ +#define __ASM_POWERPC_REG_FSL_EMB_H__ + +#ifndef __ASSEMBLY__ +/* Performance Monitor Registers */ +#define mfpmr(rn) ({unsigned int rval; \ + asm volatile("mfpmr %0," __stringify(rn) \ + : "=r" (rval)); rval;}) +#define mtpmr(rn, v) asm volatile("mtpmr " __stringify(rn) ",%0" : : "r" (v)) +#endif /* __ASSEMBLY__ */ + +/* Freescale Book E Performance Monitor APU Registers */ +#define PMRN_PMC0 0x010 /* Performance Monitor Counter 0 */ +#define PMRN_PMC1 0x011 /* Performance Monitor Counter 1 */ +#define PMRN_PMC2 0x012 /* Performance Monitor Counter 1 */ +#define PMRN_PMC3 0x013 /* Performance Monitor Counter 1 */ +#define PMRN_PMLCA0 0x090 /* PM Local Control A0 */ +#define PMRN_PMLCA1 0x091 /* PM Local Control A1 */ +#define PMRN_PMLCA2 0x092 /* PM Local Control A2 */ +#define PMRN_PMLCA3 0x093 /* PM Local Control A3 */ + +#define PMLCA_FC 0x80000000 /* Freeze Counter */ +#define PMLCA_FCS 0x40000000 /* Freeze in Supervisor */ +#define PMLCA_FCU 0x20000000 /* Freeze in User */ +#define PMLCA_FCM1 0x10000000 /* Freeze when PMM==1 */ +#define PMLCA_FCM0 0x08000000 /* Freeze when PMM==0 */ +#define PMLCA_CE 0x04000000 /* Condition Enable */ + +#define PMLCA_EVENT_MASK 0x007f0000 /* Event field */ +#define PMLCA_EVENT_SHIFT 16 + +#define PMRN_PMLCB0 0x110 /* PM Local Control B0 */ +#define PMRN_PMLCB1 0x111 /* PM Local Control B1 */ +#define PMRN_PMLCB2 0x112 /* PM Local Control B2 */ +#define PMRN_PMLCB3 0x113 /* PM Local Control B3 */ + +#define PMLCB_THRESHMUL_MASK 0x0700 /* Threshhold Multiple Field */ +#define PMLCB_THRESHMUL_SHIFT 8 + +#define PMLCB_THRESHOLD_MASK 0x003f /* Threshold Field */ +#define PMLCB_THRESHOLD_SHIFT 0 + +#define PMRN_PMGC0 0x190 /* PM Global Control 0 */ + +#define PMGC0_FAC 0x80000000 /* Freeze all Counters */ +#define PMGC0_PMIE 0x40000000 /* Interrupt Enable */ +#define PMGC0_FCECE 0x20000000 /* Freeze countes on + Enabled Condition or + Event */ + +#define PMRN_UPMC0 0x000 /* User Performance Monitor Counter 0 */ +#define PMRN_UPMC1 0x001 /* User Performance Monitor Counter 1 */ +#define PMRN_UPMC2 0x002 /* User Performance Monitor Counter 1 */ +#define PMRN_UPMC3 0x003 /* User Performance Monitor Counter 1 */ +#define PMRN_UPMLCA0 0x080 /* User PM Local Control A0 */ +#define PMRN_UPMLCA1 0x081 /* User PM Local Control A1 */ +#define PMRN_UPMLCA2 0x082 /* User PM Local Control A2 */ +#define PMRN_UPMLCA3 0x083 /* User PM Local Control A3 */ +#define PMRN_UPMLCB0 0x100 /* User PM Local Control B0 */ +#define PMRN_UPMLCB1 0x101 /* User PM Local Control B1 */ +#define PMRN_UPMLCB2 0x102 /* User PM Local Control B2 */ +#define PMRN_UPMLCB3 0x103 /* User PM Local Control B3 */ +#define PMRN_UPMGC0 0x180 /* User PM Global Control 0 */ + + +#endif /* __ASM_POWERPC_REG_FSL_EMB_H__ */ +#endif /* __KERNEL__ */ diff --git a/include/asm-powerpc/system.h b/include/asm-powerpc/system.h index 2a65ae63720..29552ff182a 100644 --- a/include/asm-powerpc/system.h +++ b/include/asm-powerpc/system.h @@ -65,7 +65,7 @@ struct task_struct; struct pt_regs; -#ifdef CONFIG_DEBUGGER +#if defined(CONFIG_DEBUGGER) || defined(CONFIG_KEXEC) extern int (*__debugger)(struct pt_regs *regs); extern int (*__debugger_ipi)(struct pt_regs *regs); diff --git a/include/asm-powerpc/vio.h b/include/asm-powerpc/vio.h index 9204c15839c..56512a968da 100644 --- a/include/asm-powerpc/vio.h +++ b/include/asm-powerpc/vio.h @@ -66,7 +66,7 @@ extern void __devinit vio_unregister_device(struct vio_dev *dev); struct device_node; -extern struct vio_dev * __devinit vio_register_device_node( +extern struct vio_dev *vio_register_device_node( struct device_node *node_vdev); extern const void *vio_get_attribute(struct vio_dev *vdev, char *which, int *length); diff --git a/include/linux/elf.h b/include/linux/elf.h index ed98c761bae..bad1b16ec49 100644 --- a/include/linux/elf.h +++ b/include/linux/elf.h @@ -357,6 +357,7 @@ typedef struct elf64_shdr { #define NT_AUXV 6 #define NT_PRXFPREG 0x46e62b7f /* copied from gdb5.1/include/elf/common.h */ #define NT_PPC_VMX 0x100 /* PowerPC Altivec/VMX registers */ +#define NT_PPC_SPE 0x101 /* PowerPC SPE/EVR registers */ #define NT_386_TLS 0x200 /* i386 TLS slots (struct user_desc) */ diff --git a/include/linux/of.h b/include/linux/of.h index b5f33efcb8e..6981016dcc2 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -50,6 +50,7 @@ extern struct device_node *of_find_matching_node(struct device_node *from, 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_get_parent(const struct device_node *node); +extern struct device_node *of_get_next_parent(struct device_node *node); extern struct device_node *of_get_next_child(const struct device_node *node, struct device_node *prev); #define for_each_child_of_node(parent, child) \ |