LinuxPPC-Dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* RE: Xilinx devicetrees
From: Koss, Mike (Mission Systems) @ 2007-12-13  2:40 UTC (permalink / raw)
  To: Stephen Neuendorffer, David H. Lynch Jr., Grant Likely,
	linuxppc-embedded
In-Reply-To: <20071125235855.95B9999804D@mail138-cpk.bigfish.com>

[-- Attachment #1: Type: text/plain, Size: 7713 bytes --]

Time for my $.02, since I am heavily weighting my future designs on the
use of the device trees. :) (and b/c I don't check my work e-mail from
home ;P)

________________________________

From: Stephen Neuendorffer [mailto:stephen.neuendorffer@xilinx.com] 
Sent: Sunday, November 25, 2007 6:59 PM
To: David H. Lynch Jr.; Grant Likely; linuxppc-embedded
Subject: RE: Xilinx devicetrees

DL>    I am not expert on this, but at Pico we already store our boot
monitor in the .bit files in BRAM.
DL>     But that is not free.  It is one of the reasons we do not use
u-boot. Our boot monitor must fit into 16K of BRAM.
DL>     Must be able to perform selftests on critical hardware, support
a flash file system, load bit files from flash to the FGA, load and
exectute elf files, allow a small set of user commands, 
DL>  and handle hosted vs. standalone operation.
DL>     And aparently extract the devicetree from a bit file and pass it
to a linux kernel.

SN> Once you can load a bitstream from flash, loading the device tree
from flash
SN> should be practically free.  In any event, why do you do this rather
than just run out of the flash (or a ram copy of the flash?)
 
This is the approach that I am currently taking with our future design.
In our system, we actually have 2 physical systems running Linux inside
the V4 (that's why there are 2 PowerPCs :) ). My current plan would be
to have the device trees stored in FLASH along w/ a single linux image.
Our bootloader on the board would then copy that information from FLASH
to RAM for both systems. This allows us to have two physically different
systems setup with different hardware, but have one linux image with two
different device trees. With a standard linux kernel weighing it at ~
3-5MB (including initramfs) that's a hefty savings in FLASH for us. Plus
it can considerably lower boot time instead of having to verify multiple
large images.

DL>  In static or fairly static hardware, that's fine. Even in somewhat
dynamic hardware with large quantities of startup resources - like a PC.
DL>  But in highly dynamic hardware with fairly limited resources it
starts to become an issue.

SN> As Grant says, the dynamic detection doesn't have to be done in the
boot loader, it could be done in the platform code.  You can largely
ignore 
SN> the device trees, or always boot with a core device tree and figure
it all out later (perhaps using version registers).  I anticipate that 
SN> the 'standard flow' will have standard platform code for any board
that uses a complete device tree.  If you have the need to do something 
SN> extraordinary, then you should feel free to hack away...  However,
It doesn't seem to me to be really necessary in your case, assuming that

SN> the device tree is packaged (somehow, TBD) along with the bitstream.
 
I don't know if packaging the device tree with the bitstream is the best
way to go. It's possible that it could lead to headaches for certain
systems that have security restrictions. The same could be said for
using it w/ the SystemACE to load it into RAM after the image. (which is
what I'm currently doing for my 2 linux images in lieu of a true on-chip
bootloader). I am already taking the security concerns into account for
future revisions of the hardware wrt to using a SystemACE, and am
planning on moving the device trees into NV storage like FLASH.

GL> No, unfortunately they don't deal with the problem you're facing
GL> (which I'm facing also).  But it will be solved if we figure out a
GL> sane way to bind the device tree up with the FPGA bitstream without
GL> consuming FPGA resources.
  
DL>    Note to Xilinx:
DL>       There MUST be some way of binding a device description to a
bit file.
DL>    neither building it into the FPGA fabric nor appending it to the
end
DL> of the bit file are perfect solutions,
DL>    The former is more powerfull and flexible but wastes precious
DL> resources. The later is more complex and puts more burdens on
DL>    software developers, and may be completely unfeasible in some
DL> environments - not mine fortunately.

SN> I don't understand the 'burden on software developers'.  The code to
do this will just be standard code.  The worst that one can say is:
SN> 1) I need several KB additional non volatile storage.  Given the
size of the FPGA bitstream, this can't be a huge constraint.
SN> 2) I can't use compile time optimization based on xparameters as
easily.  Anyone want to implement the alternatives mechanism on ppc and
microblaze?
SN> 3) Some additional boot time.  However, again, this seems marginal.
 
I do agree that using more FPGA resources is not a solution to the
problem. I'm already hitting 80% usage on a FX60 and trying to squeeze
more real estate for storage of the device tree seems silly. Especially
since that would require that every image have this extra hardware built
into it just to support booting a Linux kernel. Why should I have to
have different hardware to boot linux, versus non-kernel, xilkernel, or
other (GHS, LynxOS, etc..)?

DL>    Regardless, something must be done. An odd collection of
devicetree
DL> files co-mingled with a collection of bit files, is little better
than
DL> xparameter files all over the place.

SN> Certainly..  But in a sense, these are all intermediate files on the
path to the image on the board.  That 
SN> (and how it is interpreted by the platform code) should be generated
in a consistent fashion by EDK.  
SN> See my other email for some of the possibilities.  Are there
specific reasons why you think those 
SN> proposals are inadequate?  Now is the time when we can take
criticism, with the goal towards making 
SN> a good end solution.
 
One solution I've been thinking through (in lieu of direct support from
EDK) is to use a tcl script with xps to traverse the hardware tree and
generate the device tree. It seems like it should be relatively trivial
to obtain the information. It's just going to be a pain to write all the
handlers for each different linux driver: temac, interrupt controller,
DMA controller, etc.

In reality the best way to handle this would be to have EDK generate the
device tree as part of the library/bsp build process. Now, what I'd like
to see with regards to this is the ability to change the handler for the
generating a specific device information. An example could be the temac.
If at some point in the future the temac needs new/more information to
support its configuration/run-time then having to get a patch from
Xilinx for a EDK is way too slow. The developers should be giving the
opportunity to inject a new handler into the various parts of the device
tree generation. That way when the kernel patch is submitted, an EDK
device generator patch will be submitted at the same time to keep
everything in sync.

DL>    And once again a plea to ALWAYS make version/capabilities
registers
DL> atleast an optional part of every design.
DL>    Embeddeding a device tree into a design might be fairly
expensive. a
DL> pair of read only 32 bit registers is damn near free - basically the
DL> FPGA equivalent of atmost 64 diodes or resistors.

SN> Actually, device trees actually seem to be cheaper (in the whole
system sense) than such registers.  Unless there is something I don't
understand?
 
The issue here is that the hardware changed and the driver doesn't
support it. I think this would be fixed by having information passed to
the driver in the platform_device struct to specify information, since
its not able to be discerned by the physical hardware information:
version registers, etc.


[-- Attachment #2: Type: text/html, Size: 11617 bytes --]

^ permalink raw reply

* Re: [PATCH RFC 0/7] "NAND on UPM" and related patches
From: Chris Fester @ 2007-12-13  3:01 UTC (permalink / raw)
  To: avorontsov; +Cc: linuxppc-dev
In-Reply-To: <20071210204705.GA31263@localhost.localdomain>

On Mon, 2007-12-10 at 23:47 +0300, Anton Vorontsov wrote:
> Hi all,
> 
> Here are patches to support NAND on UPM. That driver is generic for
> all processors with FSL UPMs. And because of that, few more patches are
> needed -- GPIO API and generic FSL UPM functions.
> I would appreciate any comments and suggestions, thanks!

Hi Anton,

Apologies for not getting back sooner to comment...

We've been using a home-brewed UPM driver for our NAND for a while now.
There's several issues we've hit that I figured I would share.  The
issues involve deficiencies of both the UPM and the NAND part itself.

We modeled our driver loosely after the suggestions in this white paper:
http://www.freescale.com/files/32bit/doc/white_paper/NANDFLASHWP.pdf?fpsp=1&WT_TYPE=WhitePapers&WT_VENDOR=FREESCALE&WT_FILE_FORMAT=pdf&WT_ASSET=Documentation
We also connected the NAND according to the suggestions in the above
document.  

Something that you should be made aware of right away about the UPM -
you do not have control of the logic lines all of the time.  You only
can control them during a transaction (in our case, a byte transaction).
In other words, the time that is between reading/writing bytes, or
putting the address on the bus, or asserting commands you are not able
to hold the lines to a certain state.  Most of the control lines coming
from the UPM default to a logic 1 when unused (I believe only one
defaults to a logic 0).

The first issue is timings between versions of processors.  We've got
one board using an MPC8349 running a 1Gb ST micro part (NAND01G-B).
Another board is an MPC8347 using the same 1Gb ST micro part.  After
much time analyzing logic analyzer captures we found that write pulses
with the 8349 had to be shifted to the left a half clock in order to
properly latch the data on the bus.  This was unexpected given the two
boards are very similar and the processors are in the same family.  I
suppose the fix was easy enough, we just had to make a new version of
the microcode.  What's interesting is that Freescale said that there's
nothing different between the two processors' UPMs.  Yet there is a
distinct difference between local bus behavior on the 2 cpus, our LA
captures prove it.

The second issue was with interrupts during reads.  We noticed that some
of our boards experienced ECC errors while reading the NAND.  Logic
analyzer captures and debugging revealed that when an interrupt happened
while the driver's read code was executing, the UPM went into an idle
state, and the chip select of the part went high.  This may not be a
problem for other NAND parts, but for our ST Micro parts, the spec says
that if CS is held high for more than 100us, the part will exit page
read mode.  The default state of the UPM pins are high, so when the UPM
is idle all the signals are at logic 1.  

Anyways, when the interrupt had completed, or driver wasn't aware that
an interrupt had occurred, so it continued to do it's read loop.  The ST
Micro part was now in an undefined state.  The page read appeared to
continue just fine, but we almost always had one or more bits corrupted
for the very first resuming data byte.  

The fix that we ultimately did was to move the chip select control from
the UPM to a GPIO from the processor.  This forced the chip to never
"sleep" during an interrupt, because we could keep the chip select low
for the entire page read transaction.  This brought up other minor
issues.  You must make sure that your boot loader (U-Boot in our case)
sets the GPIO data BEFORE it sets the GPIO direction.  Otherwise the
NAND will automatically enter a start up read condition that will drive
the local bus, causing anything else on the local bus (such as a NOR
flash) to be useless...

The latest version of the app note online (link above) does not mention
the problem you may experience with interrupts.  I requested from our
Freescale support contact that Freescale document this fact and make it
available to others such that they won't bump into the same problems we
did!

The THIRD issue we had (actually, are having) is very related to our
second issue.  Our customer now wants a 2Gb NAND on our board.  We
swapped the chip out.  We included one more address byte transaction
that is needed with the larger part in the driver.  And it doesn't work.
The part is considered to be the "bigger brother" to the 1Gb part, but
apparently enough of it's state machine has changed that it doesn't
tolerate the AL and CL lines going high while chip select is low.
Incidentally, when debugging our second issue we were told by ST Micro
support that the only time AL and CL were sampled by the NAND was when
W# and R# were driven low.  Had we known that the 2Gb part wouldn't have
worked, we would have done much more major reworking to the NAND
subsystem when we respun our board.

This mail isn't meant to be a rant.  Everyone knows that this kind of
low-level hardware/software work is difficult.  This mail is meant to
caution you against using the NAND with the UPM.  Freescale may say that
it works, and present you a pretty looking white paper, but it's really
kludgy and you are pretty guaranteed to have problems.  Please encourage
your board designers to hook the NAND to some sort of CPLD device rather
than the UPM.  At least with a CPLD you have some real control over the
state of the control lines.

If you for whatever reason are stuck with the UPM...
1.)  Get your hardware people to stick inverters on the AL and CL lines.
Adjust your UPM microcode accordingly.
2.)  Put your CS under control of a GPIO pin.
3.)  Buy a nice logic analyzer, because you're going to need it!!!

I hope this helps someone out there... I've been spending a lot of time
on-and-off on this.  Every time we think we have it right one little
seemingly benign change throws the whole thing out the window...

Okie, happy coding,

Chris Fester

^ permalink raw reply

* [PATCH] Convert media-bay.c to use the kthread API
From: Paul Mackerras @ 2007-12-13  3:12 UTC (permalink / raw)
  To: linuxppc-dev

We aren't supposed to use kernel_thread directly in drivers any more,
and in fact using kthread_run is a bit simpler.

Signed-off-by: Paul Mackerras <paulus@samba.org>
---
diff --git a/drivers/macintosh/mediabay.c b/drivers/macintosh/mediabay.c
index 48d647a..080844b 100644
--- a/drivers/macintosh/mediabay.c
+++ b/drivers/macintosh/mediabay.c
@@ -20,6 +20,7 @@
 #include <linux/stddef.h>
 #include <linux/init.h>
 #include <linux/ide.h>
+#include <linux/kthread.h>
 #include <asm/prom.h>
 #include <asm/pgtable.h>
 #include <asm/io.h>
@@ -35,7 +36,6 @@
 
 
 #define MB_DEBUG
-#define MB_IGNORE_SIGNALS
 
 #ifdef MB_DEBUG
 #define MBDBG(fmt, arg...)	printk(KERN_INFO fmt , ## arg)
@@ -622,12 +622,7 @@ static int media_bay_task(void *x)
 {
 	int	i;
 
-	strcpy(current->comm, "media-bay");
-#ifdef MB_IGNORE_SIGNALS
-	sigfillset(&current->blocked);
-#endif
-
-	for (;;) {
+	while (!kthread_should_stop()) {
 		for (i = 0; i < media_bay_count; ++i) {
 			down(&media_bays[i].lock);
 			if (!media_bays[i].sleeping)
@@ -636,8 +631,6 @@ static int media_bay_task(void *x)
 		}
 
 		msleep_interruptible(MB_POLL_DELAY);
-		if (signal_pending(current))
-			return 0;
 	}
 }
 
@@ -699,7 +692,7 @@ static int __devinit media_bay_attach(struct macio_dev *mdev, const struct of_de
 
 	/* Startup kernel thread */
 	if (i == 0)
-		kernel_thread(media_bay_task, NULL, CLONE_KERNEL);
+		kthread_run(media_bay_task, NULL, "media-bay");
 
 	return 0;
 

^ permalink raw reply related

* [PATCH 0/2] Add crashdump shutdown hooks
From: Michael Neuling @ 2007-12-13  3:16 UTC (permalink / raw)
  To: Paul Mackerras; +Cc: linuxppc-dev, Michael Neuling, RAISCH, THEMANN
In-Reply-To: <20071212054512.A02B062C153@localhost.localdomain>

The following patches add crashdump shutdown hooks for POWERPC.
Signed-off-by: Michael Neuling <mikey@neuling.org>
---

This is an updated series following comments from the first post.
Adds some documentation, better code flow, fixes 32 bit compiles and
other updates based on feedback

Again these are based on paulus' for 2.6.25 tree.

^ permalink raw reply

* [PATCH 1/2] Make setjmp/longjmp code generic
From: Michael Neuling @ 2007-12-13  3:16 UTC (permalink / raw)
  To: Paul Mackerras; +Cc: linuxppc-dev, Michael Neuling, RAISCH, THEMANN
In-Reply-To: <1197515777.834799.307108384755.qpush@coopers>

This makes the setjmp/longjmp code used by xmon, generically available
to other code.  It also removes the requirement for debugger hooks to
be only called on 0x300 (data storage) exception and adds some
documentation.

Signed-off-by: Michael Neuling <mikey@neuling.org>
---

 arch/powerpc/kernel/misc.S   |  128 ++++++++++++++++++++++++++++++++++++++++
 arch/powerpc/mm/fault.c      |    6 -
 arch/powerpc/xmon/Makefile   |    2 
 arch/powerpc/xmon/setjmp.S   |  135 -------------------------------------------
 arch/powerpc/xmon/xmon.c     |   38 ++++--------
 include/asm-powerpc/setjmp.h |   19 ++++++
 6 files changed, 164 insertions(+), 164 deletions(-)

Index: clone3/arch/powerpc/kernel/misc.S
===================================================================
--- clone3.orig/arch/powerpc/kernel/misc.S
+++ clone3/arch/powerpc/kernel/misc.S
@@ -8,6 +8,8 @@
  * Adapted for iSeries by Mike Corrigan (mikejc@us.ibm.com)
  * PPC64 updates by Dave Engebretsen (engebret@us.ibm.com)
  *
+ * setjmp/longjmp and xmon_save_regs code by Paul Mackerras.
+ *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
  * as published by the Free Software Foundation; either version
@@ -15,6 +17,8 @@
  */
 #include <asm/ppc_asm.h>
 #include <asm/unistd.h>
+#include <asm/asm-compat.h>
+#include <asm/asm-offsets.h>
 
 	.text
 
@@ -51,3 +55,127 @@ _GLOBAL(kernel_execve)
 	bnslr
 	neg	r3,r3
 	blr
+
+_GLOBAL(setjmp)
+	mflr	r0
+	PPC_STL	r0,0(r3)
+	PPC_STL	r1,SZL(r3)
+	PPC_STL	r2,2*SZL(r3)
+	mfcr	r0
+	PPC_STL	r0,3*SZL(r3)
+	PPC_STL	r13,4*SZL(r3)
+	PPC_STL	r14,5*SZL(r3)
+	PPC_STL	r15,6*SZL(r3)
+	PPC_STL	r16,7*SZL(r3)
+	PPC_STL	r17,8*SZL(r3)
+	PPC_STL	r18,9*SZL(r3)
+	PPC_STL	r19,10*SZL(r3)
+	PPC_STL	r20,11*SZL(r3)
+	PPC_STL	r21,12*SZL(r3)
+	PPC_STL	r22,13*SZL(r3)
+	PPC_STL	r23,14*SZL(r3)
+	PPC_STL	r24,15*SZL(r3)
+	PPC_STL	r25,16*SZL(r3)
+	PPC_STL	r26,17*SZL(r3)
+	PPC_STL	r27,18*SZL(r3)
+	PPC_STL	r28,19*SZL(r3)
+	PPC_STL	r29,20*SZL(r3)
+	PPC_STL	r30,21*SZL(r3)
+	PPC_STL	r31,22*SZL(r3)
+	li	r3,0
+	blr
+
+_GLOBAL(longjmp)
+	PPC_LCMPI r4,0
+	bne	1f
+	li	r4,1
+1:	PPC_LL	r13,4*SZL(r3)
+	PPC_LL	r14,5*SZL(r3)
+	PPC_LL	r15,6*SZL(r3)
+	PPC_LL	r16,7*SZL(r3)
+	PPC_LL	r17,8*SZL(r3)
+	PPC_LL	r18,9*SZL(r3)
+	PPC_LL	r19,10*SZL(r3)
+	PPC_LL	r20,11*SZL(r3)
+	PPC_LL	r21,12*SZL(r3)
+	PPC_LL	r22,13*SZL(r3)
+	PPC_LL	r23,14*SZL(r3)
+	PPC_LL	r24,15*SZL(r3)
+	PPC_LL	r25,16*SZL(r3)
+	PPC_LL	r26,17*SZL(r3)
+	PPC_LL	r27,18*SZL(r3)
+	PPC_LL	r28,19*SZL(r3)
+	PPC_LL	r29,20*SZL(r3)
+	PPC_LL	r30,21*SZL(r3)
+	PPC_LL	r31,22*SZL(r3)
+	PPC_LL	r0,3*SZL(r3)
+	mtcrf	0x38,r0
+	PPC_LL	r0,0(r3)
+	PPC_LL	r1,SZL(r3)
+	PPC_LL	r2,2*SZL(r3)
+	mtlr	r0
+	mr	r3,r4
+	blr
+
+#ifdef CONFIG_XMON
+/*
+ * Grab the register values as they are now.
+ * This won't do a particularily good job because we really
+ * want our caller's caller's registers, and our caller has
+ * already executed its prologue.
+ * ToDo: We could reach back into the caller's save area to do
+ * a better job of representing the caller's state (note that
+ * that will be different for 32-bit and 64-bit, because of the
+ * different ABIs, though).
+ */
+_GLOBAL(xmon_save_regs)
+	PPC_STL	r0,0*SZL(r3)
+	PPC_STL	r2,2*SZL(r3)
+	PPC_STL	r3,3*SZL(r3)
+	PPC_STL	r4,4*SZL(r3)
+	PPC_STL	r5,5*SZL(r3)
+	PPC_STL	r6,6*SZL(r3)
+	PPC_STL	r7,7*SZL(r3)
+	PPC_STL	r8,8*SZL(r3)
+	PPC_STL	r9,9*SZL(r3)
+	PPC_STL	r10,10*SZL(r3)
+	PPC_STL	r11,11*SZL(r3)
+	PPC_STL	r12,12*SZL(r3)
+	PPC_STL	r13,13*SZL(r3)
+	PPC_STL	r14,14*SZL(r3)
+	PPC_STL	r15,15*SZL(r3)
+	PPC_STL	r16,16*SZL(r3)
+	PPC_STL	r17,17*SZL(r3)
+	PPC_STL	r18,18*SZL(r3)
+	PPC_STL	r19,19*SZL(r3)
+	PPC_STL	r20,20*SZL(r3)
+	PPC_STL	r21,21*SZL(r3)
+	PPC_STL	r22,22*SZL(r3)
+	PPC_STL	r23,23*SZL(r3)
+	PPC_STL	r24,24*SZL(r3)
+	PPC_STL	r25,25*SZL(r3)
+	PPC_STL	r26,26*SZL(r3)
+	PPC_STL	r27,27*SZL(r3)
+	PPC_STL	r28,28*SZL(r3)
+	PPC_STL	r29,29*SZL(r3)
+	PPC_STL	r30,30*SZL(r3)
+	PPC_STL	r31,31*SZL(r3)
+	/* go up one stack frame for SP */
+	PPC_LL	r4,0(r1)
+	PPC_STL	r4,1*SZL(r3)
+	/* get caller's LR */
+	PPC_LL	r0,LRSAVE(r4)
+	PPC_STL	r0,_NIP-STACK_FRAME_OVERHEAD(r3)
+	PPC_STL	r0,_LINK-STACK_FRAME_OVERHEAD(r3)
+	mfmsr	r0
+	PPC_STL	r0,_MSR-STACK_FRAME_OVERHEAD(r3)
+	mfctr	r0
+	PPC_STL	r0,_CTR-STACK_FRAME_OVERHEAD(r3)
+	mfxer	r0
+	PPC_STL	r0,_XER-STACK_FRAME_OVERHEAD(r3)
+	mfcr	r0
+	PPC_STL	r0,_CCR-STACK_FRAME_OVERHEAD(r3)
+	li	r0,0
+	PPC_STL	r0,_TRAP-STACK_FRAME_OVERHEAD(r3)
+	blr
+#endif
Index: clone3/arch/powerpc/mm/fault.c
===================================================================
--- clone3.orig/arch/powerpc/mm/fault.c
+++ clone3/arch/powerpc/mm/fault.c
@@ -167,10 +167,8 @@ int __kprobes do_page_fault(struct pt_re
 	if (notify_page_fault(regs))
 		return 0;
 
-	if (trap == 0x300) {
-		if (debugger_fault_handler(regs))
-			return 0;
-	}
+	if (unlikely(debugger_fault_handler(regs)))
+		return 0;
 
 	/* On a kernel SLB miss we can only check for a valid exception entry */
 	if (!user_mode(regs) && (address >= TASK_SIZE))
Index: clone3/arch/powerpc/xmon/Makefile
===================================================================
--- clone3.orig/arch/powerpc/xmon/Makefile
+++ clone3/arch/powerpc/xmon/Makefile
@@ -4,7 +4,7 @@ ifdef CONFIG_PPC64
 EXTRA_CFLAGS += -mno-minimal-toc
 endif
 
-obj-y			+= xmon.o setjmp.o start.o nonstdio.o
+obj-y			+= xmon.o start.o nonstdio.o
 
 ifdef CONFIG_XMON_DISASSEMBLY
 obj-y			+= ppc-dis.o ppc-opc.o
Index: clone3/arch/powerpc/xmon/setjmp.S
===================================================================
--- clone3.orig/arch/powerpc/xmon/setjmp.S
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright (C) 1996 Paul Mackerras.
- *
- *      This program is free software; you can redistribute it and/or
- *      modify it under the terms of the GNU General Public License
- *      as published by the Free Software Foundation; either version
- *      2 of the License, or (at your option) any later version.
- *
- * NOTE: assert(sizeof(buf) > 23 * sizeof(long))
- */
-#include <asm/processor.h>
-#include <asm/ppc_asm.h>
-#include <asm/asm-offsets.h>
-
-_GLOBAL(xmon_setjmp)
-	mflr	r0
-	PPC_STL	r0,0(r3)
-	PPC_STL	r1,SZL(r3)
-	PPC_STL	r2,2*SZL(r3)
-	mfcr	r0
-	PPC_STL	r0,3*SZL(r3)
-	PPC_STL	r13,4*SZL(r3)
-	PPC_STL	r14,5*SZL(r3)
-	PPC_STL	r15,6*SZL(r3)
-	PPC_STL	r16,7*SZL(r3)
-	PPC_STL	r17,8*SZL(r3)
-	PPC_STL	r18,9*SZL(r3)
-	PPC_STL	r19,10*SZL(r3)
-	PPC_STL	r20,11*SZL(r3)
-	PPC_STL	r21,12*SZL(r3)
-	PPC_STL	r22,13*SZL(r3)
-	PPC_STL	r23,14*SZL(r3)
-	PPC_STL	r24,15*SZL(r3)
-	PPC_STL	r25,16*SZL(r3)
-	PPC_STL	r26,17*SZL(r3)
-	PPC_STL	r27,18*SZL(r3)
-	PPC_STL	r28,19*SZL(r3)
-	PPC_STL	r29,20*SZL(r3)
-	PPC_STL	r30,21*SZL(r3)
-	PPC_STL	r31,22*SZL(r3)
-	li	r3,0
-	blr
-
-_GLOBAL(xmon_longjmp)
-	PPC_LCMPI r4,0
-	bne	1f
-	li	r4,1
-1:	PPC_LL	r13,4*SZL(r3)
-	PPC_LL	r14,5*SZL(r3)
-	PPC_LL	r15,6*SZL(r3)
-	PPC_LL	r16,7*SZL(r3)
-	PPC_LL	r17,8*SZL(r3)
-	PPC_LL	r18,9*SZL(r3)
-	PPC_LL	r19,10*SZL(r3)
-	PPC_LL	r20,11*SZL(r3)
-	PPC_LL	r21,12*SZL(r3)
-	PPC_LL	r22,13*SZL(r3)
-	PPC_LL	r23,14*SZL(r3)
-	PPC_LL	r24,15*SZL(r3)
-	PPC_LL	r25,16*SZL(r3)
-	PPC_LL	r26,17*SZL(r3)
-	PPC_LL	r27,18*SZL(r3)
-	PPC_LL	r28,19*SZL(r3)
-	PPC_LL	r29,20*SZL(r3)
-	PPC_LL	r30,21*SZL(r3)
-	PPC_LL	r31,22*SZL(r3)
-	PPC_LL	r0,3*SZL(r3)
-	mtcrf	0x38,r0
-	PPC_LL	r0,0(r3)
-	PPC_LL	r1,SZL(r3)
-	PPC_LL	r2,2*SZL(r3)
-	mtlr	r0
-	mr	r3,r4
-	blr
-
-/*
- * Grab the register values as they are now.
- * This won't do a particularily good job because we really
- * want our caller's caller's registers, and our caller has
- * already executed its prologue.
- * ToDo: We could reach back into the caller's save area to do
- * a better job of representing the caller's state (note that
- * that will be different for 32-bit and 64-bit, because of the
- * different ABIs, though).
- */
-_GLOBAL(xmon_save_regs)
-	PPC_STL	r0,0*SZL(r3)
-	PPC_STL	r2,2*SZL(r3)
-	PPC_STL	r3,3*SZL(r3)
-	PPC_STL	r4,4*SZL(r3)
-	PPC_STL	r5,5*SZL(r3)
-	PPC_STL	r6,6*SZL(r3)
-	PPC_STL	r7,7*SZL(r3)
-	PPC_STL	r8,8*SZL(r3)
-	PPC_STL	r9,9*SZL(r3)
-	PPC_STL	r10,10*SZL(r3)
-	PPC_STL	r11,11*SZL(r3)
-	PPC_STL	r12,12*SZL(r3)
-	PPC_STL	r13,13*SZL(r3)
-	PPC_STL	r14,14*SZL(r3)
-	PPC_STL	r15,15*SZL(r3)
-	PPC_STL	r16,16*SZL(r3)
-	PPC_STL	r17,17*SZL(r3)
-	PPC_STL	r18,18*SZL(r3)
-	PPC_STL	r19,19*SZL(r3)
-	PPC_STL	r20,20*SZL(r3)
-	PPC_STL	r21,21*SZL(r3)
-	PPC_STL	r22,22*SZL(r3)
-	PPC_STL	r23,23*SZL(r3)
-	PPC_STL	r24,24*SZL(r3)
-	PPC_STL	r25,25*SZL(r3)
-	PPC_STL	r26,26*SZL(r3)
-	PPC_STL	r27,27*SZL(r3)
-	PPC_STL	r28,28*SZL(r3)
-	PPC_STL	r29,29*SZL(r3)
-	PPC_STL	r30,30*SZL(r3)
-	PPC_STL	r31,31*SZL(r3)
-	/* go up one stack frame for SP */
-	PPC_LL	r4,0(r1)
-	PPC_STL	r4,1*SZL(r3)
-	/* get caller's LR */
-	PPC_LL	r0,LRSAVE(r4)
-	PPC_STL	r0,_NIP-STACK_FRAME_OVERHEAD(r3)
-	PPC_STL	r0,_LINK-STACK_FRAME_OVERHEAD(r3)
-	mfmsr	r0
-	PPC_STL	r0,_MSR-STACK_FRAME_OVERHEAD(r3)
-	mfctr	r0
-	PPC_STL	r0,_CTR-STACK_FRAME_OVERHEAD(r3)
-	mfxer	r0
-	PPC_STL	r0,_XER-STACK_FRAME_OVERHEAD(r3)
-	mfcr	r0
-	PPC_STL	r0,_CCR-STACK_FRAME_OVERHEAD(r3)
-	li	r0,0
-	PPC_STL	r0,_TRAP-STACK_FRAME_OVERHEAD(r3)
-	blr
Index: clone3/arch/powerpc/xmon/xmon.c
===================================================================
--- clone3.orig/arch/powerpc/xmon/xmon.c
+++ clone3/arch/powerpc/xmon/xmon.c
@@ -40,6 +40,7 @@
 #include <asm/spu.h>
 #include <asm/spu_priv1.h>
 #include <asm/firmware.h>
+#include <asm/setjmp.h>
 
 #ifdef CONFIG_PPC64
 #include <asm/hvcall.h>
@@ -71,12 +72,9 @@ static unsigned long ncsum = 4096;
 static int termch;
 static char tmpstr[128];
 
-#define JMP_BUF_LEN	23
-static long bus_error_jmp[JMP_BUF_LEN];
+static long bus_error_jmp[SETJMP_BUF_LEN];
 static int catch_memory_errors;
 static long *xmon_fault_jmp[NR_CPUS];
-#define setjmp xmon_setjmp
-#define longjmp xmon_longjmp
 
 /* Breakpoint stuff */
 struct bpt {
@@ -162,8 +160,6 @@ int xmon_no_auto_backtrace;
 extern void xmon_enter(void);
 extern void xmon_leave(void);
 
-extern long setjmp(long *);
-extern void longjmp(long *, long);
 extern void xmon_save_regs(struct pt_regs *);
 
 #ifdef CONFIG_PPC64
@@ -338,7 +334,7 @@ static int xmon_core(struct pt_regs *reg
 {
 	int cmd = 0;
 	struct bpt *bp;
-	long recurse_jmp[JMP_BUF_LEN];
+	long recurse_jmp[SETJMP_BUF_LEN];
 	unsigned long offset;
 	unsigned long flags;
 #ifdef CONFIG_SMP
@@ -1428,7 +1424,7 @@ void prregs(struct pt_regs *fp)
 			sync();
 			regs = *(struct pt_regs *)base;
 			sync();
-			__delay(200);
+			__delay(SETJMP_MACHINE_CHECK_DELAY);
 		} else {
 			catch_memory_errors = 0;
 			printf("*** Error reading registers from "REG"\n",
@@ -1497,8 +1493,7 @@ void cacheflush(void)
 				cinval((void *) adrs);
 		}
 		sync();
-		/* wait a little while to see if we get a machine check */
-		__delay(200);
+		__delay(SETJMP_MACHINE_CHECK_DELAY);
 	}
 	catch_memory_errors = 0;
 }
@@ -1533,8 +1528,7 @@ read_spr(int n)
 		ret = code();
 
 		sync();
-		/* wait a little while to see if we get a machine check */
-		__delay(200);
+		__delay(SETJMP_MACHINE_CHECK_DELAY);
 		n = size;
 	}
 
@@ -1569,8 +1563,7 @@ write_spr(int n, unsigned long val)
 		code(val);
 
 		sync();
-		/* wait a little while to see if we get a machine check */
-		__delay(200);
+		__delay(SETJMP_MACHINE_CHECK_DELAY);
 		n = size;
 	}
 }
@@ -1676,8 +1669,7 @@ mread(unsigned long adrs, void *buf, int
 			}
 		}
 		sync();
-		/* wait a little while to see if we get a machine check */
-		__delay(200);
+		__delay(SETJMP_MACHINE_CHECK_DELAY);
 		n = size;
 	}
 	catch_memory_errors = 0;
@@ -1713,8 +1705,7 @@ mwrite(unsigned long adrs, void *buf, in
 			}
 		}
 		sync();
-		/* wait a little while to see if we get a machine check */
-		__delay(200);
+		__delay(SETJMP_MACHINE_CHECK_DELAY);
 		n = size;
 	} else {
 		printf("*** Error writing address %x\n", adrs + n);
@@ -2521,8 +2512,7 @@ static void xmon_print_symbol(unsigned l
 		name = kallsyms_lookup(address, &size, &offset, &modname,
 				       tmpstr);
 		sync();
-		/* wait a little while to see if we get a machine check */
-		__delay(200);
+		__delay(SETJMP_MACHINE_CHECK_DELAY);
 	}
 
 	catch_memory_errors = 0;
@@ -2777,7 +2767,7 @@ static void stop_spus(void)
 			spu_mfc_sr1_set(spu, tmp);
 
 			sync();
-			__delay(200);
+			__delay(SETJMP_MACHINE_CHECK_DELAY);
 
 			spu_info[i].stopped_ok = 1;
 
@@ -2817,7 +2807,7 @@ static void restart_spus(void)
 					spu_info[i].saved_spu_runcntl_RW);
 
 			sync();
-			__delay(200);
+			__delay(SETJMP_MACHINE_CHECK_DELAY);
 
 			printf("Restarted spu %.2d\n", i);
 		} else {
@@ -2837,7 +2827,7 @@ do {									\
 		printf("  %-*s = "format"\n", DUMP_WIDTH,		\
 				#field, value);				\
 		sync();							\
-		__delay(200);						\
+		__delay(SETJMP_MACHINE_CHECK_DELAY);			\
 	} else {							\
 		catch_memory_errors = 0;				\
 		printf("  %-*s = *** Error reading field.\n",		\
@@ -2899,7 +2889,7 @@ static void dump_spu_ls(unsigned long nu
 		sync();
 		ls_addr = (unsigned long)spu_info[num].spu->local_store;
 		sync();
-		__delay(200);
+		__delay(SETJMP_MACHINE_CHECK_DELAY);
 	} else {
 		catch_memory_errors = 0;
 		printf("*** Error: accessing spu info for spu %d\n", num);
Index: clone3/include/asm-powerpc/setjmp.h
===================================================================
--- /dev/null
+++ clone3/include/asm-powerpc/setjmp.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2007 Michael Neuling
+ *
+ *      This program is free software; you can redistribute it and/or
+ *      modify it under the terms of the GNU General Public License
+ *      as published by the Free Software Foundation; either version
+ *      2 of the License, or (at your option) any later version.
+ *
+ */
+#ifndef _ASM_POWERPC_SETJMP_H
+#define _ASM_POWERPC_SETJMP_H
+
+#define SETJMP_BUF_LEN    23
+#define SETJMP_MACHINE_CHECK_DELAY    200
+
+extern long setjmp(long *);
+extern void longjmp(long *, long);
+
+#endif /* _ASM_POWERPC_SETJMP_H */

^ permalink raw reply

* [PATCH 2/2] kdump shutdown hook support
From: Michael Neuling @ 2007-12-13  3:16 UTC (permalink / raw)
  To: Paul Mackerras; +Cc: linuxppc-dev, Michael Neuling, RAISCH, THEMANN
In-Reply-To: <1197515777.834799.307108384755.qpush@coopers>

This adds hooks into the default_machine_crash_shutdown so drivers can
register a function to be run in the first kernel before we hand off
to the second kernel.  This should only be used in exceptional
circumstances, like where the device can't be reset in the second
kernel alone (as is the case with eHEA).  To emphasize this, the
number of handles allowed to be registered is currently #def to 1.

This uses the setjmp/longjmp code to call out to the registered hooks,
so any bogus exceptions we encounter will hopefully be recoverable.  

Tested with bogus data and instruction exceptions.

Signed-off-by: Michael Neuling <mikey@neuling.org>
---

 arch/powerpc/kernel/crash.c |  102 +++++++++++++++++++++++++++++++++++++++++---
 include/asm-powerpc/kexec.h |    3 +
 2 files changed, 100 insertions(+), 5 deletions(-)

Index: clone3/arch/powerpc/kernel/crash.c
===================================================================
--- clone3.orig/arch/powerpc/kernel/crash.c
+++ clone3/arch/powerpc/kernel/crash.c
@@ -32,6 +32,8 @@
 #include <asm/lmb.h>
 #include <asm/firmware.h>
 #include <asm/smp.h>
+#include <asm/system.h>
+#include <asm/setjmp.h>
 
 #ifdef DEBUG
 #include <asm/udbg.h>
@@ -45,6 +47,11 @@ int crashing_cpu = -1;
 static cpumask_t cpus_in_crash = CPU_MASK_NONE;
 cpumask_t cpus_in_sr = CPU_MASK_NONE;
 
+#define CRASH_HANDLER_MAX 1
+/* NULL terminated list of shutdown handles */
+static crash_shutdown_t crash_shutdown_handles[CRASH_HANDLER_MAX+1];
+static DEFINE_SPINLOCK(crash_handers_lock);
+
 #ifdef CONFIG_SMP
 static atomic_t enter_on_soft_reset = ATOMIC_INIT(0);
 
@@ -285,9 +292,72 @@ static inline void crash_kexec_stop_spus
 }
 #endif /* CONFIG_SPU_BASE */
 
+/*
+ * Register a function to be called on shutdown.  Only use this if you
+ * can't reset your device in the second kernel.
+ */
+int crash_shutdown_register(crash_shutdown_t handler)
+{
+	unsigned int i, rc;
+
+	spin_lock(&crash_handers_lock);
+	for (i = 0 ; i < CRASH_HANDLER_MAX; i++)
+		if (!crash_shutdown_handles[i]) {
+			/* Insert handle at first empty entry */
+			crash_shutdown_handles[i] = handler;
+			rc = 0;
+			break;
+		}
+
+	if (i == CRASH_HANDLER_MAX) {
+		printk(KERN_ERR "Crash shutdown handles full, "
+		       "not registered.\n");
+		rc = 1;
+	}
+
+	spin_unlock(&crash_handers_lock);
+	return rc;
+}
+EXPORT_SYMBOL(crash_shutdown_register);
+
+int crash_shutdown_unregister(crash_shutdown_t handler)
+{
+	unsigned int i, rc;
+
+	spin_lock(&crash_handers_lock);
+	for (i = 0 ; i < CRASH_HANDLER_MAX; i++)
+		if (crash_shutdown_handles[i] == handler)
+			break;
+
+	if (i == CRASH_HANDLER_MAX) {
+		printk(KERN_ERR "Crash shutdown handle not found\n");
+		rc = 1;
+	} else {
+		/* Shift handles down */
+		for (; crash_shutdown_handles[i]; i++)
+			crash_shutdown_handles[i] =
+				crash_shutdown_handles[i+1];
+		rc = 0;
+	}
+
+	spin_unlock(&crash_handers_lock);
+	return rc;
+}
+EXPORT_SYMBOL(crash_shutdown_unregister);
+
+static unsigned long crash_shutdown_buf[SETJMP_BUF_LEN];
+
+static int handle_fault(struct pt_regs *regs)
+{
+	longjmp(crash_shutdown_buf, 1);
+	return 0;
+}
+
 void default_machine_crash_shutdown(struct pt_regs *regs)
 {
-	unsigned int irq;
+	unsigned int i;
+	int (*old_handler)(struct pt_regs *regs);
+
 
 	/*
 	 * This function is only called after the system
@@ -301,15 +371,37 @@ void default_machine_crash_shutdown(stru
 	 */
 	hard_irq_disable();
 
-	for_each_irq(irq) {
-		struct irq_desc *desc = irq_desc + irq;
+	for_each_irq(i) {
+		struct irq_desc *desc = irq_desc + i;
 
 		if (desc->status & IRQ_INPROGRESS)
-			desc->chip->eoi(irq);
+			desc->chip->eoi(i);
 
 		if (!(desc->status & IRQ_DISABLED))
-			desc->chip->disable(irq);
+			desc->chip->disable(i);
+	}
+
+	/*
+	 * Call registered shutdown routines savely.  Swap out
+	 * __debugger_fault_handler, and replace on exit.
+	 */
+	old_handler = __debugger_fault_handler;
+	__debugger_fault_handler = handle_fault;
+	for (i = 0; crash_shutdown_handles[i]; i++) {
+		if (setjmp(crash_shutdown_buf) == 0) {
+			/*
+			 * Insert syncs and delay to ensure
+			 * instructions in the dangerous region don't
+			 * leak away from this protected region.
+			 */
+			asm volatile("sync; isync");
+			/* dangerous region */
+			crash_shutdown_handles[i]();
+			asm volatile("sync; isync");
+			__delay(SETJMP_MACHINE_CHECK_DELAY);
+		}
 	}
+	__debugger_fault_handler = old_handler;
 
 	/*
 	 * Make a note of crashing cpu. Will be used in machine_kexec
Index: clone3/include/asm-powerpc/kexec.h
===================================================================
--- clone3.orig/include/asm-powerpc/kexec.h
+++ clone3/include/asm-powerpc/kexec.h
@@ -123,6 +123,9 @@ struct pt_regs;
 extern void default_machine_kexec(struct kimage *image);
 extern int default_machine_kexec_prepare(struct kimage *image);
 extern void default_machine_crash_shutdown(struct pt_regs *regs);
+typedef void (*crash_shutdown_t)(void);
+extern int crash_shutdown_register(crash_shutdown_t handler);
+extern int crash_shutdown_unregister(crash_shutdown_t handler);
 
 extern void machine_kexec_simple(struct kimage *image);
 extern void crash_kexec_secondary(struct pt_regs *regs);

^ permalink raw reply

* printf - How does it work?
From: Siva Prasad @ 2007-12-13  3:23 UTC (permalink / raw)
  To: linuxppc-dev

[-- Attachment #1: Type: text/plain, Size: 385 bytes --]

Hi,

 

Can some one point me to an URL or some thing that explains how exactly
the characters from printf in user program gets printed on to the
console. Some thing like "it goes to /dev/console, and kernel reads from
....." would be great, so that I can trace it out.

 

Is there any way, we can get access to those prints from the kernel?

 

Thanks

Siva

 


[-- Attachment #2: Type: text/html, Size: 2490 bytes --]

^ permalink raw reply

* [PATCH] Convert adb.c to use kthread API and not spin on ADB requests
From: Paul Mackerras @ 2007-12-13  4:11 UTC (permalink / raw)
  To: linuxppc-dev

This converts adb.c to use the kthread API.

It also changes adb_request so that if the ADBREQ_SYNC flag is
specified, we now sleep waiting for the request to finish using an
on-stack completion rather than spinning.  To implement this, we now
require that if the ADBREQ_SYNC flag is set, the `done' parameter must
be NULL.  All of the existing callers of adb_request that pass
ADBREQ_SYNC appear to be in process context and have done == NULL.
Doing this allows us to get rid of an awful hack in adb_request()
where we used to test whether the request was coming from the adb
probe task and use a completion if it was, and otherwise spin.

This also gets rid of a static request block that was used if the req
parameter to adb_request was NULL.  None of the callers do that any
more, so the static request block is no longer necessary.

Signed-off-by: Paul Mackerras <paulus@samba.org>
---
diff --git a/drivers/macintosh/adb.c b/drivers/macintosh/adb.c
index 7b892f4..5ae28f0 100644
--- a/drivers/macintosh/adb.c
+++ b/drivers/macintosh/adb.c
@@ -35,6 +35,7 @@
 #include <linux/spinlock.h>
 #include <linux/completion.h>
 #include <linux/device.h>
+#include <linux/kthread.h>
 
 #include <asm/uaccess.h>
 #include <asm/semaphore.h>
@@ -82,9 +83,7 @@ struct adb_driver *adb_controller;
 BLOCKING_NOTIFIER_HEAD(adb_client_list);
 static int adb_got_sleep;
 static int adb_inited;
-static pid_t adb_probe_task_pid;
 static DECLARE_MUTEX(adb_probe_mutex);
-static struct completion adb_probe_task_comp;
 static int sleepy_trackpad;
 static int autopoll_devs;
 int __adb_probe_sync;
@@ -126,16 +125,6 @@ static void printADBreply(struct adb_request *req)
 }
 #endif
 
-
-static __inline__ void adb_wait_ms(unsigned int ms)
-{
-	if (current->pid && adb_probe_task_pid &&
-	  adb_probe_task_pid == current->pid)
-		msleep(ms);
-	else
-		mdelay(ms);
-}
-
 static int adb_scan_bus(void)
 {
 	int i, highFree=0, noMovement;
@@ -240,13 +229,10 @@ static int adb_scan_bus(void)
 static int
 adb_probe_task(void *x)
 {
-	strcpy(current->comm, "kadbprobe");
-
 	printk(KERN_INFO "adb: starting probe task...\n");
 	do_adb_reset_bus();
 	printk(KERN_INFO "adb: finished probe task...\n");
 
-	adb_probe_task_pid = 0;
 	up(&adb_probe_mutex);
 
 	return 0;
@@ -255,7 +241,7 @@ adb_probe_task(void *x)
 static void
 __adb_probe_task(struct work_struct *bullshit)
 {
-	adb_probe_task_pid = kernel_thread(adb_probe_task, NULL, SIGCHLD | CLONE_KERNEL);
+	kthread_run(adb_probe_task, NULL, "kadbprobe");
 }
 
 static DECLARE_WORK(adb_reset_work, __adb_probe_task);
@@ -341,7 +327,6 @@ int __init adb_init(void)
 			sleepy_trackpad = 1;
 #endif /* CONFIG_PPC */
 
-		init_completion(&adb_probe_task_comp);
 		adbdev_init();
 		adb_reset_bus();
 	}
@@ -366,7 +351,7 @@ do_adb_reset_bus(void)
 
 	if (sleepy_trackpad) {
 		/* Let the trackpad settle down */
-		adb_wait_ms(500);
+		msleep(500);
 	}
 
 	down(&adb_handler_sem);
@@ -382,7 +367,7 @@ do_adb_reset_bus(void)
 
 	if (sleepy_trackpad) {
 		/* Let the trackpad settle down */
-		adb_wait_ms(1500);
+		msleep(1500);
 	}
 
 	if (!ret) {
@@ -406,41 +391,27 @@ adb_poll(void)
 	adb_controller->poll();
 }
 
-static void
-adb_probe_wakeup(struct adb_request *req)
+static void adb_sync_req_done(struct adb_request *req)
 {
-	complete(&adb_probe_task_comp);
-}
+	struct completion *comp = req->arg;
 
-/* Static request used during probe */
-static struct adb_request adb_sreq;
-static unsigned long adb_sreq_lock; // Use semaphore ! */ 
+	complete(comp);
+}
 
 int
 adb_request(struct adb_request *req, void (*done)(struct adb_request *),
 	    int flags, int nbytes, ...)
 {
 	va_list list;
-	int i, use_sreq;
+	int i;
 	int rc;
+	struct completion comp;
 
 	if ((adb_controller == NULL) || (adb_controller->send_request == NULL))
 		return -ENXIO;
 	if (nbytes < 1)
 		return -EINVAL;
-	if (req == NULL && (flags & ADBREQ_NOSEND))
-		return -EINVAL;
-	
-	if (req == NULL) {
-		if (test_and_set_bit(0,&adb_sreq_lock)) {
-			printk("adb.c: Warning: contention on static request !\n");
-			return -EPERM;
-		}
-		req = &adb_sreq;
-		flags |= ADBREQ_SYNC;
-		use_sreq = 1;
-	} else
-		use_sreq = 0;
+
 	req->nbytes = nbytes+1;
 	req->done = done;
 	req->reply_expected = flags & ADBREQ_REPLY;
@@ -453,25 +424,18 @@ adb_request(struct adb_request *req, void (*done)(struct adb_request *),
 	if (flags & ADBREQ_NOSEND)
 		return 0;
 
-	/* Synchronous requests send from the probe thread cause it to
-	 * block. Beware that the "done" callback will be overriden !
-	 */
-	if ((flags & ADBREQ_SYNC) &&
-	    (current->pid && adb_probe_task_pid &&
-	    adb_probe_task_pid == current->pid)) {
-		req->done = adb_probe_wakeup;
-		rc = adb_controller->send_request(req, 0);
-		if (rc || req->complete)
-			goto bail;
-		wait_for_completion(&adb_probe_task_comp);
-		rc = 0;
-		goto bail;
+	/* Synchronous requests block using an on-stack completion */
+	if (flags & ADBREQ_SYNC) {
+		WARN_ON(done);
+		req->done = adb_sync_req_done;
+		req->arg = &comp;
+		init_completion(&comp);
 	}
 
-	rc = adb_controller->send_request(req, flags & ADBREQ_SYNC);
-bail:
-	if (use_sreq)
-		clear_bit(0, &adb_sreq_lock);
+	rc = adb_controller->send_request(req, 0);
+
+	if ((flags & ADBREQ_SYNC) && !rc && !req->complete)
+		wait_for_completion(&comp);
 
 	return rc;
 }

^ permalink raw reply related

* RE: Xilinx devicetrees
From: Stephen Neuendorffer @ 2007-12-13  4:52 UTC (permalink / raw)
  To: Koss, Mike (Mission Systems), David H. Lynch Jr., Grant Likely,
	linuxppc-embedded
In-Reply-To: <EDAE140DF1B2FC42B5867C22CA0B333F0A58E4@XMBIL132.northgrum.com>

[-- Attachment #1: Type: text/plain, Size: 3699 bytes --]




-----Original Message-----
From: Koss, Mike (Mission Systems) [mailto:mike.koss@ngc.com]
Sent: Mon 11/26/2007 7:31 AM
To: Stephen Neuendorffer; David H. Lynch Jr.; Grant Likely; linuxppc-embedded
Subject: RE: Xilinx devicetrees
 
Time for my $.02, since I am heavily weighting my future designs on the
use of the device trees. :) (and b/c I don't check my work e-mail from
home ;P)

________________________________


SN> As Grant says, the dynamic detection doesn't have to be done in the
boot loader, it could be done in the platform code.  You can largely
ignore 
SN> the device trees, or always boot with a core device tree and figure
it all out later (perhaps using version registers).  I anticipate that 
SN> the 'standard flow' will have standard platform code for any board
that uses a complete device tree.  If you have the need to do something 
SN> extraordinary, then you should feel free to hack away...  However,
It doesn't seem to me to be really necessary in your case, assuming that

SN> the device tree is packaged (somehow, TBD) along with the bitstream.
 
> I don't know if packaging the device tree with the bitstream is the best
> way to go. It's possible that it could lead to headaches for certain
> systems that have security restrictions. The same could be said for
> using it w/ the SystemACE to load it into RAM after the image. (which is
> what I'm currently doing for my 2 linux images in lieu of a true on-chip
> bootloader). I am already taking the security concerns into account for
> future revisions of the hardware wrt to using a SystemACE, and am
> planning on moving the device trees into NV storage like FLASH.

'with' not 'in'. either using SystemAce, or a flash image.

> One solution I've been thinking through (in lieu of direct support from
> EDK) is to use a tcl script with xps to traverse the hardware tree and
> generate the device tree. It seems like it should be relatively trivial
> to obtain the information. It's just going to be a pain to write all the
> handlers for each different linux driver: temac, interrupt controller,
> DMA controller, etc.
> In reality the best way to handle this would be to have EDK generate the
> device tree as part of the library/bsp build process. 
We have a python script to do this.  The main problem with just looking at the mhs file is that you lose all the defaults for each IP.  Hence, we've also written a BSP generator to do this.  both are at git://git.xilinx.com/gen-mhs-devtree.py
Once I can verify that they work in the mainline tree, I'll be sending out the patches that make this all work.

> Now, what I'd like
> to see with regards to this is the ability to change the handler for the
> generating a specific device information. An example could be the temac.
> If at some point in the future the temac needs new/more information to
> support its configuration/run-time then having to get a patch from
> Xilinx for a EDK is way too slow. The developers should be giving the
> opportunity to inject a new handler into the various parts of the device
> tree generation. That way when the kernel patch is submitted, an EDK
> device generator patch will be submitted at the same time to keep
> everything in sync.

Interesting idea..  I've been trying to figure out how to architect this better, but it requires some information passing within EDK that isnot today supported.  I don't see at all how to synchronize this with patches to the kernel, tho.  My approach is to describe the hardware as completely and faithfully as we can (given the information in EDK), and let the drivers do whatever with it that they want to.

Steve



[-- Attachment #2: Type: text/html, Size: 4504 bytes --]

^ permalink raw reply

* [PATCH] Convert therm_pm72.c to use the kthread API
From: Paul Mackerras @ 2007-12-13  4:54 UTC (permalink / raw)
  To: linuxppc-dev

This converts the therm_pm72.c driver to use the kthread API.  I
thought about making it use kthread_stop() instead of the `state'
variable and the `ctrl_complete' completion, but that isn't simple and
will require changing the way that `state' is used.

Signed-off-by: Paul Mackerras <paulus@samba.org>
---
diff --git a/drivers/macintosh/therm_pm72.c b/drivers/macintosh/therm_pm72.c
index e43554e..6fadc9a 100644
--- a/drivers/macintosh/therm_pm72.c
+++ b/drivers/macintosh/therm_pm72.c
@@ -121,6 +121,7 @@
 #include <linux/reboot.h>
 #include <linux/kmod.h>
 #include <linux/i2c.h>
+#include <linux/kthread.h>
 #include <asm/prom.h>
 #include <asm/machdep.h>
 #include <asm/io.h>
@@ -161,7 +162,7 @@ static struct slots_pid_state		slots_state;
 static int				state;
 static int				cpu_count;
 static int				cpu_pid_type;
-static pid_t				ctrl_task;
+static struct task_struct		*ctrl_task;
 static struct completion		ctrl_complete;
 static int				critical_state;
 static int				rackmac;
@@ -1779,8 +1780,6 @@ static int call_critical_overtemp(void)
  */
 static int main_control_loop(void *x)
 {
-	daemonize("kfand");
-
 	DBG("main_control_loop started\n");
 
 	down(&driver_lock);
@@ -1956,7 +1955,7 @@ static void start_control_loops(void)
 {
 	init_completion(&ctrl_complete);
 
-	ctrl_task = kernel_thread(main_control_loop, NULL, SIGCHLD | CLONE_KERNEL);
+	ctrl_task = kthread_run(main_control_loop, NULL, "kfand");
 }
 
 /*
@@ -1964,7 +1963,7 @@ static void start_control_loops(void)
  */
 static void stop_control_loops(void)
 {
-	if (ctrl_task != 0)
+	if (ctrl_task)
 		wait_for_completion(&ctrl_complete);
 }
 

^ permalink raw reply related

* [PATCH] Convert therm_windtunnel.c to use the kthread API
From: Paul Mackerras @ 2007-12-13  4:57 UTC (permalink / raw)
  To: linuxppc-dev

This is fairly straightforward, and lets us get rid of x.completion
as well.

Signed-off-by: Paul Mackerras <paulus@samba.org>
---
diff --git a/drivers/macintosh/therm_windtunnel.c b/drivers/macintosh/therm_windtunnel.c
index 5452da1..3722402 100644
--- a/drivers/macintosh/therm_windtunnel.c
+++ b/drivers/macintosh/therm_windtunnel.c
@@ -36,6 +36,7 @@
 #include <linux/i2c.h>
 #include <linux/slab.h>
 #include <linux/init.h>
+#include <linux/kthread.h>
 
 #include <asm/prom.h>
 #include <asm/machdep.h>
@@ -61,8 +62,7 @@ I2C_CLIENT_INSMOD;
 
 static struct {
 	volatile int		running;
-	struct completion	completion;
-	pid_t			poll_task;
+	struct task_struct	*poll_task;
 	
 	struct semaphore 	lock;
 	struct of_device	*of_dev;
@@ -282,27 +282,27 @@ restore_regs( void )
 	write_reg( x.fan, 0x00, x.r0, 1 );
 }
 
-static int
-control_loop( void *dummy )
+static int control_loop(void *dummy)
 {
-	daemonize("g4fand");
-
-	down( &x.lock );
+	down(&x.lock);
 	setup_hardware();
+	up(&x.lock);
 
-	while( x.running ) {
-		up( &x.lock );
-
+	for (;;) {
 		msleep_interruptible(8000);
-		
-		down( &x.lock );
+		if (kthread_should_stop())
+			break;
+
+		down(&x.lock);
 		poll_temp();
+		up(&x.lock);
 	}
 
+	down(&x.lock);
 	restore_regs();
-	up( &x.lock );
+	up(&x.lock);
 
-	complete_and_exit( &x.completion, 0 );
+	return 0;
 }
 
 
@@ -322,8 +322,7 @@ do_attach( struct i2c_adapter *adapter )
 		ret = i2c_probe( adapter, &addr_data, &do_probe );
 		if( x.thermostat && x.fan ) {
 			x.running = 1;
-			init_completion( &x.completion );
-			x.poll_task = kernel_thread( control_loop, NULL, SIGCHLD | CLONE_KERNEL );
+			x.poll_task = kthread_run(control_loop, NULL, "g4fand");
 		}
 	}
 	return ret;
@@ -339,7 +338,8 @@ do_detach( struct i2c_client *client )
 	else {
 		if( x.running ) {
 			x.running = 0;
-			wait_for_completion( &x.completion );
+			kthread_stop(x.poll_task);
+			x.poll_task = NULL;
 		}
 		if( client == x.thermostat )
 			x.thermostat = NULL;

^ permalink raw reply related

* Re: [PATCH 2/2] kdump shutdown hook support
From: Olof Johansson @ 2007-12-13  5:12 UTC (permalink / raw)
  To: Michael Neuling; +Cc: linuxppc-dev, RAISCH, Paul Mackerras, THEMANN
In-Reply-To: <20071213031618.DBD207038C@localhost.localdomain>

Hi,

On Thu, Dec 13, 2007 at 02:16:18PM +1100, Michael Neuling wrote:

> Index: clone3/arch/powerpc/kernel/crash.c
> ===================================================================
> --- clone3.orig/arch/powerpc/kernel/crash.c
> +++ clone3/arch/powerpc/kernel/crash.c
> @@ -32,6 +32,8 @@
>  #include <asm/lmb.h>
>  #include <asm/firmware.h>
>  #include <asm/smp.h>
> +#include <asm/system.h>
> +#include <asm/setjmp.h>
>  
>  #ifdef DEBUG
>  #include <asm/udbg.h>
> @@ -45,6 +47,11 @@ int crashing_cpu = -1;
>  static cpumask_t cpus_in_crash = CPU_MASK_NONE;
>  cpumask_t cpus_in_sr = CPU_MASK_NONE;
>  
> +#define CRASH_HANDLER_MAX 1
> +/* NULL terminated list of shutdown handles */
> +static crash_shutdown_t crash_shutdown_handles[CRASH_HANDLER_MAX+1];
> +static DEFINE_SPINLOCK(crash_handers_lock);

How about just using 'handlers' instead of 'handers' and 'handles'? :)

>  static atomic_t enter_on_soft_reset = ATOMIC_INIT(0);
>  
> @@ -285,9 +292,72 @@ static inline void crash_kexec_stop_spus
>  }
>  #endif /* CONFIG_SPU_BASE */
>  
> +/*
> + * Register a function to be called on shutdown.  Only use this if you
> + * can't reset your device in the second kernel.
> + */
> +int crash_shutdown_register(crash_shutdown_t handler)
> +{
> +	unsigned int i, rc;
> +
> +	spin_lock(&crash_handers_lock);
> +	for (i = 0 ; i < CRASH_HANDLER_MAX; i++)
> +		if (!crash_shutdown_handles[i]) {
> +			/* Insert handle at first empty entry */
> +			crash_shutdown_handles[i] = handler;
> +			rc = 0;
> +			break;
> +		}
> +
> +	if (i == CRASH_HANDLER_MAX) {
> +		printk(KERN_ERR "Crash shutdown handles full, "
> +		       "not registered.\n");
> +		rc = 1;
> +	}
> +
> +	spin_unlock(&crash_handers_lock);
> +	return rc;
> +}
> +EXPORT_SYMBOL(crash_shutdown_register);
> +
> +int crash_shutdown_unregister(crash_shutdown_t handler)
> +{
> +	unsigned int i, rc;
> +
> +	spin_lock(&crash_handers_lock);
> +	for (i = 0 ; i < CRASH_HANDLER_MAX; i++)
> +		if (crash_shutdown_handles[i] == handler)
> +			break;
> +
> +	if (i == CRASH_HANDLER_MAX) {
> +		printk(KERN_ERR "Crash shutdown handle not found\n");
> +		rc = 1;
> +	} else {
> +		/* Shift handles down */
> +		for (; crash_shutdown_handles[i]; i++)
> +			crash_shutdown_handles[i] =
> +				crash_shutdown_handles[i+1];
> +		rc = 0;
> +	}
> +
> +	spin_unlock(&crash_handers_lock);
> +	return rc;
> +}
> +EXPORT_SYMBOL(crash_shutdown_unregister);
> +
> +static unsigned long crash_shutdown_buf[SETJMP_BUF_LEN];
> +
> +static int handle_fault(struct pt_regs *regs)
> +{
> +	longjmp(crash_shutdown_buf, 1);
> +	return 0;
> +}
> +
>  void default_machine_crash_shutdown(struct pt_regs *regs)
>  {
> -	unsigned int irq;
> +	unsigned int i;
> +	int (*old_handler)(struct pt_regs *regs);
> +
>  
>  	/*
>  	 * This function is only called after the system
> @@ -301,15 +371,37 @@ void default_machine_crash_shutdown(stru
>  	 */
>  	hard_irq_disable();
>  
> -	for_each_irq(irq) {
> -		struct irq_desc *desc = irq_desc + irq;
> +	for_each_irq(i) {
> +		struct irq_desc *desc = irq_desc + i;
>  
>  		if (desc->status & IRQ_INPROGRESS)
> -			desc->chip->eoi(irq);
> +			desc->chip->eoi(i);
>  
>  		if (!(desc->status & IRQ_DISABLED))
> -			desc->chip->disable(irq);
> +			desc->chip->disable(i);
> +	}
> +
> +	/*
> +	 * Call registered shutdown routines savely.  Swap out
> +	 * __debugger_fault_handler, and replace on exit.
> +	 */
> +	old_handler = __debugger_fault_handler;
> +	__debugger_fault_handler = handle_fault;
> +	for (i = 0; crash_shutdown_handles[i]; i++) {
> +		if (setjmp(crash_shutdown_buf) == 0) {
> +			/*
> +			 * Insert syncs and delay to ensure
> +			 * instructions in the dangerous region don't
> +			 * leak away from this protected region.
> +			 */
> +			asm volatile("sync; isync");
> +			/* dangerous region */
> +			crash_shutdown_handles[i]();
> +			asm volatile("sync; isync");
> +			__delay(SETJMP_MACHINE_CHECK_DELAY);

Where is this defined?

> +		}
>  	}
> +	__debugger_fault_handler = old_handler;
>  
>  	/*
>  	 * Make a note of crashing cpu. Will be used in machine_kexec
> Index: clone3/include/asm-powerpc/kexec.h
> ===================================================================
> --- clone3.orig/include/asm-powerpc/kexec.h
> +++ clone3/include/asm-powerpc/kexec.h
> @@ -123,6 +123,9 @@ struct pt_regs;
>  extern void default_machine_kexec(struct kimage *image);
>  extern int default_machine_kexec_prepare(struct kimage *image);
>  extern void default_machine_crash_shutdown(struct pt_regs *regs);
> +typedef void (*crash_shutdown_t)(void);
> +extern int crash_shutdown_register(crash_shutdown_t handler);
> +extern int crash_shutdown_unregister(crash_shutdown_t handler);
>  
>  extern void machine_kexec_simple(struct kimage *image);
>  extern void crash_kexec_secondary(struct pt_regs *regs);
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@ozlabs.org
> https://ozlabs.org/mailman/listinfo/linuxppc-dev

^ permalink raw reply

* Re: [PATCH 2/2] kdump shutdown hook support
From: Olof Johansson @ 2007-12-13  5:13 UTC (permalink / raw)
  To: Michael Neuling; +Cc: linuxppc-dev, RAISCH, Paul Mackerras, THEMANN
In-Reply-To: <20071213051224.GA22378@lixom.net>

On Wed, Dec 12, 2007 at 11:12:24PM -0600, Olof Johansson wrote:
> Hi,

> > +			__delay(SETJMP_MACHINE_CHECK_DELAY);
> 
> Where is this defined?

Oops, nevermind. In 1/2, of course. :-)


-Olof

^ permalink raw reply

* [PATCH] Do ioremap and allocation for sleep on 3400 once at boot
From: Paul Mackerras @ 2007-12-13  5:15 UTC (permalink / raw)
  To: linuxppc-dev

Currently the sleep code on the old powerbook 3400 does an ioremap and
a kmalloc on each sleep/wakeup cycle.  This moves the ioremap and
kmalloc to boot-time code (via_pmu_start) so that they only need to be
done once.  This will be more convenient when we change to using the
generic suspend code, since it will avoid the need to do the
ioremap/kmalloc after devices have been suspended.

Signed-off-by: Paul Mackerras <paulus@samba.org>
---
diff --git a/drivers/macintosh/via-pmu.c b/drivers/macintosh/via-pmu.c
index 35e1f22..0e84cb8 100644
--- a/drivers/macintosh/via-pmu.c
+++ b/drivers/macintosh/via-pmu.c
@@ -203,6 +203,12 @@ static int proc_read_options(char *page, char **start, off_t off,
 static int proc_write_options(struct file *file, const char __user *buffer,
 			unsigned long count, void *data);
 
+#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_PPC32)
+static void powerbook_sleep_init_3400(void);
+#else
+#define powerbook_sleep_init_3400()	do { } while (0)
+#endif
+
 #ifdef CONFIG_ADB
 struct adb_driver via_pmu_driver = {
 	"PMU",
@@ -450,6 +456,10 @@ static int __init via_pmu_start(void)
 		pmu_poll();
 	} while (pmu_state != idle);
 
+	/* Do allocations and ioremaps that will be needed for sleep */
+	if (pmu_kind == PMU_OHARE_BASED)
+		powerbook_sleep_init_3400();
+
 	return 0;
 }
 
@@ -2271,26 +2281,30 @@ powerbook_sleep_Core99(void)
 #define PB3400_MEM_CTRL		0xf8000000
 #define PB3400_MEM_CTRL_SLEEP	0x70
 
-static int
-powerbook_sleep_3400(void)
+static void __iomem *pb3400_mem_ctrl;
+
+static void powerbook_sleep_init_3400(void)
+{
+	/* map in the memory controller registers */
+	pb3400_mem_ctrl = ioremap(PB3400_MEM_CTRL, 0x100);
+	if (pb3400_mem_ctrl == NULL)
+		printk(KERN_WARNING "ioremap failed: sleep won't be possible");
+
+	/* Allocate room for PCI save */
+	pbook_alloc_pci_save();
+}
+
+static int powerbook_sleep_3400(void)
 {
 	int ret, i, x;
 	unsigned int hid0;
 	unsigned long p;
 	struct adb_request sleep_req;
-	void __iomem *mem_ctrl;
 	unsigned int __iomem *mem_ctrl_sleep;
 
-	/* first map in the memory controller registers */
-	mem_ctrl = ioremap(PB3400_MEM_CTRL, 0x100);
-	if (mem_ctrl == NULL) {
-		printk("powerbook_sleep_3400: ioremap failed\n");
+	if (pb3400_mem_ctrl == NULL)
 		return -ENOMEM;
-	}
-	mem_ctrl_sleep = mem_ctrl + PB3400_MEM_CTRL_SLEEP;
-
-	/* Allocate room for PCI save */
-	pbook_alloc_pci_save();
+	mem_ctrl_sleep = pb3400_mem_ctrl + PB3400_MEM_CTRL_SLEEP;
 
 	ret = pmac_suspend_devices();
 	if (ret) {
@@ -2343,8 +2357,6 @@ powerbook_sleep_3400(void)
 		mb();
 
 	pmac_wakeup_devices();
-	pbook_free_pci_save();
-	iounmap(mem_ctrl);
 
 	return 0;
 }

^ permalink raw reply related

* How to Build Rootfile system
From: vinay kumar @ 2007-12-13  6:03 UTC (permalink / raw)
  To: linuxppc-embedded

[-- Attachment #1: Type: text/plain, Size: 291 bytes --]

Hi All,

I have booted linux on PPC440GP with treeimage(zimage+initrd).
Now my next aim is to have a more stable rootfile system.

I wanted to know the procedure of building a root file system for PPC440GP
architecture.

Pls share your knowledge and also some links.


regards to all,
vinay

[-- Attachment #2: Type: text/html, Size: 329 bytes --]

^ permalink raw reply

* Re: [RFC] ehea: kdump support using new shutdown hook
From: Michael Ellerman @ 2007-12-13  6:30 UTC (permalink / raw)
  To: Dave Jones
  Cc: Michael Neuling, Jan-Bernd Themann, netdev, linux-kernel,
	Thomas Klein, linux-ppc, Christoph Raisch, Paul Mackerras,
	Marcus Eder, Stefan Roscher
In-Reply-To: <20071212170440.GD2359@redhat.com>

[-- Attachment #1: Type: text/plain, Size: 668 bytes --]

On Wed, 2007-12-12 at 12:04 -0500, Dave Jones wrote:
> On Wed, Dec 12, 2007 at 05:53:43PM +0100, Thomas Klein wrote:
> 
>  > +static void ehea_update_adapter_handles(struct ehea_adapter *adapter)
>  > +{
>  > +	int i, k;
>  > +	int j = 0;
>  > +
>  > +	memset(adapter->res_handles, sizeof(adapter->res_handles), 0);
> 
> arguments wrong way around.

Remind me why bzero is deprecated again? :)

cheers

-- 
Michael Ellerman
OzLabs, IBM Australia Development Lab

wwweb: http://michael.ellerman.id.au
phone: +61 2 6212 1183 (tie line 70 21183)

We do not inherit the earth from our ancestors,
we borrow it from our children. - S.M.A.R.T Person

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply

* Re: [RFC] ehea: kdump support using new shutdown hook
From: Michael Ellerman @ 2007-12-13  6:41 UTC (permalink / raw)
  To: Thomas Klein
  Cc: Michael Neuling, Jan-Bernd Themann, netdev, linux-kernel,
	linux-ppc, Christoph Raisch, Paul Mackerras, Marcus Eder,
	Stefan Roscher
In-Reply-To: <200712121753.43543.osstklei@de.ibm.com>

[-- Attachment #1: Type: text/plain, Size: 5607 bytes --]

On Wed, 2007-12-12 at 17:53 +0100, Thomas Klein wrote:
> This patch adds kdump support using the new PPC crash shutdown hook to the
> ehea driver. The driver now keeps a list of firmware handles which have to
> be freed in case of a crash. The crash handler does the minimum required: it
> frees the firmware resource handles plus broadcast/multicast registrations.
> 
> Please comment.

Hi Thomas,

Here's a few ..

> ---
> diff -Nurp -X dontdiff linux-2.6.24-rc5/drivers/net/ehea/ehea.h patched_kernel/drivers/net/ehea/ehea.h
> --- linux-2.6.24-rc5/drivers/net/ehea/ehea.h	2007-12-11 04:48:43.000000000 +0100
> +++ patched_kernel/drivers/net/ehea/ehea.h	2007-12-12 17:30:53.000000000 +0100
> @@ -386,6 +386,7 @@ struct ehea_port_res {
>  
> 
>  #define EHEA_MAX_PORTS 16
> +#define EHEA_MAX_RES_HANDLES (100 * EHEA_MAX_PORTS + 10)
>  struct ehea_adapter {
>  	u64 handle;
>  	struct of_device *ofdev;
> @@ -397,6 +398,7 @@ struct ehea_adapter {
>  	u64 max_mc_mac;            /* max number of multicast mac addresses */
>  	int active_ports;
>  	struct list_head list;
> +	u64 res_handles[EHEA_MAX_RES_HANDLES];
>  };

I don't like this ..

> diff -Nurp -X dontdiff linux-2.6.24-rc5/drivers/net/ehea/ehea_main.c patched_kernel/drivers/net/ehea/ehea_main.c
> --- linux-2.6.24-rc5/drivers/net/ehea/ehea_main.c	2007-12-11 04:48:43.000000000 +0100
> +++ patched_kernel/drivers/net/ehea/ehea_main.c	2007-12-12 17:30:53.000000000 +0100
> @@ -35,6 +35,7 @@
>  #include <linux/if_ether.h>
>  #include <linux/notifier.h>
>  #include <linux/reboot.h>
> +#include <asm-powerpc/kexec.h>

Just <asm/kexec.h>

> @@ -3302,6 +3333,71 @@ static int __devexit ehea_remove(struct 
>  	return 0;
>  }
>  
> +void ehea_crash_deregister(void)
> +{
> +	struct ehea_adapter *adapter;
> +	int i;
> +	u64 hret;
> +	u8 reg_type;
> +
> +	list_for_each_entry(adapter, &adapter_list, list) {
> +		for (i = 0; i < EHEA_MAX_PORTS; i++) {
> +			struct ehea_port *port = adapter->port[i];
> +			if (port->state == EHEA_PORT_UP) {
> +				struct ehea_mc_list *mc_entry = port->mc_list;
> +				struct list_head *pos;
> +				struct list_head *temp;
> +
> +				/* Undo multicast registrations */
> +				list_for_each_safe(pos, temp,
> +						   &(port->mc_list->list)) {
> +					mc_entry = list_entry(pos,
> +							    struct ehea_mc_list,
> +							    list);
> +					ehea_multicast_reg_helper(port,
> +							      mc_entry->macaddr,
> +							      H_DEREG_BCMC);
> +				}
> +
> +				/* Undo broad registration */
> +				reg_type = EHEA_BCMC_BROADCAST |
> +					   EHEA_BCMC_UNTAGGED;
> +				ehea_h_reg_dereg_bcmc(port->adapter->handle,
> +						      port->logical_port_id,
> +						      reg_type, port->mac_addr,
> +						      0, H_DEREG_BCMC);
> +
> +				reg_type = EHEA_BCMC_BROADCAST |
> +					   EHEA_BCMC_VLANID_ALL;
> +				ehea_h_reg_dereg_bcmc(port->adapter->handle,
> +						      port->logical_port_id,
> +						      reg_type, port->mac_addr,
> +						      0, H_DEREG_BCMC);
> +			}
> +		}
> +		for (i = 0; i < EHEA_MAX_RES_HANDLES; i++) {
> +			u64 handle = adapter->res_handles[i];
> +			if (handle) {
> +				hret = ehea_h_free_resource(adapter->handle,
> +							    handle,
> +							    FORCE_FREE);
> +			}
> +		}
> +
> +		if (adapter->neq) {
> +			hret = ehea_h_free_resource(adapter->handle,
> +						    adapter->neq->fw_handle,
> +						    FORCE_FREE);
> +		}
> +
> +		if (adapter->mr.handle) {
> +			hret = ehea_h_free_resource(adapter->handle,
> +						    adapter->mr.handle,
> +						    FORCE_FREE);
> +		}
> +	}
> +}

This is sort of on the right track, I like that the ehea_h_.. routines
are basically just hypercalls, but there's a few things wrong. You're
walking a linked list, which is asking for trouble, if a single pointer
is corrupted you'll walk off into lala land. Then you're pulling fields
out of structs via pointers, again hoping that they're all valid. Then
you walk another linked list ..  And then you're pulling bits out of the
adapter again.

Ideally it would look like this:

static u64 things_to_free[];

void ehea_crash_deregister(void)
{
	for (i = 0; i < num_things_to_free; i++)
		h_call_free_a_thing(things_to_free[i]);
}

>  static int ehea_reboot_notifier(struct notifier_block *nb,
>  				unsigned long action, void *unused)
>  {
> @@ -3373,6 +3469,9 @@ int __init ehea_module_init(void)
>  		goto out;
>  
>  	register_reboot_notifier(&ehea_reboot_nb);
> +	ret = crash_shutdown_register(&ehea_crash_deregister);
> +	if (ret)
> +		ehea_info("failed registering crash handler");

Your naming is a little confusing here, you're registering the
deregister function. I think I get it, you're unregistering the fw
handles, but perhaps ehea_crash_shutdown() would be clearer.

> @@ -3396,10 +3496,15 @@ out:
>  
>  static void __exit ehea_module_exit(void)
>  {
> +	int ret;
> +
>  	flush_scheduled_work();
>  	driver_remove_file(&ehea_driver.driver, &driver_attr_capabilities);
>  	ibmebus_unregister_driver(&ehea_driver);
>  	unregister_reboot_notifier(&ehea_reboot_nb);
> +	ret = crash_shutdown_unregister(&ehea_crash_deregister);
> +	if (ret)
> +		ehea_info("failed unregistering crash handler");

You don't need ret if that's all you're going to do with it.

cheers

-- 
Michael Ellerman
OzLabs, IBM Australia Development Lab

wwweb: http://michael.ellerman.id.au
phone: +61 2 6212 1183 (tie line 70 21183)

We do not inherit the earth from our ancestors,
we borrow it from our children. - S.M.A.R.T Person

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply

* Re: [PATCH] Introduce driver_create/remove_dir
From: Greg KH @ 2007-12-13  7:10 UTC (permalink / raw)
  To: Stephen Rothwell
  Cc: linux-kernel, David Gibson, linuxppc-dev, Kyle A. Lucke, paulus
In-Reply-To: <20071212105633.725496cb.sfr@canb.auug.org.au>

On Wed, Dec 12, 2007 at 10:56:33AM +1100, Stephen Rothwell wrote:
> 
> Signed-off-by: Stephen Rothwell <sfr@canb.auug.org.au>
> ---
>  drivers/base/driver.c      |   24 ++++++++++++++++++++++++
>  drivers/net/iseries_veth.c |   15 +++++++--------
>  include/linux/device.h     |    3 +++
>  3 files changed, 34 insertions(+), 8 deletions(-)
> 
> Greg, does this look like a reasonable solution to iseries_veth accessing
> the "private" kobject in struct device_driver?  This version is against
> maimline, but the stuff you have in mm would just need to update
> driver_create_dir ...

Hm, we just want to be able to get to the kobject somehow here.  Not
create a new api that doesn't match up.  I'll think about it and figure
something that matches the other portions of the api.

> Also something along the lines of device_add_dir() might be good if you
> want to hide the kobject in struct device as well.

As devices _should_ always be dynamic, hopefully I'll not have to do
that.  But knowing some of the platform devices, I'm afraid I'll have to
do that split :)

thanks,

greg k-h

^ permalink raw reply

* Re: drivers/net/iseries_veth.c dubious sysfs usage
From: Greg KH @ 2007-12-13  7:08 UTC (permalink / raw)
  To: Michael Ellerman
  Cc: linuxppc-dev, Kyle A. Lucke, paulus, linux-kernel, David Gibson
In-Reply-To: <1196912898.14754.13.camel@concordia>

On Thu, Dec 06, 2007 at 02:48:18PM +1100, Michael Ellerman wrote:
> igoeast:~# cd /sys/class/net/eth1/
> igoeast:/sys/class/net/eth1# ls -la
> total 0
> drwxr-xr-x  4 root root    0 Dec  6 10:22 .
> drwxr-xr-x  6 root root    0 Dec  6 10:21 ..
> -r--r--r--  1 root root 4096 Dec  6 10:30 addr_len
> -r--r--r--  1 root root 4096 Dec  6 10:30 address
> -r--r--r--  1 root root 4096 Dec  6 10:30 broadcast
> -r--r--r--  1 root root 4096 Dec  6 10:30 carrier
> lrwxrwxrwx  1 root root    0 Dec  6 10:22 device -> ../../../devices/vio/3
> -r--r--r--  1 root root 4096 Dec  6 10:30 dormant
> -r--r--r--  1 root root 4096 Dec  6 10:30 features
> -rw-r--r--  1 root root 4096 Dec  6 10:30 flags
> -r--r--r--  1 root root 4096 Dec  6 10:30 ifindex
> -r--r--r--  1 root root 4096 Dec  6 10:30 iflink
> -r--r--r--  1 root root 4096 Dec  6 10:30 link_mode
> -rw-r--r--  1 root root 4096 Dec  6 10:30 mtu
> -r--r--r--  1 root root 4096 Dec  6 10:30 operstate
> drwxr-xr-x  2 root root    0 Dec  6 10:30 statistics
> lrwxrwxrwx  1 root root    0 Dec  6 10:30 subsystem -> ../../../class/net
> -rw-r--r--  1 root root 4096 Dec  6 10:30 tx_queue_len
> -r--r--r--  1 root root 4096 Dec  6 10:30 type
> -rw-r--r--  1 root root 4096 Dec  6 10:30 uevent
> drwxr-xr-x  2 root root    0 Dec  6 10:30 veth_port
> 
> Each net device has a port structure associated with it, the fields
> should be fairly self explanatory, they're all read only I think.
> 
> igoeast:/sys/class/net/eth1# find veth_port/
> veth_port/
> veth_port/mac_addr
> veth_port/lpar_map
> veth_port/stopped_map
> veth_port/promiscuous
> veth_port/num_mcast

That's fine, I'll let you fight with the network people over that :)

> igoeast:/sys/class/net/eth1# cd device/driver
> 
> igoeast:/sys/class/net/eth1/device/driver# ls -l
> total 0
> lrwxrwxrwx  1 root root    0 Dec  6 10:21 2 -> ../../../../devices/vio/2
> lrwxrwxrwx  1 root root    0 Dec  6 10:21 3 -> ../../../../devices/vio/3
> --w-------  1 root root 4096 Dec  6 10:21 bind
> drwxr-xr-x  2 root root    0 Dec  6 10:21 cnx00
> drwxr-xr-x  2 root root    0 Dec  6 10:21 cnx02
> drwxr-xr-x  2 root root    0 Dec  6 10:21 cnx03
> drwxr-xr-x  2 root root    0 Dec  6 10:21 cnx04
> lrwxrwxrwx  1 root root    0 Dec  6 10:21 module -> ../../../../module/iseries_veth
> --w-------  1 root root 4096 Dec  6 10:21 uevent
> --w-------  1 root root 4096 Dec  6 10:21 unbind
> 
> The driver has a connection to all the other lpars, this is entirely
> independent of the net devices.
> 
> igoeast:/sys/class/net/eth1/device/driver# find cnx00/
> cnx00/
> cnx00/outstanding_tx
> cnx00/remote_lp
> cnx00/num_events
> cnx00/reset_timeout
> cnx00/last_contact
> cnx00/state
> cnx00/src_inst
> cnx00/dst_inst
> cnx00/num_pending_acks
> cnx00/num_ack_events
> cnx00/ack_timeout

Hm, ok, it's odd as you are the only driver in the whole tree doing
something like this, but it seems semi-resonable, so I can't complain :)

I'll fix the core up to allow you to do this, thanks for the
explanation.

greg k-h

^ permalink raw reply

* [PATCH 1/20] [POWERPC] Reworking machine check handling and Fix 440/440A
From: Benjamin Herrenschmidt @ 2007-12-13  7:38 UTC (permalink / raw)
  To: Josh Boyer; +Cc: linuxppc-dev

This adds a cputable function pointer for the CPU-side machine
check handling. The semantic is still the same as the old one,
the one in ppc_md. overrides the one in cputable, though
ultimately we'll want to change that so the CPU gets first.

This removes CONFIG_440A which was a problem for multiplatform
kernels and instead fixes up the IVOR at runtime from a setup_cpu
function. The "A" version of the machine check also tweaks the
regs->trap value to differenciate the 2 versions at the C level.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---

 arch/powerpc/kernel/cpu_setup_44x.S |    9 +++
 arch/powerpc/kernel/cputable.c      |  105 ++++++++++++++++++++++++++++++++++++
 arch/powerpc/kernel/head_44x.S      |   14 +++-
 arch/powerpc/kernel/head_booke.h    |    2 
 arch/powerpc/kernel/traps.c         |   62 ++++++++++++++++-----
 arch/powerpc/platforms/44x/Kconfig  |    5 -
 arch/ppc/kernel/traps.c             |   98 ++++++++++++++++++++++-----------
 include/asm-powerpc/cputable.h      |   13 ++++
 include/asm-powerpc/ptrace.h        |    3 -
 include/asm-powerpc/reg_booke.h     |    3 -
 include/asm-ppc/reg_booke.h         |    2 
 11 files changed, 258 insertions(+), 58 deletions(-)

--- linux-merge.orig/arch/powerpc/kernel/cpu_setup_44x.S	2007-10-15 11:19:35.000000000 +1000
+++ linux-merge/arch/powerpc/kernel/cpu_setup_44x.S	2007-12-11 16:11:27.000000000 +1100
@@ -23,11 +23,20 @@ _GLOBAL(__setup_cpu_440epx)
 	mflr	r4
 	bl	__init_fpu_44x
 	bl	__plb_disable_wrp
+	bl	__fixup_440A_mcheck
 	mtlr	r4
 	blr
 _GLOBAL(__setup_cpu_440grx)
 	b	__plb_disable_wrp
+_GLOBAL(__setup_cpu_440gx)
+_GLOBAL(__setup_cpu_440spe)
+	b	__fixup_440A_mcheck
 
+ /* Temporary fixup for arch/ppc until we kill the whole thing */
+#ifndef CONFIG_PPC_MERGE
+_GLOBAL(__fixup_440A_mcheck)
+	blr
+#endif
 
 /* enable APU between CPU and FPU */
 _GLOBAL(__init_fpu_44x)
Index: linux-merge/arch/powerpc/kernel/cputable.c
===================================================================
--- linux-merge.orig/arch/powerpc/kernel/cputable.c	2007-11-13 11:39:44.000000000 +1100
+++ linux-merge/arch/powerpc/kernel/cputable.c	2007-12-11 16:11:27.000000000 +1100
@@ -33,7 +33,9 @@ EXPORT_SYMBOL(cur_cpu_spec);
 #ifdef CONFIG_PPC32
 extern void __setup_cpu_440ep(unsigned long offset, struct cpu_spec* spec);
 extern void __setup_cpu_440epx(unsigned long offset, struct cpu_spec* spec);
+extern void __setup_cpu_440gx(unsigned long offset, struct cpu_spec* spec);
 extern void __setup_cpu_440grx(unsigned long offset, struct cpu_spec* spec);
+extern void __setup_cpu_440spe(unsigned long offset, struct cpu_spec* spec);
 extern void __setup_cpu_603(unsigned long offset, struct cpu_spec* spec);
 extern void __setup_cpu_604(unsigned long offset, struct cpu_spec* spec);
 extern void __setup_cpu_750(unsigned long offset, struct cpu_spec* spec);
@@ -85,6 +87,7 @@ static struct cpu_spec __initdata cpu_sp
 		.pmc_type		= PPC_PMC_IBM,
 		.oprofile_cpu_type	= "ppc64/power3",
 		.oprofile_type		= PPC_OPROFILE_RS64,
+		.machine_check		= machine_check_generic,
 		.platform		= "power3",
 	},
 	{	/* Power3+ */
@@ -99,6 +102,7 @@ static struct cpu_spec __initdata cpu_sp
 		.pmc_type		= PPC_PMC_IBM,
 		.oprofile_cpu_type	= "ppc64/power3",
 		.oprofile_type		= PPC_OPROFILE_RS64,
+		.machine_check		= machine_check_generic,
 		.platform		= "power3",
 	},
 	{	/* Northstar */
@@ -113,6 +117,7 @@ static struct cpu_spec __initdata cpu_sp
 		.pmc_type		= PPC_PMC_IBM,
 		.oprofile_cpu_type	= "ppc64/rs64",
 		.oprofile_type		= PPC_OPROFILE_RS64,
+		.machine_check		= machine_check_generic,
 		.platform		= "rs64",
 	},
 	{	/* Pulsar */
@@ -127,6 +132,7 @@ static struct cpu_spec __initdata cpu_sp
 		.pmc_type		= PPC_PMC_IBM,
 		.oprofile_cpu_type	= "ppc64/rs64",
 		.oprofile_type		= PPC_OPROFILE_RS64,
+		.machine_check		= machine_check_generic,
 		.platform		= "rs64",
 	},
 	{	/* I-star */
@@ -141,6 +147,7 @@ static struct cpu_spec __initdata cpu_sp
 		.pmc_type		= PPC_PMC_IBM,
 		.oprofile_cpu_type	= "ppc64/rs64",
 		.oprofile_type		= PPC_OPROFILE_RS64,
+		.machine_check		= machine_check_generic,
 		.platform		= "rs64",
 	},
 	{	/* S-star */
@@ -155,6 +162,7 @@ static struct cpu_spec __initdata cpu_sp
 		.pmc_type		= PPC_PMC_IBM,
 		.oprofile_cpu_type	= "ppc64/rs64",
 		.oprofile_type		= PPC_OPROFILE_RS64,
+		.machine_check		= machine_check_generic,
 		.platform		= "rs64",
 	},
 	{	/* Power4 */
@@ -169,6 +177,7 @@ static struct cpu_spec __initdata cpu_sp
 		.pmc_type		= PPC_PMC_IBM,
 		.oprofile_cpu_type	= "ppc64/power4",
 		.oprofile_type		= PPC_OPROFILE_POWER4,
+		.machine_check		= machine_check_generic,
 		.platform		= "power4",
 	},
 	{	/* Power4+ */
@@ -183,6 +192,7 @@ static struct cpu_spec __initdata cpu_sp
 		.pmc_type		= PPC_PMC_IBM,
 		.oprofile_cpu_type	= "ppc64/power4",
 		.oprofile_type		= PPC_OPROFILE_POWER4,
+		.machine_check		= machine_check_generic,
 		.platform		= "power4",
 	},
 	{	/* PPC970 */
@@ -200,6 +210,7 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_restore		= __restore_cpu_ppc970,
 		.oprofile_cpu_type	= "ppc64/970",
 		.oprofile_type		= PPC_OPROFILE_POWER4,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc970",
 	},
 	{	/* PPC970FX */
@@ -217,6 +228,7 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_restore		= __restore_cpu_ppc970,
 		.oprofile_cpu_type	= "ppc64/970",
 		.oprofile_type		= PPC_OPROFILE_POWER4,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc970",
 	},
 	{	/* PPC970MP DD1.0 - no DEEPNAP, use regular 970 init */
@@ -234,6 +246,7 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_restore		= __restore_cpu_ppc970,
 		.oprofile_cpu_type	= "ppc64/970MP",
 		.oprofile_type		= PPC_OPROFILE_POWER4,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc970",
 	},
 	{	/* PPC970MP */
@@ -251,6 +264,7 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_restore		= __restore_cpu_ppc970,
 		.oprofile_cpu_type	= "ppc64/970MP",
 		.oprofile_type		= PPC_OPROFILE_POWER4,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc970",
 	},
 	{	/* PPC970GX */
@@ -267,6 +281,7 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_setup		= __setup_cpu_ppc970,
 		.oprofile_cpu_type	= "ppc64/970",
 		.oprofile_type		= PPC_OPROFILE_POWER4,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc970",
 	},
 	{	/* Power5 GR */
@@ -286,6 +301,7 @@ static struct cpu_spec __initdata cpu_sp
 		 */
 		.oprofile_mmcra_sihv	= MMCRA_SIHV,
 		.oprofile_mmcra_sipr	= MMCRA_SIPR,
+		.machine_check		= machine_check_generic,
 		.platform		= "power5",
 	},
 	{	/* Power5++ */
@@ -301,6 +317,7 @@ static struct cpu_spec __initdata cpu_sp
 		.oprofile_type		= PPC_OPROFILE_POWER4,
 		.oprofile_mmcra_sihv	= MMCRA_SIHV,
 		.oprofile_mmcra_sipr	= MMCRA_SIPR,
+		.machine_check		= machine_check_generic,
 		.platform		= "power5+",
 	},
 	{	/* Power5 GS */
@@ -317,6 +334,7 @@ static struct cpu_spec __initdata cpu_sp
 		.oprofile_type		= PPC_OPROFILE_POWER4,
 		.oprofile_mmcra_sihv	= MMCRA_SIHV,
 		.oprofile_mmcra_sipr	= MMCRA_SIPR,
+		.machine_check		= machine_check_generic,
 		.platform		= "power5+",
 	},
 	{	/* POWER6 in P5+ mode; 2.04-compliant processor */
@@ -327,6 +345,7 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_user_features	= COMMON_USER_POWER5_PLUS,
 		.icache_bsize		= 128,
 		.dcache_bsize		= 128,
+		.machine_check		= machine_check_generic,
 		.platform		= "power5+",
 	},
 	{	/* Power6 */
@@ -346,6 +365,7 @@ static struct cpu_spec __initdata cpu_sp
 		.oprofile_mmcra_sipr	= POWER6_MMCRA_SIPR,
 		.oprofile_mmcra_clear	= POWER6_MMCRA_THRM |
 			POWER6_MMCRA_OTHER,
+		.machine_check		= machine_check_generic,
 		.platform		= "power6x",
 	},
 	{	/* 2.05-compliant processor, i.e. Power6 "architected" mode */
@@ -356,6 +376,7 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_user_features	= COMMON_USER_POWER6,
 		.icache_bsize		= 128,
 		.dcache_bsize		= 128,
+		.machine_check		= machine_check_generic,
 		.platform		= "power6",
 	},
 	{	/* Cell Broadband Engine */
@@ -372,6 +393,7 @@ static struct cpu_spec __initdata cpu_sp
 		.pmc_type		= PPC_PMC_IBM,
 		.oprofile_cpu_type	= "ppc64/cell-be",
 		.oprofile_type		= PPC_OPROFILE_CELL,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc-cell-be",
 	},
 	{	/* PA Semi PA6T */
@@ -388,6 +410,7 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_restore		= __restore_cpu_pa6t,
 		.oprofile_cpu_type	= "ppc64/pa6t",
 		.oprofile_type		= PPC_OPROFILE_PA6T,
+		.machine_check		= machine_check_generic,
 		.platform		= "pa6t",
 	},
 	{	/* default match */
@@ -400,6 +423,7 @@ static struct cpu_spec __initdata cpu_sp
 		.dcache_bsize		= 128,
 		.num_pmcs		= 6,
 		.pmc_type		= PPC_PMC_IBM,
+		.machine_check		= machine_check_generic,
 		.platform		= "power4",
 	}
 #endif	/* CONFIG_PPC64 */
@@ -414,6 +438,7 @@ static struct cpu_spec __initdata cpu_sp
 			PPC_FEATURE_UNIFIED_CACHE | PPC_FEATURE_NO_TB,
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc601",
 	},
 	{	/* 603 */
@@ -425,6 +450,7 @@ static struct cpu_spec __initdata cpu_sp
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
 		.cpu_setup		= __setup_cpu_603,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc603",
 	},
 	{	/* 603e */
@@ -436,6 +462,7 @@ static struct cpu_spec __initdata cpu_sp
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
 		.cpu_setup		= __setup_cpu_603,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc603",
 	},
 	{	/* 603ev */
@@ -447,6 +474,7 @@ static struct cpu_spec __initdata cpu_sp
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
 		.cpu_setup		= __setup_cpu_603,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc603",
 	},
 	{	/* 604 */
@@ -459,6 +487,7 @@ static struct cpu_spec __initdata cpu_sp
 		.dcache_bsize		= 32,
 		.num_pmcs		= 2,
 		.cpu_setup		= __setup_cpu_604,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc604",
 	},
 	{	/* 604e */
@@ -471,6 +500,7 @@ static struct cpu_spec __initdata cpu_sp
 		.dcache_bsize		= 32,
 		.num_pmcs		= 4,
 		.cpu_setup		= __setup_cpu_604,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc604",
 	},
 	{	/* 604r */
@@ -483,6 +513,7 @@ static struct cpu_spec __initdata cpu_sp
 		.dcache_bsize		= 32,
 		.num_pmcs		= 4,
 		.cpu_setup		= __setup_cpu_604,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc604",
 	},
 	{	/* 604ev */
@@ -495,6 +526,7 @@ static struct cpu_spec __initdata cpu_sp
 		.dcache_bsize		= 32,
 		.num_pmcs		= 4,
 		.cpu_setup		= __setup_cpu_604,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc604",
 	},
 	{	/* 740/750 (0x4202, don't support TAU ?) */
@@ -507,6 +539,7 @@ static struct cpu_spec __initdata cpu_sp
 		.dcache_bsize		= 32,
 		.num_pmcs		= 4,
 		.cpu_setup		= __setup_cpu_750,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc750",
 	},
 	{	/* 750CX (80100 and 8010x?) */
@@ -519,6 +552,7 @@ static struct cpu_spec __initdata cpu_sp
 		.dcache_bsize		= 32,
 		.num_pmcs		= 4,
 		.cpu_setup		= __setup_cpu_750cx,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc750",
 	},
 	{	/* 750CX (82201 and 82202) */
@@ -531,6 +565,7 @@ static struct cpu_spec __initdata cpu_sp
 		.dcache_bsize		= 32,
 		.num_pmcs		= 4,
 		.cpu_setup		= __setup_cpu_750cx,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc750",
 	},
 	{	/* 750CXe (82214) */
@@ -543,6 +578,7 @@ static struct cpu_spec __initdata cpu_sp
 		.dcache_bsize		= 32,
 		.num_pmcs		= 4,
 		.cpu_setup		= __setup_cpu_750cx,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc750",
 	},
 	{	/* 750CXe "Gekko" (83214) */
@@ -555,6 +591,7 @@ static struct cpu_spec __initdata cpu_sp
 		.dcache_bsize		= 32,
 		.num_pmcs		= 4,
 		.cpu_setup		= __setup_cpu_750cx,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc750",
 	},
 	{	/* 750CL */
@@ -567,6 +604,7 @@ static struct cpu_spec __initdata cpu_sp
 		.dcache_bsize		= 32,
 		.num_pmcs		= 4,
 		.cpu_setup		= __setup_cpu_750,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc750",
 	},
 	{	/* 745/755 */
@@ -579,6 +617,7 @@ static struct cpu_spec __initdata cpu_sp
 		.dcache_bsize		= 32,
 		.num_pmcs		= 4,
 		.cpu_setup		= __setup_cpu_750,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc750",
 	},
 	{	/* 750FX rev 1.x */
@@ -591,6 +630,7 @@ static struct cpu_spec __initdata cpu_sp
 		.dcache_bsize		= 32,
 		.num_pmcs		= 4,
 		.cpu_setup		= __setup_cpu_750,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc750",
 	},
 	{	/* 750FX rev 2.0 must disable HID0[DPM] */
@@ -603,6 +643,7 @@ static struct cpu_spec __initdata cpu_sp
 		.dcache_bsize		= 32,
 		.num_pmcs		= 4,
 		.cpu_setup		= __setup_cpu_750,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc750",
 	},
 	{	/* 750FX (All revs except 2.0) */
@@ -615,6 +656,7 @@ static struct cpu_spec __initdata cpu_sp
 		.dcache_bsize		= 32,
 		.num_pmcs		= 4,
 		.cpu_setup		= __setup_cpu_750fx,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc750",
 	},
 	{	/* 750GX */
@@ -627,6 +669,7 @@ static struct cpu_spec __initdata cpu_sp
 		.dcache_bsize		= 32,
 		.num_pmcs		= 4,
 		.cpu_setup		= __setup_cpu_750fx,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc750",
 	},
 	{	/* 740/750 (L2CR bit need fixup for 740) */
@@ -639,6 +682,7 @@ static struct cpu_spec __initdata cpu_sp
 		.dcache_bsize		= 32,
 		.num_pmcs		= 4,
 		.cpu_setup		= __setup_cpu_750,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc750",
 	},
 	{	/* 7400 rev 1.1 ? (no TAU) */
@@ -652,6 +696,7 @@ static struct cpu_spec __initdata cpu_sp
 		.dcache_bsize		= 32,
 		.num_pmcs		= 4,
 		.cpu_setup		= __setup_cpu_7400,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc7400",
 	},
 	{	/* 7400 */
@@ -665,6 +710,7 @@ static struct cpu_spec __initdata cpu_sp
 		.dcache_bsize		= 32,
 		.num_pmcs		= 4,
 		.cpu_setup		= __setup_cpu_7400,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc7400",
 	},
 	{	/* 7410 */
@@ -678,6 +724,7 @@ static struct cpu_spec __initdata cpu_sp
 		.dcache_bsize		= 32,
 		.num_pmcs		= 4,
 		.cpu_setup		= __setup_cpu_7410,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc7400",
 	},
 	{	/* 7450 2.0 - no doze/nap */
@@ -693,6 +740,7 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_setup		= __setup_cpu_745x,
 		.oprofile_cpu_type      = "ppc/7450",
 		.oprofile_type		= PPC_OPROFILE_G4,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc7450",
 	},
 	{	/* 7450 2.1 */
@@ -708,6 +756,7 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_setup		= __setup_cpu_745x,
 		.oprofile_cpu_type      = "ppc/7450",
 		.oprofile_type		= PPC_OPROFILE_G4,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc7450",
 	},
 	{	/* 7450 2.3 and newer */
@@ -723,6 +772,7 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_setup		= __setup_cpu_745x,
 		.oprofile_cpu_type      = "ppc/7450",
 		.oprofile_type		= PPC_OPROFILE_G4,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc7450",
 	},
 	{	/* 7455 rev 1.x */
@@ -738,6 +788,7 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_setup		= __setup_cpu_745x,
 		.oprofile_cpu_type      = "ppc/7450",
 		.oprofile_type		= PPC_OPROFILE_G4,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc7450",
 	},
 	{	/* 7455 rev 2.0 */
@@ -753,6 +804,7 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_setup		= __setup_cpu_745x,
 		.oprofile_cpu_type      = "ppc/7450",
 		.oprofile_type		= PPC_OPROFILE_G4,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc7450",
 	},
 	{	/* 7455 others */
@@ -768,6 +820,7 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_setup		= __setup_cpu_745x,
 		.oprofile_cpu_type      = "ppc/7450",
 		.oprofile_type		= PPC_OPROFILE_G4,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc7450",
 	},
 	{	/* 7447/7457 Rev 1.0 */
@@ -783,6 +836,7 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_setup		= __setup_cpu_745x,
 		.oprofile_cpu_type      = "ppc/7450",
 		.oprofile_type		= PPC_OPROFILE_G4,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc7450",
 	},
 	{	/* 7447/7457 Rev 1.1 */
@@ -798,6 +852,7 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_setup		= __setup_cpu_745x,
 		.oprofile_cpu_type      = "ppc/7450",
 		.oprofile_type		= PPC_OPROFILE_G4,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc7450",
 	},
 	{	/* 7447/7457 Rev 1.2 and later */
@@ -812,6 +867,7 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_setup		= __setup_cpu_745x,
 		.oprofile_cpu_type      = "ppc/7450",
 		.oprofile_type		= PPC_OPROFILE_G4,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc7450",
 	},
 	{	/* 7447A */
@@ -827,6 +883,7 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_setup		= __setup_cpu_745x,
 		.oprofile_cpu_type      = "ppc/7450",
 		.oprofile_type		= PPC_OPROFILE_G4,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc7450",
 	},
 	{	/* 7448 */
@@ -842,6 +899,7 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_setup		= __setup_cpu_745x,
 		.oprofile_cpu_type      = "ppc/7450",
 		.oprofile_type		= PPC_OPROFILE_G4,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc7450",
 	},
 	{	/* 82xx (8240, 8245, 8260 are all 603e cores) */
@@ -853,6 +911,7 @@ static struct cpu_spec __initdata cpu_sp
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
 		.cpu_setup		= __setup_cpu_603,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc603",
 	},
 	{	/* All G2_LE (603e core, plus some) have the same pvr */
@@ -864,6 +923,7 @@ static struct cpu_spec __initdata cpu_sp
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
 		.cpu_setup		= __setup_cpu_603,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc603",
 	},
 	{	/* e300c1 (a 603e core, plus some) on 83xx */
@@ -875,6 +935,7 @@ static struct cpu_spec __initdata cpu_sp
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
 		.cpu_setup		= __setup_cpu_603,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc603",
 	},
 	{	/* e300c2 (an e300c1 core, plus some, minus FPU) on 83xx */
@@ -886,6 +947,7 @@ static struct cpu_spec __initdata cpu_sp
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
 		.cpu_setup		= __setup_cpu_603,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc603",
 	},
 	{	/* e300c3 on 83xx  */
@@ -897,6 +959,7 @@ static struct cpu_spec __initdata cpu_sp
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
 		.cpu_setup		= __setup_cpu_603,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc603",
 	},
 	{	/* default match, we assume split I/D cache & TB (non-601)... */
@@ -907,6 +970,7 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_user_features	= COMMON_USER,
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc603",
 	},
 #endif /* CLASSIC_PPC */
@@ -933,6 +997,7 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_user_features	= PPC_FEATURE_32 | PPC_FEATURE_HAS_MMU,
 		.icache_bsize		= 16,
 		.dcache_bsize		= 16,
+		.machine_check		= machine_check_4xx,
 		.platform		= "ppc403",
 	},
 	{	/* 403GCX */
@@ -944,6 +1009,7 @@ static struct cpu_spec __initdata cpu_sp
 		 	PPC_FEATURE_HAS_MMU | PPC_FEATURE_NO_TB,
 		.icache_bsize		= 16,
 		.dcache_bsize		= 16,
+		.machine_check		= machine_check_4xx,
 		.platform		= "ppc403",
 	},
 	{	/* 403G ?? */
@@ -954,6 +1020,7 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_user_features	= PPC_FEATURE_32 | PPC_FEATURE_HAS_MMU,
 		.icache_bsize		= 16,
 		.dcache_bsize		= 16,
+		.machine_check		= machine_check_4xx,
 		.platform		= "ppc403",
 	},
 	{	/* 405GP */
@@ -965,6 +1032,7 @@ static struct cpu_spec __initdata cpu_sp
 			PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC,
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
+		.machine_check		= machine_check_4xx,
 		.platform		= "ppc405",
 	},
 	{	/* STB 03xxx */
@@ -976,6 +1044,7 @@ static struct cpu_spec __initdata cpu_sp
 			PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC,
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
+		.machine_check		= machine_check_4xx,
 		.platform		= "ppc405",
 	},
 	{	/* STB 04xxx */
@@ -987,6 +1056,7 @@ static struct cpu_spec __initdata cpu_sp
 			PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC,
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
+		.machine_check		= machine_check_4xx,
 		.platform		= "ppc405",
 	},
 	{	/* NP405L */
@@ -998,6 +1068,7 @@ static struct cpu_spec __initdata cpu_sp
 			PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC,
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
+		.machine_check		= machine_check_4xx,
 		.platform		= "ppc405",
 	},
 	{	/* NP4GS3 */
@@ -1009,6 +1080,7 @@ static struct cpu_spec __initdata cpu_sp
 			PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC,
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
+		.machine_check		= machine_check_4xx,
 		.platform		= "ppc405",
 	},
 	{   /* NP405H */
@@ -1020,6 +1092,7 @@ static struct cpu_spec __initdata cpu_sp
 			PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC,
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
+		.machine_check		= machine_check_4xx,
 		.platform		= "ppc405",
 	},
 	{	/* 405GPr */
@@ -1031,6 +1104,7 @@ static struct cpu_spec __initdata cpu_sp
 			PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC,
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
+		.machine_check		= machine_check_4xx,
 		.platform		= "ppc405",
 	},
 	{   /* STBx25xx */
@@ -1042,6 +1116,7 @@ static struct cpu_spec __initdata cpu_sp
 			PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC,
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
+		.machine_check		= machine_check_4xx,
 		.platform		= "ppc405",
 	},
 	{	/* 405LP */
@@ -1052,6 +1127,7 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_user_features	= PPC_FEATURE_32 | PPC_FEATURE_HAS_MMU,
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
+		.machine_check		= machine_check_4xx,
 		.platform		= "ppc405",
 	},
 	{	/* Xilinx Virtex-II Pro  */
@@ -1063,6 +1139,7 @@ static struct cpu_spec __initdata cpu_sp
 			PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC,
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
+		.machine_check		= machine_check_4xx,
 		.platform		= "ppc405",
 	},
 	{	/* Xilinx Virtex-4 FX */
@@ -1074,6 +1151,7 @@ static struct cpu_spec __initdata cpu_sp
 			PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC,
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
+		.machine_check		= machine_check_4xx,
 		.platform		= "ppc405",
 	},
 	{	/* 405EP */
@@ -1085,6 +1163,7 @@ static struct cpu_spec __initdata cpu_sp
 			PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC,
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
+		.machine_check		= machine_check_4xx,
 		.platform		= "ppc405",
 	},
 	{	/* 405EX */
@@ -1096,6 +1175,7 @@ static struct cpu_spec __initdata cpu_sp
 			PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC,
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
+		.machine_check		= machine_check_4xx,
 		.platform		= "ppc405",
 	},
 
@@ -1109,6 +1189,7 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_user_features	= COMMON_USER_BOOKE,
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
+		.machine_check		= machine_check_4xx,
 		.platform		= "ppc440",
 	},
 	{ /* Use logical PVR for 440EP (logical pvr = pvr | 0x8) */
@@ -1120,6 +1201,7 @@ static struct cpu_spec __initdata cpu_sp
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
 		.cpu_setup		= __setup_cpu_440ep,
+		.machine_check		= machine_check_4xx,
 		.platform		= "ppc440",
 	},
 	{
@@ -1130,6 +1212,7 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_user_features	= COMMON_USER_BOOKE | PPC_FEATURE_HAS_FPU,
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
+		.machine_check		= machine_check_4xx,
 		.platform		= "ppc440",
 	},
 	{ /* Use logical PVR for 440EP (logical pvr = pvr | 0x8) */
@@ -1141,6 +1224,7 @@ static struct cpu_spec __initdata cpu_sp
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
 		.cpu_setup		= __setup_cpu_440ep,
+		.machine_check		= machine_check_4xx,
 		.platform		= "ppc440",
 	},
 	{ /* 440GRX */
@@ -1152,6 +1236,7 @@ static struct cpu_spec __initdata cpu_sp
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
 		.cpu_setup		= __setup_cpu_440grx,
+		.machine_check		= machine_check_4xx,
 		.platform		= "ppc440",
 	},
 	{ /* Use logical PVR for 440EPx (logical pvr = pvr | 0x8) */
@@ -1163,6 +1248,7 @@ static struct cpu_spec __initdata cpu_sp
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
 		.cpu_setup		= __setup_cpu_440epx,
+		.machine_check		= machine_check_440A,
 		.platform		= "ppc440",
 	},
 	{	/* 440GP Rev. B */
@@ -1173,6 +1259,7 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_user_features	= COMMON_USER_BOOKE,
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
+		.machine_check		= machine_check_4xx,
 		.platform		= "ppc440gp",
 	},
 	{	/* 440GP Rev. C */
@@ -1183,6 +1270,7 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_user_features	= COMMON_USER_BOOKE,
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
+		.machine_check		= machine_check_4xx,
 		.platform		= "ppc440gp",
 	},
 	{ /* 440GX Rev. A */
@@ -1193,6 +1281,8 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_user_features	= COMMON_USER_BOOKE,
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
+		.cpu_setup		= __setup_cpu_440gx,
+		.machine_check		= machine_check_440A,
 		.platform		= "ppc440",
 	},
 	{ /* 440GX Rev. B */
@@ -1203,6 +1293,8 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_user_features	= COMMON_USER_BOOKE,
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
+		.cpu_setup		= __setup_cpu_440gx,
+		.machine_check		= machine_check_440A,
 		.platform		= "ppc440",
 	},
 	{ /* 440GX Rev. C */
@@ -1213,6 +1305,8 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_user_features	= COMMON_USER_BOOKE,
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
+		.cpu_setup		= __setup_cpu_440gx,
+		.machine_check		= machine_check_440A,
 		.platform		= "ppc440",
 	},
 	{ /* 440GX Rev. F */
@@ -1223,6 +1317,8 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_user_features	= COMMON_USER_BOOKE,
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
+		.cpu_setup		= __setup_cpu_440gx,
+		.machine_check		= machine_check_440A,
 		.platform		= "ppc440",
 	},
 	{ /* 440SP Rev. A */
@@ -1233,6 +1329,7 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_user_features	= COMMON_USER_BOOKE,
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
+		.machine_check		= machine_check_4xx,
 		.platform		= "ppc440",
 	},
 	{ /* 440SPe Rev. A */
@@ -1243,6 +1340,8 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_user_features      = COMMON_USER_BOOKE,
 		.icache_bsize           = 32,
 		.dcache_bsize           = 32,
+		.cpu_setup		= __setup_cpu_440spe,
+		.machine_check		= machine_check_440A,
 		.platform               = "ppc440",
 	},
 	{ /* 440SPe Rev. B */
@@ -1253,6 +1352,8 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_user_features	= COMMON_USER_BOOKE,
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
+		.cpu_setup		= __setup_cpu_440spe,
+		.machine_check		= machine_check_440A,
 		.platform		= "ppc440",
 	},
 #endif /* CONFIG_44x */
@@ -1267,6 +1368,7 @@ static struct cpu_spec __initdata cpu_sp
 			PPC_FEATURE_HAS_EFP_SINGLE |
 			PPC_FEATURE_UNIFIED_CACHE,
 		.dcache_bsize		= 32,
+		.machine_check		= machine_check_e200,
 		.platform		= "ppc5554",
 	},
 	{	/* e200z6 */
@@ -1280,6 +1382,7 @@ static struct cpu_spec __initdata cpu_sp
 			PPC_FEATURE_HAS_EFP_SINGLE_COMP |
 			PPC_FEATURE_UNIFIED_CACHE,
 		.dcache_bsize		= 32,
+		.machine_check		= machine_check_e200,
 		.platform		= "ppc5554",
 	},
 	{	/* e500 */
@@ -1296,6 +1399,7 @@ static struct cpu_spec __initdata cpu_sp
 		.num_pmcs		= 4,
 		.oprofile_cpu_type	= "ppc/e500",
 		.oprofile_type		= PPC_OPROFILE_BOOKE,
+		.machine_check		= machine_check_e500,
 		.platform		= "ppc8540",
 	},
 	{	/* e500v2 */
@@ -1313,6 +1417,7 @@ static struct cpu_spec __initdata cpu_sp
 		.num_pmcs		= 4,
 		.oprofile_cpu_type	= "ppc/e500",
 		.oprofile_type		= PPC_OPROFILE_BOOKE,
+		.machine_check		= machine_check_e500,
 		.platform		= "ppc8548",
 	},
 #endif
Index: linux-merge/arch/powerpc/kernel/head_44x.S
===================================================================
--- linux-merge.orig/arch/powerpc/kernel/head_44x.S	2007-12-11 15:07:22.000000000 +1100
+++ linux-merge/arch/powerpc/kernel/head_44x.S	2007-12-11 16:11:27.000000000 +1100
@@ -289,11 +289,8 @@ interrupt_base:
 	CRITICAL_EXCEPTION(0x0100, CriticalInput, unknown_exception)
 
 	/* Machine Check Interrupt */
-#ifdef CONFIG_440A
-	MCHECK_EXCEPTION(0x0200, MachineCheck, machine_check_exception)
-#else
 	CRITICAL_EXCEPTION(0x0200, MachineCheck, machine_check_exception)
-#endif
+	MCHECK_EXCEPTION(0x0210, MachineCheckA, machine_check_exception)
 
 	/* Data Storage Interrupt */
 	START_EXCEPTION(DataStorage)
@@ -674,6 +671,15 @@ finish_tlb_load:
  */
 
 /*
+ * Adjust the machine check IVOR on 440A cores
+ */
+_GLOBAL(__fixup_440A_mcheck)
+	li	r3,MachineCheckA@l
+	mtspr	SPRN_IVOR1,r3
+	sync
+	blr
+
+/*
  * extern void giveup_altivec(struct task_struct *prev)
  *
  * The 44x core does not have an AltiVec unit.
Index: linux-merge/arch/powerpc/kernel/traps.c
===================================================================
--- linux-merge.orig/arch/powerpc/kernel/traps.c	2007-10-25 13:15:47.000000000 +1000
+++ linux-merge/arch/powerpc/kernel/traps.c	2007-12-11 16:11:27.000000000 +1100
@@ -334,18 +334,25 @@ static inline int check_io_access(struct
 #define clear_single_step(regs)	((regs)->msr &= ~MSR_SE)
 #endif
 
-static int generic_machine_check_exception(struct pt_regs *regs)
+#if defined(CONFIG_4xx)
+int machine_check_4xx(struct pt_regs *regs)
 {
 	unsigned long reason = get_mc_reason(regs);
 
-#if defined(CONFIG_4xx) && !defined(CONFIG_440A)
 	if (reason & ESR_IMCP) {
 		printk("Instruction");
 		mtspr(SPRN_ESR, reason & ~ESR_IMCP);
 	} else
 		printk("Data");
 	printk(" machine check in kernel mode.\n");
-#elif defined(CONFIG_440A)
+
+	return 0;
+}
+
+int machine_check_440A(struct pt_regs *regs)
+{
+	unsigned long reason = get_mc_reason(regs);
+
 	printk("Machine check in kernel mode.\n");
 	if (reason & ESR_IMCP){
 		printk("Instruction Synchronous Machine Check exception\n");
@@ -375,7 +382,13 @@ static int generic_machine_check_excepti
 		/* Clear MCSR */
 		mtspr(SPRN_MCSR, mcsr);
 	}
-#elif defined (CONFIG_E500)
+	return 0;
+}
+#elif defined(CONFIG_E500)
+int machine_check_e500(struct pt_regs *regs)
+{
+	unsigned long reason = get_mc_reason(regs);
+
 	printk("Machine check in kernel mode.\n");
 	printk("Caused by (from MCSR=%lx): ", reason);
 
@@ -403,7 +416,14 @@ static int generic_machine_check_excepti
 		printk("Bus - Instruction Parity Error\n");
 	if (reason & MCSR_BUS_RPERR)
 		printk("Bus - Read Parity Error\n");
-#elif defined (CONFIG_E200)
+
+	return 0;
+}
+#elif defined(CONFIG_E200)
+int machine_check_e200(struct pt_regs *regs)
+{
+	unsigned long reason = get_mc_reason(regs);
+
 	printk("Machine check in kernel mode.\n");
 	printk("Caused by (from MCSR=%lx): ", reason);
 
@@ -421,7 +441,14 @@ static int generic_machine_check_excepti
 		printk("Bus - Read Bus Error on data load\n");
 	if (reason & MCSR_BUS_WRERR)
 		printk("Bus - Write Bus Error on buffered store or cache line push\n");
-#else /* !CONFIG_4xx && !CONFIG_E500 && !CONFIG_E200 */
+
+	return 0;
+}
+#else
+int machine_check_generic(struct pt_regs *regs)
+{
+	unsigned long reason = get_mc_reason(regs);
+
 	printk("Machine check in kernel mode.\n");
 	printk("Caused by (from SRR1=%lx): ", reason);
 	switch (reason & 0x601F0000) {
@@ -451,22 +478,26 @@ static int generic_machine_check_excepti
 	default:
 		printk("Unknown values in msr\n");
 	}
-#endif /* CONFIG_4xx */
-
 	return 0;
 }
+#endif /* everything else */
 
 void machine_check_exception(struct pt_regs *regs)
 {
 	int recover = 0;
 
-	/* See if any machine dependent calls */
+	/* See if any machine dependent calls. In theory, we would want
+	 * to call the CPU first, and call the ppc_md. one if the CPU
+	 * one returns a positive number. However there is existing code
+	 * that assumes the board gets a first chance, so let's keep it
+	 * that way for now and fix things later. --BenH.
+	 */
 	if (ppc_md.machine_check_exception)
 		recover = ppc_md.machine_check_exception(regs);
-	else
-		recover = generic_machine_check_exception(regs);
+	else if (cur_cpu_spec->machine_check)
+		recover = cur_cpu_spec->machine_check(regs);
 
-	if (recover)
+	if (recover > 0)
 		return;
 
 	if (user_mode(regs)) {
@@ -476,7 +507,12 @@ void machine_check_exception(struct pt_r
 	}
 
 #if defined(CONFIG_8xx) && defined(CONFIG_PCI)
-	/* the qspan pci read routines can cause machine checks -- Cort */
+	/* the qspan pci read routines can cause machine checks -- Cort
+	 *
+	 * yuck !!! that totally needs to go away ! There are better ways
+	 * to deal with that than having a wart in the mcheck handler.
+	 * -- BenH
+	 */
 	bad_page_fault(regs, regs->dar, SIGBUS);
 	return;
 #endif
Index: linux-merge/arch/powerpc/platforms/44x/Kconfig
===================================================================
--- linux-merge.orig/arch/powerpc/platforms/44x/Kconfig	2007-10-25 13:15:47.000000000 +1000
+++ linux-merge/arch/powerpc/platforms/44x/Kconfig	2007-12-11 16:11:27.000000000 +1100
@@ -62,11 +62,6 @@ config 440GX
 config 440SP
 	bool
 
-config 440A
-	bool
-	depends on 440GX || 440EPX
-	default y
-
 # 44x errata/workaround config symbols, selected by the CPU models above
 config IBM440EP_ERR42
 	bool
Index: linux-merge/arch/powerpc/kernel/head_booke.h
===================================================================
--- linux-merge.orig/arch/powerpc/kernel/head_booke.h	2007-07-27 13:44:42.000000000 +1000
+++ linux-merge/arch/powerpc/kernel/head_booke.h	2007-12-11 16:11:27.000000000 +1100
@@ -166,7 +166,7 @@ label:
 	mfspr	r5,SPRN_ESR;					\
 	stw	r5,_ESR(r11);					\
 	addi	r3,r1,STACK_FRAME_OVERHEAD;			\
-	EXC_XFER_TEMPLATE(hdlr, n+2, (MSR_KERNEL & ~(MSR_ME|MSR_DE|MSR_CE)), \
+	EXC_XFER_TEMPLATE(hdlr, n+4, (MSR_KERNEL & ~(MSR_ME|MSR_DE|MSR_CE)), \
 			  NOCOPY, mcheck_transfer_to_handler,   \
 			  ret_from_mcheck_exc)
 
Index: linux-merge/include/asm-powerpc/ptrace.h
===================================================================
--- linux-merge.orig/include/asm-powerpc/ptrace.h	2007-09-28 11:42:10.000000000 +1000
+++ linux-merge/include/asm-powerpc/ptrace.h	2007-12-11 16:11:27.000000000 +1100
@@ -106,7 +106,8 @@ extern int ptrace_put_reg(struct task_st
  */
 #define FULL_REGS(regs)		(((regs)->trap & 1) == 0)
 #ifndef __powerpc64__
-#define IS_CRITICAL_EXC(regs)	(((regs)->trap & 2) == 0)
+#define IS_CRITICAL_EXC(regs)	(((regs)->trap & 2) != 0)
+#define IS_MCHECK_EXC(regs)	(((regs)->trap & 4) != 0)
 #endif /* ! __powerpc64__ */
 #define TRAP(regs)		((regs)->trap & ~0xF)
 #ifdef __powerpc64__
Index: linux-merge/include/asm-powerpc/reg_booke.h
===================================================================
--- linux-merge.orig/include/asm-powerpc/reg_booke.h	2007-09-28 11:42:10.000000000 +1000
+++ linux-merge/include/asm-powerpc/reg_booke.h	2007-12-11 16:11:27.000000000 +1100
@@ -207,7 +207,6 @@
 #define	CCR1_TCS	0x00000080 /* Timer Clock Select */
 
 /* Bit definitions for the MCSR. */
-#ifdef CONFIG_440A
 #define MCSR_MCS	0x80000000 /* Machine Check Summary */
 #define MCSR_IB		0x40000000 /* Instruction PLB Error */
 #define MCSR_DRB	0x20000000 /* Data Read PLB Error */
@@ -217,7 +216,7 @@
 #define MCSR_DCSP	0x02000000 /* D-Cache Search Parity Error */
 #define MCSR_DCFP	0x01000000 /* D-Cache Flush Parity Error */
 #define MCSR_IMPE	0x00800000 /* Imprecise Machine Check Exception */
-#endif
+
 #ifdef CONFIG_E500
 #define MCSR_MCP 	0x80000000UL /* Machine Check Input Pin */
 #define MCSR_ICPERR 	0x40000000UL /* I-Cache Parity Error */
Index: linux-merge/include/asm-powerpc/cputable.h
===================================================================
--- linux-merge.orig/include/asm-powerpc/cputable.h	2007-11-27 18:25:29.000000000 +1100
+++ linux-merge/include/asm-powerpc/cputable.h	2007-12-11 16:11:27.000000000 +1100
@@ -57,6 +57,14 @@ enum powerpc_pmc_type {
 	PPC_PMC_PA6T = 2,
 };
 
+struct pt_regs;
+
+extern int machine_check_generic(struct pt_regs *regs);
+extern int machine_check_4xx(struct pt_regs *regs);
+extern int machine_check_440A(struct pt_regs *regs);
+extern int machine_check_e500(struct pt_regs *regs);
+extern int machine_check_e200(struct pt_regs *regs);
+
 /* NOTE WELL: Update identify_cpu() if fields are added or removed! */
 struct cpu_spec {
 	/* CPU is matched via (PVR & pvr_mask) == pvr_value */
@@ -97,6 +105,11 @@ struct cpu_spec {
 
 	/* Name of processor class, for the ELF AT_PLATFORM entry */
 	char		*platform;
+
+	/* Processor specific machine check handling. Return negative
+	 * if the error is fatal, 1 if it was fully recovered and 0 to
+	 * pass up (not CPU originated) */
+	int		(*machine_check)(struct pt_regs *regs);
 };
 
 extern struct cpu_spec		*cur_cpu_spec;
Index: linux-merge/arch/ppc/kernel/traps.c
===================================================================
--- linux-merge.orig/arch/ppc/kernel/traps.c	2007-12-11 16:11:45.000000000 +1100
+++ linux-merge/arch/ppc/kernel/traps.c	2007-12-11 16:23:35.000000000 +1100
@@ -231,39 +231,25 @@ platform_machine_check(struct pt_regs *r
 {
 }
 
-void machine_check_exception(struct pt_regs *regs)
+#if defined(CONFIG_4xx)
+int machine_check_4xx(struct pt_regs *regs)
 {
 	unsigned long reason = get_mc_reason(regs);
 
-	if (user_mode(regs)) {
-		regs->msr |= MSR_RI;
-		_exception(SIGBUS, regs, BUS_ADRERR, regs->nip);
-		return;
-	}
-
-#if defined(CONFIG_8xx) && defined(CONFIG_PCI)
-	/* the qspan pci read routines can cause machine checks -- Cort */
-	bad_page_fault(regs, regs->dar, SIGBUS);
-	return;
-#endif
-
-	if (debugger_fault_handler) {
-		debugger_fault_handler(regs);
-		regs->msr |= MSR_RI;
-		return;
-	}
-
-	if (check_io_access(regs))
-		return;
-
-#if defined(CONFIG_4xx) && !defined(CONFIG_440A)
 	if (reason & ESR_IMCP) {
 		printk("Instruction");
 		mtspr(SPRN_ESR, reason & ~ESR_IMCP);
 	} else
 		printk("Data");
 	printk(" machine check in kernel mode.\n");
-#elif defined(CONFIG_440A)
+
+	return 0;
+}
+
+int machine_check_440A(struct pt_regs *regs)
+{
+	unsigned long reason = get_mc_reason(regs);
+
 	printk("Machine check in kernel mode.\n");
 	if (reason & ESR_IMCP){
 		printk("Instruction Synchronous Machine Check exception\n");
@@ -293,7 +279,13 @@ void machine_check_exception(struct pt_r
 		/* Clear MCSR */
 		mtspr(SPRN_MCSR, mcsr);
 	}
-#elif defined (CONFIG_E500)
+	return 0;
+}
+#elif defined(CONFIG_E500)
+int machine_check_e500(struct pt_regs *regs)
+{
+	unsigned long reason = get_mc_reason(regs);
+
 	printk("Machine check in kernel mode.\n");
 	printk("Caused by (from MCSR=%lx): ", reason);
 
@@ -305,8 +297,6 @@ void machine_check_exception(struct pt_r
 		printk("Data Cache Push Parity Error\n");
 	if (reason & MCSR_DCPERR)
 		printk("Data Cache Parity Error\n");
-	if (reason & MCSR_GL_CI)
-		printk("Guarded Load or Cache-Inhibited stwcx.\n");
 	if (reason & MCSR_BUS_IAERR)
 		printk("Bus - Instruction Address Error\n");
 	if (reason & MCSR_BUS_RAERR)
@@ -318,12 +308,19 @@ void machine_check_exception(struct pt_r
 	if (reason & MCSR_BUS_RBERR)
 		printk("Bus - Read Data Bus Error\n");
 	if (reason & MCSR_BUS_WBERR)
-		printk("Bus - Write Data Bus Error\n");
+		printk("Bus - Read Data Bus Error\n");
 	if (reason & MCSR_BUS_IPERR)
 		printk("Bus - Instruction Parity Error\n");
 	if (reason & MCSR_BUS_RPERR)
 		printk("Bus - Read Parity Error\n");
-#elif defined (CONFIG_E200)
+
+	return 0;
+}
+#elif defined(CONFIG_E200)
+int machine_check_e200(struct pt_regs *regs)
+{
+	unsigned long reason = get_mc_reason(regs);
+
 	printk("Machine check in kernel mode.\n");
 	printk("Caused by (from MCSR=%lx): ", reason);
 
@@ -341,7 +338,14 @@ void machine_check_exception(struct pt_r
 		printk("Bus - Read Bus Error on data load\n");
 	if (reason & MCSR_BUS_WRERR)
 		printk("Bus - Write Bus Error on buffered store or cache line push\n");
-#else /* !CONFIG_4xx && !CONFIG_E500 && !CONFIG_E200 */
+
+	return 0;
+}
+#else
+int machine_check_generic(struct pt_regs *regs)
+{
+	unsigned long reason = get_mc_reason(regs);
+
 	printk("Machine check in kernel mode.\n");
 	printk("Caused by (from SRR1=%lx): ", reason);
 	switch (reason & 0x601F0000) {
@@ -371,7 +375,39 @@ void machine_check_exception(struct pt_r
 	default:
 		printk("Unknown values in msr\n");
 	}
-#endif /* CONFIG_4xx */
+	return 0;
+}
+#endif /* everything else */
+
+void machine_check_exception(struct pt_regs *regs)
+{
+	int recover = 0;
+
+	if (cur_cpu_spec->machine_check)
+		recover = cur_cpu_spec->machine_check(regs);
+	if (recover > 0)
+		return;
+
+	if (user_mode(regs)) {
+		regs->msr |= MSR_RI;
+		_exception(SIGBUS, regs, BUS_ADRERR, regs->nip);
+		return;
+	}
+
+#if defined(CONFIG_8xx) && defined(CONFIG_PCI)
+	/* the qspan pci read routines can cause machine checks -- Cort */
+	bad_page_fault(regs, regs->dar, SIGBUS);
+	return;
+#endif
+
+	if (debugger_fault_handler) {
+		debugger_fault_handler(regs);
+		regs->msr |= MSR_RI;
+		return;
+	}
+
+	if (check_io_access(regs))
+		return;
 
 	/*
 	 * Optional platform-provided routine to print out
Index: linux-merge/include/asm-ppc/reg_booke.h
===================================================================
--- linux-merge.orig/include/asm-ppc/reg_booke.h	2007-12-11 16:21:38.000000000 +1100
+++ linux-merge/include/asm-ppc/reg_booke.h	2007-12-11 16:23:21.000000000 +1100
@@ -207,7 +207,7 @@
 #define	CCR1_TCS	0x00000080 /* Timer Clock Select */
 
 /* Bit definitions for the MCSR. */
-#ifdef CONFIG_440A
+#ifdef CONFIG_44x
 #define MCSR_MCS	0x80000000 /* Machine Check Summary */
 #define MCSR_IB		0x40000000 /* Instruction PLB Error */
 #define MCSR_DRB	0x20000000 /* Data Read PLB Error */

^ permalink raw reply

* [PATCH 2/20] [POWERPC] Improve support for 4xx indirect DCRs
From: Benjamin Herrenschmidt @ 2007-12-13  7:38 UTC (permalink / raw)
  To: Josh Boyer; +Cc: linuxppc-dev

Accessing indirect DCRs is done via a pair of address/data DCRs.

Such accesses are thus inherently racy, vs. interrupts, preemption
and possibly SMP if 4xx SMP cores are ever used.

This updates the mfdcri/mtdcri macros in dcr-native.h (which were
so far unused) to use a spinlock.

In addition, add some common definitions to a new dcr-regs.h file.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---

 include/asm-powerpc/dcr-native.h |   30 ++++++++++------
 include/asm-powerpc/dcr-regs.h   |   71 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 91 insertions(+), 10 deletions(-)

--- linux-work.orig/include/asm-powerpc/dcr-native.h	2007-12-10 15:52:26.000000000 +1100
+++ linux-work/include/asm-powerpc/dcr-native.h	2007-12-10 15:53:36.000000000 +1100
@@ -22,6 +22,8 @@
 #ifdef __KERNEL__
 #ifndef __ASSEMBLY__
 
+#include <linux/spinlock.h>
+
 typedef struct {
 	unsigned int base;
 } dcr_host_t;
@@ -55,20 +57,28 @@ do {								\
 } while (0)
 
 /* R/W of indirect DCRs make use of standard naming conventions for DCRs */
-#define mfdcri(base, reg)			\
-({						\
-	mtdcr(base ## _CFGADDR, base ## _ ## reg);	\
-	mfdcr(base ## _CFGDATA);			\
+extern spinlock_t dcr_ind_lock;
+
+#define mfdcri(base, reg)				\
+({							\
+	unsigned long flags; 				\
+	unsigned int val;				\
+	spin_lock_irqsave(&dcr_ind_lock, flags);	\
+	mtdcr(DCRN_ ## base ## _CONFIG_ADDR, reg);	\
+	val = mfdcr(DCRN_ ## base ## _CONFIG_DATA);	\
+	spin_unlock_irqrestore(&dcr_ind_lock, flags);	\
+	val;						\
 })
 
-#define mtdcri(base, reg, data)			\
-do {						\
-	mtdcr(base ## _CFGADDR, base ## _ ## reg);	\
-	mtdcr(base ## _CFGDATA, data);		\
+#define mtdcri(base, reg, data)				\
+do {							\
+	unsigned long flags; 				\
+	spin_lock_irqsave(&dcr_ind_lock, flags);	\
+	mtdcr(DCRN_ ## base ## _CONFIG_ADDR, reg);	\
+	mtdcr(DCRN_ ## base ## _CONFIG_DATA, data);	\
+	spin_unlock_irqrestore(&dcr_ind_lock, flags);	\
 } while (0)
 
 #endif /* __ASSEMBLY__ */
 #endif /* __KERNEL__ */
 #endif /* _ASM_POWERPC_DCR_NATIVE_H */
-
-
Index: linux-work/include/asm-powerpc/dcr-regs.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-work/include/asm-powerpc/dcr-regs.h	2007-12-10 15:53:33.000000000 +1100
@@ -0,0 +1,71 @@
+/*
+ * Common DCR / SDR / CPR register definitions used on various IBM/AMCC
+ * 4xx processors
+ *
+ *    Copyright 2007 Benjamin Herrenschmidt, IBM Corp
+ *                   <benh@kernel.crashing.org>
+ *
+ * Mostly lifted from asm-ppc/ibm4xx.h by
+ *
+ *    Copyright (c) 1999 Grant Erickson <grant@lcse.umn.edu>
+ *
+ */
+
+#ifndef __DCR_REGS_H__
+#define __DCR_REGS_H__
+
+/*
+ * Most DCRs used for controlling devices such as the MAL, DMA engine,
+ * etc... are obtained for the device tree.
+ *
+ * The definitions in this files are fixed DCRs and indirect DCRs that
+ * are commonly used outside of specific drivers or refer to core
+ * common registers that may occasionally have to be tweaked outside
+ * of the driver main register set
+ */
+
+/* CPRs (440GX and 440SP/440SPe) */
+#define DCRN_CPR0_CONFIG_ADDR	0xc
+#define DCRN_CPR0_CONFIG_DATA	0xd
+
+/* SDRs (440GX and 440SP/440SPe) */
+#define DCRN_SDR0_CONFIG_ADDR 	0xe
+#define DCRN_SDR0_CONFIG_DATA	0xf
+
+#define SDR0_PFC0		0x4100
+#define SDR0_PFC1		0x4101
+#define SDR0_PFC1_EPS		0x1c00000
+#define SDR0_PFC1_EPS_SHIFT	22
+#define SDR0_PFC1_RMII		0x02000000
+#define SDR0_MFR		0x4300
+#define SDR0_MFR_TAH0 		0x80000000  	/* TAHOE0 Enable */
+#define SDR0_MFR_TAH1 		0x40000000  	/* TAHOE1 Enable */
+#define SDR0_MFR_PCM  		0x10000000  	/* PPC440GP irq compat mode */
+#define SDR0_MFR_ECS  		0x08000000  	/* EMAC int clk */
+#define SDR0_MFR_T0TXFL		0x00080000
+#define SDR0_MFR_T0TXFH		0x00040000
+#define SDR0_MFR_T1TXFL		0x00020000
+#define SDR0_MFR_T1TXFH		0x00010000
+#define SDR0_MFR_E0TXFL		0x00008000
+#define SDR0_MFR_E0TXFH		0x00004000
+#define SDR0_MFR_E0RXFL		0x00002000
+#define SDR0_MFR_E0RXFH		0x00001000
+#define SDR0_MFR_E1TXFL		0x00000800
+#define SDR0_MFR_E1TXFH		0x00000400
+#define SDR0_MFR_E1RXFL		0x00000200
+#define SDR0_MFR_E1RXFH		0x00000100
+#define SDR0_MFR_E2TXFL		0x00000080
+#define SDR0_MFR_E2TXFH		0x00000040
+#define SDR0_MFR_E2RXFL		0x00000020
+#define SDR0_MFR_E2RXFH		0x00000010
+#define SDR0_MFR_E3TXFL		0x00000008
+#define SDR0_MFR_E3TXFH		0x00000004
+#define SDR0_MFR_E3RXFL		0x00000002
+#define SDR0_MFR_E3RXFH		0x00000001
+#define SDR0_UART0		0x0120
+#define SDR0_UART1		0x0121
+#define SDR0_UART2		0x0122
+#define SDR0_UART3		0x0123
+#define SDR0_CUST0		0x4000
+
+#endif /* __DCR_REGS_H__ */

^ permalink raw reply

* [PATCH 3/20] [POWERPC] 4xx PLB to PCI-X support
From: Benjamin Herrenschmidt @ 2007-12-13  7:38 UTC (permalink / raw)
  To: Josh Boyer; +Cc: linuxppc-dev

This adds base support code for the 4xx PCI-X bridge. It also provides
placeholders for the PCI and PCI-E version but they aren't supported
with this patch.

The bridges are configured based on device-tree properties.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---

Tested on 440GP only so far.

 arch/powerpc/sysdev/Makefile     |    3 
 arch/powerpc/sysdev/ppc4xx_pci.c |  339 +++++++++++++++++++++++++++++++++++++++
 arch/powerpc/sysdev/ppc4xx_pci.h |  106 ++++++++++++
 3 files changed, 448 insertions(+)

--- linux-merge.orig/arch/powerpc/sysdev/Makefile	2007-10-25 13:15:47.000000000 +1000
+++ linux-merge/arch/powerpc/sysdev/Makefile	2007-12-11 16:32:58.000000000 +1100
@@ -27,6 +27,9 @@ obj-$(CONFIG_PPC_I8259)		+= i8259.o
 obj-$(CONFIG_PPC_83xx)		+= ipic.o
 obj-$(CONFIG_4xx)		+= uic.o
 obj-$(CONFIG_XILINX_VIRTEX)	+= xilinx_intc.o
+ifeq ($(CONFIG_PCI),y)
+obj-$(CONFIG_4xx)		+= ppc4xx_pci.o
+endif
 endif
 
 # Temporary hack until we have migrated to asm-powerpc
Index: linux-merge/arch/powerpc/sysdev/ppc4xx_pci.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-merge/arch/powerpc/sysdev/ppc4xx_pci.c	2007-12-11 16:52:29.000000000 +1100
@@ -0,0 +1,339 @@
+/*
+ * PCI / PCI-X / PCI-Express support for 4xx parts
+ *
+ * Copyright 2007 Ben. Herrenschmidt <benh@kernel.crashing.org>, IBM Corp.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/of.h>
+
+#include <asm/io.h>
+#include <asm/pci-bridge.h>
+#include <asm/machdep.h>
+
+#include "ppc4xx_pci.h"
+
+static int dma_offset_set;
+
+/* Move that to a useable header */
+extern unsigned long total_memory;
+
+static int __init ppc4xx_parse_dma_ranges(struct pci_controller *hose,
+					  void __iomem *reg,
+					  struct resource *res)
+{
+	u64 size;
+	const u32 *ranges;
+	int rlen;
+	int pna = of_n_addr_cells(hose->dn);
+	int np = pna + 5;
+
+	/* Default */
+	res->start = 0;
+	res->end = size = 0x80000000;
+	res->flags = IORESOURCE_MEM | IORESOURCE_PREFETCH;
+
+	/* Get dma-ranges property */
+	ranges = of_get_property(hose->dn, "dma-ranges", &rlen);
+	if (ranges == NULL)
+		goto out;
+
+	/* Walk it */
+	while ((rlen -= np * 4) >= 0) {
+		u32 pci_space = ranges[0];
+		u64 pci_addr = of_read_number(ranges + 1, 2);
+		u64 cpu_addr = of_translate_dma_address(hose->dn, ranges + 3);
+		size = of_read_number(ranges + pna + 3, 2);
+		ranges += np;
+		if (cpu_addr == OF_BAD_ADDR || size == 0)
+			continue;
+
+		/* We only care about memory */
+		if ((pci_space & 0x03000000) != 0x02000000)
+			continue;
+
+		/* We currently only support memory at 0, and pci_addr
+		 * within 32 bits space
+		 */
+		if (cpu_addr != 0 || pci_addr > 0xffffffff) {
+			printk(KERN_WARNING "%s: Ignored unsupported dma range"
+			       " 0x%016llx...0x%016llx -> 0x%016llx\n",
+			       hose->dn->full_name,
+			       pci_addr, pci_addr + size - 1, cpu_addr);
+			continue;
+		}
+
+		/* Check if not prefetchable */
+		if (!(pci_space & 0x40000000))
+			res->flags &= ~IORESOURCE_PREFETCH;
+
+
+		/* Use that */
+		res->start = pci_addr;
+#ifndef CONFIG_RESOURCES_64BIT
+		/* Beware of 32 bits resources */
+		if ((pci_addr + size) > 0x100000000ull)
+			res->end = 0xffffffff;
+		else
+#endif
+			res->end = res->start + size - 1;
+		break;
+	}
+
+	/* We only support one global DMA offset */
+	if (dma_offset_set && pci_dram_offset != res->start) {
+		printk(KERN_ERR "%s: dma-ranges(s) mismatch\n",
+		       hose->dn->full_name);
+		return -ENXIO;
+	}
+
+	/* Check that we can fit all of memory as we don't support
+	 * DMA bounce buffers
+	 */
+	if (size < total_memory) {
+		printk(KERN_ERR "%s: dma-ranges too small "
+		       "(size=%llx total_memory=%lx)\n",
+		       hose->dn->full_name, size, total_memory);
+		return -ENXIO;
+	}
+
+	/* Check we are a power of 2 size and that base is a multiple of size*/
+	if (!is_power_of_2(size) ||
+	    (res->start & (size - 1)) != 0) {
+		printk(KERN_ERR "%s: dma-ranges unaligned\n",
+		       hose->dn->full_name);
+		return -ENXIO;
+	}
+
+	/* Check that we are fully contained within 32 bits space */
+	if (res->end > 0xffffffff) {
+		printk(KERN_ERR "%s: dma-ranges outside of 32 bits space\n",
+		       hose->dn->full_name);
+		return -ENXIO;
+	}
+ out:
+	dma_offset_set = 1;
+	pci_dram_offset = res->start;
+
+	printk(KERN_INFO "4xx PCI DMA offset set to 0x%08lx\n",
+	       pci_dram_offset);
+	return 0;
+}
+
+/*
+ * 4xx PCI 2.x part
+ */
+static void __init ppc4xx_probe_pci_bridge(struct device_node *np)
+{
+	/* NYI */
+}
+
+/*
+ * 4xx PCI-X part
+ */
+
+static void __init ppc4xx_configure_pcix_POMs(struct pci_controller *hose,
+					      void __iomem *reg)
+{
+	u32 lah, lal, pciah, pcial, sa;
+	int i, j;
+
+	/* Setup outbound memory windows */
+	for (i = j = 0; i < 3; i++) {
+		struct resource *res = &hose->mem_resources[i];
+
+		/* we only care about memory windows */
+		if (!(res->flags & IORESOURCE_MEM))
+			continue;
+		if (j > 1) {
+			printk(KERN_WARNING "%s: Too many ranges\n",
+			       hose->dn->full_name);
+			break;
+		}
+
+		/* Calculate register values */
+#ifdef CONFIG_PTE_64BIT
+		lah = res->start >> 32;
+		lal = res->start & 0xffffffffu;
+		pciah = (res->start - hose->pci_mem_offset) >> 32;
+		pcial = (res->start - hose->pci_mem_offset) & 0xffffffffu;
+#else
+		lah = pciah = 0;
+		lal = res->start;
+		pcial = res->start - hose->pci_mem_offset;
+#endif
+		sa = res->end + 1 - res->start;
+		if (!is_power_of_2(sa) || sa < 0x100000 ||
+		    sa > 0xffffffffu) {
+			printk(KERN_WARNING "%s: Resource out of range\n",
+			       hose->dn->full_name);
+			continue;
+		}
+		sa = (0xffffffffu << ilog2(sa)) | 0x1;
+
+		/* Program register values */
+		if (j == 0) {
+			writel(lah, reg + PCIX0_POM0LAH);
+			writel(lal, reg + PCIX0_POM0LAL);
+			writel(pciah, reg + PCIX0_POM0PCIAH);
+			writel(pcial, reg + PCIX0_POM0PCIAL);
+			writel(sa, reg + PCIX0_POM0SA);
+		} else {
+			writel(lah, reg + PCIX0_POM1LAH);
+			writel(lal, reg + PCIX0_POM1LAL);
+			writel(pciah, reg + PCIX0_POM1PCIAH);
+			writel(pcial, reg + PCIX0_POM1PCIAL);
+			writel(sa, reg + PCIX0_POM1SA);
+		}
+		j++;
+	}
+}
+
+static void __init ppc4xx_configure_pcix_PIMs(struct pci_controller *hose,
+					      void __iomem *reg,
+					      const struct resource *res,
+					      int big_pim,
+					      int enable_msi_hole)
+{
+	resource_size_t size = res->end - res->start + 1;
+	u32 sa;
+
+	/* RAM is always at 0 */
+	writel(0x00000000, reg + PCIX0_PIM0LAH);
+	writel(0x00000000, reg + PCIX0_PIM0LAL);
+
+	/* Calculate window size */
+	sa = (0xffffffffu << ilog2(size)) | 1;
+	sa |= 0x1;
+	if (res->flags & IORESOURCE_PREFETCH)
+		sa |= 0x2;
+	if (enable_msi_hole)
+		sa |= 0x4;
+	writel(sa, reg + PCIX0_PIM0SA);
+	if (big_pim)
+		writel(0xffffffff, reg + PCIX0_PIM0SAH);
+
+	/* Map on PCI side */
+	writel(0x00000000, reg + PCIX0_BAR0H);
+	writel(res->start, reg + PCIX0_BAR0L);
+	writew(0x0006, reg + PCIX0_COMMAND);
+}
+
+static void __init ppc4xx_probe_pcix_bridge(struct device_node *np)
+{
+	struct resource rsrc_cfg;
+	struct resource rsrc_reg;
+	struct resource dma_window;
+	struct pci_controller *hose = NULL;
+	void __iomem *reg = NULL;
+	const int *bus_range;
+	int big_pim = 0, msi = 0, primary = 0;
+
+	/* Fetch config space registers address */
+	if (of_address_to_resource(np, 0, &rsrc_cfg)) {
+		printk(KERN_ERR "%s:Can't get PCI-X config register base !",
+		       np->full_name);
+		return;
+	}
+	/* Fetch host bridge internal registers address */
+	if (of_address_to_resource(np, 3, &rsrc_reg)) {
+		printk(KERN_ERR "%s: Can't get PCI-X internal register base !",
+		       np->full_name);
+		return;
+	}
+
+	/* Check if it supports large PIMs (440GX) */
+	if (of_get_property(np, "large-inbound-windows", NULL))
+		big_pim = 1;
+
+	/* Check if we should enable MSIs inbound hole */
+	if (of_get_property(np, "enable-msi-hole", NULL))
+		msi = 1;
+
+	/* Check if primary bridge */
+	if (of_get_property(np, "primary", NULL))
+		primary = 1;
+
+	/* Get bus range if any */
+	bus_range = of_get_property(np, "bus-range", NULL);
+
+	/* Map registers */
+	reg = ioremap(rsrc_reg.start, rsrc_reg.end + 1 - rsrc_reg.start);
+	if (reg == NULL) {
+		printk(KERN_ERR "%s: Can't map registers !", np->full_name);
+		goto fail;
+	}
+
+	/* Allocate the host controller data structure */
+	hose = pcibios_alloc_controller(np);
+	if (!hose)
+		goto fail;
+
+	hose->first_busno = bus_range ? bus_range[0] : 0x0;
+	hose->last_busno = bus_range ? bus_range[1] : 0xff;
+
+	/* Setup config space */
+	setup_indirect_pci(hose, rsrc_cfg.start, rsrc_cfg.start + 0x4, 0);
+
+	/* Disable all windows */
+	writel(0, reg + PCIX0_POM0SA);
+	writel(0, reg + PCIX0_POM1SA);
+	writel(0, reg + PCIX0_POM2SA);
+	writel(0, reg + PCIX0_PIM0SA);
+	writel(0, reg + PCIX0_PIM1SA);
+	writel(0, reg + PCIX0_PIM2SA);
+	if (big_pim) {
+		writel(0, reg + PCIX0_PIM0SAH);
+		writel(0, reg + PCIX0_PIM2SAH);
+	}
+
+	/* Parse outbound mapping resources */
+	pci_process_bridge_OF_ranges(hose, np, primary);
+
+	/* Parse inbound mapping resources */
+	if (ppc4xx_parse_dma_ranges(hose, reg, &dma_window) != 0)
+		goto fail;
+
+	/* Configure outbound ranges POMs */
+	ppc4xx_configure_pcix_POMs(hose, reg);
+
+	/* Configure inbound ranges PIMs */
+	ppc4xx_configure_pcix_PIMs(hose, reg, &dma_window, big_pim, msi);
+
+	/* We don't need the registers anymore */
+	iounmap(reg);
+	return;
+
+ fail:
+	if (hose)
+		pcibios_free_controller(hose);
+	if (reg)
+		iounmap(reg);
+}
+
+/*
+ * 4xx PCI-Express part
+ */
+static void __init ppc4xx_probe_pciex_bridge(struct device_node *np)
+{
+	/* NYI */
+}
+
+static int __init ppc4xx_pci_find_bridges(void)
+{
+	struct device_node *np;
+
+	for_each_compatible_node(np, NULL, "ibm,plb-pciex")
+		ppc4xx_probe_pciex_bridge(np);
+	for_each_compatible_node(np, NULL, "ibm,plb-pcix")
+		ppc4xx_probe_pcix_bridge(np);
+	for_each_compatible_node(np, NULL, "ibm,plb-pci")
+		ppc4xx_probe_pci_bridge(np);
+
+	return 0;
+}
+arch_initcall(ppc4xx_pci_find_bridges);
+
Index: linux-merge/arch/powerpc/sysdev/ppc4xx_pci.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-merge/arch/powerpc/sysdev/ppc4xx_pci.h	2007-12-11 16:52:19.000000000 +1100
@@ -0,0 +1,106 @@
+/*
+ * PCI / PCI-X / PCI-Express support for 4xx parts
+ *
+ * Copyright 2007 Ben. Herrenschmidt <benh@kernel.crashing.org>, IBM Corp.
+ *
+ * Bits and pieces extracted from arch/ppc support by
+ *
+ * Matt Porter <mporter@kernel.crashing.org>
+ *
+ * Copyright 2002-2005 MontaVista Software Inc.
+ */
+#ifndef __PPC4XX_PCI_H__
+#define __PPC4XX_PCI_H__
+
+/*
+ * 4xx PCI-X bridge register definitions
+ */
+#define PCIX0_VENDID		0x000
+#define PCIX0_DEVID		0x002
+#define PCIX0_COMMAND		0x004
+#define PCIX0_STATUS		0x006
+#define PCIX0_REVID		0x008
+#define PCIX0_CLS		0x009
+#define PCIX0_CACHELS		0x00c
+#define PCIX0_LATTIM		0x00d
+#define PCIX0_HDTYPE		0x00e
+#define PCIX0_BIST		0x00f
+#define PCIX0_BAR0L		0x010
+#define PCIX0_BAR0H		0x014
+#define PCIX0_BAR1		0x018
+#define PCIX0_BAR2L		0x01c
+#define PCIX0_BAR2H		0x020
+#define PCIX0_BAR3		0x024
+#define PCIX0_CISPTR		0x028
+#define PCIX0_SBSYSVID		0x02c
+#define PCIX0_SBSYSID		0x02e
+#define PCIX0_EROMBA		0x030
+#define PCIX0_CAP		0x034
+#define PCIX0_RES0		0x035
+#define PCIX0_RES1		0x036
+#define PCIX0_RES2		0x038
+#define PCIX0_INTLN		0x03c
+#define PCIX0_INTPN		0x03d
+#define PCIX0_MINGNT		0x03e
+#define PCIX0_MAXLTNCY		0x03f
+#define PCIX0_BRDGOPT1		0x040
+#define PCIX0_BRDGOPT2		0x044
+#define PCIX0_ERREN		0x050
+#define PCIX0_ERRSTS		0x054
+#define PCIX0_PLBBESR		0x058
+#define PCIX0_PLBBEARL		0x05c
+#define PCIX0_PLBBEARH		0x060
+#define PCIX0_POM0LAL		0x068
+#define PCIX0_POM0LAH		0x06c
+#define PCIX0_POM0SA		0x070
+#define PCIX0_POM0PCIAL		0x074
+#define PCIX0_POM0PCIAH		0x078
+#define PCIX0_POM1LAL		0x07c
+#define PCIX0_POM1LAH		0x080
+#define PCIX0_POM1SA		0x084
+#define PCIX0_POM1PCIAL		0x088
+#define PCIX0_POM1PCIAH		0x08c
+#define PCIX0_POM2SA		0x090
+#define PCIX0_PIM0SAL		0x098
+#define PCIX0_PIM0SA		PCIX0_PIM0SAL
+#define PCIX0_PIM0LAL		0x09c
+#define PCIX0_PIM0LAH		0x0a0
+#define PCIX0_PIM1SA		0x0a4
+#define PCIX0_PIM1LAL		0x0a8
+#define PCIX0_PIM1LAH		0x0ac
+#define PCIX0_PIM2SAL		0x0b0
+#define PCIX0_PIM2SA		PCIX0_PIM2SAL
+#define PCIX0_PIM2LAL		0x0b4
+#define PCIX0_PIM2LAH		0x0b8
+#define PCIX0_OMCAPID		0x0c0
+#define PCIX0_OMNIPTR		0x0c1
+#define PCIX0_OMMC		0x0c2
+#define PCIX0_OMMA		0x0c4
+#define PCIX0_OMMUA		0x0c8
+#define PCIX0_OMMDATA		0x0cc
+#define PCIX0_OMMEOI		0x0ce
+#define PCIX0_PMCAPID		0x0d0
+#define PCIX0_PMNIPTR		0x0d1
+#define PCIX0_PMC		0x0d2
+#define PCIX0_PMCSR		0x0d4
+#define PCIX0_PMCSRBSE		0x0d6
+#define PCIX0_PMDATA		0x0d7
+#define PCIX0_PMSCRR		0x0d8
+#define PCIX0_CAPID		0x0dc
+#define PCIX0_NIPTR		0x0dd
+#define PCIX0_CMD		0x0de
+#define PCIX0_STS		0x0e0
+#define PCIX0_IDR		0x0e4
+#define PCIX0_CID		0x0e8
+#define PCIX0_RID		0x0ec
+#define PCIX0_PIM0SAH		0x0f8
+#define PCIX0_PIM2SAH		0x0fc
+#define PCIX0_MSGIL		0x100
+#define PCIX0_MSGIH		0x104
+#define PCIX0_MSGOL		0x108
+#define PCIX0_MSGOH		0x10c
+#define PCIX0_IM		0x1f8
+
+
+
+#endif /* __PPC4XX_PCI_H__ */

^ permalink raw reply

* [PATCH 4/20] [POWERPC] 4xx PLB to PCI 2.x support
From: Benjamin Herrenschmidt @ 2007-12-13  7:38 UTC (permalink / raw)
  To: Josh Boyer; +Cc: linuxppc-dev

This adds to the previous patch the support for the 4xx PCI 2.x
bridges.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---

This version implement the basic support for the 405GP bridge,
I haven't yet looked at differences that other implementations
may have for the PCI 2.x part.

 arch/powerpc/sysdev/ppc4xx_pci.c |  180 ++++++++++++++++++++++++++++++++++++++-
 arch/powerpc/sysdev/ppc4xx_pci.h |   19 ++++
 2 files changed, 198 insertions(+), 1 deletion(-)

--- linux-merge.orig/arch/powerpc/sysdev/ppc4xx_pci.c	2007-12-11 16:50:32.000000000 +1100
+++ linux-merge/arch/powerpc/sysdev/ppc4xx_pci.c	2007-12-11 16:50:46.000000000 +1100
@@ -21,6 +21,36 @@ static int dma_offset_set;
 /* Move that to a useable header */
 extern unsigned long total_memory;
 
+static void fixup_ppc4xx_pci_bridge(struct pci_dev *dev)
+{
+	struct pci_controller *hose;
+	int i;
+
+	if (dev->devfn != 0 || dev->bus->self != NULL)
+		return;
+
+	hose = pci_bus_to_host(dev->bus);
+	if (hose == NULL)
+		return;
+
+	if (!of_device_is_compatible(hose->dn, "ibm,plb-pciex") &&
+	    !of_device_is_compatible(hose->dn, "ibm,plb-pcix") &&
+	    !of_device_is_compatible(hose->dn, "ibm,plb-pci"))
+		return;
+
+	/* Hide the PCI host BARs from the kernel as their content doesn't
+	 * fit well in the resource management
+	 */
+	for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
+		dev->resource[i].start = dev->resource[i].end = 0;
+		dev->resource[i].flags = 0;
+	}
+
+	printk(KERN_INFO "PCI: Hiding 4xx host bridge resources %s\n",
+	       pci_name(dev));
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, fixup_ppc4xx_pci_bridge);
+
 static int __init ppc4xx_parse_dma_ranges(struct pci_controller *hose,
 					  void __iomem *reg,
 					  struct resource *res)
@@ -126,9 +156,157 @@ static int __init ppc4xx_parse_dma_range
 /*
  * 4xx PCI 2.x part
  */
+
+static void __init ppc4xx_configure_pci_PMMs(struct pci_controller *hose,
+					     void __iomem *reg)
+{
+	u32 la, ma, pcila, pciha;
+	int i, j;
+
+	/* Setup outbound memory windows */
+	for (i = j = 0; i < 3; i++) {
+		struct resource *res = &hose->mem_resources[i];
+
+		/* we only care about memory windows */
+		if (!(res->flags & IORESOURCE_MEM))
+			continue;
+		if (j > 2) {
+			printk(KERN_WARNING "%s: Too many ranges\n",
+			       hose->dn->full_name);
+			break;
+		}
+
+		/* Calculate register values */
+		la = res->start;
+#ifdef CONFIG_RESOURCES_64BIT
+		pciha = (res->start - hose->pci_mem_offset) >> 32;
+		pcila = (res->start - hose->pci_mem_offset) & 0xffffffffu;
+#else
+		pciha = 0;
+		pcila = res->start - hose->pci_mem_offset;
+#endif
+
+		ma = res->end + 1 - res->start;
+		if (!is_power_of_2(ma) || ma < 0x1000 || ma > 0xffffffffu) {
+			printk(KERN_WARNING "%s: Resource out of range\n",
+			       hose->dn->full_name);
+			continue;
+		}
+		ma = (0xffffffffu << ilog2(ma)) | 0x1;
+		if (res->flags & IORESOURCE_PREFETCH)
+			ma |= 0x2;
+
+		/* Program register values */
+		writel(la, reg + PCIL0_PMM0LA + (0x10 * j));
+		writel(pcila, reg + PCIL0_PMM0PCILA + (0x10 * j));
+		writel(pciha, reg + PCIL0_PMM0PCIHA + (0x10 * j));
+		writel(ma, reg + PCIL0_PMM0MA + (0x10 * j));
+		j++;
+	}
+}
+
+static void __init ppc4xx_configure_pci_PTMs(struct pci_controller *hose,
+					     void __iomem *reg,
+					     const struct resource *res)
+{
+	resource_size_t size = res->end - res->start + 1;
+	u32 sa;
+
+	/* Calculate window size */
+	sa = (0xffffffffu << ilog2(size)) | 1;
+	sa |= 0x1;
+
+	/* RAM is always at 0 local for now */
+	writel(0, reg + PCIL0_PTM1LA);
+	writel(sa, reg + PCIL0_PTM1MS);
+
+	/* Map on PCI side */
+	early_write_config_dword(hose, hose->first_busno, 0,
+				 PCI_BASE_ADDRESS_1, res->start);
+	early_write_config_dword(hose, hose->first_busno, 0,
+				 PCI_BASE_ADDRESS_2, 0x00000000);
+	early_write_config_word(hose, hose->first_busno, 0,
+				PCI_COMMAND, 0x0006);
+}
+
 static void __init ppc4xx_probe_pci_bridge(struct device_node *np)
 {
 	/* NYI */
+	struct resource rsrc_cfg;
+	struct resource rsrc_reg;
+	struct resource dma_window;
+	struct pci_controller *hose = NULL;
+	void __iomem *reg = NULL;
+	const int *bus_range;
+	int primary = 0;
+
+	/* Fetch config space registers address */
+	if (of_address_to_resource(np, 0, &rsrc_cfg)) {
+		printk(KERN_ERR "%s:Can't get PCI config register base !",
+		       np->full_name);
+		return;
+	}
+	/* Fetch host bridge internal registers address */
+	if (of_address_to_resource(np, 3, &rsrc_reg)) {
+		printk(KERN_ERR "%s: Can't get PCI internal register base !",
+		       np->full_name);
+		return;
+	}
+
+	/* Check if primary bridge */
+	if (of_get_property(np, "primary", NULL))
+		primary = 1;
+
+	/* Get bus range if any */
+	bus_range = of_get_property(np, "bus-range", NULL);
+
+	/* Map registers */
+	reg = ioremap(rsrc_reg.start, rsrc_reg.end + 1 - rsrc_reg.start);
+	if (reg == NULL) {
+		printk(KERN_ERR "%s: Can't map registers !", np->full_name);
+		goto fail;
+	}
+
+	/* Allocate the host controller data structure */
+	hose = pcibios_alloc_controller(np);
+	if (!hose)
+		goto fail;
+
+	hose->first_busno = bus_range ? bus_range[0] : 0x0;
+	hose->last_busno = bus_range ? bus_range[1] : 0xff;
+
+	/* Setup config space */
+	setup_indirect_pci(hose, rsrc_cfg.start, rsrc_cfg.start + 0x4, 0);
+
+	/* Disable all windows */
+	writel(0, reg + PCIL0_PMM0MA);
+	writel(0, reg + PCIL0_PMM1MA);
+	writel(0, reg + PCIL0_PMM2MA);
+	writel(0, reg + PCIL0_PTM1MS);
+	writel(0, reg + PCIL0_PTM2MS);
+
+	/* Parse outbound mapping resources */
+	pci_process_bridge_OF_ranges(hose, np, primary);
+
+	/* Parse inbound mapping resources */
+	if (ppc4xx_parse_dma_ranges(hose, reg, &dma_window) != 0)
+		goto fail;
+
+	/* Configure outbound ranges POMs */
+	ppc4xx_configure_pci_PMMs(hose, reg);
+
+	/* Configure inbound ranges PIMs */
+	ppc4xx_configure_pci_PTMs(hose, reg, &dma_window);
+
+	/* We don't need the registers anymore */
+	iounmap(reg);
+	return;
+
+ fail:
+	if (hose)
+		pcibios_free_controller(hose);
+	if (reg)
+		iounmap(reg);
 }
 
 /*
@@ -155,7 +333,7 @@ static void __init ppc4xx_configure_pcix
 		}
 
 		/* Calculate register values */
-#ifdef CONFIG_PTE_64BIT
+#ifdef CONFIG_RESOURCES_64BIT
 		lah = res->start >> 32;
 		lal = res->start & 0xffffffffu;
 		pciah = (res->start - hose->pci_mem_offset) >> 32;
Index: linux-merge/arch/powerpc/sysdev/ppc4xx_pci.h
===================================================================
--- linux-merge.orig/arch/powerpc/sysdev/ppc4xx_pci.h	2007-12-11 16:50:02.000000000 +1100
+++ linux-merge/arch/powerpc/sysdev/ppc4xx_pci.h	2007-12-11 16:50:46.000000000 +1100
@@ -101,6 +101,25 @@
 #define PCIX0_MSGOH		0x10c
 #define PCIX0_IM		0x1f8
 
+/*
+ * 4xx PCI bridge register definitions
+ */
+#define PCIL0_PMM0LA		0x00
+#define PCIL0_PMM0MA		0x04
+#define PCIL0_PMM0PCILA		0x08
+#define PCIL0_PMM0PCIHA		0x0c
+#define PCIL0_PMM1LA		0x10
+#define PCIL0_PMM1MA		0x14
+#define PCIL0_PMM1PCILA		0x18
+#define PCIL0_PMM1PCIHA		0x1c
+#define PCIL0_PMM2LA		0x20
+#define PCIL0_PMM2MA		0x24
+#define PCIL0_PMM2PCILA		0x28
+#define PCIL0_PMM2PCIHA		0x2c
+#define PCIL0_PTM1MS		0x30
+#define PCIL0_PTM1LA		0x34
+#define PCIL0_PTM2MS		0x38
+#define PCIL0_PTM2LA		0x3c
 
 
 #endif /* __PPC4XX_PCI_H__ */

^ permalink raw reply

* [PATCH 5/20] [POWERPC] 4xx PLB to PCI Express support
From: Benjamin Herrenschmidt @ 2007-12-13  7:38 UTC (permalink / raw)
  To: Josh Boyer; +Cc: linuxppc-dev

This adds to the previous 2 patches the support for the 4xx PCI Express
cells as found in the 440SPe revA, revB and 405EX.

Unfortunately, due to significant differences between these, and other
interesting "features" of those pieces of HW, the code isn't as simple
as it is for PCI and PCI-X and some of the functions differ significantly
between the 3 implementations. Thus, not only this code can only support
those 3 implementations for now and will refuse to operate on any other,
but there are added ifdef's to avoid the bloat of building a fairly large
amount of code on platforms that don't need it.

Also, this code currently only supports fully initializing root complex
nodes, not endpoint. Some more code will have to be lifted from the
arch/ppc implementation to add the endpoint support, though it's mostly
differences in memory mapping, and the question on how to represent
endpoint mode PCI in the device-tree is thus open.

Many thanks to Stefan Roese for testing & fixing up the 405EX bits !

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Stefan Roese <sr@denx.de>
---

 arch/powerpc/Kconfig             |    1 
 arch/powerpc/sysdev/Kconfig      |    8 
 arch/powerpc/sysdev/ppc4xx_pci.c |  994 ++++++++++++++++++++++++++++++++++++++-
 arch/powerpc/sysdev/ppc4xx_pci.h |  242 +++++++++
 4 files changed, 1227 insertions(+), 18 deletions(-)

--- linux-merge.orig/arch/powerpc/sysdev/ppc4xx_pci.c	2007-12-11 16:50:46.000000000 +1100
+++ linux-merge/arch/powerpc/sysdev/ppc4xx_pci.c	2007-12-11 16:50:49.000000000 +1100
@@ -3,16 +3,31 @@
  *
  * Copyright 2007 Ben. Herrenschmidt <benh@kernel.crashing.org>, IBM Corp.
  *
+ * Most PCI Express code is coming from Stefan Roese implementation for
+ * arch/ppc in the Denx tree, slightly reworked by me.
+ *
+ * Copyright 2007 DENX Software Engineering, Stefan Roese <sr@denx.de>
+ *
+ * Some of that comes itself from a previous implementation for 440SPE only
+ * by Roland Dreier:
+ *
+ * Copyright (c) 2005 Cisco Systems.  All rights reserved.
+ * Roland Dreier <rolandd@cisco.com>
+ *
  */
 
 #include <linux/kernel.h>
 #include <linux/pci.h>
 #include <linux/init.h>
 #include <linux/of.h>
+#include <linux/bootmem.h>
+#include <linux/delay.h>
 
 #include <asm/io.h>
 #include <asm/pci-bridge.h>
 #include <asm/machdep.h>
+#include <asm/dcr.h>
+#include <asm/dcr-regs.h>
 
 #include "ppc4xx_pci.h"
 
@@ -21,6 +36,17 @@ static int dma_offset_set;
 /* Move that to a useable header */
 extern unsigned long total_memory;
 
+#define U64_TO_U32_LOW(val)	((u32)((val) & 0x00000000ffffffffULL))
+#define U64_TO_U32_HIGH(val)	((u32)((val) >> 32))
+
+#ifdef CONFIG_RESOURCES_64BIT
+#define RES_TO_U32_LOW(val)	U64_TO_U32_LOW(val)
+#define RES_TO_U32_HIGH(val)	U64_TO_U32_HIGH(val)
+#else
+#define RES_TO_U32_LOW(val)	(val)
+#define RES_TO_U32_HIGH(val)	(0)
+#endif
+
 static void fixup_ppc4xx_pci_bridge(struct pci_dev *dev)
 {
 	struct pci_controller *hose;
@@ -178,13 +204,8 @@ static void __init ppc4xx_configure_pci_
 
 		/* Calculate register values */
 		la = res->start;
-#ifdef CONFIG_RESOURCES_64BIT
-		pciha = (res->start - hose->pci_mem_offset) >> 32;
-		pcila = (res->start - hose->pci_mem_offset) & 0xffffffffu;
-#else
-		pciha = 0;
-		pcila = res->start - hose->pci_mem_offset;
-#endif
+		pciha = RES_TO_U32_HIGH(res->start - hose->pci_mem_offset);
+		pcila = RES_TO_U32_LOW(res->start - hose->pci_mem_offset);
 
 		ma = res->end + 1 - res->start;
 		if (!is_power_of_2(ma) || ma < 0x1000 || ma > 0xffffffffu) {
@@ -333,16 +354,10 @@ static void __init ppc4xx_configure_pcix
 		}
 
 		/* Calculate register values */
-#ifdef CONFIG_RESOURCES_64BIT
-		lah = res->start >> 32;
-		lal = res->start & 0xffffffffu;
-		pciah = (res->start - hose->pci_mem_offset) >> 32;
-		pcial = (res->start - hose->pci_mem_offset) & 0xffffffffu;
-#else
-		lah = pciah = 0;
-		lal = res->start;
-		pcial = res->start - hose->pci_mem_offset;
-#endif
+		lah = RES_TO_U32_HIGH(res->start);
+		lal = RES_TO_U32_LOW(res->start);
+		pciah = RES_TO_U32_HIGH(res->start - hose->pci_mem_offset);
+		pcial = RES_TO_U32_LOW(res->start - hose->pci_mem_offset);
 		sa = res->end + 1 - res->start;
 		if (!is_power_of_2(sa) || sa < 0x100000 ||
 		    sa > 0xffffffffu) {
@@ -492,20 +507,963 @@ static void __init ppc4xx_probe_pcix_bri
 		iounmap(reg);
 }
 
+#ifdef CONFIG_PPC4xx_PCI_EXPRESS
+
 /*
  * 4xx PCI-Express part
+ *
+ * We support 3 parts currently based on the compatible property:
+ *
+ * ibm,plb-pciex-440speA
+ * ibm,plb-pciex-440speB
+ * ibm,plb-pciex-405ex
+ *
+ * Anything else will be rejected for now as they are all subtly
+ * different unfortunately.
+ *
  */
+
+#define MAX_PCIE_BUS_MAPPED	0x10
+
+struct ppc4xx_pciex_port
+{
+	struct pci_controller	*hose;
+	struct device_node	*node;
+	unsigned int		index;
+	int			endpoint;
+	unsigned int		sdr_base;
+	dcr_host_t		dcrs;
+	struct resource		cfg_space;
+	struct resource		utl_regs;
+};
+
+static struct ppc4xx_pciex_port *ppc4xx_pciex_ports;
+static unsigned int ppc4xx_pciex_port_count;
+
+struct ppc4xx_pciex_hwops
+{
+	int (*core_init)(struct device_node *np);
+	int (*port_init_hw)(struct ppc4xx_pciex_port *port);
+	int (*setup_utl)(struct ppc4xx_pciex_port *port);
+};
+
+static struct ppc4xx_pciex_hwops *ppc4xx_pciex_hwops;
+
+#ifdef CONFIG_44x
+
+/* Check various reset bits of the 440SPe PCIe core */
+static int __init ppc440spe_pciex_check_reset(struct device_node *np)
+{
+	u32 valPE0, valPE1, valPE2;
+	int err = 0;
+
+	/* SDR0_PEGPLLLCT1 reset */
+	if (!(mfdcri(SDR0, PESDR0_PLLLCT1) & 0x01000000)) {
+		/*
+		 * the PCIe core was probably already initialised
+		 * by firmware - let's re-reset RCSSET regs
+		 *
+		 * -- Shouldn't we also re-reset the whole thing ? -- BenH
+		 */
+		pr_debug("PCIE: SDR0_PLLLCT1 already reset.\n");
+		mtdcri(SDR0, PESDR0_440SPE_RCSSET, 0x01010000);
+		mtdcri(SDR0, PESDR1_440SPE_RCSSET, 0x01010000);
+		mtdcri(SDR0, PESDR2_440SPE_RCSSET, 0x01010000);
+	}
+
+	valPE0 = mfdcri(SDR0, PESDR0_440SPE_RCSSET);
+	valPE1 = mfdcri(SDR0, PESDR1_440SPE_RCSSET);
+	valPE2 = mfdcri(SDR0, PESDR2_440SPE_RCSSET);
+
+	/* SDR0_PExRCSSET rstgu */
+	if (!(valPE0 & 0x01000000) ||
+	    !(valPE1 & 0x01000000) ||
+	    !(valPE2 & 0x01000000)) {
+		printk(KERN_INFO "PCIE: SDR0_PExRCSSET rstgu error\n");
+		err = -1;
+	}
+
+	/* SDR0_PExRCSSET rstdl */
+	if (!(valPE0 & 0x00010000) ||
+	    !(valPE1 & 0x00010000) ||
+	    !(valPE2 & 0x00010000)) {
+		printk(KERN_INFO "PCIE: SDR0_PExRCSSET rstdl error\n");
+		err = -1;
+	}
+
+	/* SDR0_PExRCSSET rstpyn */
+	if ((valPE0 & 0x00001000) ||
+	    (valPE1 & 0x00001000) ||
+	    (valPE2 & 0x00001000)) {
+		printk(KERN_INFO "PCIE: SDR0_PExRCSSET rstpyn error\n");
+		err = -1;
+	}
+
+	/* SDR0_PExRCSSET hldplb */
+	if ((valPE0 & 0x10000000) ||
+	    (valPE1 & 0x10000000) ||
+	    (valPE2 & 0x10000000)) {
+		printk(KERN_INFO "PCIE: SDR0_PExRCSSET hldplb error\n");
+		err = -1;
+	}
+
+	/* SDR0_PExRCSSET rdy */
+	if ((valPE0 & 0x00100000) ||
+	    (valPE1 & 0x00100000) ||
+	    (valPE2 & 0x00100000)) {
+		printk(KERN_INFO "PCIE: SDR0_PExRCSSET rdy error\n");
+		err = -1;
+	}
+
+	/* SDR0_PExRCSSET shutdown */
+	if ((valPE0 & 0x00000100) ||
+	    (valPE1 & 0x00000100) ||
+	    (valPE2 & 0x00000100)) {
+		printk(KERN_INFO "PCIE: SDR0_PExRCSSET shutdown error\n");
+		err = -1;
+	}
+
+	return err;
+}
+
+/* Global PCIe core initializations for 440SPe core */
+static int __init ppc440spe_pciex_core_init(struct device_node *np)
+{
+	int time_out = 20;
+
+	/* Set PLL clock receiver to LVPECL */
+	mtdcri(SDR0, PESDR0_PLLLCT1, mfdcri(SDR0, PESDR0_PLLLCT1) | 1 << 28);
+
+	/* Shouldn't we do all the calibration stuff etc... here ? */
+	if (ppc440spe_pciex_check_reset(np))
+		return -ENXIO;
+
+	if (!(mfdcri(SDR0, PESDR0_PLLLCT2) & 0x10000)) {
+		printk(KERN_INFO "PCIE: PESDR_PLLCT2 resistance calibration "
+		       "failed (0x%08x)\n",
+		       mfdcri(SDR0, PESDR0_PLLLCT2));
+		return -1;
+	}
+
+	/* De-assert reset of PCIe PLL, wait for lock */
+	mtdcri(SDR0, PESDR0_PLLLCT1,
+	       mfdcri(SDR0, PESDR0_PLLLCT1) & ~(1 << 24));
+	udelay(3);
+
+	while (time_out) {
+		if (!(mfdcri(SDR0, PESDR0_PLLLCT3) & 0x10000000)) {
+			time_out--;
+			udelay(1);
+		} else
+			break;
+	}
+	if (!time_out) {
+		printk(KERN_INFO "PCIE: VCO output not locked\n");
+		return -1;
+	}
+
+	pr_debug("PCIE initialization OK\n");
+
+	return 3;
+}
+
+static int ppc440spe_pciex_init_port_hw(struct ppc4xx_pciex_port *port)
+{
+	u32 val = 1 << 24;
+
+	if (port->endpoint)
+		val = PTYPE_LEGACY_ENDPOINT << 20;
+	else
+		val = PTYPE_ROOT_PORT << 20;
+
+	if (port->index == 0)
+		val |= LNKW_X8 << 12;
+	else
+		val |= LNKW_X4 << 12;
+
+	mtdcri(SDR0, port->sdr_base + PESDRn_DLPSET, val);
+	mtdcri(SDR0, port->sdr_base + PESDRn_UTLSET1, 0x20222222);
+	if (of_device_is_compatible(port->node, "ibm,plb-pciex-440speA"))
+		mtdcri(SDR0, port->sdr_base + PESDRn_UTLSET2, 0x11000000);
+	mtdcri(SDR0, port->sdr_base + PESDRn_440SPE_HSSL0SET1, 0x35000000);
+	mtdcri(SDR0, port->sdr_base + PESDRn_440SPE_HSSL1SET1, 0x35000000);
+	mtdcri(SDR0, port->sdr_base + PESDRn_440SPE_HSSL2SET1, 0x35000000);
+	mtdcri(SDR0, port->sdr_base + PESDRn_440SPE_HSSL3SET1, 0x35000000);
+	if (port->index == 0) {
+		mtdcri(SDR0, port->sdr_base + PESDRn_440SPE_HSSL4SET1,
+		       0x35000000);
+		mtdcri(SDR0, port->sdr_base + PESDRn_440SPE_HSSL5SET1,
+		       0x35000000);
+		mtdcri(SDR0, port->sdr_base + PESDRn_440SPE_HSSL6SET1,
+		       0x35000000);
+		mtdcri(SDR0, port->sdr_base + PESDRn_440SPE_HSSL7SET1,
+		       0x35000000);
+	}
+	val = mfdcri(SDR0, port->sdr_base + PESDRn_RCSSET);
+	mtdcri(SDR0, port->sdr_base + PESDRn_RCSSET,
+	       (val & ~(1 << 24 | 1 << 16)) | 1 << 12);
+
+	return 0;
+}
+
+static int ppc440speA_pciex_init_utl(struct ppc4xx_pciex_port *port)
+{
+	void __iomem *utl_base;
+
+	/* XXX Check what that value means... I hate magic */
+	dcr_write(port->dcrs, DCRO_PEGPL_SPECIAL, 0x68782800);
+
+	utl_base = ioremap(port->utl_regs.start, 0x100);
+	BUG_ON(utl_base == NULL);
+
+	/*
+	 * Set buffer allocations and then assert VRB and TXE.
+	 */
+	out_be32(utl_base + PEUTL_OUTTR,   0x08000000);
+	out_be32(utl_base + PEUTL_INTR,    0x02000000);
+	out_be32(utl_base + PEUTL_OPDBSZ,  0x10000000);
+	out_be32(utl_base + PEUTL_PBBSZ,   0x53000000);
+	out_be32(utl_base + PEUTL_IPHBSZ,  0x08000000);
+	out_be32(utl_base + PEUTL_IPDBSZ,  0x10000000);
+	out_be32(utl_base + PEUTL_RCIRQEN, 0x00f00000);
+	out_be32(utl_base + PEUTL_PCTL,    0x80800066);
+
+	iounmap(utl_base);
+
+	return 0;
+}
+
+static struct ppc4xx_pciex_hwops ppc440speA_pcie_hwops __initdata =
+{
+	.core_init	= ppc440spe_pciex_core_init,
+	.port_init_hw	= ppc440spe_pciex_init_port_hw,
+	.setup_utl	= ppc440speA_pciex_init_utl,
+};
+
+static struct ppc4xx_pciex_hwops ppc440speB_pcie_hwops __initdata =
+{
+	.core_init	= ppc440spe_pciex_core_init,
+	.port_init_hw	= ppc440spe_pciex_init_port_hw,
+};
+
+
+#endif /* CONFIG_44x */
+
+#ifdef CONFIG_40x
+
+static int __init ppc405ex_pciex_core_init(struct device_node *np)
+{
+	/* Nothing to do, return 2 ports */
+	return 2;
+}
+
+static void ppc405ex_pcie_phy_reset(struct ppc4xx_pciex_port *port)
+{
+	/* Assert the PE0_PHY reset */
+	mtdcri(SDR0, port->sdr_base + PESDRn_RCSSET, 0x01010000);
+	msleep(1);
+
+	/* deassert the PE0_hotreset */
+	if (port->endpoint)
+		mtdcri(SDR0, port->sdr_base + PESDRn_RCSSET, 0x01111000);
+	else
+		mtdcri(SDR0, port->sdr_base + PESDRn_RCSSET, 0x01101000);
+
+	/* poll for phy !reset */
+	/* XXX FIXME add timeout */
+	while (!(mfdcri(SDR0, port->sdr_base + PESDRn_405EX_PHYSTA) & 0x00001000))
+		;
+
+	/* deassert the PE0_gpl_utl_reset */
+	mtdcri(SDR0, port->sdr_base + PESDRn_RCSSET, 0x00101000);
+}
+
+static int ppc405ex_pciex_init_port_hw(struct ppc4xx_pciex_port *port)
+{
+	u32 val;
+
+	if (port->endpoint)
+		val = PTYPE_LEGACY_ENDPOINT;
+	else
+		val = PTYPE_ROOT_PORT;
+
+	mtdcri(SDR0, port->sdr_base + PESDRn_DLPSET,
+	       1 << 24 | val << 20 | LNKW_X1 << 12);
+
+	mtdcri(SDR0, port->sdr_base + PESDRn_UTLSET1, 0x00000000);
+	mtdcri(SDR0, port->sdr_base + PESDRn_UTLSET2, 0x01010000);
+	mtdcri(SDR0, port->sdr_base + PESDRn_405EX_PHYSET1, 0x720F0000);
+	mtdcri(SDR0, port->sdr_base + PESDRn_405EX_PHYSET2, 0x70600003);
+
+	/*
+	 * Only reset the PHY when no link is currently established.
+	 * This is for the Atheros PCIe board which has problems to establish
+	 * the link (again) after this PHY reset. All other currently tested
+	 * PCIe boards don't show this problem.
+	 * This has to be re-tested and fixed in a later release!
+	 */
+#if 0 /* XXX FIXME: Not resetting the PHY will leave all resources
+       * configured as done previously by U-Boot. Then Linux will currently
+       * not reassign them. So the PHY reset is now done always. This will
+       * lead to problems with the Atheros PCIe board again.
+       */
+	val = mfdcri(SDR0, port->sdr_base + PESDRn_LOOP);
+	if (!(val & 0x00001000))
+		ppc405ex_pcie_phy_reset(port);
+#else
+	ppc405ex_pcie_phy_reset(port);
+#endif
+
+	dcr_write(port->dcrs, DCRO_PEGPL_CFG, 0x10000000);  /* guarded on */
+
+	return 0;
+}
+
+static int ppc405ex_pciex_init_utl(struct ppc4xx_pciex_port *port)
+{
+	void __iomem *utl_base;
+
+	dcr_write(port->dcrs, DCRO_PEGPL_SPECIAL, 0x0);
+
+	utl_base = ioremap(port->utl_regs.start, 0x100);
+	BUG_ON(utl_base == NULL);
+
+	/*
+	 * Set buffer allocations and then assert VRB and TXE.
+	 */
+	out_be32(utl_base + PEUTL_OUTTR,   0x02000000);
+	out_be32(utl_base + PEUTL_INTR,    0x02000000);
+	out_be32(utl_base + PEUTL_OPDBSZ,  0x04000000);
+	out_be32(utl_base + PEUTL_PBBSZ,   0x21000000);
+	out_be32(utl_base + PEUTL_IPHBSZ,  0x02000000);
+	out_be32(utl_base + PEUTL_IPDBSZ,  0x04000000);
+	out_be32(utl_base + PEUTL_RCIRQEN, 0x00f00000);
+	out_be32(utl_base + PEUTL_PCTL,    0x80800066);
+
+	out_be32(utl_base + PEUTL_PBCTL,   0x0800000c);
+	out_be32(utl_base + PEUTL_RCSTA,
+		 in_be32(utl_base + PEUTL_RCSTA) | 0x000040000);
+
+	iounmap(utl_base);
+
+	return 0;
+}
+
+static struct ppc4xx_pciex_hwops ppc405ex_pcie_hwops __initdata =
+{
+	.core_init	= ppc405ex_pciex_core_init,
+	.port_init_hw	= ppc405ex_pciex_init_port_hw,
+	.setup_utl	= ppc405ex_pciex_init_utl,
+};
+
+#endif /* CONFIG_40x */
+
+
+/* Check that the core has been initied and if not, do it */
+static int __init ppc4xx_pciex_check_core_init(struct device_node *np)
+{
+	static int core_init;
+	int count = -ENODEV;
+
+	if (core_init++)
+		return 0;
+
+#ifdef CONFIG_44x
+	if (of_device_is_compatible(np, "ibm,plb-pciex-440speA"))
+		ppc4xx_pciex_hwops = &ppc440speA_pcie_hwops;
+	else if (of_device_is_compatible(np, "ibm,plb-pciex-440speB"))
+		ppc4xx_pciex_hwops = &ppc440speB_pcie_hwops;
+#endif /* CONFIG_44x    */
+#ifdef CONFIG_40x
+	if (of_device_is_compatible(np, "ibm,plb-pciex-405ex"))
+		ppc4xx_pciex_hwops = &ppc405ex_pcie_hwops;
+#endif
+	if (ppc4xx_pciex_hwops == NULL) {
+		printk(KERN_WARNING "PCIE: unknown host type %s\n",
+		       np->full_name);
+		return -ENODEV;
+	}
+
+	count = ppc4xx_pciex_hwops->core_init(np);
+	if (count > 0) {
+		ppc4xx_pciex_ports =
+		       kzalloc(count * sizeof(struct ppc4xx_pciex_port),
+			       GFP_KERNEL);
+		if (ppc4xx_pciex_ports) {
+			ppc4xx_pciex_port_count = count;
+			return 0;
+		}
+		printk(KERN_WARNING "PCIE: failed to allocate ports array\n");
+		return -ENOMEM;
+	}
+	return -ENODEV;
+}
+
+static void __init ppc4xx_pciex_port_init_mapping(struct ppc4xx_pciex_port *port)
+{
+	/* We map PCI Express configuration based on the reg property */
+	dcr_write(port->dcrs, DCRO_PEGPL_CFGBAH,
+		  RES_TO_U32_HIGH(port->cfg_space.start));
+	dcr_write(port->dcrs, DCRO_PEGPL_CFGBAL,
+		  RES_TO_U32_LOW(port->cfg_space.start));
+
+	/* XXX FIXME: Use size from reg property. For now, map 512M */
+	dcr_write(port->dcrs, DCRO_PEGPL_CFGMSK, 0xe0000001);
+
+	/* We map UTL registers based on the reg property */
+	dcr_write(port->dcrs, DCRO_PEGPL_REGBAH,
+		  RES_TO_U32_HIGH(port->utl_regs.start));
+	dcr_write(port->dcrs, DCRO_PEGPL_REGBAL,
+		  RES_TO_U32_LOW(port->utl_regs.start));
+
+	/* XXX FIXME: Use size from reg property */
+	dcr_write(port->dcrs, DCRO_PEGPL_REGMSK, 0x00007001);
+
+	/* Disable all other outbound windows */
+	dcr_write(port->dcrs, DCRO_PEGPL_OMR1MSKL, 0);
+	dcr_write(port->dcrs, DCRO_PEGPL_OMR2MSKL, 0);
+	dcr_write(port->dcrs, DCRO_PEGPL_OMR3MSKL, 0);
+	dcr_write(port->dcrs, DCRO_PEGPL_MSGMSK, 0);
+}
+
+static int __init ppc4xx_pciex_port_init(struct ppc4xx_pciex_port *port)
+{
+	int attempts, rc = 0;
+	u32 val;
+
+	/* Check if it's endpoint or root complex
+	 *
+	 * XXX Do we want to use the device-tree instead ? --BenH.
+	 */
+	val = mfdcri(SDR0, port->sdr_base + PESDRn_DLPSET);
+	port->endpoint = (((val >> 20) & 0xf) != PTYPE_ROOT_PORT);
+
+	/* Init HW */
+	if (ppc4xx_pciex_hwops->port_init_hw)
+		rc = ppc4xx_pciex_hwops->port_init_hw(port);
+	if (rc != 0)
+		return rc;
+
+	/*
+	 * Notice: the following delay has critical impact on device
+	 * initialization - if too short (<50ms) the link doesn't get up.
+	 *
+	 * XXX FIXME: There are various issues with that link up thingy,
+	 * we could just wait for the link with a timeout but Stefan says
+	 * some cards need more time even after the link is up. I'll
+	 * investigate. For now, we keep a fixed 1s delay.
+	 *
+	 * Ultimately, it should be made asynchronous so all ports are
+	 * brought up simultaneously though.
+	 */
+	printk(KERN_INFO "PCIE%d: Waiting for link to go up...\n",
+	       port->index);
+	msleep(1000);
+
+	/*
+	 * Check that we exited the reset state properly
+	 */
+	val = mfdcri(SDR0, port->sdr_base + PESDRn_RCSSTS);
+	if (val & (1 << 20)) {
+		printk(KERN_WARNING "PCIE%d: PGRST failed %08x\n",
+		       port->index, val);
+		return -1;
+	}
+
+	/*
+	 * Verify link is up
+	 */
+	val = mfdcri(SDR0, port->sdr_base + PESDRn_LOOP);
+	if (!(val & 0x00001000)) {
+		printk(KERN_INFO "PCIE%d: link is not up !\n",
+		       port->index);
+		return -1;
+	}
+
+	printk(KERN_INFO "PCIE%d: link is up !\n",
+	       port->index);
+
+	/*
+	 * Initialize mapping: disable all regions and configure
+	 * CFG and REG regions based on resources in the device tree
+	 */
+	ppc4xx_pciex_port_init_mapping(port);
+
+	/*
+	 * Setup UTL registers - but only on revA!
+	 * We use default settings for revB chip.
+	 *
+	 * To be reworked. We may also be able to move that to
+	 * before the link wait
+	 * --BenH.
+	 */
+	if (ppc4xx_pciex_hwops->setup_utl)
+		ppc4xx_pciex_hwops->setup_utl(port);
+
+	/*
+	 * Check for VC0 active and assert RDY.
+	 */
+	attempts = 10;
+	while (!(mfdcri(SDR0, port->sdr_base + PESDRn_RCSSTS) & (1 << 16))) {
+		if (!(attempts--)) {
+			printk(KERN_INFO "PCIE%d: VC0 not active\n",
+			       port->index);
+			return -1;
+		}
+		msleep(1000);
+	}
+	mtdcri(SDR0, port->sdr_base + PESDRn_RCSSET,
+	       mfdcri(SDR0, port->sdr_base + PESDRn_RCSSET) | 1 << 20);
+	msleep(100);
+
+	return 0;
+}
+
+static int ppc4xx_pciex_validate_bdf(struct ppc4xx_pciex_port *port,
+				     struct pci_bus *bus,
+				     unsigned int devfn)
+{
+	static int message;
+
+	/* Endpoint can not generate upstream(remote) config cycles */
+	if (port->endpoint && bus->number != port->hose->first_busno)
+		return PCIBIOS_DEVICE_NOT_FOUND;
+
+	/* Check we are within the mapped range */
+	if (bus->number > port->hose->last_busno) {
+		if (!message) {
+			printk(KERN_WARNING "Warning! Probing bus %u"
+			       " out of range !\n", bus->number);
+			message++;
+		}
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	}
+
+	/* The root complex has only one device / function */
+	if (bus->number == port->hose->first_busno && devfn != 0)
+		return PCIBIOS_DEVICE_NOT_FOUND;
+
+	/* The other side of the RC has only one device as well */
+	if (bus->number == (port->hose->first_busno + 1) &&
+	    PCI_SLOT(devfn) != 0)
+		return PCIBIOS_DEVICE_NOT_FOUND;
+
+	return 0;
+}
+
+static void __iomem *ppc4xx_pciex_get_config_base(struct ppc4xx_pciex_port *port,
+						  struct pci_bus *bus,
+						  unsigned int devfn)
+{
+	int relbus;
+
+	/* Remove the casts when we finally remove the stupid volatile
+	 * in struct pci_controller
+	 */
+	if (bus->number == port->hose->first_busno)
+		return (void __iomem *)port->hose->cfg_addr;
+
+	relbus = bus->number - (port->hose->first_busno + 1);
+	return (void __iomem *)port->hose->cfg_data +
+		((relbus  << 20) | (devfn << 12));
+}
+
+static int ppc4xx_pciex_read_config(struct pci_bus *bus, unsigned int devfn,
+				    int offset, int len, u32 *val)
+{
+	struct pci_controller *hose = (struct pci_controller *) bus->sysdata;
+	struct ppc4xx_pciex_port *port =
+		&ppc4xx_pciex_ports[hose->indirect_type];
+	void __iomem *addr;
+	u32 gpl_cfg;
+
+	BUG_ON(hose != port->hose);
+
+	if (ppc4xx_pciex_validate_bdf(port, bus, devfn) != 0)
+		return PCIBIOS_DEVICE_NOT_FOUND;
+
+	addr = ppc4xx_pciex_get_config_base(port, bus, devfn);
+
+	/*
+	 * Reading from configuration space of non-existing device can
+	 * generate transaction errors. For the read duration we suppress
+	 * assertion of machine check exceptions to avoid those.
+	 */
+	gpl_cfg = dcr_read(port->dcrs, DCRO_PEGPL_CFG);
+	dcr_write(port->dcrs, DCRO_PEGPL_CFG, gpl_cfg | GPL_DMER_MASK_DISA);
+
+	switch (len) {
+	case 1:
+		*val = in_8((u8 *)(addr + offset));
+		break;
+	case 2:
+		*val = in_le16((u16 *)(addr + offset));
+		break;
+	default:
+		*val = in_le32((u32 *)(addr + offset));
+		break;
+	}
+
+	pr_debug("pcie-config-read: bus=%3d [%3d..%3d] devfn=0x%04x"
+		 " offset=0x%04x len=%d, addr=0x%p val=0x%08x\n",
+		 bus->number, hose->first_busno, hose->last_busno,
+		 devfn, offset, len, addr + offset, *val);
+
+	dcr_write(port->dcrs, DCRO_PEGPL_CFG, gpl_cfg);
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int ppc4xx_pciex_write_config(struct pci_bus *bus, unsigned int devfn,
+				     int offset, int len, u32 val)
+{
+	struct pci_controller *hose = (struct pci_controller *) bus->sysdata;
+	struct ppc4xx_pciex_port *port =
+		&ppc4xx_pciex_ports[hose->indirect_type];
+	void __iomem *addr;
+	u32 gpl_cfg;
+
+	if (ppc4xx_pciex_validate_bdf(port, bus, devfn) != 0)
+		return PCIBIOS_DEVICE_NOT_FOUND;
+
+	addr = ppc4xx_pciex_get_config_base(port, bus, devfn);
+
+	/*
+	 * Reading from configuration space of non-existing device can
+	 * generate transaction errors. For the read duration we suppress
+	 * assertion of machine check exceptions to avoid those.
+	 */
+	gpl_cfg = dcr_read(port->dcrs, DCRO_PEGPL_CFG);
+	dcr_write(port->dcrs, DCRO_PEGPL_CFG, gpl_cfg | GPL_DMER_MASK_DISA);
+
+	pr_debug("pcie-config-write: bus=%3d [%3d..%3d] devfn=0x%04x"
+		 " offset=0x%04x len=%d, addr=0x%p val=0x%08x\n",
+		 bus->number, hose->first_busno, hose->last_busno,
+		 devfn, offset, len, addr + offset, val);
+
+	switch (len) {
+	case 1:
+		out_8((u8 *)(addr + offset), val);
+		break;
+	case 2:
+		out_le16((u16 *)(addr + offset), val);
+		break;
+	default:
+		out_le32((u32 *)(addr + offset), val);
+		break;
+	}
+
+	dcr_write(port->dcrs, DCRO_PEGPL_CFG, gpl_cfg);
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static struct pci_ops ppc4xx_pciex_pci_ops =
+{
+	.read  = ppc4xx_pciex_read_config,
+	.write = ppc4xx_pciex_write_config,
+};
+
+static void __init ppc4xx_configure_pciex_POMs(struct ppc4xx_pciex_port *port,
+					       struct pci_controller *hose,
+					       void __iomem *mbase)
+{
+	u32 lah, lal, pciah, pcial, sa;
+	int i, j;
+
+	/* Setup outbound memory windows */
+	for (i = j = 0; i < 3; i++) {
+		struct resource *res = &hose->mem_resources[i];
+
+		/* we only care about memory windows */
+		if (!(res->flags & IORESOURCE_MEM))
+			continue;
+		if (j > 1) {
+			printk(KERN_WARNING "%s: Too many ranges\n",
+			       port->node->full_name);
+			break;
+		}
+
+		/* Calculate register values */
+		lah = RES_TO_U32_HIGH(res->start);
+		lal = RES_TO_U32_LOW(res->start);
+		pciah = RES_TO_U32_HIGH(res->start - hose->pci_mem_offset);
+		pcial = RES_TO_U32_LOW(res->start - hose->pci_mem_offset);
+		sa = res->end + 1 - res->start;
+		if (!is_power_of_2(sa) || sa < 0x100000 ||
+		    sa > 0xffffffffu) {
+			printk(KERN_WARNING "%s: Resource out of range\n",
+			       port->node->full_name);
+			continue;
+		}
+		sa = (0xffffffffu << ilog2(sa)) | 0x1;
+
+		/* Program register values */
+		switch (j) {
+		case 0:
+			out_le32(mbase + PECFG_POM0LAH, pciah);
+			out_le32(mbase + PECFG_POM0LAL, pcial);
+			dcr_write(port->dcrs, DCRO_PEGPL_OMR1BAH, lah);
+			dcr_write(port->dcrs, DCRO_PEGPL_OMR1BAL, lal);
+			dcr_write(port->dcrs, DCRO_PEGPL_OMR1MSKH, 0x7fffffff);
+			dcr_write(port->dcrs, DCRO_PEGPL_OMR1MSKL, sa | 3);
+			break;
+		case 1:
+			out_le32(mbase + PECFG_POM1LAH, pciah);
+			out_le32(mbase + PECFG_POM1LAL, pcial);
+			dcr_write(port->dcrs, DCRO_PEGPL_OMR2BAH, lah);
+			dcr_write(port->dcrs, DCRO_PEGPL_OMR2BAL, lal);
+			dcr_write(port->dcrs, DCRO_PEGPL_OMR2MSKH, 0x7fffffff);
+			dcr_write(port->dcrs, DCRO_PEGPL_OMR2MSKL, sa | 3);
+			break;
+		}
+		j++;
+	}
+
+	/* Configure IO, always 64K starting at 0 */
+	if (hose->io_resource.flags & IORESOURCE_IO) {
+		lah = RES_TO_U32_HIGH(hose->io_base_phys);
+		lal = RES_TO_U32_LOW(hose->io_base_phys);
+		out_le32(mbase + PECFG_POM2LAH, 0);
+		out_le32(mbase + PECFG_POM2LAL, 0);
+		dcr_write(port->dcrs, DCRO_PEGPL_OMR3BAH, lah);
+		dcr_write(port->dcrs, DCRO_PEGPL_OMR3BAL, lal);
+		dcr_write(port->dcrs, DCRO_PEGPL_OMR3MSKH, 0x7fffffff);
+		dcr_write(port->dcrs, DCRO_PEGPL_OMR3MSKL, 0xffff0000 | 3);
+	}
+}
+
+static void __init ppc4xx_configure_pciex_PIMs(struct ppc4xx_pciex_port *port,
+					       struct pci_controller *hose,
+					       void __iomem *mbase,
+					       struct resource *res)
+{
+	resource_size_t size = res->end - res->start + 1;
+	u64 sa;
+
+	/* Calculate window size */
+	sa = (0xffffffffffffffffull << ilog2(size));;
+	if (res->flags & IORESOURCE_PREFETCH)
+		sa |= 0x8;
+
+	out_le32(mbase + PECFG_BAR0HMPA, RES_TO_U32_HIGH(sa));
+	out_le32(mbase + PECFG_BAR0LMPA, RES_TO_U32_LOW(sa));
+
+	/* The setup of the split looks weird to me ... let's see if it works */
+	out_le32(mbase + PECFG_PIM0LAL, 0x00000000);
+	out_le32(mbase + PECFG_PIM0LAH, 0x00000000);
+	out_le32(mbase + PECFG_PIM1LAL, 0x00000000);
+	out_le32(mbase + PECFG_PIM1LAH, 0x00000000);
+	out_le32(mbase + PECFG_PIM01SAH, 0xffff0000);
+	out_le32(mbase + PECFG_PIM01SAL, 0x00000000);
+
+	/* Enable inbound mapping */
+	out_le32(mbase + PECFG_PIMEN, 0x1);
+
+	out_le32(mbase + PCI_BASE_ADDRESS_0, RES_TO_U32_LOW(res->start));
+	out_le32(mbase + PCI_BASE_ADDRESS_1, RES_TO_U32_HIGH(res->start));
+
+	/* Enable I/O, Mem, and Busmaster cycles */
+	out_le16(mbase + PCI_COMMAND,
+		 in_le16(mbase + PCI_COMMAND) |
+		 PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
+}
+
+static void __init ppc4xx_pciex_port_setup_hose(struct ppc4xx_pciex_port *port)
+{
+	struct resource dma_window;
+	struct pci_controller *hose = NULL;
+	const int *bus_range;
+	int primary = 0, busses;
+	void __iomem *mbase = NULL, *cfg_data = NULL;
+
+	/* XXX FIXME: Handle endpoint mode properly */
+	if (port->endpoint)
+		return;
+
+	/* Check if primary bridge */
+	if (of_get_property(port->node, "primary", NULL))
+		primary = 1;
+
+	/* Get bus range if any */
+	bus_range = of_get_property(port->node, "bus-range", NULL);
+
+	/* Allocate the host controller data structure */
+	hose = pcibios_alloc_controller(port->node);
+	if (!hose)
+		goto fail;
+
+	/* We stick the port number in "indirect_type" so the config space
+	 * ops can retrieve the port data structure easily
+	 */
+	hose->indirect_type = port->index;
+
+	/* Get bus range */
+	hose->first_busno = bus_range ? bus_range[0] : 0x0;
+	hose->last_busno = bus_range ? bus_range[1] : 0xff;
+
+	/* Because of how big mapping the config space is (1M per bus), we
+	 * limit how many busses we support. In the long run, we could replace
+	 * that with something akin to kmap_atomic instead. We set aside 1 bus
+	 * for the host itself too.
+	 */
+	busses = hose->last_busno - hose->first_busno; /* This is off by 1 */
+	if (busses > MAX_PCIE_BUS_MAPPED) {
+		busses = MAX_PCIE_BUS_MAPPED;
+		hose->last_busno = hose->first_busno + busses;
+	}
+
+	/* We map the external config space in cfg_data and the host config
+	 * space in cfg_addr. External space is 1M per bus, internal space
+	 * is 4K
+	 */
+	cfg_data = ioremap(port->cfg_space.start +
+				 (hose->first_busno + 1) * 0x100000,
+				 busses * 0x100000);
+	mbase = ioremap(port->cfg_space.start + 0x10000000, 0x1000);
+	if (cfg_data == NULL || mbase == NULL) {
+		printk(KERN_ERR "%s: Can't map config space !",
+		       port->node->full_name);
+		goto fail;
+	}
+
+	hose->cfg_data = cfg_data;
+	hose->cfg_addr = mbase;
+
+	pr_debug("PCIE %s, bus %d..%d\n", port->node->full_name,
+		 hose->first_busno, hose->last_busno);
+	pr_debug("     config space mapped at: root @0x%p, other @0x%p\n",
+		 hose->cfg_addr, hose->cfg_data);
+
+	/* Setup config space */
+	hose->ops = &ppc4xx_pciex_pci_ops;
+	port->hose = hose;
+	mbase = (void __iomem *)hose->cfg_addr;
+
+	/*
+	 * Set bus numbers on our root port
+	 */
+	out_8(mbase + PCI_PRIMARY_BUS, hose->first_busno);
+	out_8(mbase + PCI_SECONDARY_BUS, hose->first_busno + 1);
+	out_8(mbase + PCI_SUBORDINATE_BUS, hose->last_busno);
+
+	/*
+	 * OMRs are already reset, also disable PIMs
+	 */
+	out_le32(mbase + PECFG_PIMEN, 0);
+
+	/* Parse outbound mapping resources */
+	pci_process_bridge_OF_ranges(hose, port->node, primary);
+
+	/* Parse inbound mapping resources */
+	if (ppc4xx_parse_dma_ranges(hose, mbase, &dma_window) != 0)
+		goto fail;
+
+	/* Configure outbound ranges POMs */
+	ppc4xx_configure_pciex_POMs(port, hose, mbase);
+
+	/* Configure inbound ranges PIMs */
+	ppc4xx_configure_pciex_PIMs(port, hose, mbase, &dma_window);
+
+	/* The root complex doesn't show up if we don't set some vendor
+	 * and device IDs into it. Those are the same bogus one that the
+	 * initial code in arch/ppc add. We might want to change that.
+	 */
+	out_le16(mbase + 0x200, 0xaaa0 + port->index);
+	out_le16(mbase + 0x202, 0xbed0 + port->index);
+
+	/* Set Class Code to PCI-PCI bridge and Revision Id to 1 */
+	out_le32(mbase + 0x208, 0x06040001);
+
+	printk(KERN_INFO "PCIE%d: successfully set as root-complex\n",
+	       port->index);
+	return;
+ fail:
+	if (hose)
+		pcibios_free_controller(hose);
+	if (cfg_data)
+		iounmap(cfg_data);
+	if (mbase)
+		iounmap(mbase);
+}
+
 static void __init ppc4xx_probe_pciex_bridge(struct device_node *np)
 {
-	/* NYI */
+	struct ppc4xx_pciex_port *port;
+	const u32 *pval;
+	int portno;
+	unsigned int dcrs;
+
+	/* First, proceed to core initialization as we assume there's
+	 * only one PCIe core in the system
+	 */
+	if (ppc4xx_pciex_check_core_init(np))
+		return;
+
+	/* Get the port number from the device-tree */
+	pval = of_get_property(np, "port", NULL);
+	if (pval == NULL) {
+		printk(KERN_ERR "PCIE: Can't find port number for %s\n",
+		       np->full_name);
+		return;
+	}
+	portno = *pval;
+	if (portno >= ppc4xx_pciex_port_count) {
+		printk(KERN_ERR "PCIE: port number out of range for %s\n",
+		       np->full_name);
+		return;
+	}
+	port = &ppc4xx_pciex_ports[portno];
+	port->index = portno;
+	port->node = of_node_get(np);
+	pval = of_get_property(np, "sdr-base", NULL);
+	if (pval == NULL) {
+		printk(KERN_ERR "PCIE: missing sdr-base for %s\n",
+		       np->full_name);
+		return;
+	}
+	port->sdr_base = *pval;
+
+	/* Fetch config space registers address */
+	if (of_address_to_resource(np, 0, &port->cfg_space)) {
+		printk(KERN_ERR "%s: Can't get PCI-E config space !",
+		       np->full_name);
+		return;
+	}
+	/* Fetch host bridge internal registers address */
+	if (of_address_to_resource(np, 1, &port->utl_regs)) {
+		printk(KERN_ERR "%s: Can't get UTL register base !",
+		       np->full_name);
+		return;
+	}
+
+	/* Map DCRs */
+	dcrs = dcr_resource_start(np, 0);
+	if (dcrs == 0) {
+		printk(KERN_ERR "%s: Can't get DCR register base !",
+		       np->full_name);
+		return;
+	}
+	port->dcrs = dcr_map(np, dcrs, dcr_resource_len(np, 0));
+
+	/* Initialize the port specific registers */
+	if (ppc4xx_pciex_port_init(port))
+		return;
+
+	/* Setup the linux hose data structure */
+	ppc4xx_pciex_port_setup_hose(port);
 }
 
+#endif /* CONFIG_PPC4xx_PCI_EXPRESS */
+
 static int __init ppc4xx_pci_find_bridges(void)
 {
 	struct device_node *np;
 
+#ifdef CONFIG_PPC4xx_PCI_EXPRESS
 	for_each_compatible_node(np, NULL, "ibm,plb-pciex")
 		ppc4xx_probe_pciex_bridge(np);
+#endif
 	for_each_compatible_node(np, NULL, "ibm,plb-pcix")
 		ppc4xx_probe_pcix_bridge(np);
 	for_each_compatible_node(np, NULL, "ibm,plb-pci")
Index: linux-merge/arch/powerpc/sysdev/ppc4xx_pci.h
===================================================================
--- linux-merge.orig/arch/powerpc/sysdev/ppc4xx_pci.h	2007-12-11 16:50:46.000000000 +1100
+++ linux-merge/arch/powerpc/sysdev/ppc4xx_pci.h	2007-12-11 16:50:49.000000000 +1100
@@ -121,5 +121,247 @@
 #define PCIL0_PTM2MS		0x38
 #define PCIL0_PTM2LA		0x3c
 
+/*
+ * 4xx PCIe bridge register definitions
+ */
+
+/* DCR offsets */
+#define DCRO_PEGPL_CFGBAH		0x00
+#define DCRO_PEGPL_CFGBAL		0x01
+#define DCRO_PEGPL_CFGMSK		0x02
+#define DCRO_PEGPL_MSGBAH		0x03
+#define DCRO_PEGPL_MSGBAL		0x04
+#define DCRO_PEGPL_MSGMSK		0x05
+#define DCRO_PEGPL_OMR1BAH		0x06
+#define DCRO_PEGPL_OMR1BAL		0x07
+#define DCRO_PEGPL_OMR1MSKH		0x08
+#define DCRO_PEGPL_OMR1MSKL		0x09
+#define DCRO_PEGPL_OMR2BAH		0x0a
+#define DCRO_PEGPL_OMR2BAL		0x0b
+#define DCRO_PEGPL_OMR2MSKH		0x0c
+#define DCRO_PEGPL_OMR2MSKL		0x0d
+#define DCRO_PEGPL_OMR3BAH		0x0e
+#define DCRO_PEGPL_OMR3BAL		0x0f
+#define DCRO_PEGPL_OMR3MSKH		0x10
+#define DCRO_PEGPL_OMR3MSKL		0x11
+#define DCRO_PEGPL_REGBAH		0x12
+#define DCRO_PEGPL_REGBAL		0x13
+#define DCRO_PEGPL_REGMSK		0x14
+#define DCRO_PEGPL_SPECIAL		0x15
+#define DCRO_PEGPL_CFG			0x16
+#define DCRO_PEGPL_ESR			0x17
+#define DCRO_PEGPL_EARH			0x18
+#define DCRO_PEGPL_EARL			0x19
+#define DCRO_PEGPL_EATR			0x1a
+
+/* DMER mask */
+#define GPL_DMER_MASK_DISA	0x02000000
+
+/*
+ * System DCRs (SDRs)
+ */
+#define PESDR0_PLLLCT1			0x03a0
+#define PESDR0_PLLLCT2			0x03a1
+#define PESDR0_PLLLCT3			0x03a2
+
+/*
+ * 440SPe additional DCRs
+ */
+#define PESDR0_440SPE_UTLSET1		0x0300
+#define PESDR0_440SPE_UTLSET2		0x0301
+#define PESDR0_440SPE_DLPSET		0x0302
+#define PESDR0_440SPE_LOOP		0x0303
+#define PESDR0_440SPE_RCSSET		0x0304
+#define PESDR0_440SPE_RCSSTS		0x0305
+#define PESDR0_440SPE_HSSL0SET1		0x0306
+#define PESDR0_440SPE_HSSL0SET2		0x0307
+#define PESDR0_440SPE_HSSL0STS		0x0308
+#define PESDR0_440SPE_HSSL1SET1		0x0309
+#define PESDR0_440SPE_HSSL1SET2		0x030a
+#define PESDR0_440SPE_HSSL1STS		0x030b
+#define PESDR0_440SPE_HSSL2SET1		0x030c
+#define PESDR0_440SPE_HSSL2SET2		0x030d
+#define PESDR0_440SPE_HSSL2STS		0x030e
+#define PESDR0_440SPE_HSSL3SET1		0x030f
+#define PESDR0_440SPE_HSSL3SET2		0x0310
+#define PESDR0_440SPE_HSSL3STS		0x0311
+#define PESDR0_440SPE_HSSL4SET1		0x0312
+#define PESDR0_440SPE_HSSL4SET2		0x0313
+#define PESDR0_440SPE_HSSL4STS	       	0x0314
+#define PESDR0_440SPE_HSSL5SET1		0x0315
+#define PESDR0_440SPE_HSSL5SET2		0x0316
+#define PESDR0_440SPE_HSSL5STS		0x0317
+#define PESDR0_440SPE_HSSL6SET1		0x0318
+#define PESDR0_440SPE_HSSL6SET2		0x0319
+#define PESDR0_440SPE_HSSL6STS		0x031a
+#define PESDR0_440SPE_HSSL7SET1		0x031b
+#define PESDR0_440SPE_HSSL7SET2		0x031c
+#define PESDR0_440SPE_HSSL7STS		0x031d
+#define PESDR0_440SPE_HSSCTLSET		0x031e
+#define PESDR0_440SPE_LANE_ABCD		0x031f
+#define PESDR0_440SPE_LANE_EFGH		0x0320
+
+#define PESDR1_440SPE_UTLSET1		0x0340
+#define PESDR1_440SPE_UTLSET2		0x0341
+#define PESDR1_440SPE_DLPSET		0x0342
+#define PESDR1_440SPE_LOOP		0x0343
+#define PESDR1_440SPE_RCSSET		0x0344
+#define PESDR1_440SPE_RCSSTS		0x0345
+#define PESDR1_440SPE_HSSL0SET1		0x0346
+#define PESDR1_440SPE_HSSL0SET2		0x0347
+#define PESDR1_440SPE_HSSL0STS		0x0348
+#define PESDR1_440SPE_HSSL1SET1		0x0349
+#define PESDR1_440SPE_HSSL1SET2		0x034a
+#define PESDR1_440SPE_HSSL1STS		0x034b
+#define PESDR1_440SPE_HSSL2SET1		0x034c
+#define PESDR1_440SPE_HSSL2SET2		0x034d
+#define PESDR1_440SPE_HSSL2STS		0x034e
+#define PESDR1_440SPE_HSSL3SET1		0x034f
+#define PESDR1_440SPE_HSSL3SET2		0x0350
+#define PESDR1_440SPE_HSSL3STS		0x0351
+#define PESDR1_440SPE_HSSCTLSET		0x0352
+#define PESDR1_440SPE_LANE_ABCD		0x0353
+
+#define PESDR2_440SPE_UTLSET1		0x0370
+#define PESDR2_440SPE_UTLSET2		0x0371
+#define PESDR2_440SPE_DLPSET		0x0372
+#define PESDR2_440SPE_LOOP		0x0373
+#define PESDR2_440SPE_RCSSET		0x0374
+#define PESDR2_440SPE_RCSSTS		0x0375
+#define PESDR2_440SPE_HSSL0SET1		0x0376
+#define PESDR2_440SPE_HSSL0SET2		0x0377
+#define PESDR2_440SPE_HSSL0STS		0x0378
+#define PESDR2_440SPE_HSSL1SET1		0x0379
+#define PESDR2_440SPE_HSSL1SET2		0x037a
+#define PESDR2_440SPE_HSSL1STS		0x037b
+#define PESDR2_440SPE_HSSL2SET1		0x037c
+#define PESDR2_440SPE_HSSL2SET2		0x037d
+#define PESDR2_440SPE_HSSL2STS		0x037e
+#define PESDR2_440SPE_HSSL3SET1		0x037f
+#define PESDR2_440SPE_HSSL3SET2		0x0380
+#define PESDR2_440SPE_HSSL3STS		0x0381
+#define PESDR2_440SPE_HSSCTLSET		0x0382
+#define PESDR2_440SPE_LANE_ABCD		0x0383
+
+/*
+ * 405EX additional DCRs
+ */
+#define PESDR0_405EX_UTLSET1		0x0400
+#define PESDR0_405EX_UTLSET2		0x0401
+#define PESDR0_405EX_DLPSET		0x0402
+#define PESDR0_405EX_LOOP		0x0403
+#define PESDR0_405EX_RCSSET		0x0404
+#define PESDR0_405EX_RCSSTS		0x0405
+#define PESDR0_405EX_PHYSET1		0x0406
+#define PESDR0_405EX_PHYSET2		0x0407
+#define PESDR0_405EX_BIST		0x0408
+#define PESDR0_405EX_LPB		0x040B
+#define PESDR0_405EX_PHYSTA		0x040C
+
+#define PESDR1_405EX_UTLSET1		0x0440
+#define PESDR1_405EX_UTLSET2		0x0441
+#define PESDR1_405EX_DLPSET		0x0442
+#define PESDR1_405EX_LOOP		0x0443
+#define PESDR1_405EX_RCSSET		0x0444
+#define PESDR1_405EX_RCSSTS		0x0445
+#define PESDR1_405EX_PHYSET1		0x0446
+#define PESDR1_405EX_PHYSET2		0x0447
+#define PESDR1_405EX_BIST		0x0448
+#define PESDR1_405EX_LPB		0x044B
+#define PESDR1_405EX_PHYSTA		0x044C
+
+/*
+ * Of the above, some are common offsets from the base
+ */
+#define PESDRn_UTLSET1			0x00
+#define PESDRn_UTLSET2			0x01
+#define PESDRn_DLPSET			0x02
+#define PESDRn_LOOP			0x03
+#define PESDRn_RCSSET			0x04
+#define PESDRn_RCSSTS			0x05
+
+/* 440spe only */
+#define PESDRn_440SPE_HSSL0SET1		0x06
+#define PESDRn_440SPE_HSSL0SET2		0x07
+#define PESDRn_440SPE_HSSL0STS		0x08
+#define PESDRn_440SPE_HSSL1SET1		0x09
+#define PESDRn_440SPE_HSSL1SET2		0x0a
+#define PESDRn_440SPE_HSSL1STS		0x0b
+#define PESDRn_440SPE_HSSL2SET1		0x0c
+#define PESDRn_440SPE_HSSL2SET2		0x0d
+#define PESDRn_440SPE_HSSL2STS		0x0e
+#define PESDRn_440SPE_HSSL3SET1		0x0f
+#define PESDRn_440SPE_HSSL3SET2		0x10
+#define PESDRn_440SPE_HSSL3STS		0x11
+
+/* 440spe port 0 only */
+#define PESDRn_440SPE_HSSL4SET1		0x12
+#define PESDRn_440SPE_HSSL4SET2		0x13
+#define PESDRn_440SPE_HSSL4STS	       	0x14
+#define PESDRn_440SPE_HSSL5SET1		0x15
+#define PESDRn_440SPE_HSSL5SET2		0x16
+#define PESDRn_440SPE_HSSL5STS		0x17
+#define PESDRn_440SPE_HSSL6SET1		0x18
+#define PESDRn_440SPE_HSSL6SET2		0x19
+#define PESDRn_440SPE_HSSL6STS		0x1a
+#define PESDRn_440SPE_HSSL7SET1		0x1b
+#define PESDRn_440SPE_HSSL7SET2		0x1c
+#define PESDRn_440SPE_HSSL7STS		0x1d
+
+/* 405ex only */
+#define PESDRn_405EX_PHYSET1		0x06
+#define PESDRn_405EX_PHYSET2		0x07
+#define PESDRn_405EX_PHYSTA		0x0c
+
+/*
+ * UTL register offsets
+ */
+#define PEUTL_PBCTL		0x00
+#define PEUTL_PBBSZ		0x20
+#define PEUTL_OPDBSZ		0x68
+#define PEUTL_IPHBSZ		0x70
+#define PEUTL_IPDBSZ		0x78
+#define PEUTL_OUTTR		0x90
+#define PEUTL_INTR		0x98
+#define PEUTL_PCTL		0xa0
+#define PEUTL_RCSTA		0xB0
+#define PEUTL_RCIRQEN		0xb8
+
+/*
+ * Config space register offsets
+ */
+#define PECFG_BAR0LMPA		0x210
+#define PECFG_BAR0HMPA		0x214
+#define PECFG_BAR1MPA		0x218
+#define PECFG_BAR2LMPA		0x220
+#define PECFG_BAR2HMPA		0x224
+
+#define PECFG_PIMEN		0x33c
+#define PECFG_PIM0LAL		0x340
+#define PECFG_PIM0LAH		0x344
+#define PECFG_PIM1LAL		0x348
+#define PECFG_PIM1LAH		0x34c
+#define PECFG_PIM01SAL		0x350
+#define PECFG_PIM01SAH		0x354
+
+#define PECFG_POM0LAL		0x380
+#define PECFG_POM0LAH		0x384
+#define PECFG_POM1LAL		0x388
+#define PECFG_POM1LAH		0x38c
+#define PECFG_POM2LAL		0x390
+#define PECFG_POM2LAH		0x394
+
+
+enum
+{
+	PTYPE_ENDPOINT		= 0x0,
+	PTYPE_LEGACY_ENDPOINT	= 0x1,
+	PTYPE_ROOT_PORT		= 0x4,
+
+	LNKW_X1			= 0x1,
+	LNKW_X4			= 0x4,
+	LNKW_X8			= 0x8
+};
+
 
 #endif /* __PPC4XX_PCI_H__ */
Index: linux-merge/arch/powerpc/Kconfig
===================================================================
--- linux-merge.orig/arch/powerpc/Kconfig	2007-12-11 16:50:46.000000000 +1100
+++ linux-merge/arch/powerpc/Kconfig	2007-12-11 16:50:49.000000000 +1100
@@ -165,6 +165,7 @@ config PPC_OF_PLATFORM_PCI
 
 source "init/Kconfig"
 
+source "arch/powerpc/sysdev/Kconfig"
 source "arch/powerpc/platforms/Kconfig"
 
 menu "Kernel options"
Index: linux-merge/arch/powerpc/sysdev/Kconfig
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-merge/arch/powerpc/sysdev/Kconfig	2007-12-11 16:50:49.000000000 +1100
@@ -0,0 +1,8 @@
+# For a description of the syntax of this configuration file,
+# see Documentation/kbuild/kconfig-language.txt.
+#
+
+config PPC4xx_PCI_EXPRESS
+	bool
+	depends on PCI && 4xx
+	default n

^ permalink raw reply

* [PATCH 6/20] [POWERPC] PCI support for 4xx Ebony board
From: Benjamin Herrenschmidt @ 2007-12-13  7:38 UTC (permalink / raw)
  To: Josh Boyer; +Cc: linuxppc-dev

This wires up the 4xx PCI support & device tree bits for
440GP based Ebony platform.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---

 arch/powerpc/boot/dts/ebony.dts |   41 +++++++++++++++++++++++++++++++++++-----
 1 file changed, 36 insertions(+), 5 deletions(-)

--- linux-work.orig/arch/powerpc/boot/dts/ebony.dts	2007-10-15 11:19:35.000000000 +1000
+++ linux-work/arch/powerpc/boot/dts/ebony.dts	2007-11-27 18:27:37.000000000 +1100
@@ -284,12 +284,43 @@
 
 		};
 
-		PCIX0: pci@1234 {
+		PCIX0: pci@20ec00000 {
 			device_type = "pci";
-			/* FIXME */
-			reg = <2 0ec00000 8
-			       2 0ec80000 f0
-			       2 0ec80100 fc>;
+			#interrupt-cells = <1>;
+			#size-cells = <2>;
+			#address-cells = <3>;
+			compatible = "ibm,plb440gp-pcix", "ibm,plb-pcix";
+			primary;
+			reg = <2 0ec00000 8	/* Config space access */
+			       0 0 0		/* no IACK cycles */
+			       2 0ed00000 4     /* Special cycles */
+			       2 0ec80000 f0	/* Internal registers */
+			       2 0ec80100 fc>;	/* Internal messaging registers */
+
+			/* Outbound ranges, one memory and one IO,
+			 * later cannot be changed
+			 */
+			ranges = <02000000 0 80000000 00000003 80000000 0 80000000
+				  01000000 0 00000000 00000002 08000000 0 00010000>;
+
+			/* Inbound 2GB range starting at 0 */
+			dma-ranges = <42000000 0 0 0 0 0 80000000>;
+
+			/* Ebony has all 4 IRQ pins tied together per slot */
+			interrupt-map-mask = <f800 0 0 0>;
+			interrupt-map = <
+				/* IDSEL 1 */
+				0800 0 0 0 &UIC0 17 8
+
+				/* IDSEL 2 */
+				1000 0 0 0 &UIC0 18 8
+
+				/* IDSEL 3 */
+				1800 0 0 0 &UIC0 19 8
+
+				/* IDSEL 4 */
+				2000 0 0 0 &UIC0 1a 8
+			>;
 		};
 	};
 

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox