From benh at kernel.crashing.org Mon Jan 2 13:04:44 2006 From: benh at kernel.crashing.org (Benjamin Herrenschmidt) Date: Mon, 02 Jan 2006 13:04:44 +1100 Subject: [PATCH] powerpc: more g5 overtemp problem fix Message-ID: <1136167484.24205.8.camel@localhost.localdomain> Some G5s still occasionally experience shutdowns due to overtemp conditions despite the recent fix. After analyzing logs from such machines, it appears that the overtemp code is a bit too quick at shutting the machine down when reaching the critical temperature (tmax + 8) and doesn't leave the fan enough time to actually cool it down. This happens if the temperature of a CPU suddenly rises too high in a very short period of time, or occasionally on boot (that is the CPUs are already overtemp by the time the driver loads). This patches makes the code a bit more relaxed, leaving a few seconds to the fans to do their job before kicking the machine shutown. Signed-off-by: Benjamin Herrenschmidt --- linux-work.orig/drivers/macintosh/therm_pm72.c 2005-09-26 11:48:36.000000000 +1000 +++ linux-work/drivers/macintosh/therm_pm72.c 2005-10-07 11:03:41.000000000 +1000 @@ -923,7 +923,7 @@ if (temp_combi >= ((state0->mpu.tmax + 8) << 16)) { printk(KERN_WARNING "Warning ! Temperature way above maximum (%d) !\n", temp_combi >> 16); - state0->overtemp = CPU_MAX_OVERTEMP; + state0->overtemp += CPU_MAX_OVERTEMP / 4; } else if (temp_combi > (state0->mpu.tmax << 16)) state0->overtemp++; else @@ -998,7 +998,7 @@ printk(KERN_WARNING "Warning ! CPU %d temperature way above maximum" " (%d) !\n", state->index, temp >> 16); - state->overtemp = CPU_MAX_OVERTEMP; + state->overtemp += CPU_MAX_OVERTEMP / 4; } else if (temp > (state->mpu.tmax << 16)) state->overtemp++; else @@ -1060,7 +1060,7 @@ printk(KERN_WARNING "Warning ! CPU %d temperature way above maximum" " (%d) !\n", state->index, temp >> 16); - state->overtemp = CPU_MAX_OVERTEMP; + state->overtemp = CPU_MAX_OVERTEMP / 4; } else if (temp > (state->mpu.tmax << 16)) state->overtemp++; else From miltonm at bga.com Mon Jan 2 17:51:23 2006 From: miltonm at bga.com (Milton Miller) Date: Mon, 2 Jan 2006 00:51:23 -0600 (CST) Subject: UP FP restore fastpath missing Message-ID: <200601020651.k026pNMi011016@sullivan.realtime.net> I was reading the current powerpc process.c and noticed that __switch_to !CONFIG_SMP has code to avoid talking altivec and spe exceptions but not fp exceptions. milton From maule at sgi.com Tue Jan 3 14:22:49 2006 From: maule at sgi.com (Mark Maule) Date: Mon, 2 Jan 2006 21:22:49 -0600 Subject: [PATCH 0/3] msi abstractions and support for altix In-Reply-To: <20051222205023.GK2361@parisc-linux.org> References: <20051222201651.2019.37913.96422@lnx-maule.americas.sgi.com> <20051222202259.GA4959@suse.de> <20051222202627.GI17552@sgi.com> <20051222203415.GA28240@suse.de> <20051222203824.GJ17552@sgi.com> <20051222205023.GK2361@parisc-linux.org> Message-ID: <20060103032249.GA4957@sgi.com> On Thu, Dec 22, 2005 at 01:50:23PM -0700, Matthew Wilcox wrote: > On Thu, Dec 22, 2005 at 02:38:24PM -0600, Mark Maule wrote: > > Because on ia64 IA64_FIRST_DEVICE_VECTOR and IA64_LAST_DEVICE_VECTOR > > (from which MSI FIRST_DEVICE_VECTOR/LAST_DEVICE_VECTOR are derived) are not > > constants. The are now global variables (see change to asm-ia64/hw_irq.h) > > to allow the platform to override them. Altix uses a reduced range of > > vectors for devices, and this change was necessary to make assign_irq_vector() > > to work on altix. > > To be honest, I think this is just adding a third layer of paper over > the crack in the wall. The original code assumed x86; the ia64 port > added enough emulation to make it look like x86 and now altix fixes a > couple of assumptions. I say: bleh. > > What we actually need is an interface provided by the architecture that > allocates a new irq. I have a hankering to implement MSI on PA-RISC but > haven't found the time ... Matt, Greg, et. al: Did you guys have something in mind for a vector allocation interface? It seems to me that assign_irq_vector() more or less does what we want, but what is missing is a way for the platform to prime which vectors are available to choose from. One possibly better solution would be to call something in the init_IRQ path that would set up the vector pool available to assign_irq_vector(). Any opinions on this? I would maintain that this effort should be done independently of this patchset. thanks Mark From gregkh at suse.de Tue Jan 3 17:07:19 2006 From: gregkh at suse.de (Greg KH) Date: Mon, 2 Jan 2006 22:07:19 -0800 Subject: [PATCH 0/3] msi abstractions and support for altix In-Reply-To: <20060103032249.GA4957@sgi.com> References: <20051222201651.2019.37913.96422@lnx-maule.americas.sgi.com> <20051222202259.GA4959@suse.de> <20051222202627.GI17552@sgi.com> <20051222203415.GA28240@suse.de> <20051222203824.GJ17552@sgi.com> <20051222205023.GK2361@parisc-linux.org> <20060103032249.GA4957@sgi.com> Message-ID: <20060103060719.GA1845@suse.de> On Mon, Jan 02, 2006 at 09:22:49PM -0600, Mark Maule wrote: > On Thu, Dec 22, 2005 at 01:50:23PM -0700, Matthew Wilcox wrote: > > On Thu, Dec 22, 2005 at 02:38:24PM -0600, Mark Maule wrote: > > > Because on ia64 IA64_FIRST_DEVICE_VECTOR and IA64_LAST_DEVICE_VECTOR > > > (from which MSI FIRST_DEVICE_VECTOR/LAST_DEVICE_VECTOR are derived) are not > > > constants. The are now global variables (see change to asm-ia64/hw_irq.h) > > > to allow the platform to override them. Altix uses a reduced range of > > > vectors for devices, and this change was necessary to make assign_irq_vector() > > > to work on altix. > > > > To be honest, I think this is just adding a third layer of paper over > > the crack in the wall. The original code assumed x86; the ia64 port > > added enough emulation to make it look like x86 and now altix fixes a > > couple of assumptions. I say: bleh. > > > > What we actually need is an interface provided by the architecture that > > allocates a new irq. I have a hankering to implement MSI on PA-RISC but > > haven't found the time ... > > Matt, Greg, et. al: > > Did you guys have something in mind for a vector allocation interface? It > seems to me that assign_irq_vector() more or less does what we want, > but what is missing is a way for the platform to prime which vectors > are available to choose from. > > One possibly better solution would be to call something in the init_IRQ path > that would set up the vector pool available to assign_irq_vector(). > > Any opinions on this? I would maintain that this effort should be done > independently of this patchset. Care to write a patch showing how this would work? And why would this be independant of your other changes? thanks, greg k-h From olh at suse.de Wed Jan 4 06:58:31 2006 From: olh at suse.de (Olaf Hering) Date: Tue, 3 Jan 2006 20:58:31 +0100 Subject: [PATCH 4/11] powerpc: Add CONFIG_CRASH_DUMP In-Reply-To: <20051205003942.A65C468851@ozlabs.org> References: <1133743149.268607.418162138937.qpush@concordia> <20051205003942.A65C468851@ozlabs.org> Message-ID: <20060103195831.GA2898@suse.de> On Sun, Dec 04, Michael Ellerman wrote: > This patch adds a Kconfig variable, CONFIG_CRASH_DUMP, which configures the > built kernel for use as a Kdump kernel. > > Currently "all" this involves is changing the value of KERNELBASE to 32 MB. > +++ kexec/include/asm-powerpc/page.h > @@ -37,8 +37,15 @@ > */ > #define PAGE_MASK (~((1 << PAGE_SHIFT) - 1)) > > +#ifdef CONFIG_CRASH_DUMP > +/* Kdump kernel runs at 32 MB, change at your peril. */ > +#define PHYSICAL_START 0x2000000 > +#else > +#define PHYSICAL_START 0x0 > +#endif > + > #define PAGE_OFFSET ASM_CONST(CONFIG_KERNEL_START) > -#define KERNELBASE PAGE_OFFSET > +#define KERNELBASE (PAGE_OFFSET + PHYSICAL_START) This changes the vmlinux entry point, and breaks some assumptions in yaboot. It wont load an initrd anymore, flat_vmlinux is always false. It means also that a SLES9 installation cant load a SLES10 kernel+initrd via yaboot. Fixing it now in my tree. -- short story of a lazy sysadmin: alias appserv=wotan From jschopp at austin.ibm.com Wed Jan 4 05:51:22 2006 From: jschopp at austin.ibm.com (Joel Schopp) Date: Tue, 03 Jan 2006 12:51:22 -0600 Subject: [PATCH] ppc64: htab_initialize_secondary cannot be marked __init In-Reply-To: <20051228234629.GA18479@krispykreme> References: <20051228234629.GA18479@krispykreme> Message-ID: <43BAC7AA.6010101@austin.ibm.com> > -void __init htab_initialize_secondary(void) > +void htab_initialize_secondary(void) Wouldn't __devinit be the right thing to do? From ak at suse.de Wed Jan 4 08:57:52 2006 From: ak at suse.de (Andi Kleen) Date: Tue, 3 Jan 2006 22:57:52 +0100 Subject: [ANNOUNCE] numactl 0.9 released Message-ID: <200601032257.53039.ak@suse.de> A new release of the numactl / libnuma package has been released ftp://ftp.suse.com/pub/people/ak/numa/numactl-0.9.tar.gz af9f10f1f65a88b3368c02157718aa58 numactl-0.9.tar.gz It consists of a numactl program to run other programs with a specific NUMA policy and a libnuma shared library ("NUMA API") to set NUMA policy in applications and some additional tools. The 0.8 release had some problems (in fact it didn't even build on !x86-64 without tweaks) so I did 0.9 early. I fixed all the known bugs, added some patches that were reported after releases and added some minor new features. The compile flags can be now changed with the standard CFLAGS=.. argument, no need anymore for OPT_CFLAGS (thanks Ian!) I tweaked numademo a bit and it should be more useful now. It has more stable measurements now and reports the the results in MB/s instead of the weird units used before. There is a new random test that tests the memory performance with randomized accesses to defeat any hardware prefetching. And ppc64 support should really work now (I hope at least) And numastat now knows how to wrap the display for a large number of nodes (a killer feature!) I hope to declare this numactl 1.0 soon unless any bad bugs are reported. Any feedback appreciated. -Andi Detailed changes since 0.8: - Get rid of bogus distance.o that broke compilation on !x86-64 (sorry) - Handle CFLAGS overriding without OPT_CFLAGS (Ian Wienand) - Fix up section of get/set_mempolicy (Ian Wienand) - When no NUMA available fall back to one global node instead of one node per CPU (Samuel Thibault) - Don't rely on architecture symbols for dependency generation - Use __powerpc__ to detect PPC/PPC64 - numastat: * wrap display properly with many nodes * display nodes in forward order * install manpage in `make install'. - remove bogus numamemcpy.c - numademo: * allow standalone compile, make streamlib optional * clean up output * change output unit to standard MB/s * compile with more optimization * add random pass to fool any prefetching (slow) - make numademo compileable outside source tree - use gettimeofday instead of time stamp counters in benchmarks - support valgrind in testsuite - other minor changes From galak at gate.crashing.org Wed Jan 4 09:15:21 2006 From: galak at gate.crashing.org (Kumar Gala) Date: Tue, 3 Jan 2006 16:15:21 -0600 (CST) Subject: [PATCH] powerpc: fixing compile issue with !CONFIG_PCI in legacy_serial.c Message-ID: Only build in support for ISA and PCI cases if we have enabled CONFIG_ISA and CONFIG_PCI. Additionally, isa_bridge is a global so we shouldn't use it a parameter name since it gets redefined to NULL when !CONFIG_PCI. Signed-off-by: Kumar Gala --- commit 010d770e4b04e159ce9841b4572224579b26ae22 tree a04b75607365053bf289edc0b76923bb86b06b5c parent 8e80181ef9bf122ea5053e90cf9d9d0277c6e7ab author Kumar Gala Tue, 03 Jan 2006 16:19:45 -0600 committer Kumar Gala Tue, 03 Jan 2006 16:19:45 -0600 arch/powerpc/kernel/legacy_serial.c | 12 +++++++++++- 1 files changed, 11 insertions(+), 1 deletions(-) diff --git a/arch/powerpc/kernel/legacy_serial.c b/arch/powerpc/kernel/legacy_serial.c index 59164ba..f970ace 100644 --- a/arch/powerpc/kernel/legacy_serial.c +++ b/arch/powerpc/kernel/legacy_serial.c @@ -134,8 +134,9 @@ static int __init add_legacy_soc_port(st return add_legacy_port(np, -1, UPIO_MEM, addr, addr, NO_IRQ, flags); } +#ifdef CONFIG_ISA static int __init add_legacy_isa_port(struct device_node *np, - struct device_node *isa_bridge) + struct device_node *isa_brg) { u32 *reg; char *typep; @@ -167,7 +168,9 @@ static int __init add_legacy_isa_port(st return add_legacy_port(np, index, UPIO_PORT, reg[1], taddr, NO_IRQ, UPF_BOOT_AUTOCONF); } +#endif +#ifdef CONFIG_PCI static int __init add_legacy_pci_port(struct device_node *np, struct device_node *pci_dev) { @@ -233,6 +236,7 @@ static int __init add_legacy_pci_port(st */ return add_legacy_port(np, index, iotype, base, addr, NO_IRQ, UPF_BOOT_AUTOCONF); } +#endif /* * This is called very early, as part of setup_system() or eventually @@ -272,6 +276,7 @@ void __init find_legacy_serial_ports(voi of_node_put(soc); } +#ifdef CONFIG_ISA /* First fill our array with ISA ports */ for (np = NULL; (np = of_find_node_by_type(np, "serial"));) { struct device_node *isa = of_get_parent(np); @@ -282,7 +287,9 @@ void __init find_legacy_serial_ports(voi } of_node_put(isa); } +#endif +#ifdef CONFIG_PCI /* Next, try to locate PCI ports */ for (np = NULL; (np = of_find_all_nodes(np));) { struct device_node *pci, *parent = of_get_parent(np); @@ -312,6 +319,7 @@ void __init find_legacy_serial_ports(voi legacy_serial_console = index; of_node_put(parent); } +#endif DBG("legacy_serial_console = %d\n", legacy_serial_console); @@ -375,6 +383,7 @@ static void __init fixup_port_pio(int in struct device_node *np, struct plat_serial8250_port *port) { +#ifdef CONFIG_PCI struct pci_controller *hose; DBG("fixup_port_pio(%d)\n", index); @@ -391,6 +400,7 @@ static void __init fixup_port_pio(int in index, port->iobase, port->iobase + offset); port->iobase += offset; } +#endif } static void __init fixup_port_mmio(int index, From olof at lixom.net Wed Jan 4 09:20:12 2006 From: olof at lixom.net (Olof Johansson) Date: Tue, 3 Jan 2006 16:20:12 -0600 Subject: [PATCH] ppc64: htab_initialize_secondary cannot be marked __init In-Reply-To: <43BAC7AA.6010101@austin.ibm.com> References: <20051228234629.GA18479@krispykreme> <43BAC7AA.6010101@austin.ibm.com> Message-ID: <20060103222012.GB16278@pb15.lixom.net> On Tue, Jan 03, 2006 at 12:51:22PM -0600, Joel Schopp wrote: > >-void __init htab_initialize_secondary(void) > >+void htab_initialize_secondary(void) > > Wouldn't __devinit be the right thing to do? If anything it should be __cpuinit, but noone's gone through and marked up arch/powerpc for that at all yet so just marking a single function doesn't make much sense. -Olof From olh at suse.de Wed Jan 4 10:41:52 2006 From: olh at suse.de (Olaf Hering) Date: Wed, 4 Jan 2006 00:41:52 +0100 Subject: [PATCH 4/11] powerpc: Add CONFIG_CRASH_DUMP In-Reply-To: <43BB013F.6060403@us.ibm.com> References: <1133743149.268607.418162138937.qpush@concordia> <20051205003942.A65C468851@ozlabs.org> <20060103195831.GA2898@suse.de> <43BB013F.6060403@us.ibm.com> Message-ID: <20060103234152.GA9642@suse.de> On Tue, Jan 03, Haren Myneni wrote: > Yes, it could be a problem if we do OF boot at 32MB (CONFIG_CRASH_DUMP) > since the the initrd's location is set at 36MB by the yaboot. However, > in the kdump boot, the initrd location is not at fixed location and > changed by the kexec-tool. > > But, we found an issue (Oops) when we load the second kernel in to the > crash kernel region. During the first boot, the crash kernel region is > reserved (32M - 160M for crashkernel=128M at 32M) and the initrd is at 36M. > But, after loading the initrd, free_initrd() is freed initrd region even > though it is part of crash kernel reserved region. When we load the > second kernel using kexec-tool, we are copying into unallocated memory. > Thus caused panic. Why are all these values hardcoded? Cant this be probed at runtime somehow? I hope the only fixed address is the entry point of the inital bootloader (yaboot or zImage). I will finally change yaboot this week to not use hardcoded values anymore. -- short story of a lazy sysadmin: alias appserv=wotan From maule at sgi.com Wed Jan 4 10:50:24 2006 From: maule at sgi.com (Mark Maule) Date: Tue, 3 Jan 2006 17:50:24 -0600 Subject: [PATCH 1/3] msi vector targeting abstractions In-Reply-To: <20060103223918.GB13841@esmail.cup.hp.com> References: <20051222201651.2019.37913.96422@lnx-maule.americas.sgi.com> <20051222201657.2019.69251.48815@lnx-maule.americas.sgi.com> <20060103223918.GB13841@esmail.cup.hp.com> Message-ID: <20060103235024.GC16827@sgi.com> On Tue, Jan 03, 2006 at 02:39:18PM -0800, Grant Grundler wrote: > On Thu, Dec 22, 2005 at 02:15:49PM -0600, Mark Maule wrote: > > Abstract portions of the MSI core for platforms that do not use standard > > APIC interrupt controllers. This is implemented through a new arch-specific > > msi setup routine, and a set of msi ops which can be set on a per platform > > basis. > > ... > > + > > + msi_ops->target(vector, dest_cpu, &address_hi, &address_lo); > > + > > + pci_write_config_dword(entry->dev, msi_upper_address_reg(pos), > > + address_hi); > > pci_write_config_dword(entry->dev, msi_lower_address_reg(pos), > > - address.lo_address.value); > > + address_lo); > > set_native_irq_info(irq, cpu_mask); > > break; > > } > ... > > --- /dev/null 1970-01-01 00:00:00.000000000 +0000 > > +++ msi/drivers/pci/msi-apic.c 2005-12-22 11:09:37.022232088 -0600 > ... > > +struct msi_ops msi_apic_ops = { > > + .setup = msi_setup_apic, > > + .teardown = msi_teardown_apic, > > +#ifdef CONFIG_SMP > > + .target = msi_target_apic, > > +#endif > > Mark, > msi_target_apic() initializes address_lo parameter. > Even on a UP machine, we need inialize this value. Not sure what you mean here. target is used to retarget an existing MSI vector to a different processor. In the case of apic, this appears to be accomplished by swizzling the cpu in the low 32 bits of the msi address. Nothing needs to change in the upper 32 bits. > > If target is called unconditionally, wouldn't it be better > for msi_target_apic() always be called? target is called through the msi_ops->target vector. SN does not use msi_target_apic(), it uses sn_msi_target(). Other platforms can implement target however they need to. > > It would also be good for msi_target_apic to validate the 'dest_cpu' is online. > Maybe a BUG_ON or something like that. That wasn't a check in the original code flow ... the main protection appears to be in the upper levels in irq_affinity_write_proc(). > > grant > > ps. not done looking through this...and still curious to see where > other discussion about generic vector assignment leads. From iod00d at hp.com Wed Jan 4 09:39:18 2006 From: iod00d at hp.com (Grant Grundler) Date: Tue, 3 Jan 2006 14:39:18 -0800 Subject: [PATCH 1/3] msi vector targeting abstractions In-Reply-To: <20051222201657.2019.69251.48815@lnx-maule.americas.sgi.com> References: <20051222201651.2019.37913.96422@lnx-maule.americas.sgi.com> <20051222201657.2019.69251.48815@lnx-maule.americas.sgi.com> Message-ID: <20060103223918.GB13841@esmail.cup.hp.com> On Thu, Dec 22, 2005 at 02:15:49PM -0600, Mark Maule wrote: > Abstract portions of the MSI core for platforms that do not use standard > APIC interrupt controllers. This is implemented through a new arch-specific > msi setup routine, and a set of msi ops which can be set on a per platform > basis. ... > + > + msi_ops->target(vector, dest_cpu, &address_hi, &address_lo); > + > + pci_write_config_dword(entry->dev, msi_upper_address_reg(pos), > + address_hi); > pci_write_config_dword(entry->dev, msi_lower_address_reg(pos), > - address.lo_address.value); > + address_lo); > set_native_irq_info(irq, cpu_mask); > break; > } ... > --- /dev/null 1970-01-01 00:00:00.000000000 +0000 > +++ msi/drivers/pci/msi-apic.c 2005-12-22 11:09:37.022232088 -0600 ... > +struct msi_ops msi_apic_ops = { > + .setup = msi_setup_apic, > + .teardown = msi_teardown_apic, > +#ifdef CONFIG_SMP > + .target = msi_target_apic, > +#endif Mark, msi_target_apic() initializes address_lo parameter. Even on a UP machine, we need inialize this value. If target is called unconditionally, wouldn't it be better for msi_target_apic() always be called? It would also be good for msi_target_apic to validate the 'dest_cpu' is online. Maybe a BUG_ON or something like that. grant ps. not done looking through this...and still curious to see where other discussion about generic vector assignment leads. From iod00d at hp.com Wed Jan 4 11:01:41 2006 From: iod00d at hp.com (Grant Grundler) Date: Tue, 3 Jan 2006 16:01:41 -0800 Subject: [PATCH 2/3] per-platform IA64_{FIRST, LAST}_DEVICE_VECTOR definitions In-Reply-To: <20051222201705.2019.59377.24060@lnx-maule.americas.sgi.com> References: <20051222201651.2019.37913.96422@lnx-maule.americas.sgi.com> <20051222201705.2019.59377.24060@lnx-maule.americas.sgi.com> Message-ID: <20060104000141.GC13841@esmail.cup.hp.com> On Thu, Dec 22, 2005 at 02:15:57PM -0600, Mark Maule wrote: > Abstract IA64_FIRST_DEVICE_VECTOR/IA64_LAST_DEVICE_VECTOR since SN platforms > use a subset of the IA64 range. Implement this by making the above macros > global variables which the platform can override in it setup code. ... > Index: msi/arch/ia64/sn/kernel/irq.c > =================================================================== > --- msi.orig/arch/ia64/sn/kernel/irq.c 2005-12-21 22:59:09.199823700 -0600 > +++ msi/arch/ia64/sn/kernel/irq.c 2005-12-22 14:10:01.024578027 -0600 > @@ -203,6 +203,9 @@ > int i; > irq_desc_t *base_desc = irq_desc; > > + ia64_first_device_vector = IA64_SN2_FIRST_DEVICE_VECTOR; > + ia64_last_device_vector = IA64_SN2_LAST_DEVICE_VECTOR; Shouldn't this chunk of diff go in "PATCH [2/3] altix: msi support"? (typo: that should have been "3/3" in the original mail) thanks, grant From iod00d at hp.com Wed Jan 4 11:20:47 2006 From: iod00d at hp.com (Grant Grundler) Date: Tue, 3 Jan 2006 16:20:47 -0800 Subject: [PATCH 1/3] msi vector targeting abstractions In-Reply-To: <20060103235024.GC16827@sgi.com> References: <20051222201651.2019.37913.96422@lnx-maule.americas.sgi.com> <20051222201657.2019.69251.48815@lnx-maule.americas.sgi.com> <20060103223918.GB13841@esmail.cup.hp.com> <20060103235024.GC16827@sgi.com> Message-ID: <20060104002047.GA14810@esmail.cup.hp.com> On Tue, Jan 03, 2006 at 05:50:24PM -0600, Mark Maule wrote: > > > +struct msi_ops msi_apic_ops = { > > > + .setup = msi_setup_apic, > > > + .teardown = msi_teardown_apic, > > > +#ifdef CONFIG_SMP > > > + .target = msi_target_apic, > > > +#endif > > > > Mark, > > msi_target_apic() initializes address_lo parameter. > > Even on a UP machine, we need inialize this value. > > Not sure what you mean here. target is used to retarget an existing > MSI vector to a different processor. Right - I didn't realize the caller, set_msi_affinity(), was surrounded by "#ifdef CONFIG_SMP". But set_msi_affinity() appears to be dead code. I couldn't find any calls to set_msi_affinity() in 2.6.14 or 2.6.15. Greg, you want a patch to remove that? thanks, grant From gregkh at suse.de Wed Jan 4 11:27:38 2006 From: gregkh at suse.de (Greg KH) Date: Tue, 3 Jan 2006 16:27:38 -0800 Subject: [PATCH 1/3] msi vector targeting abstractions In-Reply-To: <20060104002047.GA14810@esmail.cup.hp.com> References: <20051222201651.2019.37913.96422@lnx-maule.americas.sgi.com> <20051222201657.2019.69251.48815@lnx-maule.americas.sgi.com> <20060103223918.GB13841@esmail.cup.hp.com> <20060103235024.GC16827@sgi.com> <20060104002047.GA14810@esmail.cup.hp.com> Message-ID: <20060104002737.GA18963@suse.de> On Tue, Jan 03, 2006 at 04:20:47PM -0800, Grant Grundler wrote: > On Tue, Jan 03, 2006 at 05:50:24PM -0600, Mark Maule wrote: > > > > +struct msi_ops msi_apic_ops = { > > > > + .setup = msi_setup_apic, > > > > + .teardown = msi_teardown_apic, > > > > +#ifdef CONFIG_SMP > > > > + .target = msi_target_apic, > > > > +#endif > > > > > > Mark, > > > msi_target_apic() initializes address_lo parameter. > > > Even on a UP machine, we need inialize this value. > > > > Not sure what you mean here. target is used to retarget an existing > > MSI vector to a different processor. > > Right - I didn't realize the caller, set_msi_affinity(), was surrounded by > "#ifdef CONFIG_SMP". > > But set_msi_affinity() appears to be dead code. > I couldn't find any calls to set_msi_affinity() in 2.6.14 or 2.6.15. > Greg, you want a patch to remove that? Yes please, that would be great to have. thanks, greg k-h From haren at us.ibm.com Wed Jan 4 09:57:03 2006 From: haren at us.ibm.com (Haren Myneni) Date: Tue, 03 Jan 2006 14:57:03 -0800 Subject: [PATCH 4/11] powerpc: Add CONFIG_CRASH_DUMP In-Reply-To: <20060103195831.GA2898@suse.de> References: <1133743149.268607.418162138937.qpush@concordia> <20051205003942.A65C468851@ozlabs.org> <20060103195831.GA2898@suse.de> Message-ID: <43BB013F.6060403@us.ibm.com> Olaf Hering wrote: > On Sun, Dec 04, Michael Ellerman wrote: > > > >>This patch adds a Kconfig variable, CONFIG_CRASH_DUMP, which configures the >>built kernel for use as a Kdump kernel. >> >>Currently "all" this involves is changing the value of KERNELBASE to 32 MB. >> >> > > > >>+++ kexec/include/asm-powerpc/page.h >>@@ -37,8 +37,15 @@ >> */ >> #define PAGE_MASK (~((1 << PAGE_SHIFT) - 1)) >> >>+#ifdef CONFIG_CRASH_DUMP >>+/* Kdump kernel runs at 32 MB, change at your peril. */ >>+#define PHYSICAL_START 0x2000000 >>+#else >>+#define PHYSICAL_START 0x0 >>+#endif >>+ >> #define PAGE_OFFSET ASM_CONST(CONFIG_KERNEL_START) >>-#define KERNELBASE PAGE_OFFSET >>+#define KERNELBASE (PAGE_OFFSET + PHYSICAL_START) >> >> > >This changes the vmlinux entry point, and breaks some assumptions in >yaboot. It wont load an initrd anymore, flat_vmlinux is always false. > >It means also that a SLES9 installation cant load a SLES10 kernel+initrd via yaboot. >Fixing it now in my tree. > > > Yes, it could be a problem if we do OF boot at 32MB (CONFIG_CRASH_DUMP) since the the initrd's location is set at 36MB by the yaboot. However, in the kdump boot, the initrd location is not at fixed location and changed by the kexec-tool. But, we found an issue (Oops) when we load the second kernel in to the crash kernel region. During the first boot, the crash kernel region is reserved (32M - 160M for crashkernel=128M at 32M) and the initrd is at 36M. But, after loading the initrd, free_initrd() is freed initrd region even though it is part of crash kernel reserved region. When we load the second kernel using kexec-tool, we are copying into unallocated memory. Thus caused panic. One solution is move the initrd during prom_init. But, prom_claim is failed if try to claim outside of first memory node (> rmo_top) on my P5 machine. The other one could be move the initrd later. Since we are freeing the initrd anyway during early boot (before we load the second kernel), it will be an extra step of moving initrd (allocating memory and copy). The following patch could fix this issue - free only regions that are not part of crash region. As this issue may also exists on other archs, made changes in the init/initramfs.c. Is there any better way to fix this problem? Thanks Haren -------------- next part -------------- A non-text attachment was scrubbed... Name: initrd_kdump_fix.patch Type: text/x-patch Size: 1708 bytes Desc: not available Url : http://ozlabs.org/pipermail/linuxppc64-dev/attachments/20060103/0260d115/attachment.bin From maule at sgi.com Wed Jan 4 14:52:16 2006 From: maule at sgi.com (Mark Maule) Date: Tue, 3 Jan 2006 21:52:16 -0600 Subject: [PATCH 1/3] msi vector targeting abstractions In-Reply-To: <20060104002737.GA18963@suse.de> References: <20051222201651.2019.37913.96422@lnx-maule.americas.sgi.com> <20051222201657.2019.69251.48815@lnx-maule.americas.sgi.com> <20060103223918.GB13841@esmail.cup.hp.com> <20060103235024.GC16827@sgi.com> <20060104002047.GA14810@esmail.cup.hp.com> <20060104002737.GA18963@suse.de> Message-ID: <20060104035216.GD16827@sgi.com> On Tue, Jan 03, 2006 at 04:27:38PM -0800, Greg KH wrote: > On Tue, Jan 03, 2006 at 04:20:47PM -0800, Grant Grundler wrote: > > On Tue, Jan 03, 2006 at 05:50:24PM -0600, Mark Maule wrote: > > > > > +struct msi_ops msi_apic_ops = { > > > > > + .setup = msi_setup_apic, > > > > > + .teardown = msi_teardown_apic, > > > > > +#ifdef CONFIG_SMP > > > > > + .target = msi_target_apic, > > > > > +#endif > > > > > > > > Mark, > > > > msi_target_apic() initializes address_lo parameter. > > > > Even on a UP machine, we need inialize this value. > > > > > > Not sure what you mean here. target is used to retarget an existing > > > MSI vector to a different processor. > > > > Right - I didn't realize the caller, set_msi_affinity(), was surrounded by > > "#ifdef CONFIG_SMP". > > > > But set_msi_affinity() appears to be dead code. > > I couldn't find any calls to set_msi_affinity() in 2.6.14 or 2.6.15. > > Greg, you want a patch to remove that? > > Yes please, that would be great to have. > > thanks, > > greg k-h Is that really dead code? From msi.h: #ifdef CONFIG_SMP #define set_msi_irq_affinity set_msi_affinity #else #define set_msi_irq_affinity NULL #endif Mark From haren at us.ibm.com Wed Jan 4 16:06:31 2006 From: haren at us.ibm.com (Haren Myneni) Date: Tue, 03 Jan 2006 21:06:31 -0800 Subject: [PATCH 4/11] powerpc: Add CONFIG_CRASH_DUMP In-Reply-To: <20060103234152.GA9642@suse.de> References: <1133743149.268607.418162138937.qpush@concordia> <20051205003942.A65C468851@ozlabs.org> <20060103195831.GA2898@suse.de> <43BB013F.6060403@us.ibm.com> <20060103234152.GA9642@suse.de> Message-ID: <43BB57D7.1050004@us.ibm.com> Olaf Hering wrote: > On Tue, Jan 03, Haren Myneni wrote: > > > >>Yes, it could be a problem if we do OF boot at 32MB (CONFIG_CRASH_DUMP) >>since the the initrd's location is set at 36MB by the yaboot. However, >>in the kdump boot, the initrd location is not at fixed location and >>changed by the kexec-tool. >> >>But, we found an issue (Oops) when we load the second kernel in to the >>crash kernel region. During the first boot, the crash kernel region is >>reserved (32M - 160M for crashkernel=128M at 32M) and the initrd is at 36M. >>But, after loading the initrd, free_initrd() is freed initrd region even >>though it is part of crash kernel reserved region. When we load the >>second kernel using kexec-tool, we are copying into unallocated memory. >>Thus caused panic. >> >> > >Why are all these values hardcoded? Cant this be probed at runtime >somehow? I hope the only fixed address is the entry point of the inital >bootloader (yaboot or zImage). >I will finally change yaboot this week to not use hardcoded values >anymore. > > > > I am not sure whether yaboot can find the initrd location at runtime. If the yaboot can probe and find the crashkernel end at run time, then we might be considering 2 issues: - At present, alloc_bottom is moved after initrd_end. But it has to be before rmo_top. Hence, need changes in prom_init.c. we also need to make sure that sufficient memory is available before rmo_top for RTAS and reserve map. Otherwise prom_claim() will get failed. Right? - As you pointed out before (minor one), the existing yaboot will not work with the new kernel. Ex: SLES9's yaboot for SLES10 kernel. From rsa at us.ibm.com Wed Jan 4 17:32:17 2006 From: rsa at us.ibm.com (Ryan Arnold) Date: Wed, 04 Jan 2006 00:32:17 -0600 Subject: [RFC PATCH 1/1] fix get_ & put_ chars in hvc_vio to fix IN/OUT _BUF assumptions In-Reply-To: <858c3c666b9ac1fd8ffd01709426114e@bga.com> References: <20051217001031.456315000@localhost> <20051217002255.601962000@localhost> <858c3c666b9ac1fd8ffd01709426114e@bga.com> Message-ID: <1136356337.9010.20.camel@localhost.localdomain> Greetings Milton, et al. On Fri, 2005-12-16 at 23:02 -0600, Milton Miller wrote: > > + > > +/* > > + * This is a design shortcoming, the number '16' is a vio required > > buffer > > + * size. This should be changeable per architecture, but hvc_struct > > relies > > + * upon it and that struct is used by all hvc_console backend > > drivers. This > > + * needs to be fixed. > > + */ > > This is a bit strong. vio requires inbuf to be at least 16, and will > process upto 16 in outbound. They could be bigger, it will only cause > the hvc_driver to loop. Outbound couuld be smaller, but it would > reduce the efficency. I wrote this comment. In retrospect it is definitely too strong. None-the-less I feel the need to address a few dangerous assumptions in the hvc_vio code based upon the size of N_INBUF and N_OUTBUF. The firmware function plpar_hcall_norets(H_PUT_TERM_CHAR, ...) will not gracefully accept a 'count' parameter exceeding '16'. It will generate an H_Parameter error. The current hvconsole.c hvc_put_chars() function does not account for this and is prone to breakage should N_OUTBUF have a size greater than '16'. Furthermore, the hvc_console driver asks its back-ends to read 'count' number of characters based upon the amount of room left in the flip buffer. This value is less-than-or-equal-to the size of N_INBUF. Presently the hvc_vio driver makes an erroneous assumption that it can forward this request directly to vio firmware and ask for a particular number of bytes to be read into its recv buffer. This is not how plpar_hcall(H_GET_TERM_CHAR,...) works. You don't ask it to read a number of bytes. After is invoked it tells you how many bytes it read, up to the firmware specified maximum of 16. Should the amount requested (count) be less than the number read from firmware we'd over-write the flip buffer. We've not noticed any problems thus-far because we currently allow the tty to drain the flip buffer before it gets too full (after every 64 bytes read). Draining after 64 bytes is unnecessary considering that the flip buffer is 512 bytes so I've removed it. To fix the back-end problem I've directed the back-end drivers to return -EAGAIN if they can't satisfy the get request size-constraint. This signals the hvc_console driver front-end that it should allow the tty to clear the flip buffer to make room for a larger 'get'. This allows us to pack the flip buffer to be nearly full. As requested, I've also removed the front-end function __hvc_write_kernel() because it is no longer necessary. The following patch is against powerpc.git Ryan S. Arnold IBM Linux Technology Center Signed-off-by: "Ryan S. Arnold" -------------- next part -------------- A non-text attachment was scrubbed... Name: powerpc.git.hvc_console.patch Type: text/x-patch Size: 4325 bytes Desc: not available Url : http://ozlabs.org/pipermail/linuxppc64-dev/attachments/20060104/bd56f19f/attachment.bin From msdemlei at cl.uni-heidelberg.de Thu Jan 5 01:20:06 2006 From: msdemlei at cl.uni-heidelberg.de (Markus Demleitner) Date: Wed, 4 Jan 2006 15:20:06 +0100 Subject: Phantom pain with windfarm on diskless iMac G5 Message-ID: <20060104142006.GA8782@victor.cl.uni-heidelberg.de> Hi, I tried 2.6.15 on my diskless iMac G5 clients today, resulting in 747 emulation mode (vrooom...!). It turns out windfarm was querying the hard disk temperature sensor, which usually is mounted on the mounting bracket Apple uses. We made the mistake of removing these (from about 30 machines:-(), which in turn made windfarm_lm75_sensor.c:wf_lm75_get return ffff, which translates into about 255 degrees celsius. No wonder windfarm pumped like there's no tomorrow. I've "fixed" this by returning some fixed low temperature if I see ffff in wf_lm75_get for now, but I *guess* it would be nice to have some way to detect the absence of the sensor (and tell it from a simple failure). However, the OF device trees still list the sensor and even the hard disk itself even on the diskless machines. Even if there were a way to detect the absence of the sensor, there's still the problem that windfarm_pm81.c insists on having a hd temp sensor to work, so a fix would probably require spoiling that wonderful if (sensor_cpu_power && sensor_cpu_temp && sensor_hd_temp) in there and replacing it with something like if (sensor_cpu_power && sensor_cpu_temp && (machine_has_hd() && sensor_hd_temp)) where I have no idea how to implement machine_has_hd(). A further similar hack would spoil wf_smu_sys_fans_tick, and uglyness prevails. In short: Am I doomed to hack the kernels of my diskless clients to eternity (or retrofit the sensors)? Or is there a sane way to treat that kind of problem? Thanks, Markus From arnd at arndb.de Thu Jan 5 06:55:53 2006 From: arnd at arndb.de (Arnd Bergmann) Date: Wed, 4 Jan 2006 19:55:53 +0000 Subject: [PATCH, version 7] cell: enable pause(0) in cpu_idle In-Reply-To: <200512201314.12932.arnd@arndb.de> References: <200512171228.21578.arnd@arndb.de> <200512201314.12932.arnd@arndb.de> Message-ID: <200601041955.53577.arnd@arndb.de> This patch enables support for pause(0) power management state for the Cell Broadband Processor, which is import for power efficient operation. The pervasive infrastructure will in the future enable us to introduce more functionality specific to the Cell's pervasive unit. From: Maximino Aguilar Signed-off-by: Arnd Bergmann --- The only comment I got for version 6 was about formatting of C style comments and I fixed those up, so let's hope this is the final version of the patch. Please apply to powerpc.git. diffstat: arch/powerpc/kernel/cputable.c | 2 arch/powerpc/kernel/traps.c | 6 arch/powerpc/platforms/cell/Makefile | 2 arch/powerpc/platforms/cell/pervasive.c | 229 ++++++++++++++++++++++ arch/powerpc/platforms/cell/pervasive.h | 62 +++++ arch/powerpc/platforms/cell/setup.c | 2 arch/powerpc/platforms/pseries/ras.c | 5 arch/powerpc/platforms/pseries/ras.h | 9 arch/powerpc/platforms/pseries/setup.c | 4 include/asm-powerpc/cputable.h | 4 include/asm-powerpc/machdep.h | 2 include/asm-powerpc/reg.h | 22 +- 12 files changed, 335 insertions(+), 14 deletions(-) Arnd <>< Index: linux-2.6.15-rc/arch/powerpc/platforms/cell/Makefile =================================================================== --- linux-2.6.15-rc.orig/arch/powerpc/platforms/cell/Makefile +++ linux-2.6.15-rc/arch/powerpc/platforms/cell/Makefile @@ -1,4 +1,6 @@ obj-y += interrupt.o iommu.o setup.o spider-pic.o +obj-y += pervasive.o + obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_SPU_FS) += spufs/ spu_base.o builtin-spufs-$(CONFIG_SPU_FS) += spu_syscalls.o Index: linux-2.6.15-rc/arch/powerpc/platforms/cell/pervasive.c =================================================================== --- /dev/null +++ linux-2.6.15-rc/arch/powerpc/platforms/cell/pervasive.c @@ -0,0 +1,229 @@ +/* + * CBE Pervasive Monitor and Debug + * + * (C) Copyright IBM Corporation 2005 + * + * Authors: Maximino Aguilar (maguilar at us.ibm.com) + * Michael N. Day (mnday at 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. + */ + +#undef DEBUG + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "pervasive.h" + +static DEFINE_SPINLOCK(cbe_pervasive_lock); +struct cbe_pervasive { + struct pmd_regs __iomem *regs; + unsigned int thread; +}; + +/* can't use per_cpu from setup_arch */ +static struct cbe_pervasive cbe_pervasive[NR_CPUS]; + +static void __init cbe_enable_pause_zero(void) +{ + unsigned long thread_switch_control; + unsigned long temp_register; + struct cbe_pervasive *p; + int thread; + + spin_lock_irq(&cbe_pervasive_lock); + p = &cbe_pervasive[smp_processor_id()]; + + if (!cbe_pervasive->regs) + goto out; + + pr_debug("Power Management: CPU %d\n", smp_processor_id()); + + /* Enable Pause(0) control bit */ + temp_register = in_be64(&p->regs->pm_control); + + out_be64(&p->regs->pm_control, + temp_register|PMD_PAUSE_ZERO_CONTROL); + + /* 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)) { + case CTRL_CT0: + thread_switch_control |= TSC_CELL_DEC_ENABLE_0; + thread = 0; + break; + case CTRL_CT1: + thread_switch_control |= TSC_CELL_DEC_ENABLE_1; + thread = 1; + break; + default: + printk(KERN_WARNING "%s: unknown configuration\n", + __FUNCTION__); + thread = -1; + break; + } + + if (p->thread != thread) + printk(KERN_WARNING "%s: device tree inconsistant, " + "cpu %i: %d/%d\n", __FUNCTION__, + smp_processor_id(), + p->thread, thread); + + mtspr(SPRN_TSC_CELL, thread_switch_control); + +out: + spin_unlock_irq(&cbe_pervasive_lock); +} + +static void cbe_idle(void) +{ + unsigned long ctrl; + + cbe_enable_pause_zero(); + + 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(); + } +} + +int cbe_system_reset_exception(struct pt_regs *regs) +{ + switch (regs->msr & SRR1_WAKEMASK) { + case SRR1_WAKEEE: + do_IRQ(regs); + break; + case SRR1_WAKEDEC: + timer_interrupt(regs); + break; + case SRR1_WAKEMT: + /* no action required */ + break; + default: + /* do system reset */ + return 0; + } + /* everything handled */ + return 1; +} + +static int __init cbe_find_pmd_mmio(int cpu, struct cbe_pervasive *p) +{ + struct device_node *node; + unsigned int *int_servers; + char *addr; + unsigned long real_address; + unsigned int size; + + struct pmd_regs __iomem *pmd_mmio_area; + int hardid, thread; + int proplen; + + pmd_mmio_area = NULL; + hardid = get_hard_smp_processor_id(cpu); + for (node = NULL; (node = of_find_node_by_type(node, "cpu"));) { + int_servers = (void *) get_property(node, + "ibm,ppc-interrupt-server#s", &proplen); + if (!int_servers) { + printk(KERN_WARNING "%s misses " + "ibm,ppc-interrupt-server#s property", + node->full_name); + continue; + } + for (thread = 0; thread < proplen / sizeof (int); thread++) { + if (hardid == int_servers[thread]) { + addr = get_property(node, "pervasive", NULL); + goto found; + } + } + } + + printk(KERN_WARNING "%s: CPU %d not found\n", __FUNCTION__, cpu); + return -EINVAL; + +found: + real_address = *(unsigned long*) addr; + addr += sizeof (unsigned long); + size = *(unsigned int*) addr; + + pr_debug("pervasive area for CPU %d at %lx, size %x\n", + cpu, real_address, size); + p->regs = __ioremap(real_address, size, _PAGE_NO_CACHE); + p->thread = thread; + return 0; +} + +void __init cell_pervasive_init(void) +{ + struct cbe_pervasive *p; + int cpu; + int ret; + + if (!cpu_has_feature(CPU_FTR_PAUSE_ZERO)) + return; + + for_each_cpu(cpu) { + p = &cbe_pervasive[cpu]; + ret = cbe_find_pmd_mmio(cpu, p); + if (ret) + return; + } + + ppc_md.idle_loop = cbe_idle; + ppc_md.system_reset_exception = cbe_system_reset_exception; +} Index: linux-2.6.15-rc/arch/powerpc/platforms/cell/pervasive.h =================================================================== --- /dev/null +++ linux-2.6.15-rc/arch/powerpc/platforms/cell/pervasive.h @@ -0,0 +1,62 @@ +/* + * Cell Pervasive Monitor and Debug interface and HW structures + * + * (C) Copyright IBM Corporation 2005 + * + * Authors: Maximino Aguilar (maguilar at us.ibm.com) + * David J. Erb (djerb at 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 PERVASIVE_H +#define PERVASIVE_H + +struct pmd_regs { + u8 pad_0x0000_0x0800[0x0800 - 0x0000]; /* 0x0000 */ + + /* 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 */ + + /* Power Management */ + u64 pm_control; /* 0x0880 */ +#define PMD_PAUSE_ZERO_CONTROL 0x10000 + u64 pm_status; /* 0x0888 */ + + /* Time Base Register */ + u64 tbr; /* 0x0890 */ + + u8 pad_0x0898_0x1000 [0x1000 - 0x0898]; /* 0x0898 */ +}; + +void __init cell_pervasive_init(void); + +#endif Index: linux-2.6.15-rc/arch/powerpc/platforms/cell/setup.c =================================================================== --- linux-2.6.15-rc.orig/arch/powerpc/platforms/cell/setup.c +++ linux-2.6.15-rc/arch/powerpc/platforms/cell/setup.c @@ -49,6 +49,7 @@ #include "interrupt.h" #include "iommu.h" +#include "pervasive.h" #ifdef DEBUG #define DBG(fmt...) udbg_printf(fmt) @@ -165,6 +166,7 @@ static void __init cell_setup_arch(void) init_pci_config_tokens(); find_and_init_phbs(); spider_init_IRQ(); + cell_pervasive_init(); #ifdef CONFIG_DUMMY_CONSOLE conswitchp = &dummy_con; #endif Index: linux-2.6.15-rc/include/asm-powerpc/cputable.h =================================================================== --- linux-2.6.15-rc.orig/include/asm-powerpc/cputable.h +++ linux-2.6.15-rc/include/asm-powerpc/cputable.h @@ -105,6 +105,7 @@ extern void do_cpu_ftr_fixups(unsigned l #define CPU_FTR_LOCKLESS_TLBIE ASM_CONST(0x0000040000000000) #define CPU_FTR_MMCRA_SIHV ASM_CONST(0x0000080000000000) #define CPU_FTR_CI_LARGE_PAGE ASM_CONST(0x0000100000000000) +#define CPU_FTR_PAUSE_ZERO ASM_CONST(0x0000200000000000) #else /* ensure on 32b processors the flags are available for compiling but * don't do anything */ @@ -304,7 +305,8 @@ enum { CPU_FTR_MMCRA_SIHV, CPU_FTRS_CELL = CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | CPU_FTR_HPTE_TABLE | CPU_FTR_PPCAS_ARCH_V2 | - CPU_FTR_ALTIVEC_COMP | CPU_FTR_MMCRA | CPU_FTR_SMT, + CPU_FTR_ALTIVEC_COMP | CPU_FTR_MMCRA | CPU_FTR_SMT | + CPU_FTR_CTRL | CPU_FTR_PAUSE_ZERO, CPU_FTRS_COMPATIBLE = CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | CPU_FTR_HPTE_TABLE | CPU_FTR_PPCAS_ARCH_V2, #endif Index: linux-2.6.15-rc/include/asm-powerpc/reg.h =================================================================== --- linux-2.6.15-rc.orig/include/asm-powerpc/reg.h +++ linux-2.6.15-rc/include/asm-powerpc/reg.h @@ -145,6 +145,10 @@ #define SPRN_CTR 0x009 /* Count Register */ #define SPRN_CTRLF 0x088 #define SPRN_CTRLT 0x098 +#define CTRL_CT 0xc0000000 /* current thread */ +#define CTRL_CT0 0x80000000 /* thread 0 */ +#define CTRL_CT1 0x40000000 /* thread 1 */ +#define CTRL_TE 0x00c00000 /* thread enable */ #define CTRL_RUNLATCH 0x1 #define SPRN_DABR 0x3F5 /* Data Address Breakpoint Register */ #define DABR_TRANSLATION (1UL << 2) @@ -257,11 +261,11 @@ #define SPRN_HID6 0x3F9 /* BE HID 6 */ #define HID6_LB (0x0F<<12) /* Concurrent Large Page Modes */ #define HID6_DLP (1<<20) /* Disable all large page modes (4K only) */ -#define SPRN_TSCR 0x399 /* Thread switch control on BE */ -#define SPRN_TTR 0x39A /* Thread switch timeout on BE */ -#define TSCR_DEC_ENABLE 0x200000 /* Decrementer Interrupt */ -#define TSCR_EE_ENABLE 0x100000 /* External Interrupt */ -#define TSCR_EE_BOOST 0x080000 /* External Interrupt Boost */ +#define SPRN_TSC_CELL 0x399 /* Thread switch control on Cell */ +#define TSC_CELL_DEC_ENABLE_0 0x400000 /* Decrementer Interrupt */ +#define TSC_CELL_DEC_ENABLE_1 0x200000 /* Decrementer Interrupt */ +#define TSC_CELL_EE_ENABLE 0x100000 /* External Interrupt */ +#define TSC_CELL_EE_BOOST 0x080000 /* External Interrupt Boost */ #define SPRN_TSC 0x3FD /* Thread switch control on others */ #define SPRN_TST 0x3FC /* Thread switch timeout on others */ #if !defined(SPRN_IAC1) && !defined(SPRN_IAC2) @@ -375,6 +379,14 @@ #define SPRN_SPRG7 0x117 /* Special Purpose Register General 7 */ #define SPRN_SRR0 0x01A /* Save/Restore Register 0 */ #define SPRN_SRR1 0x01B /* Save/Restore Register 1 */ +#define SRR1_WAKEMASK 0x00380000 /* reason for wakeup */ +#define SRR1_WAKERESET 0x00380000 /* System reset */ +#define SRR1_WAKESYSERR 0x00300000 /* System error */ +#define SRR1_WAKEEE 0x00200000 /* External interrupt */ +#define SRR1_WAKEMT 0x00280000 /* mtctrl */ +#define SRR1_WAKEDEC 0x00180000 /* Decrementer interrupt */ +#define SRR1_WAKETHERM 0x00100000 /* Thermal management interrupt */ + #ifndef SPRN_SVR #define SPRN_SVR 0x11E /* System Version Register */ #endif Index: linux-2.6.15-rc/arch/powerpc/kernel/cputable.c =================================================================== --- linux-2.6.15-rc.orig/arch/powerpc/kernel/cputable.c +++ linux-2.6.15-rc/arch/powerpc/kernel/cputable.c @@ -273,7 +273,7 @@ struct cpu_spec cpu_specs[] = { .oprofile_model = &op_model_power4, #endif }, - { /* BE DD1.x */ + { /* Cell Broadband Engine */ .pvr_mask = 0xffff0000, .pvr_value = 0x00700000, .cpu_name = "Cell Broadband Engine", Index: linux-2.6.15-rc/arch/powerpc/kernel/traps.c =================================================================== --- linux-2.6.15-rc.orig/arch/powerpc/kernel/traps.c +++ linux-2.6.15-rc/arch/powerpc/kernel/traps.c @@ -230,8 +230,10 @@ void _exception(int signr, struct pt_reg void system_reset_exception(struct pt_regs *regs) { /* See if any machine dependent calls */ - if (ppc_md.system_reset_exception) - ppc_md.system_reset_exception(regs); + if (ppc_md.system_reset_exception) { + if (ppc_md.system_reset_exception(regs)) + return; + } die("System Reset", regs, SIGABRT); Index: linux-2.6.15-rc/arch/powerpc/platforms/pseries/ras.h =================================================================== --- /dev/null +++ linux-2.6.15-rc/arch/powerpc/platforms/pseries/ras.h @@ -0,0 +1,9 @@ +#ifndef _PSERIES_RAS_H +#define _PSERIES_RAS_H + +struct pt_regs; + +extern int pSeries_system_reset_exception(struct pt_regs *regs); +extern int pSeries_machine_check_exception(struct pt_regs *regs); + +#endif /* _PSERIES_RAS_H */ Index: linux-2.6.15-rc/arch/powerpc/platforms/pseries/setup.c =================================================================== --- linux-2.6.15-rc.orig/arch/powerpc/platforms/pseries/setup.c +++ linux-2.6.15-rc/arch/powerpc/platforms/pseries/setup.c @@ -69,6 +69,7 @@ #include #include "plpar_wrappers.h" +#include "ras.h" #ifdef DEBUG #define DBG(fmt...) udbg_printf(fmt) @@ -80,9 +81,6 @@ extern void find_udbg_vterm(void); int fwnmi_active; /* TRUE if an FWNMI handler is present */ -extern void pSeries_system_reset_exception(struct pt_regs *regs); -extern int pSeries_machine_check_exception(struct pt_regs *regs); - static void pseries_shared_idle(void); static void pseries_dedicated_idle(void); Index: linux-2.6.15-rc/arch/powerpc/platforms/pseries/ras.c =================================================================== --- linux-2.6.15-rc.orig/arch/powerpc/platforms/pseries/ras.c +++ linux-2.6.15-rc/arch/powerpc/platforms/pseries/ras.c @@ -51,6 +51,8 @@ #include #include +#include "ras.h" + static unsigned char ras_log_buf[RTAS_ERROR_LOG_MAX]; static DEFINE_SPINLOCK(ras_log_buf_lock); @@ -278,7 +280,7 @@ static void fwnmi_release_errinfo(void) printk("FWNMI: nmi-interlock failed: %d\n", ret); } -void pSeries_system_reset_exception(struct pt_regs *regs) +int pSeries_system_reset_exception(struct pt_regs *regs) { if (fwnmi_active) { struct rtas_error_log *errhdr = fwnmi_get_errinfo(regs); @@ -287,6 +289,7 @@ void pSeries_system_reset_exception(stru } fwnmi_release_errinfo(); } + return 0; /* need to perform reset */ } /* Index: linux-2.6.15-rc/include/asm-powerpc/machdep.h =================================================================== --- linux-2.6.15-rc.orig/include/asm-powerpc/machdep.h +++ linux-2.6.15-rc/include/asm-powerpc/machdep.h @@ -134,7 +134,7 @@ struct machdep_calls { void (*nvram_sync)(void); /* Exception handlers */ - void (*system_reset_exception)(struct pt_regs *regs); + int (*system_reset_exception)(struct pt_regs *regs); int (*machine_check_exception)(struct pt_regs *regs); /* Motherboard/chipset features. This is a kind of general purpose From arnd at arndb.de Thu Jan 5 06:31:24 2006 From: arnd at arndb.de (Arnd Bergmann) Date: Wed, 04 Jan 2006 20:31:24 +0100 Subject: [PATCH 04/13] spufs: serialize sys_spu_run per spu References: <20060104193120.050539000@localhost> Message-ID: <20060104194500.696404000@localhost> An embedded and charset-unspecified text was scrubbed... Name: spufs-serialize-spu-run.diff Url: http://ozlabs.org/pipermail/linuxppc64-dev/attachments/20060104/33e07b1e/attachment.txt From arnd at arndb.de Thu Jan 5 06:31:22 2006 From: arnd at arndb.de (Arnd Bergmann) Date: Wed, 04 Jan 2006 20:31:22 +0100 Subject: [PATCH 02/13] spufs: dont hold root->isem in spu_forget References: <20060104193120.050539000@localhost> Message-ID: <20060104194500.352612000@localhost> An embedded and charset-unspecified text was scrubbed... Name: spufs-final-iput.diff Url: http://ozlabs.org/pipermail/linuxppc64-dev/attachments/20060104/9d59f488/attachment.txt From arnd at arndb.de Thu Jan 5 06:31:21 2006 From: arnd at arndb.de (Arnd Bergmann) Date: Wed, 04 Jan 2006 20:31:21 +0100 Subject: [PATCH 01/13] spufs: fix locking in spu_acquire_runnable References: <20060104193120.050539000@localhost> Message-ID: <20060104194500.180477000@localhost> An embedded and charset-unspecified text was scrubbed... Name: spufs-lock.diff Url: http://ozlabs.org/pipermail/linuxppc64-dev/attachments/20060104/9c37eded/attachment.txt From arnd at arndb.de Thu Jan 5 06:31:28 2006 From: arnd at arndb.de (Arnd Bergmann) Date: Wed, 04 Jan 2006 20:31:28 +0100 Subject: [PATCH 08/13] spufs: clean up use of bitops References: <20060104193120.050539000@localhost> Message-ID: <20060104194501.381895000@localhost> An embedded and charset-unspecified text was scrubbed... Name: spufs-test-bit-cleanup.diff Url: http://ozlabs.org/pipermail/linuxppc64-dev/attachments/20060104/c6c77042/attachment.txt From arnd at arndb.de Thu Jan 5 06:31:27 2006 From: arnd at arndb.de (Arnd Bergmann) Date: Wed, 04 Jan 2006 20:31:27 +0100 Subject: [PATCH 07/13] spufs: fix spufs_fill_dir error path References: <20060104193120.050539000@localhost> Message-ID: <20060104194501.210484000@localhost> An embedded and charset-unspecified text was scrubbed... Name: spufs-fill-dir-leak.diff Url: http://ozlabs.org/pipermail/linuxppc64-dev/attachments/20060104/18b6b5d0/attachment.txt From arnd at arndb.de Thu Jan 5 06:31:26 2006 From: arnd at arndb.de (Arnd Bergmann) Date: Wed, 04 Jan 2006 20:31:26 +0100 Subject: [PATCH 06/13] spufs: dont leak directories in failed spu_create References: <20060104193120.050539000@localhost> Message-ID: <20060104194501.041011000@localhost> An embedded and charset-unspecified text was scrubbed... Name: spufs-create-fix-leak.diff Url: http://ozlabs.org/pipermail/linuxppc64-dev/attachments/20060104/251ae601/attachment.txt From arnd at arndb.de Thu Jan 5 06:31:30 2006 From: arnd at arndb.de (Arnd Bergmann) Date: Wed, 04 Jan 2006 20:31:30 +0100 Subject: [PATCH 10/13] spufs: abstract priv1 register access. References: <20060104193120.050539000@localhost> Message-ID: <20060104194501.737741000@localhost> An embedded and charset-unspecified text was scrubbed... Name: spufs-priv1-hvcall.diff Url: http://ozlabs.org/pipermail/linuxppc64-dev/attachments/20060104/6115452f/attachment.txt From arnd at arndb.de Thu Jan 5 06:31:23 2006 From: arnd at arndb.de (Arnd Bergmann) Date: Wed, 04 Jan 2006 20:31:23 +0100 Subject: [PATCH 03/13] spufs: check for proper file pointer in sys_spu_run References: <20060104193120.050539000@localhost> Message-ID: <20060104194500.522025000@localhost> An embedded and charset-unspecified text was scrubbed... Name: spufs-run-check-fd.diff Url: http://ozlabs.org/pipermail/linuxppc64-dev/attachments/20060104/cbc9a713/attachment.txt From arnd at arndb.de Thu Jan 5 06:31:29 2006 From: arnd at arndb.de (Arnd Bergmann) Date: Wed, 04 Jan 2006 20:31:29 +0100 Subject: [PATCH 09/13] spufs: move spu_run call to its own file References: <20060104193120.050539000@localhost> Message-ID: <20060104194501.555900000@localhost> An embedded and charset-unspecified text was scrubbed... Name: spufs-run-c-2.diff Url: http://ozlabs.org/pipermail/linuxppc64-dev/attachments/20060104/4dbafa57/attachment.txt From arnd at arndb.de Thu Jan 5 06:31:25 2006 From: arnd at arndb.de (Arnd Bergmann) Date: Wed, 04 Jan 2006 20:31:25 +0100 Subject: [PATCH 05/13] spufs fix spu_acquire_runnable error path References: <20060104193120.050539000@localhost> Message-ID: <20060104194500.870361000@localhost> An embedded and charset-unspecified text was scrubbed... Name: spufs-aquire-runnable-fix.diff Url: http://ozlabs.org/pipermail/linuxppc64-dev/attachments/20060104/adb56348/attachment.txt From arnd at arndb.de Thu Jan 5 06:31:32 2006 From: arnd at arndb.de (Arnd Bergmann) Date: Wed, 04 Jan 2006 20:31:32 +0100 Subject: [PATCH 12/13] spufs: fix allocation on 64k pages References: <20060104193120.050539000@localhost> Message-ID: <20060104194502.080544000@localhost> An embedded and charset-unspecified text was scrubbed... Name: spufs-64k-page.diff Url: http://ozlabs.org/pipermail/linuxppc64-dev/attachments/20060104/6557d08c/attachment.txt From arnd at arndb.de Thu Jan 5 06:31:33 2006 From: arnd at arndb.de (Arnd Bergmann) Date: Wed, 04 Jan 2006 20:31:33 +0100 Subject: [PATCH 13/13] spufs: set irq affinity for running threads References: <20060104193120.050539000@localhost> Message-ID: <20060104194502.253418000@localhost> An embedded and charset-unspecified text was scrubbed... Name: spu-irq-affinity.diff Url: http://ozlabs.org/pipermail/linuxppc64-dev/attachments/20060104/5d14f103/attachment.txt From arnd at arndb.de Thu Jan 5 06:31:20 2006 From: arnd at arndb.de (Arnd Bergmann) Date: Wed, 04 Jan 2006 20:31:20 +0100 Subject: [PATCH 00/13] spufs fixes and cleanups Message-ID: <20060104193120.050539000@localhost> In a review almost a month ago, Al Viro found numerous problems in the current spufs code. I now finally found some time to go through those and attempt to fix them. There are also a few other changes in this series that should also help, in particular improved interrupt sending and an abstraction for priviledged register access (as suggested by Masato Noguchi and Geoff Levand). Please apply to powerpc.git before sending spufs upstream. Al, could you have a look over this to see if it addresses all the concerns you had and if I broke it in new ways? Arnd <>< arch/powerpc/platforms/cell/Makefile | 5 arch/powerpc/platforms/cell/interrupt.c | 42 ++-- arch/powerpc/platforms/cell/interrupt.h | 1 arch/powerpc/platforms/cell/spu_base.c | 67 +++---- arch/powerpc/platforms/cell/spu_priv1.c | 133 ++++++++++++++ arch/powerpc/platforms/cell/spufs/Makefile | 2 arch/powerpc/platforms/cell/spufs/file.c | 167 +---------------- arch/powerpc/platforms/cell/spufs/hw_ops.c | 19 -- arch/powerpc/platforms/cell/spufs/inode.c | 156 ++++++++------- arch/powerpc/platforms/cell/spufs/run.c | 131 ++++++++++++++ arch/powerpc/platforms/cell/spufs/sched.c | 13 + arch/powerpc/platforms/cell/spufs/spufs.h | 35 +++ arch/powerpc/platforms/cell/spufs/switch.c | 139 +++++---------- arch/powerpc/platforms/cell/spufs/syscalls.c | 5 arch/powerpc/platforms/cell/spufs/context.c | 11 - include/asm-powerpc/spu.h | 42 +++- 20 files changed, 565 insertions(+), 407 deletions(-) From arnd at arndb.de Thu Jan 5 06:31:31 2006 From: arnd at arndb.de (Arnd Bergmann) Date: Wed, 04 Jan 2006 20:31:31 +0100 Subject: [PATCH 11/13] spufs: fix sparse warnings References: <20060104193120.050539000@localhost> Message-ID: <20060104194501.915556000@localhost> An embedded and charset-unspecified text was scrubbed... Name: spufs-sparse-fixes.diff Url: http://ozlabs.org/pipermail/linuxppc64-dev/attachments/20060104/fcedcd1f/attachment.txt From benh at kernel.crashing.org Thu Jan 5 10:41:29 2006 From: benh at kernel.crashing.org (Benjamin Herrenschmidt) Date: Thu, 05 Jan 2006 10:41:29 +1100 Subject: Phantom pain with windfarm on diskless iMac G5 In-Reply-To: <20060104142006.GA8782@victor.cl.uni-heidelberg.de> References: <20060104142006.GA8782@victor.cl.uni-heidelberg.de> Message-ID: <1136418090.4840.21.camel@localhost.localdomain> On Wed, 2006-01-04 at 15:20 +0100, Markus Demleitner wrote: > Hi, > > I tried 2.6.15 on my diskless iMac G5 clients today, resulting in > 747 emulation mode (vrooom...!). It turns out windfarm was > querying the hard disk temperature sensor, which usually is mounted > on the mounting bracket Apple uses. We made the mistake of removing > these (from about 30 machines:-(), which in turn made > windfarm_lm75_sensor.c:wf_lm75_get return ffff, which translates into > about 255 degrees celsius. No wonder windfarm pumped like there's no > tomorrow. > > I've "fixed" this by returning some fixed low temperature if I see > ffff in wf_lm75_get for now, but I *guess* it would be nice to have > some way to detect the absence of the sensor (and tell it from a > simple failure). However, the OF device trees still list the sensor > and even the hard disk itself even on the diskless machines. The problem is to differenciate between a diskless machine and a defective sensor. In the later case, you _want_ to pump the fans. > Even if there were a way to detect the absence of the sensor, there's > still the problem that windfarm_pm81.c insists on having a hd temp > sensor to work, so a fix would probably require spoiling that > wonderful > if (sensor_cpu_power && sensor_cpu_temp && sensor_hd_temp) > in there and replacing it with something like > if (sensor_cpu_power && sensor_cpu_temp && (machine_has_hd() > && sensor_hd_temp)) > where I have no idea how to implement machine_has_hd(). A further > similar hack would spoil wf_smu_sys_fans_tick, and uglyness prevails. > > In short: Am I doomed to hack the kernels of my diskless clients to > eternity (or retrofit the sensors)? Or is there a sane way to treat > that kind of problem? Hrm... That isn't trivial as I don't see a clean way to detect that the HD is not there from windfarm without doing gross hacks, unless we can somewhat rely on the device-tree there... What we could do is: - Make pm81 start the control loops regardless of the presence of the sensor, and have the control loop itself set the disk fan to an arbitrary low value if the sensor is not there. If the sensor kicks in "later" (because lm75 loads later), it will automatically start using the full control loop. That is easy. - In lm75 itself, in case of failure, add a little hack that tests if the disk is present by looking in the device-tree, provided again that there is a node for it that can be detected... If not, then return an arbitrarily low temperature instead of a failure. Either that or a module/kernel command line option... The later is easier but less "neat" :) Ben. From ntl at pobox.com Thu Jan 5 15:42:27 2006 From: ntl at pobox.com (Nathan Lynch) Date: Wed, 4 Jan 2006 22:42:27 -0600 Subject: [PATCH 13/13] spufs: set irq affinity for running threads In-Reply-To: <20060104194502.253418000@localhost> References: <20060104193120.050539000@localhost> <20060104194502.253418000@localhost> Message-ID: <20060105044227.GD16729@localhost.localdomain> Arnd Bergmann wrote: > For far, all SPU triggered interrupts always end up on > the first SMT thread, which is a bad solution. > > This patch implements setting the affinity to the > CPU that was running last when entering execution on > an SPU. This should result in a significant reduction > in IPI calls and better cache locality for SPE thread > specific data. ... > --- linux-2.6.15-rc.orig/arch/powerpc/platforms/cell/spufs/sched.c > +++ linux-2.6.15-rc/arch/powerpc/platforms/cell/spufs/sched.c > @@ -357,6 +357,11 @@ int spu_activate(struct spu_context *ctx > if (!spu) > return (signal_pending(current)) ? -ERESTARTSYS : -EAGAIN; > bind_context(spu, ctx); > + /* > + * We're likely to wait for interrupts on the same > + * CPU that we are now on, so send them here. > + */ > + spu_irq_setaffinity(spu, smp_processor_id()); With CONFIG_DEBUG_PREEMPT this will give a warning about using smp_processor_id in pre-emptible context if I'm reading the code correctly. Maybe use raw_smp_processor_id, since setting the affinity to this cpu isn't a hard requirement? From benh at kernel.crashing.org Thu Jan 5 16:39:44 2006 From: benh at kernel.crashing.org (Benjamin Herrenschmidt) Date: Thu, 05 Jan 2006 16:39:44 +1100 Subject: [PATCH] powerpc: Add PowerMac platform function interpreter Message-ID: <1136439584.4840.48.camel@localhost.localdomain> (Preliminary, still need a little bit of work, but I would greatly appreciate some regression testing with ARCH=powerpc, this code doesn't affect ARCH=ppc, especially on any laptop or desktop released in the past 3 or 4 years). This patch adds an interpreter for the PowerMac "platform-do-*" scripts found in the device-tree, along with some of the backends to be able to execute the primitives in there. For now, the backends for uninorth/u3, the macio chip, the gpios and some devices hanging off keywest i2c. In order to do so, I had to significantly rework the PowerMac "low_i2c" layer, this work isn't complete yet as you can see. Ultimately, it will be able to help in matching device nodes to i2c interfaces. I also might kill the existing i2c-keywest and i2c-pmac-smu drivers and just do a single "stub" driver that layers on top of the low i2c. Right now, it only provides keywest platform functions of the i2c hwclock, some more work will be needed to properly do some of the manipulations needed with the hwclock hanging off the PMU bus, on some windtunnel machines for example. Currently, the code for matching with i2c_adpater isn't working as none of the "high level" drivers is calling the necessary hooks, I'll fix that in a future version of that patch. I removed the clock spreading hacks in feature.c too, since they should now be entirely done by the platform functions, but I couldn't test properly as I don't have access to one of the laptops that has those... It's a bit difficult to "veryfiy" if it works now that bogomips are gone on powerpc, since the consequence of it not working is the CPU running about 20% slower than it should, thus I'll have to write a small test program that attempts to "measure" the processor frequency... I also changed the SMP code for newer G5s (multi core) to use the platform function when available for the timebase freeze, that appears to work properly on my Quad. Anyway, here it is, comments welcome... Signed-off-by: Benjamin Herrenschmidt Index: linux-work/include/asm-powerpc/pmac_pfunc.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-work/include/asm-powerpc/pmac_pfunc.h 2006-01-04 18:35:10.000000000 +1100 @@ -0,0 +1,253 @@ +#ifndef __PMAC_PFUNC_H__ +#define __PMAC_PFUNC_H__ + +#include +#include + +/* Flags in command lists */ +#define PMF_FLAGS_ON_INIT 0x80000000u +#define PMF_FLGAS_ON_TERM 0x40000000u +#define PMF_FLAGS_ON_SLEEP 0x20000000u +#define PMF_FLAGS_ON_WAKE 0x10000000u +#define PMF_FLAGS_ON_DEMAND 0x08000000u +#define PMF_FLAGS_INT_GEN 0x04000000u +#define PMF_FLAGS_HIGH_SPEED 0x02000000u +#define PMF_FLAGS_LOW_SPEED 0x01000000u +#define PMF_FLAGS_SIDE_EFFECTS 0x00800000u + +/* + * Arguments to a platform function call. + * + * NOTE: By convention, pointer arguments point to an u32 + */ +struct pmf_args { + union { + u32 v; + u32 *p; + } u[4]; + unsigned int count; +}; + +/* + * A driver capable of interpreting commands provides a handlers + * structure filled with whatever handlers are implemented by this + * driver. Non implemented handlers are left NULL. + * + * PMF_STD_ARGS are the same arguments that are passed to the parser + * and that gets passed back to the various handlers. + * + * Interpreting a given function always start with a begin() call which + * returns an instance data to be passed around subsequent calls, and + * ends with an end() call. This allows the low level driver to implement + * locking policy or per-function instance data. + * + * For interrupt capable functions, irq_enable() is called when a client + * registers, and irq_disable() is called when the last client unregisters + * Note that irq_enable & irq_disable are called within a semaphore held + * by the core, thus you should not try to register yourself to some other + * pmf interrupt during those calls. + */ + +#define PMF_STD_ARGS struct pmf_function *func, void *instdata, \ + struct pmf_args *args + +struct pmf_function; + +struct pmf_handlers { + void * (*begin)(struct pmf_function *func, struct pmf_args *args); + void (*end)(struct pmf_function *func, void *instdata); + + int (*irq_enable)(struct pmf_function *func); + int (*irq_disable)(struct pmf_function *func); + + int (*write_gpio)(PMF_STD_ARGS, u8 value, u8 mask); + int (*read_gpio)(PMF_STD_ARGS, u8 mask, int rshift, u8 xor); + + int (*write_reg32)(PMF_STD_ARGS, u32 offset, u32 value, u32 mask); + int (*read_reg32)(PMF_STD_ARGS, u32 offset); + int (*write_reg16)(PMF_STD_ARGS, u32 offset, u16 value, u16 mask); + int (*read_reg16)(PMF_STD_ARGS, u32 offset); + int (*write_reg8)(PMF_STD_ARGS, u32 offset, u8 value, u8 mask); + int (*read_reg8)(PMF_STD_ARGS, u32 offset); + + int (*delay)(PMF_STD_ARGS, u32 duration); + + int (*wait_reg32)(PMF_STD_ARGS, u32 offset, u32 value, u32 mask); + int (*wait_reg16)(PMF_STD_ARGS, u32 offset, u16 value, u16 mask); + int (*wait_reg8)(PMF_STD_ARGS, u32 offset, u8 value, u8 mask); + + int (*read_i2c)(PMF_STD_ARGS, u32 len); + int (*write_i2c)(PMF_STD_ARGS, u32 len, const u8 *data); + int (*rmw_i2c)(PMF_STD_ARGS, u32 masklen, u32 valuelen, u32 totallen, + const u8 *maskdata, const u8 *valuedata); + + int (*read_cfg)(PMF_STD_ARGS, u32 offset, u32 len); + int (*write_cfg)(PMF_STD_ARGS, u32 offset, u32 len, const u8 *data); + int (*rmw_cfg)(PMF_STD_ARGS, u32 offset, u32 masklen, u32 valuelen, + u32 totallen, const u8 *maskdata, const u8 *valuedata); + + int (*read_i2c_sub)(PMF_STD_ARGS, u8 subaddr, u32 len); + int (*write_i2c_sub)(PMF_STD_ARGS, u8 subaddr, u32 len, const u8 *data); + int (*set_i2c_mode)(PMF_STD_ARGS, int mode); + int (*rmw_i2c_sub)(PMF_STD_ARGS, u8 subaddr, u32 masklen, u32 valuelen, + u32 totallen, const u8 *maskdata, + const u8 *valuedata); + + int (*read_reg32_msrx)(PMF_STD_ARGS, u32 offset, u32 mask, u32 shift, + u32 xor); + int (*read_reg16_msrx)(PMF_STD_ARGS, u32 offset, u32 mask, u32 shift, + u32 xor); + int (*read_reg8_msrx)(PMF_STD_ARGS, u32 offset, u32 mask, u32 shift, + u32 xor); + + int (*write_reg32_slm)(PMF_STD_ARGS, u32 offset, u32 shift, u32 mask); + int (*write_reg16_slm)(PMF_STD_ARGS, u32 offset, u32 shift, u32 mask); + int (*write_reg8_slm)(PMF_STD_ARGS, u32 offset, u32 shift, u32 mask); + + int (*mask_and_compare)(PMF_STD_ARGS, u32 len, const u8 *maskdata, + const u8 *valuedata); + + struct module *owner; +}; + + +/* + * Drivers who expose platform functions register at init time, this + * causes the platform functions for that device node to be parsed in + * advance and associated with the device. The data structures are + * partially public so a driver can walk the list of platform functions + * and eventually inspect the flags + */ +struct pmf_device; + +struct pmf_function { + /* All functions for a given driver are linked */ + struct list_head link; + + /* Function node & driver data */ + struct device_node *node; + void *driver_data; + + /* For internal use by core */ + struct pmf_device *dev; + + /* The name is the "xxx" in "platform-do-xxx", this is how + * platform functions are identified by this code. Some functions + * only operate for a given target, in which case the phandle is + * here (or 0 if the filter doesn't apply) + */ + const char *name; + u32 phandle; + + /* The flags for that function. You can have several functions + * with the same name and different flag + */ + u32 flags; + + /* The actual tokenized function blob */ + const void *data; + unsigned int length; + + /* Interrupt clients */ + struct list_head irq_clients; + + /* Refcounting */ + struct kref ref; +}; + +/* + * For platform functions that are interrupts, one can register + * irq_client structures. You canNOT use the same structure twice + * as it contains a link member. Also, the callback is called with + * a spinlock held, you must not call back into any of the pmf_* functions + * from within that callback + */ +struct pmf_irq_client { + void (*handler)(void *data); + void *data; + struct module *owner; + struct list_head link; +}; + + +/* + * Register/Unregister a function-capable driver and its handlers + */ +extern int pmf_register_driver(struct device_node *np, + struct pmf_handlers *handlers, + void *driverdata); + +extern void pmf_unregister_driver(struct device_node *np); + + +/* + * Register/Unregister interrupt clients + */ +extern int pmf_register_irq_client(struct device_node *np, + const char *name, + struct pmf_irq_client *client); + +extern void pmf_unregister_irq_client(struct device_node *np, + const char *name, + struct pmf_irq_client *client); + +/* + * Called by the handlers when an irq happens + */ +extern void pmf_do_irq(struct pmf_function *func); + + +/* + * Low level call to platform functions. + * + * The phandle can filter on the target object for functions that have + * multiple targets, the flags allow you to restrict the call to a given + * combination of flags. + * + * The args array contains as many arguments as is required by the function, + * this is dependent on the function you are calling, unfortunately Apple + * mecanism provides no way to encode that so you have to get it right at + * the call site. Some functions require no args, in which case, you can + * pass NULL. + * + * You can also pass NULL to the name. This will match any function that has + * the appropriate combination of flags & phandle or you can pass 0 to the + * phandle to match any + */ +extern int pmf_do_functions(struct device_node *np, const char *name, + u32 phandle, u32 flags, struct pmf_args *args); + + + +/* + * High level call to a platform function. + * + * This one looks for the platform-xxx first so you should call it to the + * actual target if any. It will fallback to platform-do-xxx if it can't + * find one. It will also exclusively target functions that have + * the "OnDemand" flag. + */ + +extern int pmf_call_function(struct device_node *target, const char *name, + struct pmf_args *args); + + +/* + * For low latency interrupt usage, you can lookup for on-demand functions + * using the functions below + */ + +extern struct pmf_function *pmf_find_function(struct device_node *target, + const char *name); + +extern struct pmf_function * pmf_get_function(struct pmf_function *func); +extern void pmf_put_function(struct pmf_function *func); + +extern int pmf_call_one(struct pmf_function *func, struct pmf_args *args); + + +/* Suspend/resume code called by via-pmu directly for now */ +extern void pmac_pfunc_base_suspend(void); +extern void pmac_pfunc_base_resume(void); + +#endif /* __PMAC_PFUNC_H__ */ Index: linux-work/arch/powerpc/platforms/powermac/Makefile =================================================================== --- linux-work.orig/arch/powerpc/platforms/powermac/Makefile 2006-01-04 11:55:10.000000000 +1100 +++ linux-work/arch/powerpc/platforms/powermac/Makefile 2006-01-04 11:55:25.000000000 +1100 @@ -1,7 +1,8 @@ CFLAGS_bootx_init.o += -fPIC obj-y += pic.o setup.o time.o feature.o pci.o \ - sleep.o low_i2c.o cache.o + sleep.o low_i2c.o cache.o pfunc_core.o \ + pfunc_base.o obj-$(CONFIG_PMAC_BACKLIGHT) += backlight.o obj-$(CONFIG_CPU_FREQ_PMAC) += cpufreq_32.o obj-$(CONFIG_CPU_FREQ_PMAC64) += cpufreq_64.o Index: linux-work/arch/powerpc/platforms/powermac/feature.c =================================================================== --- linux-work.orig/arch/powerpc/platforms/powermac/feature.c 2006-01-04 11:55:10.000000000 +1100 +++ linux-work/arch/powerpc/platforms/powermac/feature.c 2006-01-04 16:36:11.000000000 +1100 @@ -58,12 +58,11 @@ extern int powersave_nap; extern struct device_node *k2_skiplist[2]; - /* * We use a single global lock to protect accesses. Each driver has * to take care of its own locking */ -static DEFINE_SPINLOCK(feature_lock); +DEFINE_SPINLOCK(feature_lock); #define LOCK(flags) spin_lock_irqsave(&feature_lock, flags); #define UNLOCK(flags) spin_unlock_irqrestore(&feature_lock, flags); @@ -106,22 +105,12 @@ }; +struct device_node *uninorth_node; +u32 __iomem *uninorth_base; -/* - * Uninorth reg. access. Note that Uni-N regs are big endian - */ - -#define UN_REG(r) (uninorth_base + ((r) >> 2)) -#define UN_IN(r) (in_be32(UN_REG(r))) -#define UN_OUT(r,v) (out_be32(UN_REG(r), (v))) -#define UN_BIS(r,v) (UN_OUT((r), UN_IN(r) | (v))) -#define UN_BIC(r,v) (UN_OUT((r), UN_IN(r) & ~(v))) - -static struct device_node *uninorth_node; -static u32 __iomem *uninorth_base; static u32 uninorth_rev; static int uninorth_maj; -static void __iomem *u3_ht; +static void __iomem *u3_ht_base; /* * For each motherboard family, we have a table of functions pointers @@ -1560,8 +1549,10 @@ #ifndef CONFIG_POWER4 -static void -keylargo_shutdown(struct macio_chip *macio, int sleep_mode) + +#ifdef CONFIG_PM + +static void keylargo_shutdown(struct macio_chip *macio, int sleep_mode) { u32 temp; @@ -1614,8 +1605,7 @@ (void)MACIO_IN32(KEYLARGO_FCR0); mdelay(1); } -static void -pangea_shutdown(struct macio_chip *macio, int sleep_mode) +static void pangea_shutdown(struct macio_chip *macio, int sleep_mode) { u32 temp; @@ -1648,8 +1638,7 @@ (void)MACIO_IN32(KEYLARGO_FCR0); mdelay(1); } -static void -intrepid_shutdown(struct macio_chip *macio, int sleep_mode) +static void intrepid_shutdown(struct macio_chip *macio, int sleep_mode) { u32 temp; @@ -1676,125 +1665,6 @@ mdelay(10); } - -void pmac_tweak_clock_spreading(int enable) -{ - struct macio_chip *macio = &macio_chips[0]; - - /* Hack for doing clock spreading on some machines PowerBooks and - * iBooks. This implements the "platform-do-clockspreading" OF - * property as decoded manually on various models. For safety, we also - * check the product ID in the device-tree in cases we'll whack the i2c - * chip to make reasonably sure we won't set wrong values in there - * - * Of course, ultimately, we have to implement a real parser for - * the platform-do-* stuff... - */ - - if (macio->type == macio_intrepid) { - struct device_node *clock = - of_find_node_by_path("/uni-n at f8000000/hw-clock"); - if (clock && get_property(clock, "platform-do-clockspreading", - NULL)) { - printk(KERN_INFO "%sabling clock spreading on Intrepid" - " ASIC\n", enable ? "En" : "Dis"); - if (enable) - UN_OUT(UNI_N_CLOCK_SPREADING, 2); - else - UN_OUT(UNI_N_CLOCK_SPREADING, 0); - mdelay(40); - } - of_node_put(clock); - } - - while (machine_is_compatible("PowerBook5,2") || - machine_is_compatible("PowerBook5,3") || - machine_is_compatible("PowerBook6,2") || - machine_is_compatible("PowerBook6,3")) { - struct device_node *ui2c = of_find_node_by_type(NULL, "i2c"); - struct device_node *dt = of_find_node_by_name(NULL, "device-tree"); - u8 buffer[9]; - u32 *productID; - int i, rc, changed = 0; - - if (dt == NULL) - break; - productID = (u32 *)get_property(dt, "pid#", NULL); - if (productID == NULL) - break; - while(ui2c) { - struct device_node *p = of_get_parent(ui2c); - if (p && !strcmp(p->name, "uni-n")) - break; - ui2c = of_find_node_by_type(ui2c, "i2c"); - } - if (ui2c == NULL) - break; - DBG("Trying to bump clock speed for PID: %08x...\n", *productID); - rc = pmac_low_i2c_open(ui2c, 1); - if (rc != 0) - break; - pmac_low_i2c_setmode(ui2c, pmac_low_i2c_mode_combined); - rc = pmac_low_i2c_xfer(ui2c, 0xd2 | pmac_low_i2c_read, 0x80, buffer, 9); - DBG("read result: %d,", rc); - if (rc != 0) { - pmac_low_i2c_close(ui2c); - break; - } - for (i=0; i<9; i++) - DBG(" %02x", buffer[i]); - DBG("\n"); - - switch(*productID) { - case 0x1182: /* AlBook 12" rev 2 */ - case 0x1183: /* iBook G4 12" */ - buffer[0] = (buffer[0] & 0x8f) | 0x70; - buffer[2] = (buffer[2] & 0x7f) | 0x00; - buffer[5] = (buffer[5] & 0x80) | 0x31; - buffer[6] = (buffer[6] & 0x40) | 0xb0; - buffer[7] = (buffer[7] & 0x00) | (enable ? 0xc0 : 0xba); - buffer[8] = (buffer[8] & 0x00) | 0x30; - changed = 1; - break; - case 0x3142: /* AlBook 15" (ATI M10) */ - case 0x3143: /* AlBook 17" (ATI M10) */ - buffer[0] = (buffer[0] & 0xaf) | 0x50; - buffer[2] = (buffer[2] & 0x7f) | 0x00; - buffer[5] = (buffer[5] & 0x80) | 0x31; - buffer[6] = (buffer[6] & 0x40) | 0xb0; - buffer[7] = (buffer[7] & 0x00) | (enable ? 0xd0 : 0xc0); - buffer[8] = (buffer[8] & 0x00) | 0x30; - changed = 1; - break; - default: - DBG("i2c-hwclock: Machine model not handled\n"); - break; - } - if (!changed) { - pmac_low_i2c_close(ui2c); - break; - } - printk(KERN_INFO "%sabling clock spreading on i2c clock chip\n", - enable ? "En" : "Dis"); - - pmac_low_i2c_setmode(ui2c, pmac_low_i2c_mode_stdsub); - rc = pmac_low_i2c_xfer(ui2c, 0xd2 | pmac_low_i2c_write, 0x80, buffer, 9); - DBG("write result: %d,", rc); - pmac_low_i2c_setmode(ui2c, pmac_low_i2c_mode_combined); - rc = pmac_low_i2c_xfer(ui2c, 0xd2 | pmac_low_i2c_read, 0x80, buffer, 9); - DBG("read result: %d,", rc); - if (rc != 0) { - pmac_low_i2c_close(ui2c); - break; - } - for (i=0; i<9; i++) - DBG(" %02x", buffer[i]); - pmac_low_i2c_close(ui2c); - break; - } -} - - static int core99_sleep(void) { @@ -1951,6 +1821,8 @@ return 0; } +#endif /* CONFIG_PM */ + static long core99_sleep_state(struct device_node *node, long param, long value) { @@ -1972,10 +1844,13 @@ if ((pmac_mb.board_flags & PMAC_MB_CAN_SLEEP) == 0) return -EPERM; +#ifdef CONFIG_PM if (value == 1) return core99_sleep(); else if (value == 0) return core99_wake_up(); + +#endif /* CONFIG_PM */ return 0; } @@ -2099,7 +1974,9 @@ { PMAC_FTR_USB_ENABLE, core99_usb_enable }, { PMAC_FTR_1394_ENABLE, core99_firewire_enable }, { PMAC_FTR_1394_CABLE_POWER, core99_firewire_cable_power }, +#ifdef CONFIG_PM { PMAC_FTR_SLEEP_STATE, core99_sleep_state }, +#endif #ifdef CONFIG_SMP { PMAC_FTR_RESET_CPU, core99_reset_cpu }, #endif /* CONFIG_SMP */ @@ -2690,7 +2567,7 @@ uninorth_base = ioremap(address, 0x40000); uninorth_rev = in_be32(UN_REG(UNI_N_VERSION)); if (uninorth_maj == 3 || uninorth_maj == 4) - u3_ht = ioremap(address + U3_HT_CONFIG_BASE, 0x1000); + u3_ht_base = ioremap(address + U3_HT_CONFIG_BASE, 0x1000); printk(KERN_INFO "Found %s memory controller & host bridge" " @ 0x%08x revision: 0x%02x\n", uninorth_maj == 3 ? "U3" : @@ -2980,12 +2857,6 @@ MACIO_BIC(HEATHROW_FCR, HRW_SOUND_POWER_N); } - /* Some machine models need the clock chip to be properly setup for - * clock spreading now. This should be a platform function but we - * don't do these at the moment - */ - pmac_tweak_clock_spreading(1); - #endif /* CONFIG_POWER4 */ /* On all machines, switch modem & serial ports off */ @@ -3013,9 +2884,6 @@ return; } - /* Setup low-level i2c stuffs */ - pmac_init_low_i2c(); - /* Probe machine type */ if (probe_motherboard()) printk(KERN_WARNING "Unknown PowerMac !\n"); @@ -3048,9 +2916,9 @@ u8 px_bus, px_devfn; struct pci_controller *px_hose; - (void)in_be32(u3_ht + U3_HT_LINK_COMMAND); - ucfg = cfg = in_be32(u3_ht + U3_HT_LINK_CONFIG); - ufreq = freq = in_be32(u3_ht + U3_HT_LINK_FREQ); + (void)in_be32(u3_ht_base + U3_HT_LINK_COMMAND); + ucfg = cfg = in_be32(u3_ht_base + U3_HT_LINK_CONFIG); + ufreq = freq = in_be32(u3_ht_base + U3_HT_LINK_FREQ); dump_HT_speeds("U3 HyperTransport", cfg, freq); pcix_node = of_find_compatible_node(NULL, "pci", "pci-x"); Index: linux-work/arch/powerpc/platforms/powermac/pfunc_base.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-work/arch/powerpc/platforms/powermac/pfunc_base.c 2006-01-04 17:51:17.000000000 +1100 @@ -0,0 +1,405 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define DBG(fmt...) printk(fmt) + +static irqreturn_t macio_gpio_irq(int irq, void *data, struct pt_regs *regs) +{ + pmf_do_irq(data); + + return IRQ_HANDLED; +} + +static int macio_do_gpio_irq_enable(struct pmf_function *func) +{ + if (func->node->n_intrs < 1) + return -EINVAL; + + return request_irq(func->node->intrs[0].line, macio_gpio_irq, 0, + func->node->name, func); +} + +static int macio_do_gpio_irq_disable(struct pmf_function *func) +{ + if (func->node->n_intrs < 1) + return -EINVAL; + + free_irq(func->node->intrs[0].line, func); + return 0; +} + +static int macio_do_gpio_write(PMF_STD_ARGS, u8 value, u8 mask) +{ + u8 __iomem *addr = (u8 __iomem *)func->driver_data; + unsigned long flags; + u8 tmp; + + /* Check polarity */ + if (args && args->count && !args->u[0].v) + value = ~value; + + /* Toggle the GPIO */ + spin_lock_irqsave(&feature_lock, flags); + tmp = readb(addr); + tmp = (tmp & ~mask) | (value & mask); + DBG("Do write 0x%02x to GPIO %s (%p)\n", + tmp, func->node->full_name, addr); + writeb(tmp, addr); + spin_unlock_irqrestore(&feature_lock, flags); + + return 0; +} + +static int macio_do_gpio_read(PMF_STD_ARGS, u8 mask, int rshift, u8 xor) +{ + u8 __iomem *addr = (u8 __iomem *)func->driver_data; + u32 value; + + /* Check if we have room for reply */ + if (args == NULL || args->count == 0 || args->u[0].p == NULL) + return -EINVAL; + + value = readb(addr); + *args->u[0].p = ((value & mask) >> rshift) ^ xor; + + return 0; +} + +static int macio_do_delay(PMF_STD_ARGS, u32 duration) +{ + /* assume we can sleep ! */ + msleep((duration + 999) / 1000); + return 0; +} + +static struct pmf_handlers macio_gpio_handlers = { + .irq_enable = macio_do_gpio_irq_enable, + .irq_disable = macio_do_gpio_irq_disable, + .write_gpio = macio_do_gpio_write, + .read_gpio = macio_do_gpio_read, + .delay = macio_do_delay, +}; + +static void macio_gpio_init_one(struct macio_chip *macio) +{ + struct device_node *gparent, *gp; + + /* + * Find the "gpio" parent node + */ + + for (gparent = NULL; + (gparent = of_get_next_child(macio->of_node, gparent)) != NULL;) + if (strcmp(gparent->name, "gpio") == 0) + break; + if (gparent == NULL) + return; + + DBG("Installing GPIO functions for macio %s\n", + macio->of_node->full_name); + + /* + * Ok, got one, we dont need anything special to track them down, so + * we just create them all + */ + for (gp = NULL; (gp = of_get_next_child(gparent, gp)) != NULL;) { + u32 *reg = (u32 *)get_property(gp, "reg", NULL); + unsigned long offset; + if (reg == NULL) + continue; + offset = *reg; + /* Deal with old style device-tree. We can safely hard code the + * offset for now too even if it's a bit gross ... + */ + if (offset < 0x50) + offset += 0x50; + offset += (unsigned long)macio->base; + pmf_register_driver(gp, &macio_gpio_handlers, (void *)offset); + } + + DBG("Calling initial GPIO functions for macio %s\n", + macio->of_node->full_name); + + /* And now we run all the init ones */ + for (gp = NULL; (gp = of_get_next_child(gparent, gp)) != NULL;) + pmf_do_functions(gp, NULL, 0, PMF_FLAGS_ON_INIT, NULL); + + /* Note: We do not at this point implement the "at sleep" or "at wake" + * functions. I yet to find any for GPIOs anyway + */ +} + +static int macio_do_write_reg32(PMF_STD_ARGS, u32 offset, u32 value, u32 mask) +{ + struct macio_chip *macio = func->driver_data; + unsigned long flags; + + spin_lock_irqsave(&feature_lock, flags); + MACIO_OUT32(offset, (MACIO_IN32(offset) & ~mask) | (value & mask)); + spin_unlock_irqrestore(&feature_lock, flags); + return 0; +} + +static int macio_do_read_reg32(PMF_STD_ARGS, u32 offset) +{ + struct macio_chip *macio = func->driver_data; + + /* Check if we have room for reply */ + if (args == NULL || args->count == 0 || args->u[0].p == NULL) + return -EINVAL; + + *args->u[0].p = MACIO_IN32(offset); + return 0; +} + +static int macio_do_write_reg8(PMF_STD_ARGS, u32 offset, u8 value, u8 mask) +{ + struct macio_chip *macio = func->driver_data; + unsigned long flags; + + spin_lock_irqsave(&feature_lock, flags); + MACIO_OUT8(offset, (MACIO_IN8(offset) & ~mask) | (value & mask)); + spin_unlock_irqrestore(&feature_lock, flags); + return 0; +} + +static int macio_do_read_reg8(PMF_STD_ARGS, u32 offset) +{ + struct macio_chip *macio = func->driver_data; + + /* Check if we have room for reply */ + if (args == NULL || args->count == 0 || args->u[0].p == NULL) + return -EINVAL; + + *((u8 *)(args->u[0].p)) = MACIO_IN8(offset); + return 0; +} + +static int macio_do_read_reg32_msrx(PMF_STD_ARGS, u32 offset, u32 mask, + u32 shift, u32 xor) +{ + struct macio_chip *macio = func->driver_data; + + /* Check if we have room for reply */ + if (args == NULL || args->count == 0 || args->u[0].p == NULL) + return -EINVAL; + + *args->u[0].p = ((MACIO_IN32(offset) & mask) >> shift) ^ xor; + return 0; +} + +static int macio_do_read_reg8_msrx(PMF_STD_ARGS, u32 offset, u32 mask, + u32 shift, u32 xor) +{ + struct macio_chip *macio = func->driver_data; + + /* Check if we have room for reply */ + if (args == NULL || args->count == 0 || args->u[0].p == NULL) + return -EINVAL; + + *((u8 *)(args->u[0].p)) = ((MACIO_IN8(offset) & mask) >> shift) ^ xor; + return 0; +} + +static int macio_do_write_reg32_slm(PMF_STD_ARGS, u32 offset, u32 shift, + u32 mask) +{ + struct macio_chip *macio = func->driver_data; + unsigned long flags; + u32 tmp, val; + + /* Check args */ + if (args == NULL || args->count == 0) + return -EINVAL; + + spin_lock_irqsave(&feature_lock, flags); + tmp = MACIO_IN32(offset); + val = args->u[0].v << shift; + tmp = (tmp & ~mask) | (val & mask); + MACIO_OUT32(offset, tmp); + spin_unlock_irqrestore(&feature_lock, flags); + return 0; +} + +static int macio_do_write_reg8_slm(PMF_STD_ARGS, u32 offset, u32 shift, + u32 mask) +{ + struct macio_chip *macio = func->driver_data; + unsigned long flags; + u32 tmp, val; + + /* Check args */ + if (args == NULL || args->count == 0) + return -EINVAL; + + spin_lock_irqsave(&feature_lock, flags); + tmp = MACIO_IN8(offset); + val = args->u[0].v << shift; + tmp = (tmp & ~mask) | (val & mask); + MACIO_OUT8(offset, tmp); + spin_unlock_irqrestore(&feature_lock, flags); + return 0; +} + +static struct pmf_handlers macio_mmio_handlers = { + .write_reg32 = macio_do_write_reg32, + .read_reg32 = macio_do_read_reg32, + .write_reg8 = macio_do_write_reg8, + .read_reg32 = macio_do_read_reg8, + .read_reg32_msrx = macio_do_read_reg32_msrx, + .read_reg8_msrx = macio_do_read_reg8_msrx, + .write_reg32_slm = macio_do_write_reg32_slm, + .write_reg8_slm = macio_do_write_reg8_slm, + .delay = macio_do_delay, +}; + +static void macio_mmio_init_one(struct macio_chip *macio) +{ + DBG("Installing MMIO functions for macio %s\n", + macio->of_node->full_name); + + pmf_register_driver(macio->of_node, &macio_mmio_handlers, macio); +} + +static struct device_node *unin_hwclock; + +static int unin_do_write_reg32(PMF_STD_ARGS, u32 offset, u32 value, u32 mask) +{ + unsigned long flags; + + spin_lock_irqsave(&feature_lock, flags); + /* This is fairly bogus in darwin, but it should work for our needs + * implemeted that way: + */ + UN_OUT(offset, (UN_IN(offset) & ~mask) | (value & mask)); + spin_unlock_irqrestore(&feature_lock, flags); + return 0; +} + + +static struct pmf_handlers unin_mmio_handlers = { + .write_reg32 = unin_do_write_reg32, + .delay = macio_do_delay, +}; + +static void uninorth_install_pfunc(void) +{ + struct device_node *np; + + DBG("Installing functions for UniN %s\n", + uninorth_node->full_name); + + /* + * Install handlers for the bridge itself + */ + pmf_register_driver(uninorth_node, &unin_mmio_handlers, NULL); + pmf_do_functions(uninorth_node, NULL, 0, PMF_FLAGS_ON_INIT, NULL); + + + /* + * Install handlers for the hwclock child if any + */ + for (np = NULL; (np = of_get_next_child(uninorth_node, np)) != NULL;) + if (strcmp(np->name, "hw-clock") == 0) { + unin_hwclock = np; + break; + } + if (unin_hwclock) { + DBG("Installing functions for UniN clock %s\n", + unin_hwclock->full_name); + pmf_register_driver(unin_hwclock, &unin_mmio_handlers, NULL); + pmf_do_functions(unin_hwclock, NULL, 0, PMF_FLAGS_ON_INIT, + NULL); + } +} + +/* We export this as the SMP code might init us early */ +int __init pmac_pfunc_base_install(void) +{ + static int pfbase_inited; + int i; + + if (pfbase_inited) + return 0; + pfbase_inited = 1; + + + DBG("Installing base platform functions...\n"); + + /* + * Locate mac-io chips and install handlers + */ + for (i = 0 ; i < MAX_MACIO_CHIPS; i++) { + if (macio_chips[i].of_node) { + macio_mmio_init_one(&macio_chips[i]); + macio_gpio_init_one(&macio_chips[i]); + } + } + + /* + * Install handlers for northbridge and direct mapped hwclock + * if any. We do not implement the config space access callback + * which is only ever used for functions that we do not call in + * the current driver (enabling/disabling cells in U2, mostly used + * to restore the PCI settings, we do that differently) + */ + if (uninorth_node && uninorth_base) + uninorth_install_pfunc(); + + DBG("All base functions installed\n"); + + return 0; +} + +arch_initcall(pmac_pfunc_base_install); + +#ifdef CONFIG_PM + +/* Those can be called by pmac_feature. Ultimately, I should use a sysdev + * or a device, but for now, that's good enough until I sort out some + * ordering issues. Also, we do not bother with GPIOs, as so far I yet have + * to see a case where a GPIO function has the on-suspend or on-resume bit + */ +void pmac_pfunc_base_suspend(void) +{ + int i; + + for (i = 0 ; i < MAX_MACIO_CHIPS; i++) { + if (macio_chips[i].of_node) + pmf_do_functions(macio_chips[i].of_node, NULL, 0, + PMF_FLAGS_ON_SLEEP, NULL); + } + if (uninorth_node) + pmf_do_functions(uninorth_node, NULL, 0, + PMF_FLAGS_ON_SLEEP, NULL); + if (unin_hwclock) + pmf_do_functions(unin_hwclock, NULL, 0, + PMF_FLAGS_ON_SLEEP, NULL); +} + +void pmac_pfunc_base_resume(void) +{ + int i; + + if (unin_hwclock) + pmf_do_functions(unin_hwclock, NULL, 0, + PMF_FLAGS_ON_WAKE, NULL); + if (uninorth_node) + pmf_do_functions(uninorth_node, NULL, 0, + PMF_FLAGS_ON_WAKE, NULL); + for (i = 0 ; i < MAX_MACIO_CHIPS; i++) { + if (macio_chips[i].of_node) + pmf_do_functions(macio_chips[i].of_node, NULL, 0, + PMF_FLAGS_ON_WAKE, NULL); + } +} + +#endif /* CONFIG_PM */ Index: linux-work/arch/powerpc/platforms/powermac/pfunc_core.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-work/arch/powerpc/platforms/powermac/pfunc_core.c 2006-01-05 14:56:32.000000000 +1100 @@ -0,0 +1,989 @@ +/* + * + * FIXME: Properly make this race free with refcounting etc... + * + * FIXME: LOCKING !!! + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* Debug */ +#define LOG_PARSE(fmt...) +#define LOG_ERROR(fmt...) printk(fmt) +#define LOG_BLOB(t,b,c) +#define DBG(fmt...) printk(fmt) + +/* Command numbers */ +#define PMF_CMD_LIST 0 +#define PMF_CMD_WRITE_GPIO 1 +#define PMF_CMD_READ_GPIO 2 +#define PMF_CMD_WRITE_REG32 3 +#define PMF_CMD_READ_REG32 4 +#define PMF_CMD_WRITE_REG16 5 +#define PMF_CMD_READ_REG16 6 +#define PMF_CMD_WRITE_REG8 7 +#define PMF_CMD_READ_REG8 8 +#define PMF_CMD_DELAY 9 +#define PMF_CMD_WAIT_REG32 10 +#define PMF_CMD_WAIT_REG16 11 +#define PMF_CMD_WAIT_REG8 12 +#define PMF_CMD_READ_I2C 13 +#define PMF_CMD_WRITE_I2C 14 +#define PMF_CMD_RMW_I2C 15 +#define PMF_CMD_GEN_I2C 16 +#define PMF_CMD_SHIFT_BYTES_RIGHT 17 +#define PMF_CMD_SHIFT_BYTES_LEFT 18 +#define PMF_CMD_READ_CFG 19 +#define PMF_CMD_WRITE_CFG 20 +#define PMF_CMD_RMW_CFG 21 +#define PMF_CMD_READ_I2C_SUBADDR 22 +#define PMF_CMD_WRITE_I2C_SUBADDR 23 +#define PMF_CMD_SET_I2C_MODE 24 +#define PMF_CMD_RMW_I2C_SUBADDR 25 +#define PMF_CMD_READ_REG32_MASK_SHR_XOR 26 +#define PMF_CMD_READ_REG16_MASK_SHR_XOR 27 +#define PMF_CMD_READ_REG8_MASK_SHR_XOR 28 +#define PMF_CMD_WRITE_REG32_SHL_MASK 29 +#define PMF_CMD_WRITE_REG16_SHL_MASK 30 +#define PMF_CMD_WRITE_REG8_SHL_MASK 31 +#define PMF_CMD_MASK_AND_COMPARE 32 +#define PMF_CMD_COUNT 33 + +/* This structure holds the state of the parser while walking through + * a function definition + */ +struct pmf_cmd { + const void *cmdptr; + const void *cmdend; + struct pmf_function *func; + void *instdata; + struct pmf_args *args; + int error; +}; + +#if 0 +/* Debug output */ +static void print_blob(const char *title, const void *blob, int bytes) +{ + printk("%s", title); + while(bytes--) { + printk("%02x ", *((u8 *)blob)); + blob += 1; + } + printk("\n"); +} +#endif + +/* + * Parser helpers + */ + +static u32 pmf_next32(struct pmf_cmd *cmd) +{ + u32 value; + if ((cmd->cmdend - cmd->cmdptr) < 4) { + cmd->error = 1; + return 0; + } + value = *((u32 *)cmd->cmdptr); + cmd->cmdptr += 4; + return value; +} + +static const void* pmf_next_blob(struct pmf_cmd *cmd, int count) +{ + const void *value; + if ((cmd->cmdend - cmd->cmdptr) < count) { + cmd->error = 1; + return NULL; + } + value = cmd->cmdptr; + cmd->cmdptr += count; + return value; +} + +/* + * Individual command parsers + */ + +#define PMF_PARSE_CALL(name, cmd, handlers, p...) \ + do { \ + if (cmd->error) \ + return -ENXIO; \ + if (handlers == NULL) \ + return 0; \ + if (handlers->name) \ + return handlers->name(cmd->func, cmd->instdata, \ + cmd->args, p); \ + return -1; \ + } while(0) \ + + +static int pmf_parser_write_gpio(struct pmf_cmd *cmd, struct pmf_handlers *h) +{ + u8 value = (u8)pmf_next32(cmd); + u8 mask = (u8)pmf_next32(cmd); + + LOG_PARSE("pmf: write_gpio(value: %02x, mask: %02x)\n", value, mask); + + PMF_PARSE_CALL(write_gpio, cmd, h, value, mask); +} + +static int pmf_parser_read_gpio(struct pmf_cmd *cmd, struct pmf_handlers *h) +{ + u8 mask = (u8)pmf_next32(cmd); + int rshift = (int)pmf_next32(cmd); + u8 xor = (u8)pmf_next32(cmd); + + LOG_PARSE("pmf: read_gpio(mask: %02x, rshift: %d, xor: %02x)\n", + mask, rshift, xor); + + PMF_PARSE_CALL(read_gpio, cmd, h, mask, rshift, xor); +} + +static int pmf_parser_write_reg32(struct pmf_cmd *cmd, struct pmf_handlers *h) +{ + u32 offset = pmf_next32(cmd); + u32 value = pmf_next32(cmd); + u32 mask = pmf_next32(cmd); + + LOG_PARSE("pmf: write_reg32(offset: %08x, value: %08x, mask: %08x)\n", + offset, value, mask); + + PMF_PARSE_CALL(write_reg32, cmd, h, offset, value, mask); +} + +static int pmf_parser_read_reg32(struct pmf_cmd *cmd, struct pmf_handlers *h) +{ + u32 offset = pmf_next32(cmd); + + LOG_PARSE("pmf: read_reg32(offset: %08x)\n", offset); + + PMF_PARSE_CALL(read_reg32, cmd, h, offset); +} + + +static int pmf_parser_write_reg16(struct pmf_cmd *cmd, struct pmf_handlers *h) +{ + u32 offset = pmf_next32(cmd); + u16 value = (u16)pmf_next32(cmd); + u16 mask = (u16)pmf_next32(cmd); + + LOG_PARSE("pmf: write_reg16(offset: %08x, value: %04x, mask: %04x)\n", + offset, value, mask); + + PMF_PARSE_CALL(write_reg16, cmd, h, offset, value, mask); +} + +static int pmf_parser_read_reg16(struct pmf_cmd *cmd, struct pmf_handlers *h) +{ + u32 offset = pmf_next32(cmd); + + LOG_PARSE("pmf: read_reg16(offset: %08x)\n", offset); + + PMF_PARSE_CALL(read_reg16, cmd, h, offset); +} + + +static int pmf_parser_write_reg8(struct pmf_cmd *cmd, struct pmf_handlers *h) +{ + u32 offset = pmf_next32(cmd); + u8 value = (u16)pmf_next32(cmd); + u8 mask = (u16)pmf_next32(cmd); + + LOG_PARSE("pmf: write_reg8(offset: %08x, value: %02x, mask: %02x)\n", + offset, value, mask); + + PMF_PARSE_CALL(write_reg8, cmd, h, offset, value, mask); +} + +static int pmf_parser_read_reg8(struct pmf_cmd *cmd, struct pmf_handlers *h) +{ + u32 offset = pmf_next32(cmd); + + LOG_PARSE("pmf: read_reg8(offset: %08x)\n", offset); + + PMF_PARSE_CALL(read_reg8, cmd, h, offset); +} + +static int pmf_parser_delay(struct pmf_cmd *cmd, struct pmf_handlers *h) +{ + u32 duration = pmf_next32(cmd); + + LOG_PARSE("pmf: delay(duration: %d us)\n", duration); + + PMF_PARSE_CALL(delay, cmd, h, duration); +} + +static int pmf_parser_wait_reg32(struct pmf_cmd *cmd, struct pmf_handlers *h) +{ + u32 offset = pmf_next32(cmd); + u32 value = pmf_next32(cmd); + u32 mask = pmf_next32(cmd); + + LOG_PARSE("pmf: wait_reg32(offset: %08x, comp_value: %08x,mask: %08x)\n", + offset, value, mask); + + PMF_PARSE_CALL(wait_reg32, cmd, h, offset, value, mask); +} + +static int pmf_parser_wait_reg16(struct pmf_cmd *cmd, struct pmf_handlers *h) +{ + u32 offset = pmf_next32(cmd); + u16 value = (u16)pmf_next32(cmd); + u16 mask = (u16)pmf_next32(cmd); + + LOG_PARSE("pmf: wait_reg16(offset: %08x, comp_value: %04x,mask: %04x)\n", + offset, value, mask); + + PMF_PARSE_CALL(wait_reg16, cmd, h, offset, value, mask); +} + +static int pmf_parser_wait_reg8(struct pmf_cmd *cmd, struct pmf_handlers *h) +{ + u32 offset = pmf_next32(cmd); + u8 value = (u8)pmf_next32(cmd); + u8 mask = (u8)pmf_next32(cmd); + + LOG_PARSE("pmf: wait_reg8(offset: %08x, comp_value: %02x,mask: %02x)\n", + offset, value, mask); + + PMF_PARSE_CALL(wait_reg8, cmd, h, offset, value, mask); +} + +static int pmf_parser_read_i2c(struct pmf_cmd *cmd, struct pmf_handlers *h) +{ + u32 bytes = pmf_next32(cmd); + + LOG_PARSE("pmf: read_i2c(bytes: %ud)\n", bytes); + + PMF_PARSE_CALL(read_i2c, cmd, h, bytes); +} + +static int pmf_parser_write_i2c(struct pmf_cmd *cmd, struct pmf_handlers *h) +{ + u32 bytes = pmf_next32(cmd); + const void *blob = pmf_next_blob(cmd, bytes); + + LOG_PARSE("pmf: write_i2c(bytes: %ud) ...\n", bytes); + LOG_BLOB("pmf: data: \n", blob, bytes); + + PMF_PARSE_CALL(write_i2c, cmd, h, bytes, blob); +} + + +static int pmf_parser_rmw_i2c(struct pmf_cmd *cmd, struct pmf_handlers *h) +{ + u32 maskbytes = pmf_next32(cmd); + u32 valuesbytes = pmf_next32(cmd); + u32 totalbytes = pmf_next32(cmd); + const void *maskblob = pmf_next_blob(cmd, maskbytes); + const void *valuesblob = pmf_next_blob(cmd, valuesbytes); + + LOG_PARSE("pmf: rmw_i2c(maskbytes: %ud, valuebytes: %ud, " + "totalbytes: %d) ...\n", + maskbytes, valuesbytes, totalbytes); + LOG_BLOB("pmf: mask data: \n", maskblob, maskbytes); + LOG_BLOB("pmf: values data: \n", valuesblob, valuesbytes); + + PMF_PARSE_CALL(rmw_i2c, cmd, h, maskbytes, valuesbytes, totalbytes, + maskblob, valuesblob); +} + +static int pmf_parser_read_cfg(struct pmf_cmd *cmd, struct pmf_handlers *h) +{ + u32 offset = pmf_next32(cmd); + u32 bytes = pmf_next32(cmd); + + LOG_PARSE("pmf: read_cfg(offset: %x, bytes: %ud)\n", offset, bytes); + + PMF_PARSE_CALL(read_cfg, cmd, h, offset, bytes); +} + + +static int pmf_parser_write_cfg(struct pmf_cmd *cmd, struct pmf_handlers *h) +{ + u32 offset = pmf_next32(cmd); + u32 bytes = pmf_next32(cmd); + const void *blob = pmf_next_blob(cmd, bytes); + + LOG_PARSE("pmf: write_cfg(offset: %x, bytes: %ud)\n", offset, bytes); + LOG_BLOB("pmf: data: \n", blob, bytes); + + PMF_PARSE_CALL(write_cfg, cmd, h, offset, bytes, blob); +} + +static int pmf_parser_rmw_cfg(struct pmf_cmd *cmd, struct pmf_handlers *h) +{ + u32 offset = pmf_next32(cmd); + u32 maskbytes = pmf_next32(cmd); + u32 valuesbytes = pmf_next32(cmd); + u32 totalbytes = pmf_next32(cmd); + const void *maskblob = pmf_next_blob(cmd, maskbytes); + const void *valuesblob = pmf_next_blob(cmd, valuesbytes); + + LOG_PARSE("pmf: rmw_cfg(maskbytes: %ud, valuebytes: %ud," + " totalbytes: %d) ...\n", + maskbytes, valuesbytes, totalbytes); + LOG_BLOB("pmf: mask data: \n", maskblob, maskbytes); + LOG_BLOB("pmf: values data: \n", valuesblob, valuesbytes); + + PMF_PARSE_CALL(rmw_cfg, cmd, h, offset, maskbytes, valuesbytes, + totalbytes, maskblob, valuesblob); +} + + +static int pmf_parser_read_i2c_sub(struct pmf_cmd *cmd, struct pmf_handlers *h) +{ + u8 subaddr = (u8)pmf_next32(cmd); + u32 bytes = pmf_next32(cmd); + + LOG_PARSE("pmf: read_i2c_sub(subaddr: %x, bytes: %ud)\n", + subaddr, bytes); + + PMF_PARSE_CALL(read_i2c_sub, cmd, h, subaddr, bytes); +} + +static int pmf_parser_write_i2c_sub(struct pmf_cmd *cmd, struct pmf_handlers *h) +{ + u8 subaddr = (u8)pmf_next32(cmd); + u32 bytes = pmf_next32(cmd); + const void *blob = pmf_next_blob(cmd, bytes); + + LOG_PARSE("pmf: write_i2c_sub(subaddr: %x, bytes: %ud) ...\n", + subaddr, bytes); + LOG_BLOB("pmf: data: \n", blob, bytes); + + PMF_PARSE_CALL(write_i2c_sub, cmd, h, subaddr, bytes, blob); +} + +static int pmf_parser_set_i2c_mode(struct pmf_cmd *cmd, struct pmf_handlers *h) +{ + u32 mode = pmf_next32(cmd); + + LOG_PARSE("pmf: set_i2c_mode(mode: %d)\n", mode); + + PMF_PARSE_CALL(set_i2c_mode, cmd, h, mode); +} + + +static int pmf_parser_rmw_i2c_sub(struct pmf_cmd *cmd, struct pmf_handlers *h) +{ + u8 subaddr = (u8)pmf_next32(cmd); + u32 maskbytes = pmf_next32(cmd); + u32 valuesbytes = pmf_next32(cmd); + u32 totalbytes = pmf_next32(cmd); + const void *maskblob = pmf_next_blob(cmd, maskbytes); + const void *valuesblob = pmf_next_blob(cmd, valuesbytes); + + LOG_PARSE("pmf: rmw_i2c_sub(subaddr: %x, maskbytes: %ud, valuebytes: %ud" + ", totalbytes: %d) ...\n", + subaddr, maskbytes, valuesbytes, totalbytes); + LOG_BLOB("pmf: mask data: \n", maskblob, maskbytes); + LOG_BLOB("pmf: values data: \n", valuesblob, valuesbytes); + + PMF_PARSE_CALL(rmw_i2c_sub, cmd, h, subaddr, maskbytes, valuesbytes, + totalbytes, maskblob, valuesblob); +} + +static int pmf_parser_read_reg32_msrx(struct pmf_cmd *cmd, + struct pmf_handlers *h) +{ + u32 offset = pmf_next32(cmd); + u32 mask = pmf_next32(cmd); + u32 shift = pmf_next32(cmd); + u32 xor = pmf_next32(cmd); + + LOG_PARSE("pmf: read_reg32_msrx(offset: %x, mask: %x, shift: %x," + " xor: %x\n", offset, mask, shift, xor); + + PMF_PARSE_CALL(read_reg32_msrx, cmd, h, offset, mask, shift, xor); +} + +static int pmf_parser_read_reg16_msrx(struct pmf_cmd *cmd, + struct pmf_handlers *h) +{ + u32 offset = pmf_next32(cmd); + u32 mask = pmf_next32(cmd); + u32 shift = pmf_next32(cmd); + u32 xor = pmf_next32(cmd); + + LOG_PARSE("pmf: read_reg16_msrx(offset: %x, mask: %x, shift: %x," + " xor: %x\n", offset, mask, shift, xor); + + PMF_PARSE_CALL(read_reg16_msrx, cmd, h, offset, mask, shift, xor); +} +static int pmf_parser_read_reg8_msrx(struct pmf_cmd *cmd, + struct pmf_handlers *h) +{ + u32 offset = pmf_next32(cmd); + u32 mask = pmf_next32(cmd); + u32 shift = pmf_next32(cmd); + u32 xor = pmf_next32(cmd); + + LOG_PARSE("pmf: read_reg8_msrx(offset: %x, mask: %x, shift: %x," + " xor: %x\n", offset, mask, shift, xor); + + PMF_PARSE_CALL(read_reg8_msrx, cmd, h, offset, mask, shift, xor); +} + +static int pmf_parser_write_reg32_slm(struct pmf_cmd *cmd, + struct pmf_handlers *h) +{ + u32 offset = pmf_next32(cmd); + u32 shift = pmf_next32(cmd); + u32 mask = pmf_next32(cmd); + + LOG_PARSE("pmf: write_reg32_slm(offset: %x, shift: %x, mask: %x\n", + offset, shift, mask); + + PMF_PARSE_CALL(write_reg32_slm, cmd, h, offset, shift, mask); +} + +static int pmf_parser_write_reg16_slm(struct pmf_cmd *cmd, + struct pmf_handlers *h) +{ + u32 offset = pmf_next32(cmd); + u32 shift = pmf_next32(cmd); + u32 mask = pmf_next32(cmd); + + LOG_PARSE("pmf: write_reg16_slm(offset: %x, shift: %x, mask: %x\n", + offset, shift, mask); + + PMF_PARSE_CALL(write_reg16_slm, cmd, h, offset, shift, mask); +} + +static int pmf_parser_write_reg8_slm(struct pmf_cmd *cmd, + struct pmf_handlers *h) +{ + u32 offset = pmf_next32(cmd); + u32 shift = pmf_next32(cmd); + u32 mask = pmf_next32(cmd); + + LOG_PARSE("pmf: write_reg8_slm(offset: %x, shift: %x, mask: %x\n", + offset, shift, mask); + + PMF_PARSE_CALL(write_reg8_slm, cmd, h, offset, shift, mask); +} + +static int pmf_parser_mask_and_compare(struct pmf_cmd *cmd, + struct pmf_handlers *h) +{ + u32 bytes = pmf_next32(cmd); + const void *maskblob = pmf_next_blob(cmd, bytes); + const void *valuesblob = pmf_next_blob(cmd, bytes); + + LOG_PARSE("pmf: mask_and_compare(length: %ud ...\n", bytes); + LOG_BLOB("pmf: mask data: \n", maskblob, bytes); + LOG_BLOB("pmf: values data: \n", valuesblob, bytes); + + PMF_PARSE_CALL(mask_and_compare, cmd, h, + bytes, maskblob, valuesblob); +} + + +typedef int (*pmf_cmd_parser_t)(struct pmf_cmd *cmd, struct pmf_handlers *h); + +static pmf_cmd_parser_t pmf_parsers[PMF_CMD_COUNT] = +{ + NULL, + pmf_parser_write_gpio, + pmf_parser_read_gpio, + pmf_parser_write_reg32, + pmf_parser_read_reg32, + pmf_parser_write_reg16, + pmf_parser_read_reg16, + pmf_parser_write_reg8, + pmf_parser_read_reg8, + pmf_parser_delay, + pmf_parser_wait_reg32, + pmf_parser_wait_reg16, + pmf_parser_wait_reg8, + pmf_parser_read_i2c, + pmf_parser_write_i2c, + pmf_parser_rmw_i2c, + NULL, /* Bogus command */ + NULL, /* Shift bytes right: NYI */ + NULL, /* Shift bytes left: NYI */ + pmf_parser_read_cfg, + pmf_parser_write_cfg, + pmf_parser_rmw_cfg, + pmf_parser_read_i2c_sub, + pmf_parser_write_i2c_sub, + pmf_parser_set_i2c_mode, + pmf_parser_rmw_i2c_sub, + pmf_parser_read_reg32_msrx, + pmf_parser_read_reg16_msrx, + pmf_parser_read_reg8_msrx, + pmf_parser_write_reg32_slm, + pmf_parser_write_reg16_slm, + pmf_parser_write_reg8_slm, + pmf_parser_mask_and_compare, +}; + +struct pmf_device { + struct list_head link; + struct device_node *node; + struct pmf_handlers *handlers; + struct list_head functions; + struct kref ref; +}; + +static LIST_HEAD(pmf_devices); +static spinlock_t pmf_lock = SPIN_LOCK_UNLOCKED; + +static void pmf_release_device(struct kref *kref) +{ + struct pmf_device *dev = container_of(kref, struct pmf_device, ref); + kfree(dev); +} + +static inline void pmf_put_device(struct pmf_device *dev) +{ + kref_put(&dev->ref, pmf_release_device); +} + +static inline struct pmf_device *pmf_get_device(struct pmf_device *dev) +{ + kref_get(&dev->ref); + return dev; +} + +static inline struct pmf_device *pmf_find_device(struct device_node *np) +{ + struct pmf_device *dev; + + list_for_each_entry(dev, &pmf_devices, link) { + if (dev->node == np) + return pmf_get_device(dev); + } + return NULL; +} + +static int pmf_parse_one(struct pmf_function *func, + struct pmf_handlers *handlers, + void *instdata, struct pmf_args *args) +{ + struct pmf_cmd cmd; + u32 ccode; + int count, rc; + + cmd.cmdptr = func->data; + cmd.cmdend = func->data + func->length; + cmd.func = func; + cmd.instdata = instdata; + cmd.args = args; + cmd.error = 0; + + LOG_PARSE("pmf: func %s, %d bytes, %s...\n", + func->name, func->length, + handlers ? "executing" : "parsing"); + + /* One subcommand to parse for now */ + count = 1; + + while(count-- && cmd.cmdptr < cmd.cmdend) { + /* Get opcode */ + ccode = pmf_next32(&cmd); + /* Check if we are hitting a command list, fetch new count */ + if (ccode == 0) { + count = pmf_next32(&cmd) - 1; + ccode = pmf_next32(&cmd); + } + if (cmd.error) { + LOG_ERROR("pmf: parse error, not enough data\n"); + return -ENXIO; + } + if (ccode >= PMF_CMD_COUNT) { + LOG_ERROR("pmf: command code %d unknown !\n", ccode); + return -ENXIO; + } + if (pmf_parsers[ccode] == NULL) { + LOG_ERROR("pmf: no parser for command %d !\n", ccode); + return -ENXIO; + } + rc = pmf_parsers[ccode](&cmd, handlers); + if (rc != 0) { + LOG_ERROR("pmf: parser for command %d returned" + " error %d\n", ccode, rc); + return rc; + } + } + + /* We are doing an initial parse pass, we need to adjust the size */ + if (handlers == NULL) + func->length = cmd.cmdptr - func->data; + + return 0; +} + +static int pmf_add_function_prop(struct pmf_device *dev, void *driverdata, + const char *name, u32 *data, + unsigned int length) +{ + int count = 0; + struct pmf_function *func = NULL; + + DBG("pmf: Adding functions for platform-do-%s\n", name); + + while (length >= 12) { + /* Allocate a structure */ + func = kzalloc(sizeof(struct pmf_function), GFP_KERNEL); + if (func == NULL) + goto bail; + kref_init(&func->ref); + INIT_LIST_HEAD(&func->irq_clients); + func->node = dev->node; + func->driver_data = driverdata; + func->name = name; + func->phandle = data[0]; + func->flags = data[1]; + data += 2; + length -= 8; + func->data = data; + func->length = length; + func->dev = dev; + DBG("pmf: idx %d: flags=%08x, phandle=%08x " + " %d bytes remaining, parsing...\n", + count+1, func->flags, func->phandle, length); + if (pmf_parse_one(func, NULL, NULL, NULL)) { + kfree(func); + goto bail; + } + length -= func->length; + data = (u32 *)(((u8 *)data) + func->length); + list_add(&func->link, &dev->functions); + pmf_get_device(dev); + count++; + } + bail: + DBG("pmf: Added %d functions\n", count); + + return count; +} + +static int pmf_add_functions(struct pmf_device *dev, void *driverdata) +{ + struct property *pp; +#define PP_PREFIX "platform-do-" + const int plen = strlen(PP_PREFIX); + int count = 0; + + for (pp = dev->node->properties; pp != 0; pp = pp->next) { + char *name; + if (strncmp(pp->name, PP_PREFIX, plen) != 0) + continue; + name = pp->name + plen; + if (strlen(name) && pp->length >= 12) + count += pmf_add_function_prop(dev, driverdata, name, + (u32 *)pp->value, + pp->length); + } + return count; +} + + +int pmf_register_driver(struct device_node *np, + struct pmf_handlers *handlers, + void *driverdata) +{ + struct pmf_device *dev; + unsigned long flags; + int rc = 0; + + if (handlers == NULL) + return -EINVAL; + + DBG("pmf: registering driver for node %s\n", np->full_name); + + spin_lock_irqsave(&pmf_lock, flags); + dev = pmf_find_device(np); + spin_unlock_irqrestore(&pmf_lock, flags); + if (dev != NULL) { + DBG("pmf: already there !\n"); + pmf_put_device(dev); + return -EBUSY; + } + + dev = kzalloc(sizeof(struct pmf_device), GFP_KERNEL); + if (dev == NULL) { + DBG("pmf: no memory !\n"); + return -ENOMEM; + } + kref_init(&dev->ref); + dev->node = of_node_get(np); + dev->handlers = handlers; + INIT_LIST_HEAD(&dev->functions); + + rc = pmf_add_functions(dev, driverdata); + if (rc == 0) { + DBG("pmf: no functions, disposing.. \n"); + of_node_put(np); + kfree(dev); + return -ENODEV; + } + + spin_lock_irqsave(&pmf_lock, flags); + list_add(&dev->link, &pmf_devices); + spin_unlock_irqrestore(&pmf_lock, flags); + + return 0; +} +EXPORT_SYMBOL_GPL(pmf_register_driver); + +struct pmf_function *pmf_get_function(struct pmf_function *func) +{ + if (!try_module_get(func->dev->handlers->owner)) + return NULL; + kref_get(&func->ref); + return func; +} +EXPORT_SYMBOL_GPL(pmf_get_function); + +static void pmf_release_function(struct kref *kref) +{ + struct pmf_function *func = + container_of(kref, struct pmf_function, ref); + pmf_put_device(func->dev); + kfree(func); +} + +static inline void __pmf_put_function(struct pmf_function *func) +{ + kref_put(&func->ref, pmf_release_function); +} + +void pmf_put_function(struct pmf_function *func) +{ + if (func == NULL) + return; + module_put(func->dev->handlers->owner); + __pmf_put_function(func); +} +EXPORT_SYMBOL_GPL(pmf_put_function); + +void pmf_unregister_driver(struct device_node *np) +{ + struct pmf_device *dev; + unsigned long flags; + + DBG("pmf: unregistering driver for node %s\n", np->full_name); + + spin_lock_irqsave(&pmf_lock, flags); + dev = pmf_find_device(np); + if (dev == NULL) { + DBG("pmf: not such driver !\n"); + spin_unlock_irqrestore(&pmf_lock, flags); + return; + } + list_del(&dev->link); + + while(!list_empty(&dev->functions)) { + struct pmf_function *func = + list_entry(dev->functions.next, typeof(*func), link); + list_del(&func->link); + __pmf_put_function(func); + } + + pmf_put_device(dev); + spin_unlock_irqrestore(&pmf_lock, flags); +} +EXPORT_SYMBOL_GPL(pmf_unregister_driver); + +struct pmf_function *__pmf_find_function(struct device_node *target, + const char *name, u32 flags) +{ + struct device_node *actor = of_node_get(target); + struct pmf_device *dev; + struct pmf_function *func, *result = NULL; + char fname[64]; + u32 *prop, ph; + + /* + * Look for a "platform-*" function reference. If we can't find + * one, then we fallback to a direct call attempt + */ + snprintf(fname, 63, "platform-%s", name); + prop = (u32 *)get_property(target, fname, NULL); + if (prop == NULL) + goto find_it; + ph = *prop; + if (ph == 0) + goto find_it; + + /* + * Ok, now try to find the actor. If we can't find it, we fail, + * there is no point in falling back there + */ + of_node_put(actor); + actor = of_find_node_by_phandle(ph); + if (actor == NULL) + return NULL; + find_it: + dev = pmf_find_device(actor); + if (dev == NULL) + return NULL; + + list_for_each_entry(func, &dev->functions, link) { + if (name && strcmp(name, func->name)) + continue; + if (func->phandle && target->node != func->phandle) + continue; + if ((func->flags & flags) == 0) + continue; + result = func; + break; + } + of_node_put(actor); + pmf_put_device(dev); + return result; +} + + +int pmf_register_irq_client(struct device_node *target, + const char *name, + struct pmf_irq_client *client) +{ + struct pmf_function *func; + unsigned long flags; + + spin_lock_irqsave(&pmf_lock, flags); + func = __pmf_find_function(target, name, PMF_FLAGS_INT_GEN); + if (func == NULL) { + spin_unlock_irqrestore(&pmf_lock, flags); + return -ENODEV; + } + list_add(&client->link, &func->irq_clients); + spin_unlock_irqrestore(&pmf_lock, flags); + + return 0; +} +EXPORT_SYMBOL_GPL(pmf_register_irq_client); + +void pmf_unregister_irq_client(struct device_node *np, + const char *name, + struct pmf_irq_client *client) +{ + unsigned long flags; + + spin_lock_irqsave(&pmf_lock, flags); + list_del(&client->link); + spin_unlock_irqrestore(&pmf_lock, flags); +} +EXPORT_SYMBOL_GPL(pmf_unregister_irq_client); + + +void pmf_do_irq(struct pmf_function *func) +{ + unsigned long flags; + struct pmf_irq_client *client; + + /* For now, using a spinlock over the whole function. Can be made + * to drop the lock using 2 lists if necessary + */ + spin_lock_irqsave(&pmf_lock, flags); + list_for_each_entry(client, &func->irq_clients, link) { + if (!try_module_get(client->owner)) + continue; + client->handler(client->data); + module_put(client->owner); + } + spin_unlock_irqrestore(&pmf_lock, flags); +} +EXPORT_SYMBOL_GPL(pmf_do_irq); + + +int pmf_call_one(struct pmf_function *func, struct pmf_args *args) +{ + struct pmf_device *dev = func->dev; + void *instdata = NULL; + int rc = 0; + + DBG(" ** pmf_call_one(%s/%s) **\n", dev->node->full_name, func->name); + + if (dev->handlers->begin) + instdata = dev->handlers->begin(func, args); + rc = pmf_parse_one(func, dev->handlers, instdata, args); + if (dev->handlers->end) + dev->handlers->end(func, instdata); + + return rc; +} +EXPORT_SYMBOL_GPL(pmf_call_one); + +int pmf_do_functions(struct device_node *np, const char *name, + u32 phandle, u32 fflags, struct pmf_args *args) +{ + struct pmf_device *dev; + struct pmf_function *func, *tmp; + unsigned long flags; + int rc = -ENODEV; + + spin_lock_irqsave(&pmf_lock, flags); + + dev = pmf_find_device(np); + if (dev == NULL) { + spin_unlock_irqrestore(&pmf_lock, flags); + return -ENODEV; + } + list_for_each_entry_safe(func, tmp, &dev->functions, link) { + if (name && strcmp(name, func->name)) + continue; + if (phandle && func->phandle && phandle != func->phandle) + continue; + if ((func->flags & fflags) == 0) + continue; + if (pmf_get_function(func) == NULL) + continue; + spin_unlock_irqrestore(&pmf_lock, flags); + rc = pmf_call_one(func, args); + pmf_put_function(func); + spin_lock_irqsave(&pmf_lock, flags); + } + pmf_put_device(dev); + spin_unlock_irqrestore(&pmf_lock, flags); + + return rc; +} +EXPORT_SYMBOL_GPL(pmf_do_functions); + + +struct pmf_function *pmf_find_function(struct device_node *target, + const char *name) +{ + struct pmf_function *func; + unsigned long flags; + + spin_lock_irqsave(&pmf_lock, flags); + func = __pmf_find_function(target, name, PMF_FLAGS_ON_DEMAND); + if (func) + func = pmf_get_function(func); + spin_unlock_irqrestore(&pmf_lock, flags); + return func; +} +EXPORT_SYMBOL_GPL(pmf_find_function); + +int pmf_call_function(struct device_node *target, const char *name, + struct pmf_args *args) +{ + struct pmf_function *func = pmf_find_function(target, name); + int rc; + + if (func == NULL) + return -ENODEV; + + rc = pmf_call_one(func, args); + pmf_put_function(func); + return rc; +} +EXPORT_SYMBOL_GPL(pmf_call_function); + Index: linux-work/include/asm-powerpc/pmac_feature.h =================================================================== --- linux-work.orig/include/asm-powerpc/pmac_feature.h 2006-01-04 11:55:10.000000000 +1100 +++ linux-work/include/asm-powerpc/pmac_feature.h 2006-01-04 16:34:13.000000000 +1100 @@ -318,10 +318,6 @@ extern void pmac_suspend_agp_for_card(struct pci_dev *dev); extern void pmac_resume_agp_for_card(struct pci_dev *dev); -/* Used by the via-pmu driver for suspend/resume - */ -extern void pmac_tweak_clock_spreading(int enable); - /* * The part below is for use by macio_asic.c only, do not rely * on the data structures or constants below in a normal driver @@ -378,5 +374,24 @@ #define MACIO_IN8(r) (in_8(MACIO_FCR8(macio,r))) #define MACIO_OUT8(r,v) (out_8(MACIO_FCR8(macio,r), (v))) +/* + * Those are exported by pmac feature for internal use by arch code + * only like the platform function callbacks, do not use directly in drivers + */ +extern spinlock_t feature_lock; +extern struct device_node *uninorth_node; +extern u32 __iomem *uninorth_base; + +/* + * Uninorth reg. access. Note that Uni-N regs are big endian + */ + +#define UN_REG(r) (uninorth_base + ((r) >> 2)) +#define UN_IN(r) (in_be32(UN_REG(r))) +#define UN_OUT(r,v) (out_be32(UN_REG(r), (v))) +#define UN_BIS(r,v) (UN_OUT((r), UN_IN(r) | (v))) +#define UN_BIC(r,v) (UN_OUT((r), UN_IN(r) & ~(v))) + + #endif /* __PPC_ASM_PMAC_FEATURE_H */ #endif /* __KERNEL__ */ Index: linux-work/arch/powerpc/platforms/powermac/smp.c =================================================================== --- linux-work.orig/arch/powerpc/platforms/powermac/smp.c 2006-01-04 11:55:10.000000000 +1100 +++ linux-work/arch/powerpc/platforms/powermac/smp.c 2006-01-05 11:54:05.000000000 +1100 @@ -52,8 +52,9 @@ #include #include #include +#include -#undef DEBUG +#define DEBUG #ifdef DEBUG #define DBG(fmt...) udbg_printf(fmt) @@ -62,6 +63,7 @@ #endif extern void __secondary_start_pmac_0(void); +extern int pmac_pfunc_base_install(void); #ifdef CONFIG_PPC32 @@ -482,7 +484,7 @@ /* * G5s enable/disable the timebase via an i2c-connected clock chip. */ -static struct device_node *pmac_tb_clock_chip_host; +static struct pmac_i2c_bus *pmac_tb_clock_chip_host; static u8 pmac_tb_pulsar_addr; static void smp_core99_cypress_tb_freeze(int freeze) @@ -493,20 +495,20 @@ /* Strangely, the device-tree says address is 0xd2, but darwin * accesses 0xd0 ... */ - pmac_low_i2c_setmode(pmac_tb_clock_chip_host, - pmac_low_i2c_mode_combined); - rc = pmac_low_i2c_xfer(pmac_tb_clock_chip_host, - 0xd0 | pmac_low_i2c_read, - 0x81, &data, 1); + pmac_i2c_setmode(pmac_tb_clock_chip_host, + pmac_i2c_mode_combined); + rc = pmac_i2c_xfer(pmac_tb_clock_chip_host, + 0xd0 | pmac_i2c_read, + 1, 0x81, &data, 1); if (rc != 0) goto bail; data = (data & 0xf3) | (freeze ? 0x00 : 0x0c); - pmac_low_i2c_setmode(pmac_tb_clock_chip_host, pmac_low_i2c_mode_stdsub); - rc = pmac_low_i2c_xfer(pmac_tb_clock_chip_host, - 0xd0 | pmac_low_i2c_write, - 0x81, &data, 1); + pmac_i2c_setmode(pmac_tb_clock_chip_host, pmac_i2c_mode_stdsub); + rc = pmac_i2c_xfer(pmac_tb_clock_chip_host, + 0xd0 | pmac_i2c_write, + 1, 0x81, &data, 1); bail: if (rc != 0) { @@ -522,20 +524,20 @@ u8 data; int rc; - pmac_low_i2c_setmode(pmac_tb_clock_chip_host, - pmac_low_i2c_mode_combined); - rc = pmac_low_i2c_xfer(pmac_tb_clock_chip_host, - pmac_tb_pulsar_addr | pmac_low_i2c_read, - 0x2e, &data, 1); + pmac_i2c_setmode(pmac_tb_clock_chip_host, + pmac_i2c_mode_combined); + rc = pmac_i2c_xfer(pmac_tb_clock_chip_host, + pmac_tb_pulsar_addr | pmac_i2c_read, + 1, 0x2e, &data, 1); if (rc != 0) goto bail; data = (data & 0x88) | (freeze ? 0x11 : 0x22); - pmac_low_i2c_setmode(pmac_tb_clock_chip_host, pmac_low_i2c_mode_stdsub); - rc = pmac_low_i2c_xfer(pmac_tb_clock_chip_host, - pmac_tb_pulsar_addr | pmac_low_i2c_write, - 0x2e, &data, 1); + pmac_i2c_setmode(pmac_tb_clock_chip_host, pmac_i2c_mode_stdsub); + rc = pmac_i2c_xfer(pmac_tb_clock_chip_host, + pmac_tb_pulsar_addr | pmac_i2c_write, + 1, 0x2e, &data, 1); bail: if (rc != 0) { printk(KERN_ERR "Pulsar Timebase %s rc: %d\n", @@ -560,13 +562,15 @@ if (!ok) continue; + pmac_tb_clock_chip_host = pmac_i2c_find_bus(cc); + if (pmac_tb_clock_chip_host == NULL) + continue; reg = (u32 *)get_property(cc, "reg", NULL); if (reg == NULL) continue; - switch (*reg) { case 0xd2: - if (device_is_compatible(cc, "pulsar-legacy-slewing")) { + if (device_is_compatible(cc,"pulsar-legacy-slewing")) { pmac_tb_freeze = smp_core99_pulsar_tb_freeze; pmac_tb_pulsar_addr = 0xd2; name = "Pulsar"; @@ -585,37 +589,44 @@ break; } if (pmac_tb_freeze != NULL) { - struct device_node *p = of_get_parent(cc); - of_node_put(cc); - while(p && strcmp(p->type, "i2c")) { - cc = of_get_parent(p); - of_node_put(p); - p = cc; - } - if (p == NULL) - goto no_i2c_sync; /* Open i2c bus for synchronous access */ - if (pmac_low_i2c_open(p, 0)) { - printk(KERN_ERR "Failed top open i2c bus %s for clock" - " sync, fallback to software sync !\n", - p->full_name); - of_node_put(p); + if (pmac_i2c_open(pmac_tb_clock_chip_host, 1)) { + printk(KERN_ERR "Failed top open i2c bus for clock" + " sync, fallback to software sync !\n"); goto no_i2c_sync; } - pmac_tb_clock_chip_host = p; printk(KERN_INFO "Processor timebase sync using %s i2c clock\n", name); return; } no_i2c_sync: pmac_tb_freeze = NULL; + pmac_tb_clock_chip_host = NULL; } -#endif /* CONFIG_PPC64 */ /* - * SMP G4 and newer G5 use a GPIO to enable/disable the timebase. + * Newer G5s uses a platform function + */ + +static void smp_core99_pf