commit 40a30c14912ed4dc4b108193d92cb8c88fa431b5 Merge: f897279... 3ccfc65... Author: Paul Mackerras Date: Thu Nov 2 13:42:49 2006 +1100 Merge branch 'linux-2.6' commit f897279465a38991643616f8015369eff96241f0 Merge: 558ee73... 209ad53... Author: Paul Mackerras Date: Mon Oct 30 13:43:38 2006 +1100 Merge master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 558ee738a819ab2bf78659f358425ba6cf90390a Merge: 01248fa... 291b58d... Author: Paul Mackerras Date: Thu Oct 26 13:47:46 2006 +1000 Merge branch 'linux-2.6' commit 01248fa1593b4073aecdb620dc0b93537edd875c Merge: 24a738b... 7059abe... Author: Paul Mackerras Date: Wed Oct 25 14:22:46 2006 +1000 Merge branch 'linux-2.6' commit 24a738b7df2ece740ed98de67bd6798057e58edf Merge: 36ca4ba... f4d4c35... Author: Paul Mackerras Date: Wed Oct 25 14:21:06 2006 +1000 Merge branch 'merge' commit 36ca4ba4b9728f3c420a589a3322c2fbd7ec88b7 Author: Christian Krafft Date: Tue Oct 24 18:39:45 2006 +0200 [POWERPC] cell: add cpufreq driver for Cell BE processor This patch adds a cpufreq backend driver to enable frequency scaling on cell. Signed-off-by: Christian Krafft Signed-off-by: Arnd Bergmann Signed-off-by: Paul Mackerras commit a898497088f46252e6750405504064e2dce53117 Author: Michael Ellerman Date: Tue Oct 24 18:31:28 2006 +0200 [POWERPC] add support for dumping spu info from xmon This patch adds a command to xmon for dumping information about spu structs. The command is 'sf' for "spu fields" perhaps, and takes the spu number as an argument. This is the same value as the spu->number field, or the "phys-id" value of a context when it is bound to a physical spu. We try to catch memory errors as we dump each field, hopefully this will make the command reasonably robust, but YMMV. If people see a need we can easily add more fields to the dump in future. Output looks something like this: 0:mon> sf 0 Dumping spu fields at address c00000001ffd9e80: number = 0x0 name = spe devnode->full_name = /cpus/PowerPC,BE@0/spes/spe@0 nid = 0x0 local_store_phys = 0x20000000000 local_store = 0xd0000800801e0000 ls_size = 0x0 isrc = 0x4 node = 0x0 flags = 0x0 dar = 0x0 dsisr = 0x0 class_0_pending = 0 irqs[0] = 0x16 irqs[1] = 0x17 irqs[2] = 0x24 slb_replace = 0x0 pid = 0 prio = 0 mm = 0x0000000000000000 ctx = 0x0000000000000000 rq = 0x0000000000000000 timestamp = 0x0000000000000000 problem_phys = 0x20000040000 problem = 0xd000080080220000 problem->spu_runcntl_RW = 0x0 problem->spu_status_R = 0x0 problem->spu_npc_RW = 0x0 priv1 = 0xd000080080240000 priv1->mfc_sr1_RW = 0x33 priv2 = 0xd000080080250000 Signed-off-by: Michael Ellerman Signed-off-by: Arnd Bergmann Signed-off-by: Paul Mackerras commit ff8a8f25976aa58bbae7883405b00dcbaf4cc823 Author: Michael Ellerman Date: Tue Oct 24 18:31:27 2006 +0200 [POWERPC] add support for stopping spus from xmon This patch adds support for stopping, and restarting, spus from xmon. We use the spu master runcntl bit to stop execution, this is apparently the "right" way to control spu execution and spufs will be changed in the future to use this bit. Testing has shown that to restart execution we have to turn the master runcntl bit on and also rewrite the spu runcntl bit, even if it is already set to 1 (running). Stopping spus is triggered by the xmon command 'ss' - "spus stop" perhaps. Restarting them is triggered via 'sr'. Restart doesn't start execution on spus unless they were running prior to being stopped by xmon. Walking the spu->full_list in xmon after a panic, would mean corruption of any spu struct would make all the others inaccessible. To avoid this, and also to make the next patch easier, we cache pointers to all spus during boot. We attempt to catch and recover from errors while stopping and restarting the spus, but as with most xmon functionality there are no guarantees that performing these operations won't crash xmon itself. Signed-off-by: Michael Ellerman Signed-off-by: Arnd Bergmann Signed-off-by: Paul Mackerras commit 302eca184fb844670fb128c69e22a8a28bbce48a Author: arnd@arndb.de Date: Tue Oct 24 18:31:26 2006 +0200 [POWERPC] cell: use ppc_md->power_save instead of cbe_idle_loop This moves the cell idle function to use the default cpu_idle with a special power_save callback, like all other platforms except iSeries already do. It also makes it possible to disable this power_save function with a new powerpc-specific boot option "powersave=off". Signed-off-by: Arnd Bergmann Signed-off-by: Paul Mackerras commit b3d7dc1967d1303d4897ff9537d29f6e077de147 Author: Christian Krafft Date: Tue Oct 24 18:31:25 2006 +0200 [POWERPC] cell: add temperature to SPU and CPU sysfs entries This patch adds a module that registers sysfs attributes to CPU and SPU containing the temperature of the CBE. They can be found under /sys/devices/system/spu/cpuX/thermal/temperature[0|1] /sys/devices/system/spu/spuX/thermal/temperature The temperature is read from the on-chip temperature sensors. Signed-off-by: Christian Krafft Signed-off-by: Arnd Bergmann Signed-off-by: Paul Mackerras commit 0344c6c5387ba335bba5a66fd44714b94c98573f Author: Christian Krafft Date: Tue Oct 24 18:31:24 2006 +0200 [POWERPC] sysfs: add support for adding/removing spu sysfs attributes This patch adds two functions to create and remove sysfs attributes and attribute_group to all cpus. That allows to register sysfs attributes in a subdirectory like: /sys/devices/system/cpu/cpuX/group_name/what_ever This will be used by cbe_thermal to group all attributes dealing with thermal support in one directory. Signed-of-by: Christian Krafft Signed-off-by: Paul Mackerras commit e570beb6bb1a623849901efbf939063ec4775c9e Author: Christian Krafft Date: Tue Oct 24 18:31:23 2006 +0200 [POWERPC] cell: add support for registering sysfs attributes to spus In order to add sysfs attributes to all spu's, there is a need for a list of all available spu's. Adding the device_node makes also sense, as it is needed for proper register access. This patch also adds two functions to create and remove sysfs attributes and attribute_groups to all spus. That allows to group spu attributes in a subdirectory like: /sys/devices/system/spu/spuX/group_name/what_ever This will be used by cbe_thermal to group all attributes dealing with thermal support in one directory. Signed-off-by: Christian Krafft Signed-off-by: Arnd Bergmann Signed-off-by: Paul Mackerras commit d8bf96e0793f9576da545bac333b2de304958d68 Author: Kevin Corry Date: Tue Oct 24 18:31:22 2006 +0200 [POWERPC] cell: add low-level performance monitoring code Add routines for accessing the registers and counters in the performance monitoring unit. Signed-off-by: Kevin Corry Signed-off-by: Arnd Bergmann Signed-off-by: Paul Mackerras commit bffd4927ba4377aa38be5450e20e0fecd2523fe3 Author: Kevin Corry Date: Tue Oct 24 18:31:21 2006 +0200 [POWERPC] cell: add shadow registers for pmd_reg Many of the registers in the performance monitoring unit are write-only. We need to save a "shadow" copy when we write to those registers so we can retrieve the values if we need them later. The new cbe_pmd_shadow_regs structure is added to the cbe_regs_map structure so we have the appropriate per-node copies of these shadow values. Signed-off-by: Kevin Corry Signed-off-by: Arnd Bergmann Signed-off-by: Paul Mackerras commit 22b8c9f5baeb440a716ea760ff05290221565b4c Author: David Erb Date: Tue Oct 24 18:31:20 2006 +0200 [POWERPC] cell: update Cell BE register definitions There are a few definitions that are required by subsequent patches, so add them here. The original patch is from David Erb, but is significantly cleaned up by Kevon Corry. Cc: Kevin Corry Signed-off-by: Arnd Bergmann Signed-off-by: Paul Mackerras commit 099814bb1f9bd9081d7c85867f8eb8c049abc1b9 Author: Jeremy Kerr Date: Tue Oct 24 18:31:19 2006 +0200 [POWERPC] spufs: Add isolated-mode SPE recycling support When in isolated mode, SPEs have access to an area of persistent storage, which is per-SPE. In order for isolated-mode apps to communicate arbitrary data through this storage, we need to ensure that isolated physical SPEs can be reused for subsequent applications. Add a file ("recycle") in a spethread dir to enable isolated-mode recycling. By writing to this file, the kernel will reload the isolated-mode loader kernel, allowing a new app to be run on the same physical SPE. This requires the spu_acquire_exclusive function to enforce exclusive access to the SPE while the loader is initialised. Signed-off-by: Jeremy Kerr Signed-off-by: Arnd Bergmann Signed-off-by: Paul Mackerras commit 0afacde3df4c9980f505d9afd7cb0058389732ca Author: arnd@arndb.de Date: Tue Oct 24 18:31:18 2006 +0200 [POWERPC] spufs: allow isolated mode apps by starting the SPE loader This patch adds general support for isolated mode SPE apps. Isolated apps are started indirectly, by a dedicated loader "kernel". This patch starts the loader when spe_create is invoked with the ISOLATE flag. We do this at spe_create time to allow libspe to pass the isolated app in before calling spe_run. The loader is read from the device tree, at the location "/spu-isolation/loader". If the loader is not present, an attempt to start an isolated SPE binary will fail with -ENODEV. Update: loader needs to be correctly aligned - copy to a kmalloced buf. Update: remove workaround for systemsim/spurom 'L-bit' bug, which has been fixed. Update: don't write to runcntl on spu_run_init: SPU is already running. Update: do spu_setup_isolated earlier Tested on systemsim. Signed-off-by: Jeremy Kerr Signed-off-by: Arnd Bergmann Signed-off-by: Paul Mackerras commit eb758ce5b0d84e13cb643b6cc7cb429f6fa28258 Author: arnd@arndb.de Date: Tue Oct 24 18:31:17 2006 +0200 [POWERPC] spufs: "stautus" isnt a word. Signed-off-by: Jeremy Kerr Signed-off-by: Arnd Bergmann Signed-off-by: Paul Mackerras commit 5737edd1ddbde5ab7f63bb3cb36015edbdb7c295 Author: Mark Nutter Date: Tue Oct 24 18:31:16 2006 +0200 [POWERPC] spufs: add support for nonschedulable contexts This adds two new flags to spu_create: SPU_CREATE_NONSCHED: create a context that is never moved away from an SPE once it has started running. This flag can only be used by tasks with the CAP_SYS_NICE capability. SPU_CREATE_ISOLATED: create a nonschedulable context that enters isolation mode upon first run. This requires the SPU_CREATE_NONSCHED flag. Signed-off-by: Jeremy Kerr Signed-off-by: Arnd Bergmann Signed-off-by: Paul Mackerras commit cc21a66d7f727ab97b27af9cf763bc0b51510ffa Author: Geoff Levand Date: Tue Oct 24 18:31:15 2006 +0200 [POWERPC] cell: remove unused struct spu variable Remove the mostly unused variable isrc from struct spu and a forgotten function declaration. Signed-off-by: Geoff Levand Signed-off-by: Arnd Bergmann Signed-off-by: Paul Mackerras commit 24f43b33f74c8e8c8aabc40b728eaf9137802942 Author: Masato Noguchi Date: Tue Oct 24 18:31:14 2006 +0200 [POWERPC] spufs: wrap mfc sdr access SPRN_SDR1 and the SPE's MFC SDR are hypervisor resources and are not accessible from a logical partition. This change adds an access wrapper. When running on bare H/W, the spufs needs to only set the SPE's MFC SDR to the value of the PPE's SPRN_SDR1 once at SPE initialization, so this change renames mfc_sdr_set() to mfc_sdr_setup() and moves the access of SPRN_SDR1 into the mmio wrapper. It also removes the now unneeded member mfc_sdr_RW from struct spu_priv1_collapsed. Signed-off-by: Masato Noguchi Signed-off-by: Geoff Levand Signed-off-by: Arnd Bergmann -- Signed-off-by: Paul Mackerras commit 5414c6be57dd02e089c3eba1f5134f441733d013 Author: Michael Ellerman Date: Tue Oct 24 13:37:34 2006 +1000 [POWERPC] Make irq_dispose_mapping(NO_IRQ) a nop It makes for a friendlier API if irq_dispose_mapping(NO_IRQ) is a nop, rather than triggering a WARN_ON. Signed-off-by: Michael Ellerman Acked-by: Benjamin Herrenschmidt Signed-off-by: Paul Mackerras commit a0f467261ae041864414103b8dd6fc4487a3c7de Author: Matthew McClintock Date: Mon Oct 23 14:26:57 2006 -0500 [POWERPC] Fixed some missing files to be deleted when running make clean Fixed some missing files to be deleted when running make clean Signed-off-by: Matthew McClintock Signed-off-by: Paul Mackerras commit 41999295b6c25d799dacbbca089fdbc19f6d60f5 Author: Michael Ellerman Date: Wed Oct 18 15:53:20 2006 +1000 [POWERPC] Move iSeries initrd logic into device tree Remove the iSeries initrd logic, instead just store the initrd location and size in the device tree so generic code can do the rest for us. The iSeries code had a "feature" which the generic code lacks, ie. if the compressed initrd is bigger than the configured ram disk size, we make the ram disk size bigger. That's bogus, as the compressed size of the initrd tells us nothing about how big the ram disk needs to be. If the ram disk isn't big enough you just need to make CONFIG_BLK_DEV_RAM_SIZE larger. Signed-off-by: Michael Ellerman Signed-off-by: Paul Mackerras commit 83efafb301bc79a32799ca854fb590e82c4396e9 Author: Andy Fleming Date: Mon Oct 16 16:03:33 2006 -0500 [POWERPC] Fix up the OF functions to only do PCI stuff if PCI is actually configured The original problem that inspired this patch was solved quite some time ago (Turning off PCI didn't work), but this patch neatens things up a little (I think), by putting all the PCI stuff inside a single CONFIG_PCI block. It also removes the OF PCI bus matching entries if CONFIG_PCI is off. Signed-off-by: Paul Mackerras commit 0cfcccb4687862a34eb609d16e75b39b6cb54c80 Author: Josh Boyer Date: Fri Oct 13 10:07:01 2006 -0500 [POWERPC] Add mktree utility to arch/powerpc/boot This patch adds the mktree program that is needed to post process zImage wrappers for various PowerPC 4xx boards Signed-off-by: Josh Boyer Signed-off-by: Paul Mackerras commit 01a6372008ed450982ba38ee5fd91028b9f5a781 Author: Mark A. Greer Date: Mon Oct 16 13:54:52 2006 -0700 [POWERPC] Add simple memory allocator to bootwrapper Provide primitive malloc, free, and realloc functions for bootwrapper. Signed-off-by: Mark A. Greer Signed-off-by: Paul Mackerras commit 0c176fa80fdfa9b4e0753e37223b056994c818d2 Author: Mark A. Greer Date: Mon Oct 16 13:52:09 2006 -0700 [POWERPC] Add non-OF serial console support Add serial console support for non-OF systems. There is a generic serial console layer which calls a serial console driver. Included is the serial console driver for the ns16550 class of uarts. Necessary support routines are added as well. Signed-off-by: Mark A. Greer Signed-off-by: Paul Mackerras commit 6fb4efc68f5c0e095153510dcfa8b54a42e914ba Author: Mark A. Greer Date: Mon Oct 16 13:50:05 2006 -0700 [POWERPC] Add flatdevtree source Add the latest version of the flatdevtree code and corresponding glue. A phandle table now tracks values returned by ft_find_device(). The value returned by ft_find_device() is a phandle which is really an index into the phandle table. The phandle table contains the address of the corresponding node. When the flat dt is edited/moved, the node pointers in the phandle table are updated accordingly so no phandles kept by the caller become stale. Signed-off-by: Mark A. Greer Signed-off-by: Paul Mackerras commit c888554bf95a5a0a6ac3e2389c6bf1e03e9480ba Author: Mark A. Greer Date: Mon Oct 16 13:49:27 2006 -0700 [POWERPC] More bootwrapper reorganization More reorganization of the bootwrapper: - Add dtb section to zImage - ft_init now called by platform_init - Pack a flat dt before calling kernel - Remove size parameter from free - printf only calls console_ops.write it its not NULL - Some cleanup Signed-off-by: Mark A. Greer Signed-off-by: Paul Mackerras commit b0a779debd56514b2b5de7617a1643e5dac2d4dd Author: Paul Mackerras Date: Wed Oct 18 10:11:22 2006 +1000 [POWERPC] Make sure interrupt enable gets restored properly The lazy IRQ disable patch missed a couple of places where the interrupt enable flags need to be restored correctly. First, we weren't restoring the paca->hard_enabled flag on interrupt exit. Instead of saving it on entry, we compute it from the MSR_EE bit in the MSR we are restoring at exit. Secondly, the MMU hash miss code was clearing both paca->soft_enabled and paca->hard_enabled but not restoring them in the case where hash_page was able to resolve the miss from the Linux page tables. Signed-off-by: Paul Mackerras commit 035223fb28791f0eb0d5719727355d3f6817d228 Author: Geoff Levand Date: Thu Oct 5 11:35:10 2006 -0700 [POWERPC] Make pSeries_lpar_hpte_insert static Change the powerpc hpte_insert routines now called through ppc_md to static scope. Signed-off-by: Geoff Levand Signed-off-by: Paul Mackerras commit 0f03a43b8f0fc221986a46654282ec6a1e8c6d45 Author: David Gibson Date: Tue Oct 3 16:57:44 2006 +1000 [POWERPC] Remove todc code from ARCH=powerpc Apparently we've copied the todc drivers, for various RTCs used in embedded machines from ARCH=ppc to ARCH=powerpc, despite the fact that it's never used in the latter. This patch removes it. If we ever need these drivers (which we probably shouldn't now the RTC class stuff is in), we can transfer them one by one from ARCH=ppc, removing from the hideous abomination which is the todc "infrastructure". Signed-off-by: David Gibson Signed-off-by: Paul Mackerras commit 5b43d20a4d12d94f258bbbca5fa4df8ff8a17848 Author: Olof Johansson Date: Wed Oct 4 23:41:41 2006 -0500 [POWERPC] powerpc: Enable DEEPNAP power savings mode on 970MP Without this patch, on an idle system I get: cpu-power-0:21.638 cpu-power-1:27.102 cpu-power-2:29.343 cpu-power-3:25.784 Total: 103.8W With this patch: cpu-power-0:11.730 cpu-power-1:17.185 cpu-power-2:18.547 cpu-power-3:17.528 Total: 65.0W If I lower HZ to 100, I can get it as low as: cpu-power-0:10.938 cpu-power-1:16.021 cpu-power-2:17.245 cpu-power-3:16.145 Total: 60.2W Another (older) Quad G5 went from 54W to 39W at HZ=250. Coming back out of Deep Nap takes 40-70 cycles longer than coming back from just Nap (which already takes quite a while). I don't think it'll be a performance issue (interrupt latency on an idle system), but in case someone does measurements feel free to report them. Signed-off-by: Olof Johansson Acked-by: Michael Buesch Signed-off-by: Paul Mackerras commit d04c56f73c30a5e593202ecfcf25ed43d42363a2 Author: Paul Mackerras Date: Wed Oct 4 16:47:49 2006 +1000 [POWERPC] Lazy interrupt disabling for 64-bit machines This implements a lazy strategy for disabling interrupts. This means that local_irq_disable() et al. just clear the 'interrupts are enabled' flag in the paca. If an interrupt comes along, the interrupt entry code notices that interrupts are supposed to be disabled, and clears the EE bit in SRR1, clears the 'interrupts are hard-enabled' flag in the paca, and returns. This means that interrupts only actually get disabled in the processor when an interrupt comes along. When interrupts are enabled by local_irq_enable() et al., the code sets the interrupts-enabled flag in the paca, and then checks whether interrupts got hard-disabled. If so, it also sets the EE bit in the MSR to hard-enable the interrupts. This has the potential to improve performance, and also makes it easier to make a kernel that can boot on iSeries and on other 64-bit machines, since this lazy-disable strategy is very similar to the soft-disable strategy that iSeries already uses. This version renames paca->proc_enabled to paca->soft_enabled, and changes a couple of soft-disables in the kexec code to hard-disables, which should fix the crash that Michael Ellerman saw. This doesn't yet use a reserved CR field for the soft_enabled and hard_enabled flags. This applies on top of Stephen Rothwell's patches to make it possible to build a combined iSeries/other kernel. Signed-off-by: Paul Mackerras Index: linux-2.6/arch/powerpc/Kconfig =================================================================== --- linux-2.6.orig/arch/powerpc/Kconfig +++ linux-2.6/arch/powerpc/Kconfig @@ -594,12 +594,6 @@ config TAU_AVERAGE If in doubt, say N here. -config PPC_TODC - depends on EMBEDDED6xx - bool "Generic Time-of-day Clock (TODC) support" - ---help--- - This adds support for many TODC/RTC chips. - endmenu source arch/powerpc/platforms/embedded6xx/Kconfig Index: linux-2.6/arch/powerpc/boot/Makefile =================================================================== --- linux-2.6.orig/arch/powerpc/boot/Makefile +++ linux-2.6/arch/powerpc/boot/Makefile @@ -40,7 +40,8 @@ zliblinuxheader := zlib.h zconf.h zutil. $(addprefix $(obj)/,$(zlib) main.o): $(addprefix $(obj)/,$(zliblinuxheader)) \ $(addprefix $(obj)/,$(zlibheader)) -src-wlib := string.S stdio.c main.c div64.S $(zlib) +src-wlib := string.S stdio.c main.c flatdevtree.c flatdevtree_misc.c \ + ns16550.c serial.c simple_alloc.c div64.S util.S $(zlib) src-plat := of.c src-boot := crt0.S $(src-wlib) $(src-plat) empty.c @@ -74,7 +75,7 @@ $(obj)/zImage.lds $(obj)/zImage.coff.lds @cp $< $@ clean-files := $(zlib) $(zlibheader) $(zliblinuxheader) \ - $(obj)/empty.c + empty.c zImage.coff.lds zImage.lds zImage.sandpoint quiet_cmd_bootcc = BOOTCC $@ cmd_bootcc = $(CROSS32CC) -Wp,-MD,$(depfile) $(BOOTCFLAGS) -c -o $@ $< @@ -93,13 +94,13 @@ $(patsubst %.S,%.o, $(filter %.S, $(src- $(obj)/wrapper.a: $(obj-wlib) $(call cmd,bootar) -hostprogs-y := addnote addRamDisk hack-coff +hostprogs-y := addnote addRamDisk hack-coff mktree extra-y := $(obj)/crt0.o $(obj)/wrapper.a $(obj-plat) $(obj)/empty.o \ $(obj)/zImage.lds $(obj)/zImage.coff.lds wrapper := /bin/sh $(srctree)/$(src)/wrapper -wrapperbits := $(extra-y) $(addprefix $(obj)/,addnote hack-coff) +wrapperbits := $(extra-y) $(addprefix $(obj)/,addnote hack-coff mktree) ############# # Bits for building various flavours of zImage Index: linux-2.6/arch/powerpc/boot/flatdevtree.c =================================================================== --- /dev/null +++ linux-2.6/arch/powerpc/boot/flatdevtree.c @@ -0,0 +1,880 @@ +/* + * 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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright Pantelis Antoniou 2006 + * Copyright (C) IBM Corporation 2006 + * + * Authors: Pantelis Antoniou + * Hollis Blanchard + * Mark A. Greer + * Paul Mackerras + */ + +#include +#include +#include "flatdevtree.h" +#include "flatdevtree_env.h" + +#define _ALIGN(x, al) (((x) + (al) - 1) & ~((al) - 1)) + +/* Routines for keeping node ptrs returned by ft_find_device current */ +/* First entry not used b/c it would return 0 and be taken as NULL/error */ +static void *ft_node_add(struct ft_cxt *cxt, char *node) +{ + unsigned int i; + + for (i = 1; i < cxt->nodes_used; i++) /* already there? */ + if (cxt->node_tbl[i] == node) + return (void *)i; + + if (cxt->nodes_used < cxt->node_max) { + cxt->node_tbl[cxt->nodes_used] = node; + return (void *)cxt->nodes_used++; + } + + return NULL; +} + +static char *ft_node_ph2node(struct ft_cxt *cxt, const void *phandle) +{ + unsigned int i = (unsigned int)phandle; + + if (i < cxt->nodes_used) + return cxt->node_tbl[i]; + return NULL; +} + +static void ft_node_update_before(struct ft_cxt *cxt, char *addr, int shift) +{ + unsigned int i; + + if (shift == 0) + return; + + for (i = 1; i < cxt->nodes_used; i++) + if (cxt->node_tbl[i] < addr) + cxt->node_tbl[i] += shift; +} + +static void ft_node_update_after(struct ft_cxt *cxt, char *addr, int shift) +{ + unsigned int i; + + if (shift == 0) + return; + + for (i = 1; i < cxt->nodes_used; i++) + if (cxt->node_tbl[i] >= addr) + cxt->node_tbl[i] += shift; +} + +/* Struct used to return info from ft_next() */ +struct ft_atom { + u32 tag; + const char *name; + void *data; + u32 size; +}; + +/* Set ptrs to current one's info; return addr of next one */ +static char *ft_next(struct ft_cxt *cxt, char *p, struct ft_atom *ret) +{ + u32 sz; + + if (p >= cxt->rgn[FT_STRUCT].start + cxt->rgn[FT_STRUCT].size) + return NULL; + + ret->tag = be32_to_cpu(*(u32 *) p); + p += 4; + + switch (ret->tag) { /* Tag */ + case OF_DT_BEGIN_NODE: + ret->name = p; + ret->data = (void *)(p - 4); /* start of node */ + p += _ALIGN(strlen(p) + 1, 4); + break; + case OF_DT_PROP: + ret->size = sz = be32_to_cpu(*(u32 *) p); + ret->name = cxt->str_anchor + be32_to_cpu(*(u32 *) (p + 4)); + ret->data = (void *)(p + 8); + p += 8 + _ALIGN(sz, 4); + break; + case OF_DT_END_NODE: + case OF_DT_NOP: + break; + case OF_DT_END: + default: + p = NULL; + break; + } + + return p; +} + +#define HDR_SIZE _ALIGN(sizeof(struct boot_param_header), 8) +#define EXPAND_INCR 1024 /* alloc this much extra when expanding */ + +/* See if the regions are in the standard order and non-overlapping */ +static int ft_ordered(struct ft_cxt *cxt) +{ + char *p = (char *)cxt->bph + HDR_SIZE; + enum ft_rgn_id r; + + for (r = FT_RSVMAP; r <= FT_STRINGS; ++r) { + if (p > cxt->rgn[r].start) + return 0; + p = cxt->rgn[r].start + cxt->rgn[r].size; + } + return p <= (char *)cxt->bph + cxt->max_size; +} + +/* Copy the tree to a newly-allocated region and put things in order */ +static int ft_reorder(struct ft_cxt *cxt, int nextra) +{ + unsigned long tot; + enum ft_rgn_id r; + char *p, *pend; + int stroff; + + tot = HDR_SIZE + EXPAND_INCR; + for (r = FT_RSVMAP; r <= FT_STRINGS; ++r) + tot += cxt->rgn[r].size; + if (nextra > 0) + tot += nextra; + tot = _ALIGN(tot, 8); + + if (!cxt->realloc) + return 0; + p = cxt->realloc(NULL, tot); + if (!p) + return 0; + + memcpy(p, cxt->bph, sizeof(struct boot_param_header)); + /* offsets get fixed up later */ + + cxt->bph = (struct boot_param_header *)p; + cxt->max_size = tot; + pend = p + tot; + p += HDR_SIZE; + + memcpy(p, cxt->rgn[FT_RSVMAP].start, cxt->rgn[FT_RSVMAP].size); + cxt->rgn[FT_RSVMAP].start = p; + p += cxt->rgn[FT_RSVMAP].size; + + memcpy(p, cxt->rgn[FT_STRUCT].start, cxt->rgn[FT_STRUCT].size); + ft_node_update_after(cxt, cxt->rgn[FT_STRUCT].start, + p - cxt->rgn[FT_STRUCT].start); + cxt->p += p - cxt->rgn[FT_STRUCT].start; + cxt->rgn[FT_STRUCT].start = p; + + p = pend - cxt->rgn[FT_STRINGS].size; + memcpy(p, cxt->rgn[FT_STRINGS].start, cxt->rgn[FT_STRINGS].size); + stroff = cxt->str_anchor - cxt->rgn[FT_STRINGS].start; + cxt->rgn[FT_STRINGS].start = p; + cxt->str_anchor = p + stroff; + + cxt->isordered = 1; + return 1; +} + +static inline char *prev_end(struct ft_cxt *cxt, enum ft_rgn_id r) +{ + if (r > FT_RSVMAP) + return cxt->rgn[r - 1].start + cxt->rgn[r - 1].size; + return (char *)cxt->bph + HDR_SIZE; +} + +static inline char *next_start(struct ft_cxt *cxt, enum ft_rgn_id r) +{ + if (r < FT_STRINGS) + return cxt->rgn[r + 1].start; + return (char *)cxt->bph + cxt->max_size; +} + +/* + * See if we can expand region rgn by nextra bytes by using up + * free space after or before the region. + */ +static int ft_shuffle(struct ft_cxt *cxt, char **pp, enum ft_rgn_id rgn, + int nextra) +{ + char *p = *pp; + char *rgn_start, *rgn_end; + + rgn_start = cxt->rgn[rgn].start; + rgn_end = rgn_start + cxt->rgn[rgn].size; + if (nextra <= 0 || rgn_end + nextra <= next_start(cxt, rgn)) { + /* move following stuff */ + if (p < rgn_end) { + if (nextra < 0) + memmove(p, p - nextra, rgn_end - p + nextra); + else + memmove(p + nextra, p, rgn_end - p); + if (rgn == FT_STRUCT) + ft_node_update_after(cxt, p, nextra); + } + cxt->rgn[rgn].size += nextra; + if (rgn == FT_STRINGS) + /* assumes strings only added at beginning */ + cxt->str_anchor += nextra; + return 1; + } + if (prev_end(cxt, rgn) <= rgn_start - nextra) { + /* move preceding stuff */ + if (p > rgn_start) { + memmove(rgn_start - nextra, rgn_start, p - rgn_start); + if (rgn == FT_STRUCT) + ft_node_update_before(cxt, p, -nextra); + } + *p -= nextra; + cxt->rgn[rgn].start -= nextra; + cxt->rgn[rgn].size += nextra; + return 1; + } + return 0; +} + +static int ft_make_space(struct ft_cxt *cxt, char **pp, enum ft_rgn_id rgn, + int nextra) +{ + unsigned long size, ssize, tot; + char *str, *next; + enum ft_rgn_id r; + + if (!cxt->isordered && !ft_reorder(cxt, nextra)) + return 0; + if (ft_shuffle(cxt, pp, rgn, nextra)) + return 1; + + /* See if there is space after the strings section */ + ssize = cxt->rgn[FT_STRINGS].size; + if (cxt->rgn[FT_STRINGS].start + ssize + < (char *)cxt->bph + cxt->max_size) { + /* move strings up as far as possible */ + str = (char *)cxt->bph + cxt->max_size - ssize; + cxt->str_anchor += str - cxt->rgn[FT_STRINGS].start; + memmove(str, cxt->rgn[FT_STRINGS].start, ssize); + cxt->rgn[FT_STRINGS].start = str; + /* enough space now? */ + if (rgn >= FT_STRUCT && ft_shuffle(cxt, pp, rgn, nextra)) + return 1; + } + + /* how much total free space is there following this region? */ + tot = 0; + for (r = rgn; r < FT_STRINGS; ++r) { + char *r_end = cxt->rgn[r].start + cxt->rgn[r].size; + tot += next_start(cxt, rgn) - r_end; + } + + /* cast is to shut gcc up; we know nextra >= 0 */ + if (tot < (unsigned int)nextra) { + /* have to reallocate */ + char *newp, *new_start; + int shift; + + if (!cxt->realloc) + return 0; + size = _ALIGN(cxt->max_size + (nextra - tot) + EXPAND_INCR, 8); + newp = cxt->realloc(cxt->bph, size); + if (!newp) + return 0; + cxt->max_size = size; + shift = newp - (char *)cxt->bph; + + if (shift) { /* realloc can return same addr */ + cxt->bph = (struct boot_param_header *)newp; + ft_node_update_after(cxt, cxt->rgn[FT_STRUCT].start, + shift); + for (r = FT_RSVMAP; r <= FT_STRINGS; ++r) { + new_start = cxt->rgn[r].start + shift; + cxt->rgn[r].start = new_start; + } + *pp += shift; + cxt->str_anchor += shift; + } + + /* move strings up to the end */ + str = newp + size - ssize; + cxt->str_anchor += str - cxt->rgn[FT_STRINGS].start; + memmove(str, cxt->rgn[FT_STRINGS].start, ssize); + cxt->rgn[FT_STRINGS].start = str; + + if (ft_shuffle(cxt, pp, rgn, nextra)) + return 1; + } + + /* must be FT_RSVMAP and we need to move FT_STRUCT up */ + if (rgn == FT_RSVMAP) { + next = cxt->rgn[FT_RSVMAP].start + cxt->rgn[FT_RSVMAP].size + + nextra; + ssize = cxt->rgn[FT_STRUCT].size; + if (next + ssize >= cxt->rgn[FT_STRINGS].start) + return 0; /* "can't happen" */ + memmove(next, cxt->rgn[FT_STRUCT].start, ssize); + ft_node_update_after(cxt, cxt->rgn[FT_STRUCT].start, nextra); + cxt->rgn[FT_STRUCT].start = next; + + if (ft_shuffle(cxt, pp, rgn, nextra)) + return 1; + } + + return 0; /* "can't happen" */ +} + +static void ft_put_word(struct ft_cxt *cxt, u32 v) +{ + *(u32 *) cxt->p = cpu_to_be32(v); + cxt->p += 4; +} + +static void ft_put_bin(struct ft_cxt *cxt, const void *data, unsigned int sz) +{ + unsigned long sza = _ALIGN(sz, 4); + + /* zero out the alignment gap if necessary */ + if (sz < sza) + *(u32 *) (cxt->p + sza - 4) = 0; + + /* copy in the data */ + memcpy(cxt->p, data, sz); + + cxt->p += sza; +} + +int ft_begin_node(struct ft_cxt *cxt, const char *name) +{ + unsigned long nlen = strlen(name) + 1; + unsigned long len = 8 + _ALIGN(nlen, 4); + + if (!ft_make_space(cxt, &cxt->p, FT_STRUCT, len)) + return -1; + ft_put_word(cxt, OF_DT_BEGIN_NODE); + ft_put_bin(cxt, name, strlen(name) + 1); + return 0; +} + +void ft_end_node(struct ft_cxt *cxt) +{ + ft_put_word(cxt, OF_DT_END_NODE); +} + +void ft_nop(struct ft_cxt *cxt) +{ + if (ft_make_space(cxt, &cxt->p, FT_STRUCT, 4)) + ft_put_word(cxt, OF_DT_NOP); +} + +#define NO_STRING 0x7fffffff + +static int lookup_string(struct ft_cxt *cxt, const char *name) +{ + char *p, *end; + + p = cxt->rgn[FT_STRINGS].start; + end = p + cxt->rgn[FT_STRINGS].size; + while (p < end) { + if (strcmp(p, (char *)name) == 0) + return p - cxt->str_anchor; + p += strlen(p) + 1; + } + + return NO_STRING; +} + +/* lookup string and insert if not found */ +static int map_string(struct ft_cxt *cxt, const char *name) +{ + int off; + char *p; + + off = lookup_string(cxt, name); + if (off != NO_STRING) + return off; + p = cxt->rgn[FT_STRINGS].start; + if (!ft_make_space(cxt, &p, FT_STRINGS, strlen(name) + 1)) + return NO_STRING; + strcpy(p, name); + return p - cxt->str_anchor; +} + +int ft_prop(struct ft_cxt *cxt, const char *name, const void *data, + unsigned int sz) +{ + int off, len; + + off = lookup_string(cxt, name); + if (off == NO_STRING) + return -1; + + len = 12 + _ALIGN(sz, 4); + if (!ft_make_space(cxt, &cxt->p, FT_STRUCT, len)) + return -1; + + ft_put_word(cxt, OF_DT_PROP); + ft_put_word(cxt, sz); + ft_put_word(cxt, off); + ft_put_bin(cxt, data, sz); + return 0; +} + +int ft_prop_str(struct ft_cxt *cxt, const char *name, const char *str) +{ + return ft_prop(cxt, name, str, strlen(str) + 1); +} + +int ft_prop_int(struct ft_cxt *cxt, const char *name, unsigned int val) +{ + u32 v = cpu_to_be32((u32) val); + + return ft_prop(cxt, name, &v, 4); +} + +/* Calculate the size of the reserved map */ +static unsigned long rsvmap_size(struct ft_cxt *cxt) +{ + struct ft_reserve *res; + + res = (struct ft_reserve *)cxt->rgn[FT_RSVMAP].start; + while (res->start || res->len) + ++res; + return (char *)(res + 1) - cxt->rgn[FT_RSVMAP].start; +} + +/* Calculate the size of the struct region by stepping through it */ +static unsigned long struct_size(struct ft_cxt *cxt) +{ + char *p = cxt->rgn[FT_STRUCT].start; + char *next; + struct ft_atom atom; + + /* make check in ft_next happy */ + if (cxt->rgn[FT_STRUCT].size == 0) + cxt->rgn[FT_STRUCT].size = 0xfffffffful - (unsigned long)p; + + while ((next = ft_next(cxt, p, &atom)) != NULL) + p = next; + return p + 4 - cxt->rgn[FT_STRUCT].start; +} + +/* add `adj' on to all string offset values in the struct area */ +static void adjust_string_offsets(struct ft_cxt *cxt, int adj) +{ + char *p = cxt->rgn[FT_STRUCT].start; + char *next; + struct ft_atom atom; + int off; + + while ((next = ft_next(cxt, p, &atom)) != NULL) { + if (atom.tag == OF_DT_PROP) { + off = be32_to_cpu(*(u32 *) (p + 8)); + *(u32 *) (p + 8) = cpu_to_be32(off + adj); + } + p = next; + } +} + +/* start construction of the flat OF tree from scratch */ +void ft_begin(struct ft_cxt *cxt, void *blob, unsigned int max_size, + void *(*realloc_fn) (void *, unsigned long)) +{ + struct boot_param_header *bph = blob; + char *p; + struct ft_reserve *pres; + + /* clear the cxt */ + memset(cxt, 0, sizeof(*cxt)); + + cxt->bph = bph; + cxt->max_size = max_size; + cxt->realloc = realloc_fn; + cxt->isordered = 1; + + /* zero everything in the header area */ + memset(bph, 0, sizeof(*bph)); + + bph->magic = cpu_to_be32(OF_DT_HEADER); + bph->version = cpu_to_be32(0x10); + bph->last_comp_version = cpu_to_be32(0x10); + + /* start pointers */ + cxt->rgn[FT_RSVMAP].start = p = blob + HDR_SIZE; + cxt->rgn[FT_RSVMAP].size = sizeof(struct ft_reserve); + pres = (struct ft_reserve *)p; + cxt->rgn[FT_STRUCT].start = p += sizeof(struct ft_reserve); + cxt->rgn[FT_STRUCT].size = 4; + cxt->rgn[FT_STRINGS].start = blob + max_size; + cxt->rgn[FT_STRINGS].size = 0; + + /* init rsvmap and struct */ + pres->start = 0; + pres->len = 0; + *(u32 *) p = cpu_to_be32(OF_DT_END); + + cxt->str_anchor = blob; +} + +/* open up an existing blob to be examined or modified */ +int ft_open(struct ft_cxt *cxt, void *blob, unsigned int max_size, + unsigned int max_find_device, + void *(*realloc_fn) (void *, unsigned long)) +{ + struct boot_param_header *bph = blob; + + /* can't cope with version < 16 */ + if (be32_to_cpu(bph->version) < 16) + return -1; + + /* clear the cxt */ + memset(cxt, 0, sizeof(*cxt)); + + /* alloc node_tbl to track node ptrs returned by ft_find_device */ + ++max_find_device; + cxt->node_tbl = realloc_fn(NULL, max_find_device * sizeof(char *)); + if (!cxt->node_tbl) + return -1; + memset(cxt->node_tbl, 0, max_find_device * sizeof(char *)); + cxt->node_max = max_find_device; + cxt->nodes_used = 1; /* don't use idx 0 b/c looks like NULL */ + + cxt->bph = bph; + cxt->max_size = max_size; + cxt->realloc = realloc_fn; + + cxt->rgn[FT_RSVMAP].start = blob + be32_to_cpu(bph->off_mem_rsvmap); + cxt->rgn[FT_RSVMAP].size = rsvmap_size(cxt); + cxt->rgn[FT_STRUCT].start = blob + be32_to_cpu(bph->off_dt_struct); + cxt->rgn[FT_STRUCT].size = struct_size(cxt); + cxt->rgn[FT_STRINGS].start = blob + be32_to_cpu(bph->off_dt_strings); + cxt->rgn[FT_STRINGS].size = be32_to_cpu(bph->dt_strings_size); + /* Leave as '0' to force first ft_make_space call to do a ft_reorder + * and move dt to an area allocated by realloc. + cxt->isordered = ft_ordered(cxt); + */ + + cxt->p = cxt->rgn[FT_STRUCT].start; + cxt->str_anchor = cxt->rgn[FT_STRINGS].start; + + return 0; +} + +/* add a reserver physical area to the rsvmap */ +int ft_add_rsvmap(struct ft_cxt *cxt, u64 physaddr, u64 size) +{ + char *p; + struct ft_reserve *pres; + + p = cxt->rgn[FT_RSVMAP].start + cxt->rgn[FT_RSVMAP].size + - sizeof(struct ft_reserve); + if (!ft_make_space(cxt, &p, FT_RSVMAP, sizeof(struct ft_reserve))) + return -1; + + pres = (struct ft_reserve *)p; + pres->start = cpu_to_be64(physaddr); + pres->len = cpu_to_be64(size); + + return 0; +} + +void ft_begin_tree(struct ft_cxt *cxt) +{ + cxt->p = cxt->rgn[FT_STRUCT].start; +} + +void ft_end_tree(struct ft_cxt *cxt) +{ + struct boot_param_header *bph = cxt->bph; + char *p, *oldstr, *str, *endp; + unsigned long ssize; + int adj; + + if (!cxt->isordered) + return; /* we haven't touched anything */ + + /* adjust string offsets */ + oldstr = cxt->rgn[FT_STRINGS].start; + adj = cxt->str_anchor - oldstr; + if (adj) + adjust_string_offsets(cxt, adj); + + /* make strings end on 8-byte boundary */ + ssize = cxt->rgn[FT_STRINGS].size; + endp = (char *)_ALIGN((unsigned long)cxt->rgn[FT_STRUCT].start + + cxt->rgn[FT_STRUCT].size + ssize, 8); + str = endp - ssize; + + /* move strings down to end of structs */ + memmove(str, oldstr, ssize); + cxt->str_anchor = str; + cxt->rgn[FT_STRINGS].start = str; + + /* fill in header fields */ + p = (char *)bph; + bph->totalsize = cpu_to_be32(endp - p); + bph->off_mem_rsvmap = cpu_to_be32(cxt->rgn[FT_RSVMAP].start - p); + bph->off_dt_struct = cpu_to_be32(cxt->rgn[FT_STRUCT].start - p); + bph->off_dt_strings = cpu_to_be32(cxt->rgn[FT_STRINGS].start - p); + bph->dt_strings_size = cpu_to_be32(ssize); +} + +void *ft_find_device(struct ft_cxt *cxt, const char *srch_path) +{ + char *node; + + /* require absolute path */ + if (srch_path[0] != '/') + return NULL; + node = ft_find_descendent(cxt, cxt->rgn[FT_STRUCT].start, srch_path); + return ft_node_add(cxt, node); +} + +void *ft_find_descendent(struct ft_cxt *cxt, void *top, const char *srch_path) +{ + struct ft_atom atom; + char *p; + const char *cp, *q; + int cl; + int depth = -1; + int dmatch = 0; + const char *path_comp[FT_MAX_DEPTH]; + + cp = srch_path; + cl = 0; + p = top; + + while ((p = ft_next(cxt, p, &atom)) != NULL) { + switch (atom.tag) { + case OF_DT_BEGIN_NODE: + ++depth; + if (depth != dmatch) + break; + cxt->genealogy[depth] = atom.data; + cxt->genealogy[depth + 1] = NULL; + if (depth && !(strncmp(atom.name, cp, cl) == 0 + && (atom.name[cl] == '/' + || atom.name[cl] == '\0' + || atom.name[cl] == '@'))) + break; + path_comp[dmatch] = cp; + /* it matches so far, advance to next path component */ + cp += cl; + /* skip slashes */ + while (*cp == '/') + ++cp; + /* we're done if this is the end of the string */ + if (*cp == 0) + return atom.data; + /* look for end of this component */ + q = strchr(cp, '/'); + if (q) + cl = q - cp; + else + cl = strlen(cp); + ++dmatch; + break; + case OF_DT_END_NODE: + if (depth == 0) + return NULL; + if (dmatch > depth) { + --dmatch; + cl = cp - path_comp[dmatch] - 1; + cp = path_comp[dmatch]; + while (cl > 0 && cp[cl - 1] == '/') + --cl; + } + --depth; + break; + } + } + return NULL; +} + +void *ft_get_parent(struct ft_cxt *cxt, const void *phandle) +{ + void *node; + int d; + struct ft_atom atom; + char *p; + + node = ft_node_ph2node(cxt, phandle); + if (node == NULL) + return NULL; + + for (d = 0; cxt->genealogy[d] != NULL; ++d) + if (cxt->genealogy[d] == node) + return cxt->genealogy[d > 0 ? d - 1 : 0]; + + /* have to do it the hard way... */ + p = cxt->rgn[FT_STRUCT].start; + d = 0; + while ((p = ft_next(cxt, p, &atom)) != NULL) { + switch (atom.tag) { + case OF_DT_BEGIN_NODE: + cxt->genealogy[d] = atom.data; + if (node == atom.data) { + /* found it */ + cxt->genealogy[d + 1] = NULL; + return d > 0 ? cxt->genealogy[d - 1] : node; + } + ++d; + break; + case OF_DT_END_NODE: + --d; + break; + } + } + return NULL; +} + +int ft_get_prop(struct ft_cxt *cxt, const void *phandle, const char *propname, + void *buf, const unsigned int buflen) +{ + struct ft_atom atom; + void *node; + char *p; + int depth; + unsigned int size; + + node = ft_node_ph2node(cxt, phandle); + if (node == NULL) + return -1; + + depth = 0; + p = (char *)node; + + while ((p = ft_next(cxt, p, &atom)) != NULL) { + switch (atom.tag) { + case OF_DT_BEGIN_NODE: + ++depth; + break; + case OF_DT_PROP: + if ((depth != 1) || strcmp(atom.name, propname)) + break; + size = min(atom.size, buflen); + memcpy(buf, atom.data, size); + return atom.size; + case OF_DT_END_NODE: + if (--depth <= 0) + return -1; + } + } + return -1; +} + +int ft_set_prop(struct ft_cxt *cxt, const void *phandle, const char *propname, + const void *buf, const unsigned int buflen) +{ + struct ft_atom atom; + void *node; + char *p, *next; + int nextra, depth; + + node = ft_node_ph2node(cxt, phandle); + if (node == NULL) + return -1; + + depth = 0; + p = node; + + while ((next = ft_next(cxt, p, &atom)) != NULL) { + switch (atom.tag) { + case OF_DT_BEGIN_NODE: + ++depth; + break; + case OF_DT_END_NODE: + if (--depth > 0) + break; + /* haven't found the property, insert here */ + cxt->p = p; + return ft_prop(cxt, propname, buf, buflen); + case OF_DT_PROP: + if ((depth != 1) || strcmp(atom.name, propname)) + break; + /* found an existing property, overwrite it */ + nextra = _ALIGN(buflen, 4) - _ALIGN(atom.size, 4); + cxt->p = atom.data; + if (nextra && !ft_make_space(cxt, &cxt->p, FT_STRUCT, + nextra)) + return -1; + *(u32 *) (cxt->p - 8) = cpu_to_be32(buflen); + ft_put_bin(cxt, buf, buflen); + return 0; + } + p = next; + } + return -1; +} + +int ft_del_prop(struct ft_cxt *cxt, const void *phandle, const char *propname) +{ + struct ft_atom atom; + void *node; + char *p, *next; + int size; + + node = ft_node_ph2node(cxt, phandle); + if (node == NULL) + return -1; + + p = node; + while ((next = ft_next(cxt, p, &atom)) != NULL) { + switch (atom.tag) { + case OF_DT_BEGIN_NODE: + case OF_DT_END_NODE: + return -1; + case OF_DT_PROP: + if (strcmp(atom.name, propname)) + break; + /* found the property, remove it */ + size = 12 + -_ALIGN(atom.size, 4); + cxt->p = p; + if (!ft_make_space(cxt, &cxt->p, FT_STRUCT, -size)) + return -1; + return 0; + } + p = next; + } + return -1; +} + +void *ft_create_node(struct ft_cxt *cxt, const void *parent, const char *path) +{ + struct ft_atom atom; + char *p, *next; + int depth = 0; + + p = cxt->rgn[FT_STRUCT].start; + while ((next = ft_next(cxt, p, &atom)) != NULL) { + switch (atom.tag) { + case OF_DT_BEGIN_NODE: + ++depth; + if (depth == 1 && strcmp(atom.name, path) == 0) + /* duplicate node path, return error */ + return NULL; + break; + case OF_DT_END_NODE: + --depth; + if (depth > 0) + break; + /* end of node, insert here */ + cxt->p = p; + ft_begin_node(cxt, path); + ft_end_node(cxt); + return p; + } + p = next; + } + return NULL; +} Index: linux-2.6/arch/powerpc/boot/flatdevtree.h =================================================================== --- linux-2.6.orig/arch/powerpc/boot/flatdevtree.h +++ linux-2.6/arch/powerpc/boot/flatdevtree.h @@ -17,7 +17,7 @@ #ifndef FLATDEVTREE_H #define FLATDEVTREE_H -#include "types.h" +#include "flatdevtree_env.h" /* Definitions used by the flattened device tree */ #define OF_DT_HEADER 0xd00dfeed /* marker */ @@ -43,4 +43,64 @@ struct boot_param_header { u32 dt_strings_size; /* size of the DT strings block */ }; +struct ft_reserve { + u64 start; + u64 len; +}; + +struct ft_region { + char *start; + unsigned long size; +}; + +enum ft_rgn_id { + FT_RSVMAP, + FT_STRUCT, + FT_STRINGS, + FT_N_REGION +}; + +#define FT_MAX_DEPTH 50 + +struct ft_cxt { + struct boot_param_header *bph; + int max_size; /* maximum size of tree */ + int isordered; /* everything in standard order */ + void *(*realloc)(void *, unsigned long); + char *str_anchor; + char *p; /* current insertion point in structs */ + struct ft_region rgn[FT_N_REGION]; + void *genealogy[FT_MAX_DEPTH+1]; + char **node_tbl; + unsigned int node_max; + unsigned int nodes_used; +}; + +int ft_begin_node(struct ft_cxt *cxt, const char *name); +void ft_end_node(struct ft_cxt *cxt); + +void ft_begin_tree(struct ft_cxt *cxt); +void ft_end_tree(struct ft_cxt *cxt); + +void ft_nop(struct ft_cxt *cxt); +int ft_prop(struct ft_cxt *cxt, const char *name, + const void *data, unsigned int sz); +int ft_prop_str(struct ft_cxt *cxt, const char *name, const char *str); +int ft_prop_int(struct ft_cxt *cxt, const char *name, unsigned int val); +void ft_begin(struct ft_cxt *cxt, void *blob, unsigned int max_size, + void *(*realloc_fn)(void *, unsigned long)); +int ft_open(struct ft_cxt *cxt, void *blob, unsigned int max_size, + unsigned int max_find_device, + void *(*realloc_fn)(void *, unsigned long)); +int ft_add_rsvmap(struct ft_cxt *cxt, u64 physaddr, u64 size); + +void ft_dump_blob(const void *bphp); +void ft_merge_blob(struct ft_cxt *cxt, void *blob); +void *ft_find_device(struct ft_cxt *cxt, const char *srch_path); +void *ft_find_descendent(struct ft_cxt *cxt, void *top, const char *srch_path); +int ft_get_prop(struct ft_cxt *cxt, const void *phandle, const char *propname, + void *buf, const unsigned int buflen); +int ft_set_prop(struct ft_cxt *cxt, const void *phandle, const char *propname, + const void *buf, const unsigned int buflen); + #endif /* FLATDEVTREE_H */ Index: linux-2.6/arch/powerpc/boot/flatdevtree_env.h =================================================================== --- /dev/null +++ linux-2.6/arch/powerpc/boot/flatdevtree_env.h @@ -0,0 +1,47 @@ +/* + * This file adds the header file glue so that the shared files + * flatdevicetree.[ch] can compile and work in the powerpc bootwrapper. + * + * strncmp & strchr copied from + * Copyright (C) 1991, 1992 Linus Torvalds + * + * Maintained by: Mark A. Greer + */ +#ifndef _PPC_BOOT_FLATDEVTREE_ENV_H_ +#define _PPC_BOOT_FLATDEVTREE_ENV_H_ + +#include +#include +#include "types.h" +#include "string.h" +#include "stdio.h" +#include "ops.h" + +#define be16_to_cpu(x) (x) +#define cpu_to_be16(x) (x) +#define be32_to_cpu(x) (x) +#define cpu_to_be32(x) (x) +#define be64_to_cpu(x) (x) +#define cpu_to_be64(x) (x) + +static inline int strncmp(const char *cs, const char *ct, size_t count) +{ + signed char __res = 0; + + while (count) { + if ((__res = *cs - *ct++) != 0 || !*cs++) + break; + count--; + } + return __res; +} + +static inline char *strchr(const char *s, int c) +{ + for (; *s != (char)c; ++s) + if (*s == '\0') + return NULL; + return (char *)s; +} + +#endif /* _PPC_BOOT_FLATDEVTREE_ENV_H_ */ Index: linux-2.6/arch/powerpc/boot/flatdevtree_misc.c =================================================================== --- /dev/null +++ linux-2.6/arch/powerpc/boot/flatdevtree_misc.c @@ -0,0 +1,56 @@ +/* + * This file does the necessary interface mapping between the bootwrapper + * device tree operations and the interface provided by shared source + * files flatdevicetree.[ch]. + * + * Author: Mark A. Greer + * + * 2006 (c) MontaVista Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ +#include +#include "flatdevtree.h" +#include "ops.h" + +static struct ft_cxt cxt; + +static void *ft_finddevice(const char *name) +{ + return ft_find_device(&cxt, name); +} + +static int ft_getprop(const void *phandle, const char *propname, void *buf, + const int buflen) +{ + return ft_get_prop(&cxt, phandle, propname, buf, buflen); +} + +static int ft_setprop(const void *phandle, const char *propname, + const void *buf, const int buflen) +{ + return ft_set_prop(&cxt, phandle, propname, buf, buflen); +} + +static void ft_pack(void) +{ + ft_end_tree(&cxt); +} + +static unsigned long ft_addr(void) +{ + return (unsigned long)cxt.bph; +} + +int ft_init(void *dt_blob, unsigned int max_size, unsigned int max_find_device) +{ + dt_ops.finddevice = ft_finddevice; + dt_ops.getprop = ft_getprop; + dt_ops.setprop = ft_setprop; + dt_ops.ft_pack = ft_pack; + dt_ops.ft_addr = ft_addr; + + return ft_open(&cxt, dt_blob, max_size, max_find_device, + platform_ops.realloc); +} Index: linux-2.6/arch/powerpc/boot/io.h =================================================================== --- /dev/null +++ linux-2.6/arch/powerpc/boot/io.h @@ -0,0 +1,53 @@ +#ifndef _IO_H +#define __IO_H +/* + * Low-level I/O routines. + * + * Copied from (which has no copyright) + */ +static inline int in_8(const volatile unsigned char *addr) +{ + int ret; + + __asm__ __volatile__("lbz%U1%X1 %0,%1; twi 0,%0,0; isync" + : "=r" (ret) : "m" (*addr)); + return ret; +} + +static inline void out_8(volatile unsigned char *addr, int val) +{ + __asm__ __volatile__("stb%U0%X0 %1,%0; sync" + : "=m" (*addr) : "r" (val)); +} + +static inline unsigned in_le32(const volatile unsigned *addr) +{ + unsigned ret; + + __asm__ __volatile__("lwbrx %0,0,%1; twi 0,%0,0; isync" + : "=r" (ret) : "r" (addr), "m" (*addr)); + return ret; +} + +static inline unsigned in_be32(const volatile unsigned *addr) +{ + unsigned ret; + + __asm__ __volatile__("lwz%U1%X1 %0,%1; twi 0,%0,0; isync" + : "=r" (ret) : "m" (*addr)); + return ret; +} + +static inline void out_le32(volatile unsigned *addr, int val) +{ + __asm__ __volatile__("stwbrx %1,0,%2; sync" : "=m" (*addr) + : "r" (val), "r" (addr)); +} + +static inline void out_be32(volatile unsigned *addr, int val) +{ + __asm__ __volatile__("stw%U0%X0 %1,%0; sync" + : "=m" (*addr) : "r" (val)); +} + +#endif /* _IO_H */ Index: linux-2.6/arch/powerpc/boot/main.c =================================================================== --- linux-2.6.orig/arch/powerpc/boot/main.c +++ linux-2.6/arch/powerpc/boot/main.c @@ -27,6 +27,8 @@ extern char _vmlinux_start[]; extern char _vmlinux_end[]; extern char _initrd_start[]; extern char _initrd_end[]; +extern char _dtb_start[]; +extern char _dtb_end[]; struct addr_range { unsigned long addr; @@ -250,10 +252,6 @@ static void prep_kernel(unsigned long *a flush_cache((void *)vmlinux.addr, vmlinux.size); } -void __attribute__ ((weak)) ft_init(void *dt_blob) -{ -} - /* A buffer that may be edited by tools operating on a zImage binary so as to * edit the command line passed to vmlinux (by setting /chosen/bootargs). * The buffer is put in it's own section so that tools may locate it easier. @@ -285,19 +283,12 @@ static void set_cmdline(char *buf) setprop(devp, "bootargs", buf, strlen(buf) + 1); } -/* Section where ft can be tacked on after zImage is built */ -union blobspace { - struct boot_param_header hdr; - char space[8*1024]; -} dt_blob __attribute__((__section__("__builtin_ft"))); - struct platform_ops platform_ops; struct dt_ops dt_ops; struct console_ops console_ops; void start(unsigned long a1, unsigned long a2, void *promptr, void *sp) { - int have_dt = 0; kernel_entry_t kentry; char cmdline[COMMAND_LINE_SIZE]; @@ -306,15 +297,7 @@ void start(unsigned long a1, unsigned lo memset(&dt_ops, 0, sizeof(dt_ops)); memset(&console_ops, 0, sizeof(console_ops)); - /* Override the dt_ops and device tree if there was an flat dev - * tree attached to the zImage. - */ - if (dt_blob.hdr.magic == OF_DT_HEADER) { - have_dt = 1; - ft_init(&dt_blob); - } - - if (platform_init(promptr)) + if (platform_init(promptr, _dtb_start, _dtb_end)) exit(); if (console_ops.open && (console_ops.open() < 0)) exit(); @@ -342,8 +325,10 @@ void start(unsigned long a1, unsigned lo console_ops.close(); kentry = (kernel_entry_t) vmlinux.addr; - if (have_dt) + if (_dtb_end > _dtb_start) { + dt_ops.ft_pack(); kentry(dt_ops.ft_addr(), 0, NULL); + } else /* XXX initrd addr/size should be passed in properties */ kentry(a1, a2, promptr); Index: linux-2.6/arch/powerpc/boot/mktree.c =================================================================== --- /dev/null +++ linux-2.6/arch/powerpc/boot/mktree.c @@ -0,0 +1,152 @@ +/* + * Makes a tree bootable image for IBM Evaluation boards. + * Basically, just take a zImage, skip the ELF header, and stuff + * a 32 byte header on the front. + * + * We use htonl, which is a network macro, to make sure we're doing + * The Right Thing on an LE machine. It's non-obvious, but it should + * work on anything BSD'ish. + */ + +#include +#include +#include +#include +#include +#include +#include +#ifdef __sun__ +#include +#else +#include +#endif + +/* This gets tacked on the front of the image. There are also a few + * bytes allocated after the _start label used by the boot rom (see + * head.S for details). + */ +typedef struct boot_block { + uint32_t bb_magic; /* 0x0052504F */ + uint32_t bb_dest; /* Target address of the image */ + uint32_t bb_num_512blocks; /* Size, rounded-up, in 512 byte blks */ + uint32_t bb_debug_flag; /* Run debugger or image after load */ + uint32_t bb_entry_point; /* The image address to start */ + uint32_t bb_checksum; /* 32 bit checksum including header */ + uint32_t reserved[2]; +} boot_block_t; + +#define IMGBLK 512 +char tmpbuf[IMGBLK]; + +int main(int argc, char *argv[]) +{ + int in_fd, out_fd; + int nblks, i; + uint cksum, *cp; + struct stat st; + boot_block_t bt; + + if (argc < 3) { + fprintf(stderr, "usage: %s [entry-point]\n",argv[0]); + exit(1); + } + + if (stat(argv[1], &st) < 0) { + perror("stat"); + exit(2); + } + + nblks = (st.st_size + IMGBLK) / IMGBLK; + + bt.bb_magic = htonl(0x0052504F); + + /* If we have the optional entry point parameter, use it */ + if (argc == 4) + bt.bb_dest = bt.bb_entry_point = htonl(strtoul(argv[3], NULL, 0)); + else + bt.bb_dest = bt.bb_entry_point = htonl(0x500000); + + /* We know these from the linker command. + * ...and then move it up into memory a little more so the + * relocation can happen. + */ + bt.bb_num_512blocks = htonl(nblks); + bt.bb_debug_flag = 0; + + bt.bb_checksum = 0; + + /* To be neat and tidy :-). + */ + bt.reserved[0] = 0; + bt.reserved[1] = 0; + + if ((in_fd = open(argv[1], O_RDONLY)) < 0) { + perror("zImage open"); + exit(3); + } + + if ((out_fd = open(argv[2], (O_RDWR | O_CREAT | O_TRUNC), 0666)) < 0) { + perror("bootfile open"); + exit(3); + } + + cksum = 0; + cp = (void *)&bt; + for (i=0; i 0) { + if (read(in_fd, tmpbuf, IMGBLK) < 0) { + perror("zImage read"); + exit(5); + } + cp = (uint *)tmpbuf; + for (i=0; i + * (which had no copyright) + * Modifications: 2006 (c) MontaVista Software, Inc. + * + * Modified by: Mark A. Greer + */ +#include +#include +#include "types.h" +#include "string.h" +#include "stdio.h" +#include "io.h" +#include "ops.h" + +#define UART_DLL 0 /* Out: Divisor Latch Low */ +#define UART_DLM 1 /* Out: Divisor Latch High */ +#define UART_FCR 2 /* Out: FIFO Control Register */ +#define UART_LCR 3 /* Out: Line Control Register */ +#define UART_MCR 4 /* Out: Modem Control Register */ +#define UART_LSR 5 /* In: Line Status Register */ +#define UART_LSR_THRE 0x20 /* Transmit-hold-register empty */ +#define UART_LSR_DR 0x01 /* Receiver data ready */ +#define UART_MSR 6 /* In: Modem Status Register */ +#define UART_SCR 7 /* I/O: Scratch Register */ + +static unsigned char *reg_base; +static u32 reg_shift; + +static int ns16550_open(void) +{ + out_8(reg_base + (UART_FCR << reg_shift), 0x06); + return 0; +} + +static void ns16550_putc(unsigned char c) +{ + while ((in_8(reg_base + (UART_LSR << reg_shift)) & UART_LSR_THRE) == 0); + out_8(reg_base, c); +} + +static unsigned char ns16550_getc(void) +{ + while ((in_8(reg_base + (UART_LSR << reg_shift)) & UART_LSR_DR) == 0); + return in_8(reg_base); +} + +static u8 ns16550_tstc(void) +{ + return ((in_8(reg_base + (UART_LSR << reg_shift)) & UART_LSR_DR) != 0); +} + +int ns16550_console_init(void *devp, struct serial_console_data *scdp) +{ + int n; + + n = getprop(devp, "virtual-reg", ®_base, sizeof(reg_base)); + if (n != sizeof(reg_base)) + return -1; + + n = getprop(devp, "reg-shift", ®_shift, sizeof(reg_shift)); + if (n != sizeof(reg_shift)) + reg_shift = 0; + + scdp->open = ns16550_open; + scdp->putc = ns16550_putc; + scdp->getc = ns16550_getc; + scdp->tstc = ns16550_tstc; + scdp->close = NULL; + + return 0; +} Index: linux-2.6/arch/powerpc/boot/of.c =================================================================== --- linux-2.6.orig/arch/powerpc/boot/of.c +++ linux-2.6/arch/powerpc/boot/of.c @@ -256,24 +256,18 @@ static void of_console_write(char *buf, call_prom("write", 3, 1, of_stdout_handle, buf, len); } -int platform_init(void *promptr) +int platform_init(void *promptr, char *dt_blob_start, char *dt_blob_end) { - platform_ops.fixups = NULL; platform_ops.image_hdr = of_image_hdr; platform_ops.malloc = of_try_claim; - platform_ops.free = NULL; platform_ops.exit = of_exit; dt_ops.finddevice = of_finddevice; dt_ops.getprop = of_getprop; dt_ops.setprop = of_setprop; - dt_ops.translate_addr = NULL; console_ops.open = of_console_open; console_ops.write = of_console_write; - console_ops.edit_cmdline = NULL; - console_ops.close = NULL; - console_ops.data = NULL; prom = (int (*)(void *))promptr; return 0; Index: linux-2.6/arch/powerpc/boot/ops.h =================================================================== --- linux-2.6.orig/arch/powerpc/boot/ops.h +++ linux-2.6/arch/powerpc/boot/ops.h @@ -22,7 +22,8 @@ struct platform_ops { void (*fixups)(void); void (*image_hdr)(const void *); void * (*malloc)(u32 size); - void (*free)(void *ptr, u32 size); + void (*free)(void *ptr); + void * (*realloc)(void *ptr, unsigned long size); void (*exit)(void); }; extern struct platform_ops platform_ops; @@ -30,12 +31,11 @@ extern struct platform_ops platform_ops; /* Device Tree operations */ struct dt_ops { void * (*finddevice)(const char *name); - int (*getprop)(const void *node, const char *name, void *buf, + int (*getprop)(const void *phandle, const char *name, void *buf, const int buflen); - int (*setprop)(const void *node, const char *name, + int (*setprop)(const void *phandle, const char *name, const void *buf, const int buflen); - u64 (*translate_addr)(const char *path, const u32 *in_addr, - const u32 addr_len); + void (*ft_pack)(void); unsigned long (*ft_addr)(void); }; extern struct dt_ops dt_ops; @@ -59,10 +59,13 @@ struct serial_console_data { void (*close)(void); }; -extern int platform_init(void *promptr); -extern void simple_alloc_init(void); -extern void ft_init(void *dt_blob); -extern int serial_console_init(void); +int platform_init(void *promptr, char *dt_blob_start, char *dt_blob_end); +int ft_init(void *dt_blob, unsigned int max_size, unsigned int max_find_device); +int serial_console_init(void); +int ns16550_console_init(void *devp, struct serial_console_data *scdp); +void *simple_alloc_init(char *base, u32 heap_size, u32 granularity, + u32 max_allocs); + static inline void *finddevice(const char *name) { @@ -84,10 +87,10 @@ static inline void *malloc(u32 size) return (platform_ops.malloc) ? platform_ops.malloc(size) : NULL; } -static inline void free(void *ptr, u32 size) +static inline void free(void *ptr) { if (platform_ops.free) - platform_ops.free(ptr, size); + platform_ops.free(ptr); } static inline void exit(void) Index: linux-2.6/arch/powerpc/boot/serial.c =================================================================== --- /dev/null +++ linux-2.6/arch/powerpc/boot/serial.c @@ -0,0 +1,142 @@ +/* + * Generic serial console support + * + * Author: Mark A. Greer + * + * Code in serial_edit_cmdline() copied from + * and was written by Matt Porter . + * + * 2001,2006 (c) MontaVista Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ +#include +#include +#include "types.h" +#include "string.h" +#include "stdio.h" +#include "io.h" +#include "ops.h" + +extern void udelay(long delay); + +static int serial_open(void) +{ + struct serial_console_data *scdp = console_ops.data; + return scdp->open(); +} + +static void serial_write(char *buf, int len) +{ + struct serial_console_data *scdp = console_ops.data; + + while (*buf != '\0') + scdp->putc(*buf++); +} + +static void serial_edit_cmdline(char *buf, int len) +{ + int timer = 0, count; + char ch, *cp; + struct serial_console_data *scdp = console_ops.data; + + cp = buf; + count = strlen(buf); + cp = &buf[count]; + count++; + + while (timer++ < 5*1000) { + if (scdp->tstc()) { + while (((ch = scdp->getc()) != '\n') && (ch != '\r')) { + /* Test for backspace/delete */ + if ((ch == '\b') || (ch == '\177')) { + if (cp != buf) { + cp--; + count--; + printf("\b \b"); + } + /* Test for ^x/^u (and wipe the line) */ + } else if ((ch == '\030') || (ch == '\025')) { + while (cp != buf) { + cp--; + count--; + printf("\b \b"); + } + } else if (count < len) { + *cp++ = ch; + count++; + scdp->putc(ch); + } + } + break; /* Exit 'timer' loop */ + } + udelay(1000); /* 1 msec */ + } + *cp = 0; +} + +static void serial_close(void) +{ + struct serial_console_data *scdp = console_ops.data; + + if (scdp->close) + scdp->close(); +} + +static void *serial_get_stdout_devp(void) +{ + void *devp; + char devtype[MAX_PROP_LEN]; + char path[MAX_PATH_LEN]; + + devp = finddevice("/chosen"); + if (devp == NULL) + goto err_out; + + if (getprop(devp, "linux,stdout-path", path, MAX_PATH_LEN) > 0) { + devp = finddevice(path); + if (devp == NULL) + goto err_out; + + if ((getprop(devp, "device_type", devtype, sizeof(devtype)) > 0) + && !strcmp(devtype, "serial")) + return devp; + } +err_out: + return NULL; +} + +static struct serial_console_data serial_cd; + +/* Node's "compatible" property determines which serial driver to use */ +int serial_console_init(void) +{ + void *devp; + int rc = -1; + char compat[MAX_PROP_LEN]; + + devp = serial_get_stdout_devp(); + if (devp == NULL) + goto err_out; + + if (getprop(devp, "compatible", compat, sizeof(compat)) < 0) + goto err_out; + + if (!strcmp(compat, "ns16550")) + rc = ns16550_console_init(devp, &serial_cd); + + /* Add other serial console driver calls here */ + + if (!rc) { + console_ops.open = serial_open; + console_ops.write = serial_write; + console_ops.edit_cmdline = serial_edit_cmdline; + console_ops.close = serial_close; + console_ops.data = &serial_cd; + + return 0; + } +err_out: + return -1; +} Index: linux-2.6/arch/powerpc/boot/simple_alloc.c =================================================================== --- /dev/null +++ linux-2.6/arch/powerpc/boot/simple_alloc.c @@ -0,0 +1,149 @@ +/* + * Implement primitive realloc(3) functionality. + * + * Author: Mark A. Greer + * + * 2006 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include "types.h" +#include "page.h" +#include "string.h" +#include "ops.h" + +#define ENTRY_BEEN_USED 0x01 +#define ENTRY_IN_USE 0x02 + +static struct alloc_info { + u32 flags; + u32 base; + u32 size; +} *alloc_tbl; + +static u32 tbl_entries; +static u32 alloc_min; +static u32 next_base; +static u32 space_left; + +/* + * First time an entry is used, its base and size are set. + * An entry can be freed and re-malloc'd but its base & size don't change. + * Should be smart enough for needs of bootwrapper. + */ +static void *simple_malloc(u32 size) +{ + u32 i; + struct alloc_info *p = alloc_tbl; + + if (size == 0) + goto err_out; + + size = _ALIGN_UP(size, alloc_min); + + for (i=0; iflags & ENTRY_BEEN_USED)) { /* never been used */ + if (size <= space_left) { + p->base = next_base; + p->size = size; + p->flags = ENTRY_BEEN_USED | ENTRY_IN_USE; + next_base += size; + space_left -= size; + return (void *)p->base; + } + goto err_out; /* not enough space left */ + } + /* reuse an entry keeping same base & size */ + else if (!(p->flags & ENTRY_IN_USE) && (size <= p->size)) { + p->flags |= ENTRY_IN_USE; + return (void *)p->base; + } +err_out: + return NULL; +} + +static struct alloc_info *simple_find_entry(void *ptr) +{ + u32 i; + struct alloc_info *p = alloc_tbl; + + for (i=0; iflags & ENTRY_BEEN_USED)) + break; + if ((p->flags & ENTRY_IN_USE) && (p->base == (u32)ptr)) + return p; + } + return NULL; +} + +static void simple_free(void *ptr) +{ + struct alloc_info *p = simple_find_entry(ptr); + + if (p != NULL) + p->flags &= ~ENTRY_IN_USE; +} + +/* + * Change size of area pointed to by 'ptr' to 'size'. + * If 'ptr' is NULL, then its a malloc(). If 'size' is 0, then its a free(). + * 'ptr' must be NULL or a pointer to a non-freed area previously returned by + * simple_realloc() or simple_malloc(). + */ +static void *simple_realloc(void *ptr, unsigned long size) +{ + struct alloc_info *p; + void *new; + + if (size == 0) { + simple_free(ptr); + return NULL; + } + + if (ptr == NULL) + return simple_malloc(size); + + p = simple_find_entry(ptr); + if (p == NULL) /* ptr not from simple_malloc/simple_realloc */ + return NULL; + if (size <= p->size) /* fits in current block */ + return ptr; + + new = simple_malloc(size); + memcpy(new, ptr, p->size); + simple_free(ptr); + return new; +} + +/* + * Returns addr of first byte after heap so caller can see if it took + * too much space. If so, change args & try again. + */ +void *simple_alloc_init(char *base, u32 heap_size, u32 granularity, + u32 max_allocs) +{ + u32 heap_base, tbl_size; + + heap_size = _ALIGN_UP(heap_size, granularity); + alloc_min = granularity; + tbl_entries = max_allocs; + + tbl_size = tbl_entries * sizeof(struct alloc_info); + + alloc_tbl = (struct alloc_info *)_ALIGN_UP((unsigned long)base, 8); + memset(alloc_tbl, 0, tbl_size); + + heap_base = _ALIGN_UP((u32)alloc_tbl + tbl_size, alloc_min); + + next_base = heap_base; + space_left = heap_size; + + platform_ops.malloc = simple_malloc; + platform_ops.free = simple_free; + platform_ops.realloc = simple_realloc; + + return (void *)(heap_base + heap_size); +} Index: linux-2.6/arch/powerpc/boot/stdio.c =================================================================== --- linux-2.6.orig/arch/powerpc/boot/stdio.c +++ linux-2.6/arch/powerpc/boot/stdio.c @@ -320,6 +320,7 @@ printf(const char *fmt, ...) va_start(args, fmt); n = vsprintf(sprint_buf, fmt, args); va_end(args); - console_ops.write(sprint_buf, n); + if (console_ops.write) + console_ops.write(sprint_buf, n); return n; } Index: linux-2.6/arch/powerpc/boot/util.S =================================================================== --- /dev/null +++ linux-2.6/arch/powerpc/boot/util.S @@ -0,0 +1,88 @@ +/* + * Copied from + * + * This file contains miscellaneous low-level functions. + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * 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 + * 2 of the License, or (at your option) any later version. + * + */ +#include "ppc_asm.h" + +#define SPRN_PVR 0x11F /* Processor Version Register */ + + .text + +/* udelay (on non-601 processors) needs to know the period of the + * timebase in nanoseconds. This used to be hardcoded to be 60ns + * (period of 66MHz/4). Now a variable is used that is initialized to + * 60 for backward compatibility, but it can be overridden as necessary + * with code something like this: + * extern unsigned long timebase_period_ns; + * timebase_period_ns = 1000000000 / bd->bi_tbfreq; + */ + .data + .globl timebase_period_ns +timebase_period_ns: + .long 60 + + .text +/* + * Delay for a number of microseconds + */ + .globl udelay +udelay: + mfspr r4,SPRN_PVR + srwi r4,r4,16 + cmpwi 0,r4,1 /* 601 ? */ + bne .udelay_not_601 +00: li r0,86 /* Instructions / microsecond? */ + mtctr r0 +10: addi r0,r0,0 /* NOP */ + bdnz 10b + subic. r3,r3,1 + bne 00b + blr + +.udelay_not_601: + mulli r4,r3,1000 /* nanoseconds */ + /* Change r4 to be the number of ticks using: + * (nanoseconds + (timebase_period_ns - 1 )) / timebase_period_ns + * timebase_period_ns defaults to 60 (16.6MHz) */ + mflr r5 + bl 0f +0: mflr r6 + mtlr r5 + lis r5,0b@ha + addi r5,r5,0b@l + subf r5,r5,r6 /* In case we're relocated */ + addis r5,r5,timebase_period_ns@ha + lwz r5,timebase_period_ns@l(r5) + add r4,r4,r5 + addi r4,r4,-1 + divw r4,r4,r5 /* BUS ticks */ +1: mftbu r5 + mftb r6 + mftbu r7 + cmpw 0,r5,r7 + bne 1b /* Get [synced] base time */ + addc r9,r6,r4 /* Compute end time */ + addze r8,r5 +2: mftbu r5 + cmpw 0,r5,r8 + blt 2b + bgt 3f + mftb r6 + cmpw 0,r6,r9 + blt 2b +3: blr Index: linux-2.6/arch/powerpc/boot/wrapper =================================================================== --- linux-2.6.orig/arch/powerpc/boot/wrapper +++ linux-2.6/arch/powerpc/boot/wrapper @@ -179,11 +179,11 @@ if [ -z "$cacheit" ]; then fi if [ -n "$initrd" ]; then - addsec $tmp "$initrd" initrd + addsec $tmp "$initrd" $isection fi if [ -n "$dtb" ]; then - addsec $tmp "$dtb" dtb + addsec $tmp "$dtb" .kernel:dtb fi if [ "$platform" != "miboot" ]; then Index: linux-2.6/arch/powerpc/boot/zImage.coff.lds.S =================================================================== --- linux-2.6.orig/arch/powerpc/boot/zImage.coff.lds.S +++ linux-2.6/arch/powerpc/boot/zImage.coff.lds.S @@ -21,6 +21,10 @@ SECTIONS *(.got2) __got2_end = .; + _dtb_start = .; + *(.kernel:dtb) + _dtb_end = .; + _vmlinux_start = .; *(.kernel:vmlinux.strip) _vmlinux_end = .; Index: linux-2.6/arch/powerpc/boot/zImage.lds.S =================================================================== --- linux-2.6.orig/arch/powerpc/boot/zImage.lds.S +++ linux-2.6/arch/powerpc/boot/zImage.lds.S @@ -21,6 +21,11 @@ SECTIONS __got2_end = .; } + . = ALIGN(8); + _dtb_start = .; + .kernel:dtb : { *(.kernel:dtb) } + _dtb_end = .; + . = ALIGN(4096); _vmlinux_start = .; .kernel:vmlinux.strip : { *(.kernel:vmlinux.strip) } Index: linux-2.6/arch/powerpc/configs/cell_defconfig =================================================================== --- linux-2.6.orig/arch/powerpc/configs/cell_defconfig +++ linux-2.6/arch/powerpc/configs/cell_defconfig @@ -139,7 +139,19 @@ CONFIG_RTAS_FLASH=y CONFIG_MMIO_NVRAM=y # CONFIG_PPC_MPC106 is not set # CONFIG_PPC_970_NAP is not set -# CONFIG_CPU_FREQ is not set +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_TABLE=y +CONFIG_CPU_FREQ_DEBUG=y +CONFIG_CPU_FREQ_STAT=y +# CONFIG_CPU_FREQ_STAT_DETAILS is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +# CONFIG_CPU_FREQ_PMAC64 is not set # CONFIG_WANT_EARLY_SERIAL is not set # CONFIG_MPIC is not set @@ -149,6 +161,8 @@ CONFIG_MMIO_NVRAM=y CONFIG_SPU_FS=m CONFIG_SPU_BASE=y CONFIG_CBE_RAS=y +CONFIG_CBE_THERM=m +CONFIG_CBE_CPUFREQ=m # # Kernel options Index: linux-2.6/arch/powerpc/kernel/asm-offsets.c =================================================================== --- linux-2.6.orig/arch/powerpc/kernel/asm-offsets.c +++ linux-2.6/arch/powerpc/kernel/asm-offsets.c @@ -118,7 +118,8 @@ int main(void) DEFINE(PACASTABRR, offsetof(struct paca_struct, stab_rr)); DEFINE(PACAR1, offsetof(struct paca_struct, saved_r1)); DEFINE(PACATOC, offsetof(struct paca_struct, kernel_toc)); - DEFINE(PACAPROCENABLED, offsetof(struct paca_struct, proc_enabled)); + DEFINE(PACASOFTIRQEN, offsetof(struct paca_struct, soft_enabled)); + DEFINE(PACAHARDIRQEN, offsetof(struct paca_struct, hard_enabled)); DEFINE(PACASLBCACHE, offsetof(struct paca_struct, slb_cache)); DEFINE(PACASLBCACHEPTR, offsetof(struct paca_struct, slb_cache_ptr)); DEFINE(PACACONTEXTID, offsetof(struct paca_struct, context.id)); Index: linux-2.6/arch/powerpc/kernel/cpu_setup_ppc970.S =================================================================== --- linux-2.6.orig/arch/powerpc/kernel/cpu_setup_ppc970.S +++ linux-2.6/arch/powerpc/kernel/cpu_setup_ppc970.S @@ -83,6 +83,22 @@ _GLOBAL(__setup_cpu_ppc970) rldimi r0,r11,52,8 /* set NAP and DPM */ li r11,0 rldimi r0,r11,32,31 /* clear EN_ATTN */ + b load_hids /* Jump to shared code */ + + +_GLOBAL(__setup_cpu_ppc970MP) + /* Do nothing if not running in HV mode */ + mfmsr r0 + rldicl. r0,r0,4,63 + beqlr + + mfspr r0,SPRN_HID0 + li r11,0x15 /* clear DOZE and SLEEP */ + rldimi r0,r11,52,6 /* set DEEPNAP, NAP and DPM */ + li r11,0 + rldimi r0,r11,32,31 /* clear EN_ATTN */ + +load_hids: mtspr SPRN_HID0,r0 mfspr r0,SPRN_HID0 mfspr r0,SPRN_HID0 Index: linux-2.6/arch/powerpc/kernel/cputable.c =================================================================== --- linux-2.6.orig/arch/powerpc/kernel/cputable.c +++ linux-2.6/arch/powerpc/kernel/cputable.c @@ -42,6 +42,7 @@ extern void __setup_cpu_745x(unsigned lo #endif /* CONFIG_PPC32 */ #ifdef CONFIG_PPC64 extern void __setup_cpu_ppc970(unsigned long offset, struct cpu_spec* spec); +extern void __setup_cpu_ppc970MP(unsigned long offset, struct cpu_spec* spec); extern void __restore_cpu_ppc970(void); #endif /* CONFIG_PPC64 */ @@ -222,7 +223,7 @@ static struct cpu_spec cpu_specs[] = { .icache_bsize = 128, .dcache_bsize = 128, .num_pmcs = 8, - .cpu_setup = __setup_cpu_ppc970, + .cpu_setup = __setup_cpu_ppc970MP, .cpu_restore = __restore_cpu_ppc970, .oprofile_cpu_type = "ppc64/970", .oprofile_type = PPC_OPROFILE_POWER4, Index: linux-2.6/arch/powerpc/kernel/crash.c =================================================================== --- linux-2.6.orig/arch/powerpc/kernel/crash.c +++ linux-2.6/arch/powerpc/kernel/crash.c @@ -111,7 +111,7 @@ void crash_ipi_callback(struct pt_regs * if (!cpu_online(cpu)) return; - local_irq_disable(); + hard_irq_disable(); if (!cpu_isset(cpu, cpus_in_crash)) crash_save_this_cpu(regs, cpu); cpu_set(cpu, cpus_in_crash); @@ -289,7 +289,7 @@ void default_machine_crash_shutdown(stru * an SMP system. * The kernel is broken so disable interrupts. */ - local_irq_disable(); + hard_irq_disable(); for_each_irq(irq) { struct irq_desc *desc = irq_desc + irq; Index: linux-2.6/arch/powerpc/kernel/entry_64.S =================================================================== --- linux-2.6.orig/arch/powerpc/kernel/entry_64.S +++ linux-2.6/arch/powerpc/kernel/entry_64.S @@ -87,6 +87,10 @@ system_call_common: addi r9,r1,STACK_FRAME_OVERHEAD ld r11,exception_marker@toc(r2) std r11,-16(r9) /* "regshere" marker */ + li r10,1 + stb r10,PACASOFTIRQEN(r13) + stb r10,PACAHARDIRQEN(r13) + std r10,SOFTE(r1) #ifdef CONFIG_PPC_ISERIES BEGIN_FW_FTR_SECTION /* Hack for handling interrupts when soft-enabling on iSeries */ @@ -94,8 +98,6 @@ BEGIN_FW_FTR_SECTION andi. r10,r12,MSR_PR /* from kernel */ crand 4*cr0+eq,4*cr1+eq,4*cr0+eq beq hardware_interrupt_entry - lbz r10,PACAPROCENABLED(r13) - std r10,SOFTE(r1) END_FW_FTR_SECTION_IFSET(FW_FEATURE_ISERIES) #endif mfmsr r11 @@ -460,9 +462,9 @@ _GLOBAL(ret_from_except_lite) #endif restore: + ld r5,SOFTE(r1) #ifdef CONFIG_PPC_ISERIES BEGIN_FW_FTR_SECTION - ld r5,SOFTE(r1) cmpdi 0,r5,0 beq 4f /* Check for pending interrupts (iSeries) */ @@ -472,21 +474,25 @@ BEGIN_FW_FTR_SECTION beq+ 4f /* skip do_IRQ if no interrupts */ li r3,0 - stb r3,PACAPROCENABLED(r13) /* ensure we are soft-disabled */ + stb r3,PACASOFTIRQEN(r13) /* ensure we are soft-disabled */ ori r10,r10,MSR_EE mtmsrd r10 /* hard-enable again */ addi r3,r1,STACK_FRAME_OVERHEAD bl .do_IRQ b .ret_from_except_lite /* loop back and handle more */ - -4: stb r5,PACAPROCENABLED(r13) +4: END_FW_FTR_SECTION_IFSET(FW_FEATURE_ISERIES) #endif + stb r5,PACASOFTIRQEN(r13) ld r3,_MSR(r1) andi. r0,r3,MSR_RI beq- unrecov_restore + /* extract EE bit and use it to restore paca->hard_enabled */ + rldicl r4,r3,49,63 /* r0 = (r3 >> 15) & 1 */ + stb r4,PACAHARDIRQEN(r13) + andi. r0,r3,MSR_PR /* @@ -538,25 +544,15 @@ do_work: /* Check that preempt_count() == 0 and interrupts are enabled */ lwz r8,TI_PREEMPT(r9) cmpwi cr1,r8,0 -#ifdef CONFIG_PPC_ISERIES -BEGIN_FW_FTR_SECTION ld r0,SOFTE(r1) cmpdi r0,0 -END_FW_FTR_SECTION_IFSET(FW_FEATURE_ISERIES) -#endif -BEGIN_FW_FTR_SECTION - andi. r0,r3,MSR_EE -END_FW_FTR_SECTION_IFCLR(FW_FEATURE_ISERIES) crandc eq,cr1*4+eq,eq bne restore /* here we are preempting the current task */ 1: -#ifdef CONFIG_PPC_ISERIES -BEGIN_FW_FTR_SECTION li r0,1 - stb r0,PACAPROCENABLED(r13) -END_FW_FTR_SECTION_IFSET(FW_FEATURE_ISERIES) -#endif + stb r0,PACASOFTIRQEN(r13) + stb r0,PACAHARDIRQEN(r13) ori r10,r10,MSR_EE mtmsrd r10,1 /* reenable interrupts */ bl .preempt_schedule @@ -639,8 +635,7 @@ _GLOBAL(enter_rtas) /* There is no way it is acceptable to get here with interrupts enabled, * check it with the asm equivalent of WARN_ON */ - mfmsr r6 - andi. r0,r6,MSR_EE + lbz r0,PACASOFTIRQEN(r13) 1: tdnei r0,0 .section __bug_table,"a" .llong 1b,__LINE__ + 0x1000000, 1f, 2f @@ -649,7 +644,13 @@ _GLOBAL(enter_rtas) 1: .asciz __FILE__ 2: .asciz "enter_rtas" .previous - + + /* Hard-disable interrupts */ + mfmsr r6 + rldicl r7,r6,48,1 + rotldi r7,r7,16 + mtmsrd r7,1 + /* Unfortunately, the stack pointer and the MSR are also clobbered, * so they are saved in the PACA which allows us to restore * our original state after RTAS returns. Index: linux-2.6/arch/powerpc/kernel/head_64.S =================================================================== --- linux-2.6.orig/arch/powerpc/kernel/head_64.S +++ linux-2.6/arch/powerpc/kernel/head_64.S @@ -35,9 +35,7 @@ #include #include -#ifdef CONFIG_PPC_ISERIES #define DO_SOFT_DISABLE -#endif /* * We layout physical memory as follows: @@ -308,7 +306,9 @@ exception_marker: std r9,_LINK(r1); \ mfctr r10; /* save CTR in stackframe */ \ std r10,_CTR(r1); \ + lbz r10,PACASOFTIRQEN(r13); \ mfspr r11,SPRN_XER; /* save XER in stackframe */ \ + std r10,SOFTE(r1); \ std r11,_XER(r1); \ li r9,(n)+1; \ std r9,_TRAP(r1); /* set trap number */ \ @@ -343,6 +343,34 @@ label##_pSeries: \ EXCEPTION_PROLOG_PSERIES(PACA_EXGEN, label##_common) +#define MASKABLE_EXCEPTION_PSERIES(n, label) \ + . = n; \ + .globl label##_pSeries; \ +label##_pSeries: \ + HMT_MEDIUM; \ + mtspr SPRN_SPRG1,r13; /* save r13 */ \ + mfspr r13,SPRN_SPRG3; /* get paca address into r13 */ \ + std r9,PACA_EXGEN+EX_R9(r13); /* save r9, r10 */ \ + std r10,PACA_EXGEN+EX_R10(r13); \ + lbz r10,PACASOFTIRQEN(r13); \ + mfcr r9; \ + cmpwi r10,0; \ + beq masked_interrupt; \ + mfspr r10,SPRN_SPRG1; \ + std r10,PACA_EXGEN+EX_R13(r13); \ + std r11,PACA_EXGEN+EX_R11(r13); \ + std r12,PACA_EXGEN+EX_R12(r13); \ + clrrdi r12,r13,32; /* get high part of &label */ \ + mfmsr r10; \ + mfspr r11,SPRN_SRR0; /* save SRR0 */ \ + LOAD_HANDLER(r12,label##_common) \ + ori r10,r10,MSR_IR|MSR_DR|MSR_RI; \ + mtspr SPRN_SRR0,r12; \ + mfspr r12,SPRN_SRR1; /* and SRR1 */ \ + mtspr SPRN_SRR1,r10; \ + rfid; \ + b . /* prevent speculative execution */ + #define STD_EXCEPTION_ISERIES(n, label, area) \ .globl label##_iSeries; \ label##_iSeries: \ @@ -358,40 +386,32 @@ label##_iSeries: \ HMT_MEDIUM; \ mtspr SPRN_SPRG1,r13; /* save r13 */ \ EXCEPTION_PROLOG_ISERIES_1(PACA_EXGEN); \ - lbz r10,PACAPROCENABLED(r13); \ + lbz r10,PACASOFTIRQEN(r13); \ cmpwi 0,r10,0; \ beq- label##_iSeries_masked; \ EXCEPTION_PROLOG_ISERIES_2; \ b label##_common; \ -#ifdef DO_SOFT_DISABLE +#ifdef CONFIG_PPC_ISERIES #define DISABLE_INTS \ -BEGIN_FW_FTR_SECTION; \ - lbz r10,PACAPROCENABLED(r13); \ li r11,0; \ - std r10,SOFTE(r1); \ + stb r11,PACASOFTIRQEN(r13); \ +BEGIN_FW_FTR_SECTION; \ + stb r11,PACAHARDIRQEN(r13); \ +END_FW_FTR_SECTION_IFCLR(FW_FEATURE_ISERIES); \ +BEGIN_FW_FTR_SECTION; \ mfmsr r10; \ - stb r11,PACAPROCENABLED(r13); \ ori r10,r10,MSR_EE; \ mtmsrd r10,1; \ END_FW_FTR_SECTION_IFSET(FW_FEATURE_ISERIES) -#define ENABLE_INTS \ -BEGIN_FW_FTR_SECTION; \ - lbz r10,PACAPROCENABLED(r13); \ - mfmsr r11; \ - std r10,SOFTE(r1); \ - ori r11,r11,MSR_EE; \ -END_FW_FTR_SECTION_IFSET(FW_FEATURE_ISERIES); \ -BEGIN_FW_FTR_SECTION; \ - ld r12,_MSR(r1); \ - mfmsr r11; \ - rlwimi r11,r12,0,MSR_EE; \ -END_FW_FTR_SECTION_IFCLR(FW_FEATURE_ISERIES); \ - mtmsrd r11,1 +#else +#define DISABLE_INTS \ + li r11,0; \ + stb r11,PACASOFTIRQEN(r13); \ + stb r11,PACAHARDIRQEN(r13) -#else /* hard enable/disable interrupts */ -#define DISABLE_INTS +#endif /* CONFIG_PPC_ISERIES */ #define ENABLE_INTS \ ld r12,_MSR(r1); \ @@ -399,8 +419,6 @@ END_FW_FTR_SECTION_IFCLR(FW_FEATURE_ISER rlwimi r11,r12,0,MSR_EE; \ mtmsrd r11,1 -#endif - #define STD_EXCEPTION_COMMON(trap, label, hdlr) \ .align 7; \ .globl label##_common; \ @@ -541,11 +559,11 @@ instruction_access_slb_pSeries: mfspr r12,SPRN_SRR1 /* and SRR1 */ b .slb_miss_realmode /* Rel. branch works in real mode */ - STD_EXCEPTION_PSERIES(0x500, hardware_interrupt) + MASKABLE_EXCEPTION_PSERIES(0x500, hardware_interrupt) STD_EXCEPTION_PSERIES(0x600, alignment) STD_EXCEPTION_PSERIES(0x700, program_check) STD_EXCEPTION_PSERIES(0x800, fp_unavailable) - STD_EXCEPTION_PSERIES(0x900, decrementer) + MASKABLE_EXCEPTION_PSERIES(0x900, decrementer) STD_EXCEPTION_PSERIES(0xa00, trap_0a) STD_EXCEPTION_PSERIES(0xb00, trap_0b) @@ -597,7 +615,24 @@ system_call_pSeries: /*** pSeries interrupt support ***/ /* moved from 0xf00 */ - STD_EXCEPTION_PSERIES(., performance_monitor) + MASKABLE_EXCEPTION_PSERIES(., performance_monitor) + +/* + * An interrupt came in while soft-disabled; clear EE in SRR1, + * clear paca->hard_enabled and return. + */ +masked_interrupt: + stb r10,PACAHARDIRQEN(r13) + mtcrf 0x80,r9 + ld r9,PACA_EXGEN+EX_R9(r13) + mfspr r10,SPRN_SRR1 + rldicl r10,r10,48,1 /* clear MSR_EE */ + rotldi r10,r10,16 + mtspr SPRN_SRR1,r10 + ld r10,PACA_EXGEN+EX_R10(r13) + mfspr r13,SPRN_SPRG1 + rfid + b . .align 7 do_stab_bolted_pSeries: @@ -926,10 +961,18 @@ bad_stack: * any task or sent any task a signal, you should use * ret_from_except or ret_from_except_lite instead of this. */ +fast_exc_return_irq: /* restores irq state too */ + ld r3,SOFTE(r1) + ld r12,_MSR(r1) + stb r3,PACASOFTIRQEN(r13) /* restore paca->soft_enabled */ + rldicl r4,r12,49,63 /* get MSR_EE to LSB */ + stb r4,PACAHARDIRQEN(r13) /* restore paca->hard_enabled */ + b 1f + .globl fast_exception_return fast_exception_return: ld r12,_MSR(r1) - ld r11,_NIP(r1) +1: ld r11,_NIP(r1) andi. r3,r12,MSR_RI /* check if RI is set */ beq- unrecov_fer @@ -952,7 +995,8 @@ fast_exception_return: REST_8GPRS(2, r1) mfmsr r10 - clrrdi r10,r10,2 /* clear RI (LE is 0 already) */ + rldicl r10,r10,48,1 /* clear EE */ + rldicr r10,r10,16,61 /* clear RI (LE is 0 already) */ mtmsrd r10,1 mtspr SPRN_SRR1,r12 @@ -1325,7 +1369,17 @@ BEGIN_FW_FTR_SECTION * because ret_from_except_lite will check for and handle pending * interrupts if necessary. */ - beq 13f + beq 14f +END_FW_FTR_SECTION_IFSET(FW_FEATURE_ISERIES) +#endif +BEGIN_FW_FTR_SECTION + /* + * Here we have interrupts hard-disabled, so it is sufficient + * to restore paca->{soft,hard}_enable and get out. + */ + beq fast_exc_return_irq /* Return from exception on success */ +END_FW_FTR_SECTION_IFCLR(FW_FEATURE_ISERIES) + /* For a hash failure, we don't bother re-enabling interrupts */ ble- 12f @@ -1337,14 +1391,7 @@ BEGIN_FW_FTR_SECTION ld r3,SOFTE(r1) bl .local_irq_restore b 11f -END_FW_FTR_SECTION_IFSET(FW_FEATURE_ISERIES) -#endif -BEGIN_FW_FTR_SECTION - beq fast_exception_return /* Return from exception on success */ - ble- 12f /* Failure return from hash_page */ - - /* fall through */ -END_FW_FTR_SECTION_IFCLR(FW_FEATURE_ISERIES) +14: b .ret_from_except_lite /* Here we have a page fault that hash_page can't handle. */ handle_page_fault: @@ -1354,7 +1401,7 @@ handle_page_fault: addi r3,r1,STACK_FRAME_OVERHEAD bl .do_page_fault cmpdi r3,0 - beq+ 13f + beq+ 14b bl .save_nvgprs mr r5,r3 addi r3,r1,STACK_FRAME_OVERHEAD @@ -1371,8 +1418,6 @@ handle_page_fault: bl .low_hash_fault b .ret_from_except -13: b .ret_from_except_lite - /* here we have a segment miss */ do_ste_alloc: bl .ste_allocate /* try to insert stab entry */ @@ -1877,11 +1922,16 @@ _GLOBAL(__secondary_start) /* enable MMU and jump to start_secondary */ LOAD_REG_ADDR(r3, .start_secondary_prolog) LOAD_REG_IMMEDIATE(r4, MSR_KERNEL) -#ifdef DO_SOFT_DISABLE +#ifdef CONFIG_PPC_ISERIES BEGIN_FW_FTR_SECTION ori r4,r4,MSR_EE END_FW_FTR_SECTION_IFSET(FW_FEATURE_ISERIES) #endif +BEGIN_FW_FTR_SECTION + stb r7,PACASOFTIRQEN(r13) + stb r7,PACAHARDIRQEN(r13) +END_FW_FTR_SECTION_IFCLR(FW_FEATURE_ISERIES) + mtspr SPRN_SRR0,r3 mtspr SPRN_SRR1,r4 rfid @@ -2005,15 +2055,18 @@ _STATIC(start_here_common) /* Load up the kernel context */ 5: -#ifdef DO_SOFT_DISABLE -BEGIN_FW_FTR_SECTION li r5,0 - stb r5,PACAPROCENABLED(r13) /* Soft Disabled */ + stb r5,PACASOFTIRQEN(r13) /* Soft Disabled */ +#ifdef CONFIG_PPC_ISERIES +BEGIN_FW_FTR_SECTION mfmsr r5 ori r5,r5,MSR_EE /* Hard Enabled */ mtmsrd r5 END_FW_FTR_SECTION_IFSET(FW_FEATURE_ISERIES) #endif +BEGIN_FW_FTR_SECTION + stb r5,PACAHARDIRQEN(r13) +END_FW_FTR_SECTION_IFCLR(FW_FEATURE_ISERIES) bl .start_kernel Index: linux-2.6/arch/powerpc/kernel/idle.c =================================================================== --- linux-2.6.orig/arch/powerpc/kernel/idle.c +++ linux-2.6/arch/powerpc/kernel/idle.c @@ -39,6 +39,13 @@ #define cpu_should_die() 0 #endif +static int __init powersave_off(char *arg) +{ + ppc_md.power_save = NULL; + return 0; +} +__setup("powersave=off", powersave_off); + /* * The body of the idle task. */ Index: linux-2.6/arch/powerpc/kernel/idle_power4.S =================================================================== --- linux-2.6.orig/arch/powerpc/kernel/idle_power4.S +++ linux-2.6/arch/powerpc/kernel/idle_power4.S @@ -30,6 +30,13 @@ END_FTR_SECTION_IFCLR(CPU_FTR_CAN_NAP) beqlr /* Go to NAP now */ + mfmsr r7 + rldicl r0,r7,48,1 + rotldi r0,r0,16 + mtmsrd r0,1 /* hard-disable interrupts */ + li r0,1 + stb r0,PACASOFTIRQEN(r13) /* we'll hard-enable shortly */ + stb r0,PACAHARDIRQEN(r13) BEGIN_FTR_SECTION DSSALL sync @@ -38,7 +45,6 @@ END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC) ld r8,TI_LOCAL_FLAGS(r9) /* set napping bit */ ori r8,r8,_TLF_NAPPING /* so when we take an exception */ std r8,TI_LOCAL_FLAGS(r9) /* it will return to our caller */ - mfmsr r7 ori r7,r7,MSR_EE oris r7,r7,MSR_POW@h 1: sync Index: linux-2.6/arch/powerpc/kernel/irq.c =================================================================== --- linux-2.6.orig/arch/powerpc/kernel/irq.c +++ linux-2.6/arch/powerpc/kernel/irq.c @@ -64,8 +64,9 @@ #include #include #include -#ifdef CONFIG_PPC_ISERIES +#ifdef CONFIG_PPC64 #include +#include #endif int __irq_offset_value; @@ -95,6 +96,27 @@ extern atomic_t ipi_sent; EXPORT_SYMBOL(irq_desc); int distribute_irqs = 1; + +void local_irq_restore(unsigned long en) +{ + get_paca()->soft_enabled = en; + if (!en) + return; + + if (firmware_has_feature(FW_FEATURE_ISERIES)) { + if (get_paca()->lppaca_ptr->int_dword.any_int) + iseries_handle_interrupts(); + return; + } + + if (get_paca()->hard_enabled) + return; + /* need to hard-enable interrupts here */ + get_paca()->hard_enabled = en; + if ((int)mfspr(SPRN_DEC) < 0) + mtspr(SPRN_DEC, 1); + hard_irq_enable(); +} #endif /* CONFIG_PPC64 */ int show_interrupts(struct seq_file *p, void *v) @@ -624,10 +646,14 @@ EXPORT_SYMBOL_GPL(irq_of_parse_and_map); void irq_dispose_mapping(unsigned int virq) { - struct irq_host *host = irq_map[virq].host; + struct irq_host *host; irq_hw_number_t hwirq; unsigned long flags; + if (virq == NO_IRQ) + return; + + host = irq_map[virq].host; WARN_ON (host == NULL); if (host == NULL) return; Index: linux-2.6/arch/powerpc/kernel/ppc_ksyms.c =================================================================== --- linux-2.6.orig/arch/powerpc/kernel/ppc_ksyms.c +++ linux-2.6/arch/powerpc/kernel/ppc_ksyms.c @@ -49,6 +49,10 @@ #include #endif +#ifdef CONFIG_PPC64 +EXPORT_SYMBOL(local_irq_restore); +#endif + #ifdef CONFIG_PPC32 extern void transfer_to_handler(void); extern void do_IRQ(struct pt_regs *regs); Index: linux-2.6/arch/powerpc/kernel/prom.c =================================================================== --- linux-2.6.orig/arch/powerpc/kernel/prom.c +++ linux-2.6/arch/powerpc/kernel/prom.c @@ -1674,6 +1674,7 @@ struct device_node *of_get_cpu_node(int } return NULL; } +EXPORT_SYMBOL(of_get_cpu_node); #ifdef DEBUG static struct debugfs_blob_wrapper flat_dt_blob; Index: linux-2.6/arch/powerpc/kernel/prom_parse.c =================================================================== --- linux-2.6.orig/arch/powerpc/kernel/prom_parse.c +++ linux-2.6/arch/powerpc/kernel/prom_parse.c @@ -25,6 +25,12 @@ #define OF_CHECK_COUNTS(na, ns) ((na) > 0 && (na) <= OF_MAX_ADDR_CELLS && \ (ns) > 0) +static struct of_bus *of_match_bus(struct device_node *np); +static int __of_address_to_resource(struct device_node *dev, + const u32 *addrp, u64 size, unsigned int flags, + struct resource *r); + + /* Debug utility */ #ifdef DEBUG static void of_dump_addr(const char *s, const u32 *addr, int na) @@ -101,6 +107,7 @@ static unsigned int of_bus_default_get_f } +#ifdef CONFIG_PCI /* * PCI bus specific translator */ @@ -162,6 +169,145 @@ static unsigned int of_bus_pci_get_flags return flags; } +const u32 *of_get_pci_address(struct device_node *dev, int bar_no, u64 *size, + unsigned int *flags) +{ + const 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); + if (strcmp(bus->name, "pci")) { + of_node_put(parent); + return NULL; + } + 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 = 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 ((prop[0] & 0xff) == ((bar_no * 4) + PCI_BASE_ADDRESS_0)) { + if (size) + *size = of_read_number(prop + na, ns); + if (flags) + *flags = bus->get_flags(prop); + return prop; + } + return NULL; +} +EXPORT_SYMBOL(of_get_pci_address); + +int of_pci_address_to_resource(struct device_node *dev, int bar, + struct resource *r) +{ + const u32 *addrp; + u64 size; + unsigned int flags; + + addrp = of_get_pci_address(dev, bar, &size, &flags); + if (addrp == NULL) + return -EINVAL; + return __of_address_to_resource(dev, addrp, size, flags, r); +} +EXPORT_SYMBOL_GPL(of_pci_address_to_resource); + +static u8 of_irq_pci_swizzle(u8 slot, u8 pin) +{ + return (((pin - 1) + slot) % 4) + 1; +} + +int of_irq_map_pci(struct pci_dev *pdev, struct of_irq *out_irq) +{ + struct device_node *dn, *ppnode; + struct pci_dev *ppdev; + u32 lspec; + u32 laddr[3]; + u8 pin; + int rc; + + /* Check if we have a device node, if yes, fallback to standard OF + * parsing + */ + dn = pci_device_to_OF_node(pdev); + if (dn) + return of_irq_map_one(dn, 0, out_irq); + + /* Ok, we don't, time to have fun. Let's start by building up an + * interrupt spec. we assume #interrupt-cells is 1, which is standard + * for PCI. If you do different, then don't use that routine. + */ + rc = pci_read_config_byte(pdev, PCI_INTERRUPT_PIN, &pin); + if (rc != 0) + return rc; + /* No pin, exit */ + if (pin == 0) + return -ENODEV; + + /* Now we walk up the PCI tree */ + lspec = pin; + for (;;) { + /* Get the pci_dev of our parent */ + ppdev = pdev->bus->self; + + /* Ouch, it's a host bridge... */ + if (ppdev == NULL) { +#ifdef CONFIG_PPC64 + ppnode = pci_bus_to_OF_node(pdev->bus); +#else + struct pci_controller *host; + host = pci_bus_to_host(pdev->bus); + ppnode = host ? host->arch_data : NULL; +#endif + /* No node for host bridge ? give up */ + if (ppnode == NULL) + return -EINVAL; + } else + /* We found a P2P bridge, check if it has a node */ + ppnode = pci_device_to_OF_node(ppdev); + + /* Ok, we have found a parent with a device-node, hand over to + * the OF parsing code. + * We build a unit address from the linux device to be used for + * resolution. Note that we use the linux bus number which may + * not match your firmware bus numbering. + * Fortunately, in most cases, interrupt-map-mask doesn't include + * the bus number as part of the matching. + * You should still be careful about that though if you intend + * to rely on this function (you ship a firmware that doesn't + * create device nodes for all PCI devices). + */ + if (ppnode) + break; + + /* We can only get here if we hit a P2P bridge with no node, + * let's do standard swizzling and try again + */ + lspec = of_irq_pci_swizzle(PCI_SLOT(pdev->devfn), lspec); + pdev = ppdev; + } + + laddr[0] = (pdev->bus->number << 16) + | (pdev->devfn << 8); + laddr[1] = laddr[2] = 0; + return of_irq_map_raw(ppnode, &lspec, 1, laddr, out_irq); +} +EXPORT_SYMBOL_GPL(of_irq_map_pci); +#endif /* CONFIG_PCI */ + /* * ISA bus specific translator */ @@ -223,6 +369,7 @@ static unsigned int of_bus_isa_get_flags */ static struct of_bus of_busses[] = { +#ifdef CONFIG_PCI /* PCI */ { .name = "pci", @@ -233,6 +380,7 @@ static struct of_bus of_busses[] = { .translate = of_bus_pci_translate, .get_flags = of_bus_pci_get_flags, }, +#endif /* CONFIG_PCI */ /* ISA */ { .name = "isa", @@ -445,48 +593,6 @@ const u32 *of_get_address(struct device_ } EXPORT_SYMBOL(of_get_address); -const u32 *of_get_pci_address(struct device_node *dev, int bar_no, u64 *size, - unsigned int *flags) -{ - const 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); - if (strcmp(bus->name, "pci")) { - of_node_put(parent); - return NULL; - } - 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 = 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 ((prop[0] & 0xff) == ((bar_no * 4) + PCI_BASE_ADDRESS_0)) { - if (size) - *size = of_read_number(prop + na, ns); - if (flags) - *flags = bus->get_flags(prop); - return prop; - } - return NULL; -} -EXPORT_SYMBOL(of_get_pci_address); - static int __of_address_to_resource(struct device_node *dev, const u32 *addrp, u64 size, unsigned int flags, struct resource *r) @@ -529,20 +635,6 @@ int of_address_to_resource(struct device } EXPORT_SYMBOL_GPL(of_address_to_resource); -int of_pci_address_to_resource(struct device_node *dev, int bar, - struct resource *r) -{ - const u32 *addrp; - u64 size; - unsigned int flags; - - addrp = of_get_pci_address(dev, bar, &size, &flags); - if (addrp == NULL) - return -EINVAL; - return __of_address_to_resource(dev, addrp, size, flags, r); -} -EXPORT_SYMBOL_GPL(of_pci_address_to_resource); - void of_parse_dma_window(struct device_node *dn, const void *dma_window_prop, unsigned long *busno, unsigned long *phys, unsigned long *size) { @@ -899,91 +991,3 @@ int of_irq_map_one(struct device_node *d } EXPORT_SYMBOL_GPL(of_irq_map_one); -#ifdef CONFIG_PCI -static u8 of_irq_pci_swizzle(u8 slot, u8 pin) -{ - return (((pin - 1) + slot) % 4) + 1; -} - -int of_irq_map_pci(struct pci_dev *pdev, struct of_irq *out_irq) -{ - struct device_node *dn, *ppnode; - struct pci_dev *ppdev; - u32 lspec; - u32 laddr[3]; - u8 pin; - int rc; - - /* We need to first check if the PCI device has a PCI interrupt at all - * since we have cases where the device-node might expose non-PCI - * interrupts, but the device has no PCI interrupt to it - */ - rc = pci_read_config_byte(pdev, PCI_INTERRUPT_PIN, &pin); - if (rc != 0) - return rc; - /* No pin, exit */ - if (pin == 0) - return -ENODEV; - - /* Check if we have a device node, if yes, fallback to standard OF - * parsing - */ - dn = pci_device_to_OF_node(pdev); - if (dn) - return of_irq_map_one(dn, 0, out_irq); - - /* Ok, we don't, time to have fun. Let's start by building up an - * interrupt spec. we assume #interrupt-cells is 1, which is standard - * for PCI. If you do different, then don't use that routine. - */ - - /* Now we walk up the PCI tree */ - lspec = pin; - for (;;) { - /* Get the pci_dev of our parent */ - ppdev = pdev->bus->self; - - /* Ouch, it's a host bridge... */ - if (ppdev == NULL) { -#ifdef CONFIG_PPC64 - ppnode = pci_bus_to_OF_node(pdev->bus); -#else - struct pci_controller *host; - host = pci_bus_to_host(pdev->bus); - ppnode = host ? host->arch_data : NULL; -#endif - /* No node for host bridge ? give up */ - if (ppnode == NULL) - return -EINVAL; - } else - /* We found a P2P bridge, check if it has a node */ - ppnode = pci_device_to_OF_node(ppdev); - - /* Ok, we have found a parent with a device-node, hand over to - * the OF parsing code. - * We build a unit address from the linux device to be used for - * resolution. Note that we use the linux bus number which may - * not match your firmware bus numbering. - * Fortunately, in most cases, interrupt-map-mask doesn't include - * the bus number as part of the matching. - * You should still be careful about that though if you intend - * to rely on this function (you ship a firmware that doesn't - * create device nodes for all PCI devices). - */ - if (ppnode) - break; - - /* We can only get here if we hit a P2P bridge with no node, - * let's do standard swizzling and try again - */ - lspec = of_irq_pci_swizzle(PCI_SLOT(pdev->devfn), lspec); - pdev = ppdev; - } - - laddr[0] = (pdev->bus->number << 16) - | (pdev->devfn << 8); - laddr[1] = laddr[2] = 0; - return of_irq_map_raw(ppnode, &lspec, 1, laddr, out_irq); -} -EXPORT_SYMBOL_GPL(of_irq_map_pci); -#endif /* CONFIG_PCI */ Index: linux-2.6/arch/powerpc/kernel/setup_64.c =================================================================== --- linux-2.6.orig/arch/powerpc/kernel/setup_64.c +++ linux-2.6/arch/powerpc/kernel/setup_64.c @@ -231,8 +231,8 @@ void early_setup_secondary(void) { struct paca_struct *lpaca = get_paca(); - /* Mark enabled in PACA */ - lpaca->proc_enabled = 0; + /* Mark interrupts enabled in PACA */ + lpaca->soft_enabled = 0; /* Initialize hash table for that CPU */ htab_initialize_secondary(); Index: linux-2.6/arch/powerpc/kernel/smp.c =================================================================== --- linux-2.6.orig/arch/powerpc/kernel/smp.c +++ linux-2.6/arch/powerpc/kernel/smp.c @@ -65,6 +65,7 @@ cpumask_t cpu_sibling_map[NR_CPUS] = { [ EXPORT_SYMBOL(cpu_online_map); EXPORT_SYMBOL(cpu_possible_map); +EXPORT_SYMBOL(cpu_sibling_map); /* SMP operations for this machine */ struct smp_ops_t *smp_ops; Index: linux-2.6/arch/powerpc/kernel/sysfs.c =================================================================== --- linux-2.6.orig/arch/powerpc/kernel/sysfs.c +++ linux-2.6/arch/powerpc/kernel/sysfs.c @@ -299,6 +299,72 @@ static struct notifier_block __cpuinitda .notifier_call = sysfs_cpu_notify, }; +static DEFINE_MUTEX(cpu_mutex); + +int cpu_add_sysdev_attr(struct sysdev_attribute *attr) +{ + int cpu; + + mutex_lock(&cpu_mutex); + + for_each_possible_cpu(cpu) { + sysdev_create_file(get_cpu_sysdev(cpu), attr); + } + + mutex_unlock(&cpu_mutex); + return 0; +} +EXPORT_SYMBOL_GPL(cpu_add_sysdev_attr); + +int cpu_add_sysdev_attr_group(struct attribute_group *attrs) +{ + int cpu; + struct sys_device *sysdev; + + mutex_lock(&cpu_mutex); + + for_each_possible_cpu(cpu) { + sysdev = get_cpu_sysdev(cpu); + sysfs_create_group(&sysdev->kobj, attrs); + } + + mutex_unlock(&cpu_mutex); + return 0; +} +EXPORT_SYMBOL_GPL(cpu_add_sysdev_attr_group); + + +void cpu_remove_sysdev_attr(struct sysdev_attribute *attr) +{ + int cpu; + + mutex_lock(&cpu_mutex); + + for_each_possible_cpu(cpu) { + sysdev_remove_file(get_cpu_sysdev(cpu), attr); + } + + mutex_unlock(&cpu_mutex); +} +EXPORT_SYMBOL_GPL(cpu_remove_sysdev_attr); + +void cpu_remove_sysdev_attr_group(struct attribute_group *attrs) +{ + int cpu; + struct sys_device *sysdev; + + mutex_lock(&cpu_mutex); + + for_each_possible_cpu(cpu) { + sysdev = get_cpu_sysdev(cpu); + sysfs_remove_group(&sysdev->kobj, attrs); + } + + mutex_unlock(&cpu_mutex); +} +EXPORT_SYMBOL_GPL(cpu_remove_sysdev_attr_group); + + /* NUMA stuff */ #ifdef CONFIG_NUMA Index: linux-2.6/arch/powerpc/mm/hash_native_64.c =================================================================== --- linux-2.6.orig/arch/powerpc/mm/hash_native_64.c +++ linux-2.6/arch/powerpc/mm/hash_native_64.c @@ -123,7 +123,7 @@ static inline void native_unlock_hpte(hp clear_bit(HPTE_LOCK_BIT, word); } -long native_hpte_insert(unsigned long hpte_group, unsigned long va, +static long native_hpte_insert(unsigned long hpte_group, unsigned long va, unsigned long pa, unsigned long rflags, unsigned long vflags, int psize) { Index: linux-2.6/arch/powerpc/platforms/cell/Kconfig =================================================================== --- linux-2.6.orig/arch/powerpc/platforms/cell/Kconfig +++ linux-2.6/arch/powerpc/platforms/cell/Kconfig @@ -20,4 +20,18 @@ config CBE_RAS bool "RAS features for bare metal Cell BE" default y +config CBE_THERM + tristate "CBE thermal support" + default m + depends on CBE_RAS + +config CBE_CPUFREQ + tristate "CBE frequency scaling" + depends on CBE_RAS && CPU_FREQ + default m + help + This adds the cpufreq driver for Cell BE processors. + For details, take a look at . + If you don't have such processor, say N + endmenu Index: linux-2.6/arch/powerpc/platforms/cell/Makefile =================================================================== --- linux-2.6.orig/arch/powerpc/platforms/cell/Makefile +++ linux-2.6/arch/powerpc/platforms/cell/Makefile @@ -1,7 +1,11 @@ obj-$(CONFIG_PPC_CELL_NATIVE) += interrupt.o iommu.o setup.o \ - cbe_regs.o spider-pic.o pervasive.o + cbe_regs.o spider-pic.o pervasive.o \ + pmu.o obj-$(CONFIG_CBE_RAS) += ras.o +obj-$(CONFIG_CBE_THERM) += cbe_thermal.o +obj-$(CONFIG_CBE_CPUFREQ) += cbe_cpufreq.o + ifeq ($(CONFIG_SMP),y) obj-$(CONFIG_PPC_CELL_NATIVE) += smp.o endif Index: linux-2.6/arch/powerpc/platforms/cell/cbe_cpufreq.c =================================================================== --- /dev/null +++ linux-2.6/arch/powerpc/platforms/cell/cbe_cpufreq.c @@ -0,0 +1,248 @@ +/* + * cpufreq driver for the cell processor + * + * (C) Copyright IBM Deutschland Entwicklung GmbH 2005 + * + * Author: Christian Krafft + * + * 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 "cbe_regs.h" + +static DEFINE_MUTEX(cbe_switch_mutex); + + +/* the CBE supports an 8 step frequency scaling */ +static struct cpufreq_frequency_table cbe_freqs[] = { + {1, 0}, + {2, 0}, + {3, 0}, + {4, 0}, + {5, 0}, + {6, 0}, + {8, 0}, + {10, 0}, + {0, CPUFREQ_TABLE_END}, +}; + +/* to write to MIC register */ +static u64 MIC_Slow_Fast_Timer_table[] = { + [0 ... 7] = 0x007fc00000000000ull, +}; + +/* more values for the MIC */ +static u64 MIC_Slow_Next_Timer_table[] = { + 0x0000240000000000ull, + 0x0000268000000000ull, + 0x000029C000000000ull, + 0x00002D0000000000ull, + 0x0000300000000000ull, + 0x0000334000000000ull, + 0x000039C000000000ull, + 0x00003FC000000000ull, +}; + +/* + * hardware specific functions + */ + +static int get_pmode(int cpu) +{ + int ret; + struct cbe_pmd_regs __iomem *pmd_regs; + + pmd_regs = cbe_get_cpu_pmd_regs(cpu); + ret = in_be64(&pmd_regs->pmsr) & 0x07; + + return ret; +} + +static int set_pmode(int cpu, unsigned int pmode) +{ + struct cbe_pmd_regs __iomem *pmd_regs; + struct cbe_mic_tm_regs __iomem *mic_tm_regs; + u64 flags; + u64 value; + + local_irq_save(flags); + + mic_tm_regs = cbe_get_cpu_mic_tm_regs(cpu); + pmd_regs = cbe_get_cpu_pmd_regs(cpu); + + pr_debug("pm register is mapped at %p\n", &pmd_regs->pmcr); + pr_debug("mic register is mapped at %p\n", &mic_tm_regs->slow_fast_timer_0); + + out_be64(&mic_tm_regs->slow_fast_timer_0, MIC_Slow_Fast_Timer_table[pmode]); + out_be64(&mic_tm_regs->slow_fast_timer_1, MIC_Slow_Fast_Timer_table[pmode]); + + out_be64(&mic_tm_regs->slow_next_timer_0, MIC_Slow_Next_Timer_table[pmode]); + out_be64(&mic_tm_regs->slow_next_timer_1, MIC_Slow_Next_Timer_table[pmode]); + + value = in_be64(&pmd_regs->pmcr); + /* set bits to zero */ + value &= 0xFFFFFFFFFFFFFFF8ull; + /* set bits to next pmode */ + value |= pmode; + + out_be64(&pmd_regs->pmcr, value); + + /* wait until new pmode appears in status register */ + value = in_be64(&pmd_regs->pmsr) & 0x07; + while(value != pmode) { + cpu_relax(); + value = in_be64(&pmd_regs->pmsr) & 0x07; + } + + local_irq_restore(flags); + + return 0; +} + +/* + * cpufreq functions + */ + +static int cbe_cpufreq_cpu_init (struct cpufreq_policy *policy) +{ + u32 *max_freq; + int i, cur_pmode; + struct device_node *cpu; + + cpu = of_get_cpu_node(policy->cpu, NULL); + + if(!cpu) + return -ENODEV; + + pr_debug("init cpufreq on CPU %d\n", policy->cpu); + + max_freq = (u32*) get_property(cpu, "clock-frequency", NULL); + + if(!max_freq) + return -EINVAL; + + // we need the freq in kHz + *max_freq /= 1000; + + pr_debug("max clock-frequency is at %u kHz\n", *max_freq); + pr_debug("initializing frequency table\n"); + + // initialize frequency table + for (i=0; cbe_freqs[i].frequency!=CPUFREQ_TABLE_END; i++) { + cbe_freqs[i].frequency = *max_freq / cbe_freqs[i].index; + pr_debug("%d: %d\n", i, cbe_freqs[i].frequency); + } + + policy->governor = CPUFREQ_DEFAULT_GOVERNOR; + /* if DEBUG is enabled set_pmode() measures the correct latency of a transition */ + policy->cpuinfo.transition_latency = 25000; + + cur_pmode = get_pmode(policy->cpu); + pr_debug("current pmode is at %d\n",cur_pmode); + + policy->cur = cbe_freqs[cur_pmode].frequency; + +#ifdef CONFIG_SMP + policy->cpus = cpu_sibling_map[policy->cpu]; +#endif + + cpufreq_frequency_table_get_attr (cbe_freqs, policy->cpu); + + /* this ensures that policy->cpuinfo_min and policy->cpuinfo_max are set correctly */ + return cpufreq_frequency_table_cpuinfo (policy, cbe_freqs); +} + +static int cbe_cpufreq_cpu_exit(struct cpufreq_policy *policy) +{ + cpufreq_frequency_table_put_attr(policy->cpu); + return 0; +} + +static int cbe_cpufreq_verify(struct cpufreq_policy *policy) +{ + return cpufreq_frequency_table_verify(policy, cbe_freqs); +} + + +static int cbe_cpufreq_target(struct cpufreq_policy *policy, unsigned int target_freq, + unsigned int relation) +{ + int rc; + struct cpufreq_freqs freqs; + int cbe_pmode_new; + + cpufreq_frequency_table_target(policy, + cbe_freqs, + target_freq, + relation, + &cbe_pmode_new); + + freqs.old = policy->cur; + freqs.new = cbe_freqs[cbe_pmode_new].frequency; + freqs.cpu = policy->cpu; + + mutex_lock (&cbe_switch_mutex); + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + + pr_debug("setting frequency for cpu %d to %d kHz, 1/%d of max frequency\n", + policy->cpu, + cbe_freqs[cbe_pmode_new].frequency, + cbe_freqs[cbe_pmode_new].index); + + rc = set_pmode(policy->cpu, cbe_pmode_new); + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + mutex_unlock(&cbe_switch_mutex); + + return rc; +} + +static struct cpufreq_driver cbe_cpufreq_driver = { + .verify = cbe_cpufreq_verify, + .target = cbe_cpufreq_target, + .init = cbe_cpufreq_cpu_init, + .exit = cbe_cpufreq_cpu_exit, + .name = "cbe-cpufreq", + .owner = THIS_MODULE, + .flags = CPUFREQ_CONST_LOOPS, +}; + +/* + * module init and destoy + */ + +static int __init cbe_cpufreq_init(void) +{ + return cpufreq_register_driver(&cbe_cpufreq_driver); +} + +static void __exit cbe_cpufreq_exit(void) +{ + cpufreq_unregister_driver(&cbe_cpufreq_driver); +} + +module_init(cbe_cpufreq_init); +module_exit(cbe_cpufreq_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Christian Krafft "); Index: linux-2.6/arch/powerpc/platforms/cell/cbe_regs.c =================================================================== --- linux-2.6.orig/arch/powerpc/platforms/cell/cbe_regs.c +++ linux-2.6/arch/powerpc/platforms/cell/cbe_regs.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -18,8 +19,6 @@ #include "cbe_regs.h" -#define MAX_CBE 2 - /* * Current implementation uses "cpu" nodes. We build our own mapping * array of cpu numbers to cpu nodes locally for now to allow interrupt @@ -32,6 +31,8 @@ static struct cbe_regs_map struct device_node *cpu_node; struct cbe_pmd_regs __iomem *pmd_regs; struct cbe_iic_regs __iomem *iic_regs; + struct cbe_mic_tm_regs __iomem *mic_tm_regs; + struct cbe_pmd_shadow_regs pmd_shadow_regs; } cbe_regs_maps[MAX_CBE]; static int cbe_regs_map_count; @@ -44,6 +45,19 @@ static struct cbe_thread_map static struct cbe_regs_map *cbe_find_map(struct device_node *np) { int i; + struct device_node *tmp_np; + + if (strcasecmp(np->type, "spe") == 0) { + if (np->data == NULL) { + /* walk up path until cpu node was found */ + tmp_np = np->parent; + while (tmp_np != NULL && strcasecmp(tmp_np->type, "cpu") != 0) + tmp_np = tmp_np->parent; + + np->data = cbe_find_map(tmp_np); + } + return np->data; + } for (i = 0; i < cbe_regs_map_count; i++) if (cbe_regs_maps[i].cpu_node == np) @@ -58,6 +72,7 @@ struct cbe_pmd_regs __iomem *cbe_get_pmd return NULL; return map->pmd_regs; } +EXPORT_SYMBOL_GPL(cbe_get_pmd_regs); struct cbe_pmd_regs __iomem *cbe_get_cpu_pmd_regs(int cpu) { @@ -66,7 +81,23 @@ struct cbe_pmd_regs __iomem *cbe_get_cpu return NULL; return map->pmd_regs; } +EXPORT_SYMBOL_GPL(cbe_get_cpu_pmd_regs); + +struct cbe_pmd_shadow_regs *cbe_get_pmd_shadow_regs(struct device_node *np) +{ + struct cbe_regs_map *map = cbe_find_map(np); + if (map == NULL) + return NULL; + return &map->pmd_shadow_regs; +} +struct cbe_pmd_shadow_regs *cbe_get_cpu_pmd_shadow_regs(int cpu) +{ + struct cbe_regs_map *map = cbe_thread_map[cpu].regs; + if (map == NULL) + return NULL; + return &map->pmd_shadow_regs; +} struct cbe_iic_regs __iomem *cbe_get_iic_regs(struct device_node *np) { @@ -75,6 +106,7 @@ struct cbe_iic_regs __iomem *cbe_get_iic return NULL; return map->iic_regs; } + struct cbe_iic_regs __iomem *cbe_get_cpu_iic_regs(int cpu) { struct cbe_regs_map *map = cbe_thread_map[cpu].regs; @@ -83,6 +115,24 @@ struct cbe_iic_regs __iomem *cbe_get_cpu return map->iic_regs; } +struct cbe_mic_tm_regs __iomem *cbe_get_mic_tm_regs(struct device_node *np) +{ + struct cbe_regs_map *map = cbe_find_map(np); + if (map == NULL) + return NULL; + return map->mic_tm_regs; +} + +struct cbe_mic_tm_regs __iomem *cbe_get_cpu_mic_tm_regs(int cpu) +{ + struct cbe_regs_map *map = cbe_thread_map[cpu].regs; + if (map == NULL) + return NULL; + return map->mic_tm_regs; +} +EXPORT_SYMBOL_GPL(cbe_get_cpu_mic_tm_regs); + + void __init cbe_regs_init(void) { int i; @@ -121,6 +171,11 @@ void __init cbe_regs_init(void) prop = get_property(cpu, "iic", NULL); if (prop != NULL) map->iic_regs = ioremap(prop->address, prop->len); + + prop = (struct address_prop *)get_property(cpu, "mic-tm", + NULL); + if (prop != NULL) + map->mic_tm_regs = ioremap(prop->address, prop->len); } } Index: linux-2.6/arch/powerpc/platforms/cell/cbe_regs.h =================================================================== --- linux-2.6.orig/arch/powerpc/platforms/cell/cbe_regs.h +++ linux-2.6/arch/powerpc/platforms/cell/cbe_regs.h @@ -4,6 +4,11 @@ * This file is intended to hold the various register definitions for CBE * on-chip system devices (memory controller, IO controller, etc...) * + * (C) Copyright IBM Corporation 2001,2006 + * + * Authors: Maximino Aguilar (maguilar@us.ibm.com) + * David J. Erb (djerb@us.ibm.com) + * * (c) 2006 Benjamin Herrenschmidt , IBM Corp. */ @@ -22,6 +27,7 @@ #define HID0_CBE_THERM_INT_EN 0x0000000400000000ul #define HID0_CBE_SYSERR_INT_EN 0x0000000200000000ul +#define MAX_CBE 2 /* * @@ -29,51 +35,132 @@ * */ +/* Macros for the pm_control register. */ +#define CBE_PM_16BIT_CTR(ctr) (1 << (24 - ((ctr) & (NR_PHYS_CTRS - 1)))) +#define CBE_PM_ENABLE_PERF_MON 0x80000000 + + +union spe_reg { + u64 val; + u8 spe[8]; +}; + +union ppe_spe_reg { + u64 val; + struct { + u32 ppe; + u32 spe; + }; +}; + + struct cbe_pmd_regs { - u8 pad_0x0000_0x0800[0x0800 - 0x0000]; /* 0x0000 */ + /* Debug Bus Control */ + u64 pad_0x0000; /* 0x0000 */ + + u64 group_control; /* 0x0008 */ + + u8 pad_0x0010_0x00a8 [0x00a8 - 0x0010]; /* 0x0010 */ + + u64 debug_bus_control; /* 0x00a8 */ + + u8 pad_0x00b0_0x0100 [0x0100 - 0x00b0]; /* 0x00b0 */ + + u64 trace_aux_data; /* 0x0100 */ + u64 trace_buffer_0_63; /* 0x0108 */ + u64 trace_buffer_64_127; /* 0x0110 */ + u64 trace_address; /* 0x0118 */ + u64 ext_tr_timer; /* 0x0120 */ + + u8 pad_0x0128_0x0400 [0x0400 - 0x0128]; /* 0x0128 */ + + /* Performance Monitor */ + u64 pm_status; /* 0x0400 */ + u64 pm_control; /* 0x0408 */ + u64 pm_interval; /* 0x0410 */ + u64 pm_ctr[4]; /* 0x0418 */ + u64 pm_start_stop; /* 0x0438 */ + u64 pm07_control[8]; /* 0x0440 */ + + u8 pad_0x0480_0x0800 [0x0800 - 0x0480]; /* 0x0480 */ /* Thermal Sensor Registers */ - u64 ts_ctsr1; /* 0x0800 */ - u64 ts_ctsr2; /* 0x0808 */ - u64 ts_mtsr1; /* 0x0810 */ - u64 ts_mtsr2; /* 0x0818 */ - u64 ts_itr1; /* 0x0820 */ - u64 ts_itr2; /* 0x0828 */ - u64 ts_gitr; /* 0x0830 */ - u64 ts_isr; /* 0x0838 */ - u64 ts_imr; /* 0x0840 */ - u64 tm_cr1; /* 0x0848 */ - u64 tm_cr2; /* 0x0850 */ - u64 tm_simr; /* 0x0858 */ - u64 tm_tpr; /* 0x0860 */ - u64 tm_str1; /* 0x0868 */ - u64 tm_str2; /* 0x0870 */ - u64 tm_tsr; /* 0x0878 */ + union spe_reg ts_ctsr1; /* 0x0800 */ + u64 ts_ctsr2; /* 0x0808 */ + union spe_reg ts_mtsr1; /* 0x0810 */ + u64 ts_mtsr2; /* 0x0818 */ + union spe_reg ts_itr1; /* 0x0820 */ + u64 ts_itr2; /* 0x0828 */ + u64 ts_gitr; /* 0x0830 */ + u64 ts_isr; /* 0x0838 */ + u64 ts_imr; /* 0x0840 */ + union spe_reg tm_cr1; /* 0x0848 */ + u64 tm_cr2; /* 0x0850 */ + u64 tm_simr; /* 0x0858 */ + union ppe_spe_reg tm_tpr; /* 0x0860 */ + union spe_reg tm_str1; /* 0x0868 */ + u64 tm_str2; /* 0x0870 */ + union ppe_spe_reg tm_tsr; /* 0x0878 */ /* Power Management */ - u64 pm_control; /* 0x0880 */ -#define CBE_PMD_PAUSE_ZERO_CONTROL 0x10000 - u64 pm_status; /* 0x0888 */ + u64 pmcr; /* 0x0880 */ +#define CBE_PMD_PAUSE_ZERO_CONTROL 0x10000 + u64 pmsr; /* 0x0888 */ /* Time Base Register */ - u64 tbr; /* 0x0890 */ + u64 tbr; /* 0x0890 */ - u8 pad_0x0898_0x0c00 [0x0c00 - 0x0898]; /* 0x0898 */ + u8 pad_0x0898_0x0c00 [0x0c00 - 0x0898]; /* 0x0898 */ /* Fault Isolation Registers */ - u64 checkstop_fir; /* 0x0c00 */ - u64 recoverable_fir; - u64 spec_att_mchk_fir; - u64 fir_mode_reg; - u64 fir_enable_mask; + u64 checkstop_fir; /* 0x0c00 */ + u64 recoverable_fir; /* 0x0c08 */ + u64 spec_att_mchk_fir; /* 0x0c10 */ + u64 fir_mode_reg; /* 0x0c18 */ + u64 fir_enable_mask; /* 0x0c20 */ - u8 pad_0x0c28_0x1000 [0x1000 - 0x0c28]; /* 0x0c28 */ + u8 pad_0x0c28_0x1000 [0x1000 - 0x0c28]; /* 0x0c28 */ }; extern struct cbe_pmd_regs __iomem *cbe_get_pmd_regs(struct device_node *np); extern struct cbe_pmd_regs __iomem *cbe_get_cpu_pmd_regs(int cpu); /* + * PMU shadow registers + * + * Many of the registers in the performance monitoring unit are write-only, + * so we need to save a copy of what we write to those registers. + * + * The actual data counters are read/write. However, writing to the counters + * only takes effect if the PMU is enabled. Otherwise the value is stored in + * a hardware latch until the next time the PMU is enabled. So we save a copy + * of the counter values if we need to read them back while the PMU is + * disabled. The counter_value_in_latch field is a bitmap indicating which + * counters currently have a value waiting to be written. + */ + +#define NR_PHYS_CTRS 4 +#define NR_CTRS (NR_PHYS_CTRS * 2) + +struct cbe_pmd_shadow_regs { + u32 group_control; + u32 debug_bus_control; + u32 trace_address; + u32 ext_tr_timer; + u32 pm_status; + u32 pm_control; + u32 pm_interval; + u32 pm_start_stop; + u32 pm07_control[NR_CTRS]; + + u32 pm_ctr[NR_PHYS_CTRS]; + u32 counter_value_in_latch; +}; + +extern struct cbe_pmd_shadow_regs *cbe_get_pmd_shadow_regs(struct device_node *np); +extern struct cbe_pmd_shadow_regs *cbe_get_cpu_pmd_shadow_regs(int cpu); + +/* * * IIC unit register definitions * @@ -102,18 +189,20 @@ struct cbe_iic_regs { /* IIC interrupt registers */ struct cbe_iic_thread_regs thread[2]; /* 0x0400 */ - u64 iic_ir; /* 0x0440 */ - u64 iic_is; /* 0x0448 */ + + u64 iic_ir; /* 0x0440 */ + u64 iic_is; /* 0x0448 */ +#define CBE_IIC_IS_PMI 0x2 u8 pad_0x0450_0x0500[0x0500 - 0x0450]; /* 0x0450 */ /* IOC FIR */ u64 ioc_fir_reset; /* 0x0500 */ - u64 ioc_fir_set; - u64 ioc_checkstop_enable; - u64 ioc_fir_error_mask; - u64 ioc_syserr_enable; - u64 ioc_fir; + u64 ioc_fir_set; /* 0x0508 */ + u64 ioc_checkstop_enable; /* 0x0510 */ + u64 ioc_fir_error_mask; /* 0x0518 */ + u64 ioc_syserr_enable; /* 0x0520 */ + u64 ioc_fir; /* 0x0528 */ u8 pad_0x0530_0x1000[0x1000 - 0x0530]; /* 0x0530 */ }; @@ -122,6 +211,48 @@ extern struct cbe_iic_regs __iomem *cbe_ extern struct cbe_iic_regs __iomem *cbe_get_cpu_iic_regs(int cpu); +struct cbe_mic_tm_regs { + u8 pad_0x0000_0x0040[0x0040 - 0x0000]; /* 0x0000 */ + + u64 mic_ctl_cnfg2; /* 0x0040 */ +#define CBE_MIC_ENABLE_AUX_TRC 0x8000000000000000LL +#define CBE_MIC_DISABLE_PWR_SAV_2 0x0200000000000000LL +#define CBE_MIC_DISABLE_AUX_TRC_WRAP 0x0100000000000000LL +#define CBE_MIC_ENABLE_AUX_TRC_INT 0x0080000000000000LL + + u64 pad_0x0048; /* 0x0048 */ + + u64 mic_aux_trc_base; /* 0x0050 */ + u64 mic_aux_trc_max_addr; /* 0x0058 */ + u64 mic_aux_trc_cur_addr; /* 0x0060 */ + u64 mic_aux_trc_grf_addr; /* 0x0068 */ + u64 mic_aux_trc_grf_data; /* 0x0070 */ + + u64 pad_0x0078; /* 0x0078 */ + + u64 mic_ctl_cnfg_0; /* 0x0080 */ +#define CBE_MIC_DISABLE_PWR_SAV_0 0x8000000000000000LL + + u64 pad_0x0088; /* 0x0088 */ + + u64 slow_fast_timer_0; /* 0x0090 */ + u64 slow_next_timer_0; /* 0x0098 */ + + u8 pad_0x00a0_0x01c0[0x01c0 - 0x0a0]; /* 0x00a0 */ + + u64 mic_ctl_cnfg_1; /* 0x01c0 */ +#define CBE_MIC_DISABLE_PWR_SAV_1 0x8000000000000000LL + u64 pad_0x01c8; /* 0x01c8 */ + + u64 slow_fast_timer_1; /* 0x01d0 */ + u64 slow_next_timer_1; /* 0x01d8 */ + + u8 pad_0x01e0_0x1000[0x1000 - 0x01e0]; /* 0x01e0 */ +}; + +extern struct cbe_mic_tm_regs __iomem *cbe_get_mic_tm_regs(struct device_node *np); +extern struct cbe_mic_tm_regs __iomem *cbe_get_cpu_mic_tm_regs(int cpu); + /* Init this module early */ extern void cbe_regs_init(void); Index: linux-2.6/arch/powerpc/platforms/cell/cbe_thermal.c =================================================================== --- /dev/null +++ linux-2.6/arch/powerpc/platforms/cell/cbe_thermal.c @@ -0,0 +1,225 @@ +/* + * thermal support for the cell processor + * + * (C) Copyright IBM Deutschland Entwicklung GmbH 2005 + * + * Author: Christian Krafft + * + * 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 "cbe_regs.h" + +static struct cbe_pmd_regs __iomem *get_pmd_regs(struct sys_device *sysdev) +{ + struct spu *spu; + + spu = container_of(sysdev, struct spu, sysdev); + + return cbe_get_pmd_regs(spu->devnode); +} + +/* returns the value for a given spu in a given register */ +static u8 spu_read_register_value(struct sys_device *sysdev, union spe_reg __iomem *reg) +{ + unsigned int *id; + union spe_reg value; + struct spu *spu; + + /* getting the id from the reg attribute will not work on future device-tree layouts + * in future we should store the id to the spu struct and use it here */ + spu = container_of(sysdev, struct spu, sysdev); + id = (unsigned int *)get_property(spu->devnode, "reg", NULL); + value.val = in_be64(®->val); + + return value.spe[*id]; +} + +static ssize_t spu_show_temp(struct sys_device *sysdev, char *buf) +{ + int value; + struct cbe_pmd_regs __iomem *pmd_regs; + + pmd_regs = get_pmd_regs(sysdev); + + value = spu_read_register_value(sysdev, &pmd_regs->ts_ctsr1); + /* clear all other bits */ + value &= 0x3F; + /* temp is stored in steps of 2 degrees */ + value *= 2; + /* base temp is 65 degrees */ + value += 65; + + return sprintf(buf, "%d\n", (int) value); +} + +static ssize_t ppe_show_temp(struct sys_device *sysdev, char *buf, int pos) +{ + struct cbe_pmd_regs __iomem *pmd_regs; + u64 value; + + pmd_regs = cbe_get_cpu_pmd_regs(sysdev->id); + value = in_be64(&pmd_regs->ts_ctsr2); + + /* access the corresponding byte */ + value >>= pos; + /* clear all other bits */ + value &= 0x3F; + /* temp is stored in steps of 2 degrees */ + value *= 2; + /* base temp is 65 degrees */ + value += 65; + + return sprintf(buf, "%d\n", (int) value); +} + + +/* shows the temperature of the DTS on the PPE, + * located near the linear thermal sensor */ +static ssize_t ppe_show_temp0(struct sys_device *sysdev, char *buf) +{ + return ppe_show_temp(sysdev, buf, 32); +} + +/* shows the temperature of the second DTS on the PPE */ +static ssize_t ppe_show_temp1(struct sys_device *sysdev, char *buf) +{ + return ppe_show_temp(sysdev, buf, 0); +} + +static struct sysdev_attribute attr_spu_temperature = { + .attr = {.name = "temperature", .mode = 0400 }, + .show = spu_show_temp, +}; + +static struct attribute *spu_attributes[] = { + &attr_spu_temperature.attr, +}; + +static struct attribute_group spu_attribute_group = { + .name = "thermal", + .attrs = spu_attributes, +}; + +static struct sysdev_attribute attr_ppe_temperature0 = { + .attr = {.name = "temperature0", .mode = 0400 }, + .show = ppe_show_temp0, +}; + +static struct sysdev_attribute attr_ppe_temperature1 = { + .attr = {.name = "temperature1", .mode = 0400 }, + .show = ppe_show_temp1, +}; + +static struct attribute *ppe_attributes[] = { + &attr_ppe_temperature0.attr, + &attr_ppe_temperature1.attr, +}; + +static struct attribute_group ppe_attribute_group = { + .name = "thermal", + .attrs = ppe_attributes, +}; + +/* + * initialize throttling with default values + */ +static void __init init_default_values(void) +{ + int cpu; + struct cbe_pmd_regs __iomem *pmd_regs; + struct sys_device *sysdev; + union ppe_spe_reg tpr; + union spe_reg str1; + u64 str2; + union spe_reg cr1; + u64 cr2; + + /* TPR defaults */ + /* ppe + * 1F - no full stop + * 08 - dynamic throttling starts if over 80 degrees + * 03 - dynamic throttling ceases if below 70 degrees */ + tpr.ppe = 0x1F0803; + /* spe + * 10 - full stopped when over 96 degrees + * 08 - dynamic throttling starts if over 80 degrees + * 03 - dynamic throttling ceases if below 70 degrees + */ + tpr.spe = 0x100803; + + /* STR defaults */ + /* str1 + * 10 - stop 16 of 32 cycles + */ + str1.val = 0x1010101010101010ull; + /* str2 + * 10 - stop 16 of 32 cycles + */ + str2 = 0x10; + + /* CR defaults */ + /* cr1 + * 4 - normal operation + */ + cr1.val = 0x0404040404040404ull; + /* cr2 + * 4 - normal operation + */ + cr2 = 0x04; + + for_each_possible_cpu (cpu) { + pr_debug("processing cpu %d\n", cpu); + sysdev = get_cpu_sysdev(cpu); + pmd_regs = cbe_get_cpu_pmd_regs(sysdev->id); + + out_be64(&pmd_regs->tm_str2, str2); + out_be64(&pmd_regs->tm_str1.val, str1.val); + out_be64(&pmd_regs->tm_tpr.val, tpr.val); + out_be64(&pmd_regs->tm_cr1.val, cr1.val); + out_be64(&pmd_regs->tm_cr2, cr2); + } +} + + +static int __init thermal_init(void) +{ + init_default_values(); + + spu_add_sysdev_attr_group(&spu_attribute_group); + cpu_add_sysdev_attr_group(&ppe_attribute_group); + + return 0; +} +module_init(thermal_init); + +static void __exit thermal_exit(void) +{ + spu_remove_sysdev_attr_group(&spu_attribute_group); + cpu_remove_sysdev_attr_group(&ppe_attribute_group); +} +module_exit(thermal_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Christian Krafft "); + Index: linux-2.6/arch/powerpc/platforms/cell/pervasive.c =================================================================== --- linux-2.6.orig/arch/powerpc/platforms/cell/pervasive.c +++ linux-2.6/arch/powerpc/platforms/cell/pervasive.c @@ -38,32 +38,16 @@ #include "pervasive.h" #include "cbe_regs.h" -static DEFINE_SPINLOCK(cbe_pervasive_lock); - -static void __init cbe_enable_pause_zero(void) +static void cbe_power_save(void) { - unsigned long thread_switch_control; - unsigned long temp_register; - struct cbe_pmd_regs __iomem *pregs; - - spin_lock_irq(&cbe_pervasive_lock); - pregs = cbe_get_cpu_pmd_regs(smp_processor_id()); - if (pregs == NULL) - goto out; - - pr_debug("Power Management: CPU %d\n", smp_processor_id()); - - /* Enable Pause(0) control bit */ - temp_register = in_be64(&pregs->pm_control); - - out_be64(&pregs->pm_control, - temp_register | CBE_PMD_PAUSE_ZERO_CONTROL); + unsigned long ctrl, thread_switch_control; + ctrl = mfspr(SPRN_CTRLF); /* Enable DEC and EE interrupt request */ thread_switch_control = mfspr(SPRN_TSC_CELL); thread_switch_control |= TSC_CELL_EE_ENABLE | TSC_CELL_EE_BOOST; - switch ((mfspr(SPRN_CTRLF) & CTRL_CT)) { + switch (ctrl & CTRL_CT) { case CTRL_CT0: thread_switch_control |= TSC_CELL_DEC_ENABLE_0; break; @@ -75,58 +59,21 @@ static void __init cbe_enable_pause_zero __FUNCTION__); break; } - mtspr(SPRN_TSC_CELL, thread_switch_control); -out: - spin_unlock_irq(&cbe_pervasive_lock); -} - -static void cbe_idle(void) -{ - unsigned long ctrl; - - /* Why do we do that on every idle ? Couldn't that be done once for - * all or do we lose the state some way ? Also, the pm_control - * register setting, that can't be set once at boot ? We really want - * to move that away in order to implement a simple powersave + /* + * go into low thread priority, medium priority will be + * restored for us after wake-up. */ - cbe_enable_pause_zero(); + HMT_low(); - while (1) { - if (!need_resched()) { - local_irq_disable(); - while (!need_resched()) { - /* go into low thread priority */ - HMT_low(); - - /* - * atomically disable thread execution - * and runlatch. - * External and Decrementer exceptions - * are still handled when the thread - * is disabled but now enter in - * cbe_system_reset_exception() - */ - ctrl = mfspr(SPRN_CTRLF); - ctrl &= ~(CTRL_RUNLATCH | CTRL_TE); - mtspr(SPRN_CTRLT, ctrl); - } - /* restore thread prio */ - HMT_medium(); - local_irq_enable(); - } - - /* - * turn runlatch on again before scheduling the - * process we just woke up - */ - ppc64_runlatch_on(); - - preempt_enable_no_resched(); - schedule(); - preempt_disable(); - } + /* + * atomically disable thread execution and runlatch. + * External and Decrementer exceptions are still handled when the + * thread is disabled but now enter in cbe_system_reset_exception() + */ + ctrl &= ~(CTRL_RUNLATCH | CTRL_TE); + mtspr(SPRN_CTRLT, ctrl); } static int cbe_system_reset_exception(struct pt_regs *regs) @@ -158,9 +105,20 @@ static int cbe_system_reset_exception(st void __init cbe_pervasive_init(void) { + int cpu; if (!cpu_has_feature(CPU_FTR_PAUSE_ZERO)) return; - ppc_md.idle_loop = cbe_idle; + for_each_possible_cpu(cpu) { + struct cbe_pmd_regs __iomem *regs = cbe_get_cpu_pmd_regs(cpu); + if (!regs) + continue; + + /* Enable Pause(0) control bit */ + out_be64(®s->pmcr, in_be64(®s->pmcr) | + CBE_PMD_PAUSE_ZERO_CONTROL); + } + + ppc_md.power_save = cbe_power_save; ppc_md.system_reset_exception = cbe_system_reset_exception; } Index: linux-2.6/arch/powerpc/platforms/cell/pmu.c =================================================================== --- /dev/null +++ linux-2.6/arch/powerpc/platforms/cell/pmu.c @@ -0,0 +1,328 @@ +/* + * Cell Broadband Engine Performance Monitor + * + * (C) Copyright IBM Corporation 2001,2006 + * + * Author: + * David Erb (djerb@us.ibm.com) + * Kevin Corry (kevcorry@us.ibm.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include + +#include "cbe_regs.h" +#include "interrupt.h" +#include "pmu.h" + +/* + * When writing to write-only mmio addresses, save a shadow copy. All of the + * registers are 32-bit, but stored in the upper-half of a 64-bit field in + * pmd_regs. + */ + +#define WRITE_WO_MMIO(reg, x) \ + do { \ + u32 _x = (x); \ + struct cbe_pmd_regs __iomem *pmd_regs; \ + struct cbe_pmd_shadow_regs *shadow_regs; \ + pmd_regs = cbe_get_cpu_pmd_regs(cpu); \ + shadow_regs = cbe_get_cpu_pmd_shadow_regs(cpu); \ + out_be64(&(pmd_regs->reg), (((u64)_x) << 32)); \ + shadow_regs->reg = _x; \ + } while (0) + +#define READ_SHADOW_REG(val, reg) \ + do { \ + struct cbe_pmd_shadow_regs *shadow_regs; \ + shadow_regs = cbe_get_cpu_pmd_shadow_regs(cpu); \ + (val) = shadow_regs->reg; \ + } while (0) + +#define READ_MMIO_UPPER32(val, reg) \ + do { \ + struct cbe_pmd_regs __iomem *pmd_regs; \ + pmd_regs = cbe_get_cpu_pmd_regs(cpu); \ + (val) = (u32)(in_be64(&pmd_regs->reg) >> 32); \ + } while (0) + +/* + * Physical counter registers. + * Each physical counter can act as one 32-bit counter or two 16-bit counters. + */ + +u32 cbe_read_phys_ctr(u32 cpu, u32 phys_ctr) +{ + u32 val_in_latch, val = 0; + + if (phys_ctr < NR_PHYS_CTRS) { + READ_SHADOW_REG(val_in_latch, counter_value_in_latch); + + /* Read the latch or the actual counter, whichever is newer. */ + if (val_in_latch & (1 << phys_ctr)) { + READ_SHADOW_REG(val, pm_ctr[phys_ctr]); + } else { + READ_MMIO_UPPER32(val, pm_ctr[phys_ctr]); + } + } + + return val; +} + +void cbe_write_phys_ctr(u32 cpu, u32 phys_ctr, u32 val) +{ + struct cbe_pmd_shadow_regs *shadow_regs; + u32 pm_ctrl; + + if (phys_ctr < NR_PHYS_CTRS) { + /* Writing to a counter only writes to a hardware latch. + * The new value is not propagated to the actual counter + * until the performance monitor is enabled. + */ + WRITE_WO_MMIO(pm_ctr[phys_ctr], val); + + pm_ctrl = cbe_read_pm(cpu, pm_control); + if (pm_ctrl & CBE_PM_ENABLE_PERF_MON) { + /* The counters are already active, so we need to + * rewrite the pm_control register to "re-enable" + * the PMU. + */ + cbe_write_pm(cpu, pm_control, pm_ctrl); + } else { + shadow_regs = cbe_get_cpu_pmd_shadow_regs(cpu); + shadow_regs->counter_value_in_latch |= (1 << phys_ctr); + } + } +} + +/* + * "Logical" counter registers. + * These will read/write 16-bits or 32-bits depending on the + * current size of the counter. Counters 4 - 7 are always 16-bit. + */ + +u32 cbe_read_ctr(u32 cpu, u32 ctr) +{ + u32 val; + u32 phys_ctr = ctr & (NR_PHYS_CTRS - 1); + + val = cbe_read_phys_ctr(cpu, phys_ctr); + + if (cbe_get_ctr_size(cpu, phys_ctr) == 16) + val = (ctr < NR_PHYS_CTRS) ? (val >> 16) : (val & 0xffff); + + return val; +} + +void cbe_write_ctr(u32 cpu, u32 ctr, u32 val) +{ + u32 phys_ctr; + u32 phys_val; + + phys_ctr = ctr & (NR_PHYS_CTRS - 1); + + if (cbe_get_ctr_size(cpu, phys_ctr) == 16) { + phys_val = cbe_read_phys_ctr(cpu, phys_ctr); + + if (ctr < NR_PHYS_CTRS) + val = (val << 16) | (phys_val & 0xffff); + else + val = (val & 0xffff) | (phys_val & 0xffff0000); + } + + cbe_write_phys_ctr(cpu, phys_ctr, val); +} + +/* + * Counter-control registers. + * Each "logical" counter has a corresponding control register. + */ + +u32 cbe_read_pm07_control(u32 cpu, u32 ctr) +{ + u32 pm07_control = 0; + + if (ctr < NR_CTRS) + READ_SHADOW_REG(pm07_control, pm07_control[ctr]); + + return pm07_control; +} + +void cbe_write_pm07_control(u32 cpu, u32 ctr, u32 val) +{ + if (ctr < NR_CTRS) + WRITE_WO_MMIO(pm07_control[ctr], val); +} + +/* + * Other PMU control registers. Most of these are write-only. + */ + +u32 cbe_read_pm(u32 cpu, enum pm_reg_name reg) +{ + u32 val = 0; + + switch (reg) { + case group_control: + READ_SHADOW_REG(val, group_control); + break; + + case debug_bus_control: + READ_SHADOW_REG(val, debug_bus_control); + break; + + case trace_address: + READ_MMIO_UPPER32(val, trace_address); + break; + + case ext_tr_timer: + READ_SHADOW_REG(val, ext_tr_timer); + break; + + case pm_status: + READ_MMIO_UPPER32(val, pm_status); + break; + + case pm_control: + READ_SHADOW_REG(val, pm_control); + break; + + case pm_interval: + READ_SHADOW_REG(val, pm_interval); + break; + + case pm_start_stop: + READ_SHADOW_REG(val, pm_start_stop); + break; + } + + return val; +} + +void cbe_write_pm(u32 cpu, enum pm_reg_name reg, u32 val) +{ + switch (reg) { + case group_control: + WRITE_WO_MMIO(group_control, val); + break; + + case debug_bus_control: + WRITE_WO_MMIO(debug_bus_control, val); + break; + + case trace_address: + WRITE_WO_MMIO(trace_address, val); + break; + + case ext_tr_timer: + WRITE_WO_MMIO(ext_tr_timer, val); + break; + + case pm_status: + WRITE_WO_MMIO(pm_status, val); + break; + + case pm_control: + WRITE_WO_MMIO(pm_control, val); + break; + + case pm_interval: + WRITE_WO_MMIO(pm_interval, val); + break; + + case pm_start_stop: + WRITE_WO_MMIO(pm_start_stop, val); + break; + } +} + +/* + * Get/set the size of a physical counter to either 16 or 32 bits. + */ + +u32 cbe_get_ctr_size(u32 cpu, u32 phys_ctr) +{ + u32 pm_ctrl, size = 0; + + if (phys_ctr < NR_PHYS_CTRS) { + pm_ctrl = cbe_read_pm(cpu, pm_control); + size = (pm_ctrl & CBE_PM_16BIT_CTR(phys_ctr)) ? 16 : 32; + } + + return size; +} + +void cbe_set_ctr_size(u32 cpu, u32 phys_ctr, u32 ctr_size) +{ + u32 pm_ctrl; + + if (phys_ctr < NR_PHYS_CTRS) { + pm_ctrl = cbe_read_pm(cpu, pm_control); + switch (ctr_size) { + case 16: + pm_ctrl |= CBE_PM_16BIT_CTR(phys_ctr); + break; + + case 32: + pm_ctrl &= ~CBE_PM_16BIT_CTR(phys_ctr); + break; + } + cbe_write_pm(cpu, pm_control, pm_ctrl); + } +} + +/* + * Enable/disable the entire performance monitoring unit. + * When we enable the PMU, all pending writes to counters get committed. + */ + +void cbe_enable_pm(u32 cpu) +{ + struct cbe_pmd_shadow_regs *shadow_regs; + u32 pm_ctrl; + + shadow_regs = cbe_get_cpu_pmd_shadow_regs(cpu); + shadow_regs->counter_value_in_latch = 0; + + pm_ctrl = cbe_read_pm(cpu, pm_control) | CBE_PM_ENABLE_PERF_MON; + cbe_write_pm(cpu, pm_control, pm_ctrl); +} + +void cbe_disable_pm(u32 cpu) +{ + u32 pm_ctrl; + pm_ctrl = cbe_read_pm(cpu, pm_control) & ~CBE_PM_ENABLE_PERF_MON; + cbe_write_pm(cpu, pm_control, pm_ctrl); +} + +/* + * Reading from the trace_buffer. + * The trace buffer is two 64-bit registers. Reading from + * the second half automatically increments the trace_address. + */ + +void cbe_read_trace_buffer(u32 cpu, u64 *buf) +{ + struct cbe_pmd_regs __iomem *pmd_regs = cbe_get_cpu_pmd_regs(cpu); + + *buf++ = in_be64(&pmd_regs->trace_buffer_0_63); + *buf++ = in_be64(&pmd_regs->trace_buffer_64_127); +} + Index: linux-2.6/arch/powerpc/platforms/cell/pmu.h =================================================================== --- /dev/null +++ linux-2.6/arch/powerpc/platforms/cell/pmu.h @@ -0,0 +1,57 @@ +/* + * Cell Broadband Engine Performance Monitor + * + * (C) Copyright IBM Corporation 2001,2006 + * + * Author: + * David Erb (djerb@us.ibm.com) + * Kevin Corry (kevcorry@us.ibm.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __PERFMON_H__ +#define __PERFMON_H__ + +enum pm_reg_name { + group_control, + debug_bus_control, + trace_address, + ext_tr_timer, + pm_status, + pm_control, + pm_interval, + pm_start_stop, +}; + +extern u32 cbe_read_phys_ctr(u32 cpu, u32 phys_ctr); +extern void cbe_write_phys_ctr(u32 cpu, u32 phys_ctr, u32 val); +extern u32 cbe_read_ctr(u32 cpu, u32 ctr); +extern void cbe_write_ctr(u32 cpu, u32 ctr, u32 val); + +extern u32 cbe_read_pm07_control(u32 cpu, u32 ctr); +extern void cbe_write_pm07_control(u32 cpu, u32 ctr, u32 val); +extern u32 cbe_read_pm (u32 cpu, enum pm_reg_name reg); +extern void cbe_write_pm (u32 cpu, enum pm_reg_name reg, u32 val); + +extern u32 cbe_get_ctr_size(u32 cpu, u32 phys_ctr); +extern void cbe_set_ctr_size(u32 cpu, u32 phys_ctr, u32 ctr_size); + +extern void cbe_enable_pm(u32 cpu); +extern void cbe_disable_pm(u32 cpu); + +extern void cbe_read_trace_buffer(u32 cpu, u64 *buf); + +#endif Index: linux-2.6/arch/powerpc/platforms/cell/spu_base.c =================================================================== --- linux-2.6.orig/arch/powerpc/platforms/cell/spu_base.c +++ linux-2.6/arch/powerpc/platforms/cell/spu_base.c @@ -38,6 +38,7 @@ #include #include #include +#include #include "interrupt.h" @@ -89,7 +90,30 @@ static int __spu_trap_data_seg(struct sp printk("%s: invalid access during switch!\n", __func__); return 1; } - if (!mm || (REGION_ID(ea) != USER_REGION_ID)) { + esid = (ea & ESID_MASK) | SLB_ESID_V; + + switch(REGION_ID(ea)) { + case USER_REGION_ID: +#ifdef CONFIG_HUGETLB_PAGE + if (in_hugepage_area(mm->context, ea)) + llp = mmu_psize_defs[mmu_huge_psize].sllp; + else +#endif + llp = mmu_psize_defs[mmu_virtual_psize].sllp; + vsid = (get_vsid(mm->context.id, ea) << SLB_VSID_SHIFT) | + SLB_VSID_USER | llp; + break; + case VMALLOC_REGION_ID: + llp = mmu_psize_defs[mmu_virtual_psize].sllp; + vsid = (get_kernel_vsid(ea) << SLB_VSID_SHIFT) | + SLB_VSID_KERNEL | llp; + break; + case KERNEL_REGION_ID: + llp = mmu_psize_defs[mmu_linear_psize].sllp; + vsid = (get_kernel_vsid(ea) << SLB_VSID_SHIFT) | + SLB_VSID_KERNEL | llp; + break; + default: /* Future: support kernel segments so that drivers * can use SPUs. */ @@ -97,16 +121,6 @@ static int __spu_trap_data_seg(struct sp return 1; } - esid = (ea & ESID_MASK) | SLB_ESID_V; -#ifdef CONFIG_HUGETLB_PAGE - if (in_hugepage_area(mm->context, ea)) - llp = mmu_psize_defs[mmu_huge_psize].sllp; - else -#endif - llp = mmu_psize_defs[mmu_virtual_psize].sllp; - vsid = (get_vsid(mm->context.id, ea) << SLB_VSID_SHIFT) | - SLB_VSID_USER | llp; - out_be64(&priv2->slb_index_W, spu->slb_replace); out_be64(&priv2->slb_vsid_RW, vsid); out_be64(&priv2->slb_esid_RW, esid); @@ -320,6 +334,7 @@ static void spu_free_irqs(struct spu *sp } static struct list_head spu_list[MAX_NUMNODES]; +static LIST_HEAD(spu_full_list); static DEFINE_MUTEX(spu_mutex); static void spu_init_channels(struct spu *spu) @@ -364,8 +379,7 @@ struct spu *spu_alloc_node(int node) if (!list_empty(&spu_list[node])) { spu = list_entry(spu_list[node].next, struct spu, list); list_del_init(&spu->list); - pr_debug("Got SPU %x %d %d\n", - spu->isrc, spu->number, spu->node); + pr_debug("Got SPU %d %d\n", spu->number, spu->node); spu_init_channels(spu); } mutex_unlock(&spu_mutex); @@ -591,7 +605,6 @@ static int __init spu_map_interrupts_old /* Add the node number */ isrc |= spu->node << IIC_IRQ_NODE_SHIFT; - spu->isrc = isrc; /* Now map interrupts of all 3 classes */ spu->irqs[0] = irq_create_mapping(NULL, IIC_IRQ_CLASS_0 | isrc); @@ -733,15 +746,56 @@ struct sysdev_class spu_sysdev_class = { set_kset_name("spu") }; -static ssize_t spu_show_isrc(struct sys_device *sysdev, char *buf) +int spu_add_sysdev_attr(struct sysdev_attribute *attr) { - struct spu *spu = container_of(sysdev, struct spu, sysdev); - return sprintf(buf, "%d\n", spu->isrc); + struct spu *spu; + mutex_lock(&spu_mutex); + + list_for_each_entry(spu, &spu_full_list, full_list) + sysdev_create_file(&spu->sysdev, attr); + mutex_unlock(&spu_mutex); + return 0; } -static SYSDEV_ATTR(isrc, 0400, spu_show_isrc, NULL); +EXPORT_SYMBOL_GPL(spu_add_sysdev_attr); + +int spu_add_sysdev_attr_group(struct attribute_group *attrs) +{ + struct spu *spu; + mutex_lock(&spu_mutex); -extern int attach_sysdev_to_node(struct sys_device *dev, int nid); + list_for_each_entry(spu, &spu_full_list, full_list) + sysfs_create_group(&spu->sysdev.kobj, attrs); + + mutex_unlock(&spu_mutex); + return 0; +} +EXPORT_SYMBOL_GPL(spu_add_sysdev_attr_group); + + +void spu_remove_sysdev_attr(struct sysdev_attribute *attr) +{ + struct spu *spu; + mutex_lock(&spu_mutex); + + list_for_each_entry(spu, &spu_full_list, full_list) + sysdev_remove_file(&spu->sysdev, attr); + + mutex_unlock(&spu_mutex); +} +EXPORT_SYMBOL_GPL(spu_remove_sysdev_attr); + +void spu_remove_sysdev_attr_group(struct attribute_group *attrs) +{ + struct spu *spu; + mutex_lock(&spu_mutex); + + list_for_each_entry(spu, &spu_full_list, full_list) + sysfs_remove_group(&spu->sysdev.kobj, attrs); + + mutex_unlock(&spu_mutex); +} +EXPORT_SYMBOL_GPL(spu_remove_sysdev_attr_group); static int spu_create_sysdev(struct spu *spu) { @@ -756,8 +810,6 @@ static int spu_create_sysdev(struct spu return ret; } - if (spu->isrc != 0) - sysdev_create_file(&spu->sysdev, &attr_isrc); sysfs_add_device_to_node(&spu->sysdev, spu->nid); return 0; @@ -765,7 +817,6 @@ static int spu_create_sysdev(struct spu static void spu_destroy_sysdev(struct spu *spu) { - sysdev_remove_file(&spu->sysdev, &attr_isrc); sysfs_remove_device_from_node(&spu->sysdev, spu->nid); sysdev_unregister(&spu->sysdev); } @@ -805,7 +856,7 @@ static int __init create_spu(struct devi if (ret) goto out_unmap; spin_lock_init(&spu->register_lock); - spu_mfc_sdr_set(spu, mfspr(SPRN_SDR1)); + spu_mfc_sdr_setup(spu); spu_mfc_sr1_set(spu, 0x33); mutex_lock(&spu_mutex); @@ -819,10 +870,13 @@ static int __init create_spu(struct devi goto out_free_irqs; list_add(&spu->list, &spu_list[spu->node]); + list_add(&spu->full_list, &spu_full_list); + spu->devnode = of_node_get(spe); + mutex_unlock(&spu_mutex); - pr_debug(KERN_DEBUG "Using SPE %s %02x %p %p %p %p %d\n", - spu->name, spu->isrc, spu->local_store, + pr_debug(KERN_DEBUG "Using SPE %s %p %p %p %p %d\n", + spu->name, spu->local_store, spu->problem, spu->priv1, spu->priv2, spu->number); goto out; @@ -841,6 +895,9 @@ out: static void destroy_spu(struct spu *spu) { list_del_init(&spu->list); + list_del_init(&spu->full_list); + + of_node_put(spu->devnode); spu_destroy_sysdev(spu); spu_free_irqs(spu); @@ -887,6 +944,9 @@ static int __init init_spu_base(void) break; } } + + xmon_register_spus(&spu_full_list); + return ret; } module_init(init_spu_base); Index: linux-2.6/arch/powerpc/platforms/cell/spu_priv1_mmio.c =================================================================== --- linux-2.6.orig/arch/powerpc/platforms/cell/spu_priv1_mmio.c +++ linux-2.6/arch/powerpc/platforms/cell/spu_priv1_mmio.c @@ -84,9 +84,9 @@ static void mfc_dsisr_set(struct spu *sp out_be64(&spu->priv1->mfc_dsisr_RW, dsisr); } -static void mfc_sdr_set(struct spu *spu, u64 sdr) +static void mfc_sdr_setup(struct spu *spu) { - out_be64(&spu->priv1->mfc_sdr_RW, sdr); + out_be64(&spu->priv1->mfc_sdr_RW, mfspr(SPRN_SDR1)); } static void mfc_sr1_set(struct spu *spu, u64 sr1) @@ -146,7 +146,7 @@ const struct spu_priv1_ops spu_priv1_mmi .mfc_dar_get = mfc_dar_get, .mfc_dsisr_get = mfc_dsisr_get, .mfc_dsisr_set = mfc_dsisr_set, - .mfc_sdr_set = mfc_sdr_set, + .mfc_sdr_setup = mfc_sdr_setup, .mfc_sr1_set = mfc_sr1_set, .mfc_sr1_get = mfc_sr1_get, .mfc_tclass_id_set = mfc_tclass_id_set, Index: linux-2.6/arch/powerpc/platforms/cell/spufs/context.c =================================================================== --- linux-2.6.orig/arch/powerpc/platforms/cell/spufs/context.c +++ linux-2.6/arch/powerpc/platforms/cell/spufs/context.c @@ -120,6 +120,33 @@ void spu_unmap_mappings(struct spu_conte unmap_mapping_range(ctx->signal2, 0, 0x4000, 1); } +int spu_acquire_exclusive(struct spu_context *ctx) +{ + int ret = 0; + + down_write(&ctx->state_sema); + /* ctx is about to be freed, can't acquire any more */ + if (!ctx->owner) { + ret = -EINVAL; + goto out; + } + + if (ctx->state == SPU_STATE_SAVED) { + ret = spu_activate(ctx, 0); + if (ret) + goto out; + ctx->state = SPU_STATE_RUNNABLE; + } else { + /* We need to exclude userspace access to the context. */ + spu_unmap_mappings(ctx); + } + +out: + if (ret) + up_write(&ctx->state_sema); + return ret; +} + int spu_acquire_runnable(struct spu_context *ctx) { int ret = 0; Index: linux-2.6/arch/powerpc/platforms/cell/spufs/file.c =================================================================== --- linux-2.6.orig/arch/powerpc/platforms/cell/spufs/file.c +++ linux-2.6/arch/powerpc/platforms/cell/spufs/file.c @@ -1343,6 +1343,37 @@ static struct file_operations spufs_mfc_ .mmap = spufs_mfc_mmap, }; + +static int spufs_recycle_open(struct inode *inode, struct file *file) +{ + file->private_data = SPUFS_I(inode)->i_ctx; + return nonseekable_open(inode, file); +} + +static ssize_t spufs_recycle_write(struct file *file, + const char __user *buffer, size_t size, loff_t *pos) +{ + struct spu_context *ctx = file->private_data; + int ret; + + if (!(ctx->flags & SPU_CREATE_ISOLATE)) + return -EINVAL; + + if (size < 1) + return -EINVAL; + + ret = spu_recycle_isolated(ctx); + + if (ret) + return ret; + return size; +} + +static struct file_operations spufs_recycle_fops = { + .open = spufs_recycle_open, + .write = spufs_recycle_write, +}; + static void spufs_npc_set(void *data, u64 val) { struct spu_context *ctx = data; @@ -1531,3 +1562,26 @@ struct tree_descr spufs_dir_contents[] = { "object-id", &spufs_object_id_ops, 0666, }, {}, }; + +struct tree_descr spufs_dir_nosched_contents[] = { + { "mem", &spufs_mem_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, }, + { "mss", &spufs_mss_fops, 0666, }, + { "mfc", &spufs_mfc_fops, 0666, }, + { "cntl", &spufs_cntl_fops, 0666, }, + { "npc", &spufs_npc_ops, 0666, }, + { "psmap", &spufs_psmap_fops, 0666, }, + { "phys-id", &spufs_id_ops, 0666, }, + { "object-id", &spufs_object_id_ops, 0666, }, + { "recycle", &spufs_recycle_fops, 0222, }, + {}, +}; Index: linux-2.6/arch/powerpc/platforms/cell/spufs/hw_ops.c =================================================================== --- linux-2.6.orig/arch/powerpc/platforms/cell/spufs/hw_ops.c +++ linux-2.6/arch/powerpc/platforms/cell/spufs/hw_ops.c @@ -219,8 +219,11 @@ static char *spu_hw_get_ls(struct spu_co static void spu_hw_runcntl_write(struct spu_context *ctx, u32 val) { - eieio(); + spin_lock_irq(&ctx->spu->register_lock); + if (val & SPU_RUNCNTL_ISOLATE) + out_be64(&ctx->spu->priv2->spu_privcntl_RW, 4LL); out_be32(&ctx->spu->problem->spu_runcntl_RW, val); + spin_unlock_irq(&ctx->spu->register_lock); } static void spu_hw_runcntl_stop(struct spu_context *ctx) Index: linux-2.6/arch/powerpc/platforms/cell/spufs/inode.c =================================================================== --- linux-2.6.orig/arch/powerpc/platforms/cell/spufs/inode.c +++ linux-2.6/arch/powerpc/platforms/cell/spufs/inode.c @@ -33,6 +33,8 @@ #include #include +#include +#include #include #include #include @@ -41,6 +43,7 @@ #include "spufs.h" static kmem_cache_t *spufs_inode_cache; +static char *isolated_loader; static struct inode * spufs_alloc_inode(struct super_block *sb) @@ -233,6 +236,95 @@ struct file_operations spufs_context_fop .fsync = simple_sync_file, }; +static int spu_setup_isolated(struct spu_context *ctx) +{ + int ret; + u64 __iomem *mfc_cntl; + u64 sr1; + u32 status; + unsigned long timeout; + const u32 status_loading = SPU_STATUS_RUNNING + | SPU_STATUS_ISOLATED_STATE | SPU_STATUS_ISOLATED_LOAD_STATUS; + + if (!isolated_loader) + return -ENODEV; + + if ((ret = spu_acquire_exclusive(ctx)) != 0) + return ret; + + mfc_cntl = &ctx->spu->priv2->mfc_control_RW; + + /* purge the MFC DMA queue to ensure no spurious accesses before we + * enter kernel mode */ + timeout = jiffies + HZ; + out_be64(mfc_cntl, MFC_CNTL_PURGE_DMA_REQUEST); + while ((in_be64(mfc_cntl) & MFC_CNTL_PURGE_DMA_STATUS_MASK) + != MFC_CNTL_PURGE_DMA_COMPLETE) { + if (time_after(jiffies, timeout)) { + printk(KERN_ERR "%s: timeout flushing MFC DMA queue\n", + __FUNCTION__); + ret = -EIO; + goto out_unlock; + } + cond_resched(); + } + + /* put the SPE in kernel mode to allow access to the loader */ + sr1 = spu_mfc_sr1_get(ctx->spu); + sr1 &= ~MFC_STATE1_PROBLEM_STATE_MASK; + spu_mfc_sr1_set(ctx->spu, sr1); + + /* start the loader */ + ctx->ops->signal1_write(ctx, (unsigned long)isolated_loader >> 32); + ctx->ops->signal2_write(ctx, + (unsigned long)isolated_loader & 0xffffffff); + + ctx->ops->runcntl_write(ctx, + SPU_RUNCNTL_RUNNABLE | SPU_RUNCNTL_ISOLATE); + + ret = 0; + timeout = jiffies + HZ; + while (((status = ctx->ops->status_read(ctx)) & status_loading) == + status_loading) { + if (time_after(jiffies, timeout)) { + printk(KERN_ERR "%s: timeout waiting for loader\n", + __FUNCTION__); + ret = -EIO; + goto out_drop_priv; + } + cond_resched(); + } + + if (!(status & SPU_STATUS_RUNNING)) { + /* If isolated LOAD has failed: run SPU, we will get a stop-and + * signal later. */ + pr_debug("%s: isolated LOAD failed\n", __FUNCTION__); + ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_RUNNABLE); + ret = -EACCES; + + } else if (!(status & SPU_STATUS_ISOLATED_STATE)) { + /* This isn't allowed by the CBEA, but check anyway */ + pr_debug("%s: SPU fell out of isolated mode?\n", __FUNCTION__); + ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_STOP); + ret = -EINVAL; + } + +out_drop_priv: + /* Finished accessing the loader. Drop kernel mode */ + sr1 |= MFC_STATE1_PROBLEM_STATE_MASK; + spu_mfc_sr1_set(ctx->spu, sr1); + +out_unlock: + spu_release_exclusive(ctx); + return ret; +} + +int spu_recycle_isolated(struct spu_context *ctx) +{ + ctx->ops->runcntl_stop(ctx); + return spu_setup_isolated(ctx); +} + static int spufs_mkdir(struct inode *dir, struct dentry *dentry, unsigned int flags, int mode) @@ -256,10 +348,14 @@ spufs_mkdir(struct inode *dir, struct de goto out_iput; ctx->flags = flags; - inode->i_op = &spufs_dir_inode_operations; inode->i_fop = &simple_dir_operations; - ret = spufs_fill_dir(dentry, spufs_dir_contents, mode, ctx); + if (flags & SPU_CREATE_NOSCHED) + ret = spufs_fill_dir(dentry, spufs_dir_nosched_contents, + mode, ctx); + else + ret = spufs_fill_dir(dentry, spufs_dir_contents, mode, ctx); + if (ret) goto out_free_ctx; @@ -308,6 +404,16 @@ static int spufs_create_context(struct i { int ret; + ret = -EPERM; + if ((flags & SPU_CREATE_NOSCHED) && + !capable(CAP_SYS_NICE)) + goto out_unlock; + + ret = -EINVAL; + if ((flags & (SPU_CREATE_NOSCHED | SPU_CREATE_ISOLATE)) + == SPU_CREATE_ISOLATE) + goto out_unlock; + ret = spufs_mkdir(inode, dentry, flags, mode & S_IRWXUGO); if (ret) goto out_unlock; @@ -327,6 +433,13 @@ static int spufs_create_context(struct i out_unlock: mutex_unlock(&inode->i_mutex); out: + if (ret >= 0 && (flags & SPU_CREATE_ISOLATE)) { + int setup_err = spu_setup_isolated( + SPUFS_I(dentry->d_inode)->i_ctx); + if (setup_err) + ret = setup_err; + } + dput(dentry); return ret; } @@ -541,6 +654,30 @@ spufs_parse_options(char *options, struc return 1; } +static void +spufs_init_isolated_loader(void) +{ + struct device_node *dn; + const char *loader; + int size; + + dn = of_find_node_by_path("/spu-isolation"); + if (!dn) + return; + + loader = get_property(dn, "loader", &size); + if (!loader) + return; + + /* kmalloc should align on a 16 byte boundary..* */ + isolated_loader = kmalloc(size, GFP_KERNEL); + if (!isolated_loader) + return; + + memcpy(isolated_loader, loader, size); + printk(KERN_INFO "spufs: SPU isolation mode enabled\n"); +} + static int spufs_create_root(struct super_block *sb, void *data) { @@ -626,6 +763,8 @@ static int __init spufs_init(void) ret = register_spu_syscalls(&spufs_calls); if (ret) goto out_fs; + + spufs_init_isolated_loader(); return 0; out_fs: unregister_filesystem(&spufs_type); Index: linux-2.6/arch/powerpc/platforms/cell/spufs/run.c =================================================================== --- linux-2.6.orig/arch/powerpc/platforms/cell/spufs/run.c +++ linux-2.6/arch/powerpc/platforms/cell/spufs/run.c @@ -1,3 +1,5 @@ +#define DEBUG + #include #include @@ -51,11 +53,17 @@ static inline int spu_stopped(struct spu static inline int spu_run_init(struct spu_context *ctx, u32 * npc) { int ret; + unsigned long runcntl = SPU_RUNCNTL_RUNNABLE; if ((ret = spu_acquire_runnable(ctx)) != 0) return ret; - ctx->ops->npc_write(ctx, *npc); - ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_RUNNABLE); + + /* if we're in isolated mode, we would have started the SPU + * earlier, so don't do it again now. */ + if (!(ctx->flags & SPU_CREATE_ISOLATE)) { + ctx->ops->npc_write(ctx, *npc); + ctx->ops->runcntl_write(ctx, runcntl); + } return 0; } Index: linux-2.6/arch/powerpc/platforms/cell/spufs/spufs.h =================================================================== --- linux-2.6.orig/arch/powerpc/platforms/cell/spufs/spufs.h +++ linux-2.6/arch/powerpc/platforms/cell/spufs/spufs.h @@ -135,6 +135,7 @@ struct spufs_inode_info { container_of(inode, struct spufs_inode_info, vfs_inode) extern struct tree_descr spufs_dir_contents[]; +extern struct tree_descr spufs_dir_nosched_contents[]; /* system call implementation */ long spufs_run_spu(struct file *file, @@ -162,6 +163,12 @@ 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_acquire_exclusive(struct spu_context *ctx); + +static inline void spu_release_exclusive(struct spu_context *ctx) +{ + up_write(&ctx->state_sema); +} int spu_activate(struct spu_context *ctx, u64 flags); void spu_deactivate(struct spu_context *ctx); @@ -169,6 +176,7 @@ void spu_yield(struct spu_context *ctx); int __init spu_sched_init(void); void __exit spu_sched_exit(void); +int spu_recycle_isolated(struct spu_context *ctx); /* * spufs_wait * Same as wait_event_interruptible(), except that here Index: linux-2.6/arch/powerpc/platforms/cell/spufs/switch.c =================================================================== --- linux-2.6.orig/arch/powerpc/platforms/cell/spufs/switch.c +++ linux-2.6/arch/powerpc/platforms/cell/spufs/switch.c @@ -102,7 +102,7 @@ static inline int check_spu_isolate(stru * saved at this time. */ isolate_state = SPU_STATUS_ISOLATED_STATE | - SPU_STATUS_ISOLATED_LOAD_STAUTUS | SPU_STATUS_ISOLATED_EXIT_STAUTUS; + SPU_STATUS_ISOLATED_LOAD_STATUS | SPU_STATUS_ISOLATED_EXIT_STATUS; return (in_be32(&prob->spu_status_R) & isolate_state) ? 1 : 0; } @@ -1046,12 +1046,12 @@ static inline int suspend_spe(struct spu */ if (in_be32(&prob->spu_status_R) & SPU_STATUS_RUNNING) { if (in_be32(&prob->spu_status_R) & - SPU_STATUS_ISOLATED_EXIT_STAUTUS) { + SPU_STATUS_ISOLATED_EXIT_STATUS) { POLL_WHILE_TRUE(in_be32(&prob->spu_status_R) & SPU_STATUS_RUNNING); } if ((in_be32(&prob->spu_status_R) & - SPU_STATUS_ISOLATED_LOAD_STAUTUS) + SPU_STATUS_ISOLATED_LOAD_STATUS) || (in_be32(&prob->spu_status_R) & SPU_STATUS_ISOLATED_STATE)) { out_be32(&prob->spu_runcntl_RW, SPU_RUNCNTL_STOP); @@ -1085,7 +1085,7 @@ static inline void clear_spu_status(stru */ if (!(in_be32(&prob->spu_status_R) & SPU_STATUS_RUNNING)) { if (in_be32(&prob->spu_status_R) & - SPU_STATUS_ISOLATED_EXIT_STAUTUS) { + SPU_STATUS_ISOLATED_EXIT_STATUS) { spu_mfc_sr1_set(spu, MFC_STATE1_MASTER_RUN_CONTROL_MASK); eieio(); @@ -1095,7 +1095,7 @@ static inline void clear_spu_status(stru SPU_STATUS_RUNNING); } if ((in_be32(&prob->spu_status_R) & - SPU_STATUS_ISOLATED_LOAD_STAUTUS) + SPU_STATUS_ISOLATED_LOAD_STATUS) || (in_be32(&prob->spu_status_R) & SPU_STATUS_ISOLATED_STATE)) { spu_mfc_sr1_set(spu, @@ -1916,6 +1916,51 @@ static void save_lscsa(struct spu_state wait_spu_stopped(prev, spu); /* Step 57. */ } +static void force_spu_isolate_exit(struct spu *spu) +{ + struct spu_problem __iomem *prob = spu->problem; + struct spu_priv2 __iomem *priv2 = spu->priv2; + + /* Stop SPE execution and wait for completion. */ + out_be32(&prob->spu_runcntl_RW, SPU_RUNCNTL_STOP); + iobarrier_rw(); + POLL_WHILE_TRUE(in_be32(&prob->spu_status_R) & SPU_STATUS_RUNNING); + + /* Restart SPE master runcntl. */ + spu_mfc_sr1_set(spu, MFC_STATE1_MASTER_RUN_CONTROL_MASK); + iobarrier_w(); + + /* Initiate isolate exit request and wait for completion. */ + out_be64(&priv2->spu_privcntl_RW, 4LL); + iobarrier_w(); + out_be32(&prob->spu_runcntl_RW, 2); + iobarrier_rw(); + POLL_WHILE_FALSE((in_be32(&prob->spu_status_R) + & SPU_STATUS_STOPPED_BY_STOP)); + + /* Reset load request to normal. */ + out_be64(&priv2->spu_privcntl_RW, SPU_PRIVCNT_LOAD_REQUEST_NORMAL); + iobarrier_w(); +} + +/** + * stop_spu_isolate + * Check SPU run-control state and force isolated + * exit function as necessary. + */ +static void stop_spu_isolate(struct spu *spu) +{ + struct spu_problem __iomem *prob = spu->problem; + + if (in_be32(&prob->spu_status_R) & SPU_STATUS_ISOLATED_STATE) { + /* The SPU is in isolated state; the only way + * to get it out is to perform an isolated + * exit (clean) operation. + */ + force_spu_isolate_exit(spu); + } +} + static void harvest(struct spu_state *prev, struct spu *spu) { /* @@ -1928,6 +1973,7 @@ static void harvest(struct spu_state *pr inhibit_user_access(prev, spu); /* Step 3. */ terminate_spu_app(prev, spu); /* Step 4. */ set_switch_pending(prev, spu); /* Step 5. */ + stop_spu_isolate(spu); /* NEW. */ remove_other_spu_access(prev, spu); /* Step 6. */ suspend_mfc(prev, spu); /* Step 7. */ wait_suspend_mfc_complete(prev, spu); /* Step 8. */ @@ -2096,11 +2142,11 @@ int spu_save(struct spu_state *prev, str acquire_spu_lock(spu); /* Step 1. */ rc = __do_spu_save(prev, spu); /* Steps 2-53. */ release_spu_lock(spu); - if (rc) { + if (rc != 0 && rc != 2 && rc != 6) { panic("%s failed on SPU[%d], rc=%d.\n", __func__, spu->number, rc); } - return rc; + return 0; } EXPORT_SYMBOL_GPL(spu_save); @@ -2165,9 +2211,6 @@ static void init_priv1(struct spu_state 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 | Index: linux-2.6/arch/powerpc/platforms/iseries/dt.c =================================================================== --- linux-2.6.orig/arch/powerpc/platforms/iseries/dt.c +++ linux-2.6/arch/powerpc/platforms/iseries/dt.c @@ -41,6 +41,7 @@ #include "call_pci.h" #include "pci.h" #include "it_exp_vpd_panel.h" +#include "naca.h" #ifdef DEBUG #define DBG(fmt...) udbg_printf(fmt) @@ -205,13 +206,11 @@ static void __init dt_prop_u32(struct is dt_prop(dt, name, &data, sizeof(u32)); } -#ifdef notyet static void __init dt_prop_u64(struct iseries_flat_dt *dt, const char *name, u64 data) { dt_prop(dt, name, &data, sizeof(u64)); } -#endif static void __init dt_prop_u64_list(struct iseries_flat_dt *dt, const char *name, u64 *data, int n) @@ -306,6 +305,17 @@ static void __init dt_model(struct iseri dt_prop_u32(dt, "ibm,partition-no", HvLpConfig_getLpIndex()); } +static void __init dt_initrd(struct iseries_flat_dt *dt) +{ +#ifdef CONFIG_BLK_DEV_INITRD + if (naca.xRamDisk) { + dt_prop_u64(dt, "linux,initrd-start", (u64)naca.xRamDisk); + dt_prop_u64(dt, "linux,initrd-end", + (u64)naca.xRamDisk + naca.xRamDiskSize * HW_PAGE_SIZE); + } +#endif +} + static void __init dt_do_vdevice(struct iseries_flat_dt *dt, const char *name, u32 reg, int unit, const char *type, const char *compat, int end) @@ -641,6 +651,7 @@ void * __init build_flat_dt(unsigned lon /* /chosen */ dt_start_node(iseries_dt, "chosen"); dt_prop_str(iseries_dt, "bootargs", cmd_line); + dt_initrd(iseries_dt); dt_end_node(iseries_dt); dt_cpus(iseries_dt); Index: linux-2.6/arch/powerpc/platforms/iseries/ksyms.c =================================================================== --- linux-2.6.orig/arch/powerpc/platforms/iseries/ksyms.c +++ linux-2.6/arch/powerpc/platforms/iseries/ksyms.c @@ -19,9 +19,3 @@ EXPORT_SYMBOL(HvCall4); EXPORT_SYMBOL(HvCall5); EXPORT_SYMBOL(HvCall6); EXPORT_SYMBOL(HvCall7); - -#ifdef CONFIG_SMP -EXPORT_SYMBOL(local_get_flags); -EXPORT_SYMBOL(local_irq_disable); -EXPORT_SYMBOL(local_irq_restore); -#endif Index: linux-2.6/arch/powerpc/platforms/iseries/misc.S =================================================================== --- linux-2.6.orig/arch/powerpc/platforms/iseries/misc.S +++ linux-2.6/arch/powerpc/platforms/iseries/misc.S @@ -19,39 +19,8 @@ .text -/* unsigned long local_save_flags(void) */ -_GLOBAL(local_get_flags) - lbz r3,PACAPROCENABLED(r13) - blr - -/* unsigned long local_irq_disable(void) */ -_GLOBAL(local_irq_disable) - lbz r3,PACAPROCENABLED(r13) - li r4,0 - stb r4,PACAPROCENABLED(r13) - blr /* Done */ - -/* void local_irq_restore(unsigned long flags) */ -_GLOBAL(local_irq_restore) - lbz r5,PACAPROCENABLED(r13) - /* Check if things are setup the way we want _already_. */ - cmpw 0,r3,r5 - beqlr - /* are we enabling interrupts? */ - cmpdi 0,r3,0 - stb r3,PACAPROCENABLED(r13) - beqlr - /* Check pending interrupts */ - /* A decrementer, IPI or PMC interrupt may have occurred - * while we were in the hypervisor (which enables) */ - ld r4,PACALPPACAPTR(r13) - ld r4,LPPACAANYINT(r4) - cmpdi r4,0 - beqlr - - /* - * Handle pending interrupts in interrupt context - */ +/* Handle pending interrupts in interrupt context */ +_GLOBAL(iseries_handle_interrupts) li r0,0x5555 sc blr Index: linux-2.6/arch/powerpc/platforms/iseries/setup.c =================================================================== --- linux-2.6.orig/arch/powerpc/platforms/iseries/setup.c +++ linux-2.6/arch/powerpc/platforms/iseries/setup.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include @@ -80,8 +79,6 @@ extern void iSeries_pci_final_fixup(void static void iSeries_pci_final_fixup(void) { } #endif -extern int rd_size; /* Defined in drivers/block/rd.c */ - extern unsigned long iSeries_recal_tb; extern unsigned long iSeries_recal_titan; @@ -295,24 +292,6 @@ static void __init iSeries_init_early(vo { DBG(" -> iSeries_init_early()\n"); -#if defined(CONFIG_BLK_DEV_INITRD) - /* - * If the init RAM disk has been configured and there is - * a non-zero starting address for it, set it up - */ - if (naca.xRamDisk) { - initrd_start = (unsigned long)__va(naca.xRamDisk); - initrd_end = initrd_start + naca.xRamDiskSize * HW_PAGE_SIZE; - initrd_below_start_ok = 1; // ramdisk in kernel space - ROOT_DEV = Root_RAM0; - if (((rd_size * 1024) / HW_PAGE_SIZE) < naca.xRamDiskSize) - rd_size = (naca.xRamDiskSize * HW_PAGE_SIZE) / 1024; - } else -#endif /* CONFIG_BLK_DEV_INITRD */ - { - /* ROOT_DEV = MKDEV(VIODASD_MAJOR, 1); */ - } - iSeries_recal_tb = get_tb(); iSeries_recal_titan = HvCallXm_loadTod(); @@ -331,17 +310,6 @@ static void __init iSeries_init_early(vo mf_init(); - /* If we were passed an initrd, set the ROOT_DEV properly if the values - * look sensible. If not, clear initrd reference. - */ -#ifdef CONFIG_BLK_DEV_INITRD - if (initrd_start >= KERNELBASE && initrd_end >= KERNELBASE && - initrd_end > initrd_start) - ROOT_DEV = Root_RAM0; - else - initrd_start = initrd_end = 0; -#endif /* CONFIG_BLK_DEV_INITRD */ - DBG(" <- iSeries_init_early()\n"); } Index: linux-2.6/arch/powerpc/platforms/pseries/lpar.c =================================================================== --- linux-2.6.orig/arch/powerpc/platforms/pseries/lpar.c +++ linux-2.6/arch/powerpc/platforms/pseries/lpar.c @@ -282,7 +282,7 @@ void vpa_init(int cpu) } } -long pSeries_lpar_hpte_insert(unsigned long hpte_group, +static long pSeries_lpar_hpte_insert(unsigned long hpte_group, unsigned long va, unsigned long pa, unsigned long rflags, unsigned long vflags, int psize) @@ -506,7 +506,7 @@ static void pSeries_lpar_hpte_invalidate * Take a spinlock around flushes to avoid bouncing the hypervisor tlbie * lock. */ -void pSeries_lpar_flush_hash_range(unsigned long number, int local) +static void pSeries_lpar_flush_hash_range(unsigned long number, int local) { int i; unsigned long flags = 0; Index: linux-2.6/arch/powerpc/sysdev/Makefile =================================================================== --- linux-2.6.orig/arch/powerpc/sysdev/Makefile +++ linux-2.6/arch/powerpc/sysdev/Makefile @@ -10,7 +10,6 @@ obj-$(CONFIG_40x) += dcr.o obj-$(CONFIG_U3_DART) += dart_iommu.o obj-$(CONFIG_MMIO_NVRAM) += mmio_nvram.o obj-$(CONFIG_FSL_SOC) += fsl_soc.o -obj-$(CONFIG_PPC_TODC) += todc.o obj-$(CONFIG_TSI108_BRIDGE) += tsi108_pci.o tsi108_dev.o obj-$(CONFIG_QUICC_ENGINE) += qe_lib/ Index: linux-2.6/arch/powerpc/sysdev/todc.c =================================================================== --- linux-2.6.orig/arch/powerpc/sysdev/todc.c +++ /dev/null @@ -1,392 +0,0 @@ -/* - * Time of Day Clock support for the M48T35, M48T37, M48T59, and MC146818 - * Real Time Clocks/Timekeepers. - * - * Author: Mark A. Greer - * - * 2001-2004 (c) MontaVista, Software, Inc. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. - */ -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -/* - * Depending on the hardware on your board and your board design, the - * RTC/NVRAM may be accessed either directly (like normal memory) or via - * address/data registers. If your board uses the direct method, set - * 'nvram_data' to the base address of your nvram and leave 'nvram_as0' and - * 'nvram_as1' NULL. If your board uses address/data regs to access nvram, - * set 'nvram_as0' to the address of the lower byte, set 'nvram_as1' to the - * address of the upper byte (leave NULL if using mc146818), and set - * 'nvram_data' to the address of the 8-bit data register. - * - * Note: Even though the documentation for the various RTC chips say that it - * take up to a second before it starts updating once the 'R' bit is - * cleared, they always seem to update even though we bang on it many - * times a second. This is true, except for the Dallas Semi 1746/1747 - * (possibly others). Those chips seem to have a real problem whenever - * we set the 'R' bit before reading them, they basically stop counting. - * --MAG - */ - -/* - * 'todc_info' should be initialized in your *_setup.c file to - * point to a fully initialized 'todc_info_t' structure. - * This structure holds all the register offsets for your particular - * TODC/RTC chip. - * TODC_ALLOC()/TODC_INIT() will allocate and initialize this table for you. - */ - -#ifdef RTC_FREQ_SELECT -#undef RTC_FREQ_SELECT -#define RTC_FREQ_SELECT control_b /* Register A */ -#endif - -#ifdef RTC_CONTROL -#undef RTC_CONTROL -#define RTC_CONTROL control_a /* Register B */ -#endif - -#ifdef RTC_INTR_FLAGS -#undef RTC_INTR_FLAGS -#define RTC_INTR_FLAGS watchdog /* Register C */ -#endif - -#ifdef RTC_VALID -#undef RTC_VALID -#define RTC_VALID interrupts /* Register D */ -#endif - -/* Access routines when RTC accessed directly (like normal memory) */ -u_char -todc_direct_read_val(int addr) -{ - return readb((void __iomem *)(todc_info->nvram_data + addr)); -} - -void -todc_direct_write_val(int addr, unsigned char val) -{ - writeb(val, (void __iomem *)(todc_info->nvram_data + addr)); - return; -} - -/* Access routines for accessing m48txx type chips via addr/data regs */ -u_char -todc_m48txx_read_val(int addr) -{ - outb(addr, todc_info->nvram_as0); - outb(addr>>todc_info->as0_bits, todc_info->nvram_as1); - return inb(todc_info->nvram_data); -} - -void -todc_m48txx_write_val(int addr, unsigned char val) -{ - outb(addr, todc_info->nvram_as0); - outb(addr>>todc_info->as0_bits, todc_info->nvram_as1); - outb(val, todc_info->nvram_data); - return; -} - -/* Access routines for accessing mc146818 type chips via addr/data regs */ -u_char -todc_mc146818_read_val(int addr) -{ - outb_p(addr, todc_info->nvram_as0); - return inb_p(todc_info->nvram_data); -} - -void -todc_mc146818_write_val(int addr, unsigned char val) -{ - outb_p(addr, todc_info->nvram_as0); - outb_p(val, todc_info->nvram_data); -} - - -/* - * Routines to make RTC chips with NVRAM buried behind an addr/data pair - * have the NVRAM and clock regs appear at the same level. - * The NVRAM will appear to start at addr 0 and the clock regs will appear - * to start immediately after the NVRAM (actually, start at offset - * todc_info->nvram_size). - */ -static inline u_char -todc_read_val(int addr) -{ - u_char val; - - if (todc_info->sw_flags & TODC_FLAG_2_LEVEL_NVRAM) { - if (addr < todc_info->nvram_size) { /* NVRAM */ - ppc_md.rtc_write_val(todc_info->nvram_addr_reg, addr); - val = ppc_md.rtc_read_val(todc_info->nvram_data_reg); - } else { /* Clock Reg */ - addr -= todc_info->nvram_size; - val = ppc_md.rtc_read_val(addr); - } - } else - val = ppc_md.rtc_read_val(addr); - - return val; -} - -static inline void -todc_write_val(int addr, u_char val) -{ - if (todc_info->sw_flags & TODC_FLAG_2_LEVEL_NVRAM) { - if (addr < todc_info->nvram_size) { /* NVRAM */ - ppc_md.rtc_write_val(todc_info->nvram_addr_reg, addr); - ppc_md.rtc_write_val(todc_info->nvram_data_reg, val); - } else { /* Clock Reg */ - addr -= todc_info->nvram_size; - ppc_md.rtc_write_val(addr, val); - } - } else - ppc_md.rtc_write_val(addr, val); -} - -/* - * TODC routines - * - * There is some ugly stuff in that there are assumptions for the mc146818. - * - * Assumptions: - * - todc_info->control_a has the offset as mc146818 Register B reg - * - todc_info->control_b has the offset as mc146818 Register A reg - * - m48txx control reg's write enable or 'W' bit is same as - * mc146818 Register B 'SET' bit (i.e., 0x80) - * - * These assumptions were made to make the code simpler. - */ -long __init -todc_time_init(void) -{ - u_char cntl_b; - - if (!ppc_md.rtc_read_val) - ppc_md.rtc_read_val = ppc_md.nvram_read_val; - if (!ppc_md.rtc_write_val) - ppc_md.rtc_write_val = ppc_md.nvram_write_val; - - cntl_b = todc_read_val(todc_info->control_b); - - if (todc_info->rtc_type == TODC_TYPE_MC146818) { - if ((cntl_b & 0x70) != 0x20) { - printk(KERN_INFO "TODC real-time-clock was stopped." - " Now starting..."); - cntl_b &= ~0x70; - cntl_b |= 0x20; - } - - todc_write_val(todc_info->control_b, cntl_b); - } else if (todc_info->rtc_type == TODC_TYPE_DS17285) { - u_char mode; - - mode = todc_read_val(TODC_TYPE_DS17285_CNTL_A); - /* Make sure countdown clear is not set */ - mode &= ~0x40; - /* Enable oscillator, extended register set */ - mode |= 0x30; - todc_write_val(TODC_TYPE_DS17285_CNTL_A, mode); - - } else if (todc_info->rtc_type == TODC_TYPE_DS1501) { - u_char month; - - todc_info->enable_read = TODC_DS1501_CNTL_B_TE; - todc_info->enable_write = TODC_DS1501_CNTL_B_TE; - - month = todc_read_val(todc_info->month); - - if ((month & 0x80) == 0x80) { - printk(KERN_INFO "TODC %s %s\n", - "real-time-clock was stopped.", - "Now starting..."); - month &= ~0x80; - todc_write_val(todc_info->month, month); - } - - cntl_b &= ~TODC_DS1501_CNTL_B_TE; - todc_write_val(todc_info->control_b, cntl_b); - } else { /* must be a m48txx type */ - u_char cntl_a; - - todc_info->enable_read = TODC_MK48TXX_CNTL_A_R; - todc_info->enable_write = TODC_MK48TXX_CNTL_A_W; - - cntl_a = todc_read_val(todc_info->control_a); - - /* Check & clear STOP bit in control B register */ - if (cntl_b & TODC_MK48TXX_DAY_CB) { - printk(KERN_INFO "TODC %s %s\n", - "real-time-clock was stopped.", - "Now starting..."); - - cntl_a |= todc_info->enable_write; - cntl_b &= ~TODC_MK48TXX_DAY_CB;/* Start Oscil */ - - todc_write_val(todc_info->control_a, cntl_a); - todc_write_val(todc_info->control_b, cntl_b); - } - - /* Make sure READ & WRITE bits are cleared. */ - cntl_a &= ~(todc_info->enable_write | todc_info->enable_read); - todc_write_val(todc_info->control_a, cntl_a); - } - - return 0; -} - -/* - * There is some ugly stuff in that there are assumptions that for a mc146818, - * the todc_info->control_a has the offset of the mc146818 Register B reg and - * that the register'ss 'SET' bit is the same as the m48txx's write enable - * bit in the control register of the m48txx (i.e., 0x80). - * - * It was done to make the code look simpler. - */ -void -todc_get_rtc_time(struct rtc_time *tm) -{ - uint year = 0, mon = 0, mday = 0, hour = 0, min = 0, sec = 0; - uint limit, i; - u_char save_control, uip = 0; - extern void GregorianDay(struct rtc_time *); - - spin_lock(&rtc_lock); - save_control = todc_read_val(todc_info->control_a); - - if (todc_info->rtc_type != TODC_TYPE_MC146818) { - limit = 1; - - switch (todc_info->rtc_type) { - case TODC_TYPE_DS1553: - case TODC_TYPE_DS1557: - case TODC_TYPE_DS1743: - case TODC_TYPE_DS1746: /* XXXX BAD HACK -> FIX */ - case TODC_TYPE_DS1747: - case TODC_TYPE_DS17285: - break; - default: - todc_write_val(todc_info->control_a, - (save_control | todc_info->enable_read)); - } - } else - limit = 100000000; - - for (i=0; irtc_type == TODC_TYPE_MC146818) - uip = todc_read_val(todc_info->RTC_FREQ_SELECT); - - sec = todc_read_val(todc_info->seconds) & 0x7f; - min = todc_read_val(todc_info->minutes) & 0x7f; - hour = todc_read_val(todc_info->hours) & 0x3f; - mday = todc_read_val(todc_info->day_of_month) & 0x3f; - mon = todc_read_val(todc_info->month) & 0x1f; - year = todc_read_val(todc_info->year) & 0xff; - - if (todc_info->rtc_type == TODC_TYPE_MC146818) { - uip |= todc_read_val(todc_info->RTC_FREQ_SELECT); - if ((uip & RTC_UIP) == 0) - break; - } - } - - if (todc_info->rtc_type != TODC_TYPE_MC146818) { - switch (todc_info->rtc_type) { - case TODC_TYPE_DS1553: - case TODC_TYPE_DS1557: - case TODC_TYPE_DS1743: - case TODC_TYPE_DS1746: /* XXXX BAD HACK -> FIX */ - case TODC_TYPE_DS1747: - case TODC_TYPE_DS17285: - break; - default: - save_control &= ~(todc_info->enable_read); - todc_write_val(todc_info->control_a, save_control); - } - } - spin_unlock(&rtc_lock); - - if ((todc_info->rtc_type != TODC_TYPE_MC146818) - || ((save_control & RTC_DM_BINARY) == 0) - || RTC_ALWAYS_BCD) { - BCD_TO_BIN(sec); - BCD_TO_BIN(min); - BCD_TO_BIN(hour); - BCD_TO_BIN(mday); - BCD_TO_BIN(mon); - BCD_TO_BIN(year); - } - - if ((year + 1900) < 1970) { - year += 100; - } - - tm->tm_sec = sec; - tm->tm_min = min; - tm->tm_hour = hour; - tm->tm_mday = mday; - tm->tm_mon = mon; - tm->tm_year = year; - - GregorianDay(tm); -} - -int -todc_set_rtc_time(struct rtc_time *tm) -{ - u_char save_control, save_freq_select = 0; - - spin_lock(&rtc_lock); - save_control = todc_read_val(todc_info->control_a); - - /* Assuming MK48T59_RTC_CA_WRITE & RTC_SET are equal */ - todc_write_val(todc_info->control_a, - (save_control | todc_info->enable_write)); - save_control &= ~(todc_info->enable_write); /* in case it was set */ - - if (todc_info->rtc_type == TODC_TYPE_MC146818) { - save_freq_select = todc_read_val(todc_info->RTC_FREQ_SELECT); - todc_write_val(todc_info->RTC_FREQ_SELECT, - save_freq_select | RTC_DIV_RESET2); - } - - if ((todc_info->rtc_type != TODC_TYPE_MC146818) - || ((save_control & RTC_DM_BINARY) == 0) - || RTC_ALWAYS_BCD) { - BIN_TO_BCD(tm->tm_sec); - BIN_TO_BCD(tm->tm_min); - BIN_TO_BCD(tm->tm_hour); - BIN_TO_BCD(tm->tm_mon); - BIN_TO_BCD(tm->tm_mday); - BIN_TO_BCD(tm->tm_year); - } - - todc_write_val(todc_info->seconds, tm->tm_sec); - todc_write_val(todc_info->minutes, tm->tm_min); - todc_write_val(todc_info->hours, tm->tm_hour); - todc_write_val(todc_info->month, tm->tm_mon); - todc_write_val(todc_info->day_of_month, tm->tm_mday); - todc_write_val(todc_info->year, tm->tm_year); - - todc_write_val(todc_info->control_a, save_control); - - if (todc_info->rtc_type == TODC_TYPE_MC146818) - todc_write_val(todc_info->RTC_FREQ_SELECT, save_freq_select); - - spin_unlock(&rtc_lock); - return 0; -} Index: linux-2.6/arch/powerpc/xmon/xmon.c =================================================================== --- linux-2.6.orig/arch/powerpc/xmon/xmon.c +++ linux-2.6/arch/powerpc/xmon/xmon.c @@ -35,6 +35,8 @@ #include #include #include +#include +#include #ifdef CONFIG_PPC64 #include @@ -145,6 +147,8 @@ static void xmon_print_symbol(unsigned l const char *after); static const char *getvecname(unsigned long vec); +static int do_spu_cmd(void); + int xmon_no_auto_backtrace; extern int print_insn_powerpc(unsigned long, unsigned long, int); @@ -207,8 +211,13 @@ Commands:\n\ mi show information about memory allocation\n\ p call a procedure\n\ r print registers\n\ - s single step\n\ - S print special registers\n\ + s single step\n" +#ifdef CONFIG_PPC_CELL +" ss stop execution on all spus\n\ + sr restore execution on stopped spus\n\ + sf # dump spu fields for spu # (in hex)\n" +#endif +" S print special registers\n\ t print backtrace\n\ x exit monitor and recover\n\ X exit monitor and dont recover\n" @@ -516,6 +525,7 @@ int xmon(struct pt_regs *excp) xmon_save_regs(®s); excp = ®s; } + return xmon_core(excp, 0); } EXPORT_SYMBOL(xmon); @@ -808,6 +818,8 @@ cmds(struct pt_regs *excp) cacheflush(); break; case 's': + if (do_spu_cmd() == 0) + break; if (do_step(excp)) return cmd; break; @@ -2630,3 +2642,194 @@ void __init xmon_setup(void) if (xmon_early) debugger(NULL); } + +#ifdef CONFIG_PPC_CELL + +struct spu_info { + struct spu *spu; + u64 saved_mfc_sr1_RW; + u32 saved_spu_runcntl_RW; + u8 stopped_ok; +}; + +#define XMON_NUM_SPUS 16 /* Enough for current hardware */ + +static struct spu_info spu_info[XMON_NUM_SPUS]; + +void xmon_register_spus(struct list_head *list) +{ + struct spu *spu; + + list_for_each_entry(spu, list, full_list) { + if (spu->number >= XMON_NUM_SPUS) { + WARN_ON(1); + continue; + } + + spu_info[spu->number].spu = spu; + spu_info[spu->number].stopped_ok = 0; + } +} + +static void stop_spus(void) +{ + struct spu *spu; + int i; + u64 tmp; + + for (i = 0; i < XMON_NUM_SPUS; i++) { + if (!spu_info[i].spu) + continue; + + if (setjmp(bus_error_jmp) == 0) { + catch_memory_errors = 1; + sync(); + + spu = spu_info[i].spu; + + spu_info[i].saved_spu_runcntl_RW = + in_be32(&spu->problem->spu_runcntl_RW); + + tmp = spu_mfc_sr1_get(spu); + spu_info[i].saved_mfc_sr1_RW = tmp; + + tmp &= ~MFC_STATE1_MASTER_RUN_CONTROL_MASK; + spu_mfc_sr1_set(spu, tmp); + + sync(); + __delay(200); + + spu_info[i].stopped_ok = 1; + printf("Stopped spu %.2d\n", i); + } else { + catch_memory_errors = 0; + printf("*** Error stopping spu %.2d\n", i); + } + catch_memory_errors = 0; + } +} + +static void restart_spus(void) +{ + struct spu *spu; + int i; + + for (i = 0; i < XMON_NUM_SPUS; i++) { + if (!spu_info[i].spu) + continue; + + if (!spu_info[i].stopped_ok) { + printf("*** Error, spu %d was not successfully stopped" + ", not restarting\n", i); + continue; + } + + if (setjmp(bus_error_jmp) == 0) { + catch_memory_errors = 1; + sync(); + + spu = spu_info[i].spu; + spu_mfc_sr1_set(spu, spu_info[i].saved_mfc_sr1_RW); + out_be32(&spu->problem->spu_runcntl_RW, + spu_info[i].saved_spu_runcntl_RW); + + sync(); + __delay(200); + + printf("Restarted spu %.2d\n", i); + } else { + catch_memory_errors = 0; + printf("*** Error restarting spu %.2d\n", i); + } + catch_memory_errors = 0; + } +} + +#define DUMP_WIDTH 23 +#define DUMP_FIELD(obj, format, field) \ +do { \ + if (setjmp(bus_error_jmp) == 0) { \ + catch_memory_errors = 1; \ + sync(); \ + printf(" %-*s = "format"\n", DUMP_WIDTH, \ + #field, obj->field); \ + sync(); \ + __delay(200); \ + } else { \ + catch_memory_errors = 0; \ + printf(" %-*s = *** Error reading field.\n", \ + DUMP_WIDTH, #field); \ + } \ + catch_memory_errors = 0; \ +} while (0) + +static void dump_spu_fields(struct spu *spu) +{ + printf("Dumping spu fields at address %p:\n", spu); + + DUMP_FIELD(spu, "0x%x", number); + DUMP_FIELD(spu, "%s", name); + DUMP_FIELD(spu, "%s", devnode->full_name); + DUMP_FIELD(spu, "0x%x", nid); + DUMP_FIELD(spu, "0x%lx", local_store_phys); + DUMP_FIELD(spu, "0x%p", local_store); + DUMP_FIELD(spu, "0x%lx", ls_size); + DUMP_FIELD(spu, "0x%x", node); + DUMP_FIELD(spu, "0x%lx", flags); + DUMP_FIELD(spu, "0x%lx", dar); + DUMP_FIELD(spu, "0x%lx", dsisr); + DUMP_FIELD(spu, "%d", class_0_pending); + DUMP_FIELD(spu, "0x%lx", irqs[0]); + DUMP_FIELD(spu, "0x%lx", irqs[1]); + DUMP_FIELD(spu, "0x%lx", irqs[2]); + DUMP_FIELD(spu, "0x%x", slb_replace); + DUMP_FIELD(spu, "%d", pid); + DUMP_FIELD(spu, "%d", prio); + DUMP_FIELD(spu, "0x%p", mm); + DUMP_FIELD(spu, "0x%p", ctx); + DUMP_FIELD(spu, "0x%p", rq); + DUMP_FIELD(spu, "0x%p", timestamp); + DUMP_FIELD(spu, "0x%lx", problem_phys); + DUMP_FIELD(spu, "0x%p", problem); + DUMP_FIELD(spu, "0x%x", problem->spu_runcntl_RW); + DUMP_FIELD(spu, "0x%x", problem->spu_status_R); + DUMP_FIELD(spu, "0x%x", problem->spu_npc_RW); + DUMP_FIELD(spu, "0x%p", priv1); + + if (spu->priv1) + DUMP_FIELD(spu, "0x%lx", priv1->mfc_sr1_RW); + + DUMP_FIELD(spu, "0x%p", priv2); +} + +static int do_spu_cmd(void) +{ + unsigned long num = 0; + int cmd; + + cmd = inchar(); + switch (cmd) { + case 's': + stop_spus(); + break; + case 'r': + restart_spus(); + break; + case 'f': + if (scanhex(&num) && num < XMON_NUM_SPUS && spu_info[num].spu) + dump_spu_fields(spu_info[num].spu); + else + printf("*** Error: invalid spu number\n"); + break; + default: + return -1; + } + + return 0; +} +#else /* ! CONFIG_PPC_CELL */ +static int do_spu_cmd(void) +{ + return -1; +} +#endif Index: linux-2.6/include/asm-powerpc/hw_irq.h =================================================================== --- linux-2.6.orig/include/asm-powerpc/hw_irq.h +++ linux-2.6/include/asm-powerpc/hw_irq.h @@ -7,16 +7,30 @@ #ifdef __KERNEL__ #include +#include #include #include extern void timer_interrupt(struct pt_regs *); -#ifdef CONFIG_PPC_ISERIES +#ifdef CONFIG_PPC64 +#include + +static inline unsigned long local_get_flags(void) +{ + return get_paca()->soft_enabled; +} + +static inline unsigned long local_irq_disable(void) +{ + unsigned long flag = get_paca()->soft_enabled; + get_paca()->soft_enabled = 0; + barrier(); + return flag; +} -extern unsigned long local_get_flags(void); -extern unsigned long local_irq_disable(void); extern void local_irq_restore(unsigned long); +extern void iseries_handle_interrupts(void); #define local_irq_enable() local_irq_restore(1) #define local_save_flags(flags) ((flags) = local_get_flags()) @@ -24,17 +38,14 @@ extern void local_irq_restore(unsigned l #define irqs_disabled() (local_get_flags() == 0) +#define hard_irq_enable() __mtmsrd(mfmsr() | MSR_EE, 1) +#define hard_irq_disable() __mtmsrd(mfmsr() & ~MSR_EE, 1) + #else #if defined(CONFIG_BOOKE) #define SET_MSR_EE(x) mtmsr(x) #define local_irq_restore(flags) __asm__ __volatile__("wrtee %0" : : "r" (flags) : "memory") -#elif defined(__powerpc64__) -#define SET_MSR_EE(x) __mtmsrd(x, 1) -#define local_irq_restore(flags) do { \ - __asm__ __volatile__("": : :"memory"); \ - __mtmsrd((flags), 1); \ -} while(0) #else #define SET_MSR_EE(x) mtmsr(x) #define local_irq_restore(flags) mtmsr(flags) @@ -81,7 +92,7 @@ static inline void local_irq_save_ptr(un #define local_irq_save(flags) local_irq_save_ptr(&flags) #define irqs_disabled() ((mfmsr() & MSR_EE) == 0) -#endif /* CONFIG_PPC_ISERIES */ +#endif /* CONFIG_PPC64 */ #define mask_irq(irq) \ ({ \ Index: linux-2.6/include/asm-powerpc/mmu.h =================================================================== --- linux-2.6.orig/include/asm-powerpc/mmu.h +++ linux-2.6/include/asm-powerpc/mmu.h @@ -248,21 +248,6 @@ extern void hpte_init_native(void); extern void hpte_init_lpar(void); extern void hpte_init_iSeries(void); -extern long pSeries_lpar_hpte_insert(unsigned long hpte_group, - unsigned long va, unsigned long prpn, - unsigned long rflags, - unsigned long vflags, int psize); - -extern long native_hpte_insert(unsigned long hpte_group, - unsigned long va, unsigned long prpn, - unsigned long rflags, - unsigned long vflags, int psize); - -extern long iSeries_hpte_insert(unsigned long hpte_group, - unsigned long va, unsigned long prpn, - unsigned long rflags, - unsigned long vflags, int psize); - extern void stabs_alloc(void); extern void slb_initialize(void); extern void slb_flush_and_rebolt(void); Index: linux-2.6/include/asm-powerpc/paca.h =================================================================== --- linux-2.6.orig/include/asm-powerpc/paca.h +++ linux-2.6/include/asm-powerpc/paca.h @@ -93,7 +93,8 @@ struct paca_struct { u64 stab_rr; /* stab/slb round-robin counter */ u64 saved_r1; /* r1 save for RTAS calls */ u64 saved_msr; /* MSR saved here by enter_rtas */ - u8 proc_enabled; /* irq soft-enable flag */ + u8 soft_enabled; /* irq soft-enable flag */ + u8 hard_enabled; /* set if irqs are enabled in MSR */ u8 io_sync; /* writel() needs spin_unlock sync */ /* Stuff for accurate time accounting */ Index: linux-2.6/include/asm-powerpc/spu.h =================================================================== --- linux-2.6.orig/include/asm-powerpc/spu.h +++ linux-2.6/include/asm-powerpc/spu.h @@ -115,10 +115,10 @@ struct spu { struct spu_priv2 __iomem *priv2; struct list_head list; struct list_head sched_list; + struct list_head full_list; int number; int nid; unsigned int irqs[3]; - u32 isrc; u32 node; u64 flags; u64 dar; @@ -144,6 +144,8 @@ struct spu { char irq_c1[8]; char irq_c2[8]; + struct device_node *devnode; + struct sys_device sysdev; }; @@ -182,8 +184,10 @@ extern struct spufs_calls { */ #define SPU_CREATE_EVENTS_ENABLED 0x0001 #define SPU_CREATE_GANG 0x0002 +#define SPU_CREATE_NOSCHED 0x0004 +#define SPU_CREATE_ISOLATE 0x0008 -#define SPU_CREATE_FLAG_ALL 0x0003 /* mask of all valid flags */ +#define SPU_CREATE_FLAG_ALL 0x000f /* mask of all valid flags */ #ifdef CONFIG_SPU_FS_MODULE @@ -199,6 +203,12 @@ static inline void unregister_spu_syscal } #endif /* MODULE */ +int spu_add_sysdev_attr(struct sysdev_attribute *attr); +void spu_remove_sysdev_attr(struct sysdev_attribute *attr); + +int spu_add_sysdev_attr_group(struct attribute_group *attrs); +void spu_remove_sysdev_attr_group(struct attribute_group *attrs); + /* * Notifier blocks: @@ -277,6 +287,7 @@ struct spu_problem { u32 spu_runcntl_RW; /* 0x401c */ #define SPU_RUNCNTL_STOP 0L #define SPU_RUNCNTL_RUNNABLE 1L +#define SPU_RUNCNTL_ISOLATE 2L u8 pad_0x4020_0x4024[0x4]; /* 0x4020 */ u32 spu_status_R; /* 0x4024 */ #define SPU_STOP_STATUS_SHIFT 16 @@ -289,8 +300,8 @@ struct spu_problem { #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 +#define SPU_STATUS_ISOLATED_LOAD_STATUS 0x200 +#define SPU_STATUS_ISOLATED_EXIT_STATUS 0x400 u8 pad_0x4028_0x402c[0x4]; /* 0x4028 */ u32 spu_spe_R; /* 0x402c */ u8 pad_0x4030_0x4034[0x4]; /* 0x4030 */ Index: linux-2.6/include/asm-powerpc/spu_csa.h =================================================================== --- linux-2.6.orig/include/asm-powerpc/spu_csa.h +++ linux-2.6/include/asm-powerpc/spu_csa.h @@ -151,7 +151,6 @@ struct spu_priv1_collapsed { 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; Index: linux-2.6/include/asm-powerpc/spu_priv1.h =================================================================== --- linux-2.6.orig/include/asm-powerpc/spu_priv1.h +++ linux-2.6/include/asm-powerpc/spu_priv1.h @@ -37,7 +37,7 @@ struct spu_priv1_ops u64 (*mfc_dar_get) (struct spu *spu); u64 (*mfc_dsisr_get) (struct spu *spu); void (*mfc_dsisr_set) (struct spu *spu, u64 dsisr); - void (*mfc_sdr_set) (struct spu *spu, u64 sdr); + void (*mfc_sdr_setup) (struct spu *spu); void (*mfc_sr1_set) (struct spu *spu, u64 sr1); u64 (*mfc_sr1_get) (struct spu *spu); void (*mfc_tclass_id_set) (struct spu *spu, u64 tclass_id); @@ -112,9 +112,9 @@ spu_mfc_dsisr_set (struct spu *spu, u64 } static inline void -spu_mfc_sdr_set (struct spu *spu, u64 sdr) +spu_mfc_sdr_setup (struct spu *spu) { - spu_priv1_ops->mfc_sdr_set(spu, sdr); + spu_priv1_ops->mfc_sdr_setup(spu); } static inline void Index: linux-2.6/include/asm-powerpc/todc.h =================================================================== --- linux-2.6.orig/include/asm-powerpc/todc.h +++ /dev/null @@ -1,487 +0,0 @@ -/* - * Definitions for the M48Txx and mc146818 series of Time of day/Real Time - * Clock chips. - * - * Author: Mark A. Greer - * - * 2001 (c) MontaVista, Software, Inc. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. - */ - -/* - * Support for the M48T37/M48T59/.../mc146818 Real Time Clock chips. - * Purpose is to make one generic file that handles all of these chips instead - * of every platform implementing the same code over & over again. - */ - -#ifndef __PPC_KERNEL_TODC_H -#define __PPC_KERNEL_TODC_H - -typedef struct { - uint rtc_type; /* your particular chip */ - - /* - * Following are the addresses of the AS0, AS1, and DATA registers - * of these chips. Note that these are board-specific. - */ - unsigned int nvram_as0; - unsigned int nvram_as1; - unsigned int nvram_data; - - /* - * Define bits to stop external set of regs from changing so - * the chip can be read/written reliably. - */ - unsigned char enable_read; - unsigned char enable_write; - - /* - * Following is the number of AS0 address bits. This is normally - * 8 but some bad hardware routes address lines incorrectly. - */ - int as0_bits; - - int nvram_size; /* Size of NVRAM on chip */ - int sw_flags; /* Software control flags */ - - /* Following are the register offsets for the particular chip */ - int year; - int month; - int day_of_month; - int day_of_week; - int hours; - int minutes; - int seconds; - int control_b; - int control_a; - int watchdog; - int interrupts; - int alarm_date; - int alarm_hour; - int alarm_minutes; - int alarm_seconds; - int century; - int flags; - - /* - * Some RTC chips have their NVRAM buried behind a addr/data pair of - * regs on the first level/clock registers. The following fields - * are the addresses for those addr/data regs. - */ - int nvram_addr_reg; - int nvram_data_reg; -} todc_info_t; - -/* - * Define the types of TODC/RTC variants that are supported in - * arch/ppc/kernel/todc_time.c - * Make a new one of these for any chip somehow differs from what's already - * defined. That way, if you ever need to put in code to touch those - * bits/registers in todc_time.c, you can put it inside an - * 'if (todc_info->rtc_type == TODC_TYPE_XXX)' so you won't break - * anyone else. - */ -#define TODC_TYPE_MK48T35 1 -#define TODC_TYPE_MK48T37 2 -#define TODC_TYPE_MK48T59 3 -#define TODC_TYPE_DS1693 4 /* Dallas DS1693 RTC */ -#define TODC_TYPE_DS1743 5 /* Dallas DS1743 RTC */ -#define TODC_TYPE_DS1746 6 /* Dallas DS1746 RTC */ -#define TODC_TYPE_DS1747 7 /* Dallas DS1747 RTC */ -#define TODC_TYPE_DS1501 8 /* Dallas DS1501 RTC */ -#define TODC_TYPE_DS1643 9 /* Dallas DS1643 RTC */ -#define TODC_TYPE_PC97307 10 /* PC97307 internal RTC */ -#define TODC_TYPE_DS1557 11 /* Dallas DS1557 RTC */ -#define TODC_TYPE_DS17285 12 /* Dallas DS17285 RTC */ -#define TODC_TYPE_DS1553 13 /* Dallas DS1553 RTC */ -#define TODC_TYPE_MC146818 100 /* Leave room for m48txx's */ - -/* - * Bit to clear/set to enable reads/writes to the chip - */ -#define TODC_MK48TXX_CNTL_A_R 0x40 -#define TODC_MK48TXX_CNTL_A_W 0x80 -#define TODC_MK48TXX_DAY_CB 0x80 - -#define TODC_DS1501_CNTL_B_TE 0x80 - -/* - * Define flag bits used by todc routines. - */ -#define TODC_FLAG_2_LEVEL_NVRAM 0x00000001 - -/* - * Define the values for the various RTC's that should to into the todc_info - * table. - * Note: The XXX_NVRAM_SIZE, XXX_NVRAM_ADDR_REG, and XXX_NVRAM_DATA_REG only - * matter if XXX_SW_FLAGS has TODC_FLAG_2_LEVEL_NVRAM set. - */ -#define TODC_TYPE_MK48T35_NVRAM_SIZE 0x7ff8 -#define TODC_TYPE_MK48T35_SW_FLAGS 0 -#define TODC_TYPE_MK48T35_YEAR 0x7fff -#define TODC_TYPE_MK48T35_MONTH 0x7ffe -#define TODC_TYPE_MK48T35_DOM 0x7ffd /* Day of Month */ -#define TODC_TYPE_MK48T35_DOW 0x7ffc /* Day of Week */ -#define TODC_TYPE_MK48T35_HOURS 0x7ffb -#define TODC_TYPE_MK48T35_MINUTES 0x7ffa -#define TODC_TYPE_MK48T35_SECONDS 0x7ff9 -#define TODC_TYPE_MK48T35_CNTL_B 0x7ff9 -#define TODC_TYPE_MK48T35_CNTL_A 0x7ff8 -#define TODC_TYPE_MK48T35_WATCHDOG 0x0000 -#define TODC_TYPE_MK48T35_INTERRUPTS 0x0000 -#define TODC_TYPE_MK48T35_ALARM_DATE 0x0000 -#define TODC_TYPE_MK48T35_ALARM_HOUR 0x0000 -#define TODC_TYPE_MK48T35_ALARM_MINUTES 0x0000 -#define TODC_TYPE_MK48T35_ALARM_SECONDS 0x0000 -#define TODC_TYPE_MK48T35_CENTURY 0x0000 -#define TODC_TYPE_MK48T35_FLAGS 0x0000 -#define TODC_TYPE_MK48T35_NVRAM_ADDR_REG 0 -#define TODC_TYPE_MK48T35_NVRAM_DATA_REG 0 - -#define TODC_TYPE_MK48T37_NVRAM_SIZE 0x7ff0 -#define TODC_TYPE_MK48T37_SW_FLAGS 0 -#define TODC_TYPE_MK48T37_YEAR 0x7fff -#define TODC_TYPE_MK48T37_MONTH 0x7ffe -#define TODC_TYPE_MK48T37_DOM 0x7ffd /* Day of Month */ -#define TODC_TYPE_MK48T37_DOW 0x7ffc /* Day of Week */ -#define TODC_TYPE_MK48T37_HOURS 0x7ffb -#define TODC_TYPE_MK48T37_MINUTES 0x7ffa -#define TODC_TYPE_MK48T37_SECONDS 0x7ff9 -#define TODC_TYPE_MK48T37_CNTL_B 0x7ff9 -#define TODC_TYPE_MK48T37_CNTL_A 0x7ff8 -#define TODC_TYPE_MK48T37_WATCHDOG 0x7ff7 -#define TODC_TYPE_MK48T37_INTERRUPTS 0x7ff6 -#define TODC_TYPE_MK48T37_ALARM_DATE 0x7ff5 -#define TODC_TYPE_MK48T37_ALARM_HOUR 0x7ff4 -#define TODC_TYPE_MK48T37_ALARM_MINUTES 0x7ff3 -#define TODC_TYPE_MK48T37_ALARM_SECONDS 0x7ff2 -#define TODC_TYPE_MK48T37_CENTURY 0x7ff1 -#define TODC_TYPE_MK48T37_FLAGS 0x7ff0 -#define TODC_TYPE_MK48T37_NVRAM_ADDR_REG 0 -#define TODC_TYPE_MK48T37_NVRAM_DATA_REG 0 - -#define TODC_TYPE_MK48T59_NVRAM_SIZE 0x1ff0 -#define TODC_TYPE_MK48T59_SW_FLAGS 0 -#define TODC_TYPE_MK48T59_YEAR 0x1fff -#define TODC_TYPE_MK48T59_MONTH 0x1ffe -#define TODC_TYPE_MK48T59_DOM 0x1ffd /* Day of Month */ -#define TODC_TYPE_MK48T59_DOW 0x1ffc /* Day of Week */ -#define TODC_TYPE_MK48T59_HOURS 0x1ffb -#define TODC_TYPE_MK48T59_MINUTES 0x1ffa -#define TODC_TYPE_MK48T59_SECONDS 0x1ff9 -#define TODC_TYPE_MK48T59_CNTL_B 0x1ff9 -#define TODC_TYPE_MK48T59_CNTL_A 0x1ff8 -#define TODC_TYPE_MK48T59_WATCHDOG 0x1fff -#define TODC_TYPE_MK48T59_INTERRUPTS 0x1fff -#define TODC_TYPE_MK48T59_ALARM_DATE 0x1fff -#define TODC_TYPE_MK48T59_ALARM_HOUR 0x1fff -#define TODC_TYPE_MK48T59_ALARM_MINUTES 0x1fff -#define TODC_TYPE_MK48T59_ALARM_SECONDS 0x1fff -#define TODC_TYPE_MK48T59_CENTURY 0x1fff -#define TODC_TYPE_MK48T59_FLAGS 0x1fff -#define TODC_TYPE_MK48T59_NVRAM_ADDR_REG 0 -#define TODC_TYPE_MK48T59_NVRAM_DATA_REG 0 - -#define TODC_TYPE_DS1501_NVRAM_SIZE 0x100 -#define TODC_TYPE_DS1501_SW_FLAGS TODC_FLAG_2_LEVEL_NVRAM -#define TODC_TYPE_DS1501_YEAR (TODC_TYPE_DS1501_NVRAM_SIZE + 0x06) -#define TODC_TYPE_DS1501_MONTH (TODC_TYPE_DS1501_NVRAM_SIZE + 0x05) -#define TODC_TYPE_DS1501_DOM (TODC_TYPE_DS1501_NVRAM_SIZE + 0x04) -#define TODC_TYPE_DS1501_DOW (TODC_TYPE_DS1501_NVRAM_SIZE + 0x03) -#define TODC_TYPE_DS1501_HOURS (TODC_TYPE_DS1501_NVRAM_SIZE + 0x02) -#define TODC_TYPE_DS1501_MINUTES (TODC_TYPE_DS1501_NVRAM_SIZE + 0x01) -#define TODC_TYPE_DS1501_SECONDS (TODC_TYPE_DS1501_NVRAM_SIZE + 0x00) -#define TODC_TYPE_DS1501_CNTL_B (TODC_TYPE_DS1501_NVRAM_SIZE + 0x0f) -#define TODC_TYPE_DS1501_CNTL_A (TODC_TYPE_DS1501_NVRAM_SIZE + 0x0f) -#define TODC_TYPE_DS1501_WATCHDOG (TODC_TYPE_DS1501_NVRAM_SIZE + 0xff) -#define TODC_TYPE_DS1501_INTERRUPTS (TODC_TYPE_DS1501_NVRAM_SIZE + 0xff) -#define TODC_TYPE_DS1501_ALARM_DATE (TODC_TYPE_DS1501_NVRAM_SIZE + 0x0b) -#define TODC_TYPE_DS1501_ALARM_HOUR (TODC_TYPE_DS1501_NVRAM_SIZE + 0x0a) -#define TODC_TYPE_DS1501_ALARM_MINUTES (TODC_TYPE_DS1501_NVRAM_SIZE + 0x09) -#define TODC_TYPE_DS1501_ALARM_SECONDS (TODC_TYPE_DS1501_NVRAM_SIZE + 0x08) -#define TODC_TYPE_DS1501_CENTURY (TODC_TYPE_DS1501_NVRAM_SIZE + 0x07) -#define TODC_TYPE_DS1501_FLAGS (TODC_TYPE_DS1501_NVRAM_SIZE + 0xff) -#define TODC_TYPE_DS1501_NVRAM_ADDR_REG 0x10 -#define TODC_TYPE_DS1501_NVRAM_DATA_REG 0x13 - -#define TODC_TYPE_DS1553_NVRAM_SIZE 0x1ff0 -#define TODC_TYPE_DS1553_SW_FLAGS 0 -#define TODC_TYPE_DS1553_YEAR 0x1fff -#define TODC_TYPE_DS1553_MONTH 0x1ffe -#define TODC_TYPE_DS1553_DOM 0x1ffd /* Day of Month */ -#define TODC_TYPE_DS1553_DOW 0x1ffc /* Day of Week */ -#define TODC_TYPE_DS1553_HOURS 0x1ffb -#define TODC_TYPE_DS1553_MINUTES 0x1ffa -#define TODC_TYPE_DS1553_SECONDS 0x1ff9 -#define TODC_TYPE_DS1553_CNTL_B 0x1ff9 -#define TODC_TYPE_DS1553_CNTL_A 0x1ff8 /* control_a R/W regs */ -#define TODC_TYPE_DS1553_WATCHDOG 0x1ff7 -#define TODC_TYPE_DS1553_INTERRUPTS 0x1ff6 -#define TODC_TYPE_DS1553_ALARM_DATE 0x1ff5 -#define TODC_TYPE_DS1553_ALARM_HOUR 0x1ff4 -#define TODC_TYPE_DS1553_ALARM_MINUTES 0x1ff3 -#define TODC_TYPE_DS1553_ALARM_SECONDS 0x1ff2 -#define TODC_TYPE_DS1553_CENTURY 0x1ff8 -#define TODC_TYPE_DS1553_FLAGS 0x1ff0 -#define TODC_TYPE_DS1553_NVRAM_ADDR_REG 0 -#define TODC_TYPE_DS1553_NVRAM_DATA_REG 0 - -#define TODC_TYPE_DS1557_NVRAM_SIZE 0x7fff0 -#define TODC_TYPE_DS1557_SW_FLAGS 0 -#define TODC_TYPE_DS1557_YEAR 0x7ffff -#define TODC_TYPE_DS1557_MONTH 0x7fffe -#define TODC_TYPE_DS1557_DOM 0x7fffd /* Day of Month */ -#define TODC_TYPE_DS1557_DOW 0x7fffc /* Day of Week */ -#define TODC_TYPE_DS1557_HOURS 0x7fffb -#define TODC_TYPE_DS1557_MINUTES 0x7fffa -#define TODC_TYPE_DS1557_SECONDS 0x7fff9 -#define TODC_TYPE_DS1557_CNTL_B 0x7fff9 -#define TODC_TYPE_DS1557_CNTL_A 0x7fff8 /* control_a R/W regs */ -#define TODC_TYPE_DS1557_WATCHDOG 0x7fff7 -#define TODC_TYPE_DS1557_INTERRUPTS 0x7fff6 -#define TODC_TYPE_DS1557_ALARM_DATE 0x7fff5 -#define TODC_TYPE_DS1557_ALARM_HOUR 0x7fff4 -#define TODC_TYPE_DS1557_ALARM_MINUTES 0x7fff3 -#define TODC_TYPE_DS1557_ALARM_SECONDS 0x7fff2 -#define TODC_TYPE_DS1557_CENTURY 0x7fff8 -#define TODC_TYPE_DS1557_FLAGS 0x7fff0 -#define TODC_TYPE_DS1557_NVRAM_ADDR_REG 0 -#define TODC_TYPE_DS1557_NVRAM_DATA_REG 0 - -#define TODC_TYPE_DS1643_NVRAM_SIZE 0x1ff8 -#define TODC_TYPE_DS1643_SW_FLAGS 0 -#define TODC_TYPE_DS1643_YEAR 0x1fff -#define TODC_TYPE_DS1643_MONTH 0x1ffe -#define TODC_TYPE_DS1643_DOM 0x1ffd /* Day of Month */ -#define TODC_TYPE_DS1643_DOW 0x1ffc /* Day of Week */ -#define TODC_TYPE_DS1643_HOURS 0x1ffb -#define TODC_TYPE_DS1643_MINUTES 0x1ffa -#define TODC_TYPE_DS1643_SECONDS 0x1ff9 -#define TODC_TYPE_DS1643_CNTL_B 0x1ff9 -#define TODC_TYPE_DS1643_CNTL_A 0x1ff8 /* control_a R/W regs */ -#define TODC_TYPE_DS1643_WATCHDOG 0x1fff -#define TODC_TYPE_DS1643_INTERRUPTS 0x1fff -#define TODC_TYPE_DS1643_ALARM_DATE 0x1fff -#define TODC_TYPE_DS1643_ALARM_HOUR 0x1fff -#define TODC_TYPE_DS1643_ALARM_MINUTES 0x1fff -#define TODC_TYPE_DS1643_ALARM_SECONDS 0x1fff -#define TODC_TYPE_DS1643_CENTURY 0x1ff8 -#define TODC_TYPE_DS1643_FLAGS 0x1fff -#define TODC_TYPE_DS1643_NVRAM_ADDR_REG 0 -#define TODC_TYPE_DS1643_NVRAM_DATA_REG 0 - -#define TODC_TYPE_DS1693_NVRAM_SIZE 0 /* Not handled yet */ -#define TODC_TYPE_DS1693_SW_FLAGS 0 -#define TODC_TYPE_DS1693_YEAR 0x09 -#define TODC_TYPE_DS1693_MONTH 0x08 -#define TODC_TYPE_DS1693_DOM 0x07 /* Day of Month */ -#define TODC_TYPE_DS1693_DOW 0x06 /* Day of Week */ -#define TODC_TYPE_DS1693_HOURS 0x04 -#define TODC_TYPE_DS1693_MINUTES 0x02 -#define TODC_TYPE_DS1693_SECONDS 0x00 -#define TODC_TYPE_DS1693_CNTL_B 0x0b -#define TODC_TYPE_DS1693_CNTL_A 0x0a -#define TODC_TYPE_DS1693_WATCHDOG 0xff -#define TODC_TYPE_DS1693_INTERRUPTS 0xff -#define TODC_TYPE_DS1693_ALARM_DATE 0x49 -#define TODC_TYPE_DS1693_ALARM_HOUR 0x05 -#define TODC_TYPE_DS1693_ALARM_MINUTES 0x03 -#define TODC_TYPE_DS1693_ALARM_SECONDS 0x01 -#define TODC_TYPE_DS1693_CENTURY 0x48 -#define TODC_TYPE_DS1693_FLAGS 0xff -#define TODC_TYPE_DS1693_NVRAM_ADDR_REG 0 -#define TODC_TYPE_DS1693_NVRAM_DATA_REG 0 - -#define TODC_TYPE_DS1743_NVRAM_SIZE 0x1ff8 -#define TODC_TYPE_DS1743_SW_FLAGS 0 -#define TODC_TYPE_DS1743_YEAR 0x1fff -#define TODC_TYPE_DS1743_MONTH 0x1ffe -#define TODC_TYPE_DS1743_DOM 0x1ffd /* Day of Month */ -#define TODC_TYPE_DS1743_DOW 0x1ffc /* Day of Week */ -#define TODC_TYPE_DS1743_HOURS 0x1ffb -#define TODC_TYPE_DS1743_MINUTES 0x1ffa -#define TODC_TYPE_DS1743_SECONDS 0x1ff9 -#define TODC_TYPE_DS1743_CNTL_B 0x1ff9 -#define TODC_TYPE_DS1743_CNTL_A 0x1ff8 /* control_a R/W regs */ -#define TODC_TYPE_DS1743_WATCHDOG 0x1fff -#define TODC_TYPE_DS1743_INTERRUPTS 0x1fff -#define TODC_TYPE_DS1743_ALARM_DATE 0x1fff -#define TODC_TYPE_DS1743_ALARM_HOUR 0x1fff -#define TODC_TYPE_DS1743_ALARM_MINUTES 0x1fff -#define TODC_TYPE_DS1743_ALARM_SECONDS 0x1fff -#define TODC_TYPE_DS1743_CENTURY 0x1ff8 -#define TODC_TYPE_DS1743_FLAGS 0x1fff -#define TODC_TYPE_DS1743_NVRAM_ADDR_REG 0 -#define TODC_TYPE_DS1743_NVRAM_DATA_REG 0 - -#define TODC_TYPE_DS1746_NVRAM_SIZE 0x1fff8 -#define TODC_TYPE_DS1746_SW_FLAGS 0 -#define TODC_TYPE_DS1746_YEAR 0x1ffff -#define TODC_TYPE_DS1746_MONTH 0x1fffe -#define TODC_TYPE_DS1746_DOM 0x1fffd /* Day of Month */ -#define TODC_TYPE_DS1746_DOW 0x1fffc /* Day of Week */ -#define TODC_TYPE_DS1746_HOURS 0x1fffb -#define TODC_TYPE_DS1746_MINUTES 0x1fffa -#define TODC_TYPE_DS1746_SECONDS 0x1fff9 -#define TODC_TYPE_DS1746_CNTL_B 0x1fff9 -#define TODC_TYPE_DS1746_CNTL_A 0x1fff8 /* control_a R/W regs */ -#define TODC_TYPE_DS1746_WATCHDOG 0x00000 -#define TODC_TYPE_DS1746_INTERRUPTS 0x00000 -#define TODC_TYPE_DS1746_ALARM_DATE 0x00000 -#define TODC_TYPE_DS1746_ALARM_HOUR 0x00000 -#define TODC_TYPE_DS1746_ALARM_MINUTES 0x00000 -#define TODC_TYPE_DS1746_ALARM_SECONDS 0x00000 -#define TODC_TYPE_DS1746_CENTURY 0x00000 -#define TODC_TYPE_DS1746_FLAGS 0x00000 -#define TODC_TYPE_DS1746_NVRAM_ADDR_REG 0 -#define TODC_TYPE_DS1746_NVRAM_DATA_REG 0 - -#define TODC_TYPE_DS1747_NVRAM_SIZE 0x7fff8 -#define TODC_TYPE_DS1747_SW_FLAGS 0 -#define TODC_TYPE_DS1747_YEAR 0x7ffff -#define TODC_TYPE_DS1747_MONTH 0x7fffe -#define TODC_TYPE_DS1747_DOM 0x7fffd /* Day of Month */ -#define TODC_TYPE_DS1747_DOW 0x7fffc /* Day of Week */ -#define TODC_TYPE_DS1747_HOURS 0x7fffb -#define TODC_TYPE_DS1747_MINUTES 0x7fffa -#define TODC_TYPE_DS1747_SECONDS 0x7fff9 -#define TODC_TYPE_DS1747_CNTL_B 0x7fff9 -#define TODC_TYPE_DS1747_CNTL_A 0x7fff8 /* control_a R/W regs */ -#define TODC_TYPE_DS1747_WATCHDOG 0x00000 -#define TODC_TYPE_DS1747_INTERRUPTS 0x00000 -#define TODC_TYPE_DS1747_ALARM_DATE 0x00000 -#define TODC_TYPE_DS1747_ALARM_HOUR 0x00000 -#define TODC_TYPE_DS1747_ALARM_MINUTES 0x00000 -#define TODC_TYPE_DS1747_ALARM_SECONDS 0x00000 -#define TODC_TYPE_DS1747_CENTURY 0x00000 -#define TODC_TYPE_DS1747_FLAGS 0x00000 -#define TODC_TYPE_DS1747_NVRAM_ADDR_REG 0 -#define TODC_TYPE_DS1747_NVRAM_DATA_REG 0 - -#define TODC_TYPE_DS17285_NVRAM_SIZE (0x1000-0x80) /* 4Kx8 NVRAM (minus RTC regs) */ -#define TODC_TYPE_DS17285_SW_FLAGS TODC_FLAG_2_LEVEL_NVRAM -#define TODC_TYPE_DS17285_SECONDS (TODC_TYPE_DS17285_NVRAM_SIZE + 0x00) -#define TODC_TYPE_DS17285_ALARM_SECONDS (TODC_TYPE_DS17285_NVRAM_SIZE + 0x01) -#define TODC_TYPE_DS17285_MINUTES (TODC_TYPE_DS17285_NVRAM_SIZE + 0x02) -#define TODC_TYPE_DS17285_ALARM_MINUTES (TODC_TYPE_DS17285_NVRAM_SIZE + 0x03) -#define TODC_TYPE_DS17285_HOURS (TODC_TYPE_DS17285_NVRAM_SIZE + 0x04) -#define TODC_TYPE_DS17285_ALARM_HOUR (TODC_TYPE_DS17285_NVRAM_SIZE + 0x05) -#define TODC_TYPE_DS17285_DOW (TODC_TYPE_DS17285_NVRAM_SIZE + 0x06) -#define TODC_TYPE_DS17285_DOM (TODC_TYPE_DS17285_NVRAM_SIZE + 0x07) -#define TODC_TYPE_DS17285_MONTH (TODC_TYPE_DS17285_NVRAM_SIZE + 0x08) -#define TODC_TYPE_DS17285_YEAR (TODC_TYPE_DS17285_NVRAM_SIZE + 0x09) -#define TODC_TYPE_DS17285_CNTL_A (TODC_TYPE_DS17285_NVRAM_SIZE + 0x0A) -#define TODC_TYPE_DS17285_CNTL_B (TODC_TYPE_DS17285_NVRAM_SIZE + 0x0B) -#define TODC_TYPE_DS17285_CNTL_C (TODC_TYPE_DS17285_NVRAM_SIZE + 0x0C) -#define TODC_TYPE_DS17285_CNTL_D (TODC_TYPE_DS17285_NVRAM_SIZE + 0x0D) -#define TODC_TYPE_DS17285_WATCHDOG 0 -#define TODC_TYPE_DS17285_INTERRUPTS 0 -#define TODC_TYPE_DS17285_ALARM_DATE 0 -#define TODC_TYPE_DS17285_CENTURY 0 -#define TODC_TYPE_DS17285_FLAGS 0 -#define TODC_TYPE_DS17285_NVRAM_ADDR_REG 0x50 -#define TODC_TYPE_DS17285_NVRAM_DATA_REG 0x53 - -#define TODC_TYPE_MC146818_NVRAM_SIZE 0 /* XXXX */ -#define TODC_TYPE_MC146818_SW_FLAGS 0 -#define TODC_TYPE_MC146818_YEAR 0x09 -#define TODC_TYPE_MC146818_MONTH 0x08 -#define TODC_TYPE_MC146818_DOM 0x07 /* Day of Month */ -#define TODC_TYPE_MC146818_DOW 0x06 /* Day of Week */ -#define TODC_TYPE_MC146818_HOURS 0x04 -#define TODC_TYPE_MC146818_MINUTES 0x02 -#define TODC_TYPE_MC146818_SECONDS 0x00 -#define TODC_TYPE_MC146818_CNTL_B 0x0a -#define TODC_TYPE_MC146818_CNTL_A 0x0b /* control_a R/W regs */ -#define TODC_TYPE_MC146818_WATCHDOG 0 -#define TODC_TYPE_MC146818_INTERRUPTS 0x0c -#define TODC_TYPE_MC146818_ALARM_DATE 0xff -#define TODC_TYPE_MC146818_ALARM_HOUR 0x05 -#define TODC_TYPE_MC146818_ALARM_MINUTES 0x03 -#define TODC_TYPE_MC146818_ALARM_SECONDS 0x01 -#define TODC_TYPE_MC146818_CENTURY 0xff -#define TODC_TYPE_MC146818_FLAGS 0xff -#define TODC_TYPE_MC146818_NVRAM_ADDR_REG 0 -#define TODC_TYPE_MC146818_NVRAM_DATA_REG 0 - -#define TODC_TYPE_PC97307_NVRAM_SIZE 0 /* No NVRAM? */ -#define TODC_TYPE_PC97307_SW_FLAGS 0 -#define TODC_TYPE_PC97307_YEAR 0x09 -#define TODC_TYPE_PC97307_MONTH 0x08 -#define TODC_TYPE_PC97307_DOM 0x07 /* Day of Month */ -#define TODC_TYPE_PC97307_DOW 0x06 /* Day of Week */ -#define TODC_TYPE_PC97307_HOURS 0x04 -#define TODC_TYPE_PC97307_MINUTES 0x02 -#define TODC_TYPE_PC97307_SECONDS 0x00 -#define TODC_TYPE_PC97307_CNTL_B 0x0a -#define TODC_TYPE_PC97307_CNTL_A 0x0b /* control_a R/W regs */ -#define TODC_TYPE_PC97307_WATCHDOG 0x0c -#define TODC_TYPE_PC97307_INTERRUPTS 0x0d -#define TODC_TYPE_PC97307_ALARM_DATE 0xff -#define TODC_TYPE_PC97307_ALARM_HOUR 0x05 -#define TODC_TYPE_PC97307_ALARM_MINUTES 0x03 -#define TODC_TYPE_PC97307_ALARM_SECONDS 0x01 -#define TODC_TYPE_PC97307_CENTURY 0xff -#define TODC_TYPE_PC97307_FLAGS 0xff -#define TODC_TYPE_PC97307_NVRAM_ADDR_REG 0 -#define TODC_TYPE_PC97307_NVRAM_DATA_REG 0 - -/* - * Define macros to allocate and init the todc_info_t table that will - * be used by the todc_time.c routines. - */ -#define TODC_ALLOC() \ - static todc_info_t todc_info_alloc; \ - todc_info_t *todc_info = &todc_info_alloc; - -#define TODC_INIT(clock_type, as0, as1, data, bits) { \ - todc_info->rtc_type = clock_type; \ - \ - todc_info->nvram_as0 = (unsigned int)(as0); \ - todc_info->nvram_as1 = (unsigned int)(as1); \ - todc_info->nvram_data = (unsigned int)(data); \ - \ - todc_info->as0_bits = (bits); \ - \ - todc_info->nvram_size = clock_type ##_NVRAM_SIZE; \ - todc_info->sw_flags = clock_type ##_SW_FLAGS; \ - \ - todc_info->year = clock_type ##_YEAR; \ - todc_info->month = clock_type ##_MONTH; \ - todc_info->day_of_month = clock_type ##_DOM; \ - todc_info->day_of_week = clock_type ##_DOW; \ - todc_info->hours = clock_type ##_HOURS; \ - todc_info->minutes = clock_type ##_MINUTES; \ - todc_info->seconds = clock_type ##_SECONDS; \ - todc_info->control_b = clock_type ##_CNTL_B; \ - todc_info->control_a = clock_type ##_CNTL_A; \ - todc_info->watchdog = clock_type ##_WATCHDOG; \ - todc_info->interrupts = clock_type ##_INTERRUPTS; \ - todc_info->alarm_date = clock_type ##_ALARM_DATE; \ - todc_info->alarm_hour = clock_type ##_ALARM_HOUR; \ - todc_info->alarm_minutes = clock_type ##_ALARM_MINUTES; \ - todc_info->alarm_seconds = clock_type ##_ALARM_SECONDS; \ - todc_info->century = clock_type ##_CENTURY; \ - todc_info->flags = clock_type ##_FLAGS; \ - \ - todc_info->nvram_addr_reg = clock_type ##_NVRAM_ADDR_REG; \ - todc_info->nvram_data_reg = clock_type ##_NVRAM_DATA_REG; \ -} - -extern todc_info_t *todc_info; - -unsigned char todc_direct_read_val(int addr); -void todc_direct_write_val(int addr, unsigned char val); -unsigned char todc_m48txx_read_val(int addr); -void todc_m48txx_write_val(int addr, unsigned char val); -unsigned char todc_mc146818_read_val(int addr); -void todc_mc146818_write_val(int addr, unsigned char val); - -long todc_time_init(void); -void todc_get_rtc_time(struct rtc_time *); -int todc_set_rtc_time(struct rtc_time *); -void todc_calibrate_decr(void); - -#endif /* __PPC_KERNEL_TODC_H */ Index: linux-2.6/include/asm-powerpc/xmon.h =================================================================== --- linux-2.6.orig/include/asm-powerpc/xmon.h +++ linux-2.6/include/asm-powerpc/xmon.h @@ -14,8 +14,10 @@ #ifdef CONFIG_XMON extern void xmon_setup(void); +extern void xmon_register_spus(struct list_head *list); #else static inline void xmon_setup(void) { }; +static inline void xmon_register_spus(struct list_head *list) { }; #endif #endif /* __KERNEL __ */ Index: linux-2.6/include/linux/cpu.h =================================================================== --- linux-2.6.orig/include/linux/cpu.h +++ linux-2.6/include/linux/cpu.h @@ -33,6 +33,14 @@ struct cpu { extern int register_cpu(struct cpu *cpu, int num); extern struct sys_device *get_cpu_sysdev(unsigned cpu); + +extern int cpu_add_sysdev_attr(struct sysdev_attribute *attr); +extern void cpu_remove_sysdev_attr(struct sysdev_attribute *attr); + +extern int cpu_add_sysdev_attr_group(struct attribute_group *attrs); +extern void cpu_remove_sysdev_attr_group(struct attribute_group *attrs); + + #ifdef CONFIG_HOTPLUG_CPU extern void unregister_cpu(struct cpu *cpu); #endif