GIT 5a2516156c591fc3d2059fbd93f97e15eb6010d6 master.kernel.org:/pub/scm/linux/kernel/git/paulus/powerpc.git commit 5a2516156c591fc3d2059fbd93f97e15eb6010d6 Author: Paul Mackerras Date: Tue Nov 29 17:17:02 2005 +1100 powerpc: Fix up some compile errors in the PCI error recovery code is gone now, and the PCI error recovery constants in include/linux/pci.h changed their names in the process of getting accepted. Signed-off-by: Paul Mackerras commit be3b5d1be053ccb41e91fa5a6f43ef5db301357d Author: Linas Vepstas Date: Thu Nov 3 18:55:25 2005 -0600 [PATCH] powerpc: get rid of per_cpu EEH counters 242-eeh-no-percpu-counters.patch Remove per-cpu counters from the EEH code. These statistics counters are incremented at a very low frequency, and the performance gains of per-cpu variables are negligable. By contrast, the counters weren't safe against cpu off/online operations, and its not worth the effort to make them so (other than to turn them into plain globals). Signed-off-by: Linas Vepstas Signed-off-by: Paul Mackerras commit 76c902b919098860f3d4e125f847abcc4cb1782a Author: Linas Vepstas Date: Thu Nov 3 18:55:19 2005 -0600 [PATCH] powerpc: Save device BARs much earlier in the boot sequence 241-eeh-save-bars-earlier.patch Save the PCI device bars *before* any PCI probing is done. Signed-off-by: Linas Vepstas Signed-off-by: Paul Mackerras commit e71d9e598533c1889e7162f5f4647e5d378c102c Author: Linas Vepstas Date: Thu Nov 3 18:55:14 2005 -0600 [PATCH] powerpc: IOMMU: don't ioremap null addresses 240-ioremap-null-ptr-test.patch Under highly unusual circumstances, a buggy driver will ask a null ptr to be ioremapped, an operation that curently succeeds but leads to later trouble. Instead, refuse to remap the null pointer. Signed-off-by: Linas Vepstas Signed-off-by: Paul Mackerras commit 216810296bb97d39da8e176822e9de78d2f00187 Author: Linas Vepstas Date: Thu Nov 3 18:55:01 2005 -0600 [PATCH] powerpc: handle multifunction PCI devices properly 239-eeh-multifunction-consolidate.patch New-style firmware will often place multiple different functions under a non-EEH-aware parent. However, these devices might share a common PE "partition endpoint" and config address, ad thus any EEH events will affect all of the devices in common. This patch makes the effort to find all of these common devices and handle them together. Signed-off-by: Linas Vepstas Signed-off-by: Paul Mackerras commit e06b942521eb2cdaf232726f45a820d5837acb12 Author: Linas Vepstas Date: Thu Nov 3 18:54:54 2005 -0600 [PATCH] powerpc: Don't continue with PCI Error recovery if slot reset failed. 238-eeh-stop-if-reset_failed.patch If the firmware is unable to reset the PCI slot for some reason, then don't attempt any further recovery steps after that point. Instead, mark the device as permanently failed. Signed-off-by: Linas Vepstas Signed-off-by: Paul Mackerras commit 78379b6c5fc17b6666c40b05988e6708e98479c0 Author: Linas Vepstas Date: Thu Nov 3 18:54:47 2005 -0600 [PATCH] powerpc: set up the RTAS token just like the rest of them. 237-eeh-bridge-token.patch Minor: the rtas-bridge token should be set up the same way that all the other rtas tokens are set up. Signed-off-by: Linas Vepstas Signed-off-by: Paul Mackerras commit c2bc904a28095aca0b04a37854b63b78622a032e Author: Linas Vepstas Date: Thu Nov 3 18:54:39 2005 -0600 [PATCH] powerpc: Use PE configuration address consistently 236-eeh-config-addr.patch The PE configuration address wasn't being cnsistently used in all locations where a config address is called for. This patch adds it to the places it should have appeared in. Signed-off-by: Linas Vepstas Signed-off-by: Paul Mackerras commit beb45c93d494a11c36e5b24f638e610db8428b54 Author: Linas Vepstas Date: Thu Nov 3 18:54:34 2005 -0600 [PATCH] powerpc: bugfix: fill in uninitialized field 235-eeh-set-pcidev-bugfix.patch The pci device field of the pci_dn struct should be initialized to a valid value. Signed-off-by: Linas Vepstas Signed-off-by: Paul Mackerras commit 48408e708282d4d0269136ff27ea5acbd9410b5a Author: Linas Vepstas Date: Thu Nov 3 18:54:29 2005 -0600 [PATCH] powerpc: Remove duplicate code 234-eeh-find-pe.patch The find_device_pe() routine is duplicated in two files. Remove one of the two copies, declare the other extern. Signed-off-by: Linas Vepstas Signed-off-by: Paul Mackerras commit 241239e6aff69788a177d97c5d06fe9995c74cca Author: Linas Vepstas Date: Thu Nov 3 18:54:23 2005 -0600 [PATCH] powerpc: remove bogus printk 233-eeh-buid-fix.patch Remove un-desired warning print from EEH code. Signed-off-by: Linas Vepstas Signed-off-by: Paul Mackerras commit 9fcfb5d35b5294659f9299aa9cae6fd16325c07e Author: Linas Vepstas Date: Thu Nov 3 18:53:20 2005 -0600 [PATCH] powerpc: Add "partitionable endpoint" support 26-eeh-partition-endpoint.patch New versions of firmware introduce a new method by which the "partitionable endpoint" (the point at which the pci bus is cut) should be located. This code adds the support for this (mandatory) new feature. Signed-off-by: Linas Vepstas Signed-off-by: Paul Mackerras commit b0b291d59906d4a9a89ed9e34d9fd684c7188924 Author: Linas Vepstas Date: Thu Nov 3 18:53:07 2005 -0600 [PATCH] powerpc: Split out PCI address cache to its own file 25-pci-address-cache.patch The core EEH file is rather large. This patch splits out a self-contained chunk of it into its own file. This is the chunk that performes the caching and lookup of pci devices based on the i/o addresses of thier resoures. This code is almos architecture-independent and could be used by any system that wanted to find a pci device based only on the i/o address used by the device. Signed-off-by: Linas Vepstas Signed-off-by: Paul Mackerras commit e8ca11b460c4c9c7fa6b529be221529ebd770e38 Author: Linas Vepstas Date: Thu Nov 3 18:52:49 2005 -0600 [PATCH] powerpc: PCI Error Recovery: PPC64 core recovery routines Various PCI bus errors can be signaled by newer PCI controllers. The core error recovery routines are architecture dependent. This patch adds a recovery infrastructure for the PPC64 pSeries systems. Signed-off-by: Linas Vepstas Signed-off-by: Paul Mackerras commit b0a257338f00675a3f1f40747d961cf67cec608a Author: Linas Vepstas Date: Thu Nov 17 15:43:31 2005 -0800 [PATCH] PCI Error Recovery: header file patch Various PCI bus errors can be signaled by newer PCI controllers. Recovering from those errors requires an infrastructure to notify affected device drivers of the error, and a way of walking through a reset sequence. This patch adds a set of callbacks to be used by error recovery routines to notify device drivers of the various stages of recovery. Signed-off-by: Linas Vepstas Signed-off-by: Greg Kroah-Hartman commit 01f378afa1b9c9b279e3b8cbb76d26bcedc2b7df Author: Linas Vepstas Date: Thu Nov 3 18:52:16 2005 -0600 [PATCH] powerpc: migrate common PCI hotplug code 23-rpaphp-migrate.patch (parts) This patch moves some pci device add & remove code from the PCI hotplug directory to the arch/powerpc/kernel directory, and cleans it up a tad. The primary reason for this is that the code performs some fairly generic operations that are shared with the PCI error recovery code (living in the arch/powerpc/kernel directory). Signed-off-by: Linas Vepstas Signed-off-by: Paul Mackerras commit be313ef622cff6d80a4315a6908dc9b54d0a4aa7 Author: Linas Vepstas Date: Thu Nov 3 18:52:01 2005 -0600 [PATCH] powerpc: make pcibios_claim_one_bus available to other code 22-rpaphp-eliminate-dupe-code.patch (parts) The RPAPHP code contains two routines that appear to be gratuitous copies of very similar pci code. In particular, rpaphp_claim_resource ~~ pci_claim_resource rpadlpar_claim_one_bus == pcibios_claim_one_bus This makes pcibios_claim_one_bus from arch/powerpc/kernel/pci_64.c available to the RPAPHP code. Signed-off-by: Linas Vepstas Signed-off-by: Paul Mackerras commit 03e0a535c65f90badd82d0bc5e662deddf75aba5 Author: Linas Vepstas Date: Thu Nov 3 18:51:31 2005 -0600 [PATCH] powerpc: PCI hotplug common code elimination 20-rpaphp-eeh-cleanup.patch This patch move some code from the rpaphp directory, to the powerpc directory, where it should have been all along (Among other things, I need it in the powerpc directory for the PCI error recovery.) Please note that patch affects TWO maintainers: Paul, after applying the powerpc part, please ask that GregKH appli the PCI part. It is safe to have the powerpc part go in first. It would be bad to have the PCI part go in first. Signed-off-by: Linas Vepstas Signed-off-by: Paul Mackerras commit f1fdc0117004d343698b9830e141491d5ae320d1 Author: Mike Kravetz Date: Mon Nov 28 16:33:24 2005 -0800 [PATCH] powerpc: Minor numa memory code cleanup I started to add missing of_node_put() calls to the routines that determine the number of cells for memory. Decided to combine the routines instead of making separate node lookups. Changed variable names to help with some confusion as to meaning. Signed-off-by: Mike Kravetz Signed-off-by: Paul Mackerras commit 4dee0280dcdef2bd6543bb21b3079034b5d939fb Author: Benjamin Herrenschmidt Date: Tue Nov 29 11:21:59 2005 +1100 [PATCH] powerpc: More serial probe fixes (#2) This fixes the new serial probe code with some PCI MMIO UARTs, and fixes CHRP build with ARCH=powerpc. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Paul Mackerras commit bac9212ec38c8bde609d4fe83f19c8d4fe3ab577 Author: David Woodhouse Date: Sat Nov 26 14:44:47 2005 +0000 [PATCH] ppc: Make ARCH=ppc build again with new syscall path This makes ARCH=ppc build in your powerpc tree again, with the new syscall entry/exit path. Still doesn't actually boot on my Pegasos; the last thing I see is 'MMU:exit'. But at least it builds -- I'll look at why it doesn't boot later, so that I can see if the mv643xx_eth actually works with ARCH=ppc (it doesn't with ARCH=powerpc; two in every three packets I receive are offset by 4 bytes). Signed-off-by: David Woodhouse Signed-off-by: Paul Mackerras commit c054e0c07fb8799a34af4c79d2614ccb65e8adfc Author: Marcelo Tosatti Date: Thu Nov 24 11:32:09 2005 -0200 [PATCH] ppc32: m8xx watchdog update This updates m8xx_wdt as follows: 1) Remove now obsolete fpos check in the write() function. The driver is currently non functional due to this bug. 2) Use in/out macros for register access. 3) Allows m8xx_wdt to use a kernel timer instead of the builtin RTC/PIT for keep-alive trigger (which is responsible for servicing the watchdog until an userspace application takes over). For instance Cyclades PRxK boards (MPC 855T based) have a non-functional internal RTC/PIT unit. Behaviour for boards with RTC/PIT is unchaged. 4) The last change required moving the RTCSC register setting code to a weak function which can be overriden by board specific files. Otherwise the timer init code trashes the register making it impossible for m8xx_wdt to detect the situation. Signed-off-by: Marcelo Tosatti Signed-off-by: Paul Mackerras commit 81c22fa8b867aa152541da6d52589f361fdaff60 Author: David Woodhouse Date: Thu Nov 24 17:36:20 2005 +0000 [PATCH] Fix code that saves NVGPRS in 32-bit signal frame On Thu, 2005-11-24 at 12:51 +0000, David Woodhouse wrote: > Somehow this one slipped through the cracks; when we ended up in > do_signal() on a 32-bit kernel but without having the caller-saved > registers into the regs, we didn't set the TIF_SAVE_NVGPRS flag to > ensure they got saved later. Oh, and if we actually set the flag, then we fairly quickly find out that I was a bit overzealous in copying code from entry_64.S ... :) Signed-off-by: David Woodhouse Signed-off-by: Paul Mackerras commit 578b5221afb34102e8228729c28766f450c3ea48 Author: David Woodhouse Date: Thu Nov 24 12:51:40 2005 +0000 [PATCH] Save NVGPRS in 32-bit signal frame Somehow this one slipped through the cracks; when we ended up in do_signal() on a 32-bit kernel but without having the caller-saved registers into the regs, we didn't set the TIF_SAVE_NVGPRS flag to ensure they got saved later. Signed-off-by: David Woodhouse Signed-off-by: Paul Mackerras commit ffc8148590502363f16c0ec52bf52f86eb034b9f Author: David Woodhouse Date: Thu Nov 24 17:08:56 2005 +0000 [PATCH] powerpc: serial port discovery: cope with broken firmware On Tue, 2005-11-22 at 15:49 +1100, Benjamin Herrenschmidt wrote: > This moves the discovery of legacy serial ports to a separate file, > makes it common to ppc32 and ppc64, and reworks it to use the new OF > address translators to get to the ports early. This new version can also > detect some PCI serial cards using legacy chips and will probably match > those discovered port with the default console choice. This makes it deal with the fact that the Pegasos firmware reports that its clock frequency is zero... Signed-off-by: David Woodhouse Signed-off-by: Paul Mackerras commit b425c2e1b407543702626c982eb03b0b0a9668c6 Author: Benjamin Herrenschmidt Date: Thu Nov 24 17:34:03 2005 +1100 [PATCH] powerpc: Fix g5 build with xmon My previous patches inadvertently broke building a G5 kernel with CONFIG_XMON enabled. This fixes it. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Paul Mackerras commit c70b6ade68bcb8be55977bb856b0a04a7e74d16f Author: David Gibson Date: Thu Nov 24 16:51:31 2005 +1100 [PATCH] powerpc: Remove some unneeded fields from the paca This patch removes several unnecessary fields from the paca: - next_jiffy_update_tb was simply unused. Remove trivially. - The exdsi exception save area was not used. There were plans to use it, but they never seem to have gone anywhere. If they ever do, we can put it back. Remove from the paca, and from asm-offsets.c - The default_decr field was used from asm, but was only ever assigned the value of tb_ticks_per_jiffy. Just access tb_ticks_per_jiffy from asm directly instead. Built and booted on POWER5 LPAR and iSeries RS64. Signed-off-by: David Gibson Signed-off-by: Paul Mackerras commit 88981bea2ace2dd23b95672365c20a74d58eff3a Author: David Gibson Date: Thu Nov 24 16:34:45 2005 +1100 [PATCH] powerpc: Remove ItLpRegSave area from the paca On iSeries, the paca contains, amongst other things an ItLpRegSave structure used by the hypervisor to save registers. The hypervisor locates this area through a pointer at the beginning of the paca, so the structure itself can be located elsewhere. This patch moves the reg_save area out into its own array. This reduces the amount of iSeries specific gunk which is visible to general powerpc code via paca.h Built and booted on POWER5 LPAR and iSeries RS64. Signed-off-by: David Gibson Signed-off-by: Paul Mackerras commit 71644d4a14869077adf7f5f8818d96649d1bad70 Author: David Gibson Date: Thu Nov 24 15:30:37 2005 +1100 [PATCH] is_aligned_hugepage_range() cleanup Quite a long time back, prepare_hugepage_range() replaced is_aligned_hugepage_range() as the callback from mm/mmap.c to arch code to verify is an address range is suitable for a hugepage mapping. is_aligned_hugepage_range() stuck around, but only to implement prepare_hugepage_range() on archs which didn't implement their own. Most archs (everything except ia64 and powerpc) used the same implementation of is_aligned_hugepage_range(). On powerpc, which implements its own prepare_hugepage_range(), the custom version was never used. This patch cleans up the situation by abolishing is_aligned_hugepage_range(). Instead prepare_hugepage_range() is defined directly. Most archs use the default version, which simple checks the given region is aligned to the size of a hugepage. ia64 and powerpc define custom versions. The ia64 one simply checks that the range is in the correct address space region in addition to being suitably aligned. The powerpc version (just as previously) checks for suitable addresses, and if necessary performs low-level MMU frobbing to set up new areas for use by hugepages. Built and tested on POWER5 LPAR (powerpc), G5 (powerpc) and Pentium M (i386). Signed-off-by: David Gibson Signed-off-by: Paul Mackerras commit 1b60b5c01c2165df84c11cc3559531b50d4109ea Author: David Gibson Date: Thu Nov 24 14:16:15 2005 +1100 [PATCH] powerpc: Make hugepage mappings respect hint addresses Currently, the powerpc version of hugetlb_get_unmapped_area() entirely ignores the hint address. The only way to get a hugepage mapping at a specified address is with MAP_FIXED, in which case there's no way (short of parsing /proc/self/maps) for userspace to tell if it will clobber an existing mapping. This is inconvenient, so the patch below makes hugepage mappings use the given hint address if possible. Signed-off-by: David Gibson Signed-off-by: Paul Mackerras commit af85c856fbb8f7bd0d66077cb058c295a67f29c8 Author: Kumar Gala Date: Wed Nov 23 13:03:37 2005 -0600 [PATCH] powerpc: Fix suboptimal uImage target Sam Ravnborg pointed out that calling if_changed was redundant in the rule since a prerequisite had to have changed for us to get there. Signed-off-by: Kumar Gala Signed-off-by: Paul Mackerras commit 7cc96c516ef99ca90bd5a8ad17b915432fff3a8b Author: Kumar Gala Date: Wed Nov 23 12:43:15 2005 -0600 [PATCH] powerpc: Add support for building uImages powerpc: Add support for building uImages Add support to build a kernel image bootable by u-boot. Most of the makefile foo is taken from arch/ppc/boot/images/Makefile Signed-off-by: Kumar Gala Signed-off-by: Paul Mackerras commit 4695288d8079ba04c1b458f58c501efdf67aa8b2 Author: Stephen Rothwell Date: Thu Nov 17 18:04:37 2005 +1100 powerpc: more iseries irq work Make get_IRQ return a pending irq number so it can be handled in the powerpc geeneric code. Signed-off-by: Stephen Rothwell commit 195698c4f3904e416276208d465eb7c1e813dc3b Author: Stephen Rothwell Date: Thu Nov 17 16:14:17 2005 +1100 powerpc: reduce include in irq.c Signed-off-by: Stephen Rothwell commit 5fee9b3b39eb55c7e3619a3b36ceeabffeb8f144 Author: Stephen Rothwell Date: Wed Nov 16 18:53:29 2005 +1100 powerpc: partly merge iseries do_IRQ Hide some of the iseries details in iSeries_get_irq. Signed-off-by: Stephen Rothwell commit a5283c3567a3585448c84767cb9a8f540878f199 Author: Stephen Rothwell Date: Wed Nov 16 18:10:40 2005 +1100 powerpc: use end_IRQ for iseries irqs Encode the sub bus number into the real irq number (even though it is always zero for now) so that we have enough information to do the EOI in iseries_end_IRQ. Signed-off-by: Stephen Rothwell commit e8ee382c4cba7df33d6a9ea3966ea3fb61f1bc47 Author: Stephen Rothwell Date: Wed Nov 16 17:47:43 2005 +1100 powerpc: cleanup iseries irq.c Remove two useless counters. DeStropify. Signed-off-by: Stephen Rothwell commit c44811390fea79ed06e658c5214e46db104ec54f Author: Benjamin Herrenschmidt Date: Wed Nov 23 17:59:04 2005 +1100 [PATCH] powerpc: convert macio_asic to use prom_parse Converts the macio_asic core to use the new OF parsing routines instead of relying on the pre-parsed values in struct device_node. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Paul Mackerras commit da4a0249d45c71be6f97c2eeba54d7d138044613 Author: Benjamin Herrenschmidt Date: Wed Nov 23 17:58:13 2005 +1100 [PATCH] powerpc: Add back support for booting from BootX (#2) ARCH=powerpc couldn't boot from BootX as it uses a "different" way of getting in the kernel. This patch adds the necessary trampolines, creating a flattened device-tree from the tree passed from MacOS, and initializing the btext engine early for really-early debugging. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Paul Mackerras commit d537e7ac037556e9834d231a63f8fe8ef98e50a1 Author: Benjamin Herrenschmidt Date: Wed Nov 23 17:57:25 2005 +1100 [PATCH] powerpc: Unify udbg (#2) This patch unifies udbg for both ppc32 and ppc64 when building the merged achitecture. xmon now has a single "back end". The powermac udbg stuff gets enriched with some ADB capabilities and btext output. In addition, the early_init callback is now called on ppc32 as well, approx. in the same order as ppc64 regarding device-tree manipulations. The init sequences of ppc32 and ppc64 are getting closer, I'll unify them in a later patch. For now, you can force udbg to the scc using "sccdbg" or to btext using "btextdbg" on powermacs. I'll implement a cleaner way of forcing udbg output to something else than the autodetected OF output device in a later patch. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Paul Mackerras commit 4ba22ae452b704dd4d616c6b12e311d2869c2a5f Author: Benjamin Herrenschmidt Date: Wed Nov 23 17:56:06 2005 +1100 [PATCH] powerpc: serial port discovery (#2) This moves the discovery of legacy serial ports to a separate file, makes it common to ppc32 and ppc64, and reworks it to use the new OF address translators to get to the ports early. This new version can also detect some PCI serial cards using legacy chips and will probably match those discovered port with the default console choice. Only ppc64 gets udbg still yet, unifying udbg isn't finished yet. It also adds some speed-probing code to udbg so that the default console can come up at the same speed it was set to by the firmware. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Paul Mackerras commit 71048b4cfce5fea0944a5a7848bdf9a5c747b947 Author: Benjamin Herrenschmidt Date: Wed Nov 23 17:53:42 2005 +1100 [PATCH] powerpc: Add OF address parsing code (#2) Parsing addresses extracted from Open Firmware isn't a simple matter. We have various bits of code that try to do it in various place, including some heuristics in prom.c that pre-parse addresses at boot and fill device-nodes "addrs", but those are dodgy at best and I want to deprecate them. So this patch introduces a new set of routines that should be capable of parsing most types of addresses and translating them into CPU physical addresses. It currently works for things on PCI busses and ISA busses and should work on "standard" busses like the root bus or the MacIO bus that don't put funky flags in addresses. If you have other bus types that do use funky flags, you'll have to add new bus type translators, which is fairly easy. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Paul Mackerras commit 57d99f1a4bb09261ec24c454df9e33dbfdca00d7 Author: Stephen Rothwell Date: Tue Nov 22 12:05:26 2005 +1100 [PATCH] powerpc: remove arch/powerpc/include hack for 64 bit With the removal of include/asm-powerpc, we no longer need arch/powerpc/include/asm for the 64 bit build. We also do not need -Iarch/powerpc for the 64 bit build either. Signed-off-by: Stephen Rothwell Signed-off-by: Paul Mackerras commit 4923a889c5027da49496f51a7ffad84862bb5d11 Author: Hugh Dickins Date: Mon Nov 21 20:36:29 2005 +0000 [PATCH] mm: powerpc init_mm without ptlock Restore an earlier mod which went missing in the powerpc reshuffle: the 4xx mmu_mapin_ram does not need to take init_mm.page_table_lock. Signed-off-by: Hugh Dickins Signed-off-by: Paul Mackerras commit ebc0d3850117c847d82c78b48bb87b57fe0d05ea Author: Hugh Dickins Date: Mon Nov 21 20:35:01 2005 +0000 [PATCH] mm: powerpc ptlock comments Update comments (only) on page_table_lock and mmap_sem in arch/powerpc. Removed the comment on page_table_lock from hash_huge_page: since it's no longer taking page_table_lock itself, it's irrelevant whether others are; but how it is safe (even against huge file truncation?) I can't say. Signed-off-by: Hugh Dickins Signed-off-by: Paul Mackerras commit 325e2997a1ceb6d2ca00e79b724c7c598ddd43a9 Author: David Woodhouse Date: Fri Nov 18 12:15:33 2005 +0000 [PATCH] ppc64 syscall_exit_work: call the save_nvgprs function, not its descriptor. On Tue, 2005-11-15 at 18:52 +0000, David Woodhouse wrote: > This cleanup patch speeds up the null syscall path on ppc64 by about 3%, > and brings the ppc32 and ppc64 code slightly closer together. Needs this unless your binutils, like mine, are clever enough to notice my stupidity and fix it up automatically... Spotted by Paul. Signed-off-by: David Woodhouse Signed-off-by: Paul Mackerras commit bacad10e92602ff6bedf1146e02a6b1c453d931d Author: Paul Mackerras Date: Fri Nov 18 22:30:06 2005 +1100 ppc: remove duplicate bseip.h include/asm-ppc/bseip.h is a duplicate of arch/ppc/platforms/bseip.h and is not referenced anywhere, so get rid of it. Pointed out by Marcelo Tosatti. Signed-off-by: Paul Mackerras commit 52d3765dc9c767324ba49cb0a99b98a4a38cfcb0 Author: Paul Mackerras Date: Fri Nov 18 13:16:43 2005 +1100 powerpc: Update __NR_syscalls to account for SPU syscalls A previous patch ended up not increasing __NR_syscalls to account for the new SPU syscalls (probably my fault). Signed-off-by: Paul Mackerras commit a92f0011ae42f3de8dfb243c6596dbba5cd23872 Author: Arnd Bergmann Date: Tue Nov 15 15:53:52 2005 -0500 [PATCH] spufs: cooperative scheduler support This adds a scheduler for SPUs to make it possible to use more logical SPUs than physical ones are present in the system. Currently, there is no support for preempting a running SPU thread, they have to leave the SPU by either triggering an event on the SPU that causes it to return to the owning thread or by sending a signal to it. This patch also adds operations that enable accessing an SPU in either runnable or saved state. We use an RW semaphore to protect the state of the SPU from changing underneath us, while we are holding it readable. In order to change the state, it is acquired writeable and a context save or restore is executed before downgrading the semaphore to read-only. From: Mark Nutter , Uli Weigand Signed-off-by: Arnd Bergmann Signed-off-by: Paul Mackerras commit 7c365e9f9373e726529669d841a4ef1b3b27a6c4 Author: Mark Nutter Date: Tue Nov 15 15:53:51 2005 -0500 [PATCH] spufs: add spu-side context switch code Add the source code that is used to generate spu_save_dump.h and spu_restore_dump.h. Since a full spu tool chain is needed to generate these files, the default remains to use the shipped versions in order to keep the number of tools for building the kernel down. Signed-off-by: Arnd Bergmann Signed-off-by: Paul Mackerras commit 22fd54ab437501bae493917df31f13c758d5b451 Author: Mark Nutter Date: Tue Nov 15 15:53:50 2005 -0500 [PATCH] kernel-side context switch code for spufs This adds the code needed to perform a context switch from spufs, following the recommended 76-step sequence. Signed-off-by: Arnd Bergmann Signed-off-by: Paul Mackerras commit c3ba9498efff9714be19df67e331ef84145887a3 Author: Mark Nutter Date: Tue Nov 15 15:53:49 2005 -0500 [PATCH] spufs: switchable spu contexts Add some infrastructure for saving and restoring the context of an SPE. This patch creates a new structure that can hold the whole state of a physical SPE in memory. It also contains code that avoids races during the context switch and the binary code that is loaded to the SPU in order to access its registers. The actual PPE- and SPE-side context switch code are two separate patches. Signed-off-by: Arnd Bergmann Signed-off-by: Paul Mackerras commit 93572b44d2446aba85fecfaf9cd0ccd794861347 Author: Arnd Bergmann Date: Tue Nov 15 15:53:48 2005 -0500 [PATCH] spufs: The SPU file system, base This is the current version of the spu file system, used for driving SPEs on the Cell Broadband Engine. This release is almost identical to the version for the 2.6.14 kernel posted earlier, which is available as part of the Cell BE Linux distribution from http://www.bsc.es/projects/deepcomputing/linuxoncell/. The first patch provides all the interfaces for running spu application, but does not have any support for debugging SPU tasks or for scheduling. Both these functionalities are added in the subsequent patches. See Documentation/filesystems/spufs.txt on how to use spufs. Signed-off-by: Arnd Bergmann Signed-off-by: Paul Mackerras commit 2bc5b321d6fbe1a3292b04c160c559578a0ca057 Author: Heiko J Schick Date: Wed Nov 16 08:56:43 2005 +0100 [PATCH] powerpc: IBMEBUS bus support This patch adds the necessary core bus support used by device drivers that sit on the IBM GX bus on modern pSeries machines like the Galaxy infiniband for example. It provide transparent DMA ops (the low level driver works with virtual addresses directly) along with a simple bus layer using the Open Firmware matching routines. Signed-off-by: Heiko J Schick Signed-off-by: Paul Mackerras commit 72b10d1ae7fcaf375a2ed6cba5a4ca8eb5880150 Author: David Gibson Date: Wed Nov 16 15:43:48 2005 +1100 [PATCH] powerpc: Remove imalloc.h asm-ppc64/imalloc.h is only included from files in arch/powerpc/mm. We already have a header for mm local definitions, arch/powerpc/mm/mmu_decl.h. Thus, this patch moves the contents of imalloc.h into mmu_decl.h. The only exception are the definitions of PHBS_IO_BASE, IMALLOC_BASE and IMALLOC_END. Those are moved into pgtable.h, next to similar definitions of VMALLOC_START and VMALLOC_SIZE. Built for multiplatform 32bit and 64bit (ARCH=powerpc). Signed-off-by: David Gibson Signed-off-by: Paul Mackerras commit 710449d7d5ecd68c9966261f24fcc972bcd20057 Author: Benjamin Herrenschmidt Date: Tue Nov 15 14:34:24 2005 +1100 [PATCH] powerpc: Merge align.c (#2) Need testing !!! This patch merges align.c, the result isn't quite what was in ppc64 nor what was in ppc32 :) It should implement all the functionalities of both though. Kumar, since you played with that in the past, I suppose you have some test cases for verifying that it works properly before I dig out the 601 machine ? :) Since it's likely that I won't be able to test all scenario, code inspection is much welcome. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Paul Mackerras commit 6b687cdc87cad3f512ad61d7f86d544e79959899 Author: David Woodhouse Date: Tue Nov 15 18:52:18 2005 +0000 [PATCH] syscall entry/exit revamp This cleanup patch speeds up the null syscall path on ppc64 by about 3%, and brings the ppc32 and ppc64 code slightly closer together. The ppc64 code was checking current_thread_info()->flags twice in the syscall exit path; once for TIF_SYSCALL_T_OR_A before disabling interrupts, and then again for TIF_SIGPENDING|TIF_NEED_RESCHED etc after disabling interrupts. Now we do the same as ppc32 -- check the flags only once in the fast path, and re-enable interrupts if necessary in the ptrace case. The patch abolishes the 'syscall_noerror' member of struct thread_info and replaces it with a TIF_NOERROR bit in the flags, which is handled in the slow path. This shortens the syscall entry code, which no longer needs to clear syscall_noerror. The patch adds a TIF_SAVE_NVGPRS flag which causes the syscall exit slow path to save the non-volatile GPRs into a signal frame. This removes the need for the assembly wrappers around sys_sigsuspend(), sys_rt_sigsuspend(), et al which existed solely to save those registers in advance. It also means I don't have to add new wrappers for ppoll() and pselect(), which is what I was supposed to be doing when I got distracted into this... Finally, it unifies the ppc64 and ppc32 methods of handling syscall exit directly into a signal handler (as required by sigsuspend et al) by introducing a TIF_RESTOREALL flag which causes _all_ the registers to be reloaded from the pt_regs by taking the ret_from_exception path, instead of the normal syscall exit path which stomps on the callee-saved GPRs. It appears to pass an LTP test run on ppc64, and passes basic testing on ppc32 too. Brief tests of ptrace functionality with strace and gdb also appear OK. I wouldn't send it to Linus for 2.6.15 just yet though :) Signed-off-by: David Woodhouse Signed-off-by: Paul Mackerras commit cb01efde8caaab7a9147a51c98cdf8be3ff34899 Author: Mike Kravetz Date: Mon Nov 14 16:12:49 2005 -0800 [PATCH] Remove SPAN_OTHER_NODES config definition The config option SPAN_OTHER_NODES was created so that we could make pSeries numa layouts work within the DISCONTIG memory model. Now that DISCONTIG has been replaced by SPARSEMEM, we can eliminate this option. I'll be sending a separate patch to Andrew to remove the arch independent code as pSeries was the only arch that needed this. Signed-off-by: Mike Kravetz Signed-off-by: Paul Mackerras commit 3221103eb4c7afc8c15b6d2df8e3784450db6e1d Author: Kumar Gala Date: Mon Nov 14 12:54:33 2005 -0600 [PATCH] powerpc: moved ipic code to arch/powerpc Moved 83xx and QUICC Engine interrupt handling code into arch/powerpc as a precursor of getting 83xx sub-arch building in arch/powerpc. Signed-off-by: Kumar Gala Signed-off-by: Paul Mackerras commit 3438ebe315fcaae488144858b8a31af0f50c864b Author: Michael Ellerman Date: Mon Nov 14 23:35:00 2005 +1100 [PATCH] powerpc: Merge kexec This patch merges, to some extent, the PPC32 and PPC64 kexec implementations. We adopt the PPC32 approach of having ppc_md callbacks for the kexec functions. The current PPC64 implementation becomes the "default" implementation for PPC64 which platforms can select if they need no special treatment. I've added these default callbacks to pseries/maple/cell/powermac, this means iSeries no longer supports kexec - but it never worked anyway. I've renamed PPC32's machine_kexec_simple to default_machine_kexec, inline with PPC64. Judging by the comments it might be better named machine_kexec_non_of, or something, but at the moment it's the only implementation for PPC32 so it's the "default". Kexec requires machine_shutdown(), which is in machine_kexec.c on PPC32, but we already have in setup-common.c on powerpc. All this does is call ppc_md.nvram_sync, which only powermac implements, so instead make machine_shutdown a ppc_md member and have it call core99_nvram_sync directly on powermac. I've also stuck relocate_kernel.S into misc_32.S for powerpc. Built for ARCH=ppc, and 32 & 64 bit ARCH=powerpc, with KEXEC=y/n. Booted on P5 LPAR and successfully kexec'ed. Should apply on top of 493f25ef4087395891c99fcfe2c72e62e293e89f. Signed-off-by: Michael Ellerman Signed-off-by: Paul Mackerras commit 3f065054f92063dfd14f304c0f7f7c6065ae93a0 Author: Adrian Bunk Date: Sun Nov 13 02:26:09 2005 +0100 [PATCH] PPC_PREP: remove unneeded exports This patch removes the EXPORT_SYMBOL'ed but completely unused variable ucSystemType and removes the unneeded EXPORT_SYMBOL(_prep_type). Signed-off-by: Adrian Bunk Acked-by: Tom Rini Signed-off-by: Paul Mackerras --- diff --git a/Documentation/filesystems/spufs.txt b/Documentation/filesystems/spufs.txt new file mode 100644 index 0000000..8edc395 --- /dev/null +++ b/Documentation/filesystems/spufs.txt @@ -0,0 +1,521 @@ +SPUFS(2) Linux Programmer's Manual SPUFS(2) + + + +NAME + spufs - the SPU file system + + +DESCRIPTION + The SPU file system is used on PowerPC machines that implement the Cell + Broadband Engine Architecture in order to access Synergistic Processor + Units (SPUs). + + The file system provides a name space similar to posix shared memory or + message queues. Users that have write permissions on the file system + can use spu_create(2) to establish SPU contexts in the spufs root. + + Every SPU context is represented by a directory containing a predefined + set of files. These files can be used for manipulating the state of the + logical SPU. Users can change permissions on those files, but not actu- + ally add or remove files. + + +MOUNT OPTIONS + uid= + set the user owning the mount point, the default is 0 (root). + + gid= + set the group owning the mount point, the default is 0 (root). + + +FILES + The files in spufs mostly follow the standard behavior for regular sys- + tem calls like read(2) or write(2), but often support only a subset of + the operations supported on regular file systems. This list details the + supported operations and the deviations from the behaviour in the + respective man pages. + + All files that support the read(2) operation also support readv(2) and + all files that support the write(2) operation also support writev(2). + All files support the access(2) and stat(2) family of operations, but + only the st_mode, st_nlink, st_uid and st_gid fields of struct stat + contain reliable information. + + All files support the chmod(2)/fchmod(2) and chown(2)/fchown(2) opera- + tions, but will not be able to grant permissions that contradict the + possible operations, e.g. read access on the wbox file. + + The current set of files is: + + + /mem + the contents of the local storage memory of the SPU. This can be + accessed like a regular shared memory file and contains both code and + data in the address space of the SPU. The possible operations on an + open mem file are: + + read(2), pread(2), write(2), pwrite(2), lseek(2) + These operate as documented, with the exception that seek(2), + write(2) and pwrite(2) are not supported beyond the end of the + file. The file size is the size of the local storage of the SPU, + which normally is 256 kilobytes. + + mmap(2) + Mapping mem into the process address space gives access to the + SPU local storage within the process address space. Only + MAP_SHARED mappings are allowed. + + + /mbox + The first SPU to CPU communication mailbox. This file is read-only and + can be read in units of 32 bits. The file can only be used in non- + blocking mode and it even poll() will not block on it. The possible + operations on an open mbox file are: + + read(2) + If a count smaller than four is requested, read returns -1 and + sets errno to EINVAL. If there is no data available in the mail + box, the return value is set to -1 and errno becomes EAGAIN. + When data has been read successfully, four bytes are placed in + the data buffer and the value four is returned. + + + /ibox + The second SPU to CPU communication mailbox. This file is similar to + the first mailbox file, but can be read in blocking I/O mode, and the + poll familiy of system calls can be used to wait for it. The possible + operations on an open ibox file are: + + read(2) + If a count smaller than four is requested, read returns -1 and + sets errno to EINVAL. If there is no data available in the mail + box and the file descriptor has been opened with O_NONBLOCK, the + return value is set to -1 and errno becomes EAGAIN. + + If there is no data available in the mail box and the file + descriptor has been opened without O_NONBLOCK, the call will + block until the SPU writes to its interrupt mailbox channel. + When data has been read successfully, four bytes are placed in + the data buffer and the value four is returned. + + poll(2) + Poll on the ibox file returns (POLLIN | POLLRDNORM) whenever + data is available for reading. + + + /wbox + The CPU to SPU communation mailbox. It is write-only can can be written + in units of 32 bits. If the mailbox is full, write() will block and + poll can be used to wait for it becoming empty again. The possible + operations on an open wbox file are: write(2) If a count smaller than + four is requested, write returns -1 and sets errno to EINVAL. If there + is no space available in the mail box and the file descriptor has been + opened with O_NONBLOCK, the return value is set to -1 and errno becomes + EAGAIN. + + If there is no space available in the mail box and the file descriptor + has been opened without O_NONBLOCK, the call will block until the SPU + reads from its PPE mailbox channel. When data has been read success- + fully, four bytes are placed in the data buffer and the value four is + returned. + + poll(2) + Poll on the ibox file returns (POLLOUT | POLLWRNORM) whenever + space is available for writing. + + + /mbox_stat + /ibox_stat + /wbox_stat + Read-only files that contain the length of the current queue, i.e. how + many words can be read from mbox or ibox or how many words can be + written to wbox without blocking. The files can be read only in 4-byte + units and return a big-endian binary integer number. The possible + operations on an open *box_stat file are: + + read(2) + If a count smaller than four is requested, read returns -1 and + sets errno to EINVAL. Otherwise, a four byte value is placed in + the data buffer, containing the number of elements that can be + read from (for mbox_stat and ibox_stat) or written to (for + wbox_stat) the respective mail box without blocking or resulting + in EAGAIN. + + + /npc + /decr + /decr_status + /spu_tag_mask + /event_mask + /srr0 + Internal registers of the SPU. The representation is an ASCII string + with the numeric value of the next instruction to be executed. These + can be used in read/write mode for debugging, but normal operation of + programs should not rely on them because access to any of them except + npc requires an SPU context save and is therefore very inefficient. + + The contents of these files are: + + npc Next Program Counter + + decr SPU Decrementer + + decr_status Decrementer Status + + spu_tag_mask MFC tag mask for SPU DMA + + event_mask Event mask for SPU interrupts + + srr0 Interrupt Return address register + + + The possible operations on an open npc, decr, decr_status, + spu_tag_mask, event_mask or srr0 file are: + + read(2) + When the count supplied to the read call is shorter than the + required length for the pointer value plus a newline character, + subsequent reads from the same file descriptor will result in + completing the string, regardless of changes to the register by + a running SPU task. When a complete string has been read, all + subsequent read operations will return zero bytes and a new file + descriptor needs to be opened to read the value again. + + write(2) + A write operation on the file results in setting the register to + the value given in the string. The string is parsed from the + beginning to the first non-numeric character or the end of the + buffer. Subsequent writes to the same file descriptor overwrite + the previous setting. + + + /fpcr + This file gives access to the Floating Point Status and Control Regis- + ter as a four byte long file. The operations on the fpcr file are: + + read(2) + If a count smaller than four is requested, read returns -1 and + sets errno to EINVAL. Otherwise, a four byte value is placed in + the data buffer, containing the current value of the fpcr regis- + ter. + + write(2) + If a count smaller than four is requested, write returns -1 and + sets errno to EINVAL. Otherwise, a four byte value is copied + from the data buffer, updating the value of the fpcr register. + + + /signal1 + /signal2 + The two signal notification channels of an SPU. These are read-write + files that operate on a 32 bit word. Writing to one of these files + triggers an interrupt on the SPU. The value writting to the signal + files can be read from the SPU through a channel read or from host user + space through the file. After the value has been read by the SPU, it + is reset to zero. The possible operations on an open signal1 or sig- + nal2 file are: + + read(2) + If a count smaller than four is requested, read returns -1 and + sets errno to EINVAL. Otherwise, a four byte value is placed in + the data buffer, containing the current value of the specified + signal notification register. + + write(2) + If a count smaller than four is requested, write returns -1 and + sets errno to EINVAL. Otherwise, a four byte value is copied + from the data buffer, updating the value of the specified signal + notification register. The signal notification register will + either be replaced with the input data or will be updated to the + bitwise OR or the old value and the input data, depending on the + contents of the signal1_type, or signal2_type respectively, + file. + + + /signal1_type + /signal2_type + These two files change the behavior of the signal1 and signal2 notifi- + cation files. The contain a numerical ASCII string which is read as + either "1" or "0". In mode 0 (overwrite), the hardware replaces the + contents of the signal channel with the data that is written to it. in + mode 1 (logical OR), the hardware accumulates the bits that are subse- + quently written to it. The possible operations on an open signal1_type + or signal2_type file are: + + read(2) + When the count supplied to the read call is shorter than the + required length for the digit plus a newline character, subse- + quent reads from the same file descriptor will result in com- + pleting the string. When a complete string has been read, all + subsequent read operations will return zero bytes and a new file + descriptor needs to be opened to read the value again. + + write(2) + A write operation on the file results in setting the register to + the value given in the string. The string is parsed from the + beginning to the first non-numeric character or the end of the + buffer. Subsequent writes to the same file descriptor overwrite + the previous setting. + + +EXAMPLES + /etc/fstab entry + none /spu spufs gid=spu 0 0 + + +AUTHORS + Arnd Bergmann , Mark Nutter , + Ulrich Weigand + +SEE ALSO + capabilities(7), close(2), spu_create(2), spu_run(2), spufs(7) + + + +Linux 2005-09-28 SPUFS(2) + +------------------------------------------------------------------------------ + +SPU_RUN(2) Linux Programmer's Manual SPU_RUN(2) + + + +NAME + spu_run - execute an spu context + + +SYNOPSIS + #include + + int spu_run(int fd, unsigned int *npc, unsigned int *event); + +DESCRIPTION + The spu_run system call is used on PowerPC machines that implement the + Cell Broadband Engine Architecture in order to access Synergistic Pro- + cessor Units (SPUs). It uses the fd that was returned from spu_cre- + ate(2) to address a specific SPU context. When the context gets sched- + uled to a physical SPU, it starts execution at the instruction pointer + passed in npc. + + Execution of SPU code happens synchronously, meaning that spu_run does + not return while the SPU is still running. If there is a need to exe- + cute SPU code in parallel with other code on either the main CPU or + other SPUs, you need to create a new thread of execution first, e.g. + using the pthread_create(3) call. + + When spu_run returns, the current value of the SPU instruction pointer + is written back to npc, so you can call spu_run again without updating + the pointers. + + event can be a NULL pointer or point to an extended status code that + gets filled when spu_run returns. It can be one of the following con- + stants: + + SPE_EVENT_DMA_ALIGNMENT + A DMA alignment error + + SPE_EVENT_SPE_DATA_SEGMENT + A DMA segmentation error + + SPE_EVENT_SPE_DATA_STORAGE + A DMA storage error + + If NULL is passed as the event argument, these errors will result in a + signal delivered to the calling process. + +RETURN VALUE + spu_run returns the value of the spu_status register or -1 to indicate + an error and set errno to one of the error codes listed below. The + spu_status register value contains a bit mask of status codes and + optionally a 14 bit code returned from the stop-and-signal instruction + on the SPU. The bit masks for the status codes are: + + 0x02 SPU was stopped by stop-and-signal. + + 0x04 SPU was stopped by halt. + + 0x08 SPU is waiting for a channel. + + 0x10 SPU is in single-step mode. + + 0x20 SPU has tried to execute an invalid instruction. + + 0x40 SPU has tried to access an invalid channel. + + 0x3fff0000 + The bits masked with this value contain the code returned from + stop-and-signal. + + There are always one or more of the lower eight bits set or an error + code is returned from spu_run. + +ERRORS + EAGAIN or EWOULDBLOCK + fd is in non-blocking mode and spu_run would block. + + EBADF fd is not a valid file descriptor. + + EFAULT npc is not a valid pointer or status is neither NULL nor a valid + pointer. + + EINTR A signal occured while spu_run was in progress. The npc value + has been updated to the new program counter value if necessary. + + EINVAL fd is not a file descriptor returned from spu_create(2). + + ENOMEM Insufficient memory was available to handle a page fault result- + ing from an MFC direct memory access. + + ENOSYS the functionality is not provided by the current system, because + either the hardware does not provide SPUs or the spufs module is + not loaded. + + +NOTES + spu_run is meant to be used from libraries that implement a more + abstract interface to SPUs, not to be used from regular applications. + See http://www.bsc.es/projects/deepcomputing/linuxoncell/ for the rec- + ommended libraries. + + +CONFORMING TO + This call is Linux specific and only implemented by the ppc64 architec- + ture. Programs using this system call are not portable. + + +BUGS + The code does not yet fully implement all features lined out here. + + +AUTHOR + Arnd Bergmann + +SEE ALSO + capabilities(7), close(2), spu_create(2), spufs(7) + + + +Linux 2005-09-28 SPU_RUN(2) + +------------------------------------------------------------------------------ + +SPU_CREATE(2) Linux Programmer's Manual SPU_CREATE(2) + + + +NAME + spu_create - create a new spu context + + +SYNOPSIS + #include + #include + + int spu_create(const char *pathname, int flags, mode_t mode); + +DESCRIPTION + The spu_create system call is used on PowerPC machines that implement + the Cell Broadband Engine Architecture in order to access Synergistic + Processor Units (SPUs). It creates a new logical context for an SPU in + pathname and returns a handle to associated with it. pathname must + point to a non-existing directory in the mount point of the SPU file + system (spufs). When spu_create is successful, a directory gets cre- + ated on pathname and it is populated with files. + + The returned file handle can only be passed to spu_run(2) or closed, + other operations are not defined on it. When it is closed, all associ- + ated directory entries in spufs are removed. When the last file handle + pointing either inside of the context directory or to this file + descriptor is closed, the logical SPU context is destroyed. + + The parameter flags can be zero or any bitwise or'd combination of the + following constants: + + SPU_RAWIO + Allow mapping of some of the hardware registers of the SPU into + user space. This flag requires the CAP_SYS_RAWIO capability, see + capabilities(7). + + The mode parameter specifies the permissions used for creating the new + directory in spufs. mode is modified with the user's umask(2) value + and then used for both the directory and the files contained in it. The + file permissions mask out some more bits of mode because they typically + support only read or write access. See stat(2) for a full list of the + possible mode values. + + +RETURN VALUE + spu_create returns a new file descriptor. It may return -1 to indicate + an error condition and set errno to one of the error codes listed + below. + + +ERRORS + EACCESS + The current user does not have write access on the spufs mount + point. + + EEXIST An SPU context already exists at the given path name. + + EFAULT pathname is not a valid string pointer in the current address + space. + + EINVAL pathname is not a directory in the spufs mount point. + + ELOOP Too many symlinks were found while resolving pathname. + + EMFILE The process has reached its maximum open file limit. + + ENAMETOOLONG + pathname was too long. + + ENFILE The system has reached the global open file limit. + + ENOENT Part of pathname could not be resolved. + + ENOMEM The kernel could not allocate all resources required. + + ENOSPC There are not enough SPU resources available to create a new + context or the user specific limit for the number of SPU con- + texts has been reached. + + ENOSYS the functionality is not provided by the current system, because + either the hardware does not provide SPUs or the spufs module is + not loaded. + + ENOTDIR + A part of pathname is not a directory. + + + +NOTES + spu_create is meant to be used from libraries that implement a more + abstract interface to SPUs, not to be used from regular applications. + See http://www.bsc.es/projects/deepcomputing/linuxoncell/ for the rec- + ommended libraries. + + +FILES + pathname must point to a location beneath the mount point of spufs. By + convention, it gets mounted in /spu. + + +CONFORMING TO + This call is Linux specific and only implemented by the ppc64 architec- + ture. Programs using this system call are not portable. + + +BUGS + The code does not yet fully implement all features lined out here. + + +AUTHOR + Arnd Bergmann + +SEE ALSO + capabilities(7), close(2), spu_run(2), spufs(7) + + + +Linux 2005-09-28 SPU_CREATE(2) diff --git a/arch/i386/mm/hugetlbpage.c b/arch/i386/mm/hugetlbpage.c index d524127..a7d8915 100644 --- a/arch/i386/mm/hugetlbpage.c +++ b/arch/i386/mm/hugetlbpage.c @@ -48,18 +48,6 @@ pte_t *huge_pte_offset(struct mm_struct return (pte_t *) pmd; } -/* - * This function checks for proper alignment of input addr and len parameters. - */ -int is_aligned_hugepage_range(unsigned long addr, unsigned long len) -{ - if (len & ~HPAGE_MASK) - return -EINVAL; - if (addr & ~HPAGE_MASK) - return -EINVAL; - return 0; -} - #if 0 /* This is just for testing */ struct page * follow_huge_addr(struct mm_struct *mm, unsigned long address, int write) diff --git a/arch/ia64/mm/hugetlbpage.c b/arch/ia64/mm/hugetlbpage.c index 2d13889..9dbc7da 100644 --- a/arch/ia64/mm/hugetlbpage.c +++ b/arch/ia64/mm/hugetlbpage.c @@ -68,9 +68,10 @@ huge_pte_offset (struct mm_struct *mm, u #define mk_pte_huge(entry) { pte_val(entry) |= _PAGE_P; } /* - * This function checks for proper alignment of input addr and len parameters. + * Don't actually need to do any preparation, but need to make sure + * the address is in the right region. */ -int is_aligned_hugepage_range(unsigned long addr, unsigned long len) +int prepare_hugepage_range(unsigned long addr, unsigned long len) { if (len & ~HPAGE_MASK) return -EINVAL; diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index bb2efdd..21bfac0 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -50,7 +50,7 @@ config PPC config EARLY_PRINTK bool - default y if PPC64 + default y config COMPAT bool @@ -384,6 +384,13 @@ config IBMVIO bool default y +config IBMEBUS + depends on PPC_PSERIES + bool "Support for GX bus based adapters" + default y + help + Bus device driver for GX bus based adapters. + config PPC_MPC106 bool default n @@ -475,6 +482,7 @@ source arch/powerpc/platforms/embedded6x source arch/powerpc/platforms/4xx/Kconfig source arch/powerpc/platforms/85xx/Kconfig source arch/powerpc/platforms/8xx/Kconfig +source arch/powerpc/platforms/cell/Kconfig menu "Kernel options" diff --git a/arch/powerpc/Makefile b/arch/powerpc/Makefile index a13eb57..5f80e58 100644 --- a/arch/powerpc/Makefile +++ b/arch/powerpc/Makefile @@ -151,7 +151,7 @@ CPPFLAGS_vmlinux.lds := -Upowerpc # All the instructions talk about "make bzImage". bzImage: zImage -BOOT_TARGETS = zImage zImage.initrd znetboot znetboot.initrd vmlinux.sm +BOOT_TARGETS = zImage zImage.initrd znetboot znetboot.initrd vmlinux.sm uImage .PHONY: $(BOOT_TARGETS) diff --git a/arch/powerpc/boot/Makefile b/arch/powerpc/boot/Makefile index 9770f58..22726ae 100644 --- a/arch/powerpc/boot/Makefile +++ b/arch/powerpc/boot/Makefile @@ -143,6 +143,36 @@ $(obj)/zImage.initrd: $(obj)/zImage.init @cp -f $< $@ $(call if_changed,addnote) +#----------------------------------------------------------- +# build u-boot images +#----------------------------------------------------------- +quiet_cmd_mygzip = GZIP $@ +cmd_mygzip = gzip -f -9 < $< > $@.$$$$ && mv $@.$$$$ $@ + +quiet_cmd_objbin = OBJCOPY $@ + cmd_objbin = $(OBJCOPY) -O binary $< $@ + +quiet_cmd_uimage = UIMAGE $@ + cmd_uimage = $(CONFIG_SHELL) $(MKIMAGE) -A ppc -O linux -T kernel \ + -C gzip -a 00000000 -e 00000000 -n 'Linux-$(KERNELRELEASE)' \ + -d $< $@ + +MKIMAGE := $(srctree)/scripts/mkuboot.sh +targets += uImage +extra-y += vmlinux.bin vmlinux.gz + +$(obj)/vmlinux.bin: vmlinux FORCE + $(call if_changed,objbin) + +$(obj)/vmlinux.gz: $(obj)/vmlinux.bin FORCE + $(call if_changed,mygzip) + +$(obj)/uImage: $(obj)/vmlinux.gz + $(Q)rm -f $@ + $(call cmd,uimage) + @echo -n ' Image: $@ ' + @if [ -f $@ ]; then echo 'is ready' ; else echo 'not made'; fi + install: $(CONFIGURE) $(BOOTIMAGE) sh -x $(srctree)/$(src)/install.sh "$(KERNELRELEASE)" vmlinux System.map "$(INSTALL_PATH)" "$(BOOTIMAGE)" diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile index 9ed551b..8971492 100644 --- a/arch/powerpc/kernel/Makefile +++ b/arch/powerpc/kernel/Makefile @@ -12,12 +12,13 @@ CFLAGS_btext.o += -fPIC endif obj-y := semaphore.o cputable.o ptrace.o syscalls.o \ - irq.o align.o signal_32.o pmc.o vdso.o + irq.o align.o signal_32.o pmc.o vdso.o \ + prom_parse.o obj-y += vdso32/ obj-$(CONFIG_PPC64) += setup_64.o binfmt_elf32.o sys_ppc32.o \ signal_64.o ptrace32.o systbl.o \ paca.o ioctl32.o cpu_setup_power4.o \ - firmware.o sysfs.o udbg.o idle_64.o + firmware.o sysfs.o idle_64.o obj-$(CONFIG_PPC64) += vdso64/ obj-$(CONFIG_ALTIVEC) += vecemu.o vector.o obj-$(CONFIG_POWER4) += idle_power4.o @@ -30,11 +31,8 @@ obj-$(CONFIG_RTAS_FLASH) += rtas_flash.o obj-$(CONFIG_RTAS_PROC) += rtas-proc.o obj-$(CONFIG_LPARCFG) += lparcfg.o obj-$(CONFIG_IBMVIO) += vio.o +obj-$(CONFIG_IBMEBUS) += ibmebus.o obj-$(CONFIG_GENERIC_TBSYNC) += smp-tbsync.o -obj-$(CONFIG_PPC_PSERIES) += udbg_16550.o -obj-$(CONFIG_PPC_MAPLE) += udbg_16550.o -udbgscc-$(CONFIG_PPC64) := udbg_scc.o -obj-$(CONFIG_PPC_PMAC) += $(udbgscc-y) obj64-$(CONFIG_PPC_MULTIPLATFORM) += nvram_64.o ifeq ($(CONFIG_PPC_MERGE),y) @@ -48,7 +46,7 @@ extra-$(CONFIG_8xx) := head_8xx.o extra-y += vmlinux.lds obj-y += process.o init_task.o time.o \ - prom.o traps.o setup-common.o + prom.o traps.o setup-common.o udbg.o obj-$(CONFIG_PPC32) += entry_32.o setup_32.o misc_32.o systbl.o obj-$(CONFIG_PPC64) += misc_64.o dma_64.o iommu.o obj-$(CONFIG_PPC_OF) += prom_init.o @@ -57,16 +55,17 @@ obj-$(CONFIG_BOOTX_TEXT) += btext.o obj-$(CONFIG_6xx) += idle_6xx.o obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_KPROBES) += kprobes.o - +obj-$(CONFIG_PPC_MULTIPLATFORM) += legacy_serial.o +obj-$(CONFIG_PPC_MULTIPLATFORM) += udbg_16550.o module-$(CONFIG_PPC64) += module_64.o obj-$(CONFIG_MODULES) += $(module-y) pci64-$(CONFIG_PPC64) += pci_64.o pci_dn.o pci_iommu.o \ pci_direct_iommu.o iomap.o obj-$(CONFIG_PCI) += $(pci64-y) - -kexec64-$(CONFIG_PPC64) += machine_kexec_64.o -obj-$(CONFIG_KEXEC) += $(kexec64-y) +kexec-$(CONFIG_PPC64) := machine_kexec_64.o +kexec-$(CONFIG_PPC32) := machine_kexec_32.o +obj-$(CONFIG_KEXEC) += machine_kexec.o $(kexec-y) ifeq ($(CONFIG_PPC_ISERIES),y) $(obj)/head_64.o: $(obj)/lparmap.s diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c index 91538d2..56399c5 100644 --- a/arch/powerpc/kernel/asm-offsets.c +++ b/arch/powerpc/kernel/asm-offsets.c @@ -92,9 +92,9 @@ int main(void) DEFINE(TI_FLAGS, offsetof(struct thread_info, flags)); DEFINE(TI_PREEMPT, offsetof(struct thread_info, preempt_count)); - DEFINE(TI_SC_NOERR, offsetof(struct thread_info, syscall_noerror)); -#ifdef CONFIG_PPC32 + DEFINE(TI_SIGFRAME, offsetof(struct thread_info, nvgprs_frame)); DEFINE(TI_TASK, offsetof(struct thread_info, task)); +#ifdef CONFIG_PPC32 DEFINE(TI_EXECDOMAIN, offsetof(struct thread_info, exec_domain)); DEFINE(TI_CPU, offsetof(struct thread_info, cpu)); #endif /* CONFIG_PPC32 */ @@ -131,11 +131,9 @@ int main(void) DEFINE(PACALOWHTLBAREAS, offsetof(struct paca_struct, context.low_htlb_areas)); DEFINE(PACAHIGHHTLBAREAS, offsetof(struct paca_struct, context.high_htlb_areas)); #endif /* CONFIG_HUGETLB_PAGE */ - DEFINE(PACADEFAULTDECR, offsetof(struct paca_struct, default_decr)); DEFINE(PACA_EXGEN, offsetof(struct paca_struct, exgen)); DEFINE(PACA_EXMC, offsetof(struct paca_struct, exmc)); DEFINE(PACA_EXSLB, offsetof(struct paca_struct, exslb)); - DEFINE(PACA_EXDSI, offsetof(struct paca_struct, exdsi)); DEFINE(PACAEMERGSP, offsetof(struct paca_struct, emergency_sp)); DEFINE(PACALPPACA, offsetof(struct paca_struct, lppaca)); DEFINE(PACAHWCPUID, offsetof(struct paca_struct, hw_cpu_id)); diff --git a/arch/powerpc/kernel/btext.c b/arch/powerpc/kernel/btext.c index bdfba92..893dd24 100644 --- a/arch/powerpc/kernel/btext.c +++ b/arch/powerpc/kernel/btext.c @@ -31,15 +31,18 @@ static void draw_byte_32(unsigned char * static void draw_byte_16(unsigned char *bits, unsigned int *base, int rb); static void draw_byte_8(unsigned char *bits, unsigned int *base, int rb); -static int g_loc_X; -static int g_loc_Y; -static int g_max_loc_X; -static int g_max_loc_Y; - -static int dispDeviceRowBytes; -static int dispDeviceDepth; -static int dispDeviceRect[4]; -static unsigned char *dispDeviceBase, *logicalDisplayBase; +#define __force_data __attribute__((__section__(".data"))) + +static int g_loc_X __force_data; +static int g_loc_Y __force_data; +static int g_max_loc_X __force_data; +static int g_max_loc_Y __force_data; + +static int dispDeviceRowBytes __force_data; +static int dispDeviceDepth __force_data; +static int dispDeviceRect[4] __force_data; +static unsigned char *dispDeviceBase __force_data; +static unsigned char *logicalDisplayBase __force_data; unsigned long disp_BAT[2] __initdata = {0, 0}; @@ -47,7 +50,7 @@ unsigned long disp_BAT[2] __initdata = { static unsigned char vga_font[cmapsz]; -int boot_text_mapped; +int boot_text_mapped __force_data = 0; int force_printk_to_btext = 0; #ifdef CONFIG_PPC32 @@ -66,8 +69,7 @@ int force_printk_to_btext = 0; * is really badly aligned, but I didn't encounter this case * yet. */ -void __init -btext_prepare_BAT(void) +void __init btext_prepare_BAT(void) { unsigned long vaddr = KERNELBASE + 0x10000000; unsigned long addr; @@ -95,12 +97,13 @@ btext_prepare_BAT(void) } #endif -/* This function will enable the early boot text when doing OF booting. This - * way, xmon output should work too + +/* This function can be used to enable the early boot text when doing + * OF booting or within bootx init. It must be followed by a btext_unmap() + * call before the logical address becomes unuseable */ -void __init -btext_setup_display(int width, int height, int depth, int pitch, - unsigned long address) +void __init btext_setup_display(int width, int height, int depth, int pitch, + unsigned long address) { g_loc_X = 0; g_loc_Y = 0; @@ -116,6 +119,11 @@ btext_setup_display(int width, int heigh boot_text_mapped = 1; } +void __init btext_unmap(void) +{ + boot_text_mapped = 0; +} + /* Here's a small text engine to use during early boot * or for debugging purposes * @@ -127,7 +135,7 @@ btext_setup_display(int width, int heigh * changes. */ -void map_boot_text(void) +static void map_boot_text(void) { unsigned long base, offset, size; unsigned char *vbase; @@ -175,8 +183,9 @@ int btext_initialize(struct device_node if (prop) address = *prop; - /* FIXME: Add support for PCI reg properties */ - + /* FIXME: Add support for PCI reg properties. Right now, only + * reliable on macs + */ if (address == 0) return -EINVAL; @@ -184,7 +193,6 @@ int btext_initialize(struct device_node g_loc_Y = 0; g_max_loc_X = width / 8; g_max_loc_Y = height / 16; - logicalDisplayBase = (unsigned char *)address; dispDeviceBase = (unsigned char *)address; dispDeviceRowBytes = pitch; dispDeviceDepth = depth; @@ -197,7 +205,7 @@ int btext_initialize(struct device_node return 0; } -void __init init_boot_display(void) +int __init btext_find_display(int allow_nonstdout) { char *name; struct device_node *np = NULL; @@ -218,8 +226,8 @@ void __init init_boot_display(void) } if (np) rc = btext_initialize(np); - if (rc == 0) - return; + if (rc == 0 || !allow_nonstdout) + return rc; for (np = NULL; (np = of_find_node_by_type(np, "display"));) { if (get_property(np, "linux,opened", NULL)) { @@ -228,8 +236,9 @@ void __init init_boot_display(void) printk("result: %d\n", rc); } if (rc == 0) - return; + break; } + return rc; } /* Calc the base address of a given point (x,y) */ @@ -277,44 +286,83 @@ EXPORT_SYMBOL(btext_update_display); void btext_clearscreen(void) { - unsigned long *base = (unsigned long *)calc_base(0, 0); + unsigned int *base = (unsigned int *)calc_base(0, 0); unsigned long width = ((dispDeviceRect[2] - dispDeviceRect[0]) * - (dispDeviceDepth >> 3)) >> 3; + (dispDeviceDepth >> 3)) >> 2; int i,j; for (i=0; i<(dispDeviceRect[3] - dispDeviceRect[1]); i++) { - unsigned long *ptr = base; + unsigned int *ptr = base; for(j=width; j; --j) *(ptr++) = 0; - base += (dispDeviceRowBytes >> 3); + base += (dispDeviceRowBytes >> 2); + } +} + +void btext_flushscreen(void) +{ + unsigned int *base = (unsigned int *)calc_base(0, 0); + unsigned long width = ((dispDeviceRect[2] - dispDeviceRect[0]) * + (dispDeviceDepth >> 3)) >> 2; + int i,j; + + for (i=0; i < (dispDeviceRect[3] - dispDeviceRect[1]); i++) + { + unsigned int *ptr = base; + for(j = width; j > 0; j -= 8) { + __asm__ __volatile__ ("dcbst 0,%0" :: "r" (ptr)); + ptr += 8; + } + base += (dispDeviceRowBytes >> 2); } + __asm__ __volatile__ ("sync" ::: "memory"); } +void btext_flushline(void) +{ + unsigned int *base = (unsigned int *)calc_base(0, g_loc_Y << 4); + unsigned long width = ((dispDeviceRect[2] - dispDeviceRect[0]) * + (dispDeviceDepth >> 3)) >> 2; + int i,j; + + for (i=0; i < 16; i++) + { + unsigned int *ptr = base; + for(j = width; j > 0; j -= 8) { + __asm__ __volatile__ ("dcbst 0,%0" :: "r" (ptr)); + ptr += 8; + } + base += (dispDeviceRowBytes >> 2); + } + __asm__ __volatile__ ("sync" ::: "memory"); +} + + #ifndef NO_SCROLL static void scrollscreen(void) { - unsigned long *src = (unsigned long *)calc_base(0,16); - unsigned long *dst = (unsigned long *)calc_base(0,0); + unsigned int *src = (unsigned int *)calc_base(0,16); + unsigned int *dst = (unsigned int *)calc_base(0,0); unsigned long width = ((dispDeviceRect[2] - dispDeviceRect[0]) * - (dispDeviceDepth >> 3)) >> 3; + (dispDeviceDepth >> 3)) >> 2; int i,j; for (i=0; i<(dispDeviceRect[3] - dispDeviceRect[1] - 16); i++) { - unsigned long *src_ptr = src; - unsigned long *dst_ptr = dst; + unsigned int *src_ptr = src; + unsigned int *dst_ptr = dst; for(j=width; j; --j) *(dst_ptr++) = *(src_ptr++); - src += (dispDeviceRowBytes >> 3); - dst += (dispDeviceRowBytes >> 3); + src += (dispDeviceRowBytes >> 2); + dst += (dispDeviceRowBytes >> 2); } for (i=0; i<16; i++) { - unsigned long *dst_ptr = dst; + unsigned int *dst_ptr = dst; for(j=width; j; --j) *(dst_ptr++) = 0; - dst += (dispDeviceRowBytes >> 3); + dst += (dispDeviceRowBytes >> 2); } } #endif /* ndef NO_SCROLL */ @@ -377,6 +425,14 @@ void btext_drawstring(const char *c) btext_drawchar(*c++); } +void btext_drawtext(const char *c, unsigned int len) +{ + if (!boot_text_mapped) + return; + while (len--) + btext_drawchar(*c++); +} + void btext_drawhex(unsigned long v) { char *hex_table = "0123456789abcdef"; diff --git a/arch/powerpc/kernel/dma_64.c b/arch/powerpc/kernel/dma_64.c index 7c34196..36aaa76 100644 --- a/arch/powerpc/kernel/dma_64.c +++ b/arch/powerpc/kernel/dma_64.c @@ -10,6 +10,7 @@ /* Include the busses we support */ #include #include +#include #include #include @@ -23,6 +24,10 @@ static struct dma_mapping_ops *get_dma_o if (dev->bus == &vio_bus_type) return &vio_dma_ops; #endif +#ifdef CONFIG_IBMEBUS + if (dev->bus == &ibmebus_bus_type) + return &ibmebus_dma_ops; +#endif return NULL; } @@ -47,6 +52,10 @@ int dma_set_mask(struct device *dev, u64 if (dev->bus == &vio_bus_type) return -EIO; #endif /* CONFIG_IBMVIO */ +#ifdef CONFIG_IBMEBUS + if (dev->bus == &ibmebus_bus_type) + return -EIO; +#endif BUG(); return 0; } diff --git a/arch/powerpc/kernel/entry_32.S b/arch/powerpc/kernel/entry_32.S index 2e99ae4..036b71d 100644 --- a/arch/powerpc/kernel/entry_32.S +++ b/arch/powerpc/kernel/entry_32.S @@ -200,8 +200,6 @@ _GLOBAL(DoSyscall) bl do_show_syscall #endif /* SHOW_SYSCALLS */ rlwinm r10,r1,0,0,(31-THREAD_SHIFT) /* current_thread_info() */ - li r11,0 - stb r11,TI_SC_NOERR(r10) lwz r11,TI_FLAGS(r10) andi. r11,r11,_TIF_SYSCALL_T_OR_A bne- syscall_dotrace @@ -222,25 +220,21 @@ ret_from_syscall: bl do_show_syscall_exit #endif mr r6,r3 - li r11,-_LAST_ERRNO - cmplw 0,r3,r11 rlwinm r12,r1,0,0,(31-THREAD_SHIFT) /* current_thread_info() */ - blt+ 30f - lbz r11,TI_SC_NOERR(r12) - cmpwi r11,0 - bne 30f - neg r3,r3 - lwz r10,_CCR(r1) /* Set SO bit in CR */ - oris r10,r10,0x1000 - stw r10,_CCR(r1) - /* disable interrupts so current_thread_info()->flags can't change */ -30: LOAD_MSR_KERNEL(r10,MSR_KERNEL) /* doesn't include MSR_EE */ + LOAD_MSR_KERNEL(r10,MSR_KERNEL) /* doesn't include MSR_EE */ SYNC MTMSRD(r10) lwz r9,TI_FLAGS(r12) - andi. r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SIGPENDING|_TIF_NEED_RESCHED) + li r8,-_LAST_ERRNO + andi. r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SIGPENDING|_TIF_NEED_RESCHED|_TIF_RESTOREALL) bne- syscall_exit_work + cmplw 0,r3,r8 + blt+ syscall_exit_cont + lwz r11,_CCR(r1) /* Load CR */ + neg r3,r3 + oris r11,r11,0x1000 /* Set SO bit in CR */ + stw r11,_CCR(r1) syscall_exit_cont: #if defined(CONFIG_4xx) || defined(CONFIG_BOOKE) /* If the process has its own DBCR0 value, load it up. The single @@ -292,46 +286,113 @@ syscall_dotrace: b syscall_dotrace_cont syscall_exit_work: - stw r6,RESULT(r1) /* Save result */ + andi. r0,r9,_TIF_RESTOREALL + bne- 2f + cmplw 0,r3,r8 + blt+ 1f + andi. r0,r9,_TIF_NOERROR + bne- 1f + lwz r11,_CCR(r1) /* Load CR */ + neg r3,r3 + oris r11,r11,0x1000 /* Set SO bit in CR */ + stw r11,_CCR(r1) + +1: stw r6,RESULT(r1) /* Save result */ stw r3,GPR3(r1) /* Update return value */ - andi. r0,r9,_TIF_SYSCALL_T_OR_A - beq 5f - ori r10,r10,MSR_EE - SYNC - MTMSRD(r10) /* re-enable interrupts */ +2: andi. r0,r9,(_TIF_PERSYSCALL_MASK) + beq 4f + + /* Clear per-syscall TIF flags if any are set, but _leave_ + _TIF_SAVE_NVGPRS set in r9 since we haven't dealt with that + yet. */ + + li r11,_TIF_PERSYSCALL_MASK + addi r12,r12,TI_FLAGS +3: lwarx r8,0,r12 + andc r8,r8,r11 +#ifdef CONFIG_IBM405_ERR77 + dcbt 0,r12 +#endif + stwcx. r8,0,r12 + bne- 3b + subi r12,r12,TI_FLAGS + +4: /* Anything which requires enabling interrupts? */ + andi. r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP|_TIF_SAVE_NVGPRS) + beq 7f + + /* Save NVGPRS if they're not saved already */ lwz r4,_TRAP(r1) andi. r4,r4,1 - beq 4f + beq 5f SAVE_NVGPRS(r1) li r4,0xc00 stw r4,_TRAP(r1) -4: + + /* Re-enable interrupts */ +5: ori r10,r10,MSR_EE + SYNC + MTMSRD(r10) + + andi. r0,r9,_TIF_SAVE_NVGPRS + bne save_user_nvgprs + +save_user_nvgprs_cont: + andi. r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP) + beq 7f + addi r3,r1,STACK_FRAME_OVERHEAD bl do_syscall_trace_leave REST_NVGPRS(r1) -2: - lwz r3,GPR3(r1) + +6: lwz r3,GPR3(r1) LOAD_MSR_KERNEL(r10,MSR_KERNEL) /* doesn't include MSR_EE */ SYNC MTMSRD(r10) /* disable interrupts again */ rlwinm r12,r1,0,0,(31-THREAD_SHIFT) /* current_thread_info() */ lwz r9,TI_FLAGS(r12) -5: +7: andi. r0,r9,_TIF_NEED_RESCHED - bne 1f + bne 8f lwz r5,_MSR(r1) andi. r5,r5,MSR_PR - beq syscall_exit_cont + beq ret_from_except andi. r0,r9,_TIF_SIGPENDING - beq syscall_exit_cont + beq ret_from_except b do_user_signal -1: +8: ori r10,r10,MSR_EE SYNC MTMSRD(r10) /* re-enable interrupts */ bl schedule - b 2b + b 6b + +save_user_nvgprs: + lwz r8,TI_SIGFRAME(r12) + +.macro savewords start, end + 1: stw \start,4*(\start)(r8) + .section __ex_table,"a" + .align 2 + .long 1b,save_user_nvgprs_fault + .previous + .if \end - \start + savewords "(\start+1)",\end + .endif +.endm + savewords 14,31 + b save_user_nvgprs_cont + + +save_user_nvgprs_fault: + li r3,11 /* SIGSEGV */ + lwz r4,TI_TASK(r12) + bl force_sigsegv + rlwinm r12,r1,0,0,(31-THREAD_SHIFT) /* current_thread_info() */ + lwz r9,TI_FLAGS(r12) + b save_user_nvgprs_cont + #ifdef SHOW_SYSCALLS do_show_syscall: #ifdef SHOW_SYSCALLS_TASK @@ -401,28 +462,10 @@ show_syscalls_task: #endif /* SHOW_SYSCALLS */ /* - * The sigsuspend and rt_sigsuspend system calls can call do_signal - * and thus put the process into the stopped state where we might - * want to examine its user state with ptrace. Therefore we need - * to save all the nonvolatile registers (r13 - r31) before calling - * the C code. + * The fork/clone functions need to copy the full register set into + * the child process. Therefore we need to save all the nonvolatile + * registers (r13 - r31) before calling the C code. */ - .globl ppc_sigsuspend -ppc_sigsuspend: - SAVE_NVGPRS(r1) - lwz r0,_TRAP(r1) - rlwinm r0,r0,0,0,30 /* clear LSB to indicate full */ - stw r0,_TRAP(r1) /* register set saved */ - b sys_sigsuspend - - .globl ppc_rt_sigsuspend -ppc_rt_sigsuspend: - SAVE_NVGPRS(r1) - lwz r0,_TRAP(r1) - rlwinm r0,r0,0,0,30 - stw r0,_TRAP(r1) - b sys_rt_sigsuspend - .globl ppc_fork ppc_fork: SAVE_NVGPRS(r1) @@ -447,14 +490,6 @@ ppc_clone: stw r0,_TRAP(r1) /* register set saved */ b sys_clone - .globl ppc_swapcontext -ppc_swapcontext: - SAVE_NVGPRS(r1) - lwz r0,_TRAP(r1) - rlwinm r0,r0,0,0,30 /* clear LSB to indicate full */ - stw r0,_TRAP(r1) /* register set saved */ - b sys_swapcontext - /* * Top-level page fault handling. * This is in assembler because if do_page_fault tells us that @@ -626,16 +661,6 @@ END_FTR_SECTION_IFSET(CPU_FTR_601) .long ret_from_except #endif - .globl sigreturn_exit -sigreturn_exit: - subi r1,r3,STACK_FRAME_OVERHEAD - rlwinm r12,r1,0,0,(31-THREAD_SHIFT) /* current_thread_info() */ - lwz r9,TI_FLAGS(r12) - andi. r0,r9,_TIF_SYSCALL_T_OR_A - beq+ ret_from_except_full - bl do_syscall_trace_leave - /* fall through */ - .globl ret_from_except_full ret_from_except_full: REST_NVGPRS(r1) @@ -658,7 +683,7 @@ user_exc_return: /* r10 contains MSR_KE /* Check current_thread_info()->flags */ rlwinm r9,r1,0,0,(31-THREAD_SHIFT) lwz r9,TI_FLAGS(r9) - andi. r0,r9,(_TIF_SIGPENDING|_TIF_NEED_RESCHED) + andi. r0,r9,(_TIF_SIGPENDING|_TIF_NEED_RESCHED|_TIF_RESTOREALL) bne do_work restore_user: diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S index 2d22bf0..2eb6f54 100644 --- a/arch/powerpc/kernel/entry_64.S +++ b/arch/powerpc/kernel/entry_64.S @@ -113,9 +113,7 @@ system_call_common: addi r9,r1,STACK_FRAME_OVERHEAD #endif clrrdi r11,r1,THREAD_SHIFT - li r12,0 ld r10,TI_FLAGS(r11) - stb r12,TI_SC_NOERR(r11) andi. r11,r10,_TIF_SYSCALL_T_OR_A bne- syscall_dotrace syscall_dotrace_cont: @@ -144,24 +142,12 @@ system_call: /* label this so stack tr bctrl /* Call handler */ syscall_exit: + std r3,RESULT(r1) #ifdef SHOW_SYSCALLS - std r3,GPR3(r1) bl .do_show_syscall_exit - ld r3,GPR3(r1) + ld r3,RESULT(r1) #endif - std r3,RESULT(r1) - ld r5,_CCR(r1) - li r10,-_LAST_ERRNO - cmpld r3,r10 clrrdi r12,r1,THREAD_SHIFT - bge- syscall_error -syscall_error_cont: - - /* check for syscall tracing or audit */ - ld r9,TI_FLAGS(r12) - andi. r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP) - bne- syscall_exit_trace -syscall_exit_trace_cont: /* disable interrupts so current_thread_info()->flags can't change, and so that we don't get interrupted after loading SRR0/1. */ @@ -173,8 +159,13 @@ syscall_exit_trace_cont: rotldi r10,r10,16 mtmsrd r10,1 ld r9,TI_FLAGS(r12) - andi. r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SIGPENDING|_TIF_NEED_RESCHED) + li r11,-_LAST_ERRNO + andi. r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP|_TIF_SIGPENDING|_TIF_NEED_RESCHED|_TIF_RESTOREALL|_TIF_SAVE_NVGPRS|_TIF_NOERROR) bne- syscall_exit_work + cmpld r3,r11 + ld r5,_CCR(r1) + bge- syscall_error +syscall_error_cont: ld r7,_NIP(r1) stdcx. r0,0,r1 /* to clear the reservation */ andi. r6,r8,MSR_PR @@ -193,21 +184,12 @@ syscall_exit_trace_cont: rfid b . /* prevent speculative execution */ -syscall_enosys: - li r3,-ENOSYS - std r3,RESULT(r1) - clrrdi r12,r1,THREAD_SHIFT - ld r5,_CCR(r1) - -syscall_error: - lbz r11,TI_SC_NOERR(r12) - cmpwi 0,r11,0 - bne- syscall_error_cont - neg r3,r3 +syscall_error: oris r5,r5,0x1000 /* Set SO bit in CR */ + neg r3,r3 std r5,_CCR(r1) b syscall_error_cont - + /* Traced system call support */ syscall_dotrace: bl .save_nvgprs @@ -225,21 +207,69 @@ syscall_dotrace: ld r10,TI_FLAGS(r10) b syscall_dotrace_cont -syscall_exit_trace: - std r3,GPR3(r1) - bl .save_nvgprs +syscall_enosys: + li r3,-ENOSYS + b syscall_exit + +syscall_exit_work: + /* If TIF_RESTOREALL is set, don't scribble on either r3 or ccr. + If TIF_NOERROR is set, just save r3 as it is. */ + + andi. r0,r9,_TIF_RESTOREALL + bne- 2f + cmpld r3,r11 /* r10 is -LAST_ERRNO */ + blt+ 1f + andi. r0,r9,_TIF_NOERROR + bne- 1f + ld r5,_CCR(r1) + neg r3,r3 + oris r5,r5,0x1000 /* Set SO bit in CR */ + std r5,_CCR(r1) +1: std r3,GPR3(r1) +2: andi. r0,r9,(_TIF_PERSYSCALL_MASK) + beq 4f + + /* Clear per-syscall TIF flags if any are set, but _leave_ + _TIF_SAVE_NVGPRS set in r9 since we haven't dealt with that + yet. */ + + li r11,_TIF_PERSYSCALL_MASK + addi r12,r12,TI_FLAGS +3: ldarx r10,0,r12 + andc r10,r10,r11 + stdcx. r10,0,r12 + bne- 3b + subi r12,r12,TI_FLAGS + +4: bl .save_nvgprs + /* Anything else left to do? */ + andi. r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP|_TIF_SAVE_NVGPRS) + beq .ret_from_except_lite + + /* Re-enable interrupts */ + mfmsr r10 + ori r10,r10,MSR_EE + mtmsrd r10,1 + + andi. r0,r9,_TIF_SAVE_NVGPRS + bne save_user_nvgprs + + /* If tracing, re-enable interrupts and do it */ +save_user_nvgprs_cont: + andi. r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP) + beq 5f + addi r3,r1,STACK_FRAME_OVERHEAD bl .do_syscall_trace_leave REST_NVGPRS(r1) - ld r3,GPR3(r1) - ld r5,_CCR(r1) clrrdi r12,r1,THREAD_SHIFT - b syscall_exit_trace_cont -/* Stuff to do on exit from a system call. */ -syscall_exit_work: - std r3,GPR3(r1) - std r5,_CCR(r1) + /* Disable interrupts again and handle other work if any */ +5: mfmsr r10 + rldicl r10,r10,48,1 + rotldi r10,r10,16 + mtmsrd r10,1 + b .ret_from_except_lite /* Save non-volatile GPRs, if not already saved. */ @@ -252,6 +282,52 @@ _GLOBAL(save_nvgprs) std r0,_TRAP(r1) blr + +save_user_nvgprs: + ld r10,TI_SIGFRAME(r12) + andi. r0,r9,_TIF_32BIT + beq- save_user_nvgprs_64 + + /* 32-bit save to userspace */ + +.macro savewords start, end + 1: stw \start,4*(\start)(r10) + .section __ex_table,"a" + .align 3 + .llong 1b,save_user_nvgprs_fault + .previous + .if \end - \start + savewords "(\start+1)",\end + .endif +.endm + savewords 14,31 + b save_user_nvgprs_cont + +save_user_nvgprs_64: + /* 64-bit save to userspace */ + +.macro savelongs start, end + 1: std \start,8*(\start)(r10) + .section __ex_table,"a" + .align 3 + .llong 1b,save_user_nvgprs_fault + .previous + .if \end - \start + savelongs "(\start+1)",\end + .endif +.endm + savelongs 14,31 + b save_user_nvgprs_cont + +save_user_nvgprs_fault: + li r3,11 /* SIGSEGV */ + ld r4,TI_TASK(r12) + bl .force_sigsegv + + clrrdi r12,r1,THREAD_SHIFT + ld r9,TI_FLAGS(r12) + b save_user_nvgprs_cont + /* * The sigsuspend and rt_sigsuspend system calls can call do_signal * and thus put the process into the stopped state where we might @@ -260,35 +336,6 @@ _GLOBAL(save_nvgprs) * the C code. Similarly, fork, vfork and clone need the full * register state on the stack so that it can be copied to the child. */ -_GLOBAL(ppc32_sigsuspend) - bl .save_nvgprs - bl .compat_sys_sigsuspend - b 70f - -_GLOBAL(ppc64_rt_sigsuspend) - bl .save_nvgprs - bl .sys_rt_sigsuspend - b 70f - -_GLOBAL(ppc32_rt_sigsuspend) - bl .save_nvgprs - bl .compat_sys_rt_sigsuspend -70: cmpdi 0,r3,0 - /* If it returned an error, we need to return via syscall_exit to set - the SO bit in cr0 and potentially stop for ptrace. */ - bne syscall_exit - /* If sigsuspend() returns zero, we are going into a signal handler. We - may need to call audit_syscall_exit() to mark the exit from sigsuspend() */ -#ifdef CONFIG_AUDITSYSCALL - ld r3,PACACURRENT(r13) - ld r4,AUDITCONTEXT(r3) - cmpdi 0,r4,0 - beq .ret_from_except /* No audit_context: Leave immediately. */ - li r4, 2 /* AUDITSC_FAILURE */ - li r5,-4 /* It's always -EINTR */ - bl .audit_syscall_exit -#endif - b .ret_from_except _GLOBAL(ppc_fork) bl .save_nvgprs @@ -305,37 +352,6 @@ _GLOBAL(ppc_clone) bl .sys_clone b syscall_exit -_GLOBAL(ppc32_swapcontext) - bl .save_nvgprs - bl .compat_sys_swapcontext - b 80f - -_GLOBAL(ppc64_swapcontext) - bl .save_nvgprs - bl .sys_swapcontext - b 80f - -_GLOBAL(ppc32_sigreturn) - bl .compat_sys_sigreturn - b 80f - -_GLOBAL(ppc32_rt_sigreturn) - bl .compat_sys_rt_sigreturn - b 80f - -_GLOBAL(ppc64_rt_sigreturn) - bl .sys_rt_sigreturn - -80: cmpdi 0,r3,0 - blt syscall_exit - clrrdi r4,r1,THREAD_SHIFT - ld r4,TI_FLAGS(r4) - andi. r4,r4,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP) - beq+ 81f - addi r3,r1,STACK_FRAME_OVERHEAD - bl .do_syscall_trace_leave -81: b .ret_from_except - _GLOBAL(ret_from_fork) bl .schedule_tail REST_NVGPRS(r1) diff --git a/arch/powerpc/kernel/head_32.S b/arch/powerpc/kernel/head_32.S index ccdf947..6359e36 100644 --- a/arch/powerpc/kernel/head_32.S +++ b/arch/powerpc/kernel/head_32.S @@ -125,6 +125,19 @@ __start: bl prom_init trap +/* + * Check for BootX signature when supporting PowerMac and branch to + * appropriate trampoline if it's present + */ +#ifdef CONFIG_PPC_PMAC +1: lis r31,0x426f + ori r31,r31,0x6f58 + cmpw 0,r3,r31 + bne 1f + bl bootx_init + trap +#endif /* CONFIG_PPC_PMAC */ + 1: mr r31,r3 /* save parameters */ mr r30,r4 li r24,0 /* cpu # */ @@ -153,6 +166,9 @@ __after_mmu_off: bl flush_tlbs bl initial_bats +#if !defined(CONFIG_APUS) && defined(CONFIG_BOOTX_TEXT) + bl setup_disp_bat +#endif /* * Call setup_cpu for CPU 0 and initialize 6xx Idle @@ -1306,6 +1322,32 @@ initial_bats: blr +#if !defined(CONFIG_APUS) && defined(CONFIG_BOOTX_TEXT) +setup_disp_bat: + /* + * setup the display bat prepared for us in prom.c + */ + mflr r8 + bl reloc_offset + mtlr r8 + addis r8,r3,disp_BAT@ha + addi r8,r8,disp_BAT@l + cmpwi cr0,r8,0 + beqlr + lwz r11,0(r8) + lwz r8,4(r8) + mfspr r9,SPRN_PVR + rlwinm r9,r9,16,16,31 /* r9 = 1 for 601, 4 for 604 */ + cmpwi 0,r9,1 + beq 1f + mtspr SPRN_DBAT3L,r8 + mtspr SPRN_DBAT3U,r11 + blr +1: mtspr SPRN_IBAT3L,r8 + mtspr SPRN_IBAT3U,r11 + blr +#endif /* !defined(CONFIG_APUS) && defined(CONFIG_BOOTX_TEXT) */ + #ifdef CONFIG_8260 /* Jump into the system reset for the rom. * We first disable the MMU, and then jump to the ROM reset address. diff --git a/arch/powerpc/kernel/head_64.S b/arch/powerpc/kernel/head_64.S index 8a8bf79..881e18e 100644 --- a/arch/powerpc/kernel/head_64.S +++ b/arch/powerpc/kernel/head_64.S @@ -726,7 +726,8 @@ iSeries_secondary_smp_loop: decrementer_iSeries_masked: li r11,1 stb r11,PACALPPACA+LPPACADECRINT(r13) - lwz r12,PACADEFAULTDECR(r13) + LOADBASE(r12,tb_ticks_per_jiffy) + lwz r12,OFF(tb_ticks_per_jiffy)(r13) mtspr SPRN_DEC,r12 /* fall through */ diff --git a/arch/powerpc/kernel/ibmebus.c b/arch/powerpc/kernel/ibmebus.c new file mode 100644 index 0000000..e47d40a --- /dev/null +++ b/arch/powerpc/kernel/ibmebus.c @@ -0,0 +1,396 @@ +/* + * IBM PowerPC IBM eBus Infrastructure Support. + * + * Copyright (c) 2005 IBM Corporation + * Heiko J Schick + * + * All rights reserved. + * + * This source code is distributed under a dual license of GPL v2.0 and OpenIB + * BSD. + * + * OpenIB BSD License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include + +static struct ibmebus_dev ibmebus_bus_device = { /* fake "parent" device */ + .name = ibmebus_bus_device.ofdev.dev.bus_id, + .ofdev.dev.bus_id = "ibmebus", + .ofdev.dev.bus = &ibmebus_bus_type, +}; + +static void *ibmebus_alloc_coherent(struct device *dev, + size_t size, + dma_addr_t *dma_handle, + gfp_t flag) +{ + void *mem; + + mem = kmalloc(size, flag); + *dma_handle = (dma_addr_t)mem; + + return mem; +} + +static void ibmebus_free_coherent(struct device *dev, + size_t size, void *vaddr, + dma_addr_t dma_handle) +{ + kfree(vaddr); +} + +static dma_addr_t ibmebus_map_single(struct device *dev, + void *ptr, + size_t size, + enum dma_data_direction direction) +{ + return (dma_addr_t)(ptr); +} + +static void ibmebus_unmap_single(struct device *dev, + dma_addr_t dma_addr, + size_t size, + enum dma_data_direction direction) +{ + return; +} + +static int ibmebus_map_sg(struct device *dev, + struct scatterlist *sg, + int nents, enum dma_data_direction direction) +{ + int i; + + for (i = 0; i < nents; i++) { + sg[i].dma_address = (dma_addr_t)page_address(sg[i].page) + + sg[i].offset; + sg[i].dma_length = sg[i].length; + } + + return nents; +} + +static void ibmebus_unmap_sg(struct device *dev, + struct scatterlist *sg, + int nents, enum dma_data_direction direction) +{ + return; +} + +static int ibmebus_dma_supported(struct device *dev, u64 mask) +{ + return 1; +} + +struct dma_mapping_ops ibmebus_dma_ops = { + .alloc_coherent = ibmebus_alloc_coherent, + .free_coherent = ibmebus_free_coherent, + .map_single = ibmebus_map_single, + .unmap_single = ibmebus_unmap_single, + .map_sg = ibmebus_map_sg, + .unmap_sg = ibmebus_unmap_sg, + .dma_supported = ibmebus_dma_supported, +}; + +static int ibmebus_bus_probe(struct device *dev) +{ + struct ibmebus_dev *ibmebusdev = to_ibmebus_dev(dev); + struct ibmebus_driver *ibmebusdrv = to_ibmebus_driver(dev->driver); + const struct of_device_id *id; + int error = -ENODEV; + + if (!ibmebusdrv->probe) + return error; + + id = of_match_device(ibmebusdrv->id_table, &ibmebusdev->ofdev); + if (id) { + error = ibmebusdrv->probe(ibmebusdev, id); + } + + return error; +} + +static int ibmebus_bus_remove(struct device *dev) +{ + struct ibmebus_dev *ibmebusdev = to_ibmebus_dev(dev); + struct ibmebus_driver *ibmebusdrv = to_ibmebus_driver(dev->driver); + + if (ibmebusdrv->remove) { + return ibmebusdrv->remove(ibmebusdev); + } + + return 0; +} + +static void __devinit ibmebus_dev_release(struct device *dev) +{ + of_node_put(to_ibmebus_dev(dev)->ofdev.node); + kfree(to_ibmebus_dev(dev)); +} + +static ssize_t ibmebusdev_show_name(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s\n", to_ibmebus_dev(dev)->name); +} +static DEVICE_ATTR(name, S_IRUSR | S_IRGRP | S_IROTH, ibmebusdev_show_name, + NULL); + +static struct ibmebus_dev* __devinit ibmebus_register_device_common( + struct ibmebus_dev *dev, char *name) +{ + int err = 0; + + dev->name = name; + dev->ofdev.dev.parent = &ibmebus_bus_device.ofdev.dev; + dev->ofdev.dev.bus = &ibmebus_bus_type; + dev->ofdev.dev.release = ibmebus_dev_release; + + /* An ibmebusdev is based on a of_device. We have to change the + * bus type to use our own DMA mapping operations. + */ + if ((err = of_device_register(&dev->ofdev)) != 0) { + printk(KERN_ERR "%s: failed to register device (%d).\n", + __FUNCTION__, err); + return NULL; + } + + device_create_file(&dev->ofdev.dev, &dev_attr_name); + + return dev; +} + +static struct ibmebus_dev* __devinit ibmebus_register_device_node( + struct device_node *dn) +{ + struct ibmebus_dev *dev; + char *loc_code; + int length; + + loc_code = (char *)get_property(dn, "ibm,loc-code", NULL); + if (!loc_code) { + printk(KERN_WARNING "%s: node %s missing 'ibm,loc-code'\n", + __FUNCTION__, dn->name ? dn->name : ""); + return NULL; + } + + if (strlen(loc_code) == 0) { + printk(KERN_WARNING "%s: 'ibm,loc-code' is invalid\n", + __FUNCTION__); + return NULL; + } + + dev = kmalloc(sizeof(struct ibmebus_dev), GFP_KERNEL); + if (!dev) { + return NULL; + } + memset(dev, 0, sizeof(struct ibmebus_dev)); + + dev->ofdev.node = of_node_get(dn); + + length = strlen(loc_code); + memcpy(dev->ofdev.dev.bus_id, loc_code + + (length - min(length, BUS_ID_SIZE - 1)), + min(length, BUS_ID_SIZE - 1)); + + /* Register with generic device framework. */ + if (ibmebus_register_device_common(dev, dn->name) == NULL) { + kfree(dev); + return NULL; + } + + return dev; +} + +static void ibmebus_probe_of_nodes(char* name) +{ + struct device_node *dn = NULL; + + while ((dn = of_find_node_by_name(dn, name))) { + if (ibmebus_register_device_node(dn) == NULL) { + of_node_put(dn); + + return; + } + } + + of_node_put(dn); + + return; +} + +static void ibmebus_add_devices_by_id(struct of_device_id *idt) +{ + while (strlen(idt->name) > 0) { + ibmebus_probe_of_nodes(idt->name); + idt++; + } + + return; +} + +static int ibmebus_match_helper(struct device *dev, void *data) +{ + if (strcmp((char*)data, to_ibmebus_dev(dev)->name) == 0) + return 1; + + return 0; +} + +static int ibmebus_unregister_device(struct device *dev) +{ + device_remove_file(dev, &dev_attr_name); + of_device_unregister(to_of_device(dev)); + + return 0; +} + +static void ibmebus_remove_devices_by_id(struct of_device_id *idt) +{ + struct device *dev; + + while (strlen(idt->name) > 0) { + while ((dev = bus_find_device(&ibmebus_bus_type, NULL, + (void*)idt->name, + ibmebus_match_helper))) { + ibmebus_unregister_device(dev); + } + idt++; + + } + + return; +} + +int ibmebus_register_driver(struct ibmebus_driver *drv) +{ + int err = 0; + + drv->driver.name = drv->name; + drv->driver.bus = &ibmebus_bus_type; + drv->driver.probe = ibmebus_bus_probe; + drv->driver.remove = ibmebus_bus_remove; + + if ((err = driver_register(&drv->driver) != 0)) + return err; + + ibmebus_add_devices_by_id(drv->id_table); + + return 0; +} +EXPORT_SYMBOL(ibmebus_register_driver); + +void ibmebus_unregister_driver(struct ibmebus_driver *drv) +{ + driver_unregister(&drv->driver); + ibmebus_remove_devices_by_id(drv->id_table); +} +EXPORT_SYMBOL(ibmebus_unregister_driver); + +int ibmebus_request_irq(struct ibmebus_dev *dev, + u32 ist, + irqreturn_t (*handler)(int, void*, struct pt_regs *), + unsigned long irq_flags, const char * devname, + void *dev_id) +{ + unsigned int irq = virt_irq_create_mapping(ist); + + if (irq == NO_IRQ) + return -EINVAL; + + irq = irq_offset_up(irq); + + return request_irq(irq, handler, + irq_flags, devname, dev_id); +} +EXPORT_SYMBOL(ibmebus_request_irq); + +void ibmebus_free_irq(struct ibmebus_dev *dev, u32 ist, void *dev_id) +{ + unsigned int irq = virt_irq_create_mapping(ist); + + irq = irq_offset_up(irq); + free_irq(irq, dev_id); + + return; +} +EXPORT_SYMBOL(ibmebus_free_irq); + +static int ibmebus_bus_match(struct device *dev, struct device_driver *drv) +{ + const struct ibmebus_dev *ebus_dev = to_ibmebus_dev(dev); + struct ibmebus_driver *ebus_drv = to_ibmebus_driver(drv); + const struct of_device_id *ids = ebus_drv->id_table; + const struct of_device_id *found_id; + + if (!ids) + return 0; + + found_id = of_match_device(ids, &ebus_dev->ofdev); + if (found_id) + return 1; + + return 0; +} + +struct bus_type ibmebus_bus_type = { + .name = "ibmebus", + .match = ibmebus_bus_match, +}; +EXPORT_SYMBOL(ibmebus_bus_type); + +static int __init ibmebus_bus_init(void) +{ + int err; + + printk(KERN_INFO "IBM eBus Device Driver\n"); + + err = bus_register(&ibmebus_bus_type); + if (err) { + printk(KERN_ERR ":%s: failed to register IBM eBus.\n", + __FUNCTION__); + return err; + } + + err = device_register(&ibmebus_bus_device.ofdev.dev); + if (err) { + printk(KERN_WARNING "%s: device_register returned %i\n", + __FUNCTION__, err); + bus_unregister(&ibmebus_bus_type); + + return err; + } + + return 0; +} +__initcall(ibmebus_bus_init); diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c index 5a71ed9..5651032 100644 --- a/arch/powerpc/kernel/irq.c +++ b/arch/powerpc/kernel/irq.c @@ -31,7 +31,6 @@ * to reduce code space and undefined function references. */ -#include #include #include #include @@ -44,18 +43,12 @@ #include #include #include -#include #include #include -#include -#include #include #include #include #include -#ifdef CONFIG_PPC64 -#include -#endif #include #include @@ -66,8 +59,7 @@ #include #include #include -#ifdef CONFIG_PPC64 -#include +#ifdef CONFIG_PPC_ISERIES #include #endif @@ -78,10 +70,6 @@ EXPORT_SYMBOL(__irq_offset_value); static int ppc_spurious_interrupts; -#if defined(CONFIG_PPC_ISERIES) && defined(CONFIG_SMP) -extern void iSeries_smp_message_recv(struct pt_regs *); -#endif - #ifdef CONFIG_PPC32 #define NR_MASK_WORDS ((NR_IRQS + 31) / 32) @@ -195,49 +183,6 @@ void fixup_irqs(cpumask_t map) } #endif -#ifdef CONFIG_PPC_ISERIES -void do_IRQ(struct pt_regs *regs) -{ - struct paca_struct *lpaca; - - irq_enter(); - -#ifdef CONFIG_DEBUG_STACKOVERFLOW - /* Debugging check for stack overflow: is there less than 2KB free? */ - { - long sp; - - sp = __get_SP() & (THREAD_SIZE-1); - - if (unlikely(sp < (sizeof(struct thread_info) + 2048))) { - printk("do_IRQ: stack overflow: %ld\n", - sp - sizeof(struct thread_info)); - dump_stack(); - } - } -#endif - - lpaca = get_paca(); -#ifdef CONFIG_SMP - if (lpaca->lppaca.int_dword.fields.ipi_cnt) { - lpaca->lppaca.int_dword.fields.ipi_cnt = 0; - iSeries_smp_message_recv(regs); - } -#endif /* CONFIG_SMP */ - if (hvlpevent_is_pending()) - process_hvlpevents(regs); - - irq_exit(); - - if (lpaca->lppaca.int_dword.fields.decr_int) { - lpaca->lppaca.int_dword.fields.decr_int = 0; - /* Signal a fake decrementer interrupt */ - timer_interrupt(regs); - } -} - -#else /* CONFIG_PPC_ISERIES */ - void do_IRQ(struct pt_regs *regs) { int irq; @@ -286,16 +231,24 @@ void do_IRQ(struct pt_regs *regs) } else #endif __do_IRQ(irq, regs); - } else -#ifdef CONFIG_PPC32 - if (irq != -2) -#endif - /* That's not SMP safe ... but who cares ? */ - ppc_spurious_interrupts++; + } else if (irq != -2) + /* That's not SMP safe ... but who cares ? */ + ppc_spurious_interrupts++; + irq_exit(); -} -#endif /* CONFIG_PPC_ISERIES */ +#ifdef CONFIG_PPC_ISERIES + { + struct paca_struct *lpaca = get_paca(); + + if (lpaca->lppaca.int_dword.fields.decr_int) { + lpaca->lppaca.int_dword.fields.decr_int = 0; + /* Signal a fake decrementer interrupt */ + timer_interrupt(regs); + } + } +#endif +} void __init init_IRQ(void) { diff --git a/arch/powerpc/kernel/legacy_serial.c b/arch/powerpc/kernel/legacy_serial.c new file mode 100644 index 0000000..83023bb --- /dev/null +++ b/arch/powerpc/kernel/legacy_serial.c @@ -0,0 +1,504 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef DEBUG + +#ifdef DEBUG +#define DBG(fmt...) do { printk(fmt); } while(0) +#else +#define DBG(fmt...) do { } while(0) +#endif + +#define MAX_LEGACY_SERIAL_PORTS 8 + +static struct plat_serial8250_port +legacy_serial_ports[MAX_LEGACY_SERIAL_PORTS+1]; +static struct legacy_serial_info { + struct device_node *np; + unsigned int speed; + unsigned int clock; + phys_addr_t taddr; +} legacy_serial_infos[MAX_LEGACY_SERIAL_PORTS]; +static unsigned int legacy_serial_count; +static int legacy_serial_console = -1; + +static int __init add_legacy_port(struct device_node *np, int want_index, + int iotype, phys_addr_t base, + phys_addr_t taddr, unsigned long irq) +{ + u32 *clk, *spd, clock = BASE_BAUD * 16; + int index; + + /* get clock freq. if present */ + clk = (u32 *)get_property(np, "clock-frequency", NULL); + if (clk && *clk) + clock = *clk; + + /* get default speed if present */ + spd = (u32 *)get_property(np, "current-speed", NULL); + + /* If we have a location index, then try to use it */ + if (want_index >= 0 && want_index < MAX_LEGACY_SERIAL_PORTS) + index = want_index; + else + index = legacy_serial_count; + + /* if our index is still out of range, that mean that + * array is full, we could scan for a free slot but that + * make little sense to bother, just skip the port + */ + if (index >= MAX_LEGACY_SERIAL_PORTS) + return -1; + if (index >= legacy_serial_count) + legacy_serial_count = index + 1; + + /* Check if there is a port who already claimed our slot */ + if (legacy_serial_infos[index].np != 0) { + /* if we still have some room, move it, else override */ + if (legacy_serial_count < MAX_LEGACY_SERIAL_PORTS) { + printk(KERN_INFO "Moved legacy port %d -> %d\n", + index, legacy_serial_count); + legacy_serial_ports[legacy_serial_count] = + legacy_serial_ports[index]; + legacy_serial_infos[legacy_serial_count] = + legacy_serial_infos[index]; + legacy_serial_count++; + } else { + printk(KERN_INFO "Replacing legacy port %d\n", index); + } + } + + /* Now fill the entry */ + memset(&legacy_serial_ports[index], 0, + sizeof(struct plat_serial8250_port)); + if (iotype == UPIO_PORT) + legacy_serial_ports[index].iobase = base; + else + legacy_serial_ports[index].mapbase = base; + legacy_serial_ports[index].iotype = iotype; + legacy_serial_ports[index].uartclk = clock; + legacy_serial_ports[index].irq = irq; + legacy_serial_ports[index].flags = ASYNC_BOOT_AUTOCONF; + legacy_serial_infos[index].taddr = taddr; + legacy_serial_infos[index].np = of_node_get(np); + legacy_serial_infos[index].clock = clock; + legacy_serial_infos[index].speed = spd ? *spd : 0; + + printk(KERN_INFO "Found legacy serial port %d for %s\n", + index, np->full_name); + printk(KERN_INFO " %s=%llx, taddr=%llx, irq=%lx, clk=%d, speed=%d\n", + (iotype == UPIO_PORT) ? "port" : "mem", + (unsigned long long)base, (unsigned long long)taddr, irq, + legacy_serial_ports[index].uartclk, + legacy_serial_infos[index].speed); + + return index; +} + +static int __init add_legacy_isa_port(struct device_node *np, + struct device_node *isa_bridge) +{ + u32 *reg; + char *typep; + int index = -1; + phys_addr_t taddr; + + /* Get the ISA port number */ + reg = (u32 *)get_property(np, "reg", NULL); + if (reg == NULL) + return -1; + + /* Verify it's an IO port, we don't support anything else */ + if (!(reg[0] & 0x00000001)) + return -1; + + /* Now look for an "ibm,aix-loc" property that gives us ordering + * if any... + */ + typep = (char *)get_property(np, "ibm,aix-loc", NULL); + + /* If we have a location index, then use it */ + if (typep && *typep == 'S') + index = simple_strtol(typep+1, NULL, 0) - 1; + + /* Translate ISA address */ + taddr = of_translate_address(np, reg); + + /* Add port, irq will be dealt with later */ + return add_legacy_port(np, index, UPIO_PORT, reg[1], taddr, NO_IRQ); + +} + +static int __init add_legacy_pci_port(struct device_node *np, + struct device_node *pci_dev) +{ + phys_addr_t addr, base; + u32 *addrp; + int iotype, index = -1, lindex = 0; + + /* We only support ports that have a clock frequency properly + * encoded in the device-tree (that is have an fcode). Anything + * else can't be used that early and will be normally probed by + * the generic 8250_pci driver later on. The reason is that 8250 + * compatible UARTs on PCI need all sort of quirks (port offsets + * etc...) that this code doesn't know about + */ + if (get_property(np, "clock-frequency", NULL) == NULL) + return -1; + + /* Get the PCI address. Assume BAR 0 */ + addrp = of_get_pci_address(pci_dev, 0, NULL); + if (addrp == NULL) + return -1; + + /* We only support BAR 0 for now */ + iotype = (addrp[0] & 0x02000000) ? UPIO_MEM : UPIO_PORT; + addr = of_translate_address(pci_dev, addrp); + + /* Set the IO base to the same as the translated address for MMIO, + * or to the domain local IO base for PIO (it will be fixed up later) + */ + if (iotype == UPIO_MEM) + base = addr; + else + base = addrp[2]; + + /* Try to guess an index... If we have subdevices of the pci dev, + * we get to their "reg" property + */ + if (np != pci_dev) { + u32 *reg = (u32 *)get_property(np, "reg", NULL); + if (reg && (*reg < 4)) + index = lindex = *reg; + } + + /* Local index means it's the Nth port in the PCI chip. Unfortunately + * the offset to add here is device specific. We know about those + * EXAR ports and we default to the most common case. If your UART + * doesn't work for these settings, you'll have to add your own special + * cases here + */ + if (device_is_compatible(pci_dev, "pci13a8,152") || + device_is_compatible(pci_dev, "pci13a8,154") || + device_is_compatible(pci_dev, "pci13a8,158")) { + addr += 0x200 * lindex; + base += 0x200 * lindex; + } else { + addr += 8 * lindex; + base += 8 * lindex; + } + + /* Add port, irq will be dealt with later. We passed a translated + * IO port value. It will be fixed up later along with the irq + */ + return add_legacy_port(np, index, iotype, base, addr, NO_IRQ); +} + +/* + * This is called very early, as part of setup_system() or eventually + * setup_arch(), basically before anything else in this file. This function + * will try to build a list of all the available 8250-compatible serial ports + * in the machine using the Open Firmware device-tree. It currently only deals + * with ISA and PCI busses but could be extended. It allows a very early boot + * console to be initialized, that list is also used later to provide 8250 with + * the machine non-PCI ports and to properly pick the default console port + */ +void __init find_legacy_serial_ports(void) +{ + struct device_node *np, *stdout; + char *path; + int index; + + DBG(" -> find_legacy_serial_port()\n"); + + /* Now find out if one of these is out firmware console */ + path = (char *)get_property(of_chosen, "linux,stdout-path", NULL); + if (path == NULL) { + DBG(" no linux,stdout-path !\n"); + return; + } + stdout = of_find_node_by_path(path); + if (stdout) { + DBG("stdout is %s\n", stdout->full_name); + } + + /* First fill our array with ISA ports */ + for (np = NULL; (np = of_find_node_by_type(np, "serial"));) { + struct device_node *isa = of_get_parent(np); + if (isa && !strcmp(isa->name, "isa")) { + index = add_legacy_isa_port(np, isa); + if (index >= 0 && np == stdout) + legacy_serial_console = index; + } + of_node_put(isa); + } + + /* Next, try to locate PCI ports */ + for (np = NULL; (np = of_find_all_nodes(np));) { + struct device_node *pci, *parent = of_get_parent(np); + if (parent && !strcmp(parent->name, "isa")) { + of_node_put(parent); + continue; + } + if (strcmp(np->name, "serial") && strcmp(np->type, "serial")) { + of_node_put(parent); + continue; + } + /* Check for known pciclass, and also check wether we have + * a device with child nodes for ports or not + */ + if (device_is_compatible(np, "pciclass,0700") || + device_is_compatible(np, "pciclass,070002")) + pci = np; + else if (device_is_compatible(parent, "pciclass,0700") || + device_is_compatible(parent, "pciclass,070002")) + pci = parent; + else { + of_node_put(parent); + continue; + } + index = add_legacy_pci_port(np, pci); + if (index >= 0 && np == stdout) + legacy_serial_console = index; + of_node_put(parent); + } + + DBG("legacy_serial_console = %d\n", legacy_serial_console); + + /* udbg is 64 bits only for now, that will change soon though ... */ + while (legacy_serial_console >= 0) { + struct legacy_serial_info *info = + &legacy_serial_infos[legacy_serial_console]; + void __iomem *addr; + + if (info->taddr == 0) + break; + addr = ioremap(info->taddr, 0x1000); + if (addr == NULL) + break; + if (info->speed == 0) + info->speed = udbg_probe_uart_speed(addr, info->clock); + DBG("default console speed = %d\n", info->speed); + udbg_init_uart(addr, info->speed, info->clock); + break; + } + + DBG(" <- find_legacy_serial_port()\n"); +} + +static struct platform_device serial_device = { + .name = "serial8250", + .id = PLAT8250_DEV_PLATFORM, + .dev = { + .platform_data = legacy_serial_ports, + }, +}; + +static void __init fixup_port_irq(int index, + struct device_node *np, + struct plat_serial8250_port *port) +{ + DBG("fixup_port_irq(%d)\n", index); + + /* Check for interrupts in that node */ + if (np->n_intrs > 0) { + port->irq = np->intrs[0].line; + DBG(" port %d (%s), irq=%d\n", + index, np->full_name, port->irq); + return; + } + + /* Check for interrupts in the parent */ + np = of_get_parent(np); + if (np == NULL) + return; + + if (np->n_intrs > 0) { + port->irq = np->intrs[0].line; + DBG(" port %d (%s), irq=%d\n", + index, np->full_name, port->irq); + } + of_node_put(np); +} + +static void __init fixup_port_pio(int index, + struct device_node *np, + struct plat_serial8250_port *port) +{ + struct pci_controller *hose; + + DBG("fixup_port_pio(%d)\n", index); + + hose = pci_find_hose_for_OF_device(np); + if (hose) { + unsigned long offset = (unsigned long)hose->io_base_virt - +#ifdef CONFIG_PPC64 + pci_io_base; +#else + isa_io_base; +#endif + DBG("port %d, IO %lx -> %lx\n", + index, port->iobase, port->iobase + offset); + port->iobase += offset; + } +} + +static void __init fixup_port_mmio(int index, + struct device_node *np, + struct plat_serial8250_port *port) +{ + DBG("fixup_port_mmio(%d)\n", index); + + port->membase = ioremap(port->mapbase, 0x100); +} + +/* + * This is called as an arch initcall, hopefully before the PCI bus is + * probed and/or the 8250 driver loaded since we need to register our + * platform devices before 8250 PCI ones are detected as some of them + * must properly "override" the platform ones. + * + * This function fixes up the interrupt value for platform ports as it + * couldn't be done earlier before interrupt maps have been parsed. It + * also "corrects" the IO address for PIO ports for the same reason, + * since earlier, the PHBs virtual IO space wasn't assigned yet. It then + * registers all those platform ports for use by the 8250 driver when it + * finally loads. + */ +static int __init serial_dev_init(void) +{ + int i; + + if (legacy_serial_count == 0) + return -ENODEV; + + /* + * Before we register the platfrom serial devices, we need + * to fixup their interrutps and their IO ports. + */ + DBG("Fixing serial ports interrupts and IO ports ...\n"); + + for (i = 0; i < legacy_serial_count; i++) { + struct plat_serial8250_port *port = &legacy_serial_ports[i]; + struct device_node *np = legacy_serial_infos[i].np; + + if (port->irq == NO_IRQ) + fixup_port_irq(i, np, port); + if (port->iotype == UPIO_PORT) + fixup_port_pio(i, np, port); + if (port->iotype == UPIO_MEM) + fixup_port_mmio(i, np, port); + } + + DBG("Registering platform serial ports\n"); + + return platform_device_register(&serial_device); +} +arch_initcall(serial_dev_init); + + +/* + * This is called very early, as part of console_init() (typically just after + * time_init()). This function is respondible for trying to find a good + * default console on serial ports. It tries to match the open firmware + * default output with one of the available serial console drivers, either + * one of the platform serial ports that have been probed earlier by + * find_legacy_serial_ports() or some more platform specific ones. + */ +static int __init check_legacy_serial_console(void) +{ + struct device_node *prom_stdout = NULL; + int speed = 0, offset = 0; + char *name; + u32 *spd; + + DBG(" -> check_legacy_serial_console()\n"); + + /* The user has requested a console so this is already set up. */ + if (strstr(saved_command_line, "console=")) { + DBG(" console was specified !\n"); + return -EBUSY; + } + + if (!of_chosen) { + DBG(" of_chosen is NULL !\n"); + return -ENODEV; + } + /* We are getting a weird phandle from OF ... */ + /* ... So use the full path instead */ + name = (char *)get_property(of_chosen, "linux,stdout-path", NULL); + if (name == NULL) { + DBG(" no linux,stdout-path !\n"); + return -ENODEV; + } + prom_stdout = of_find_node_by_path(name); + if (!prom_stdout) { + DBG(" can't find stdout package %s !\n", name); + return -ENODEV; + } + DBG("stdout is %s\n", prom_stdout->full_name); + + name = (char *)get_property(prom_stdout, "name", NULL); + if (!name) { + DBG(" stdout package has no name !\n"); + goto not_found; + } + spd = (u32 *)get_property(prom_stdout, "current-speed", NULL); + if (spd) + speed = *spd; + + if (0) + ; +#ifdef CONFIG_SERIAL_8250_CONSOLE + else if (strcmp(name, "serial") == 0) { + int i; + /* Look for it in probed array */ + for (i = 0; i < legacy_serial_count; i++) { + if (prom_stdout != legacy_serial_infos[i].np) + continue; + offset = i; + speed = legacy_serial_infos[i].speed; + break; + } + if (i >= legacy_serial_count) + goto not_found; + } +#endif /* CONFIG_SERIAL_8250_CONSOLE */ +#ifdef CONFIG_SERIAL_PMACZILOG_CONSOLE + else if (strcmp(name, "ch-a") == 0) + offset = 0; + else if (strcmp(name, "ch-b") == 0) + offset = 1; +#endif /* CONFIG_SERIAL_PMACZILOG_CONSOLE */ + else + goto not_found; + of_node_put(prom_stdout); + + DBG("Found serial console at ttyS%d\n", offset); + + if (speed) { + static char __initdata opt[16]; + sprintf(opt, "%d", speed); + return add_preferred_console("ttyS", offset, opt); + } else + return add_preferred_console("ttyS", offset, NULL); + + not_found: + DBG("No preferred console found !\n"); + of_node_put(prom_stdout); + return -ENODEV; +} +console_initcall(check_legacy_serial_console); + diff --git a/arch/powerpc/kernel/machine_kexec.c b/arch/powerpc/kernel/machine_kexec.c new file mode 100644 index 0000000..d8225c7 --- /dev/null +++ b/arch/powerpc/kernel/machine_kexec.c @@ -0,0 +1,67 @@ +/* + * Code to handle transition of Linux booting another kernel. + * + * Copyright (C) 2002-2003 Eric Biederman + * GameCube/ppc32 port Copyright (C) 2004 Albert Herranz + * Copyright (C) 2005 IBM Corporation. + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#include +#include +#include +#include + +/* + * Provide a dummy crash_notes definition until crash dump is implemented. + * This prevents breakage of crash_notes attribute in kernel/ksysfs.c. + */ +note_buf_t crash_notes[NR_CPUS]; + +void machine_crash_shutdown(struct pt_regs *regs) +{ + if (ppc_md.machine_crash_shutdown) + ppc_md.machine_crash_shutdown(); +} + +/* + * Do what every setup is needed on image and the + * reboot code buffer to allow us to avoid allocations + * later. + */ +int machine_kexec_prepare(struct kimage *image) +{ + if (ppc_md.machine_kexec_prepare) + return ppc_md.machine_kexec_prepare(image); + /* + * Fail if platform doesn't provide its own machine_kexec_prepare + * implementation. + */ + return -ENOSYS; +} + +void machine_kexec_cleanup(struct kimage *image) +{ + if (ppc_md.machine_kexec_cleanup) + ppc_md.machine_kexec_cleanup(image); +} + +/* + * Do not allocate memory (or fail in any way) in machine_kexec(). + * We are past the point of no return, committed to rebooting now. + */ +NORET_TYPE void machine_kexec(struct kimage *image) +{ + if (ppc_md.machine_kexec) + ppc_md.machine_kexec(image); + else { + /* + * Fall back to normal restart if platform doesn't provide + * its own kexec function, and user insist to kexec... + */ + machine_restart(NULL); + } + for(;;); +} diff --git a/arch/powerpc/kernel/machine_kexec_32.c b/arch/powerpc/kernel/machine_kexec_32.c new file mode 100644 index 0000000..4436061 --- /dev/null +++ b/arch/powerpc/kernel/machine_kexec_32.c @@ -0,0 +1,65 @@ +/* + * PPC32 code to handle Linux booting another kernel. + * + * Copyright (C) 2002-2003 Eric Biederman + * GameCube/ppc32 port Copyright (C) 2004 Albert Herranz + * Copyright (C) 2005 IBM Corporation. + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#include +#include +#include +#include +#include +#include + +typedef NORET_TYPE void (*relocate_new_kernel_t)( + unsigned long indirection_page, + unsigned long reboot_code_buffer, + unsigned long start_address) ATTRIB_NORET; + +/* + * This is a generic machine_kexec function suitable at least for + * non-OpenFirmware embedded platforms. + * It merely copies the image relocation code to the control page and + * jumps to it. + * A platform specific function may just call this one. + */ +void default_machine_kexec(struct kimage *image) +{ + const extern unsigned char relocate_new_kernel[]; + const extern unsigned int relocate_new_kernel_size; + unsigned long page_list; + unsigned long reboot_code_buffer, reboot_code_buffer_phys; + relocate_new_kernel_t rnk; + + /* Interrupts aren't acceptable while we reboot */ + local_irq_disable(); + + page_list = image->head; + + /* we need both effective and real address here */ + reboot_code_buffer = + (unsigned long)page_address(image->control_code_page); + reboot_code_buffer_phys = virt_to_phys((void *)reboot_code_buffer); + + /* copy our kernel relocation code to the control code page */ + memcpy((void *)reboot_code_buffer, relocate_new_kernel, + relocate_new_kernel_size); + + flush_icache_range(reboot_code_buffer, + reboot_code_buffer + KEXEC_CONTROL_CODE_SIZE); + printk(KERN_INFO "Bye!\n"); + + /* now call it */ + rnk = (relocate_new_kernel_t) reboot_code_buffer; + (*rnk)(page_list, reboot_code_buffer_phys, image->start); +} + +int default_machine_kexec_prepare(struct kimage *image) +{ + return 0; +} diff --git a/arch/powerpc/kernel/machine_kexec_64.c b/arch/powerpc/kernel/machine_kexec_64.c index 97c51e4..ec0f06b 100644 --- a/arch/powerpc/kernel/machine_kexec_64.c +++ b/arch/powerpc/kernel/machine_kexec_64.c @@ -1,5 +1,5 @@ /* - * machine_kexec.c - handle transition of Linux booting another kernel + * PPC64 code to handle Linux booting another kernel. * * Copyright (C) 2004-2005, IBM Corp. * @@ -28,21 +28,7 @@ #define HASH_GROUP_SIZE 0x80 /* size of each hash group, asm/mmu.h */ -/* Have this around till we move it into crash specific file */ -note_buf_t crash_notes[NR_CPUS]; - -/* Dummy for now. Not sure if we need to have a crash shutdown in here - * and if what it will achieve. Letting it be now to compile the code - * in generic kexec environment - */ -void machine_crash_shutdown(struct pt_regs *regs) -{ - /* do nothing right now */ - /* smp_relase_cpus() if we want smp on panic kernel */ - /* cpu_irq_down to isolate us until we are ready */ -} - -int machine_kexec_prepare(struct kimage *image) +int default_machine_kexec_prepare(struct kimage *image) { int i; unsigned long begin, end; /* limits of segment */ @@ -111,11 +97,6 @@ int machine_kexec_prepare(struct kimage return 0; } -void machine_kexec_cleanup(struct kimage *image) -{ - /* we do nothing in prepare that needs to be undone */ -} - #define IND_FLAGS (IND_DESTINATION | IND_INDIRECTION | IND_DONE | IND_SOURCE) static void copy_segments(unsigned long ind) @@ -283,7 +264,7 @@ extern NORET_TYPE void kexec_sequence(vo void (*clear_all)(void)) ATTRIB_NORET; /* too late to fail here */ -void machine_kexec(struct kimage *image) +void default_machine_kexec(struct kimage *image) { /* prepare control code if any */ diff --git a/arch/powerpc/kernel/misc_32.S b/arch/powerpc/kernel/misc_32.S index 624a983..01d0d97 100644 --- a/arch/powerpc/kernel/misc_32.S +++ b/arch/powerpc/kernel/misc_32.S @@ -5,6 +5,10 @@ * Largely rewritten by Cort Dougan (cort@cs.nmt.edu) * and Paul Mackerras. * + * kexec bits: + * Copyright (C) 2002-2003 Eric Biederman + * GameCube/ppc32 port Copyright (C) 2004 Albert Herranz + * * 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 @@ -24,6 +28,8 @@ #include #include #include +#include +#include .text @@ -1006,3 +1012,110 @@ _GLOBAL(execve) */ _GLOBAL(__main) blr + +#ifdef CONFIG_KEXEC + /* + * Must be relocatable PIC code callable as a C function. + */ + .globl relocate_new_kernel +relocate_new_kernel: + /* r3 = page_list */ + /* r4 = reboot_code_buffer */ + /* r5 = start_address */ + + li r0, 0 + + /* + * Set Machine Status Register to a known status, + * switch the MMU off and jump to 1: in a single step. + */ + + mr r8, r0 + ori r8, r8, MSR_RI|MSR_ME + mtspr SPRN_SRR1, r8 + addi r8, r4, 1f - relocate_new_kernel + mtspr SPRN_SRR0, r8 + sync + rfi + +1: + /* from this point address translation is turned off */ + /* and interrupts are disabled */ + + /* set a new stack at the bottom of our page... */ + /* (not really needed now) */ + addi r1, r4, KEXEC_CONTROL_CODE_SIZE - 8 /* for LR Save+Back Chain */ + stw r0, 0(r1) + + /* Do the copies */ + li r6, 0 /* checksum */ + mr r0, r3 + b 1f + +0: /* top, read another word for the indirection page */ + lwzu r0, 4(r3) + +1: + /* is it a destination page? (r8) */ + rlwinm. r7, r0, 0, 31, 31 /* IND_DESTINATION (1<<0) */ + beq 2f + + rlwinm r8, r0, 0, 0, 19 /* clear kexec flags, page align */ + b 0b + +2: /* is it an indirection page? (r3) */ + rlwinm. r7, r0, 0, 30, 30 /* IND_INDIRECTION (1<<1) */ + beq 2f + + rlwinm r3, r0, 0, 0, 19 /* clear kexec flags, page align */ + subi r3, r3, 4 + b 0b + +2: /* are we done? */ + rlwinm. r7, r0, 0, 29, 29 /* IND_DONE (1<<2) */ + beq 2f + b 3f + +2: /* is it a source page? (r9) */ + rlwinm. r7, r0, 0, 28, 28 /* IND_SOURCE (1<<3) */ + beq 0b + + rlwinm r9, r0, 0, 0, 19 /* clear kexec flags, page align */ + + li r7, PAGE_SIZE / 4 + mtctr r7 + subi r9, r9, 4 + subi r8, r8, 4 +9: + lwzu r0, 4(r9) /* do the copy */ + xor r6, r6, r0 + stwu r0, 4(r8) + dcbst 0, r8 + sync + icbi 0, r8 + bdnz 9b + + addi r9, r9, 4 + addi r8, r8, 4 + b 0b + +3: + + /* To be certain of avoiding problems with self-modifying code + * execute a serializing instruction here. + */ + isync + sync + + /* jump to the entry point, usually the setup routine */ + mtlr r5 + blrl + +1: b 1b + +relocate_new_kernel_end: + + .globl relocate_new_kernel_size +relocate_new_kernel_size: + .long relocate_new_kernel_end - relocate_new_kernel +#endif diff --git a/arch/powerpc/kernel/paca.c b/arch/powerpc/kernel/paca.c index a7b68f9..999bdd8 100644 --- a/arch/powerpc/kernel/paca.c +++ b/arch/powerpc/kernel/paca.c @@ -17,6 +17,7 @@ #include #include #include +#include #include @@ -26,8 +27,7 @@ extern unsigned long __toc_start; /* The Paca is an array with one entry per processor. Each contains an * lppaca, which contains the information shared between the - * hypervisor and Linux. Each also contains an ItLpRegSave area which - * is used by the hypervisor to save registers. + * hypervisor and Linux. * On systems with hardware multi-threading, there are two threads * per processor. The Paca array must contain an entry for each thread. * The VPD Areas will give a max logical processors = 2 * max physical @@ -37,7 +37,6 @@ extern unsigned long __toc_start; #define PACA_INIT_COMMON(number, start, asrr, asrv) \ .lock_token = 0x8000, \ .paca_index = (number), /* Paca Index */ \ - .default_decr = 0x00ff0000, /* Initial Decr */ \ .kernel_toc = (unsigned long)(&__toc_start) + 0x8000UL, \ .stab_real = (asrr), /* Real pointer to segment table */ \ .stab_addr = (asrv), /* Virt pointer to segment table */ \ @@ -57,11 +56,7 @@ extern unsigned long __toc_start; #ifdef CONFIG_PPC_ISERIES #define PACA_INIT_ISERIES(number) \ .lppaca_ptr = &paca[number].lppaca, \ - .reg_save_ptr = &paca[number].reg_save, \ - .reg_save = { \ - .xDesc = 0xd397d9e2, /* "LpRS" */ \ - .xSize = sizeof(struct ItLpRegSave) \ - } + .reg_save_ptr = &iseries_reg_save[number], #define PACA_INIT(number) \ { \ diff --git a/arch/powerpc/kernel/pci_64.c b/arch/powerpc/kernel/pci_64.c index 8b6008a..5f241fc 100644 --- a/arch/powerpc/kernel/pci_64.c +++ b/arch/powerpc/kernel/pci_64.c @@ -251,7 +251,7 @@ void pcibios_free_controller(struct pci_ kfree(phb); } -static void __init pcibios_claim_one_bus(struct pci_bus *b) +void __devinit pcibios_claim_one_bus(struct pci_bus *b) { struct pci_dev *dev; struct pci_bus *child_bus; @@ -1223,6 +1223,7 @@ void __devinit pcibios_fixup_device_reso } EXPORT_SYMBOL(pcibios_fixup_device_resources); + static void __devinit do_bus_setup(struct pci_bus *bus) { struct pci_dev *dev; @@ -1306,6 +1307,20 @@ void pci_resource_to_user(const struct p *end = rsrc->end + offset; } +struct pci_controller* pci_find_hose_for_OF_device(struct device_node* node) +{ + if (!have_of) + return NULL; + while(node) { + struct pci_controller *hose, *tmp; + list_for_each_entry_safe(hose, tmp, &hose_list, list_node) + if (hose->arch_data == node) + return hose; + node = node->parent; + } + return NULL; +} + #endif /* CONFIG_PPC_MULTIPLATFORM */ diff --git a/arch/powerpc/kernel/ppc_ksyms.c b/arch/powerpc/kernel/ppc_ksyms.c index 94db257..b275814 100644 --- a/arch/powerpc/kernel/ppc_ksyms.c +++ b/arch/powerpc/kernel/ppc_ksyms.c @@ -76,11 +76,6 @@ EXPORT_SYMBOL(single_step_exception); EXPORT_SYMBOL(sys_sigreturn); #endif -#if defined(CONFIG_PPC_PREP) -EXPORT_SYMBOL(_prep_type); -EXPORT_SYMBOL(ucSystemType); -#endif - EXPORT_SYMBOL(strcpy); EXPORT_SYMBOL(strncpy); EXPORT_SYMBOL(strcat); diff --git a/arch/powerpc/kernel/prom_parse.c b/arch/powerpc/kernel/prom_parse.c new file mode 100644 index 0000000..9c2a5be --- /dev/null +++ b/arch/powerpc/kernel/prom_parse.c @@ -0,0 +1,420 @@ +#undef DEBUG + +#include +#include +#include +#include +#include + +#ifdef DEBUG +#define DBG(fmt...) do { printk(fmt); } while(0) +#else +#define DBG(fmt...) do { } while(0) +#endif + +#ifdef CONFIG_PPC64 +#define PRu64 "%lx" +#else +#define PRu64 "%llx" +#endif + +/* Max address size we deal with */ +#define OF_MAX_ADDR_CELLS 4 +#define OF_CHECK_COUNTS(na, ns) ((na) > 0 && (na) <= OF_MAX_ADDR_CELLS && \ + (ns) > 0) + +/* Debug utility */ +#ifdef DEBUG +static void of_dump_addr(const char *s, u32 *addr, int na) +{ + printk("%s", s); + while(na--) + printk(" %08x", *(addr++)); + printk("\n"); +} +#else +static void of_dump_addr(const char *s, u32 *addr, int na) { } +#endif + +/* Read a big address */ +static inline u64 of_read_addr(u32 *cell, int size) +{ + u64 r = 0; + while (size--) + r = (r << 32) | *(cell++); + return r; +} + +/* Callbacks for bus specific translators */ +struct of_bus { + const char *name; + const char *addresses; + int (*match)(struct device_node *parent); + void (*count_cells)(struct device_node *child, + int *addrc, int *sizec); + u64 (*map)(u32 *addr, u32 *range, int na, int ns, int pna); + int (*translate)(u32 *addr, u64 offset, int na); +}; + + +/* + * Default translator (generic bus) + */ + +static void of_default_count_cells(struct device_node *dev, + int *addrc, int *sizec) +{ + if (addrc) + *addrc = prom_n_addr_cells(dev); + if (sizec) + *sizec = prom_n_size_cells(dev); +} + +static u64 of_default_map(u32 *addr, u32 *range, int na, int ns, int pna) +{ + u64 cp, s, da; + + cp = of_read_addr(range, na); + s = of_read_addr(range + na + pna, ns); + da = of_read_addr(addr, na); + + DBG("OF: default map, cp="PRu64", s="PRu64", da="PRu64"\n", + cp, s, da); + + if (da < cp || da >= (cp + s)) + return OF_BAD_ADDR; + return da - cp; +} + +static int of_default_translate(u32 *addr, u64 offset, int na) +{ + u64 a = of_read_addr(addr, na); + memset(addr, 0, na * 4); + a += offset; + if (na > 1) + addr[na - 2] = a >> 32; + addr[na - 1] = a & 0xffffffffu; + + return 0; +} + + +/* + * PCI bus specific translator + */ + +static int of_bus_pci_match(struct device_node *np) +{ + return !strcmp(np->type, "pci"); +} + +static void of_bus_pci_count_cells(struct device_node *np, + int *addrc, int *sizec) +{ + if (addrc) + *addrc = 3; + if (sizec) + *sizec = 2; +} + +static u64 of_bus_pci_map(u32 *addr, u32 *range, int na, int ns, int pna) +{ + u64 cp, s, da; + + /* Check address type match */ + if ((addr[0] ^ range[0]) & 0x03000000) + return OF_BAD_ADDR; + + /* Read address values, skipping high cell */ + cp = of_read_addr(range + 1, na - 1); + s = of_read_addr(range + na + pna, ns); + da = of_read_addr(addr + 1, na - 1); + + DBG("OF: PCI map, cp="PRu64", s="PRu64", da="PRu64"\n", cp, s, da); + + if (da < cp || da >= (cp + s)) + return OF_BAD_ADDR; + return da - cp; +} + +static int of_bus_pci_translate(u32 *addr, u64 offset, int na) +{ + return of_default_translate(addr + 1, offset, na - 1); +} + +/* + * ISA bus specific translator + */ + +static int of_bus_isa_match(struct device_node *np) +{ + return !strcmp(np->name, "isa"); +} + +static void of_bus_isa_count_cells(struct device_node *child, + int *addrc, int *sizec) +{ + if (addrc) + *addrc = 2; + if (sizec) + *sizec = 1; +} + +static u64 of_bus_isa_map(u32 *addr, u32 *range, int na, int ns, int pna) +{ + u64 cp, s, da; + + /* Check address type match */ + if ((addr[0] ^ range[0]) & 0x00000001) + return OF_BAD_ADDR; + + /* Read address values, skipping high cell */ + cp = of_read_addr(range + 1, na - 1); + s = of_read_addr(range + na + pna, ns); + da = of_read_addr(addr + 1, na - 1); + + DBG("OF: ISA map, cp="PRu64", s="PRu64", da="PRu64"\n", cp, s, da); + + if (da < cp || da >= (cp + s)) + return OF_BAD_ADDR; + return da - cp; +} + +static int of_bus_isa_translate(u32 *addr, u64 offset, int na) +{ + return of_default_translate(addr + 1, offset, na - 1); +} + +/* + * Array of bus specific translators + */ + +static struct of_bus of_busses[] = { + /* PCI */ + { + .name = "pci", + .addresses = "assigned-addresses", + .match = of_bus_pci_match, + .count_cells = of_bus_pci_count_cells, + .map = of_bus_pci_map, + .translate = of_bus_pci_translate, + }, + /* ISA */ + { + .name = "isa", + .addresses = "reg", + .match = of_bus_isa_match, + .count_cells = of_bus_isa_count_cells, + .map = of_bus_isa_map, + .translate = of_bus_isa_translate, + }, + /* Default */ + { + .name = "default", + .addresses = "reg", + .match = NULL, + .count_cells = of_default_count_cells, + .map = of_default_map, + .translate = of_default_translate, + }, +}; + +static struct of_bus *of_match_bus(struct device_node *np) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(of_busses); i ++) + if (!of_busses[i].match || of_busses[i].match(np)) + return &of_busses[i]; + BUG(); + return NULL; +} + +static int of_translate_one(struct device_node *parent, struct of_bus *bus, + struct of_bus *pbus, u32 *addr, + int na, int ns, int pna) +{ + u32 *ranges; + unsigned int rlen; + int rone; + u64 offset = OF_BAD_ADDR; + + /* Normally, an absence of a "ranges" property means we are + * crossing a non-translatable boundary, and thus the addresses + * below the current not cannot be converted to CPU physical ones. + * Unfortunately, while this is very clear in the spec, it's not + * what Apple understood, and they do have things like /uni-n or + * /ht nodes with no "ranges" property and a lot of perfectly + * useable mapped devices below them. Thus we treat the absence of + * "ranges" as equivalent to an empty "ranges" property which means + * a 1:1 translation at that level. It's up to the caller not to try + * to translate addresses that aren't supposed to be translated in + * the first place. --BenH. + */ + ranges = (u32 *)get_property(parent, "ranges", &rlen); + if (ranges == NULL || rlen == 0) { + offset = of_read_addr(addr, na); + memset(addr, 0, pna); + goto finish; + } + + DBG("OF: walking ranges...\n"); + + /* Now walk through the ranges */ + rlen /= 4; + rone = na + pna + ns; + for (; rlen >= rone; rlen -= rone, ranges += rone) { + offset = bus->map(addr, ranges, na, ns, pna); + if (offset != OF_BAD_ADDR) + break; + } + if (offset == OF_BAD_ADDR) { + DBG("OF: not found !\n"); + return 1; + } + memcpy(addr, ranges + na, 4 * pna); + + finish: + of_dump_addr("OF: parent translation for:", addr, pna); + DBG("OF: with offset: %lx\n", offset); + + /* Translate it into parent bus space */ + return pbus->translate(addr, offset, pna); +} + + +/* + * Translate an address from the device-tree into a CPU physical address, + * this walks up the tree and applies the various bus mappings on the + * way. + * + * Note: We consider that crossing any level with #size-cells == 0 to mean + * that translation is impossible (that is we are not dealing with a value + * that can be mapped to a cpu physical address). This is not really specified + * that way, but this is traditionally the way IBM at least do things + */ +u64 of_translate_address(struct device_node *dev, u32 *in_addr) +{ + struct device_node *parent = NULL; + struct of_bus *bus, *pbus; + u32 addr[OF_MAX_ADDR_CELLS]; + int na, ns, pna, pns; + u64 result = OF_BAD_ADDR; + + DBG("OF: ** translation for device %s **\n", dev->full_name); + + /* Increase refcount at current level */ + of_node_get(dev); + + /* Get parent & match bus type */ + parent = of_get_parent(dev); + if (parent == NULL) + goto bail; + bus = of_match_bus(parent); + + /* Cound address cells & copy address locally */ + bus->count_cells(dev, &na, &ns); + if (!OF_CHECK_COUNTS(na, ns)) { + printk(KERN_ERR "prom_parse: Bad cell count for %s\n", + dev->full_name); + goto bail; + } + memcpy(addr, in_addr, na * 4); + + DBG("OF: bus is %s (na=%d, ns=%d) on %s\n", + bus->name, na, ns, parent->full_name); + of_dump_addr("OF: translating address:", addr, na); + + /* Translate */ + for (;;) { + /* Switch to parent bus */ + of_node_put(dev); + dev = parent; + parent = of_get_parent(dev); + + /* If root, we have finished */ + if (parent == NULL) { + DBG("OF: reached root node\n"); + result = of_read_addr(addr, na); + break; + } + + /* Get new parent bus and counts */ + pbus = of_match_bus(parent); + pbus->count_cells(dev, &pna, &pns); + if (!OF_CHECK_COUNTS(pna, pns)) { + printk(KERN_ERR "prom_parse: Bad cell count for %s\n", + dev->full_name); + break; + } + + DBG("OF: parent bus is %s (na=%d, ns=%d) on %s\n", + pbus->name, pna, pns, parent->full_name); + + /* Apply bus translation */ + if (of_translate_one(dev, bus, pbus, addr, na, ns, pna)) + break; + + /* Complete the move up one level */ + na = pna; + ns = pns; + bus = pbus; + + of_dump_addr("OF: one level translation:", addr, na); + } + bail: + of_node_put(parent); + of_node_put(dev); + + return result; +} +EXPORT_SYMBOL(of_translate_address); + +u32 *of_get_address(struct device_node *dev, int index, u64 *size) +{ + u32 *prop; + unsigned int psize; + struct device_node *parent; + struct of_bus *bus; + int onesize, i, na, ns; + + /* Get parent & match bus type */ + parent = of_get_parent(dev); + if (parent == NULL) + return NULL; + bus = of_match_bus(parent); + bus->count_cells(dev, &na, &ns); + of_node_put(parent); + if (!OF_CHECK_COUNTS(na, ns)) + return NULL; + + /* Get "reg" or "assigned-addresses" property */ + prop = (u32 *)get_property(dev, bus->addresses, &psize); + if (prop == NULL) + return NULL; + psize /= 4; + + onesize = na + ns; + for (i = 0; psize >= onesize; psize -= onesize, prop += onesize, i++) + if (i == index) { + if (size) + *size = of_read_addr(prop + na, ns); + return prop; + } + return NULL; +} +EXPORT_SYMBOL(of_get_address); + +u32 *of_get_pci_address(struct device_node *dev, int bar_no, u64 *size) +{ + u32 *addr; + int index; + + for (index = 0; (addr = of_get_address(dev, index, size)) != NULL; + index++) { + if ((addr[0] & 0xff) == ((bar_no * 4) + PCI_BASE_ADDRESS_0)) + return addr; + } + return NULL; +} +EXPORT_SYMBOL(of_get_pci_address); diff --git a/arch/powerpc/kernel/rtas_pci.c b/arch/powerpc/kernel/rtas_pci.c index 60dec24..aa26176 100644 --- a/arch/powerpc/kernel/rtas_pci.c +++ b/arch/powerpc/kernel/rtas_pci.c @@ -72,7 +72,7 @@ static int of_device_available(struct de return 0; } -static int rtas_read_config(struct pci_dn *pdn, int where, int size, u32 *val) +int rtas_read_config(struct pci_dn *pdn, int where, int size, u32 *val) { int returnval = -1; unsigned long buid, addr; diff --git a/arch/powerpc/kernel/setup-common.c b/arch/powerpc/kernel/setup-common.c index bd3eb42..a6d8aeb 100644 --- a/arch/powerpc/kernel/setup-common.c +++ b/arch/powerpc/kernel/setup-common.c @@ -93,8 +93,8 @@ EXPORT_SYMBOL(ppc_do_canonicalize_irqs); /* also used by kexec */ void machine_shutdown(void) { - if (ppc_md.nvram_sync) - ppc_md.nvram_sync(); + if (ppc_md.machine_shutdown) + ppc_md.machine_shutdown(); } void machine_restart(char *cmd) @@ -294,129 +294,6 @@ struct seq_operations cpuinfo_op = { .show = show_cpuinfo, }; -#ifdef CONFIG_PPC_MULTIPLATFORM -static int __init set_preferred_console(void) -{ - struct device_node *prom_stdout = NULL; - char *name; - u32 *spd; - int offset = 0; - - DBG(" -> set_preferred_console()\n"); - - /* The user has requested a console so this is already set up. */ - if (strstr(saved_command_line, "console=")) { - DBG(" console was specified !\n"); - return -EBUSY; - } - - if (!of_chosen) { - DBG(" of_chosen is NULL !\n"); - return -ENODEV; - } - /* We are getting a weird phandle from OF ... */ - /* ... So use the full path instead */ - name = (char *)get_property(of_chosen, "linux,stdout-path", NULL); - if (name == NULL) { - DBG(" no linux,stdout-path !\n"); - return -ENODEV; - } - prom_stdout = of_find_node_by_path(name); - if (!prom_stdout) { - DBG(" can't find stdout package %s !\n", name); - return -ENODEV; - } - DBG("stdout is %s\n", prom_stdout->full_name); - - name = (char *)get_property(prom_stdout, "name", NULL); - if (!name) { - DBG(" stdout package has no name !\n"); - goto not_found; - } - spd = (u32 *)get_property(prom_stdout, "current-speed", NULL); - - if (0) - ; -#ifdef CONFIG_SERIAL_8250_CONSOLE - else if (strcmp(name, "serial") == 0) { - int i; - u32 *reg = (u32 *)get_property(prom_stdout, "reg", &i); - if (i > 8) { - switch (reg[1]) { - case 0x3f8: - offset = 0; - break; - case 0x2f8: - offset = 1; - break; - case 0x898: - offset = 2; - break; - case 0x890: - offset = 3; - break; - default: - /* We dont recognise the serial port */ - goto not_found; - } - } - } -#endif /* CONFIG_SERIAL_8250_CONSOLE */ -#ifdef CONFIG_PPC_PSERIES - else if (strcmp(name, "vty") == 0) { - u32 *reg = (u32 *)get_property(prom_stdout, "reg", NULL); - char *compat = (char *)get_property(prom_stdout, "compatible", NULL); - - if (reg && compat && (strcmp(compat, "hvterm-protocol") == 0)) { - /* Host Virtual Serial Interface */ - switch (reg[0]) { - case 0x30000000: - offset = 0; - break; - case 0x30000001: - offset = 1; - break; - default: - goto not_found; - } - of_node_put(prom_stdout); - DBG("Found hvsi console at offset %d\n", offset); - return add_preferred_console("hvsi", offset, NULL); - } else { - /* pSeries LPAR virtual console */ - of_node_put(prom_stdout); - DBG("Found hvc console\n"); - return add_preferred_console("hvc", 0, NULL); - } - } -#endif /* CONFIG_PPC_PSERIES */ -#ifdef CONFIG_SERIAL_PMACZILOG_CONSOLE - else if (strcmp(name, "ch-a") == 0) - offset = 0; - else if (strcmp(name, "ch-b") == 0) - offset = 1; -#endif /* CONFIG_SERIAL_PMACZILOG_CONSOLE */ - else - goto not_found; - of_node_put(prom_stdout); - - DBG("Found serial console at ttyS%d\n", offset); - - if (spd) { - static char __initdata opt[16]; - sprintf(opt, "%d", *spd); - return add_preferred_console("ttyS", offset, opt); - } else - return add_preferred_console("ttyS", offset, NULL); - - not_found: - DBG("No preferred console found !\n"); - of_node_put(prom_stdout); - return -ENODEV; -} -console_initcall(set_preferred_console); -#endif /* CONFIG_PPC_MULTIPLATFORM */ - void __init check_for_initrd(void) { #ifdef CONFIG_BLK_DEV_INITRD diff --git a/arch/powerpc/kernel/setup_32.c b/arch/powerpc/kernel/setup_32.c index e569433..79d434f 100644 --- a/arch/powerpc/kernel/setup_32.c +++ b/arch/powerpc/kernel/setup_32.c @@ -39,6 +39,8 @@ #include #include #include +#include +#include #include "setup.h" @@ -172,12 +174,23 @@ void __init platform_init(void) */ void __init machine_init(unsigned long dt_ptr, unsigned long phys) { + /* If btext is enabled, we might have a BAT setup for early display, + * thus we do enable some very basic udbg output + */ +#ifdef CONFIG_BOOTX_TEXT + udbg_putc = btext_drawchar; +#endif + + /* Do some early initialization based on the flat device tree */ early_init_devtree(__va(dt_ptr)); + /* Check default command line */ #ifdef CONFIG_CMDLINE - strlcpy(cmd_line, CONFIG_CMDLINE, sizeof(cmd_line)); + if (cmd_line[0] == 0) + strlcpy(cmd_line, CONFIG_CMDLINE, sizeof(cmd_line)); #endif /* CONFIG_CMDLINE */ + /* Base init based on machine type */ platform_init(); #ifdef CONFIG_6xx @@ -282,25 +295,22 @@ void __init setup_arch(char **cmdline_p) unflatten_device_tree(); check_for_initrd(); - finish_device_tree(); - smp_setup_cpu_maps(); + if (ppc_md.init_early) + ppc_md.init_early(); -#ifdef CONFIG_BOOTX_TEXT - init_boot_display(); +#ifdef CONFIG_PPC_MULTIPLATFORM + find_legacy_serial_ports(); #endif + finish_device_tree(); -#ifdef CONFIG_PPC_PMAC - /* This could be called "early setup arch", it must be done - * now because xmon need it - */ - if (_machine == _MACH_Pmac) - pmac_feature_init(); /* New cool way */ -#endif + smp_setup_cpu_maps(); #ifdef CONFIG_XMON_DEFAULT xmon_init(1); #endif + /* Register early console */ + register_early_udbg_console(); #if defined(CONFIG_KGDB) if (ppc_md.kgdb_map_scc) diff --git a/arch/powerpc/kernel/setup_64.c b/arch/powerpc/kernel/setup_64.c index 608fee7..a82db8d 100644 --- a/arch/powerpc/kernel/setup_64.c +++ b/arch/powerpc/kernel/setup_64.c @@ -451,16 +451,21 @@ void __init setup_system(void) */ ppc_md.init_early(); + /* + * We can discover serial ports now since the above did setup the + * hash table management for us, thus ioremap works. We do that early + * so that further code can be debugged + */ +#ifdef CONFIG_PPC_MULTIPLATFORM + find_legacy_serial_ports(); +#endif + /* * "Finish" the device-tree, that is do the actual parsing of * some of the properties like the interrupt map */ finish_device_tree(); -#ifdef CONFIG_BOOTX_TEXT - init_boot_display(); -#endif - /* * Initialize xmon */ @@ -649,187 +654,6 @@ void ppc64_terminate_msg(unsigned int sr printk("[terminate]%04x %s\n", src, msg); } -#ifndef CONFIG_PPC_ISERIES -/* - * This function can be used by platforms to "find" legacy serial ports. - * It works for "serial" nodes under an "isa" node, and will try to - * respect the "ibm,aix-loc" property if any. It works with up to 8 - * ports. - */ - -#define MAX_LEGACY_SERIAL_PORTS 8 -static struct plat_serial8250_port serial_ports[MAX_LEGACY_SERIAL_PORTS+1]; -static unsigned int old_serial_count; - -void __init generic_find_legacy_serial_ports(u64 *physport, - unsigned int *default_speed) -{ - struct device_node *np; - u32 *sizeprop; - - struct isa_reg_property { - u32 space; - u32 address; - u32 size; - }; - struct pci_reg_property { - struct pci_address addr; - u32 size_hi; - u32 size_lo; - }; - - DBG(" -> generic_find_legacy_serial_port()\n"); - - *physport = 0; - if (default_speed) - *default_speed = 0; - - np = of_find_node_by_path("/"); - if (!np) - return; - - /* First fill our array */ - for (np = NULL; (np = of_find_node_by_type(np, "serial"));) { - struct device_node *isa, *pci; - struct isa_reg_property *reg; - unsigned long phys_size, addr_size, io_base; - u32 *rangesp; - u32 *interrupts, *clk, *spd; - char *typep; - int index, rlen, rentsize; - - /* Ok, first check if it's under an "isa" parent */ - isa = of_get_parent(np); - if (!isa || strcmp(isa->name, "isa")) { - DBG("%s: no isa parent found\n", np->full_name); - continue; - } - - /* Now look for an "ibm,aix-loc" property that gives us ordering - * if any... - */ - typep = (char *)get_property(np, "ibm,aix-loc", NULL); - - /* Get the ISA port number */ - reg = (struct isa_reg_property *)get_property(np, "reg", NULL); - if (reg == NULL) - goto next_port; - /* We assume the interrupt number isn't translated ... */ - interrupts = (u32 *)get_property(np, "interrupts", NULL); - /* get clock freq. if present */ - clk = (u32 *)get_property(np, "clock-frequency", NULL); - /* get default speed if present */ - spd = (u32 *)get_property(np, "current-speed", NULL); - /* Default to locate at end of array */ - index = old_serial_count; /* end of the array by default */ - - /* If we have a location index, then use it */ - if (typep && *typep == 'S') { - index = simple_strtol(typep+1, NULL, 0) - 1; - /* if index is out of range, use end of array instead */ - if (index >= MAX_LEGACY_SERIAL_PORTS) - index = old_serial_count; - /* if our index is still out of range, that mean that - * array is full, we could scan for a free slot but that - * make little sense to bother, just skip the port - */ - if (index >= MAX_LEGACY_SERIAL_PORTS) - goto next_port; - if (index >= old_serial_count) - old_serial_count = index + 1; - /* Check if there is a port who already claimed our slot */ - if (serial_ports[index].iobase != 0) { - /* if we still have some room, move it, else override */ - if (old_serial_count < MAX_LEGACY_SERIAL_PORTS) { - DBG("Moved legacy port %d -> %d\n", index, - old_serial_count); - serial_ports[old_serial_count++] = - serial_ports[index]; - } else { - DBG("Replacing legacy port %d\n", index); - } - } - } - if (index >= MAX_LEGACY_SERIAL_PORTS) - goto next_port; - if (index >= old_serial_count) - old_serial_count = index + 1; - - /* Now fill the entry */ - memset(&serial_ports[index], 0, sizeof(struct plat_serial8250_port)); - serial_ports[index].uartclk = clk ? *clk : BASE_BAUD * 16; - serial_ports[index].iobase = reg->address; - serial_ports[index].irq = interrupts ? interrupts[0] : 0; - serial_ports[index].flags = ASYNC_BOOT_AUTOCONF; - - DBG("Added legacy port, index: %d, port: %x, irq: %d, clk: %d\n", - index, - serial_ports[index].iobase, - serial_ports[index].irq, - serial_ports[index].uartclk); - - /* Get phys address of IO reg for port 1 */ - if (index != 0) - goto next_port; - - pci = of_get_parent(isa); - if (!pci) { - DBG("%s: no pci parent found\n", np->full_name); - goto next_port; - } - - rangesp = (u32 *)get_property(pci, "ranges", &rlen); - if (rangesp == NULL) { - of_node_put(pci); - goto next_port; - } - rlen /= 4; - - /* we need the #size-cells of the PCI bridge node itself */ - phys_size = 1; - sizeprop = (u32 *)get_property(pci, "#size-cells", NULL); - if (sizeprop != NULL) - phys_size = *sizeprop; - /* we need the parent #addr-cells */ - addr_size = prom_n_addr_cells(pci); - rentsize = 3 + addr_size + phys_size; - io_base = 0; - for (;rlen >= rentsize; rlen -= rentsize,rangesp += rentsize) { - if (((rangesp[0] >> 24) & 0x3) != 1) - continue; /* not IO space */ - io_base = rangesp[3]; - if (addr_size == 2) - io_base = (io_base << 32) | rangesp[4]; - } - if (io_base != 0) { - *physport = io_base + reg->address; - if (default_speed && spd) - *default_speed = *spd; - } - of_node_put(pci); - next_port: - of_node_put(isa); - } - - DBG(" <- generic_find_legacy_serial_port()\n"); -} - -static struct platform_device serial_device = { - .name = "serial8250", - .id = PLAT8250_DEV_PLATFORM, - .dev = { - .platform_data = serial_ports, - }, -}; - -static int __init serial_dev_init(void) -{ - return platform_device_register(&serial_device); -} -arch_initcall(serial_dev_init); - -#endif /* CONFIG_PPC_ISERIES */ - int check_legacy_ioport(unsigned long base_port) { if (ppc_md.check_legacy_ioport == NULL) diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c index 5a2eba6..d3f0b6d 100644 --- a/arch/powerpc/kernel/signal_32.c +++ b/arch/powerpc/kernel/signal_32.c @@ -76,7 +76,6 @@ * registers from *regs. This is what we need * to do when a signal has been delivered. */ -#define sigreturn_exit(regs) return 0 #define GP_REGS_SIZE min(sizeof(elf_gregset_t32), sizeof(struct pt_regs32)) #undef __SIGNAL_FRAMESIZE @@ -156,9 +155,17 @@ static inline int save_general_regs(stru elf_greg_t64 *gregs = (elf_greg_t64 *)regs; int i; - for (i = 0; i <= PT_RESULT; i ++) + if (!FULL_REGS(regs)) { + set_thread_flag(TIF_SAVE_NVGPRS); + current_thread_info()->nvgprs_frame = frame->mc_gregs; + } + + for (i = 0; i <= PT_RESULT; i ++) { + if (i == 14 && !FULL_REGS(regs)) + i = 32; if (__put_user((unsigned int)gregs[i], &frame->mc_gregs[i])) return -EFAULT; + } return 0; } @@ -179,8 +186,6 @@ static inline int restore_general_regs(s #else /* CONFIG_PPC64 */ -extern void sigreturn_exit(struct pt_regs *); - #define GP_REGS_SIZE min(sizeof(elf_gregset_t), sizeof(struct pt_regs)) static inline int put_sigset_t(sigset_t __user *uset, sigset_t *set) @@ -214,6 +219,15 @@ static inline int get_old_sigaction(stru static inline int save_general_regs(struct pt_regs *regs, struct mcontext __user *frame) { + if (!FULL_REGS(regs)) { + /* Zero out the unsaved GPRs to avoid information + leak, and set TIF_SAVE_NVGPRS to ensure that the + registers do actually get saved later. */ + memset(®s->gpr[14], 0, 18 * sizeof(unsigned long)); + current_thread_info()->nvgprs_frame = &frame->mc_gregs; + set_thread_flag(TIF_SAVE_NVGPRS); + } + return __copy_to_user(&frame->mc_gregs, regs, GP_REGS_SIZE); } @@ -256,8 +270,10 @@ long sys_sigsuspend(old_sigset_t mask, i while (1) { current->state = TASK_INTERRUPTIBLE; schedule(); - if (do_signal(&saveset, regs)) - sigreturn_exit(regs); + if (do_signal(&saveset, regs)) { + set_thread_flag(TIF_RESTOREALL); + return 0; + } } } @@ -292,8 +308,10 @@ long sys_rt_sigsuspend( while (1) { current->state = TASK_INTERRUPTIBLE; schedule(); - if (do_signal(&saveset, regs)) - sigreturn_exit(regs); + if (do_signal(&saveset, regs)) { + set_thread_flag(TIF_RESTOREALL); + return 0; + } } } @@ -391,9 +409,6 @@ struct rt_sigframe { static int save_user_regs(struct pt_regs *regs, struct mcontext __user *frame, int sigret) { -#ifdef CONFIG_PPC32 - CHECK_FULL_REGS(regs); -#endif /* Make sure floating point registers are stored in regs */ flush_fp_to_thread(current); @@ -828,12 +843,6 @@ static int handle_rt_signal(unsigned lon regs->gpr[6] = (unsigned long) rt_sf; regs->nip = (unsigned long) ka->sa.sa_handler; regs->trap = 0; -#ifdef CONFIG_PPC64 - regs->result = 0; - - if (test_thread_flag(TIF_SINGLESTEP)) - ptrace_notify(SIGTRAP); -#endif return 1; badframe: @@ -911,8 +920,8 @@ long sys_swapcontext(struct ucontext __u */ if (do_setcontext(new_ctx, regs, 0)) do_exit(SIGSEGV); - sigreturn_exit(regs); - /* doesn't actually return back to here */ + + set_thread_flag(TIF_RESTOREALL); return 0; } @@ -945,12 +954,11 @@ long sys_rt_sigreturn(int r3, int r4, in * nobody does any... */ compat_sys_sigaltstack((u32)(u64)&rt_sf->uc.uc_stack, 0, 0, 0, 0, 0, regs); - return (int)regs->result; #else do_sigaltstack(&rt_sf->uc.uc_stack, NULL, regs->gpr[1]); - sigreturn_exit(regs); /* doesn't return here */ - return 0; #endif + set_thread_flag(TIF_RESTOREALL); + return 0; bad: force_sig(SIGSEGV, current); @@ -1041,9 +1049,7 @@ int sys_debug_setcontext(struct ucontext */ do_sigaltstack(&ctx->uc_stack, NULL, regs->gpr[1]); - sigreturn_exit(regs); - /* doesn't actually return back to here */ - + set_thread_flag(TIF_RESTOREALL); out: return 0; } @@ -1107,12 +1113,6 @@ static int handle_signal(unsigned long s regs->gpr[4] = (unsigned long) sc; regs->nip = (unsigned long) ka->sa.sa_handler; regs->trap = 0; -#ifdef CONFIG_PPC64 - regs->result = 0; - - if (test_thread_flag(TIF_SINGLESTEP)) - ptrace_notify(SIGTRAP); -#endif return 1; @@ -1160,12 +1160,8 @@ long sys_sigreturn(int r3, int r4, int r || restore_user_regs(regs, sr, 1)) goto badframe; -#ifdef CONFIG_PPC64 - return (int)regs->result; -#else - sigreturn_exit(regs); /* doesn't return */ + set_thread_flag(TIF_RESTOREALL); return 0; -#endif badframe: force_sig(SIGSEGV, current); diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c index 1decf27..5462bef 100644 --- a/arch/powerpc/kernel/signal_64.c +++ b/arch/powerpc/kernel/signal_64.c @@ -96,8 +96,10 @@ long sys_rt_sigsuspend(sigset_t __user * while (1) { current->state = TASK_INTERRUPTIBLE; schedule(); - if (do_signal(&saveset, regs)) + if (do_signal(&saveset, regs)) { + set_thread_flag(TIF_RESTOREALL); return 0; + } } } @@ -152,6 +154,14 @@ static long setup_sigcontext(struct sigc err |= __put_user(0, &sc->v_regs); #endif /* CONFIG_ALTIVEC */ err |= __put_user(&sc->gp_regs, &sc->regs); + if (!FULL_REGS(regs)) { + /* Zero out the unsaved GPRs to avoid information + leak, and set TIF_SAVE_NVGPRS to ensure that the + registers do actually get saved later. */ + memset(®s->gpr[14], 0, 18 * sizeof(unsigned long)); + set_thread_flag(TIF_SAVE_NVGPRS); + current_thread_info()->nvgprs_frame = &sc->gp_regs; + } err |= __copy_to_user(&sc->gp_regs, regs, GP_REGS_SIZE); err |= __copy_to_user(&sc->fp_regs, ¤t->thread.fpr, FP_REGS_SIZE); err |= __put_user(signr, &sc->signal); @@ -340,6 +350,7 @@ int sys_swapcontext(struct ucontext __us do_exit(SIGSEGV); /* This returns like rt_sigreturn */ + set_thread_flag(TIF_RESTOREALL); return 0; } @@ -372,7 +383,8 @@ int sys_rt_sigreturn(unsigned long r3, u */ do_sigaltstack(&uc->uc_stack, NULL, regs->gpr[1]); - return regs->result; + set_thread_flag(TIF_RESTOREALL); + return 0; badframe: #if DEBUG_SIG @@ -454,9 +466,6 @@ static int setup_rt_frame(int signr, str if (err) goto badframe; - if (test_thread_flag(TIF_SINGLESTEP)) - ptrace_notify(SIGTRAP); - return 1; badframe: @@ -502,6 +511,8 @@ static inline void syscall_restart(struc * we only get here if there is a handler, we dont restart. */ regs->result = -EINTR; + regs->gpr[3] = EINTR; + regs->ccr |= 0x10000000; break; case -ERESTARTSYS: /* ERESTARTSYS means to restart the syscall if there is no @@ -509,6 +520,8 @@ static inline void syscall_restart(struc */ if (!(ka->sa.sa_flags & SA_RESTART)) { regs->result = -EINTR; + regs->gpr[3] = EINTR; + regs->ccr |= 0x10000000; break; } /* fallthrough */ diff --git a/arch/powerpc/kernel/smp.c b/arch/powerpc/kernel/smp.c index 30374d2..a90df6b 100644 --- a/arch/powerpc/kernel/smp.c +++ b/arch/powerpc/kernel/smp.c @@ -452,10 +452,6 @@ int __devinit __cpu_up(unsigned int cpu) if (smp_ops->cpu_bootable && !smp_ops->cpu_bootable(cpu)) return -EINVAL; -#ifdef CONFIG_PPC64 - paca[cpu].default_decr = tb_ticks_per_jiffy; -#endif - /* Make sure callin-map entry is 0 (can be leftover a CPU * hotplug */ diff --git a/arch/powerpc/kernel/systbl.S b/arch/powerpc/kernel/systbl.S index 65eaea9..989f628 100644 --- a/arch/powerpc/kernel/systbl.S +++ b/arch/powerpc/kernel/systbl.S @@ -113,7 +113,7 @@ SYSCALL(sgetmask) COMPAT_SYS(ssetmask) SYSCALL(setreuid) SYSCALL(setregid) -SYSX(sys_ni_syscall,ppc32_sigsuspend,ppc_sigsuspend) +SYS32ONLY(sigsuspend) COMPAT_SYS(sigpending) COMPAT_SYS(sethostname) COMPAT_SYS(setrlimit) @@ -160,7 +160,7 @@ SYSCALL(swapoff) COMPAT_SYS(sysinfo) COMPAT_SYS(ipc) SYSCALL(fsync) -SYSX(sys_ni_syscall,ppc32_sigreturn,sys_sigreturn) +SYS32ONLY(sigreturn) PPC_SYS(clone) COMPAT_SYS(setdomainname) PPC_SYS(newuname) @@ -213,13 +213,13 @@ COMPAT_SYS(nfsservctl) SYSCALL(setresgid) SYSCALL(getresgid) COMPAT_SYS(prctl) -SYSX(ppc64_rt_sigreturn,ppc32_rt_sigreturn,sys_rt_sigreturn) +COMPAT_SYS(rt_sigreturn) COMPAT_SYS(rt_sigaction) COMPAT_SYS(rt_sigprocmask) COMPAT_SYS(rt_sigpending) COMPAT_SYS(rt_sigtimedwait) COMPAT_SYS(rt_sigqueueinfo) -SYSX(ppc64_rt_sigsuspend,ppc32_rt_sigsuspend,ppc_rt_sigsuspend) +COMPAT_SYS(rt_sigsuspend) COMPAT_SYS(pread64) COMPAT_SYS(pwrite64) SYSCALL(chown) @@ -290,7 +290,7 @@ COMPAT_SYS(clock_settime) COMPAT_SYS(clock_gettime) COMPAT_SYS(clock_getres) COMPAT_SYS(clock_nanosleep) -SYSX(ppc64_swapcontext,ppc32_swapcontext,ppc_swapcontext) +COMPAT_SYS(swapcontext) COMPAT_SYS(tgkill) COMPAT_SYS(utimes) COMPAT_SYS(statfs64) @@ -319,3 +319,5 @@ COMPAT_SYS(ioprio_get) SYSCALL(inotify_init) SYSCALL(inotify_add_watch) SYSCALL(inotify_rm_watch) +SYSCALL(spu_run) +SYSCALL(spu_create) diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c index de84797..56f50e9 100644 --- a/arch/powerpc/kernel/time.c +++ b/arch/powerpc/kernel/time.c @@ -699,10 +699,6 @@ void __init time_init(void) div128_by_32(1024*1024, 0, tb_ticks_per_sec, &res); tb_to_xs = res.result_low; -#ifdef CONFIG_PPC64 - get_paca()->default_decr = tb_ticks_per_jiffy; -#endif - /* * Compute scale factor for sched_clock. * The calibrate_decr() function has set tb_ticks_per_sec, diff --git a/arch/powerpc/kernel/udbg.c b/arch/powerpc/kernel/udbg.c index 0d878e7..cc2df5e 100644 --- a/arch/powerpc/kernel/udbg.c +++ b/arch/powerpc/kernel/udbg.c @@ -16,8 +16,8 @@ #include #include -void (*udbg_putc)(unsigned char c); -unsigned char (*udbg_getc)(void); +void (*udbg_putc)(char c); +char (*udbg_getc)(void); int (*udbg_getc_poll)(void); /* udbg library, used by xmon et al */ @@ -78,7 +78,7 @@ int udbg_read(char *buf, int buflen) #define UDBG_BUFSIZE 256 void udbg_printf(const char *fmt, ...) { - unsigned char buf[UDBG_BUFSIZE]; + char buf[UDBG_BUFSIZE]; va_list args; va_start(args, fmt); @@ -99,7 +99,7 @@ static void udbg_console_write(struct co static struct console udbg_console = { .name = "udbg", .write = udbg_console_write, - .flags = CON_PRINTBUFFER, + .flags = CON_PRINTBUFFER | CON_ENABLED, .index = -1, }; @@ -116,6 +116,8 @@ void __init disable_early_printk(void) /* called by setup_system */ void register_early_udbg_console(void) { + if (early_console_initialized) + return; early_console_initialized = 1; register_console(&udbg_console); } diff --git a/arch/powerpc/kernel/udbg_16550.c b/arch/powerpc/kernel/udbg_16550.c index 9313574..28a58da 100644 --- a/arch/powerpc/kernel/udbg_16550.c +++ b/arch/powerpc/kernel/udbg_16550.c @@ -43,9 +43,11 @@ struct NS16550 { #define LSR_TEMT 0x40 /* Xmitter empty */ #define LSR_ERR 0x80 /* Error */ +#define LCR_DLAB 0x80 + static volatile struct NS16550 __iomem *udbg_comport; -static void udbg_550_putc(unsigned char c) +static void udbg_550_putc(char c) { if (udbg_comport) { while ((in_8(&udbg_comport->lsr) & LSR_THRE) == 0) @@ -67,7 +69,7 @@ static int udbg_550_getc_poll(void) return -1; } -static unsigned char udbg_550_getc(void) +static char udbg_550_getc(void) { if (udbg_comport) { while ((in_8(&udbg_comport->lsr) & LSR_DR) == 0) @@ -77,29 +79,70 @@ static unsigned char udbg_550_getc(void) return 0; } -void udbg_init_uart(void __iomem *comport, unsigned int speed) +void udbg_init_uart(void __iomem *comport, unsigned int speed, + unsigned int clock) { - u16 dll = speed ? (115200 / speed) : 12; + unsigned int dll, base_bauds = clock / 16; + + if (speed == 0) + speed = 9600; + dll = base_bauds / speed; if (comport) { udbg_comport = (struct NS16550 __iomem *)comport; out_8(&udbg_comport->lcr, 0x00); out_8(&udbg_comport->ier, 0xff); out_8(&udbg_comport->ier, 0x00); - out_8(&udbg_comport->lcr, 0x80); /* Access baud rate */ - out_8(&udbg_comport->dll, dll & 0xff); /* 1 = 115200, 2 = 57600, - 3 = 38400, 12 = 9600 baud */ - out_8(&udbg_comport->dlm, dll >> 8); /* dll >> 8 which should be zero - for fast rates; */ - out_8(&udbg_comport->lcr, 0x03); /* 8 data, 1 stop, no parity */ - out_8(&udbg_comport->mcr, 0x03); /* RTS/DTR */ - out_8(&udbg_comport->fcr ,0x07); /* Clear & enable FIFOs */ + out_8(&udbg_comport->lcr, LCR_DLAB); + out_8(&udbg_comport->dll, dll & 0xff); + out_8(&udbg_comport->dlm, dll >> 8); + /* 8 data, 1 stop, no parity */ + out_8(&udbg_comport->lcr, 0x03); + /* RTS/DTR */ + out_8(&udbg_comport->mcr, 0x03); + /* Clear & enable FIFOs */ + out_8(&udbg_comport->fcr ,0x07); udbg_putc = udbg_550_putc; udbg_getc = udbg_550_getc; udbg_getc_poll = udbg_550_getc_poll; } } +unsigned int udbg_probe_uart_speed(void __iomem *comport, unsigned int clock) +{ + unsigned int dll, dlm, divisor, prescaler, speed; + u8 old_lcr; + volatile struct NS16550 __iomem *port = comport; + + old_lcr = in_8(&port->lcr); + + /* select divisor latch registers. */ + out_8(&port->lcr, LCR_DLAB); + + /* now, read the divisor */ + dll = in_8(&port->dll); + dlm = in_8(&port->dlm); + divisor = dlm << 8 | dll; + + /* check prescaling */ + if (in_8(&port->mcr) & 0x80) + prescaler = 4; + else + prescaler = 1; + + /* restore the LCR */ + out_8(&port->lcr, old_lcr); + + /* calculate speed */ + speed = (clock / prescaler) / (divisor * 16); + + /* sanity check */ + if (speed < 9600 || speed > 115200) + speed = 9600; + + return speed; +} + #ifdef CONFIG_PPC_MAPLE void udbg_maple_real_putc(unsigned char c) { diff --git a/arch/powerpc/kernel/udbg_scc.c b/arch/powerpc/kernel/udbg_scc.c deleted file mode 100644 index 820c535..0000000 --- a/arch/powerpc/kernel/udbg_scc.c +++ /dev/null @@ -1,135 +0,0 @@ -/* - * udbg for for zilog scc ports as found on Apple PowerMacs - * - * Copyright (C) 2001-2005 PPC 64 Team, IBM Corp - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - */ -#include -#include -#include -#include -#include -#include -#include - -extern u8 real_readb(volatile u8 __iomem *addr); -extern void real_writeb(u8 data, volatile u8 __iomem *addr); - -#define SCC_TXRDY 4 -#define SCC_RXRDY 1 - -static volatile u8 __iomem *sccc; -static volatile u8 __iomem *sccd; - -static void udbg_scc_putc(unsigned char c) -{ - if (sccc) { - while ((in_8(sccc) & SCC_TXRDY) == 0) - ; - out_8(sccd, c); - if (c == '\n') - udbg_scc_putc('\r'); - } -} - -static int udbg_scc_getc_poll(void) -{ - if (sccc) { - if ((in_8(sccc) & SCC_RXRDY) != 0) - return in_8(sccd); - else - return -1; - } - return -1; -} - -static unsigned char udbg_scc_getc(void) -{ - if (sccc) { - while ((in_8(sccc) & SCC_RXRDY) == 0) - ; - return in_8(sccd); - } - return 0; -} - -static unsigned char scc_inittab[] = { - 13, 0, /* set baud rate divisor */ - 12, 0, - 14, 1, /* baud rate gen enable, src=rtxc */ - 11, 0x50, /* clocks = br gen */ - 5, 0xea, /* tx 8 bits, assert DTR & RTS */ - 4, 0x46, /* x16 clock, 1 stop */ - 3, 0xc1, /* rx enable, 8 bits */ -}; - -void udbg_init_scc(struct device_node *np) -{ - u32 *reg; - unsigned long addr; - int i, x; - - if (np == NULL) - np = of_find_node_by_name(NULL, "escc"); - if (np == NULL || np->parent == NULL) - return; - - udbg_printf("found SCC...\n"); - /* Get address within mac-io ASIC */ - reg = (u32 *)get_property(np, "reg", NULL); - if (reg == NULL) - return; - addr = reg[0]; - udbg_printf("local addr: %lx\n", addr); - /* Get address of mac-io PCI itself */ - reg = (u32 *)get_property(np->parent, "assigned-addresses", NULL); - if (reg == NULL) - return; - addr += reg[2]; - udbg_printf("final addr: %lx\n", addr); - - /* Setup for 57600 8N1 */ - addr += 0x20; - sccc = (volatile u8 * __iomem) ioremap(addr & PAGE_MASK, PAGE_SIZE) ; - sccc += addr & ~PAGE_MASK; - sccd = sccc + 0x10; - - udbg_printf("ioremap result sccc: %p\n", sccc); - mb(); - - for (i = 20000; i != 0; --i) - x = in_8(sccc); - out_8(sccc, 0x09); /* reset A or B side */ - out_8(sccc, 0xc0); - for (i = 0; i < sizeof(scc_inittab); ++i) - out_8(sccc, scc_inittab[i]); - - udbg_putc = udbg_scc_putc; - udbg_getc = udbg_scc_getc; - udbg_getc_poll = udbg_scc_getc_poll; - - udbg_puts("Hello World !\n"); -} - -static void udbg_real_scc_putc(unsigned char c) -{ - while ((real_readb(sccc) & SCC_TXRDY) == 0) - ; - real_writeb(c, sccd); - if (c == '\n') - udbg_real_scc_putc('\r'); -} - -void udbg_init_pmac_realmode(void) -{ - sccc = (volatile u8 __iomem *)0x80013020ul; - sccd = (volatile u8 __iomem *)0x80013030ul; - - udbg_putc = udbg_real_scc_putc; - udbg_getc = NULL; - udbg_getc_poll = NULL; -} diff --git a/arch/powerpc/mm/hash_utils_64.c b/arch/powerpc/mm/hash_utils_64.c index 706e8a6..0807d9d 100644 --- a/arch/powerpc/mm/hash_utils_64.c +++ b/arch/powerpc/mm/hash_utils_64.c @@ -644,6 +644,7 @@ int hash_page(unsigned long ea, unsigned DBG_LOW(" -> rc=%d\n", rc); return rc; } +EXPORT_SYMBOL_GPL(hash_page); void hash_preload(struct mm_struct *mm, unsigned long ea, unsigned long access, unsigned long trap) diff --git a/arch/powerpc/mm/hugetlbpage.c b/arch/powerpc/mm/hugetlbpage.c index 6bc9dba..f3ac452 100644 --- a/arch/powerpc/mm/hugetlbpage.c +++ b/arch/powerpc/mm/hugetlbpage.c @@ -133,21 +133,6 @@ pte_t huge_ptep_get_and_clear(struct mm_ return __pte(old); } -/* - * This function checks for proper alignment of input addr and len parameters. - */ -int is_aligned_hugepage_range(unsigned long addr, unsigned long len) -{ - if (len & ~HPAGE_MASK) - return -EINVAL; - if (addr & ~HPAGE_MASK) - return -EINVAL; - if (! (within_hugepage_low_range(addr, len) - || within_hugepage_high_range(addr, len)) ) - return -EINVAL; - return 0; -} - static void flush_low_segments(void *parm) { u16 areas = (unsigned long) parm; @@ -524,6 +509,17 @@ fail: return addr; } +static int htlb_check_hinted_area(unsigned long addr, unsigned long len) +{ + struct vm_area_struct *vma; + + vma = find_vma(current->mm, addr); + if (!vma || ((addr + len) <= vma->vm_start)) + return 0; + + return -ENOMEM; +} + static unsigned long htlb_get_low_area(unsigned long len, u16 segmask) { unsigned long addr = 0; @@ -584,6 +580,7 @@ unsigned long hugetlb_get_unmapped_area( { int lastshift; u16 areamask, curareas; + struct vm_area_struct *vma; if (HPAGE_SHIFT == 0) return -EINVAL; @@ -593,15 +590,28 @@ unsigned long hugetlb_get_unmapped_area( if (!cpu_has_feature(CPU_FTR_16M_PAGE)) return -EINVAL; + /* Paranoia, caller should have dealt with this */ + BUG_ON((addr + len) < addr); + if (test_thread_flag(TIF_32BIT)) { + /* Paranoia, caller should have dealt with this */ + BUG_ON((addr + len) > 0x100000000UL); + curareas = current->mm->context.low_htlb_areas; - /* First see if we can do the mapping in the existing - * low areas */ + /* First see if we can use the hint address */ + if (addr && (htlb_check_hinted_area(addr, len) == 0)) { + areamask = LOW_ESID_MASK(addr, len); + if (open_low_hpage_areas(current->mm, areamask) == 0) + return addr; + } + + /* Next see if we can map in the existing low areas */ addr = htlb_get_low_area(len, curareas); if (addr != -ENOMEM) return addr; + /* Finally go looking for areas to open */ lastshift = 0; for (areamask = LOW_ESID_MASK(0x100000000UL-len, len); ! lastshift; areamask >>=1) { @@ -616,12 +626,22 @@ unsigned long hugetlb_get_unmapped_area( } else { curareas = current->mm->context.high_htlb_areas; - /* First see if we can do the mapping in the existing - * high areas */ + /* First see if we can use the hint address */ + /* We discourage 64-bit processes from doing hugepage + * mappings below 4GB (must use MAP_FIXED) */ + if ((addr >= 0x100000000UL) + && (htlb_check_hinted_area(addr, len) == 0)) { + areamask = HTLB_AREA_MASK(addr, len); + if (open_high_hpage_areas(current->mm, areamask) == 0) + return addr; + } + + /* Next see if we can map in the existing high areas */ addr = htlb_get_high_area(len, curareas); if (addr != -ENOMEM) return addr; + /* Finally go looking for areas to open */ lastshift = 0; for (areamask = HTLB_AREA_MASK(TASK_SIZE_USER64-len, len); ! lastshift; areamask >>=1) { diff --git a/arch/powerpc/mm/init_32.c b/arch/powerpc/mm/init_32.c index 7d4b8b5..7d0d75c 100644 --- a/arch/powerpc/mm/init_32.c +++ b/arch/powerpc/mm/init_32.c @@ -188,6 +188,11 @@ void __init MMU_init(void) if (ppc_md.progress) ppc_md.progress("MMU:exit", 0x211); + + /* From now on, btext is no longer BAT mapped if it was at all */ +#ifdef CONFIG_BOOTX_TEXT + btext_unmap(); +#endif } /* This is only called until mem_init is done. */ diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c index f72cf87..4cb39de 100644 --- a/arch/powerpc/mm/numa.c +++ b/arch/powerpc/mm/numa.c @@ -254,29 +254,17 @@ static int __init find_min_common_depth( return depth; } -static int __init get_mem_addr_cells(void) +static void __init get_n_mem_cells(int *n_addr_cells, int *n_size_cells) { struct device_node *memory = NULL; - int rc; memory = of_find_node_by_type(memory, "memory"); - if (!memory) - return 0; /* it won't matter */ - - rc = prom_n_addr_cells(memory); - return rc; -} - -static int __init get_mem_size_cells(void) -{ - struct device_node *memory = NULL; - int rc; - - memory = of_find_node_by_type(memory, "memory"); - if (!memory) - return 0; /* it won't matter */ - rc = prom_n_size_cells(memory); - return rc; + if (memory) { + *n_addr_cells = prom_n_addr_cells(memory); + *n_size_cells = prom_n_size_cells(memory); + of_node_put(memory); + } + /* if (!memory) we are in trouble, let other code error out */ } static unsigned long __init read_n_cells(int n, unsigned int **buf) @@ -386,7 +374,7 @@ static int __init parse_numa_properties( { struct device_node *cpu = NULL; struct device_node *memory = NULL; - int addr_cells, size_cells; + int n_addr_cells, n_size_cells; int max_domain; unsigned long i; @@ -425,8 +413,7 @@ static int __init parse_numa_properties( } } - addr_cells = get_mem_addr_cells(); - size_cells = get_mem_size_cells(); + get_n_mem_cells(&n_addr_cells, &n_size_cells); memory = NULL; while ((memory = of_find_node_by_type(memory, "memory")) != NULL) { unsigned long start; @@ -443,8 +430,8 @@ static int __init parse_numa_properties( ranges = memory->n_addrs; new_range: /* these are order-sensitive, and modify the buffer pointer */ - start = read_n_cells(addr_cells, &memcell_buf); - size = read_n_cells(size_cells, &memcell_buf); + start = read_n_cells(n_addr_cells, &memcell_buf); + size = read_n_cells(n_size_cells, &memcell_buf); numa_domain = of_node_numa_domain(memory); diff --git a/arch/powerpc/mm/pgtable_64.c b/arch/powerpc/mm/pgtable_64.c index 2ffca63..7b278d8 100644 --- a/arch/powerpc/mm/pgtable_64.c +++ b/arch/powerpc/mm/pgtable_64.c @@ -174,7 +174,7 @@ void __iomem * __ioremap(unsigned long a pa = addr & PAGE_MASK; size = PAGE_ALIGN(addr + size) - pa; - if (size == 0) + if ((size == 0) || (pa == 0)) return NULL; if (mem_init_done) { diff --git a/arch/powerpc/platforms/cell/Kconfig b/arch/powerpc/platforms/cell/Kconfig new file mode 100644 index 0000000..3157071 --- /dev/null +++ b/arch/powerpc/platforms/cell/Kconfig @@ -0,0 +1,13 @@ +menu "Cell Broadband Engine options" + depends on PPC_CELL + +config SPU_FS + tristate "SPU file system" + default m + depends on PPC_CELL + help + The SPU file system is used to access Synergistic Processing + Units on machines implementing the Broadband Processor + Architecture. + +endmenu diff --git a/arch/powerpc/platforms/cell/Makefile b/arch/powerpc/platforms/cell/Makefile index 55e094b..74616cf 100644 --- a/arch/powerpc/platforms/cell/Makefile +++ b/arch/powerpc/platforms/cell/Makefile @@ -1,2 +1,5 @@ obj-y += interrupt.o iommu.o setup.o spider-pic.o obj-$(CONFIG_SMP) += smp.o +obj-$(CONFIG_SPU_FS) += spufs/ spu_base.o +builtin-spufs-$(CONFIG_SPU_FS) += spu_syscalls.o +obj-y += $(builtin-spufs-m) diff --git a/arch/powerpc/platforms/cell/setup.c b/arch/powerpc/platforms/cell/setup.c index 9a49563..25e0f68 100644 --- a/arch/powerpc/platforms/cell/setup.c +++ b/arch/powerpc/platforms/cell/setup.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -67,6 +68,77 @@ void cell_show_cpuinfo(struct seq_file * of_node_put(root); } +#ifdef CONFIG_SPARSEMEM +static int __init find_spu_node_id(struct device_node *spe) +{ + unsigned int *id; +#ifdef CONFIG_NUMA + struct device_node *cpu; + cpu = spe->parent->parent; + id = (unsigned int *)get_property(cpu, "node-id", NULL); +#else + id = NULL; +#endif + return id ? *id : 0; +} + +static void __init cell_spuprop_present(struct device_node *spe, + const char *prop, int early) +{ + struct address_prop { + unsigned long address; + unsigned int len; + } __attribute__((packed)) *p; + int proplen; + + unsigned long start_pfn, end_pfn, pfn; + int node_id; + + p = (void*)get_property(spe, prop, &proplen); + WARN_ON(proplen != sizeof (*p)); + + node_id = find_spu_node_id(spe); + + start_pfn = p->address >> PAGE_SHIFT; + end_pfn = (p->address + p->len + PAGE_SIZE - 1) >> PAGE_SHIFT; + + /* We need to call memory_present *before* the call to sparse_init, + but we can initialize the page structs only *after* that call. + Thus, we're being called twice. */ + if (early) + memory_present(node_id, start_pfn, end_pfn); + else { + /* As the pages backing SPU LS and I/O are outside the range + of regular memory, their page structs were not initialized + by free_area_init. Do it here instead. */ + for (pfn = start_pfn; pfn < end_pfn; pfn++) { + struct page *page = pfn_to_page(pfn); + set_page_links(page, ZONE_DMA, node_id, pfn); + set_page_count(page, 0); + reset_page_mapcount(page); + SetPageReserved(page); + INIT_LIST_HEAD(&page->lru); + } + } +} + +static void __init cell_spumem_init(int early) +{ + struct device_node *node; + for (node = of_find_node_by_type(NULL, "spe"); + node; node = of_find_node_by_type(node, "spe")) { + cell_spuprop_present(node, "local-store", early); + cell_spuprop_present(node, "problem", early); + cell_spuprop_present(node, "priv1", early); + cell_spuprop_present(node, "priv2", early); + } +} +#else +static void __init cell_spumem_init(int early) +{ +} +#endif + static void cell_progress(char *s, unsigned short hex) { printk("*** %04x : %s\n", hex, s ? s : ""); @@ -98,6 +170,8 @@ static void __init cell_setup_arch(void) #endif mmio_nvram_init(); + + cell_spumem_init(0); } /* @@ -113,6 +187,8 @@ static void __init cell_init_early(void) ppc64_interrupt_controller = IC_CELL_PIC; + cell_spumem_init(1); + DBG(" <- cell_init_early()\n"); } @@ -138,4 +214,8 @@ struct machdep_calls __initdata cell_md .set_rtc_time = rtas_set_rtc_time, .calibrate_decr = generic_calibrate_decr, .progress = cell_progress, +#ifdef CONFIG_KEXEC + .machine_kexec = default_machine_kexec, + .machine_kexec_prepare = default_machine_kexec_prepare, +#endif }; diff --git a/arch/powerpc/platforms/cell/spu_base.c b/arch/powerpc/platforms/cell/spu_base.c new file mode 100644 index 0000000..408c455 --- /dev/null +++ b/arch/powerpc/platforms/cell/spu_base.c @@ -0,0 +1,775 @@ +/* + * Low-level SPU handling + * + * (C) Copyright IBM Deutschland Entwicklung GmbH 2005 + * + * Author: Arnd Bergmann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#define DEBUG 1 + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "interrupt.h" + +static int __spu_trap_invalid_dma(struct spu *spu) +{ + pr_debug("%s\n", __FUNCTION__); + force_sig(SIGBUS, /* info, */ current); + return 0; +} + +static int __spu_trap_dma_align(struct spu *spu) +{ + pr_debug("%s\n", __FUNCTION__); + force_sig(SIGBUS, /* info, */ current); + return 0; +} + +static int __spu_trap_error(struct spu *spu) +{ + pr_debug("%s\n", __FUNCTION__); + force_sig(SIGILL, /* info, */ current); + return 0; +} + +static void spu_restart_dma(struct spu *spu) +{ + struct spu_priv2 __iomem *priv2 = spu->priv2; + + if (!test_bit(SPU_CONTEXT_SWITCH_PENDING_nr, &spu->flags)) + out_be64(&priv2->mfc_control_RW, MFC_CNTL_RESTART_DMA_COMMAND); +} + +static int __spu_trap_data_seg(struct spu *spu, unsigned long ea) +{ + struct spu_priv2 __iomem *priv2 = spu->priv2; + struct mm_struct *mm = spu->mm; + u64 esid, vsid; + + pr_debug("%s\n", __FUNCTION__); + + if (test_bit(SPU_CONTEXT_SWITCH_ACTIVE_nr, &spu->flags)) { + /* SLBs are pre-loaded for context switch, so + * we should never get here! + */ + printk("%s: invalid access during switch!\n", __func__); + return 1; + } + if (!mm || (REGION_ID(ea) != USER_REGION_ID)) { + /* Future: support kernel segments so that drivers + * can use SPUs. + */ + pr_debug("invalid region access at %016lx\n", ea); + return 1; + } + + esid = (ea & ESID_MASK) | SLB_ESID_V; + vsid = (get_vsid(mm->context.id, ea) << SLB_VSID_SHIFT) | SLB_VSID_USER; + if (in_hugepage_area(mm->context, ea)) + vsid |= SLB_VSID_L; + + out_be64(&priv2->slb_index_W, spu->slb_replace); + out_be64(&priv2->slb_vsid_RW, vsid); + out_be64(&priv2->slb_esid_RW, esid); + + spu->slb_replace++; + if (spu->slb_replace >= 8) + spu->slb_replace = 0; + + spu_restart_dma(spu); + + return 0; +} + +extern int hash_page(unsigned long ea, unsigned long access, unsigned long trap); //XXX +static int __spu_trap_data_map(struct spu *spu, unsigned long ea, u64 dsisr) +{ + pr_debug("%s\n", __FUNCTION__); + + /* Handle kernel space hash faults immediately. + User hash faults need to be deferred to process context. */ + if ((dsisr & MFC_DSISR_PTE_NOT_FOUND) + && REGION_ID(ea) != USER_REGION_ID + && hash_page(ea, _PAGE_PRESENT, 0x300) == 0) { + spu_restart_dma(spu); + return 0; + } + + if (test_bit(SPU_CONTEXT_SWITCH_ACTIVE_nr, &spu->flags)) { + printk("%s: invalid access during switch!\n", __func__); + return 1; + } + + spu->dar = ea; + spu->dsisr = dsisr; + mb(); + wake_up(&spu->stop_wq); + return 0; +} + +static int __spu_trap_mailbox(struct spu *spu) +{ + if (spu->ibox_callback) + spu->ibox_callback(spu); + + /* atomically disable SPU mailbox interrupts */ + spin_lock(&spu->register_lock); + out_be64(&spu->priv1->int_mask_class2_RW, + in_be64(&spu->priv1->int_mask_class2_RW) & ~0x1); + spin_unlock(&spu->register_lock); + return 0; +} + +static int __spu_trap_stop(struct spu *spu) +{ + pr_debug("%s\n", __FUNCTION__); + spu->stop_code = in_be32(&spu->problem->spu_status_R); + wake_up(&spu->stop_wq); + return 0; +} + +static int __spu_trap_halt(struct spu *spu) +{ + pr_debug("%s\n", __FUNCTION__); + spu->stop_code = in_be32(&spu->problem->spu_status_R); + wake_up(&spu->stop_wq); + return 0; +} + +static int __spu_trap_tag_group(struct spu *spu) +{ + pr_debug("%s\n", __FUNCTION__); + /* wake_up(&spu->dma_wq); */ + return 0; +} + +static int __spu_trap_spubox(struct spu *spu) +{ + if (spu->wbox_callback) + spu->wbox_callback(spu); + + /* atomically disable SPU mailbox interrupts */ + spin_lock(&spu->register_lock); + out_be64(&spu->priv1->int_mask_class2_RW, + in_be64(&spu->priv1->int_mask_class2_RW) & ~0x10); + spin_unlock(&spu->register_lock); + return 0; +} + +static irqreturn_t +spu_irq_class_0(int irq, void *data, struct pt_regs *regs) +{ + struct spu *spu; + + spu = data; + spu->class_0_pending = 1; + wake_up(&spu->stop_wq); + + return IRQ_HANDLED; +} + +static int +spu_irq_class_0_bottom(struct spu *spu) +{ + unsigned long stat; + + spu->class_0_pending = 0; + + stat = in_be64(&spu->priv1->int_stat_class0_RW); + + if (stat & 1) /* invalid MFC DMA */ + __spu_trap_invalid_dma(spu); + + if (stat & 2) /* invalid DMA alignment */ + __spu_trap_dma_align(spu); + + if (stat & 4) /* error on SPU */ + __spu_trap_error(spu); + + out_be64(&spu->priv1->int_stat_class0_RW, stat); + return 0; +} + +static irqreturn_t +spu_irq_class_1(int irq, void *data, struct pt_regs *regs) +{ + struct spu *spu; + unsigned long stat, mask, dar, dsisr; + + spu = data; + + /* atomically read & clear class1 status. */ + spin_lock(&spu->register_lock); + mask = in_be64(&spu->priv1->int_mask_class1_RW); + stat = in_be64(&spu->priv1->int_stat_class1_RW) & mask; + dar = in_be64(&spu->priv1->mfc_dar_RW); + dsisr = in_be64(&spu->priv1->mfc_dsisr_RW); + out_be64(&spu->priv1->mfc_dsisr_RW, 0UL); + out_be64(&spu->priv1->int_stat_class1_RW, stat); + spin_unlock(&spu->register_lock); + + if (stat & 1) /* segment fault */ + __spu_trap_data_seg(spu, dar); + + if (stat & 2) { /* mapping fault */ + __spu_trap_data_map(spu, dar, dsisr); + } + + if (stat & 4) /* ls compare & suspend on get */ + ; + + if (stat & 8) /* ls compare & suspend on put */ + ; + + return stat ? IRQ_HANDLED : IRQ_NONE; +} + +static irqreturn_t +spu_irq_class_2(int irq, void *data, struct pt_regs *regs) +{ + struct spu *spu; + unsigned long stat; + + spu = data; + stat = in_be64(&spu->priv1->int_stat_class2_RW); + + pr_debug("class 2 interrupt %d, %lx, %lx\n", irq, stat, + in_be64(&spu->priv1->int_mask_class2_RW)); + + + if (stat & 1) /* PPC core mailbox */ + __spu_trap_mailbox(spu); + + if (stat & 2) /* SPU stop-and-signal */ + __spu_trap_stop(spu); + + if (stat & 4) /* SPU halted */ + __spu_trap_halt(spu); + + if (stat & 8) /* DMA tag group complete */ + __spu_trap_tag_group(spu); + + if (stat & 0x10) /* SPU mailbox threshold */ + __spu_trap_spubox(spu); + + out_be64(&spu->priv1->int_stat_class2_RW, stat); + return stat ? IRQ_HANDLED : IRQ_NONE; +} + +static int +spu_request_irqs(struct spu *spu) +{ + int ret; + int irq_base; + + irq_base = IIC_NODE_STRIDE * spu->node + IIC_SPE_OFFSET; + + snprintf(spu->irq_c0, sizeof (spu->irq_c0), "spe%02d.0", spu->number); + ret = request_irq(irq_base + spu->isrc, + spu_irq_class_0, 0, spu->irq_c0, spu); + if (ret) + goto out; + out_be64(&spu->priv1->int_mask_class0_RW, 0x7); + + snprintf(spu->irq_c1, sizeof (spu->irq_c1), "spe%02d.1", spu->number); + ret = request_irq(irq_base + IIC_CLASS_STRIDE + spu->isrc, + spu_irq_class_1, 0, spu->irq_c1, spu); + if (ret) + goto out1; + out_be64(&spu->priv1->int_mask_class1_RW, 0x3); + + snprintf(spu->irq_c2, sizeof (spu->irq_c2), "spe%02d.2", spu->number); + ret = request_irq(irq_base + 2*IIC_CLASS_STRIDE + spu->isrc, + spu_irq_class_2, 0, spu->irq_c2, spu); + if (ret) + goto out2; + out_be64(&spu->priv1->int_mask_class2_RW, 0xe); + goto out; + +out2: + free_irq(irq_base + IIC_CLASS_STRIDE + spu->isrc, spu); +out1: + free_irq(irq_base + spu->isrc, spu); +out: + return ret; +} + +static void +spu_free_irqs(struct spu *spu) +{ + int irq_base; + + irq_base = IIC_NODE_STRIDE * spu->node + IIC_SPE_OFFSET; + + free_irq(irq_base + spu->isrc, spu); + free_irq(irq_base + IIC_CLASS_STRIDE + spu->isrc, spu); + free_irq(irq_base + 2*IIC_CLASS_STRIDE + spu->isrc, spu); +} + +static LIST_HEAD(spu_list); +static DECLARE_MUTEX(spu_mutex); + +static void spu_init_channels(struct spu *spu) +{ + static const struct { + unsigned channel; + unsigned count; + } zero_list[] = { + { 0x00, 1, }, { 0x01, 1, }, { 0x03, 1, }, { 0x04, 1, }, + { 0x18, 1, }, { 0x19, 1, }, { 0x1b, 1, }, { 0x1d, 1, }, + }, count_list[] = { + { 0x00, 0, }, { 0x03, 0, }, { 0x04, 0, }, { 0x15, 16, }, + { 0x17, 1, }, { 0x18, 0, }, { 0x19, 0, }, { 0x1b, 0, }, + { 0x1c, 1, }, { 0x1d, 0, }, { 0x1e, 1, }, + }; + struct spu_priv2 *priv2; + int i; + + priv2 = spu->priv2; + + /* initialize all channel data to zero */ + for (i = 0; i < ARRAY_SIZE(zero_list); i++) { + int count; + + out_be64(&priv2->spu_chnlcntptr_RW, zero_list[i].channel); + for (count = 0; count < zero_list[i].count; count++) + out_be64(&priv2->spu_chnldata_RW, 0); + } + + /* initialize channel counts to meaningful values */ + for (i = 0; i < ARRAY_SIZE(count_list); i++) { + out_be64(&priv2->spu_chnlcntptr_RW, count_list[i].channel); + out_be64(&priv2->spu_chnlcnt_RW, count_list[i].count); + } +} + +static void spu_init_regs(struct spu *spu) +{ + out_be64(&spu->priv1->int_mask_class0_RW, 0x7); + out_be64(&spu->priv1->int_mask_class1_RW, 0x3); + out_be64(&spu->priv1->int_mask_class2_RW, 0xe); +} + +struct spu *spu_alloc(void) +{ + struct spu *spu; + + down(&spu_mutex); + if (!list_empty(&spu_list)) { + spu = list_entry(spu_list.next, struct spu, list); + list_del_init(&spu->list); + pr_debug("Got SPU %x %d\n", spu->isrc, spu->number); + } else { + pr_debug("No SPU left\n"); + spu = NULL; + } + up(&spu_mutex); + + if (spu) { + spu_init_channels(spu); + spu_init_regs(spu); + } + + return spu; +} +EXPORT_SYMBOL(spu_alloc); + +void spu_free(struct spu *spu) +{ + down(&spu_mutex); + list_add_tail(&spu->list, &spu_list); + up(&spu_mutex); +} +EXPORT_SYMBOL(spu_free); + +static int spu_handle_mm_fault(struct spu *spu) +{ + struct mm_struct *mm = spu->mm; + struct vm_area_struct *vma; + u64 ea, dsisr, is_write; + int ret; + + ea = spu->dar; + dsisr = spu->dsisr; +#if 0 + if (!IS_VALID_EA(ea)) { + return -EFAULT; + } +#endif /* XXX */ + if (mm == NULL) { + return -EFAULT; + } + if (mm->pgd == NULL) { + return -EFAULT; + } + + down_read(&mm->mmap_sem); + vma = find_vma(mm, ea); + if (!vma) + goto bad_area; + if (vma->vm_start <= ea) + goto good_area; + if (!(vma->vm_flags & VM_GROWSDOWN)) + goto bad_area; +#if 0 + if (expand_stack(vma, ea)) + goto bad_area; +#endif /* XXX */ +good_area: + is_write = dsisr & MFC_DSISR_ACCESS_PUT; + if (is_write) { + if (!(vma->vm_flags & VM_WRITE)) + goto bad_area; + } else { + if (dsisr & MFC_DSISR_ACCESS_DENIED) + goto bad_area; + if (!(vma->vm_flags & (VM_READ | VM_EXEC))) + goto bad_area; + } + ret = 0; + switch (handle_mm_fault(mm, vma, ea, is_write)) { + case VM_FAULT_MINOR: + current->min_flt++; + break; + case VM_FAULT_MAJOR: + current->maj_flt++; + break; + case VM_FAULT_SIGBUS: + ret = -EFAULT; + goto bad_area; + case VM_FAULT_OOM: + ret = -ENOMEM; + goto bad_area; + default: + BUG(); + } + up_read(&mm->mmap_sem); + return ret; + +bad_area: + up_read(&mm->mmap_sem); + return -EFAULT; +} + +static int spu_handle_pte_fault(struct spu *spu) +{ + u64 ea, dsisr, access, error = 0UL; + int ret = 0; + + ea = spu->dar; + dsisr = spu->dsisr; + if (dsisr & MFC_DSISR_PTE_NOT_FOUND) { + access = (_PAGE_PRESENT | _PAGE_USER); + access |= (dsisr & MFC_DSISR_ACCESS_PUT) ? _PAGE_RW : 0UL; + if (hash_page(ea, access, 0x300) != 0) + error |= CLASS1_ENABLE_STORAGE_FAULT_INTR; + } + if ((error & CLASS1_ENABLE_STORAGE_FAULT_INTR) || + (dsisr & MFC_DSISR_ACCESS_DENIED)) { + if ((ret = spu_handle_mm_fault(spu)) != 0) + error |= CLASS1_ENABLE_STORAGE_FAULT_INTR; + else + error &= ~CLASS1_ENABLE_STORAGE_FAULT_INTR; + } + spu->dar = 0UL; + spu->dsisr = 0UL; + if (!error) { + spu_restart_dma(spu); + } else { + __spu_trap_invalid_dma(spu); + } + return ret; +} + +static inline int spu_pending(struct spu *spu, u32 * stat) +{ + struct spu_problem __iomem *prob = spu->problem; + u64 pte_fault; + + *stat = in_be32(&prob->spu_status_R); + pte_fault = spu->dsisr & + (MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED); + return (!(*stat & 0x1) || pte_fault || spu->class_0_pending) ? 1 : 0; +} + +int spu_run(struct spu *spu) +{ + struct spu_problem __iomem *prob; + struct spu_priv1 __iomem *priv1; + struct spu_priv2 __iomem *priv2; + u32 status; + int ret; + + prob = spu->problem; + priv1 = spu->priv1; + priv2 = spu->priv2; + + /* Let SPU run. */ + eieio(); + out_be32(&prob->spu_runcntl_RW, SPU_RUNCNTL_RUNNABLE); + + do { + ret = wait_event_interruptible(spu->stop_wq, + spu_pending(spu, &status)); + + if (spu->dsisr & + (MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED)) + ret = spu_handle_pte_fault(spu); + + if (spu->class_0_pending) + spu_irq_class_0_bottom(spu); + + if (!ret && signal_pending(current)) + ret = -ERESTARTSYS; + + } while (!ret && !(status & + (SPU_STATUS_STOPPED_BY_STOP | + SPU_STATUS_STOPPED_BY_HALT))); + + /* Ensure SPU is stopped. */ + out_be32(&prob->spu_runcntl_RW, SPU_RUNCNTL_STOP); + eieio(); + while (in_be32(&prob->spu_status_R) & SPU_STATUS_RUNNING) + cpu_relax(); + + out_be64(&priv2->slb_invalidate_all_W, 0); + out_be64(&priv1->tlb_invalidate_entry_W, 0UL); + eieio(); + + /* Check for SPU breakpoint. */ + if (unlikely(current->ptrace & PT_PTRACED)) { + status = in_be32(&prob->spu_status_R); + + if ((status & SPU_STATUS_STOPPED_BY_STOP) + && status >> SPU_STOP_STATUS_SHIFT == 0x3fff) { + force_sig(SIGTRAP, current); + ret = -ERESTARTSYS; + } + } + + return ret; +} +EXPORT_SYMBOL(spu_run); + +static void __iomem * __init map_spe_prop(struct device_node *n, + const char *name) +{ + struct address_prop { + unsigned long address; + unsigned int len; + } __attribute__((packed)) *prop; + + void *p; + int proplen; + + p = get_property(n, name, &proplen); + if (proplen != sizeof (struct address_prop)) + return NULL; + + prop = p; + + return ioremap(prop->address, prop->len); +} + +static void spu_unmap(struct spu *spu) +{ + iounmap(spu->priv2); + iounmap(spu->priv1); + iounmap(spu->problem); + iounmap((u8 __iomem *)spu->local_store); +} + +static int __init spu_map_device(struct spu *spu, struct device_node *spe) +{ + char *prop; + int ret; + + ret = -ENODEV; + prop = get_property(spe, "isrc", NULL); + if (!prop) + goto out; + spu->isrc = *(unsigned int *)prop; + + spu->name = get_property(spe, "name", NULL); + if (!spu->name) + goto out; + + prop = get_property(spe, "local-store", NULL); + if (!prop) + goto out; + spu->local_store_phys = *(unsigned long *)prop; + + /* we use local store as ram, not io memory */ + spu->local_store = (void __force *)map_spe_prop(spe, "local-store"); + if (!spu->local_store) + goto out; + + spu->problem= map_spe_prop(spe, "problem"); + if (!spu->problem) + goto out_unmap; + + spu->priv1= map_spe_prop(spe, "priv1"); + if (!spu->priv1) + goto out_unmap; + + spu->priv2= map_spe_prop(spe, "priv2"); + if (!spu->priv2) + goto out_unmap; + ret = 0; + goto out; + +out_unmap: + spu_unmap(spu); +out: + return ret; +} + +static int __init find_spu_node_id(struct device_node *spe) +{ + unsigned int *id; + struct device_node *cpu; + + cpu = spe->parent->parent; + id = (unsigned int *)get_property(cpu, "node-id", NULL); + + return id ? *id : 0; +} + +static int __init create_spu(struct device_node *spe) +{ + struct spu *spu; + int ret; + static int number; + + ret = -ENOMEM; + spu = kmalloc(sizeof (*spu), GFP_KERNEL); + if (!spu) + goto out; + + ret = spu_map_device(spu, spe); + if (ret) + goto out_free; + + spu->node = find_spu_node_id(spe); + spu->stop_code = 0; + spu->slb_replace = 0; + spu->mm = NULL; + spu->ctx = NULL; + spu->rq = NULL; + spu->pid = 0; + spu->class_0_pending = 0; + spu->flags = 0UL; + spu->dar = 0UL; + spu->dsisr = 0UL; + spin_lock_init(&spu->register_lock); + + out_be64(&spu->priv1->mfc_sdr_RW, mfspr(SPRN_SDR1)); + out_be64(&spu->priv1->mfc_sr1_RW, 0x33); + + init_waitqueue_head(&spu->stop_wq); + spu->ibox_callback = NULL; + spu->wbox_callback = NULL; + + down(&spu_mutex); + spu->number = number++; + ret = spu_request_irqs(spu); + if (ret) + goto out_unmap; + + list_add(&spu->list, &spu_list); + up(&spu_mutex); + + pr_debug(KERN_DEBUG "Using SPE %s %02x %p %p %p %p %d\n", + spu->name, spu->isrc, spu->local_store, + spu->problem, spu->priv1, spu->priv2, spu->number); + goto out; + +out_unmap: + up(&spu_mutex); + spu_unmap(spu); +out_free: + kfree(spu); +out: + return ret; +} + +static void destroy_spu(struct spu *spu) +{ + list_del_init(&spu->list); + + spu_free_irqs(spu); + spu_unmap(spu); + kfree(spu); +} + +static void cleanup_spu_base(void) +{ + struct spu *spu, *tmp; + down(&spu_mutex); + list_for_each_entry_safe(spu, tmp, &spu_list, list) + destroy_spu(spu); + up(&spu_mutex); +} +module_exit(cleanup_spu_base); + +static int __init init_spu_base(void) +{ + struct device_node *node; + int ret; + + ret = -ENODEV; + for (node = of_find_node_by_type(NULL, "spe"); + node; node = of_find_node_by_type(node, "spe")) { + ret = create_spu(node); + if (ret) { + printk(KERN_WARNING "%s: Error initializing %s\n", + __FUNCTION__, node->name); + cleanup_spu_base(); + break; + } + } + /* in some old firmware versions, the spe is called 'spc', so we + look for that as well */ + for (node = of_find_node_by_type(NULL, "spc"); + node; node = of_find_node_by_type(node, "spc")) { + ret = create_spu(node); + if (ret) { + printk(KERN_WARNING "%s: Error initializing %s\n", + __FUNCTION__, node->name); + cleanup_spu_base(); + break; + } + } + return ret; +} +module_init(init_spu_base); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Arnd Bergmann "); diff --git a/arch/powerpc/platforms/cell/spu_syscalls.c b/arch/powerpc/platforms/cell/spu_syscalls.c new file mode 100644 index 0000000..43e0b18 --- /dev/null +++ b/arch/powerpc/platforms/cell/spu_syscalls.c @@ -0,0 +1,86 @@ +/* + * SPU file system -- system call stubs + * + * (C) Copyright IBM Deutschland Entwicklung GmbH 2005 + * + * Author: Arnd Bergmann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include +#include +#include + +#include + +struct spufs_calls spufs_calls = { + .owner = NULL, +}; + +/* These stub syscalls are needed to have the actual implementation + * within a loadable module. When spufs is built into the kernel, + * this file is not used and the syscalls directly enter the fs code */ + +asmlinkage long sys_spu_create(const char __user *name, + unsigned int flags, mode_t mode) +{ + long ret; + + ret = -ENOSYS; + if (try_module_get(spufs_calls.owner)) { + ret = spufs_calls.create_thread(name, flags, mode); + module_put(spufs_calls.owner); + } + return ret; +} + +asmlinkage long sys_spu_run(int fd, __u32 __user *unpc, __u32 __user *ustatus) +{ + long ret; + struct file *filp; + int fput_needed; + + ret = -ENOSYS; + if (try_module_get(spufs_calls.owner)) { + ret = -EBADF; + filp = fget_light(fd, &fput_needed); + if (filp) { + ret = spufs_calls.spu_run(filp, unpc, ustatus); + fput_light(filp, fput_needed); + } + module_put(spufs_calls.owner); + } + return ret; +} + +int register_spu_syscalls(struct spufs_calls *calls) +{ + if (spufs_calls.owner) + return -EBUSY; + + spufs_calls.create_thread = calls->create_thread; + spufs_calls.spu_run = calls->spu_run; + smp_mb(); + spufs_calls.owner = calls->owner; + return 0; +} +EXPORT_SYMBOL_GPL(register_spu_syscalls); + +void unregister_spu_syscalls(struct spufs_calls *calls) +{ + BUG_ON(spufs_calls.owner != calls->owner); + spufs_calls.owner = NULL; +} +EXPORT_SYMBOL_GPL(unregister_spu_syscalls); diff --git a/arch/powerpc/platforms/cell/spufs/Makefile b/arch/powerpc/platforms/cell/spufs/Makefile new file mode 100644 index 0000000..ac86b25 --- /dev/null +++ b/arch/powerpc/platforms/cell/spufs/Makefile @@ -0,0 +1,54 @@ +obj-$(CONFIG_SPU_FS) += spufs.o +spufs-y += inode.o file.o context.o switch.o syscalls.o +spufs-y += sched.o backing_ops.o hw_ops.o + +# Rules to build switch.o with the help of SPU tool chain +SPU_CROSS := spu- +SPU_CC := $(SPU_CROSS)gcc +SPU_AS := $(SPU_CROSS)gcc +SPU_LD := $(SPU_CROSS)ld +SPU_OBJCOPY := $(SPU_CROSS)objcopy +SPU_CFLAGS := -O2 -Wall -I$(srctree)/include -I$(objtree)/include2 +SPU_AFLAGS := -c -D__ASSEMBLY__ -I$(srctree)/include -I$(objtree)/include2 +SPU_LDFLAGS := -N -Ttext=0x0 + +$(obj)/switch.o: $(obj)/spu_save_dump.h $(obj)/spu_restore_dump.h + +# Compile SPU files + cmd_spu_cc = $(SPU_CC) $(SPU_CFLAGS) -c -o $@ $< +quiet_cmd_spu_cc = SPU_CC $@ +$(obj)/spu_%.o: $(src)/spu_%.c + $(call if_changed,spu_cc) + +# Assemble SPU files + cmd_spu_as = $(SPU_AS) $(SPU_AFLAGS) -o $@ $< +quiet_cmd_spu_as = SPU_AS $@ +$(obj)/spu_%.o: $(src)/spu_%.S + $(call if_changed,spu_as) + +# Link SPU Executables + cmd_spu_ld = $(SPU_LD) $(SPU_LDFLAGS) -o $@ $^ +quiet_cmd_spu_ld = SPU_LD $@ +$(obj)/spu_%: $(obj)/spu_%_crt0.o $(obj)/spu_%.o + $(call if_changed,spu_ld) + +# Copy into binary format + cmd_spu_objcopy = $(SPU_OBJCOPY) -O binary $< $@ +quiet_cmd_spu_objcopy = OBJCOPY $@ +$(obj)/spu_%.bin: $(src)/spu_% + $(call if_changed,spu_objcopy) + +# create C code from ELF executable +cmd_hexdump = ( \ + echo "/*" ; \ + echo " * $*_dump.h: Copyright (C) 2005 IBM." ; \ + echo " * Hex-dump auto generated from $*.c." ; \ + echo " * Do not edit!" ; \ + echo " */" ; \ + echo "static unsigned int $*_code[] __page_aligned = {" ; \ + hexdump -v -e '4/4 "0x%08x, " "\n"' $< ; \ + echo "};" ; \ + ) > $@ +quiet_cmd_hexdump = HEXDUMP $@ +$(obj)/%_dump.h: $(obj)/%.bin + $(call if_changed,hexdump) diff --git a/arch/powerpc/platforms/cell/spufs/backing_ops.c b/arch/powerpc/platforms/cell/spufs/backing_ops.c new file mode 100644 index 0000000..caf0984 --- /dev/null +++ b/arch/powerpc/platforms/cell/spufs/backing_ops.c @@ -0,0 +1,252 @@ +/* backing_ops.c - query/set operations on saved SPU context. + * + * Copyright (C) IBM 2005 + * Author: Mark Nutter + * + * These register operations allow SPUFS to operate on saved + * SPU contexts rather than hardware. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include "spufs.h" + +/* + * Reads/writes to various problem and priv2 registers require + * state changes, i.e. generate SPU events, modify channel + * counts, etc. + */ + +static void gen_spu_event(struct spu_context *ctx, u32 event) +{ + u64 ch0_cnt; + u64 ch0_data; + u64 ch1_data; + + ch0_cnt = ctx->csa.spu_chnlcnt_RW[0]; + ch0_data = ctx->csa.spu_chnldata_RW[0]; + ch1_data = ctx->csa.spu_chnldata_RW[1]; + ctx->csa.spu_chnldata_RW[0] |= event; + if ((ch0_cnt == 0) && !(ch0_data & event) && (ch1_data & event)) { + ctx->csa.spu_chnlcnt_RW[0] = 1; + } +} + +static int spu_backing_mbox_read(struct spu_context *ctx, u32 * data) +{ + u32 mbox_stat; + int ret = 0; + + spin_lock(&ctx->csa.register_lock); + mbox_stat = ctx->csa.prob.mb_stat_R; + if (mbox_stat & 0x0000ff) { + /* Read the first available word. + * Implementation note: the depth + * of pu_mb_R is currently 1. + */ + *data = ctx->csa.prob.pu_mb_R; + ctx->csa.prob.mb_stat_R &= ~(0x0000ff); + ctx->csa.spu_chnlcnt_RW[28] = 1; + gen_spu_event(ctx, MFC_PU_MAILBOX_AVAILABLE_EVENT); + ret = 4; + } + spin_unlock(&ctx->csa.register_lock); + return ret; +} + +static u32 spu_backing_mbox_stat_read(struct spu_context *ctx) +{ + return ctx->csa.prob.mb_stat_R; +} + +static int spu_backing_ibox_read(struct spu_context *ctx, u32 * data) +{ + int ret; + + spin_lock(&ctx->csa.register_lock); + if (ctx->csa.prob.mb_stat_R & 0xff0000) { + /* Read the first available word. + * Implementation note: the depth + * of puint_mb_R is currently 1. + */ + *data = ctx->csa.priv2.puint_mb_R; + ctx->csa.prob.mb_stat_R &= ~(0xff0000); + ctx->csa.spu_chnlcnt_RW[30] = 1; + gen_spu_event(ctx, MFC_PU_INT_MAILBOX_AVAILABLE_EVENT); + ret = 4; + } else { + /* make sure we get woken up by the interrupt */ + ctx->csa.priv1.int_mask_class2_RW |= 0x1UL; + ret = 0; + } + spin_unlock(&ctx->csa.register_lock); + return ret; +} + +static int spu_backing_wbox_write(struct spu_context *ctx, u32 data) +{ + int ret; + + spin_lock(&ctx->csa.register_lock); + if ((ctx->csa.prob.mb_stat_R) & 0x00ff00) { + int slot = ctx->csa.spu_chnlcnt_RW[29]; + int avail = (ctx->csa.prob.mb_stat_R & 0x00ff00) >> 8; + + /* We have space to write wbox_data. + * Implementation note: the depth + * of spu_mb_W is currently 4. + */ + BUG_ON(avail != (4 - slot)); + ctx->csa.spu_mailbox_data[slot] = data; + ctx->csa.spu_chnlcnt_RW[29] = ++slot; + ctx->csa.prob.mb_stat_R = (((4 - slot) & 0xff) << 8); + gen_spu_event(ctx, MFC_SPU_MAILBOX_WRITTEN_EVENT); + ret = 4; + } else { + /* make sure we get woken up by the interrupt when space + becomes available */ + ctx->csa.priv1.int_mask_class2_RW |= 0x10; + ret = 0; + } + spin_unlock(&ctx->csa.register_lock); + return ret; +} + +static u32 spu_backing_signal1_read(struct spu_context *ctx) +{ + return ctx->csa.spu_chnldata_RW[3]; +} + +static void spu_backing_signal1_write(struct spu_context *ctx, u32 data) +{ + spin_lock(&ctx->csa.register_lock); + if (ctx->csa.priv2.spu_cfg_RW & 0x1) + ctx->csa.spu_chnldata_RW[3] |= data; + else + ctx->csa.spu_chnldata_RW[3] = data; + ctx->csa.spu_chnlcnt_RW[3] = 1; + gen_spu_event(ctx, MFC_SIGNAL_1_EVENT); + spin_unlock(&ctx->csa.register_lock); +} + +static u32 spu_backing_signal2_read(struct spu_context *ctx) +{ + return ctx->csa.spu_chnldata_RW[4]; +} + +static void spu_backing_signal2_write(struct spu_context *ctx, u32 data) +{ + spin_lock(&ctx->csa.register_lock); + if (ctx->csa.priv2.spu_cfg_RW & 0x2) + ctx->csa.spu_chnldata_RW[4] |= data; + else + ctx->csa.spu_chnldata_RW[4] = data; + ctx->csa.spu_chnlcnt_RW[4] = 1; + gen_spu_event(ctx, MFC_SIGNAL_2_EVENT); + spin_unlock(&ctx->csa.register_lock); +} + +static void spu_backing_signal1_type_set(struct spu_context *ctx, u64 val) +{ + u64 tmp; + + spin_lock(&ctx->csa.register_lock); + tmp = ctx->csa.priv2.spu_cfg_RW; + if (val) + tmp |= 1; + else + tmp &= ~1; + ctx->csa.priv2.spu_cfg_RW = tmp; + spin_unlock(&ctx->csa.register_lock); +} + +static u64 spu_backing_signal1_type_get(struct spu_context *ctx) +{ + return ((ctx->csa.priv2.spu_cfg_RW & 1) != 0); +} + +static void spu_backing_signal2_type_set(struct spu_context *ctx, u64 val) +{ + u64 tmp; + + spin_lock(&ctx->csa.register_lock); + tmp = ctx->csa.priv2.spu_cfg_RW; + if (val) + tmp |= 2; + else + tmp &= ~2; + ctx->csa.priv2.spu_cfg_RW = tmp; + spin_unlock(&ctx->csa.register_lock); +} + +static u64 spu_backing_signal2_type_get(struct spu_context *ctx) +{ + return ((ctx->csa.priv2.spu_cfg_RW & 2) != 0); +} + +static u32 spu_backing_npc_read(struct spu_context *ctx) +{ + return ctx->csa.prob.spu_npc_RW; +} + +static void spu_backing_npc_write(struct spu_context *ctx, u32 val) +{ + ctx->csa.prob.spu_npc_RW = val; +} + +static u32 spu_backing_status_read(struct spu_context *ctx) +{ + return ctx->csa.prob.spu_status_R; +} + +static char *spu_backing_get_ls(struct spu_context *ctx) +{ + return ctx->csa.lscsa->ls; +} + +struct spu_context_ops spu_backing_ops = { + .mbox_read = spu_backing_mbox_read, + .mbox_stat_read = spu_backing_mbox_stat_read, + .ibox_read = spu_backing_ibox_read, + .wbox_write = spu_backing_wbox_write, + .signal1_read = spu_backing_signal1_read, + .signal1_write = spu_backing_signal1_write, + .signal2_read = spu_backing_signal2_read, + .signal2_write = spu_backing_signal2_write, + .signal1_type_set = spu_backing_signal1_type_set, + .signal1_type_get = spu_backing_signal1_type_get, + .signal2_type_set = spu_backing_signal2_type_set, + .signal2_type_get = spu_backing_signal2_type_get, + .npc_read = spu_backing_npc_read, + .npc_write = spu_backing_npc_write, + .status_read = spu_backing_status_read, + .get_ls = spu_backing_get_ls, +}; diff --git a/arch/powerpc/platforms/cell/spufs/context.c b/arch/powerpc/platforms/cell/spufs/context.c new file mode 100644 index 0000000..5d6195f --- /dev/null +++ b/arch/powerpc/platforms/cell/spufs/context.c @@ -0,0 +1,163 @@ +/* + * SPU file system -- SPU context management + * + * (C) Copyright IBM Deutschland Entwicklung GmbH 2005 + * + * Author: Arnd Bergmann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include "spufs.h" + +struct spu_context *alloc_spu_context(struct address_space *local_store) +{ + struct spu_context *ctx; + ctx = kmalloc(sizeof *ctx, GFP_KERNEL); + if (!ctx) + goto out; + /* Binding to physical processor deferred + * until spu_activate(). + */ + spu_init_csa(&ctx->csa); + if (!ctx->csa.lscsa) { + goto out_free; + } + spin_lock_init(&ctx->mmio_lock); + kref_init(&ctx->kref); + init_rwsem(&ctx->state_sema); + init_waitqueue_head(&ctx->ibox_wq); + init_waitqueue_head(&ctx->wbox_wq); + ctx->ibox_fasync = NULL; + ctx->wbox_fasync = NULL; + ctx->state = SPU_STATE_SAVED; + ctx->local_store = local_store; + ctx->spu = NULL; + ctx->ops = &spu_backing_ops; + ctx->owner = get_task_mm(current); + goto out; +out_free: + kfree(ctx); + ctx = NULL; +out: + return ctx; +} + +void destroy_spu_context(struct kref *kref) +{ + struct spu_context *ctx; + ctx = container_of(kref, struct spu_context, kref); + down_write(&ctx->state_sema); + spu_deactivate(ctx); + ctx->ibox_fasync = NULL; + ctx->wbox_fasync = NULL; + up_write(&ctx->state_sema); + spu_fini_csa(&ctx->csa); + kfree(ctx); +} + +struct spu_context * get_spu_context(struct spu_context *ctx) +{ + kref_get(&ctx->kref); + return ctx; +} + +int put_spu_context(struct spu_context *ctx) +{ + return kref_put(&ctx->kref, &destroy_spu_context); +} + +/* give up the mm reference when the context is about to be destroyed */ +void spu_forget(struct spu_context *ctx) +{ + struct mm_struct *mm; + spu_acquire_saved(ctx); + mm = ctx->owner; + ctx->owner = NULL; + mmput(mm); + spu_release(ctx); +} + +void spu_acquire(struct spu_context *ctx) +{ + down_read(&ctx->state_sema); +} + +void spu_release(struct spu_context *ctx) +{ + up_read(&ctx->state_sema); +} + +static void spu_unmap_mappings(struct spu_context *ctx) +{ + unmap_mapping_range(ctx->local_store, 0, LS_SIZE, 1); +} + +int spu_acquire_runnable(struct spu_context *ctx) +{ + int ret = 0; + + down_read(&ctx->state_sema); + if (ctx->state == SPU_STATE_RUNNABLE) + return 0; + /* ctx is about to be freed, can't acquire any more */ + if (!ctx->owner) { + ret = -EINVAL; + goto out; + } + up_read(&ctx->state_sema); + + down_write(&ctx->state_sema); + if (ctx->state == SPU_STATE_SAVED) { + spu_unmap_mappings(ctx); + ret = spu_activate(ctx, 0); + ctx->state = SPU_STATE_RUNNABLE; + } + downgrade_write(&ctx->state_sema); + if (ret) + goto out; + + /* On success, we return holding the lock */ + return ret; +out: + /* Release here, to simplify calling code. */ + up_read(&ctx->state_sema); + + return ret; +} + +void spu_acquire_saved(struct spu_context *ctx) +{ + down_read(&ctx->state_sema); + + if (ctx->state == SPU_STATE_SAVED) + return; + + up_read(&ctx->state_sema); + down_write(&ctx->state_sema); + + if (ctx->state == SPU_STATE_RUNNABLE) { + spu_unmap_mappings(ctx); + spu_deactivate(ctx); + ctx->state = SPU_STATE_SAVED; + } + + downgrade_write(&ctx->state_sema); +} diff --git a/arch/powerpc/platforms/cell/spufs/file.c b/arch/powerpc/platforms/cell/spufs/file.c new file mode 100644 index 0000000..786fdb1 --- /dev/null +++ b/arch/powerpc/platforms/cell/spufs/file.c @@ -0,0 +1,855 @@ +/* + * SPU file system -- file contents + * + * (C) Copyright IBM Deutschland Entwicklung GmbH 2005 + * + * Author: Arnd Bergmann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "spufs.h" + + +static int +spufs_mem_open(struct inode *inode, struct file *file) +{ + struct spufs_inode_info *i = SPUFS_I(inode); + file->private_data = i->i_ctx; + file->f_mapping = i->i_ctx->local_store; + return 0; +} + +static ssize_t +spufs_mem_read(struct file *file, char __user *buffer, + size_t size, loff_t *pos) +{ + struct spu_context *ctx = file->private_data; + char *local_store; + int ret; + + spu_acquire(ctx); + + local_store = ctx->ops->get_ls(ctx); + ret = simple_read_from_buffer(buffer, size, pos, local_store, LS_SIZE); + + spu_release(ctx); + return ret; +} + +static ssize_t +spufs_mem_write(struct file *file, const char __user *buffer, + size_t size, loff_t *pos) +{ + struct spu_context *ctx = file->private_data; + char *local_store; + int ret; + + size = min_t(ssize_t, LS_SIZE - *pos, size); + if (size <= 0) + return -EFBIG; + *pos += size; + + spu_acquire(ctx); + + local_store = ctx->ops->get_ls(ctx); + ret = copy_from_user(local_store + *pos - size, + buffer, size) ? -EFAULT : size; + + spu_release(ctx); + return ret; +} + +#ifdef CONFIG_SPARSEMEM +static struct page * +spufs_mem_mmap_nopage(struct vm_area_struct *vma, + unsigned long address, int *type) +{ + struct page *page = NOPAGE_SIGBUS; + + struct spu_context *ctx = vma->vm_file->private_data; + unsigned long offset = address - vma->vm_start; + offset += vma->vm_pgoff << PAGE_SHIFT; + + spu_acquire(ctx); + + if (ctx->state == SPU_STATE_SAVED) + page = vmalloc_to_page(ctx->csa.lscsa->ls + offset); + else + page = pfn_to_page((ctx->spu->local_store_phys + offset) + >> PAGE_SHIFT); + + spu_release(ctx); + + if (type) + *type = VM_FAULT_MINOR; + + return page; +} + +static struct vm_operations_struct spufs_mem_mmap_vmops = { + .nopage = spufs_mem_mmap_nopage, +}; + +static int +spufs_mem_mmap(struct file *file, struct vm_area_struct *vma) +{ + if (!(vma->vm_flags & VM_SHARED)) + return -EINVAL; + + /* FIXME: */ + vma->vm_flags |= VM_RESERVED; + vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot) + | _PAGE_NO_CACHE); + + vma->vm_ops = &spufs_mem_mmap_vmops; + return 0; +} +#endif + +static struct file_operations spufs_mem_fops = { + .open = spufs_mem_open, + .read = spufs_mem_read, + .write = spufs_mem_write, + .llseek = generic_file_llseek, +#ifdef CONFIG_SPARSEMEM + .mmap = spufs_mem_mmap, +#endif +}; + +static int +spufs_regs_open(struct inode *inode, struct file *file) +{ + struct spufs_inode_info *i = SPUFS_I(inode); + file->private_data = i->i_ctx; + return 0; +} + +static ssize_t +spufs_regs_read(struct file *file, char __user *buffer, + size_t size, loff_t *pos) +{ + struct spu_context *ctx = file->private_data; + struct spu_lscsa *lscsa = ctx->csa.lscsa; + int ret; + + spu_acquire_saved(ctx); + + ret = simple_read_from_buffer(buffer, size, pos, + lscsa->gprs, sizeof lscsa->gprs); + + spu_release(ctx); + return ret; +} + +static ssize_t +spufs_regs_write(struct file *file, const char __user *buffer, + size_t size, loff_t *pos) +{ + struct spu_context *ctx = file->private_data; + struct spu_lscsa *lscsa = ctx->csa.lscsa; + int ret; + + size = min_t(ssize_t, sizeof lscsa->gprs - *pos, size); + if (size <= 0) + return -EFBIG; + *pos += size; + + spu_acquire_saved(ctx); + + ret = copy_from_user(lscsa->gprs + *pos - size, + buffer, size) ? -EFAULT : size; + + spu_release(ctx); + return ret; +} + +static struct file_operations spufs_regs_fops = { + .open = spufs_regs_open, + .read = spufs_regs_read, + .write = spufs_regs_write, + .llseek = generic_file_llseek, +}; + +static ssize_t +spufs_fpcr_read(struct file *file, char __user * buffer, + size_t size, loff_t * pos) +{ + struct spu_context *ctx = file->private_data; + struct spu_lscsa *lscsa = ctx->csa.lscsa; + int ret; + + spu_acquire_saved(ctx); + + ret = simple_read_from_buffer(buffer, size, pos, + &lscsa->fpcr, sizeof(lscsa->fpcr)); + + spu_release(ctx); + return ret; +} + +static ssize_t +spufs_fpcr_write(struct file *file, const char __user * buffer, + size_t size, loff_t * pos) +{ + struct spu_context *ctx = file->private_data; + struct spu_lscsa *lscsa = ctx->csa.lscsa; + int ret; + + size = min_t(ssize_t, sizeof(lscsa->fpcr) - *pos, size); + if (size <= 0) + return -EFBIG; + *pos += size; + + spu_acquire_saved(ctx); + + ret = copy_from_user((char *)&lscsa->fpcr + *pos - size, + buffer, size) ? -EFAULT : size; + + spu_release(ctx); + return ret; +} + +static struct file_operations spufs_fpcr_fops = { + .open = spufs_regs_open, + .read = spufs_fpcr_read, + .write = spufs_fpcr_write, + .llseek = generic_file_llseek, +}; + +/* generic open function for all pipe-like files */ +static int spufs_pipe_open(struct inode *inode, struct file *file) +{ + struct spufs_inode_info *i = SPUFS_I(inode); + file->private_data = i->i_ctx; + + return nonseekable_open(inode, file); +} + +static ssize_t spufs_mbox_read(struct file *file, char __user *buf, + size_t len, loff_t *pos) +{ + struct spu_context *ctx = file->private_data; + u32 mbox_data; + int ret; + + if (len < 4) + return -EINVAL; + + spu_acquire(ctx); + ret = ctx->ops->mbox_read(ctx, &mbox_data); + spu_release(ctx); + + if (!ret) + return -EAGAIN; + + if (copy_to_user(buf, &mbox_data, sizeof mbox_data)) + return -EFAULT; + + return 4; +} + +static struct file_operations spufs_mbox_fops = { + .open = spufs_pipe_open, + .read = spufs_mbox_read, +}; + +static ssize_t spufs_mbox_stat_read(struct file *file, char __user *buf, + size_t len, loff_t *pos) +{ + struct spu_context *ctx = file->private_data; + u32 mbox_stat; + + if (len < 4) + return -EINVAL; + + spu_acquire(ctx); + + mbox_stat = ctx->ops->mbox_stat_read(ctx) & 0xff; + + spu_release(ctx); + + if (copy_to_user(buf, &mbox_stat, sizeof mbox_stat)) + return -EFAULT; + + return 4; +} + +static struct file_operations spufs_mbox_stat_fops = { + .open = spufs_pipe_open, + .read = spufs_mbox_stat_read, +}; + +/* + * spufs_wait + * Same as wait_event_interruptible(), except that here + * we need to call spu_release(ctx) before sleeping, and + * then spu_acquire(ctx) when awoken. + */ + +#define spufs_wait(wq, condition) \ +({ \ + int __ret = 0; \ + DEFINE_WAIT(__wait); \ + for (;;) { \ + prepare_to_wait(&(wq), &__wait, TASK_INTERRUPTIBLE); \ + if (condition) \ + break; \ + if (!signal_pending(current)) { \ + spu_release(ctx); \ + schedule(); \ + spu_acquire(ctx); \ + continue; \ + } \ + __ret = -ERESTARTSYS; \ + break; \ + } \ + finish_wait(&(wq), &__wait); \ + __ret; \ +}) + +/* low-level ibox access function */ +size_t spu_ibox_read(struct spu_context *ctx, u32 *data) +{ + return ctx->ops->ibox_read(ctx, data); +} + +static int spufs_ibox_fasync(int fd, struct file *file, int on) +{ + struct spu_context *ctx = file->private_data; + + return fasync_helper(fd, file, on, &ctx->ibox_fasync); +} + +/* interrupt-level ibox callback function. */ +void spufs_ibox_callback(struct spu *spu) +{ + struct spu_context *ctx = spu->ctx; + + wake_up_all(&ctx->ibox_wq); + kill_fasync(&ctx->ibox_fasync, SIGIO, POLLIN); +} + +static ssize_t spufs_ibox_read(struct file *file, char __user *buf, + size_t len, loff_t *pos) +{ + struct spu_context *ctx = file->private_data; + u32 ibox_data; + ssize_t ret; + + if (len < 4) + return -EINVAL; + + spu_acquire(ctx); + + ret = 0; + if (file->f_flags & O_NONBLOCK) { + if (!spu_ibox_read(ctx, &ibox_data)) + ret = -EAGAIN; + } else { + ret = spufs_wait(ctx->ibox_wq, spu_ibox_read(ctx, &ibox_data)); + } + + spu_release(ctx); + + if (ret) + return ret; + + ret = 4; + if (copy_to_user(buf, &ibox_data, sizeof ibox_data)) + ret = -EFAULT; + + return ret; +} + +static unsigned int spufs_ibox_poll(struct file *file, poll_table *wait) +{ + struct spu_context *ctx = file->private_data; + u32 mbox_stat; + unsigned int mask; + + spu_acquire(ctx); + + mbox_stat = ctx->ops->mbox_stat_read(ctx); + + spu_release(ctx); + + poll_wait(file, &ctx->ibox_wq, wait); + + mask = 0; + if (mbox_stat & 0xff0000) + mask |= POLLIN | POLLRDNORM; + + return mask; +} + +static struct file_operations spufs_ibox_fops = { + .open = spufs_pipe_open, + .read = spufs_ibox_read, + .poll = spufs_ibox_poll, + .fasync = spufs_ibox_fasync, +}; + +static ssize_t spufs_ibox_stat_read(struct file *file, char __user *buf, + size_t len, loff_t *pos) +{ + struct spu_context *ctx = file->private_data; + u32 ibox_stat; + + if (len < 4) + return -EINVAL; + + spu_acquire(ctx); + ibox_stat = (ctx->ops->mbox_stat_read(ctx) >> 16) & 0xff; + spu_release(ctx); + + if (copy_to_user(buf, &ibox_stat, sizeof ibox_stat)) + return -EFAULT; + + return 4; +} + +static struct file_operations spufs_ibox_stat_fops = { + .open = spufs_pipe_open, + .read = spufs_ibox_stat_read, +}; + +/* low-level mailbox write */ +size_t spu_wbox_write(struct spu_context *ctx, u32 data) +{ + return ctx->ops->wbox_write(ctx, data); +} + +static int spufs_wbox_fasync(int fd, struct file *file, int on) +{ + struct spu_context *ctx = file->private_data; + int ret; + + ret = fasync_helper(fd, file, on, &ctx->wbox_fasync); + + return ret; +} + +/* interrupt-level wbox callback function. */ +void spufs_wbox_callback(struct spu *spu) +{ + struct spu_context *ctx = spu->ctx; + + wake_up_all(&ctx->wbox_wq); + kill_fasync(&ctx->wbox_fasync, SIGIO, POLLOUT); +} + +static ssize_t spufs_wbox_write(struct file *file, const char __user *buf, + size_t len, loff_t *pos) +{ + struct spu_context *ctx = file->private_data; + u32 wbox_data; + int ret; + + if (len < 4) + return -EINVAL; + + if (copy_from_user(&wbox_data, buf, sizeof wbox_data)) + return -EFAULT; + + spu_acquire(ctx); + + ret = 0; + if (file->f_flags & O_NONBLOCK) { + if (!spu_wbox_write(ctx, wbox_data)) + ret = -EAGAIN; + } else { + ret = spufs_wait(ctx->wbox_wq, spu_wbox_write(ctx, wbox_data)); + } + + spu_release(ctx); + + return ret ? ret : sizeof wbox_data; +} + +static unsigned int spufs_wbox_poll(struct file *file, poll_table *wait) +{ + struct spu_context *ctx = file->private_data; + u32 mbox_stat; + unsigned int mask; + + spu_acquire(ctx); + mbox_stat = ctx->ops->mbox_stat_read(ctx); + spu_release(ctx); + + poll_wait(file, &ctx->wbox_wq, wait); + + mask = 0; + if (mbox_stat & 0x00ff00) + mask = POLLOUT | POLLWRNORM; + + return mask; +} + +static struct file_operations spufs_wbox_fops = { + .open = spufs_pipe_open, + .write = spufs_wbox_write, + .poll = spufs_wbox_poll, + .fasync = spufs_wbox_fasync, +}; + +static ssize_t spufs_wbox_stat_read(struct file *file, char __user *buf, + size_t len, loff_t *pos) +{ + struct spu_context *ctx = file->private_data; + u32 wbox_stat; + + if (len < 4) + return -EINVAL; + + spu_acquire(ctx); + wbox_stat = (ctx->ops->mbox_stat_read(ctx) >> 8) & 0xff; + spu_release(ctx); + + if (copy_to_user(buf, &wbox_stat, sizeof wbox_stat)) + return -EFAULT; + + return 4; +} + +static struct file_operations spufs_wbox_stat_fops = { + .open = spufs_pipe_open, + .read = spufs_wbox_stat_read, +}; + +long spufs_run_spu(struct file *file, struct spu_context *ctx, + u32 *npc, u32 *status) +{ + int ret; + + ret = spu_acquire_runnable(ctx); + if (ret) + return ret; + + ctx->ops->npc_write(ctx, *npc); + + ret = spu_run(ctx->spu); + + if (!ret) + ret = ctx->ops->status_read(ctx); + + *npc = ctx->ops->npc_read(ctx); + + spu_release(ctx); + spu_yield(ctx); + return ret; +} + +static ssize_t spufs_signal1_read(struct file *file, char __user *buf, + size_t len, loff_t *pos) +{ + struct spu_context *ctx = file->private_data; + u32 data; + + if (len < 4) + return -EINVAL; + + spu_acquire(ctx); + data = ctx->ops->signal1_read(ctx); + spu_release(ctx); + + if (copy_to_user(buf, &data, 4)) + return -EFAULT; + + return 4; +} + +static ssize_t spufs_signal1_write(struct file *file, const char __user *buf, + size_t len, loff_t *pos) +{ + struct spu_context *ctx; + u32 data; + + ctx = file->private_data; + + if (len < 4) + return -EINVAL; + + if (copy_from_user(&data, buf, 4)) + return -EFAULT; + + spu_acquire(ctx); + ctx->ops->signal1_write(ctx, data); + spu_release(ctx); + + return 4; +} + +static struct file_operations spufs_signal1_fops = { + .open = spufs_pipe_open, + .read = spufs_signal1_read, + .write = spufs_signal1_write, +}; + +static ssize_t spufs_signal2_read(struct file *file, char __user *buf, + size_t len, loff_t *pos) +{ + struct spu_context *ctx; + u32 data; + + ctx = file->private_data; + + if (len < 4) + return -EINVAL; + + spu_acquire(ctx); + data = ctx->ops->signal2_read(ctx); + spu_release(ctx); + + if (copy_to_user(buf, &data, 4)) + return -EFAULT; + + return 4; +} + +static ssize_t spufs_signal2_write(struct file *file, const char __user *buf, + size_t len, loff_t *pos) +{ + struct spu_context *ctx; + u32 data; + + ctx = file->private_data; + + if (len < 4) + return -EINVAL; + + if (copy_from_user(&data, buf, 4)) + return -EFAULT; + + spu_acquire(ctx); + ctx->ops->signal2_write(ctx, data); + spu_release(ctx); + + return 4; +} + +static struct file_operations spufs_signal2_fops = { + .open = spufs_pipe_open, + .read = spufs_signal2_read, + .write = spufs_signal2_write, +}; + +static void spufs_signal1_type_set(void *data, u64 val) +{ + struct spu_context *ctx = data; + + spu_acquire(ctx); + ctx->ops->signal1_type_set(ctx, val); + spu_release(ctx); +} + +static u64 spufs_signal1_type_get(void *data) +{ + struct spu_context *ctx = data; + u64 ret; + + spu_acquire(ctx); + ret = ctx->ops->signal1_type_get(ctx); + spu_release(ctx); + + return ret; +} +DEFINE_SIMPLE_ATTRIBUTE(spufs_signal1_type, spufs_signal1_type_get, + spufs_signal1_type_set, "%llu"); + +static void spufs_signal2_type_set(void *data, u64 val) +{ + struct spu_context *ctx = data; + + spu_acquire(ctx); + ctx->ops->signal2_type_set(ctx, val); + spu_release(ctx); +} + +static u64 spufs_signal2_type_get(void *data) +{ + struct spu_context *ctx = data; + u64 ret; + + spu_acquire(ctx); + ret = ctx->ops->signal2_type_get(ctx); + spu_release(ctx); + + return ret; +} +DEFINE_SIMPLE_ATTRIBUTE(spufs_signal2_type, spufs_signal2_type_get, + spufs_signal2_type_set, "%llu"); + +static void spufs_npc_set(void *data, u64 val) +{ + struct spu_context *ctx = data; + spu_acquire(ctx); + ctx->ops->npc_write(ctx, val); + spu_release(ctx); +} + +static u64 spufs_npc_get(void *data) +{ + struct spu_context *ctx = data; + u64 ret; + spu_acquire(ctx); + ret = ctx->ops->npc_read(ctx); + spu_release(ctx); + return ret; +} +DEFINE_SIMPLE_ATTRIBUTE(spufs_npc_ops, spufs_npc_get, spufs_npc_set, "%llx\n") + +static void spufs_decr_set(void *data, u64 val) +{ + struct spu_context *ctx = data; + struct spu_lscsa *lscsa = ctx->csa.lscsa; + spu_acquire_saved(ctx); + lscsa->decr.slot[0] = (u32) val; + spu_release(ctx); +} + +static u64 spufs_decr_get(void *data) +{ + struct spu_context *ctx = data; + struct spu_lscsa *lscsa = ctx->csa.lscsa; + u64 ret; + spu_acquire_saved(ctx); + ret = lscsa->decr.slot[0]; + spu_release(ctx); + return ret; +} +DEFINE_SIMPLE_ATTRIBUTE(spufs_decr_ops, spufs_decr_get, spufs_decr_set, + "%llx\n") + +static void spufs_decr_status_set(void *data, u64 val) +{ + struct spu_context *ctx = data; + struct spu_lscsa *lscsa = ctx->csa.lscsa; + spu_acquire_saved(ctx); + lscsa->decr_status.slot[0] = (u32) val; + spu_release(ctx); +} + +static u64 spufs_decr_status_get(void *data) +{ + struct spu_context *ctx = data; + struct spu_lscsa *lscsa = ctx->csa.lscsa; + u64 ret; + spu_acquire_saved(ctx); + ret = lscsa->decr_status.slot[0]; + spu_release(ctx); + return ret; +} +DEFINE_SIMPLE_ATTRIBUTE(spufs_decr_status_ops, spufs_decr_status_get, + spufs_decr_status_set, "%llx\n") + +static void spufs_spu_tag_mask_set(void *data, u64 val) +{ + struct spu_context *ctx = data; + struct spu_lscsa *lscsa = ctx->csa.lscsa; + spu_acquire_saved(ctx); + lscsa->tag_mask.slot[0] = (u32) val; + spu_release(ctx); +} + +static u64 spufs_spu_tag_mask_get(void *data) +{ + struct spu_context *ctx = data; + struct spu_lscsa *lscsa = ctx->csa.lscsa; + u64 ret; + spu_acquire_saved(ctx); + ret = lscsa->tag_mask.slot[0]; + spu_release(ctx); + return ret; +} +DEFINE_SIMPLE_ATTRIBUTE(spufs_spu_tag_mask_ops, spufs_spu_tag_mask_get, + spufs_spu_tag_mask_set, "%llx\n") + +static void spufs_event_mask_set(void *data, u64 val) +{ + struct spu_context *ctx = data; + struct spu_lscsa *lscsa = ctx->csa.lscsa; + spu_acquire_saved(ctx); + lscsa->event_mask.slot[0] = (u32) val; + spu_release(ctx); +} + +static u64 spufs_event_mask_get(void *data) +{ + struct spu_context *ctx = data; + struct spu_lscsa *lscsa = ctx->csa.lscsa; + u64 ret; + spu_acquire_saved(ctx); + ret = lscsa->event_mask.slot[0]; + spu_release(ctx); + return ret; +} +DEFINE_SIMPLE_ATTRIBUTE(spufs_event_mask_ops, spufs_event_mask_get, + spufs_event_mask_set, "%llx\n") + +static void spufs_srr0_set(void *data, u64 val) +{ + struct spu_context *ctx = data; + struct spu_lscsa *lscsa = ctx->csa.lscsa; + spu_acquire_saved(ctx); + lscsa->srr0.slot[0] = (u32) val; + spu_release(ctx); +} + +static u64 spufs_srr0_get(void *data) +{ + struct spu_context *ctx = data; + struct spu_lscsa *lscsa = ctx->csa.lscsa; + u64 ret; + spu_acquire_saved(ctx); + ret = lscsa->srr0.slot[0]; + spu_release(ctx); + return ret; +} +DEFINE_SIMPLE_ATTRIBUTE(spufs_srr0_ops, spufs_srr0_get, spufs_srr0_set, + "%llx\n") + +struct tree_descr spufs_dir_contents[] = { + { "mem", &spufs_mem_fops, 0666, }, + { "regs", &spufs_regs_fops, 0666, }, + { "mbox", &spufs_mbox_fops, 0444, }, + { "ibox", &spufs_ibox_fops, 0444, }, + { "wbox", &spufs_wbox_fops, 0222, }, + { "mbox_stat", &spufs_mbox_stat_fops, 0444, }, + { "ibox_stat", &spufs_ibox_stat_fops, 0444, }, + { "wbox_stat", &spufs_wbox_stat_fops, 0444, }, + { "signal1", &spufs_signal1_fops, 0666, }, + { "signal2", &spufs_signal2_fops, 0666, }, + { "signal1_type", &spufs_signal1_type, 0666, }, + { "signal2_type", &spufs_signal2_type, 0666, }, + { "npc", &spufs_npc_ops, 0666, }, + { "fpcr", &spufs_fpcr_fops, 0666, }, + { "decr", &spufs_decr_ops, 0666, }, + { "decr_status", &spufs_decr_status_ops, 0666, }, + { "spu_tag_mask", &spufs_spu_tag_mask_ops, 0666, }, + { "event_mask", &spufs_event_mask_ops, 0666, }, + { "srr0", &spufs_srr0_ops, 0666, }, + {}, +}; diff --git a/arch/powerpc/platforms/cell/spufs/hw_ops.c b/arch/powerpc/platforms/cell/spufs/hw_ops.c new file mode 100644 index 0000000..2e90cae --- /dev/null +++ b/arch/powerpc/platforms/cell/spufs/hw_ops.c @@ -0,0 +1,206 @@ +/* hw_ops.c - query/set operations on active SPU context. + * + * Copyright (C) IBM 2005 + * Author: Mark Nutter + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include "spufs.h" + +static int spu_hw_mbox_read(struct spu_context *ctx, u32 * data) +{ + struct spu *spu = ctx->spu; + struct spu_problem __iomem *prob = spu->problem; + u32 mbox_stat; + int ret = 0; + + spin_lock_irq(&spu->register_lock); + mbox_stat = in_be32(&prob->mb_stat_R); + if (mbox_stat & 0x0000ff) { + *data = in_be32(&prob->pu_mb_R); + ret = 4; + } + spin_unlock_irq(&spu->register_lock); + return ret; +} + +static u32 spu_hw_mbox_stat_read(struct spu_context *ctx) +{ + return in_be32(&ctx->spu->problem->mb_stat_R); +} + +static int spu_hw_ibox_read(struct spu_context *ctx, u32 * data) +{ + struct spu *spu = ctx->spu; + struct spu_problem __iomem *prob = spu->problem; + struct spu_priv1 __iomem *priv1 = spu->priv1; + struct spu_priv2 __iomem *priv2 = spu->priv2; + int ret; + + spin_lock_irq(&spu->register_lock); + if (in_be32(&prob->mb_stat_R) & 0xff0000) { + /* read the first available word */ + *data = in_be64(&priv2->puint_mb_R); + ret = 4; + } else { + /* make sure we get woken up by the interrupt */ + out_be64(&priv1->int_mask_class2_RW, + in_be64(&priv1->int_mask_class2_RW) | 0x1); + ret = 0; + } + spin_unlock_irq(&spu->register_lock); + return ret; +} + +static int spu_hw_wbox_write(struct spu_context *ctx, u32 data) +{ + struct spu *spu = ctx->spu; + struct spu_problem __iomem *prob = spu->problem; + struct spu_priv1 __iomem *priv1 = spu->priv1; + int ret; + + spin_lock_irq(&spu->register_lock); + if (in_be32(&prob->mb_stat_R) & 0x00ff00) { + /* we have space to write wbox_data to */ + out_be32(&prob->spu_mb_W, data); + ret = 4; + } else { + /* make sure we get woken up by the interrupt when space + becomes available */ + out_be64(&priv1->int_mask_class2_RW, + in_be64(&priv1->int_mask_class2_RW) | 0x10); + ret = 0; + } + spin_unlock_irq(&spu->register_lock); + return ret; +} + +static u32 spu_hw_signal1_read(struct spu_context *ctx) +{ + return in_be32(&ctx->spu->problem->signal_notify1); +} + +static void spu_hw_signal1_write(struct spu_context *ctx, u32 data) +{ + out_be32(&ctx->spu->problem->signal_notify1, data); +} + +static u32 spu_hw_signal2_read(struct spu_context *ctx) +{ + return in_be32(&ctx->spu->problem->signal_notify1); +} + +static void spu_hw_signal2_write(struct spu_context *ctx, u32 data) +{ + out_be32(&ctx->spu->problem->signal_notify2, data); +} + +static void spu_hw_signal1_type_set(struct spu_context *ctx, u64 val) +{ + struct spu *spu = ctx->spu; + struct spu_priv2 __iomem *priv2 = spu->priv2; + u64 tmp; + + spin_lock_irq(&spu->register_lock); + tmp = in_be64(&priv2->spu_cfg_RW); + if (val) + tmp |= 1; + else + tmp &= ~1; + out_be64(&priv2->spu_cfg_RW, tmp); + spin_unlock_irq(&spu->register_lock); +} + +static u64 spu_hw_signal1_type_get(struct spu_context *ctx) +{ + return ((in_be64(&ctx->spu->priv2->spu_cfg_RW) & 1) != 0); +} + +static void spu_hw_signal2_type_set(struct spu_context *ctx, u64 val) +{ + struct spu *spu = ctx->spu; + struct spu_priv2 __iomem *priv2 = spu->priv2; + u64 tmp; + + spin_lock_irq(&spu->register_lock); + tmp = in_be64(&priv2->spu_cfg_RW); + if (val) + tmp |= 2; + else + tmp &= ~2; + out_be64(&priv2->spu_cfg_RW, tmp); + spin_unlock_irq(&spu->register_lock); +} + +static u64 spu_hw_signal2_type_get(struct spu_context *ctx) +{ + return ((in_be64(&ctx->spu->priv2->spu_cfg_RW) & 2) != 0); +} + +static u32 spu_hw_npc_read(struct spu_context *ctx) +{ + return in_be32(&ctx->spu->problem->spu_npc_RW); +} + +static void spu_hw_npc_write(struct spu_context *ctx, u32 val) +{ + out_be32(&ctx->spu->problem->spu_npc_RW, val); +} + +static u32 spu_hw_status_read(struct spu_context *ctx) +{ + return in_be32(&ctx->spu->problem->spu_status_R); +} + +static char *spu_hw_get_ls(struct spu_context *ctx) +{ + return ctx->spu->local_store; +} + +struct spu_context_ops spu_hw_ops = { + .mbox_read = spu_hw_mbox_read, + .mbox_stat_read = spu_hw_mbox_stat_read, + .ibox_read = spu_hw_ibox_read, + .wbox_write = spu_hw_wbox_write, + .signal1_read = spu_hw_signal1_read, + .signal1_write = spu_hw_signal1_write, + .signal2_read = spu_hw_signal2_read, + .signal2_write = spu_hw_signal2_write, + .signal1_type_set = spu_hw_signal1_type_set, + .signal1_type_get = spu_hw_signal1_type_get, + .signal2_type_set = spu_hw_signal2_type_set, + .signal2_type_get = spu_hw_signal2_type_get, + .npc_read = spu_hw_npc_read, + .npc_write = spu_hw_npc_write, + .status_read = spu_hw_status_read, + .get_ls = spu_hw_get_ls, +}; diff --git a/arch/powerpc/platforms/cell/spufs/inode.c b/arch/powerpc/platforms/cell/spufs/inode.c new file mode 100644 index 0000000..2c3ba4e --- /dev/null +++ b/arch/powerpc/platforms/cell/spufs/inode.c @@ -0,0 +1,464 @@ +/* + * SPU file system + * + * (C) Copyright IBM Deutschland Entwicklung GmbH 2005 + * + * Author: Arnd Bergmann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "spufs.h" + +static kmem_cache_t *spufs_inode_cache; + +static struct inode * +spufs_alloc_inode(struct super_block *sb) +{ + struct spufs_inode_info *ei; + + ei = kmem_cache_alloc(spufs_inode_cache, SLAB_KERNEL); + if (!ei) + return NULL; + return &ei->vfs_inode; +} + +static void +spufs_destroy_inode(struct inode *inode) +{ + kmem_cache_free(spufs_inode_cache, SPUFS_I(inode)); +} + +static void +spufs_init_once(void *p, kmem_cache_t * cachep, unsigned long flags) +{ + struct spufs_inode_info *ei = p; + + if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == + SLAB_CTOR_CONSTRUCTOR) { + inode_init_once(&ei->vfs_inode); + } +} + +static struct inode * +spufs_new_inode(struct super_block *sb, int mode) +{ + struct inode *inode; + + inode = new_inode(sb); + if (!inode) + goto out; + + inode->i_mode = mode; + inode->i_uid = current->fsuid; + inode->i_gid = current->fsgid; + inode->i_blksize = PAGE_CACHE_SIZE; + inode->i_blocks = 0; + inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; +out: + return inode; +} + +static int +spufs_setattr(struct dentry *dentry, struct iattr *attr) +{ + struct inode *inode = dentry->d_inode; + + if ((attr->ia_valid & ATTR_SIZE) && + (attr->ia_size != inode->i_size)) + return -EINVAL; + return inode_setattr(inode, attr); +} + + +static int +spufs_new_file(struct super_block *sb, struct dentry *dentry, + struct file_operations *fops, int mode, + struct spu_context *ctx) +{ + static struct inode_operations spufs_file_iops = { + .setattr = spufs_setattr, + }; + struct inode *inode; + int ret; + + ret = -ENOSPC; + inode = spufs_new_inode(sb, S_IFREG | mode); + if (!inode) + goto out; + + ret = 0; + inode->i_op = &spufs_file_iops; + inode->i_fop = fops; + inode->u.generic_ip = SPUFS_I(inode)->i_ctx = get_spu_context(ctx); + d_add(dentry, inode); +out: + return ret; +} + +static void +spufs_delete_inode(struct inode *inode) +{ + if (SPUFS_I(inode)->i_ctx) + put_spu_context(SPUFS_I(inode)->i_ctx); + clear_inode(inode); +} + +static int +spufs_fill_dir(struct dentry *dir, struct tree_descr *files, + int mode, struct spu_context *ctx) +{ + struct dentry *dentry; + int ret; + + while (files->name && files->name[0]) { + ret = -ENOMEM; + dentry = d_alloc_name(dir, files->name); + if (!dentry) + goto out; + ret = spufs_new_file(dir->d_sb, dentry, files->ops, + files->mode & mode, ctx); + if (ret) + goto out; + files++; + } + return 0; +out: + // FIXME: remove all files that are left + + return ret; +} + +static int spufs_rmdir(struct inode *root, struct dentry *dir_dentry) +{ + struct dentry *dentry, *tmp; + struct spu_context *ctx; + int err; + + /* remove all entries */ + err = 0; + list_for_each_entry_safe(dentry, tmp, &dir_dentry->d_subdirs, d_child) { + spin_lock(&dcache_lock); + spin_lock(&dentry->d_lock); + if (!(d_unhashed(dentry)) && dentry->d_inode) { + dget_locked(dentry); + __d_drop(dentry); + spin_unlock(&dentry->d_lock); + simple_unlink(dir_dentry->d_inode, dentry); + spin_unlock(&dcache_lock); + dput(dentry); + } else { + spin_unlock(&dentry->d_lock); + spin_unlock(&dcache_lock); + } + } + + /* We have to give up the mm_struct */ + ctx = SPUFS_I(dir_dentry->d_inode)->i_ctx; + spu_forget(ctx); + + if (!err) { + shrink_dcache_parent(dir_dentry); + err = simple_rmdir(root, dir_dentry); + } + return err; +} + +static int spufs_dir_close(struct inode *inode, struct file *file) +{ + struct inode *dir; + struct dentry *dentry; + int ret; + + dentry = file->f_dentry; + dir = dentry->d_parent->d_inode; + down(&dir->i_sem); + ret = spufs_rmdir(dir, file->f_dentry); + WARN_ON(ret); + up(&dir->i_sem); + return dcache_dir_close(inode, file); +} + +struct inode_operations spufs_dir_inode_operations = { + .lookup = simple_lookup, +}; + +struct file_operations spufs_autodelete_dir_operations = { + .open = dcache_dir_open, + .release = spufs_dir_close, + .llseek = dcache_dir_lseek, + .read = generic_read_dir, + .readdir = dcache_readdir, + .fsync = simple_sync_file, +}; + +static int +spufs_mkdir(struct inode *dir, struct dentry *dentry, int mode) +{ + int ret; + struct inode *inode; + struct spu_context *ctx; + + ret = -ENOSPC; + inode = spufs_new_inode(dir->i_sb, mode | S_IFDIR); + if (!inode) + goto out; + + if (dir->i_mode & S_ISGID) { + inode->i_gid = dir->i_gid; + inode->i_mode &= S_ISGID; + } + ctx = alloc_spu_context(inode->i_mapping); + SPUFS_I(inode)->i_ctx = ctx; + if (!ctx) + goto out_iput; + + inode->i_op = &spufs_dir_inode_operations; + inode->i_fop = &simple_dir_operations; + ret = spufs_fill_dir(dentry, spufs_dir_contents, mode, ctx); + if (ret) + goto out_free_ctx; + + d_instantiate(dentry, inode); + dget(dentry); + dir->i_nlink++; + goto out; + +out_free_ctx: + put_spu_context(ctx); +out_iput: + iput(inode); +out: + return ret; +} + +long +spufs_create_thread(struct nameidata *nd, const char *name, + unsigned int flags, mode_t mode) +{ + struct dentry *dentry; + struct file *filp; + int ret; + + /* need to be at the root of spufs */ + ret = -EINVAL; + if (nd->dentry->d_sb->s_magic != SPUFS_MAGIC || + nd->dentry != nd->dentry->d_sb->s_root) + goto out; + + dentry = lookup_create(nd, 1); + ret = PTR_ERR(dentry); + if (IS_ERR(dentry)) + goto out_dir; + + ret = -EEXIST; + if (dentry->d_inode) + goto out_dput; + + mode &= ~current->fs->umask; + ret = spufs_mkdir(nd->dentry->d_inode, dentry, mode & S_IRWXUGO); + if (ret) + goto out_dput; + + ret = get_unused_fd(); + if (ret < 0) + goto out_dput; + + dentry->d_inode->i_nlink++; + + filp = filp_open(name, O_RDONLY, mode); + if (IS_ERR(filp)) { + // FIXME: remove directory again + put_unused_fd(ret); + ret = PTR_ERR(filp); + } else { + filp->f_op = &spufs_autodelete_dir_operations; + fd_install(ret, filp); + } + +out_dput: + dput(dentry); +out_dir: + up(&nd->dentry->d_inode->i_sem); +out: + return ret; +} + +/* File system initialization */ +enum { + Opt_uid, Opt_gid, Opt_err, +}; + +static match_table_t spufs_tokens = { + { Opt_uid, "uid=%d" }, + { Opt_gid, "gid=%d" }, + { Opt_err, NULL }, +}; + +static int +spufs_parse_options(char *options, struct inode *root) +{ + char *p; + substring_t args[MAX_OPT_ARGS]; + + while ((p = strsep(&options, ",")) != NULL) { + int token, option; + + if (!*p) + continue; + + token = match_token(p, spufs_tokens, args); + switch (token) { + case Opt_uid: + if (match_int(&args[0], &option)) + return 0; + root->i_uid = option; + break; + case Opt_gid: + if (match_int(&args[0], &option)) + return 0; + root->i_gid = option; + break; + default: + return 0; + } + } + return 1; +} + +static int +spufs_create_root(struct super_block *sb, void *data) +{ + struct inode *inode; + int ret; + + ret = -ENOMEM; + inode = spufs_new_inode(sb, S_IFDIR | 0775); + if (!inode) + goto out; + + inode->i_op = &spufs_dir_inode_operations; + inode->i_fop = &simple_dir_operations; + SPUFS_I(inode)->i_ctx = NULL; + + ret = -EINVAL; + if (!spufs_parse_options(data, inode)) + goto out_iput; + + ret = -ENOMEM; + sb->s_root = d_alloc_root(inode); + if (!sb->s_root) + goto out_iput; + + return 0; +out_iput: + iput(inode); +out: + return ret; +} + +static int +spufs_fill_super(struct super_block *sb, void *data, int silent) +{ + static struct super_operations s_ops = { + .alloc_inode = spufs_alloc_inode, + .destroy_inode = spufs_destroy_inode, + .statfs = simple_statfs, + .delete_inode = spufs_delete_inode, + .drop_inode = generic_delete_inode, + }; + + sb->s_maxbytes = MAX_LFS_FILESIZE; + sb->s_blocksize = PAGE_CACHE_SIZE; + sb->s_blocksize_bits = PAGE_CACHE_SHIFT; + sb->s_magic = SPUFS_MAGIC; + sb->s_op = &s_ops; + + return spufs_create_root(sb, data); +} + +static struct super_block * +spufs_get_sb(struct file_system_type *fstype, int flags, + const char *name, void *data) +{ + return get_sb_single(fstype, flags, data, spufs_fill_super); +} + +static struct file_system_type spufs_type = { + .owner = THIS_MODULE, + .name = "spufs", + .get_sb = spufs_get_sb, + .kill_sb = kill_litter_super, +}; + +static int spufs_init(void) +{ + int ret; + ret = -ENOMEM; + spufs_inode_cache = kmem_cache_create("spufs_inode_cache", + sizeof(struct spufs_inode_info), 0, + SLAB_HWCACHE_ALIGN, spufs_init_once, NULL); + + if (!spufs_inode_cache) + goto out; + if (spu_sched_init() != 0) { + kmem_cache_destroy(spufs_inode_cache); + goto out; + } + ret = register_filesystem(&spufs_type); + if (ret) + goto out_cache; + ret = register_spu_syscalls(&spufs_calls); + if (ret) + goto out_fs; + return 0; +out_fs: + unregister_filesystem(&spufs_type); +out_cache: + kmem_cache_destroy(spufs_inode_cache); +out: + return ret; +} +module_init(spufs_init); + +static void spufs_exit(void) +{ + spu_sched_exit(); + unregister_spu_syscalls(&spufs_calls); + unregister_filesystem(&spufs_type); + kmem_cache_destroy(spufs_inode_cache); +} +module_exit(spufs_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Arnd Bergmann "); + diff --git a/arch/powerpc/platforms/cell/spufs/sched.c b/arch/powerpc/platforms/cell/spufs/sched.c new file mode 100644 index 0000000..c0d9d83 --- /dev/null +++ b/arch/powerpc/platforms/cell/spufs/sched.c @@ -0,0 +1,419 @@ +/* sched.c - SPU scheduler. + * + * Copyright (C) IBM 2005 + * Author: Mark Nutter + * + * SPU scheduler, based on Linux thread priority. For now use + * a simple "cooperative" yield model with no preemption. SPU + * scheduling will eventually be preemptive: When a thread with + * a higher static priority gets ready to run, then an active SPU + * context will be preempted and returned to the waitq. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#define DEBUG 1 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include "spufs.h" + +#define SPU_BITMAP_SIZE (((MAX_PRIO+BITS_PER_LONG)/BITS_PER_LONG)+1) +struct spu_prio_array { + atomic_t nr_blocked; + unsigned long bitmap[SPU_BITMAP_SIZE]; + wait_queue_head_t waitq[MAX_PRIO]; +}; + +/* spu_runqueue - This is the main runqueue data structure for SPUs. */ +struct spu_runqueue { + struct semaphore sem; + unsigned long nr_active; + unsigned long nr_idle; + unsigned long nr_switches; + struct list_head active_list; + struct list_head idle_list; + struct spu_prio_array prio; +}; + +static struct spu_runqueue *spu_runqueues = NULL; + +static inline struct spu_runqueue *spu_rq(void) +{ + /* Future: make this a per-NODE array, + * and use cpu_to_node(smp_processor_id()) + */ + return spu_runqueues; +} + +static inline struct spu *del_idle(struct spu_runqueue *rq) +{ + struct spu *spu; + + BUG_ON(rq->nr_idle <= 0); + BUG_ON(list_empty(&rq->idle_list)); + /* Future: Move SPU out of low-power SRI state. */ + spu = list_entry(rq->idle_list.next, struct spu, sched_list); + list_del_init(&spu->sched_list); + rq->nr_idle--; + return spu; +} + +static inline void del_active(struct spu_runqueue *rq, struct spu *spu) +{ + BUG_ON(rq->nr_active <= 0); + BUG_ON(list_empty(&rq->active_list)); + list_del_init(&spu->sched_list); + rq->nr_active--; +} + +static inline void add_idle(struct spu_runqueue *rq, struct spu *spu) +{ + /* Future: Put SPU into low-power SRI state. */ + list_add_tail(&spu->sched_list, &rq->idle_list); + rq->nr_idle++; +} + +static inline void add_active(struct spu_runqueue *rq, struct spu *spu) +{ + rq->nr_active++; + rq->nr_switches++; + list_add_tail(&spu->sched_list, &rq->active_list); +} + +static void prio_wakeup(struct spu_runqueue *rq) +{ + if (atomic_read(&rq->prio.nr_blocked) && rq->nr_idle) { + int best = sched_find_first_bit(rq->prio.bitmap); + if (best < MAX_PRIO) { + wait_queue_head_t *wq = &rq->prio.waitq[best]; + wake_up_interruptible_nr(wq, 1); + } + } +} + +static void prio_wait(struct spu_runqueue *rq, u64 flags) +{ + int prio = current->prio; + wait_queue_head_t *wq = &rq->prio.waitq[prio]; + DEFINE_WAIT(wait); + + __set_bit(prio, rq->prio.bitmap); + atomic_inc(&rq->prio.nr_blocked); + prepare_to_wait_exclusive(wq, &wait, TASK_INTERRUPTIBLE); + if (!signal_pending(current)) { + up(&rq->sem); + pr_debug("%s: pid=%d prio=%d\n", __FUNCTION__, + current->pid, current->prio); + schedule(); + down(&rq->sem); + } + finish_wait(wq, &wait); + atomic_dec(&rq->prio.nr_blocked); + if (!waitqueue_active(wq)) + __clear_bit(prio, rq->prio.bitmap); +} + +static inline int is_best_prio(struct spu_runqueue *rq) +{ + int best_prio; + + best_prio = sched_find_first_bit(rq->prio.bitmap); + return (current->prio < best_prio) ? 1 : 0; +} + +static inline void mm_needs_global_tlbie(struct mm_struct *mm) +{ + /* Global TLBIE broadcast required with SPEs. */ +#if (NR_CPUS > 1) + __cpus_setall(&mm->cpu_vm_mask, NR_CPUS); +#else + __cpus_setall(&mm->cpu_vm_mask, NR_CPUS+1); /* is this ok? */ +#endif +} + +static inline void bind_context(struct spu *spu, struct spu_context *ctx) +{ + pr_debug("%s: pid=%d SPU=%d\n", __FUNCTION__, current->pid, + spu->number); + spu->ctx = ctx; + spu->flags = 0; + ctx->spu = spu; + ctx->ops = &spu_hw_ops; + spu->pid = current->pid; + spu->prio = current->prio; + spu->mm = ctx->owner; + mm_needs_global_tlbie(spu->mm); + spu->ibox_callback = spufs_ibox_callback; + spu->wbox_callback = spufs_wbox_callback; + mb(); + spu_restore(&ctx->csa, spu); +} + +static inline void unbind_context(struct spu *spu, struct spu_context *ctx) +{ + pr_debug("%s: unbind pid=%d SPU=%d\n", __FUNCTION__, + spu->pid, spu->number); + spu_save(&ctx->csa, spu); + ctx->state = SPU_STATE_SAVED; + spu->ibox_callback = NULL; + spu->wbox_callback = NULL; + spu->mm = NULL; + spu->pid = 0; + spu->prio = MAX_PRIO; + ctx->ops = &spu_backing_ops; + ctx->spu = NULL; + spu->ctx = NULL; +} + +static struct spu *preempt_active(struct spu_runqueue *rq) +{ + struct list_head *p; + struct spu_context *ctx; + struct spu *spu; + + /* Future: implement real preemption. For now just + * boot a lower priority ctx that is in "detached" + * state, i.e. on a processor but not currently in + * spu_run(). + */ + list_for_each(p, &rq->active_list) { + spu = list_entry(p, struct spu, sched_list); + if (current->prio < spu->prio) { + ctx = spu->ctx; + if (down_write_trylock(&ctx->state_sema)) { + if (ctx->state != SPU_STATE_RUNNABLE) { + up_write(&ctx->state_sema); + continue; + } + pr_debug("%s: booting pid=%d from SPU %d\n", + __FUNCTION__, spu->pid, spu->number); + del_active(rq, spu); + up(&rq->sem); + unbind_context(spu, ctx); + up_write(&ctx->state_sema); + return spu; + } + } + } + return NULL; +} + +static struct spu *get_idle_spu(u64 flags) +{ + struct spu_runqueue *rq; + struct spu *spu = NULL; + + rq = spu_rq(); + down(&rq->sem); + for (;;) { + if (rq->nr_idle > 0) { + if (is_best_prio(rq)) { + /* Fall through. */ + spu = del_idle(rq); + break; + } else { + prio_wakeup(rq); + up(&rq->sem); + yield(); + if (signal_pending(current)) { + return NULL; + } + rq = spu_rq(); + down(&rq->sem); + continue; + } + } else { + if (is_best_prio(rq)) { + if ((spu = preempt_active(rq)) != NULL) + return spu; + } + prio_wait(rq, flags); + if (signal_pending(current)) { + prio_wakeup(rq); + spu = NULL; + break; + } + continue; + } + } + up(&rq->sem); + return spu; +} + +static void put_idle_spu(struct spu *spu) +{ + struct spu_runqueue *rq = spu->rq; + + down(&rq->sem); + add_idle(rq, spu); + prio_wakeup(rq); + up(&rq->sem); +} + +static int get_active_spu(struct spu *spu) +{ + struct spu_runqueue *rq = spu->rq; + struct list_head *p; + struct spu *tmp; + int rc = 0; + + down(&rq->sem); + list_for_each(p, &rq->active_list) { + tmp = list_entry(p, struct spu, sched_list); + if (tmp == spu) { + del_active(rq, spu); + rc = 1; + break; + } + } + up(&rq->sem); + return rc; +} + +static void put_active_spu(struct spu *spu) +{ + struct spu_runqueue *rq = spu->rq; + + down(&rq->sem); + add_active(rq, spu); + up(&rq->sem); +} + +/* Lock order: + * spu_activate() & spu_deactivate() require the + * caller to have down_write(&ctx->state_sema). + * + * The rq->sem is breifly held (inside or outside a + * given ctx lock) for list management, but is never + * held during save/restore. + */ + +int spu_activate(struct spu_context *ctx, u64 flags) +{ + struct spu *spu; + + if (ctx->spu) + return 0; + spu = get_idle_spu(flags); + if (!spu) + return (signal_pending(current)) ? -ERESTARTSYS : -EAGAIN; + bind_context(spu, ctx); + put_active_spu(spu); + return 0; +} + +void spu_deactivate(struct spu_context *ctx) +{ + struct spu *spu; + int needs_idle; + + spu = ctx->spu; + if (!spu) + return; + needs_idle = get_active_spu(spu); + unbind_context(spu, ctx); + if (needs_idle) + put_idle_spu(spu); +} + +void spu_yield(struct spu_context *ctx) +{ + struct spu *spu; + + if (!down_write_trylock(&ctx->state_sema)) + return; + spu = ctx->spu; + if ((ctx->state == SPU_STATE_RUNNABLE) && + (sched_find_first_bit(spu->rq->prio.bitmap) <= current->prio)) { + pr_debug("%s: yielding SPU %d\n", __FUNCTION__, spu->number); + spu_deactivate(ctx); + ctx->state = SPU_STATE_SAVED; + } + up_write(&ctx->state_sema); +} + +int __init spu_sched_init(void) +{ + struct spu_runqueue *rq; + struct spu *spu; + int i; + + rq = spu_runqueues = kmalloc(sizeof(struct spu_runqueue), GFP_KERNEL); + if (!rq) { + printk(KERN_WARNING "%s: Unable to allocate runqueues.\n", + __FUNCTION__); + return 1; + } + memset(rq, 0, sizeof(struct spu_runqueue)); + init_MUTEX(&rq->sem); + INIT_LIST_HEAD(&rq->active_list); + INIT_LIST_HEAD(&rq->idle_list); + rq->nr_active = 0; + rq->nr_idle = 0; + rq->nr_switches = 0; + atomic_set(&rq->prio.nr_blocked, 0); + for (i = 0; i < MAX_PRIO; i++) { + init_waitqueue_head(&rq->prio.waitq[i]); + __clear_bit(i, rq->prio.bitmap); + } + __set_bit(MAX_PRIO, rq->prio.bitmap); + for (;;) { + spu = spu_alloc(); + if (!spu) + break; + pr_debug("%s: adding SPU[%d]\n", __FUNCTION__, spu->number); + add_idle(rq, spu); + spu->rq = rq; + } + if (!rq->nr_idle) { + printk(KERN_WARNING "%s: No available SPUs.\n", __FUNCTION__); + kfree(rq); + return 1; + } + return 0; +} + +void __exit spu_sched_exit(void) +{ + struct spu_runqueue *rq = spu_rq(); + struct spu *spu; + + if (!rq) { + printk(KERN_WARNING "%s: no runqueues!\n", __FUNCTION__); + return; + } + while (rq->nr_idle > 0) { + spu = del_idle(rq); + if (!spu) + break; + spu_free(spu); + } + kfree(rq); +} diff --git a/arch/powerpc/platforms/cell/spufs/spu_restore.c b/arch/powerpc/platforms/cell/spufs/spu_restore.c new file mode 100644 index 0000000..0bf723d --- /dev/null +++ b/arch/powerpc/platforms/cell/spufs/spu_restore.c @@ -0,0 +1,336 @@ +/* + * spu_restore.c + * + * (C) Copyright IBM Corp. 2005 + * + * SPU-side context restore sequence outlined in + * Synergistic Processor Element Book IV + * + * Author: Mark Nutter + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#ifndef LS_SIZE +#define LS_SIZE 0x40000 /* 256K (in bytes) */ +#endif + +typedef unsigned int u32; +typedef unsigned long long u64; + +#include +#include +#include "spu_utils.h" + +#define BR_INSTR 0x327fff80 /* br -4 */ +#define NOP_INSTR 0x40200000 /* nop */ +#define HEQ_INSTR 0x7b000000 /* heq $0, $0 */ +#define STOP_INSTR 0x00000000 /* stop 0x0 */ +#define ILLEGAL_INSTR 0x00800000 /* illegal instr */ +#define RESTORE_COMPLETE 0x00003ffc /* stop 0x3ffc */ + +static inline void fetch_regs_from_mem(addr64 lscsa_ea) +{ + unsigned int ls = (unsigned int)®s_spill[0]; + unsigned int size = sizeof(regs_spill); + unsigned int tag_id = 0; + unsigned int cmd = 0x40; /* GET */ + + spu_writech(MFC_LSA, ls); + spu_writech(MFC_EAH, lscsa_ea.ui[0]); + spu_writech(MFC_EAL, lscsa_ea.ui[1]); + spu_writech(MFC_Size, size); + spu_writech(MFC_TagID, tag_id); + spu_writech(MFC_Cmd, cmd); +} + +static inline void restore_upper_240kb(addr64 lscsa_ea) +{ + unsigned int ls = 16384; + unsigned int list = (unsigned int)&dma_list[0]; + unsigned int size = sizeof(dma_list); + unsigned int tag_id = 0; + unsigned int cmd = 0x44; /* GETL */ + + /* Restore, Step 4: + * Enqueue the GETL command (tag 0) to the MFC SPU command + * queue to transfer the upper 240 kb of LS from CSA. + */ + spu_writech(MFC_LSA, ls); + spu_writech(MFC_EAH, lscsa_ea.ui[0]); + spu_writech(MFC_EAL, list); + spu_writech(MFC_Size, size); + spu_writech(MFC_TagID, tag_id); + spu_writech(MFC_Cmd, cmd); +} + +static inline void restore_decr(void) +{ + unsigned int offset; + unsigned int decr_running; + unsigned int decr; + + /* Restore, Step 6: + * If the LSCSA "decrementer running" flag is set + * then write the SPU_WrDec channel with the + * decrementer value from LSCSA. + */ + offset = LSCSA_QW_OFFSET(decr_status); + decr_running = regs_spill[offset].slot[0]; + if (decr_running) { + offset = LSCSA_QW_OFFSET(decr); + decr = regs_spill[offset].slot[0]; + spu_writech(SPU_WrDec, decr); + } +} + +static inline void write_ppu_mb(void) +{ + unsigned int offset; + unsigned int data; + + /* Restore, Step 11: + * Write the MFC_WrOut_MB channel with the PPU_MB + * data from LSCSA. + */ + offset = LSCSA_QW_OFFSET(ppu_mb); + data = regs_spill[offset].slot[0]; + spu_writech(SPU_WrOutMbox, data); +} + +static inline void write_ppuint_mb(void) +{ + unsigned int offset; + unsigned int data; + + /* Restore, Step 12: + * Write the MFC_WrInt_MB channel with the PPUINT_MB + * data from LSCSA. + */ + offset = LSCSA_QW_OFFSET(ppuint_mb); + data = regs_spill[offset].slot[0]; + spu_writech(SPU_WrOutIntrMbox, data); +} + +static inline void restore_fpcr(void) +{ + unsigned int offset; + vector unsigned int fpcr; + + /* Restore, Step 13: + * Restore the floating-point status and control + * register from the LSCSA. + */ + offset = LSCSA_QW_OFFSET(fpcr); + fpcr = regs_spill[offset].v; + spu_mtfpscr(fpcr); +} + +static inline void restore_srr0(void) +{ + unsigned int offset; + unsigned int srr0; + + /* Restore, Step 14: + * Restore the SPU SRR0 data from the LSCSA. + */ + offset = LSCSA_QW_OFFSET(srr0); + srr0 = regs_spill[offset].slot[0]; + spu_writech(SPU_WrSRR0, srr0); +} + +static inline void restore_event_mask(void) +{ + unsigned int offset; + unsigned int event_mask; + + /* Restore, Step 15: + * Restore the SPU_RdEventMsk data from the LSCSA. + */ + offset = LSCSA_QW_OFFSET(event_mask); + event_mask = regs_spill[offset].slot[0]; + spu_writech(SPU_WrEventMask, event_mask); +} + +static inline void restore_tag_mask(void) +{ + unsigned int offset; + unsigned int tag_mask; + + /* Restore, Step 16: + * Restore the SPU_RdTagMsk data from the LSCSA. + */ + offset = LSCSA_QW_OFFSET(tag_mask); + tag_mask = regs_spill[offset].slot[0]; + spu_writech(MFC_WrTagMask, tag_mask); +} + +static inline void restore_complete(void) +{ + extern void exit_fini(void); + unsigned int *exit_instrs = (unsigned int *)exit_fini; + unsigned int offset; + unsigned int stopped_status; + unsigned int stopped_code; + + /* Restore, Step 18: + * Issue a stop-and-signal instruction with + * "good context restore" signal value. + * + * Restore, Step 19: + * There may be additional instructions placed + * here by the PPE Sequence for SPU Context + * Restore in order to restore the correct + * "stopped state". + * + * This step is handled here by analyzing the + * LSCSA.stopped_status and then modifying the + * exit() function to behave appropriately. + */ + + offset = LSCSA_QW_OFFSET(stopped_status); + stopped_status = regs_spill[offset].slot[0]; + stopped_code = regs_spill[offset].slot[1]; + + switch (stopped_status) { + case SPU_STOPPED_STATUS_P_I: + /* SPU_Status[P,I]=1. Add illegal instruction + * followed by stop-and-signal instruction after + * end of restore code. + */ + exit_instrs[0] = RESTORE_COMPLETE; + exit_instrs[1] = ILLEGAL_INSTR; + exit_instrs[2] = STOP_INSTR | stopped_code; + break; + case SPU_STOPPED_STATUS_P_H: + /* SPU_Status[P,H]=1. Add 'heq $0, $0' followed + * by stop-and-signal instruction after end of + * restore code. + */ + exit_instrs[0] = RESTORE_COMPLETE; + exit_instrs[1] = HEQ_INSTR; + exit_instrs[2] = STOP_INSTR | stopped_code; + break; + case SPU_STOPPED_STATUS_S_P: + /* SPU_Status[S,P]=1. Add nop instruction + * followed by 'br -4' after end of restore + * code. + */ + exit_instrs[0] = RESTORE_COMPLETE; + exit_instrs[1] = STOP_INSTR | stopped_code; + exit_instrs[2] = NOP_INSTR; + exit_instrs[3] = BR_INSTR; + break; + case SPU_STOPPED_STATUS_S_I: + /* SPU_Status[S,I]=1. Add illegal instruction + * followed by 'br -4' after end of restore code. + */ + exit_instrs[0] = RESTORE_COMPLETE; + exit_instrs[1] = ILLEGAL_INSTR; + exit_instrs[2] = NOP_INSTR; + exit_instrs[3] = BR_INSTR; + break; + case SPU_STOPPED_STATUS_I: + /* SPU_Status[I]=1. Add illegal instruction followed + * by infinite loop after end of restore sequence. + */ + exit_instrs[0] = RESTORE_COMPLETE; + exit_instrs[1] = ILLEGAL_INSTR; + exit_instrs[2] = NOP_INSTR; + exit_instrs[3] = BR_INSTR; + break; + case SPU_STOPPED_STATUS_S: + /* SPU_Status[S]=1. Add two 'nop' instructions. */ + exit_instrs[0] = RESTORE_COMPLETE; + exit_instrs[1] = NOP_INSTR; + exit_instrs[2] = NOP_INSTR; + exit_instrs[3] = BR_INSTR; + break; + case SPU_STOPPED_STATUS_H: + /* SPU_Status[H]=1. Add 'heq $0, $0' instruction + * after end of restore code. + */ + exit_instrs[0] = RESTORE_COMPLETE; + exit_instrs[1] = HEQ_INSTR; + exit_instrs[2] = NOP_INSTR; + exit_instrs[3] = BR_INSTR; + break; + case SPU_STOPPED_STATUS_P: + /* SPU_Status[P]=1. Add stop-and-signal instruction + * after end of restore code. + */ + exit_instrs[0] = RESTORE_COMPLETE; + exit_instrs[1] = STOP_INSTR | stopped_code; + break; + case SPU_STOPPED_STATUS_R: + /* SPU_Status[I,S,H,P,R]=0. Add infinite loop. */ + exit_instrs[0] = RESTORE_COMPLETE; + exit_instrs[1] = NOP_INSTR; + exit_instrs[2] = NOP_INSTR; + exit_instrs[3] = BR_INSTR; + break; + default: + /* SPU_Status[R]=1. No additonal instructions. */ + break; + } + spu_sync(); +} + +/** + * main - entry point for SPU-side context restore. + * + * This code deviates from the documented sequence in the + * following aspects: + * + * 1. The EA for LSCSA is passed from PPE in the + * signal notification channels. + * 2. The register spill area is pulled by SPU + * into LS, rather than pushed by PPE. + * 3. All 128 registers are restored by exit(). + * 4. The exit() function is modified at run + * time in order to properly restore the + * SPU_Status register. + */ +int main() +{ + addr64 lscsa_ea; + + lscsa_ea.ui[0] = spu_readch(SPU_RdSigNotify1); + lscsa_ea.ui[1] = spu_readch(SPU_RdSigNotify2); + fetch_regs_from_mem(lscsa_ea); + + set_event_mask(); /* Step 1. */ + set_tag_mask(); /* Step 2. */ + build_dma_list(lscsa_ea); /* Step 3. */ + restore_upper_240kb(lscsa_ea); /* Step 4. */ + /* Step 5: done by 'exit'. */ + restore_decr(); /* Step 6. */ + enqueue_putllc(lscsa_ea); /* Step 7. */ + set_tag_update(); /* Step 8. */ + read_tag_status(); /* Step 9. */ + read_llar_status(); /* Step 10. */ + write_ppu_mb(); /* Step 11. */ + write_ppuint_mb(); /* Step 12. */ + restore_fpcr(); /* Step 13. */ + restore_srr0(); /* Step 14. */ + restore_event_mask(); /* Step 15. */ + restore_tag_mask(); /* Step 16. */ + /* Step 17. done by 'exit'. */ + restore_complete(); /* Step 18. */ + + return 0; +} diff --git a/arch/powerpc/platforms/cell/spufs/spu_restore_crt0.S b/arch/powerpc/platforms/cell/spufs/spu_restore_crt0.S new file mode 100644 index 0000000..2905949 --- /dev/null +++ b/arch/powerpc/platforms/cell/spufs/spu_restore_crt0.S @@ -0,0 +1,116 @@ +/* + * crt0_r.S: Entry function for SPU-side context restore. + * + * Copyright (C) 2005 IBM + * + * Entry and exit function for SPU-side of the context restore + * sequence. Sets up an initial stack frame, then branches to + * 'main'. On return, restores all 128 registers from the LSCSA + * and exits. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include + +.data +.align 7 +.globl regs_spill +regs_spill: +.space SIZEOF_SPU_SPILL_REGS, 0x0 + +.text +.global _start +_start: + /* Initialize the stack pointer to point to 16368 + * (16kb-16). The back chain pointer is initialized + * to NULL. + */ + il $0, 0 + il $SP, 16368 + stqd $0, 0($SP) + + /* Allocate a minimum stack frame for the called main. + * This is needed so that main has a place to save the + * link register when it calls another function. + */ + stqd $SP, -160($SP) + ai $SP, $SP, -160 + + /* Call the program's main function. */ + brsl $0, main + +.global exit +.global _exit +exit: +_exit: + /* SPU Context Restore, Step 5: Restore the remaining 112 GPRs. */ + ila $3, regs_spill + 256 +restore_regs: + lqr $4, restore_reg_insts +restore_reg_loop: + ai $4, $4, 4 + .balignl 16, 0x40200000 +restore_reg_insts: /* must be quad-word aligned. */ + lqd $16, 0($3) + lqd $17, 16($3) + lqd $18, 32($3) + lqd $19, 48($3) + andi $5, $4, 0x7F + stqr $4, restore_reg_insts + ai $3, $3, 64 + brnz $5, restore_reg_loop + + /* SPU Context Restore Step 17: Restore the first 16 GPRs. */ + lqa $0, regs_spill + 0 + lqa $1, regs_spill + 16 + lqa $2, regs_spill + 32 + lqa $3, regs_spill + 48 + lqa $4, regs_spill + 64 + lqa $5, regs_spill + 80 + lqa $6, regs_spill + 96 + lqa $7, regs_spill + 112 + lqa $8, regs_spill + 128 + lqa $9, regs_spill + 144 + lqa $10, regs_spill + 160 + lqa $11, regs_spill + 176 + lqa $12, regs_spill + 192 + lqa $13, regs_spill + 208 + lqa $14, regs_spill + 224 + lqa $15, regs_spill + 240 + + /* Under normal circumstances, the 'exit' function + * terminates with 'stop SPU_RESTORE_COMPLETE', + * indicating that the SPU-side restore code has + * completed. + * + * However it is possible that instructions immediately + * following the 'stop 0x3ffc' have been modified at run + * time so as to recreate the exact SPU_Status settings + * from the application, e.g. illegal instruciton, halt, + * etc. + */ +.global exit_fini +.global _exit_fini +exit_fini: +_exit_fini: + stop SPU_RESTORE_COMPLETE + stop 0 + stop 0 + stop 0 + + /* Pad the size of this crt0.o to be multiple of 16 bytes. */ +.balignl 16, 0x0 diff --git a/arch/powerpc/platforms/cell/spufs/spu_restore_dump.h_shipped b/arch/powerpc/platforms/cell/spufs/spu_restore_dump.h_shipped new file mode 100644 index 0000000..1b2355f --- /dev/null +++ b/arch/powerpc/platforms/cell/spufs/spu_restore_dump.h_shipped @@ -0,0 +1,231 @@ +/* + * spu_restore_dump.h: Copyright (C) 2005 IBM. + * Hex-dump auto generated from spu_restore.c. + * Do not edit! + */ +static unsigned int spu_restore_code[] __page_aligned = { +0x40800000, 0x409ff801, 0x24000080, 0x24fd8081, +0x1cd80081, 0x33001180, 0x42030003, 0x33800284, +0x1c010204, 0x40200000, 0x40200000, 0x40200000, +0x34000190, 0x34004191, 0x34008192, 0x3400c193, +0x141fc205, 0x23fffd84, 0x1c100183, 0x217ffa85, +0x3080a000, 0x3080a201, 0x3080a402, 0x3080a603, +0x3080a804, 0x3080aa05, 0x3080ac06, 0x3080ae07, +0x3080b008, 0x3080b209, 0x3080b40a, 0x3080b60b, +0x3080b80c, 0x3080ba0d, 0x3080bc0e, 0x3080be0f, +0x00003ffc, 0x00000000, 0x00000000, 0x00000000, +0x01a00182, 0x3ec00083, 0xb0a14103, 0x01a00204, +0x3ec10082, 0x4202800e, 0x04000703, 0xb0a14202, +0x21a00803, 0x3fbf028d, 0x3f20068d, 0x3fbe0682, +0x3fe30102, 0x21a00882, 0x3f82028f, 0x3fe3078f, +0x3fbf0784, 0x3f200204, 0x3fbe0204, 0x3fe30204, +0x04000203, 0x21a00903, 0x40848002, 0x21a00982, +0x40800003, 0x21a00a03, 0x40802002, 0x21a00a82, +0x21a00083, 0x40800082, 0x21a00b02, 0x10002818, +0x40a80002, 0x32800007, 0x4207000c, 0x18008208, +0x40a0000b, 0x4080020a, 0x40800709, 0x00200000, +0x42070002, 0x3ac30384, 0x1cffc489, 0x00200000, +0x18008383, 0x38830382, 0x4cffc486, 0x3ac28185, +0xb0408584, 0x28830382, 0x1c020387, 0x38828182, +0xb0408405, 0x1802c408, 0x28828182, 0x217ff886, +0x04000583, 0x21a00803, 0x3fbe0682, 0x3fe30102, +0x04000106, 0x21a00886, 0x04000603, 0x21a00903, +0x40803c02, 0x21a00982, 0x40800003, 0x04000184, +0x21a00a04, 0x40802202, 0x21a00a82, 0x42028005, +0x34208702, 0x21002282, 0x21a00804, 0x21a00886, +0x3fbf0782, 0x3f200102, 0x3fbe0102, 0x3fe30102, +0x21a00902, 0x40804003, 0x21a00983, 0x21a00a04, +0x40805a02, 0x21a00a82, 0x40800083, 0x21a00b83, +0x01a00c02, 0x01a00d83, 0x3420c282, 0x21a00e02, +0x34210283, 0x21a00f03, 0x34200284, 0x77400200, +0x3421c282, 0x21a00702, 0x34218283, 0x21a00083, +0x34214282, 0x21a00b02, 0x4200480c, 0x00200000, +0x1c010286, 0x34220284, 0x34220302, 0x0f608203, +0x5c024204, 0x3b81810b, 0x42013c02, 0x00200000, +0x18008185, 0x38808183, 0x3b814182, 0x21004e84, +0x4020007f, 0x35000100, 0x000004e0, 0x000002a0, +0x000002e8, 0x00000428, 0x00000360, 0x000002e8, +0x000004a0, 0x00000468, 0x000003c8, 0x00000360, +0x409ffe02, 0x30801203, 0x40800204, 0x3ec40085, +0x10009c09, 0x3ac10606, 0xb060c105, 0x4020007f, +0x4020007f, 0x20801203, 0x38810602, 0xb0408586, +0x28810602, 0x32004180, 0x34204702, 0x21a00382, +0x4020007f, 0x327fdc80, 0x409ffe02, 0x30801203, +0x40800204, 0x3ec40087, 0x40800405, 0x00200000, +0x40800606, 0x3ac10608, 0x3ac14609, 0x3ac1860a, +0xb060c107, 0x20801203, 0x41004003, 0x38810602, +0x4020007f, 0xb0408188, 0x4020007f, 0x28810602, +0x41201002, 0x38814603, 0x10009c09, 0xb060c109, +0x4020007f, 0x28814603, 0x41193f83, 0x38818602, +0x60ffc003, 0xb040818a, 0x28818602, 0x32003080, +0x409ffe02, 0x30801203, 0x40800204, 0x3ec40087, +0x41201008, 0x10009c14, 0x40800405, 0x3ac10609, +0x40800606, 0x3ac1460a, 0xb060c107, 0x3ac1860b, +0x20801203, 0x38810602, 0xb0408409, 0x28810602, +0x38814603, 0xb060c40a, 0x4020007f, 0x28814603, +0x41193f83, 0x38818602, 0x60ffc003, 0xb040818b, +0x28818602, 0x32002380, 0x409ffe02, 0x30801204, +0x40800205, 0x3ec40083, 0x40800406, 0x3ac14607, +0x3ac18608, 0xb0810103, 0x41004002, 0x20801204, +0x4020007f, 0x38814603, 0x10009c0b, 0xb060c107, +0x4020007f, 0x4020007f, 0x28814603, 0x38818602, +0x4020007f, 0x4020007f, 0xb0408588, 0x28818602, +0x4020007f, 0x32001780, 0x409ffe02, 0x1000640e, +0x40800204, 0x30801203, 0x40800405, 0x3ec40087, +0x40800606, 0x3ac10608, 0x3ac14609, 0x3ac1860a, +0xb060c107, 0x20801203, 0x413d8003, 0x38810602, +0x4020007f, 0x327fd780, 0x409ffe02, 0x10007f0c, +0x40800205, 0x30801204, 0x40800406, 0x3ec40083, +0x3ac14607, 0x3ac18608, 0xb0810103, 0x413d8002, +0x20801204, 0x38814603, 0x4020007f, 0x327feb80, +0x409ffe02, 0x30801203, 0x40800204, 0x3ec40087, +0x40800405, 0x1000650a, 0x40800606, 0x3ac10608, +0x3ac14609, 0x3ac1860a, 0xb060c107, 0x20801203, +0x38810602, 0xb0408588, 0x4020007f, 0x327fc980, +0x00400000, 0x40800003, 0x4020007f, 0x35000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +}; diff --git a/arch/powerpc/platforms/cell/spufs/spu_save.c b/arch/powerpc/platforms/cell/spufs/spu_save.c new file mode 100644 index 0000000..196033b --- /dev/null +++ b/arch/powerpc/platforms/cell/spufs/spu_save.c @@ -0,0 +1,195 @@ +/* + * spu_save.c + * + * (C) Copyright IBM Corp. 2005 + * + * SPU-side context save sequence outlined in + * Synergistic Processor Element Book IV + * + * Author: Mark Nutter + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#ifndef LS_SIZE +#define LS_SIZE 0x40000 /* 256K (in bytes) */ +#endif + +typedef unsigned int u32; +typedef unsigned long long u64; + +#include +#include +#include "spu_utils.h" + +static inline void save_event_mask(void) +{ + unsigned int offset; + + /* Save, Step 2: + * Read the SPU_RdEventMsk channel and save to the LSCSA. + */ + offset = LSCSA_QW_OFFSET(event_mask); + regs_spill[offset].slot[0] = spu_readch(SPU_RdEventStatMask); +} + +static inline void save_tag_mask(void) +{ + unsigned int offset; + + /* Save, Step 3: + * Read the SPU_RdTagMsk channel and save to the LSCSA. + */ + offset = LSCSA_QW_OFFSET(tag_mask); + regs_spill[offset].slot[0] = spu_readch(MFC_RdTagMask); +} + +static inline void save_upper_240kb(addr64 lscsa_ea) +{ + unsigned int ls = 16384; + unsigned int list = (unsigned int)&dma_list[0]; + unsigned int size = sizeof(dma_list); + unsigned int tag_id = 0; + unsigned int cmd = 0x24; /* PUTL */ + + /* Save, Step 7: + * Enqueue the PUTL command (tag 0) to the MFC SPU command + * queue to transfer the remaining 240 kb of LS to CSA. + */ + spu_writech(MFC_LSA, ls); + spu_writech(MFC_EAH, lscsa_ea.ui[0]); + spu_writech(MFC_EAL, list); + spu_writech(MFC_Size, size); + spu_writech(MFC_TagID, tag_id); + spu_writech(MFC_Cmd, cmd); +} + +static inline void save_fpcr(void) +{ + // vector unsigned int fpcr; + unsigned int offset; + + /* Save, Step 9: + * Issue the floating-point status and control register + * read instruction, and save to the LSCSA. + */ + offset = LSCSA_QW_OFFSET(fpcr); + regs_spill[offset].v = spu_mffpscr(); +} + +static inline void save_decr(void) +{ + unsigned int offset; + + /* Save, Step 10: + * Read and save the SPU_RdDec channel data to + * the LSCSA. + */ + offset = LSCSA_QW_OFFSET(decr); + regs_spill[offset].slot[0] = spu_readch(SPU_RdDec); +} + +static inline void save_srr0(void) +{ + unsigned int offset; + + /* Save, Step 11: + * Read and save the SPU_WSRR0 channel data to + * the LSCSA. + */ + offset = LSCSA_QW_OFFSET(srr0); + regs_spill[offset].slot[0] = spu_readch(SPU_RdSRR0); +} + +static inline void spill_regs_to_mem(addr64 lscsa_ea) +{ + unsigned int ls = (unsigned int)®s_spill[0]; + unsigned int size = sizeof(regs_spill); + unsigned int tag_id = 0; + unsigned int cmd = 0x20; /* PUT */ + + /* Save, Step 13: + * Enqueue a PUT command (tag 0) to send the LSCSA + * to the CSA. + */ + spu_writech(MFC_LSA, ls); + spu_writech(MFC_EAH, lscsa_ea.ui[0]); + spu_writech(MFC_EAL, lscsa_ea.ui[1]); + spu_writech(MFC_Size, size); + spu_writech(MFC_TagID, tag_id); + spu_writech(MFC_Cmd, cmd); +} + +static inline void enqueue_sync(addr64 lscsa_ea) +{ + unsigned int tag_id = 0; + unsigned int cmd = 0xCC; + + /* Save, Step 14: + * Enqueue an MFC_SYNC command (tag 0). + */ + spu_writech(MFC_TagID, tag_id); + spu_writech(MFC_Cmd, cmd); +} + +static inline void save_complete(void) +{ + /* Save, Step 18: + * Issue a stop-and-signal instruction indicating + * "save complete". Note: This function will not + * return!! + */ + spu_stop(SPU_SAVE_COMPLETE); +} + +/** + * main - entry point for SPU-side context save. + * + * This code deviates from the documented sequence as follows: + * + * 1. The EA for LSCSA is passed from PPE in the + * signal notification channels. + * 2. All 128 registers are saved by crt0.o. + */ +int main() +{ + addr64 lscsa_ea; + + lscsa_ea.ui[0] = spu_readch(SPU_RdSigNotify1); + lscsa_ea.ui[1] = spu_readch(SPU_RdSigNotify2); + + /* Step 1: done by exit(). */ + save_event_mask(); /* Step 2. */ + save_tag_mask(); /* Step 3. */ + set_event_mask(); /* Step 4. */ + set_tag_mask(); /* Step 5. */ + build_dma_list(lscsa_ea); /* Step 6. */ + save_upper_240kb(lscsa_ea); /* Step 7. */ + /* Step 8: done by exit(). */ + save_fpcr(); /* Step 9. */ + save_decr(); /* Step 10. */ + save_srr0(); /* Step 11. */ + enqueue_putllc(lscsa_ea); /* Step 12. */ + spill_regs_to_mem(lscsa_ea); /* Step 13. */ + enqueue_sync(lscsa_ea); /* Step 14. */ + set_tag_update(); /* Step 15. */ + read_tag_status(); /* Step 16. */ + read_llar_status(); /* Step 17. */ + save_complete(); /* Step 18. */ + + return 0; +} diff --git a/arch/powerpc/platforms/cell/spufs/spu_save_crt0.S b/arch/powerpc/platforms/cell/spufs/spu_save_crt0.S new file mode 100644 index 0000000..6659d6a --- /dev/null +++ b/arch/powerpc/platforms/cell/spufs/spu_save_crt0.S @@ -0,0 +1,102 @@ +/* + * crt0_s.S: Entry function for SPU-side context save. + * + * Copyright (C) 2005 IBM + * + * Entry function for SPU-side of the context save sequence. + * Saves all 128 GPRs, sets up an initial stack frame, then + * branches to 'main'. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include + +.data +.align 7 +.globl regs_spill +regs_spill: +.space SIZEOF_SPU_SPILL_REGS, 0x0 + +.text +.global _start +_start: + /* SPU Context Save Step 1: Save the first 16 GPRs. */ + stqa $0, regs_spill + 0 + stqa $1, regs_spill + 16 + stqa $2, regs_spill + 32 + stqa $3, regs_spill + 48 + stqa $4, regs_spill + 64 + stqa $5, regs_spill + 80 + stqa $6, regs_spill + 96 + stqa $7, regs_spill + 112 + stqa $8, regs_spill + 128 + stqa $9, regs_spill + 144 + stqa $10, regs_spill + 160 + stqa $11, regs_spill + 176 + stqa $12, regs_spill + 192 + stqa $13, regs_spill + 208 + stqa $14, regs_spill + 224 + stqa $15, regs_spill + 240 + + /* SPU Context Save, Step 8: Save the remaining 112 GPRs. */ + ila $3, regs_spill + 256 +save_regs: + lqr $4, save_reg_insts +save_reg_loop: + ai $4, $4, 4 + .balignl 16, 0x40200000 +save_reg_insts: /* must be quad-word aligned. */ + stqd $16, 0($3) + stqd $17, 16($3) + stqd $18, 32($3) + stqd $19, 48($3) + andi $5, $4, 0x7F + stqr $4, save_reg_insts + ai $3, $3, 64 + brnz $5, save_reg_loop + + /* Initialize the stack pointer to point to 16368 + * (16kb-16). The back chain pointer is initialized + * to NULL. + */ + il $0, 0 + il $SP, 16368 + stqd $0, 0($SP) + + /* Allocate a minimum stack frame for the called main. + * This is needed so that main has a place to save the + * link register when it calls another function. + */ + stqd $SP, -160($SP) + ai $SP, $SP, -160 + + /* Call the program's main function. */ + brsl $0, main + + /* In this case main should not return; if it does + * there has been an error in the sequence. Execute + * stop-and-signal with code=0. + */ +.global exit +.global _exit +exit: +_exit: + stop 0x0 + + /* Pad the size of this crt0.o to be multiple of 16 bytes. */ +.balignl 16, 0x0 + diff --git a/arch/powerpc/platforms/cell/spufs/spu_save_dump.h_shipped b/arch/powerpc/platforms/cell/spufs/spu_save_dump.h_shipped new file mode 100644 index 0000000..39e5400 --- /dev/null +++ b/arch/powerpc/platforms/cell/spufs/spu_save_dump.h_shipped @@ -0,0 +1,191 @@ +/* + * spu_save_dump.h: Copyright (C) 2005 IBM. + * Hex-dump auto generated from spu_save.c. + * Do not edit! + */ +static unsigned int spu_save_code[] __page_aligned = { +0x20805000, 0x20805201, 0x20805402, 0x20805603, +0x20805804, 0x20805a05, 0x20805c06, 0x20805e07, +0x20806008, 0x20806209, 0x2080640a, 0x2080660b, +0x2080680c, 0x20806a0d, 0x20806c0e, 0x20806e0f, +0x4201c003, 0x33800184, 0x1c010204, 0x40200000, +0x24000190, 0x24004191, 0x24008192, 0x2400c193, +0x141fc205, 0x23fffd84, 0x1c100183, 0x217ffb85, +0x40800000, 0x409ff801, 0x24000080, 0x24fd8081, +0x1cd80081, 0x33000180, 0x00000000, 0x00000000, +0x01a00182, 0x3ec00083, 0xb1c38103, 0x01a00204, +0x3ec10082, 0x4201400d, 0xb1c38202, 0x01a00583, +0x34218682, 0x3ed80684, 0xb0408184, 0x24218682, +0x01a00603, 0x00200000, 0x34214682, 0x3ed40684, +0xb0408184, 0x40800003, 0x24214682, 0x21a00083, +0x40800082, 0x21a00b02, 0x4020007f, 0x1000251e, +0x40a80002, 0x32800008, 0x4205c00c, 0x00200000, +0x40a0000b, 0x3f82070f, 0x4080020a, 0x40800709, +0x3fe3078f, 0x3fbf0783, 0x3f200183, 0x3fbe0183, +0x3fe30187, 0x18008387, 0x4205c002, 0x3ac30404, +0x1cffc489, 0x00200000, 0x18008403, 0x38830402, +0x4cffc486, 0x3ac28185, 0xb0408584, 0x28830402, +0x1c020408, 0x38828182, 0xb0408385, 0x1802c387, +0x28828182, 0x217ff886, 0x04000582, 0x32800007, +0x21a00802, 0x3fbf0705, 0x3f200285, 0x3fbe0285, +0x3fe30285, 0x21a00885, 0x04000603, 0x21a00903, +0x40803c02, 0x21a00982, 0x04000386, 0x21a00a06, +0x40801202, 0x21a00a82, 0x73000003, 0x24200683, +0x01a00404, 0x00200000, 0x34204682, 0x3ec40683, +0xb0408203, 0x24204682, 0x01a00783, 0x00200000, +0x3421c682, 0x3edc0684, 0xb0408184, 0x2421c682, +0x21a00806, 0x21a00885, 0x3fbf0784, 0x3f200204, +0x3fbe0204, 0x3fe30204, 0x21a00904, 0x40804002, +0x21a00982, 0x21a00a06, 0x40805a02, 0x21a00a82, +0x04000683, 0x21a00803, 0x21a00885, 0x21a00904, +0x40848002, 0x21a00982, 0x21a00a06, 0x40801002, +0x21a00a82, 0x21a00a06, 0x40806602, 0x00200000, +0x35800009, 0x21a00a82, 0x40800083, 0x21a00b83, +0x01a00c02, 0x01a00d83, 0x00003ffb, 0x40800003, +0x4020007f, 0x35000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +}; diff --git a/arch/powerpc/platforms/cell/spufs/spu_utils.h b/arch/powerpc/platforms/cell/spufs/spu_utils.h new file mode 100644 index 0000000..58359fe --- /dev/null +++ b/arch/powerpc/platforms/cell/spufs/spu_utils.h @@ -0,0 +1,160 @@ +/* + * utils.h: Utilities for SPU-side of the context switch operation. + * + * (C) Copyright IBM 2005 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _SPU_CONTEXT_UTILS_H_ +#define _SPU_CONTEXT_UTILS_H_ + +/* + * 64-bit safe EA. + */ +typedef union { + unsigned long long ull; + unsigned int ui[2]; +} addr64; + +/* + * 128-bit register template. + */ +typedef union { + unsigned int slot[4]; + vector unsigned int v; +} spu_reg128v; + +/* + * DMA list structure. + */ +struct dma_list_elem { + unsigned int size; + unsigned int ea_low; +}; + +/* + * Declare storage for 8-byte aligned DMA list. + */ +struct dma_list_elem dma_list[15] __attribute__ ((aligned(8))); + +/* + * External definition for storage + * declared in crt0. + */ +extern spu_reg128v regs_spill[NR_SPU_SPILL_REGS]; + +/* + * Compute LSCSA byte offset for a given field. + */ +static struct spu_lscsa *dummy = (struct spu_lscsa *)0; +#define LSCSA_BYTE_OFFSET(_field) \ + ((char *)(&(dummy->_field)) - (char *)(&(dummy->gprs[0].slot[0]))) +#define LSCSA_QW_OFFSET(_field) (LSCSA_BYTE_OFFSET(_field) >> 4) + +static inline void set_event_mask(void) +{ + unsigned int event_mask = 0; + + /* Save, Step 4: + * Restore, Step 1: + * Set the SPU_RdEventMsk channel to zero to mask + * all events. + */ + spu_writech(SPU_WrEventMask, event_mask); +} + +static inline void set_tag_mask(void) +{ + unsigned int tag_mask = 1; + + /* Save, Step 5: + * Restore, Step 2: + * Set the SPU_WrTagMsk channel to '01' to unmask + * only tag group 0. + */ + spu_writech(MFC_WrTagMask, tag_mask); +} + +static inline void build_dma_list(addr64 lscsa_ea) +{ + unsigned int ea_low; + int i; + + /* Save, Step 6: + * Restore, Step 3: + * Update the effective address for the CSA in the + * pre-canned DMA-list in local storage. + */ + ea_low = lscsa_ea.ui[1]; + ea_low += LSCSA_BYTE_OFFSET(ls[16384]); + + for (i = 0; i < 15; i++, ea_low += 16384) { + dma_list[i].size = 16384; + dma_list[i].ea_low = ea_low; + } +} + +static inline void enqueue_putllc(addr64 lscsa_ea) +{ + unsigned int ls = 0; + unsigned int size = 128; + unsigned int tag_id = 0; + unsigned int cmd = 0xB4; /* PUTLLC */ + + /* Save, Step 12: + * Restore, Step 7: + * Send a PUTLLC (tag 0) command to the MFC using + * an effective address in the CSA in order to + * remove any possible lock-line reservation. + */ + spu_writech(MFC_LSA, ls); + spu_writech(MFC_EAH, lscsa_ea.ui[0]); + spu_writech(MFC_EAL, lscsa_ea.ui[1]); + spu_writech(MFC_Size, size); + spu_writech(MFC_TagID, tag_id); + spu_writech(MFC_Cmd, cmd); +} + +static inline void set_tag_update(void) +{ + unsigned int update_any = 1; + + /* Save, Step 15: + * Restore, Step 8: + * Write the MFC_TagUpdate channel with '01'. + */ + spu_writech(MFC_WrTagUpdate, update_any); +} + +static inline void read_tag_status(void) +{ + /* Save, Step 16: + * Restore, Step 9: + * Read the MFC_TagStat channel data. + */ + spu_readch(MFC_RdTagStat); +} + +static inline void read_llar_status(void) +{ + /* Save, Step 17: + * Restore, Step 10: + * Read the MFC_AtomicStat channel data. + */ + spu_readch(MFC_RdAtomicStat); +} + +#endif /* _SPU_CONTEXT_UTILS_H_ */ diff --git a/arch/powerpc/platforms/cell/spufs/spufs.h b/arch/powerpc/platforms/cell/spufs/spufs.h new file mode 100644 index 0000000..93c6a05 --- /dev/null +++ b/arch/powerpc/platforms/cell/spufs/spufs.h @@ -0,0 +1,122 @@ +/* + * SPU file system + * + * (C) Copyright IBM Deutschland Entwicklung GmbH 2005 + * + * Author: Arnd Bergmann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#ifndef SPUFS_H +#define SPUFS_H + +#include +#include +#include +#include + +#include +#include + +/* The magic number for our file system */ +enum { + SPUFS_MAGIC = 0x23c9b64e, +}; + +struct spu_context_ops; + +struct spu_context { + struct spu *spu; /* pointer to a physical SPU */ + struct spu_state csa; /* SPU context save area. */ + spinlock_t mmio_lock; /* protects mmio access */ + struct address_space *local_store;/* local store backing store */ + + enum { SPU_STATE_RUNNABLE, SPU_STATE_SAVED } state; + struct rw_semaphore state_sema; + + struct mm_struct *owner; + + struct kref kref; + wait_queue_head_t ibox_wq; + wait_queue_head_t wbox_wq; + struct fasync_struct *ibox_fasync; + struct fasync_struct *wbox_fasync; + struct spu_context_ops *ops; +}; + +/* SPU context query/set operations. */ +struct spu_context_ops { + int (*mbox_read) (struct spu_context * ctx, u32 * data); + u32(*mbox_stat_read) (struct spu_context * ctx); + int (*ibox_read) (struct spu_context * ctx, u32 * data); + int (*wbox_write) (struct spu_context * ctx, u32 data); + u32(*signal1_read) (struct spu_context * ctx); + void (*signal1_write) (struct spu_context * ctx, u32 data); + u32(*signal2_read) (struct spu_context * ctx); + void (*signal2_write) (struct spu_context * ctx, u32 data); + void (*signal1_type_set) (struct spu_context * ctx, u64 val); + u64(*signal1_type_get) (struct spu_context * ctx); + void (*signal2_type_set) (struct spu_context * ctx, u64 val); + u64(*signal2_type_get) (struct spu_context * ctx); + u32(*npc_read) (struct spu_context * ctx); + void (*npc_write) (struct spu_context * ctx, u32 data); + u32(*status_read) (struct spu_context * ctx); + char*(*get_ls) (struct spu_context * ctx); +}; + +extern struct spu_context_ops spu_hw_ops; +extern struct spu_context_ops spu_backing_ops; + +struct spufs_inode_info { + struct spu_context *i_ctx; + struct inode vfs_inode; +}; +#define SPUFS_I(inode) \ + container_of(inode, struct spufs_inode_info, vfs_inode) + +extern struct tree_descr spufs_dir_contents[]; + +/* system call implementation */ +long spufs_run_spu(struct file *file, + struct spu_context *ctx, u32 *npc, u32 *status); +long spufs_create_thread(struct nameidata *nd, const char *name, + unsigned int flags, mode_t mode); + +/* context management */ +struct spu_context * alloc_spu_context(struct address_space *local_store); +void destroy_spu_context(struct kref *kref); +struct spu_context * get_spu_context(struct spu_context *ctx); +int put_spu_context(struct spu_context *ctx); + +void spu_forget(struct spu_context *ctx); +void spu_acquire(struct spu_context *ctx); +void spu_release(struct spu_context *ctx); +int spu_acquire_runnable(struct spu_context *ctx); +void spu_acquire_saved(struct spu_context *ctx); + +int spu_activate(struct spu_context *ctx, u64 flags); +void spu_deactivate(struct spu_context *ctx); +void spu_yield(struct spu_context *ctx); +int __init spu_sched_init(void); +void __exit spu_sched_exit(void); + +size_t spu_wbox_write(struct spu_context *ctx, u32 data); +size_t spu_ibox_read(struct spu_context *ctx, u32 *data); + +/* irq callback funcs. */ +void spufs_ibox_callback(struct spu *spu); +void spufs_wbox_callback(struct spu *spu); + +#endif diff --git a/arch/powerpc/platforms/cell/spufs/switch.c b/arch/powerpc/platforms/cell/spufs/switch.c new file mode 100644 index 0000000..5126625 --- /dev/null +++ b/arch/powerpc/platforms/cell/spufs/switch.c @@ -0,0 +1,2215 @@ +/* + * spu_switch.c + * + * (C) Copyright IBM Corp. 2005 + * + * Author: Mark Nutter + * + * Host-side part of SPU context switch sequence outlined in + * Synergistic Processor Element, Book IV. + * + * A fully premptive switch of an SPE is very expensive in terms + * of time and system resources. SPE Book IV indicates that SPE + * allocation should follow a "serially reusable device" model, + * in which the SPE is assigned a task until it completes. When + * this is not possible, this sequence may be used to premptively + * save, and then later (optionally) restore the context of a + * program executing on an SPE. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "spu_save_dump.h" +#include "spu_restore_dump.h" + +#if 0 +#define POLL_WHILE_TRUE(_c) { \ + do { \ + } while (_c); \ + } +#else +#define RELAX_SPIN_COUNT 1000 +#define POLL_WHILE_TRUE(_c) { \ + do { \ + int _i; \ + for (_i=0; _iproblem; + u32 isolate_state; + + /* Save, Step 2: + * Save, Step 6: + * If SPU_Status[E,L,IS] any field is '1', this + * SPU is in isolate state and cannot be context + * saved at this time. + */ + isolate_state = SPU_STATUS_ISOLATED_STATE | + SPU_STATUS_ISOLATED_LOAD_STAUTUS | SPU_STATUS_ISOLATED_EXIT_STAUTUS; + return (in_be32(&prob->spu_status_R) & isolate_state) ? 1 : 0; +} + +static inline void disable_interrupts(struct spu_state *csa, struct spu *spu) +{ + struct spu_priv1 __iomem *priv1 = spu->priv1; + + /* Save, Step 3: + * Restore, Step 2: + * Save INT_Mask_class0 in CSA. + * Write INT_MASK_class0 with value of 0. + * Save INT_Mask_class1 in CSA. + * Write INT_MASK_class1 with value of 0. + * Save INT_Mask_class2 in CSA. + * Write INT_MASK_class2 with value of 0. + */ + spin_lock_irq(&spu->register_lock); + if (csa) { + csa->priv1.int_mask_class0_RW = + in_be64(&priv1->int_mask_class0_RW); + csa->priv1.int_mask_class1_RW = + in_be64(&priv1->int_mask_class1_RW); + csa->priv1.int_mask_class2_RW = + in_be64(&priv1->int_mask_class2_RW); + } + out_be64(&priv1->int_mask_class0_RW, 0UL); + out_be64(&priv1->int_mask_class1_RW, 0UL); + out_be64(&priv1->int_mask_class2_RW, 0UL); + eieio(); + spin_unlock_irq(&spu->register_lock); +} + +static inline void set_watchdog_timer(struct spu_state *csa, struct spu *spu) +{ + /* Save, Step 4: + * Restore, Step 25. + * Set a software watchdog timer, which specifies the + * maximum allowable time for a context save sequence. + * + * For present, this implementation will not set a global + * watchdog timer, as virtualization & variable system load + * may cause unpredictable execution times. + */ +} + +static inline void inhibit_user_access(struct spu_state *csa, struct spu *spu) +{ + /* Save, Step 5: + * Restore, Step 3: + * Inhibit user-space access (if provided) to this + * SPU by unmapping the virtual pages assigned to + * the SPU memory-mapped I/O (MMIO) for problem + * state. TBD. + */ +} + +static inline void set_switch_pending(struct spu_state *csa, struct spu *spu) +{ + /* Save, Step 7: + * Restore, Step 5: + * Set a software context switch pending flag. + */ + set_bit(SPU_CONTEXT_SWITCH_PENDING_nr, &spu->flags); + mb(); +} + +static inline void save_mfc_cntl(struct spu_state *csa, struct spu *spu) +{ + struct spu_priv2 __iomem *priv2 = spu->priv2; + + /* Save, Step 8: + * Read and save MFC_CNTL[Ss]. + */ + if (csa) { + csa->priv2.mfc_control_RW = in_be64(&priv2->mfc_control_RW) & + MFC_CNTL_SUSPEND_DMA_STATUS_MASK; + } +} + +static inline void save_spu_runcntl(struct spu_state *csa, struct spu *spu) +{ + struct spu_problem __iomem *prob = spu->problem; + + /* Save, Step 9: + * Save SPU_Runcntl in the CSA. This value contains + * the "Application Desired State". + */ + csa->prob.spu_runcntl_RW = in_be32(&prob->spu_runcntl_RW); +} + +static inline void save_mfc_sr1(struct spu_state *csa, struct spu *spu) +{ + struct spu_priv1 __iomem *priv1 = spu->priv1; + + /* Save, Step 10: + * Save MFC_SR1 in the CSA. + */ + csa->priv1.mfc_sr1_RW = in_be64(&priv1->mfc_sr1_RW); +} + +static inline void save_spu_status(struct spu_state *csa, struct spu *spu) +{ + struct spu_problem __iomem *prob = spu->problem; + + /* Save, Step 11: + * Read SPU_Status[R], and save to CSA. + */ + if ((in_be32(&prob->spu_status_R) & SPU_STATUS_RUNNING) == 0) { + csa->prob.spu_status_R = in_be32(&prob->spu_status_R); + } else { + u32 stopped; + + out_be32(&prob->spu_runcntl_RW, SPU_RUNCNTL_STOP); + eieio(); + POLL_WHILE_TRUE(in_be32(&prob->spu_status_R) & + SPU_STATUS_RUNNING); + stopped = + SPU_STATUS_INVALID_INSTR | SPU_STATUS_SINGLE_STEP | + SPU_STATUS_STOPPED_BY_HALT | SPU_STATUS_STOPPED_BY_STOP; + if ((in_be32(&prob->spu_status_R) & stopped) == 0) + csa->prob.spu_status_R = SPU_STATUS_RUNNING; + else + csa->prob.spu_status_R = in_be32(&prob->spu_status_R); + } +} + +static inline void save_mfc_decr(struct spu_state *csa, struct spu *spu) +{ + struct spu_priv2 __iomem *priv2 = spu->priv2; + + /* Save, Step 12: + * Read MFC_CNTL[Ds]. Update saved copy of + * CSA.MFC_CNTL[Ds]. + */ + if (in_be64(&priv2->mfc_control_RW) & MFC_CNTL_DECREMENTER_RUNNING) { + csa->priv2.mfc_control_RW |= MFC_CNTL_DECREMENTER_RUNNING; + csa->suspend_time = get_cycles(); + out_be64(&priv2->spu_chnlcntptr_RW, 7ULL); + eieio(); + csa->spu_chnldata_RW[7] = in_be64(&priv2->spu_chnldata_RW); + eieio(); + } +} + +static inline void halt_mfc_decr(struct spu_state *csa, struct spu *spu) +{ + struct spu_priv2 __iomem *priv2 = spu->priv2; + + /* Save, Step 13: + * Write MFC_CNTL[Dh] set to a '1' to halt + * the decrementer. + */ + out_be64(&priv2->mfc_control_RW, MFC_CNTL_DECREMENTER_HALTED); + eieio(); +} + +static inline void save_timebase(struct spu_state *csa, struct spu *spu) +{ + /* Save, Step 14: + * Read PPE Timebase High and Timebase low registers + * and save in CSA. TBD. + */ + csa->suspend_time = get_cycles(); +} + +static inline void remove_other_spu_access(struct spu_state *csa, + struct spu *spu) +{ + /* Save, Step 15: + * Remove other SPU access to this SPU by unmapping + * this SPU's pages from their address space. TBD. + */ +} + +static inline void do_mfc_mssync(struct spu_state *csa, struct spu *spu) +{ + struct spu_problem __iomem *prob = spu->problem; + + /* Save, Step 16: + * Restore, Step 11. + * Write SPU_MSSync register. Poll SPU_MSSync[P] + * for a value of 0. + */ + out_be64(&prob->spc_mssync_RW, 1UL); + POLL_WHILE_TRUE(in_be64(&prob->spc_mssync_RW) & MS_SYNC_PENDING); +} + +static inline void issue_mfc_tlbie(struct spu_state *csa, struct spu *spu) +{ + struct spu_priv1 __iomem *priv1 = spu->priv1; + + /* Save, Step 17: + * Restore, Step 12. + * Restore, Step 48. + * Write TLB_Invalidate_Entry[IS,VPN,L,Lp]=0 register. + * Then issue a PPE sync instruction. + */ + out_be64(&priv1->tlb_invalidate_entry_W, 0UL); + mb(); +} + +static inline void handle_pending_interrupts(struct spu_state *csa, + struct spu *spu) +{ + /* Save, Step 18: + * Handle any pending interrupts from this SPU + * here. This is OS or hypervisor specific. One + * option is to re-enable interrupts to handle any + * pending interrupts, with the interrupt handlers + * recognizing the software Context Switch Pending + * flag, to ensure the SPU execution or MFC command + * queue is not restarted. TBD. + */ +} + +static inline void save_mfc_queues(struct spu_state *csa, struct spu *spu) +{ + struct spu_priv2 __iomem *priv2 = spu->priv2; + int i; + + /* Save, Step 19: + * If MFC_Cntl[Se]=0 then save + * MFC command queues. + */ + if ((in_be64(&priv2->mfc_control_RW) & MFC_CNTL_DMA_QUEUES_EMPTY) == 0) { + for (i = 0; i < 8; i++) { + csa->priv2.puq[i].mfc_cq_data0_RW = + in_be64(&priv2->puq[i].mfc_cq_data0_RW); + csa->priv2.puq[i].mfc_cq_data1_RW = + in_be64(&priv2->puq[i].mfc_cq_data1_RW); + csa->priv2.puq[i].mfc_cq_data2_RW = + in_be64(&priv2->puq[i].mfc_cq_data2_RW); + csa->priv2.puq[i].mfc_cq_data3_RW = + in_be64(&priv2->puq[i].mfc_cq_data3_RW); + } + for (i = 0; i < 16; i++) { + csa->priv2.spuq[i].mfc_cq_data0_RW = + in_be64(&priv2->spuq[i].mfc_cq_data0_RW); + csa->priv2.spuq[i].mfc_cq_data1_RW = + in_be64(&priv2->spuq[i].mfc_cq_data1_RW); + csa->priv2.spuq[i].mfc_cq_data2_RW = + in_be64(&priv2->spuq[i].mfc_cq_data2_RW); + csa->priv2.spuq[i].mfc_cq_data3_RW = + in_be64(&priv2->spuq[i].mfc_cq_data3_RW); + } + } +} + +static inline void save_ppu_querymask(struct spu_state *csa, struct spu *spu) +{ + struct spu_problem __iomem *prob = spu->problem; + + /* Save, Step 20: + * Save the PPU_QueryMask register + * in the CSA. + */ + csa->prob.dma_querymask_RW = in_be32(&prob->dma_querymask_RW); +} + +static inline void save_ppu_querytype(struct spu_state *csa, struct spu *spu) +{ + struct spu_problem __iomem *prob = spu->problem; + + /* Save, Step 21: + * Save the PPU_QueryType register + * in the CSA. + */ + csa->prob.dma_querytype_RW = in_be32(&prob->dma_querytype_RW); +} + +static inline void save_mfc_csr_tsq(struct spu_state *csa, struct spu *spu) +{ + struct spu_priv2 __iomem *priv2 = spu->priv2; + + /* Save, Step 22: + * Save the MFC_CSR_TSQ register + * in the LSCSA. + */ + csa->priv2.spu_tag_status_query_RW = + in_be64(&priv2->spu_tag_status_query_RW); +} + +static inline void save_mfc_csr_cmd(struct spu_state *csa, struct spu *spu) +{ + struct spu_priv2 __iomem *priv2 = spu->priv2; + + /* Save, Step 23: + * Save the MFC_CSR_CMD1 and MFC_CSR_CMD2 + * registers in the CSA. + */ + csa->priv2.spu_cmd_buf1_RW = in_be64(&priv2->spu_cmd_buf1_RW); + csa->priv2.spu_cmd_buf2_RW = in_be64(&priv2->spu_cmd_buf2_RW); +} + +static inline void save_mfc_csr_ato(struct spu_state *csa, struct spu *spu) +{ + struct spu_priv2 __iomem *priv2 = spu->priv2; + + /* Save, Step 24: + * Save the MFC_CSR_ATO register in + * the CSA. + */ + csa->priv2.spu_atomic_status_RW = in_be64(&priv2->spu_atomic_status_RW); +} + +static inline void save_mfc_tclass_id(struct spu_state *csa, struct spu *spu) +{ + struct spu_priv1 __iomem *priv1 = spu->priv1; + + /* Save, Step 25: + * Save the MFC_TCLASS_ID register in + * the CSA. + */ + csa->priv1.mfc_tclass_id_RW = in_be64(&priv1->mfc_tclass_id_RW); +} + +static inline void set_mfc_tclass_id(struct spu_state *csa, struct spu *spu) +{ + struct spu_priv1 __iomem *priv1 = spu->priv1; + + /* Save, Step 26: + * Restore, Step 23. + * Write the MFC_TCLASS_ID register with + * the value 0x10000000. + */ + out_be64(&priv1->mfc_tclass_id_RW, 0x10000000); + eieio(); +} + +static inline void purge_mfc_queue(struct spu_state *csa, struct spu *spu) +{ + struct spu_priv2 __iomem *priv2 = spu->priv2; + + /* Save, Step 27: + * Restore, Step 14. + * Write MFC_CNTL[Pc]=1 (purge queue). + */ + out_be64(&priv2->mfc_control_RW, MFC_CNTL_PURGE_DMA_REQUEST); + eieio(); +} + +static inline void wait_purge_complete(struct spu_state *csa, struct spu *spu) +{ + struct spu_priv2 __iomem *priv2 = spu->priv2; + + /* Save, Step 28: + * Poll MFC_CNTL[Ps] until value '11' is read + * (purge complete). + */ + POLL_WHILE_FALSE(in_be64(&priv2->mfc_control_RW) & + MFC_CNTL_PURGE_DMA_COMPLETE); +} + +static inline void save_mfc_slbs(struct spu_state *csa, struct spu *spu) +{ + struct spu_priv1 __iomem *priv1 = spu->priv1; + struct spu_priv2 __iomem *priv2 = spu->priv2; + int i; + + /* Save, Step 29: + * If MFC_SR1[R]='1', save SLBs in CSA. + */ + if (in_be64(&priv1->mfc_sr1_RW) & MFC_STATE1_RELOCATE_MASK) { + csa->priv2.slb_index_W = in_be64(&priv2->slb_index_W); + for (i = 0; i < 8; i++) { + out_be64(&priv2->slb_index_W, i); + eieio(); + csa->slb_esid_RW[i] = in_be64(&priv2->slb_esid_RW); + csa->slb_vsid_RW[i] = in_be64(&priv2->slb_vsid_RW); + eieio(); + } + } +} + +static inline void setup_mfc_sr1(struct spu_state *csa, struct spu *spu) +{ + struct spu_priv1 __iomem *priv1 = spu->priv1; + + /* Save, Step 30: + * Restore, Step 18: + * Write MFC_SR1 with MFC_SR1[D=0,S=1] and + * MFC_SR1[TL,R,Pr,T] set correctly for the + * OS specific environment. + * + * Implementation note: The SPU-side code + * for save/restore is privileged, so the + * MFC_SR1[Pr] bit is not set. + * + */ + out_be64(&priv1->mfc_sr1_RW, (MFC_STATE1_MASTER_RUN_CONTROL_MASK | + MFC_STATE1_RELOCATE_MASK | + MFC_STATE1_BUS_TLBIE_MASK)); +} + +static inline void save_spu_npc(struct spu_state *csa, struct spu *spu) +{ + struct spu_problem __iomem *prob = spu->problem; + + /* Save, Step 31: + * Save SPU_NPC in the CSA. + */ + csa->prob.spu_npc_RW = in_be32(&prob->spu_npc_RW); +} + +static inline void save_spu_privcntl(struct spu_state *csa, struct spu *spu) +{ + struct spu_priv2 __iomem *priv2 = spu->priv2; + + /* Save, Step 32: + * Save SPU_PrivCntl in the CSA. + */ + csa->priv2.spu_privcntl_RW = in_be64(&priv2->spu_privcntl_RW); +} + +static inline void reset_spu_privcntl(struct spu_state *csa, struct spu *spu) +{ + struct spu_priv2 __iomem *priv2 = spu->priv2; + + /* Save, Step 33: + * Restore, Step 16: + * Write SPU_PrivCntl[S,Le,A] fields reset to 0. + */ + out_be64(&priv2->spu_privcntl_RW, 0UL); + eieio(); +} + +static inline void save_spu_lslr(struct spu_state *csa, struct spu *spu) +{ + struct spu_priv2 __iomem *priv2 = spu->priv2; + + /* Save, Step 34: + * Save SPU_LSLR in the CSA. + */ + csa->priv2.spu_lslr_RW = in_be64(&priv2->spu_lslr_RW); +} + +static inline void reset_spu_lslr(struct spu_state *csa, struct spu *spu) +{ + struct spu_priv2 __iomem *priv2 = spu->priv2; + + /* Save, Step 35: + * Restore, Step 17. + * Reset SPU_LSLR. + */ + out_be64(&priv2->spu_lslr_RW, LS_ADDR_MASK); + eieio(); +} + +static inline void save_spu_cfg(struct spu_state *csa, struct spu *spu) +{ + struct spu_priv2 __iomem *priv2 = spu->priv2; + + /* Save, Step 36: + * Save SPU_Cfg in the CSA. + */ + csa->priv2.spu_cfg_RW = in_be64(&priv2->spu_cfg_RW); +} + +static inline void save_pm_trace(struct spu_state *csa, struct spu *spu) +{ + /* Save, Step 37: + * Save PM_Trace_Tag_Wait_Mask in the CSA. + * Not performed by this implementation. + */ +} + +static inline void save_mfc_rag(struct spu_state *csa, struct spu *spu) +{ + struct spu_priv1 __iomem *priv1 = spu->priv1; + + /* Save, Step 38: + * Save RA_GROUP_ID register and the + * RA_ENABLE reigster in the CSA. + */ + csa->priv1.resource_allocation_groupID_RW = + in_be64(&priv1->resource_allocation_groupID_RW); + csa->priv1.resource_allocation_enable_RW = + in_be64(&priv1->resource_allocation_enable_RW); +} + +static inline void save_ppu_mb_stat(struct spu_state *csa, struct spu *spu) +{ + struct spu_problem __iomem *prob = spu->problem; + + /* Save, Step 39: + * Save MB_Stat register in the CSA. + */ + csa->prob.mb_stat_R = in_be32(&prob->mb_stat_R); +} + +static inline void save_ppu_mb(struct spu_state *csa, struct spu *spu) +{ + struct spu_problem __iomem *prob = spu->problem; + + /* Save, Step 40: + * Save the PPU_MB register in the CSA. + */ + csa->prob.pu_mb_R = in_be32(&prob->pu_mb_R); +} + +static inline void save_ppuint_mb(struct spu_state *csa, struct spu *spu) +{ + struct spu_priv2 __iomem *priv2 = spu->priv2; + + /* Save, Step 41: + * Save the PPUINT_MB register in the CSA. + */ + csa->priv2.puint_mb_R = in_be64(&priv2->puint_mb_R); +} + +static inline void save_ch_part1(struct spu_state *csa, struct spu *spu) +{ + struct spu_priv2 __iomem *priv2 = spu->priv2; + u64 idx, ch_indices[7] = { 0UL, 1UL, 3UL, 4UL, 24UL, 25UL, 27UL }; + int i; + + /* Save, Step 42: + * Save the following CH: [0,1,3,4,24,25,27] + */ + for (i = 0; i < 7; i++) { + idx = ch_indices[i]; + out_be64(&priv2->spu_chnlcntptr_RW, idx); + eieio(); + csa->spu_chnldata_RW[idx] = in_be64(&priv2->spu_chnldata_RW); + csa->spu_chnlcnt_RW[idx] = in_be64(&priv2->spu_chnlcnt_RW); + out_be64(&priv2->spu_chnldata_RW, 0UL); + out_be64(&priv2->spu_chnlcnt_RW, 0UL); + eieio(); + } +} + +static inline void save_spu_mb(struct spu_state *csa, struct spu *spu) +{ + struct spu_priv2 __iomem *priv2 = spu->priv2; + int i; + + /* Save, Step 43: + * Save SPU Read Mailbox Channel. + */ + out_be64(&priv2->spu_chnlcntptr_RW, 29UL); + eieio(); + csa->spu_chnlcnt_RW[29] = in_be64(&priv2->spu_chnlcnt_RW); + for (i = 0; i < 4; i++) { + csa->spu_mailbox_data[i] = in_be64(&priv2->spu_chnldata_RW); + } + out_be64(&priv2->spu_chnlcnt_RW, 0UL); + eieio(); +} + +static inline void save_mfc_cmd(struct spu_state *csa, struct spu *spu) +{ + struct spu_priv2 __iomem *priv2 = spu->priv2; + + /* Save, Step 44: + * Save MFC_CMD Channel. + */ + out_be64(&priv2->spu_chnlcntptr_RW, 21UL); + eieio(); + csa->spu_chnlcnt_RW[21] = in_be64(&priv2->spu_chnlcnt_RW); + eieio(); +} + +static inline void reset_ch(struct spu_state *csa, struct spu *spu) +{ + struct spu_priv2 __iomem *priv2 = spu->priv2; + u64 ch_indices[4] = { 21UL, 23UL, 28UL, 30UL }; + u64 ch_counts[4] = { 16UL, 1UL, 1UL, 1UL }; + u64 idx; + int i; + + /* Save, Step 45: + * Reset the following CH: [21, 23, 28, 30] + */ + for (i = 0; i < 4; i++) { + idx = ch_indices[i]; + out_be64(&priv2->spu_chnlcntptr_RW, idx); + eieio(); + out_be64(&priv2->spu_chnlcnt_RW, ch_counts[i]); + eieio(); + } +} + +static inline void resume_mfc_queue(struct spu_state *csa, struct spu *spu) +{ + struct spu_priv2 __iomem *priv2 = spu->priv2; + + /* Save, Step 46: + * Restore, Step 25. + * Write MFC_CNTL[Sc]=0 (resume queue processing). + */ + out_be64(&priv2->mfc_control_RW, MFC_CNTL_RESUME_DMA_QUEUE); +} + +static inline void invalidate_slbs(struct spu_state *csa, struct spu *spu) +{ + struct spu_priv1 __iomem *priv1 = spu->priv1; + struct spu_priv2 __iomem *priv2 = spu->priv2; + + /* Save, Step 45: + * Restore, Step 19: + * If MFC_SR1[R]=1, write 0 to SLB_Invalidate_All. + */ + if (in_be64(&priv1->mfc_sr1_RW) & MFC_STATE1_RELOCATE_MASK) { + out_be64(&priv2->slb_invalidate_all_W, 0UL); + eieio(); + } +} + +static inline void get_kernel_slb(u64 ea, u64 slb[2]) +{ + slb[0] = (get_kernel_vsid(ea) << SLB_VSID_SHIFT) | SLB_VSID_KERNEL; + slb[1] = (ea & ESID_MASK) | SLB_ESID_V; + + /* Large pages are used for kernel text/data, but not vmalloc. */ + if (cpu_has_feature(CPU_FTR_16M_PAGE) + && REGION_ID(ea) == KERNEL_REGION_ID) + slb[0] |= SLB_VSID_L; +} + +static inline void load_mfc_slb(struct spu *spu, u64 slb[2], int slbe) +{ + struct spu_priv2 __iomem *priv2 = spu->priv2; + + out_be64(&priv2->slb_index_W, slbe); + eieio(); + out_be64(&priv2->slb_vsid_RW, slb[0]); + out_be64(&priv2->slb_esid_RW, slb[1]); + eieio(); +} + +static inline void setup_mfc_slbs(struct spu_state *csa, struct spu *spu) +{ + u64 code_slb[2]; + u64 lscsa_slb[2]; + + /* Save, Step 47: + * Restore, Step 30. + * If MFC_SR1[R]=1, write 0 to SLB_Invalidate_All + * register, then initialize SLB_VSID and SLB_ESID + * to provide access to SPU context save code and + * LSCSA. + * + * This implementation places both the context + * switch code and LSCSA in kernel address space. + * + * Further this implementation assumes that the + * MFC_SR1[R]=1 (in other words, assume that + * translation is desired by OS environment). + */ + invalidate_slbs(csa, spu); + get_kernel_slb((unsigned long)&spu_save_code[0], code_slb); + get_kernel_slb((unsigned long)csa->lscsa, lscsa_slb); + load_mfc_slb(spu, code_slb, 0); + if ((lscsa_slb[0] != code_slb[0]) || (lscsa_slb[1] != code_slb[1])) + load_mfc_slb(spu, lscsa_slb, 1); +} + +static inline void set_switch_active(struct spu_state *csa, struct spu *spu) +{ + /* Save, Step 48: + * Restore, Step 23. + * Change the software context switch pending flag + * to context switch active. + */ + set_bit(SPU_CONTEXT_SWITCH_ACTIVE_nr, &spu->flags); + clear_bit(SPU_CONTEXT_SWITCH_PENDING_nr, &spu->flags); + mb(); +} + +static inline void enable_interrupts(struct spu_state *csa, struct spu *spu) +{ + struct spu_priv1 __iomem *priv1 = spu->priv1; + unsigned long class1_mask = CLASS1_ENABLE_SEGMENT_FAULT_INTR | + CLASS1_ENABLE_STORAGE_FAULT_INTR; + + /* Save, Step 49: + * Restore, Step 22: + * Reset and then enable interrupts, as + * needed by OS. + * + * This implementation enables only class1 + * (translation) interrupts. + */ + spin_lock_irq(&spu->register_lock); + out_be64(&priv1->int_stat_class0_RW, ~(0UL)); + out_be64(&priv1->int_stat_class1_RW, ~(0UL)); + out_be64(&priv1->int_stat_class2_RW, ~(0UL)); + out_be64(&priv1->int_mask_class0_RW, 0UL); + out_be64(&priv1->int_mask_class1_RW, class1_mask); + out_be64(&priv1->int_mask_class2_RW, 0UL); + spin_unlock_irq(&spu->register_lock); +} + +static inline int send_mfc_dma(struct spu *spu, unsigned long ea, + unsigned int ls_offset, unsigned int size, + unsigned int tag, unsigned int rclass, + unsigned int cmd) +{ + struct spu_problem __iomem *prob = spu->problem; + union mfc_tag_size_class_cmd command; + unsigned int transfer_size; + volatile unsigned int status = 0x0; + + while (size > 0) { + transfer_size = + (size > MFC_MAX_DMA_SIZE) ? MFC_MAX_DMA_SIZE : size; + command.u.mfc_size = transfer_size; + command.u.mfc_tag = tag; + command.u.mfc_rclassid = rclass; + command.u.mfc_cmd = cmd; + do { + out_be32(&prob->mfc_lsa_W, ls_offset); + out_be64(&prob->mfc_ea_W, ea); + out_be64(&prob->mfc_union_W.all64, command.all64); + status = + in_be32(&prob->mfc_union_W.by32.mfc_class_cmd32); + if (unlikely(status & 0x2)) { + cpu_relax(); + } + } while (status & 0x3); + size -= transfer_size; + ea += transfer_size; + ls_offset += transfer_size; + } + return 0; +} + +static inline void save_ls_16kb(struct spu_state *csa, struct spu *spu) +{ + unsigned long addr = (unsigned long)&csa->lscsa->ls[0]; + unsigned int ls_offset = 0x0; + unsigned int size = 16384; + unsigned int tag = 0; + unsigned int rclass = 0; + unsigned int cmd = MFC_PUT_CMD; + + /* Save, Step 50: + * Issue a DMA command to copy the first 16K bytes + * of local storage to the CSA. + */ + send_mfc_dma(spu, addr, ls_offset, size, tag, rclass, cmd); +} + +static inline void set_spu_npc(struct spu_state *csa, struct spu *spu) +{ + struct spu_problem __iomem *prob = spu->problem; + + /* Save, Step 51: + * Restore, Step 31. + * Write SPU_NPC[IE]=0 and SPU_NPC[LSA] to entry + * point address of context save code in local + * storage. + * + * This implementation uses SPU-side save/restore + * programs with entry points at LSA of 0. + */ + out_be32(&prob->spu_npc_RW, 0); + eieio(); +} + +static inline void set_signot1(struct spu_state *csa, struct spu *spu) +{ + struct spu_problem __iomem *prob = spu->problem; + union { + u64 ull; + u32 ui[2]; + } addr64; + + /* Save, Step 52: + * Restore, Step 32: + * Write SPU_Sig_Notify_1 register with upper 32-bits + * of the CSA.LSCSA effective address. + */ + addr64.ull = (u64) csa->lscsa; + out_be32(&prob->signal_notify1, addr64.ui[0]); + eieio(); +} + +static inline void set_signot2(struct spu_state *csa, struct spu *spu) +{ + struct spu_problem __iomem *prob = spu->problem; + union { + u64 ull; + u32 ui[2]; + } addr64; + + /* Save, Step 53: + * Restore, Step 33: + * Write SPU_Sig_Notify_2 register with lower 32-bits + * of the CSA.LSCSA effective address. + */ + addr64.ull = (u64) csa->lscsa; + out_be32(&prob->signal_notify2, addr64.ui[1]); + eieio(); +} + +static inline void send_save_code(struct spu_state *csa, struct spu *spu) +{ + unsigned long addr = (unsigned long)&spu_save_code[0]; + unsigned int ls_offset = 0x0; + unsigned int size = sizeof(spu_save_code); + unsigned int tag = 0; + unsigned int rclass = 0; + unsigned int cmd = MFC_GETFS_CMD; + + /* Save, Step 54: + * Issue a DMA command to copy context save code + * to local storage and start SPU. + */ + send_mfc_dma(spu, addr, ls_offset, size, tag, rclass, cmd); +} + +static inline void set_ppu_querymask(struct spu_state *csa, struct spu *spu) +{ + struct spu_problem __iomem *prob = spu->problem; + + /* Save, Step 55: + * Restore, Step 38. + * Write PPU_QueryMask=1 (enable Tag Group 0) + * and issue eieio instruction. + */ + out_be32(&prob->dma_querymask_RW, MFC_TAGID_TO_TAGMASK(0)); + eieio(); +} + +static inline void wait_tag_complete(struct spu_state *csa, struct spu *spu) +{ + struct spu_priv1 __iomem *priv1 = spu->priv1; + struct spu_problem __iomem *prob = spu->problem; + u32 mask = MFC_TAGID_TO_TAGMASK(0); + unsigned long flags; + + /* Save, Step 56: + * Restore, Step 39. + * Restore, Step 39. + * Restore, Step 46. + * Poll PPU_TagStatus[gn] until 01 (Tag group 0 complete) + * or write PPU_QueryType[TS]=01 and wait for Tag Group + * Complete Interrupt. Write INT_Stat_Class0 or + * INT_Stat_Class2 with value of 'handled'. + */ + POLL_WHILE_FALSE(in_be32(&prob->dma_tagstatus_R) & mask); + + local_irq_save(flags); + out_be64(&priv1->int_stat_class0_RW, ~(0UL)); + out_be64(&priv1->int_stat_class2_RW, ~(0UL)); + local_irq_restore(flags); +} + +static inline void wait_spu_stopped(struct spu_state *csa, struct spu *spu) +{ + struct spu_priv1 __iomem *priv1 = spu->priv1; + struct spu_problem __iomem *prob = spu->problem; + unsigned long flags; + + /* Save, Step 57: + * Restore, Step 40. + * Poll until SPU_Status[R]=0 or wait for SPU Class 0 + * or SPU Class 2 interrupt. Write INT_Stat_class0 + * or INT_Stat_class2 with value of handled. + */ + POLL_WHILE_TRUE(in_be32(&prob->spu_status_R) & SPU_STATUS_RUNNING); + + local_irq_save(flags); + out_be64(&priv1->int_stat_class0_RW, ~(0UL)); + out_be64(&priv1->int_stat_class2_RW, ~(0UL)); + local_irq_restore(flags); +} + +static inline int check_save_status(struct spu_state *csa, struct spu *spu) +{ + struct spu_problem __iomem *prob = spu->problem; + u32 complete; + + /* Save, Step 54: + * If SPU_Status[P]=1 and SPU_Status[SC] = "success", + * context save succeeded, otherwise context save + * failed. + */ + complete = ((SPU_SAVE_COMPLETE << SPU_STOP_STATUS_SHIFT) | + SPU_STATUS_STOPPED_BY_STOP); + return (in_be32(&prob->spu_status_R) != complete) ? 1 : 0; +} + +static inline void terminate_spu_app(struct spu_state *csa, struct spu *spu) +{ + /* Restore, Step 4: + * If required, notify the "using application" that + * the SPU task has been terminated. TBD. + */ +} + +static inline void suspend_mfc(struct spu_state *csa, struct spu *spu) +{ + struct spu_priv2 __iomem *priv2 = spu->priv2; + + /* Restore, Step 7: + * Restore, Step 47. + * Write MFC_Cntl[Dh,Sc]='1','1' to suspend + * the queue and halt the decrementer. + */ + out_be64(&priv2->mfc_control_RW, MFC_CNTL_SUSPEND_DMA_QUEUE | + MFC_CNTL_DECREMENTER_HALTED); + eieio(); +} + +static inline void wait_suspend_mfc_complete(struct spu_state *csa, + struct spu *spu) +{ + struct spu_priv2 __iomem *priv2 = spu->priv2; + + /* Restore, Step 8: + * Restore, Step 47. + * Poll MFC_CNTL[Ss] until 11 is returned. + */ + POLL_WHILE_FALSE(in_be64(&priv2->mfc_control_RW) & + MFC_CNTL_SUSPEND_COMPLETE); +} + +static inline int suspend_spe(struct spu_state *csa, struct spu *spu) +{ + struct spu_problem __iomem *prob = spu->problem; + + /* Restore, Step 9: + * If SPU_Status[R]=1, stop SPU execution + * and wait for stop to complete. + * + * Returns 1 if SPU_Status[R]=1 on entry. + * 0 otherwise + */ + if (in_be32(&prob->spu_status_R) & SPU_STATUS_RUNNING) { + if (in_be32(&prob->spu_status_R) & + SPU_STATUS_ISOLATED_EXIT_STAUTUS) { + POLL_WHILE_TRUE(in_be32(&prob->spu_status_R) & + SPU_STATUS_RUNNING); + } + if ((in_be32(&prob->spu_status_R) & + SPU_STATUS_ISOLATED_LOAD_STAUTUS) + || (in_be32(&prob->spu_status_R) & + SPU_STATUS_ISOLATED_STATE)) { + out_be32(&prob->spu_runcntl_RW, SPU_RUNCNTL_STOP); + eieio(); + POLL_WHILE_TRUE(in_be32(&prob->spu_status_R) & + SPU_STATUS_RUNNING); + out_be32(&prob->spu_runcntl_RW, 0x2); + eieio(); + POLL_WHILE_TRUE(in_be32(&prob->spu_status_R) & + SPU_STATUS_RUNNING); + } + if (in_be32(&prob->spu_status_R) & + SPU_STATUS_WAITING_FOR_CHANNEL) { + out_be32(&prob->spu_runcntl_RW, SPU_RUNCNTL_STOP); + eieio(); + POLL_WHILE_TRUE(in_be32(&prob->spu_status_R) & + SPU_STATUS_RUNNING); + } + return 1; + } + return 0; +} + +static inline void clear_spu_status(struct spu_state *csa, struct spu *spu) +{ + struct spu_problem __iomem *prob = spu->problem; + struct spu_priv1 __iomem *priv1 = spu->priv1; + + /* Restore, Step 10: + * If SPU_Status[R]=0 and SPU_Status[E,L,IS]=1, + * release SPU from isolate state. + */ + if (!(in_be32(&prob->spu_status_R) & SPU_STATUS_RUNNING)) { + if (in_be32(&prob->spu_status_R) & + SPU_STATUS_ISOLATED_EXIT_STAUTUS) { + out_be64(&priv1->mfc_sr1_RW, + MFC_STATE1_MASTER_RUN_CONTROL_MASK); + eieio(); + out_be32(&prob->spu_runcntl_RW, SPU_RUNCNTL_RUNNABLE); + eieio(); + POLL_WHILE_TRUE(in_be32(&prob->spu_status_R) & + SPU_STATUS_RUNNING); + } + if ((in_be32(&prob->spu_status_R) & + SPU_STATUS_ISOLATED_LOAD_STAUTUS) + || (in_be32(&prob->spu_status_R) & + SPU_STATUS_ISOLATED_STATE)) { + out_be64(&priv1->mfc_sr1_RW, + MFC_STATE1_MASTER_RUN_CONTROL_MASK); + eieio(); + out_be32(&prob->spu_runcntl_RW, 0x2); + eieio(); + POLL_WHILE_TRUE(in_be32(&prob->spu_status_R) & + SPU_STATUS_RUNNING); + } + } +} + +static inline void reset_ch_part1(struct spu_state *csa, struct spu *spu) +{ + struct spu_priv2 __iomem *priv2 = spu->priv2; + u64 ch_indices[7] = { 0UL, 1UL, 3UL, 4UL, 24UL, 25UL, 27UL }; + u64 idx; + int i; + + /* Restore, Step 20: + * Reset the following CH: [0,1,3,4,24,25,27] + */ + for (i = 0; i < 7; i++) { + idx = ch_indices[i]; + out_be64(&priv2->spu_chnlcntptr_RW, idx); + eieio(); + out_be64(&priv2->spu_chnldata_RW, 0UL); + out_be64(&priv2->spu_chnlcnt_RW, 0UL); + eieio(); + } +} + +static inline void reset_ch_part2(struct spu_state *csa, struct spu *spu) +{ + struct spu_priv2 __iomem *priv2 = spu->priv2; + u64 ch_indices[5] = { 21UL, 23UL, 28UL, 29UL, 30UL }; + u64 ch_counts[5] = { 16UL, 1UL, 1UL, 0UL, 1UL }; + u64 idx; + int i; + + /* Restore, Step 21: + * Reset the following CH: [21, 23, 28, 29, 30] + */ + for (i = 0; i < 5; i++) { + idx = ch_indices[i]; + out_be64(&priv2->spu_chnlcntptr_RW, idx); + eieio(); + out_be64(&priv2->spu_chnlcnt_RW, ch_counts[i]); + eieio(); + } +} + +static inline void setup_spu_status_part1(struct spu_state *csa, + struct spu *spu) +{ + u32 status_P = SPU_STATUS_STOPPED_BY_STOP; + u32 status_I = SPU_STATUS_INVALID_INSTR; + u32 status_H = SPU_STATUS_STOPPED_BY_HALT; + u32 status_S = SPU_STATUS_SINGLE_STEP; + u32 status_S_I = SPU_STATUS_SINGLE_STEP | SPU_STATUS_INVALID_INSTR; + u32 status_S_P = SPU_STATUS_SINGLE_STEP | SPU_STATUS_STOPPED_BY_STOP; + u32 status_P_H = SPU_STATUS_STOPPED_BY_HALT |SPU_STATUS_STOPPED_BY_STOP; + u32 status_P_I = SPU_STATUS_STOPPED_BY_STOP |SPU_STATUS_INVALID_INSTR; + u32 status_code; + + /* Restore, Step 27: + * If the CSA.SPU_Status[I,S,H,P]=1 then add the correct + * instruction sequence to the end of the SPU based restore + * code (after the "context restored" stop and signal) to + * restore the correct SPU status. + * + * NOTE: Rather than modifying the SPU executable, we + * instead add a new 'stopped_status' field to the + * LSCSA. The SPU-side restore reads this field and + * takes the appropriate action when exiting. + */ + + status_code = + (csa->prob.spu_status_R >> SPU_STOP_STATUS_SHIFT) & 0xFFFF; + if ((csa->prob.spu_status_R & status_P_I) == status_P_I) { + + /* SPU_Status[P,I]=1 - Illegal Instruction followed + * by Stop and Signal instruction, followed by 'br -4'. + * + */ + csa->lscsa->stopped_status.slot[0] = SPU_STOPPED_STATUS_P_I; + csa->lscsa->stopped_status.slot[1] = status_code; + + } else if ((csa->prob.spu_status_R & status_P_H) == status_P_H) { + + /* SPU_Status[P,H]=1 - Halt Conditional, followed + * by Stop and Signal instruction, followed by + * 'br -4'. + */ + csa->lscsa->stopped_status.slot[0] = SPU_STOPPED_STATUS_P_H; + csa->lscsa->stopped_status.slot[1] = status_code; + + } else if ((csa->prob.spu_status_R & status_S_P) == status_S_P) { + + /* SPU_Status[S,P]=1 - Stop and Signal instruction + * followed by 'br -4'. + */ + csa->lscsa->stopped_status.slot[0] = SPU_STOPPED_STATUS_S_P; + csa->lscsa->stopped_status.slot[1] = status_code; + + } else if ((csa->prob.spu_status_R & status_S_I) == status_S_I) { + + /* SPU_Status[S,I]=1 - Illegal instruction followed + * by 'br -4'. + */ + csa->lscsa->stopped_status.slot[0] = SPU_STOPPED_STATUS_S_I; + csa->lscsa->stopped_status.slot[1] = status_code; + + } else if ((csa->prob.spu_status_R & status_P) == status_P) { + + /* SPU_Status[P]=1 - Stop and Signal instruction + * followed by 'br -4'. + */ + csa->lscsa->stopped_status.slot[0] = SPU_STOPPED_STATUS_P; + csa->lscsa->stopped_status.slot[1] = status_code; + + } else if ((csa->prob.spu_status_R & status_H) == status_H) { + + /* SPU_Status[H]=1 - Halt Conditional, followed + * by 'br -4'. + */ + csa->lscsa->stopped_status.slot[0] = SPU_STOPPED_STATUS_H; + + } else if ((csa->prob.spu_status_R & status_S) == status_S) { + + /* SPU_Status[S]=1 - Two nop instructions. + */ + csa->lscsa->stopped_status.slot[0] = SPU_STOPPED_STATUS_S; + + } else if ((csa->prob.spu_status_R & status_I) == status_I) { + + /* SPU_Status[I]=1 - Illegal instruction followed + * by 'br -4'. + */ + csa->lscsa->stopped_status.slot[0] = SPU_STOPPED_STATUS_I; + + } +} + +static inline void setup_spu_status_part2(struct spu_state *csa, + struct spu *spu) +{ + u32 mask; + + /* Restore, Step 28: + * If the CSA.SPU_Status[I,S,H,P,R]=0 then + * add a 'br *' instruction to the end of + * the SPU based restore code. + * + * NOTE: Rather than modifying the SPU executable, we + * instead add a new 'stopped_status' field to the + * LSCSA. The SPU-side restore reads this field and + * takes the appropriate action when exiting. + */ + mask = SPU_STATUS_INVALID_INSTR | + SPU_STATUS_SINGLE_STEP | + SPU_STATUS_STOPPED_BY_HALT | + SPU_STATUS_STOPPED_BY_STOP | SPU_STATUS_RUNNING; + if (!(csa->prob.spu_status_R & mask)) { + csa->lscsa->stopped_status.slot[0] = SPU_STOPPED_STATUS_R; + } +} + +static inline void restore_mfc_rag(struct spu_state *csa, struct spu *spu) +{ + struct spu_priv1 __iomem *priv1 = spu->priv1; + + /* Restore, Step 29: + * Restore RA_GROUP_ID register and the + * RA_ENABLE reigster from the CSA. + */ + out_be64(&priv1->resource_allocation_groupID_RW, + csa->priv1.resource_allocation_groupID_RW); + out_be64(&priv1->resource_allocation_enable_RW, + csa->priv1.resource_allocation_enable_RW); +} + +static inline void send_restore_code(struct spu_state *csa, struct spu *spu) +{ + unsigned long addr = (unsigned long)&spu_restore_code[0]; + unsigned int ls_offset = 0x0; + unsigned int size = sizeof(spu_restore_code); + unsigned int tag = 0; + unsigned int rclass = 0; + unsigned int cmd = MFC_GETFS_CMD; + + /* Restore, Step 37: + * Issue MFC DMA command to copy context + * restore code to local storage. + */ + send_mfc_dma(spu, addr, ls_offset, size, tag, rclass, cmd); +} + +static inline void setup_decr(struct spu_state *csa, struct spu *spu) +{ + /* Restore, Step 34: + * If CSA.MFC_CNTL[Ds]=1 (decrementer was + * running) then adjust decrementer, set + * decrementer running status in LSCSA, + * and set decrementer "wrapped" status + * in LSCSA. + */ + if (csa->priv2.mfc_control_RW & MFC_CNTL_DECREMENTER_RUNNING) { + cycles_t resume_time = get_cycles(); + cycles_t delta_time = resume_time - csa->suspend_time; + + csa->lscsa->decr.slot[0] = delta_time; + } +} + +static inline void setup_ppu_mb(struct spu_state *csa, struct spu *spu) +{ + /* Restore, Step 35: + * Copy the CSA.PU_MB data into the LSCSA. + */ + csa->lscsa->ppu_mb.slot[0] = csa->prob.pu_mb_R; +} + +static inline void setup_ppuint_mb(struct spu_state *csa, struct spu *spu) +{ + /* Restore, Step 36: + * Copy the CSA.PUINT_MB data into the LSCSA. + */ + csa->lscsa->ppuint_mb.slot[0] = csa->priv2.puint_mb_R; +} + +static inline int check_restore_status(struct spu_state *csa, struct spu *spu) +{ + struct spu_problem __iomem *prob = spu->problem; + u32 complete; + + /* Restore, Step 40: + * If SPU_Status[P]=1 and SPU_Status[SC] = "success", + * context restore succeeded, otherwise context restore + * failed. + */ + complete = ((SPU_RESTORE_COMPLETE << SPU_STOP_STATUS_SHIFT) | + SPU_STATUS_STOPPED_BY_STOP); + return (in_be32(&prob->spu_status_R) != complete) ? 1 : 0; +} + +static inline void restore_spu_privcntl(struct spu_state *csa, struct spu *spu) +{ + struct spu_priv2 __iomem *priv2 = spu->priv2; + + /* Restore, Step 41: + * Restore SPU_PrivCntl from the CSA. + */ + out_be64(&priv2->spu_privcntl_RW, csa->priv2.spu_privcntl_RW); + eieio(); +} + +static inline void restore_status_part1(struct spu_state *csa, struct spu *spu) +{ + struct spu_problem __iomem *prob = spu->problem; + u32 mask; + + /* Restore, Step 42: + * If any CSA.SPU_Status[I,S,H,P]=1, then + * restore the error or single step state. + */ + mask = SPU_STATUS_INVALID_INSTR | + SPU_STATUS_SINGLE_STEP | + SPU_STATUS_STOPPED_BY_HALT | SPU_STATUS_STOPPED_BY_STOP; + if (csa->prob.spu_status_R & mask) { + out_be32(&prob->spu_runcntl_RW, SPU_RUNCNTL_RUNNABLE); + eieio(); + POLL_WHILE_TRUE(in_be32(&prob->spu_status_R) & + SPU_STATUS_RUNNING); + } +} + +static inline void restore_status_part2(struct spu_state *csa, struct spu *spu) +{ + struct spu_problem __iomem *prob = spu->problem; + u32 mask; + + /* Restore, Step 43: + * If all CSA.SPU_Status[I,S,H,P,R]=0 then write + * SPU_RunCntl[R0R1]='01', wait for SPU_Status[R]=1, + * then write '00' to SPU_RunCntl[R0R1] and wait + * for SPU_Status[R]=0. + */ + mask = SPU_STATUS_INVALID_INSTR | + SPU_STATUS_SINGLE_STEP | + SPU_STATUS_STOPPED_BY_HALT | + SPU_STATUS_STOPPED_BY_STOP | SPU_STATUS_RUNNING; + if (!(csa->prob.spu_status_R & mask)) { + out_be32(&prob->spu_runcntl_RW, SPU_RUNCNTL_RUNNABLE); + eieio(); + POLL_WHILE_FALSE(in_be32(&prob->spu_status_R) & + SPU_STATUS_RUNNING); + out_be32(&prob->spu_runcntl_RW, SPU_RUNCNTL_STOP); + eieio(); + POLL_WHILE_TRUE(in_be32(&prob->spu_status_R) & + SPU_STATUS_RUNNING); + } +} + +static inline void restore_ls_16kb(struct spu_state *csa, struct spu *spu) +{ + unsigned long addr = (unsigned long)&csa->lscsa->ls[0]; + unsigned int ls_offset = 0x0; + unsigned int size = 16384; + unsigned int tag = 0; + unsigned int rclass = 0; + unsigned int cmd = MFC_GET_CMD; + + /* Restore, Step 44: + * Issue a DMA command to restore the first + * 16kb of local storage from CSA. + */ + send_mfc_dma(spu, addr, ls_offset, size, tag, rclass, cmd); +} + +static inline void clear_interrupts(struct spu_state *csa, struct spu *spu) +{ + struct spu_priv1 __iomem *priv1 = spu->priv1; + + /* Restore, Step 49: + * Write INT_MASK_class0 with value of 0. + * Write INT_MASK_class1 with value of 0. + * Write INT_MASK_class2 with value of 0. + * Write INT_STAT_class0 with value of -1. + * Write INT_STAT_class1 with value of -1. + * Write INT_STAT_class2 with value of -1. + */ + spin_lock_irq(&spu->register_lock); + out_be64(&priv1->int_mask_class0_RW, 0UL); + out_be64(&priv1->int_mask_class1_RW, 0UL); + out_be64(&priv1->int_mask_class2_RW, 0UL); + out_be64(&priv1->int_stat_class0_RW, ~(0UL)); + out_be64(&priv1->int_stat_class1_RW, ~(0UL)); + out_be64(&priv1->int_stat_class2_RW, ~(0UL)); + spin_unlock_irq(&spu->register_lock); +} + +static inline void restore_mfc_queues(struct spu_state *csa, struct spu *spu) +{ + struct spu_priv2 __iomem *priv2 = spu->priv2; + int i; + + /* Restore, Step 50: + * If MFC_Cntl[Se]!=0 then restore + * MFC command queues. + */ + if ((csa->priv2.mfc_control_RW & MFC_CNTL_DMA_QUEUES_EMPTY_MASK) == 0) { + for (i = 0; i < 8; i++) { + out_be64(&priv2->puq[i].mfc_cq_data0_RW, + csa->priv2.puq[i].mfc_cq_data0_RW); + out_be64(&priv2->puq[i].mfc_cq_data1_RW, + csa->priv2.puq[i].mfc_cq_data1_RW); + out_be64(&priv2->puq[i].mfc_cq_data2_RW, + csa->priv2.puq[i].mfc_cq_data2_RW); + out_be64(&priv2->puq[i].mfc_cq_data3_RW, + csa->priv2.puq[i].mfc_cq_data3_RW); + } + for (i = 0; i < 16; i++) { + out_be64(&priv2->spuq[i].mfc_cq_data0_RW, + csa->priv2.spuq[i].mfc_cq_data0_RW); + out_be64(&priv2->spuq[i].mfc_cq_data1_RW, + csa->priv2.spuq[i].mfc_cq_data1_RW); + out_be64(&priv2->spuq[i].mfc_cq_data2_RW, + csa->priv2.spuq[i].mfc_cq_data2_RW); + out_be64(&priv2->spuq[i].mfc_cq_data3_RW, + csa->priv2.spuq[i].mfc_cq_data3_RW); + } + } + eieio(); +} + +static inline void restore_ppu_querymask(struct spu_state *csa, struct spu *spu) +{ + struct spu_problem __iomem *prob = spu->problem; + + /* Restore, Step 51: + * Restore the PPU_QueryMask register from CSA. + */ + out_be32(&prob->dma_querymask_RW, csa->prob.dma_querymask_RW); + eieio(); +} + +static inline void restore_ppu_querytype(struct spu_state *csa, struct spu *spu) +{ + struct spu_problem __iomem *prob = spu->problem; + + /* Restore, Step 52: + * Restore the PPU_QueryType register from CSA. + */ + out_be32(&prob->dma_querytype_RW, csa->prob.dma_querytype_RW); + eieio(); +} + +static inline void restore_mfc_csr_tsq(struct spu_state *csa, struct spu *spu) +{ + struct spu_priv2 __iomem *priv2 = spu->priv2; + + /* Restore, Step 53: + * Restore the MFC_CSR_TSQ register from CSA. + */ + out_be64(&priv2->spu_tag_status_query_RW, + csa->priv2.spu_tag_status_query_RW); + eieio(); +} + +static inline void restore_mfc_csr_cmd(struct spu_state *csa, struct spu *spu) +{ + struct spu_priv2 __iomem *priv2 = spu->priv2; + + /* Restore, Step 54: + * Restore the MFC_CSR_CMD1 and MFC_CSR_CMD2 + * registers from CSA. + */ + out_be64(&priv2->spu_cmd_buf1_RW, csa->priv2.spu_cmd_buf1_RW); + out_be64(&priv2->spu_cmd_buf2_RW, csa->priv2.spu_cmd_buf2_RW); + eieio(); +} + +static inline void restore_mfc_csr_ato(struct spu_state *csa, struct spu *spu) +{ + struct spu_priv2 __iomem *priv2 = spu->priv2; + + /* Restore, Step 55: + * Restore the MFC_CSR_ATO register from CSA. + */ + out_be64(&priv2->spu_atomic_status_RW, csa->priv2.spu_atomic_status_RW); +} + +static inline void restore_mfc_tclass_id(struct spu_state *csa, struct spu *spu) +{ + struct spu_priv1 __iomem *priv1 = spu->priv1; + + /* Restore, Step 56: + * Restore the MFC_TCLASS_ID register from CSA. + */ + out_be64(&priv1->mfc_tclass_id_RW, csa->priv1.mfc_tclass_id_RW); + eieio(); +} + +static inline void set_llr_event(struct spu_state *csa, struct spu *spu) +{ + u64 ch0_cnt, ch0_data; + u64 ch1_data; + + /* Restore, Step 57: + * Set the Lock Line Reservation Lost Event by: + * 1. OR CSA.SPU_Event_Status with bit 21 (Lr) set to 1. + * 2. If CSA.SPU_Channel_0_Count=0 and + * CSA.SPU_Wr_Event_Mask[Lr]=1 and + * CSA.SPU_Event_Status[Lr]=0 then set + * CSA.SPU_Event_Status_Count=1. + */ + ch0_cnt = csa->spu_chnlcnt_RW[0]; + ch0_data = csa->spu_chnldata_RW[0]; + ch1_data = csa->spu_chnldata_RW[1]; + csa->spu_chnldata_RW[0] |= MFC_LLR_LOST_EVENT; + if ((ch0_cnt == 0) && !(ch0_data & MFC_LLR_LOST_EVENT) && + (ch1_data & MFC_LLR_LOST_EVENT)) { + csa->spu_chnlcnt_RW[0] = 1; + } +} + +static inline void restore_decr_wrapped(struct spu_state *csa, struct spu *spu) +{ + /* Restore, Step 58: + * If the status of the CSA software decrementer + * "wrapped" flag is set, OR in a '1' to + * CSA.SPU_Event_Status[Tm]. + */ + if (csa->lscsa->decr_status.slot[0] == 1) { + csa->spu_chnldata_RW[0] |= 0x20; + } + if ((csa->lscsa->decr_status.slot[0] == 1) && + (csa->spu_chnlcnt_RW[0] == 0 && + ((csa->spu_chnldata_RW[2] & 0x20) == 0x0) && + ((csa->spu_chnldata_RW[0] & 0x20) != 0x1))) { + csa->spu_chnlcnt_RW[0] = 1; + } +} + +static inline void restore_ch_part1(struct spu_state *csa, struct spu *spu) +{ + struct spu_priv2 __iomem *priv2 = spu->priv2; + u64 idx, ch_indices[7] = { 0UL, 1UL, 3UL, 4UL, 24UL, 25UL, 27UL }; + int i; + + /* Restore, Step 59: + * Restore the following CH: [0,1,3,4,24,25,27] + */ + for (i = 0; i < 7; i++) { + idx = ch_indices[i]; + out_be64(&priv2->spu_chnlcntptr_RW, idx); + eieio(); + out_be64(&priv2->spu_chnldata_RW, csa->spu_chnldata_RW[idx]); + out_be64(&priv2->spu_chnlcnt_RW, csa->spu_chnlcnt_RW[idx]); + eieio(); + } +} + +static inline void restore_ch_part2(struct spu_state *csa, struct spu *spu) +{ + struct spu_priv2 __iomem *priv2 = spu->priv2; + u64 ch_indices[3] = { 9UL, 21UL, 23UL }; + u64 ch_counts[3] = { 1UL, 16UL, 1UL }; + u64 idx; + int i; + + /* Restore, Step 60: + * Restore the following CH: [9,21,23]. + */ + ch_counts[0] = 1UL; + ch_counts[1] = csa->spu_chnlcnt_RW[21]; + ch_counts[2] = 1UL; + for (i = 0; i < 3; i++) { + idx = ch_indices[i]; + out_be64(&priv2->spu_chnlcntptr_RW, idx); + eieio(); + out_be64(&priv2->spu_chnlcnt_RW, ch_counts[i]); + eieio(); + } +} + +static inline void restore_spu_lslr(struct spu_state *csa, struct spu *spu) +{ + struct spu_priv2 __iomem *priv2 = spu->priv2; + + /* Restore, Step 61: + * Restore the SPU_LSLR register from CSA. + */ + out_be64(&priv2->spu_lslr_RW, csa->priv2.spu_lslr_RW); + eieio(); +} + +static inline void restore_spu_cfg(struct spu_state *csa, struct spu *spu) +{ + struct spu_priv2 __iomem *priv2 = spu->priv2; + + /* Restore, Step 62: + * Restore the SPU_Cfg register from CSA. + */ + out_be64(&priv2->spu_cfg_RW, csa->priv2.spu_cfg_RW); + eieio(); +} + +static inline void restore_pm_trace(struct spu_state *csa, struct spu *spu) +{ + /* Restore, Step 63: + * Restore PM_Trace_Tag_Wait_Mask from CSA. + * Not performed by this implementation. + */ +} + +static inline void restore_spu_npc(struct spu_state *csa, struct spu *spu) +{ + struct spu_problem __iomem *prob = spu->problem; + + /* Restore, Step 64: + * Restore SPU_NPC from CSA. + */ + out_be32(&prob->spu_npc_RW, csa->prob.spu_npc_RW); + eieio(); +} + +static inline void restore_spu_mb(struct spu_state *csa, struct spu *spu) +{ + struct spu_priv2 __iomem *priv2 = spu->priv2; + int i; + + /* Restore, Step 65: + * Restore MFC_RdSPU_MB from CSA. + */ + out_be64(&priv2->spu_chnlcntptr_RW, 29UL); + eieio(); + out_be64(&priv2->spu_chnlcnt_RW, csa->spu_chnlcnt_RW[29]); + for (i = 0; i < 4; i++) { + out_be64(&priv2->spu_chnldata_RW, csa->spu_mailbox_data[i]); + } + eieio(); +} + +static inline void check_ppu_mb_stat(struct spu_state *csa, struct spu *spu) +{ + struct spu_problem __iomem *prob = spu->problem; + u32 dummy = 0; + + /* Restore, Step 66: + * If CSA.MB_Stat[P]=0 (mailbox empty) then + * read from the PPU_MB register. + */ + if ((csa->prob.mb_stat_R & 0xFF) == 0) { + dummy = in_be32(&prob->pu_mb_R); + eieio(); + } +} + +static inline void check_ppuint_mb_stat(struct spu_state *csa, struct spu *spu) +{ + struct spu_priv1 __iomem *priv1 = spu->priv1; + struct spu_priv2 __iomem *priv2 = spu->priv2; + u64 dummy = 0UL; + + /* Restore, Step 66: + * If CSA.MB_Stat[I]=0 (mailbox empty) then + * read from the PPUINT_MB register. + */ + if ((csa->prob.mb_stat_R & 0xFF0000) == 0) { + dummy = in_be64(&priv2->puint_mb_R); + eieio(); + out_be64(&priv1->int_stat_class2_RW, + CLASS2_ENABLE_MAILBOX_INTR); + eieio(); + } +} + +static inline void restore_mfc_slbs(struct spu_state *csa, struct spu *spu) +{ + struct spu_priv2 __iomem *priv2 = spu->priv2; + int i; + + /* Restore, Step 68: + * If MFC_SR1[R]='1', restore SLBs from CSA. + */ + if (csa->priv1.mfc_sr1_RW & MFC_STATE1_RELOCATE_MASK) { + for (i = 0; i < 8; i++) { + out_be64(&priv2->slb_index_W, i); + eieio(); + out_be64(&priv2->slb_esid_RW, csa->slb_esid_RW[i]); + out_be64(&priv2->slb_vsid_RW, csa->slb_vsid_RW[i]); + eieio(); + } + out_be64(&priv2->slb_index_W, csa->priv2.slb_index_W); + eieio(); + } +} + +static inline void restore_mfc_sr1(struct spu_state *csa, struct spu *spu) +{ + struct spu_priv1 __iomem *priv1 = spu->priv1; + + /* Restore, Step 69: + * Restore the MFC_SR1 register from CSA. + */ + out_be64(&priv1->mfc_sr1_RW, csa->priv1.mfc_sr1_RW); + eieio(); +} + +static inline void restore_other_spu_access(struct spu_state *csa, + struct spu *spu) +{ + /* Restore, Step 70: + * Restore other SPU mappings to this SPU. TBD. + */ +} + +static inline void restore_spu_runcntl(struct spu_state *csa, struct spu *spu) +{ + struct spu_problem __iomem *prob = spu->problem; + + /* Restore, Step 71: + * If CSA.SPU_Status[R]=1 then write + * SPU_RunCntl[R0R1]='01'. + */ + if (csa->prob.spu_status_R & SPU_STATUS_RUNNING) { + out_be32(&prob->spu_runcntl_RW, SPU_RUNCNTL_RUNNABLE); + eieio(); + } +} + +static inline void restore_mfc_cntl(struct spu_state *csa, struct spu *spu) +{ + struct spu_priv2 __iomem *priv2 = spu->priv2; + + /* Restore, Step 72: + * Restore the MFC_CNTL register for the CSA. + */ + out_be64(&priv2->mfc_control_RW, csa->priv2.mfc_control_RW); + eieio(); +} + +static inline void enable_user_access(struct spu_state *csa, struct spu *spu) +{ + /* Restore, Step 73: + * Enable user-space access (if provided) to this + * SPU by mapping the virtual pages assigned to + * the SPU memory-mapped I/O (MMIO) for problem + * state. TBD. + */ +} + +static inline void reset_switch_active(struct spu_state *csa, struct spu *spu) +{ + /* Restore, Step 74: + * Reset the "context switch active" flag. + */ + clear_bit(SPU_CONTEXT_SWITCH_ACTIVE_nr, &spu->flags); + mb(); +} + +static inline void reenable_interrupts(struct spu_state *csa, struct spu *spu) +{ + struct spu_priv1 __iomem *priv1 = spu->priv1; + + /* Restore, Step 75: + * Re-enable SPU interrupts. + */ + spin_lock_irq(&spu->register_lock); + out_be64(&priv1->int_mask_class0_RW, csa->priv1.int_mask_class0_RW); + out_be64(&priv1->int_mask_class1_RW, csa->priv1.int_mask_class1_RW); + out_be64(&priv1->int_mask_class2_RW, csa->priv1.int_mask_class2_RW); + spin_unlock_irq(&spu->register_lock); +} + +static int quiece_spu(struct spu_state *prev, struct spu *spu) +{ + /* + * Combined steps 2-18 of SPU context save sequence, which + * quiesce the SPU state (disable SPU execution, MFC command + * queues, decrementer, SPU interrupts, etc.). + * + * Returns 0 on success. + * 2 if failed step 2. + * 6 if failed step 6. + */ + + if (check_spu_isolate(prev, spu)) { /* Step 2. */ + return 2; + } + disable_interrupts(prev, spu); /* Step 3. */ + set_watchdog_timer(prev, spu); /* Step 4. */ + inhibit_user_access(prev, spu); /* Step 5. */ + if (check_spu_isolate(prev, spu)) { /* Step 6. */ + return 6; + } + set_switch_pending(prev, spu); /* Step 7. */ + save_mfc_cntl(prev, spu); /* Step 8. */ + save_spu_runcntl(prev, spu); /* Step 9. */ + save_mfc_sr1(prev, spu); /* Step 10. */ + save_spu_status(prev, spu); /* Step 11. */ + save_mfc_decr(prev, spu); /* Step 12. */ + halt_mfc_decr(prev, spu); /* Step 13. */ + save_timebase(prev, spu); /* Step 14. */ + remove_other_spu_access(prev, spu); /* Step 15. */ + do_mfc_mssync(prev, spu); /* Step 16. */ + issue_mfc_tlbie(prev, spu); /* Step 17. */ + handle_pending_interrupts(prev, spu); /* Step 18. */ + + return 0; +} + +static void save_csa(struct spu_state *prev, struct spu *spu) +{ + /* + * Combine steps 19-44 of SPU context save sequence, which + * save regions of the privileged & problem state areas. + */ + + save_mfc_queues(prev, spu); /* Step 19. */ + save_ppu_querymask(prev, spu); /* Step 20. */ + save_ppu_querytype(prev, spu); /* Step 21. */ + save_mfc_csr_tsq(prev, spu); /* Step 22. */ + save_mfc_csr_cmd(prev, spu); /* Step 23. */ + save_mfc_csr_ato(prev, spu); /* Step 24. */ + save_mfc_tclass_id(prev, spu); /* Step 25. */ + set_mfc_tclass_id(prev, spu); /* Step 26. */ + purge_mfc_queue(prev, spu); /* Step 27. */ + wait_purge_complete(prev, spu); /* Step 28. */ + save_mfc_slbs(prev, spu); /* Step 29. */ + setup_mfc_sr1(prev, spu); /* Step 30. */ + save_spu_npc(prev, spu); /* Step 31. */ + save_spu_privcntl(prev, spu); /* Step 32. */ + reset_spu_privcntl(prev, spu); /* Step 33. */ + save_spu_lslr(prev, spu); /* Step 34. */ + reset_spu_lslr(prev, spu); /* Step 35. */ + save_spu_cfg(prev, spu); /* Step 36. */ + save_pm_trace(prev, spu); /* Step 37. */ + save_mfc_rag(prev, spu); /* Step 38. */ + save_ppu_mb_stat(prev, spu); /* Step 39. */ + save_ppu_mb(prev, spu); /* Step 40. */ + save_ppuint_mb(prev, spu); /* Step 41. */ + save_ch_part1(prev, spu); /* Step 42. */ + save_spu_mb(prev, spu); /* Step 43. */ + save_mfc_cmd(prev, spu); /* Step 44. */ + reset_ch(prev, spu); /* Step 45. */ +} + +static void save_lscsa(struct spu_state *prev, struct spu *spu) +{ + /* + * Perform steps 46-57 of SPU context save sequence, + * which save regions of the local store and register + * file. + */ + + resume_mfc_queue(prev, spu); /* Step 46. */ + setup_mfc_slbs(prev, spu); /* Step 47. */ + set_switch_active(prev, spu); /* Step 48. */ + enable_interrupts(prev, spu); /* Step 49. */ + save_ls_16kb(prev, spu); /* Step 50. */ + set_spu_npc(prev, spu); /* Step 51. */ + set_signot1(prev, spu); /* Step 52. */ + set_signot2(prev, spu); /* Step 53. */ + send_save_code(prev, spu); /* Step 54. */ + set_ppu_querymask(prev, spu); /* Step 55. */ + wait_tag_complete(prev, spu); /* Step 56. */ + wait_spu_stopped(prev, spu); /* Step 57. */ +} + +static void harvest(struct spu_state *prev, struct spu *spu) +{ + /* + * Perform steps 2-25 of SPU context restore sequence, + * which resets an SPU either after a failed save, or + * when using SPU for first time. + */ + + disable_interrupts(prev, spu); /* Step 2. */ + inhibit_user_access(prev, spu); /* Step 3. */ + terminate_spu_app(prev, spu); /* Step 4. */ + set_switch_pending(prev, spu); /* Step 5. */ + remove_other_spu_access(prev, spu); /* Step 6. */ + suspend_mfc(prev, spu); /* Step 7. */ + wait_suspend_mfc_complete(prev, spu); /* Step 8. */ + if (!suspend_spe(prev, spu)) /* Step 9. */ + clear_spu_status(prev, spu); /* Step 10. */ + do_mfc_mssync(prev, spu); /* Step 11. */ + issue_mfc_tlbie(prev, spu); /* Step 12. */ + handle_pending_interrupts(prev, spu); /* Step 13. */ + purge_mfc_queue(prev, spu); /* Step 14. */ + wait_purge_complete(prev, spu); /* Step 15. */ + reset_spu_privcntl(prev, spu); /* Step 16. */ + reset_spu_lslr(prev, spu); /* Step 17. */ + setup_mfc_sr1(prev, spu); /* Step 18. */ + invalidate_slbs(prev, spu); /* Step 19. */ + reset_ch_part1(prev, spu); /* Step 20. */ + reset_ch_part2(prev, spu); /* Step 21. */ + enable_interrupts(prev, spu); /* Step 22. */ + set_switch_active(prev, spu); /* Step 23. */ + set_mfc_tclass_id(prev, spu); /* Step 24. */ + resume_mfc_queue(prev, spu); /* Step 25. */ +} + +static void restore_lscsa(struct spu_state *next, struct spu *spu) +{ + /* + * Perform steps 26-40 of SPU context restore sequence, + * which restores regions of the local store and register + * file. + */ + + set_watchdog_timer(next, spu); /* Step 26. */ + setup_spu_status_part1(next, spu); /* Step 27. */ + setup_spu_status_part2(next, spu); /* Step 28. */ + restore_mfc_rag(next, spu); /* Step 29. */ + setup_mfc_slbs(next, spu); /* Step 30. */ + set_spu_npc(next, spu); /* Step 31. */ + set_signot1(next, spu); /* Step 32. */ + set_signot2(next, spu); /* Step 33. */ + setup_decr(next, spu); /* Step 34. */ + setup_ppu_mb(next, spu); /* Step 35. */ + setup_ppuint_mb(next, spu); /* Step 36. */ + send_restore_code(next, spu); /* Step 37. */ + set_ppu_querymask(next, spu); /* Step 38. */ + wait_tag_complete(next, spu); /* Step 39. */ + wait_spu_stopped(next, spu); /* Step 40. */ +} + +static void restore_csa(struct spu_state *next, struct spu *spu) +{ + /* + * Combine steps 41-76 of SPU context restore sequence, which + * restore regions of the privileged & problem state areas. + */ + + restore_spu_privcntl(next, spu); /* Step 41. */ + restore_status_part1(next, spu); /* Step 42. */ + restore_status_part2(next, spu); /* Step 43. */ + restore_ls_16kb(next, spu); /* Step 44. */ + wait_tag_complete(next, spu); /* Step 45. */ + suspend_mfc(next, spu); /* Step 46. */ + wait_suspend_mfc_complete(next, spu); /* Step 47. */ + issue_mfc_tlbie(next, spu); /* Step 48. */ + clear_interrupts(next, spu); /* Step 49. */ + restore_mfc_queues(next, spu); /* Step 50. */ + restore_ppu_querymask(next, spu); /* Step 51. */ + restore_ppu_querytype(next, spu); /* Step 52. */ + restore_mfc_csr_tsq(next, spu); /* Step 53. */ + restore_mfc_csr_cmd(next, spu); /* Step 54. */ + restore_mfc_csr_ato(next, spu); /* Step 55. */ + restore_mfc_tclass_id(next, spu); /* Step 56. */ + set_llr_event(next, spu); /* Step 57. */ + restore_decr_wrapped(next, spu); /* Step 58. */ + restore_ch_part1(next, spu); /* Step 59. */ + restore_ch_part2(next, spu); /* Step 60. */ + restore_spu_lslr(next, spu); /* Step 61. */ + restore_spu_cfg(next, spu); /* Step 62. */ + restore_pm_trace(next, spu); /* Step 63. */ + restore_spu_npc(next, spu); /* Step 64. */ + restore_spu_mb(next, spu); /* Step 65. */ + check_ppu_mb_stat(next, spu); /* Step 66. */ + check_ppuint_mb_stat(next, spu); /* Step 67. */ + restore_mfc_slbs(next, spu); /* Step 68. */ + restore_mfc_sr1(next, spu); /* Step 69. */ + restore_other_spu_access(next, spu); /* Step 70. */ + restore_spu_runcntl(next, spu); /* Step 71. */ + restore_mfc_cntl(next, spu); /* Step 72. */ + enable_user_access(next, spu); /* Step 73. */ + reset_switch_active(next, spu); /* Step 74. */ + reenable_interrupts(next, spu); /* Step 75. */ +} + +static int __do_spu_save(struct spu_state *prev, struct spu *spu) +{ + int rc; + + /* + * SPU context save can be broken into three phases: + * + * (a) quiesce [steps 2-16]. + * (b) save of CSA, performed by PPE [steps 17-42] + * (c) save of LSCSA, mostly performed by SPU [steps 43-52]. + * + * Returns 0 on success. + * 2,6 if failed to quiece SPU + * 53 if SPU-side of save failed. + */ + + rc = quiece_spu(prev, spu); /* Steps 2-16. */ + switch (rc) { + default: + case 2: + case 6: + harvest(prev, spu); + return rc; + break; + case 0: + break; + } + save_csa(prev, spu); /* Steps 17-43. */ + save_lscsa(prev, spu); /* Steps 44-53. */ + return check_save_status(prev, spu); /* Step 54. */ +} + +static int __do_spu_restore(struct spu_state *next, struct spu *spu) +{ + int rc; + + /* + * SPU context restore can be broken into three phases: + * + * (a) harvest (or reset) SPU [steps 2-24]. + * (b) restore LSCSA [steps 25-40], mostly performed by SPU. + * (c) restore CSA [steps 41-76], performed by PPE. + * + * The 'harvest' step is not performed here, but rather + * as needed below. + */ + + restore_lscsa(next, spu); /* Steps 24-39. */ + rc = check_restore_status(next, spu); /* Step 40. */ + switch (rc) { + default: + /* Failed. Return now. */ + return rc; + break; + case 0: + /* Fall through to next step. */ + break; + } + restore_csa(next, spu); + + return 0; +} + +/** + * spu_save - SPU context save, with locking. + * @prev: pointer to SPU context save area, to be saved. + * @spu: pointer to SPU iomem structure. + * + * Acquire locks, perform the save operation then return. + */ +int spu_save(struct spu_state *prev, struct spu *spu) +{ + int rc; + + acquire_spu_lock(spu); /* Step 1. */ + rc = __do_spu_save(prev, spu); /* Steps 2-53. */ + release_spu_lock(spu); + if (rc) { + panic("%s failed on SPU[%d], rc=%d.\n", + __func__, spu->number, rc); + } + return rc; +} + +/** + * spu_restore - SPU context restore, with harvest and locking. + * @new: pointer to SPU context save area, to be restored. + * @spu: pointer to SPU iomem structure. + * + * Perform harvest + restore, as we may not be coming + * from a previous succesful save operation, and the + * hardware state is unknown. + */ +int spu_restore(struct spu_state *new, struct spu *spu) +{ + int rc; + + acquire_spu_lock(spu); + harvest(NULL, spu); + spu->stop_code = 0; + spu->dar = 0; + spu->dsisr = 0; + spu->slb_replace = 0; + spu->class_0_pending = 0; + rc = __do_spu_restore(new, spu); + release_spu_lock(spu); + if (rc) { + panic("%s failed on SPU[%d] rc=%d.\n", + __func__, spu->number, rc); + } + return rc; +} + +/** + * spu_harvest - SPU harvest (reset) operation + * @spu: pointer to SPU iomem structure. + * + * Perform SPU harvest (reset) operation. + */ +void spu_harvest(struct spu *spu) +{ + acquire_spu_lock(spu); + harvest(NULL, spu); + release_spu_lock(spu); +} + +static void init_prob(struct spu_state *csa) +{ + csa->spu_chnlcnt_RW[9] = 1; + csa->spu_chnlcnt_RW[21] = 16; + csa->spu_chnlcnt_RW[23] = 1; + csa->spu_chnlcnt_RW[28] = 1; + csa->spu_chnlcnt_RW[30] = 1; + csa->prob.spu_runcntl_RW = SPU_RUNCNTL_STOP; +} + +static void init_priv1(struct spu_state *csa) +{ + /* Enable decode, relocate, tlbie response, master runcntl. */ + csa->priv1.mfc_sr1_RW = MFC_STATE1_LOCAL_STORAGE_DECODE_MASK | + MFC_STATE1_MASTER_RUN_CONTROL_MASK | + MFC_STATE1_PROBLEM_STATE_MASK | + MFC_STATE1_RELOCATE_MASK | MFC_STATE1_BUS_TLBIE_MASK; + + /* Set storage description. */ + csa->priv1.mfc_sdr_RW = mfspr(SPRN_SDR1); + + /* Enable OS-specific set of interrupts. */ + csa->priv1.int_mask_class0_RW = CLASS0_ENABLE_DMA_ALIGNMENT_INTR | + CLASS0_ENABLE_INVALID_DMA_COMMAND_INTR | + CLASS0_ENABLE_SPU_ERROR_INTR; + csa->priv1.int_mask_class1_RW = CLASS1_ENABLE_SEGMENT_FAULT_INTR | + CLASS1_ENABLE_STORAGE_FAULT_INTR; + csa->priv1.int_mask_class2_RW = CLASS2_ENABLE_MAILBOX_INTR | + CLASS2_ENABLE_SPU_STOP_INTR | CLASS2_ENABLE_SPU_HALT_INTR; +} + +static void init_priv2(struct spu_state *csa) +{ + csa->priv2.spu_lslr_RW = LS_ADDR_MASK; + csa->priv2.mfc_control_RW = MFC_CNTL_RESUME_DMA_QUEUE | + MFC_CNTL_NORMAL_DMA_QUEUE_OPERATION | + MFC_CNTL_DMA_QUEUES_EMPTY_MASK; +} + +/** + * spu_alloc_csa - allocate and initialize an SPU context save area. + * + * Allocate and initialize the contents of an SPU context save area. + * This includes enabling address translation, interrupt masks, etc., + * as appropriate for the given OS environment. + * + * Note that storage for the 'lscsa' is allocated separately, + * as it is by far the largest of the context save regions, + * and may need to be pinned or otherwise specially aligned. + */ +void spu_init_csa(struct spu_state *csa) +{ + struct spu_lscsa *lscsa; + unsigned char *p; + + if (!csa) + return; + memset(csa, 0, sizeof(struct spu_state)); + + lscsa = vmalloc(sizeof(struct spu_lscsa)); + if (!lscsa) + return; + + memset(lscsa, 0, sizeof(struct spu_lscsa)); + csa->lscsa = lscsa; + csa->register_lock = SPIN_LOCK_UNLOCKED; + + /* Set LS pages reserved to allow for user-space mapping. */ + for (p = lscsa->ls; p < lscsa->ls + LS_SIZE; p += PAGE_SIZE) + SetPageReserved(vmalloc_to_page(p)); + + init_prob(csa); + init_priv1(csa); + init_priv2(csa); +} + +void spu_fini_csa(struct spu_state *csa) +{ + /* Clear reserved bit before vfree. */ + unsigned char *p; + for (p = csa->lscsa->ls; p < csa->lscsa->ls + LS_SIZE; p += PAGE_SIZE) + ClearPageReserved(vmalloc_to_page(p)); + + vfree(csa->lscsa); +} diff --git a/arch/powerpc/platforms/cell/spufs/syscalls.c b/arch/powerpc/platforms/cell/spufs/syscalls.c new file mode 100644 index 0000000..17a2b51 --- /dev/null +++ b/arch/powerpc/platforms/cell/spufs/syscalls.c @@ -0,0 +1,100 @@ +#include +#include +#include +#include +#include + +#include + +#include "spufs.h" + +/** + * sys_spu_run - run code loaded into an SPU + * + * @unpc: next program counter for the SPU + * @ustatus: status of the SPU + * + * This system call transfers the control of execution of a + * user space thread to an SPU. It will return when the + * SPU has finished executing or when it hits an error + * condition and it will be interrupted if a signal needs + * to be delivered to a handler in user space. + * + * The next program counter is set to the passed value + * before the SPU starts fetching code and the user space + * pointer gets updated with the new value when returning + * from kernel space. + * + * The status value returned from spu_run reflects the + * value of the spu_status register after the SPU has stopped. + * + */ +long do_spu_run(struct file *filp, __u32 __user *unpc, __u32 __user *ustatus) +{ + long ret; + struct spufs_inode_info *i; + u32 npc, status; + + ret = -EFAULT; + if (get_user(npc, unpc) || get_user(status, ustatus)) + goto out; + + ret = -EINVAL; + if (filp->f_vfsmnt->mnt_sb->s_magic != SPUFS_MAGIC) + goto out; + + i = SPUFS_I(filp->f_dentry->d_inode); + ret = spufs_run_spu(filp, i->i_ctx, &npc, &status); + + if (put_user(npc, unpc) || put_user(status, ustatus)) + ret = -EFAULT; +out: + return ret; +} + +#ifndef MODULE +asmlinkage long sys_spu_run(int fd, __u32 __user *unpc, __u32 __user *ustatus) +{ + int fput_needed; + struct file *filp; + long ret; + + ret = -EBADF; + filp = fget_light(fd, &fput_needed); + if (filp) { + ret = do_spu_run(filp, unpc, ustatus); + fput_light(filp, fput_needed); + } + + return ret; +} +#endif + +asmlinkage long sys_spu_create(const char __user *pathname, + unsigned int flags, mode_t mode) +{ + char *tmp; + int ret; + + tmp = getname(pathname); + ret = PTR_ERR(tmp); + if (!IS_ERR(tmp)) { + struct nameidata nd; + + ret = path_lookup(tmp, LOOKUP_PARENT| + LOOKUP_OPEN|LOOKUP_CREATE, &nd); + if (!ret) { + ret = spufs_create_thread(&nd, pathname, flags, mode); + path_release(&nd); + } + putname(tmp); + } + + return ret; +} + +struct spufs_calls spufs_calls = { + .create_thread = sys_spu_create, + .spu_run = do_spu_run, + .owner = THIS_MODULE, +}; diff --git a/arch/powerpc/platforms/chrp/setup.c b/arch/powerpc/platforms/chrp/setup.c index dda5f2c..4ec8ba7 100644 --- a/arch/powerpc/platforms/chrp/setup.c +++ b/arch/powerpc/platforms/chrp/setup.c @@ -49,7 +49,6 @@ #include #include #include -#include #include #include #include @@ -58,7 +57,6 @@ #include "chrp.h" void rtas_indicator_progress(char *, unsigned short); -void btext_progress(char *, unsigned short); int _chrp_type; EXPORT_SYMBOL(_chrp_type); @@ -264,11 +262,6 @@ void __init chrp_setup_arch(void) ppc_md.set_rtc_time = rtas_set_rtc_time; } -#ifdef CONFIG_BOOTX_TEXT - if (ppc_md.progress == NULL && boot_text_mapped) - ppc_md.progress = btext_progress; -#endif - #ifdef CONFIG_BLK_DEV_INITRD /* this is fine for chrp */ initrd_below_start_ok = 1; @@ -522,12 +515,3 @@ void __init chrp_init(void) smp_ops = &chrp_smp_ops; #endif /* CONFIG_SMP */ } - -#ifdef CONFIG_BOOTX_TEXT -void -btext_progress(char *s, unsigned short hex) -{ - btext_drawstring(s); - btext_drawstring("\n"); -} -#endif /* CONFIG_BOOTX_TEXT */ diff --git a/arch/powerpc/platforms/iseries/irq.c b/arch/powerpc/platforms/iseries/irq.c index a58daa1..42e978e 100644 --- a/arch/powerpc/platforms/iseries/irq.c +++ b/arch/powerpc/platforms/iseries/irq.c @@ -35,161 +35,138 @@ #include #include +#include #include #include #include +#include #include "irq.h" #include "call_pci.h" -static long Pci_Interrupt_Count; -static long Pci_Event_Count; - -enum XmPciLpEvent_Subtype { - XmPciLpEvent_BusCreated = 0, // PHB has been created - XmPciLpEvent_BusError = 1, // PHB has failed - XmPciLpEvent_BusFailed = 2, // Msg to Secondary, Primary failed bus - XmPciLpEvent_NodeFailed = 4, // Multi-adapter bridge has failed - XmPciLpEvent_NodeRecovered = 5, // Multi-adapter bridge has recovered - XmPciLpEvent_BusRecovered = 12, // PHB has been recovered - XmPciLpEvent_UnQuiesceBus = 18, // Secondary bus unqiescing - XmPciLpEvent_BridgeError = 21, // Bridge Error - XmPciLpEvent_SlotInterrupt = 22 // Slot interrupt -}; - -struct XmPciLpEvent_BusInterrupt { - HvBusNumber busNumber; - HvSubBusNumber subBusNumber; -}; +#if defined(CONFIG_SMP) +extern void iSeries_smp_message_recv(struct pt_regs *); +#endif -struct XmPciLpEvent_NodeInterrupt { - HvBusNumber busNumber; - HvSubBusNumber subBusNumber; - HvAgentId deviceId; +enum pci_event_type { + pe_bus_created = 0, /* PHB has been created */ + pe_bus_error = 1, /* PHB has failed */ + pe_bus_failed = 2, /* Msg to Secondary, Primary failed bus */ + pe_node_failed = 4, /* Multi-adapter bridge has failed */ + pe_node_recovered = 5, /* Multi-adapter bridge has recovered */ + pe_bus_recovered = 12, /* PHB has been recovered */ + pe_unquiese_bus = 18, /* Secondary bus unqiescing */ + pe_bridge_error = 21, /* Bridge Error */ + pe_slot_interrupt = 22 /* Slot interrupt */ }; -struct XmPciLpEvent { - struct HvLpEvent hvLpEvent; - +struct pci_event { + struct HvLpEvent event; union { - u64 alignData; // Align on an 8-byte boundary - + u64 __align; /* Align on an 8-byte boundary */ struct { u32 fisr; - HvBusNumber busNumber; - HvSubBusNumber subBusNumber; - HvAgentId deviceId; - } slotInterrupt; - - struct XmPciLpEvent_BusInterrupt busFailed; - struct XmPciLpEvent_BusInterrupt busRecovered; - struct XmPciLpEvent_BusInterrupt busCreated; - - struct XmPciLpEvent_NodeInterrupt nodeFailed; - struct XmPciLpEvent_NodeInterrupt nodeRecovered; - - } eventData; - + HvBusNumber bus_number; + HvSubBusNumber sub_bus_number; + HvAgentId dev_id; + } slot; + struct { + HvBusNumber bus_number; + HvSubBusNumber sub_bus_number; + } bus; + struct { + HvBusNumber bus_number; + HvSubBusNumber sub_bus_number; + HvAgentId dev_id; + } node; + } data; }; -static void intReceived(struct XmPciLpEvent *eventParm, - struct pt_regs *regsParm) +static DEFINE_SPINLOCK(pending_irqs_lock); +static int num_pending_irqs; +static int pending_irqs[NR_IRQS]; + +static void int_received(struct pci_event *event, struct pt_regs *regs) { int irq; -#ifdef CONFIG_IRQSTACKS - struct thread_info *curtp, *irqtp; -#endif - ++Pci_Interrupt_Count; - - switch (eventParm->hvLpEvent.xSubtype) { - case XmPciLpEvent_SlotInterrupt: - irq = eventParm->hvLpEvent.xCorrelationToken; - /* Dispatch the interrupt handlers for this irq */ -#ifdef CONFIG_IRQSTACKS - /* Switch to the irq stack to handle this */ - curtp = current_thread_info(); - irqtp = hardirq_ctx[smp_processor_id()]; - if (curtp != irqtp) { - irqtp->task = curtp->task; - irqtp->flags = 0; - call___do_IRQ(irq, regsParm, irqtp); - irqtp->task = NULL; - if (irqtp->flags) - set_bits(irqtp->flags, &curtp->flags); - } else -#endif - __do_IRQ(irq, regsParm); - HvCallPci_eoi(eventParm->eventData.slotInterrupt.busNumber, - eventParm->eventData.slotInterrupt.subBusNumber, - eventParm->eventData.slotInterrupt.deviceId); + switch (event->event.xSubtype) { + case pe_slot_interrupt: + irq = event->event.xCorrelationToken; + if (irq < NR_IRQS) { + spin_lock(&pending_irqs_lock); + pending_irqs[irq]++; + num_pending_irqs++; + spin_unlock(&pending_irqs_lock); + } else { + printk(KERN_WARNING "int_received: bad irq number %d\n", + irq); + HvCallPci_eoi(event->data.slot.bus_number, + event->data.slot.sub_bus_number, + event->data.slot.dev_id); + } break; /* Ignore error recovery events for now */ - case XmPciLpEvent_BusCreated: - printk(KERN_INFO "intReceived: system bus %d created\n", - eventParm->eventData.busCreated.busNumber); + case pe_bus_created: + printk(KERN_INFO "int_received: system bus %d created\n", + event->data.bus.bus_number); break; - case XmPciLpEvent_BusError: - case XmPciLpEvent_BusFailed: - printk(KERN_INFO "intReceived: system bus %d failed\n", - eventParm->eventData.busFailed.busNumber); + case pe_bus_error: + case pe_bus_failed: + printk(KERN_INFO "int_received: system bus %d failed\n", + event->data.bus.bus_number); break; - case XmPciLpEvent_BusRecovered: - case XmPciLpEvent_UnQuiesceBus: - printk(KERN_INFO "intReceived: system bus %d recovered\n", - eventParm->eventData.busRecovered.busNumber); + case pe_bus_recovered: + case pe_unquiese_bus: + printk(KERN_INFO "int_received: system bus %d recovered\n", + event->data.bus.bus_number); break; - case XmPciLpEvent_NodeFailed: - case XmPciLpEvent_BridgeError: + case pe_node_failed: + case pe_bridge_error: printk(KERN_INFO - "intReceived: multi-adapter bridge %d/%d/%d failed\n", - eventParm->eventData.nodeFailed.busNumber, - eventParm->eventData.nodeFailed.subBusNumber, - eventParm->eventData.nodeFailed.deviceId); + "int_received: multi-adapter bridge %d/%d/%d failed\n", + event->data.node.bus_number, + event->data.node.sub_bus_number, + event->data.node.dev_id); break; - case XmPciLpEvent_NodeRecovered: + case pe_node_recovered: printk(KERN_INFO - "intReceived: multi-adapter bridge %d/%d/%d recovered\n", - eventParm->eventData.nodeRecovered.busNumber, - eventParm->eventData.nodeRecovered.subBusNumber, - eventParm->eventData.nodeRecovered.deviceId); + "int_received: multi-adapter bridge %d/%d/%d recovered\n", + event->data.node.bus_number, + event->data.node.sub_bus_number, + event->data.node.dev_id); break; default: printk(KERN_ERR - "intReceived: unrecognized event subtype 0x%x\n", - eventParm->hvLpEvent.xSubtype); + "int_received: unrecognized event subtype 0x%x\n", + event->event.xSubtype); break; } } -static void XmPciLpEvent_handler(struct HvLpEvent *eventParm, - struct pt_regs *regsParm) +static void pci_event_handler(struct HvLpEvent *event, struct pt_regs *regs) { -#ifdef CONFIG_PCI - ++Pci_Event_Count; - - if (eventParm && (eventParm->xType == HvLpEvent_Type_PciIo)) { - switch (eventParm->xFlags.xFunction) { + if (event && (event->xType == HvLpEvent_Type_PciIo)) { + switch (event->xFlags.xFunction) { case HvLpEvent_Function_Int: - intReceived((struct XmPciLpEvent *)eventParm, regsParm); + int_received((struct pci_event *)event, regs); break; case HvLpEvent_Function_Ack: printk(KERN_ERR - "XmPciLpEvent_handler: unexpected ack received\n"); + "pci_event_handler: unexpected ack received\n"); break; default: printk(KERN_ERR - "XmPciLpEvent_handler: unexpected event function %d\n", - (int)eventParm->xFlags.xFunction); + "pci_event_handler: unexpected event function %d\n", + (int)event->xFlags.xFunction); break; } - } else if (eventParm) + } else if (event) printk(KERN_ERR - "XmPciLpEvent_handler: Unrecognized PCI event type 0x%x\n", - (int)eventParm->xType); + "pci_event_handler: Unrecognized PCI event type 0x%x\n", + (int)event->xType); else - printk(KERN_ERR "XmPciLpEvent_handler: NULL event received\n"); -#endif + printk(KERN_ERR "pci_event_handler: NULL event received\n"); } /* @@ -199,20 +176,21 @@ static void XmPciLpEvent_handler(struct void __init iSeries_init_IRQ(void) { /* Register PCI event handler and open an event path */ - int xRc; + int ret; - xRc = HvLpEvent_registerHandler(HvLpEvent_Type_PciIo, - &XmPciLpEvent_handler); - if (xRc == 0) { - xRc = HvLpEvent_openPath(HvLpEvent_Type_PciIo, 0); - if (xRc != 0) - printk(KERN_ERR "iSeries_init_IRQ: open event path " - "failed with rc 0x%x\n", xRc); + ret = HvLpEvent_registerHandler(HvLpEvent_Type_PciIo, + &pci_event_handler); + if (ret == 0) { + ret = HvLpEvent_openPath(HvLpEvent_Type_PciIo, 0); + if (ret != 0) + printk(KERN_ERR "iseries_init_IRQ: open event path " + "failed with rc 0x%x\n", ret); } else - printk(KERN_ERR "iSeries_init_IRQ: register handler " - "failed with rc 0x%x\n", xRc); + printk(KERN_ERR "iseries_init_IRQ: register handler " + "failed with rc 0x%x\n", ret); } +#define REAL_IRQ_TO_SUBBUS(irq) (((irq) >> 14) & 0xff) #define REAL_IRQ_TO_BUS(irq) ((((irq) >> 6) & 0xff) + 1) #define REAL_IRQ_TO_IDSEL(irq) ((((irq) >> 3) & 7) + 1) #define REAL_IRQ_TO_FUNC(irq) ((irq) & 7) @@ -221,40 +199,40 @@ void __init iSeries_init_IRQ(void) * This will be called by device drivers (via enable_IRQ) * to enable INTA in the bridge interrupt status register. */ -static void iSeries_enable_IRQ(unsigned int irq) +static void iseries_enable_IRQ(unsigned int irq) { - u32 bus, deviceId, function, mask; - const u32 subBus = 0; + u32 bus, dev_id, function, mask; + const u32 sub_bus = 0; unsigned int rirq = virt_irq_to_real_map[irq]; /* The IRQ has already been locked by the caller */ bus = REAL_IRQ_TO_BUS(rirq); function = REAL_IRQ_TO_FUNC(rirq); - deviceId = (REAL_IRQ_TO_IDSEL(rirq) << 4) + function; + dev_id = (REAL_IRQ_TO_IDSEL(rirq) << 4) + function; /* Unmask secondary INTA */ mask = 0x80000000; - HvCallPci_unmaskInterrupts(bus, subBus, deviceId, mask); + HvCallPci_unmaskInterrupts(bus, sub_bus, dev_id, mask); } -/* This is called by iSeries_activate_IRQs */ -static unsigned int iSeries_startup_IRQ(unsigned int irq) +/* This is called by iseries_activate_IRQs */ +static unsigned int iseries_startup_IRQ(unsigned int irq) { - u32 bus, deviceId, function, mask; - const u32 subBus = 0; + u32 bus, dev_id, function, mask; + const u32 sub_bus = 0; unsigned int rirq = virt_irq_to_real_map[irq]; bus = REAL_IRQ_TO_BUS(rirq); function = REAL_IRQ_TO_FUNC(rirq); - deviceId = (REAL_IRQ_TO_IDSEL(rirq) << 4) + function; + dev_id = (REAL_IRQ_TO_IDSEL(rirq) << 4) + function; /* Link the IRQ number to the bridge */ - HvCallXm_connectBusUnit(bus, subBus, deviceId, irq); + HvCallXm_connectBusUnit(bus, sub_bus, dev_id, irq); /* Unmask bridge interrupts in the FISR */ mask = 0x01010000 << function; - HvCallPci_unmaskFisr(bus, subBus, deviceId, mask); - iSeries_enable_IRQ(irq); + HvCallPci_unmaskFisr(bus, sub_bus, dev_id, mask); + iseries_enable_IRQ(irq); return 0; } @@ -279,78 +257,115 @@ void __init iSeries_activate_IRQs() } /* this is not called anywhere currently */ -static void iSeries_shutdown_IRQ(unsigned int irq) +static void iseries_shutdown_IRQ(unsigned int irq) { - u32 bus, deviceId, function, mask; - const u32 subBus = 0; + u32 bus, dev_id, function, mask; + const u32 sub_bus = 0; unsigned int rirq = virt_irq_to_real_map[irq]; /* irq should be locked by the caller */ bus = REAL_IRQ_TO_BUS(rirq); function = REAL_IRQ_TO_FUNC(rirq); - deviceId = (REAL_IRQ_TO_IDSEL(rirq) << 4) + function; + dev_id = (REAL_IRQ_TO_IDSEL(rirq) << 4) + function; /* Invalidate the IRQ number in the bridge */ - HvCallXm_connectBusUnit(bus, subBus, deviceId, 0); + HvCallXm_connectBusUnit(bus, sub_bus, dev_id, 0); /* Mask bridge interrupts in the FISR */ mask = 0x01010000 << function; - HvCallPci_maskFisr(bus, subBus, deviceId, mask); + HvCallPci_maskFisr(bus, sub_bus, dev_id, mask); } /* * This will be called by device drivers (via disable_IRQ) * to disable INTA in the bridge interrupt status register. */ -static void iSeries_disable_IRQ(unsigned int irq) +static void iseries_disable_IRQ(unsigned int irq) { - u32 bus, deviceId, function, mask; - const u32 subBus = 0; + u32 bus, dev_id, function, mask; + const u32 sub_bus = 0; unsigned int rirq = virt_irq_to_real_map[irq]; /* The IRQ has already been locked by the caller */ bus = REAL_IRQ_TO_BUS(rirq); function = REAL_IRQ_TO_FUNC(rirq); - deviceId = (REAL_IRQ_TO_IDSEL(rirq) << 4) + function; + dev_id = (REAL_IRQ_TO_IDSEL(rirq) << 4) + function; /* Mask secondary INTA */ mask = 0x80000000; - HvCallPci_maskInterrupts(bus, subBus, deviceId, mask); + HvCallPci_maskInterrupts(bus, sub_bus, dev_id, mask); } -/* - * This does nothing because there is not enough information - * provided to do the EOI HvCall. This is done by XmPciLpEvent.c - */ -static void iSeries_end_IRQ(unsigned int irq) +static void iseries_end_IRQ(unsigned int irq) { + unsigned int rirq = virt_irq_to_real_map[irq]; + + HvCallPci_eoi(REAL_IRQ_TO_BUS(rirq), REAL_IRQ_TO_SUBBUS(rirq), + (REAL_IRQ_TO_IDSEL(rirq) << 4) + REAL_IRQ_TO_FUNC(rirq)); } static hw_irq_controller iSeries_IRQ_handler = { .typename = "iSeries irq controller", - .startup = iSeries_startup_IRQ, - .shutdown = iSeries_shutdown_IRQ, - .enable = iSeries_enable_IRQ, - .disable = iSeries_disable_IRQ, - .end = iSeries_end_IRQ + .startup = iseries_startup_IRQ, + .shutdown = iseries_shutdown_IRQ, + .enable = iseries_enable_IRQ, + .disable = iseries_disable_IRQ, + .end = iseries_end_IRQ }; /* * This is called out of iSeries_scan_slot to allocate an IRQ for an EADS slot * It calculates the irq value for the slot. - * Note that subBusNumber is always 0 (at the moment at least). + * Note that sub_bus is always 0 (at the moment at least). */ -int __init iSeries_allocate_IRQ(HvBusNumber busNumber, - HvSubBusNumber subBusNumber, HvAgentId deviceId) +int __init iSeries_allocate_IRQ(HvBusNumber bus, + HvSubBusNumber sub_bus, HvAgentId dev_id) { int virtirq; unsigned int realirq; - u8 idsel = (deviceId >> 4); - u8 function = deviceId & 7; + u8 idsel = (dev_id >> 4); + u8 function = dev_id & 7; - realirq = ((busNumber - 1) << 6) + ((idsel - 1) << 3) + function; + realirq = (((((sub_bus << 8) + (bus - 1)) << 3) + (idsel - 1)) << 3) + + function; virtirq = virt_irq_create_mapping(realirq); irq_desc[virtirq].handler = &iSeries_IRQ_handler; return virtirq; } + +/* + * Get the next pending IRQ. + */ +int iSeries_get_irq(struct pt_regs *regs) +{ + struct paca_struct *lpaca; + /* -2 means ignore this interrupt */ + int irq = -2; + + lpaca = get_paca(); +#ifdef CONFIG_SMP + if (lpaca->lppaca.int_dword.fields.ipi_cnt) { + lpaca->lppaca.int_dword.fields.ipi_cnt = 0; + iSeries_smp_message_recv(regs); + } +#endif /* CONFIG_SMP */ + if (hvlpevent_is_pending()) + process_hvlpevents(regs); + + if (num_pending_irqs) { + spin_lock(&pending_irqs_lock); + for (irq = 0; irq < NR_IRQS; irq++) { + if (pending_irqs[irq]) { + pending_irqs[irq]--; + num_pending_irqs--; + break; + } + } + spin_unlock(&pending_irqs_lock); + if (irq >= NR_IRQS) + irq = -2; + } + + return irq; +} diff --git a/arch/powerpc/platforms/iseries/irq.h b/arch/powerpc/platforms/iseries/irq.h index 5f643f1..b9c801b 100644 --- a/arch/powerpc/platforms/iseries/irq.h +++ b/arch/powerpc/platforms/iseries/irq.h @@ -4,5 +4,6 @@ extern void iSeries_init_IRQ(void); extern int iSeries_allocate_IRQ(HvBusNumber, HvSubBusNumber, HvAgentId); extern void iSeries_activate_IRQs(void); +extern int iSeries_get_irq(struct pt_regs *); #endif /* _ISERIES_IRQ_H */ diff --git a/arch/powerpc/platforms/iseries/lpardata.c b/arch/powerpc/platforms/iseries/lpardata.c index bb8c915..ea72385 100644 --- a/arch/powerpc/platforms/iseries/lpardata.c +++ b/arch/powerpc/platforms/iseries/lpardata.c @@ -225,3 +225,10 @@ struct ItVpdAreas itVpdAreas = { 0,0 } }; + +struct ItLpRegSave iseries_reg_save[] = { + [0 ... (NR_CPUS-1)] = { + .xDesc = 0xd397d9e2, /* "LpRS" */ + .xSize = sizeof(struct ItLpRegSave), + }, +}; diff --git a/arch/powerpc/platforms/iseries/setup.c b/arch/powerpc/platforms/iseries/setup.c index da26639..ad5ef80 100644 --- a/arch/powerpc/platforms/iseries/setup.c +++ b/arch/powerpc/platforms/iseries/setup.c @@ -571,16 +571,6 @@ static void iSeries_show_cpuinfo(struct /* * Document me. - * and Implement me. - */ -static int iSeries_get_irq(struct pt_regs *regs) -{ - /* -2 means ignore this interrupt */ - return -2; -} - -/* - * Document me. */ static void iSeries_restart(char *cmd) { diff --git a/arch/powerpc/platforms/maple/setup.c b/arch/powerpc/platforms/maple/setup.c index 7ece898..8724e03 100644 --- a/arch/powerpc/platforms/maple/setup.c +++ b/arch/powerpc/platforms/maple/setup.c @@ -51,6 +51,7 @@ #include #include #include +#include #include #include #include @@ -191,20 +192,6 @@ static void __init maple_init_early(void */ hpte_init_native(); - /* Find the serial port */ - generic_find_legacy_serial_ports(&physport, &default_speed); - - DBG("phys port addr: %lx\n", (long)physport); - - if (physport) { - void *comport; - /* Map the uart for udbg. */ - comport = (void *)ioremap(physport, 16); - udbg_init_uart(comport, default_speed); - - DBG("Hello World !\n"); - } - /* Setup interrupt mapping options */ ppc64_interrupt_controller = IC_OPEN_PIC; @@ -292,4 +279,8 @@ struct machdep_calls __initdata maple_md .calibrate_decr = generic_calibrate_decr, .progress = maple_progress, .idle_loop = native_idle, +#ifdef CONFIG_KEXEC + .machine_kexec = default_machine_kexec, + .machine_kexec_prepare = default_machine_kexec_prepare, +#endif }; diff --git a/arch/powerpc/platforms/powermac/Makefile b/arch/powerpc/platforms/powermac/Makefile index c9df44f..faa1a2c 100644 --- a/arch/powerpc/platforms/powermac/Makefile +++ b/arch/powerpc/platforms/powermac/Makefile @@ -1,3 +1,5 @@ +CFLAGS_bootx_init.o += -fPIC + obj-y += pic.o setup.o time.o feature.o pci.o \ sleep.o low_i2c.o cache.o obj-$(CONFIG_PMAC_BACKLIGHT) += backlight.o @@ -6,4 +8,6 @@ obj-$(CONFIG_CPU_FREQ_PMAC64) += cpufreq obj-$(CONFIG_NVRAM) += nvram.o # ppc64 pmac doesn't define CONFIG_NVRAM but needs nvram stuff obj-$(CONFIG_PPC64) += nvram.o +obj-$(CONFIG_PPC32) += bootx_init.o obj-$(CONFIG_SMP) += smp.o +obj-$(CONFIG_PPC_MERGE) += udbg_scc.o udbg_adb.o diff --git a/arch/powerpc/platforms/powermac/bootx_init.c b/arch/powerpc/platforms/powermac/bootx_init.c new file mode 100644 index 0000000..fa8b4d7 --- /dev/null +++ b/arch/powerpc/platforms/powermac/bootx_init.c @@ -0,0 +1,547 @@ +/* + * Early boot support code for BootX bootloader + * + * Copyright (C) 2005 Ben. Herrenschmidt (benh@kernel.crashing.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef DEBUG +#define SET_BOOT_BAT + +#ifdef DEBUG +#define DBG(fmt...) do { bootx_printf(fmt); } while(0) +#else +#define DBG(fmt...) do { } while(0) +#endif + +extern void __start(unsigned long r3, unsigned long r4, unsigned long r5); + +static unsigned long __initdata bootx_dt_strbase; +static unsigned long __initdata bootx_dt_strend; +static unsigned long __initdata bootx_node_chosen; +static boot_infos_t * __initdata bootx_info; +static char __initdata bootx_disp_path[256]; + +/* Is boot-info compatible ? */ +#define BOOT_INFO_IS_COMPATIBLE(bi) \ + ((bi)->compatible_version <= BOOT_INFO_VERSION) +#define BOOT_INFO_IS_V2_COMPATIBLE(bi) ((bi)->version >= 2) +#define BOOT_INFO_IS_V4_COMPATIBLE(bi) ((bi)->version >= 4) + +#ifdef CONFIG_BOOTX_TEXT +static void __init bootx_printf(const char *format, ...) +{ + const char *p, *q, *s; + va_list args; + unsigned long v; + + va_start(args, format); + for (p = format; *p != 0; p = q) { + for (q = p; *q != 0 && *q != '\n' && *q != '%'; ++q) + ; + if (q > p) + btext_drawtext(p, q - p); + if (*q == 0) + break; + if (*q == '\n') { + ++q; + btext_flushline(); + btext_drawstring("\r\n"); + btext_flushline(); + continue; + } + ++q; + if (*q == 0) + break; + switch (*q) { + case 's': + ++q; + s = va_arg(args, const char *); + if (s == NULL) + s = ""; + btext_drawstring(s); + break; + case 'x': + ++q; + v = va_arg(args, unsigned long); + btext_drawhex(v); + break; + } + } +} +#else /* CONFIG_BOOTX_TEXT */ +static void __init bootx_printf(const char *format, ...) {} +#endif /* CONFIG_BOOTX_TEXT */ + +static void * __init bootx_early_getprop(unsigned long base, + unsigned long node, + char *prop) +{ + struct bootx_dt_node *np = (struct bootx_dt_node *)(base + node); + u32 *ppp = &np->properties; + + while(*ppp) { + struct bootx_dt_prop *pp = + (struct bootx_dt_prop *)(base + *ppp); + + if (strcmp((char *)((unsigned long)pp->name + base), + prop) == 0) { + return (void *)((unsigned long)pp->value + base); + } + ppp = &pp->next; + } + return NULL; +} + +#define dt_push_token(token, mem) \ + do { \ + *(mem) = _ALIGN_UP(*(mem),4); \ + *((u32 *)*(mem)) = token; \ + *(mem) += 4; \ + } while(0) + +static unsigned long __init bootx_dt_find_string(char *str) +{ + char *s, *os; + + s = os = (char *)bootx_dt_strbase; + s += 4; + while (s < (char *)bootx_dt_strend) { + if (strcmp(s, str) == 0) + return s - os; + s += strlen(s) + 1; + } + return 0; +} + +static void __init bootx_dt_add_prop(char *name, void *data, int size, + unsigned long *mem_end) +{ + unsigned long soff = bootx_dt_find_string(name); + if (data == NULL) + size = 0; + if (soff == 0) { + bootx_printf("WARNING: Can't find string index for <%s>\n", + name); + return; + } + if (size > 0x20000) { + bootx_printf("WARNING: ignoring large property "); + bootx_printf("%s length 0x%x\n", name, size); + return; + } + dt_push_token(OF_DT_PROP, mem_end); + dt_push_token(size, mem_end); + dt_push_token(soff, mem_end); + + /* push property content */ + if (size && data) { + memcpy((void *)*mem_end, data, size); + *mem_end = _ALIGN_UP(*mem_end + size, 4); + } +} + +static void __init bootx_add_chosen_props(unsigned long base, + unsigned long *mem_end) +{ + u32 val = _MACH_Pmac; + + bootx_dt_add_prop("linux,platform", &val, 4, mem_end); + + if (bootx_info->kernelParamsOffset) { + char *args = (char *)((unsigned long)bootx_info) + + bootx_info->kernelParamsOffset; + bootx_dt_add_prop("bootargs", args, strlen(args) + 1, mem_end); + } + if (bootx_info->ramDisk) { + val = ((unsigned long)bootx_info) + bootx_info->ramDisk; + bootx_dt_add_prop("linux,initrd-start", &val, 4, mem_end); + val += bootx_info->ramDiskSize; + bootx_dt_add_prop("linux,initrd-end", &val, 4, mem_end); + } + if (strlen(bootx_disp_path)) + bootx_dt_add_prop("linux,stdout-path", bootx_disp_path, + strlen(bootx_disp_path) + 1, mem_end); +} + +static void __init bootx_add_display_props(unsigned long base, + unsigned long *mem_end) +{ + bootx_dt_add_prop("linux,boot-display", NULL, 0, mem_end); + bootx_dt_add_prop("linux,opened", NULL, 0, mem_end); +} + +static void __init bootx_dt_add_string(char *s, unsigned long *mem_end) +{ + unsigned int l = strlen(s) + 1; + memcpy((void *)*mem_end, s, l); + bootx_dt_strend = *mem_end = *mem_end + l; +} + +static void __init bootx_scan_dt_build_strings(unsigned long base, + unsigned long node, + unsigned long *mem_end) +{ + struct bootx_dt_node *np = (struct bootx_dt_node *)(base + node); + u32 *cpp, *ppp = &np->properties; + unsigned long soff; + char *namep; + + /* Keep refs to known nodes */ + namep = np->full_name ? (char *)(base + np->full_name) : NULL; + if (namep == NULL) { + bootx_printf("Node without a full name !\n"); + namep = ""; + } + DBG("* strings: %s\n", namep); + + if (!strcmp(namep, "/chosen")) { + DBG(" detected /chosen ! adding properties names !\n"); + bootx_dt_add_string("linux,platform", mem_end); + bootx_dt_add_string("linux,stdout-path", mem_end); + bootx_dt_add_string("linux,initrd-start", mem_end); + bootx_dt_add_string("linux,initrd-end", mem_end); + bootx_dt_add_string("bootargs", mem_end); + bootx_node_chosen = node; + } + if (node == bootx_info->dispDeviceRegEntryOffset) { + DBG(" detected display ! adding properties names !\n"); + bootx_dt_add_string("linux,boot-display", mem_end); + bootx_dt_add_string("linux,opened", mem_end); + strncpy(bootx_disp_path, namep, 255); + } + + /* get and store all property names */ + while (*ppp) { + struct bootx_dt_prop *pp = + (struct bootx_dt_prop *)(base + *ppp); + + namep = pp->name ? (char *)(base + pp->name) : NULL; + if (namep == NULL || strcmp(namep, "name") == 0) + goto next; + /* get/create string entry */ + soff = bootx_dt_find_string(namep); + if (soff == 0) + bootx_dt_add_string(namep, mem_end); + next: + ppp = &pp->next; + } + + /* do all our children */ + cpp = &np->child; + while(*cpp) { + np = (struct bootx_dt_node *)(base + *cpp); + bootx_scan_dt_build_strings(base, *cpp, mem_end); + cpp = &np->sibling; + } +} + +static void __init bootx_scan_dt_build_struct(unsigned long base, + unsigned long node, + unsigned long *mem_end) +{ + struct bootx_dt_node *np = (struct bootx_dt_node *)(base + node); + u32 *cpp, *ppp = &np->properties; + char *namep, *p, *ep, *lp; + int l; + + dt_push_token(OF_DT_BEGIN_NODE, mem_end); + + /* get the node's full name */ + namep = np->full_name ? (char *)(base + np->full_name) : NULL; + if (namep == NULL) + namep = ""; + l = strlen(namep); + + DBG("* struct: %s\n", namep); + + /* Fixup an Apple bug where they have bogus \0 chars in the + * middle of the path in some properties, and extract + * the unit name (everything after the last '/'). + */ + memcpy((void *)*mem_end, namep, l + 1); + namep = (char *)*mem_end; + for (lp = p = namep, ep = namep + l; p < ep; p++) { + if (*p == '/') + lp = namep; + else if (*p != 0) + *lp++ = *p; + } + *lp = 0; + *mem_end = _ALIGN_UP((unsigned long)lp + 1, 4); + + /* get and store all properties */ + while (*ppp) { + struct bootx_dt_prop *pp = + (struct bootx_dt_prop *)(base + *ppp); + + namep = pp->name ? (char *)(base + pp->name) : NULL; + /* Skip "name" */ + if (namep == NULL || !strcmp(namep, "name")) + goto next; + /* Skip "bootargs" in /chosen too as we replace it */ + if (node == bootx_node_chosen && !strcmp(namep, "bootargs")) + goto next; + + /* push property head */ + bootx_dt_add_prop(namep, + pp->value ? (void *)(base + pp->value): NULL, + pp->length, mem_end); + next: + ppp = &pp->next; + } + + if (node == bootx_node_chosen) + bootx_add_chosen_props(base, mem_end); + if (node == bootx_info->dispDeviceRegEntryOffset) + bootx_add_display_props(base, mem_end); + + /* do all our children */ + cpp = &np->child; + while(*cpp) { + np = (struct bootx_dt_node *)(base + *cpp); + bootx_scan_dt_build_struct(base, *cpp, mem_end); + cpp = &np->sibling; + } + + dt_push_token(OF_DT_END_NODE, mem_end); +} + +static unsigned long __init bootx_flatten_dt(unsigned long start) +{ + boot_infos_t *bi = bootx_info; + unsigned long mem_start, mem_end; + struct boot_param_header *hdr; + unsigned long base; + u64 *rsvmap; + + /* Start using memory after the big blob passed by BootX, get + * some space for the header + */ + mem_start = mem_end = _ALIGN_UP(((unsigned long)bi) + start, 4); + DBG("Boot params header at: %x\n", mem_start); + hdr = (struct boot_param_header *)mem_start; + mem_end += sizeof(struct boot_param_header); + rsvmap = (u64 *)(_ALIGN_UP(mem_end, 8)); + hdr->off_mem_rsvmap = ((unsigned long)rsvmap) - mem_start; + mem_end = ((unsigned long)rsvmap) + 8 * sizeof(u64); + + /* Get base of tree */ + base = ((unsigned long)bi) + bi->deviceTreeOffset; + + /* Build string array */ + DBG("Building string array at: %x\n", mem_end); + DBG("Device Tree Base=%x\n", base); + bootx_dt_strbase = mem_end; + mem_end += 4; + bootx_dt_strend = mem_end; + bootx_scan_dt_build_strings(base, 4, &mem_end); + hdr->off_dt_strings = bootx_dt_strbase - mem_start; + hdr->dt_strings_size = bootx_dt_strend - bootx_dt_strbase; + + /* Build structure */ + mem_end = _ALIGN(mem_end, 16); + DBG("Building device tree structure at: %x\n", mem_end); + hdr->off_dt_struct = mem_end - mem_start; + bootx_scan_dt_build_struct(base, 4, &mem_end); + dt_push_token(OF_DT_END, &mem_end); + + /* Finish header */ + hdr->boot_cpuid_phys = 0; + hdr->magic = OF_DT_HEADER; + hdr->totalsize = mem_end - mem_start; + hdr->version = OF_DT_VERSION; + /* Version 16 is not backward compatible */ + hdr->last_comp_version = 0x10; + + /* Reserve the whole thing and copy the reserve map in, we + * also bump mem_reserve_cnt to cause further reservations to + * fail since it's too late. + */ + mem_end = _ALIGN(mem_end, PAGE_SIZE); + DBG("End of boot params: %x\n", mem_end); + rsvmap[0] = mem_start; + rsvmap[1] = mem_end; + rsvmap[2] = 0; + rsvmap[3] = 0; + + return (unsigned long)hdr; +} + + +#ifdef CONFIG_BOOTX_TEXT +static void __init btext_welcome(boot_infos_t *bi) +{ + unsigned long flags; + unsigned long pvr; + + bootx_printf("Welcome to Linux, kernel " UTS_RELEASE "\n"); + bootx_printf("\nlinked at : 0x%x", KERNELBASE); + bootx_printf("\nframe buffer at : 0x%x", bi->dispDeviceBase); + bootx_printf(" (phys), 0x%x", bi->logicalDisplayBase); + bootx_printf(" (log)"); + bootx_printf("\nklimit : 0x%x",(unsigned long)klimit); + bootx_printf("\nboot_info at : 0x%x", bi); + __asm__ __volatile__ ("mfmsr %0" : "=r" (flags)); + bootx_printf("\nMSR : 0x%x", flags); + __asm__ __volatile__ ("mfspr %0, 287" : "=r" (pvr)); + bootx_printf("\nPVR : 0x%x", pvr); + pvr >>= 16; + if (pvr > 1) { + __asm__ __volatile__ ("mfspr %0, 1008" : "=r" (flags)); + bootx_printf("\nHID0 : 0x%x", flags); + } + if (pvr == 8 || pvr == 12 || pvr == 0x800c) { + __asm__ __volatile__ ("mfspr %0, 1019" : "=r" (flags)); + bootx_printf("\nICTC : 0x%x", flags); + } +#ifdef DEBUG + bootx_printf("\n\n"); + bootx_printf("bi->deviceTreeOffset : 0x%x\n", + bi->deviceTreeOffset); + bootx_printf("bi->deviceTreeSize : 0x%x\n", + bi->deviceTreeSize); +#endif + bootx_printf("\n\n"); +} +#endif /* CONFIG_BOOTX_TEXT */ + +void __init bootx_init(unsigned long r3, unsigned long r4) +{ + boot_infos_t *bi = (boot_infos_t *) r4; + unsigned long hdr; + unsigned long space; + unsigned long ptr, x; + char *model; + unsigned long offset = reloc_offset(); + + reloc_got2(offset); + + bootx_info = bi; + + /* We haven't cleared any bss at this point, make sure + * what we need is initialized + */ + bootx_dt_strbase = bootx_dt_strend = 0; + bootx_node_chosen = 0; + bootx_disp_path[0] = 0; + + if (!BOOT_INFO_IS_V2_COMPATIBLE(bi)) + bi->logicalDisplayBase = bi->dispDeviceBase; + +#ifdef CONFIG_BOOTX_TEXT + btext_setup_display(bi->dispDeviceRect[2] - bi->dispDeviceRect[0], + bi->dispDeviceRect[3] - bi->dispDeviceRect[1], + bi->dispDeviceDepth, bi->dispDeviceRowBytes, + (unsigned long)bi->logicalDisplayBase); + btext_clearscreen(); + btext_flushscreen(); +#endif /* CONFIG_BOOTX_TEXT */ + + /* + * Test if boot-info is compatible. Done only in config + * CONFIG_BOOTX_TEXT since there is nothing much we can do + * with an incompatible version, except display a message + * and eventually hang the processor... + * + * I'll try to keep enough of boot-info compatible in the + * future to always allow display of this message; + */ + if (!BOOT_INFO_IS_COMPATIBLE(bi)) { + bootx_printf(" !!! WARNING - Incompatible version" + " of BootX !!!\n\n\n"); + for (;;) + ; + } + if (bi->architecture != BOOT_ARCH_PCI) { + bootx_printf(" !!! WARNING - Usupported machine" + " architecture !\n"); + for (;;) + ; + } + +#ifdef CONFIG_BOOTX_TEXT + btext_welcome(bi); +#endif + /* New BootX enters kernel with MMU off, i/os are not allowed + * here. This hack will have been done by the boostrap anyway. + */ + if (bi->version < 4) { + /* + * XXX If this is an iMac, turn off the USB controller. + */ + model = (char *) bootx_early_getprop(r4 + bi->deviceTreeOffset, + 4, "model"); + if (model + && (strcmp(model, "iMac,1") == 0 + || strcmp(model, "PowerMac1,1") == 0)) { + bootx_printf("iMac,1 detected, shutting down USB \n"); + out_le32((unsigned *)0x80880008, 1); /* XXX */ + } + } + + /* Get a pointer that points above the device tree, args, ramdisk, + * etc... to use for generating the flattened tree + */ + if (bi->version < 5) { + space = bi->deviceTreeOffset + bi->deviceTreeSize; + if (bi->ramDisk) + space = bi->ramDisk + bi->ramDiskSize; + } else + space = bi->totalParamsSize; + + bootx_printf("Total space used by parameters & ramdisk: %x \n", space); + + /* New BootX will have flushed all TLBs and enters kernel with + * MMU switched OFF, so this should not be useful anymore. + */ + if (bi->version < 4) { + bootx_printf("Touching pages...\n"); + + /* + * Touch each page to make sure the PTEs for them + * are in the hash table - the aim is to try to avoid + * getting DSI exceptions while copying the kernel image. + */ + for (ptr = ((unsigned long) &_stext) & PAGE_MASK; + ptr < (unsigned long)bi + space; ptr += PAGE_SIZE) + x = *(volatile unsigned long *)ptr; + } + + /* Ok, now we need to generate a flattened device-tree to pass + * to the kernel + */ + bootx_printf("Preparing boot params...\n"); + + hdr = bootx_flatten_dt(space); + +#ifdef CONFIG_BOOTX_TEXT +#ifdef SET_BOOT_BAT + bootx_printf("Preparing BAT...\n"); + btext_prepare_BAT(); +#else + btext_unmap(); +#endif +#endif + + reloc_got2(-offset); + + __start(hdr, KERNELBASE + offset, 0); +} diff --git a/arch/powerpc/platforms/powermac/feature.c b/arch/powerpc/platforms/powermac/feature.c index 0d7fa00..4c35799 100644 --- a/arch/powerpc/platforms/powermac/feature.c +++ b/arch/powerpc/platforms/powermac/feature.c @@ -2596,6 +2596,8 @@ found: */ static void __init probe_uninorth(void) { + u32 *addrp; + phys_addr_t address; unsigned long actrl; /* Locate core99 Uni-N */ @@ -2605,20 +2607,23 @@ static void __init probe_uninorth(void) uninorth_node = of_find_node_by_name(NULL, "u3"); uninorth_u3 = 1; } - if (uninorth_node && uninorth_node->n_addrs > 0) { - unsigned long address = uninorth_node->addrs[0].address; - uninorth_base = ioremap(address, 0x40000); - uninorth_rev = in_be32(UN_REG(UNI_N_VERSION)); - if (uninorth_u3) - u3_ht = ioremap(address + U3_HT_CONFIG_BASE, 0x1000); - } else - uninorth_node = NULL; + if (uninorth_node == NULL) + return; - if (!uninorth_node) + addrp = (u32 *)get_property(uninorth_node, "reg", NULL); + if (addrp == NULL) + return; + address = of_translate_address(uninorth_node, addrp); + if (address == 0) return; + uninorth_base = ioremap(address, 0x40000); + uninorth_rev = in_be32(UN_REG(UNI_N_VERSION)); + if (uninorth_u3) + u3_ht = ioremap(address + U3_HT_CONFIG_BASE, 0x1000); - printk(KERN_INFO "Found %s memory controller & host bridge, revision: %d\n", - uninorth_u3 ? "U3" : "UniNorth", uninorth_rev); + printk(KERN_INFO "Found %s memory controller & host bridge," + " revision: %d\n", uninorth_u3 ? "U3" : "UniNorth", + uninorth_rev); printk(KERN_INFO "Mapped at 0x%08lx\n", (unsigned long)uninorth_base); /* Set the arbitrer QAck delay according to what Apple does @@ -2642,18 +2647,17 @@ static void __init probe_one_macio(const { struct device_node* node; int i; - volatile u32 __iomem * base; - u32* revp; + volatile u32 __iomem *base; + u32 *addrp, *revp; + phys_addr_t addr; + u64 size; - node = find_devices(name); - if (!node || !node->n_addrs) - return; - if (compat) - do { - if (device_is_compatible(node, compat)) - break; - node = node->next; - } while (node); + for (node = NULL; (node = of_find_node_by_name(node, name)) != NULL;) { + if (!compat) + break; + if (device_is_compatible(node, compat)) + break; + } if (!node) return; for(i=0; i= MAX_MACIO_CHIPS) { printk(KERN_ERR "pmac_feature: Please increase MAX_MACIO_CHIPS !\n"); printk(KERN_ERR "pmac_feature: %s skipped\n", node->full_name); return; } - base = ioremap(node->addrs[0].address, node->addrs[0].size); + addrp = of_get_pci_address(node, 0, &size); + if (addrp == NULL) { + printk(KERN_ERR "pmac_feature: %s: can't find base !\n", + node->full_name); + return; + } + addr = of_translate_address(node, addrp); + if (addr == 0) { + printk(KERN_ERR "pmac_feature: %s, can't translate base !\n", + node->full_name); + return; + } + base = ioremap(addr, (unsigned long)size); if (!base) { - printk(KERN_ERR "pmac_feature: Can't map mac-io chip !\n"); + printk(KERN_ERR "pmac_feature: %s, can't map mac-io chip !\n", + node->full_name); return; } if (type == macio_keylargo) { diff --git a/arch/powerpc/platforms/powermac/low_i2c.c b/arch/powerpc/platforms/powermac/low_i2c.c index f3f39e8..606e0ed 100644 --- a/arch/powerpc/platforms/powermac/low_i2c.c +++ b/arch/powerpc/platforms/powermac/low_i2c.c @@ -36,7 +36,7 @@ #ifdef DEBUG #define DBG(x...) do {\ - printk(KERN_DEBUG "KW:" x); \ + printk(KERN_DEBUG "low_i2c:" x); \ } while(0) #else #define DBG(x...) @@ -342,7 +342,7 @@ static int keywest_low_i2c_func(struct l static void keywest_low_i2c_add(struct device_node *np) { struct low_i2c_host *host = find_low_i2c_host(NULL); - u32 *psteps, *prate, steps, aoffset = 0; + u32 *psteps, *prate, *addrp, steps; struct device_node *parent; if (host == NULL) { @@ -352,6 +352,16 @@ static void keywest_low_i2c_add(struct d } memset(host, 0, sizeof(*host)); + /* Apple is kind enough to provide a valid AAPL,address property + * on all i2c keywest nodes so far ... we would have to fallback + * to macio parsing if that wasn't the case + */ + addrp = (u32 *)get_property(np, "AAPL,address", NULL); + if (addrp == NULL) { + printk(KERN_ERR "low_i2c: Can't find address for %s\n", + np->full_name); + return; + } init_MUTEX(&host->mutex); host->np = of_node_get(np); psteps = (u32 *)get_property(np, "AAPL,address-step", NULL); @@ -360,12 +370,10 @@ static void keywest_low_i2c_add(struct d steps >>= 1; parent = of_get_parent(np); host->num_channels = 1; - if (parent && parent->name[0] == 'u') { + if (parent && parent->name[0] == 'u') host->num_channels = 2; - aoffset = 3; - } /* Select interface rate */ - host->speed = KW_I2C_MODE_100KHZ; + host->speed = KW_I2C_MODE_25KHZ; prate = (u32 *)get_property(np, "AAPL,i2c-rate", NULL); if (prate) switch(*prate) { case 100: @@ -379,9 +387,12 @@ static void keywest_low_i2c_add(struct d break; } + printk(KERN_INFO "low_i2c: Bus %s found at 0x%08x, %d channels," + " speed = %d KHz\n", + np->full_name, *addrp, host->num_channels, prate ? *prate : 25); + host->mode = pmac_low_i2c_mode_std; - host->base = ioremap(np->addrs[0].address + aoffset, - np->addrs[0].size); + host->base = ioremap((*addrp), 0x1000); host->func = keywest_low_i2c_func; } diff --git a/arch/powerpc/platforms/powermac/nvram.c b/arch/powerpc/platforms/powermac/nvram.c index 4042e2f..59e0e51 100644 --- a/arch/powerpc/platforms/powermac/nvram.c +++ b/arch/powerpc/platforms/powermac/nvram.c @@ -549,6 +549,7 @@ static int __init core99_nvram_setup(str ppc_md.nvram_write = core99_nvram_write; ppc_md.nvram_size = core99_nvram_size; ppc_md.nvram_sync = core99_nvram_sync; + ppc_md.machine_shutdown = core99_nvram_sync; /* * Maybe we could be smarter here though making an exclusive list * of known flash chips is a bit nasty as older OF didn't provide us diff --git a/arch/powerpc/platforms/powermac/pic.c b/arch/powerpc/platforms/powermac/pic.c index 90040c4..a36527c 100644 --- a/arch/powerpc/platforms/powermac/pic.c +++ b/arch/powerpc/platforms/powermac/pic.c @@ -471,7 +471,7 @@ void __init pmac_pic_init(void) setup_irq(nmi_irq, &xmon_action); } } -#endif /* CONFIG_XMON */ +#endif /* defined(CONFIG_XMON) && defined(CONFIG_PPC32) */ return; } irqctrler = NULL; diff --git a/arch/powerpc/platforms/powermac/setup.c b/arch/powerpc/platforms/powermac/setup.c index 7acb054..6ee620f 100644 --- a/arch/powerpc/platforms/powermac/setup.c +++ b/arch/powerpc/platforms/powermac/setup.c @@ -60,6 +60,7 @@ #include #include #include +#include #include #include #include @@ -76,6 +77,7 @@ #include #include #include +#include #include "pmac.h" @@ -321,16 +323,6 @@ void __init pmac_setup_arch(void) l2cr_init(); #endif /* CONFIG_PPC32 */ -#ifdef CONFIG_PPC64 - /* Probe motherboard chipset */ - /* this is done earlier in setup_arch for 32-bit */ - pmac_feature_init(); - - /* We can NAP */ - powersave_nap = 1; - printk(KERN_INFO "Using native/NAP idle loop\n"); -#endif - #ifdef CONFIG_KGDB zs_kgdb_hook(0); #endif @@ -621,13 +613,26 @@ static void __init pmac_init_early(void) * and call ioremap */ hpte_init_native(); +#endif - /* Init SCC */ - if (strstr(cmd_line, "sccdbg")) { - sccdbg = 1; - udbg_init_scc(NULL); + /* Enable early btext debug if requested */ + if (strstr(cmd_line, "btextdbg")) { + udbg_adb_init_early(); + register_early_udbg_console(); } + /* Probe motherboard chipset */ + pmac_feature_init(); + + /* We can NAP */ + powersave_nap = 1; + printk(KERN_INFO "Using native/NAP idle loop\n"); + + /* Initialize debug stuff */ + udbg_scc_init(!!strstr(cmd_line, "sccdbg")); + udbg_adb_init(!!strstr(cmd_line, "btextdbg")); + +#ifdef CONFIG_PPC64 /* Setup interrupt mapping options */ ppc64_interrupt_controller = IC_OPEN_PIC; @@ -637,19 +642,8 @@ static void __init pmac_init_early(void) static void __init pmac_progress(char *s, unsigned short hex) { -#ifdef CONFIG_PPC64 - if (sccdbg) { - udbg_puts(s); - udbg_puts("\n"); - return; - } -#endif -#ifdef CONFIG_BOOTX_TEXT - if (boot_text_mapped) { - btext_drawstring(s); - btext_drawchar('\n'); - } -#endif /* CONFIG_BOOTX_TEXT */ + udbg_puts(s); + udbg_puts("\n"); } /* @@ -734,7 +728,8 @@ static int __init pmac_probe(int platfor } #ifdef CONFIG_PPC64 -static int pmac_probe_mode(struct pci_bus *bus) +/* Move that to pci.c */ +static int pmac_pci_probe_mode(struct pci_bus *bus) { struct device_node *node = bus->sysdata; @@ -770,10 +765,14 @@ struct machdep_calls __initdata pmac_md .check_legacy_ioport = pmac_check_legacy_ioport, .progress = pmac_progress, #ifdef CONFIG_PPC64 - .pci_probe_mode = pmac_probe_mode, + .pci_probe_mode = pmac_pci_probe_mode, .idle_loop = native_idle, .enable_pmcs = power4_enable_pmcs, +#ifdef CONFIG_KEXEC + .machine_kexec = default_machine_kexec, + .machine_kexec_prepare = default_machine_kexec_prepare, #endif +#endif /* CONFIG_PPC64 */ #ifdef CONFIG_PPC32 .pcibios_enable_device_hook = pmac_pci_enable_device_hook, .pcibios_after_init = pmac_pcibios_after_init, diff --git a/arch/powerpc/platforms/powermac/udbg_adb.c b/arch/powerpc/platforms/powermac/udbg_adb.c new file mode 100644 index 0000000..e51de55 --- /dev/null +++ b/arch/powerpc/platforms/powermac/udbg_adb.c @@ -0,0 +1,218 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * This implementation is "special", it can "patch" the current + * udbg implementation and work on top of it. It must thus be + * initialized last + */ + +static void (*udbg_adb_old_putc)(char c); +static char (*udbg_adb_old_getc)(void); +static int (*udbg_adb_old_getc_poll)(void); + +static enum { + input_adb_none, + input_adb_pmu, + input_adb_cuda, +} input_type = input_adb_none; + +static int udbg_adb_use_btext; + +int xmon_wants_key, xmon_adb_keycode; + +static inline void udbg_adb_poll(void) +{ +#ifdef CONFIG_ADB_PMU + if (input_type == input_adb_pmu) + pmu_poll_adb(); +#endif /* CONFIG_ADB_PMU */ +#ifdef CONFIG_ADB_CUDA + if (input_type == input_adb_cuda) + cuda_poll(); +#endif /* CONFIG_ADB_CUDA */ +} + +#ifdef CONFIG_BOOTX_TEXT +static int xmon_adb_shiftstate; + +static unsigned char xmon_keytab[128] = + "asdfhgzxcv\000bqwer" /* 0x00 - 0x0f */ + "yt123465=97-80]o" /* 0x10 - 0x1f */ + "u[ip\rlj'k;\\,/nm." /* 0x20 - 0x2f */ + "\t `\177\0\033\0\0\0\0\0\0\0\0\0\0" /* 0x30 - 0x3f */ + "\0.\0*\0+\0\0\0\0\0/\r\0-\0" /* 0x40 - 0x4f */ + "\0\0000123456789\0\0\0"; /* 0x50 - 0x5f */ + +static unsigned char xmon_shift_keytab[128] = + "ASDFHGZXCV\000BQWER" /* 0x00 - 0x0f */ + "YT!@#$^%+(&_*)}O" /* 0x10 - 0x1f */ + "U{IP\rLJ\"K:|" /* 0x20 - 0x2f */ + "\t ~\177\0\033\0\0\0\0\0\0\0\0\0\0" /* 0x30 - 0x3f */ + "\0.\0*\0+\0\0\0\0\0/\r\0-\0" /* 0x40 - 0x4f */ + "\0\0000123456789\0\0\0"; /* 0x50 - 0x5f */ + +static char udbg_adb_local_getc(void) +{ + int k, t, on; + + xmon_wants_key = 1; + for (;;) { + xmon_adb_keycode = -1; + t = 0; + on = 0; + k = -1; + do { + if (--t < 0) { + on = 1 - on; + btext_drawchar(on? 0xdb: 0x20); + btext_drawchar('\b'); + t = 200000; + } + udbg_adb_poll(); + if (udbg_adb_old_getc_poll) + k = udbg_adb_old_getc_poll(); + } while (k == -1 && xmon_adb_keycode == -1); + if (on) + btext_drawstring(" \b"); + if (k != -1) + return k; + k = xmon_adb_keycode; + + /* test for shift keys */ + if ((k & 0x7f) == 0x38 || (k & 0x7f) == 0x7b) { + xmon_adb_shiftstate = (k & 0x80) == 0; + continue; + } + if (k >= 0x80) + continue; /* ignore up transitions */ + k = (xmon_adb_shiftstate? xmon_shift_keytab: xmon_keytab)[k]; + if (k != 0) + break; + } + xmon_wants_key = 0; + return k; +} +#endif /* CONFIG_BOOTX_TEXT */ + +static char udbg_adb_getc(void) +{ +#ifdef CONFIG_BOOTX_TEXT + if (udbg_adb_use_btext && input_type != input_adb_none) + return udbg_adb_local_getc(); +#endif + if (udbg_adb_old_getc) + return udbg_adb_old_getc(); + return -1; +} + +/* getc_poll() is not really used, unless you have the xmon-over modem + * hack that doesn't quite concern us here, thus we just poll the low level + * ADB driver to prevent it from timing out and call back the original poll + * routine. + */ +static int udbg_adb_getc_poll(void) +{ + udbg_adb_poll(); + + if (udbg_adb_old_getc_poll) + return udbg_adb_old_getc_poll(); + return -1; +} + +static void udbg_adb_putc(char c) +{ +#ifdef CONFIG_BOOTX_TEXT + if (udbg_adb_use_btext) + btext_drawchar(c); +#endif + if (udbg_adb_old_putc) + return udbg_adb_old_putc(c); +} + +void udbg_adb_init_early(void) +{ +#ifdef CONFIG_BOOTX_TEXT + if (btext_find_display(1) == 0) { + udbg_adb_use_btext = 1; + udbg_putc = udbg_adb_putc; + } +#endif +} + +int udbg_adb_init(int force_btext) +{ + struct device_node *np; + + /* Capture existing callbacks */ + udbg_adb_old_putc = udbg_putc; + udbg_adb_old_getc = udbg_getc; + udbg_adb_old_getc_poll = udbg_getc_poll; + + /* Check if our early init was already called */ + if (udbg_adb_old_putc == udbg_adb_putc || + udbg_adb_old_putc == btext_drawchar) + udbg_adb_old_putc = NULL; + + /* Set ours as output */ + udbg_putc = udbg_adb_putc; + udbg_getc = udbg_adb_getc; + udbg_getc_poll = udbg_adb_getc_poll; + +#ifdef CONFIG_BOOTX_TEXT + /* Check if we should use btext output */ + if (btext_find_display(force_btext) == 0) + udbg_adb_use_btext = 1; +#endif + + /* See if there is a keyboard in the device tree with a parent + * of type "adb". If not, we return a failure, but we keep the + * bext output set for now + */ + for (np = NULL; (np = of_find_node_by_name(np, "keyboard")) != NULL;) { + struct device_node *parent = of_get_parent(np); + int found = (parent && !strcmp(parent->type, "adb") == 0); + of_node_put(parent); + if (found) + break; + } + if (np == NULL) + return -ENODEV; + of_node_put(np); + +#ifdef CONFIG_ADB_PMU + if (find_via_pmu()) + input_type = input_adb_pmu; +#endif +#ifdef CONFIG_ADB_CUDA + if (find_via_cuda()) + input_type = input_adb_cuda; +#endif + + /* Same as above: nothing found, keep btext set for output */ + if (input_type == input_adb_none) + return -ENODEV; + + return 0; +} diff --git a/arch/powerpc/platforms/powermac/udbg_scc.c b/arch/powerpc/platforms/powermac/udbg_scc.c new file mode 100644 index 0000000..df6dec4 --- /dev/null +++ b/arch/powerpc/platforms/powermac/udbg_scc.c @@ -0,0 +1,165 @@ +/* + * udbg for for zilog scc ports as found on Apple PowerMacs + * + * Copyright (C) 2001-2005 PPC 64 Team, IBM Corp + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include + +extern u8 real_readb(volatile u8 __iomem *addr); +extern void real_writeb(u8 data, volatile u8 __iomem *addr); + +#define SCC_TXRDY 4 +#define SCC_RXRDY 1 + +static volatile u8 __iomem *sccc; +static volatile u8 __iomem *sccd; + +static void udbg_scc_putc(char c) +{ + if (sccc) { + while ((in_8(sccc) & SCC_TXRDY) == 0) + ; + out_8(sccd, c); + if (c == '\n') + udbg_scc_putc('\r'); + } +} + +static int udbg_scc_getc_poll(void) +{ + if (sccc) { + if ((in_8(sccc) & SCC_RXRDY) != 0) + return in_8(sccd); + else + return -1; + } + return -1; +} + +static char udbg_scc_getc(void) +{ + if (sccc) { + while ((in_8(sccc) & SCC_RXRDY) == 0) + ; + return in_8(sccd); + } + return 0; +} + +static unsigned char scc_inittab[] = { + 13, 0, /* set baud rate divisor */ + 12, 0, + 14, 1, /* baud rate gen enable, src=rtxc */ + 11, 0x50, /* clocks = br gen */ + 5, 0xea, /* tx 8 bits, assert DTR & RTS */ + 4, 0x46, /* x16 clock, 1 stop */ + 3, 0xc1, /* rx enable, 8 bits */ +}; + +void udbg_scc_init(int force_scc) +{ + u32 *reg; + unsigned long addr; + struct device_node *stdout = NULL, *escc = NULL, *macio = NULL; + struct device_node *ch, *ch_def = NULL, *ch_a = NULL; + char *path; + int i, x; + + escc = of_find_node_by_name(NULL, "escc"); + if (escc == NULL) + goto bail; + macio = of_get_parent(escc); + if (macio == NULL) + goto bail; + path = (char *)get_property(of_chosen, "linux,stdout-path", NULL); + if (path != NULL) + stdout = of_find_node_by_path(path); + for (ch = NULL; (ch = of_get_next_child(escc, ch)) != NULL;) { + if (ch == stdout) + ch_def = of_node_get(ch); + if (strcmp(ch->name, "ch-a") == 0) + ch_a = of_node_get(ch); + } + if (ch_def == NULL && !force_scc) + goto bail; + + ch = ch_def ? ch_def : ch_a; + + /* Get address within mac-io ASIC */ + reg = (u32 *)get_property(escc, "reg", NULL); + if (reg == NULL) + goto bail; + addr = reg[0]; + + /* Get address of mac-io PCI itself */ + reg = (u32 *)get_property(macio, "assigned-addresses", NULL); + if (reg == NULL) + goto bail; + addr += reg[2]; + + /* Lock the serial port */ + pmac_call_feature(PMAC_FTR_SCC_ENABLE, ch, + PMAC_SCC_ASYNC | PMAC_SCC_FLAG_XMON, 1); + + + /* Setup for 57600 8N1 */ + if (ch == ch_a) + addr += 0x20; + sccc = (volatile u8 * __iomem) ioremap(addr & PAGE_MASK, PAGE_SIZE) ; + sccc += addr & ~PAGE_MASK; + sccd = sccc + 0x10; + + mb(); + + for (i = 20000; i != 0; --i) + x = in_8(sccc); + out_8(sccc, 0x09); /* reset A or B side */ + out_8(sccc, 0xc0); + for (i = 0; i < sizeof(scc_inittab); ++i) + out_8(sccc, scc_inittab[i]); + + udbg_putc = udbg_scc_putc; + udbg_getc = udbg_scc_getc; + udbg_getc_poll = udbg_scc_getc_poll; + + udbg_puts("Hello World !\n"); + + bail: + of_node_put(macio); + of_node_put(escc); + of_node_put(stdout); + of_node_put(ch_def); + of_node_put(ch_a); +} + +#ifdef CONFIG_PPC64 +static void udbg_real_scc_putc(char c) +{ + while ((real_readb(sccc) & SCC_TXRDY) == 0) + ; + real_writeb(c, sccd); + if (c == '\n') + udbg_real_scc_putc('\r'); +} + +void udbg_init_pmac_realmode(void) +{ + sccc = (volatile u8 __iomem *)0x80013020ul; + sccd = (volatile u8 __iomem *)0x80013030ul; + + udbg_putc = udbg_real_scc_putc; + udbg_getc = NULL; + udbg_getc_poll = NULL; +} +#endif /* CONFIG_PPC64 */ diff --git a/arch/powerpc/platforms/pseries/Makefile b/arch/powerpc/platforms/pseries/Makefile index 06d5ef5..61616d1 100644 --- a/arch/powerpc/platforms/pseries/Makefile +++ b/arch/powerpc/platforms/pseries/Makefile @@ -1,10 +1,10 @@ obj-y := pci.o lpar.o hvCall.o nvram.o reconfig.o \ - setup.o iommu.o ras.o rtasd.o + setup.o iommu.o ras.o rtasd.o pci_dlpar.o obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_IBMVIO) += vio.o obj-$(CONFIG_XICS) += xics.o obj-$(CONFIG_SCANLOG) += scanlog.o -obj-$(CONFIG_EEH) += eeh.o eeh_event.o +obj-$(CONFIG_EEH) += eeh.o eeh_cache.o eeh_driver.o eeh_event.o obj-$(CONFIG_HVC_CONSOLE) += hvconsole.o obj-$(CONFIG_HVCS) += hvcserver.o diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c index c8d2a40..136365c 100644 --- a/arch/powerpc/platforms/pseries/eeh.c +++ b/arch/powerpc/platforms/pseries/eeh.c @@ -76,15 +76,14 @@ */ #define EEH_MAX_FAILS 100000 -/* Misc forward declaraions */ -static void eeh_save_bars(struct pci_dev * pdev, struct pci_dn *pdn); - /* RTAS tokens */ static int ibm_set_eeh_option; static int ibm_set_slot_reset; static int ibm_read_slot_reset_state; static int ibm_read_slot_reset_state2; static int ibm_slot_error_detail; +static int ibm_get_config_addr_info; +static int ibm_configure_bridge; int eeh_subsystem_enabled; EXPORT_SYMBOL(eeh_subsystem_enabled); @@ -98,308 +97,23 @@ static DEFINE_SPINLOCK(slot_errbuf_lock) static int eeh_error_buf_size; /* System monitoring statistics */ -static DEFINE_PER_CPU(unsigned long, no_device); -static DEFINE_PER_CPU(unsigned long, no_dn); -static DEFINE_PER_CPU(unsigned long, no_cfg_addr); -static DEFINE_PER_CPU(unsigned long, ignored_check); -static DEFINE_PER_CPU(unsigned long, total_mmio_ffs); -static DEFINE_PER_CPU(unsigned long, false_positives); -static DEFINE_PER_CPU(unsigned long, ignored_failures); -static DEFINE_PER_CPU(unsigned long, slot_resets); - -/** - * The pci address cache subsystem. This subsystem places - * PCI device address resources into a red-black tree, sorted - * according to the address range, so that given only an i/o - * address, the corresponding PCI device can be **quickly** - * found. It is safe to perform an address lookup in an interrupt - * context; this ability is an important feature. - * - * Currently, the only customer of this code is the EEH subsystem; - * thus, this code has been somewhat tailored to suit EEH better. - * In particular, the cache does *not* hold the addresses of devices - * for which EEH is not enabled. - * - * (Implementation Note: The RB tree seems to be better/faster - * than any hash algo I could think of for this problem, even - * with the penalty of slow pointer chases for d-cache misses). - */ -struct pci_io_addr_range -{ - struct rb_node rb_node; - unsigned long addr_lo; - unsigned long addr_hi; - struct pci_dev *pcidev; - unsigned int flags; -}; - -static struct pci_io_addr_cache -{ - struct rb_root rb_root; - spinlock_t piar_lock; -} pci_io_addr_cache_root; - -static inline struct pci_dev *__pci_get_device_by_addr(unsigned long addr) -{ - struct rb_node *n = pci_io_addr_cache_root.rb_root.rb_node; - - while (n) { - struct pci_io_addr_range *piar; - piar = rb_entry(n, struct pci_io_addr_range, rb_node); - - if (addr < piar->addr_lo) { - n = n->rb_left; - } else { - if (addr > piar->addr_hi) { - n = n->rb_right; - } else { - pci_dev_get(piar->pcidev); - return piar->pcidev; - } - } - } - - return NULL; -} - -/** - * pci_get_device_by_addr - Get device, given only address - * @addr: mmio (PIO) phys address or i/o port number - * - * Given an mmio phys address, or a port number, find a pci device - * that implements this address. Be sure to pci_dev_put the device - * when finished. I/O port numbers are assumed to be offset - * from zero (that is, they do *not* have pci_io_addr added in). - * It is safe to call this function within an interrupt. - */ -static struct pci_dev *pci_get_device_by_addr(unsigned long addr) -{ - struct pci_dev *dev; - unsigned long flags; - - spin_lock_irqsave(&pci_io_addr_cache_root.piar_lock, flags); - dev = __pci_get_device_by_addr(addr); - spin_unlock_irqrestore(&pci_io_addr_cache_root.piar_lock, flags); - return dev; -} - -#ifdef DEBUG -/* - * Handy-dandy debug print routine, does nothing more - * than print out the contents of our addr cache. - */ -static void pci_addr_cache_print(struct pci_io_addr_cache *cache) -{ - struct rb_node *n; - int cnt = 0; - - n = rb_first(&cache->rb_root); - while (n) { - struct pci_io_addr_range *piar; - piar = rb_entry(n, struct pci_io_addr_range, rb_node); - printk(KERN_DEBUG "PCI: %s addr range %d [%lx-%lx]: %s\n", - (piar->flags & IORESOURCE_IO) ? "i/o" : "mem", cnt, - piar->addr_lo, piar->addr_hi, pci_name(piar->pcidev)); - cnt++; - n = rb_next(n); - } -} -#endif - -/* Insert address range into the rb tree. */ -static struct pci_io_addr_range * -pci_addr_cache_insert(struct pci_dev *dev, unsigned long alo, - unsigned long ahi, unsigned int flags) -{ - struct rb_node **p = &pci_io_addr_cache_root.rb_root.rb_node; - struct rb_node *parent = NULL; - struct pci_io_addr_range *piar; - - /* Walk tree, find a place to insert into tree */ - while (*p) { - parent = *p; - piar = rb_entry(parent, struct pci_io_addr_range, rb_node); - if (ahi < piar->addr_lo) { - p = &parent->rb_left; - } else if (alo > piar->addr_hi) { - p = &parent->rb_right; - } else { - if (dev != piar->pcidev || - alo != piar->addr_lo || ahi != piar->addr_hi) { - printk(KERN_WARNING "PIAR: overlapping address range\n"); - } - return piar; - } - } - piar = (struct pci_io_addr_range *)kmalloc(sizeof(struct pci_io_addr_range), GFP_ATOMIC); - if (!piar) - return NULL; - - piar->addr_lo = alo; - piar->addr_hi = ahi; - piar->pcidev = dev; - piar->flags = flags; - -#ifdef DEBUG - printk(KERN_DEBUG "PIAR: insert range=[%lx:%lx] dev=%s\n", - alo, ahi, pci_name (dev)); -#endif - - rb_link_node(&piar->rb_node, parent, p); - rb_insert_color(&piar->rb_node, &pci_io_addr_cache_root.rb_root); - - return piar; -} - -static void __pci_addr_cache_insert_device(struct pci_dev *dev) -{ - struct device_node *dn; - struct pci_dn *pdn; - int i; - int inserted = 0; - - dn = pci_device_to_OF_node(dev); - if (!dn) { - printk(KERN_WARNING "PCI: no pci dn found for dev=%s\n", pci_name(dev)); - return; - } - - /* Skip any devices for which EEH is not enabled. */ - pdn = PCI_DN(dn); - if (!(pdn->eeh_mode & EEH_MODE_SUPPORTED) || - pdn->eeh_mode & EEH_MODE_NOCHECK) { -#ifdef DEBUG - printk(KERN_INFO "PCI: skip building address cache for=%s - %s\n", - pci_name(dev), pdn->node->full_name); -#endif - return; - } - - /* The cache holds a reference to the device... */ - pci_dev_get(dev); - - /* Walk resources on this device, poke them into the tree */ - for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { - unsigned long start = pci_resource_start(dev,i); - unsigned long end = pci_resource_end(dev,i); - unsigned int flags = pci_resource_flags(dev,i); - - /* We are interested only bus addresses, not dma or other stuff */ - if (0 == (flags & (IORESOURCE_IO | IORESOURCE_MEM))) - continue; - if (start == 0 || ~start == 0 || end == 0 || ~end == 0) - continue; - pci_addr_cache_insert(dev, start, end, flags); - inserted = 1; - } +static unsigned long no_device; +static unsigned long no_dn; +static unsigned long no_cfg_addr; +static unsigned long ignored_check; +static unsigned long total_mmio_ffs; +static unsigned long false_positives; +static unsigned long ignored_failures; +static unsigned long slot_resets; - /* If there was nothing to add, the cache has no reference... */ - if (!inserted) - pci_dev_put(dev); -} - -/** - * pci_addr_cache_insert_device - Add a device to the address cache - * @dev: PCI device whose I/O addresses we are interested in. - * - * In order to support the fast lookup of devices based on addresses, - * we maintain a cache of devices that can be quickly searched. - * This routine adds a device to that cache. - */ -static void pci_addr_cache_insert_device(struct pci_dev *dev) -{ - unsigned long flags; - - spin_lock_irqsave(&pci_io_addr_cache_root.piar_lock, flags); - __pci_addr_cache_insert_device(dev); - spin_unlock_irqrestore(&pci_io_addr_cache_root.piar_lock, flags); -} - -static inline void __pci_addr_cache_remove_device(struct pci_dev *dev) -{ - struct rb_node *n; - int removed = 0; - -restart: - n = rb_first(&pci_io_addr_cache_root.rb_root); - while (n) { - struct pci_io_addr_range *piar; - piar = rb_entry(n, struct pci_io_addr_range, rb_node); - - if (piar->pcidev == dev) { - rb_erase(n, &pci_io_addr_cache_root.rb_root); - removed = 1; - kfree(piar); - goto restart; - } - n = rb_next(n); - } - - /* The cache no longer holds its reference to this device... */ - if (removed) - pci_dev_put(dev); -} - -/** - * pci_addr_cache_remove_device - remove pci device from addr cache - * @dev: device to remove - * - * Remove a device from the addr-cache tree. - * This is potentially expensive, since it will walk - * the tree multiple times (once per resource). - * But so what; device removal doesn't need to be that fast. - */ -static void pci_addr_cache_remove_device(struct pci_dev *dev) -{ - unsigned long flags; - - spin_lock_irqsave(&pci_io_addr_cache_root.piar_lock, flags); - __pci_addr_cache_remove_device(dev); - spin_unlock_irqrestore(&pci_io_addr_cache_root.piar_lock, flags); -} - -/** - * pci_addr_cache_build - Build a cache of I/O addresses - * - * Build a cache of pci i/o addresses. This cache will be used to - * find the pci device that corresponds to a given address. - * This routine scans all pci busses to build the cache. - * Must be run late in boot process, after the pci controllers - * have been scaned for devices (after all device resources are known). - */ -void __init pci_addr_cache_build(void) -{ - struct device_node *dn; - struct pci_dev *dev = NULL; - - if (!eeh_subsystem_enabled) - return; - - spin_lock_init(&pci_io_addr_cache_root.piar_lock); - - while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) { - /* Ignore PCI bridges ( XXX why ??) */ - if ((dev->class >> 16) == PCI_BASE_CLASS_BRIDGE) { - continue; - } - pci_addr_cache_insert_device(dev); - - /* Save the BAR's; firmware doesn't restore these after EEH reset */ - dn = pci_device_to_OF_node(dev); - eeh_save_bars(dev, PCI_DN(dn)); - } - -#ifdef DEBUG - /* Verify tree built up above, echo back the list of addrs. */ - pci_addr_cache_print(&pci_io_addr_cache_root); -#endif -} +#define IS_BRIDGE(class_code) (((class_code)<<16) == PCI_BASE_CLASS_BRIDGE) /* --------------------------------------------------------------- */ -/* Above lies the PCI Address Cache. Below lies the EEH event infrastructure */ +/* Below lies the EEH event infrastructure */ void eeh_slot_error_detail (struct pci_dn *pdn, int severity) { + int config_addr; unsigned long flags; int rc; @@ -407,8 +121,13 @@ void eeh_slot_error_detail (struct pci_d spin_lock_irqsave(&slot_errbuf_lock, flags); memset(slot_errbuf, 0, eeh_error_buf_size); + /* Use PE configuration address, if present */ + config_addr = pdn->eeh_config_addr; + if (pdn->eeh_pe_config_addr) + config_addr = pdn->eeh_pe_config_addr; + rc = rtas_call(ibm_slot_error_detail, - 8, 1, NULL, pdn->eeh_config_addr, + 8, 1, NULL, config_addr, BUID_HI(pdn->phb->buid), BUID_LO(pdn->phb->buid), NULL, 0, virt_to_phys(slot_errbuf), @@ -428,6 +147,7 @@ void eeh_slot_error_detail (struct pci_d static int read_slot_reset_state(struct pci_dn *pdn, int rets[]) { int token, outputs; + int config_addr; if (ibm_read_slot_reset_state2 != RTAS_UNKNOWN_SERVICE) { token = ibm_read_slot_reset_state2; @@ -438,7 +158,12 @@ static int read_slot_reset_state(struct outputs = 3; } - return rtas_call(token, 3, outputs, rets, pdn->eeh_config_addr, + /* Use PE configuration address, if present */ + config_addr = pdn->eeh_config_addr; + if (pdn->eeh_pe_config_addr) + config_addr = pdn->eeh_pe_config_addr; + + return rtas_call(token, 3, outputs, rets, config_addr, BUID_HI(pdn->phb->buid), BUID_LO(pdn->phb->buid)); } @@ -462,7 +187,7 @@ static inline unsigned long eeh_token_to /** * Return the "partitionable endpoint" (pe) under which this device lies */ -static struct device_node * find_device_pe(struct device_node *dn) +struct device_node * find_device_pe(struct device_node *dn) { while ((dn->parent) && PCI_DN(dn->parent) && (PCI_DN(dn->parent)->eeh_mode & EEH_MODE_SUPPORTED)) { @@ -485,6 +210,11 @@ static void __eeh_mark_slot (struct devi if (PCI_DN(dn)) { PCI_DN(dn)->eeh_mode |= mode_flag; + /* Mark the pci device driver too */ + struct pci_dev *dev = PCI_DN(dn)->pcidev; + if (dev && dev->driver) + dev->error_state = pci_channel_io_frozen; + if (dn->child) __eeh_mark_slot (dn->child, mode_flag); } @@ -495,6 +225,11 @@ static void __eeh_mark_slot (struct devi void eeh_mark_slot (struct device_node *dn, int mode_flag) { dn = find_device_pe (dn); + + /* Back up one, since config addrs might be shared */ + if (PCI_DN(dn) && PCI_DN(dn)->eeh_pe_config_addr) + dn = dn->parent; + PCI_DN(dn)->eeh_mode |= mode_flag; __eeh_mark_slot (dn->child, mode_flag); } @@ -516,7 +251,13 @@ void eeh_clear_slot (struct device_node { unsigned long flags; spin_lock_irqsave(&confirm_error_lock, flags); + dn = find_device_pe (dn); + + /* Back up one, since config addrs might be shared */ + if (PCI_DN(dn) && PCI_DN(dn)->eeh_pe_config_addr) + dn = dn->parent; + PCI_DN(dn)->eeh_mode &= ~mode_flag; PCI_DN(dn)->eeh_check_count = 0; __eeh_clear_slot (dn->child, mode_flag); @@ -544,15 +285,16 @@ int eeh_dn_check_failure(struct device_n int rets[3]; unsigned long flags; struct pci_dn *pdn; + enum pci_channel_state state; int rc = 0; - __get_cpu_var(total_mmio_ffs)++; + total_mmio_ffs++; if (!eeh_subsystem_enabled) return 0; if (!dn) { - __get_cpu_var(no_dn)++; + no_dn++; return 0; } pdn = PCI_DN(dn); @@ -560,7 +302,7 @@ int eeh_dn_check_failure(struct device_n /* Access to IO BARs might get this far and still not want checking. */ if (!(pdn->eeh_mode & EEH_MODE_SUPPORTED) || pdn->eeh_mode & EEH_MODE_NOCHECK) { - __get_cpu_var(ignored_check)++; + ignored_check++; #ifdef DEBUG printk ("EEH:ignored check (%x) for %s %s\n", pdn->eeh_mode, pci_name (dev), dn->full_name); @@ -568,8 +310,8 @@ int eeh_dn_check_failure(struct device_n return 0; } - if (!pdn->eeh_config_addr) { - __get_cpu_var(no_cfg_addr)++; + if (!pdn->eeh_config_addr && !pdn->eeh_pe_config_addr) { + no_cfg_addr++; return 0; } @@ -611,7 +353,7 @@ int eeh_dn_check_failure(struct device_n if (ret != 0) { printk(KERN_WARNING "EEH: read_slot_reset_state() failed; rc=%d dn=%s\n", ret, dn->full_name); - __get_cpu_var(false_positives)++; + false_positives++; rc = 0; goto dn_unlock; } @@ -620,14 +362,14 @@ int eeh_dn_check_failure(struct device_n if (rets[1] != 1) { printk(KERN_WARNING "EEH: event on unsupported device, rc=%d dn=%s\n", ret, dn->full_name); - __get_cpu_var(false_positives)++; + false_positives++; rc = 0; goto dn_unlock; } /* If not the kind of error we know about, punt. */ if (rets[0] != 2 && rets[0] != 4 && rets[0] != 5) { - __get_cpu_var(false_positives)++; + false_positives++; rc = 0; goto dn_unlock; } @@ -635,12 +377,12 @@ int eeh_dn_check_failure(struct device_n /* Note that config-io to empty slots may fail; * we recognize empty because they don't have children. */ if ((rets[0] == 5) && (dn->child == NULL)) { - __get_cpu_var(false_positives)++; + false_positives++; rc = 0; goto dn_unlock; } - __get_cpu_var(slot_resets)++; + slot_resets++; /* Avoid repeated reports of this failure, including problems * with other functions on this device, and functions under @@ -648,8 +390,13 @@ int eeh_dn_check_failure(struct device_n eeh_mark_slot (dn, EEH_MODE_ISOLATED); spin_unlock_irqrestore(&confirm_error_lock, flags); - eeh_send_failure_event (dn, dev, rets[0], rets[2]); - + state = pci_channel_io_normal; + if ((rets[0] == 2) || (rets[0] == 4)) + state = pci_channel_io_frozen; + if (rets[0] == 5) + state = pci_channel_io_perm_failure; + eeh_send_failure_event (dn, dev, state, rets[2]); + /* Most EEH events are due to device driver bugs. Having * a stack trace will help the device-driver authors figure * out what happened. So print that out. */ @@ -685,7 +432,7 @@ unsigned long eeh_check_failure(const vo addr = eeh_token_to_phys((unsigned long __force) token); dev = pci_get_device_by_addr(addr); if (!dev) { - __get_cpu_var(no_device)++; + no_device++; return val; } @@ -716,11 +463,16 @@ eeh_slot_availability(struct pci_dn *pdn if (rc) return rc; if (rets[1] == 0) return -1; /* EEH is not supported */ - if (rets[0] == 0) return 0; /* Oll Korrect */ + if (rets[0] == 0) return 0; /* Oll Korrect */ if (rets[0] == 5) { if (rets[2] == 0) return -1; /* permanently unavailable */ return rets[2]; /* number of millisecs to wait */ } + if (rets[0] == 1) + return 250; + + printk (KERN_ERR "EEH: Slot unavailable: rc=%d, rets=%d %d %d\n", + rc, rets[0], rets[1], rets[2]); return -1; } @@ -737,6 +489,7 @@ eeh_slot_availability(struct pci_dn *pdn static void rtas_pci_slot_reset(struct pci_dn *pdn, int state) { + int config_addr; int rc; BUG_ON (pdn==NULL); @@ -747,8 +500,13 @@ rtas_pci_slot_reset(struct pci_dn *pdn, return; } + /* Use PE configuration address, if present */ + config_addr = pdn->eeh_config_addr; + if (pdn->eeh_pe_config_addr) + config_addr = pdn->eeh_pe_config_addr; + rc = rtas_call(ibm_set_slot_reset,4,1, NULL, - pdn->eeh_config_addr, + config_addr, BUID_HI(pdn->phb->buid), BUID_LO(pdn->phb->buid), state); @@ -761,9 +519,11 @@ rtas_pci_slot_reset(struct pci_dn *pdn, /** rtas_set_slot_reset -- assert the pci #RST line for 1/4 second * dn -- device node to be reset. + * + * Return 0 if success, else a non-zero value. */ -void +int rtas_set_slot_reset(struct pci_dn *pdn) { int i, rc; @@ -793,10 +553,21 @@ rtas_set_slot_reset(struct pci_dn *pdn) * ready to be used; if not, wait for recovery. */ for (i=0; i<10; i++) { rc = eeh_slot_availability (pdn); - if (rc <= 0) break; + if (rc < 0) + printk (KERN_ERR "EEH: failed (%d) to reset slot %s\n", rc, pdn->node->full_name); + if (rc == 0) + return 0; + if (rc < 0) + return -1; msleep (rc+100); } + + rc = eeh_slot_availability (pdn); + if (rc) + printk (KERN_ERR "EEH: timeout resetting slot %s\n", pdn->node->full_name); + + return rc; } /* ------------------------------------------------------- */ @@ -851,7 +622,7 @@ void eeh_restore_bars(struct pci_dn *pdn if (!pdn) return; - if (! pdn->eeh_is_bridge) + if ((pdn->eeh_mode & EEH_MODE_SUPPORTED) && !IS_BRIDGE(pdn->class_code)) __restore_bars (pdn); dn = pdn->node->child; @@ -869,30 +640,30 @@ void eeh_restore_bars(struct pci_dn *pdn * PCI devices are added individuallly; but, for the restore, * an entire slot is reset at a time. */ -static void eeh_save_bars(struct pci_dev * pdev, struct pci_dn *pdn) +static void eeh_save_bars(struct pci_dn *pdn) { int i; - if (!pdev || !pdn ) + if (!pdn ) return; for (i = 0; i < 16; i++) - pci_read_config_dword(pdev, i * 4, &pdn->config_space[i]); - - if (pdev->hdr_type == PCI_HEADER_TYPE_BRIDGE) - pdn->eeh_is_bridge = 1; + rtas_read_config(pdn, i * 4, 4, &pdn->config_space[i]); } void rtas_configure_bridge(struct pci_dn *pdn) { - int token = rtas_token ("ibm,configure-bridge"); + int config_addr; int rc; - if (token == RTAS_UNKNOWN_SERVICE) - return; - rc = rtas_call(token,3,1, NULL, - pdn->eeh_config_addr, + /* Use PE configuration address, if present */ + config_addr = pdn->eeh_config_addr; + if (pdn->eeh_pe_config_addr) + config_addr = pdn->eeh_pe_config_addr; + + rc = rtas_call(ibm_configure_bridge,3,1, NULL, + config_addr, BUID_HI(pdn->phb->buid), BUID_LO(pdn->phb->buid)); if (rc) { @@ -927,6 +698,7 @@ static void *early_enable_eeh(struct dev int enable; struct pci_dn *pdn = PCI_DN(dn); + pdn->class_code = *class_code; pdn->eeh_mode = 0; pdn->eeh_check_count = 0; pdn->eeh_freeze_count = 0; @@ -953,8 +725,10 @@ static void *early_enable_eeh(struct dev * But there are a few cases like display devices that make sense. */ enable = 1; /* i.e. we will do checking */ +#if 0 if ((*class_code >> 16) == PCI_BASE_CLASS_DISPLAY) enable = 0; +#endif if (!enable) pdn->eeh_mode |= EEH_MODE_NOCHECK; @@ -973,8 +747,22 @@ static void *early_enable_eeh(struct dev eeh_subsystem_enabled = 1; pdn->eeh_mode |= EEH_MODE_SUPPORTED; pdn->eeh_config_addr = regs[0]; + + /* If the newer, better, ibm,get-config-addr-info is supported, + * then use that instead. */ + pdn->eeh_pe_config_addr = 0; + if (ibm_get_config_addr_info != RTAS_UNKNOWN_SERVICE) { + unsigned int rets[2]; + ret = rtas_call (ibm_get_config_addr_info, 4, 2, rets, + pdn->eeh_config_addr, + info->buid_hi, info->buid_lo, + 0); + if (ret == 0) + pdn->eeh_pe_config_addr = rets[0]; + } #ifdef DEBUG - printk(KERN_DEBUG "EEH: %s: eeh enabled\n", dn->full_name); + printk(KERN_DEBUG "EEH: %s: eeh enabled, config=%x pe_config=%x\n", + dn->full_name, pdn->eeh_config_addr, pdn->eeh_pe_config_addr); #endif } else { @@ -993,6 +781,7 @@ static void *early_enable_eeh(struct dev dn->full_name); } + eeh_save_bars(pdn); return NULL; } @@ -1026,6 +815,8 @@ void __init eeh_init(void) ibm_read_slot_reset_state2 = rtas_token("ibm,read-slot-reset-state2"); ibm_read_slot_reset_state = rtas_token("ibm,read-slot-reset-state"); ibm_slot_error_detail = rtas_token("ibm,slot-error-detail"); + ibm_get_config_addr_info = rtas_token("ibm,get-config-addr-info"); + ibm_configure_bridge = rtas_token ("ibm,configure-bridge"); if (ibm_set_eeh_option == RTAS_UNKNOWN_SERVICE) return; @@ -1080,12 +871,10 @@ void eeh_add_device_early(struct device_ if (!dn || !PCI_DN(dn)) return; phb = PCI_DN(dn)->phb; - if (NULL == phb || 0 == phb->buid) { - printk(KERN_WARNING "EEH: Expected buid but found none for %s\n", - dn->full_name); - dump_stack(); + + /* USB Bus children of PCI devices will not have BUID's */ + if (NULL == phb || 0 == phb->buid) return; - } info.buid_hi = BUID_HI(phb->buid); info.buid_lo = BUID_LO(phb->buid); @@ -1093,6 +882,15 @@ void eeh_add_device_early(struct device_ } EXPORT_SYMBOL_GPL(eeh_add_device_early); +void eeh_add_device_tree_early(struct device_node *dn) +{ + struct device_node *sib; + for (sib = dn->child; sib; sib = sib->sibling) + eeh_add_device_tree_early(sib); + eeh_add_device_early(dn); +} +EXPORT_SYMBOL_GPL(eeh_add_device_tree_early); + /** * eeh_add_device_late - perform EEH initialization for the indicated pci device * @dev: pci device for which to set up EEH @@ -1118,7 +916,6 @@ void eeh_add_device_late(struct pci_dev pdn->pcidev = dev; pci_addr_cache_insert_device (dev); - eeh_save_bars(dev, pdn); } EXPORT_SYMBOL_GPL(eeh_add_device_late); @@ -1147,27 +944,28 @@ void eeh_remove_device(struct pci_dev *d } EXPORT_SYMBOL_GPL(eeh_remove_device); -static int proc_eeh_show(struct seq_file *m, void *v) +void eeh_remove_bus_device(struct pci_dev *dev) { - unsigned int cpu; - unsigned long ffs = 0, positives = 0, failures = 0; - unsigned long resets = 0; - unsigned long no_dev = 0, no_dn = 0, no_cfg = 0, no_check = 0; - - for_each_cpu(cpu) { - ffs += per_cpu(total_mmio_ffs, cpu); - positives += per_cpu(false_positives, cpu); - failures += per_cpu(ignored_failures, cpu); - resets += per_cpu(slot_resets, cpu); - no_dev += per_cpu(no_device, cpu); - no_dn += per_cpu(no_dn, cpu); - no_cfg += per_cpu(no_cfg_addr, cpu); - no_check += per_cpu(ignored_check, cpu); + eeh_remove_device(dev); + if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { + struct pci_bus *bus = dev->subordinate; + struct list_head *ln; + if (!bus) + return; + for (ln = bus->devices.next; ln != &bus->devices; ln = ln->next) { + struct pci_dev *pdev = pci_dev_b(ln); + if (pdev) + eeh_remove_bus_device(pdev); + } } +} +EXPORT_SYMBOL_GPL(eeh_remove_bus_device); +static int proc_eeh_show(struct seq_file *m, void *v) +{ if (0 == eeh_subsystem_enabled) { seq_printf(m, "EEH Subsystem is globally disabled\n"); - seq_printf(m, "eeh_total_mmio_ffs=%ld\n", ffs); + seq_printf(m, "eeh_total_mmio_ffs=%ld\n", total_mmio_ffs); } else { seq_printf(m, "EEH Subsystem is enabled\n"); seq_printf(m, @@ -1179,8 +977,10 @@ static int proc_eeh_show(struct seq_file "eeh_false_positives=%ld\n" "eeh_ignored_failures=%ld\n" "eeh_slot_resets=%ld\n", - no_dev, no_dn, no_cfg, no_check, - ffs, positives, failures, resets); + no_device, no_dn, no_cfg_addr, + ignored_check, total_mmio_ffs, + false_positives, ignored_failures, + slot_resets); } return 0; diff --git a/arch/powerpc/platforms/pseries/eeh_cache.c b/arch/powerpc/platforms/pseries/eeh_cache.c new file mode 100644 index 0000000..d4a402c --- /dev/null +++ b/arch/powerpc/platforms/pseries/eeh_cache.c @@ -0,0 +1,316 @@ +/* + * eeh_cache.c + * PCI address cache; allows the lookup of PCI devices based on I/O address + * + * Copyright (C) 2004 Linas Vepstas IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include + +#undef DEBUG + +/** + * The pci address cache subsystem. This subsystem places + * PCI device address resources into a red-black tree, sorted + * according to the address range, so that given only an i/o + * address, the corresponding PCI device can be **quickly** + * found. It is safe to perform an address lookup in an interrupt + * context; this ability is an important feature. + * + * Currently, the only customer of this code is the EEH subsystem; + * thus, this code has been somewhat tailored to suit EEH better. + * In particular, the cache does *not* hold the addresses of devices + * for which EEH is not enabled. + * + * (Implementation Note: The RB tree seems to be better/faster + * than any hash algo I could think of for this problem, even + * with the penalty of slow pointer chases for d-cache misses). + */ +struct pci_io_addr_range +{ + struct rb_node rb_node; + unsigned long addr_lo; + unsigned long addr_hi; + struct pci_dev *pcidev; + unsigned int flags; +}; + +static struct pci_io_addr_cache +{ + struct rb_root rb_root; + spinlock_t piar_lock; +} pci_io_addr_cache_root; + +static inline struct pci_dev *__pci_get_device_by_addr(unsigned long addr) +{ + struct rb_node *n = pci_io_addr_cache_root.rb_root.rb_node; + + while (n) { + struct pci_io_addr_range *piar; + piar = rb_entry(n, struct pci_io_addr_range, rb_node); + + if (addr < piar->addr_lo) { + n = n->rb_left; + } else { + if (addr > piar->addr_hi) { + n = n->rb_right; + } else { + pci_dev_get(piar->pcidev); + return piar->pcidev; + } + } + } + + return NULL; +} + +/** + * pci_get_device_by_addr - Get device, given only address + * @addr: mmio (PIO) phys address or i/o port number + * + * Given an mmio phys address, or a port number, find a pci device + * that implements this address. Be sure to pci_dev_put the device + * when finished. I/O port numbers are assumed to be offset + * from zero (that is, they do *not* have pci_io_addr added in). + * It is safe to call this function within an interrupt. + */ +struct pci_dev *pci_get_device_by_addr(unsigned long addr) +{ + struct pci_dev *dev; + unsigned long flags; + + spin_lock_irqsave(&pci_io_addr_cache_root.piar_lock, flags); + dev = __pci_get_device_by_addr(addr); + spin_unlock_irqrestore(&pci_io_addr_cache_root.piar_lock, flags); + return dev; +} + +#ifdef DEBUG +/* + * Handy-dandy debug print routine, does nothing more + * than print out the contents of our addr cache. + */ +static void pci_addr_cache_print(struct pci_io_addr_cache *cache) +{ + struct rb_node *n; + int cnt = 0; + + n = rb_first(&cache->rb_root); + while (n) { + struct pci_io_addr_range *piar; + piar = rb_entry(n, struct pci_io_addr_range, rb_node); + printk(KERN_DEBUG "PCI: %s addr range %d [%lx-%lx]: %s\n", + (piar->flags & IORESOURCE_IO) ? "i/o" : "mem", cnt, + piar->addr_lo, piar->addr_hi, pci_name(piar->pcidev)); + cnt++; + n = rb_next(n); + } +} +#endif + +/* Insert address range into the rb tree. */ +static struct pci_io_addr_range * +pci_addr_cache_insert(struct pci_dev *dev, unsigned long alo, + unsigned long ahi, unsigned int flags) +{ + struct rb_node **p = &pci_io_addr_cache_root.rb_root.rb_node; + struct rb_node *parent = NULL; + struct pci_io_addr_range *piar; + + /* Walk tree, find a place to insert into tree */ + while (*p) { + parent = *p; + piar = rb_entry(parent, struct pci_io_addr_range, rb_node); + if (ahi < piar->addr_lo) { + p = &parent->rb_left; + } else if (alo > piar->addr_hi) { + p = &parent->rb_right; + } else { + if (dev != piar->pcidev || + alo != piar->addr_lo || ahi != piar->addr_hi) { + printk(KERN_WARNING "PIAR: overlapping address range\n"); + } + return piar; + } + } + piar = (struct pci_io_addr_range *)kmalloc(sizeof(struct pci_io_addr_range), GFP_ATOMIC); + if (!piar) + return NULL; + + piar->addr_lo = alo; + piar->addr_hi = ahi; + piar->pcidev = dev; + piar->flags = flags; + +#ifdef DEBUG + printk(KERN_DEBUG "PIAR: insert range=[%lx:%lx] dev=%s\n", + alo, ahi, pci_name (dev)); +#endif + + rb_link_node(&piar->rb_node, parent, p); + rb_insert_color(&piar->rb_node, &pci_io_addr_cache_root.rb_root); + + return piar; +} + +static void __pci_addr_cache_insert_device(struct pci_dev *dev) +{ + struct device_node *dn; + struct pci_dn *pdn; + int i; + int inserted = 0; + + dn = pci_device_to_OF_node(dev); + if (!dn) { + printk(KERN_WARNING "PCI: no pci dn found for dev=%s\n", pci_name(dev)); + return; + } + + /* Skip any devices for which EEH is not enabled. */ + pdn = PCI_DN(dn); + if (!(pdn->eeh_mode & EEH_MODE_SUPPORTED) || + pdn->eeh_mode & EEH_MODE_NOCHECK) { +#ifdef DEBUG + printk(KERN_INFO "PCI: skip building address cache for=%s - %s\n", + pci_name(dev), pdn->node->full_name); +#endif + return; + } + + /* The cache holds a reference to the device... */ + pci_dev_get(dev); + + /* Walk resources on this device, poke them into the tree */ + for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { + unsigned long start = pci_resource_start(dev,i); + unsigned long end = pci_resource_end(dev,i); + unsigned int flags = pci_resource_flags(dev,i); + + /* We are interested only bus addresses, not dma or other stuff */ + if (0 == (flags & (IORESOURCE_IO | IORESOURCE_MEM))) + continue; + if (start == 0 || ~start == 0 || end == 0 || ~end == 0) + continue; + pci_addr_cache_insert(dev, start, end, flags); + inserted = 1; + } + + /* If there was nothing to add, the cache has no reference... */ + if (!inserted) + pci_dev_put(dev); +} + +/** + * pci_addr_cache_insert_device - Add a device to the address cache + * @dev: PCI device whose I/O addresses we are interested in. + * + * In order to support the fast lookup of devices based on addresses, + * we maintain a cache of devices that can be quickly searched. + * This routine adds a device to that cache. + */ +void pci_addr_cache_insert_device(struct pci_dev *dev) +{ + unsigned long flags; + + spin_lock_irqsave(&pci_io_addr_cache_root.piar_lock, flags); + __pci_addr_cache_insert_device(dev); + spin_unlock_irqrestore(&pci_io_addr_cache_root.piar_lock, flags); +} + +static inline void __pci_addr_cache_remove_device(struct pci_dev *dev) +{ + struct rb_node *n; + int removed = 0; + +restart: + n = rb_first(&pci_io_addr_cache_root.rb_root); + while (n) { + struct pci_io_addr_range *piar; + piar = rb_entry(n, struct pci_io_addr_range, rb_node); + + if (piar->pcidev == dev) { + rb_erase(n, &pci_io_addr_cache_root.rb_root); + removed = 1; + kfree(piar); + goto restart; + } + n = rb_next(n); + } + + /* The cache no longer holds its reference to this device... */ + if (removed) + pci_dev_put(dev); +} + +/** + * pci_addr_cache_remove_device - remove pci device from addr cache + * @dev: device to remove + * + * Remove a device from the addr-cache tree. + * This is potentially expensive, since it will walk + * the tree multiple times (once per resource). + * But so what; device removal doesn't need to be that fast. + */ +void pci_addr_cache_remove_device(struct pci_dev *dev) +{ + unsigned long flags; + + spin_lock_irqsave(&pci_io_addr_cache_root.piar_lock, flags); + __pci_addr_cache_remove_device(dev); + spin_unlock_irqrestore(&pci_io_addr_cache_root.piar_lock, flags); +} + +/** + * pci_addr_cache_build - Build a cache of I/O addresses + * + * Build a cache of pci i/o addresses. This cache will be used to + * find the pci device that corresponds to a given address. + * This routine scans all pci busses to build the cache. + * Must be run late in boot process, after the pci controllers + * have been scaned for devices (after all device resources are known). + */ +void __init pci_addr_cache_build(void) +{ + struct device_node *dn; + struct pci_dev *dev = NULL; + + spin_lock_init(&pci_io_addr_cache_root.piar_lock); + + while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) { + /* Ignore PCI bridges */ + if ((dev->class >> 16) == PCI_BASE_CLASS_BRIDGE) + continue; + + pci_addr_cache_insert_device(dev); + + dn = pci_device_to_OF_node(dev); + pci_dev_get (dev); /* matching put is in eeh_remove_device() */ + PCI_DN(dn)->pcidev = dev; + } + +#ifdef DEBUG + /* Verify tree built up above, echo back the list of addrs. */ + pci_addr_cache_print(&pci_io_addr_cache_root); +#endif +} + diff --git a/arch/powerpc/platforms/pseries/eeh_driver.c b/arch/powerpc/platforms/pseries/eeh_driver.c new file mode 100644 index 0000000..6373372 --- /dev/null +++ b/arch/powerpc/platforms/pseries/eeh_driver.c @@ -0,0 +1,376 @@ +/* + * PCI Error Recovery Driver for RPA-compliant PPC64 platform. + * Copyright (C) 2004, 2005 Linas Vepstas + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Send feedback to + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +static inline const char * pcid_name (struct pci_dev *pdev) +{ + if (pdev->dev.driver) + return pdev->dev.driver->name; + return ""; +} + +#ifdef DEBUG +static void print_device_node_tree (struct pci_dn *pdn, int dent) +{ + int i; + if (!pdn) return; + for (i=0;inode->name, pdn->eeh_mode, pdn->eeh_config_addr, + pdn->eeh_pe_config_addr, pdn->node->full_name); + dent += 3; + struct device_node *pc = pdn->node->child; + while (pc) { + print_device_node_tree(PCI_DN(pc), dent); + pc = pc->sibling; + } +} +#endif + +/** + * irq_in_use - return true if this irq is being used + */ +static int irq_in_use(unsigned int irq) +{ + int rc = 0; + unsigned long flags; + struct irq_desc *desc = irq_desc + irq; + + spin_lock_irqsave(&desc->lock, flags); + if (desc->action) + rc = 1; + spin_unlock_irqrestore(&desc->lock, flags); + return rc; +} + +/* ------------------------------------------------------- */ +/** eeh_report_error - report an EEH error to each device, + * collect up and merge the device responses. + */ + +static void eeh_report_error(struct pci_dev *dev, void *userdata) +{ + enum pci_ers_result rc, *res = userdata; + struct pci_driver *driver = dev->driver; + + dev->error_state = pci_channel_io_frozen; + + if (!driver) + return; + + if (irq_in_use (dev->irq)) { + struct device_node *dn = pci_device_to_OF_node(dev); + PCI_DN(dn)->eeh_mode |= EEH_MODE_IRQ_DISABLED; + disable_irq_nosync(dev->irq); + } + if (!driver->err_handler) + return; + if (!driver->err_handler->error_detected) + return; + + rc = driver->err_handler->error_detected (dev, pci_channel_io_frozen); + if (*res == PCI_ERS_RESULT_NONE) *res = rc; + if (*res == PCI_ERS_RESULT_NEED_RESET) return; + if (*res == PCI_ERS_RESULT_DISCONNECT && + rc == PCI_ERS_RESULT_NEED_RESET) *res = rc; +} + +/** eeh_report_reset -- tell this device that the pci slot + * has been reset. + */ + +static void eeh_report_reset(struct pci_dev *dev, void *userdata) +{ + struct pci_driver *driver = dev->driver; + struct device_node *dn = pci_device_to_OF_node(dev); + + if (!driver) + return; + + if ((PCI_DN(dn)->eeh_mode) & EEH_MODE_IRQ_DISABLED) { + PCI_DN(dn)->eeh_mode &= ~EEH_MODE_IRQ_DISABLED; + enable_irq(dev->irq); + } + if (!driver->err_handler) + return; + if (!driver->err_handler->slot_reset) + return; + + driver->err_handler->slot_reset(dev); +} + +static void eeh_report_resume(struct pci_dev *dev, void *userdata) +{ + struct pci_driver *driver = dev->driver; + + dev->error_state = pci_channel_io_normal; + + if (!driver) + return; + if (!driver->err_handler) + return; + if (!driver->err_handler->resume) + return; + + driver->err_handler->resume(dev); +} + +static void eeh_report_failure(struct pci_dev *dev, void *userdata) +{ + struct pci_driver *driver = dev->driver; + + dev->error_state = pci_channel_io_perm_failure; + + if (!driver) + return; + + if (irq_in_use (dev->irq)) { + struct device_node *dn = pci_device_to_OF_node(dev); + PCI_DN(dn)->eeh_mode |= EEH_MODE_IRQ_DISABLED; + disable_irq_nosync(dev->irq); + } + if (!driver->err_handler) + return; + if (!driver->err_handler->error_detected) + return; + driver->err_handler->error_detected(dev, pci_channel_io_perm_failure); +} + +/* ------------------------------------------------------- */ +/** + * handle_eeh_events -- reset a PCI device after hard lockup. + * + * pSeries systems will isolate a PCI slot if the PCI-Host + * bridge detects address or data parity errors, DMA's + * occuring to wild addresses (which usually happen due to + * bugs in device drivers or in PCI adapter firmware). + * Slot isolations also occur if #SERR, #PERR or other misc + * PCI-related errors are detected. + * + * Recovery process consists of unplugging the device driver + * (which generated hotplug events to userspace), then issuing + * a PCI #RST to the device, then reconfiguring the PCI config + * space for all bridges & devices under this slot, and then + * finally restarting the device drivers (which cause a second + * set of hotplug events to go out to userspace). + */ + +/** + * eeh_reset_device() -- perform actual reset of a pci slot + * Args: bus: pointer to the pci bus structure corresponding + * to the isolated slot. A non-null value will + * cause all devices under the bus to be removed + * and then re-added. + * pe_dn: pointer to a "Partionable Endpoint" device node. + * This is the top-level structure on which pci + * bus resets can be performed. + */ + +static int eeh_reset_device (struct pci_dn *pe_dn, struct pci_bus *bus) +{ + int rc; + if (bus) + pcibios_remove_pci_devices(bus); + + /* Reset the pci controller. (Asserts RST#; resets config space). + * Reconfigure bridges and devices. Don't try to bring the system + * up if the reset failed for some reason. */ + rc = rtas_set_slot_reset(pe_dn); + if (rc) + return rc; + + /* New-style config addrs might be shared across multiple devices, + * Walk over all functions on this device */ + if (pe_dn->eeh_pe_config_addr) { + struct device_node *pe = pe_dn->node; + pe = pe->parent->child; + while (pe) { + struct pci_dn *ppe = PCI_DN(pe); + if (pe_dn->eeh_pe_config_addr == ppe->eeh_pe_config_addr) { + rtas_configure_bridge(ppe); + eeh_restore_bars(ppe); + } + pe = pe->sibling; + } + } else { + rtas_configure_bridge(pe_dn); + eeh_restore_bars(pe_dn); + } + + /* Give the system 5 seconds to finish running the user-space + * hotplug shutdown scripts, e.g. ifdown for ethernet. Yes, + * this is a hack, but if we don't do this, and try to bring + * the device up before the scripts have taken it down, + * potentially weird things happen. + */ + if (bus) { + ssleep (5); + pcibios_add_pci_devices(bus); + } + + return 0; +} + +/* The longest amount of time to wait for a pci device + * to come back on line, in seconds. + */ +#define MAX_WAIT_FOR_RECOVERY 15 + +void handle_eeh_events (struct eeh_event *event) +{ + struct device_node *frozen_dn; + struct pci_dn *frozen_pdn; + struct pci_bus *frozen_bus; + int rc = 0; + enum pci_ers_result result = PCI_ERS_RESULT_NONE; + + frozen_dn = find_device_pe(event->dn); + frozen_bus = pcibios_find_pci_bus(frozen_dn); + + if (!frozen_dn) { + printk(KERN_ERR "EEH: Error: Cannot find partition endpoint for %s\n", + pci_name(event->dev)); + return; + } + + /* There are two different styles for coming up with the PE. + * In the old style, it was the highest EEH-capable device + * which was always an EADS pci bridge. In the new style, + * there might not be any EADS bridges, and even when there are, + * the firmware marks them as "EEH incapable". So another + * two-step is needed to find the pci bus.. */ + if (!frozen_bus) + frozen_bus = pcibios_find_pci_bus (frozen_dn->parent); + + if (!frozen_bus) { + printk(KERN_ERR "EEH: Cannot find PCI bus for %s\n", + frozen_dn->full_name); + return; + } + +#if 0 + /* We may get "permanent failure" messages on empty slots. + * These are false alarms. Empty slots have no child dn. */ + if ((event->state == pci_channel_io_perm_failure) && (frozen_device == NULL)) + return; +#endif + + frozen_pdn = PCI_DN(frozen_dn); + frozen_pdn->eeh_freeze_count++; + + if (frozen_pdn->eeh_freeze_count > EEH_MAX_ALLOWED_FREEZES) + goto hard_fail; + + /* If the reset state is a '5' and the time to reset is 0 (infinity) + * or is more then 15 seconds, then mark this as a permanent failure. + */ + if ((event->state == pci_channel_io_perm_failure) && + ((event->time_unavail <= 0) || + (event->time_unavail > MAX_WAIT_FOR_RECOVERY*1000))) + goto hard_fail; + + eeh_slot_error_detail(frozen_pdn, 1 /* Temporary Error */); + printk(KERN_WARNING + "EEH: This PCI device has failed %d times since last reboot: %s - %s\n", + frozen_pdn->eeh_freeze_count, + pci_name (frozen_pdn->pcidev), + pcid_name(frozen_pdn->pcidev)); + + /* Walk the various device drivers attached to this slot through + * a reset sequence, giving each an opportunity to do what it needs + * to accomplish the reset. Each child gets a report of the + * status ... if any child can't handle the reset, then the entire + * slot is dlpar removed and added. + */ + pci_walk_bus(frozen_bus, eeh_report_error, &result); + + /* If all device drivers were EEH-unaware, then shut + * down all of the device drivers, and hope they + * go down willingly, without panicing the system. + */ + if (result == PCI_ERS_RESULT_NONE) { + rc = eeh_reset_device(frozen_pdn, frozen_bus); + if (rc) + goto hard_fail; + } + + /* If any device called out for a reset, then reset the slot */ + if (result == PCI_ERS_RESULT_NEED_RESET) { + rc = eeh_reset_device(frozen_pdn, NULL); + if (rc) + goto hard_fail; + pci_walk_bus(frozen_bus, eeh_report_reset, 0); + } + + /* If all devices reported they can proceed, the re-enable PIO */ + if (result == PCI_ERS_RESULT_CAN_RECOVER) { + /* XXX Not supported; we brute-force reset the device */ + rc = eeh_reset_device(frozen_pdn, NULL); + if (rc) + goto hard_fail; + pci_walk_bus(frozen_bus, eeh_report_reset, 0); + } + + /* Tell all device drivers that they can resume operations */ + pci_walk_bus(frozen_bus, eeh_report_resume, 0); + + return; + +hard_fail: + /* + * About 90% of all real-life EEH failures in the field + * are due to poorly seated PCI cards. Only 10% or so are + * due to actual, failed cards. + */ + printk(KERN_ERR + "EEH: PCI device %s - %s has failed %d times \n" + "and has been permanently disabled. Please try reseating\n" + "this device or replacing it.\n", + pci_name (frozen_pdn->pcidev), + pcid_name(frozen_pdn->pcidev), + frozen_pdn->eeh_freeze_count); + + eeh_slot_error_detail(frozen_pdn, 2 /* Permanent Error */); + + /* Notify all devices that they're about to go down. */ + pci_walk_bus(frozen_bus, eeh_report_failure, 0); + + /* Shut down the device drivers for good. */ + pcibios_remove_pci_devices(frozen_bus); +} + +/* ---------- end of file ---------- */ diff --git a/arch/powerpc/platforms/pseries/eeh_event.c b/arch/powerpc/platforms/pseries/eeh_event.c index 9249733..9a9961f 100644 --- a/arch/powerpc/platforms/pseries/eeh_event.c +++ b/arch/powerpc/platforms/pseries/eeh_event.c @@ -21,6 +21,7 @@ #include #include #include +#include /** Overview: * EEH error states may be detected within exception handlers; @@ -37,31 +38,6 @@ static void eeh_thread_launcher(void *); DECLARE_WORK(eeh_event_wq, eeh_thread_launcher, NULL); /** - * eeh_panic - call panic() for an eeh event that cannot be handled. - * The philosophy of this routine is that it is better to panic and - * halt the OS than it is to risk possible data corruption by - * oblivious device drivers that don't know better. - * - * @dev pci device that had an eeh event - * @reset_state current reset state of the device slot - */ -static void eeh_panic(struct pci_dev *dev, int reset_state) -{ - /* - * Since the panic_on_oops sysctl is used to halt the system - * in light of potential corruption, we can use it here. - */ - if (panic_on_oops) { - panic("EEH: MMIO failure (%d) on device:%s\n", reset_state, - pci_name(dev)); - } - else { - printk(KERN_INFO "EEH: Ignored MMIO failure (%d) on device:%s\n", - reset_state, pci_name(dev)); - } -} - -/** * eeh_event_handler - dispatch EEH events. The detection of a frozen * slot can occur inside an interrupt, where it can be hard to do * anything about it. The goal of this routine is to pull these @@ -82,10 +58,16 @@ static int eeh_event_handler(void * dumm spin_lock_irqsave(&eeh_eventlist_lock, flags); event = NULL; + + /* Unqueue the event, get ready to process. */ if (!list_empty(&eeh_eventlist)) { event = list_entry(eeh_eventlist.next, struct eeh_event, list); list_del(&event->list); } + + if (event) + eeh_mark_slot(event->dn, EEH_MODE_RECOVERING); + spin_unlock_irqrestore(&eeh_eventlist_lock, flags); if (event == NULL) break; @@ -93,8 +75,11 @@ static int eeh_event_handler(void * dumm printk(KERN_INFO "EEH: Detected PCI bus error on device %s\n", pci_name(event->dev)); - eeh_panic (event->dev, event->state); + handle_eeh_events(event); + + eeh_clear_slot(event->dn, EEH_MODE_RECOVERING); + pci_dev_put(event->dev); kfree(event); } @@ -122,7 +107,7 @@ static void eeh_thread_launcher(void *du */ int eeh_send_failure_event (struct device_node *dn, struct pci_dev *dev, - int state, + enum pci_channel_state state, int time_unavail) { unsigned long flags; diff --git a/arch/powerpc/platforms/pseries/lpar.c b/arch/powerpc/platforms/pseries/lpar.c index a50e5f3..231b4b0 100644 --- a/arch/powerpc/platforms/pseries/lpar.c +++ b/arch/powerpc/platforms/pseries/lpar.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -60,7 +61,7 @@ extern void pSeries_find_serial_port(voi int vtermno; /* virtual terminal# for udbg */ #define __ALIGNED__ __attribute__((__aligned__(sizeof(long)))) -static void udbg_hvsi_putc(unsigned char c) +static void udbg_hvsi_putc(char c) { /* packet's seqno isn't used anyways */ uint8_t packet[] __ALIGNED__ = { 0xff, 5, 0, 0, c }; @@ -111,7 +112,7 @@ static int udbg_hvsi_getc_poll(void) return ch; } -static unsigned char udbg_hvsi_getc(void) +static char udbg_hvsi_getc(void) { int ch; for (;;) { @@ -127,7 +128,7 @@ static unsigned char udbg_hvsi_getc(void } } -static void udbg_putcLP(unsigned char c) +static void udbg_putcLP(char c) { char buf[16]; unsigned long rc; @@ -172,7 +173,7 @@ static int udbg_getc_pollLP(void) return ch; } -static unsigned char udbg_getcLP(void) +static char udbg_getcLP(void) { int ch; for (;;) { @@ -191,7 +192,7 @@ static unsigned char udbg_getcLP(void) /* call this from early_init() for a working debug console on * vterm capable LPAR machines */ -void udbg_init_debug_lpar(void) +void __init udbg_init_debug_lpar(void) { vtermno = 0; udbg_putc = udbg_putcLP; @@ -200,63 +201,54 @@ void udbg_init_debug_lpar(void) } /* returns 0 if couldn't find or use /chosen/stdout as console */ -int find_udbg_vterm(void) +void __init find_udbg_vterm(void) { struct device_node *stdout_node; u32 *termno; char *name; - int found = 0; + int add_console; /* find the boot console from /chosen/stdout */ if (!of_chosen) - return 0; + return; name = (char *)get_property(of_chosen, "linux,stdout-path", NULL); if (name == NULL) - return 0; + return; stdout_node = of_find_node_by_path(name); if (!stdout_node) - return 0; - - /* now we have the stdout node; figure out what type of device it is. */ + return; name = (char *)get_property(stdout_node, "name", NULL); if (!name) { printk(KERN_WARNING "stdout node missing 'name' property!\n"); goto out; } + /* The user has requested a console so this is already set up. */ + add_console = !strstr(cmd_line, "console="); - if (strncmp(name, "vty", 3) == 0) { - if (device_is_compatible(stdout_node, "hvterm1")) { - termno = (u32 *)get_property(stdout_node, "reg", NULL); - if (termno) { - vtermno = termno[0]; - udbg_putc = udbg_putcLP; - udbg_getc = udbg_getcLP; - udbg_getc_poll = udbg_getc_pollLP; - found = 1; - } - } else if (device_is_compatible(stdout_node, "hvterm-protocol")) { - termno = (u32 *)get_property(stdout_node, "reg", NULL); - if (termno) { - vtermno = termno[0]; - udbg_putc = udbg_hvsi_putc; - udbg_getc = udbg_hvsi_getc; - udbg_getc_poll = udbg_hvsi_getc_poll; - found = 1; - } - } - } else if (strncmp(name, "serial", 6)) { - /* XXX fix ISA serial console */ - printk(KERN_WARNING "serial stdout on LPAR ('%s')! " - "can't print udbg messages\n", - stdout_node->full_name); - } else { - printk(KERN_WARNING "don't know how to print to stdout '%s'\n", - stdout_node->full_name); - } + /* Check if it's a virtual terminal */ + if (strncmp(name, "vty", 3) != 0) + goto out; + termno = (u32 *)get_property(stdout_node, "reg", NULL); + if (termno == NULL) + goto out; + vtermno = termno[0]; + if (device_is_compatible(stdout_node, "hvterm1")) { + udbg_putc = udbg_putcLP; + udbg_getc = udbg_getcLP; + udbg_getc_poll = udbg_getc_pollLP; + if (add_console) + add_preferred_console("hvc", termno[0] & 0xff, NULL); + } else if (device_is_compatible(stdout_node, "hvterm-protocol")) { + vtermno = termno[0]; + udbg_putc = udbg_hvsi_putc; + udbg_getc = udbg_hvsi_getc; + udbg_getc_poll = udbg_hvsi_getc_poll; + if (add_console) + add_preferred_console("hvsi", termno[0] & 0xff, NULL); + } out: of_node_put(stdout_node); - return found; } void vpa_init(int cpu) diff --git a/arch/powerpc/platforms/pseries/pci_dlpar.c b/arch/powerpc/platforms/pseries/pci_dlpar.c new file mode 100644 index 0000000..283377a --- /dev/null +++ b/arch/powerpc/platforms/pseries/pci_dlpar.c @@ -0,0 +1,174 @@ +/* + * PCI Dynamic LPAR, PCI Hot Plug and PCI EEH recovery code + * for RPA-compliant PPC64 platform. + * Copyright (C) 2003 Linda Xie + * Copyright (C) 2005 International Business Machines + * + * Updates, 2005, John Rose + * Updates, 2005, Linas Vepstas + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include + +static struct pci_bus * +find_bus_among_children(struct pci_bus *bus, + struct device_node *dn) +{ + struct pci_bus *child = NULL; + struct list_head *tmp; + struct device_node *busdn; + + busdn = pci_bus_to_OF_node(bus); + if (busdn == dn) + return bus; + + list_for_each(tmp, &bus->children) { + child = find_bus_among_children(pci_bus_b(tmp), dn); + if (child) + break; + }; + return child; +} + +struct pci_bus * +pcibios_find_pci_bus(struct device_node *dn) +{ + struct pci_dn *pdn = dn->data; + + if (!pdn || !pdn->phb || !pdn->phb->bus) + return NULL; + + return find_bus_among_children(pdn->phb->bus, dn); +} + +/** + * pcibios_remove_pci_devices - remove all devices under this bus + * + * Remove all of the PCI devices under this bus both from the + * linux pci device tree, and from the powerpc EEH address cache. + */ +void +pcibios_remove_pci_devices(struct pci_bus *bus) +{ + struct pci_dev *dev, *tmp; + + list_for_each_entry_safe(dev, tmp, &bus->devices, bus_list) { + eeh_remove_bus_device(dev); + pci_remove_bus_device(dev); + } +} + +/* Must be called before pci_bus_add_devices */ +static void +pcibios_fixup_new_pci_devices(struct pci_bus *bus, int fix_bus) +{ + struct pci_dev *dev; + + list_for_each_entry(dev, &bus->devices, bus_list) { + /* + * Skip already-present devices (which are on the + * global device list.) + */ + if (list_empty(&dev->global_list)) { + int i; + + /* Need to setup IOMMU tables */ + ppc_md.iommu_dev_setup(dev); + + if(fix_bus) + pcibios_fixup_device_resources(dev, bus); + pci_read_irq_line(dev); + for (i = 0; i < PCI_NUM_RESOURCES; i++) { + struct resource *r = &dev->resource[i]; + + if (r->parent || !r->start || !r->flags) + continue; + pci_claim_resource(dev, i); + } + } + } +} + +static int +pcibios_pci_config_bridge(struct pci_dev *dev) +{ + u8 sec_busno; + struct pci_bus *child_bus; + struct pci_dev *child_dev; + + /* Get busno of downstream bus */ + pci_read_config_byte(dev, PCI_SECONDARY_BUS, &sec_busno); + + /* Add to children of PCI bridge dev->bus */ + child_bus = pci_add_new_bus(dev->bus, dev, sec_busno); + if (!child_bus) { + printk (KERN_ERR "%s: could not add second bus\n", __FUNCTION__); + return -EIO; + } + sprintf(child_bus->name, "PCI Bus #%02x", child_bus->number); + + pci_scan_child_bus(child_bus); + + list_for_each_entry(child_dev, &child_bus->devices, bus_list) { + eeh_add_device_late(child_dev); + } + + /* Fixup new pci devices without touching bus struct */ + pcibios_fixup_new_pci_devices(child_bus, 0); + + /* Make the discovered devices available */ + pci_bus_add_devices(child_bus); + return 0; +} + +/** + * pcibios_add_pci_devices - adds new pci devices to bus + * + * This routine will find and fixup new pci devices under + * the indicated bus. This routine presumes that there + * might already be some devices under this bridge, so + * it carefully tries to add only new devices. (And that + * is how this routine differs from other, similar pcibios + * routines.) + */ +void +pcibios_add_pci_devices(struct pci_bus * bus) +{ + int slotno, num; + struct pci_dev *dev; + struct device_node *dn = pci_bus_to_OF_node(bus); + + eeh_add_device_tree_early(dn); + + /* pci_scan_slot should find all children */ + slotno = PCI_SLOT(PCI_DN(dn->child)->devfn); + num = pci_scan_slot(bus, PCI_DEVFN(slotno, 0)); + if (num) { + pcibios_fixup_new_pci_devices(bus, 1); + pci_bus_add_devices(bus); + } + + list_for_each_entry(dev, &bus->devices, bus_list) { + eeh_add_device_late (dev); + if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) + pcibios_pci_config_bridge(dev); + } +} diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c index 4a465f0..8828dc3 100644 --- a/arch/powerpc/platforms/pseries/setup.c +++ b/arch/powerpc/platforms/pseries/setup.c @@ -56,6 +56,7 @@ #include #include #include +#include #include #include #include "xics.h" @@ -78,8 +79,6 @@ extern void find_udbg_vterm(void); extern void system_reset_fwnmi(void); /* from head.S */ extern void machine_check_fwnmi(void); /* from head.S */ -extern void generic_find_legacy_serial_ports(u64 *physport, - unsigned int *default_speed); int fwnmi_active; /* TRUE if an FWNMI handler is present */ @@ -365,10 +364,7 @@ static int pseries_set_xdabr(unsigned lo */ static void __init pSeries_init_early(void) { - void *comport; int iommu_off = 0; - unsigned int default_speed; - u64 physport; DBG(" -> pSeries_init_early()\n"); @@ -382,17 +378,8 @@ static void __init pSeries_init_early(vo get_property(of_chosen, "linux,iommu-off", NULL)); } - generic_find_legacy_serial_ports(&physport, &default_speed); - if (platform_is_lpar()) find_udbg_vterm(); - else if (physport) { - /* Map the uart for udbg. */ - comport = (void *)ioremap(physport, 16); - udbg_init_uart(comport, default_speed); - - DBG("Hello World !\n"); - } if (firmware_has_feature(FW_FEATURE_DABR)) ppc_md.set_dabr = pseries_set_dabr; @@ -638,5 +625,7 @@ struct machdep_calls __initdata pSeries_ .machine_check_exception = pSeries_machine_check_exception, #ifdef CONFIG_KEXEC .kexec_cpu_down = pseries_kexec_cpu_down, + .machine_kexec = default_machine_kexec, + .machine_kexec_prepare = default_machine_kexec_prepare, #endif }; diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile index 6b7efcf..b3e3636 100644 --- a/arch/powerpc/sysdev/Makefile +++ b/arch/powerpc/sysdev/Makefile @@ -6,3 +6,4 @@ obj-$(CONFIG_BOOKE) += dcr.o obj-$(CONFIG_40x) += dcr.o obj-$(CONFIG_U3_DART) += u3_iommu.o obj-$(CONFIG_MMIO_NVRAM) += mmio_nvram.o +obj-$(CONFIG_83xx) += ipic.o diff --git a/arch/powerpc/sysdev/ipic.c b/arch/powerpc/sysdev/ipic.c new file mode 100644 index 0000000..8f01e0f --- /dev/null +++ b/arch/powerpc/sysdev/ipic.c @@ -0,0 +1,646 @@ +/* + * include/asm-ppc/ipic.c + * + * IPIC routines implementations. + * + * Copyright 2005 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ipic.h" + +static struct ipic p_ipic; +static struct ipic * primary_ipic; + +static struct ipic_info ipic_info[] = { + [9] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_D, + .force = IPIC_SIFCR_H, + .bit = 24, + .prio_mask = 0, + }, + [10] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_D, + .force = IPIC_SIFCR_H, + .bit = 25, + .prio_mask = 1, + }, + [11] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_D, + .force = IPIC_SIFCR_H, + .bit = 26, + .prio_mask = 2, + }, + [14] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_D, + .force = IPIC_SIFCR_H, + .bit = 29, + .prio_mask = 5, + }, + [15] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_D, + .force = IPIC_SIFCR_H, + .bit = 30, + .prio_mask = 6, + }, + [16] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_D, + .force = IPIC_SIFCR_H, + .bit = 31, + .prio_mask = 7, + }, + [17] = { + .pend = IPIC_SEPNR, + .mask = IPIC_SEMSR, + .prio = IPIC_SMPRR_A, + .force = IPIC_SEFCR, + .bit = 1, + .prio_mask = 5, + }, + [18] = { + .pend = IPIC_SEPNR, + .mask = IPIC_SEMSR, + .prio = IPIC_SMPRR_A, + .force = IPIC_SEFCR, + .bit = 2, + .prio_mask = 6, + }, + [19] = { + .pend = IPIC_SEPNR, + .mask = IPIC_SEMSR, + .prio = IPIC_SMPRR_A, + .force = IPIC_SEFCR, + .bit = 3, + .prio_mask = 7, + }, + [20] = { + .pend = IPIC_SEPNR, + .mask = IPIC_SEMSR, + .prio = IPIC_SMPRR_B, + .force = IPIC_SEFCR, + .bit = 4, + .prio_mask = 4, + }, + [21] = { + .pend = IPIC_SEPNR, + .mask = IPIC_SEMSR, + .prio = IPIC_SMPRR_B, + .force = IPIC_SEFCR, + .bit = 5, + .prio_mask = 5, + }, + [22] = { + .pend = IPIC_SEPNR, + .mask = IPIC_SEMSR, + .prio = IPIC_SMPRR_B, + .force = IPIC_SEFCR, + .bit = 6, + .prio_mask = 6, + }, + [23] = { + .pend = IPIC_SEPNR, + .mask = IPIC_SEMSR, + .prio = IPIC_SMPRR_B, + .force = IPIC_SEFCR, + .bit = 7, + .prio_mask = 7, + }, + [32] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_A, + .force = IPIC_SIFCR_H, + .bit = 0, + .prio_mask = 0, + }, + [33] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_A, + .force = IPIC_SIFCR_H, + .bit = 1, + .prio_mask = 1, + }, + [34] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_A, + .force = IPIC_SIFCR_H, + .bit = 2, + .prio_mask = 2, + }, + [35] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_A, + .force = IPIC_SIFCR_H, + .bit = 3, + .prio_mask = 3, + }, + [36] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_A, + .force = IPIC_SIFCR_H, + .bit = 4, + .prio_mask = 4, + }, + [37] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_A, + .force = IPIC_SIFCR_H, + .bit = 5, + .prio_mask = 5, + }, + [38] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_A, + .force = IPIC_SIFCR_H, + .bit = 6, + .prio_mask = 6, + }, + [39] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_A, + .force = IPIC_SIFCR_H, + .bit = 7, + .prio_mask = 7, + }, + [48] = { + .pend = IPIC_SEPNR, + .mask = IPIC_SEMSR, + .prio = IPIC_SMPRR_A, + .force = IPIC_SEFCR, + .bit = 0, + .prio_mask = 4, + }, + [64] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_L, + .prio = IPIC_SMPRR_A, + .force = IPIC_SIFCR_L, + .bit = 0, + .prio_mask = 0, + }, + [65] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_L, + .prio = IPIC_SMPRR_A, + .force = IPIC_SIFCR_L, + .bit = 1, + .prio_mask = 1, + }, + [66] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_L, + .prio = IPIC_SMPRR_A, + .force = IPIC_SIFCR_L, + .bit = 2, + .prio_mask = 2, + }, + [67] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_L, + .prio = IPIC_SMPRR_A, + .force = IPIC_SIFCR_L, + .bit = 3, + .prio_mask = 3, + }, + [68] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_L, + .prio = IPIC_SMPRR_B, + .force = IPIC_SIFCR_L, + .bit = 4, + .prio_mask = 0, + }, + [69] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_L, + .prio = IPIC_SMPRR_B, + .force = IPIC_SIFCR_L, + .bit = 5, + .prio_mask = 1, + }, + [70] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_L, + .prio = IPIC_SMPRR_B, + .force = IPIC_SIFCR_L, + .bit = 6, + .prio_mask = 2, + }, + [71] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_L, + .prio = IPIC_SMPRR_B, + .force = IPIC_SIFCR_L, + .bit = 7, + .prio_mask = 3, + }, + [72] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_L, + .prio = 0, + .force = IPIC_SIFCR_L, + .bit = 8, + }, + [73] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_L, + .prio = 0, + .force = IPIC_SIFCR_L, + .bit = 9, + }, + [74] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_L, + .prio = 0, + .force = IPIC_SIFCR_L, + .bit = 10, + }, + [75] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_L, + .prio = 0, + .force = IPIC_SIFCR_L, + .bit = 11, + }, + [76] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_L, + .prio = 0, + .force = IPIC_SIFCR_L, + .bit = 12, + }, + [77] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_L, + .prio = 0, + .force = IPIC_SIFCR_L, + .bit = 13, + }, + [78] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_L, + .prio = 0, + .force = IPIC_SIFCR_L, + .bit = 14, + }, + [79] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_L, + .prio = 0, + .force = IPIC_SIFCR_L, + .bit = 15, + }, + [80] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_L, + .prio = 0, + .force = IPIC_SIFCR_L, + .bit = 16, + }, + [84] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_L, + .prio = 0, + .force = IPIC_SIFCR_L, + .bit = 20, + }, + [85] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_L, + .prio = 0, + .force = IPIC_SIFCR_L, + .bit = 21, + }, + [90] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_L, + .prio = 0, + .force = IPIC_SIFCR_L, + .bit = 26, + }, + [91] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_L, + .prio = 0, + .force = IPIC_SIFCR_L, + .bit = 27, + }, +}; + +static inline u32 ipic_read(volatile u32 __iomem *base, unsigned int reg) +{ + return in_be32(base + (reg >> 2)); +} + +static inline void ipic_write(volatile u32 __iomem *base, unsigned int reg, u32 value) +{ + out_be32(base + (reg >> 2), value); +} + +static inline struct ipic * ipic_from_irq(unsigned int irq) +{ + return primary_ipic; +} + +static void ipic_enable_irq(unsigned int irq) +{ + struct ipic *ipic = ipic_from_irq(irq); + unsigned int src = irq - ipic->irq_offset; + u32 temp; + + temp = ipic_read(ipic->regs, ipic_info[src].mask); + temp |= (1 << (31 - ipic_info[src].bit)); + ipic_write(ipic->regs, ipic_info[src].mask, temp); +} + +static void ipic_disable_irq(unsigned int irq) +{ + struct ipic *ipic = ipic_from_irq(irq); + unsigned int src = irq - ipic->irq_offset; + u32 temp; + + temp = ipic_read(ipic->regs, ipic_info[src].mask); + temp &= ~(1 << (31 - ipic_info[src].bit)); + ipic_write(ipic->regs, ipic_info[src].mask, temp); +} + +static void ipic_disable_irq_and_ack(unsigned int irq) +{ + struct ipic *ipic = ipic_from_irq(irq); + unsigned int src = irq - ipic->irq_offset; + u32 temp; + + ipic_disable_irq(irq); + + temp = ipic_read(ipic->regs, ipic_info[src].pend); + temp |= (1 << (31 - ipic_info[src].bit)); + ipic_write(ipic->regs, ipic_info[src].pend, temp); +} + +static void ipic_end_irq(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) + ipic_enable_irq(irq); +} + +struct hw_interrupt_type ipic = { + .typename = " IPIC ", + .enable = ipic_enable_irq, + .disable = ipic_disable_irq, + .ack = ipic_disable_irq_and_ack, + .end = ipic_end_irq, +}; + +void __init ipic_init(phys_addr_t phys_addr, + unsigned int flags, + unsigned int irq_offset, + unsigned char *senses, + unsigned int senses_count) +{ + u32 i, temp = 0; + + primary_ipic = &p_ipic; + primary_ipic->regs = ioremap(phys_addr, MPC83xx_IPIC_SIZE); + + primary_ipic->irq_offset = irq_offset; + + ipic_write(primary_ipic->regs, IPIC_SICNR, 0x0); + + /* default priority scheme is grouped. If spread mode is required + * configure SICFR accordingly */ + if (flags & IPIC_SPREADMODE_GRP_A) + temp |= SICFR_IPSA; + if (flags & IPIC_SPREADMODE_GRP_D) + temp |= SICFR_IPSD; + if (flags & IPIC_SPREADMODE_MIX_A) + temp |= SICFR_MPSA; + if (flags & IPIC_SPREADMODE_MIX_B) + temp |= SICFR_MPSB; + + ipic_write(primary_ipic->regs, IPIC_SICNR, temp); + + /* handle MCP route */ + temp = 0; + if (flags & IPIC_DISABLE_MCP_OUT) + temp = SERCR_MCPR; + ipic_write(primary_ipic->regs, IPIC_SERCR, temp); + + /* handle routing of IRQ0 to MCP */ + temp = ipic_read(primary_ipic->regs, IPIC_SEMSR); + + if (flags & IPIC_IRQ0_MCP) + temp |= SEMSR_SIRQ0; + else + temp &= ~SEMSR_SIRQ0; + + ipic_write(primary_ipic->regs, IPIC_SEMSR, temp); + + for (i = 0 ; i < NR_IPIC_INTS ; i++) { + irq_desc[i+irq_offset].handler = &ipic; + irq_desc[i+irq_offset].status = IRQ_LEVEL; + } + + temp = 0; + for (i = 0 ; i < senses_count ; i++) { + if ((senses[i] & IRQ_SENSE_MASK) == IRQ_SENSE_EDGE) { + temp |= 1 << (15 - i); + if (i != 0) + irq_desc[i + irq_offset + MPC83xx_IRQ_EXT1 - 1].status = 0; + else + irq_desc[irq_offset + MPC83xx_IRQ_EXT0].status = 0; + } + } + ipic_write(primary_ipic->regs, IPIC_SECNR, temp); + + printk ("IPIC (%d IRQ sources, %d External IRQs) at %p\n", NR_IPIC_INTS, + senses_count, primary_ipic->regs); +} + +int ipic_set_priority(unsigned int irq, unsigned int priority) +{ + struct ipic *ipic = ipic_from_irq(irq); + unsigned int src = irq - ipic->irq_offset; + u32 temp; + + if (priority > 7) + return -EINVAL; + if (src > 127) + return -EINVAL; + if (ipic_info[src].prio == 0) + return -EINVAL; + + temp = ipic_read(ipic->regs, ipic_info[src].prio); + + if (priority < 4) { + temp &= ~(0x7 << (20 + (3 - priority) * 3)); + temp |= ipic_info[src].prio_mask << (20 + (3 - priority) * 3); + } else { + temp &= ~(0x7 << (4 + (7 - priority) * 3)); + temp |= ipic_info[src].prio_mask << (4 + (7 - priority) * 3); + } + + ipic_write(ipic->regs, ipic_info[src].prio, temp); + + return 0; +} + +void ipic_set_highest_priority(unsigned int irq) +{ + struct ipic *ipic = ipic_from_irq(irq); + unsigned int src = irq - ipic->irq_offset; + u32 temp; + + temp = ipic_read(ipic->regs, IPIC_SICFR); + + /* clear and set HPI */ + temp &= 0x7f000000; + temp |= (src & 0x7f) << 24; + + ipic_write(ipic->regs, IPIC_SICFR, temp); +} + +void ipic_set_default_priority(void) +{ + ipic_set_priority(MPC83xx_IRQ_TSEC1_TX, 0); + ipic_set_priority(MPC83xx_IRQ_TSEC1_RX, 1); + ipic_set_priority(MPC83xx_IRQ_TSEC1_ERROR, 2); + ipic_set_priority(MPC83xx_IRQ_TSEC2_TX, 3); + ipic_set_priority(MPC83xx_IRQ_TSEC2_RX, 4); + ipic_set_priority(MPC83xx_IRQ_TSEC2_ERROR, 5); + ipic_set_priority(MPC83xx_IRQ_USB2_DR, 6); + ipic_set_priority(MPC83xx_IRQ_USB2_MPH, 7); + + ipic_set_priority(MPC83xx_IRQ_UART1, 0); + ipic_set_priority(MPC83xx_IRQ_UART2, 1); + ipic_set_priority(MPC83xx_IRQ_SEC2, 2); + ipic_set_priority(MPC83xx_IRQ_IIC1, 5); + ipic_set_priority(MPC83xx_IRQ_IIC2, 6); + ipic_set_priority(MPC83xx_IRQ_SPI, 7); + ipic_set_priority(MPC83xx_IRQ_RTC_SEC, 0); + ipic_set_priority(MPC83xx_IRQ_PIT, 1); + ipic_set_priority(MPC83xx_IRQ_PCI1, 2); + ipic_set_priority(MPC83xx_IRQ_PCI2, 3); + ipic_set_priority(MPC83xx_IRQ_EXT0, 4); + ipic_set_priority(MPC83xx_IRQ_EXT1, 5); + ipic_set_priority(MPC83xx_IRQ_EXT2, 6); + ipic_set_priority(MPC83xx_IRQ_EXT3, 7); + ipic_set_priority(MPC83xx_IRQ_RTC_ALR, 0); + ipic_set_priority(MPC83xx_IRQ_MU, 1); + ipic_set_priority(MPC83xx_IRQ_SBA, 2); + ipic_set_priority(MPC83xx_IRQ_DMA, 3); + ipic_set_priority(MPC83xx_IRQ_EXT4, 4); + ipic_set_priority(MPC83xx_IRQ_EXT5, 5); + ipic_set_priority(MPC83xx_IRQ_EXT6, 6); + ipic_set_priority(MPC83xx_IRQ_EXT7, 7); +} + +void ipic_enable_mcp(enum ipic_mcp_irq mcp_irq) +{ + struct ipic *ipic = primary_ipic; + u32 temp; + + temp = ipic_read(ipic->regs, IPIC_SERMR); + temp |= (1 << (31 - mcp_irq)); + ipic_write(ipic->regs, IPIC_SERMR, temp); +} + +void ipic_disable_mcp(enum ipic_mcp_irq mcp_irq) +{ + struct ipic *ipic = primary_ipic; + u32 temp; + + temp = ipic_read(ipic->regs, IPIC_SERMR); + temp &= (1 << (31 - mcp_irq)); + ipic_write(ipic->regs, IPIC_SERMR, temp); +} + +u32 ipic_get_mcp_status(void) +{ + return ipic_read(primary_ipic->regs, IPIC_SERMR); +} + +void ipic_clear_mcp_status(u32 mask) +{ + ipic_write(primary_ipic->regs, IPIC_SERMR, mask); +} + +/* Return an interrupt vector or -1 if no interrupt is pending. */ +int ipic_get_irq(struct pt_regs *regs) +{ + int irq; + + irq = ipic_read(primary_ipic->regs, IPIC_SIVCR) & 0x7f; + + if (irq == 0) /* 0 --> no irq is pending */ + irq = -1; + + return irq; +} + +static struct sysdev_class ipic_sysclass = { + set_kset_name("ipic"), +}; + +static struct sys_device device_ipic = { + .id = 0, + .cls = &ipic_sysclass, +}; + +static int __init init_ipic_sysfs(void) +{ + int rc; + + if (!primary_ipic->regs) + return -ENODEV; + printk(KERN_DEBUG "Registering ipic with sysfs...\n"); + + rc = sysdev_class_register(&ipic_sysclass); + if (rc) { + printk(KERN_ERR "Failed registering ipic sys class\n"); + return -ENODEV; + } + rc = sysdev_register(&device_ipic); + if (rc) { + printk(KERN_ERR "Failed registering ipic sys device\n"); + return -ENODEV; + } + return 0; +} + +subsys_initcall(init_ipic_sysfs); diff --git a/arch/powerpc/sysdev/ipic.h b/arch/powerpc/sysdev/ipic.h new file mode 100644 index 0000000..a7ce7da --- /dev/null +++ b/arch/powerpc/sysdev/ipic.h @@ -0,0 +1,49 @@ +/* + * arch/ppc/kernel/ipic.h + * + * IPIC private definitions and structure. + * + * Maintainer: Kumar Gala + * + * Copyright 2005 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. + */ +#ifndef __IPIC_H__ +#define __IPIC_H__ + +#include + +#define MPC83xx_IPIC_SIZE (0x00100) + +/* System Global Interrupt Configuration Register */ +#define SICFR_IPSA 0x00010000 +#define SICFR_IPSD 0x00080000 +#define SICFR_MPSA 0x00200000 +#define SICFR_MPSB 0x00400000 + +/* System External Interrupt Mask Register */ +#define SEMSR_SIRQ0 0x00008000 + +/* System Error Control Register */ +#define SERCR_MCPR 0x00000001 + +struct ipic { + volatile u32 __iomem *regs; + unsigned int irq_offset; +}; + +struct ipic_info { + u8 pend; /* pending register offset from base */ + u8 mask; /* mask register offset from base */ + u8 prio; /* priority register offset from base */ + u8 force; /* force register offset from base */ + u8 bit; /* register bit position (as per doc) + bit mask = 1 << (31 - bit) */ + u8 prio_mask; /* priority mask value */ +}; + +#endif /* __IPIC_H__ */ diff --git a/arch/powerpc/xmon/Makefile b/arch/powerpc/xmon/Makefile index b20312e..109d874 100644 --- a/arch/powerpc/xmon/Makefile +++ b/arch/powerpc/xmon/Makefile @@ -3,9 +3,5 @@ ifdef CONFIG_PPC64 EXTRA_CFLAGS += -mno-minimal-toc endif - -obj-$(CONFIG_8xx) += start_8xx.o -obj-$(CONFIG_6xx) += start_32.o -obj-$(CONFIG_4xx) += start_32.o -obj-$(CONFIG_PPC64) += start_64.o -obj-y += xmon.o ppc-dis.o ppc-opc.o setjmp.o nonstdio.o +obj-y += xmon.o ppc-dis.o ppc-opc.o setjmp.o start.o \ + nonstdio.o diff --git a/arch/powerpc/xmon/start.c b/arch/powerpc/xmon/start.c new file mode 100644 index 0000000..712552c --- /dev/null +++ b/arch/powerpc/xmon/start.c @@ -0,0 +1,34 @@ +/* + * Copyright (C) 1996 Paul Mackerras. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include +#include +#include "nonstdio.h" + +void xmon_map_scc(void) +{ +} + +int xmon_write(void *ptr, int nb) +{ + return udbg_write(ptr, nb); +} + +int xmon_readchar(void) +{ + if (udbg_getc) + return udbg_getc(); + return -1; +} + +int xmon_read_poll(void) +{ + if (udbg_getc_poll) + return udbg_getc_poll(); + return -1; +} diff --git a/arch/powerpc/xmon/start_32.c b/arch/powerpc/xmon/start_32.c deleted file mode 100644 index c2464df..0000000 --- a/arch/powerpc/xmon/start_32.c +++ /dev/null @@ -1,441 +0,0 @@ -/* - * Copyright (C) 1996 Paul Mackerras. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "nonstdio.h" - -static volatile unsigned char __iomem *sccc, *sccd; -unsigned int TXRDY, RXRDY, DLAB; - -static int use_serial; -static int use_screen; -static int via_modem; -static int xmon_use_sccb; -static struct device_node *channel_node; - -void buf_access(void) -{ - if (DLAB) - sccd[3] &= ~DLAB; /* reset DLAB */ -} - -extern int adb_init(void); - -#ifdef CONFIG_PPC_CHRP -/* - * This looks in the "ranges" property for the primary PCI host bridge - * to find the physical address of the start of PCI/ISA I/O space. - * It is basically a cut-down version of pci_process_bridge_OF_ranges. - */ -static unsigned long chrp_find_phys_io_base(void) -{ - struct device_node *node; - unsigned int *ranges; - unsigned long base = CHRP_ISA_IO_BASE; - int rlen = 0; - int np; - - node = find_devices("isa"); - if (node != NULL) { - node = node->parent; - if (node == NULL || node->type == NULL - || strcmp(node->type, "pci") != 0) - node = NULL; - } - if (node == NULL) - node = find_devices("pci"); - if (node == NULL) - return base; - - ranges = (unsigned int *) get_property(node, "ranges", &rlen); - np = prom_n_addr_cells(node) + 5; - while ((rlen -= np * sizeof(unsigned int)) >= 0) { - if ((ranges[0] >> 24) == 1 && ranges[2] == 0) { - /* I/O space starting at 0, grab the phys base */ - base = ranges[np - 3]; - break; - } - ranges += np; - } - return base; -} -#endif /* CONFIG_PPC_CHRP */ - -void xmon_map_scc(void) -{ -#ifdef CONFIG_PPC_MULTIPLATFORM - volatile unsigned char __iomem *base; - - if (_machine == _MACH_Pmac) { - struct device_node *np; - unsigned long addr; -#ifdef CONFIG_BOOTX_TEXT - if (!use_screen && !use_serial - && !machine_is_compatible("iMac")) { - /* see if there is a keyboard in the device tree - with a parent of type "adb" */ - for (np = find_devices("keyboard"); np; np = np->next) - if (np->parent && np->parent->type - && strcmp(np->parent->type, "adb") == 0) - break; - - /* needs to be hacked if xmon_printk is to be used - from within find_via_pmu() */ -#ifdef CONFIG_ADB_PMU - if (np != NULL && boot_text_mapped && find_via_pmu()) - use_screen = 1; -#endif -#ifdef CONFIG_ADB_CUDA - if (np != NULL && boot_text_mapped && find_via_cuda()) - use_screen = 1; -#endif - } - if (!use_screen && (np = find_devices("escc")) != NULL) { - /* - * look for the device node for the serial port - * we're using and see if it says it has a modem - */ - char *name = xmon_use_sccb? "ch-b": "ch-a"; - char *slots; - int l; - - np = np->child; - while (np != NULL && strcmp(np->name, name) != 0) - np = np->sibling; - if (np != NULL) { - /* XXX should parse this properly */ - channel_node = np; - slots = get_property(np, "slot-names", &l); - if (slots != NULL && l >= 10 - && strcmp(slots+4, "Modem") == 0) - via_modem = 1; - } - } - btext_drawstring("xmon uses "); - if (use_screen) - btext_drawstring("screen and keyboard\n"); - else { - if (via_modem) - btext_drawstring("modem on "); - btext_drawstring(xmon_use_sccb? "printer": "modem"); - btext_drawstring(" port\n"); - } - -#endif /* CONFIG_BOOTX_TEXT */ - -#ifdef CHRP_ESCC - addr = 0xc1013020; -#else - addr = 0xf3013020; -#endif - TXRDY = 4; - RXRDY = 1; - - np = find_devices("mac-io"); - if (np && np->n_addrs) - addr = np->addrs[0].address + 0x13020; - base = (volatile unsigned char *) ioremap(addr & PAGE_MASK, PAGE_SIZE); - sccc = base + (addr & ~PAGE_MASK); - sccd = sccc + 0x10; - - } else { - base = (volatile unsigned char *) isa_io_base; - -#ifdef CONFIG_PPC_CHRP - if (_machine == _MACH_chrp) - base = (volatile unsigned char __iomem *) - ioremap(chrp_find_phys_io_base(), 0x1000); -#endif - - sccc = base + 0x3fd; - sccd = base + 0x3f8; - if (xmon_use_sccb) { - sccc -= 0x100; - sccd -= 0x100; - } - TXRDY = 0x20; - RXRDY = 1; - DLAB = 0x80; - } -#elif defined(CONFIG_GEMINI) - /* should already be mapped by the kernel boot */ - sccc = (volatile unsigned char __iomem *) 0xffeffb0d; - sccd = (volatile unsigned char __iomem *) 0xffeffb08; - TXRDY = 0x20; - RXRDY = 1; - DLAB = 0x80; -#elif defined(CONFIG_405GP) - sccc = (volatile unsigned char __iomem *)0xef600305; - sccd = (volatile unsigned char __iomem *)0xef600300; - TXRDY = 0x20; - RXRDY = 1; - DLAB = 0x80; -#endif /* platform */ -} - -static int scc_initialized = 0; - -void xmon_init_scc(void); -extern void cuda_poll(void); - -static inline void do_poll_adb(void) -{ -#ifdef CONFIG_ADB_PMU - if (sys_ctrler == SYS_CTRLER_PMU) - pmu_poll_adb(); -#endif /* CONFIG_ADB_PMU */ -#ifdef CONFIG_ADB_CUDA - if (sys_ctrler == SYS_CTRLER_CUDA) - cuda_poll(); -#endif /* CONFIG_ADB_CUDA */ -} - -int xmon_write(void *ptr, int nb) -{ - char *p = ptr; - int i, c, ct; - -#ifdef CONFIG_SMP - static unsigned long xmon_write_lock; - int lock_wait = 1000000; - int locked; - - while ((locked = test_and_set_bit(0, &xmon_write_lock)) != 0) - if (--lock_wait == 0) - break; -#endif - -#ifdef CONFIG_BOOTX_TEXT - if (use_screen) { - /* write it on the screen */ - for (i = 0; i < nb; ++i) - btext_drawchar(*p++); - goto out; - } -#endif - if (!scc_initialized) - xmon_init_scc(); - ct = 0; - for (i = 0; i < nb; ++i) { - while ((*sccc & TXRDY) == 0) - do_poll_adb(); - c = p[i]; - if (c == '\n' && !ct) { - c = '\r'; - ct = 1; - --i; - } else { - ct = 0; - } - buf_access(); - *sccd = c; - eieio(); - } - - out: -#ifdef CONFIG_SMP - if (!locked) - clear_bit(0, &xmon_write_lock); -#endif - return nb; -} - -int xmon_wants_key; -int xmon_adb_keycode; - -#ifdef CONFIG_BOOTX_TEXT -static int xmon_adb_shiftstate; - -static unsigned char xmon_keytab[128] = - "asdfhgzxcv\000bqwer" /* 0x00 - 0x0f */ - "yt123465=97-80]o" /* 0x10 - 0x1f */ - "u[ip\rlj'k;\\,/nm." /* 0x20 - 0x2f */ - "\t `\177\0\033\0\0\0\0\0\0\0\0\0\0" /* 0x30 - 0x3f */ - "\0.\0*\0+\0\0\0\0\0/\r\0-\0" /* 0x40 - 0x4f */ - "\0\0000123456789\0\0\0"; /* 0x50 - 0x5f */ - -static unsigned char xmon_shift_keytab[128] = - "ASDFHGZXCV\000BQWER" /* 0x00 - 0x0f */ - "YT!@#$^%+(&_*)}O" /* 0x10 - 0x1f */ - "U{IP\rLJ\"K:|" /* 0x20 - 0x2f */ - "\t ~\177\0\033\0\0\0\0\0\0\0\0\0\0" /* 0x30 - 0x3f */ - "\0.\0*\0+\0\0\0\0\0/\r\0-\0" /* 0x40 - 0x4f */ - "\0\0000123456789\0\0\0"; /* 0x50 - 0x5f */ - -static int xmon_get_adb_key(void) -{ - int k, t, on; - - xmon_wants_key = 1; - for (;;) { - xmon_adb_keycode = -1; - t = 0; - on = 0; - do { - if (--t < 0) { - on = 1 - on; - btext_drawchar(on? 0xdb: 0x20); - btext_drawchar('\b'); - t = 200000; - } - do_poll_adb(); - } while (xmon_adb_keycode == -1); - k = xmon_adb_keycode; - if (on) - btext_drawstring(" \b"); - - /* test for shift keys */ - if ((k & 0x7f) == 0x38 || (k & 0x7f) == 0x7b) { - xmon_adb_shiftstate = (k & 0x80) == 0; - continue; - } - if (k >= 0x80) - continue; /* ignore up transitions */ - k = (xmon_adb_shiftstate? xmon_shift_keytab: xmon_keytab)[k]; - if (k != 0) - break; - } - xmon_wants_key = 0; - return k; -} -#endif /* CONFIG_BOOTX_TEXT */ - -int xmon_readchar(void) -{ -#ifdef CONFIG_BOOTX_TEXT - if (use_screen) - return xmon_get_adb_key(); -#endif - if (!scc_initialized) - xmon_init_scc(); - while ((*sccc & RXRDY) == 0) - do_poll_adb(); - buf_access(); - return *sccd; -} - -int xmon_read_poll(void) -{ - if ((*sccc & RXRDY) == 0) { - do_poll_adb(); - return -1; - } - buf_access(); - return *sccd; -} - -static unsigned char scc_inittab[] = { - 13, 0, /* set baud rate divisor */ - 12, 1, - 14, 1, /* baud rate gen enable, src=rtxc */ - 11, 0x50, /* clocks = br gen */ - 5, 0xea, /* tx 8 bits, assert DTR & RTS */ - 4, 0x46, /* x16 clock, 1 stop */ - 3, 0xc1, /* rx enable, 8 bits */ -}; - -void xmon_init_scc(void) -{ - if ( _machine == _MACH_chrp ) - { - sccd[3] = 0x83; eieio(); /* LCR = 8N1 + DLAB */ - sccd[0] = 12; eieio(); /* DLL = 9600 baud */ - sccd[1] = 0; eieio(); - sccd[2] = 0; eieio(); /* FCR = 0 */ - sccd[3] = 3; eieio(); /* LCR = 8N1 */ - sccd[1] = 0; eieio(); /* IER = 0 */ - } - else if ( _machine == _MACH_Pmac ) - { - int i, x; - unsigned long timeout; - - if (channel_node != 0) - pmac_call_feature( - PMAC_FTR_SCC_ENABLE, - channel_node, - PMAC_SCC_ASYNC | PMAC_SCC_FLAG_XMON, 1); - printk(KERN_INFO "Serial port locked ON by debugger !\n"); - if (via_modem && channel_node != 0) { - unsigned int t0; - - pmac_call_feature( - PMAC_FTR_MODEM_ENABLE, - channel_node, 0, 1); - printk(KERN_INFO "Modem powered up by debugger !\n"); - t0 = get_tbl(); - timeout = 3 * tb_ticks_per_sec; - if (timeout == 0) - /* assume 25MHz if tb_ticks_per_sec not set */ - timeout = 75000000; - while (get_tbl() - t0 < timeout) - eieio(); - } - /* use the B channel if requested */ - if (xmon_use_sccb) { - sccc = (volatile unsigned char *) - ((unsigned long)sccc & ~0x20); - sccd = sccc + 0x10; - } - for (i = 20000; i != 0; --i) { - x = *sccc; eieio(); - } - *sccc = 9; eieio(); /* reset A or B side */ - *sccc = ((unsigned long)sccc & 0x20)? 0x80: 0x40; eieio(); - for (i = 0; i < sizeof(scc_inittab); ++i) { - *sccc = scc_inittab[i]; - eieio(); - } - } - scc_initialized = 1; - if (via_modem) { - for (;;) { - xmon_write("ATE1V1\r", 7); - if (xmon_expect("OK", 5)) { - xmon_write("ATA\r", 4); - if (xmon_expect("CONNECT", 40)) - break; - } - xmon_write("+++", 3); - xmon_expect("OK", 3); - } - } -} - -void xmon_enter(void) -{ -#ifdef CONFIG_ADB_PMU - if (_machine == _MACH_Pmac) { - pmu_suspend(); - } -#endif -} - -void xmon_leave(void) -{ -#ifdef CONFIG_ADB_PMU - if (_machine == _MACH_Pmac) { - pmu_resume(); - } -#endif -} diff --git a/arch/powerpc/xmon/start_64.c b/arch/powerpc/xmon/start_64.c deleted file mode 100644 index 712552c..0000000 --- a/arch/powerpc/xmon/start_64.c +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 1996 Paul Mackerras. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - */ -#include -#include -#include "nonstdio.h" - -void xmon_map_scc(void) -{ -} - -int xmon_write(void *ptr, int nb) -{ - return udbg_write(ptr, nb); -} - -int xmon_readchar(void) -{ - if (udbg_getc) - return udbg_getc(); - return -1; -} - -int xmon_read_poll(void) -{ - if (udbg_getc_poll) - return udbg_getc_poll(); - return -1; -} diff --git a/arch/powerpc/xmon/start_8xx.c b/arch/powerpc/xmon/start_8xx.c deleted file mode 100644 index 4c17b04..0000000 --- a/arch/powerpc/xmon/start_8xx.c +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 1996 Paul Mackerras. - * Copyright (C) 2000 Dan Malek. - * Quick hack of Paul's code to make XMON work on 8xx processors. Lots - * of assumptions, like the SMC1 is used, it has been initialized by the - * loader at some point, and we can just stuff and suck bytes. - * We rely upon the 8xx uart driver to support us, as the interface - * changes between boot up and operational phases of the kernel. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include "nonstdio.h" - -extern int xmon_8xx_write(char *str, int nb); -extern int xmon_8xx_read_poll(void); -extern int xmon_8xx_read_char(void); - -void xmon_map_scc(void) -{ - cpmp = (cpm8xx_t *)&(((immap_t *)IMAP_ADDR)->im_cpm); -} - -void xmon_init_scc(void); - -int xmon_write(void *ptr, int nb) -{ - return(xmon_8xx_write(ptr, nb)); -} - -int xmon_readchar(void) -{ - return xmon_8xx_read_char(); -} - -int xmon_read_poll(void) -{ - return(xmon_8xx_read_poll()); -} diff --git a/arch/ppc/boot/images/Makefile b/arch/ppc/boot/images/Makefile index 532e7ef..58415d5 100644 --- a/arch/ppc/boot/images/Makefile +++ b/arch/ppc/boot/images/Makefile @@ -26,7 +26,7 @@ quiet_cmd_uimage = UIMAGE $@ targets += uImage $(obj)/uImage: $(obj)/vmlinux.gz $(Q)rm -f $@ - $(call if_changed,uimage) + $(call cmd,uimage) @echo -n ' Image: $@ ' @if [ -f $@ ]; then echo 'is ready' ; else echo 'not made'; fi diff --git a/arch/ppc/kernel/Makefile b/arch/ppc/kernel/Makefile index 0bb23fc..e6c1d61 100644 --- a/arch/ppc/kernel/Makefile +++ b/arch/ppc/kernel/Makefile @@ -49,5 +49,4 @@ obj-$(CONFIG_TAU) += temp.o ifndef CONFIG_E200 obj-$(CONFIG_FSL_BOOKE) += perfmon_fsl_booke.o endif -obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o endif diff --git a/arch/ppc/kernel/asm-offsets.c b/arch/ppc/kernel/asm-offsets.c index fe0e767..7964bf6 100644 --- a/arch/ppc/kernel/asm-offsets.c +++ b/arch/ppc/kernel/asm-offsets.c @@ -131,7 +131,7 @@ main(void) DEFINE(CPU_SPEC_FEATURES, offsetof(struct cpu_spec, cpu_features)); DEFINE(CPU_SPEC_SETUP, offsetof(struct cpu_spec, cpu_setup)); - DEFINE(TI_SC_NOERR, offsetof(struct thread_info, syscall_noerror)); + DEFINE(TI_SIGFRAME, offsetof(struct thread_info, nvgprs_frame)); DEFINE(TI_TASK, offsetof(struct thread_info, task)); DEFINE(TI_EXECDOMAIN, offsetof(struct thread_info, exec_domain)); DEFINE(TI_FLAGS, offsetof(struct thread_info, flags)); diff --git a/arch/ppc/kernel/entry.S b/arch/ppc/kernel/entry.S index f044edb..a48b950 100644 --- a/arch/ppc/kernel/entry.S +++ b/arch/ppc/kernel/entry.S @@ -200,8 +200,6 @@ _GLOBAL(DoSyscall) bl do_show_syscall #endif /* SHOW_SYSCALLS */ rlwinm r10,r1,0,0,18 /* current_thread_info() */ - li r11,0 - stb r11,TI_SC_NOERR(r10) lwz r11,TI_FLAGS(r10) andi. r11,r11,_TIF_SYSCALL_T_OR_A bne- syscall_dotrace @@ -222,25 +220,21 @@ ret_from_syscall: bl do_show_syscall_exit #endif mr r6,r3 - li r11,-_LAST_ERRNO - cmplw 0,r3,r11 rlwinm r12,r1,0,0,18 /* current_thread_info() */ - blt+ 30f - lbz r11,TI_SC_NOERR(r12) - cmpwi r11,0 - bne 30f - neg r3,r3 - lwz r10,_CCR(r1) /* Set SO bit in CR */ - oris r10,r10,0x1000 - stw r10,_CCR(r1) - /* disable interrupts so current_thread_info()->flags can't change */ -30: LOAD_MSR_KERNEL(r10,MSR_KERNEL) /* doesn't include MSR_EE */ + LOAD_MSR_KERNEL(r10,MSR_KERNEL) /* doesn't include MSR_EE */ SYNC MTMSRD(r10) lwz r9,TI_FLAGS(r12) - andi. r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SIGPENDING|_TIF_NEED_RESCHED) + li r8,-_LAST_ERRNO + andi. r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SIGPENDING|_TIF_NEED_RESCHED|_TIF_RESTOREALL) bne- syscall_exit_work + cmplw 0,r3,r8 + blt+ syscall_exit_cont + lwz r11,_CCR(r1) /* Load CR */ + neg r3,r3 + oris r11,r11,0x1000 /* Set SO bit in CR */ + stw r11,_CCR(r1) syscall_exit_cont: #if defined(CONFIG_4xx) || defined(CONFIG_BOOKE) /* If the process has its own DBCR0 value, load it up. The single @@ -292,46 +286,113 @@ syscall_dotrace: b syscall_dotrace_cont syscall_exit_work: - stw r6,RESULT(r1) /* Save result */ + andi. r0,r9,_TIF_RESTOREALL + bne- 2f + cmplw 0,r3,r8 + blt+ 1f + andi. r0,r9,_TIF_NOERROR + bne- 1f + lwz r11,_CCR(r1) /* Load CR */ + neg r3,r3 + oris r11,r11,0x1000 /* Set SO bit in CR */ + stw r11,_CCR(r1) + +1: stw r6,RESULT(r1) /* Save result */ stw r3,GPR3(r1) /* Update return value */ - andi. r0,r9,_TIF_SYSCALL_T_OR_A - beq 5f - ori r10,r10,MSR_EE - SYNC - MTMSRD(r10) /* re-enable interrupts */ +2: andi. r0,r9,(_TIF_PERSYSCALL_MASK) + beq 4f + + /* Clear per-syscall TIF flags if any are set, but _leave_ + _TIF_SAVE_NVGPRS set in r9 since we haven't dealt with that + yet. */ + + li r11,_TIF_PERSYSCALL_MASK + addi r12,r12,TI_FLAGS +3: lwarx r8,0,r12 + andc r8,r8,r11 +#ifdef CONFIG_IBM405_ERR77 + dcbt 0,r12 +#endif + stwcx. r8,0,r12 + bne- 3b + subi r12,r12,TI_FLAGS + +4: /* Anything which requires enabling interrupts? */ + andi. r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP|_TIF_SAVE_NVGPRS) + beq 7f + + /* Save NVGPRS if they're not saved already */ lwz r4,TRAP(r1) andi. r4,r4,1 - beq 4f + beq 5f SAVE_NVGPRS(r1) li r4,0xc00 stw r4,TRAP(r1) -4: + + /* Re-enable interrupts */ +5: ori r10,r10,MSR_EE + SYNC + MTMSRD(r10) + + andi. r0,r9,_TIF_SAVE_NVGPRS + bne save_user_nvgprs + +save_user_nvgprs_cont: + andi. r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP) + beq 7f + addi r3,r1,STACK_FRAME_OVERHEAD bl do_syscall_trace_leave REST_NVGPRS(r1) -2: - lwz r3,GPR3(r1) + +6: lwz r3,GPR3(r1) LOAD_MSR_KERNEL(r10,MSR_KERNEL) /* doesn't include MSR_EE */ SYNC MTMSRD(r10) /* disable interrupts again */ rlwinm r12,r1,0,0,18 /* current_thread_info() */ lwz r9,TI_FLAGS(r12) -5: +7: andi. r0,r9,_TIF_NEED_RESCHED - bne 1f + bne 8f lwz r5,_MSR(r1) andi. r5,r5,MSR_PR - beq syscall_exit_cont + beq ret_from_except andi. r0,r9,_TIF_SIGPENDING - beq syscall_exit_cont + beq ret_from_except b do_user_signal -1: +8: ori r10,r10,MSR_EE SYNC MTMSRD(r10) /* re-enable interrupts */ bl schedule - b 2b + b 6b + +save_user_nvgprs: + lwz r8,TI_SIGFRAME(r12) + +.macro savewords start, end + 1: stw \start,4*(\start)(r8) + .section __ex_table,"a" + .align 2 + .long 1b,save_user_nvgprs_fault + .previous + .if \end - \start + savewords "(\start+1)",\end + .endif +.endm + savewords 14,31 + b save_user_nvgprs_cont + + +save_user_nvgprs_fault: + li r3,11 /* SIGSEGV */ + lwz r4,TI_TASK(r12) + bl force_sigsegv + rlwinm r12,r1,0,0,18 /* current_thread_info() */ + lwz r9,TI_FLAGS(r12) + b save_user_nvgprs_cont + #ifdef SHOW_SYSCALLS do_show_syscall: #ifdef SHOW_SYSCALLS_TASK @@ -401,28 +462,10 @@ show_syscalls_task: #endif /* SHOW_SYSCALLS */ /* - * The sigsuspend and rt_sigsuspend system calls can call do_signal - * and thus put the process into the stopped state where we might - * want to examine its user state with ptrace. Therefore we need - * to save all the nonvolatile registers (r13 - r31) before calling - * the C code. + * The fork/clone functions need to copy the full register set into + * the child process. Therefore we need to save all the nonvolatile + * registers (r13 - r31) before calling the C code. */ - .globl ppc_sigsuspend -ppc_sigsuspend: - SAVE_NVGPRS(r1) - lwz r0,TRAP(r1) - rlwinm r0,r0,0,0,30 /* clear LSB to indicate full */ - stw r0,TRAP(r1) /* register set saved */ - b sys_sigsuspend - - .globl ppc_rt_sigsuspend -ppc_rt_sigsuspend: - SAVE_NVGPRS(r1) - lwz r0,TRAP(r1) - rlwinm r0,r0,0,0,30 - stw r0,TRAP(r1) - b sys_rt_sigsuspend - .globl ppc_fork ppc_fork: SAVE_NVGPRS(r1) @@ -447,14 +490,6 @@ ppc_clone: stw r0,TRAP(r1) /* register set saved */ b sys_clone - .globl ppc_swapcontext -ppc_swapcontext: - SAVE_NVGPRS(r1) - lwz r0,TRAP(r1) - rlwinm r0,r0,0,0,30 /* clear LSB to indicate full */ - stw r0,TRAP(r1) /* register set saved */ - b sys_swapcontext - /* * Top-level page fault handling. * This is in assembler because if do_page_fault tells us that @@ -626,16 +661,6 @@ END_FTR_SECTION_IFSET(CPU_FTR_601) .long ret_from_except #endif - .globl sigreturn_exit -sigreturn_exit: - subi r1,r3,STACK_FRAME_OVERHEAD - rlwinm r12,r1,0,0,18 /* current_thread_info() */ - lwz r9,TI_FLAGS(r12) - andi. r0,r9,_TIF_SYSCALL_T_OR_A - beq+ ret_from_except_full - bl do_syscall_trace_leave - /* fall through */ - .globl ret_from_except_full ret_from_except_full: REST_NVGPRS(r1) @@ -658,7 +683,7 @@ user_exc_return: /* r10 contains MSR_KE /* Check current_thread_info()->flags */ rlwinm r9,r1,0,0,18 lwz r9,TI_FLAGS(r9) - andi. r0,r9,(_TIF_SIGPENDING|_TIF_NEED_RESCHED) + andi. r0,r9,(_TIF_SIGPENDING|_TIF_NEED_RESCHED|_TIF_RESTOREALL) bne do_work restore_user: diff --git a/arch/ppc/kernel/misc.S b/arch/ppc/kernel/misc.S index 5e61124..fb5658b 100644 --- a/arch/ppc/kernel/misc.S +++ b/arch/ppc/kernel/misc.S @@ -1197,7 +1197,7 @@ _GLOBAL(sys_call_table) .long sys_ssetmask .long sys_setreuid /* 70 */ .long sys_setregid - .long ppc_sigsuspend + .long sys_sigsuspend .long sys_sigpending .long sys_sethostname .long sys_setrlimit /* 75 */ @@ -1303,7 +1303,7 @@ _GLOBAL(sys_call_table) .long sys_rt_sigpending /* 175 */ .long sys_rt_sigtimedwait .long sys_rt_sigqueueinfo - .long ppc_rt_sigsuspend + .long sys_rt_sigsuspend .long sys_pread64 .long sys_pwrite64 /* 180 */ .long sys_chown @@ -1374,7 +1374,7 @@ _GLOBAL(sys_call_table) .long sys_clock_gettime .long sys_clock_getres .long sys_clock_nanosleep - .long ppc_swapcontext + .long sys_swapcontext .long sys_tgkill /* 250 */ .long sys_utimes .long sys_statfs64 diff --git a/arch/ppc/kernel/pci.c b/arch/ppc/kernel/pci.c index f7fae5f..0aa1841 100644 --- a/arch/ppc/kernel/pci.c +++ b/arch/ppc/kernel/pci.c @@ -815,8 +815,7 @@ EXPORT_SYMBOL(pci_device_to_OF_node); * to set pci_assign_all_buses to 1 and still use RTAS for PCI * config cycles. */ -struct pci_controller* -pci_find_hose_for_OF_device(struct device_node* node) +struct pci_controller* pci_find_hose_for_OF_device(struct device_node* node) { if (!have_of) return NULL; diff --git a/arch/ppc/kernel/ppc_ksyms.c b/arch/ppc/kernel/ppc_ksyms.c index bb6a5c6..95075f9 100644 --- a/arch/ppc/kernel/ppc_ksyms.c +++ b/arch/ppc/kernel/ppc_ksyms.c @@ -82,10 +82,6 @@ EXPORT_SYMBOL(ppc_n_lost_interrupts); EXPORT_SYMBOL(ISA_DMA_THRESHOLD); EXPORT_SYMBOL(DMA_MODE_READ); EXPORT_SYMBOL(DMA_MODE_WRITE); -#if defined(CONFIG_PPC_PREP) -EXPORT_SYMBOL(_prep_type); -EXPORT_SYMBOL(ucSystemType); -#endif #if !defined(__INLINE_BITOPS) EXPORT_SYMBOL(set_bit); @@ -311,7 +307,6 @@ EXPORT_SYMBOL(__res); EXPORT_SYMBOL(next_mmu_context); EXPORT_SYMBOL(set_context); -EXPORT_SYMBOL_GPL(__handle_mm_fault); /* For MOL */ EXPORT_SYMBOL(disarm_decr); #ifdef CONFIG_PPC_STD_MMU extern long mol_trampoline; diff --git a/arch/ppc/kernel/setup.c b/arch/ppc/kernel/setup.c index 0eb0b70..e707c6f 100644 --- a/arch/ppc/kernel/setup.c +++ b/arch/ppc/kernel/setup.c @@ -744,6 +744,9 @@ void __init setup_arch(char **cmdline_p) /* so udelay does something sensible, assume <= 1000 bogomips */ loops_per_jiffy = 500000000 / HZ; + if (ppc_md.init_early) + ppc_md.init_early(); + #ifdef CONFIG_PPC_MULTIPLATFORM /* This could be called "early setup arch", it must be done * now because xmon need it diff --git a/arch/ppc/platforms/prep_setup.c b/arch/ppc/platforms/prep_setup.c index 4415748..a6a1889 100644 --- a/arch/ppc/platforms/prep_setup.c +++ b/arch/ppc/platforms/prep_setup.c @@ -72,7 +72,6 @@ TODC_ALLOC(); -unsigned char ucSystemType; unsigned char ucBoardRev; unsigned char ucBoardRevMaj, ucBoardRevMin; diff --git a/arch/ppc/syslib/Makefile b/arch/ppc/syslib/Makefile index 5b7f2b8..84ef030 100644 --- a/arch/ppc/syslib/Makefile +++ b/arch/ppc/syslib/Makefile @@ -96,7 +96,7 @@ ifeq ($(CONFIG_85xx),y) obj-$(CONFIG_PCI) += pci_auto.o endif obj-$(CONFIG_RAPIDIO) += ppc85xx_rio.o -obj-$(CONFIG_83xx) += ipic.o ppc83xx_setup.o ppc_sys.o \ +obj-$(CONFIG_83xx) += ppc83xx_setup.o ppc_sys.o \ mpc83xx_sys.o mpc83xx_devices.o ifeq ($(CONFIG_83xx),y) obj-$(CONFIG_PCI) += pci_auto.o diff --git a/arch/ppc/syslib/ipic.c b/arch/ppc/syslib/ipic.c deleted file mode 100644 index 8f01e0f..0000000 --- a/arch/ppc/syslib/ipic.c +++ /dev/null @@ -1,646 +0,0 @@ -/* - * include/asm-ppc/ipic.c - * - * IPIC routines implementations. - * - * Copyright 2005 Freescale Semiconductor, Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ipic.h" - -static struct ipic p_ipic; -static struct ipic * primary_ipic; - -static struct ipic_info ipic_info[] = { - [9] = { - .pend = IPIC_SIPNR_H, - .mask = IPIC_SIMSR_H, - .prio = IPIC_SIPRR_D, - .force = IPIC_SIFCR_H, - .bit = 24, - .prio_mask = 0, - }, - [10] = { - .pend = IPIC_SIPNR_H, - .mask = IPIC_SIMSR_H, - .prio = IPIC_SIPRR_D, - .force = IPIC_SIFCR_H, - .bit = 25, - .prio_mask = 1, - }, - [11] = { - .pend = IPIC_SIPNR_H, - .mask = IPIC_SIMSR_H, - .prio = IPIC_SIPRR_D, - .force = IPIC_SIFCR_H, - .bit = 26, - .prio_mask = 2, - }, - [14] = { - .pend = IPIC_SIPNR_H, - .mask = IPIC_SIMSR_H, - .prio = IPIC_SIPRR_D, - .force = IPIC_SIFCR_H, - .bit = 29, - .prio_mask = 5, - }, - [15] = { - .pend = IPIC_SIPNR_H, - .mask = IPIC_SIMSR_H, - .prio = IPIC_SIPRR_D, - .force = IPIC_SIFCR_H, - .bit = 30, - .prio_mask = 6, - }, - [16] = { - .pend = IPIC_SIPNR_H, - .mask = IPIC_SIMSR_H, - .prio = IPIC_SIPRR_D, - .force = IPIC_SIFCR_H, - .bit = 31, - .prio_mask = 7, - }, - [17] = { - .pend = IPIC_SEPNR, - .mask = IPIC_SEMSR, - .prio = IPIC_SMPRR_A, - .force = IPIC_SEFCR, - .bit = 1, - .prio_mask = 5, - }, - [18] = { - .pend = IPIC_SEPNR, - .mask = IPIC_SEMSR, - .prio = IPIC_SMPRR_A, - .force = IPIC_SEFCR, - .bit = 2, - .prio_mask = 6, - }, - [19] = { - .pend = IPIC_SEPNR, - .mask = IPIC_SEMSR, - .prio = IPIC_SMPRR_A, - .force = IPIC_SEFCR, - .bit = 3, - .prio_mask = 7, - }, - [20] = { - .pend = IPIC_SEPNR, - .mask = IPIC_SEMSR, - .prio = IPIC_SMPRR_B, - .force = IPIC_SEFCR, - .bit = 4, - .prio_mask = 4, - }, - [21] = { - .pend = IPIC_SEPNR, - .mask = IPIC_SEMSR, - .prio = IPIC_SMPRR_B, - .force = IPIC_SEFCR, - .bit = 5, - .prio_mask = 5, - }, - [22] = { - .pend = IPIC_SEPNR, - .mask = IPIC_SEMSR, - .prio = IPIC_SMPRR_B, - .force = IPIC_SEFCR, - .bit = 6, - .prio_mask = 6, - }, - [23] = { - .pend = IPIC_SEPNR, - .mask = IPIC_SEMSR, - .prio = IPIC_SMPRR_B, - .force = IPIC_SEFCR, - .bit = 7, - .prio_mask = 7, - }, - [32] = { - .pend = IPIC_SIPNR_H, - .mask = IPIC_SIMSR_H, - .prio = IPIC_SIPRR_A, - .force = IPIC_SIFCR_H, - .bit = 0, - .prio_mask = 0, - }, - [33] = { - .pend = IPIC_SIPNR_H, - .mask = IPIC_SIMSR_H, - .prio = IPIC_SIPRR_A, - .force = IPIC_SIFCR_H, - .bit = 1, - .prio_mask = 1, - }, - [34] = { - .pend = IPIC_SIPNR_H, - .mask = IPIC_SIMSR_H, - .prio = IPIC_SIPRR_A, - .force = IPIC_SIFCR_H, - .bit = 2, - .prio_mask = 2, - }, - [35] = { - .pend = IPIC_SIPNR_H, - .mask = IPIC_SIMSR_H, - .prio = IPIC_SIPRR_A, - .force = IPIC_SIFCR_H, - .bit = 3, - .prio_mask = 3, - }, - [36] = { - .pend = IPIC_SIPNR_H, - .mask = IPIC_SIMSR_H, - .prio = IPIC_SIPRR_A, - .force = IPIC_SIFCR_H, - .bit = 4, - .prio_mask = 4, - }, - [37] = { - .pend = IPIC_SIPNR_H, - .mask = IPIC_SIMSR_H, - .prio = IPIC_SIPRR_A, - .force = IPIC_SIFCR_H, - .bit = 5, - .prio_mask = 5, - }, - [38] = { - .pend = IPIC_SIPNR_H, - .mask = IPIC_SIMSR_H, - .prio = IPIC_SIPRR_A, - .force = IPIC_SIFCR_H, - .bit = 6, - .prio_mask = 6, - }, - [39] = { - .pend = IPIC_SIPNR_H, - .mask = IPIC_SIMSR_H, - .prio = IPIC_SIPRR_A, - .force = IPIC_SIFCR_H, - .bit = 7, - .prio_mask = 7, - }, - [48] = { - .pend = IPIC_SEPNR, - .mask = IPIC_SEMSR, - .prio = IPIC_SMPRR_A, - .force = IPIC_SEFCR, - .bit = 0, - .prio_mask = 4, - }, - [64] = { - .pend = IPIC_SIPNR_H, - .mask = IPIC_SIMSR_L, - .prio = IPIC_SMPRR_A, - .force = IPIC_SIFCR_L, - .bit = 0, - .prio_mask = 0, - }, - [65] = { - .pend = IPIC_SIPNR_H, - .mask = IPIC_SIMSR_L, - .prio = IPIC_SMPRR_A, - .force = IPIC_SIFCR_L, - .bit = 1, - .prio_mask = 1, - }, - [66] = { - .pend = IPIC_SIPNR_H, - .mask = IPIC_SIMSR_L, - .prio = IPIC_SMPRR_A, - .force = IPIC_SIFCR_L, - .bit = 2, - .prio_mask = 2, - }, - [67] = { - .pend = IPIC_SIPNR_H, - .mask = IPIC_SIMSR_L, - .prio = IPIC_SMPRR_A, - .force = IPIC_SIFCR_L, - .bit = 3, - .prio_mask = 3, - }, - [68] = { - .pend = IPIC_SIPNR_H, - .mask = IPIC_SIMSR_L, - .prio = IPIC_SMPRR_B, - .force = IPIC_SIFCR_L, - .bit = 4, - .prio_mask = 0, - }, - [69] = { - .pend = IPIC_SIPNR_H, - .mask = IPIC_SIMSR_L, - .prio = IPIC_SMPRR_B, - .force = IPIC_SIFCR_L, - .bit = 5, - .prio_mask = 1, - }, - [70] = { - .pend = IPIC_SIPNR_H, - .mask = IPIC_SIMSR_L, - .prio = IPIC_SMPRR_B, - .force = IPIC_SIFCR_L, - .bit = 6, - .prio_mask = 2, - }, - [71] = { - .pend = IPIC_SIPNR_H, - .mask = IPIC_SIMSR_L, - .prio = IPIC_SMPRR_B, - .force = IPIC_SIFCR_L, - .bit = 7, - .prio_mask = 3, - }, - [72] = { - .pend = IPIC_SIPNR_H, - .mask = IPIC_SIMSR_L, - .prio = 0, - .force = IPIC_SIFCR_L, - .bit = 8, - }, - [73] = { - .pend = IPIC_SIPNR_H, - .mask = IPIC_SIMSR_L, - .prio = 0, - .force = IPIC_SIFCR_L, - .bit = 9, - }, - [74] = { - .pend = IPIC_SIPNR_H, - .mask = IPIC_SIMSR_L, - .prio = 0, - .force = IPIC_SIFCR_L, - .bit = 10, - }, - [75] = { - .pend = IPIC_SIPNR_H, - .mask = IPIC_SIMSR_L, - .prio = 0, - .force = IPIC_SIFCR_L, - .bit = 11, - }, - [76] = { - .pend = IPIC_SIPNR_H, - .mask = IPIC_SIMSR_L, - .prio = 0, - .force = IPIC_SIFCR_L, - .bit = 12, - }, - [77] = { - .pend = IPIC_SIPNR_H, - .mask = IPIC_SIMSR_L, - .prio = 0, - .force = IPIC_SIFCR_L, - .bit = 13, - }, - [78] = { - .pend = IPIC_SIPNR_H, - .mask = IPIC_SIMSR_L, - .prio = 0, - .force = IPIC_SIFCR_L, - .bit = 14, - }, - [79] = { - .pend = IPIC_SIPNR_H, - .mask = IPIC_SIMSR_L, - .prio = 0, - .force = IPIC_SIFCR_L, - .bit = 15, - }, - [80] = { - .pend = IPIC_SIPNR_H, - .mask = IPIC_SIMSR_L, - .prio = 0, - .force = IPIC_SIFCR_L, - .bit = 16, - }, - [84] = { - .pend = IPIC_SIPNR_H, - .mask = IPIC_SIMSR_L, - .prio = 0, - .force = IPIC_SIFCR_L, - .bit = 20, - }, - [85] = { - .pend = IPIC_SIPNR_H, - .mask = IPIC_SIMSR_L, - .prio = 0, - .force = IPIC_SIFCR_L, - .bit = 21, - }, - [90] = { - .pend = IPIC_SIPNR_H, - .mask = IPIC_SIMSR_L, - .prio = 0, - .force = IPIC_SIFCR_L, - .bit = 26, - }, - [91] = { - .pend = IPIC_SIPNR_H, - .mask = IPIC_SIMSR_L, - .prio = 0, - .force = IPIC_SIFCR_L, - .bit = 27, - }, -}; - -static inline u32 ipic_read(volatile u32 __iomem *base, unsigned int reg) -{ - return in_be32(base + (reg >> 2)); -} - -static inline void ipic_write(volatile u32 __iomem *base, unsigned int reg, u32 value) -{ - out_be32(base + (reg >> 2), value); -} - -static inline struct ipic * ipic_from_irq(unsigned int irq) -{ - return primary_ipic; -} - -static void ipic_enable_irq(unsigned int irq) -{ - struct ipic *ipic = ipic_from_irq(irq); - unsigned int src = irq - ipic->irq_offset; - u32 temp; - - temp = ipic_read(ipic->regs, ipic_info[src].mask); - temp |= (1 << (31 - ipic_info[src].bit)); - ipic_write(ipic->regs, ipic_info[src].mask, temp); -} - -static void ipic_disable_irq(unsigned int irq) -{ - struct ipic *ipic = ipic_from_irq(irq); - unsigned int src = irq - ipic->irq_offset; - u32 temp; - - temp = ipic_read(ipic->regs, ipic_info[src].mask); - temp &= ~(1 << (31 - ipic_info[src].bit)); - ipic_write(ipic->regs, ipic_info[src].mask, temp); -} - -static void ipic_disable_irq_and_ack(unsigned int irq) -{ - struct ipic *ipic = ipic_from_irq(irq); - unsigned int src = irq - ipic->irq_offset; - u32 temp; - - ipic_disable_irq(irq); - - temp = ipic_read(ipic->regs, ipic_info[src].pend); - temp |= (1 << (31 - ipic_info[src].bit)); - ipic_write(ipic->regs, ipic_info[src].pend, temp); -} - -static void ipic_end_irq(unsigned int irq) -{ - if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) - ipic_enable_irq(irq); -} - -struct hw_interrupt_type ipic = { - .typename = " IPIC ", - .enable = ipic_enable_irq, - .disable = ipic_disable_irq, - .ack = ipic_disable_irq_and_ack, - .end = ipic_end_irq, -}; - -void __init ipic_init(phys_addr_t phys_addr, - unsigned int flags, - unsigned int irq_offset, - unsigned char *senses, - unsigned int senses_count) -{ - u32 i, temp = 0; - - primary_ipic = &p_ipic; - primary_ipic->regs = ioremap(phys_addr, MPC83xx_IPIC_SIZE); - - primary_ipic->irq_offset = irq_offset; - - ipic_write(primary_ipic->regs, IPIC_SICNR, 0x0); - - /* default priority scheme is grouped. If spread mode is required - * configure SICFR accordingly */ - if (flags & IPIC_SPREADMODE_GRP_A) - temp |= SICFR_IPSA; - if (flags & IPIC_SPREADMODE_GRP_D) - temp |= SICFR_IPSD; - if (flags & IPIC_SPREADMODE_MIX_A) - temp |= SICFR_MPSA; - if (flags & IPIC_SPREADMODE_MIX_B) - temp |= SICFR_MPSB; - - ipic_write(primary_ipic->regs, IPIC_SICNR, temp); - - /* handle MCP route */ - temp = 0; - if (flags & IPIC_DISABLE_MCP_OUT) - temp = SERCR_MCPR; - ipic_write(primary_ipic->regs, IPIC_SERCR, temp); - - /* handle routing of IRQ0 to MCP */ - temp = ipic_read(primary_ipic->regs, IPIC_SEMSR); - - if (flags & IPIC_IRQ0_MCP) - temp |= SEMSR_SIRQ0; - else - temp &= ~SEMSR_SIRQ0; - - ipic_write(primary_ipic->regs, IPIC_SEMSR, temp); - - for (i = 0 ; i < NR_IPIC_INTS ; i++) { - irq_desc[i+irq_offset].handler = &ipic; - irq_desc[i+irq_offset].status = IRQ_LEVEL; - } - - temp = 0; - for (i = 0 ; i < senses_count ; i++) { - if ((senses[i] & IRQ_SENSE_MASK) == IRQ_SENSE_EDGE) { - temp |= 1 << (15 - i); - if (i != 0) - irq_desc[i + irq_offset + MPC83xx_IRQ_EXT1 - 1].status = 0; - else - irq_desc[irq_offset + MPC83xx_IRQ_EXT0].status = 0; - } - } - ipic_write(primary_ipic->regs, IPIC_SECNR, temp); - - printk ("IPIC (%d IRQ sources, %d External IRQs) at %p\n", NR_IPIC_INTS, - senses_count, primary_ipic->regs); -} - -int ipic_set_priority(unsigned int irq, unsigned int priority) -{ - struct ipic *ipic = ipic_from_irq(irq); - unsigned int src = irq - ipic->irq_offset; - u32 temp; - - if (priority > 7) - return -EINVAL; - if (src > 127) - return -EINVAL; - if (ipic_info[src].prio == 0) - return -EINVAL; - - temp = ipic_read(ipic->regs, ipic_info[src].prio); - - if (priority < 4) { - temp &= ~(0x7 << (20 + (3 - priority) * 3)); - temp |= ipic_info[src].prio_mask << (20 + (3 - priority) * 3); - } else { - temp &= ~(0x7 << (4 + (7 - priority) * 3)); - temp |= ipic_info[src].prio_mask << (4 + (7 - priority) * 3); - } - - ipic_write(ipic->regs, ipic_info[src].prio, temp); - - return 0; -} - -void ipic_set_highest_priority(unsigned int irq) -{ - struct ipic *ipic = ipic_from_irq(irq); - unsigned int src = irq - ipic->irq_offset; - u32 temp; - - temp = ipic_read(ipic->regs, IPIC_SICFR); - - /* clear and set HPI */ - temp &= 0x7f000000; - temp |= (src & 0x7f) << 24; - - ipic_write(ipic->regs, IPIC_SICFR, temp); -} - -void ipic_set_default_priority(void) -{ - ipic_set_priority(MPC83xx_IRQ_TSEC1_TX, 0); - ipic_set_priority(MPC83xx_IRQ_TSEC1_RX, 1); - ipic_set_priority(MPC83xx_IRQ_TSEC1_ERROR, 2); - ipic_set_priority(MPC83xx_IRQ_TSEC2_TX, 3); - ipic_set_priority(MPC83xx_IRQ_TSEC2_RX, 4); - ipic_set_priority(MPC83xx_IRQ_TSEC2_ERROR, 5); - ipic_set_priority(MPC83xx_IRQ_USB2_DR, 6); - ipic_set_priority(MPC83xx_IRQ_USB2_MPH, 7); - - ipic_set_priority(MPC83xx_IRQ_UART1, 0); - ipic_set_priority(MPC83xx_IRQ_UART2, 1); - ipic_set_priority(MPC83xx_IRQ_SEC2, 2); - ipic_set_priority(MPC83xx_IRQ_IIC1, 5); - ipic_set_priority(MPC83xx_IRQ_IIC2, 6); - ipic_set_priority(MPC83xx_IRQ_SPI, 7); - ipic_set_priority(MPC83xx_IRQ_RTC_SEC, 0); - ipic_set_priority(MPC83xx_IRQ_PIT, 1); - ipic_set_priority(MPC83xx_IRQ_PCI1, 2); - ipic_set_priority(MPC83xx_IRQ_PCI2, 3); - ipic_set_priority(MPC83xx_IRQ_EXT0, 4); - ipic_set_priority(MPC83xx_IRQ_EXT1, 5); - ipic_set_priority(MPC83xx_IRQ_EXT2, 6); - ipic_set_priority(MPC83xx_IRQ_EXT3, 7); - ipic_set_priority(MPC83xx_IRQ_RTC_ALR, 0); - ipic_set_priority(MPC83xx_IRQ_MU, 1); - ipic_set_priority(MPC83xx_IRQ_SBA, 2); - ipic_set_priority(MPC83xx_IRQ_DMA, 3); - ipic_set_priority(MPC83xx_IRQ_EXT4, 4); - ipic_set_priority(MPC83xx_IRQ_EXT5, 5); - ipic_set_priority(MPC83xx_IRQ_EXT6, 6); - ipic_set_priority(MPC83xx_IRQ_EXT7, 7); -} - -void ipic_enable_mcp(enum ipic_mcp_irq mcp_irq) -{ - struct ipic *ipic = primary_ipic; - u32 temp; - - temp = ipic_read(ipic->regs, IPIC_SERMR); - temp |= (1 << (31 - mcp_irq)); - ipic_write(ipic->regs, IPIC_SERMR, temp); -} - -void ipic_disable_mcp(enum ipic_mcp_irq mcp_irq) -{ - struct ipic *ipic = primary_ipic; - u32 temp; - - temp = ipic_read(ipic->regs, IPIC_SERMR); - temp &= (1 << (31 - mcp_irq)); - ipic_write(ipic->regs, IPIC_SERMR, temp); -} - -u32 ipic_get_mcp_status(void) -{ - return ipic_read(primary_ipic->regs, IPIC_SERMR); -} - -void ipic_clear_mcp_status(u32 mask) -{ - ipic_write(primary_ipic->regs, IPIC_SERMR, mask); -} - -/* Return an interrupt vector or -1 if no interrupt is pending. */ -int ipic_get_irq(struct pt_regs *regs) -{ - int irq; - - irq = ipic_read(primary_ipic->regs, IPIC_SIVCR) & 0x7f; - - if (irq == 0) /* 0 --> no irq is pending */ - irq = -1; - - return irq; -} - -static struct sysdev_class ipic_sysclass = { - set_kset_name("ipic"), -}; - -static struct sys_device device_ipic = { - .id = 0, - .cls = &ipic_sysclass, -}; - -static int __init init_ipic_sysfs(void) -{ - int rc; - - if (!primary_ipic->regs) - return -ENODEV; - printk(KERN_DEBUG "Registering ipic with sysfs...\n"); - - rc = sysdev_class_register(&ipic_sysclass); - if (rc) { - printk(KERN_ERR "Failed registering ipic sys class\n"); - return -ENODEV; - } - rc = sysdev_register(&device_ipic); - if (rc) { - printk(KERN_ERR "Failed registering ipic sys device\n"); - return -ENODEV; - } - return 0; -} - -subsys_initcall(init_ipic_sysfs); diff --git a/arch/ppc/syslib/ipic.h b/arch/ppc/syslib/ipic.h deleted file mode 100644 index a7ce7da..0000000 --- a/arch/ppc/syslib/ipic.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * arch/ppc/kernel/ipic.h - * - * IPIC private definitions and structure. - * - * Maintainer: Kumar Gala - * - * Copyright 2005 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. - */ -#ifndef __IPIC_H__ -#define __IPIC_H__ - -#include - -#define MPC83xx_IPIC_SIZE (0x00100) - -/* System Global Interrupt Configuration Register */ -#define SICFR_IPSA 0x00010000 -#define SICFR_IPSD 0x00080000 -#define SICFR_MPSA 0x00200000 -#define SICFR_MPSB 0x00400000 - -/* System External Interrupt Mask Register */ -#define SEMSR_SIRQ0 0x00008000 - -/* System Error Control Register */ -#define SERCR_MCPR 0x00000001 - -struct ipic { - volatile u32 __iomem *regs; - unsigned int irq_offset; -}; - -struct ipic_info { - u8 pend; /* pending register offset from base */ - u8 mask; /* mask register offset from base */ - u8 prio; /* priority register offset from base */ - u8 force; /* force register offset from base */ - u8 bit; /* register bit position (as per doc) - bit mask = 1 << (31 - bit) */ - u8 prio_mask; /* priority mask value */ -}; - -#endif /* __IPIC_H__ */ diff --git a/arch/ppc/syslib/m8xx_setup.c b/arch/ppc/syslib/m8xx_setup.c index 1cc3abe..688616d 100644 --- a/arch/ppc/syslib/m8xx_setup.c +++ b/arch/ppc/syslib/m8xx_setup.c @@ -135,6 +135,16 @@ static struct irqaction tbint_irqaction .name = "tbint", }; +/* per-board overridable init_internal_rtc() function. */ +void __init __attribute__ ((weak)) +init_internal_rtc(void) +{ + /* Disable the RTC one second and alarm interrupts. */ + out_be16(&((immap_t *)IMAP_ADDR)->im_sit.sit_rtcsc, in_be16(&((immap_t *)IMAP_ADDR)->im_sit.sit_rtcsc) & ~(RTCSC_SIE | RTCSC_ALE)); + /* Enable the RTC */ + out_be16(&((immap_t *)IMAP_ADDR)->im_sit.sit_rtcsc, in_be16(&((immap_t *)IMAP_ADDR)->im_sit.sit_rtcsc) | (RTCSC_RTF | RTCSC_RTE)); +} + /* The decrementer counts at the system (internal) clock frequency divided by * sixteen, or external oscillator divided by four. We force the processor * to use system clock divided by sixteen. @@ -183,10 +193,7 @@ void __init m8xx_calibrate_decr(void) out_be32(&((immap_t *)IMAP_ADDR)->im_sitk.sitk_rtcsck, KAPWR_KEY); out_be32(&((immap_t *)IMAP_ADDR)->im_sitk.sitk_tbk, KAPWR_KEY); - /* Disable the RTC one second and alarm interrupts. */ - out_be16(&((immap_t *)IMAP_ADDR)->im_sit.sit_rtcsc, in_be16(&((immap_t *)IMAP_ADDR)->im_sit.sit_rtcsc) & ~(RTCSC_SIE | RTCSC_ALE)); - /* Enable the RTC */ - out_be16(&((immap_t *)IMAP_ADDR)->im_sit.sit_rtcsc, in_be16(&((immap_t *)IMAP_ADDR)->im_sit.sit_rtcsc) | (RTCSC_RTF | RTCSC_RTE)); + init_internal_rtc(); /* Enabling the decrementer also enables the timebase interrupts * (or from the other point of view, to get decrementer interrupts diff --git a/arch/ppc/syslib/m8xx_wdt.c b/arch/ppc/syslib/m8xx_wdt.c index a21632d..df6c955 100644 --- a/arch/ppc/syslib/m8xx_wdt.c +++ b/arch/ppc/syslib/m8xx_wdt.c @@ -19,6 +19,7 @@ #include static int wdt_timeout; +int m8xx_has_internal_rtc = 0; static irqreturn_t m8xx_wdt_interrupt(int, void *, struct pt_regs *); static struct irqaction m8xx_wdt_irqaction = { @@ -45,35 +46,15 @@ static irqreturn_t m8xx_wdt_interrupt(in return IRQ_HANDLED; } -void __init m8xx_wdt_handler_install(bd_t * binfo) +#define SYPCR_SWP 0x1 +#define SYPCR_SWE 0x4 + + +void __init m8xx_wdt_install_irq(volatile immap_t *imap, bd_t *binfo) { - volatile immap_t *imap = (volatile immap_t *)IMAP_ADDR; u32 pitc; - u32 sypcr; u32 pitrtclk; - sypcr = in_be32(&imap->im_siu_conf.sc_sypcr); - - if (!(sypcr & 0x04)) { - printk(KERN_NOTICE "m8xx_wdt: wdt disabled (SYPCR: 0x%08X)\n", - sypcr); - return; - } - - m8xx_wdt_reset(); - - printk(KERN_NOTICE - "m8xx_wdt: active wdt found (SWTC: 0x%04X, SWP: 0x%01X)\n", - (sypcr >> 16), sypcr & 0x01); - - wdt_timeout = (sypcr >> 16) & 0xFFFF; - - if (!wdt_timeout) - wdt_timeout = 0xFFFF; - - if (sypcr & 0x01) - wdt_timeout *= 2048; - /* * Fire trigger if half of the wdt ticked down */ @@ -98,6 +79,67 @@ void __init m8xx_wdt_handler_install(bd_ printk(KERN_NOTICE "m8xx_wdt: keep-alive trigger installed (PITC: 0x%04X)\n", pitc); +} + +static void m8xx_wdt_timer_func(unsigned long data); + +static struct timer_list m8xx_wdt_timer = + TIMER_INITIALIZER(m8xx_wdt_timer_func, 0, 0); + +void m8xx_wdt_stop_timer(void) +{ + del_timer(&m8xx_wdt_timer); +} + +void m8xx_wdt_install_timer(void) +{ + m8xx_wdt_timer.expires = jiffies + (HZ/2); + add_timer(&m8xx_wdt_timer); +} + +static void m8xx_wdt_timer_func(unsigned long data) +{ + m8xx_wdt_reset(); + m8xx_wdt_install_timer(); +} + +void __init m8xx_wdt_handler_install(bd_t * binfo) +{ + volatile immap_t *imap = (volatile immap_t *)IMAP_ADDR; + u32 sypcr; + + sypcr = in_be32(&imap->im_siu_conf.sc_sypcr); + + if (!(sypcr & SYPCR_SWE)) { + printk(KERN_NOTICE "m8xx_wdt: wdt disabled (SYPCR: 0x%08X)\n", + sypcr); + return; + } + + m8xx_wdt_reset(); + + printk(KERN_NOTICE + "m8xx_wdt: active wdt found (SWTC: 0x%04X, SWP: 0x%01X)\n", + (sypcr >> 16), sypcr & SYPCR_SWP); + + wdt_timeout = (sypcr >> 16) & 0xFFFF; + + if (!wdt_timeout) + wdt_timeout = 0xFFFF; + + if (sypcr & SYPCR_SWP) + wdt_timeout *= 2048; + + m8xx_has_internal_rtc = in_be16(&imap->im_sit.sit_rtcsc) & RTCSC_RTE; + + /* if the internal RTC is off use a kernel timer */ + if (!m8xx_has_internal_rtc) { + if (wdt_timeout < (binfo->bi_intfreq/HZ)) + printk(KERN_ERR "m8xx_wdt: timeout too short for ktimer!\n"); + m8xx_wdt_install_timer(); + } else + m8xx_wdt_install_irq(imap, binfo); + wdt_timeout /= binfo->bi_intfreq; } diff --git a/arch/ppc/syslib/m8xx_wdt.h b/arch/ppc/syslib/m8xx_wdt.h index 0d81a9f..e75835f 100644 --- a/arch/ppc/syslib/m8xx_wdt.h +++ b/arch/ppc/syslib/m8xx_wdt.h @@ -9,8 +9,12 @@ #ifndef _PPC_SYSLIB_M8XX_WDT_H #define _PPC_SYSLIB_M8XX_WDT_H +extern int m8xx_has_internal_rtc; + extern void m8xx_wdt_handler_install(bd_t * binfo); extern int m8xx_wdt_get_timeout(void); extern void m8xx_wdt_reset(void); +extern void m8xx_wdt_install_timer(void); +extern void m8xx_wdt_stop_timer(void); #endif /* _PPC_SYSLIB_M8XX_WDT_H */ diff --git a/arch/sh/mm/hugetlbpage.c b/arch/sh/mm/hugetlbpage.c index 6b7a768..a3568fd 100644 --- a/arch/sh/mm/hugetlbpage.c +++ b/arch/sh/mm/hugetlbpage.c @@ -84,18 +84,6 @@ pte_t huge_ptep_get_and_clear(struct mm_ return entry; } -/* - * This function checks for proper alignment of input addr and len parameters. - */ -int is_aligned_hugepage_range(unsigned long addr, unsigned long len) -{ - if (len & ~HPAGE_MASK) - return -EINVAL; - if (addr & ~HPAGE_MASK) - return -EINVAL; - return 0; -} - struct page *follow_huge_addr(struct mm_struct *mm, unsigned long address, int write) { diff --git a/arch/sh64/mm/hugetlbpage.c b/arch/sh64/mm/hugetlbpage.c index ed6a505..3d89f2a 100644 --- a/arch/sh64/mm/hugetlbpage.c +++ b/arch/sh64/mm/hugetlbpage.c @@ -84,18 +84,6 @@ pte_t huge_ptep_get_and_clear(struct mm_ return entry; } -/* - * This function checks for proper alignment of input addr and len parameters. - */ -int is_aligned_hugepage_range(unsigned long addr, unsigned long len) -{ - if (len & ~HPAGE_MASK) - return -EINVAL; - if (addr & ~HPAGE_MASK) - return -EINVAL; - return 0; -} - struct page *follow_huge_addr(struct mm_struct *mm, unsigned long address, int write) { diff --git a/arch/sparc64/mm/hugetlbpage.c b/arch/sparc64/mm/hugetlbpage.c index 625cbb3..686cc8e 100644 --- a/arch/sparc64/mm/hugetlbpage.c +++ b/arch/sparc64/mm/hugetlbpage.c @@ -92,18 +92,6 @@ pte_t huge_ptep_get_and_clear(struct mm_ return entry; } -/* - * This function checks for proper alignment of input addr and len parameters. - */ -int is_aligned_hugepage_range(unsigned long addr, unsigned long len) -{ - if (len & ~HPAGE_MASK) - return -EINVAL; - if (addr & ~HPAGE_MASK) - return -EINVAL; - return 0; -} - struct page *follow_huge_addr(struct mm_struct *mm, unsigned long address, int write) { diff --git a/drivers/char/watchdog/mpc8xx_wdt.c b/drivers/char/watchdog/mpc8xx_wdt.c index 56d62ba..ac6fbac 100644 --- a/drivers/char/watchdog/mpc8xx_wdt.c +++ b/drivers/char/watchdog/mpc8xx_wdt.c @@ -27,7 +27,10 @@ static void mpc8xx_wdt_handler_disable(v { volatile immap_t *imap = (volatile immap_t *)IMAP_ADDR; - imap->im_sit.sit_piscr &= ~(PISCR_PIE | PISCR_PTE); + if (!m8xx_has_internal_rtc) + m8xx_wdt_stop_timer(); + else + out_be32(imap->im_sit.sit_piscr, in_be32(&imap->im_sit.sit_piscr) & ~(PISCR_PIE | PISCR_PTE)); printk(KERN_NOTICE "mpc8xx_wdt: keep-alive handler deactivated\n"); } @@ -36,7 +39,10 @@ static void mpc8xx_wdt_handler_enable(vo { volatile immap_t *imap = (volatile immap_t *)IMAP_ADDR; - imap->im_sit.sit_piscr |= PISCR_PIE | PISCR_PTE; + if (!m8xx_has_internal_rtc) + m8xx_wdt_install_timer(); + else + out_be32(&imap->im_sit.sit_piscr, in_be32(&imap->im_sit.sit_piscr) | PISCR_PIE | PISCR_PTE); printk(KERN_NOTICE "mpc8xx_wdt: keep-alive handler activated\n"); } @@ -68,9 +74,6 @@ static int mpc8xx_wdt_release(struct ino static ssize_t mpc8xx_wdt_write(struct file *file, const char *data, size_t len, loff_t * ppos) { - if (ppos != &file->f_pos) - return -ESPIPE; - if (len) m8xx_wdt_reset(); diff --git a/drivers/i2c/busses/i2c-keywest.c b/drivers/i2c/busses/i2c-keywest.c index d61f748..93e7080 100644 --- a/drivers/i2c/busses/i2c-keywest.c +++ b/drivers/i2c/busses/i2c-keywest.c @@ -505,16 +505,23 @@ static int create_iface(struct device_node *np, struct device *dev) { unsigned long steps; - unsigned bsteps, tsize, i, nchan, addroffset; + unsigned bsteps, tsize, i, nchan; struct keywest_iface* iface; - u32 *psteps, *prate; + u32 *psteps, *prate, *addrp; int rc; - if (np->n_intrs < 1 || np->n_addrs < 1) { - printk(KERN_ERR "%s: Missing interrupt or address !\n", + if (np->n_intrs < 1) { + printk(KERN_ERR "%s: Missing interrupt !\n", np->full_name); return -ENODEV; } + addrp = (u32 *)get_property(np, "AAPL,address", NULL); + if (addrp == NULL) { + printk(KERN_ERR "%s: Can't find address !\n", + np->full_name); + return -ENODEV; + } + if (pmac_low_i2c_lock(np)) return -ENODEV; @@ -525,13 +532,10 @@ create_iface(struct device_node *np, str for (bsteps = 0; (steps & 0x01) == 0; bsteps++) steps >>= 1; - if (np->parent->name[0] == 'u') { + if (np->parent->name[0] == 'u') nchan = 2; - addroffset = 3; - } else { - addroffset = 0; + else nchan = 1; - } tsize = sizeof(struct keywest_iface) + (sizeof(struct keywest_chan) + 4) * nchan; @@ -550,8 +554,7 @@ create_iface(struct device_node *np, str iface->irq = np->intrs[0].line; iface->channels = (struct keywest_chan *) (((unsigned long)(iface + 1) + 3UL) & ~3UL); - iface->base = ioremap(np->addrs[0].address + addroffset, - np->addrs[0].size); + iface->base = ioremap(*addrp, 0x1000); if (!iface->base) { printk(KERN_ERR "i2c-keywest: can't map inteface !\n"); kfree(iface); diff --git a/drivers/macintosh/macio_asic.c b/drivers/macintosh/macio_asic.c index c34c96d..cb16585 100644 --- a/drivers/macintosh/macio_asic.c +++ b/drivers/macintosh/macio_asic.c @@ -3,6 +3,13 @@ * a MacIO ASIC. Interface to new driver model mostly * stolen from the PCI version. * + * Copyright (C) 2005 Ben. Herrenschmidt (benh@kernel.crashing.org) + * + * 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. + * * TODO: * * - Don't probe below media bay by default, but instead provide @@ -218,12 +225,14 @@ postcore_initcall(macio_bus_driver_init) /** - * macio_release_dev - free a macio device structure when all users of it are finished. + * macio_release_dev - free a macio device structure when all users of it are + * finished. * @dev: device that's been disconnected * - * Will be called only by the device core when all users of this macio device are - * done. This currently means never as we don't hot remove any macio device yet, - * though that will happen with mediabay based devices in a later implementation. + * Will be called only by the device core when all users of this macio device + * are done. This currently means never as we don't hot remove any macio + * device yet, though that will happen with mediabay based devices in a later + * implementation. */ static void macio_release_dev(struct device *dev) { @@ -242,7 +251,8 @@ static void macio_release_dev(struct dev * If this routine returns non-null, then the resource is completely * skipped. */ -static int macio_resource_quirks(struct device_node *np, struct resource *res, int index) +static int macio_resource_quirks(struct device_node *np, struct resource *res, + int index) { if (res->flags & IORESOURCE_MEM) { /* Grand Central has too large resource 0 on some machines */ @@ -258,12 +268,15 @@ static int macio_resource_quirks(struct np->addrs[index].size = 0x100; res->end = res->start + 0xff; } - /* ESCC parent eats child resources. We could have added a level of hierarchy, - * but I don't really feel the need for it */ + /* ESCC parent eats child resources. We could have added a + * level of hierarchy, but I don't really feel the need + * for it + */ if (!strcmp(np->name, "escc")) return 1; /* ESCC has bogus resources >= 3 */ - if (index >= 3 && !(strcmp(np->name, "ch-a") && strcmp(np->name, "ch-b"))) + if (index >= 3 && !(strcmp(np->name, "ch-a") && + strcmp(np->name, "ch-b"))) return 1; /* Media bay has too many resources, keep only first one */ if (index > 0 && !strcmp(np->name, "media-bay")) @@ -285,6 +298,71 @@ static int macio_resource_quirks(struct } +static void macio_setup_interrupts(struct macio_dev *dev) +{ + struct device_node *np = dev->ofdev.node; + int i,j; + + /* For now, we use pre-parsed entries in the device-tree for + * interrupt routing and addresses, but we should change that + * to dynamically parsed entries and so get rid of most of the + * clutter in struct device_node + */ + for (i = j = 0; i < np->n_intrs; i++) { + struct resource *res = &dev->interrupt[j]; + + if (j >= MACIO_DEV_COUNT_IRQS) + break; + res->start = np->intrs[i].line; + res->flags = IORESOURCE_IO; + if (np->intrs[j].sense) + res->flags |= IORESOURCE_IRQ_LOWLEVEL; + else + res->flags |= IORESOURCE_IRQ_HIGHEDGE; + res->name = dev->ofdev.dev.bus_id; + if (macio_resource_quirks(np, res, i)) + memset(res, 0, sizeof(struct resource)); + else + j++; + } + dev->n_interrupts = j; +} + +static void macio_setup_resources(struct macio_dev *dev, + struct resource *parent_res) +{ + struct device_node *np = dev->ofdev.node; + u32 *addr; + u64 size; + int index; + + for (index = 0; (addr = of_get_address(np, index, &size)) != NULL; + index++) { + struct resource *res = &dev->resource[index]; + if (index >= MACIO_DEV_COUNT_RESOURCES) + break; + res->start = of_translate_address(np, addr); + res->end = res->start + (unsigned long)size - 1; + res->flags = IORESOURCE_MEM; + res->name = dev->ofdev.dev.bus_id; + + if (macio_resource_quirks(np, res, index)) { + memset(res, 0, sizeof(struct resource)); + continue; + } + /* Currently, we consider failure as harmless, this may + * change in the future, once I've found all the device + * tree bugs in older machines & worked around them + */ + if (insert_resource(parent_res, res)) { + printk(KERN_WARNING "Can't request resource " + "%d for MacIO device %s\n", + index, dev->ofdev.dev.bus_id); + } + } + dev->n_resources = index; +} + /** * macio_add_one_device - Add one device from OF node to the device tree * @chip: pointer to the macio_chip holding the device @@ -294,12 +372,13 @@ static int macio_resource_quirks(struct * When media-bay is changed to hotswap drivers, this function will * be exposed to the bay driver some way... */ -static struct macio_dev * macio_add_one_device(struct macio_chip *chip, struct device *parent, - struct device_node *np, struct macio_dev *in_bay, +static struct macio_dev * macio_add_one_device(struct macio_chip *chip, + struct device *parent, + struct device_node *np, + struct macio_dev *in_bay, struct resource *parent_res) { struct macio_dev *dev; - int i, j; u32 *reg; if (np == NULL) @@ -326,7 +405,8 @@ static struct macio_dev * macio_add_one_ /* MacIO itself has a different reg, we use it's PCI base */ if (np == chip->of_node) { - sprintf(dev->ofdev.dev.bus_id, "%1d.%08lx:%.*s", chip->lbus.index, + sprintf(dev->ofdev.dev.bus_id, "%1d.%08lx:%.*s", + chip->lbus.index, #ifdef CONFIG_PCI pci_resource_start(chip->lbus.pdev, 0), #else @@ -335,57 +415,16 @@ static struct macio_dev * macio_add_one_ MAX_NODE_NAME_SIZE, np->name); } else { reg = (u32 *)get_property(np, "reg", NULL); - sprintf(dev->ofdev.dev.bus_id, "%1d.%08x:%.*s", chip->lbus.index, + sprintf(dev->ofdev.dev.bus_id, "%1d.%08x:%.*s", + chip->lbus.index, reg ? *reg : 0, MAX_NODE_NAME_SIZE, np->name); } - /* For now, we use pre-parsed entries in the device-tree for - * interrupt routing and addresses, but we should change that - * to dynamically parsed entries and so get rid of most of the - * clutter in struct device_node - */ - for (i = j = 0; i < np->n_intrs; i++) { - struct resource *res = &dev->interrupt[j]; - - if (j >= MACIO_DEV_COUNT_IRQS) - break; - res->start = np->intrs[i].line; - res->flags = IORESOURCE_IO; - if (np->intrs[j].sense) - res->flags |= IORESOURCE_IRQ_LOWLEVEL; - else - res->flags |= IORESOURCE_IRQ_HIGHEDGE; - res->name = dev->ofdev.dev.bus_id; - if (macio_resource_quirks(np, res, i)) - memset(res, 0, sizeof(struct resource)); - else - j++; - } - dev->n_interrupts = j; - for (i = j = 0; i < np->n_addrs; i++) { - struct resource *res = &dev->resource[j]; - - if (j >= MACIO_DEV_COUNT_RESOURCES) - break; - res->start = np->addrs[i].address; - res->end = np->addrs[i].address + np->addrs[i].size - 1; - res->flags = IORESOURCE_MEM; - res->name = dev->ofdev.dev.bus_id; - if (macio_resource_quirks(np, res, i)) - memset(res, 0, sizeof(struct resource)); - else { - j++; - /* Currently, we consider failure as harmless, this may - * change in the future, once I've found all the device - * tree bugs in older machines & worked around them - */ - if (insert_resource(parent_res, res)) - printk(KERN_WARNING "Can't request resource %d for MacIO" - " device %s\n", i, dev->ofdev.dev.bus_id); - } - } - dev->n_resources = j; + /* Setup interrupts & resources */ + macio_setup_interrupts(dev); + macio_setup_resources(dev, parent_res); + /* Register with core */ if (of_device_register(&dev->ofdev) != 0) { printk(KERN_DEBUG"macio: device registration error for %s!\n", dev->ofdev.dev.bus_id); @@ -442,36 +481,42 @@ static void macio_pci_add_devices(struct /* First scan 1st level */ for (np = NULL; (np = of_get_next_child(pnode, np)) != NULL;) { - if (!macio_skip_device(np)) { - of_node_get(np); - mdev = macio_add_one_device(chip, &rdev->ofdev.dev, np, NULL, root_res); - if (mdev == NULL) - of_node_put(np); - else if (strncmp(np->name, "media-bay", 9) == 0) - mbdev = mdev; - else if (strncmp(np->name, "escc", 4) == 0) - sdev = mdev; - } + if (macio_skip_device(np)) + continue; + of_node_get(np); + mdev = macio_add_one_device(chip, &rdev->ofdev.dev, np, NULL, + root_res); + if (mdev == NULL) + of_node_put(np); + else if (strncmp(np->name, "media-bay", 9) == 0) + mbdev = mdev; + else if (strncmp(np->name, "escc", 4) == 0) + sdev = mdev; } /* Add media bay devices if any */ if (mbdev) - for (np = NULL; (np = of_get_next_child(mbdev->ofdev.node, np)) != NULL;) - if (!macio_skip_device(np)) { - of_node_get(np); - if (macio_add_one_device(chip, &mbdev->ofdev.dev, np, mbdev, - root_res) == NULL) - of_node_put(np); - } + for (np = NULL; (np = of_get_next_child(mbdev->ofdev.node, np)) + != NULL;) { + if (macio_skip_device(np)) + continue; + of_node_get(np); + if (macio_add_one_device(chip, &mbdev->ofdev.dev, np, + mbdev, root_res) == NULL) + of_node_put(np); + } + /* Add serial ports if any */ if (sdev) { - for (np = NULL; (np = of_get_next_child(sdev->ofdev.node, np)) != NULL;) - if (!macio_skip_device(np)) { - of_node_get(np); - if (macio_add_one_device(chip, &sdev->ofdev.dev, np, NULL, - root_res) == NULL) - of_node_put(np); - } + for (np = NULL; (np = of_get_next_child(sdev->ofdev.node, np)) + != NULL;) { + if (macio_skip_device(np)) + continue; + of_node_get(np); + if (macio_add_one_device(chip, &sdev->ofdev.dev, np, + NULL, root_res) == NULL) + of_node_put(np); + } } } @@ -519,7 +564,8 @@ void macio_unregister_driver(struct maci * Returns 0 on success, or %EBUSY on error. A warning * message is also printed on failure. */ -int macio_request_resource(struct macio_dev *dev, int resource_no, const char *name) +int macio_request_resource(struct macio_dev *dev, int resource_no, + const char *name) { if (macio_resource_len(dev, resource_no) == 0) return 0; @@ -606,20 +652,20 @@ static int __devinit macio_pci_probe(str if (ent->vendor != PCI_VENDOR_ID_APPLE) return -ENODEV; - /* Note regarding refcounting: We assume pci_device_to_OF_node() is ported - * to new OF APIs and returns a node with refcount incremented. This isn't - * the case today, but on the other hand ppc32 doesn't do refcounting. This - * will have to be fixed when going to ppc64. --BenH. + /* Note regarding refcounting: We assume pci_device_to_OF_node() is + * ported to new OF APIs and returns a node with refcount incremented. */ np = pci_device_to_OF_node(pdev); if (np == NULL) return -ENODEV; - /* This assumption is wrong, fix that here for now until I fix the arch */ + /* The above assumption is wrong !!! + * fix that here for now until I fix the arch code + */ of_node_get(np); - /* We also assume that pmac_feature will have done a get() on nodes stored - * in the macio chips array + /* We also assume that pmac_feature will have done a get() on nodes + * stored in the macio chips array */ chip = macio_find(np, macio_unknown); of_node_put(np); @@ -639,9 +685,9 @@ static int __devinit macio_pci_probe(str /* * HACK ALERT: The WallStreet PowerBook and some OHare based machines - * have 2 macio ASICs. I must probe the "main" one first or IDE ordering - * will be incorrect. So I put on "hold" the second one since it seem to - * appear first on PCI + * have 2 macio ASICs. I must probe the "main" one first or IDE + * ordering will be incorrect. So I put on "hold" the second one since + * it seem to appear first on PCI */ if (chip->type == macio_gatwick || chip->type == macio_ohareII) if (macio_chips[0].lbus.pdev == NULL) { diff --git a/drivers/macintosh/via-cuda.c b/drivers/macintosh/via-cuda.c index d843a6c..18ff770 100644 --- a/drivers/macintosh/via-cuda.c +++ b/drivers/macintosh/via-cuda.c @@ -127,39 +127,34 @@ struct adb_driver via_cuda_driver = { #endif /* CONFIG_ADB */ #ifdef CONFIG_PPC -int __init -find_via_cuda(void) +int __init find_via_cuda(void) { - int err; struct adb_request req; + phys_addr_t taddr; + u32 *reg; + int err; if (vias != 0) return 1; - vias = find_devices("via-cuda"); + vias = of_find_node_by_name(NULL, "via-cuda"); if (vias == 0) return 0; - if (vias->next != 0) - printk(KERN_WARNING "Warning: only using 1st via-cuda\n"); - -#if 0 - { int i; - - printk("find_via_cuda: node = %p, addrs =", vias->node); - for (i = 0; i < vias->n_addrs; ++i) - printk(" %x(%x)", vias->addrs[i].address, vias->addrs[i].size); - printk(", intrs ="); - for (i = 0; i < vias->n_intrs; ++i) - printk(" %x", vias->intrs[i].line); - printk("\n"); } -#endif - if (vias->n_addrs != 1 || vias->n_intrs != 1) { - printk(KERN_ERR "via-cuda: expecting 1 address (%d) and 1 interrupt (%d)\n", - vias->n_addrs, vias->n_intrs); - if (vias->n_addrs < 1 || vias->n_intrs < 1) - return 0; + reg = (u32 *)get_property(vias, "reg", NULL); + if (reg == NULL) { + printk(KERN_ERR "via-cuda: No \"reg\" property !\n"); + goto fail; + } + taddr = of_translate_address(vias, reg); + if (taddr == 0) { + printk(KERN_ERR "via-cuda: Can't translate address !\n"); + goto fail; + } + via = ioremap(taddr, 0x2000); + if (via == NULL) { + printk(KERN_ERR "via-cuda: Can't map address !\n"); + goto fail; } - via = ioremap(vias->addrs->address, 0x2000); cuda_state = idle; sys_ctrler = SYS_CTRLER_CUDA; @@ -185,6 +180,11 @@ find_via_cuda(void) cuda_poll(); return 1; + + fail: + of_node_put(vias); + vias = NULL; + return 0; } #endif /* CONFIG_PPC */ diff --git a/drivers/macintosh/via-pmu.c b/drivers/macintosh/via-pmu.c index 5640435..13881f1 100644 --- a/drivers/macintosh/via-pmu.c +++ b/drivers/macintosh/via-pmu.c @@ -147,6 +147,7 @@ static struct device_node *vias; static int pmu_kind = PMU_UNKNOWN; static int pmu_fully_inited = 0; static int pmu_has_adb; +static struct device_node *gpio_node; static unsigned char __iomem *gpio_reg = NULL; static int gpio_irq = -1; static int gpio_irq_enabled = -1; @@ -295,22 +296,26 @@ static struct backlight_controller pmu_b }; #endif /* CONFIG_PMAC_BACKLIGHT */ -int -find_via_pmu(void) +int __init find_via_pmu(void) { + phys_addr_t taddr; + u32 *reg; + if (via != 0) return 1; - vias = find_devices("via-pmu"); - if (vias == 0) + vias = of_find_node_by_name(NULL, "via-pmu"); + if (vias == NULL) return 0; - if (vias->next != 0) - printk(KERN_WARNING "Warning: only using 1st via-pmu\n"); - if (vias->n_addrs < 1 || vias->n_intrs < 1) { - printk(KERN_ERR "via-pmu: %d addresses, %d interrupts!\n", - vias->n_addrs, vias->n_intrs); - if (vias->n_addrs < 1 || vias->n_intrs < 1) - return 0; + reg = (u32 *)get_property(vias, "reg", NULL); + if (reg == NULL) { + printk(KERN_ERR "via-pmu: No \"reg\" property !\n"); + goto fail; + } + taddr = of_translate_address(vias, reg); + if (taddr == 0) { + printk(KERN_ERR "via-pmu: Can't translate address !\n"); + goto fail; } spin_lock_init(&pmu_lock); @@ -331,7 +336,8 @@ find_via_pmu(void) pmu_kind = PMU_HEATHROW_BASED; else if (device_is_compatible(vias->parent, "Keylargo") || device_is_compatible(vias->parent, "K2-Keylargo")) { - struct device_node *gpio, *gpiop; + struct device_node *gpiop; + phys_addr_t gaddr = 0; pmu_kind = PMU_KEYLARGO_BASED; pmu_has_adb = (find_type_devices("adb") != NULL); @@ -341,19 +347,24 @@ find_via_pmu(void) PMU_INT_TICK | PMU_INT_ENVIRONMENT; - gpiop = find_devices("gpio"); - if (gpiop && gpiop->n_addrs) { - gpio_reg = ioremap(gpiop->addrs->address, 0x10); - gpio = find_devices("extint-gpio1"); - if (gpio == NULL) - gpio = find_devices("pmu-interrupt"); - if (gpio && gpio->parent == gpiop && gpio->n_intrs) - gpio_irq = gpio->intrs[0].line; + gpiop = of_find_node_by_name(NULL, "gpio"); + if (gpiop) { + reg = (u32 *)get_property(gpiop, "reg", NULL); + if (reg) + gaddr = of_translate_address(gpiop, reg); + if (gaddr != 0) + gpio_reg = ioremap(gaddr, 0x10); } + if (gpio_reg == NULL) + printk(KERN_ERR "via-pmu: Can't find GPIO reg !\n"); } else pmu_kind = PMU_UNKNOWN; - via = ioremap(vias->addrs->address, 0x2000); + via = ioremap(taddr, 0x2000); + if (via == NULL) { + printk(KERN_ERR "via-pmu: Can't map address !\n"); + goto fail; + } out_8(&via[IER], IER_CLR | 0x7f); /* disable all intrs */ out_8(&via[IFR], 0x7f); /* clear IFR */ @@ -371,17 +382,19 @@ find_via_pmu(void) sys_ctrler = SYS_CTRLER_PMU; return 1; + fail: + of_node_put(vias); + vias = NULL; + return 0; } #ifdef CONFIG_ADB -static int -pmu_probe(void) +static int pmu_probe(void) { return vias == NULL? -ENODEV: 0; } -static int __init -pmu_init(void) +static int __init pmu_init(void) { if (vias == NULL) return -ENODEV; @@ -405,7 +418,7 @@ static int __init via_pmu_start(void) bright_req_2.complete = 1; batt_req.complete = 1; -#if defined(CONFIG_PPC32) && !defined(CONFIG_PPC_MERGE) +#ifndef CONFIG_PPC_MERGE if (pmu_kind == PMU_KEYLARGO_BASED) openpic_set_irq_priority(vias->intrs[0].line, OPENPIC_PRIORITY_DEFAULT + 1); @@ -418,10 +431,22 @@ static int __init via_pmu_start(void) return -EAGAIN; } - if (pmu_kind == PMU_KEYLARGO_BASED && gpio_irq != -1) { - if (request_irq(gpio_irq, gpio1_interrupt, 0, "GPIO1 ADB", (void *)0)) - printk(KERN_ERR "pmu: can't get irq %d (GPIO1)\n", gpio_irq); - gpio_irq_enabled = 1; + if (pmu_kind == PMU_KEYLARGO_BASED) { + gpio_node = of_find_node_by_name(NULL, "extint-gpio1"); + if (gpio_node == NULL) + gpio_node = of_find_node_by_name(NULL, + "pmu-interrupt"); + if (gpio_node && gpio_node->n_intrs > 0) + gpio_irq = gpio_node->intrs[0].line; + + if (gpio_irq != -1) { + if (request_irq(gpio_irq, gpio1_interrupt, 0, + "GPIO1 ADB", (void *)0)) + printk(KERN_ERR "pmu: can't get irq %d" + " (GPIO1)\n", gpio_irq); + else + gpio_irq_enabled = 1; + } } /* Enable interrupts */ @@ -1371,7 +1396,6 @@ next: } pmu_done(req); } else { -#if defined(CONFIG_XMON) && !defined(CONFIG_PPC64) if (len == 4 && data[1] == 0x2c) { extern int xmon_wants_key, xmon_adb_keycode; if (xmon_wants_key) { @@ -1379,7 +1403,6 @@ next: return; } } -#endif /* defined(CONFIG_XMON) && !defined(CONFIG_PPC64) */ #ifdef CONFIG_ADB /* * XXX On the [23]400 the PMU gives us an up diff --git a/include/asm-ia64/page.h b/include/asm-ia64/page.h index 9dd9da1..4b26229 100644 --- a/include/asm-ia64/page.h +++ b/include/asm-ia64/page.h @@ -57,6 +57,7 @@ # define HAVE_ARCH_HUGETLB_UNMAPPED_AREA # define ARCH_HAS_HUGEPAGE_ONLY_RANGE +# define ARCH_HAS_PREPARE_HUGEPAGE_RANGE #endif /* CONFIG_HUGETLB_PAGE */ #ifdef __ASSEMBLY__ diff --git a/include/asm-powerpc/bootx.h b/include/asm-powerpc/bootx.h new file mode 100644 index 0000000..ed626b7 --- /dev/null +++ b/include/asm-powerpc/bootx.h @@ -0,0 +1,166 @@ +/* + * This file describes the structure passed from the BootX application + * (for MacOS) when it is used to boot Linux. + * + * Written by Benjamin Herrenschmidt. + */ + + +#ifndef __ASM_BOOTX_H__ +#define __ASM_BOOTX_H__ + +#ifdef macintosh +#include +#include "linux_type_defs.h" +#endif + +#ifdef macintosh +/* All this requires PowerPC alignment */ +#pragma options align=power +#endif + +/* On kernel entry: + * + * r3 = 0x426f6f58 ('BooX') + * r4 = pointer to boot_infos + * r5 = NULL + * + * Data and instruction translation disabled, interrupts + * disabled, kernel loaded at physical 0x00000000 on PCI + * machines (will be different on NuBus). + */ + +#define BOOT_INFO_VERSION 5 +#define BOOT_INFO_COMPATIBLE_VERSION 1 + +/* Bit in the architecture flag mask. More to be defined in + future versions. Note that either BOOT_ARCH_PCI or + BOOT_ARCH_NUBUS is set. The other BOOT_ARCH_NUBUS_xxx are + set additionally when BOOT_ARCH_NUBUS is set. + */ +#define BOOT_ARCH_PCI 0x00000001UL +#define BOOT_ARCH_NUBUS 0x00000002UL +#define BOOT_ARCH_NUBUS_PDM 0x00000010UL +#define BOOT_ARCH_NUBUS_PERFORMA 0x00000020UL +#define BOOT_ARCH_NUBUS_POWERBOOK 0x00000040UL + +/* Maximum number of ranges in phys memory map */ +#define MAX_MEM_MAP_SIZE 26 + +/* This is the format of an element in the physical memory map. Note that + the map is optional and current BootX will only build it for pre-PCI + machines */ +typedef struct boot_info_map_entry +{ + __u32 physAddr; /* Physical starting address */ + __u32 size; /* Size in bytes */ +} boot_info_map_entry_t; + + +/* Here are the boot informations that are passed to the bootstrap + * Note that the kernel arguments and the device tree are appended + * at the end of this structure. */ +typedef struct boot_infos +{ + /* Version of this structure */ + __u32 version; + /* backward compatible down to version: */ + __u32 compatible_version; + + /* NEW (vers. 2) this holds the current _logical_ base addr of + the frame buffer (for use by early boot message) */ + __u8* logicalDisplayBase; + + /* NEW (vers. 4) Apple's machine identification */ + __u32 machineID; + + /* NEW (vers. 4) Detected hw architecture */ + __u32 architecture; + + /* The device tree (internal addresses relative to the beginning of the tree, + * device tree offset relative to the beginning of this structure). + * On pre-PCI macintosh (BOOT_ARCH_PCI bit set to 0 in architecture), this + * field is 0. + */ + __u32 deviceTreeOffset; /* Device tree offset */ + __u32 deviceTreeSize; /* Size of the device tree */ + + /* Some infos about the current MacOS display */ + __u32 dispDeviceRect[4]; /* left,top,right,bottom */ + __u32 dispDeviceDepth; /* (8, 16 or 32) */ + __u8* dispDeviceBase; /* base address (physical) */ + __u32 dispDeviceRowBytes; /* rowbytes (in bytes) */ + __u32 dispDeviceColorsOffset; /* Colormap (8 bits only) or 0 (*) */ + /* Optional offset in the registry to the current + * MacOS display. (Can be 0 when not detected) */ + __u32 dispDeviceRegEntryOffset; + + /* Optional pointer to boot ramdisk (offset from this structure) */ + __u32 ramDisk; + __u32 ramDiskSize; /* size of ramdisk image */ + + /* Kernel command line arguments (offset from this structure) */ + __u32 kernelParamsOffset; + + /* ALL BELOW NEW (vers. 4) */ + + /* This defines the physical memory. Valid with BOOT_ARCH_NUBUS flag + (non-PCI) only. On PCI, memory is contiguous and it's size is in the + device-tree. */ + boot_info_map_entry_t + physMemoryMap[MAX_MEM_MAP_SIZE]; /* Where the phys memory is */ + __u32 physMemoryMapSize; /* How many entries in map */ + + + /* The framebuffer size (optional, currently 0) */ + __u32 frameBufferSize; /* Represents a max size, can be 0. */ + + /* NEW (vers. 5) */ + + /* Total params size (args + colormap + device tree + ramdisk) */ + __u32 totalParamsSize; + +} boot_infos_t; + +/* (*) The format of the colormap is 256 * 3 * 2 bytes. Each color index + * is represented by 3 short words containing a 16 bits (unsigned) color + * component. Later versions may contain the gamma table for direct-color + * devices here. + */ +#define BOOTX_COLORTABLE_SIZE (256UL*3UL*2UL) + +/* BootX passes the device-tree using a format that comes from earlier + * ppc32 kernels. This used to match what is in prom.h, but not anymore + * so we now define it here + */ +struct bootx_dt_prop { + u32 name; + int length; + u32 value; + u32 next; +}; + +struct bootx_dt_node { + u32 unused0; + u32 unused1; + u32 phandle; /* not really available */ + u32 unused2; + u32 unused3; + u32 unused4; + u32 unused5; + u32 full_name; + u32 properties; + u32 parent; + u32 child; + u32 sibling; + u32 next; + u32 allnext; +}; + +extern void bootx_init(unsigned long r4, unsigned long phys); + +#ifdef macintosh +#pragma options align=reset +#endif + +#endif diff --git a/include/asm-powerpc/btext.h b/include/asm-powerpc/btext.h index 71cce36..906f46e 100644 --- a/include/asm-powerpc/btext.h +++ b/include/asm-powerpc/btext.h @@ -7,21 +7,22 @@ #define __PPC_BTEXT_H #ifdef __KERNEL__ -extern void btext_clearscreen(void); -extern void btext_flushscreen(void); - -extern int boot_text_mapped; - -extern int btext_initialize(struct device_node *np); - -extern void map_boot_text(void); -extern void init_boot_display(void); +extern int btext_find_display(int allow_nonstdout); extern void btext_update_display(unsigned long phys, int width, int height, int depth, int pitch); +extern void btext_setup_display(int width, int height, int depth, int pitch, + unsigned long address); +extern void btext_prepare_BAT(void); +extern void btext_unmap(void); extern void btext_drawchar(char c); extern void btext_drawstring(const char *str); extern void btext_drawhex(unsigned long v); +extern void btext_drawtext(const char *c, unsigned int len); + +extern void btext_clearscreen(void); +extern void btext_flushscreen(void); +extern void btext_flushline(void); #endif /* __KERNEL__ */ #endif /* __PPC_BTEXT_H */ diff --git a/include/asm-powerpc/eeh.h b/include/asm-powerpc/eeh.h index f8633aa..40334d7 100644 --- a/include/asm-powerpc/eeh.h +++ b/include/asm-powerpc/eeh.h @@ -33,9 +33,11 @@ struct device_node; extern int eeh_subsystem_enabled; /* Values for eeh_mode bits in device_node */ -#define EEH_MODE_SUPPORTED (1<<0) -#define EEH_MODE_NOCHECK (1<<1) -#define EEH_MODE_ISOLATED (1<<2) +#define EEH_MODE_SUPPORTED (1<<0) +#define EEH_MODE_NOCHECK (1<<1) +#define EEH_MODE_ISOLATED (1<<2) +#define EEH_MODE_RECOVERING (1<<3) +#define EEH_MODE_IRQ_DISABLED (1<<4) /* Max number of EEH freezes allowed before we consider the device * to be permanently disabled. */ @@ -57,6 +59,7 @@ void __init pci_addr_cache_build(void); * to finish the eeh setup for this device. */ void eeh_add_device_early(struct device_node *); +void eeh_add_device_tree_early(struct device_node *); void eeh_add_device_late(struct pci_dev *); /** @@ -72,6 +75,15 @@ void eeh_add_device_late(struct pci_dev void eeh_remove_device(struct pci_dev *); /** + * eeh_remove_device_recursive - undo EEH for device & children. + * @dev: pci device to be removed + * + * As above, this removes the device; it also removes child + * pci devices as well. + */ +void eeh_remove_bus_device(struct pci_dev *); + +/** * EEH_POSSIBLE_ERROR() -- test for possible MMIO failure. * * If this macro yields TRUE, the caller relays to eeh_check_failure() diff --git a/include/asm-powerpc/eeh_event.h b/include/asm-powerpc/eeh_event.h index d168a30..5f50da2 100644 --- a/include/asm-powerpc/eeh_event.h +++ b/include/asm-powerpc/eeh_event.h @@ -29,7 +29,7 @@ struct eeh_event { struct list_head list; struct device_node *dn; /* struct device node */ struct pci_dev *dev; /* affected device */ - int state; + enum pci_channel_state state; /* PCI bus state for the affected device */ int time_unavail; /* milliseconds until device might be available */ }; @@ -46,7 +46,10 @@ struct eeh_event { */ int eeh_send_failure_event (struct device_node *dn, struct pci_dev *dev, - int reset_state, + enum pci_channel_state state, int time_unavail); +/* Main recovery function */ +void handle_eeh_events (struct eeh_event *); + #endif /* ASM_PPC64_EEH_EVENT_H */ diff --git a/include/asm-powerpc/ibmebus.h b/include/asm-powerpc/ibmebus.h new file mode 100644 index 0000000..55982b8 --- /dev/null +++ b/include/asm-powerpc/ibmebus.h @@ -0,0 +1,83 @@ +/* + * IBM PowerPC eBus Infrastructure Support. + * + * Copyright (c) 2005 IBM Corporation + * Heiko J Schick + * + * All rights reserved. + * + * This source code is distributed under a dual license of GPL v2.0 and OpenIB + * BSD. + * + * OpenIB BSD License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _ASM_EBUS_H +#define _ASM_EBUS_H + +#include +#include +#include +#include + +extern struct dma_mapping_ops ibmebus_dma_ops; +extern struct bus_type ibmebus_bus_type; + +struct ibmebus_dev { + char *name; + struct of_device ofdev; +}; + +struct ibmebus_driver { + char *name; + struct of_device_id *id_table; + int (*probe) (struct ibmebus_dev *dev, const struct of_device_id *id); + int (*remove) (struct ibmebus_dev *dev); + struct device_driver driver; +}; + +int ibmebus_register_driver(struct ibmebus_driver *drv); +void ibmebus_unregister_driver(struct ibmebus_driver *drv); + +int ibmebus_request_irq(struct ibmebus_dev *dev, + u32 ist, + irqreturn_t (*handler)(int, void*, struct pt_regs *), + unsigned long irq_flags, const char * devname, + void *dev_id); +void ibmebus_free_irq(struct ibmebus_dev *dev, u32 ist, void *dev_id); + +static inline struct ibmebus_driver *to_ibmebus_driver(struct device_driver *drv) +{ + return container_of(drv, struct ibmebus_driver, driver); +} + +static inline struct ibmebus_dev *to_ibmebus_dev(struct device *dev) +{ + return container_of(dev, struct ibmebus_dev, ofdev.dev); +} + + +#endif /* _ASM_IBMEBUS_H */ diff --git a/include/asm-powerpc/ipic.h b/include/asm-powerpc/ipic.h new file mode 100644 index 0000000..0fe396a --- /dev/null +++ b/include/asm-powerpc/ipic.h @@ -0,0 +1,85 @@ +/* + * include/asm-ppc/ipic.h + * + * IPIC external definitions and structure. + * + * Maintainer: Kumar Gala + * + * Copyright 2005 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. + */ +#ifdef __KERNEL__ +#ifndef __ASM_IPIC_H__ +#define __ASM_IPIC_H__ + +#include + +/* Flags when we init the IPIC */ +#define IPIC_SPREADMODE_GRP_A 0x00000001 +#define IPIC_SPREADMODE_GRP_D 0x00000002 +#define IPIC_SPREADMODE_MIX_A 0x00000004 +#define IPIC_SPREADMODE_MIX_B 0x00000008 +#define IPIC_DISABLE_MCP_OUT 0x00000010 +#define IPIC_IRQ0_MCP 0x00000020 + +/* IPIC registers offsets */ +#define IPIC_SICFR 0x00 /* System Global Interrupt Configuration Register */ +#define IPIC_SIVCR 0x04 /* System Global Interrupt Vector Register */ +#define IPIC_SIPNR_H 0x08 /* System Internal Interrupt Pending Register (HIGH) */ +#define IPIC_SIPNR_L 0x0C /* System Internal Interrupt Pending Register (LOW) */ +#define IPIC_SIPRR_A 0x10 /* System Internal Interrupt group A Priority Register */ +#define IPIC_SIPRR_B 0x14 /* System Internal Interrupt group B Priority Register */ +#define IPIC_SIPRR_C 0x18 /* System Internal Interrupt group C Priority Register */ +#define IPIC_SIPRR_D 0x1C /* System Internal Interrupt group D Priority Register */ +#define IPIC_SIMSR_H 0x20 /* System Internal Interrupt Mask Register (HIGH) */ +#define IPIC_SIMSR_L 0x24 /* System Internal Interrupt Mask Register (LOW) */ +#define IPIC_SICNR 0x28 /* System Internal Interrupt Control Register */ +#define IPIC_SEPNR 0x2C /* System External Interrupt Pending Register */ +#define IPIC_SMPRR_A 0x30 /* System Mixed Interrupt group A Priority Register */ +#define IPIC_SMPRR_B 0x34 /* System Mixed Interrupt group B Priority Register */ +#define IPIC_SEMSR 0x38 /* System External Interrupt Mask Register */ +#define IPIC_SECNR 0x3C /* System External Interrupt Control Register */ +#define IPIC_SERSR 0x40 /* System Error Status Register */ +#define IPIC_SERMR 0x44 /* System Error Mask Register */ +#define IPIC_SERCR 0x48 /* System Error Control Register */ +#define IPIC_SIFCR_H 0x50 /* System Internal Interrupt Force Register (HIGH) */ +#define IPIC_SIFCR_L 0x54 /* System Internal Interrupt Force Register (LOW) */ +#define IPIC_SEFCR 0x58 /* System External Interrupt Force Register */ +#define IPIC_SERFR 0x5C /* System Error Force Register */ +#define IPIC_SCVCR 0x60 /* System Critical Interrupt Vector Register */ +#define IPIC_SMVCR 0x64 /* System Management Interrupt Vector Register */ + +enum ipic_prio_grp { + IPIC_INT_GRP_A = IPIC_SIPRR_A, + IPIC_INT_GRP_D = IPIC_SIPRR_D, + IPIC_MIX_GRP_A = IPIC_SMPRR_A, + IPIC_MIX_GRP_B = IPIC_SMPRR_B, +}; + +enum ipic_mcp_irq { + IPIC_MCP_IRQ0 = 0, + IPIC_MCP_WDT = 1, + IPIC_MCP_SBA = 2, + IPIC_MCP_PCI1 = 5, + IPIC_MCP_PCI2 = 6, + IPIC_MCP_MU = 7, +}; + +extern void ipic_init(phys_addr_t phys_addr, unsigned int flags, + unsigned int irq_offset, + unsigned char *senses, unsigned int senses_count); +extern int ipic_set_priority(unsigned int irq, unsigned int priority); +extern void ipic_set_highest_priority(unsigned int irq); +extern void ipic_set_default_priority(void); +extern void ipic_enable_mcp(enum ipic_mcp_irq mcp_irq); +extern void ipic_disable_mcp(enum ipic_mcp_irq mcp_irq); +extern u32 ipic_get_mcp_status(void); +extern void ipic_clear_mcp_status(u32 mask); +extern int ipic_get_irq(struct pt_regs *regs); + +#endif /* __ASM_IPIC_H__ */ +#endif /* __KERNEL__ */ diff --git a/include/asm-powerpc/iseries/it_lp_reg_save.h b/include/asm-powerpc/iseries/it_lp_reg_save.h index 288044b..81824e1 100644 --- a/include/asm-powerpc/iseries/it_lp_reg_save.h +++ b/include/asm-powerpc/iseries/it_lp_reg_save.h @@ -81,4 +81,6 @@ struct ItLpRegSave { u8 xRsvd3[176]; // Reserved 350-3FF }; +extern struct ItLpRegSave iseries_reg_save[]; + #endif /* _ITLPREGSAVE_H */ diff --git a/include/asm-powerpc/kexec.h b/include/asm-powerpc/kexec.h index c72ffc7..934b498 100644 --- a/include/asm-powerpc/kexec.h +++ b/include/asm-powerpc/kexec.h @@ -41,10 +41,11 @@ extern note_buf_t crash_notes[]; extern void kexec_smp_wait(void); /* get and clear naca physid, wait for master to copy new code to 0 */ extern void __init kexec_setup(void); -#else -struct kimage; -extern void machine_kexec_simple(struct kimage *image); #endif +struct kimage; +extern void default_machine_kexec(struct kimage *image); +extern int default_machine_kexec_prepare(struct kimage *image); + #endif /* ! __ASSEMBLY__ */ #endif /* _ASM_POWERPC_KEXEC_H */ diff --git a/include/asm-powerpc/machdep.h b/include/asm-powerpc/machdep.h index c011abb..d6a1a2b 100644 --- a/include/asm-powerpc/machdep.h +++ b/include/asm-powerpc/machdep.h @@ -27,6 +27,9 @@ struct device_node; struct iommu_table; struct rtc_time; struct file; +#ifdef CONFIG_KEXEC +struct kimage; +#endif #ifdef CONFIG_SMP struct smp_ops_t { @@ -207,14 +210,14 @@ struct machdep_calls { /* this is for modules, since _machine can be a define -- Cort */ int ppc_machine; +#endif /* CONFIG_PPC32 */ -#ifdef CONFIG_KEXEC /* Called to shutdown machine specific hardware not already controlled * by other drivers. - * XXX Should we move this one out of kexec scope? */ void (*machine_shutdown)(void); +#ifdef CONFIG_KEXEC /* Called to do the minimal shutdown needed to run a kexec'd kernel * to run successfully. * XXX Should we move this one out of kexec scope? @@ -237,7 +240,6 @@ struct machdep_calls { */ void (*machine_kexec)(struct kimage *image); #endif /* CONFIG_KEXEC */ -#endif /* CONFIG_PPC32 */ }; extern void default_idle(void); diff --git a/include/asm-powerpc/mmu.h b/include/asm-powerpc/mmu.h index c1b4bba..588c180 100644 --- a/include/asm-powerpc/mmu.h +++ b/include/asm-powerpc/mmu.h @@ -393,6 +393,10 @@ static inline unsigned long get_vsid(uns #define VSID_SCRAMBLE(pvsid) (((pvsid) * VSID_MULTIPLIER) % VSID_MODULUS) #define KERNEL_VSID(ea) VSID_SCRAMBLE(GET_ESID(ea)) +/* Physical address used by some IO functions */ +typedef unsigned long phys_addr_t; + + #endif /* __ASSEMBLY */ #endif /* CONFIG_PPC64 */ diff --git a/include/asm-powerpc/paca.h b/include/asm-powerpc/paca.h index 92c765c..59a41db 100644 --- a/include/asm-powerpc/paca.h +++ b/include/asm-powerpc/paca.h @@ -18,7 +18,6 @@ #include #include #include -#include #include register struct paca_struct *local_paca asm("r13"); @@ -31,9 +30,9 @@ struct task_struct; * * This structure is not directly accessed by firmware or the service * processor except for the first two pointers that point to the - * lppaca area and the ItLpRegSave area for this CPU. Both the - * lppaca and ItLpRegSave objects are currently contained within the - * PACA but they do not need to be. + * lppaca area and the ItLpRegSave area for this CPU. The lppaca + * object is currently contained within the PACA but it doesn't need + * to be. */ struct paca_struct { /* @@ -48,7 +47,9 @@ struct paca_struct { * accessed by the firmware */ struct lppaca *lppaca_ptr; /* Pointer to LpPaca for PLIC */ - struct ItLpRegSave *reg_save_ptr; /* Pointer to LpRegSave for PLIC */ +#ifdef CONFIG_PPC_ISERIES + void *reg_save_ptr; /* Pointer to LpRegSave for PLIC */ +#endif /* CONFIG_PPC_ISERIES */ /* * MAGIC: the spinlock functions in arch/ppc64/lib/locks.c @@ -59,7 +60,6 @@ struct paca_struct { u16 lock_token; /* Constant 0x8000, used in locks */ u16 paca_index; /* Logical processor number */ - u32 default_decr; /* Default decrementer value */ u64 kernel_toc; /* Kernel TOC address */ u64 stab_real; /* Absolute address of segment table */ u64 stab_addr; /* Virtual address of segment table */ @@ -90,14 +90,10 @@ struct paca_struct { struct task_struct *__current; /* Pointer to current */ u64 kstack; /* Saved Kernel stack addr */ u64 stab_rr; /* stab/slb round-robin counter */ - u64 next_jiffy_update_tb; /* TB value for next jiffy update */ u64 saved_r1; /* r1 save for RTAS calls */ u64 saved_msr; /* MSR saved here by enter_rtas */ u8 proc_enabled; /* irq soft-enable flag */ - /* not yet used */ - u64 exdsi[8]; /* used for linear mapping hash table misses */ - /* * iSeries structure which the hypervisor knows about - * this structure should not cross a page boundary. @@ -110,9 +106,6 @@ struct paca_struct { * cross a page boundary. */ struct lppaca lppaca __attribute__((__aligned__(0x400))); -#ifdef CONFIG_PPC_ISERIES - struct ItLpRegSave reg_save; -#endif }; extern struct paca_struct paca[]; diff --git a/include/asm-powerpc/pci-bridge.h b/include/asm-powerpc/pci-bridge.h index 223ec7b..69fadd4 100644 --- a/include/asm-powerpc/pci-bridge.h +++ b/include/asm-powerpc/pci-bridge.h @@ -60,16 +60,17 @@ struct pci_controller; struct iommu_table; struct pci_dn { - int busno; /* for pci devices */ - int bussubno; /* for pci devices */ - int devfn; /* for pci devices */ + int busno; /* pci bus number */ + int bussubno; /* pci subordinate bus number */ + int devfn; /* pci device and function number */ + int class_code; /* pci device class */ #ifdef CONFIG_PPC_PSERIES int eeh_mode; /* See eeh.h for possible EEH_MODEs */ int eeh_config_addr; + int eeh_pe_config_addr; /* new-style partition endpoint address */ int eeh_check_count; /* # times driver ignored error */ int eeh_freeze_count; /* # times this device froze up. */ - int eeh_is_bridge; /* device is pci-to-pci bridge */ #endif int pci_ext_config_space; /* for pci devices */ struct pci_controller *phb; /* for pci devices */ @@ -125,9 +126,18 @@ static inline struct device_node *pci_bu return bus->sysdata; /* Must be root bus (PHB) */ } +/** Find the bus corresponding to the indicated device node */ +struct pci_bus * pcibios_find_pci_bus(struct device_node *dn); + extern void pci_process_bridge_OF_ranges(struct pci_controller *hose, struct device_node *dev, int primary); +/** Remove all of the PCI devices under this bus */ +void pcibios_remove_pci_devices(struct pci_bus *bus); + +/** Discover new pci devices under this bus, and add them */ +void pcibios_add_pci_devices(struct pci_bus * bus); + extern int pcibios_remove_root_bus(struct pci_controller *phb); extern void phbs_remap_io(void); @@ -140,6 +150,9 @@ static inline struct pci_controller *pci return PCI_DN(busdn)->phb; } +extern struct pci_controller* +pci_find_hose_for_OF_device(struct device_node* node); + extern struct pci_controller * pcibios_alloc_controller(struct device_node *dev); extern void pcibios_free_controller(struct pci_controller *phb); diff --git a/include/asm-powerpc/pci.h b/include/asm-powerpc/pci.h index d5934a0..5d2c9e6 100644 --- a/include/asm-powerpc/pci.h +++ b/include/asm-powerpc/pci.h @@ -216,6 +216,8 @@ extern int remap_bus_range(struct pci_bu extern void pcibios_fixup_device_resources(struct pci_dev *dev, struct pci_bus *bus); +extern void pcibios_claim_one_bus(struct pci_bus *b); + extern struct pci_controller *init_phb_dynamic(struct device_node *dn); extern struct pci_dev *of_create_pci_dev(struct device_node *node, diff --git a/include/asm-powerpc/ppc-pci.h b/include/asm-powerpc/ppc-pci.h index 36cdc86..472ff0c 100644 --- a/include/asm-powerpc/ppc-pci.h +++ b/include/asm-powerpc/ppc-pci.h @@ -51,6 +51,21 @@ extern unsigned long pci_probe_only; /* ---- EEH internal-use-only related routines ---- */ #ifdef CONFIG_EEH + +void pci_addr_cache_insert_device(struct pci_dev *dev); +void pci_addr_cache_remove_device(struct pci_dev *dev); +void pci_addr_cache_build(void); +struct pci_dev *pci_get_device_by_addr(unsigned long addr); + +/** + * eeh_slot_error_detail -- record and EEH error condition to the log + * @severity: 1 if temporary, 2 if permanent failure. + * + * Obtains the the EEH error details from the RTAS subsystem, + * and then logs these details with the RTAS error log system. + */ +void eeh_slot_error_detail (struct pci_dn *pdn, int severity); + /** * rtas_set_slot_reset -- unfreeze a frozen slot * @@ -58,8 +73,10 @@ extern unsigned long pci_probe_only; * does this by asserting the PCI #RST line for 1/8th of * a second; this routine will sleep while the adapter is * being reset. + * + * Returns a non-zero value if the reset failed. */ -void rtas_set_slot_reset (struct pci_dn *); +int rtas_set_slot_reset (struct pci_dn *); /** * eeh_restore_bars - Restore device configuration info. @@ -83,6 +100,7 @@ void eeh_restore_bars(struct pci_dn *); void rtas_configure_bridge(struct pci_dn *); int rtas_write_config(struct pci_dn *, int where, int size, u32 val); +int rtas_read_config(struct pci_dn *, int where, int size, u32 *val); /** * mark and clear slots: find "partition endpoint" PE and set or @@ -91,6 +109,9 @@ int rtas_write_config(struct pci_dn *, i void eeh_mark_slot (struct device_node *dn, int mode_flag); void eeh_clear_slot (struct device_node *dn, int mode_flag); +/* Find the associated "Partiationable Endpoint" PE */ +struct device_node * find_device_pe(struct device_node *dn); + #endif #endif /* _ASM_POWERPC_PPC_PCI_H */ diff --git a/include/asm-powerpc/processor.h b/include/asm-powerpc/processor.h index d12382d..f3db984 100644 --- a/include/asm-powerpc/processor.h +++ b/include/asm-powerpc/processor.h @@ -68,7 +68,6 @@ extern int _chrp_type; * vendor. Board revision is also made available. This will be moved * elsewhere soon */ -extern unsigned char ucSystemType; extern unsigned char ucBoardRev; extern unsigned char ucBoardRevMaj, ucBoardRevMin; diff --git a/include/asm-powerpc/prom.h b/include/asm-powerpc/prom.h index f999df1..fb732c9 100644 --- a/include/asm-powerpc/prom.h +++ b/include/asm-powerpc/prom.h @@ -223,5 +223,14 @@ extern struct resource *request_OF_resou int index, const char* name_postfix); extern int release_OF_resource(struct device_node* node, int index); +/* + * Address translation function(s) + */ +#define OF_BAD_ADDR ((u64)-1) +extern u64 of_translate_address(struct device_node *np, u32 *addr); +extern u32 *of_get_address(struct device_node *dev, int index, u64 *size); +extern u32 *of_get_pci_address(struct device_node *dev, int bar_no, u64 *size); + + #endif /* __KERNEL__ */ #endif /* _POWERPC_PROM_H */ diff --git a/include/asm-powerpc/ptrace.h b/include/asm-powerpc/ptrace.h index 1f7ecdb..9c550b3 100644 --- a/include/asm-powerpc/ptrace.h +++ b/include/asm-powerpc/ptrace.h @@ -87,7 +87,7 @@ extern unsigned long profile_pc(struct p #define force_successful_syscall_return() \ do { \ - current_thread_info()->syscall_noerror = 1; \ + set_thread_flag(TIF_NOERROR); \ } while(0) /* diff --git a/include/asm-powerpc/serial.h b/include/asm-powerpc/serial.h index b273d63..6dc9546 100644 --- a/include/asm-powerpc/serial.h +++ b/include/asm-powerpc/serial.h @@ -15,4 +15,6 @@ /* Default baud base if not found in device-tree */ #define BASE_BAUD ( 1843200 / 16 ) +extern void find_legacy_serial_ports(void); + #endif /* _PPC64_SERIAL_H */ diff --git a/include/asm-powerpc/spu.h b/include/asm-powerpc/spu.h new file mode 100644 index 0000000..092ec97 --- /dev/null +++ b/include/asm-powerpc/spu.h @@ -0,0 +1,579 @@ +/* + * SPU core / file system interface and HW structures + * + * (C) Copyright IBM Deutschland Entwicklung GmbH 2005 + * + * Author: Arnd Bergmann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _SPU_H +#define _SPU_H +#include +#include +#include + +#define LS_ORDER (6) /* 256 kb */ + +#define LS_SIZE (PAGE_SIZE << LS_ORDER) +#define LS_ADDR_MASK (LS_SIZE - 1) + +#define MFC_PUT_CMD 0x20 +#define MFC_PUTS_CMD 0x28 +#define MFC_PUTR_CMD 0x30 +#define MFC_PUTF_CMD 0x22 +#define MFC_PUTB_CMD 0x21 +#define MFC_PUTFS_CMD 0x2A +#define MFC_PUTBS_CMD 0x29 +#define MFC_PUTRF_CMD 0x32 +#define MFC_PUTRB_CMD 0x31 +#define MFC_PUTL_CMD 0x24 +#define MFC_PUTRL_CMD 0x34 +#define MFC_PUTLF_CMD 0x26 +#define MFC_PUTLB_CMD 0x25 +#define MFC_PUTRLF_CMD 0x36 +#define MFC_PUTRLB_CMD 0x35 + +#define MFC_GET_CMD 0x40 +#define MFC_GETS_CMD 0x48 +#define MFC_GETF_CMD 0x42 +#define MFC_GETB_CMD 0x41 +#define MFC_GETFS_CMD 0x4A +#define MFC_GETBS_CMD 0x49 +#define MFC_GETL_CMD 0x44 +#define MFC_GETLF_CMD 0x46 +#define MFC_GETLB_CMD 0x45 + +#define MFC_SDCRT_CMD 0x80 +#define MFC_SDCRTST_CMD 0x81 +#define MFC_SDCRZ_CMD 0x89 +#define MFC_SDCRS_CMD 0x8D +#define MFC_SDCRF_CMD 0x8F + +#define MFC_GETLLAR_CMD 0xD0 +#define MFC_PUTLLC_CMD 0xB4 +#define MFC_PUTLLUC_CMD 0xB0 +#define MFC_PUTQLLUC_CMD 0xB8 +#define MFC_SNDSIG_CMD 0xA0 +#define MFC_SNDSIGB_CMD 0xA1 +#define MFC_SNDSIGF_CMD 0xA2 +#define MFC_BARRIER_CMD 0xC0 +#define MFC_EIEIO_CMD 0xC8 +#define MFC_SYNC_CMD 0xCC + +#define MFC_MIN_DMA_SIZE_SHIFT 4 /* 16 bytes */ +#define MFC_MAX_DMA_SIZE_SHIFT 14 /* 16384 bytes */ +#define MFC_MIN_DMA_SIZE (1 << MFC_MIN_DMA_SIZE_SHIFT) +#define MFC_MAX_DMA_SIZE (1 << MFC_MAX_DMA_SIZE_SHIFT) +#define MFC_MIN_DMA_SIZE_MASK (MFC_MIN_DMA_SIZE - 1) +#define MFC_MAX_DMA_SIZE_MASK (MFC_MAX_DMA_SIZE - 1) +#define MFC_MIN_DMA_LIST_SIZE 0x0008 /* 8 bytes */ +#define MFC_MAX_DMA_LIST_SIZE 0x4000 /* 16K bytes */ + +#define MFC_TAGID_TO_TAGMASK(tag_id) (1 << (tag_id & 0x1F)) + +/* Events for Channels 0-2 */ +#define MFC_DMA_TAG_STATUS_UPDATE_EVENT 0x00000001 +#define MFC_DMA_TAG_CMD_STALL_NOTIFY_EVENT 0x00000002 +#define MFC_DMA_QUEUE_AVAILABLE_EVENT 0x00000008 +#define MFC_SPU_MAILBOX_WRITTEN_EVENT 0x00000010 +#define MFC_DECREMENTER_EVENT 0x00000020 +#define MFC_PU_INT_MAILBOX_AVAILABLE_EVENT 0x00000040 +#define MFC_PU_MAILBOX_AVAILABLE_EVENT 0x00000080 +#define MFC_SIGNAL_2_EVENT 0x00000100 +#define MFC_SIGNAL_1_EVENT 0x00000200 +#define MFC_LLR_LOST_EVENT 0x00000400 +#define MFC_PRIV_ATTN_EVENT 0x00000800 +#define MFC_MULTI_SRC_EVENT 0x00001000 + +/* Flags indicating progress during context switch. */ +#define SPU_CONTEXT_SWITCH_PENDING_nr 0UL +#define SPU_CONTEXT_SWITCH_ACTIVE_nr 1UL +#define SPU_CONTEXT_SWITCH_PENDING (1UL << SPU_CONTEXT_SWITCH_PENDING_nr) +#define SPU_CONTEXT_SWITCH_ACTIVE (1UL << SPU_CONTEXT_SWITCH_ACTIVE_nr) + +struct spu_context; +struct spu_runqueue; + +struct spu { + char *name; + unsigned long local_store_phys; + u8 *local_store; + struct spu_problem __iomem *problem; + struct spu_priv1 __iomem *priv1; + struct spu_priv2 __iomem *priv2; + struct list_head list; + struct list_head sched_list; + int number; + u32 isrc; + u32 node; + u64 flags; + u64 dar; + u64 dsisr; + struct kref kref; + size_t ls_size; + unsigned int slb_replace; + struct mm_struct *mm; + struct spu_context *ctx; + struct spu_runqueue *rq; + pid_t pid; + int prio; + int class_0_pending; + spinlock_t register_lock; + + u32 stop_code; + wait_queue_head_t stop_wq; + void (* wbox_callback)(struct spu *spu); + void (* ibox_callback)(struct spu *spu); + + char irq_c0[8]; + char irq_c1[8]; + char irq_c2[8]; +}; + +struct spu *spu_alloc(void); +void spu_free(struct spu *spu); +int spu_run(struct spu *spu); + +extern struct spufs_calls { + asmlinkage long (*create_thread)(const char __user *name, + unsigned int flags, mode_t mode); + asmlinkage long (*spu_run)(struct file *filp, __u32 __user *unpc, + __u32 __user *ustatus); + struct module *owner; +} spufs_calls; + +#ifdef CONFIG_SPU_FS_MODULE +int register_spu_syscalls(struct spufs_calls *calls); +void unregister_spu_syscalls(struct spufs_calls *calls); +#else +static inline int register_spu_syscalls(struct spufs_calls *calls) +{ + return 0; +} +static inline void unregister_spu_syscalls(struct spufs_calls *calls) +{ +} +#endif /* MODULE */ + + +/* + * This defines the Local Store, Problem Area and Privlege Area of an SPU. + */ + +union mfc_tag_size_class_cmd { + struct { + u16 mfc_size; + u16 mfc_tag; + u8 pad; + u8 mfc_rclassid; + u16 mfc_cmd; + } u; + struct { + u32 mfc_size_tag32; + u32 mfc_class_cmd32; + } by32; + u64 all64; +}; + +struct mfc_cq_sr { + u64 mfc_cq_data0_RW; + u64 mfc_cq_data1_RW; + u64 mfc_cq_data2_RW; + u64 mfc_cq_data3_RW; +}; + +struct spu_problem { +#define MS_SYNC_PENDING 1L + u64 spc_mssync_RW; /* 0x0000 */ + u8 pad_0x0008_0x3000[0x3000 - 0x0008]; + + /* DMA Area */ + u8 pad_0x3000_0x3004[0x4]; /* 0x3000 */ + u32 mfc_lsa_W; /* 0x3004 */ + u64 mfc_ea_W; /* 0x3008 */ + union mfc_tag_size_class_cmd mfc_union_W; /* 0x3010 */ + u8 pad_0x3018_0x3104[0xec]; /* 0x3018 */ + u32 dma_qstatus_R; /* 0x3104 */ + u8 pad_0x3108_0x3204[0xfc]; /* 0x3108 */ + u32 dma_querytype_RW; /* 0x3204 */ + u8 pad_0x3208_0x321c[0x14]; /* 0x3208 */ + u32 dma_querymask_RW; /* 0x321c */ + u8 pad_0x3220_0x322c[0xc]; /* 0x3220 */ + u32 dma_tagstatus_R; /* 0x322c */ +#define DMA_TAGSTATUS_INTR_ANY 1u +#define DMA_TAGSTATUS_INTR_ALL 2u + u8 pad_0x3230_0x4000[0x4000 - 0x3230]; /* 0x3230 */ + + /* SPU Control Area */ + u8 pad_0x4000_0x4004[0x4]; /* 0x4000 */ + u32 pu_mb_R; /* 0x4004 */ + u8 pad_0x4008_0x400c[0x4]; /* 0x4008 */ + u32 spu_mb_W; /* 0x400c */ + u8 pad_0x4010_0x4014[0x4]; /* 0x4010 */ + u32 mb_stat_R; /* 0x4014 */ + u8 pad_0x4018_0x401c[0x4]; /* 0x4018 */ + u32 spu_runcntl_RW; /* 0x401c */ +#define SPU_RUNCNTL_STOP 0L +#define SPU_RUNCNTL_RUNNABLE 1L + u8 pad_0x4020_0x4024[0x4]; /* 0x4020 */ + u32 spu_status_R; /* 0x4024 */ +#define SPU_STOP_STATUS_SHIFT 16 +#define SPU_STATUS_STOPPED 0x0 +#define SPU_STATUS_RUNNING 0x1 +#define SPU_STATUS_STOPPED_BY_STOP 0x2 +#define SPU_STATUS_STOPPED_BY_HALT 0x4 +#define SPU_STATUS_WAITING_FOR_CHANNEL 0x8 +#define SPU_STATUS_SINGLE_STEP 0x10 +#define SPU_STATUS_INVALID_INSTR 0x20 +#define SPU_STATUS_INVALID_CH 0x40 +#define SPU_STATUS_ISOLATED_STATE 0x80 +#define SPU_STATUS_ISOLATED_LOAD_STAUTUS 0x200 +#define SPU_STATUS_ISOLATED_EXIT_STAUTUS 0x400 + u8 pad_0x4028_0x402c[0x4]; /* 0x4028 */ + u32 spu_spe_R; /* 0x402c */ + u8 pad_0x4030_0x4034[0x4]; /* 0x4030 */ + u32 spu_npc_RW; /* 0x4034 */ + u8 pad_0x4038_0x14000[0x14000 - 0x4038]; /* 0x4038 */ + + /* Signal Notification Area */ + u8 pad_0x14000_0x1400c[0xc]; /* 0x14000 */ + u32 signal_notify1; /* 0x1400c */ + u8 pad_0x14010_0x1c00c[0x7ffc]; /* 0x14010 */ + u32 signal_notify2; /* 0x1c00c */ +} __attribute__ ((aligned(0x20000))); + +/* SPU Privilege 2 State Area */ +struct spu_priv2 { + /* MFC Registers */ + u8 pad_0x0000_0x1100[0x1100 - 0x0000]; /* 0x0000 */ + + /* SLB Management Registers */ + u8 pad_0x1100_0x1108[0x8]; /* 0x1100 */ + u64 slb_index_W; /* 0x1108 */ +#define SLB_INDEX_MASK 0x7L + u64 slb_esid_RW; /* 0x1110 */ + u64 slb_vsid_RW; /* 0x1118 */ +#define SLB_VSID_SUPERVISOR_STATE (0x1ull << 11) +#define SLB_VSID_SUPERVISOR_STATE_MASK (0x1ull << 11) +#define SLB_VSID_PROBLEM_STATE (0x1ull << 10) +#define SLB_VSID_PROBLEM_STATE_MASK (0x1ull << 10) +#define SLB_VSID_EXECUTE_SEGMENT (0x1ull << 9) +#define SLB_VSID_NO_EXECUTE_SEGMENT (0x1ull << 9) +#define SLB_VSID_EXECUTE_SEGMENT_MASK (0x1ull << 9) +#define SLB_VSID_4K_PAGE (0x0 << 8) +#define SLB_VSID_LARGE_PAGE (0x1ull << 8) +#define SLB_VSID_PAGE_SIZE_MASK (0x1ull << 8) +#define SLB_VSID_CLASS_MASK (0x1ull << 7) +#define SLB_VSID_VIRTUAL_PAGE_SIZE_MASK (0x1ull << 6) + u64 slb_invalidate_entry_W; /* 0x1120 */ + u64 slb_invalidate_all_W; /* 0x1128 */ + u8 pad_0x1130_0x2000[0x2000 - 0x1130]; /* 0x1130 */ + + /* Context Save / Restore Area */ + struct mfc_cq_sr spuq[16]; /* 0x2000 */ + struct mfc_cq_sr puq[8]; /* 0x2200 */ + u8 pad_0x2300_0x3000[0x3000 - 0x2300]; /* 0x2300 */ + + /* MFC Control */ + u64 mfc_control_RW; /* 0x3000 */ +#define MFC_CNTL_RESUME_DMA_QUEUE (0ull << 0) +#define MFC_CNTL_SUSPEND_DMA_QUEUE (1ull << 0) +#define MFC_CNTL_SUSPEND_DMA_QUEUE_MASK (1ull << 0) +#define MFC_CNTL_NORMAL_DMA_QUEUE_OPERATION (0ull << 8) +#define MFC_CNTL_SUSPEND_IN_PROGRESS (1ull << 8) +#define MFC_CNTL_SUSPEND_COMPLETE (3ull << 8) +#define MFC_CNTL_SUSPEND_DMA_STATUS_MASK (3ull << 8) +#define MFC_CNTL_DMA_QUEUES_EMPTY (1ull << 14) +#define MFC_CNTL_DMA_QUEUES_EMPTY_MASK (1ull << 14) +#define MFC_CNTL_PURGE_DMA_REQUEST (1ull << 15) +#define MFC_CNTL_PURGE_DMA_IN_PROGRESS (1ull << 24) +#define MFC_CNTL_PURGE_DMA_COMPLETE (3ull << 24) +#define MFC_CNTL_PURGE_DMA_STATUS_MASK (3ull << 24) +#define MFC_CNTL_RESTART_DMA_COMMAND (1ull << 32) +#define MFC_CNTL_DMA_COMMAND_REISSUE_PENDING (1ull << 32) +#define MFC_CNTL_DMA_COMMAND_REISSUE_STATUS_MASK (1ull << 32) +#define MFC_CNTL_MFC_PRIVILEGE_STATE (2ull << 33) +#define MFC_CNTL_MFC_PROBLEM_STATE (3ull << 33) +#define MFC_CNTL_MFC_KEY_PROTECTION_STATE_MASK (3ull << 33) +#define MFC_CNTL_DECREMENTER_HALTED (1ull << 35) +#define MFC_CNTL_DECREMENTER_RUNNING (1ull << 40) +#define MFC_CNTL_DECREMENTER_STATUS_MASK (1ull << 40) + u8 pad_0x3008_0x4000[0x4000 - 0x3008]; /* 0x3008 */ + + /* Interrupt Mailbox */ + u64 puint_mb_R; /* 0x4000 */ + u8 pad_0x4008_0x4040[0x4040 - 0x4008]; /* 0x4008 */ + + /* SPU Control */ + u64 spu_privcntl_RW; /* 0x4040 */ +#define SPU_PRIVCNTL_MODE_NORMAL (0x0ull << 0) +#define SPU_PRIVCNTL_MODE_SINGLE_STEP (0x1ull << 0) +#define SPU_PRIVCNTL_MODE_MASK (0x1ull << 0) +#define SPU_PRIVCNTL_NO_ATTENTION_EVENT (0x0ull << 1) +#define SPU_PRIVCNTL_ATTENTION_EVENT (0x1ull << 1) +#define SPU_PRIVCNTL_ATTENTION_EVENT_MASK (0x1ull << 1) +#define SPU_PRIVCNT_LOAD_REQUEST_NORMAL (0x0ull << 2) +#define SPU_PRIVCNT_LOAD_REQUEST_ENABLE_MASK (0x1ull << 2) + u8 pad_0x4048_0x4058[0x10]; /* 0x4048 */ + u64 spu_lslr_RW; /* 0x4058 */ + u64 spu_chnlcntptr_RW; /* 0x4060 */ + u64 spu_chnlcnt_RW; /* 0x4068 */ + u64 spu_chnldata_RW; /* 0x4070 */ + u64 spu_cfg_RW; /* 0x4078 */ + u8 pad_0x4080_0x5000[0x5000 - 0x4080]; /* 0x4080 */ + + /* PV2_ImplRegs: Implementation-specific privileged-state 2 regs */ + u64 spu_pm_trace_tag_status_RW; /* 0x5000 */ + u64 spu_tag_status_query_RW; /* 0x5008 */ +#define TAG_STATUS_QUERY_CONDITION_BITS (0x3ull << 32) +#define TAG_STATUS_QUERY_MASK_BITS (0xffffffffull) + u64 spu_cmd_buf1_RW; /* 0x5010 */ +#define SPU_COMMAND_BUFFER_1_LSA_BITS (0x7ffffull << 32) +#define SPU_COMMAND_BUFFER_1_EAH_BITS (0xffffffffull) + u64 spu_cmd_buf2_RW; /* 0x5018 */ +#define SPU_COMMAND_BUFFER_2_EAL_BITS ((0xffffffffull) << 32) +#define SPU_COMMAND_BUFFER_2_TS_BITS (0xffffull << 16) +#define SPU_COMMAND_BUFFER_2_TAG_BITS (0x3full) + u64 spu_atomic_status_RW; /* 0x5020 */ +} __attribute__ ((aligned(0x20000))); + +/* SPU Privilege 1 State Area */ +struct spu_priv1 { + /* Control and Configuration Area */ + u64 mfc_sr1_RW; /* 0x000 */ +#define MFC_STATE1_LOCAL_STORAGE_DECODE_MASK 0x01ull +#define MFC_STATE1_BUS_TLBIE_MASK 0x02ull +#define MFC_STATE1_REAL_MODE_OFFSET_ENABLE_MASK 0x04ull +#define MFC_STATE1_PROBLEM_STATE_MASK 0x08ull +#define MFC_STATE1_RELOCATE_MASK 0x10ull +#define MFC_STATE1_MASTER_RUN_CONTROL_MASK 0x20ull + u64 mfc_lpid_RW; /* 0x008 */ + u64 spu_idr_RW; /* 0x010 */ + u64 mfc_vr_RO; /* 0x018 */ +#define MFC_VERSION_BITS (0xffff << 16) +#define MFC_REVISION_BITS (0xffff) +#define MFC_GET_VERSION_BITS(vr) (((vr) & MFC_VERSION_BITS) >> 16) +#define MFC_GET_REVISION_BITS(vr) ((vr) & MFC_REVISION_BITS) + u64 spu_vr_RO; /* 0x020 */ +#define SPU_VERSION_BITS (0xffff << 16) +#define SPU_REVISION_BITS (0xffff) +#define SPU_GET_VERSION_BITS(vr) (vr & SPU_VERSION_BITS) >> 16 +#define SPU_GET_REVISION_BITS(vr) (vr & SPU_REVISION_BITS) + u8 pad_0x28_0x100[0x100 - 0x28]; /* 0x28 */ + + + /* Interrupt Area */ + u64 int_mask_class0_RW; /* 0x100 */ +#define CLASS0_ENABLE_DMA_ALIGNMENT_INTR 0x1L +#define CLASS0_ENABLE_INVALID_DMA_COMMAND_INTR 0x2L +#define CLASS0_ENABLE_SPU_ERROR_INTR 0x4L +#define CLASS0_ENABLE_MFC_FIR_INTR 0x8L + u64 int_mask_class1_RW; /* 0x108 */ +#define CLASS1_ENABLE_SEGMENT_FAULT_INTR 0x1L +#define CLASS1_ENABLE_STORAGE_FAULT_INTR 0x2L +#define CLASS1_ENABLE_LS_COMPARE_SUSPEND_ON_GET_INTR 0x4L +#define CLASS1_ENABLE_LS_COMPARE_SUSPEND_ON_PUT_INTR 0x8L + u64 int_mask_class2_RW; /* 0x110 */ +#define CLASS2_ENABLE_MAILBOX_INTR 0x1L +#define CLASS2_ENABLE_SPU_STOP_INTR 0x2L +#define CLASS2_ENABLE_SPU_HALT_INTR 0x4L +#define CLASS2_ENABLE_SPU_DMA_TAG_GROUP_COMPLETE_INTR 0x8L + u8 pad_0x118_0x140[0x28]; /* 0x118 */ + u64 int_stat_class0_RW; /* 0x140 */ + u64 int_stat_class1_RW; /* 0x148 */ + u64 int_stat_class2_RW; /* 0x150 */ + u8 pad_0x158_0x180[0x28]; /* 0x158 */ + u64 int_route_RW; /* 0x180 */ + + /* Interrupt Routing */ + u8 pad_0x188_0x200[0x200 - 0x188]; /* 0x188 */ + + /* Atomic Unit Control Area */ + u64 mfc_atomic_flush_RW; /* 0x200 */ +#define mfc_atomic_flush_enable 0x1L + u8 pad_0x208_0x280[0x78]; /* 0x208 */ + u64 resource_allocation_groupID_RW; /* 0x280 */ + u64 resource_allocation_enable_RW; /* 0x288 */ + u8 pad_0x290_0x3c8[0x3c8 - 0x290]; /* 0x290 */ + + /* SPU_Cache_ImplRegs: Implementation-dependent cache registers */ + + u64 smf_sbi_signal_sel; /* 0x3c8 */ +#define smf_sbi_mask_lsb 56 +#define smf_sbi_shift (63 - smf_sbi_mask_lsb) +#define smf_sbi_mask (0x301LL << smf_sbi_shift) +#define smf_sbi_bus0_bits (0x001LL << smf_sbi_shift) +#define smf_sbi_bus2_bits (0x100LL << smf_sbi_shift) +#define smf_sbi2_bus0_bits (0x201LL << smf_sbi_shift) +#define smf_sbi2_bus2_bits (0x300LL << smf_sbi_shift) + u64 smf_ato_signal_sel; /* 0x3d0 */ +#define smf_ato_mask_lsb 35 +#define smf_ato_shift (63 - smf_ato_mask_lsb) +#define smf_ato_mask (0x3LL << smf_ato_shift) +#define smf_ato_bus0_bits (0x2LL << smf_ato_shift) +#define smf_ato_bus2_bits (0x1LL << smf_ato_shift) + u8 pad_0x3d8_0x400[0x400 - 0x3d8]; /* 0x3d8 */ + + /* TLB Management Registers */ + u64 mfc_sdr_RW; /* 0x400 */ + u8 pad_0x408_0x500[0xf8]; /* 0x408 */ + u64 tlb_index_hint_RO; /* 0x500 */ + u64 tlb_index_W; /* 0x508 */ + u64 tlb_vpn_RW; /* 0x510 */ + u64 tlb_rpn_RW; /* 0x518 */ + u8 pad_0x520_0x540[0x20]; /* 0x520 */ + u64 tlb_invalidate_entry_W; /* 0x540 */ + u64 tlb_invalidate_all_W; /* 0x548 */ + u8 pad_0x550_0x580[0x580 - 0x550]; /* 0x550 */ + + /* SPU_MMU_ImplRegs: Implementation-dependent MMU registers */ + u64 smm_hid; /* 0x580 */ +#define PAGE_SIZE_MASK 0xf000000000000000ull +#define PAGE_SIZE_16MB_64KB 0x2000000000000000ull + u8 pad_0x588_0x600[0x600 - 0x588]; /* 0x588 */ + + /* MFC Status/Control Area */ + u64 mfc_accr_RW; /* 0x600 */ +#define MFC_ACCR_EA_ACCESS_GET (1 << 0) +#define MFC_ACCR_EA_ACCESS_PUT (1 << 1) +#define MFC_ACCR_LS_ACCESS_GET (1 << 3) +#define MFC_ACCR_LS_ACCESS_PUT (1 << 4) + u8 pad_0x608_0x610[0x8]; /* 0x608 */ + u64 mfc_dsisr_RW; /* 0x610 */ +#define MFC_DSISR_PTE_NOT_FOUND (1 << 30) +#define MFC_DSISR_ACCESS_DENIED (1 << 27) +#define MFC_DSISR_ATOMIC (1 << 26) +#define MFC_DSISR_ACCESS_PUT (1 << 25) +#define MFC_DSISR_ADDR_MATCH (1 << 22) +#define MFC_DSISR_LS (1 << 17) +#define MFC_DSISR_L (1 << 16) +#define MFC_DSISR_ADDRESS_OVERFLOW (1 << 0) + u8 pad_0x618_0x620[0x8]; /* 0x618 */ + u64 mfc_dar_RW; /* 0x620 */ + u8 pad_0x628_0x700[0x700 - 0x628]; /* 0x628 */ + + /* Replacement Management Table (RMT) Area */ + u64 rmt_index_RW; /* 0x700 */ + u8 pad_0x708_0x710[0x8]; /* 0x708 */ + u64 rmt_data1_RW; /* 0x710 */ + u8 pad_0x718_0x800[0x800 - 0x718]; /* 0x718 */ + + /* Control/Configuration Registers */ + u64 mfc_dsir_R; /* 0x800 */ +#define MFC_DSIR_Q (1 << 31) +#define MFC_DSIR_SPU_QUEUE MFC_DSIR_Q + u64 mfc_lsacr_RW; /* 0x808 */ +#define MFC_LSACR_COMPARE_MASK ((~0ull) << 32) +#define MFC_LSACR_COMPARE_ADDR ((~0ull) >> 32) + u64 mfc_lscrr_R; /* 0x810 */ +#define MFC_LSCRR_Q (1 << 31) +#define MFC_LSCRR_SPU_QUEUE MFC_LSCRR_Q +#define MFC_LSCRR_QI_SHIFT 32 +#define MFC_LSCRR_QI_MASK ((~0ull) << MFC_LSCRR_QI_SHIFT) + u8 pad_0x818_0x820[0x8]; /* 0x818 */ + u64 mfc_tclass_id_RW; /* 0x820 */ +#define MFC_TCLASS_ID_ENABLE (1L << 0L) +#define MFC_TCLASS_SLOT2_ENABLE (1L << 5L) +#define MFC_TCLASS_SLOT1_ENABLE (1L << 6L) +#define MFC_TCLASS_SLOT0_ENABLE (1L << 7L) +#define MFC_TCLASS_QUOTA_2_SHIFT 8L +#define MFC_TCLASS_QUOTA_1_SHIFT 16L +#define MFC_TCLASS_QUOTA_0_SHIFT 24L +#define MFC_TCLASS_QUOTA_2_MASK (0x1FL << MFC_TCLASS_QUOTA_2_SHIFT) +#define MFC_TCLASS_QUOTA_1_MASK (0x1FL << MFC_TCLASS_QUOTA_1_SHIFT) +#define MFC_TCLASS_QUOTA_0_MASK (0x1FL << MFC_TCLASS_QUOTA_0_SHIFT) + u8 pad_0x828_0x900[0x900 - 0x828]; /* 0x828 */ + + /* Real Mode Support Registers */ + u64 mfc_rm_boundary; /* 0x900 */ + u8 pad_0x908_0x938[0x30]; /* 0x908 */ + u64 smf_dma_signal_sel; /* 0x938 */ +#define mfc_dma1_mask_lsb 41 +#define mfc_dma1_shift (63 - mfc_dma1_mask_lsb) +#define mfc_dma1_mask (0x3LL << mfc_dma1_shift) +#define mfc_dma1_bits (0x1LL << mfc_dma1_shift) +#define mfc_dma2_mask_lsb 43 +#define mfc_dma2_shift (63 - mfc_dma2_mask_lsb) +#define mfc_dma2_mask (0x3LL << mfc_dma2_shift) +#define mfc_dma2_bits (0x1LL << mfc_dma2_shift) + u8 pad_0x940_0xa38[0xf8]; /* 0x940 */ + u64 smm_signal_sel; /* 0xa38 */ +#define smm_sig_mask_lsb 12 +#define smm_sig_shift (63 - smm_sig_mask_lsb) +#define smm_sig_mask (0x3LL << smm_sig_shift) +#define smm_sig_bus0_bits (0x2LL << smm_sig_shift) +#define smm_sig_bus2_bits (0x1LL << smm_sig_shift) + u8 pad_0xa40_0xc00[0xc00 - 0xa40]; /* 0xa40 */ + + /* DMA Command Error Area */ + u64 mfc_cer_R; /* 0xc00 */ +#define MFC_CER_Q (1 << 31) +#define MFC_CER_SPU_QUEUE MFC_CER_Q + u8 pad_0xc08_0x1000[0x1000 - 0xc08]; /* 0xc08 */ + + /* PV1_ImplRegs: Implementation-dependent privileged-state 1 regs */ + /* DMA Command Error Area */ + u64 spu_ecc_cntl_RW; /* 0x1000 */ +#define SPU_ECC_CNTL_E (1ull << 0ull) +#define SPU_ECC_CNTL_ENABLE SPU_ECC_CNTL_E +#define SPU_ECC_CNTL_DISABLE (~SPU_ECC_CNTL_E & 1L) +#define SPU_ECC_CNTL_S (1ull << 1ull) +#define SPU_ECC_STOP_AFTER_ERROR SPU_ECC_CNTL_S +#define SPU_ECC_CONTINUE_AFTER_ERROR (~SPU_ECC_CNTL_S & 2L) +#define SPU_ECC_CNTL_B (1ull << 2ull) +#define SPU_ECC_BACKGROUND_ENABLE SPU_ECC_CNTL_B +#define SPU_ECC_BACKGROUND_DISABLE (~SPU_ECC_CNTL_B & 4L) +#define SPU_ECC_CNTL_I_SHIFT 3ull +#define SPU_ECC_CNTL_I_MASK (3ull << SPU_ECC_CNTL_I_SHIFT) +#define SPU_ECC_WRITE_ALWAYS (~SPU_ECC_CNTL_I & 12L) +#define SPU_ECC_WRITE_CORRECTABLE (1ull << SPU_ECC_CNTL_I_SHIFT) +#define SPU_ECC_WRITE_UNCORRECTABLE (3ull << SPU_ECC_CNTL_I_SHIFT) +#define SPU_ECC_CNTL_D (1ull << 5ull) +#define SPU_ECC_DETECTION_ENABLE SPU_ECC_CNTL_D +#define SPU_ECC_DETECTION_DISABLE (~SPU_ECC_CNTL_D & 32L) + u64 spu_ecc_stat_RW; /* 0x1008 */ +#define SPU_ECC_CORRECTED_ERROR (1ull << 0ul) +#define SPU_ECC_UNCORRECTED_ERROR (1ull << 1ul) +#define SPU_ECC_SCRUB_COMPLETE (1ull << 2ul) +#define SPU_ECC_SCRUB_IN_PROGRESS (1ull << 3ul) +#define SPU_ECC_INSTRUCTION_ERROR (1ull << 4ul) +#define SPU_ECC_DATA_ERROR (1ull << 5ul) +#define SPU_ECC_DMA_ERROR (1ull << 6ul) +#define SPU_ECC_STATUS_CNT_MASK (256ull << 8) + u64 spu_ecc_addr_RW; /* 0x1010 */ + u64 spu_err_mask_RW; /* 0x1018 */ +#define SPU_ERR_ILLEGAL_INSTR (1ull << 0ul) +#define SPU_ERR_ILLEGAL_CHANNEL (1ull << 1ul) + u8 pad_0x1020_0x1028[0x1028 - 0x1020]; /* 0x1020 */ + + /* SPU Debug-Trace Bus (DTB) Selection Registers */ + u64 spu_trig0_sel; /* 0x1028 */ + u64 spu_trig1_sel; /* 0x1030 */ + u64 spu_trig2_sel; /* 0x1038 */ + u64 spu_trig3_sel; /* 0x1040 */ + u64 spu_trace_sel; /* 0x1048 */ +#define spu_trace_sel_mask 0x1f1fLL +#define spu_trace_sel_bus0_bits 0x1000LL +#define spu_trace_sel_bus2_bits 0x0010LL + u64 spu_event0_sel; /* 0x1050 */ + u64 spu_event1_sel; /* 0x1058 */ + u64 spu_event2_sel; /* 0x1060 */ + u64 spu_event3_sel; /* 0x1068 */ + u64 spu_trace_cntl; /* 0x1070 */ +} __attribute__ ((aligned(0x2000))); + +#endif diff --git a/include/asm-powerpc/spu_csa.h b/include/asm-powerpc/spu_csa.h new file mode 100644 index 0000000..2a8af41 --- /dev/null +++ b/include/asm-powerpc/spu_csa.h @@ -0,0 +1,256 @@ +/* + * spu_csa.h: Definitions for SPU context save area (CSA). + * + * (C) Copyright IBM 2005 + * + * Author: Mark Nutter + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _SPU_CSA_H_ +#define _SPU_CSA_H_ + +/* + * Total number of 128-bit registers. + */ +#define NR_SPU_GPRS 128 +#define NR_SPU_SPRS 9 +#define NR_SPU_REGS_PAD 7 +#define NR_SPU_SPILL_REGS 144 /* GPRS + SPRS + PAD */ +#define SIZEOF_SPU_SPILL_REGS NR_SPU_SPILL_REGS * 16 + +#define SPU_SAVE_COMPLETE 0x3FFB +#define SPU_RESTORE_COMPLETE 0x3FFC + +/* + * Definitions for various 'stopped' status conditions, + * to be recreated during context restore. + */ +#define SPU_STOPPED_STATUS_P 1 +#define SPU_STOPPED_STATUS_I 2 +#define SPU_STOPPED_STATUS_H 3 +#define SPU_STOPPED_STATUS_S 4 +#define SPU_STOPPED_STATUS_S_I 5 +#define SPU_STOPPED_STATUS_S_P 6 +#define SPU_STOPPED_STATUS_P_H 7 +#define SPU_STOPPED_STATUS_P_I 8 +#define SPU_STOPPED_STATUS_R 9 + +#ifndef __ASSEMBLY__ +/** + * spu_reg128 - generic 128-bit register definition. + */ +struct spu_reg128 { + u32 slot[4]; +}; + +/** + * struct spu_lscsa - Local Store Context Save Area. + * @gprs: Array of saved registers. + * @fpcr: Saved floating point status control register. + * @decr: Saved decrementer value. + * @decr_status: Indicates decrementer run status. + * @ppu_mb: Saved PPU mailbox data. + * @ppuint_mb: Saved PPU interrupting mailbox data. + * @tag_mask: Saved tag group mask. + * @event_mask: Saved event mask. + * @srr0: Saved SRR0. + * @stopped_status: Conditions to be recreated by restore. + * @ls: Saved contents of Local Storage Area. + * + * The LSCSA represents state that is primarily saved and + * restored by SPU-side code. + */ +struct spu_lscsa { + struct spu_reg128 gprs[128]; + struct spu_reg128 fpcr; + struct spu_reg128 decr; + struct spu_reg128 decr_status; + struct spu_reg128 ppu_mb; + struct spu_reg128 ppuint_mb; + struct spu_reg128 tag_mask; + struct spu_reg128 event_mask; + struct spu_reg128 srr0; + struct spu_reg128 stopped_status; + struct spu_reg128 pad[119]; /* 'ls' must be page-aligned. */ + unsigned char ls[LS_SIZE]; +}; + +#ifdef __KERNEL__ + +/* + * struct spu_problem_collapsed - condensed problem state area, w/o pads. + */ +struct spu_problem_collapsed { + u64 spc_mssync_RW; + u32 mfc_lsa_W; + u32 unused_pad0; + u64 mfc_ea_W; + union mfc_tag_size_class_cmd mfc_union_W; + u32 dma_qstatus_R; + u32 dma_querytype_RW; + u32 dma_querymask_RW; + u32 dma_tagstatus_R; + u32 pu_mb_R; + u32 spu_mb_W; + u32 mb_stat_R; + u32 spu_runcntl_RW; + u32 spu_status_R; + u32 spu_spc_R; + u32 spu_npc_RW; + u32 signal_notify1; + u32 signal_notify2; + u32 unused_pad1; +}; + +/* + * struct spu_priv1_collapsed - condensed privileged 1 area, w/o pads. + */ +struct spu_priv1_collapsed { + u64 mfc_sr1_RW; + u64 mfc_lpid_RW; + u64 spu_idr_RW; + u64 mfc_vr_RO; + u64 spu_vr_RO; + u64 int_mask_class0_RW; + u64 int_mask_class1_RW; + u64 int_mask_class2_RW; + u64 int_stat_class0_RW; + u64 int_stat_class1_RW; + u64 int_stat_class2_RW; + u64 int_route_RW; + u64 mfc_atomic_flush_RW; + u64 resource_allocation_groupID_RW; + u64 resource_allocation_enable_RW; + u64 mfc_fir_R; + u64 mfc_fir_status_or_W; + u64 mfc_fir_status_and_W; + u64 mfc_fir_mask_R; + u64 mfc_fir_mask_or_W; + u64 mfc_fir_mask_and_W; + u64 mfc_fir_chkstp_enable_RW; + u64 smf_sbi_signal_sel; + u64 smf_ato_signal_sel; + u64 mfc_sdr_RW; + u64 tlb_index_hint_RO; + u64 tlb_index_W; + u64 tlb_vpn_RW; + u64 tlb_rpn_RW; + u64 tlb_invalidate_entry_W; + u64 tlb_invalidate_all_W; + u64 smm_hid; + u64 mfc_accr_RW; + u64 mfc_dsisr_RW; + u64 mfc_dar_RW; + u64 rmt_index_RW; + u64 rmt_data1_RW; + u64 mfc_dsir_R; + u64 mfc_lsacr_RW; + u64 mfc_lscrr_R; + u64 mfc_tclass_id_RW; + u64 mfc_rm_boundary; + u64 smf_dma_signal_sel; + u64 smm_signal_sel; + u64 mfc_cer_R; + u64 pu_ecc_cntl_RW; + u64 pu_ecc_stat_RW; + u64 spu_ecc_addr_RW; + u64 spu_err_mask_RW; + u64 spu_trig0_sel; + u64 spu_trig1_sel; + u64 spu_trig2_sel; + u64 spu_trig3_sel; + u64 spu_trace_sel; + u64 spu_event0_sel; + u64 spu_event1_sel; + u64 spu_event2_sel; + u64 spu_event3_sel; + u64 spu_trace_cntl; +}; + +/* + * struct spu_priv2_collapsed - condensed priviliged 2 area, w/o pads. + */ +struct spu_priv2_collapsed { + u64 slb_index_W; + u64 slb_esid_RW; + u64 slb_vsid_RW; + u64 slb_invalidate_entry_W; + u64 slb_invalidate_all_W; + struct mfc_cq_sr spuq[16]; + struct mfc_cq_sr puq[8]; + u64 mfc_control_RW; + u64 puint_mb_R; + u64 spu_privcntl_RW; + u64 spu_lslr_RW; + u64 spu_chnlcntptr_RW; + u64 spu_chnlcnt_RW; + u64 spu_chnldata_RW; + u64 spu_cfg_RW; + u64 spu_tag_status_query_RW; + u64 spu_cmd_buf1_RW; + u64 spu_cmd_buf2_RW; + u64 spu_atomic_status_RW; +}; + +/** + * struct spu_state + * @lscsa: Local Store Context Save Area. + * @prob: Collapsed Problem State Area, w/o pads. + * @priv1: Collapsed Privileged 1 Area, w/o pads. + * @priv2: Collapsed Privileged 2 Area, w/o pads. + * @spu_chnlcnt_RW: Array of saved channel counts. + * @spu_chnldata_RW: Array of saved channel data. + * @suspend_time: Time stamp when decrementer disabled. + * @slb_esid_RW: Array of saved SLB esid entries. + * @slb_vsid_RW: Array of saved SLB vsid entries. + * + * Structure representing the whole of the SPU + * context save area (CSA). This struct contains + * all of the state necessary to suspend and then + * later optionally resume execution of an SPU + * context. + * + * The @lscsa region is by far the largest, and is + * allocated separately so that it may either be + * pinned or mapped to/from application memory, as + * appropriate for the OS environment. + */ +struct spu_state { + struct spu_lscsa *lscsa; + struct spu_problem_collapsed prob; + struct spu_priv1_collapsed priv1; + struct spu_priv2_collapsed priv2; + u64 spu_chnlcnt_RW[32]; + u64 spu_chnldata_RW[32]; + u32 spu_mailbox_data[4]; + u32 pu_mailbox_data[1]; + unsigned long suspend_time; + u64 slb_esid_RW[8]; + u64 slb_vsid_RW[8]; + spinlock_t register_lock; +}; + +extern void spu_init_csa(struct spu_state *csa); +extern void spu_fini_csa(struct spu_state *csa); +extern int spu_save(struct spu_state *prev, struct spu *spu); +extern int spu_restore(struct spu_state *new, struct spu *spu); +extern int spu_switch(struct spu_state *prev, struct spu_state *new, + struct spu *spu); + +#endif /* __KERNEL__ */ +#endif /* !__ASSEMBLY__ */ +#endif /* _SPU_CSA_H_ */ diff --git a/include/asm-powerpc/thread_info.h b/include/asm-powerpc/thread_info.h index e525f49..ac1e80e 100644 --- a/include/asm-powerpc/thread_info.h +++ b/include/asm-powerpc/thread_info.h @@ -37,8 +37,7 @@ struct thread_info { int preempt_count; /* 0 => preemptable, <0 => BUG */ struct restart_block restart_block; - /* set by force_successful_syscall_return */ - unsigned char syscall_noerror; + void *nvgprs_frame; /* low level flags - has atomic operations done on it */ unsigned long flags ____cacheline_aligned_in_smp; }; @@ -123,6 +122,9 @@ static inline struct thread_info *curren #define TIF_SINGLESTEP 9 /* singlestepping active */ #define TIF_MEMDIE 10 #define TIF_SECCOMP 11 /* secure computing */ +#define TIF_RESTOREALL 12 /* Restore all regs (implies NOERROR) */ +#define TIF_SAVE_NVGPRS 13 /* Save r14-r31 in signal frame */ +#define TIF_NOERROR 14 /* Force successful syscall return */ /* as above, but as bit values */ #define _TIF_SYSCALL_TRACE (1< #include -extern void (*udbg_putc)(unsigned char c); -extern unsigned char (*udbg_getc)(void); +extern void (*udbg_putc)(char c); +extern char (*udbg_getc)(void); extern int (*udbg_getc_poll)(void); extern void udbg_puts(const char *s); @@ -24,8 +24,14 @@ extern int udbg_read(char *buf, int bufl extern void register_early_udbg_console(void); extern void udbg_printf(const char *fmt, ...); -extern void udbg_init_uart(void __iomem *comport, unsigned int speed); +extern void udbg_init_uart(void __iomem *comport, unsigned int speed, + unsigned int clock); +extern unsigned int udbg_probe_uart_speed(void __iomem *comport, + unsigned int clock); struct device_node; -extern void udbg_init_scc(struct device_node *np); +extern void udbg_scc_init(int force_scc); +extern int udbg_adb_init(int force_btext); +extern void udbg_adb_init_early(void); + #endif /* _ASM_POWERPC_UDBG_H */ diff --git a/include/asm-powerpc/unistd.h b/include/asm-powerpc/unistd.h index 0991dfc..19eaac3 100644 --- a/include/asm-powerpc/unistd.h +++ b/include/asm-powerpc/unistd.h @@ -296,8 +296,10 @@ #define __NR_inotify_init 275 #define __NR_inotify_add_watch 276 #define __NR_inotify_rm_watch 277 +#define __NR_spu_run 278 +#define __NR_spu_create 279 -#define __NR_syscalls 278 +#define __NR_syscalls 280 #ifdef __KERNEL__ #define __NR__exit __NR_exit diff --git a/include/asm-ppc/bseip.h b/include/asm-ppc/bseip.h deleted file mode 100644 index 691f4a5..0000000 --- a/include/asm-ppc/bseip.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * A collection of structures, addresses, and values associated with - * the Bright Star Engineering ip-Engine board. Copied from the MBX stuff. - * - * Copyright (c) 1998 Dan Malek (dmalek@jlc.net) - */ -#ifndef __MACH_BSEIP_DEFS -#define __MACH_BSEIP_DEFS - -#ifndef __ASSEMBLY__ -/* A Board Information structure that is given to a program when - * prom starts it up. - */ -typedef struct bd_info { - unsigned int bi_memstart; /* Memory start address */ - unsigned int bi_memsize; /* Memory (end) size in bytes */ - unsigned int bi_intfreq; /* Internal Freq, in Hz */ - unsigned int bi_busfreq; /* Bus Freq, in Hz */ - unsigned char bi_enetaddr[6]; - unsigned int bi_baudrate; -} bd_t; - -extern bd_t m8xx_board_info; - -/* Memory map is configured by the PROM startup. - * All we need to get started is the IMMR. - */ -#define IMAP_ADDR ((uint)0xff000000) -#define IMAP_SIZE ((uint)(64 * 1024)) -#define PCMCIA_MEM_ADDR ((uint)0x04000000) -#define PCMCIA_MEM_SIZE ((uint)(64 * 1024)) -#endif /* !__ASSEMBLY__ */ - -/* We don't use the 8259. -*/ -#define NR_8259_INTS 0 - -#endif diff --git a/include/asm-ppc/btext.h b/include/asm-ppc/btext.h index ccaefab..ed36302 100644 --- a/include/asm-ppc/btext.h +++ b/include/asm-ppc/btext.h @@ -17,7 +17,7 @@ extern unsigned long disp_BAT[2]; extern boot_infos_t disp_bi; extern int boot_text_mapped; -extern void init_boot_display(void); +extern void btext_init(boot_infos_t *bi); extern void btext_welcome(void); extern void btext_prepare_BAT(void); extern void btext_setup_display(int width, int height, int depth, int pitch, diff --git a/include/asm-ppc/ipic.h b/include/asm-ppc/ipic.h deleted file mode 100644 index 0fe396a..0000000 --- a/include/asm-ppc/ipic.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * include/asm-ppc/ipic.h - * - * IPIC external definitions and structure. - * - * Maintainer: Kumar Gala - * - * Copyright 2005 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. - */ -#ifdef __KERNEL__ -#ifndef __ASM_IPIC_H__ -#define __ASM_IPIC_H__ - -#include - -/* Flags when we init the IPIC */ -#define IPIC_SPREADMODE_GRP_A 0x00000001 -#define IPIC_SPREADMODE_GRP_D 0x00000002 -#define IPIC_SPREADMODE_MIX_A 0x00000004 -#define IPIC_SPREADMODE_MIX_B 0x00000008 -#define IPIC_DISABLE_MCP_OUT 0x00000010 -#define IPIC_IRQ0_MCP 0x00000020 - -/* IPIC registers offsets */ -#define IPIC_SICFR 0x00 /* System Global Interrupt Configuration Register */ -#define IPIC_SIVCR 0x04 /* System Global Interrupt Vector Register */ -#define IPIC_SIPNR_H 0x08 /* System Internal Interrupt Pending Register (HIGH) */ -#define IPIC_SIPNR_L 0x0C /* System Internal Interrupt Pending Register (LOW) */ -#define IPIC_SIPRR_A 0x10 /* System Internal Interrupt group A Priority Register */ -#define IPIC_SIPRR_B 0x14 /* System Internal Interrupt group B Priority Register */ -#define IPIC_SIPRR_C 0x18 /* System Internal Interrupt group C Priority Register */ -#define IPIC_SIPRR_D 0x1C /* System Internal Interrupt group D Priority Register */ -#define IPIC_SIMSR_H 0x20 /* System Internal Interrupt Mask Register (HIGH) */ -#define IPIC_SIMSR_L 0x24 /* System Internal Interrupt Mask Register (LOW) */ -#define IPIC_SICNR 0x28 /* System Internal Interrupt Control Register */ -#define IPIC_SEPNR 0x2C /* System External Interrupt Pending Register */ -#define IPIC_SMPRR_A 0x30 /* System Mixed Interrupt group A Priority Register */ -#define IPIC_SMPRR_B 0x34 /* System Mixed Interrupt group B Priority Register */ -#define IPIC_SEMSR 0x38 /* System External Interrupt Mask Register */ -#define IPIC_SECNR 0x3C /* System External Interrupt Control Register */ -#define IPIC_SERSR 0x40 /* System Error Status Register */ -#define IPIC_SERMR 0x44 /* System Error Mask Register */ -#define IPIC_SERCR 0x48 /* System Error Control Register */ -#define IPIC_SIFCR_H 0x50 /* System Internal Interrupt Force Register (HIGH) */ -#define IPIC_SIFCR_L 0x54 /* System Internal Interrupt Force Register (LOW) */ -#define IPIC_SEFCR 0x58 /* System External Interrupt Force Register */ -#define IPIC_SERFR 0x5C /* System Error Force Register */ -#define IPIC_SCVCR 0x60 /* System Critical Interrupt Vector Register */ -#define IPIC_SMVCR 0x64 /* System Management Interrupt Vector Register */ - -enum ipic_prio_grp { - IPIC_INT_GRP_A = IPIC_SIPRR_A, - IPIC_INT_GRP_D = IPIC_SIPRR_D, - IPIC_MIX_GRP_A = IPIC_SMPRR_A, - IPIC_MIX_GRP_B = IPIC_SMPRR_B, -}; - -enum ipic_mcp_irq { - IPIC_MCP_IRQ0 = 0, - IPIC_MCP_WDT = 1, - IPIC_MCP_SBA = 2, - IPIC_MCP_PCI1 = 5, - IPIC_MCP_PCI2 = 6, - IPIC_MCP_MU = 7, -}; - -extern void ipic_init(phys_addr_t phys_addr, unsigned int flags, - unsigned int irq_offset, - unsigned char *senses, unsigned int senses_count); -extern int ipic_set_priority(unsigned int irq, unsigned int priority); -extern void ipic_set_highest_priority(unsigned int irq); -extern void ipic_set_default_priority(void); -extern void ipic_enable_mcp(enum ipic_mcp_irq mcp_irq); -extern void ipic_disable_mcp(enum ipic_mcp_irq mcp_irq); -extern u32 ipic_get_mcp_status(void); -extern void ipic_clear_mcp_status(u32 mask); -extern int ipic_get_irq(struct pt_regs *regs); - -#endif /* __ASM_IPIC_H__ */ -#endif /* __KERNEL__ */ diff --git a/include/asm-ppc/machdep.h b/include/asm-ppc/machdep.h index f01255b..39200de 100644 --- a/include/asm-ppc/machdep.h +++ b/include/asm-ppc/machdep.h @@ -35,8 +35,10 @@ struct machdep_calls { int (*get_irq)(struct pt_regs *); /* A general init function, called by ppc_init in init/main.c. - May be NULL. */ + May be NULL. DEPRECATED ! */ void (*init)(void); + /* For compatibility with merged platforms */ + void (*init_early)(void); void (*restart)(char *cmd); void (*power_off)(void); diff --git a/include/asm-ppc/prom.h b/include/asm-ppc/prom.h index 3e39827..a10a2d6 100644 --- a/include/asm-ppc/prom.h +++ b/include/asm-ppc/prom.h @@ -136,5 +136,15 @@ extern unsigned long sub_reloc_offset(un #define PTRRELOC(x) ((typeof(x))add_reloc_offset((unsigned long)(x))) #define PTRUNRELOC(x) ((typeof(x))sub_reloc_offset((unsigned long)(x))) + +/* + * Address translation function(s) + */ +#define OF_BAD_ADDR ((u64)-1) +extern u64 of_translate_address(struct device_node *np, u32 *addr); +extern u32 *of_get_address(struct device_node *dev, int index, u64 *size); +extern u32 *of_get_pci_address(struct device_node *dev, int bar_no, u64 *size); + + #endif /* _PPC_PROM_H */ #endif /* __KERNEL__ */ diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h index 1056717..07e5d8b 100644 --- a/include/linux/hugetlb.h +++ b/include/linux/hugetlb.h @@ -39,7 +39,6 @@ struct page *follow_huge_addr(struct mm_ int write); struct page *follow_huge_pmd(struct mm_struct *mm, unsigned long address, pmd_t *pmd, int write); -int is_aligned_hugepage_range(unsigned long addr, unsigned long len); int pmd_huge(pmd_t pmd); #ifndef ARCH_HAS_HUGEPAGE_ONLY_RANGE @@ -49,8 +48,18 @@ int pmd_huge(pmd_t pmd); #endif #ifndef ARCH_HAS_PREPARE_HUGEPAGE_RANGE -#define prepare_hugepage_range(addr, len) \ - is_aligned_hugepage_range(addr, len) +/* + * If the arch doesn't supply something else, assume that hugepage + * size aligned regions are ok without further preparation. + */ +static inline int prepare_hugepage_range(unsigned long addr, unsigned long len) +{ + if (len & ~HPAGE_MASK) + return -EINVAL; + if (addr & ~HPAGE_MASK) + return -EINVAL; + return 0; +} #else int prepare_hugepage_range(unsigned long addr, unsigned long len); #endif @@ -91,7 +100,6 @@ static inline unsigned long hugetlb_tota #define hugetlb_report_meminfo(buf) 0 #define hugetlb_report_node_meminfo(n, buf) 0 #define follow_huge_pmd(mm, addr, pmd, write) NULL -#define is_aligned_hugepage_range(addr, len) 0 #define prepare_hugepage_range(addr, len) (-EINVAL) #define pmd_huge(x) 0 #define is_hugepage_only_range(mm, addr, len) 0 diff --git a/include/linux/pci.h b/include/linux/pci.h index de690ca..eff399a 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -78,6 +78,23 @@ typedef int __bitwise pci_power_t; #define PCI_UNKNOWN ((pci_power_t __force) 5) #define PCI_POWER_ERROR ((pci_power_t __force) -1) +/** The pci_channel state describes connectivity between the CPU and + * the pci device. If some PCI bus between here and the pci device + * has crashed or locked up, this info is reflected here. + */ +typedef unsigned int __bitwise pci_channel_state_t; + +enum pci_channel_state { + /* I/O channel is in normal state */ + pci_channel_io_normal = (__force pci_channel_state_t) 1, + + /* I/O to channel is blocked */ + pci_channel_io_frozen = (__force pci_channel_state_t) 2, + + /* PCI card is dead */ + pci_channel_io_perm_failure = (__force pci_channel_state_t) 3, +}; + /* * The pci_dev structure is used to describe PCI devices. */ @@ -110,6 +127,7 @@ struct pci_dev { this is D0-D3, D0 being fully functional, and D3 being off. */ + pci_channel_state_t error_state; /* current connectivity state */ struct device dev; /* Generic device interface */ /* device is compatible with these IDs */ @@ -232,6 +250,54 @@ struct pci_dynids { unsigned int use_driver_data:1; /* pci_driver->driver_data is used */ }; +/* ---------------------------------------------------------------- */ +/** PCI Error Recovery System (PCI-ERS). If a PCI device driver provides + * a set fof callbacks in struct pci_error_handlers, then that device driver + * will be notified of PCI bus errors, and will be driven to recovery + * when an error occurs. + */ + +typedef unsigned int __bitwise pci_ers_result_t; + +enum pci_ers_result { + /* no result/none/not supported in device driver */ + PCI_ERS_RESULT_NONE = (__force pci_ers_result_t) 1, + + /* Device driver can recover without slot reset */ + PCI_ERS_RESULT_CAN_RECOVER = (__force pci_ers_result_t) 2, + + /* Device driver wants slot to be reset. */ + PCI_ERS_RESULT_NEED_RESET = (__force pci_ers_result_t) 3, + + /* Device has completely failed, is unrecoverable */ + PCI_ERS_RESULT_DISCONNECT = (__force pci_ers_result_t) 4, + + /* Device driver is fully recovered and operational */ + PCI_ERS_RESULT_RECOVERED = (__force pci_ers_result_t) 5, +}; + +/* PCI bus error event callbacks */ +struct pci_error_handlers +{ + /* PCI bus error detected on this device */ + pci_ers_result_t (*error_detected)(struct pci_dev *dev, + enum pci_channel_state error); + + /* MMIO has been re-enabled, but not DMA */ + pci_ers_result_t (*mmio_enabled)(struct pci_dev *dev); + + /* PCI Express link has been reset */ + pci_ers_result_t (*link_reset)(struct pci_dev *dev); + + /* PCI slot has been reset */ + pci_ers_result_t (*slot_reset)(struct pci_dev *dev); + + /* Device driver may resume normal operations */ + void (*resume)(struct pci_dev *dev); +}; + +/* ---------------------------------------------------------------- */ + struct module; struct pci_driver { struct list_head node; @@ -244,6 +310,7 @@ struct pci_driver { int (*enable_wake) (struct pci_dev *dev, pci_power_t state, int enable); /* Enable wake event */ void (*shutdown) (struct pci_dev *dev); + struct pci_error_handlers *err_handler; struct device_driver driver; struct pci_dynids dynids; }; diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index c7007b1..44fdd48 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -512,4 +512,9 @@ asmlinkage long sys_ioprio_get(int which asmlinkage long sys_set_mempolicy(int mode, unsigned long __user *nmask, unsigned long maxnode); +asmlinkage long sys_spu_run(int fd, __u32 __user *unpc, + __u32 __user *ustatus); +asmlinkage long sys_spu_create(const char __user *name, + unsigned int flags, mode_t mode); + #endif diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c index 1ab2370..d4739a4 100644 --- a/kernel/sys_ni.c +++ b/kernel/sys_ni.c @@ -90,3 +90,5 @@ cond_syscall(sys_pciconfig_iobase); cond_syscall(sys32_ipc); cond_syscall(sys32_sysctl); cond_syscall(ppc_rtas); +cond_syscall(sys_spu_run); +cond_syscall(sys_spu_create); diff --git a/mm/memory.c b/mm/memory.c index 9ab206b..6ea2eb9 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -2141,6 +2141,8 @@ int __handle_mm_fault(struct mm_struct * return handle_pte_fault(mm, vma, address, pte, pmd, write_access); } +EXPORT_SYMBOL_GPL(__handle_mm_fault); + #ifndef __PAGETABLE_PUD_FOLDED /* * Allocate page upper directory.