All of lore.kernel.org
 help / color / mirror / Atom feed
* RE: 2.6.19-rc5 x86_64 irq 22: nobody cared
From: Lu, Yinghai @ 2006-11-10 20:16 UTC (permalink / raw)
  To: Andi Kleen, Olivier Nicolas, Eric W. Biederman, Andrew Morton
  Cc: Adrian Bunk, Stephen Hemminger, Takashi Iwai, Jaroslav Kysela,
	linux-kernel, gregkh, linux-pci, len.brown, linux-acpi,
	Linus Torvalds, Lu, Yinghai

That didn't fix the bug.



-----Original Message-----
From: Olivier Nicolas [mailto:olivn@trollprod.org] 
Sent: Friday, November 10, 2006 12:03 PM
To: Lu, Yinghai
Subject: Re: 2.6.19-rc5 x86_64 irq 22: nobody cared

Bad day today,

Kernel compiled with the first parch irq_mcp55.diff

and with the disable_msi option removed.



#options snd-hda-intel disable_msi=1


ACPI: PCI Interrupt Link [ASA2] enabled at IRQ 21
__assign_irq_vector: irq=15, vector=79, domain=000000ff, mask=00000003,
cpu_online_map=00000003
ACPI: PCI Interrupt 0000:00:0d.2[C] -> Link [ASA2] -> GSI 21 (level,
low) -> IRQ 21
PCI: Setting latency timer of device 0000:00:0d.2 to 64
ata5: SATA max UDMA/133 cmd 0xC400 ctl 0xC002 bmdma 0xB400 irq 21
ata6: SATA max UDMA/133 cmd 0xBC00 ctl 0xB802 bmdma 0xB408 irq 21

ACPI: PCI Interrupt Link [AAZA] enabled at IRQ 21
ACPI: PCI Interrupt 0000:00:0e.1[B] -> Link [AAZA] -> GSI 21 (level,
low) -> IRQ 21
__assign_irq_vector: irq=139, vector=91, domain=000000ff, mask=00000003,
cpu_online_map=00000003
__assign_irq_vector: irq=139, old_vector=91, domain=000000ff,
mask=00000003, cpu_online_map=00000003
PCI: Setting latency timer of device 0000:00:0e.1 to 64
irq 21: nobody cared (try booting with the "irqpoll" option)

Call Trace:
 <IRQ>  [<ffffffff8025a055>] __report_bad_irq+0x35/0x90
 [<ffffffff8025a2d3>] note_interrupt+0x223/0x280
 [<ffffffff8025ad41>] handle_fasteoi_irq+0xb1/0xf0
 [<ffffffff8020b17c>] call_softirq+0x1c/0x30
 [<ffffffff8020d1ba>] do_IRQ+0x8a/0xe0
 [<ffffffff802092f0>] default_idle+0x0/0x50
 [<ffffffff8020a571>] ret_from_intr+0x0/0xa
 <EOI>  [<ffffffff80209319>] default_idle+0x29/0x50
 [<ffffffff8020939b>] cpu_idle+0x5b/0x80
 [<ffffffff8050039c>] start_secondary+0x50c/0x520

handlers:
[<ffffffff8807f150>] (nv_generic_interrupt+0x0/0xc0 [sata_nv])
Disabling IRQ #21
ALSA sound/pci/hda/hda_intel.c:543: hda_intel: No response from codec,
disabling MSI...

So kernl assign irq 21 to sata2, and later share irq 21 with audio.

But audio get MSI, and at that time, it may do sth bad to irq21 that it
is still shared with SATA2.

YH




^ permalink raw reply

* LVM2/lib/metadata mirror.c
From: agk @ 2006-11-10 20:15 UTC (permalink / raw)
  To: lvm-devel

CVSROOT:	/cvs/lvm2
Module name:	LVM2
Changes by:	agk at sourceware.org	2006-11-10 20:15:10

Modified files:
	lib/metadata   : mirror.c 

Log message:
	fix cast

Patches:
http://sourceware.org/cgi-bin/cvsweb.cgi/LVM2/lib/metadata/mirror.c.diff?cvsroot=lvm2&r1=1.35&r2=1.36

--- LVM2/lib/metadata/mirror.c	2006/11/10 19:35:03	1.35
+++ LVM2/lib/metadata/mirror.c	2006/11/10 20:15:10	1.36
@@ -44,7 +44,7 @@
 {
 	uint64_t region_max;
 
-	region_max = (1 << (ffs((int)extents) - 1)) * extent_size;
+	region_max = (1 << (ffs((int)extents) - 1)) * (uint64_t) extent_size;
 
 	if (region_max < UINT32_MAX && region_size > region_max) {
 		region_size = (uint32_t) region_max;



^ permalink raw reply

* Re: [RFC] bootloader improvements - pygrub-kernel-config
From: Daniel P. Berrange @ 2006-11-10 20:14 UTC (permalink / raw)
  To: John Levon; +Cc: xen-devel
In-Reply-To: <20061109192240.GC16717@totally.trollied.org>

On Thu, Nov 09, 2006 at 07:22:40PM +0000, John Levon wrote:
> 
> # HG changeset patch
> # User john.levon@sun.com
> # Date 1163095800 28800
> # Node ID 0da173ee886e13bd4116c7d085cd4a4704ffe279
> # Parent  7c80aaffe16f5e4037d10660d262ed7b22894b01
> Split out bootloader-derived parameters separately from config-file-specified
> ones. This allows a config file to still specify kernel/ramdisk, even if a
> bootloader is being used.

I'm not sure this is a good idea - its essentially completely inverting
the current semantics for bootloader/kernel params in the config files.
Currently a 'kernel' parameter will always override a 'bootloader' param,
but with this change a 'bootloader' param will always override a 'kernel'
param. I can see that both approaches have their merits, I don't think
we can ever pick one prioritization rule which satisfies everyone here
and thus for sake of compatability we should keep the current semantics
for kernel/booloader prioritization in the configs.

> diff --git a/tools/python/xen/xend/image.py b/tools/python/xen/xend/image.py
> --- a/tools/python/xen/xend/image.py
> +++ b/tools/python/xen/xend/image.py
> @@ -70,7 +70,9 @@ class ImageHandler:
>          self.vm = vm
>  
>          self.kernel = None
> +        self.boot_kernel = None
>          self.ramdisk = None
> +        self.boot_ramdisk = None
>          self.cmdline = None
>  
>          self.configure(imageConfig, deviceConfig)
> @@ -82,6 +84,14 @@ class ImageHandler:
>              return sxp.child_value(imageConfig, name, default)
>  
>          self.kernel = get_cfg("kernel")
> +        self.ramdisk = get_cfg("ramdisk", '')
> +        self.boot_kernel = get_cfg("boot_kernel")
> +        if not self.boot_kernel:
> +            self.boot_kernel = self.kernel
> +        self.boot_ramdisk = get_cfg("boot_ramdisk")
> +        if not self.boot_ramdisk:
> +            self.boot_ramdisk = self.ramdisk
> +

This is also changing the semantics of the SEXPR exposed by XenD - previously
the ('vmlinux') and ('initrd') parameters would refer to the live kernel
in the guest, but now you'd have to look at the boot_XXX variants instead.

If we did want to change the prioritzation such that bootloader overrides
any explicit kernel/initrd, then I think we should just have the values
from bootloader directly overwrite the existing named SXPR fields rather
than adding new boot_XX fields.

Regards,
Dan.
-- 
|=- Red Hat, Engineering, Emerging Technologies, Boston.  +1 978 392 2496 -=|
|=-           Perl modules: http://search.cpan.org/~danberr/              -=|
|=-               Projects: http://freshmeat.net/~danielpb/               -=|
|=-  GnuPG: 7D3B9505   F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505  -=| 

^ permalink raw reply

* Re: bcm43xx-d80211 broadcast reception with WPA
From: Michael Buesch @ 2006-11-10 20:12 UTC (permalink / raw)
  To: Paul Hampson; +Cc: netdev
In-Reply-To: <ej09qf$2nv$1@sea.gmane.org>

On Thursday 09 November 2006 23:23, Paul Hampson wrote:
> Hi,
> 
> Long time lurker, first time poster. ^_^
> 
> I've been backporting the bcm43xx-d80211 driver to whatever the released
> 2.6 kernel was using the rt2x00 project's d80211 stack (equivalent to
> current wireless-dev but with a workaround for not having a ieee80211_dev
> pointer and still using the _tfm interface instead of the _cypher interface.)
> 
> As of last night's wireless-dev tree bcm43xx, everything seems to be
> operating fine except incoming broadcast traffic is coming in 14 bytes too
> long and scrambled. I presume this means it's not decrypting properly...

It sounds like a bug in the hardware decryption setup.
Are you using TKIP or not?

Incoming mcast frames are handled in a special way in hardware.
The keyidx field of the packet is used to lookup the key, as
far as I know. (Otherwise the MAC address is used).
Can I see a full dmesg log and a capture log on the broken machine, please?

-- 
Greetings Michael.

^ permalink raw reply

* Re: 2.6.19-rc5 x86_64 irq 22: nobody cared
From: Eric W. Biederman @ 2006-11-10 20:11 UTC (permalink / raw)
  To: Lu, Yinghai
  Cc: Andi Kleen, Olivier Nicolas, Andrew Morton, Adrian Bunk,
	Stephen Hemminger, Takashi Iwai, Jaroslav Kysela, linux-kernel,
	gregkh, linux-pci, len.brown, linux-acpi, Linus Torvalds
In-Reply-To: <5986589C150B2F49A46483AC44C7BCA49071D9@ssvlexmb2.amd.com>

"Lu, Yinghai" <yinghai.lu@amd.com> writes:

> Andi,
>
> The two patches solve the problems that irq nobody care.
>
> They are already in your tree. But first one I wonder if you put correct
> one in your tree.

YH.  This is a completely different problem.  The irq is properly setup
and received but none of the drivers wanted it.

Eric

^ permalink raw reply

* Re: [BUG] All Kerberos mounts stop working, restarting rpc.svcgssd helps
From: Kevin Coffman @ 2006-11-10 20:11 UTC (permalink / raw)
  To: Steinar H. Gunderson; +Cc: J. Bruce Fields, nfs
In-Reply-To: <20061108005045.GA4524@uio.no>

On 11/7/06, Steinar H. Gunderson <sgunderson@bigfoot.com> wrote:
> On Tue, Nov 07, 2006 at 07:44:40PM -0500, J. Bruce Fields wrote:
> >>>> http://home.samfundet.no/~sesse/svcgssd-strace.log.bz2
> >>> 18:48:39 ERROR 404: Not Found.
> >> Hm, that's odd. Oh well, I found a log here now, at least -- try it now.
> > Nope.  Are you sure there isn't a typo in the URL?  I'm just
> > cut-n-pasting it.
>
> Gah! It's not my day today. Try once more, please.

I didn't find any clues in the strace output either.  I'm not sure
where to look next.

K.C.

-------------------------------------------------------------------------
Using Tomcat but need to do more? Need to support web services, security?
Get stuff done quickly with pre-integrated technology to make your job easier
Download IBM WebSphere Application Server v.1.0.1 based on Apache Geronimo
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=120709&bid=263057&dat=121642
_______________________________________________
NFS maillist  -  NFS@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/nfs

^ permalink raw reply

* boot scripts and dm-multipath
From: Edward Goggin @ 2006-11-10 20:11 UTC (permalink / raw)
  To: dm-devel

Resending this since I think I got no feedback last time.

Linux distributions currently invoke /sbin/multipath fairly early in the
boot sequence, followed sometime later by /sbin/multipathd.  I think
that these distributions run the risk of incurring a boot-time hang
when/if read I/Os issued (by kpartx in /etc/boot.multipath for instance)
to dm-multipath devices before multipathd is started, fail (due to
failures which are not SCSI transport related) when sent to storage
targets configured with the dm-multipath queue_if_no_path feature.
Without multipathd running there is no ability to timeout the "queue I/O
forever" behavior during an all-paths-down use case.

In these cases, the dm-multipath device is created because the storage
target responds successfully to the device id inquiry request for each
path but all path tests and read/write I/O requests issued on all paths
to the device will fail.  If the dm-multipath device was configured with
the queue_if_no_path feature, the kernel dm-multipath code will queue
the failed read/write I/O indefinitely.

Newer versions of multipathd (that is, ones based on
multipath-tools-0.4.7) do not need to invoke multipath in order to
configure the dm-multipath, simply invoking multipathd suffices.  Is it
reasonable to change these scripts to invoke multipathd instead of
multipath at early boot and not invoke multipath at all from these
scripts?

^ permalink raw reply

* Re: 2.6.19-rc3 system freezes when ripping with cdparanoia at ioctl(SG_IO)
From: Luben Tuikov @ 2006-11-10 20:08 UTC (permalink / raw)
  To: dougg
  Cc: Tejun Heo, Brice Goglin, Jens Axboe, Gregor Jasny, Linux Kernel,
	Jeff Garzik, linux-ide, monty, linux-scsi
In-Reply-To: <4554777B.7050708@torque.net>

--- Douglas Gilbert <dougg@torque.net> wrote:
> Luben Tuikov wrote:
> > --- Douglas Gilbert <dougg@torque.net> wrote:
> >> Tejun Heo wrote:
> >>> [CC'ing Monty and Douglas.]
> >>>
> >>> Hello, the original thread can be read from the following URL.
> >>>
> >>> http://thread.gmane.org/gmane.linux.ide/13708/focus=13708
> >>>
> >>> Brice Goglin wrote:
> >>>> ens Axboe wrote:
> >>>>> On Mon, Oct 30 2006, Gregor Jasny wrote:
> >>>>>  
> >>>>>> 2006/10/30, Jens Axboe <jens.axboe@oracle.com>:
> >>>>>>    
> >>>>>>> Can you confirm that 2.6.18 works?
> >>>>>>>       
> >>>>>> The reporter of [1] states that his SATA Thinkpad freezes with 2.6.17
> >>>>>> and 2.6.18, too.
> >>>>>>
> >>>>>> Gregor
> >>>>>>
> >>>>>> [1] http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=391901
> >>>>>>     
> >>>>> Ok, mainly just checking if this was a potential dupe of another bug.
> >>>>>
> >>>>>   
> >>>> Jens (or anybody else who has any idea of how to debug this),
> >>>>
> >>>> Did you have a chance to reproduce the problem? I guess we "only" need a
> >>>> machine with SATA/ata_piix and cdparanoia 3.10. If you want me to debug
> >>>> some stuff, feel free to tell me what. But, since it freezes the machine
> >>>> and sysrq doesn't even work, I don't really know what to try...
> >>>>
> >>>> I just tried on rc5 and rc5-mm1, both have the problem (as 2.6.16, .17
> >>>> and .18 do, don't know about earlier kernels). I didn't have a audio CD
> >>>> here, so I tried abcde on a DVD on purpose. With cdparanoia 3.10-pre0
> >>>> (from Debian testing), it reports nothing during about 5 seconds and
> >>>> then the machine freezes. With cdparanoia 3a9.8-11 (from Debian stable),
> >>>> it reports an error very quickly, and dmesg gets a couple line like
> >>>> these:
> >>>>     sg_write: data in/out 12/12 bytes for SCSI command 0x43--guessing
> >>>> data in;
> >>>>        program cdparanoia not setting count and/or reply_len properly
> >>> Okay, here's the story.
> >>>
> >>> In interface/scan_devices.c::cdda_identify_scsi(), cdparanoia calls
> >>> scsi_inquiry() to identify the device and determine interface type. This
> >>> seems to be the first time to actually issue commands to the device.  As
> >>> interface type isn't completely determined, for sg devices, it first
> >>> issues the command w/ d->interface set to SGIO_SCSI.  If that fails, it
> >>> falls back to SGIO_SCSI_BUGGY1.
> >>>
> >>> For to-device request, both SGIO_SCSI and SGIO_SCSI_BUGGY1 set
> >>> sg_io_hdr.dxfer_direction to SG_DXFER_TO_DEV.  But for from-device
> >>> request, SGIO_SCSI uses SG_DXFER_TO_FROM_DEV while SGIO_SCSI_BUGGY1 uses
> >>> SG_DXFER_FROM_DEV.  So, cdparanoia first issues inquiry w/
> >>> SG_DXFER_TO_FROM_DEV and if that fails falls back to SG_DXFER_FROM_DEV.
> >>>
> >>> drivers/scsi/sg.c interprets SG_DXFER_TO_FROM_DEV as read while
> >>> block/scsi_ioctl.c interprets it as write.  I guess this is historic
> >>> thing (scsi/sg.c updated but block/scsi_ioctl.c is forgotten).  As
> >>> written above, cdparanoia can handle both cases as long as the kernel
> >>> promptly fails command issued with the wrong direction.
> >>>
> >>> This works for most PATA ATAPI devices.  Most devices detect reversed
> >>> transfer and terminate the command promptly.  But this doesn't seem to
> >>> be true for SATA device.  Many just hang and time out commands with the
> >>> wrong transfer direction.  If you consider that most early SATA ATAPI
> >>> devices are actually PATA + bridge, this is sorta inevitable.  The
> >>> PATA-SATA bridge cannot issue D2H FIS to abort the command by itself.
> >>> It's just mirroring the status of PATA side and PATA side doesn't know
> >>> SATA protocol mismatch has occurred.
> >>>
> >>> So, IDENTIFY w/ write-DMA protocol times out after quite some seconds.
> >>> This is where things go worse from bad.  SATA controllers which have
> >>> shadow TF registers don't handle timeout conditions very well,
> >>> especially when they're waiting for data transfer.  They basically hold
> >>> the PCI bus and hang till the transfer completes (which never happens).
> >>>  That's where the hard lock up comes from.
> >>>
> >>> Jens, I think we need to match block sg's behavior to SCSI's.  Monty,
> >>> the timeout and hard lock up are due to hardware restrictions.  Kernel
> >>> and libata can't do much about it.  So, please find other way to detect
> >>> interface.
> >> Tejun,
> >> Your SG_DXFER_TO_FROM_DEV analysis is correct.
> >>
> >> The stupid ~!@# who wrote the code, and the documentation
> >> for it, defined SG_DXFER_TO_FROM_DEV to mean a "transfer
> >> from device" operation where the kernel buffer receiving
> >> the DMA transfer was prefilled with data that the application
> >> provided. That certainly isn't a bidirectional transfer to/from
> >> the device, but it is a bidirectional transfer to kernel
> >> buffers when indirect IO is used.
> >>
> >> Why do this? Because the 'resid' field indicating how much
> >> less data was transferred in a "from_device" transfer than
> >> was requested, was not added to SCSI infrastructure till much
> >> later. There are still LLDs out there that don't implement it.
> >> It also reflected a similar technique used with the sg_header
> >> structure (circa 1992) for precisely the same reason. And
> >> application writers wanted that functionality. Joerg was the
> >> first name of one such application writer.
> >>
> >>
> >> Coincidentally I am sitting on a patch from Luben Tuikov
> >> to cause the same breakage in the sg driver itself.
> > 
> > Here is a link to the recently posted 8 month patch:
> > http://marc.theaimsgroup.com/?l=linux-scsi&m=116267031029025&w=2
> > 
> > The patch would appear to fix the problem Tejun is describing.
> > 
> > I cannot quite remember exactly what I was doing that day 8 months
> > ago, but was either disk or tape devices testing and arrived
> > at that patch.
> > 
> > This patch had been in my dev (gateway) tree for the last 8
> > months, without any problems.
> > 
> >     Luben
> > 
> > 
> >> Nobody has proposed a patch to the documentation for
> >> the explanation of SG_DXFER_TO_FROM_DEV :-)
> >>     http://www.torque.net/sg/p/sg_v3_ho.html
>                                   ^^^^^^^^^^^^^
> 
> Luben,
> The failure being reported is that the block layer
> SG_IO ioctl already does what you are proposing to
> do for the sg driver.
> 
> Hence an application, cdparanoia in this case, since
> it coded against documented behaviour, assumes that
> SG_DXFER_TO_FROM_DEV will read from the device.
> See the definition of SG_DXFER_TO_FROM_DEV in sg.h and
> the document above.
> 
> So your proposed patch would compound the problem. The
> solution is _not_ to change the sg driver and put the
> equivalent of the reverse of your patch in the block
> layer SG_IO ioctl.
> 
> There is nothing to stop a new direction flag being
> added called SG_DXFER_BIDIRECTIONAL that maps to
> DMA_BIDIRECTIONAL.

Sounds good!

  Luben

P.S. I'd love to see SG_DXFER_TO_FROM_DEV completely ripped out
of sg.c, for obvious reasons.  Can you not duplicate the resid "fix"
it provides into "FROM_DEV" -- do apps really rely on it?


^ permalink raw reply

* [PATCH 16/16] cell: add ps3 platform system bus support
From: Geoff Levand @ 2006-11-10 20:04 UTC (permalink / raw)
  To: Paul Mackerras; +Cc: linuxppc-dev

Adds a PS3 Platform system bus driver.  This system bus is a virtual
bus used to present the ps3pf system devices in the LDM.

Signed-off-by: Geoff Levand <geoffrey.levand@am.sony.com>

---
 arch/powerpc/kernel/dma_64.c |    9 +
 drivers/Makefile             |    1 
 drivers/ps3pf/Makefile       |    1 
 drivers/ps3pf/system-bus.c   |  356 +++++++++++++++++++++++++++++++++++++++++++
 include/asm-powerpc/ps3pf.h  |   75 +++++++++
 5 files changed, 442 insertions(+)

Index: cell--common--6/arch/powerpc/kernel/dma_64.c
===================================================================
--- cell--common--6.orig/arch/powerpc/kernel/dma_64.c
+++ cell--common--6/arch/powerpc/kernel/dma_64.c
@@ -11,6 +11,7 @@
 #include <linux/pci.h>
 #include <asm/vio.h>
 #include <asm/ibmebus.h>
+#include <asm/ps3pf.h>
 #include <asm/scatterlist.h>
 #include <asm/bug.h>
 
@@ -28,6 +29,10 @@
 	if (dev->bus == &ibmebus_bus_type)
 		return &ibmebus_dma_ops;
 #endif
+#ifdef CONFIG_PS3PF
+	if (dev->bus == &ps3pf_system_bus_type)
+		return &ps3pf_dma_ops;
+#endif
 	return NULL;
 }
 
@@ -55,6 +60,10 @@
 	if (dev->bus == &ibmebus_bus_type)
 		return -EIO;
 #endif
+#ifdef CONFIG_PS3PF
+	if (dev->bus == &ps3pf_system_bus_type)
+		return -EIO;
+#endif
 	BUG();
 	return 0;
 }
Index: cell--common--6/drivers/Makefile
===================================================================
--- cell--common--6.orig/drivers/Makefile
+++ cell--common--6/drivers/Makefile
@@ -77,3 +77,4 @@
 obj-$(CONFIG_SUPERH)		+= sh/
 obj-$(CONFIG_GENERIC_TIME)	+= clocksource/
 obj-$(CONFIG_DMA_ENGINE)	+= dma/
+obj-$(CONFIG_PS3PF)		+= ps3pf/
Index: cell--common--6/drivers/ps3pf/Makefile
===================================================================
--- /dev/null
+++ cell--common--6/drivers/ps3pf/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_PS3PF) += system-bus.o
Index: cell--common--6/drivers/ps3pf/system-bus.c
===================================================================
--- /dev/null
+++ cell--common--6/drivers/ps3pf/system-bus.c
@@ -0,0 +1,356 @@
+/*
+ * system-bus.c - PS3 Platform system bus driver
+ *
+ *  Copyright (C) 2006 Sony Computer Entertainment Inc.
+ *  Copyright 2006 Sony Corp.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#undef DEBUG
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+
+#include <asm/udbg.h>
+#include <asm/ps3pf.h>
+#include <asm/lv1call.h>
+
+#define dump_mmio_region(_a) _dump_mmio_region(_a, __func__, __LINE__)
+static void _dump_mmio_region(const struct ps3pf_mmio_region* r,
+	const char* func, int line)
+{
+	pr_debug("%s:%d: dev       %u:%u\n", func, line, r->did.bus_id,
+		r->did.dev_id);
+	pr_debug("%s:%d: bus_addr  %lxh\n", func, line, r->bus_addr);
+	pr_debug("%s:%d: len       %lxh\n", func, line, r->len);
+	pr_debug("%s:%d: lpar_addr %lxh\n", func, line, r->lpar_addr);
+}
+
+int ps3pf_mmio_region_create(struct ps3pf_mmio_region *r)
+{
+	int result;
+
+	result = lv1_map_device_mmio_region(r->did.bus_id, r->did.dev_id,
+		r->bus_addr, r->len, r->page_size, &r->lpar_addr);
+
+	if (result) {
+		pr_debug("%s:%d: lv1_map_device_mmio_region failed: %s\n",
+			__func__, __LINE__, ps3pf_result(result));
+		r->lpar_addr = r->len = r->bus_addr = 0;
+	}
+
+	dump_mmio_region(r);
+	return result;
+}
+
+int ps3pf_free_mmio_region(struct ps3pf_mmio_region *r)
+{
+	int result;
+
+	result = lv1_unmap_device_mmio_region(r->did.bus_id, r->did.dev_id,
+		r->bus_addr);
+
+	if (result)
+		pr_debug("%s:%d: lv1_unmap_device_mmio_region failed: %s\n",
+			__func__, __LINE__, ps3pf_result(result));
+
+	r->lpar_addr = r->len = r->bus_addr = 0;
+	return result;
+}
+
+static int ps3pf_system_bus_match(struct device *_dev,
+	struct device_driver *_drv)
+{
+	int result;
+	struct ps3pf_system_bus_driver *drv = to_ps3pf_system_bus_driver(_drv);
+	struct ps3pf_system_bus_device *dev = to_ps3pf_system_bus_device(_dev);
+
+	result = dev->match_id == drv->match_id;
+
+	pr_info("%s:%d: dev=%u(%s), drv=%u(%s): %s\n", __func__, __LINE__,
+		dev->match_id, dev->core.bus_id, drv->match_id, drv->core.name,
+		(result ? "match" : "miss"));
+	return result;
+}
+
+static int ps3pf_system_bus_probe(struct device *_dev)
+{
+	int result;
+	struct ps3pf_system_bus_device *dev = to_ps3pf_system_bus_device(_dev);
+	struct ps3pf_system_bus_driver *drv =
+		to_ps3pf_system_bus_driver(_dev->driver);
+
+	result = lv1_open_device(dev->did.bus_id, dev->did.dev_id, 0);
+
+	if (result) {
+		pr_debug("%s:%d: lv1_open_device failed (%d)\n",
+			__func__, __LINE__, result);
+		result = -EACCES;
+		goto clean_none;
+	}
+
+	if (dev->d_region->did.bus_id) {
+		result = ps3pf_dma_region_create(dev->d_region);
+
+		if (result) {
+			pr_debug("%s:%d: ps3pf_dma_region_create failed (%d)\n",
+				__func__, __LINE__, result);
+			BUG_ON("check region type");
+			result = -EINVAL;
+			goto clean_device;
+		}
+	}
+
+	BUG_ON(!drv);
+
+	if (drv->probe)
+		result = drv->probe(dev);
+	else
+		pr_info("%s:%d: %s no probe method\n", __func__, __LINE__,
+			dev->core.bus_id);
+
+	if (result) {
+		pr_debug("%s:%d: drv->probe failed\n", __func__, __LINE__);
+		goto clean_dma;
+	}
+
+	return result;
+
+clean_dma:
+	ps3pf_dma_region_free(dev->d_region);
+clean_device:
+	lv1_close_device(dev->did.bus_id, dev->did.dev_id);
+clean_none:
+	return result;
+}
+
+static int ps3pf_system_bus_remove(struct device *_dev)
+{
+	struct ps3pf_system_bus_device *dev = to_ps3pf_system_bus_device(_dev);
+	struct ps3pf_system_bus_driver *drv =
+		to_ps3pf_system_bus_driver(_dev->driver);
+
+	if (drv->remove)
+		drv->remove(dev);
+	else
+		pr_info("%s:%d: %s no remove method\n", __func__, __LINE__,
+			dev->core.bus_id);
+
+	ps3pf_dma_region_free(dev->d_region);
+	ps3pf_free_mmio_region(dev->m_region);
+	lv1_close_device(dev->did.bus_id, dev->did.dev_id);
+
+	return 0;
+}
+
+struct bus_type ps3pf_system_bus_type = {
+        .name = "ps3pf_system_bus",
+	.match = ps3pf_system_bus_match,
+	.probe = ps3pf_system_bus_probe,
+	.remove = ps3pf_system_bus_remove,
+};
+
+int __init ps3pf_system_bus_init(void)
+{
+	int result;
+
+	result = bus_register(&ps3pf_system_bus_type);
+	BUG_ON(result);
+	return result;
+}
+
+core_initcall(ps3pf_system_bus_init);
+
+/* Allocates a contiguous real buffer and creates mappings over it.
+ * Returns the virtual address of the buffer and sets dma_handle
+ * to the dma address (mapping) of the first page.
+ */
+
+static void * ps3pf_alloc_coherent(struct device *_dev, size_t size,
+	dma_addr_t *dma_handle, gfp_t flag)
+{
+	int result;
+	struct ps3pf_system_bus_device *dev = to_ps3pf_system_bus_device(_dev);
+	unsigned long virt_addr;
+
+	BUG_ON(!dev->d_region->bus_addr);
+
+	flag &= ~(__GFP_DMA | __GFP_HIGHMEM);
+	flag |= __GFP_ZERO;
+
+	virt_addr = __get_free_pages(flag, get_order(size));
+
+	if (!virt_addr) {
+		pr_debug("%s:%d: get_free_pages failed\n", __func__, __LINE__);
+		goto clean_none;
+	}
+
+	result = ps3pf_dma_map(dev->d_region, virt_addr, size, dma_handle);
+
+	if (result) {
+		pr_debug("%s:%d: ps3pf_dma_map failed (%d)\n",
+			__func__, __LINE__, result);
+		BUG_ON("check region type");
+		goto clean_alloc;
+	}
+
+	return (void*)virt_addr;
+
+clean_alloc:
+	free_pages(virt_addr, get_order(size));
+clean_none:
+	dma_handle = NULL;
+	return NULL;
+}
+
+static void ps3pf_free_coherent(struct device *_dev, size_t size, void *vaddr,
+	dma_addr_t dma_handle)
+{
+	struct ps3pf_system_bus_device *dev = to_ps3pf_system_bus_device(_dev);
+
+	ps3pf_dma_unmap(dev->d_region, dma_handle, size);
+	free_pages((unsigned long)vaddr, get_order(size));
+}
+
+/* Creates TCEs for a user provided buffer.  The user buffer must be
+ * contiguous real kernel storage (not vmalloc).  The address of the buffer
+ * passed here is the kernel (virtual) address of the buffer.  The buffer
+ * need not be page aligned, the dma_addr_t returned will point to the same
+ * byte within the page as vaddr.
+ */
+
+static dma_addr_t ps3pf_map_single(struct device *_dev, void *ptr, size_t size,
+	enum dma_data_direction direction)
+{
+	struct ps3pf_system_bus_device *dev = to_ps3pf_system_bus_device(_dev);
+	int result;
+	unsigned long bus_addr;
+
+	result = ps3pf_dma_map(dev->d_region, (unsigned long)ptr, size,
+		&bus_addr);
+
+	if (result) {
+		pr_debug("%s:%d: ps3pf_dma_map failed (%d)\n",
+			__func__, __LINE__, result);
+	}
+
+	return bus_addr;
+}
+
+static void ps3pf_unmap_single(struct device *_dev, dma_addr_t dma_addr,
+	size_t size, enum dma_data_direction direction)
+{
+	struct ps3pf_system_bus_device *dev = to_ps3pf_system_bus_device(_dev);
+	int result;
+
+	result = ps3pf_dma_unmap(dev->d_region, dma_addr, size);
+
+	if (result) {
+		pr_debug("%s:%d: ps3pf_dma_unmap failed (%d)\n",
+			__func__, __LINE__, result);
+	}
+}
+
+static int ps3pf_map_sg(struct device *_dev, struct scatterlist *sg, int nents,
+	enum dma_data_direction direction)
+{
+#if defined(CONFIG_PS3PF_DYNAMIC_DMA)
+	BUG_ON("do");
+#endif
+	return 0;
+}
+
+static void ps3pf_unmap_sg(struct device *_dev, struct scatterlist *sg,
+	int nents, enum dma_data_direction direction)
+{
+#if defined(CONFIG_PS3PF_DYNAMIC_DMA)
+	BUG_ON("do");
+#endif
+}
+
+static int ps3pf_dma_supported(struct device *_dev, u64 mask)
+{
+	return 1;
+}
+
+struct dma_mapping_ops ps3pf_dma_ops = {
+	.alloc_coherent = ps3pf_alloc_coherent,
+	.free_coherent = ps3pf_free_coherent,
+	.map_single = ps3pf_map_single,
+	.unmap_single = ps3pf_unmap_single,
+	.map_sg = ps3pf_map_sg,
+	.unmap_sg = ps3pf_unmap_sg,
+	.dma_supported = ps3pf_dma_supported
+};
+
+/**
+ * ps3pf_system_bus_release_device - remove a device from the system bus
+ */
+
+static void ps3pf_system_bus_release_device(struct device *_dev)
+{
+	struct ps3pf_system_bus_device *dev = to_ps3pf_system_bus_device(_dev);
+	kfree(dev);
+}
+
+/**
+ * ps3pf_system_bus_device_register - add a device to the system bus
+ *
+ * ps3pf_system_bus_device_register() expects the dev object to be allocated
+ * dynamically by the caller.  The system bus takes ownership of the dev
+ * object and frees the object in ps3pf_system_bus_release_device().
+ */
+
+int ps3pf_system_bus_device_register(struct ps3pf_system_bus_device *dev)
+{
+	int result;
+	static unsigned int dev_count = 1;
+
+	dev->core.parent = NULL;
+	dev->core.bus = &ps3pf_system_bus_type;
+	dev->core.release = ps3pf_system_bus_release_device;
+
+	snprintf(dev->core.bus_id, sizeof(dev->core.bus_id), "sb_%02x",
+		dev_count++);
+
+	pr_debug("%s:%d add %s\n", __func__, __LINE__, dev->core.bus_id);
+
+	result = device_register(&dev->core);
+	return result;
+}
+
+EXPORT_SYMBOL_GPL(ps3pf_system_bus_device_register);
+
+int ps3pf_system_bus_driver_register(struct ps3pf_system_bus_driver *drv)
+{
+	int result;
+
+	drv->core.bus = &ps3pf_system_bus_type;
+
+	result = driver_register(&drv->core);
+	return result;
+}
+
+EXPORT_SYMBOL_GPL(ps3pf_system_bus_driver_register);
+
+void ps3pf_system_bus_driver_unregister(struct ps3pf_system_bus_driver *drv)
+{
+	driver_unregister(&drv->core);
+}
+
+EXPORT_SYMBOL_GPL(ps3pf_system_bus_driver_unregister);
Index: cell--common--6/include/asm-powerpc/ps3pf.h
===================================================================
--- cell--common--6.orig/include/asm-powerpc/ps3pf.h
+++ cell--common--6/include/asm-powerpc/ps3pf.h
@@ -376,4 +376,79 @@
 int ps3pf_repository_read_spu_resource_id(unsigned int res_index,
 	enum ps3pf_spu_resource_type* resource_type, unsigned int *resource_id);
 
+
+/* system bus routines */
+
+enum ps3pf_match_id {
+	PS3PF_MATCH_ID_TEST_DRIVER = 1,
+	PS3PF_MATCH_ID_EHCI,
+	PS3PF_MATCH_ID_OHCI,
+	PS3PF_MATCH_ID_GELIC,
+	PS3PF_MATCH_ID_PS3_AV,
+	PS3PF_MATCH_ID_SYS_MANAGER,
+};
+
+/**
+ * struct ps3pf_system_bus_device - a device on the system bus
+ */
+
+struct ps3pf_system_bus_device {
+	enum ps3pf_match_id match_id;
+	struct ps3pf_device_id did;
+	unsigned int interrupt_id;
+/*	struct iommu_table *iommu_table; -- waiting for Ben's cleanups */
+	struct ps3pf_dma_region *d_region;
+	struct ps3pf_mmio_region *m_region;
+	struct device core;
+};
+
+/**
+ * struct ps3pf_system_bus_driver - a driver for a device on the system bus
+ */
+
+struct ps3pf_system_bus_driver {
+	enum ps3pf_match_id match_id;
+	struct device_driver core;
+	int (*probe)(struct ps3pf_system_bus_device *);
+	int (*remove)(struct ps3pf_system_bus_device *);
+/*	int (*suspend)(struct ps3pf_system_bus_device *, pm_message_t); */
+/*	int (*resume)(struct ps3pf_system_bus_device *); */
+};
+
+int ps3pf_system_bus_device_register(struct ps3pf_system_bus_device *dev);
+int ps3pf_system_bus_driver_register(struct ps3pf_system_bus_driver *drv);
+void ps3pf_system_bus_driver_unregister(struct ps3pf_system_bus_driver *drv);
+static inline struct ps3pf_system_bus_driver *to_ps3pf_system_bus_driver(
+	struct device_driver *_drv)
+{
+	return container_of(_drv, struct ps3pf_system_bus_driver, core);
+}
+static inline struct ps3pf_system_bus_device *to_ps3pf_system_bus_device(
+	struct device *_dev)
+{
+	return container_of(_dev, struct ps3pf_system_bus_device, core);
+}
+
+/**
+ * ps3pf_system_bus_set_drvdata -
+ * @dev: device structure
+ * @data: Data to set
+ */
+
+static inline void ps3pf_system_bus_set_driver_data(
+	struct ps3pf_system_bus_device *dev, void *data)
+{
+	dev->core.driver_data = data;
+}
+static inline void *ps3pf_system_bus_get_driver_data(
+	struct ps3pf_system_bus_device *dev)
+{
+	return dev->core.driver_data;
+}
+
+/* These two need global scope for get_dma_ops(). */
+
+extern struct bus_type ps3pf_system_bus_type;
+extern struct dma_mapping_ops ps3pf_dma_ops;
+
 #endif

^ permalink raw reply

* [PATCH 15/16] cell: add ps3 platform spu support
From: Geoff Levand @ 2006-11-10 20:03 UTC (permalink / raw)
  To: Paul Mackerras; +Cc: linuxppc-dev, Arnd Bergmann

Adds spu support for the PS3 Platform.

Signed-off-by: Geoff Levand <geoffrey.levand@am.sony.com>

---

This patch requires my previous patch which split the existing spu support into
platform specific and platform independent parts.

 arch/powerpc/platforms/ps3pf/Makefile   |    3 
 arch/powerpc/platforms/ps3pf/platform.h |    8 
 arch/powerpc/platforms/ps3pf/setup.c    |    2 
 arch/powerpc/platforms/ps3pf/spu.c      |  625 ++++++++++++++++++++++++++++++++
 4 files changed, 638 insertions(+)

Index: cell--common--6/arch/powerpc/platforms/ps3pf/Makefile
===================================================================
--- cell--common--6.orig/arch/powerpc/platforms/ps3pf/Makefile
+++ cell--common--6/arch/powerpc/platforms/ps3pf/Makefile
@@ -1,2 +1,5 @@
 obj-$(CONFIG_PS3PF) += setup.o mm.o smp.o time.o hvcall.o htab.o repository.o
 obj-$(CONFIG_PS3PF) += interrupt.o exports.o os-area.o
+
+spu-base-$(CONFIG_PS3PF) += spu.o
+obj-$(CONFIG_SPU_BASE) += $(spu-base-y)
Index: cell--common--6/arch/powerpc/platforms/ps3pf/platform.h
===================================================================
--- cell--common--6.orig/arch/powerpc/platforms/ps3pf/platform.h
+++ cell--common--6/arch/powerpc/platforms/ps3pf/platform.h
@@ -56,4 +56,12 @@
 int __init ps3pf_os_area_init(void);
 u64 ps3pf_os_area_rtc_diff(void);
 
+/* spu */
+
+#if defined(CONFIG_SPU_BASE)
+void ps3pf_spu_set_platform (void);
+#else
+static inline void ps3pf_spu_set_platform (void) {}
+#endif
+
 #endif
Index: cell--common--6/arch/powerpc/platforms/ps3pf/setup.c
===================================================================
--- cell--common--6.orig/arch/powerpc/platforms/ps3pf/setup.c
+++ cell--common--6/arch/powerpc/platforms/ps3pf/setup.c
@@ -76,6 +76,8 @@
 {
 	pr_debug(" -> %s:%d\n", __func__, __LINE__);
 
+	ps3pf_spu_set_platform();
+
 	ps3pf_map_htab();
 
 #if defined(CONFIG_BLK_DEV_INITRD)
Index: cell--common--6/arch/powerpc/platforms/ps3pf/spu.c
===================================================================
--- /dev/null
+++ cell--common--6/arch/powerpc/platforms/ps3pf/spu.c
@@ -0,0 +1,625 @@
+/*
+ * spu.c - PS3 Platform spu routines.
+ *
+ *  Copyright (C) 2006 Sony Computer Entertainment Inc.
+ *  Copyright 2006 Sony Corp.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#undef DEBUG
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/mmzone.h>
+#include <linux/io.h>
+#include <linux/mm.h>
+
+#include <asm/spu.h>
+#include <asm/spu_priv1.h>
+#include <asm/ps3pf.h>
+#include <asm/lv1call.h>
+
+/* spu_management_ops */
+
+/**
+ * enum spe_type - Type of spe to create.
+ * @spe_type_logical: Standard logical spe.
+ *
+ * For use with lv1_construct_logical_spe().  The current HV does not support
+ * any types other than those listed.
+ */
+
+enum spe_type {
+	spe_type_logical = 0,
+};
+
+/**
+ * struct spe_shadow - logical spe shadow register area.
+ *
+ * Read-only shadow of spe registers.
+ */
+
+struct spe_shadow {
+	u8 padding_0000[0x0140];
+	u64 int_status_class0_RW;       /* 0x0140 */
+	u64 int_status_class1_RW;       /* 0x0148 */
+	u64 int_status_class2_RW;       /* 0x0150 */
+	u8 padding_0158[0x0610-0x0158];
+	u64 mfc_dsisr_RW;               /* 0x0610 */
+	u8 padding_0618[0x0620-0x0618];
+	u64 mfc_dar_RW;                 /* 0x0620 */
+	u8 padding_0628[0x0800-0x0628];
+	u64 mfc_dsipr_R;                /* 0x0800 */
+	u8 padding_0808[0x0810-0x0808];
+	u64 mfc_lscrr_R;                /* 0x0810 */
+	u8 padding_0818[0x0c00-0x0818];
+	u64 mfc_cer_R;                  /* 0x0c00 */
+	u8 padding_0c08[0x0f00-0x0c08];
+	u64 spe_execution_status;       /* 0x0f00 */
+	u8 padding_0f08[0x1000-0x0f08];
+} __attribute__ ((packed));
+
+
+/**
+ * enum spe_ex_state - Logical spe execution state.
+ * @spe_ex_state_unexecutable: Uninitialized.
+ * @spe_ex_state_executable: Enabled, not ready.
+ * @spe_ex_state_executed: Ready for use.
+ *
+ * The execution state (status) of the logical spe as reported in
+ * struct spe_shadow:spe_execution_status.
+ */
+
+enum spe_ex_state {
+	spe_ex_state_unexecutable = 0,
+	spe_ex_state_executable = 2,
+	spe_ex_state_executed = 3,
+};
+
+/**
+ * struct priv1_cache - Cached values of priv1 registers.
+ * @masks[]: Array of cached spe interrupt masks, indexed by class.
+ * @sr1: Cached mfc_sr1 register.
+ * @tclass_id: Cached mfc_tclass_id register.
+ */
+
+struct priv1_cache {
+	u64 masks[3];
+	u64 sr1;
+	u64 tclass_id;
+};
+
+/**
+ * struct spu_pdata - Platform state variables.
+ * @spe_id: HV spe id returned by lv1_construct_logical_spe().
+ * @resource_id: HV spe resource id returned by
+ * 	ps3pf_repository_read_spe_resource_id().
+ * @priv2_addr: lpar address of spe priv2 area returned by
+ * 	lv1_construct_logical_spe().
+ * @shadow_addr: lpar address of spe register shadow area returned by
+ * 	lv1_construct_logical_spe().
+ * @shadow: Virtual (ioremap) address of spe register shadow area.
+ * @cache: Cached values of priv1 registers.
+ */
+
+struct spu_pdata {
+	u64 spe_id;
+	u64 resource_id;
+	u64 priv2_addr;
+	u64 shadow_addr;
+	struct spe_shadow __iomem *shadow;
+	struct priv1_cache cache;
+};
+
+static struct spu_pdata *spu_pdata(struct spu *spu)
+{
+	return spu->pdata;
+}
+
+#define dump_areas(_a, _b, _c, _d, _e) \
+	_dump_areas(_a, _b, _c, _d, _e, __func__, __LINE__)
+static void _dump_areas(unsigned int spe_id, unsigned long priv2,
+	unsigned long problem, unsigned long ls, unsigned long shadow,
+	const char* func, int line)
+{
+	pr_debug("%s:%d: spe_id:  %xh (%u)\n", func, line, spe_id, spe_id);
+	pr_debug("%s:%d: priv2:   %lxh\n", func, line, priv2);
+	pr_debug("%s:%d: problem: %lxh\n", func, line, problem);
+	pr_debug("%s:%d: ls:      %lxh\n", func, line, ls);
+	pr_debug("%s:%d: shadow:  %lxh\n", func, line, shadow);
+}
+
+static unsigned long get_vas_id(void)
+{
+	unsigned long id;
+
+	lv1_get_logical_ppe_id(&id);
+	lv1_get_virtual_address_space_id_of_ppe(id, &id);
+
+	return id;
+}
+
+static int __init construct_spu(struct spu *spu)
+{
+	int result;
+	unsigned long unused;
+
+	result = lv1_construct_logical_spe(PAGE_SHIFT, PAGE_SHIFT, PAGE_SHIFT,
+		PAGE_SHIFT, PAGE_SHIFT, get_vas_id(), spe_type_logical,
+		&spu_pdata(spu)->priv2_addr, &spu->problem_phys,
+		&spu->local_store_phys, &unused,
+		&spu_pdata(spu)->shadow_addr,
+		&spu_pdata(spu)->spe_id);
+
+	if (result) {
+		pr_debug("%s:%d: lv1_construct_logical_spe failed: %s\n",
+			__func__, __LINE__, ps3pf_result(result));
+		return result;
+	}
+
+	return result;
+}
+
+static int __init add_spu_pages(unsigned long start_addr, unsigned long size)
+{
+	int result;
+	unsigned long start_pfn;
+	unsigned long nr_pages;
+	struct pglist_data *pgdata;
+	struct zone *zone;
+
+	BUG_ON(!mem_init_done);
+
+	start_pfn = start_addr >> PAGE_SHIFT;
+	nr_pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
+
+	pgdata = NODE_DATA(0);
+	zone = pgdata->node_zones;
+
+	result = __add_pages(zone, start_pfn, nr_pages);
+
+	if (result)
+		pr_debug("%s:%d: __add_pages failed: (%d)\n",
+			__func__, __LINE__, result);
+
+	return result;
+}
+
+static void spu_unmap(struct spu *spu)
+{
+	iounmap(spu->priv2);
+	iounmap(spu->problem);
+	iounmap((__force u8 __iomem *)spu->local_store);
+	iounmap(spu_pdata(spu)->shadow);
+}
+
+static int __init setup_areas(struct spu *spu)
+{
+	struct table {char* name; unsigned long addr; unsigned long size;};
+	int result;
+
+	/* setup pages */
+
+	result = add_spu_pages(spu->local_store_phys, LS_SIZE);
+	if (result)
+		goto fail_add;
+
+	result = add_spu_pages(spu->problem_phys, sizeof(struct spu_problem));
+	if (result)
+		goto fail_add;
+
+	/* ioremap */
+
+	spu_pdata(spu)->shadow = __ioremap(
+		spu_pdata(spu)->shadow_addr, sizeof(struct spe_shadow),
+		PAGE_READONLY | _PAGE_NO_CACHE | _PAGE_GUARDED);
+	if (!spu_pdata(spu)->shadow) {
+		pr_debug("%s:%d: ioremap shadow failed\n", __func__, __LINE__);
+		goto fail_ioremap;
+	}
+
+	spu->local_store = ioremap(spu->local_store_phys, LS_SIZE);
+	if (!spu->local_store) {
+		pr_debug("%s:%d: ioremap local_store failed\n",
+			__func__, __LINE__);
+		goto fail_ioremap;
+	}
+
+	spu->problem = ioremap(spu->problem_phys,
+		sizeof(struct spu_problem));
+	if (!spu->problem) {
+		pr_debug("%s:%d: ioremap problem failed\n", __func__, __LINE__);
+		goto fail_ioremap;
+	}
+
+	spu->priv2 = ioremap(spu_pdata(spu)->priv2_addr,
+		sizeof(struct spu_priv2));
+	if (!spu->priv2) {
+		pr_debug("%s:%d: ioremap priv2 failed\n", __func__, __LINE__);
+		goto fail_ioremap;
+	}
+
+	dump_areas(spu_pdata(spu)->spe_id, spu_pdata(spu)->priv2_addr,
+		spu->problem_phys, spu->local_store_phys,
+		spu_pdata(spu)->shadow_addr);
+	dump_areas(spu_pdata(spu)->spe_id, (unsigned long)spu->priv2,
+		(unsigned long)spu->problem, (unsigned long)spu->local_store,
+		(unsigned long)spu_pdata(spu)->shadow);
+
+	return 0;
+
+fail_ioremap:
+	spu_unmap(spu);
+fail_add:
+	return result;
+}
+
+static int __init setup_interrupts(struct spu *spu)
+{
+	int result;
+
+	result = ps3pf_alloc_spe_irq(spu_pdata(spu)->spe_id, 0,
+		&spu->irqs[0]);
+
+	if (result)
+		goto fail_alloc_0;
+
+	result = ps3pf_alloc_spe_irq(spu_pdata(spu)->spe_id, 1,
+		&spu->irqs[1]);
+
+	if (result)
+		goto fail_alloc_1;
+
+	result = ps3pf_alloc_spe_irq(spu_pdata(spu)->spe_id, 2,
+		&spu->irqs[2]);
+
+	if (result)
+		goto fail_alloc_2;
+
+	return result;
+
+fail_alloc_2:
+	ps3pf_free_spe_irq(spu->irqs[1]);
+fail_alloc_1:
+	ps3pf_free_spe_irq(spu->irqs[0]);
+fail_alloc_0:
+	spu->irqs[0] = spu->irqs[1] = spu->irqs[2] = NO_IRQ;
+	return result;
+}
+
+static int __init enable_spu(struct spu *spu)
+{
+	int result;
+
+	result = lv1_enable_logical_spe(spu_pdata(spu)->spe_id,
+		spu_pdata(spu)->resource_id);
+
+	if (result) {
+		pr_debug("%s:%d: lv1_enable_logical_spe failed: %s\n",
+			__func__, __LINE__, ps3pf_result(result));
+		goto fail_enable;
+	}
+
+	result = setup_areas(spu);
+
+	if (result)
+		goto fail_areas;
+
+	result = setup_interrupts(spu);
+
+	if (result)
+		goto fail_interrupts;
+
+	return 0;
+
+fail_interrupts:
+	spu_unmap(spu);
+fail_areas:
+	lv1_disable_logical_spe(spu_pdata(spu)->spe_id, 0);
+fail_enable:
+	return result;
+}
+
+static int ps3pf_destroy_spu(struct spu *spu)
+{
+	int result;
+
+	pr_debug("%s:%d spu_%d\n", __func__, __LINE__, spu->number);
+
+	result = lv1_disable_logical_spe(spu_pdata(spu)->spe_id, 0);
+
+	if (result) {
+		pr_debug("%s:%d: lv1_disable_logical_spe failed: %s\n",
+			__func__, __LINE__, ps3pf_result(result));
+		BUG();
+	}
+
+	ps3pf_free_spe_irq(spu->irqs[2]);
+	ps3pf_free_spe_irq(spu->irqs[1]);
+	ps3pf_free_spe_irq(spu->irqs[0]);
+
+	spu->irqs[0] = spu->irqs[1] = spu->irqs[2] = NO_IRQ;
+
+	spu_unmap(spu);
+
+	result = lv1_destruct_logical_spe(spu_pdata(spu)->spe_id);
+
+	if (result) {
+		pr_debug("%s:%d: lv1_destruct_logical_spe failed: %s\n",
+			__func__, __LINE__, ps3pf_result(result));
+		BUG();
+	}
+
+	kfree(spu->pdata);
+	spu->pdata = NULL;
+
+	return 0;
+}
+
+static int __init ps3pf_create_spu(struct spu *spu, void *data)
+{
+	int result;
+
+	pr_debug("%s:%d spu_%d\n", __func__, __LINE__, spu->number);
+
+	spu->pdata = kzalloc(sizeof(struct spu_pdata),
+		GFP_KERNEL);
+
+	if (!spu->pdata) {
+		result = -ENOMEM;
+		goto fail_malloc;
+	}
+
+	spu_pdata(spu)->resource_id = (unsigned long)data;
+
+	/* Init cached reg values to HV defaults. */
+
+	spu_pdata(spu)->cache.sr1 = 0x33;
+
+	result = construct_spu(spu);
+
+	if (result)
+		goto fail_construct;
+
+	/* For now, just go ahead and enable it. */
+
+	result = enable_spu(spu);
+
+	if (result)
+		goto fail_enable;
+
+	/* Make sure the spu is in spe_ex_state_executed. */
+
+	/* need something better here!!! */
+	while (in_be64(&spu_pdata(spu)->shadow->spe_execution_status)
+		!= spe_ex_state_executed)
+		(void)0;
+
+	return result;
+
+fail_enable:
+fail_construct:
+	ps3pf_destroy_spu(spu);
+fail_malloc:
+	return result;
+}
+
+static int __init ps3pf_enumerate_spus(int (*fn)(void *data))
+{
+	int result;
+	unsigned int num_resource_id;
+	unsigned int i;
+
+	result = ps3pf_repository_read_num_spu_resource_id(&num_resource_id);
+
+	pr_debug("%s:%d: num_resource_id %u\n", __func__, __LINE__,
+		num_resource_id);
+
+	/*
+	 * For now, just create logical spus equal to the number
+	 * of physical spus reserved for the partition.
+	 */
+
+	for (i = 0; i < num_resource_id; i++) {
+		enum ps3pf_spu_resource_type resource_type;
+		unsigned int resource_id;
+
+		result = ps3pf_repository_read_spu_resource_id(i,
+			&resource_type, &resource_id);
+
+		if (result)
+			break;
+
+		if (resource_type == ps3pf_spu_resource_type_exclusive) {
+			result = fn((void*)(unsigned long)resource_id);
+
+			if (result)
+				break;
+		}
+	}
+
+	if (result)
+		printk(KERN_WARNING "%s:%d: Error initializing spus\n",
+			__func__, __LINE__);
+
+	return result;
+}
+
+const struct spu_management_ops spu_management_ps3pf_ops = {
+	.enumerate_spus = ps3pf_enumerate_spus,
+	.create_spu = ps3pf_create_spu,
+	.destroy_spu = ps3pf_destroy_spu,
+};
+
+/* spu_priv1_ops */
+
+static void int_mask_and(struct spu *spu, int class, u64 mask)
+{
+	u64 old_mask;
+
+	/* are these serialized by caller??? */
+	old_mask = spu_int_mask_get(spu, class);
+	spu_int_mask_set(spu, class, old_mask & mask);
+}
+
+static void int_mask_or(struct spu *spu, int class, u64 mask)
+{
+	u64 old_mask;
+
+	old_mask = spu_int_mask_get(spu, class);
+	spu_int_mask_set(spu, class, old_mask | mask);
+}
+
+static void int_mask_set(struct spu *spu, int class, u64 mask)
+{
+	spu_pdata(spu)->cache.masks[class] = mask;
+	lv1_set_spe_interrupt_mask(spu_pdata(spu)->spe_id, class,
+		spu_pdata(spu)->cache.masks[class]);
+}
+
+static u64 int_mask_get(struct spu *spu, int class)
+{
+	return spu_pdata(spu)->cache.masks[class];
+}
+
+static void int_stat_clear(struct spu *spu, int class, u64 stat)
+{
+	/* Note that MFC_DSISR will be cleared when class1[MF] is set. */
+
+	lv1_clear_spe_interrupt_status(spu_pdata(spu)->spe_id, class,
+		stat, 0);
+}
+
+static u64 int_stat_get(struct spu *spu, int class)
+{
+	u64 stat;
+
+	lv1_get_spe_interrupt_status(spu_pdata(spu)->spe_id, class, &stat);
+	return stat;
+}
+
+static void cpu_affinity_set(struct spu *spu, int cpu)
+{
+	/* No support. */
+}
+
+static u64 mfc_dar_get(struct spu *spu)
+{
+	return in_be64(&spu_pdata(spu)->shadow->mfc_dar_RW);
+}
+
+static void mfc_dsisr_set(struct spu *spu, u64 dsisr)
+{
+	/* Nothing to do, cleared in int_stat_clear(). */
+}
+
+static u64 mfc_dsisr_get(struct spu *spu)
+{
+	return in_be64(&spu_pdata(spu)->shadow->mfc_dsisr_RW);
+}
+
+static void mfc_sdr_setup(struct spu *spu)
+{
+	/* Nothing to do. */
+}
+
+static void mfc_sr1_set(struct spu *spu, u64 sr1)
+{
+	/* Check bits allowed by HV. */
+
+	static const u64 allowed = ~(MFC_STATE1_LOCAL_STORAGE_DECODE_MASK
+		| MFC_STATE1_PROBLEM_STATE_MASK);
+
+	BUG_ON((sr1 & allowed) != (spu_pdata(spu)->cache.sr1 & allowed));
+
+	spu_pdata(spu)->cache.sr1 = sr1;
+	lv1_set_spe_privilege_state_area_1_register(
+		spu_pdata(spu)->spe_id,
+		offsetof(struct spu_priv1, mfc_sr1_RW),
+		spu_pdata(spu)->cache.sr1);
+}
+
+static u64 mfc_sr1_get(struct spu *spu)
+{
+	return spu_pdata(spu)->cache.sr1;
+}
+
+static void mfc_tclass_id_set(struct spu *spu, u64 tclass_id)
+{
+	spu_pdata(spu)->cache.tclass_id = tclass_id;
+	lv1_set_spe_privilege_state_area_1_register(
+		spu_pdata(spu)->spe_id,
+		offsetof(struct spu_priv1, mfc_tclass_id_RW),
+		spu_pdata(spu)->cache.tclass_id);
+}
+
+static u64 mfc_tclass_id_get(struct spu *spu)
+{
+	return spu_pdata(spu)->cache.tclass_id;
+}
+
+static void tlb_invalidate(struct spu *spu)
+{
+	/* Nothing to do. */
+}
+
+static void resource_allocation_groupID_set(struct spu *spu, u64 id)
+{
+	/* No support. */
+}
+
+static u64 resource_allocation_groupID_get(struct spu *spu)
+{
+	return 0; /* No support. */
+}
+
+static void resource_allocation_enable_set(struct spu *spu, u64 enable)
+{
+	/* No support. */
+}
+
+static u64 resource_allocation_enable_get(struct spu *spu)
+{
+	return 0; /* No support. */
+}
+
+const struct spu_priv1_ops spu_priv1_ps3pf_ops = {
+	.int_mask_and = int_mask_and,
+	.int_mask_or = int_mask_or,
+	.int_mask_set = int_mask_set,
+	.int_mask_get = int_mask_get,
+	.int_stat_clear = int_stat_clear,
+	.int_stat_get = int_stat_get,
+	.cpu_affinity_set = cpu_affinity_set,
+	.mfc_dar_get = mfc_dar_get,
+	.mfc_dsisr_set = mfc_dsisr_set,
+	.mfc_dsisr_get = mfc_dsisr_get,
+	.mfc_sdr_setup = mfc_sdr_setup,
+	.mfc_sr1_set = mfc_sr1_set,
+	.mfc_sr1_get = mfc_sr1_get,
+	.mfc_tclass_id_set = mfc_tclass_id_set,
+	.mfc_tclass_id_get = mfc_tclass_id_get,
+	.tlb_invalidate = tlb_invalidate,
+	.resource_allocation_groupID_set = resource_allocation_groupID_set,
+	.resource_allocation_groupID_get = resource_allocation_groupID_get,
+	.resource_allocation_enable_set = resource_allocation_enable_set,
+	.resource_allocation_enable_get = resource_allocation_enable_get,
+};
+
+void ps3pf_spu_set_platform(void)
+{
+	spu_priv1_ops = &spu_priv1_ps3pf_ops;
+	spu_management_ops = &spu_management_ps3pf_ops;
+}

^ permalink raw reply

* [PATCH 14/16] powerpc: add ps3 platform OS params support
From: Geoff Levand @ 2006-11-10 20:03 UTC (permalink / raw)
  To: Paul Mackerras; +Cc: linuxppc-dev

Add support to access the parameter data from the ps3pf other OS area of flash
memory.  The parameter data mainly holds user preferences like static ip
address, etc.

Signed-off-by: Geoff Levand <geoffrey.levand@am.sony.com>

---
 arch/powerpc/platforms/ps3pf/Makefile  |    2 
 arch/powerpc/platforms/ps3pf/os-area.c |  245 +++++++++++++++++++++++++++++++++
 arch/powerpc/platforms/ps3pf/setup.c   |    1 
 3 files changed, 247 insertions(+), 1 deletion(-)

Index: cell--common--6/arch/powerpc/platforms/ps3pf/Makefile
===================================================================
--- cell--common--6.orig/arch/powerpc/platforms/ps3pf/Makefile
+++ cell--common--6/arch/powerpc/platforms/ps3pf/Makefile
@@ -1,2 +1,2 @@
 obj-$(CONFIG_PS3PF) += setup.o mm.o smp.o time.o hvcall.o htab.o repository.o
-obj-$(CONFIG_PS3PF) += interrupt.o exports.o
+obj-$(CONFIG_PS3PF) += interrupt.o exports.o os-area.o
Index: cell--common--6/arch/powerpc/platforms/ps3pf/os-area.c
===================================================================
--- /dev/null
+++ cell--common--6/arch/powerpc/platforms/ps3pf/os-area.c
@@ -0,0 +1,245 @@
+/*
+ * os-area.c - PS3 Platform OS data area.
+ *
+ *  Copyright (C) 2006 Sony Computer Entertainment Inc.
+ *  Copyright 2006 Sony Corp.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#undef DEBUG
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+
+#include <asm/lmb.h>
+#include <asm/ps3pf.h>
+
+#include "platform.h"
+
+enum {
+	segment_size = 0x200,
+	ldr_format_raw = 0,
+	ldr_format_gzip = 1,
+	boot_flag_game_os = 0,
+	boot_flag_other_os = 1,
+	av_multi_out_ntsc = 0,
+	av_multi_out_pal_rgb = 1,
+	av_multi_out_pal_ycbcr = 2,
+	av_multi_out_secam = 3,
+	ctrl_button_o_is_yes = 0,
+	ctrl_button_x_is_yes = 1,
+};
+
+/**
+ * struct header - os area header segment.
+ * @magic_num: Always 'cell_ext_os_area'.
+ * @hdr_version: Header format version number.
+ * @os_region_offset: Segment number of os region.
+ * @bootloader_offset: Segment number of bootloader image.
+ * @ldr_format: ldr_format flag.
+ * @ldr_size: Size of bootloader image in bytes.
+ */
+
+struct header {
+	s8 magic_num[16];
+	u32 hdr_version;
+	u32 os_image_offset;
+	u32 bootloader_offset;
+	u32 _reserved_1;
+	u32 ldr_format;
+	u32 ldr_size;
+	u32 _reserved_2[6];
+} __attribute__ ((packed));
+
+/**
+ * struct params - os area params segment.
+ * @boot_flag: User preference of operating system.
+ * @num_params: Number of params in this (params) segment.
+ * @rtc_diff: Difference in seconds between 1970 and the ps3pf rtc value.
+ * @av_multi_out: User preference of AV output.
+ * @ctrl_button: User preference of controller button config.
+ * @static_ip_addr: User preference of static IP address.
+ * @network_mask: User preference of static network mask.
+ * @default_gateway: User preference of static default gateway.
+ * @dns_primary: User preference of static primary dns server.
+ * @dns_secondary: User preference of static secondary dns server.
+ *
+ * User preference of zero for static_ip_addr means use dhcp.
+ */
+
+struct params {
+	u32 boot_flag;
+	u32 _reserved_1[3];
+	u32 num_params;
+	u32 _reserved_2[3];
+	/* param 0 */
+	s64 rtc_diff;
+	u8 av_multi_out;
+	u8 ctrl_button;
+	u8 _reserved_3[6];
+	/* param 1 */
+	u8 static_ip_addr[4];
+	u8 network_mask[4];
+	u8 default_gateway[4];
+	u8 _reserved_4[4];
+	/* param 2 */
+	u8 dns_primary[4];
+	u8 dns_secondary[4];
+	u8 _reserved_5[8];
+} __attribute__ ((packed));
+
+/**
+ * struct os_params - Static working copies of data from the os area.
+ *
+ * For the convinience of the guest, the HV makes a copy of the os area in
+ * flash to a high address in the boot memory region and then puts that RAM
+ * address and the byte count into the repository for retreval by the guest.
+ * We copy the data we want into a static variable and allow the memory setup
+ * by the HV to be claimed by the lmb manager.
+ */
+
+struct os_params {
+	/* param 0 */
+	s64 rtc_diff;
+	unsigned int av_multi_out;
+	unsigned int ctrl_button;
+	/* param 1 */
+	u8 static_ip_addr[4];
+	u8 network_mask[4];
+	u8 default_gateway[4];
+	/* param 2 */
+	u8 dns_primary[4];
+	u8 dns_secondary[4];
+} static os_params;
+
+#define dump_header(_a) _dump_header(_a, __func__, __LINE__)
+static void _dump_header(const struct header __iomem *h, const char* func,
+	int line)
+{
+	pr_debug("%s:%d: h.magic_num:         '%s'\n", func, line,
+		h->magic_num);
+	pr_debug("%s:%d: h.hdr_version:       %u\n", func, line,
+		h->hdr_version);
+	pr_debug("%s:%d: h.os_image_offset:   %u\n", func, line,
+		h->os_image_offset);
+	pr_debug("%s:%d: h.bootloader_offset: %u\n", func, line,
+		h->bootloader_offset);
+	pr_debug("%s:%d: h.ldr_format:        %u\n", func, line,
+		h->ldr_format);
+	pr_debug("%s:%d: h.ldr_size:          %xh\n", func, line,
+		h->ldr_size);
+}
+
+#define dump_params(_a) _dump_params(_a, __func__, __LINE__)
+static void _dump_params(const struct params __iomem *p, const char* func,
+	int line)
+{
+	pr_debug("%s:%d: p.boot_flag:       %u\n", func, line, p->boot_flag);
+	pr_debug("%s:%d: p.num_params:      %u\n", func, line, p->num_params);
+	pr_debug("%s:%d: p.rtc_diff         %ld\n", func, line, p->rtc_diff);
+	pr_debug("%s:%d: p.av_multi_out     %u\n", func, line, p->av_multi_out);
+	pr_debug("%s:%d: p.ctrl_button:     %u\n", func, line, p->ctrl_button);
+	pr_debug("%s:%d: p.static_ip_addr:  %u.%u.%u.%u\n", func, line,
+		p->static_ip_addr[0], p->static_ip_addr[1],
+		p->static_ip_addr[2], p->static_ip_addr[3]);
+	pr_debug("%s:%d: p.network_mask:    %u.%u.%u.%u\n", func, line,
+		p->network_mask[0], p->network_mask[1],
+		p->network_mask[2], p->network_mask[3]);
+	pr_debug("%s:%d: p.default_gateway: %u.%u.%u.%u\n", func, line,
+		p->default_gateway[0], p->default_gateway[1],
+		p->default_gateway[2], p->default_gateway[3]);
+	pr_debug("%s:%d: p.dns_primary:     %u.%u.%u.%u\n", func, line,
+		p->dns_primary[0], p->dns_primary[1],
+		p->dns_primary[2], p->dns_primary[3]);
+	pr_debug("%s:%d: p.dns_secondary:   %u.%u.%u.%u\n", func, line,
+		p->dns_secondary[0], p->dns_secondary[1],
+		p->dns_secondary[2], p->dns_secondary[3]);
+}
+
+static int __init verify_header(const struct header *header)
+{
+	if (memcmp(header->magic_num, "cell_ext_os_area", 16)) {
+		pr_debug("%s:%d magic_num failed\n", __func__, __LINE__);
+		return -1;
+	}
+
+	if (header->hdr_version != 1) {
+		pr_debug("%s:%d hdr_version failed\n", __func__, __LINE__);
+		return -1;
+	}
+
+	if (header->os_image_offset > header->bootloader_offset) {
+		pr_debug("%s:%d offsets failed\n", __func__, __LINE__);
+		return -1;
+	}
+
+	return 0;
+}
+
+int __init ps3pf_os_area_init(void)
+{
+	int result;
+	u64 lpar_addr;
+	unsigned int size;
+	struct header *header;
+	struct params *params;
+
+	result = ps3pf_repository_read_boot_dat_info(&lpar_addr, &size);
+
+	if (result) {
+		pr_debug("%s:%d ps3pf_repository_read_boot_dat_info failed\n",
+			__func__, __LINE__);
+		return result;
+	}
+
+	header = (struct header *)__va(lpar_addr);
+	params = (struct params *)__va(lpar_addr + segment_size);
+
+	result = verify_header(header);
+
+	if (result) {
+		pr_debug("%s:%d verify_header failed\n", __func__, __LINE__);
+		dump_header(header);
+		return -EIO;
+	}
+
+	dump_header(header);
+	dump_params(params);
+
+	os_params.rtc_diff = params->rtc_diff;
+	os_params.av_multi_out = params->av_multi_out;
+	if (0) { /* currently not used */
+		os_params.ctrl_button = params->ctrl_button;
+		memcpy(os_params.static_ip_addr, params->static_ip_addr, 4);
+		memcpy(os_params.network_mask, params->network_mask, 4);
+		memcpy(os_params.default_gateway, params->default_gateway, 4);
+		memcpy(os_params.dns_secondary, params->dns_secondary, 4);
+	}
+
+	return result;
+}
+
+/**
+ * ps3pf_os_area_rtc_diff - Returns the ps3pf rtc diff value.
+ *
+ * The ps3pf rtc maintains a value that approximates seconds since
+ * 2000-01-01 00:00:00 UTC.  Returns the exact number of seconds from 1970 to
+ * 2000 when os_params.rtc_diff has not been properly set up.
+ */
+
+u64 ps3pf_os_area_rtc_diff(void)
+{
+	return os_params.rtc_diff ? os_params.rtc_diff : 946684800UL;
+}
Index: cell--common--6/arch/powerpc/platforms/ps3pf/setup.c
===================================================================
--- cell--common--6.orig/arch/powerpc/platforms/ps3pf/setup.c
+++ cell--common--6/arch/powerpc/platforms/ps3pf/setup.c
@@ -115,6 +115,7 @@
 
 	powerpc_firmware_features |= FW_FEATURE_LPAR;
 
+	ps3pf_os_area_init();
 	ps3pf_mm_init();
 	ps3pf_mm_vas_create(&htab_size);
 	ps3pf_hpte_init(htab_size);

^ permalink raw reply

* [PATCH 13/16] powerpc: add ps3 platform lpar addressing
From: Geoff Levand @ 2006-11-10 20:03 UTC (permalink / raw)
  To: Paul Mackerras; +Cc: linuxppc-dev

Adds some needed bits for a config option PS3PF_USE_LPAR_ADDR that disables
the ps3pf lpar address translation mechanism.  This is a currently needed
workaround for limitations in the design of the spu support.

Signed-off-by: Geoff Levand <geoffrey.levand@am.sony.com>

---
 arch/powerpc/platforms/ps3pf/Kconfig |   11 +++++++++++
 include/asm-powerpc/sparsemem.h      |    6 ++++++
 2 files changed, 17 insertions(+)

Index: cell--common--6/arch/powerpc/platforms/ps3pf/Kconfig
===================================================================
--- cell--common--6.orig/arch/powerpc/platforms/ps3pf/Kconfig
+++ cell--common--6/arch/powerpc/platforms/ps3pf/Kconfig
@@ -29,4 +29,15 @@
 	  This support is mainly for Linux kernel development.  If unsure,
 	  say N.
 
+config PS3PF_USE_LPAR_ADDR
+	depends on PS3PF && EXPERIMENTAL
+	bool "PS3 Platform use lpar address space"
+	default y
+	help
+	  This option is solely for experimentation by experts.  Disables
+	  translation of lpar addresses.  SPE support currently won't work
+	  without this set to y.
+
+	  If you have any doubt, choose the default y.
+
 endmenu
Index: cell--common--6/include/asm-powerpc/sparsemem.h
===================================================================
--- cell--common--6.orig/include/asm-powerpc/sparsemem.h
+++ cell--common--6/include/asm-powerpc/sparsemem.h
@@ -9,8 +9,14 @@
  * MAX_PHYSMEM_BITS		2^N: how much memory we can have in that space
  */
 #define SECTION_SIZE_BITS       24
+
+#if defined(CONFIG_PS3PF_USE_LPAR_ADDR)
+#define MAX_PHYSADDR_BITS       47
+#define MAX_PHYSMEM_BITS        47
+#else
 #define MAX_PHYSADDR_BITS       44
 #define MAX_PHYSMEM_BITS        44
+#endif
 
 #ifdef CONFIG_MEMORY_HOTPLUG
 extern void create_section_mapping(unsigned long start, unsigned long end);

^ permalink raw reply

* [PATCH 12/16] powerpc: add ps3 platform interrupt support
From: Geoff Levand @ 2006-11-10 20:03 UTC (permalink / raw)
  To: Paul Mackerras; +Cc: linuxppc-dev

Adds routines to interface with the ps3pf interrupt services.

Signed-off-by: Geoff Levand <geoffrey.levand@am.sony.com>

---
 arch/powerpc/platforms/ps3pf/interrupt.c |  583 +++++++++++++++++++++++++++++++
 1 file changed, 583 insertions(+)

Index: cell--common--6/arch/powerpc/platforms/ps3pf/interrupt.c
===================================================================
--- /dev/null
+++ cell--common--6/arch/powerpc/platforms/ps3pf/interrupt.c
@@ -0,0 +1,583 @@
+/*
+ * interrupt.c - PS3 Platform interrupt routines.
+ *
+ *  Copyright (C) 2006 Sony Computer Entertainment Inc.
+ *  Copyright 2006 Sony Corp.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#define DEBUG 1
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/irq.h>
+
+#include <asm/machdep.h>
+#include <asm/udbg.h>
+#include <asm/ps3pf.h>
+#include <asm/lv1call.h>
+
+#include "platform.h"
+
+#if defined(DEBUG)
+#undef pr_debug
+#define pr_debug(fmt...) udbg_printf(fmt)
+#endif
+
+unsigned long ps3pf_legacy_virq_to_outlet(unsigned int virq)
+{
+	return virq_to_hw(virq);
+}
+
+/**
+ * ps3pf_alloc_io_irq - Assign a virq to a system bus device.
+ * interrupt_id: The device interrupt id read from the system repository.
+ * @virq: The assigned Linux virq.
+ *
+ * An io irq represents a non-virtualized device interrupt.  interrupt_id
+ * coresponds to the interrupt number of the interrupt controller.
+ */
+
+int ps3pf_alloc_io_irq(unsigned int interrupt_id, unsigned int *virq)
+{
+	int result;
+	unsigned long outlet;
+
+	result = lv1_construct_io_irq_outlet(interrupt_id, &outlet);
+
+	if (result) {
+		pr_debug("%s:%d: lv1_construct_io_irq_outlet failed: %s\n",
+			__func__, __LINE__, ps3pf_result(result));
+		return result;
+	}
+
+	*virq = irq_create_mapping(NULL, outlet);
+
+	pr_debug("%s:%d: interrupt_id %u => outlet %lu, virq %u\n",
+		__func__, __LINE__, interrupt_id, outlet, *virq);
+
+	return 0;
+}
+
+int ps3pf_free_io_irq(unsigned int virq)
+{
+	int result;
+
+	result = lv1_destruct_io_irq_outlet(virq_to_hw(virq));
+
+	if (!result)
+		pr_debug("%s:%d: lv1_destruct_io_irq_outlet failed: %s\n",
+			__func__, __LINE__, ps3pf_result(result));
+
+	irq_dispose_mapping(virq);
+
+	return result;
+}
+
+/**
+ * ps3pf_alloc_event_irq - Allocate a virq for use with a system event.
+ * @virq: The assigned Linux virq.
+ *
+ * The virq can be used with lv1_connect_interrupt_event_receive_port() to
+ * arrange to receive events, or with ps3pf_send_event_locally() to signal
+ * events.
+ */
+
+int ps3pf_alloc_event_irq(unsigned int *virq)
+{
+	int result;
+	unsigned long outlet;
+
+	result = lv1_construct_event_receive_port(&outlet);
+
+	if (result) {
+		pr_debug("%s:%d: lv1_construct_event_receive_port failed: %s\n",
+			__func__, __LINE__, ps3pf_result(result));
+		*virq = NO_IRQ;
+		return result;
+	}
+
+	*virq = irq_create_mapping(NULL, outlet);
+
+	pr_debug("%s:%d: outlet %lu, virq %u\n", __func__, __LINE__, outlet,
+		*virq);
+
+	return 0;
+}
+
+int ps3pf_free_event_irq(unsigned int virq)
+{
+	int result;
+
+	pr_debug(" -> %s:%d\n", __func__, __LINE__);
+
+	result = lv1_destruct_event_receive_port(virq_to_hw(virq));
+
+	if (result)
+		pr_debug("%s:%d: lv1_destruct_event_receive_port failed: %s\n",
+			__func__, __LINE__, ps3pf_result(result));
+
+	irq_dispose_mapping(virq);
+
+	pr_debug(" <- %s:%d\n", __func__, __LINE__);
+	return result;
+}
+
+int ps3pf_send_event_locally(unsigned int virq)
+{
+	return lv1_send_event_locally(virq_to_hw(virq));
+}
+
+/**
+ * ps3pf_connect_event_irq - Assign a virq to a system bus device.
+ * @did: The HV device identifier read from the system repository.
+ * @interrupt_id: The device interrupt id read from the system repository.
+ * @virq: The assigned Linux virq.
+ *
+ * An event irq represents a virtual device interrupt.  The interrupt_id
+ * coresponds to the software interrupt number.
+ */
+
+int ps3pf_connect_event_irq(const struct ps3pf_device_id *did,
+	unsigned int interrupt_id, unsigned int *virq)
+{
+	int result;
+
+	result = ps3pf_alloc_event_irq(virq);
+
+	if (result)
+		return result;
+
+	result = lv1_connect_interrupt_event_receive_port(did->bus_id,
+		did->dev_id, virq_to_hw(*virq), interrupt_id);
+
+	if (result) {
+		pr_debug("%s:%d: lv1_connect_interrupt_event_receive_port"
+			" failed: %s\n", __func__, __LINE__,
+			ps3pf_result(result));
+		ps3pf_free_event_irq(*virq);
+		*virq = NO_IRQ;
+		return result;
+	}
+
+	pr_debug("%s:%d: interrupt_id %u, virq %u\n", __func__, __LINE__,
+		interrupt_id, *virq);
+
+	return 0;
+}
+
+int ps3pf_disconnect_event_irq(const struct ps3pf_device_id *did,
+	unsigned int interrupt_id, unsigned int virq)
+{
+	int result;
+
+	pr_debug(" -> %s:%d: interrupt_id %u, virq %u\n", __func__, __LINE__,
+		interrupt_id, virq);
+
+	result = lv1_disconnect_interrupt_event_receive_port(did->bus_id,
+		did->dev_id, virq_to_hw(virq), interrupt_id);
+
+	if (result)
+		pr_debug("%s:%d: lv1_disconnect_interrupt_event_receive_port"
+			" failed: %s\n", __func__, __LINE__,
+			ps3pf_result(result));
+
+	ps3pf_free_event_irq(virq);
+
+	pr_debug(" <- %s:%d\n", __func__, __LINE__);
+	return result;
+}
+
+/**
+ * ps3pf_alloc_vuart_irq - Configure the system virtual uart virq.
+ * @virt_addr_bmp: The caller supplied virtual uart interrupt bitmap.
+ * @virq: The assigned Linux virq.
+ *
+ * The system supports only a single virtual uart, so multiple calls without
+ * freeing the interrupt will return a wrong state error.
+ */
+
+int ps3pf_alloc_vuart_irq(void* virt_addr_bmp, unsigned int *virq)
+{
+	int result;
+	unsigned long outlet;
+	unsigned long lpar_addr;
+
+	BUG_ON(!is_kernel_addr((unsigned long)virt_addr_bmp));
+
+	lpar_addr = ps3pf_mm_phys_to_lpar(__pa(virt_addr_bmp));
+
+	result = lv1_configure_virtual_uart_irq(lpar_addr, &outlet);
+
+	if (result) {
+		pr_debug("%s:%d: lv1_configure_virtual_uart_irq failed: %s\n",
+			__func__, __LINE__, ps3pf_result(result));
+		return result;
+	}
+
+	*virq = irq_create_mapping(NULL, outlet);
+
+	pr_debug("%s:%d: outlet %lu, virq %u\n", __func__, __LINE__,
+		outlet, *virq);
+
+	return 0;
+}
+
+int ps3pf_free_vuart_irq(unsigned int virq)
+{
+	int result;
+
+	result = lv1_deconfigure_virtual_uart_irq();
+
+	if (result) {
+		pr_debug("%s:%d: lv1_configure_virtual_uart_irq failed: %s\n",
+			__func__, __LINE__, ps3pf_result(result));
+		return result;
+	}
+
+	irq_dispose_mapping(virq);
+
+	return result;
+}
+
+/**
+ * ps3pf_alloc_spe_irq - Configure an spe virq.
+ * @spe_id: The spe_id returned from lv1_construct_logical_spe().
+ * @class: The spe interrupt class {0,1,2}.
+ * @virq: The assigned Linux virq.
+ *
+ */
+
+int ps3pf_alloc_spe_irq(unsigned long spe_id, unsigned int class,
+	unsigned int *virq)
+{
+	int result;
+	unsigned long outlet;
+
+	BUG_ON(class > 2);
+
+	result = lv1_get_spe_irq_outlet(spe_id, class, &outlet);
+
+	if (result) {
+		pr_debug("%s:%d: lv1_get_spe_irq_outlet failed: %s\n",
+			__func__, __LINE__, ps3pf_result(result));
+		return result;
+	}
+
+	*virq = irq_create_mapping(NULL, outlet);
+
+	pr_debug("%s:%d: spe_id %lu, class %u, outlet %lu, virq %u\n",
+		__func__, __LINE__, spe_id, class, outlet, *virq);
+
+	return 0;
+}
+
+int ps3pf_free_spe_irq(unsigned int virq)
+{
+	irq_dispose_mapping(virq);
+	return 0;
+}
+
+#define PS3PF_INVALID_OUTLET ((irq_hw_number_t)-1)
+#define PS3PF_PLUG_MAX 63
+
+/**
+ * struct bmp - a per cpu irq status and mask bitmap structure
+ * @status: 256 bit status bitmap indexed by plug
+ * @unused_1:
+ * @mask: 256 bit mask bitmap indexed by plug
+ * @unused_2:
+ * @lock:
+ * @ipi_debug_brk_mask:
+ *
+ * The HV mantains per SMT thread mappings of HV outlet to HV plug on
+ * behalf of the guest.  These mappings are implemented as 256 bit guest
+ * supplied bitmaps indexed by plug number.  The address of the bitmaps are
+ * registered with the HV through lv1_configure_irq_state_bitmap().
+ *
+ * The HV supports 256 plugs per thread, assigned as {0..255}, for a total
+ * of 512 plugs supported on a processor.  To simplify the logic this
+ * implementation equates HV plug value to linux virq value, constrains each
+ * interrupt to have a system wide unique plug number, and limits the range
+ * of the plug values to map into the first dword of the bitmaps.  This
+ * gives a usable range of plug values of  {NUM_ISA_INTERRUPTS..63}.  Note
+ * that there is no constraint on how many in this set an individual thread
+ * can aquire.
+ */
+
+struct bmp {
+	struct {
+		unsigned long status;
+		unsigned long unused_1[3];
+		unsigned long mask;
+		unsigned long unused_2[3];
+	} __attribute__ ((packed));
+	spinlock_t lock;
+	unsigned long ipi_debug_brk_mask;
+};
+
+/**
+ * struct private - a per cpu data structure
+ * @node: HV node id
+ * @cpu: HV thread id
+ * @bmp: an HV bmp structure
+ */
+
+struct private {
+	unsigned long node;
+	unsigned int cpu;
+	struct bmp bmp;
+};
+
+#if defined(DEBUG)
+static void _dump_64_bmp(const char *header, const unsigned long *p, unsigned cpu,
+	const char* func, int line)
+{
+	pr_debug("%s:%d: %s %u {%04lx_%04lx_%04lx_%04lx}\n",
+		func, line, header, cpu,
+		*p >> 48, (*p >> 32) & 0xffff, (*p >> 16) & 0xffff,
+		*p & 0xffff);
+}
+
+static void _dump_256_bmp(const char *header, const unsigned long *p, unsigned cpu,
+	const char* func, int line)
+{
+	pr_debug("%s:%d: %s %u {%016lx:%016lx:%016lx:%016lx}\n",
+		func, line, header, cpu, p[0], p[1], p[2], p[3]);
+}
+
+#define dump_bmp(_x) _dump_bmp(_x, __func__, __LINE__)
+static void _dump_bmp(struct private* pd, const char* func, int line)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&pd->bmp.lock, flags);
+	_dump_64_bmp("stat", &pd->bmp.status, pd->cpu, func, line);
+	_dump_64_bmp("mask", &pd->bmp.mask, pd->cpu, func, line);
+	spin_unlock_irqrestore(&pd->bmp.lock, flags);
+}
+
+#define dump_mask(_x) _dump_mask(_x, __func__, __LINE__)
+static void _dump_mask(struct private* pd, const char* func, int line)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&pd->bmp.lock, flags);
+	_dump_64_bmp("mask", &pd->bmp.mask, pd->cpu, func, line);
+	spin_unlock_irqrestore(&pd->bmp.lock, flags);
+}
+#else
+static void dump_bmp(struct private* pd) {};
+static void dump_mask(struct private* pd) {};
+#endif /* defined(DEBUG) */
+
+static void chip_mask(unsigned int virq)
+{
+	unsigned long flags;
+	struct private *pd = get_irq_chip_data(virq);
+
+	pr_debug("%s:%d: cpu %u, virq %d\n", __func__, __LINE__, pd->cpu, virq);
+
+	BUG_ON(virq < NUM_ISA_INTERRUPTS);
+	BUG_ON(virq > PS3PF_PLUG_MAX);
+
+	spin_lock_irqsave(&pd->bmp.lock, flags);
+	pd->bmp.mask &= ~(0x8000000000000000UL >> virq);
+	spin_unlock_irqrestore(&pd->bmp.lock, flags);
+
+	lv1_did_update_interrupt_mask(pd->node, pd->cpu);
+}
+
+static void chip_unmask(unsigned int virq)
+{
+	unsigned long flags;
+	struct private *pd = get_irq_chip_data(virq);
+
+	pr_debug("%s:%d: cpu %u, virq %d\n", __func__, __LINE__, pd->cpu, virq);
+
+	BUG_ON(virq < NUM_ISA_INTERRUPTS);
+	BUG_ON(virq > PS3PF_PLUG_MAX);
+
+	spin_lock_irqsave(&pd->bmp.lock, flags);
+	pd->bmp.mask |= (0x8000000000000000UL >> virq);
+	spin_unlock_irqrestore(&pd->bmp.lock, flags);
+
+	lv1_did_update_interrupt_mask(pd->node, pd->cpu);
+}
+
+static void chip_eoi(unsigned int virq)
+{
+	lv1_end_of_interrupt(virq);
+}
+
+static struct irq_chip irq_chip = {
+	.name = "ps3pf",
+	.mask = chip_mask,
+	.unmask = chip_unmask,
+	.eoi = chip_eoi,
+};
+
+static void host_unmap(struct irq_host *h, unsigned int virq)
+{
+	int result;
+
+	pr_debug("%s:%d: virq %d\n", __func__, __LINE__, virq);
+
+	lv1_disconnect_irq_plug(virq);
+
+	result = set_irq_chip_data(virq, NULL);
+	BUG_ON(result);
+}
+
+static DEFINE_PER_CPU(struct private, private);
+
+static int host_map(struct irq_host *h, unsigned int virq,
+	irq_hw_number_t hwirq)
+{
+	int result;
+	unsigned int cpu;
+
+	pr_debug(" -> %s:%d\n", __func__, __LINE__);
+	pr_debug("%s:%d: hwirq %lu => virq %u\n", __func__, __LINE__, hwirq,
+		virq);
+
+	/* bind this virq to a cpu */
+
+	preempt_disable();
+	cpu = smp_processor_id();
+	result = lv1_connect_irq_plug(virq, hwirq);
+	preempt_enable();
+
+	if (result) {
+		pr_info("%s:%d: lv1_connect_irq_plug failed:"
+			" %s\n", __func__, __LINE__, ps3pf_result(result));
+		return -EPERM;
+	}
+
+	result = set_irq_chip_data(virq, &per_cpu(private, cpu));
+	BUG_ON(result);
+
+	set_irq_chip_and_handler(virq, &irq_chip, handle_fasteoi_irq);
+
+	pr_debug(" <- %s:%d\n", __func__, __LINE__);
+	return result;
+}
+
+static struct irq_host_ops host_ops = {
+	.map = host_map,
+	.unmap = host_unmap,
+};
+
+void __init ps3pf_register_ipi_debug_brk(unsigned int cpu, unsigned int virq)
+{
+	struct private *pd = &per_cpu(private, cpu);
+
+	pd->bmp.ipi_debug_brk_mask = 0x8000000000000000UL >> virq;
+
+	pr_debug("%s:%d: cpu %u, virq %u, mask %lxh\n", __func__, __LINE__,
+		cpu, virq, pd->bmp.ipi_debug_brk_mask);
+}
+
+static int bmp_get_and_clear_status_bit(struct bmp *m)
+{
+	unsigned long flags;
+	unsigned int bit;
+	unsigned long x;
+
+	spin_lock_irqsave(&m->lock, flags);
+
+	/* check for ipi break first to stop this cpu ASAP */
+
+	if (m->status & m->ipi_debug_brk_mask) {
+		m->status &= ~m->ipi_debug_brk_mask;
+		spin_unlock_irqrestore(&m->lock, flags);
+		return __ilog2(m->ipi_debug_brk_mask);
+	}
+
+	x = (m->status & m->mask);
+
+	for (bit = NUM_ISA_INTERRUPTS, x <<= bit; x; bit++, x <<= 1)
+		if (x & 0x8000000000000000UL) {
+			m->status &= ~(0x8000000000000000UL >> bit);
+			spin_unlock_irqrestore(&m->lock, flags);
+			return bit;
+		}
+
+	spin_unlock_irqrestore(&m->lock, flags);
+
+	pr_debug("%s:%d: not found\n", __func__, __LINE__);
+	return -1;
+}
+
+unsigned int ps3pf_get_irq(void)
+{
+	int plug;
+
+	struct private *pd = &__get_cpu_var(private);
+
+	plug = bmp_get_and_clear_status_bit(&pd->bmp);
+
+	if (plug < 1) {
+		pr_debug("%s:%d: no plug found: cpu %u\n", __func__, __LINE__,
+			pd->cpu);
+		dump_bmp(&per_cpu(private, 0));
+		dump_bmp(&per_cpu(private, 1));
+		return NO_IRQ;
+	}
+
+#if defined(DEBUG)
+	if (plug < NUM_ISA_INTERRUPTS || plug > PS3PF_PLUG_MAX) {
+		pr_debug("%s:%d: bad plug number: %u \n", __func__, __LINE__,
+			plug);
+		dump_bmp(&per_cpu(private, 0));
+		dump_bmp(&per_cpu(private, 1));
+		BUG();
+	}
+#endif
+	return plug;
+}
+
+void __init ps3pf_init_IRQ(void)
+{
+	int result;
+	unsigned long node;
+	unsigned cpu;
+	struct irq_host *host;
+
+	lv1_get_logical_ppe_id(&node);
+
+	host = irq_alloc_host(IRQ_HOST_MAP_NOMAP, 0, &host_ops,
+		PS3PF_INVALID_OUTLET);
+	irq_set_default_host(host);
+	irq_set_virq_count(PS3PF_PLUG_MAX + 1);
+
+	for_each_possible_cpu(cpu) {
+		struct private *pd = &per_cpu(private, cpu);
+
+		pd->node = node;
+		pd->cpu = cpu;
+		spin_lock_init(&pd->bmp.lock);
+
+		result = lv1_configure_irq_state_bitmap(node, cpu,
+			ps3pf_mm_phys_to_lpar(__pa(&pd->bmp.status)));
+
+		if (result)
+			pr_debug("%s:%d: lv1_configure_irq_state_bitmap failed:"
+				" %s\n", __func__, __LINE__,
+				ps3pf_result(result));
+	}
+
+	ppc_md.get_irq = ps3pf_get_irq;
+}

^ permalink raw reply

* [PATCH 11/16] powerpc: add ps3 platform repository support
From: Geoff Levand @ 2006-11-10 20:03 UTC (permalink / raw)
  To: Paul Mackerras; +Cc: linuxppc-dev

This adds support for the PS3 Platform repository.


Signed-off-by: Geoff Levand <geoffrey.levand@am.sony.com>

---
 arch/powerpc/platforms/ps3pf/repository.c |  842 ++++++++++++++++++++++++++++++
 include/asm-powerpc/ps3pf.h               |  139 ++++
 2 files changed, 981 insertions(+)

Index: cell--common--6/arch/powerpc/platforms/ps3pf/repository.c
===================================================================
--- /dev/null
+++ cell--common--6/arch/powerpc/platforms/ps3pf/repository.c
@@ -0,0 +1,842 @@
+/*
+ * repository.c - PS3 Platform repository routines.
+ *
+ *  Copyright (C) 2006 Sony Computer Entertainment Inc.
+ *  Copyright 2006 Sony Corp.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#undef DEBUG
+
+#include <asm/ps3pf.h>
+#include <asm/lv1call.h>
+
+enum ps3pf_vendor_id {
+	ps3pf_vendor_id_none = 0,
+	ps3pf_vendor_id_sony = 0x8000000000000000UL,
+};
+
+enum ps3pf_lpar_id {
+	ps3pf_lpar_id_current = 0,
+	ps3pf_lpar_id_pme = 1,
+};
+
+#define dump_field(_a, _b) _dump_field(_a, _b, __func__, __LINE__)
+static void _dump_field(const char *hdr, u64 n, const char* func, int line)
+{
+#if defined(DEBUG)
+	char s[16];
+	const char *const in = (const char *)&n;
+	unsigned int i;
+
+	for (i = 0; i < 8; i++)
+		s[i] = (in[i] <= 126 && in[i] >= 32) ? in[i] : '.';
+	s[i] = 0;
+
+	pr_debug("%s:%d: %s%016lx : %s\n", func, line, hdr, n, s);
+#endif
+}
+
+#define dump_node_name(_a, _b, _c, _d, _e) \
+	_dump_node_name(_a, _b, _c, _d, _e, __func__, __LINE__)
+static void _dump_node_name (unsigned int lpar_id, u64 n1, u64 n2, u64 n3,
+	u64 n4, const char* func, int line)
+{
+	pr_debug("%s:%d: lpar: %u\n", func, line, lpar_id);
+	_dump_field("n1: ", n1, func, line);
+	_dump_field("n2: ", n2, func, line);
+	_dump_field("n3: ", n3, func, line);
+	_dump_field("n4: ", n4, func, line);
+}
+
+#define dump_node(_a, _b, _c, _d, _e, _f, _g) \
+	_dump_node(_a, _b, _c, _d, _e, _f, _g, __func__, __LINE__)
+static void _dump_node(unsigned int lpar_id, u64 n1, u64 n2, u64 n3, u64 n4,
+	u64 v1, u64 v2, const char* func, int line)
+{
+	pr_debug("%s:%d: lpar: %u\n", func, line, lpar_id);
+	_dump_field("n1: ", n1, func, line);
+	_dump_field("n2: ", n2, func, line);
+	_dump_field("n3: ", n3, func, line);
+	_dump_field("n4: ", n4, func, line);
+	pr_debug("%s:%d: v1: %016lx\n", func, line, v1);
+	pr_debug("%s:%d: v2: %016lx\n", func, line, v2);
+}
+
+/**
+ * make_first_field - Make the first field of a repository node name.
+ * @text: Text portion of the field.
+ * @index: Numeric index portion of the field.  Use zero for 'don't care'.
+ *
+ * This routine sets the vendor id to zero (non-vendor specific).
+ * Returns field value.
+ */
+
+static u64 make_first_field(const char *text, u64 index)
+{
+	u64 n;
+
+	strncpy((char *)&n, text, 8);
+	return ps3pf_vendor_id_none + (n >> 32) + index;
+}
+
+/**
+ * make_field - Make subsequent fields of a repository node name.
+ * @text: Text portion of the field.  Use "" for 'don't care'.
+ * @index: Numeric index portion of the field.  Use zero for 'don't care'.
+ *
+ * Returns field value.
+ */
+
+static u64 make_field(const char *text, u64 index)
+{
+	u64 n;
+
+	strncpy((char *)&n, text, 8);
+	return n + index;
+}
+
+/**
+ * read_node - Read a repository node from raw fields.
+ * @n1: First field of node name.
+ * @n2: Second field of node name.  Use zero for 'don't care'.
+ * @n3: Third field of node name.  Use zero for 'don't care'.
+ * @n4: Fourth field of node name.  Use zero for 'don't care'.
+ * @v1: First repository value (high word).
+ * @v2: Second repository value (low word).  Optional parameter, use zero
+ *      for 'don't care'.
+ */
+
+static int read_node(unsigned int lpar_id, u64 n1, u64 n2, u64 n3, u64 n4,
+	u64 *_v1, u64 *_v2)
+{
+	int result;
+	u64 v1;
+	u64 v2;
+
+	if (lpar_id == ps3pf_lpar_id_current) {
+		u64 id;
+		lv1_get_logical_partition_id(&id);
+		lpar_id = id;
+	}
+
+	result = lv1_get_repository_node_value(lpar_id, n1, n2, n3, n4, &v1,
+		&v2);
+
+	if (result) {
+		pr_debug("%s:%d: lv1_get_repository_node_value failed: %s\n",
+			__func__, __LINE__, ps3pf_result(result));
+		dump_node_name(lpar_id, n1, n2, n3, n4);
+		return result;
+	}
+
+	dump_node(lpar_id, n1, n2, n3, n4, v1, v2);
+
+	if (_v1)
+		*_v1 = v1;
+	if (_v2)
+		*_v2 = v2;
+
+	if (v1 && !_v1)
+		pr_debug("%s:%d: warning: discarding non-zero v1: %016lx\n",
+			__func__, __LINE__, v1);
+	if (v2 && !_v2)
+		pr_debug("%s:%d: warning: discarding non-zero v2: %016lx\n",
+			__func__, __LINE__, v2);
+
+	return result;
+}
+
+int ps3pf_repository_read_bus_str(unsigned int bus_index, const char *bus_str,
+	u64 *value)
+{
+	return read_node(ps3pf_lpar_id_pme,
+		make_first_field("bus", bus_index),
+		make_field(bus_str, 0),
+		0, 0,
+		value, 0);
+}
+
+int ps3pf_repository_read_bus_id(unsigned int bus_index, unsigned int *bus_id)
+{
+	int result;
+	u64 v1;
+	u64 v2; /* unused */
+
+	result = read_node(ps3pf_lpar_id_pme,
+		make_first_field("bus", bus_index),
+		make_field("id", 0),
+		0, 0,
+		&v1, &v2);
+	*bus_id = v1;
+	return result;
+}
+
+int ps3pf_repository_read_bus_type(unsigned int bus_index,
+	enum ps3pf_bus_type *bus_type)
+{
+	int result;
+	u64 v1;
+
+	result = read_node(ps3pf_lpar_id_pme,
+		make_first_field("bus", bus_index),
+		make_field("type", 0),
+		0, 0,
+		&v1, 0);
+	*bus_type = v1;
+	return result;
+}
+
+int ps3pf_repository_read_bus_num_dev(unsigned int bus_index,
+	unsigned int *num_dev)
+{
+	int result;
+	u64 v1;
+
+	result = read_node(ps3pf_lpar_id_pme,
+		make_first_field("bus", bus_index),
+		make_field("num_dev", 0),
+		0, 0,
+		&v1, 0);
+	*num_dev = v1;
+	return result;
+}
+
+int ps3pf_repository_read_dev_str(unsigned int bus_index,
+	unsigned int dev_index, const char *dev_str, u64 *value)
+{
+	return read_node(ps3pf_lpar_id_pme,
+		make_first_field("bus", bus_index),
+		make_field("dev", dev_index),
+		make_field(dev_str, 0),
+		0,
+		value, 0);
+}
+
+int ps3pf_repository_read_dev_id(unsigned int bus_index, unsigned int dev_index,
+	unsigned int *dev_id)
+{
+	int result;
+	u64 v1;
+
+	result = read_node(ps3pf_lpar_id_pme,
+		make_first_field("bus", bus_index),
+		make_field("dev", dev_index),
+		make_field("id", 0),
+		0,
+		&v1, 0);
+	*dev_id = v1;
+	return result;
+}
+
+int ps3pf_repository_read_dev_type(unsigned int bus_index,
+	unsigned int dev_index, enum ps3pf_dev_type *dev_type)
+{
+	int result;
+	u64 v1;
+
+	result = read_node(ps3pf_lpar_id_pme,
+		make_first_field("bus", bus_index),
+		make_field("dev", dev_index),
+		make_field("type", 0),
+		0,
+		&v1, 0);
+	*dev_type = v1;
+	return result;
+}
+
+int ps3pf_repository_read_dev_intr(unsigned int bus_index,
+	unsigned int dev_index, unsigned int intr_index,
+	unsigned int *intr_type, unsigned int* interrupt_id)
+{
+	int result;
+	u64 v1;
+	u64 v2;
+
+	result = read_node(ps3pf_lpar_id_pme,
+		make_first_field("bus", bus_index),
+		make_field("dev", dev_index),
+		make_field("intr", intr_index),
+		0,
+		&v1, &v2);
+	*intr_type = v1;
+	*interrupt_id = v2;
+	return result;
+}
+
+int ps3pf_repository_read_dev_reg_type(unsigned int bus_index,
+	unsigned int dev_index, unsigned int reg_index, unsigned int *reg_type)
+{
+	int result;
+	u64 v1;
+
+	result = read_node(ps3pf_lpar_id_pme,
+		make_first_field("bus", bus_index),
+		make_field("dev", dev_index),
+		make_field("reg", reg_index),
+		make_field("type", 0),
+		&v1, 0);
+	*reg_type = v1;
+	return result;
+}
+
+int ps3pf_repository_read_dev_reg_addr(unsigned int bus_index,
+	unsigned int dev_index, unsigned int reg_index, u64 *bus_addr, u64 *len)
+{
+	return read_node(ps3pf_lpar_id_pme,
+		make_first_field("bus", bus_index),
+		make_field("dev", dev_index),
+		make_field("reg", reg_index),
+		make_field("data", 0),
+		bus_addr, len);
+}
+
+int ps3pf_repository_read_dev_reg(unsigned int bus_index,
+	unsigned int dev_index, unsigned int reg_index, unsigned int *reg_type,
+	u64 *bus_addr, u64 *len)
+{
+	int result = ps3pf_repository_read_dev_reg_type(bus_index, dev_index,
+		reg_index, reg_type);
+	return result ? result
+		: ps3pf_repository_read_dev_reg_addr(bus_index, dev_index,
+		reg_index, bus_addr, len);
+}
+
+#if defined(DEBUG)
+int ps3pf_repository_dump_resource_info(unsigned int bus_index,
+	unsigned int dev_index)
+{
+	int result = 0;
+	unsigned int res_index;
+
+	pr_debug(" -> %s:%d: (%u:%u)\n", __func__, __LINE__,
+		bus_index, dev_index);
+
+	for (res_index = 0; res_index < 10; res_index++) {
+		enum ps3pf_interrupt_type intr_type;
+		unsigned int interrupt_id;
+
+		result = ps3pf_repository_read_dev_intr(bus_index, dev_index,
+			res_index, &intr_type, &interrupt_id);
+
+		if (result) {
+			if (result !=  LV1_NO_ENTRY)
+				pr_debug("%s:%d ps3pf_repository_read_dev_intr"
+					" (%u:%u) failed\n", __func__, __LINE__,
+					bus_index, dev_index);
+			break;
+		}
+
+		pr_debug("%s:%d (%u:%u) intr_type %u, interrupt_id %u\n",
+			__func__, __LINE__, bus_index, dev_index, intr_type,
+			interrupt_id);
+	}
+
+	for (res_index = 0; res_index < 10; res_index++) {
+		enum ps3pf_region_type reg_type;
+		u64 bus_addr;
+		u64 len;
+
+		result = ps3pf_repository_read_dev_reg(bus_index, dev_index,
+			res_index, &reg_type, &bus_addr, &len);
+
+		if (result) {
+			if (result !=  LV1_NO_ENTRY)
+				pr_debug("%s:%d ps3pf_repository_read_dev_reg"
+					" (%u:%u) failed\n", __func__, __LINE__,
+					bus_index, dev_index);
+			break;
+		}
+
+		pr_debug("%s:%d (%u:%u) reg_type %u, bus_addr %lxh, len %lxh\n",
+			__func__, __LINE__, bus_index, dev_index, reg_type,
+			bus_addr, len);
+	}
+
+	pr_debug(" <- %s:%d\n", __func__, __LINE__);
+	return result;
+}
+
+static int dump_device_info(unsigned int bus_index, unsigned int num_dev)
+{
+	int result = 0;
+	unsigned int dev_index;
+
+	pr_debug(" -> %s:%d: bus_%u\n", __func__, __LINE__, bus_index);
+
+	for (dev_index = 0; dev_index < num_dev; dev_index++) {
+		enum ps3pf_dev_type dev_type;
+		unsigned int dev_id;
+
+		result = ps3pf_repository_read_dev_type(bus_index, dev_index,
+			&dev_type);
+
+		if (result) {
+			pr_debug("%s:%d ps3pf_repository_read_dev_type"
+				" (%u:%u) failed\n", __func__, __LINE__,
+				bus_index, dev_index);
+			break;
+		}
+
+		result = ps3pf_repository_read_dev_id(bus_index, dev_index,
+			&dev_id);
+
+		if (result) {
+			pr_debug("%s:%d ps3pf_repository_read_dev_id"
+				" (%u:%u) failed\n", __func__, __LINE__,
+				bus_index, dev_index);
+			continue;
+		}
+
+		pr_debug("%s:%d  (%u:%u): dev_type %u, dev_id %u\n", __func__,
+			__LINE__, bus_index, dev_index, dev_type, dev_id);
+
+		ps3pf_repository_dump_resource_info(bus_index, dev_index);
+	}
+
+	pr_debug(" <- %s:%d\n", __func__, __LINE__);
+	return result;
+}
+
+int ps3pf_repository_dump_bus_info(void)
+{
+	int result = 0;
+	unsigned int bus_index;
+
+	pr_debug(" -> %s:%d\n", __func__, __LINE__);
+
+	for (bus_index = 0; bus_index < 10; bus_index++) {
+		enum ps3pf_bus_type bus_type;
+		unsigned int bus_id;
+		unsigned int num_dev;
+
+		result = ps3pf_repository_read_bus_type(bus_index, &bus_type);
+
+		if (result) {
+			pr_debug("%s:%d read_bus_type(%u) failed\n",
+				__func__, __LINE__, bus_index);
+			break;
+		}
+
+		result = ps3pf_repository_read_bus_id(bus_index, &bus_id);
+
+		if (result) {
+			pr_debug("%s:%d read_bus_id(%u) failed\n",
+				__func__, __LINE__, bus_index);
+			continue;
+		}
+
+		if (bus_index != bus_id)
+			pr_debug("%s:%d bus_index != bus_id\n",
+				__func__, __LINE__);
+
+		result = ps3pf_repository_read_bus_num_dev(bus_index, &num_dev);
+
+		if (result) {
+			pr_debug("%s:%d read_bus_num_dev(%u) failed\n",
+				__func__, __LINE__, bus_index);
+			continue;
+		}
+
+		pr_debug("%s:%d bus_%u: bus_type %u, bus_id %u, num_dev %u\n",
+			__func__, __LINE__, bus_index, bus_type, bus_id,
+			num_dev);
+
+		dump_device_info(bus_index, num_dev);
+	}
+
+	pr_debug(" <- %s:%d\n", __func__, __LINE__);
+	return result;
+}
+#endif /* defined(DEBUG) */
+
+static int find_device(unsigned int bus_index, unsigned int num_dev,
+	unsigned int start_dev_index, enum ps3pf_dev_type dev_type,
+	struct ps3pf_repository_device *dev)
+{
+	int result = 0;
+	unsigned int dev_index;
+
+	pr_debug("%s:%d: find dev_type %u\n", __func__, __LINE__, dev_type);
+
+	dev->dev_index = UINT_MAX;
+
+	for (dev_index = start_dev_index; dev_index < num_dev; dev_index++) {
+		enum ps3pf_dev_type x;
+
+		result = ps3pf_repository_read_dev_type(bus_index, dev_index,
+			&x);
+
+		if (result) {
+			pr_debug("%s:%d read_dev_type failed\n",
+				__func__, __LINE__);
+			return result;
+		}
+
+		if (x == dev_type)
+			break;
+	}
+
+	BUG_ON(dev_index == num_dev);
+
+	pr_debug("%s:%d: found dev_type %u at dev_index %u\n",
+		__func__, __LINE__, dev_type, dev_index);
+
+	result = ps3pf_repository_read_dev_id(bus_index, dev_index,
+		&dev->did.dev_id);
+
+	if (result) {
+		pr_debug("%s:%d read_dev_id failed\n",
+			__func__, __LINE__);
+		return result;
+	}
+
+	dev->dev_index = dev_index;
+
+	pr_debug("%s:%d found: dev_id %u\n", __func__, __LINE__,
+		dev->did.dev_id);
+
+	return result;
+}
+
+int ps3pf_repository_find_device (enum ps3pf_bus_type bus_type,
+	enum ps3pf_dev_type dev_type,
+	const struct ps3pf_repository_device *start_dev,
+	struct ps3pf_repository_device *dev)
+{
+	int result = 0;
+	unsigned int bus_index;
+	unsigned int num_dev;
+
+	pr_debug("%s:%d: find bus_type %u, dev_type %u\n", __func__, __LINE__,
+		bus_type, dev_type);
+
+	dev->bus_index = UINT_MAX;
+
+	for (bus_index = start_dev ? start_dev->bus_index : 0; bus_index < 10;
+		bus_index++) {
+		enum ps3pf_bus_type x;
+
+		result = ps3pf_repository_read_bus_type(bus_index, &x);
+
+		if (result) {
+			pr_debug("%s:%d read_bus_type failed\n",
+				__func__, __LINE__);
+			return result;
+		}
+		if (x == bus_type)
+			break;
+	}
+
+	BUG_ON(bus_index == 10);
+
+	pr_debug("%s:%d: found bus_type %u at bus_index %u\n",
+		__func__, __LINE__, bus_type, bus_index);
+
+	result = ps3pf_repository_read_bus_num_dev(bus_index, &num_dev);
+
+	if (result) {
+		pr_debug("%s:%d read_bus_num_dev failed\n",
+			__func__, __LINE__);
+		return result;
+	}
+
+	result = find_device(bus_index, num_dev, start_dev
+		? start_dev->dev_index + 1 : 0, dev_type, dev);
+
+	if (result) {
+		pr_debug("%s:%d get_did failed\n", __func__, __LINE__);
+		return result;
+	}
+
+	result = ps3pf_repository_read_bus_id(bus_index, &dev->did.bus_id);
+
+	if (result) {
+		pr_debug("%s:%d read_bus_id failed\n",
+			__func__, __LINE__);
+		return result;
+	}
+
+	dev->bus_index = bus_index;
+
+	pr_debug("%s:%d found: bus_id %u, dev_id %u\n",
+		__func__, __LINE__, dev->did.bus_id, dev->did.dev_id);
+
+	return result;
+}
+
+int ps3pf_repository_find_interrupt(const struct ps3pf_repository_device *dev,
+	enum ps3pf_interrupt_type intr_type, unsigned int *interrupt_id)
+{
+	int result = 0;
+	unsigned int res_index;
+
+	pr_debug("%s:%d: find intr_type %u\n", __func__, __LINE__, intr_type);
+
+	*interrupt_id = UINT_MAX;
+
+	for (res_index = 0; res_index < 10; res_index++) {
+		enum ps3pf_interrupt_type t;
+		unsigned int id;
+
+		result = ps3pf_repository_read_dev_intr(dev->bus_index,
+			dev->dev_index, res_index, &t, &id);
+
+		if (result) {
+			pr_debug("%s:%d read_dev_intr failed\n",
+				__func__, __LINE__);
+			return result;
+		}
+
+		if (t == intr_type) {
+			*interrupt_id = id;
+			break;
+		}
+	}
+
+	BUG_ON(res_index == 10);
+
+	pr_debug("%s:%d: found intr_type %u at res_index %u\n",
+		__func__, __LINE__, intr_type, res_index);
+
+	return result;
+}
+
+int ps3pf_repository_find_region(const struct ps3pf_repository_device *dev,
+	enum ps3pf_region_type reg_type, u64 *bus_addr, u64 *len)
+{
+	int result = 0;
+	unsigned int res_index;
+
+	pr_debug("%s:%d: find reg_type %u\n", __func__, __LINE__, reg_type);
+
+	*bus_addr = *len = 0;
+
+	for (res_index = 0; res_index < 10; res_index++) {
+		enum ps3pf_region_type t;
+		u64 a;
+		u64 l;
+
+		result = ps3pf_repository_read_dev_reg(dev->bus_index,
+			dev->dev_index, res_index, &t, &a, &l);
+
+		if (result) {
+			pr_debug("%s:%d read_dev_reg failed\n",
+				__func__, __LINE__);
+			return result;
+		}
+
+		if (t == reg_type) {
+			*bus_addr = a;
+			*len = l;
+			break;
+		}
+	}
+
+	BUG_ON(res_index == 10);
+
+	pr_debug("%s:%d: found reg_type %u at res_index %u\n",
+		__func__, __LINE__, reg_type, res_index);
+
+	return result;
+}
+
+int ps3pf_repository_read_rm_size(unsigned int ppe_id, u64 *rm_size)
+{
+	return read_node(ps3pf_lpar_id_current,
+		make_first_field("bi", 0),
+		make_field("pu", 0),
+		ppe_id,
+		make_field("rm_size", 0),
+		rm_size, 0);
+}
+
+int ps3pf_repository_read_region_total(u64 *region_total)
+{
+	return read_node(ps3pf_lpar_id_current,
+		make_first_field("bi", 0),
+		make_field("rgntotal", 0),
+		0, 0,
+		region_total, 0);
+}
+
+/**
+ * ps3pf_repository_read_mm_info - Read mm info for single pu system.
+ * @rm_base: Real mode memory base address.
+ * @rm_size: Real mode memory size.
+ * @region_total: Maximum memory region size.
+ */
+
+int ps3pf_repository_read_mm_info(u64 *rm_base, u64 *rm_size, u64 *region_total)
+{
+	int result;
+	u64 ppe_id;
+
+	lv1_get_logical_ppe_id(&ppe_id);
+	*rm_base = 0;
+	result = ps3pf_repository_read_rm_size(ppe_id, rm_size);
+	return result ? result
+		: ps3pf_repository_read_region_total(region_total);
+}
+
+/**
+ * ps3pf_repository_read_num_spu_reserved - Number of physical spus reserved.
+ * @num_spu: Number of physical spus.
+ */
+
+int ps3pf_repository_read_num_spu_reserved(unsigned int *num_spu_reserved)
+{
+	int result;
+	u64 v1;
+
+	result = read_node(ps3pf_lpar_id_current,
+		make_first_field("bi", 0),
+		make_field("spun", 0),
+		0, 0,
+		&v1, 0);
+	*num_spu_reserved = v1;
+	return result;
+}
+
+/**
+ * ps3pf_repository_read_num_spu_resource_id - Number of spu resource reservations.
+ * @num_resource_id: Number of spu resource ids.
+ */
+
+int ps3pf_repository_read_num_spu_resource_id(unsigned int *num_resource_id)
+{
+	int result;
+	u64 v1;
+
+	result = read_node(ps3pf_lpar_id_current,
+		make_first_field("bi", 0),
+		make_field("spursvn", 0),
+		0, 0,
+		&v1, 0);
+	*num_resource_id = v1;
+	return result;
+}
+
+/**
+ * ps3pf_repository_read_spu_resource_id - spu resource reservation id value.
+ * @res_index: Resource reservation index.
+ * @resource_type: Resource reservation type.
+ * @resource_id: Resource reservation id.
+ */
+
+int ps3pf_repository_read_spu_resource_id(unsigned int res_index,
+	enum ps3pf_spu_resource_type* resource_type, unsigned int *resource_id)
+{
+	int result;
+	u64 v1;
+	u64 v2;
+
+	result = read_node(ps3pf_lpar_id_current,
+		make_first_field("bi", 0),
+		make_field("spursv", 0),
+		res_index,
+		0,
+		&v1, &v2);
+	*resource_type = v1;
+	*resource_id = v2;
+	return result;
+}
+
+int ps3pf_repository_read_boot_dat_address(u64 *address)
+{
+	return read_node(ps3pf_lpar_id_current,
+		make_first_field("bi", 0),
+		make_field("boot_dat", 0),
+		make_field("address", 0),
+		0,
+		address, 0);
+}
+
+int ps3pf_repository_read_boot_dat_size(unsigned int *size)
+{
+	int result;
+	u64 v1;
+
+	result = read_node(ps3pf_lpar_id_current,
+		make_first_field("bi", 0),
+		make_field("boot_dat", 0),
+		make_field("size", 0),
+		0,
+		&v1, 0);
+	*size = v1;
+	return result;
+}
+
+/**
+  * ps3pf_repository_read_boot_dat_info - Get address and size of cell_ext_os_area.
+  * address: lpar address of cell_ext_os_area
+  * @size: size of cell_ext_os_area
+  */
+
+int ps3pf_repository_read_boot_dat_info(u64 *lpar_addr, unsigned int *size)
+{
+	int result;
+
+	*size = 0;
+	result = ps3pf_repository_read_boot_dat_address(lpar_addr);
+	return result ? result
+		: ps3pf_repository_read_boot_dat_size(size);
+}
+
+int ps3pf_repository_read_num_be(unsigned int *num_be)
+{
+	int result;
+	u64 v1;
+
+	result = read_node(ps3pf_lpar_id_pme,
+		make_first_field("ben", 0),
+		0,
+		0,
+		0,
+		&v1, 0);
+	*num_be = v1;
+	return result;
+}
+
+int ps3pf_repository_read_be_node_id(unsigned int be_index, u64 *node_id)
+{
+	return read_node(ps3pf_lpar_id_pme,
+		make_first_field("be", be_index),
+		0,
+		0,
+		0,
+		node_id, 0);
+}
+
+int ps3pf_repository_read_tb_freq(u64 node_id, u64 *tb_freq)
+{
+	return read_node(ps3pf_lpar_id_pme,
+		make_first_field("be", 0),
+		node_id,
+		make_field("clock", 0),
+		0,
+		tb_freq, 0);
+}
+
+int ps3pf_repository_read_be_tb_freq(unsigned int be_index, u64 *tb_freq)
+{
+	int result;
+	u64 node_id;
+
+	*tb_freq = 0;
+	result = ps3pf_repository_read_be_node_id(0, &node_id);
+	return result ? result
+		: ps3pf_repository_read_tb_freq(node_id, tb_freq);
+}
Index: cell--common--6/include/asm-powerpc/ps3pf.h
===================================================================
--- cell--common--6.orig/include/asm-powerpc/ps3pf.h
+++ cell--common--6/include/asm-powerpc/ps3pf.h
@@ -237,4 +237,143 @@
 #endif
 }
 
+/* repository bus info */
+
+enum ps3pf_bus_type {
+	ps3pf_bus_type_sb = 4,
+	ps3pf_bus_type_storage = 5,
+};
+
+enum ps3pf_dev_type {
+	ps3pf_dev_type_sb_gelic = 3,
+	ps3pf_dev_type_sb_usb = 4,
+	ps3pf_dev_type_sb_gpio = 6,
+};
+
+int ps3pf_repository_read_bus_str(unsigned int bus_index, const char *bus_str,
+	u64 *value);
+int ps3pf_repository_read_bus_id(unsigned int bus_index, unsigned int *bus_id);
+int ps3pf_repository_read_bus_type(unsigned int bus_index,
+	enum ps3pf_bus_type *bus_type);
+int ps3pf_repository_read_bus_num_dev(unsigned int bus_index,
+	unsigned int *num_dev);
+
+/* repository bus device info */
+
+enum ps3pf_interrupt_type {
+	ps3pf_interrupt_type_event_port = 2,
+	ps3pf_interrupt_type_sb_ohci = 3,
+	ps3pf_interrupt_type_sb_ehci = 4,
+	ps3pf_interrupt_type_other = 5,
+};
+
+enum ps3pf_region_type {
+	ps3pf_region_type_sb_ohci = 3,
+	ps3pf_region_type_sb_ehci = 4,
+	ps3pf_region_type_sb_gpio = 5,
+};
+
+int ps3pf_repository_read_dev_str(unsigned int bus_index,
+	unsigned int dev_index, const char *dev_str, u64 *value);
+int ps3pf_repository_read_dev_id(unsigned int bus_index, unsigned int dev_index,
+	unsigned int *dev_id);
+int ps3pf_repository_read_dev_type(unsigned int bus_index,
+	unsigned int dev_index, enum ps3pf_dev_type *dev_type);
+int ps3pf_repository_read_dev_intr(unsigned int bus_index,
+	unsigned int dev_index, unsigned int intr_index,
+	enum ps3pf_interrupt_type *intr_type, unsigned int *interrupt_id);
+int ps3pf_repository_read_dev_reg_type(unsigned int bus_index,
+	unsigned int dev_index, unsigned int reg_index,
+	enum ps3pf_region_type *reg_type);
+int ps3pf_repository_read_dev_reg_addr(unsigned int bus_index,
+	unsigned int dev_index, unsigned int reg_index, u64 *bus_addr,
+	u64 *len);
+int ps3pf_repository_read_dev_reg(unsigned int bus_index,
+	unsigned int dev_index, unsigned int reg_index,
+	enum ps3pf_region_type *reg_type, u64 *bus_addr, u64 *len);
+
+/* repository bus enumerators */
+
+struct ps3pf_repository_device {
+	unsigned int bus_index;
+	unsigned int dev_index;
+	struct ps3pf_device_id did;
+};
+
+int ps3pf_repository_find_device(enum ps3pf_bus_type bus_type,
+	enum ps3pf_dev_type dev_type,
+	const struct ps3pf_repository_device *start_dev,
+	struct ps3pf_repository_device *dev);
+static inline int ps3pf_repository_find_first_device(
+	enum ps3pf_bus_type bus_type, enum ps3pf_dev_type dev_type,
+	struct ps3pf_repository_device *dev)
+{
+	return ps3pf_repository_find_device(bus_type, dev_type, NULL, dev);
+}
+int ps3pf_repository_find_interrupt(const struct ps3pf_repository_device *dev,
+	enum ps3pf_interrupt_type intr_type, unsigned int *interrupt_id);
+int ps3pf_repository_find_region(const struct ps3pf_repository_device *dev,
+	enum ps3pf_region_type reg_type, u64 *bus_addr, u64 *len);
+
+/* repository block device info */
+
+int ps3pf_repository_read_dev_port(unsigned int bus_index,
+	unsigned int dev_index, u64 *port);
+int ps3pf_repository_read_dev_blk_size(unsigned int bus_index,
+	unsigned int dev_index, u64 *blk_size);
+int ps3pf_repository_read_dev_num_blocks(unsigned int bus_index,
+	unsigned int dev_index, u64 *num_blocks);
+int ps3pf_repository_read_dev_num_regions(unsigned int bus_index,
+	unsigned int dev_index, unsigned int *num_regions);
+int ps3pf_repository_read_dev_region_id(unsigned int bus_index,
+	unsigned int dev_index, unsigned int region_index,
+	unsigned int *region_id);
+int ps3pf_repository_read_dev_region_size(unsigned int bus_index,
+	unsigned int dev_index,	unsigned int region_index, u64 *region_size);
+int ps3pf_repository_read_dev_region_start(unsigned int bus_index,
+	unsigned int dev_index, unsigned int region_index, u64 *region_start);
+
+/* repository pu and memory info */
+
+int ps3pf_repository_read_num_pu(unsigned int *num_pu);
+int ps3pf_repository_read_ppe_id(unsigned int *pu_index, unsigned int *ppe_id);
+int ps3pf_repository_read_rm_base(unsigned int ppe_id, u64 *rm_base);
+int ps3pf_repository_read_rm_size(unsigned int ppe_id, u64 *rm_size);
+int ps3pf_repository_read_region_total(u64 *region_total);
+int ps3pf_repository_read_mm_info(u64 *rm_base, u64 *rm_size,
+	u64 *region_total);
+
+/* repository pme info */
+
+int ps3pf_repository_read_num_be(unsigned int *num_be);
+int ps3pf_repository_read_be_node_id(unsigned int be_index, u64 *node_id);
+int ps3pf_repository_read_tb_freq(u64 node_id, u64 *tb_freq);
+int ps3pf_repository_read_be_tb_freq(unsigned int be_index, u64 *tb_freq);
+
+/* repository other OS area */
+
+int ps3pf_repository_read_boot_dat_addr(u64 *lpar_addr);
+int ps3pf_repository_read_boot_dat_size(unsigned int *size);
+int ps3pf_repository_read_boot_dat_info(u64 *lpar_addr, unsigned int *size);
+
+/* repository spu info */
+
+/**
+ * enum spu_resource_type - Type of spu resource.
+ * @spu_resource_type_shared: Logical spu is shared with other partions.
+ * @spu_resource_type_exclusive: Logical spu is not shared with other partions.
+ *
+ * Returned by ps3pf_repository_read_spu_resource_id().
+ */
+
+enum ps3pf_spu_resource_type {
+	ps3pf_spu_resource_type_shared = 0,
+	ps3pf_spu_resource_type_exclusive = 0x8000000000000000UL,
+};
+
+int ps3pf_repository_read_num_spu_reserved(unsigned int *num_spu_reserved);
+int ps3pf_repository_read_num_spu_resource_id(unsigned int *num_resource_id);
+int ps3pf_repository_read_spu_resource_id(unsigned int res_index,
+	enum ps3pf_spu_resource_type* resource_type, unsigned int *resource_id);
+
 #endif

^ permalink raw reply

* [PATCH 10/16] powerpc: add ps3 platform htab routines
From: Geoff Levand @ 2006-11-10 20:02 UTC (permalink / raw)
  To: Paul Mackerras; +Cc: linuxppc-dev

Adds the pagetable management routines for the PS3 Platform.

Signed-off-by: Geoff Levand <geoffrey.levand@am.sony.com>

---
 arch/powerpc/platforms/ps3pf/htab.c |  288 ++++++++++++++++++++++++++++++++++++
 1 file changed, 288 insertions(+)

Index: cell--common--6/arch/powerpc/platforms/ps3pf/htab.c
===================================================================
--- /dev/null
+++ cell--common--6/arch/powerpc/platforms/ps3pf/htab.c
@@ -0,0 +1,288 @@
+/*
+ * htab.c - PS3 Platform pagetable management.
+ *
+ *  Copyright (C) 2006 Sony Computer Entertainment Inc.
+ *  Copyright 2006 Sony Corp.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#undef DEBUG
+
+#include <linux/kernel.h>
+
+#include <asm/machdep.h>
+#include <asm/lmb.h>
+#include <asm/udbg.h>
+#include <asm/ps3pf.h>
+#include <asm/lv1call.h>
+
+#include "platform.h"
+
+#if defined(DEBUG)
+#undef pr_debug
+#define pr_debug(fmt...) udbg_printf(fmt)
+#endif
+
+static hpte_t *htab;
+static unsigned long htab_addr;
+static unsigned char *bolttab;
+static unsigned char *inusetab;
+
+static spinlock_t ps3pf_bolttab_lock = SPIN_LOCK_UNLOCKED;
+
+#define debug_dump_hpte(_a, _b, _c, _d, _e, _f, _g) \
+	_debug_dump_hpte(_a, _b, _c, _d, _e, _f, _g, __func__, __LINE__)
+static void _debug_dump_hpte(unsigned long pa, unsigned long va,
+	unsigned long group, unsigned long bitmap, hpte_t lhpte, int psize,
+	unsigned long slot, const char* func, int line)
+{
+	pr_debug("%s:%d: pa     = %lxh\n", func, line, pa);
+	pr_debug("%s:%d: lpar   = %lxh\n", func, line,
+		ps3pf_mm_phys_to_lpar(pa));
+	pr_debug("%s:%d: va     = %lxh\n", func, line, va);
+	pr_debug("%s:%d: group  = %lxh\n", func, line, group);
+	pr_debug("%s:%d: bitmap = %lxh\n", func, line, bitmap);
+	pr_debug("%s:%d: hpte.v = %lxh\n", func, line, lhpte.v);
+	pr_debug("%s:%d: hpte.r = %lxh\n", func, line, lhpte.r);
+	pr_debug("%s:%d: psize  = %xh\n", func, line, psize);
+	pr_debug("%s:%d: slot   = %lxh\n", func, line, slot);
+}
+
+static long ps3pf_hpte_insert(unsigned long hpte_group, unsigned long va,
+	unsigned long pa, unsigned long rflags, unsigned long vflags, int psize)
+{
+	unsigned long slot;
+	hpte_t lhpte;
+	int secondary = 0;
+	unsigned long result;
+	unsigned long bitmap;
+	unsigned long flags;
+	unsigned long p_pteg, s_pteg, b_index, b_mask, cb, ci;
+
+	vflags &= ~HPTE_V_SECONDARY; /* this bit is ignored */
+
+	lhpte.v = hpte_encode_v(va, psize) | vflags | HPTE_V_VALID;
+	lhpte.r = hpte_encode_r(ps3pf_mm_phys_to_lpar(pa), psize) | rflags;
+
+	p_pteg = hpte_group / HPTES_PER_GROUP;
+	s_pteg = ~p_pteg & htab_hash_mask;
+
+	spin_lock_irqsave(&ps3pf_bolttab_lock, flags);
+
+	if (bolttab[p_pteg] == 0xff && bolttab[s_pteg] == 0xff) {
+		pr_debug("%s:%d: htab full\n", __func__, __LINE__);
+		BUG();
+	}
+
+	bitmap = (inusetab[p_pteg] << 8) | inusetab[s_pteg];
+
+	if (bitmap == 0xffff) {
+		/*
+		 * PTEG is full. Search for victim.
+		 */
+		bitmap &= ~((bolttab[p_pteg] << 8) | bolttab[s_pteg]);
+		do {
+			ci = mftb() & 15;
+			cb = 0x8000UL >> ci;
+		} while ((cb & bitmap) == 0);
+	} else {
+		/*
+		 * search free slot in hardware order
+		 *	[primary]	0, 2, 4, 6, 1, 3, 5, 7
+		 *	[secondary]	0, 2, 4, 6, 1, 3, 5, 7
+		 */
+		for (ci = 0; ci < HPTES_PER_GROUP; ci += 2) {
+			cb = 0x8000UL >> ci;
+			if ((cb & bitmap) == 0)
+				goto found;
+		}
+		for (ci = 1; ci < HPTES_PER_GROUP; ci += 2) {
+			cb = 0x8000UL >> ci;
+			if ((cb & bitmap) == 0)
+				goto found;
+		}
+		for (ci = HPTES_PER_GROUP; ci < HPTES_PER_GROUP*2; ci += 2) {
+			cb = 0x8000UL >> ci;
+			if ((cb & bitmap) == 0)
+				goto found;
+		}
+		for (ci = HPTES_PER_GROUP+1; ci < HPTES_PER_GROUP*2; ci += 2) {
+			cb = 0x8000UL >> ci;
+			if ((cb & bitmap) == 0)
+				goto found;
+		}
+	}
+
+found:
+	if (ci < HPTES_PER_GROUP) {
+		slot = p_pteg * HPTES_PER_GROUP + ci;
+	} else {
+		slot = s_pteg * HPTES_PER_GROUP + (ci & 7);
+		/* lhpte.dw0.dw0.h = 1; */
+		vflags |= HPTE_V_SECONDARY;
+		lhpte.v |= HPTE_V_SECONDARY;
+	}
+
+	result = lv1_write_htab_entry(0, slot, lhpte.v, lhpte.r);
+
+	if (result) {
+		pr_debug("%s:%d: lv1_write_htab_entry failed: %s\n",
+			__func__, __LINE__, ps3pf_result(result));
+		debug_dump_hpte(pa, va, hpte_group, bitmap, lhpte, psize,
+			slot);
+		BUG();
+	}
+
+	/*
+	 * If used slot is not in primary HPTE group,
+	 * the slot should be in secondary HPTE group.
+	 */
+
+	if ((hpte_group ^ slot) & ~(HPTES_PER_GROUP - 1)) {
+		secondary = 1;
+		b_index = s_pteg;
+	} else {
+		secondary = 0;
+		b_index = p_pteg;
+	}
+
+	b_mask = (lhpte.v & HPTE_V_BOLTED) ? 1 << 7 : 0 << 7;
+	bolttab[b_index] |= b_mask >> (slot & 7);
+	b_mask = 1 << 7;
+	inusetab[b_index] |= b_mask >> (slot & 7);
+	spin_unlock_irqrestore(&ps3pf_bolttab_lock, flags);
+
+	return (slot & 7) | (secondary << 3);
+}
+
+static long ps3pf_hpte_remove(unsigned long hpte_group)
+{
+	panic("ps3pf_hpte_remove() not implemented");
+	return 0;
+}
+
+static long ps3pf_hpte_updatepp(unsigned long slot, unsigned long newpp,
+	unsigned long va, int psize, int local)
+{
+	unsigned long flags;
+	unsigned long result;
+	unsigned long pteg, bit;
+	unsigned long hpte_v, want_v;
+
+	want_v = hpte_encode_v(va, psize);
+
+	spin_lock_irqsave(&ps3pf_bolttab_lock, flags);
+
+	hpte_v = htab[slot].v;
+	if (!HPTE_V_COMPARE(hpte_v, want_v) || !(hpte_v & HPTE_V_VALID)) {
+		spin_unlock_irqrestore(&ps3pf_bolttab_lock, flags);
+
+		/* ps3pf_hpte_insert() will be used to update PTE */
+		return -1;
+	}
+
+	result = lv1_write_htab_entry(0, slot, 0, 0);
+
+	if (result != 0) {
+		pr_debug("%s: result = %ld (0x%lx)\n",
+		       __func__, result, result);
+		pr_debug("va=%lx slot=%lx psize=%d\n",
+		       va, slot, psize);
+		BUG();
+	}
+
+	pteg = slot / HPTES_PER_GROUP;
+	bit = slot % HPTES_PER_GROUP;
+	inusetab[pteg] &= ~(0x80 >> bit);
+
+	spin_unlock_irqrestore(&ps3pf_bolttab_lock, flags);
+
+	/* ps3pf_hpte_insert() will be used to update PTE */
+	return -1;
+}
+
+static void ps3pf_hpte_updateboltedpp(unsigned long newpp, unsigned long ea,
+	int psize)
+{
+	panic("ps3pf_hpte_updateboltedpp() not implemented");
+}
+
+static void ps3pf_hpte_invalidate(unsigned long slot, unsigned long va,
+	int psize, int local)
+{
+	unsigned long flags;
+	unsigned long result;
+	unsigned long pteg, bit;
+
+	spin_lock_irqsave(&ps3pf_bolttab_lock, flags);
+	result = lv1_write_htab_entry(0, slot, 0, 0);
+
+	if (result != 0) {
+		pr_debug("%s: result = %ld (0x%lx)\n",
+		       __func__, result, result);
+		pr_debug("va=%lx slot=%lx psize=%d\n",
+		       va, slot, psize);
+		BUG();
+	}
+
+	pteg = slot / HPTES_PER_GROUP;
+	bit = slot % HPTES_PER_GROUP;
+	inusetab[pteg] &= ~(0x80 >> bit);
+	spin_unlock_irqrestore(&ps3pf_bolttab_lock, flags);
+}
+
+static void ps3pf_hpte_clear(void)
+{
+	lv1_unmap_htab(htab_addr);
+}
+
+void __init ps3pf_hpte_init(unsigned long htab_size)
+{
+	long bitmap_size;
+
+	pr_debug(" -> %s:%d\n", __func__, __LINE__);
+
+	ppc_md.hpte_invalidate = ps3pf_hpte_invalidate;
+	ppc_md.hpte_updatepp = ps3pf_hpte_updatepp;
+	ppc_md.hpte_updateboltedpp = ps3pf_hpte_updateboltedpp;
+	ppc_md.hpte_insert = ps3pf_hpte_insert;
+	ppc_md.hpte_remove = ps3pf_hpte_remove;
+	ppc_md.hpte_clear_all = ps3pf_hpte_clear;
+
+	ppc64_pft_size = __ilog2(htab_size);
+
+	bitmap_size = htab_size / sizeof(hpte_t) / 8;
+
+	bolttab = __va(lmb_alloc(bitmap_size, 1));
+	inusetab = __va(lmb_alloc(bitmap_size, 1));
+
+	memset(bolttab, 0, bitmap_size);
+	memset(inusetab, 0, bitmap_size);
+
+	pr_debug(" <- %s:%d\n", __func__, __LINE__);
+}
+
+void __init ps3pf_map_htab(void)
+{
+	long result;
+	unsigned long htab_size = (1UL << ppc64_pft_size);
+
+	result = lv1_map_htab(0, &htab_addr);
+
+	htab = (hpte_t *)__ioremap(htab_addr, htab_size, PAGE_READONLY_X);
+
+	pr_debug("%s:%d: lpar %016lxh, virt %016lxh\n", __func__, __LINE__,
+		htab_addr, (unsigned long)htab);
+}

^ permalink raw reply

* [PATCH 9/16] powerpc: add ps3 platform feature bits
From: Geoff Levand @ 2006-11-10 20:02 UTC (permalink / raw)
  To: Paul Mackerras; +Cc: linuxppc-dev

Adds the needed firmware feature bits for ps3pf.

Signed-off-by: Geoff Levand <geoffrey.levand@am.sony.com>

---
 include/asm-powerpc/firmware.h |    8 ++++++++
 1 file changed, 8 insertions(+)

Index: cell--common--6/include/asm-powerpc/firmware.h
===================================================================
--- cell--common--6.orig/include/asm-powerpc/firmware.h
+++ cell--common--6/include/asm-powerpc/firmware.h
@@ -58,6 +58,8 @@
 	FW_FEATURE_PSERIES_ALWAYS = 0,
 	FW_FEATURE_ISERIES_POSSIBLE = FW_FEATURE_ISERIES | FW_FEATURE_LPAR,
 	FW_FEATURE_ISERIES_ALWAYS = FW_FEATURE_ISERIES | FW_FEATURE_LPAR,
+	FW_FEATURE_PS3PF_POSSIBLE = FW_FEATURE_LPAR,
+	FW_FEATURE_PS3PF_ALWAYS = FW_FEATURE_LPAR,
 	FW_FEATURE_POSSIBLE =
 #ifdef CONFIG_PPC_PSERIES
 		FW_FEATURE_PSERIES_POSSIBLE |
@@ -65,6 +67,9 @@
 #ifdef CONFIG_PPC_ISERIES
 		FW_FEATURE_ISERIES_POSSIBLE |
 #endif
+#ifdef CONFIG_PS3PF
+		FW_FEATURE_PS3PF_POSSIBLE |
+#endif
 		0,
 	FW_FEATURE_ALWAYS =
 #ifdef CONFIG_PPC_PSERIES
@@ -73,6 +78,9 @@
 #ifdef CONFIG_PPC_ISERIES
 		FW_FEATURE_ISERIES_ALWAYS &
 #endif
+#ifdef CONFIG_PS3PF
+		FW_FEATURE_PS3PF_ALWAYS &
+#endif
 		FW_FEATURE_POSSIBLE,
 
 #else /* CONFIG_PPC64 */

^ permalink raw reply

* RE: [Fastboot] Kexec with latest kernel fail
From: Lu, Yinghai @ 2006-11-10 20:01 UTC (permalink / raw)
  To: ebiederm, Andi Kleen; +Cc: Horms, yhlu, Fastboot mailing list, linux-kernel

in e820_reserves request_resource for 0xf0000-0xf03ff for reserve and
0xf0400-4G, will return busy with system_rom_resource (0xf0000-0xfffff)
that is put in the resources list by probe_roms,

We need to add back the patch for e820. otherwise all box with old
linuxbios will be broken to get correct in /proc/iomem in x86_64. and
can not use kexec any more.

Comments?

YH



^ permalink raw reply

* [PATCH 8/16] powerpc: add ps3 platform hvcalls
From: Geoff Levand @ 2006-11-10 20:02 UTC (permalink / raw)
  To: Paul Mackerras; +Cc: linuxppc-dev

Adds the ps3pf hvcalls.

Signed-off-by: Geoff Levand <geoffrey.levand@am.sony.com>

---
 arch/powerpc/platforms/ps3pf/exports.c |   27 +
 arch/powerpc/platforms/ps3pf/hvcall.S  |  804 +++++++++++++++++++++++++++++++++
 include/asm-powerpc/lv1call.h          |  345 ++++++++++++++
 3 files changed, 1176 insertions(+)

Index: cell--common--6/arch/powerpc/platforms/ps3pf/exports.c
===================================================================
--- /dev/null
+++ cell--common--6/arch/powerpc/platforms/ps3pf/exports.c
@@ -0,0 +1,27 @@
+/*
+ * exports.c - PS3 Platform HV call exports for modules.
+ *
+ *  Copyright (C) 2006 Sony Computer Entertainment Inc.
+ *  Copyright 2006 Sony Corp.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/module.h>
+
+#define LV1_CALL(name, in, out, num)                          \
+  extern s64 _lv1_##name(LV1_##in##_IN_##out##_OUT_ARG_DECL); \
+  EXPORT_SYMBOL(_lv1_##name);
+
+#include <asm/lv1call.h>
Index: cell--common--6/arch/powerpc/platforms/ps3pf/hvcall.S
===================================================================
--- /dev/null
+++ cell--common--6/arch/powerpc/platforms/ps3pf/hvcall.S
@@ -0,0 +1,804 @@
+/*
+ * lv1call.S - PS3 Platform HV call interface.
+ *
+ *  Copyright (C) 2006 Sony Computer Entertainment Inc.
+ *  Copyright 2006 Sony Corp.
+ *  Copyright 2003, 2004 (c) MontaVista Software, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <asm/processor.h>
+#include <asm/ppc_asm.h>
+
+#define lv1call .long 0x44000022; extsw r3, r3
+
+#define LV1_N_IN_0_OUT(API_NAME, API_NUMBER)	\
+_GLOBAL(_##API_NAME)				\
+						\
+	mflr	r0;				\
+	std	r0, 16(r1);			\
+						\
+	li      r11, API_NUMBER;		\
+	lv1call;				\
+						\
+	ld	r0, 16(r1);			\
+	mtlr	r0;				\
+	blr
+
+#define LV1_0_IN_0_OUT LV1_N_IN_0_OUT
+#define LV1_1_IN_0_OUT LV1_N_IN_0_OUT
+#define LV1_2_IN_0_OUT LV1_N_IN_0_OUT
+#define LV1_3_IN_0_OUT LV1_N_IN_0_OUT
+#define LV1_4_IN_0_OUT LV1_N_IN_0_OUT
+#define LV1_5_IN_0_OUT LV1_N_IN_0_OUT
+#define LV1_6_IN_0_OUT LV1_N_IN_0_OUT
+#define LV1_7_IN_0_OUT LV1_N_IN_0_OUT
+
+#define LV1_0_IN_1_OUT(API_NAME, API_NUMBER)	\
+_GLOBAL(_##API_NAME)				\
+						\
+	mflr	r0;				\
+	std	r0, 16(r1);			\
+						\
+	stdu    r3, -8(r1);			\
+						\
+	li      r11, API_NUMBER;		\
+	lv1call;				\
+						\
+	addi	r1, r1, 8;			\
+	ld	r11, -8(r1);			\
+	std	r4, 0(r11);			\
+						\
+	ld	r0, 16(r1);			\
+	mtlr	r0;				\
+	blr
+
+#define LV1_0_IN_2_OUT(API_NAME, API_NUMBER)	\
+_GLOBAL(_##API_NAME)				\
+						\
+	mflr	r0;				\
+	std	r0, 16(r1);			\
+						\
+	std     r3, -8(r1);			\
+	stdu	r4, -16(r1);			\
+						\
+	li      r11, API_NUMBER;		\
+	lv1call;				\
+						\
+	addi	r1, r1, 16;			\
+	ld	r11, -8(r1);			\
+	std	r4, 0(r11);			\
+	ld	r11, -16(r1);			\
+	std	r5, 0(r11);			\
+						\
+	ld	r0, 16(r1);			\
+	mtlr	r0;				\
+	blr
+
+#define LV1_0_IN_3_OUT(API_NAME, API_NUMBER)	\
+_GLOBAL(_##API_NAME)				\
+						\
+	mflr	r0;				\
+	std	r0, 16(r1);			\
+						\
+	std     r3, -8(r1);			\
+	std	r4, -16(r1);			\
+	stdu	r5, -24(r1);			\
+						\
+	li      r11, API_NUMBER;		\
+	lv1call;				\
+						\
+	addi	r1, r1, 24;			\
+	ld	r11, -8(r1);			\
+	std	r4, 0(r11);			\
+	ld	r11, -16(r1);			\
+	std	r5, 0(r11);			\
+	ld	r11, -24(r1);			\
+	std	r6, 0(r11);			\
+						\
+	ld	r0, 16(r1);			\
+	mtlr	r0;				\
+	blr
+
+#define LV1_0_IN_7_OUT(API_NAME, API_NUMBER)	\
+_GLOBAL(_##API_NAME)				\
+						\
+	mflr	r0;				\
+	std	r0, 16(r1);			\
+						\
+	std     r3, -8(r1);			\
+	std	r4, -16(r1);			\
+	std	r5, -24(r1);			\
+	std	r6, -32(r1);			\
+	std	r7, -40(r1);			\
+	std	r8, -48(r1);			\
+	stdu	r9, -56(r1);			\
+						\
+	li      r11, API_NUMBER;		\
+	lv1call;				\
+						\
+	addi	r1, r1, 56;			\
+	ld	r11, -8(r1);			\
+	std	r4, 0(r11);			\
+	ld	r11, -16(r1);			\
+	std	r5, 0(r11);			\
+	ld	r11, -24(r1);			\
+	std	r6, 0(r11);			\
+	ld	r11, -32(r1);			\
+	std	r7, 0(r11);			\
+	ld	r11, -40(r1);			\
+	std	r8, 0(r11);			\
+	ld	r11, -48(r1);			\
+	std	r9, 0(r11);			\
+	ld	r11, -56(r1);			\
+	std	r10, 0(r11);			\
+						\
+	ld	r0, 16(r1);			\
+	mtlr	r0;				\
+	blr
+
+#define LV1_1_IN_1_OUT(API_NAME, API_NUMBER)	\
+_GLOBAL(_##API_NAME)				\
+						\
+	mflr	r0;				\
+	std	r0, 16(r1);			\
+						\
+	stdu    r4, -8(r1);			\
+						\
+	li      r11, API_NUMBER;		\
+	lv1call;				\
+						\
+	addi	r1, r1, 8;			\
+	ld	r11, -8(r1);			\
+	std	r4, 0(r11);			\
+						\
+	ld	r0, 16(r1);			\
+	mtlr	r0;				\
+	blr
+
+#define LV1_1_IN_2_OUT(API_NAME, API_NUMBER)	\
+_GLOBAL(_##API_NAME)				\
+						\
+	mflr	r0;				\
+	std	r0, 16(r1);			\
+						\
+	std     r4, -8(r1);			\
+	stdu	r5, -16(r1);			\
+						\
+	li      r11, API_NUMBER;		\
+	lv1call;				\
+						\
+	addi	r1, r1, 16;			\
+	ld	r11, -8(r1);			\
+	std	r4, 0(r11);			\
+	ld	r11, -16(r1);			\
+	std	r5, 0(r11);			\
+						\
+	ld	r0, 16(r1);			\
+	mtlr	r0;				\
+	blr
+
+#define LV1_1_IN_3_OUT(API_NAME, API_NUMBER)	\
+_GLOBAL(_##API_NAME)				\
+						\
+	mflr	r0;				\
+	std	r0, 16(r1);			\
+						\
+	std     r4, -8(r1);			\
+	std	r5, -16(r1);			\
+	stdu	r6, -24(r1);			\
+						\
+	li      r11, API_NUMBER;		\
+	lv1call;				\
+						\
+	addi	r1, r1, 24;			\
+	ld	r11, -8(r1);			\
+	std	r4, 0(r11);			\
+	ld	r11, -16(r1);			\
+	std	r5, 0(r11);			\
+	ld	r11, -24(r1);			\
+	std	r6, 0(r11);			\
+						\
+	ld	r0, 16(r1);			\
+	mtlr	r0;				\
+	blr
+
+#define LV1_1_IN_4_OUT(API_NAME, API_NUMBER)	\
+_GLOBAL(_##API_NAME)				\
+						\
+	mflr	r0;				\
+	std	r0, 16(r1);			\
+						\
+	std     r4, -8(r1);			\
+	std	r5, -16(r1);			\
+	std	r6, -24(r1);			\
+	stdu	r7, -32(r1);			\
+						\
+	li      r11, API_NUMBER;		\
+	lv1call;				\
+						\
+	addi	r1, r1, 32;			\
+	ld	r11, -8(r1);			\
+	std	r4, 0(r11);			\
+	ld	r11, -16(r1);			\
+	std	r5, 0(r11);			\
+	ld	r11, -24(r1);			\
+	std	r6, 0(r11);			\
+	ld	r11, -32(r1);			\
+	std	r7, 0(r11);			\
+						\
+	ld	r0, 16(r1);			\
+	mtlr	r0;				\
+	blr
+
+#define LV1_1_IN_5_OUT(API_NAME, API_NUMBER)	\
+_GLOBAL(_##API_NAME)				\
+						\
+	mflr	r0;				\
+	std	r0, 16(r1);			\
+						\
+	std     r4, -8(r1);			\
+	std	r5, -16(r1);			\
+	std	r6, -24(r1);			\
+	std	r7, -32(r1);			\
+	stdu	r8, -40(r1);			\
+						\
+	li      r11, API_NUMBER;		\
+	lv1call;				\
+						\
+	addi	r1, r1, 40;			\
+	ld	r11, -8(r1);			\
+	std	r4, 0(r11);			\
+	ld	r11, -16(r1);			\
+	std	r5, 0(r11);			\
+	ld	r11, -24(r1);			\
+	std	r6, 0(r11);			\
+	ld	r11, -32(r1);			\
+	std	r7, 0(r11);			\
+	ld	r11, -40(r1);			\
+	std	r8, 0(r11);			\
+						\
+	ld	r0, 16(r1);			\
+	mtlr	r0;				\
+	blr
+
+#define LV1_1_IN_6_OUT(API_NAME, API_NUMBER)	\
+_GLOBAL(_##API_NAME)				\
+						\
+	mflr	r0;				\
+	std	r0, 16(r1);			\
+						\
+	std     r4, -8(r1);			\
+	std	r5, -16(r1);			\
+	std	r6, -24(r1);			\
+	std	r7, -32(r1);			\
+	std	r8, -40(r1);			\
+	stdu	r9, -48(r1);			\
+						\
+	li      r11, API_NUMBER;		\
+	lv1call;				\
+						\
+	addi	r1, r1, 48;			\
+	ld	r11, -8(r1);			\
+	std	r4, 0(r11);			\
+	ld	r11, -16(r1);			\
+	std	r5, 0(r11);			\
+	ld	r11, -24(r1);			\
+	std	r6, 0(r11);			\
+	ld	r11, -32(r1);			\
+	std	r7, 0(r11);			\
+	ld	r11, -40(r1);			\
+	std	r8, 0(r11);			\
+	ld	r11, -48(r1);			\
+	std	r9, 0(r11);			\
+						\
+	ld	r0, 16(r1);			\
+	mtlr	r0;				\
+	blr
+
+#define LV1_1_IN_7_OUT(API_NAME, API_NUMBER)	\
+_GLOBAL(_##API_NAME)				\
+						\
+	mflr	r0;				\
+	std	r0, 16(r1);			\
+						\
+	std     r4, -8(r1);			\
+	std	r5, -16(r1);			\
+	std	r6, -24(r1);			\
+	std	r7, -32(r1);			\
+	std	r8, -40(r1);			\
+	std	r9, -48(r1);			\
+	stdu	r10, -56(r1);			\
+						\
+	li      r11, API_NUMBER;		\
+	lv1call;				\
+						\
+	addi	r1, r1, 56;			\
+	ld	r11, -8(r1);			\
+	std	r4, 0(r11);			\
+	ld	r11, -16(r1);			\
+	std	r5, 0(r11);			\
+	ld	r11, -24(r1);			\
+	std	r6, 0(r11);			\
+	ld	r11, -32(r1);			\
+	std	r7, 0(r11);			\
+	ld	r11, -40(r1);			\
+	std	r8, 0(r11);			\
+	ld	r11, -48(r1);			\
+	std	r9, 0(r11);			\
+	ld	r11, -56(r1);			\
+	std	r10, 0(r11);			\
+						\
+	ld	r0, 16(r1);			\
+	mtlr	r0;				\
+	blr
+
+#define LV1_2_IN_1_OUT(API_NAME, API_NUMBER)	\
+_GLOBAL(_##API_NAME)				\
+						\
+	mflr	r0;				\
+	std	r0, 16(r1);			\
+						\
+	stdu	r5, -8(r1);			\
+						\
+	li      r11, API_NUMBER;		\
+	lv1call;				\
+						\
+	addi	r1, r1, 8;			\
+	ld	r11, -8(r1);			\
+	std	r4, 0(r11);			\
+						\
+	ld	r0, 16(r1);			\
+	mtlr	r0;				\
+	blr
+
+#define LV1_2_IN_2_OUT(API_NAME, API_NUMBER)	\
+_GLOBAL(_##API_NAME)				\
+						\
+	mflr	r0;				\
+	std	r0, 16(r1);			\
+						\
+	std     r5, -8(r1);			\
+	stdu	r6, -16(r1);			\
+						\
+	li      r11, API_NUMBER;		\
+	lv1call;				\
+						\
+	addi	r1, r1, 16;			\
+	ld	r11, -8(r1);			\
+	std	r4, 0(r11);			\
+	ld	r11, -16(r1);			\
+	std	r5, 0(r11);			\
+						\
+	ld	r0, 16(r1);			\
+	mtlr	r0;				\
+	blr
+
+#define LV1_2_IN_3_OUT(API_NAME, API_NUMBER)	\
+_GLOBAL(_##API_NAME)				\
+						\
+	mflr	r0;				\
+	std	r0, 16(r1);			\
+						\
+	std     r5, -8(r1);			\
+	std	r6, -16(r1);			\
+	stdu	r7, -24(r1);			\
+						\
+	li      r11, API_NUMBER;		\
+	lv1call;				\
+						\
+	addi	r1, r1, 24;			\
+	ld	r11, -8(r1);			\
+	std	r4, 0(r11);			\
+	ld	r11, -16(r1);			\
+	std	r5, 0(r11);			\
+	ld	r11, -24(r1);			\
+	std	r6, 0(r11);			\
+						\
+	ld	r0, 16(r1);			\
+	mtlr	r0;				\
+	blr
+
+#define LV1_2_IN_4_OUT(API_NAME, API_NUMBER)	\
+_GLOBAL(_##API_NAME)				\
+						\
+	mflr	r0;				\
+	std	r0, 16(r1);			\
+						\
+	std     r5, -8(r1);			\
+	std	r6, -16(r1);			\
+	std	r7, -24(r1);			\
+	stdu	r8, -32(r1);			\
+						\
+	li      r11, API_NUMBER;		\
+	lv1call;				\
+						\
+	addi	r1, r1, 32;			\
+	ld	r11, -8(r1);			\
+	std	r4, 0(r11);			\
+	ld	r11, -16(r1);			\
+	std	r5, 0(r11);			\
+	ld	r11, -24(r1);			\
+	std	r6, 0(r11);			\
+	ld	r11, -32(r1);			\
+	std	r7, 0(r11);			\
+						\
+	ld	r0, 16(r1);			\
+	mtlr	r0;				\
+	blr
+
+#define LV1_2_IN_5_OUT(API_NAME, API_NUMBER)	\
+_GLOBAL(_##API_NAME)				\
+						\
+	mflr	r0;				\
+	std	r0, 16(r1);			\
+						\
+	std     r5, -8(r1);			\
+	std	r6, -16(r1);			\
+	std	r7, -24(r1);			\
+	std	r8, -32(r1);			\
+	stdu	r9, -40(r1);			\
+						\
+	li      r11, API_NUMBER;		\
+	lv1call;				\
+						\
+	addi	r1, r1, 40;			\
+	ld	r11, -8(r1);			\
+	std	r4, 0(r11);			\
+	ld	r11, -16(r1);			\
+	std	r5, 0(r11);			\
+	ld	r11, -24(r1);			\
+	std	r6, 0(r11);			\
+	ld	r11, -32(r1);			\
+	std	r7, 0(r11);			\
+	ld	r11, -40(r1);			\
+	std	r8, 0(r11);			\
+						\
+	ld	r0, 16(r1);			\
+	mtlr	r0;				\
+	blr
+
+#define LV1_3_IN_1_OUT(API_NAME, API_NUMBER)	\
+_GLOBAL(_##API_NAME)				\
+						\
+	mflr	r0;				\
+	std	r0, 16(r1);			\
+						\
+	stdu	r6, -8(r1);			\
+						\
+	li      r11, API_NUMBER;		\
+	lv1call;				\
+						\
+	addi	r1, r1, 8;			\
+	ld	r11, -8(r1);			\
+	std	r4, 0(r11);			\
+						\
+	ld	r0, 16(r1);			\
+	mtlr	r0;				\
+	blr
+
+#define LV1_3_IN_2_OUT(API_NAME, API_NUMBER)	\
+_GLOBAL(_##API_NAME)				\
+						\
+	mflr	r0;				\
+	std	r0, 16(r1);			\
+						\
+	std     r6, -8(r1);			\
+	stdu	r7, -16(r1);			\
+						\
+	li      r11, API_NUMBER;		\
+	lv1call;				\
+						\
+	addi	r1, r1, 16;			\
+	ld	r11, -8(r1);			\
+	std	r4, 0(r11);			\
+	ld	r11, -16(r1);			\
+	std	r5, 0(r11);			\
+						\
+	ld	r0, 16(r1);			\
+	mtlr	r0;				\
+	blr
+
+#define LV1_3_IN_3_OUT(API_NAME, API_NUMBER)	\
+_GLOBAL(_##API_NAME)				\
+						\
+	mflr	r0;				\
+	std	r0, 16(r1);			\
+						\
+	std     r6, -8(r1);			\
+	std	r7, -16(r1);			\
+	stdu	r8, -24(r1);			\
+						\
+	li      r11, API_NUMBER;		\
+	lv1call;				\
+						\
+	addi	r1, r1, 24;			\
+	ld	r11, -8(r1);			\
+	std	r4, 0(r11);			\
+	ld	r11, -16(r1);			\
+	std	r5, 0(r11);			\
+	ld	r11, -24(r1);			\
+	std	r6, 0(r11);			\
+						\
+	ld	r0, 16(r1);			\
+	mtlr	r0;				\
+	blr
+
+#define LV1_4_IN_1_OUT(API_NAME, API_NUMBER)	\
+_GLOBAL(_##API_NAME)				\
+						\
+	mflr	r0;				\
+	std	r0, 16(r1);			\
+						\
+	stdu    r7, -8(r1);			\
+						\
+	li      r11, API_NUMBER;		\
+	lv1call;				\
+						\
+	addi	r1, r1, 8;			\
+	ld	r11, -8(r1);			\
+	std	r4, 0(r11);			\
+						\
+	ld	r0, 16(r1);			\
+	mtlr	r0;				\
+	blr
+
+#define LV1_4_IN_2_OUT(API_NAME, API_NUMBER)	\
+_GLOBAL(_##API_NAME)				\
+						\
+	mflr	r0;				\
+	std	r0, 16(r1);			\
+						\
+	std     r7, -8(r1);			\
+	stdu	r8, -16(r1);			\
+						\
+	li      r11, API_NUMBER;		\
+	lv1call;				\
+						\
+	addi	r1, r1, 16;			\
+	ld	r11, -8(r1);			\
+	std	r4, 0(r11);			\
+	ld	r11, -16(r1);			\
+	std	r5, 0(r11);			\
+						\
+	ld	r0, 16(r1);			\
+	mtlr	r0;				\
+	blr
+
+#define LV1_4_IN_3_OUT(API_NAME, API_NUMBER)	\
+_GLOBAL(_##API_NAME)				\
+						\
+	mflr	r0;				\
+	std	r0, 16(r1);			\
+						\
+	std     r7, -8(r1);			\
+	std	r8, -16(r1);			\
+	stdu	r9, -24(r1);			\
+						\
+	li      r11, API_NUMBER;		\
+	lv1call;				\
+						\
+	addi	r1, r1, 24;			\
+	ld	r11, -8(r1);			\
+	std	r4, 0(r11);			\
+	ld	r11, -16(r1);			\
+	std	r5, 0(r11);			\
+	ld	r11, -24(r1);			\
+	std	r6, 0(r11);			\
+						\
+	ld	r0, 16(r1);			\
+	mtlr	r0;				\
+	blr
+
+#define LV1_5_IN_1_OUT(API_NAME, API_NUMBER)	\
+_GLOBAL(_##API_NAME)				\
+						\
+	mflr	r0;				\
+	std	r0, 16(r1);			\
+						\
+	stdu    r8, -8(r1);			\
+						\
+	li      r11, API_NUMBER;		\
+	lv1call;				\
+						\
+	addi	r1, r1, 8;			\
+	ld	r11, -8(r1);			\
+	std	r4, 0(r11);			\
+						\
+	ld	r0, 16(r1);			\
+	mtlr	r0;				\
+	blr
+
+#define LV1_5_IN_2_OUT(API_NAME, API_NUMBER)	\
+_GLOBAL(_##API_NAME)				\
+						\
+	mflr	r0;				\
+	std	r0, 16(r1);			\
+						\
+	std     r8, -8(r1);			\
+	stdu	r9, -16(r1);			\
+						\
+	li      r11, API_NUMBER;		\
+	lv1call;				\
+						\
+	addi	r1, r1, 16;			\
+	ld	r11, -8(r1);			\
+	std	r4, 0(r11);			\
+	ld	r11, -16(r1);			\
+	std	r5, 0(r11);			\
+						\
+	ld	r0, 16(r1);			\
+	mtlr	r0;				\
+	blr
+
+#define LV1_5_IN_3_OUT(API_NAME, API_NUMBER)	\
+_GLOBAL(_##API_NAME)				\
+						\
+	mflr	r0;				\
+	std	r0, 16(r1);			\
+						\
+	std     r8, -8(r1);			\
+	std	r9, -16(r1);			\
+	stdu	r10, -24(r1);			\
+						\
+	li      r11, API_NUMBER;		\
+	lv1call;				\
+						\
+	addi	r1, r1, 24;			\
+	ld	r11, -8(r1);			\
+	std	r4, 0(r11);			\
+	ld	r11, -16(r1);			\
+	std	r5, 0(r11);			\
+	ld	r11, -24(r1);			\
+	std	r6, 0(r11);			\
+						\
+	ld	r0, 16(r1);			\
+	mtlr	r0;				\
+	blr
+
+#define LV1_6_IN_1_OUT(API_NAME, API_NUMBER)	\
+_GLOBAL(_##API_NAME)				\
+						\
+	mflr	r0;				\
+	std	r0, 16(r1);			\
+						\
+	stdu    r9, -8(r1);			\
+						\
+	li      r11, API_NUMBER;		\
+	lv1call;				\
+						\
+	addi	r1, r1, 8;			\
+	ld	r11, -8(r1);			\
+	std	r4, 0(r11);			\
+						\
+	ld	r0, 16(r1);			\
+	mtlr	r0;				\
+	blr
+
+#define LV1_6_IN_2_OUT(API_NAME, API_NUMBER)	\
+_GLOBAL(_##API_NAME)				\
+						\
+	mflr	r0;				\
+	std	r0, 16(r1);			\
+						\
+	std     r9, -8(r1);			\
+	stdu    r10, -16(r1);			\
+						\
+	li      r11, API_NUMBER;		\
+	lv1call;				\
+						\
+	addi	r1, r1, 16;			\
+	ld	r11, -8(r1);			\
+	std	r4, 0(r11);			\
+	ld	r11, -16(r1);			\
+	std	r5, 0(r11);			\
+						\
+	ld	r0, 16(r1);			\
+	mtlr	r0;				\
+	blr
+
+#define LV1_6_IN_3_OUT(API_NAME, API_NUMBER)	\
+_GLOBAL(_##API_NAME)				\
+						\
+	mflr	r0;				\
+	std	r0, 16(r1);			\
+						\
+	std     r9, -8(r1);			\
+	stdu    r10, -16(r1);			\
+						\
+	li      r11, API_NUMBER;		\
+	lv1call;				\
+						\
+	addi	r1, r1, 16;			\
+	ld	r11, -8(r1);			\
+	std	r4, 0(r11);			\
+	ld	r11, -16(r1);			\
+	std	r5, 0(r11);			\
+	ld	r11, 48+8*8(r1);		\
+	std	r6, 0(r11);			\
+						\
+	ld	r0, 16(r1);			\
+	mtlr	r0;				\
+	blr
+
+#define LV1_7_IN_1_OUT(API_NAME, API_NUMBER)	\
+_GLOBAL(_##API_NAME)				\
+						\
+	mflr	r0;				\
+	std	r0, 16(r1);			\
+						\
+	stdu    r10, -8(r1);			\
+						\
+	li      r11, API_NUMBER;		\
+	lv1call;				\
+						\
+	addi	r1, r1, 8;			\
+	ld	r11, -8(r1);			\
+	std	r4, 0(r11);			\
+						\
+	ld	r0, 16(r1);			\
+	mtlr	r0;				\
+	blr
+
+#define LV1_7_IN_6_OUT(API_NAME, API_NUMBER)	\
+_GLOBAL(_##API_NAME)				\
+						\
+	mflr	r0;				\
+	std	r0, 16(r1);			\
+						\
+	std	r10, 48+8*7(r1);		\
+						\
+	li	r11, API_NUMBER;		\
+	lv1call;				\
+						\
+	ld	r11, 48+8*7(r1);		\
+	std	r4, 0(r11);			\
+	ld	r11, 48+8*8(r1);		\
+	std	r5, 0(r11);			\
+	ld	r11, 48+8*9(r1);		\
+	std	r6, 0(r11);			\
+	ld	r11, 48+8*10(r1);		\
+	std	r7, 0(r11);			\
+	ld	r11, 48+8*11(r1);		\
+	std	r8, 0(r11);			\
+	ld	r11, 48+8*12(r1);		\
+	std	r9, 0(r11);			\
+						\
+	ld	r0, 16(r1);			\
+	mtlr	r0;				\
+	blr
+
+#define LV1_8_IN_1_OUT(API_NAME, API_NUMBER)	\
+_GLOBAL(_##API_NAME)				\
+						\
+	mflr	r0;				\
+	std	r0, 16(r1);			\
+						\
+	li      r11, API_NUMBER;		\
+	lv1call;				\
+						\
+	ld	r11, 48+8*8(r1);		\
+	std	r4, 0(r11);			\
+						\
+	ld	r0, 16(r1);			\
+	mtlr	r0;				\
+	blr
+
+	.text
+
+/* the lv1 underscored call definitions expand here */
+
+#define LV1_CALL(name, in, out, num) LV1_##in##_IN_##out##_OUT(lv1_##name, num)
+#include <asm/lv1call.h>
Index: cell--common--6/include/asm-powerpc/lv1call.h
===================================================================
--- /dev/null
+++ cell--common--6/include/asm-powerpc/lv1call.h
@@ -0,0 +1,345 @@
+/*
+ * lv1call.h - PS3 Platform HV call interface.
+ *
+ *  Copyright (C) 2006 Sony Computer Entertainment Inc.
+ *  Copyright 2006 Sony Corp.
+ *  Copyright 2003, 2004 (c) MontaVista Software, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#if !defined(_8DE1E4A7_DB2B_4F40_B9CB_8A6EC9645057)
+#define _8DE1E4A7_DB2B_4F40_B9CB_8A6EC9645057
+
+#if !defined(__ASSEMBLY__)
+
+#include <linux/types.h>
+
+/* lv1 call declaration macros */
+
+#define LV1_1_IN_ARG_DECL u64 in_1
+#define LV1_2_IN_ARG_DECL LV1_1_IN_ARG_DECL, u64 in_2
+#define LV1_3_IN_ARG_DECL LV1_2_IN_ARG_DECL, u64 in_3
+#define LV1_4_IN_ARG_DECL LV1_3_IN_ARG_DECL, u64 in_4
+#define LV1_5_IN_ARG_DECL LV1_4_IN_ARG_DECL, u64 in_5
+#define LV1_6_IN_ARG_DECL LV1_5_IN_ARG_DECL, u64 in_6
+#define LV1_7_IN_ARG_DECL LV1_6_IN_ARG_DECL, u64 in_7
+#define LV1_8_IN_ARG_DECL LV1_7_IN_ARG_DECL, u64 in_8
+#define LV1_1_OUT_ARG_DECL u64 *out_1
+#define LV1_2_OUT_ARG_DECL LV1_1_OUT_ARG_DECL, u64 *out_2
+#define LV1_3_OUT_ARG_DECL LV1_2_OUT_ARG_DECL, u64 *out_3
+#define LV1_4_OUT_ARG_DECL LV1_3_OUT_ARG_DECL, u64 *out_4
+#define LV1_5_OUT_ARG_DECL LV1_4_OUT_ARG_DECL, u64 *out_5
+#define LV1_6_OUT_ARG_DECL LV1_5_OUT_ARG_DECL, u64 *out_6
+#define LV1_7_OUT_ARG_DECL LV1_6_OUT_ARG_DECL, u64 *out_7
+
+#define LV1_0_IN_0_OUT_ARG_DECL void
+#define LV1_1_IN_0_OUT_ARG_DECL LV1_1_IN_ARG_DECL
+#define LV1_2_IN_0_OUT_ARG_DECL LV1_2_IN_ARG_DECL
+#define LV1_3_IN_0_OUT_ARG_DECL LV1_3_IN_ARG_DECL
+#define LV1_4_IN_0_OUT_ARG_DECL LV1_4_IN_ARG_DECL
+#define LV1_5_IN_0_OUT_ARG_DECL LV1_5_IN_ARG_DECL
+#define LV1_6_IN_0_OUT_ARG_DECL LV1_6_IN_ARG_DECL
+#define LV1_7_IN_0_OUT_ARG_DECL LV1_7_IN_ARG_DECL
+
+#define LV1_0_IN_1_OUT_ARG_DECL                    LV1_1_OUT_ARG_DECL
+#define LV1_1_IN_1_OUT_ARG_DECL LV1_1_IN_ARG_DECL, LV1_1_OUT_ARG_DECL
+#define LV1_2_IN_1_OUT_ARG_DECL LV1_2_IN_ARG_DECL, LV1_1_OUT_ARG_DECL
+#define LV1_3_IN_1_OUT_ARG_DECL LV1_3_IN_ARG_DECL, LV1_1_OUT_ARG_DECL
+#define LV1_4_IN_1_OUT_ARG_DECL LV1_4_IN_ARG_DECL, LV1_1_OUT_ARG_DECL
+#define LV1_5_IN_1_OUT_ARG_DECL LV1_5_IN_ARG_DECL, LV1_1_OUT_ARG_DECL
+#define LV1_6_IN_1_OUT_ARG_DECL LV1_6_IN_ARG_DECL, LV1_1_OUT_ARG_DECL
+#define LV1_7_IN_1_OUT_ARG_DECL LV1_7_IN_ARG_DECL, LV1_1_OUT_ARG_DECL
+#define LV1_8_IN_1_OUT_ARG_DECL LV1_8_IN_ARG_DECL, LV1_1_OUT_ARG_DECL
+
+#define LV1_0_IN_2_OUT_ARG_DECL                    LV1_2_OUT_ARG_DECL
+#define LV1_1_IN_2_OUT_ARG_DECL LV1_1_IN_ARG_DECL, LV1_2_OUT_ARG_DECL
+#define LV1_2_IN_2_OUT_ARG_DECL LV1_2_IN_ARG_DECL, LV1_2_OUT_ARG_DECL
+#define LV1_3_IN_2_OUT_ARG_DECL LV1_3_IN_ARG_DECL, LV1_2_OUT_ARG_DECL
+#define LV1_4_IN_2_OUT_ARG_DECL LV1_4_IN_ARG_DECL, LV1_2_OUT_ARG_DECL
+#define LV1_5_IN_2_OUT_ARG_DECL LV1_5_IN_ARG_DECL, LV1_2_OUT_ARG_DECL
+#define LV1_6_IN_2_OUT_ARG_DECL LV1_6_IN_ARG_DECL, LV1_2_OUT_ARG_DECL
+#define LV1_7_IN_2_OUT_ARG_DECL LV1_7_IN_ARG_DECL, LV1_2_OUT_ARG_DECL
+
+#define LV1_0_IN_3_OUT_ARG_DECL                    LV1_3_OUT_ARG_DECL
+#define LV1_1_IN_3_OUT_ARG_DECL LV1_1_IN_ARG_DECL, LV1_3_OUT_ARG_DECL
+#define LV1_2_IN_3_OUT_ARG_DECL LV1_2_IN_ARG_DECL, LV1_3_OUT_ARG_DECL
+#define LV1_3_IN_3_OUT_ARG_DECL LV1_3_IN_ARG_DECL, LV1_3_OUT_ARG_DECL
+#define LV1_4_IN_3_OUT_ARG_DECL LV1_4_IN_ARG_DECL, LV1_3_OUT_ARG_DECL
+#define LV1_5_IN_3_OUT_ARG_DECL LV1_5_IN_ARG_DECL, LV1_3_OUT_ARG_DECL
+#define LV1_6_IN_3_OUT_ARG_DECL LV1_6_IN_ARG_DECL, LV1_3_OUT_ARG_DECL
+#define LV1_7_IN_3_OUT_ARG_DECL LV1_7_IN_ARG_DECL, LV1_3_OUT_ARG_DECL
+
+#define LV1_0_IN_4_OUT_ARG_DECL                    LV1_4_OUT_ARG_DECL
+#define LV1_1_IN_4_OUT_ARG_DECL LV1_1_IN_ARG_DECL, LV1_4_OUT_ARG_DECL
+#define LV1_2_IN_4_OUT_ARG_DECL LV1_2_IN_ARG_DECL, LV1_4_OUT_ARG_DECL
+#define LV1_3_IN_4_OUT_ARG_DECL LV1_3_IN_ARG_DECL, LV1_4_OUT_ARG_DECL
+#define LV1_4_IN_4_OUT_ARG_DECL LV1_4_IN_ARG_DECL, LV1_4_OUT_ARG_DECL
+#define LV1_5_IN_4_OUT_ARG_DECL LV1_5_IN_ARG_DECL, LV1_4_OUT_ARG_DECL
+#define LV1_6_IN_4_OUT_ARG_DECL LV1_6_IN_ARG_DECL, LV1_4_OUT_ARG_DECL
+#define LV1_7_IN_4_OUT_ARG_DECL LV1_7_IN_ARG_DECL, LV1_4_OUT_ARG_DECL
+
+#define LV1_0_IN_5_OUT_ARG_DECL                    LV1_5_OUT_ARG_DECL
+#define LV1_1_IN_5_OUT_ARG_DECL LV1_1_IN_ARG_DECL, LV1_5_OUT_ARG_DECL
+#define LV1_2_IN_5_OUT_ARG_DECL LV1_2_IN_ARG_DECL, LV1_5_OUT_ARG_DECL
+#define LV1_3_IN_5_OUT_ARG_DECL LV1_3_IN_ARG_DECL, LV1_5_OUT_ARG_DECL
+#define LV1_4_IN_5_OUT_ARG_DECL LV1_4_IN_ARG_DECL, LV1_5_OUT_ARG_DECL
+#define LV1_5_IN_5_OUT_ARG_DECL LV1_5_IN_ARG_DECL, LV1_5_OUT_ARG_DECL
+#define LV1_6_IN_5_OUT_ARG_DECL LV1_6_IN_ARG_DECL, LV1_5_OUT_ARG_DECL
+#define LV1_7_IN_5_OUT_ARG_DECL LV1_7_IN_ARG_DECL, LV1_5_OUT_ARG_DECL
+
+#define LV1_0_IN_6_OUT_ARG_DECL                    LV1_6_OUT_ARG_DECL
+#define LV1_1_IN_6_OUT_ARG_DECL LV1_1_IN_ARG_DECL, LV1_6_OUT_ARG_DECL
+#define LV1_2_IN_6_OUT_ARG_DECL LV1_2_IN_ARG_DECL, LV1_6_OUT_ARG_DECL
+#define LV1_3_IN_6_OUT_ARG_DECL LV1_3_IN_ARG_DECL, LV1_6_OUT_ARG_DECL
+#define LV1_4_IN_6_OUT_ARG_DECL LV1_4_IN_ARG_DECL, LV1_6_OUT_ARG_DECL
+#define LV1_5_IN_6_OUT_ARG_DECL LV1_5_IN_ARG_DECL, LV1_6_OUT_ARG_DECL
+#define LV1_6_IN_6_OUT_ARG_DECL LV1_6_IN_ARG_DECL, LV1_6_OUT_ARG_DECL
+#define LV1_7_IN_6_OUT_ARG_DECL LV1_7_IN_ARG_DECL, LV1_6_OUT_ARG_DECL
+
+#define LV1_0_IN_7_OUT_ARG_DECL                    LV1_7_OUT_ARG_DECL
+#define LV1_1_IN_7_OUT_ARG_DECL LV1_1_IN_ARG_DECL, LV1_7_OUT_ARG_DECL
+#define LV1_2_IN_7_OUT_ARG_DECL LV1_2_IN_ARG_DECL, LV1_7_OUT_ARG_DECL
+#define LV1_3_IN_7_OUT_ARG_DECL LV1_3_IN_ARG_DECL, LV1_7_OUT_ARG_DECL
+#define LV1_4_IN_7_OUT_ARG_DECL LV1_4_IN_ARG_DECL, LV1_7_OUT_ARG_DECL
+#define LV1_5_IN_7_OUT_ARG_DECL LV1_5_IN_ARG_DECL, LV1_7_OUT_ARG_DECL
+#define LV1_6_IN_7_OUT_ARG_DECL LV1_6_IN_ARG_DECL, LV1_7_OUT_ARG_DECL
+#define LV1_7_IN_7_OUT_ARG_DECL LV1_7_IN_ARG_DECL, LV1_7_OUT_ARG_DECL
+
+#define LV1_1_IN_ARGS in_1
+#define LV1_2_IN_ARGS LV1_1_IN_ARGS, in_2
+#define LV1_3_IN_ARGS LV1_2_IN_ARGS, in_3
+#define LV1_4_IN_ARGS LV1_3_IN_ARGS, in_4
+#define LV1_5_IN_ARGS LV1_4_IN_ARGS, in_5
+#define LV1_6_IN_ARGS LV1_5_IN_ARGS, in_6
+#define LV1_7_IN_ARGS LV1_6_IN_ARGS, in_7
+#define LV1_8_IN_ARGS LV1_7_IN_ARGS, in_8
+
+#define LV1_1_OUT_ARGS out_1
+#define LV1_2_OUT_ARGS LV1_1_OUT_ARGS, out_2
+#define LV1_3_OUT_ARGS LV1_2_OUT_ARGS, out_3
+#define LV1_4_OUT_ARGS LV1_3_OUT_ARGS, out_4
+#define LV1_5_OUT_ARGS LV1_4_OUT_ARGS, out_5
+#define LV1_6_OUT_ARGS LV1_5_OUT_ARGS, out_6
+#define LV1_7_OUT_ARGS LV1_6_OUT_ARGS, out_7
+
+#define LV1_0_IN_0_OUT_ARGS
+#define LV1_1_IN_0_OUT_ARGS LV1_1_IN_ARGS
+#define LV1_2_IN_0_OUT_ARGS LV1_2_IN_ARGS
+#define LV1_3_IN_0_OUT_ARGS LV1_3_IN_ARGS
+#define LV1_4_IN_0_OUT_ARGS LV1_4_IN_ARGS
+#define LV1_5_IN_0_OUT_ARGS LV1_5_IN_ARGS
+#define LV1_6_IN_0_OUT_ARGS LV1_6_IN_ARGS
+#define LV1_7_IN_0_OUT_ARGS LV1_7_IN_ARGS
+
+#define LV1_0_IN_1_OUT_ARGS                LV1_1_OUT_ARGS
+#define LV1_1_IN_1_OUT_ARGS LV1_1_IN_ARGS, LV1_1_OUT_ARGS
+#define LV1_2_IN_1_OUT_ARGS LV1_2_IN_ARGS, LV1_1_OUT_ARGS
+#define LV1_3_IN_1_OUT_ARGS LV1_3_IN_ARGS, LV1_1_OUT_ARGS
+#define LV1_4_IN_1_OUT_ARGS LV1_4_IN_ARGS, LV1_1_OUT_ARGS
+#define LV1_5_IN_1_OUT_ARGS LV1_5_IN_ARGS, LV1_1_OUT_ARGS
+#define LV1_6_IN_1_OUT_ARGS LV1_6_IN_ARGS, LV1_1_OUT_ARGS
+#define LV1_7_IN_1_OUT_ARGS LV1_7_IN_ARGS, LV1_1_OUT_ARGS
+#define LV1_8_IN_1_OUT_ARGS LV1_8_IN_ARGS, LV1_1_OUT_ARGS
+
+#define LV1_0_IN_2_OUT_ARGS                LV1_2_OUT_ARGS
+#define LV1_1_IN_2_OUT_ARGS LV1_1_IN_ARGS, LV1_2_OUT_ARGS
+#define LV1_2_IN_2_OUT_ARGS LV1_2_IN_ARGS, LV1_2_OUT_ARGS
+#define LV1_3_IN_2_OUT_ARGS LV1_3_IN_ARGS, LV1_2_OUT_ARGS
+#define LV1_4_IN_2_OUT_ARGS LV1_4_IN_ARGS, LV1_2_OUT_ARGS
+#define LV1_5_IN_2_OUT_ARGS LV1_5_IN_ARGS, LV1_2_OUT_ARGS
+#define LV1_6_IN_2_OUT_ARGS LV1_6_IN_ARGS, LV1_2_OUT_ARGS
+#define LV1_7_IN_2_OUT_ARGS LV1_7_IN_ARGS, LV1_2_OUT_ARGS
+
+#define LV1_0_IN_3_OUT_ARGS                LV1_3_OUT_ARGS
+#define LV1_1_IN_3_OUT_ARGS LV1_1_IN_ARGS, LV1_3_OUT_ARGS
+#define LV1_2_IN_3_OUT_ARGS LV1_2_IN_ARGS, LV1_3_OUT_ARGS
+#define LV1_3_IN_3_OUT_ARGS LV1_3_IN_ARGS, LV1_3_OUT_ARGS
+#define LV1_4_IN_3_OUT_ARGS LV1_4_IN_ARGS, LV1_3_OUT_ARGS
+#define LV1_5_IN_3_OUT_ARGS LV1_5_IN_ARGS, LV1_3_OUT_ARGS
+#define LV1_6_IN_3_OUT_ARGS LV1_6_IN_ARGS, LV1_3_OUT_ARGS
+#define LV1_7_IN_3_OUT_ARGS LV1_7_IN_ARGS, LV1_3_OUT_ARGS
+
+#define LV1_0_IN_4_OUT_ARGS                LV1_4_OUT_ARGS
+#define LV1_1_IN_4_OUT_ARGS LV1_1_IN_ARGS, LV1_4_OUT_ARGS
+#define LV1_2_IN_4_OUT_ARGS LV1_2_IN_ARGS, LV1_4_OUT_ARGS
+#define LV1_3_IN_4_OUT_ARGS LV1_3_IN_ARGS, LV1_4_OUT_ARGS
+#define LV1_4_IN_4_OUT_ARGS LV1_4_IN_ARGS, LV1_4_OUT_ARGS
+#define LV1_5_IN_4_OUT_ARGS LV1_5_IN_ARGS, LV1_4_OUT_ARGS
+#define LV1_6_IN_4_OUT_ARGS LV1_6_IN_ARGS, LV1_4_OUT_ARGS
+#define LV1_7_IN_4_OUT_ARGS LV1_7_IN_ARGS, LV1_4_OUT_ARGS
+
+#define LV1_0_IN_5_OUT_ARGS                LV1_5_OUT_ARGS
+#define LV1_1_IN_5_OUT_ARGS LV1_1_IN_ARGS, LV1_5_OUT_ARGS
+#define LV1_2_IN_5_OUT_ARGS LV1_2_IN_ARGS, LV1_5_OUT_ARGS
+#define LV1_3_IN_5_OUT_ARGS LV1_3_IN_ARGS, LV1_5_OUT_ARGS
+#define LV1_4_IN_5_OUT_ARGS LV1_4_IN_ARGS, LV1_5_OUT_ARGS
+#define LV1_5_IN_5_OUT_ARGS LV1_5_IN_ARGS, LV1_5_OUT_ARGS
+#define LV1_6_IN_5_OUT_ARGS LV1_6_IN_ARGS, LV1_5_OUT_ARGS
+#define LV1_7_IN_5_OUT_ARGS LV1_7_IN_ARGS, LV1_5_OUT_ARGS
+
+#define LV1_0_IN_6_OUT_ARGS                LV1_6_OUT_ARGS
+#define LV1_1_IN_6_OUT_ARGS LV1_1_IN_ARGS, LV1_6_OUT_ARGS
+#define LV1_2_IN_6_OUT_ARGS LV1_2_IN_ARGS, LV1_6_OUT_ARGS
+#define LV1_3_IN_6_OUT_ARGS LV1_3_IN_ARGS, LV1_6_OUT_ARGS
+#define LV1_4_IN_6_OUT_ARGS LV1_4_IN_ARGS, LV1_6_OUT_ARGS
+#define LV1_5_IN_6_OUT_ARGS LV1_5_IN_ARGS, LV1_6_OUT_ARGS
+#define LV1_6_IN_6_OUT_ARGS LV1_6_IN_ARGS, LV1_6_OUT_ARGS
+#define LV1_7_IN_6_OUT_ARGS LV1_7_IN_ARGS, LV1_6_OUT_ARGS
+
+#define LV1_0_IN_7_OUT_ARGS                LV1_7_OUT_ARGS
+#define LV1_1_IN_7_OUT_ARGS LV1_1_IN_ARGS, LV1_7_OUT_ARGS
+#define LV1_2_IN_7_OUT_ARGS LV1_2_IN_ARGS, LV1_7_OUT_ARGS
+#define LV1_3_IN_7_OUT_ARGS LV1_3_IN_ARGS, LV1_7_OUT_ARGS
+#define LV1_4_IN_7_OUT_ARGS LV1_4_IN_ARGS, LV1_7_OUT_ARGS
+#define LV1_5_IN_7_OUT_ARGS LV1_5_IN_ARGS, LV1_7_OUT_ARGS
+#define LV1_6_IN_7_OUT_ARGS LV1_6_IN_ARGS, LV1_7_OUT_ARGS
+#define LV1_7_IN_7_OUT_ARGS LV1_7_IN_ARGS, LV1_7_OUT_ARGS
+
+/*
+ * This LV1_CALL() macro is for use by callers.  It expands into an
+ * inline call wrapper and an underscored HV call declaration.  The
+ * wrapper can be used to instrument the lv1 call interface.  The
+ * file lv1call.S defines its own LV1_CALL() macro to expand into
+ * the actual underscored call definition.
+ */
+
+#if !defined(LV1_CALL)
+#define LV1_CALL(name, in, out, num)                               \
+  extern s64 _lv1_##name(LV1_##in##_IN_##out##_OUT_ARG_DECL);      \
+  static inline int lv1_##name(LV1_##in##_IN_##out##_OUT_ARG_DECL) \
+    {return _lv1_##name(LV1_##in##_IN_##out##_OUT_ARGS);}
+#endif
+
+#endif /* !defined(__ASSEMBLY__) */
+
+/* lv1 call table */
+
+LV1_CALL(allocate_memory,                               4, 2,   0 )
+LV1_CALL(write_htab_entry,                              4, 0,   1 )
+LV1_CALL(construct_virtual_address_space,               3, 2,   2 )
+LV1_CALL(invalidate_htab_entries,                       5, 0,   3 )
+LV1_CALL(get_virtual_address_space_id_of_ppe,           1, 1,   4 )
+LV1_CALL(query_logical_partition_address_region_info,   1, 5,   6 )
+LV1_CALL(select_virtual_address_space,                  1, 0,   7 )
+LV1_CALL(pause,                                         1, 0,   9 )
+LV1_CALL(destruct_virtual_address_space,                1, 0,  10 )
+LV1_CALL(configure_irq_state_bitmap,                    3, 0,  11 )
+LV1_CALL(connect_irq_plug_ext,                          5, 0,  12 )
+LV1_CALL(release_memory,                                1, 0,  13 )
+LV1_CALL(disconnect_irq_plug_ext,                       3, 0,  17 )
+LV1_CALL(construct_event_receive_port,                  0, 1,  18 )
+LV1_CALL(destruct_event_receive_port,                   1, 0,  19 )
+LV1_CALL(send_event_locally,                            1, 0,  24 )
+LV1_CALL(end_of_interrupt,                              1, 0,  27 )
+LV1_CALL(connect_irq_plug,                              2, 0,  28 )
+LV1_CALL(disconnect_irq_plug,                           1, 0,  29 )
+LV1_CALL(end_of_interrupt_ext,                          3, 0,  30 )
+LV1_CALL(did_update_interrupt_mask,                     2, 0,  31 )
+LV1_CALL(shutdown_logical_partition,                    1, 0,  44 )
+LV1_CALL(destruct_logical_spe,                          1, 0,  54 )
+LV1_CALL(construct_logical_spe,                         7, 6,  57 )
+LV1_CALL(set_spe_interrupt_mask,                        3, 0,  61 )
+LV1_CALL(set_spe_transition_notifier,                   3, 0,  64 )
+LV1_CALL(disable_logical_spe,                           2, 0,  65 )
+LV1_CALL(clear_spe_interrupt_status,                    4, 0,  66 )
+LV1_CALL(get_spe_interrupt_status,                      2, 1,  67 )
+LV1_CALL(get_logical_ppe_id,                            0, 1,  69 )
+LV1_CALL(set_interrupt_mask,                            5, 0,  73 )
+LV1_CALL(get_logical_partition_id,                      0, 1,  74 )
+LV1_CALL(configure_execution_time_variable,             1, 0,  77 )
+LV1_CALL(get_spe_irq_outlet,                            2, 1,  78 )
+LV1_CALL(set_spe_privilege_state_area_1_register,       3, 0,  79 )
+LV1_CALL(create_repository_node,                        6, 0,  90 )
+LV1_CALL(get_repository_node_value,                     5, 2,  91 )
+LV1_CALL(modify_repository_node_value,                  6, 0,  92 )
+LV1_CALL(remove_repository_node,                        4, 0,  93 )
+LV1_CALL(read_htab_entries,                             2, 5,  95 )
+LV1_CALL(set_dabr,                                      2, 0,  96 )
+LV1_CALL(get_total_execution_time,                      2, 1, 103 )
+LV1_CALL(construct_io_irq_outlet,                       1, 1, 120 )
+LV1_CALL(destruct_io_irq_outlet,                        1, 0, 121 )
+LV1_CALL(map_htab,                                      1, 1, 122 )
+LV1_CALL(unmap_htab,                                    1, 0, 123 )
+LV1_CALL(get_version_info,                              0, 1, 127 )
+LV1_CALL(insert_htab_entry,                             6, 3, 158 )
+LV1_CALL(read_virtual_uart,                             3, 1, 162 )
+LV1_CALL(write_virtual_uart,                            3, 1, 163 )
+LV1_CALL(set_virtual_uart_param,                        3, 0, 164 )
+LV1_CALL(get_virtual_uart_param,                        2, 1, 165 )
+LV1_CALL(configure_virtual_uart_irq,                    1, 1, 166 )
+LV1_CALL(open_device,                                   3, 0, 170 )
+LV1_CALL(close_device,                                  2, 0, 171 )
+LV1_CALL(map_device_mmio_region,                        5, 1, 172 )
+LV1_CALL(unmap_device_mmio_region,                      3, 0, 173 )
+LV1_CALL(allocate_device_dma_region,                    5, 1, 174 )
+LV1_CALL(free_device_dma_region,                        3, 0, 175 )
+LV1_CALL(map_device_dma_region,                         6, 0, 176 )
+LV1_CALL(unmap_device_dma_region,                       4, 0, 177 )
+LV1_CALL(net_add_multicast_address,                     4, 0, 185 )
+LV1_CALL(net_remove_multicast_address,                  4, 0, 186 )
+LV1_CALL(net_start_tx_dma,                              4, 0, 187 )
+LV1_CALL(net_stop_tx_dma,                               3, 0, 188 )
+LV1_CALL(net_start_rx_dma,                              4, 0, 189 )
+LV1_CALL(net_stop_rx_dma,                               3, 0, 190 )
+LV1_CALL(net_set_interrupt_status_indicator,            4, 0, 191 )
+LV1_CALL(net_set_interrupt_mask,                        4, 0, 193 )
+LV1_CALL(net_control,                                   6, 2, 194 )
+LV1_CALL(connect_interrupt_event_receive_port,          4, 0, 197 )
+LV1_CALL(disconnect_interrupt_event_receive_port,       4, 0, 198 )
+LV1_CALL(get_spe_all_interrupt_statuses,                1, 1, 199 )
+LV1_CALL(deconfigure_virtual_uart_irq,                  0, 0, 202 )
+LV1_CALL(enable_logical_spe,                            2, 0, 207 )
+LV1_CALL(gpu_open,                                      1, 0, 210 )
+LV1_CALL(gpu_close,                                     0, 0, 211 )
+LV1_CALL(gpu_device_map,                                1, 2, 212 )
+LV1_CALL(gpu_device_unmap,                              1, 0, 213 )
+LV1_CALL(gpu_memory_allocate,                           5, 2, 214 )
+LV1_CALL(gpu_memory_free,                               1, 0, 216 )
+LV1_CALL(gpu_context_allocate,                          2, 5, 217 )
+LV1_CALL(gpu_context_free,                              1, 0, 218 )
+LV1_CALL(gpu_context_iomap,                             5, 0, 221 )
+LV1_CALL(gpu_context_attribute,                         6, 0, 225 )
+LV1_CALL(gpu_context_intr,                              1, 1, 227 )
+LV1_CALL(gpu_attribute,                                 5, 0, 228 )
+LV1_CALL(get_rtc,                                       0, 2, 232 )
+LV1_CALL(set_ppe_periodic_tracer_frequency,             1, 0, 240 )
+LV1_CALL(start_ppe_periodic_tracer,                     5, 0, 241 )
+LV1_CALL(stop_ppe_periodic_tracer,                      1, 1, 242 )
+LV1_CALL(storage_read,                                  6, 1, 245 )
+LV1_CALL(storage_write,                                 6, 1, 246 )
+LV1_CALL(storage_send_device_command,                   6, 1, 248 )
+LV1_CALL(storage_get_async_status,                      1, 2, 249 )
+LV1_CALL(storage_check_async_status,                    2, 1, 254 )
+LV1_CALL(panic,                                         1, 0, 255 )
+LV1_CALL(construct_lpm,                                 6, 3, 140 )
+LV1_CALL(destruct_lpm,                                  1, 0, 141 )
+LV1_CALL(start_lpm,                                     1, 0, 142 )
+LV1_CALL(stop_lpm,                                      1, 1, 143 )
+LV1_CALL(copy_lpm_trace_buffer,                         3, 1, 144 )
+LV1_CALL(add_lpm_event_bookmark,                        5, 0, 145 )
+LV1_CALL(delete_lpm_event_bookmark,                     3, 0, 146 )
+LV1_CALL(set_lpm_interrupt_mask,                        3, 1, 147 )
+LV1_CALL(get_lpm_interrupt_status,                      1, 1, 148 )
+LV1_CALL(set_lpm_general_control,                       5, 2, 149 )
+LV1_CALL(set_lpm_interval,                              3, 1, 150 )
+LV1_CALL(set_lpm_trigger_control,                       3, 1, 151 )
+LV1_CALL(set_lpm_counter_control,                       4, 1, 152 )
+LV1_CALL(set_lpm_group_control,                         3, 1, 153 )
+LV1_CALL(set_lpm_debug_bus_control,                     3, 1, 154 )
+LV1_CALL(set_lpm_counter,                               5, 2, 155 )
+LV1_CALL(set_lpm_signal,                                7, 0, 156 )
+LV1_CALL(set_lpm_spr_trigger,                           2, 0, 157 )
+
+#endif

^ permalink raw reply

* [PATCH 7/16] powerpc: add support for ps3 platform
From: Geoff Levand @ 2006-11-10 20:02 UTC (permalink / raw)
  To: Paul Mackerras; +Cc: linuxppc-dev

This adds the core platform support for the PS3 game console and other
platforms using the PS3 Platform hypervisor.


Signed-off-by: Geoff Levand <geoffrey.levand@am.sony.com>

---
 MAINTAINERS                             |    7 
 arch/powerpc/Kconfig                    |   11 
 arch/powerpc/platforms/Makefile         |    1 
 arch/powerpc/platforms/ps3pf/Kconfig    |   32 +
 arch/powerpc/platforms/ps3pf/Makefile   |    2 
 arch/powerpc/platforms/ps3pf/mm.c       |  872 ++++++++++++++++++++++++++++++++
 arch/powerpc/platforms/ps3pf/platform.h |   59 ++
 arch/powerpc/platforms/ps3pf/setup.c    |  177 ++++++
 arch/powerpc/platforms/ps3pf/smp.c      |  164 ++++++
 arch/powerpc/platforms/ps3pf/time.c     |  105 +++
 include/asm-powerpc/ps3pf.h             |  240 ++++++++
 11 files changed, 1669 insertions(+), 1 deletion(-)

Index: cell--common--6/MAINTAINERS
===================================================================
--- cell--common--6.orig/MAINTAINERS
+++ cell--common--6/MAINTAINERS
@@ -2423,6 +2423,13 @@
 W:	http://www.pnd-pc.demon.co.uk/promise/
 S:	Maintained
 
+PS3PF (PS3 PLATFORM) SUPPORT
+P:	Geoff Levand
+M:	geoffrey.levand@am.sony.com
+L:	linuxppc-dev@ozlabs.org
+L:	cbe-oss-dev@ozlabs.org
+S:	Supported
+
 PVRUSB2 VIDEO4LINUX DRIVER
 P:	Mike Isely
 M:	isely@pobox.com
Index: cell--common--6/arch/powerpc/Kconfig
===================================================================
--- cell--common--6.orig/arch/powerpc/Kconfig
+++ cell--common--6/arch/powerpc/Kconfig
@@ -461,6 +461,14 @@
 	depends on PPC_RTAS
 	default n
 
+config PS3PF
+	bool "PS3 Platform"
+	depends on PPC_MULTIPLATFORM && PPC64
+	select PPC_CELL
+	help
+	  This option enables support for the PS3 game console
+	  and other platforms using the PS3 Platform hypervisor.
+
 config XICS
 	depends on PPC_PSERIES
 	bool
@@ -604,6 +612,7 @@
 source arch/powerpc/platforms/86xx/Kconfig
 source arch/powerpc/platforms/8xx/Kconfig
 source arch/powerpc/platforms/cell/Kconfig
+source arch/powerpc/platforms/ps3pf/Kconfig
 
 menu "Kernel options"
 
@@ -876,7 +885,7 @@
 	bool "PCI support" if 40x || CPM2 || PPC_83xx || PPC_85xx || PPC_86xx \
 		|| PPC_MPC52xx || (EMBEDDED && PPC_ISERIES) || MPC7448HPC2
 	default y if !40x && !CPM2 && !8xx && !APUS && !PPC_83xx \
-		&& !PPC_85xx && !PPC_86xx
+		&& !PPC_85xx && !PPC_86xx && !PS3PF
 	default PCI_PERMEDIA if !4xx && !CPM2 && !8xx && APUS
 	default PCI_QSPAN if !4xx && !CPM2 && 8xx
 	help
Index: cell--common--6/arch/powerpc/platforms/Makefile
===================================================================
--- cell--common--6.orig/arch/powerpc/platforms/Makefile
+++ cell--common--6/arch/powerpc/platforms/Makefile
@@ -15,4 +15,5 @@
 obj-$(CONFIG_PPC_MAPLE)		+= maple/
 obj-$(CONFIG_PPC_PASEMI)		+= pasemi/
 obj-$(CONFIG_PPC_CELL)		+= cell/
+obj-$(CONFIG_PS3PF)		+= ps3pf/
 obj-$(CONFIG_EMBEDDED6xx)	+= embedded6xx/
Index: cell--common--6/arch/powerpc/platforms/ps3pf/Kconfig
===================================================================
--- /dev/null
+++ cell--common--6/arch/powerpc/platforms/ps3pf/Kconfig
@@ -0,0 +1,32 @@
+menu "PS3 Platform Options"
+	depends on PS3PF
+
+config PS3PF_HTAB_SIZE
+	depends on PS3PF
+	int "PS3 Platform pagetable size"
+	range 18 20
+	default 20
+	help
+	  This option is only for experts who may have the desire to fine
+	  tune the pagetable size on their system.  The value here is
+	  expressed as the log2 of the page table size.  Valid values are
+	  18, 19, and 20, corresponding to 256KB, 512KB and 1MB respectively.
+
+	  If unsure, choose the default (20) with the confidence that your
+	  system will have optimal runtime performance.
+
+config PS3PF_DYNAMIC_DMA
+	depends on PS3PF && EXPERIMENTAL
+	bool "PS3 Platform dynamic DMA page table management"
+	default n
+	help
+	  This option will enable kernel support to take advantage of the
+	  per device dynamic DMA page table management provided by the Cell
+	  processor's IO Controller.  This support incurs some runtime
+	  overhead and also slightly increases kernel memory usage.  The
+	  current implementation should be considered experimental.
+
+	  This support is mainly for Linux kernel development.  If unsure,
+	  say N.
+
+endmenu
Index: cell--common--6/arch/powerpc/platforms/ps3pf/Makefile
===================================================================
--- /dev/null
+++ cell--common--6/arch/powerpc/platforms/ps3pf/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_PS3PF) += setup.o mm.o smp.o time.o hvcall.o htab.o repository.o
+obj-$(CONFIG_PS3PF) += interrupt.o exports.o
Index: cell--common--6/arch/powerpc/platforms/ps3pf/mm.c
===================================================================
--- /dev/null
+++ cell--common--6/arch/powerpc/platforms/ps3pf/mm.c
@@ -0,0 +1,872 @@
+/*
+ * mm.c - PS3 Platform address space management.
+ *
+ *  Copyright (C) 2006 Sony Computer Entertainment Inc.
+ *  Copyright 2006 Sony Corp.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#undef DEBUG
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/memory_hotplug.h>
+
+#include <asm/lmb.h>
+#include <asm/udbg.h>
+#include <asm/ps3pf.h>
+#include <asm/lv1call.h>
+
+#include "platform.h"
+
+#if defined(DEBUG)
+#undef pr_debug
+#define pr_debug(fmt...) udbg_printf(fmt)
+#endif
+
+enum {
+#if defined(CONFIG_PS3PF_USE_LPAR_ADDR)
+	USE_LPAR_ADDR = 1,
+#else
+	USE_LPAR_ADDR = 0,
+#endif
+#if defined(CONFIG_PS3PF_DYNAMIC_DMA)
+	USE_DYNAMIC_DMA = 1,
+#else
+	USE_DYNAMIC_DMA = 0,
+#endif
+};
+
+enum page_size {
+	page_size_4k = 12U,
+	page_size_64k = 16U,
+	page_size_16m = 24U,
+};
+
+enum allocate_memory {
+	/* bit 63: transferability */
+	LV1_AM_TF_NO = 0x00,
+	LV1_AM_TF_YES = 0x01,
+	/* bit 62: destruction scheme */
+	LV1_AM_DS_NO_CONNECTIONS = 0x00,
+	LV1_AM_DS_ANYTIME = 0x02,
+	/* bit 61: fail or alternative */
+	LV1_AM_FA_FAIL = 0x00,
+	LV1_AM_FA_ALTERNATIVE = 0x04,
+	/* bit 60: lpar address */
+	LV1_AM_ADDR_ANY = 0x00,
+	LV1_AM_ADDR_0 = 0x08,
+};
+
+static unsigned long make_page_sizes(enum page_size a, enum page_size b)
+{
+	return ((unsigned long)a << 56) | ((unsigned long)b << 48);
+}
+
+/* valid htab sizes are {18,19,20} = 256K, 512K, 1M */
+
+enum {
+	htab_size_max = 20U, /* HV limit of 1MB */
+	htab_size_min = 18U, /* CPU limit of 256KB */
+};
+
+/*============================================================================*/
+/* virtual address space routines                                             */
+/*============================================================================*/
+
+/**
+ * struct mem_region - memory region structure
+ * @base: base address
+ * @size: size in bytes
+ * @offset: difference between base and rm.size
+ */
+
+struct mem_region {
+	unsigned long base;
+	unsigned long size;
+	unsigned long offset;
+};
+
+/**
+ * struct map - address space state variables holder
+ * @total: total memory available as reported by HV
+ * @vas_id - HV virtual address space id
+ * @htab_size: htab size in bytes
+ *
+ * The HV virtual address space (vas) allows for hotplug memory regions.
+ * Memory regions can be created and destroyed in the vas at runtime.
+ * @rm: real mode (bootmem) region
+ * @r1: hotplug memory region(s)
+ *
+ * ps3pf addresses
+ * virt_addr: a cpu 'translated' effective address
+ * phys_addr: an address in what Linux thinks is the physical address space
+ * lpar_addr: an address in the HV virtual address space
+ * bus_addr: an io controller 'translated' address on a device bus
+ */
+
+struct map {
+	unsigned long total;
+	unsigned long vas_id;
+	unsigned long htab_size;
+	struct mem_region rm;
+	struct mem_region r1;
+};
+
+#define debug_dump_map(x) _debug_dump_map(x, __func__, __LINE__)
+static void _debug_dump_map(const struct map* m, const char* func, int line)
+{
+	pr_debug("%s:%d: map.total     = %lxh\n", func, line, m->total);
+	pr_debug("%s:%d: map.rm.size   = %lxh\n", func, line, m->rm.size);
+	pr_debug("%s:%d: map.vas_id    = %lu\n", func, line, m->vas_id);
+	pr_debug("%s:%d: map.htab_size = %lxh\n", func, line, m->htab_size);
+	pr_debug("%s:%d: map.r1.base   = %lxh\n", func, line, m->r1.base);
+	pr_debug("%s:%d: map.r1.offset = %lxh\n", func, line, m->r1.offset);
+	pr_debug("%s:%d: map.r1.size   = %lxh\n", func, line, m->r1.size);
+}
+
+static struct map map;
+
+/**
+ * ps3pf_mm_phys_to_lpar - translate a linux physical address to lpar address
+ * @phys_addr: linux physical address
+ */
+
+unsigned long ps3pf_mm_phys_to_lpar(unsigned long phys_addr)
+{
+	BUG_ON(is_kernel_addr(phys_addr));
+	if (USE_LPAR_ADDR)
+		return phys_addr;
+	else
+		return (phys_addr < map.rm.size || phys_addr >= map.total)
+			? phys_addr : phys_addr + map.r1.offset;
+}
+
+EXPORT_SYMBOL(ps3pf_mm_phys_to_lpar);
+
+/**
+ * ps3pf_mm_vas_create - create the virtual address space
+ */
+
+void __init ps3pf_mm_vas_create(unsigned long* htab_size)
+{
+	int result;
+	unsigned long start_address;
+	unsigned long size;
+	unsigned long access_right;
+	unsigned long max_page_size;
+	unsigned long flags;
+
+	result = lv1_query_logical_partition_address_region_info(0,
+		&start_address, &size, &access_right, &max_page_size,
+		&flags);
+
+	if (result) {
+		pr_debug("%s:%d: lv1_query_logical_partition_address_region_info "
+			"failed: %s\n", __func__, __LINE__,
+			ps3pf_result(result));
+		goto fail;
+	}
+
+	if (max_page_size < page_size_16m) {
+		pr_debug("%s:%d: bad max_page_size %lxh\n", __func__, __LINE__,
+			max_page_size);
+		goto fail;
+	}
+
+	BUILD_BUG_ON(CONFIG_PS3PF_HTAB_SIZE > htab_size_max);
+	BUILD_BUG_ON(CONFIG_PS3PF_HTAB_SIZE < htab_size_min);
+
+	result = lv1_construct_virtual_address_space(CONFIG_PS3PF_HTAB_SIZE,
+			2, make_page_sizes(page_size_16m, page_size_64k),
+			&map.vas_id, &map.htab_size);
+
+	if (result) {
+		pr_debug("%s:%d: lv1_construct_virtual_address_space failed: %s\n",
+			__func__, __LINE__, ps3pf_result(result));
+		goto fail;
+	}
+
+	result = lv1_select_virtual_address_space(map.vas_id);
+
+	if (result) {
+		pr_debug("%s:%d: lv1_select_virtual_address_space failed: %s\n",
+			__func__, __LINE__, ps3pf_result(result));
+		goto fail;
+	}
+
+	*htab_size = map.htab_size;
+
+	debug_dump_map(&map);
+
+	return;
+
+fail:
+	panic("ps3pf_mm_vas_create failed");
+}
+
+/**
+ * ps3pf_mm_vas_destroy -
+ */
+
+void ps3pf_mm_vas_destroy(void)
+{
+	if (map.vas_id) {
+		lv1_select_virtual_address_space(0);
+		lv1_destruct_virtual_address_space(map.vas_id);
+		map.vas_id = 0;
+	}
+}
+
+/*============================================================================*/
+/* memory hotplug routines                                                    */
+/*============================================================================*/
+
+/**
+ * ps3pf_mm_region_create - create a memory region in the vas
+ * @r: pointer to a struct mem_region to accept initialized values
+ * @size: requested region size
+ *
+ * This implementation creates the region with the vas large page size.
+ * @size is rounded down to a multiple of the vas large page size.
+ */
+
+int ps3pf_mm_region_create(struct mem_region *r, unsigned long size)
+{
+	int result;
+	unsigned long muid;
+
+	r->size = _ALIGN_DOWN(size, 1 << page_size_16m);
+
+	pr_debug("%s:%d requested  %lxh\n", __func__, __LINE__, size);
+	pr_debug("%s:%d actual     %lxh\n", __func__, __LINE__, r->size);
+	pr_debug("%s:%d difference %lxh (%luMB)\n", __func__, __LINE__,
+		(unsigned long)(size - r->size),
+		(size - r->size) / 1024 / 1024);
+
+	if (r->size == 0) {
+		pr_debug("%s:%d: size == 0\n", __func__, __LINE__);
+		result = -1;
+		goto zero_region;
+	}
+
+	result = lv1_allocate_memory(r->size, page_size_16m, 0,
+		LV1_AM_TF_NO | LV1_AM_DS_ANYTIME | LV1_AM_FA_ALTERNATIVE
+		| LV1_AM_ADDR_ANY, &r->base, &muid);
+
+	if (result || r->base < map.rm.size) {
+		pr_debug("%s:%d: lv1_allocate_memory failed: %s\n",
+			__func__, __LINE__, ps3pf_result(result));
+		goto zero_region;
+	}
+
+	r->offset = r->base - map.rm.size;
+	return result;
+
+zero_region:
+	r->size = r->base = r->offset = 0;
+	return result;
+}
+
+/**
+ * ps3pf_mm_region_destroy - destroy a memory region
+ * @r: pointer to struct mem_region
+ */
+
+void ps3pf_mm_region_destroy(struct mem_region *r)
+{
+	if (r->base) {
+		lv1_release_memory(r->base);
+		r->size = r->base = r->offset = 0;
+		map.total = map.rm.size;
+	}
+}
+
+/**
+ * ps3pf_mm_add_memory - hot add memory
+ * @r: pointer to dma region structure
+ * @lpar_addr: HV lpar address
+ */
+
+static int __init ps3pf_mm_add_memory(void)
+{
+	int result;
+	unsigned long start_addr;
+	unsigned long start_pfn;
+	unsigned long nr_pages;
+
+	BUG_ON(!mem_init_done);
+
+	start_addr = USE_LPAR_ADDR ? map.r1.base : map.rm.size;
+	start_pfn = start_addr >> PAGE_SHIFT;
+	nr_pages = (map.r1.size + PAGE_SIZE - 1) >> PAGE_SHIFT;
+
+	pr_debug("%s:%d: start_addr %lxh, start_pfn %lxh, nr_pages %lxh\n",
+		__func__, __LINE__, start_addr, start_pfn, nr_pages);
+
+	result = add_memory(0, start_addr, map.r1.size);
+
+	if (result) {
+		pr_debug("%s:%d: add_memory failed: (%d)\n",
+			__func__, __LINE__, result);
+		return result;
+	}
+
+	result = online_pages(start_pfn, nr_pages);
+
+	if (result)
+		pr_debug("%s:%d: online_pages failed: (%d)\n",
+			__func__, __LINE__, result);
+
+	return result;
+}
+
+core_initcall(ps3pf_mm_add_memory);
+
+/*============================================================================*/
+/* dma routines                                                               */
+/*============================================================================*/
+
+/**
+ * dma_lpar_to_bus - Translate an lpar address to ioc mapped bus address.
+ * @r: pointer to dma region structure
+ * @lpar_addr: HV lpar address
+ */
+
+static unsigned long dma_lpar_to_bus(struct ps3pf_dma_region *r,
+	unsigned long lpar_addr)
+{
+	BUG_ON(lpar_addr >= map.r1.base + map.r1.size);
+	return r->bus_addr + (lpar_addr <= map.rm.size ? lpar_addr
+		: lpar_addr - map.r1.offset);
+}
+
+#define dma_dump_region(_a) _dma_dump_region(_a, __func__, __LINE__)
+static void _dma_dump_region(const struct ps3pf_dma_region *r, const char* func,
+	int line)
+{
+	pr_debug("%s:%d: dev        %u:%u\n", func, line, r->did.bus_id,
+		r->did.dev_id);
+	pr_debug("%s:%d: page_size  %u\n", func, line, r->page_size);
+	pr_debug("%s:%d: bus_addr   %lxh\n", func, line, r->bus_addr);
+	pr_debug("%s:%d: len        %lxh\n", func, line, r->len);
+}
+
+/**
+ * dma_chunk - A chunk of dma pages mapped by the io controller.
+ * @region - The dma region that owns this chunk.
+ * @lpar_addr: Starting lpar address of the area to map.
+ * @bus_addr: Starting ioc bus address of the area to map.
+ * @len: Length in bytes of the area to map.
+ * @link: A struct list_head used with struct ps3pf_dma_region.chunk_list, the
+ * list of all chuncks owned by the region.
+ *
+ * This implementation uses a very simple dma page manager
+ * based on the dma_chunk structure.  This scheme assumes
+ * that all drivers use very well behaved dma ops.
+ */
+
+struct dma_chunk {
+	struct ps3pf_dma_region *region;
+	unsigned long lpar_addr;
+	unsigned long bus_addr;
+	unsigned long len;
+	struct list_head link;
+	unsigned int usage_count;
+};
+
+#define dma_dump_chunk(_a) _dma_dump_chunk(_a, __func__, __LINE__)
+static void _dma_dump_chunk (const struct dma_chunk* c, const char* func,
+	int line)
+{
+	pr_debug("%s:%d: r.dev        %u:%u\n", func, line,
+		c->region->did.bus_id, c->region->did.dev_id);
+	pr_debug("%s:%d: r.bus_addr   %lxh\n", func, line, c->region->bus_addr);
+	pr_debug("%s:%d: r.page_size  %u\n", func, line, c->region->page_size);
+	pr_debug("%s:%d: r.len        %lxh\n", func, line, c->region->len);
+	pr_debug("%s:%d: c.lpar_addr  %lxh\n", func, line, c->lpar_addr);
+	pr_debug("%s:%d: c.bus_addr   %lxh\n", func, line, c->bus_addr);
+	pr_debug("%s:%d: c.len        %lxh\n", func, line, c->len);
+}
+
+static struct dma_chunk * dma_find_chunk(struct ps3pf_dma_region *r,
+	unsigned long bus_addr, unsigned long len)
+{
+	struct dma_chunk *c;
+	unsigned long aligned_bus = _ALIGN_DOWN(bus_addr, 1 << r->page_size);
+	unsigned long aligned_len = _ALIGN_UP(len, 1 << r->page_size);
+
+	list_for_each_entry(c, &r->chunk_list.head, link) {
+		/* intersection */
+		if (aligned_bus >= c->bus_addr
+			&& aligned_bus < c->bus_addr + c->len
+			&& aligned_bus + aligned_len <= c->bus_addr + c->len) {
+			return c;
+		}
+		/* below */
+		if (aligned_bus + aligned_len <= c->bus_addr) {
+			continue;
+		}
+		/* above */
+		if (aligned_bus >= c->bus_addr + c->len) {
+			continue;
+		}
+
+		/* we don't handle the multi-chunk case for now */
+
+		dma_dump_chunk(c);
+		BUG();
+	}
+	return NULL;
+}
+
+static int dma_free_chunk(struct dma_chunk *c)
+{
+	int result = 0;
+
+	if (c->bus_addr) {
+		result = lv1_unmap_device_dma_region(c->region->did.bus_id,
+			c->region->did.dev_id, c->bus_addr, c->len);
+		BUG_ON(result);
+	}
+
+	kfree(c);
+	return result;
+}
+
+/**
+ * dma_map_pages - Maps dma pages into the io controller bus address space.
+ * @r: Pointer to a struct ps3pf_dma_region.
+ * @virt_addr: Starting virtual address of the area to map.
+ * @len: Length in bytes of the area to map.
+ * c_out: A pointer to receive an allocated struct dma_chunk for this area.
+ *
+ * This is the lowest level dma mapping routine, and is the one that will
+ * make the HV call to add the pages into the io controller address space.
+ */
+
+static int dma_map_pages(struct ps3pf_dma_region *r, unsigned long virt_addr,
+	unsigned long len, struct dma_chunk **c_out)
+{
+	int result;
+	unsigned long phys_addr;
+	struct dma_chunk *c;
+
+	phys_addr = is_kernel_addr(virt_addr) ? __pa(virt_addr) : virt_addr;
+
+	c = kzalloc(sizeof(struct dma_chunk), GFP_ATOMIC);
+
+	if (!c) {
+		result = -ENOMEM;
+		goto fail_alloc;
+	}
+
+	c->region = r;
+	c->lpar_addr = ps3pf_mm_phys_to_lpar(phys_addr);
+	c->bus_addr = dma_lpar_to_bus(r, c->lpar_addr);
+	c->len = len;
+
+	result = lv1_map_device_dma_region(c->region->did.bus_id,
+		c->region->did.dev_id, c->lpar_addr, c->bus_addr, c->len,
+		0xf800000000000000UL);
+
+	if (result) {
+		pr_debug("%s:%d: lv1_map_device_dma_region failed: %s\n",
+			__func__, __LINE__, ps3pf_result(result));
+		goto fail_map;
+	}
+
+	list_add(&c->link, &r->chunk_list.head);
+
+	*c_out = c;
+	return 0;
+
+fail_map:
+	kfree(c);
+fail_alloc:
+	*c_out = NULL;
+	pr_debug(" <- %s:%d\n", __func__, __LINE__);
+	return result;
+}
+
+/**
+ * dma_region_create - Create a device dma region.
+ * @r: Pointer to a struct ps3pf_dma_region.
+ *
+ * This is the lowest level dma region create routine, and is the one that
+ * will make the HV call to create the region.
+ */
+
+static int dma_region_create(struct ps3pf_dma_region* r)
+{
+	int result;
+
+	r->len = _ALIGN_UP(map.total, 1 << r->page_size);
+	INIT_LIST_HEAD(&r->chunk_list.head);
+	spin_lock_init(&r->chunk_list.lock);
+
+	result = lv1_allocate_device_dma_region(r->did.bus_id, r->did.dev_id,
+		r->len, r->page_size, r->region_type, &r->bus_addr);
+
+	dma_dump_region(r);
+
+	if (result) {
+		pr_debug("%s:%d: lv1_allocate_device_dma_region failed: %s\n",
+			__func__, __LINE__, ps3pf_result(result));
+		r->len = r->bus_addr = 0;
+	}
+
+	return result;
+}
+
+/**
+ * dma_region_free - Free a device dma region.
+ * @r: Pointer to a struct ps3pf_dma_region.
+ *
+ * This is the lowest level dma region free routine, and is the one that
+ * will make the HV call to free the region.
+ */
+
+static int dma_region_free(struct ps3pf_dma_region* r)
+{
+	int result;
+	struct dma_chunk *c;
+	struct dma_chunk *tmp;
+
+	list_for_each_entry_safe(c, tmp, &r->chunk_list.head, link) {
+		list_del(&c->link);
+		dma_free_chunk(c);
+	}
+
+	result = lv1_free_device_dma_region(r->did.bus_id, r->did.dev_id,
+		r->bus_addr);
+
+	if (result)
+		pr_debug("%s:%d: lv1_free_device_dma_region failed: %s\n",
+			__func__, __LINE__, ps3pf_result(result));
+
+	r->len = r->bus_addr = 0;
+
+	return result;
+}
+
+/**
+ * dma_map_area - Map an area of memory into a device dma region.
+ * @r: Pointer to a struct ps3pf_dma_region.
+ * @virt_addr: Starting virtual address of the area to map.
+ * @len: Length in bytes of the area to map.
+ * @bus_addr: A pointer to return the starting ioc bus address of the area to
+ * map.
+ *
+ * This is the common dma mapping routine.
+ */
+
+static int dma_map_area(struct ps3pf_dma_region *r, unsigned long virt_addr,
+	unsigned long len, unsigned long *bus_addr)
+{
+	int result;
+	unsigned long flags;
+	struct dma_chunk *c;
+	unsigned long phys_addr = is_kernel_addr(virt_addr) ? __pa(virt_addr)
+		: virt_addr;
+
+	*bus_addr = dma_lpar_to_bus(r, ps3pf_mm_phys_to_lpar(phys_addr));
+
+	if (!USE_DYNAMIC_DMA) {
+		unsigned long lpar_addr = ps3pf_mm_phys_to_lpar(phys_addr);
+		pr_debug(" -> %s:%d\n", __func__, __LINE__);
+		pr_debug("%s:%d virt_addr %lxh\n", __func__, __LINE__,
+			virt_addr);
+		pr_debug("%s:%d phys_addr %lxh\n", __func__, __LINE__,
+			phys_addr);
+		pr_debug("%s:%d lpar_addr %lxh\n", __func__, __LINE__,
+			lpar_addr);
+		pr_debug("%s:%d len       %lxh\n", __func__, __LINE__, len);
+		pr_debug("%s:%d bus_addr  %lxh (%lxh)\n", __func__, __LINE__,
+		*bus_addr, len);
+	}
+
+	spin_lock_irqsave(&r->chunk_list.lock, flags);
+	c = dma_find_chunk(r, *bus_addr, len);
+
+	if (c) {
+		c->usage_count++;
+		spin_unlock_irqrestore(&r->chunk_list.lock, flags);
+		return 0;
+	}
+
+	result = dma_map_pages(r, _ALIGN_DOWN(virt_addr, 1 << r->page_size),
+		_ALIGN_UP(len, 1 << r->page_size), &c);
+
+	if (result) {
+		*bus_addr = 0;
+		pr_debug("%s:%d: dma_map_pages failed (%d)\n",
+			__func__, __LINE__, result);
+		spin_unlock_irqrestore(&r->chunk_list.lock, flags);
+		return result;
+	}
+
+	c->usage_count = 1;
+
+	spin_unlock_irqrestore(&r->chunk_list.lock, flags);
+	return result;
+}
+
+/**
+ * dma_unmap_area - Unmap an area of memory from a device dma region.
+ * @r: Pointer to a struct ps3pf_dma_region.
+ * @bus_addr: The starting ioc bus address of the area to unmap.
+ * @len: Length in bytes of the area to unmap.
+ *
+ * This is the common dma unmap routine.
+ */
+
+int dma_unmap_area(struct ps3pf_dma_region *r, unsigned long bus_addr,
+	unsigned long len)
+{
+	unsigned long flags;
+	struct dma_chunk *c;
+
+	spin_lock_irqsave(&r->chunk_list.lock, flags);
+	c = dma_find_chunk(r, bus_addr, len);
+
+	if (!c) {
+		unsigned long aligned_bus = _ALIGN_DOWN(bus_addr,
+			1 << r->page_size);
+		unsigned long aligned_len = _ALIGN_UP(len, 1 << r->page_size);
+		pr_debug("%s:%d: not found: bus_addr %lxh\n",
+			__func__, __LINE__, bus_addr);
+		pr_debug("%s:%d: not found: len %lxh\n",
+			__func__, __LINE__, len);
+		pr_debug("%s:%d: not found: aligned_bus %lxh\n",
+			__func__, __LINE__, aligned_bus);
+		pr_debug("%s:%d: not found: aligned_len %lxh\n",
+			__func__, __LINE__, aligned_len);
+		BUG();
+	}
+
+	c->usage_count--;
+
+	if (!c->usage_count) {
+		list_del(&c->link);
+		dma_free_chunk(c);
+	}
+
+	spin_unlock_irqrestore(&r->chunk_list.lock, flags);
+	return 0;
+}
+
+/**
+ * dma_region_create_linear - Setup a linear dma maping for a device.
+ * @r: Pointer to a struct ps3pf_dma_region.
+ *
+ * This routine creates an HV dma region for the device and maps all available
+ * ram into the io controller bus address space.
+ */
+
+static int dma_region_create_linear(struct ps3pf_dma_region *r)
+{
+	int result;
+	unsigned long tmp;
+
+	/* force 16M dma pages for linear mapping */
+
+	if (r->page_size != ps3pf_dma_16m) {
+		pr_info("%s:%d: forcing 16M pages for linear map\n",
+			__func__, __LINE__);
+		r->page_size = ps3pf_dma_16m;
+	}
+
+	result = dma_region_create(r);
+
+	if (result) {
+		pr_debug("%s:%d: ps3pf_dma_region_create failed\n",
+			__func__, __LINE__);
+		BUG();
+	}
+
+	result = dma_map_area(r, map.rm.base, map.rm.size, &tmp);
+
+	if (result) {
+		pr_debug("%s:%d: ps3pf_dma_map_pages failed\n",
+			__func__, __LINE__);
+		BUG();
+	}
+
+	if (USE_LPAR_ADDR)
+		result = dma_map_area(r, map.r1.base, map.r1.size,
+			&tmp);
+	else
+		result = dma_map_area(r, map.rm.size, map.r1.size,
+			&tmp);
+
+	if (result) {
+		pr_debug("%s:%d: ps3pf_dma_map_pages failed\n",
+			__func__, __LINE__);
+		BUG();
+	}
+
+	return result;
+}
+
+/**
+ * dma_region_free_linear - Free a linear dma mapping for a device.
+ * @r: Pointer to a struct ps3pf_dma_region.
+ *
+ * This routine will unmap all mapped areas and free the HV dma region.
+ */
+
+static int dma_region_free_linear(struct ps3pf_dma_region *r)
+{
+	int result;
+
+	result = dma_unmap_area(r, dma_lpar_to_bus(r, 0), map.rm.size);
+
+	if (result) {
+		pr_debug("%s:%d: ps3pf_dma_unmap_pages failed\n",
+			__func__, __LINE__);
+		BUG();
+	}
+
+	result = dma_unmap_area(r, dma_lpar_to_bus(r, map.r1.base),
+		map.r1.size);
+
+	if (result) {
+		pr_debug("%s:%d: ps3pf_dma_unmap_pages failed\n",
+			__func__, __LINE__);
+		BUG();
+	}
+
+	result = dma_region_free(r);
+
+	if (result) {
+		pr_debug("%s:%d: ps3pf_dma_region_free failed\n",
+			__func__, __LINE__);
+		BUG();
+	}
+	return result;
+}
+
+/**
+ * dma_map_area_linear - Map an area of memory into a device dma region.
+ * @r: Pointer to a struct ps3pf_dma_region.
+ * @virt_addr: Starting virtual address of the area to map.
+ * @len: Length in bytes of the area to map.
+ * @bus_addr: A pointer to return the starting ioc bus address of the area to
+ * map.
+ *
+ * This routine just returns the coresponding bus address.  Actual mapping
+ * occurs in dma_region_create_linear().
+ */
+
+static int dma_map_area_linear(struct ps3pf_dma_region *r,
+	unsigned long virt_addr, unsigned long len, unsigned long *bus_addr)
+{
+	unsigned long phys_addr = is_kernel_addr(virt_addr) ? __pa(virt_addr)
+		: virt_addr;
+	*bus_addr = dma_lpar_to_bus(r, ps3pf_mm_phys_to_lpar(phys_addr));
+	return 0;
+}
+
+/**
+ * dma_unmap_area_linear - Unmap an area of memory from a device dma region.
+ * @r: Pointer to a struct ps3pf_dma_region.
+ * @bus_addr: The starting ioc bus address of the area to unmap.
+ * @len: Length in bytes of the area to unmap.
+ *
+ * This routine does nothing.  Unmapping occurs in dma_region_free_linear().
+ */
+
+static int dma_unmap_area_linear(struct ps3pf_dma_region *r,
+	unsigned long bus_addr, unsigned long len)
+{
+	return 0;
+}
+
+int ps3pf_dma_region_create(struct ps3pf_dma_region *r)
+{
+	return (USE_DYNAMIC_DMA)
+		? dma_region_create(r)
+		: dma_region_create_linear(r);
+}
+
+int ps3pf_dma_region_free(struct ps3pf_dma_region *r)
+{
+	return (USE_DYNAMIC_DMA)
+		? dma_region_free(r)
+		: dma_region_free_linear(r);
+}
+
+int ps3pf_dma_map(struct ps3pf_dma_region *r, unsigned long virt_addr,
+	unsigned long len, unsigned long *bus_addr)
+{
+	return (USE_DYNAMIC_DMA)
+		? dma_map_area(r, virt_addr, len, bus_addr)
+		: dma_map_area_linear(r, virt_addr, len, bus_addr);
+}
+
+int ps3pf_dma_unmap(struct ps3pf_dma_region *r, unsigned long bus_addr,
+	unsigned long len)
+{
+	return (USE_DYNAMIC_DMA) ? dma_unmap_area(r, bus_addr, len)
+		: dma_unmap_area_linear(r, bus_addr, len);
+}
+
+/*============================================================================*/
+/* system startup routines                                                    */
+/*============================================================================*/
+
+/**
+ * ps3pf_mm_init - initialize the address space state variables
+ */
+
+void __init ps3pf_mm_init(void)
+{
+	int result;
+
+	pr_debug(" -> %s:%d\n", __func__, __LINE__);
+
+	result = ps3pf_repository_read_mm_info(&map.rm.base, &map.rm.size,
+		&map.total);
+
+	if (result)
+		panic("ps3pf_repository_read_mm_info() failed");
+
+	map.rm.offset = map.rm.base;
+	map.vas_id = map.htab_size = 0;
+
+	/* this implementation assumes map.rm.base is zero */
+
+	BUG_ON(map.rm.base);
+	BUG_ON(!map.rm.size);
+
+	lmb_add(map.rm.base, map.rm.size);
+	lmb_analyze();
+
+	// arrange to do this in ps3pf_mm_add_memory!!!
+	ps3pf_mm_region_create(&map.r1, map.total - map.rm.size);
+
+	pr_debug(" <- %s:%d\n", __func__, __LINE__);
+}
+
+/**
+ * ps3pf_mm_shutdown - final cleanup of address space
+ */
+
+void ps3pf_mm_shutdown(void)
+{
+	ps3pf_mm_region_destroy(&map.r1);
+	map.total = map.rm.size;
+}
Index: cell--common--6/arch/powerpc/platforms/ps3pf/platform.h
===================================================================
--- /dev/null
+++ cell--common--6/arch/powerpc/platforms/ps3pf/platform.h
@@ -0,0 +1,59 @@
+/*
+ * platform.h - PS3 Platform declarations.
+ *
+ *  Copyright (C) 2006 Sony Computer Entertainment Inc.
+ *  Copyright 2006 Sony Corp.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#if !defined(_510B7842_EE09_4B12_BE5C_D217383D50C7)
+#define _510B7842_EE09_4B12_BE5C_D217383D50C7
+
+#include <linux/rtc.h>
+
+/* htab */
+
+void __init ps3pf_hpte_init(unsigned long htab_size);
+void __init ps3pf_map_htab(void);
+
+/* mm */
+
+void __init ps3pf_mm_init(void);
+void __init ps3pf_mm_vas_create(unsigned long* htab_size);
+void ps3pf_mm_shutdown(void);
+
+/* irq */
+
+void ps3pf_init_IRQ(void);
+void __init ps3pf_register_ipi_debug_brk(unsigned int cpu, unsigned int virq);
+
+/* smp */
+
+void smp_init_ps3pf(void);
+void ps3pf_smp_cleanup_cpu(int cpu);
+
+/* time */
+
+void __init ps3pf_calibrate_decr(void);
+unsigned long __init ps3pf_get_boot_time(void);
+void ps3pf_get_rtc_time(struct rtc_time *time);
+int ps3pf_set_rtc_time(struct rtc_time *time);
+
+/* os area */
+
+int __init ps3pf_os_area_init(void);
+u64 ps3pf_os_area_rtc_diff(void);
+
+#endif
Index: cell--common--6/arch/powerpc/platforms/ps3pf/setup.c
===================================================================
--- /dev/null
+++ cell--common--6/arch/powerpc/platforms/ps3pf/setup.c
@@ -0,0 +1,177 @@
+/*
+ * setup.c - PS3 Platform setup routines.
+ *
+ *  Copyright (C) 2006 Sony Computer Entertainment Inc.
+ *  Copyright 2006 Sony Corp.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#undef DEBUG
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/root_dev.h>
+#include <linux/console.h>
+#include <linux/kexec.h>
+
+#include <asm/machdep.h>
+#include <asm/firmware.h>
+#include <asm/time.h>
+#include <asm/iommu.h>
+#include <asm/udbg.h>
+#include <asm/lv1call.h>
+
+#include "platform.h"
+
+#if defined(DEBUG)
+#undef pr_debug
+#define pr_debug(fmt...) udbg_printf(fmt)
+#endif
+
+static void ps3pf_show_cpuinfo(struct seq_file *m)
+{
+	seq_printf(m, "machine\t\t: %s\n", ppc_md.name);
+}
+
+static void ps3pf_power_save(void)
+{
+	/*
+	 * lv1_pause() puts the PPE thread into inactive state until an
+	 * irq on an unmasked plug exists. MSR[EE] has no effect.
+	 * flags: 0 = wake on DEC interrupt, 1 = ignore DEC interrupt.
+	 */
+
+	lv1_pause(0);
+}
+
+static void ps3pf_panic(char *str)
+{
+	pr_debug("%s:%d %s\n", __func__, __LINE__, str);
+
+#ifdef CONFIG_SMP
+	smp_send_stop();
+#endif
+	printk("\n");
+	printk("   System does not reboot automatically.\n");
+	printk("   Please press POWER button.\n");
+	printk("\n");
+
+	for (;;) ;
+}
+
+static void __init ps3pf_setup_arch(void)
+{
+	pr_debug(" -> %s:%d\n", __func__, __LINE__);
+
+	ps3pf_map_htab();
+
+#if defined(CONFIG_BLK_DEV_INITRD)
+ 	if (initrd_start)
+ 		ROOT_DEV = Root_RAM0;
+ 	else
+#endif
+#if defined(CONFIG_ROOT_NFS)
+		ROOT_DEV = Root_NFS;
+#else
+ 		ROOT_DEV = Root_HDA1;
+#endif
+
+#ifdef CONFIG_SMP
+	smp_init_ps3pf();
+#endif
+
+#ifdef CONFIG_DUMMY_CONSOLE
+	conswitchp = &dummy_con;
+#endif
+
+	ppc_md.power_save = ps3pf_power_save;
+
+	pr_debug(" <- %s:%d\n", __func__, __LINE__);
+}
+
+static void __init ps3pf_progress(char *s, unsigned short hex)
+{
+	printk("*** %04x : %s\n", hex, s ? s : "");
+}
+
+static int __init ps3pf_probe(void)
+{
+	unsigned long htab_size;
+
+	pr_debug(" -> %s:%d\n", __func__, __LINE__);
+
+	powerpc_firmware_features |= FW_FEATURE_LPAR;
+
+	ps3pf_mm_init();
+	ps3pf_mm_vas_create(&htab_size);
+	ps3pf_hpte_init(htab_size);
+
+	pr_debug(" <- %s:%d\n", __func__, __LINE__);
+	return 1;
+}
+
+#if defined(CONFIG_KEXEC)
+static void ps3pf_kexec_cpu_down(int crash_shutdown, int secondary)
+{
+	pr_debug(" -> %s:%d\n", __func__, __LINE__);
+
+	if (secondary) {
+		int cpu;
+		for_each_online_cpu(cpu)
+			if (cpu)
+				ps3pf_smp_cleanup_cpu(cpu);
+	} else
+		ps3pf_smp_cleanup_cpu(0);
+
+	pr_debug(" <- %s:%d\n", __func__, __LINE__);
+}
+
+static void ps3pf_machine_kexec(struct kimage *image)
+{
+	unsigned long ppe_id;
+
+	pr_debug(" -> %s:%d\n", __func__, __LINE__);
+
+	lv1_get_logical_ppe_id(&ppe_id);
+	lv1_configure_irq_state_bitmap(ppe_id, 0, 0);
+	ps3pf_mm_shutdown();
+	ps3pf_mm_vas_destroy();
+
+	default_machine_kexec(image);
+
+	pr_debug(" <- %s:%d\n", __func__, __LINE__);
+}
+#endif
+
+define_machine(ps3pf) {
+	.name				= "PS3PF",
+	.probe				= ps3pf_probe,
+	.setup_arch			= ps3pf_setup_arch,
+	.show_cpuinfo			= ps3pf_show_cpuinfo,
+	.init_IRQ			= ps3pf_init_IRQ,
+	.panic				= ps3pf_panic,
+	.get_boot_time			= ps3pf_get_boot_time,
+	.set_rtc_time			= ps3pf_set_rtc_time,
+	.get_rtc_time			= ps3pf_get_rtc_time,
+	.calibrate_decr			= ps3pf_calibrate_decr,
+	.progress			= ps3pf_progress,
+#if defined(CONFIG_KEXEC)
+	.kexec_cpu_down			= ps3pf_kexec_cpu_down,
+	.machine_kexec			= ps3pf_machine_kexec,
+	.machine_kexec_prepare		= ps3pf_machine_kexec_prepare,
+	.machine_crash_shutdown		= default_machine_crash_shutdown,
+#endif
+};
Index: cell--common--6/arch/powerpc/platforms/ps3pf/smp.c
===================================================================
--- /dev/null
+++ cell--common--6/arch/powerpc/platforms/ps3pf/smp.c
@@ -0,0 +1,164 @@
+/*
+ * smp.c - PS3 Platform SMP routines.
+ *
+ *  Copyright (C) 2006 Sony Computer Entertainment Inc.
+ *  Copyright 2006 Sony Corp.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#undef DEBUG
+
+#include <linux/kernel.h>
+#include <linux/smp.h>
+
+#include <asm/machdep.h>
+#include <asm/udbg.h>
+#include <asm/ps3pf.h>
+
+#include "platform.h"
+
+#if defined(DEBUG)
+#undef pr_debug
+#define pr_debug(fmt...) udbg_printf(fmt)
+#endif
+
+#if !defined(PPC_MSG_MIGRATE_TASK)
+static const int PPC_MSG_MIGRATE_TASK = 2;
+#endif
+
+static irqreturn_t ipi_function_handler(int irq, void *msg)
+{
+	smp_message_recv((int)(long)msg);
+	return IRQ_HANDLED;
+}
+
+/**
+  * virqs - a per cpu array of virqs for ipi use
+  */
+
+#define MSG_COUNT 4
+static DEFINE_PER_CPU(unsigned int, virqs[MSG_COUNT]);
+
+static const char *names[MSG_COUNT] = {
+	"ipi call",
+	"ipi reschedule",
+	"ipi migrate",
+	"ipi debug brk"
+};
+
+static void do_message_pass(int target, int msg)
+{
+	int result;
+	unsigned int virq;
+
+	if (msg >= MSG_COUNT) {
+		pr_debug("%s:%d: bad msg: %d\n", __func__, __LINE__, msg);
+		return;
+	}
+
+	virq = per_cpu(virqs, target)[msg];
+	result = ps3pf_send_event_locally(virq);
+
+	if (result)
+		pr_debug("%s:%d: ps3pf_send_event_locally(%d, %d) failed"
+			" (%d)\n", __func__, __LINE__, target, msg, result);
+}
+
+static void ps3pf_smp_message_pass(int target, int msg)
+{
+	int cpu;
+
+	if (target < NR_CPUS)
+		do_message_pass(target, msg);
+	else if (target == MSG_ALL_BUT_SELF) {
+		for_each_online_cpu(cpu)
+			if (cpu != smp_processor_id())
+				do_message_pass(cpu, msg);
+	} else {
+		for_each_online_cpu(cpu)
+			do_message_pass(cpu, msg);
+	}
+}
+
+static int ps3pf_smp_probe(void)
+{
+	return 2;
+}
+
+static void __init ps3pf_smp_setup_cpu(int cpu)
+{
+	int result;
+	unsigned int *virqs = per_cpu(virqs, cpu);
+	int i;
+
+	pr_debug(" -> %s:%d: (%d)\n", __func__, __LINE__, cpu);
+
+	/*
+	 * Check assumptions on virqs[] indexing. If this
+	 * check fails, then a different mapping of PPC_MSG_
+	 * to index needs to be setup.
+	 */
+
+	BUILD_BUG_ON(PPC_MSG_CALL_FUNCTION  != 0);
+	BUILD_BUG_ON(PPC_MSG_RESCHEDULE     != 1);
+	BUILD_BUG_ON(PPC_MSG_MIGRATE_TASK   != 2);
+	BUILD_BUG_ON(PPC_MSG_DEBUGGER_BREAK != 3);
+
+	for (i = 0; i < MSG_COUNT; i++) {
+		result = ps3pf_alloc_event_irq(&virqs[i]);
+
+		if (result)
+			continue;
+
+		pr_debug("%s:%d: (%d, %d) => virq %u\n",
+			__func__, __LINE__, cpu, i, virqs[i]);
+
+
+		request_irq(virqs[i], ipi_function_handler, IRQF_DISABLED,
+			names[i], (void*)(long)i);
+	}
+
+	ps3pf_register_ipi_debug_brk(cpu, virqs[PPC_MSG_DEBUGGER_BREAK]);
+
+	pr_debug(" <- %s:%d: (%d)\n", __func__, __LINE__, cpu);
+}
+
+void ps3pf_smp_cleanup_cpu(int cpu)
+{
+	unsigned int *virqs = per_cpu(virqs, cpu);
+	int i;
+
+	pr_debug(" -> %s:%d: (%d)\n", __func__, __LINE__, cpu);
+	for (i = 0; i < MSG_COUNT; i++) {
+		ps3pf_free_event_irq(virqs[i]);
+		free_irq(virqs[i], (void*)(long)i);
+		virqs[i] = NO_IRQ;
+	}
+	pr_debug(" <- %s:%d: (%d)\n", __func__, __LINE__, cpu);
+}
+
+static struct smp_ops_t ps3pf_smp_ops = {
+	.probe		= ps3pf_smp_probe,
+	.message_pass	= ps3pf_smp_message_pass,
+	.kick_cpu	= smp_generic_kick_cpu,
+	.setup_cpu	= ps3pf_smp_setup_cpu,
+};
+
+void smp_init_ps3pf(void)
+{
+	pr_debug(" -> %s\n", __func__);
+	smp_ops = &ps3pf_smp_ops;
+	pr_debug(" <- %s\n", __func__);
+}
Index: cell--common--6/arch/powerpc/platforms/ps3pf/time.c
===================================================================
--- /dev/null
+++ cell--common--6/arch/powerpc/platforms/ps3pf/time.c
@@ -0,0 +1,105 @@
+/*
+ * time.c - PS3 Platform time and rtc routines.
+ *
+ *  Copyright (C) 2006 Sony Computer Entertainment Inc.
+ *  Copyright 2006 Sony Corp.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#undef DEBUG
+
+#include <linux/kernel.h>
+
+#include <asm/rtc.h>
+#include <asm/lv1call.h>
+#include <asm/ps3pf.h>
+
+#include "platform.h"
+
+#define dump_tm(_a) _dump_tm(_a, __func__, __LINE__)
+static void _dump_tm(const struct rtc_time *tm, const char* func, int line)
+{
+	pr_debug("%s:%d tm_sec  %d\n", func, line, tm->tm_sec);
+	pr_debug("%s:%d tm_min  %d\n", func, line, tm->tm_min);
+	pr_debug("%s:%d tm_hour %d\n", func, line, tm->tm_hour);
+	pr_debug("%s:%d tm_mday %d\n", func, line, tm->tm_mday);
+	pr_debug("%s:%d tm_mon  %d\n", func, line, tm->tm_mon);
+	pr_debug("%s:%d tm_year %d\n", func, line, tm->tm_year);
+	pr_debug("%s:%d tm_wday %d\n", func, line, tm->tm_wday);
+}
+
+#define dump_time(_a) _dump_time(_a, __func__, __LINE__)
+static void _dump_time(int time, const char* func, int line)
+{
+	struct rtc_time tm;
+
+	to_tm(time, &tm);
+
+	pr_debug("%s:%d time    %d\n", func, line, time);
+	_dump_tm(&tm, func, line);
+}
+
+/**
+ * rtc_shift - Difference in seconds between 1970 and the ps3pf rtc value.
+ */
+
+static s64 rtc_shift;
+
+void __init ps3pf_calibrate_decr(void)
+{
+	int result;
+	u64 tmp;
+
+	result = ps3pf_repository_read_be_tb_freq(0, &tmp);
+	BUG_ON(result);
+
+	ppc_tb_freq = tmp;
+	ppc_proc_freq = ppc_tb_freq * 40;
+
+	rtc_shift = ps3pf_os_area_rtc_diff();
+}
+
+static u64 read_rtc(void)
+{
+	int result;
+	u64 rtc_val;
+	u64 tb_val;
+
+	result = lv1_get_rtc(&rtc_val, &tb_val);
+	BUG_ON(result);
+
+	return rtc_val;
+}
+
+int ps3pf_set_rtc_time(struct rtc_time *tm)
+{
+	u64 now = mktime(tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
+		tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+	rtc_shift = now - read_rtc();
+	return 0;
+}
+
+void ps3pf_get_rtc_time(struct rtc_time *tm)
+{
+	to_tm(read_rtc() + rtc_shift, tm);
+	tm->tm_year -= 1900;
+	tm->tm_mon -= 1;
+}
+
+unsigned long __init ps3pf_get_boot_time(void)
+{
+	return read_rtc() + rtc_shift;
+}
Index: cell--common--6/include/asm-powerpc/ps3pf.h
===================================================================
--- /dev/null
+++ cell--common--6/include/asm-powerpc/ps3pf.h
@@ -0,0 +1,240 @@
+/*
+ * ps3fp.h - PS3 Platform declarations
+ *
+ *  Copyright (C) 2006 Sony Computer Entertainment Inc.
+ *  Copyright 2006 Sony Corp.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#if !defined(_A380E61A_0F69_48BF_A5DA_8FDC3BE8F31F)
+#define _A380E61A_0F69_48BF_A5DA_8FDC3BE8F31F
+
+#include <linux/compiler.h> /* for __deprecated */
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/device.h>
+
+/**
+ * struct ps3pf_device_id - HV bus device identifier from the system repository
+ * @bus_id: HV bus id, {1..} (zero invalid)
+ * @dev_id: HV device id, {0..}
+ */
+
+struct ps3pf_device_id {
+	unsigned int bus_id;
+	unsigned int dev_id;
+};
+
+
+/* dma routines */
+
+enum ps3pf_dma_page_size {ps3pf_dma_4k = 12U, ps3pf_dma_64k = 16U,
+	ps3pf_dma_1m = 20U, ps3pf_dma_16m = 24U};
+enum ps3pf_dma_region_type {ps3pf_dma_other = 0, ps3pf_dma_internal = 2};
+
+/**
+ * struct ps3pf_dma_region - A per device dma state variables structure
+ * @did: The HV device id.
+ * @page_size: The ioc pagesize.
+ * @region_type: The HV region type.
+ * @bus_addr: The 'translated' bus address of the region.
+ * @len: The length in bytes of the region.
+ * @chunk_list: Opaque variable used by the ioc page manager.
+ */
+
+struct ps3pf_dma_region {
+	struct ps3pf_device_id did;
+	enum ps3pf_dma_page_size page_size;
+	enum ps3pf_dma_region_type region_type;
+	unsigned long bus_addr;
+	unsigned long len;
+	struct {
+		spinlock_t lock;
+		struct list_head head;
+	} chunk_list;
+};
+
+/**
+ * struct ps3pf_dma_region_init - Helper to initialize structure variables
+ *
+ * Helper to properly initialize variables prior to calling
+ * ps3pf_system_bus_device_register.
+ */
+
+static inline void ps3pf_dma_region_init(struct ps3pf_dma_region *r,
+	const struct ps3pf_device_id* did, enum ps3pf_dma_page_size page_size,
+	enum ps3pf_dma_region_type region_type)
+{
+	r->did = *did;
+	r->page_size = page_size;
+	r->region_type = region_type;
+}
+int ps3pf_dma_region_create(struct ps3pf_dma_region *r);
+int ps3pf_dma_region_free(struct ps3pf_dma_region *r);
+int ps3pf_dma_map(struct ps3pf_dma_region *r, unsigned long virt_addr,
+	unsigned long len, unsigned long *bus_addr);
+int ps3pf_dma_unmap(struct ps3pf_dma_region *r, unsigned long bus_addr,
+	unsigned long len);
+
+/* mmio routines */
+
+enum ps3pf_mmio_page_size {ps3pf_mmio_4k = 12U, ps3pf_mmio_64k = 16U};
+
+/**
+ * struct ps3pf_mmio_region - a per device mmio state variables structure
+ *
+ * Current systems can be supported with a single region per device.
+ */
+
+struct ps3pf_mmio_region {
+	struct ps3pf_device_id did;
+	unsigned long bus_addr;
+	unsigned long len;
+	enum ps3pf_mmio_page_size page_size;
+	unsigned long lpar_addr;
+};
+
+/**
+ * struct ps3pf_mmio_region_init - Helper to initialize structure variables
+ *
+ * Helper to properly initialize variables prior to calling
+ * ps3pf_system_bus_device_register.
+ */
+
+static inline void ps3pf_mmio_region_init(struct ps3pf_mmio_region *r,
+	const struct ps3pf_device_id* did, unsigned long bus_addr,
+	unsigned long len, enum ps3pf_mmio_page_size page_size)
+{
+	r->did = *did;
+	r->bus_addr = bus_addr;
+	r->len = len;
+	r->page_size = page_size;
+}
+int ps3pf_mmio_region_create(struct ps3pf_mmio_region *r);
+int ps3pf_free_mmio_region(struct ps3pf_mmio_region *r);
+unsigned long ps3pf_mm_phys_to_lpar(unsigned long phys_addr);
+
+/* inrerrupt routines */
+
+int ps3pf_alloc_io_irq(unsigned int interrupt_id, unsigned int *virq);
+int ps3pf_free_io_irq(unsigned int virq);
+int ps3pf_alloc_event_irq(unsigned int *virq);
+int ps3pf_free_event_irq(unsigned int virq);
+int ps3pf_send_event_locally(unsigned int virq);
+int ps3pf_connect_event_irq(const struct ps3pf_device_id *did,
+	unsigned int interrupt_id, unsigned int *virq);
+int ps3pf_disconnect_event_irq(const struct ps3pf_device_id *did,
+	unsigned int interrupt_id, unsigned int virq);
+int ps3pf_alloc_vuart_irq(void* virt_addr_bmp, unsigned int *virq);
+int ps3pf_free_vuart_irq(unsigned int virq);
+int ps3pf_alloc_spe_irq(unsigned long spe_id, unsigned int class,
+	unsigned int *virq);
+int ps3pf_free_spe_irq(unsigned int virq);
+unsigned long __deprecated ps3pf_legacy_virq_to_outlet(unsigned int virq);
+
+/* lv1 result codes */
+
+enum lv1_result {
+	LV1_SUCCESS                     = 0,
+	/* not used                       -1 */
+	LV1_RESOURCE_SHORTAGE           = -2,
+	LV1_NO_PRIVILEGE                = -3,
+	LV1_DENIED_BY_POLICY            = -4,
+	LV1_ACCESS_VIOLATION            = -5,
+	LV1_NO_ENTRY                    = -6,
+	LV1_DUPLICATE_ENTRY             = -7,
+	LV1_TYPE_MISMATCH               = -8,
+	LV1_BUSY                        = -9,
+	LV1_EMPTY                       = -10,
+	LV1_WRONG_STATE                 = -11,
+	/* not used                       -12 */
+	LV1_NO_MATCH                    = -13,
+	LV1_ALREADY_CONNECTED           = -14,
+	LV1_UNSUPPORTED_PARAMETER_VALUE = -15,
+	LV1_CONDITION_NOT_SATISFIED     = -16,
+	LV1_ILLEGAL_PARAMETER_VALUE     = -17,
+	LV1_BAD_OPTION                  = -18,
+	LV1_IMPLEMENTATION_LIMITATION   = -19,
+	LV1_NOT_IMPLEMENTED             = -20,
+	LV1_INVALID_CLASS_ID            = -21,
+	LV1_CONSTRAINT_NOT_SATISFIED    = -22,
+	LV1_ALIGNMENT_ERROR             = -23,
+	LV1_INTERNAL_ERROR              = -32768,
+};
+
+static inline const char* ps3pf_result(int result)
+{
+#if defined(DEBUG)
+	switch (result) {
+	case LV1_SUCCESS:
+		return "LV1_SUCCESS (0)";
+	case -1:
+		return "** unknown result ** (-1)";
+	case LV1_RESOURCE_SHORTAGE:
+		return "LV1_RESOURCE_SHORTAGE (-2)";
+	case LV1_NO_PRIVILEGE:
+		return "LV1_NO_PRIVILEGE (-3)";
+	case LV1_DENIED_BY_POLICY:
+		return "LV1_DENIED_BY_POLICY (-4)";
+	case LV1_ACCESS_VIOLATION:
+		return "LV1_ACCESS_VIOLATION (-5)";
+	case LV1_NO_ENTRY:
+		return "LV1_NO_ENTRY (-6)";
+	case LV1_DUPLICATE_ENTRY:
+		return "LV1_DUPLICATE_ENTRY (-7)";
+	case LV1_TYPE_MISMATCH:
+		return "LV1_TYPE_MISMATCH (-8)";
+	case LV1_BUSY:
+		return "LV1_BUSY (-9)";
+	case LV1_EMPTY:
+		return "LV1_EMPTY (-10)";
+	case LV1_WRONG_STATE:
+		return "LV1_WRONG_STATE (-11)";
+	case -12:
+		return "** unknown result ** (-12)";
+	case LV1_NO_MATCH:
+		return "LV1_NO_MATCH (-13)";
+	case LV1_ALREADY_CONNECTED:
+		return "LV1_ALREADY_CONNECTED (-14)";
+	case LV1_UNSUPPORTED_PARAMETER_VALUE:
+		return "LV1_UNSUPPORTED_PARAMETER_VALUE (-15)";
+	case LV1_CONDITION_NOT_SATISFIED:
+		return "LV1_CONDITION_NOT_SATISFIED (-16)";
+	case LV1_ILLEGAL_PARAMETER_VALUE:
+		return "LV1_ILLEGAL_PARAMETER_VALUE (-17)";
+	case LV1_BAD_OPTION:
+		return "LV1_BAD_OPTION (-18)";
+	case LV1_IMPLEMENTATION_LIMITATION:
+		return "LV1_IMPLEMENTATION_LIMITATION (-19)";
+	case LV1_NOT_IMPLEMENTED:
+		return "LV1_NOT_IMPLEMENTED (-20)";
+	case LV1_INVALID_CLASS_ID:
+		return "LV1_INVALID_CLASS_ID (-21)";
+	case LV1_CONSTRAINT_NOT_SATISFIED:
+		return "LV1_CONSTRAINT_NOT_SATISFIED (-22)";
+	case LV1_ALIGNMENT_ERROR:
+		return "LV1_ALIGNMENT_ERROR (-23)";
+	case LV1_INTERNAL_ERROR:
+		return "LV1_INTERNAL_ERROR (-32768)";
+	default:
+		BUG();
+		return "** unknown result **";
+	};
+#else
+	return "";
+#endif
+}
+
+#endif

^ permalink raw reply

* Re: [PATCH]: PowerPC: make sure the rtas stop-self token is defined.
From: Nathan Lynch @ 2006-11-10 20:01 UTC (permalink / raw)
  To: Linas Vepstas; +Cc: linuxppc-dev, paulus
In-Reply-To: <20061110182253.GV30625@austin.ibm.com>

Linas Vepstas wrote:
> 
> There are a variety of code paths that lead to rtas_stop_self()
> being called, primarily through cpu_die(). However, rtas_stop_self() 
> has a BUG_ON(rtas_args->token == RTAS_UNKNOWN_SERVICE); in it, and
> this rtas token is only set up if CONFIG_HOTPLUG_CPU is defined.
> 
> Rather than wrapping all of the callers of rtas_stop_self()
> with CONFIG_HOTPLUG_CPU, it seems wiser to just unwrap the token
> definition.

Is there actually a code path that calls rtas_stop_self with
CONFIG_HOTPLUG_CPU=n?  That would be a bug, I think.

^ permalink raw reply

* [PATCH 1/16] powerpc: add virq_to_hw accessor routine
From: Geoff Levand @ 2006-11-10 20:01 UTC (permalink / raw)
  To: Paul Mackerras; +Cc: linuxppc-dev

This adds an accessor routine virq_to_hw() to the
virq routines which hides the implementation details
of the virq to hwirq map.


Signed-off-by: Geoff Levand <geoffrey.levand@am.sony.com>

---
 include/asm-powerpc/irq.h |    4 ++++
 1 file changed, 4 insertions(+)

Index: cell--common--6/include/asm-powerpc/irq.h
===================================================================
--- cell--common--6.orig/include/asm-powerpc/irq.h
+++ cell--common--6/include/asm-powerpc/irq.h
@@ -135,6 +135,10 @@
 
 extern struct irq_map_entry irq_map[NR_IRQS];
 
+static inline irq_hw_number_t virq_to_hw(unsigned int virq)
+{
+	return irq_map[virq].hwirq;
+}
 
 /**
  * irq_alloc_host - Allocate a new irq_host data structure

^ permalink raw reply

* [PATCH 6/16] cell: abstract spu management routines
From: Geoff Levand @ 2006-11-10 20:01 UTC (permalink / raw)
  To: Paul Mackerras; +Cc: linuxppc-dev, Arnd Bergmann

This adds a platform specific spu management abstraction and the coresponding
routines to support the IBM Cell Blade.  It also removes the hypervisor only
resources that were included in struct spu.

Three new platform specific routines are introduced, spu_enumerate_spus(),
spu_create_spu() and spu_destroy_spu().  The underlining design uses a new
type, struct spu_management_ops, to hold function pointers that the platform
setup code is expected to initialize to instances appropriate to that platform.

For the IBM Cell Blade support, I put the hypervisor only resources that were
in struct spu into a platform specific data structure struct spu_pdata.


Signed-off-by: Geoff Levand <geoffrey.levand@am.sony.com>

---

Michael,

Unfortunately, for your xmon spu support, your DUMP_FIELD is setup in such a
way that it is not easy to change to use from inside spu_priv1_mmio.c, so I
left of_dump_pdata_fields() empty.  We'll need to work on something usable
there, or make some other way to abstract those platform specific spu
variables.

 arch/powerpc/platforms/cell/cbe_thermal.c    |    5 
 arch/powerpc/platforms/cell/setup.c          |    3 
 arch/powerpc/platforms/cell/spu_base.c       |  308 +-------------------
 arch/powerpc/platforms/cell/spu_priv1_mmio.c |  406 +++++++++++++++++++++++++--
 arch/powerpc/platforms/cell/spu_priv1_mmio.h |   26 +
 arch/powerpc/xmon/xmon.c                     |    8 
 include/asm-powerpc/spu.h                    |    5 
 include/asm-powerpc/spu_priv1.h              |   48 ++-
 8 files changed, 481 insertions(+), 328 deletions(-)

Index: cell--common--6/arch/powerpc/platforms/cell/cbe_thermal.c
===================================================================
--- cell--common--6.orig/arch/powerpc/platforms/cell/cbe_thermal.c
+++ cell--common--6/arch/powerpc/platforms/cell/cbe_thermal.c
@@ -29,6 +29,7 @@
 #include <asm/prom.h>
 
 #include "cbe_regs.h"
+#include "spu_priv1_mmio.h"
 
 static struct cbe_pmd_regs __iomem *get_pmd_regs(struct sys_device *sysdev)
 {
@@ -36,7 +37,7 @@
 
 	spu = container_of(sysdev, struct spu, sysdev);
 
-	return cbe_get_pmd_regs(spu->devnode);
+	return cbe_get_pmd_regs(spu_devnode(spu));
 }
 
 /* returns the value for a given spu in a given register */
@@ -49,7 +50,7 @@
 	/* getting the id from the reg attribute will not work on future device-tree layouts
 	 * in future we should store the id to the spu struct and use it here */
 	spu = container_of(sysdev, struct spu, sysdev);
-	id = (unsigned int *)get_property(spu->devnode, "reg", NULL);
+	id = (unsigned int *)get_property(spu_devnode(spu), "reg", NULL);
 	value.val = in_be64(&reg->val);
 
 	return value.spe[*id];
Index: cell--common--6/arch/powerpc/platforms/cell/setup.c
===================================================================
--- cell--common--6.orig/arch/powerpc/platforms/cell/setup.c
+++ cell--common--6/arch/powerpc/platforms/cell/setup.c
@@ -97,7 +97,8 @@
 static void __init cell_setup_arch(void)
 {
 #ifdef CONFIG_SPU_BASE
-	spu_priv1_ops         = &spu_priv1_mmio_ops;
+	spu_priv1_ops = &spu_priv1_mmio_ops;
+	spu_management_ops = &spu_management_of_ops;
 #endif
 
 	cbe_regs_init();
Index: cell--common--6/arch/powerpc/platforms/cell/spu_base.c
===================================================================
--- cell--common--6.orig/arch/powerpc/platforms/cell/spu_base.c
+++ cell--common--6/arch/powerpc/platforms/cell/spu_base.c
@@ -25,23 +25,17 @@
 #include <linux/interrupt.h>
 #include <linux/list.h>
 #include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/poll.h>
 #include <linux/ptrace.h>
 #include <linux/slab.h>
 #include <linux/wait.h>
-
-#include <asm/firmware.h>
-#include <asm/io.h>
-#include <asm/prom.h>
+#include <linux/mm.h>
+#include <linux/io.h>
 #include <linux/mutex.h>
 #include <asm/spu.h>
 #include <asm/spu_priv1.h>
-#include <asm/mmu_context.h>
 #include <asm/xmon.h>
 
-#include "interrupt.h"
-
+const struct spu_management_ops *spu_management_ops;
 const struct spu_priv1_ops *spu_priv1_ops;
 
 EXPORT_SYMBOL_GPL(spu_priv1_ops);
@@ -512,235 +506,6 @@
 	return ret;
 }
 
-static int __init find_spu_node_id(struct device_node *spe)
-{
-	const unsigned int *id;
-	struct device_node *cpu;
-	cpu = spe->parent->parent;
-	id = get_property(cpu, "node-id", NULL);
-	return id ? *id : 0;
-}
-
-static int __init cell_spuprop_present(struct spu *spu, struct device_node *spe,
-		const char *prop)
-{
-	static DEFINE_MUTEX(add_spumem_mutex);
-
-	const struct address_prop {
-		unsigned long address;
-		unsigned int len;
-	} __attribute__((packed)) *p;
-	int proplen;
-
-	unsigned long start_pfn, nr_pages;
-	struct pglist_data *pgdata;
-	struct zone *zone;
-	int ret;
-
-	p = get_property(spe, prop, &proplen);
-	WARN_ON(proplen != sizeof (*p));
-
-	start_pfn = p->address >> PAGE_SHIFT;
-	nr_pages = ((unsigned long)p->len + PAGE_SIZE - 1) >> PAGE_SHIFT;
-
-	pgdata = NODE_DATA(spu->nid);
-	zone = pgdata->node_zones;
-
-	/* XXX rethink locking here */
-	mutex_lock(&add_spumem_mutex);
-	ret = __add_pages(zone, start_pfn, nr_pages);
-	mutex_unlock(&add_spumem_mutex);
-
-	return ret;
-}
-
-static void __iomem * __init map_spe_prop(struct spu *spu,
-		struct device_node *n, const char *name)
-{
-	const struct address_prop {
-		unsigned long address;
-		unsigned int len;
-	} __attribute__((packed)) *prop;
-
-	const void *p;
-	int proplen;
-	void __iomem *ret = NULL;
-	int err = 0;
-
-	p = get_property(n, name, &proplen);
-	if (proplen != sizeof (struct address_prop))
-		return NULL;
-
-	prop = p;
-
-	err = cell_spuprop_present(spu, n, name);
-	if (err && (err != -EEXIST))
-		goto out;
-
-	ret = ioremap(prop->address, prop->len);
-
- out:
-	return ret;
-}
-
-static void spu_unmap(struct spu *spu)
-{
-	iounmap(spu->priv2);
-	iounmap(spu->priv1);
-	iounmap(spu->problem);
-	iounmap((__force u8 __iomem *)spu->local_store);
-}
-
-/* This function shall be abstracted for HV platforms */
-static int __init spu_map_interrupts_old(struct spu *spu, struct device_node *np)
-{
-	unsigned int isrc;
-	const u32 *tmp;
-
-	/* Get the interrupt source unit from the device-tree */
-	tmp = get_property(np, "isrc", NULL);
-	if (!tmp)
-		return -ENODEV;
-	isrc = tmp[0];
-
-	/* Add the node number */
-	isrc |= spu->node << IIC_IRQ_NODE_SHIFT;
-
-	/* Now map interrupts of all 3 classes */
-	spu->irqs[0] = irq_create_mapping(NULL, IIC_IRQ_CLASS_0 | isrc);
-	spu->irqs[1] = irq_create_mapping(NULL, IIC_IRQ_CLASS_1 | isrc);
-	spu->irqs[2] = irq_create_mapping(NULL, IIC_IRQ_CLASS_2 | isrc);
-
-	/* Right now, we only fail if class 2 failed */
-	return spu->irqs[2] == NO_IRQ ? -EINVAL : 0;
-}
-
-static int __init spu_map_device_old(struct spu *spu, struct device_node *node)
-{
-	const char *prop;
-	int ret;
-
-	ret = -ENODEV;
-	spu->name = get_property(node, "name", NULL);
-	if (!spu->name)
-		goto out;
-
-	prop = get_property(node, "local-store", NULL);
-	if (!prop)
-		goto out;
-	spu->local_store_phys = *(unsigned long *)prop;
-
-	/* we use local store as ram, not io memory */
-	spu->local_store = (void __force *)
-		map_spe_prop(spu, node, "local-store");
-	if (!spu->local_store)
-		goto out;
-
-	prop = get_property(node, "problem", NULL);
-	if (!prop)
-		goto out_unmap;
-	spu->problem_phys = *(unsigned long *)prop;
-
-	spu->problem= map_spe_prop(spu, node, "problem");
-	if (!spu->problem)
-		goto out_unmap;
-
-	spu->priv1= map_spe_prop(spu, node, "priv1");
-	/* priv1 is not available on a hypervisor */
-
-	spu->priv2= map_spe_prop(spu, node, "priv2");
-	if (!spu->priv2)
-		goto out_unmap;
-	ret = 0;
-	goto out;
-
-out_unmap:
-	spu_unmap(spu);
-out:
-	return ret;
-}
-
-static int __init spu_map_interrupts(struct spu *spu, struct device_node *np)
-{
-	struct of_irq oirq;
-	int ret;
-	int i;
-
-	for (i=0; i < 3; i++) {
-		ret = of_irq_map_one(np, i, &oirq);
-		if (ret)
-			goto err;
-
-		ret = -EINVAL;
-		spu->irqs[i] = irq_create_of_mapping(oirq.controller,
-					oirq.specifier, oirq.size);
-		if (spu->irqs[i] == NO_IRQ)
-			goto err;
-	}
-	return 0;
-
-err:
-	pr_debug("failed to map irq %x for spu %s\n", *oirq.specifier, spu->name);
-	for (; i >= 0; i--) {
-		if (spu->irqs[i] != NO_IRQ)
-			irq_dispose_mapping(spu->irqs[i]);
-	}
-	return ret;
-}
-
-static int spu_map_resource(struct device_node *node, int nr,
-		void __iomem** virt, unsigned long *phys)
-{
-	struct resource resource = { };
-	int ret;
-
-	ret = of_address_to_resource(node, 0, &resource);
-	if (ret)
-		goto out;
-
-	if (phys)
-		*phys = resource.start;
-	*virt = ioremap(resource.start, resource.end - resource.start);
-	if (!*virt)
-		ret = -EINVAL;
-
-out:
-	return ret;
-}
-
-static int __init spu_map_device(struct spu *spu, struct device_node *node)
-{
-	int ret = -ENODEV;
-	spu->name = get_property(node, "name", NULL);
-	if (!spu->name)
-		goto out;
-
-	ret = spu_map_resource(node, 0, (void __iomem**)&spu->local_store,
-					&spu->local_store_phys);
-	if (ret)
-		goto out;
-	ret = spu_map_resource(node, 1, (void __iomem**)&spu->problem,
-					&spu->problem_phys);
-	if (ret)
-		goto out_unmap;
-	ret = spu_map_resource(node, 2, (void __iomem**)&spu->priv2,
-					NULL);
-	if (ret)
-		goto out_unmap;
-
-	if (!firmware_has_feature(FW_FEATURE_LPAR))
-		ret = spu_map_resource(node, 3, (void __iomem**)&spu->priv1,
-					NULL);
-	if (ret)
-		goto out_unmap;
-	return 0;
-
-out_unmap:
-	spu_unmap(spu);
-out:
-	pr_debug("failed to map spe %s: %d\n", spu->name, ret);
-	return ret;
-}
 
 struct sysdev_class spu_sysdev_class = {
 	set_kset_name("spu")
@@ -821,7 +586,7 @@
 	sysdev_unregister(&spu->sysdev);
 }
 
-static int __init create_spu(struct device_node *spe)
+static int __init create_spu(void *data)
 {
 	struct spu *spu;
 	int ret;
@@ -832,60 +597,37 @@
 	if (!spu)
 		goto out;
 
-	spu->node = find_spu_node_id(spe);
-	if (spu->node >= MAX_NUMNODES) {
-		printk(KERN_WARNING "SPE %s on node %d ignored,"
-		       " node number too big\n", spe->full_name, spu->node);
-		printk(KERN_WARNING "Check if CONFIG_NUMA is enabled.\n");
-		return -ENODEV;
-	}
-	spu->nid = of_node_to_nid(spe);
-	if (spu->nid == -1)
-		spu->nid = 0;
+	spin_lock_init(&spu->register_lock);
+	mutex_lock(&spu_mutex);
+	spu->number = number++;
+	mutex_unlock(&spu_mutex);
+
+	ret = spu_create_spu(spu, data);
 
-	ret = spu_map_device(spu, spe);
-	/* try old method */
-	if (ret)
-		ret = spu_map_device_old(spu, spe);
 	if (ret)
 		goto out_free;
 
-	ret = spu_map_interrupts(spu, spe);
-	if (ret)
-		ret = spu_map_interrupts_old(spu, spe);
-	if (ret)
-		goto out_unmap;
-	spin_lock_init(&spu->register_lock);
 	spu_mfc_sdr_setup(spu);
 	spu_mfc_sr1_set(spu, 0x33);
-	mutex_lock(&spu_mutex);
-
-	spu->number = number++;
 	ret = spu_request_irqs(spu);
 	if (ret)
-		goto out_unlock;
+		goto out_destroy;
 
 	ret = spu_create_sysdev(spu);
 	if (ret)
 		goto out_free_irqs;
 
+	mutex_lock(&spu_mutex);
 	list_add(&spu->list, &spu_list[spu->node]);
 	list_add(&spu->full_list, &spu_full_list);
-	spu->devnode = of_node_get(spe);
-
 	mutex_unlock(&spu_mutex);
 
-	pr_debug(KERN_DEBUG "Using SPE %s %p %p %p %p %d\n",
-		spu->name, spu->local_store,
-		spu->problem, spu->priv1, spu->priv2, spu->number);
 	goto out;
 
 out_free_irqs:
 	spu_free_irqs(spu);
-out_unlock:
-	mutex_unlock(&spu_mutex);
-out_unmap:
-	spu_unmap(spu);
+out_destroy:
+	spu_destroy_spu(spu);
 out_free:
 	kfree(spu);
 out:
@@ -897,11 +639,9 @@
 	list_del_init(&spu->list);
 	list_del_init(&spu->full_list);
 
-	of_node_put(spu->devnode);
-
 	spu_destroy_sysdev(spu);
 	spu_free_irqs(spu);
-	spu_unmap(spu);
+	spu_destroy_spu(spu);
 	kfree(spu);
 }
 
@@ -922,7 +662,6 @@
 
 static int __init init_spu_base(void)
 {
-	struct device_node *node;
 	int i, ret;
 
 	/* create sysdev class for spus */
@@ -933,16 +672,13 @@
 	for (i = 0; i < MAX_NUMNODES; i++)
 		INIT_LIST_HEAD(&spu_list[i]);
 
-	ret = -ENODEV;
-	for (node = of_find_node_by_type(NULL, "spe");
-			node; node = of_find_node_by_type(node, "spe")) {
-		ret = create_spu(node);
-		if (ret) {
-			printk(KERN_WARNING "%s: Error initializing %s\n",
-				__FUNCTION__, node->name);
-			cleanup_spu_base();
-			break;
-		}
+	ret = spu_enumerate_spus(create_spu);
+
+	if (ret) {
+		printk(KERN_WARNING "%s: Error initializing spus\n",
+			__FUNCTION__);
+		cleanup_spu_base();
+		return ret;
 	}
 
 	xmon_register_spus(&spu_full_list);
Index: cell--common--6/arch/powerpc/platforms/cell/spu_priv1_mmio.c
===================================================================
--- cell--common--6.orig/arch/powerpc/platforms/cell/spu_priv1_mmio.c
+++ cell--common--6/arch/powerpc/platforms/cell/spu_priv1_mmio.c
@@ -18,120 +18,480 @@
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
+#undef DEBUG
+
+#include <linux/interrupt.h>
+#include <linux/list.h>
 #include <linux/module.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/mm.h>
+#include <linux/io.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
 
-#include <asm/io.h>
 #include <asm/spu.h>
 #include <asm/spu_priv1.h>
+#include <asm/firmware.h>
+#include <asm/prom.h>
 
 #include "interrupt.h"
+#include "spu_priv1_mmio.h"
+
+struct spu_pdata {
+	int nid;
+	struct device_node *devnode;
+	struct spu_priv1 __iomem *priv1;
+};
+
+static struct spu_pdata *spu_get_pdata(struct spu *spu)
+{
+	BUG_ON(!spu->pdata);
+	return spu->pdata;
+}
+
+struct device_node *spu_devnode(struct spu *spu)
+{
+	return spu_get_pdata(spu)->devnode;
+}
+
+EXPORT_SYMBOL_GPL(spu_devnode);
+
+static int __init find_spu_node_id(struct device_node *spe)
+{
+	const unsigned int *id;
+	struct device_node *cpu;
+	cpu = spe->parent->parent;
+	id = get_property(cpu, "node-id", NULL);
+	return id ? *id : 0;
+}
+
+static int __init cell_spuprop_present(struct spu *spu, struct device_node *spe,
+		const char *prop)
+{
+	static DEFINE_MUTEX(add_spumem_mutex);
+
+	const struct address_prop {
+		unsigned long address;
+		unsigned int len;
+	} __attribute__((packed)) *p;
+	int proplen;
+
+	unsigned long start_pfn, nr_pages;
+	struct pglist_data *pgdata;
+	struct zone *zone;
+	int ret;
+
+	p = get_property(spe, prop, &proplen);
+	WARN_ON(proplen != sizeof (*p));
+
+	start_pfn = p->address >> PAGE_SHIFT;
+	nr_pages = ((unsigned long)p->len + PAGE_SIZE - 1) >> PAGE_SHIFT;
+
+	pgdata = NODE_DATA(spu_get_pdata(spu)->nid);
+	zone = pgdata->node_zones;
+
+	/* XXX rethink locking here */
+	mutex_lock(&add_spumem_mutex);
+	ret = __add_pages(zone, start_pfn, nr_pages);
+	mutex_unlock(&add_spumem_mutex);
+
+	return ret;
+}
+
+static void __iomem * __init map_spe_prop(struct spu *spu,
+		struct device_node *n, const char *name)
+{
+	const struct address_prop {
+		unsigned long address;
+		unsigned int len;
+	} __attribute__((packed)) *prop;
+
+	const void *p;
+	int proplen;
+	void __iomem *ret = NULL;
+	int err = 0;
+
+	p = get_property(n, name, &proplen);
+	if (proplen != sizeof (struct address_prop))
+		return NULL;
+
+	prop = p;
+
+	err = cell_spuprop_present(spu, n, name);
+	if (err && (err != -EEXIST))
+		goto out;
+
+	ret = ioremap(prop->address, prop->len);
+
+ out:
+	return ret;
+}
+
+static void spu_unmap(struct spu *spu)
+{
+	iounmap(spu->priv2);
+	iounmap(spu_get_pdata(spu)->priv1);
+	iounmap(spu->problem);
+	iounmap((__force u8 __iomem *)spu->local_store);
+}
+
+static int __init spu_map_interrupts_old(struct spu *spu,
+	struct device_node *np)
+{
+	unsigned int isrc;
+	const u32 *tmp;
+
+	/* Get the interrupt source unit from the device-tree */
+	tmp = get_property(np, "isrc", NULL);
+	if (!tmp)
+		return -ENODEV;
+	isrc = tmp[0];
+
+	/* Add the node number */
+	isrc |= spu->node << IIC_IRQ_NODE_SHIFT;
+
+	/* Now map interrupts of all 3 classes */
+	spu->irqs[0] = irq_create_mapping(NULL, IIC_IRQ_CLASS_0 | isrc);
+	spu->irqs[1] = irq_create_mapping(NULL, IIC_IRQ_CLASS_1 | isrc);
+	spu->irqs[2] = irq_create_mapping(NULL, IIC_IRQ_CLASS_2 | isrc);
+
+	/* Right now, we only fail if class 2 failed */
+	return spu->irqs[2] == NO_IRQ ? -EINVAL : 0;
+}
+
+static int __init spu_map_device_old(struct spu *spu, struct device_node *node)
+{
+	const char *prop;
+	int ret;
+
+	ret = -ENODEV;
+	spu->name = get_property(node, "name", NULL);
+	if (!spu->name)
+		goto out;
+
+	prop = get_property(node, "local-store", NULL);
+	if (!prop)
+		goto out;
+	spu->local_store_phys = *(unsigned long *)prop;
+
+	/* we use local store as ram, not io memory */
+	spu->local_store = (void __force *)
+		map_spe_prop(spu, node, "local-store");
+	if (!spu->local_store)
+		goto out;
+
+	prop = get_property(node, "problem", NULL);
+	if (!prop)
+		goto out_unmap;
+	spu->problem_phys = *(unsigned long *)prop;
+
+	spu->problem= map_spe_prop(spu, node, "problem");
+	if (!spu->problem)
+		goto out_unmap;
+
+	spu_get_pdata(spu)->priv1= map_spe_prop(spu, node, "priv1");
+
+	spu->priv2= map_spe_prop(spu, node, "priv2");
+	if (!spu->priv2)
+		goto out_unmap;
+	ret = 0;
+	goto out;
+
+out_unmap:
+	spu_unmap(spu);
+out:
+	return ret;
+}
+
+static int __init spu_map_interrupts(struct spu *spu, struct device_node *np)
+{
+	struct of_irq oirq;
+	int ret;
+	int i;
+
+	for (i=0; i < 3; i++) {
+		ret = of_irq_map_one(np, i, &oirq);
+		if (ret)
+			goto err;
+
+		ret = -EINVAL;
+		spu->irqs[i] = irq_create_of_mapping(oirq.controller,
+					oirq.specifier, oirq.size);
+		if (spu->irqs[i] == NO_IRQ)
+			goto err;
+	}
+	return 0;
+
+err:
+	pr_debug("failed to map irq %x for spu %s\n", *oirq.specifier,
+		spu->name);
+	for (; i >= 0; i--) {
+		if (spu->irqs[i] != NO_IRQ)
+			irq_dispose_mapping(spu->irqs[i]);
+	}
+	return ret;
+}
+
+static int spu_map_resource(struct device_node *node, int nr,
+		void __iomem** virt, unsigned long *phys)
+{
+	struct resource resource = { };
+	int ret;
+
+	ret = of_address_to_resource(node, 0, &resource);
+	if (ret)
+		goto out;
+
+	if (phys)
+		*phys = resource.start;
+	*virt = ioremap(resource.start, resource.end - resource.start);
+	if (!*virt)
+		ret = -EINVAL;
+
+out:
+	return ret;
+}
+
+static int __init spu_map_device(struct spu *spu, struct device_node *node)
+{
+	int ret = -ENODEV;
+	spu->name = get_property(node, "name", NULL);
+	if (!spu->name)
+		goto out;
+
+	ret = spu_map_resource(node, 0, (void __iomem**)&spu->local_store,
+					&spu->local_store_phys);
+	if (ret)
+		goto out;
+	ret = spu_map_resource(node, 1, (void __iomem**)&spu->problem,
+					&spu->problem_phys);
+	if (ret)
+		goto out_unmap;
+	ret = spu_map_resource(node, 2, (void __iomem**)&spu->priv2,
+					NULL);
+	if (ret)
+		goto out_unmap;
+
+	if (!firmware_has_feature(FW_FEATURE_LPAR))
+		ret = spu_map_resource(node, 3,
+			(void __iomem**)&spu_get_pdata(spu)->priv1, NULL);
+	if (ret)
+		goto out_unmap;
+	return 0;
+
+out_unmap:
+	spu_unmap(spu);
+out:
+	pr_debug("failed to map spe %s: %d\n", spu->name, ret);
+	return ret;
+}
+
+static int __init of_enumerate_spus(int (*fn)(void *data))
+{
+	int ret;
+	struct device_node *node;
+
+	ret = -ENODEV;
+	for (node = of_find_node_by_type(NULL, "spe");
+			node; node = of_find_node_by_type(node, "spe")) {
+		ret = fn(node);
+		if (ret) {
+			printk(KERN_WARNING "%s: Error initializing %s\n",
+				__FUNCTION__, node->name);
+			break;
+		}
+	}
+	return ret;
+}
+
+static int __init of_create_spu(struct spu *spu, void *data)
+{
+	int ret;
+	struct device_node *spe = (struct device_node *)data;
+
+	spu->pdata = kzalloc(sizeof(struct spu_pdata),
+		GFP_KERNEL);
+	if (!spu->pdata) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	spu->node = find_spu_node_id(spe);
+	if (spu->node >= MAX_NUMNODES) {
+		printk(KERN_WARNING "SPE %s on node %d ignored,"
+		       " node number too big\n", spe->full_name, spu->node);
+		printk(KERN_WARNING "Check if CONFIG_NUMA is enabled.\n");
+		ret = -ENODEV;
+		goto out_free;
+	}
+
+	spu_get_pdata(spu)->nid = of_node_to_nid(spe);
+	if (spu_get_pdata(spu)->nid == -1)
+		spu_get_pdata(spu)->nid = 0;
+
+	ret = spu_map_device(spu, spe);
+	/* try old method */
+	if (ret)
+		ret = spu_map_device_old(spu, spe);
+	if (ret)
+		goto out_free;
+
+	ret = spu_map_interrupts(spu, spe);
+	if (ret)
+		ret = spu_map_interrupts_old(spu, spe);
+	if (ret)
+		goto out_unmap;
+
+	spu_get_pdata(spu)->devnode = of_node_get(spe);
+
+	pr_debug(KERN_DEBUG "Using SPE %s %p %p %p %p %d\n", spu->name,
+		spu->local_store, spu->problem, spu_get_pdata(spu)->priv1,
+		spu->priv2, spu->number);
+	goto out;
+
+out_unmap:
+	spu_unmap(spu);
+out_free:
+	kfree(spu->pdata);
+	spu->pdata = NULL;
+out:
+	return ret;
+}
+
+static int of_destroy_spu(struct spu *spu)
+{
+	spu_unmap(spu);
+	of_node_put(spu_get_pdata(spu)->devnode);
+	kfree(spu->pdata);
+	spu->pdata = NULL;
+	return 0;
+}
+
+static void of_dump_pdata_fields(struct spu *spu)
+{
+}
+
+const struct spu_management_ops spu_management_of_ops = {
+	.enumerate_spus = of_enumerate_spus,
+	.create_spu = of_create_spu,
+	.destroy_spu = of_destroy_spu,
+	.dump_pdata_fields = of_dump_pdata_fields,
+};
 
 static void int_mask_and(struct spu *spu, int class, u64 mask)
 {
 	u64 old_mask;
 
-	old_mask = in_be64(&spu->priv1->int_mask_RW[class]);
-	out_be64(&spu->priv1->int_mask_RW[class], old_mask & mask);
+	old_mask = in_be64(&spu_get_pdata(spu)->priv1->int_mask_RW[class]);
+	out_be64(&spu_get_pdata(spu)->priv1->int_mask_RW[class],
+		old_mask & mask);
 }
 
 static void int_mask_or(struct spu *spu, int class, u64 mask)
 {
 	u64 old_mask;
 
-	old_mask = in_be64(&spu->priv1->int_mask_RW[class]);
-	out_be64(&spu->priv1->int_mask_RW[class], old_mask | mask);
+	old_mask = in_be64(&spu_get_pdata(spu)->priv1->int_mask_RW[class]);
+	out_be64(&spu_get_pdata(spu)->priv1->int_mask_RW[class],
+		old_mask | mask);
 }
 
 static void int_mask_set(struct spu *spu, int class, u64 mask)
 {
-	out_be64(&spu->priv1->int_mask_RW[class], mask);
+	out_be64(&spu_get_pdata(spu)->priv1->int_mask_RW[class], mask);
 }
 
 static u64 int_mask_get(struct spu *spu, int class)
 {
-	return in_be64(&spu->priv1->int_mask_RW[class]);
+	return in_be64(&spu_get_pdata(spu)->priv1->int_mask_RW[class]);
 }
 
 static void int_stat_clear(struct spu *spu, int class, u64 stat)
 {
-	out_be64(&spu->priv1->int_stat_RW[class], stat);
+	out_be64(&spu_get_pdata(spu)->priv1->int_stat_RW[class], stat);
 }
 
 static u64 int_stat_get(struct spu *spu, int class)
 {
-	return in_be64(&spu->priv1->int_stat_RW[class]);
+	return in_be64(&spu_get_pdata(spu)->priv1->int_stat_RW[class]);
 }
 
 static void cpu_affinity_set(struct spu *spu, int cpu)
 {
 	u64 target = iic_get_target_id(cpu);
 	u64 route = target << 48 | target << 32 | target << 16;
-	out_be64(&spu->priv1->int_route_RW, route);
+	out_be64(&spu_get_pdata(spu)->priv1->int_route_RW, route);
 }
 
 static u64 mfc_dar_get(struct spu *spu)
 {
-	return in_be64(&spu->priv1->mfc_dar_RW);
+	return in_be64(&spu_get_pdata(spu)->priv1->mfc_dar_RW);
 }
 
 static u64 mfc_dsisr_get(struct spu *spu)
 {
-	return in_be64(&spu->priv1->mfc_dsisr_RW);
+	return in_be64(&spu_get_pdata(spu)->priv1->mfc_dsisr_RW);
 }
 
 static void mfc_dsisr_set(struct spu *spu, u64 dsisr)
 {
-	out_be64(&spu->priv1->mfc_dsisr_RW, dsisr);
+	out_be64(&spu_get_pdata(spu)->priv1->mfc_dsisr_RW, dsisr);
 }
 
 static void mfc_sdr_setup(struct spu *spu)
 {
-	out_be64(&spu->priv1->mfc_sdr_RW, mfspr(SPRN_SDR1));
+	out_be64(&spu_get_pdata(spu)->priv1->mfc_sdr_RW, mfspr(SPRN_SDR1));
 }
 
 static void mfc_sr1_set(struct spu *spu, u64 sr1)
 {
-	out_be64(&spu->priv1->mfc_sr1_RW, sr1);
+	out_be64(&spu_get_pdata(spu)->priv1->mfc_sr1_RW, sr1);
 }
 
 static u64 mfc_sr1_get(struct spu *spu)
 {
-	return in_be64(&spu->priv1->mfc_sr1_RW);
+	return in_be64(&spu_get_pdata(spu)->priv1->mfc_sr1_RW);
 }
 
 static void mfc_tclass_id_set(struct spu *spu, u64 tclass_id)
 {
-	out_be64(&spu->priv1->mfc_tclass_id_RW, tclass_id);
+	out_be64(&spu_get_pdata(spu)->priv1->mfc_tclass_id_RW, tclass_id);
 }
 
 static u64 mfc_tclass_id_get(struct spu *spu)
 {
-	return in_be64(&spu->priv1->mfc_tclass_id_RW);
+	return in_be64(&spu_get_pdata(spu)->priv1->mfc_tclass_id_RW);
 }
 
 static void tlb_invalidate(struct spu *spu)
 {
-	out_be64(&spu->priv1->tlb_invalidate_entry_W, 0ul);
+	out_be64(&spu_get_pdata(spu)->priv1->tlb_invalidate_entry_W, 0ul);
 }
 
 static void resource_allocation_groupID_set(struct spu *spu, u64 id)
 {
-	out_be64(&spu->priv1->resource_allocation_groupID_RW, id);
+	out_be64(&spu_get_pdata(spu)->priv1->resource_allocation_groupID_RW,
+		id);
 }
 
 static u64 resource_allocation_groupID_get(struct spu *spu)
 {
-	return in_be64(&spu->priv1->resource_allocation_groupID_RW);
+	return in_be64(
+		&spu_get_pdata(spu)->priv1->resource_allocation_groupID_RW);
 }
 
 static void resource_allocation_enable_set(struct spu *spu, u64 enable)
 {
-	out_be64(&spu->priv1->resource_allocation_enable_RW, enable);
+	out_be64(&spu_get_pdata(spu)->priv1->resource_allocation_enable_RW,
+		enable);
 }
 
 static u64 resource_allocation_enable_get(struct spu *spu)
 {
-	return in_be64(&spu->priv1->resource_allocation_enable_RW);
+	return in_be64(
+		&spu_get_pdata(spu)->priv1->resource_allocation_enable_RW);
 }
 
 const struct spu_priv1_ops spu_priv1_mmio_ops =
Index: cell--common--6/arch/powerpc/platforms/cell/spu_priv1_mmio.h
===================================================================
--- /dev/null
+++ cell--common--6/arch/powerpc/platforms/cell/spu_priv1_mmio.h
@@ -0,0 +1,26 @@
+/*
+ * spu hypervisor abstraction for direct hardware access.
+ *
+ *  Copyright (C) 2006 Sony Computer Entertainment Inc.
+ *  Copyright 2006 Sony Corp.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef SPU_PRIV1_MMIO_H
+#define SPU_PRIV1_MMIO_H
+
+struct device_node *spu_devnode(struct spu *spu);
+
+#endif /* SPU_PRIV1_MMIO_H */
Index: cell--common--6/arch/powerpc/xmon/xmon.c
===================================================================
--- cell--common--6.orig/arch/powerpc/xmon/xmon.c
+++ cell--common--6/arch/powerpc/xmon/xmon.c
@@ -2769,8 +2769,6 @@
 
 	DUMP_FIELD(spu, "0x%x", number);
 	DUMP_FIELD(spu, "%s", name);
-	DUMP_FIELD(spu, "%s", devnode->full_name);
-	DUMP_FIELD(spu, "0x%x", nid);
 	DUMP_FIELD(spu, "0x%lx", local_store_phys);
 	DUMP_FIELD(spu, "0x%p", local_store);
 	DUMP_FIELD(spu, "0x%lx", ls_size);
@@ -2794,12 +2792,8 @@
 	DUMP_FIELD(spu, "0x%x", problem->spu_runcntl_RW);
 	DUMP_FIELD(spu, "0x%x", problem->spu_status_R);
 	DUMP_FIELD(spu, "0x%x", problem->spu_npc_RW);
-	DUMP_FIELD(spu, "0x%p", priv1);
-
-	if (spu->priv1)
-		DUMP_FIELD(spu, "0x%lx", priv1->mfc_sr1_RW);
-
 	DUMP_FIELD(spu, "0x%p", priv2);
+	spu_dump_pdata_fields(spu);
 }
 
 static int do_spu_cmd(void)
Index: cell--common--6/include/asm-powerpc/spu.h
===================================================================
--- cell--common--6.orig/include/asm-powerpc/spu.h
+++ cell--common--6/include/asm-powerpc/spu.h
@@ -111,13 +111,11 @@
 	u8 *local_store;
 	unsigned long problem_phys;
 	struct spu_problem __iomem *problem;
-	struct spu_priv1 __iomem *priv1;
 	struct spu_priv2 __iomem *priv2;
 	struct list_head list;
 	struct list_head sched_list;
 	struct list_head full_list;
 	int number;
-	int nid;
 	unsigned int irqs[3];
 	u32 node;
 	u64 flags;
@@ -144,8 +142,7 @@
 	char irq_c1[8];
 	char irq_c2[8];
 
-	struct device_node *devnode;
-
+	void* pdata; /* platform private data */
 	struct sys_device sysdev;
 };
 
Index: cell--common--6/include/asm-powerpc/spu_priv1.h
===================================================================
--- cell--common--6.orig/include/asm-powerpc/spu_priv1.h
+++ cell--common--6/include/asm-powerpc/spu_priv1.h
@@ -21,12 +21,13 @@
 #define _SPU_PRIV1_H
 #if defined(__KERNEL__)
 
+#include <linux/types.h>
+
 struct spu;
 
 /* access to priv1 registers */
 
-struct spu_priv1_ops
-{
+struct spu_priv1_ops {
 	void (*int_mask_and) (struct spu *spu, int class, u64 mask);
 	void (*int_mask_or) (struct spu *spu, int class, u64 mask);
 	void (*int_mask_set) (struct spu *spu, int class, u64 mask);
@@ -171,12 +172,49 @@
 	return spu_priv1_ops->resource_allocation_enable_get(spu);
 }
 
-/* The declarations folowing are put here for convenience
- * and only intended to be used by the platform setup code
- * for initializing spu_priv1_ops.
+/* spu management abstraction */
+
+struct spu_management_ops {
+	int (*enumerate_spus)(int (*fn)(void *data));
+	int (*create_spu)(struct spu *spu, void *data);
+	int (*destroy_spu)(struct spu *spu);
+	void (*dump_pdata_fields)(struct spu *spu);
+};
+
+extern const struct spu_management_ops* spu_management_ops;
+
+static inline int
+spu_enumerate_spus (int (*fn)(void *data))
+{
+	return spu_management_ops->enumerate_spus(fn);
+}
+
+static inline int
+spu_create_spu (struct spu *spu, void *data)
+{
+	return spu_management_ops->create_spu(spu, data);
+}
+
+static inline int
+spu_destroy_spu (struct spu *spu)
+{
+	return spu_management_ops->destroy_spu(spu);
+}
+
+static inline void
+spu_dump_pdata_fields (struct spu *spu)
+{
+	if (spu_management_ops->dump_pdata_fields)
+		spu_management_ops->dump_pdata_fields(spu);
+}
+
+/*
+ * The declarations folowing are put here for convenience
+ * and only intended to be used by the platform setup code.
  */
 
 extern const struct spu_priv1_ops spu_priv1_mmio_ops;
+extern const struct spu_management_ops spu_management_of_ops;
 
 #endif /* __KERNEL__ */
 #endif

^ permalink raw reply

* [PATCH 5/16] cell: replace spu.nid with spu.node
From: Geoff Levand @ 2006-11-10 20:01 UTC (permalink / raw)
  To: Paul Mackerras; +Cc: linuxppc-dev, Arnd Bergmann

Replace the use of the platform specific variable spu.nid with the
platform independednt variable spu.node.


Signed-off-by: Geoff Levand <geoffrey.levand@am.sony.com>

---
 arch/powerpc/platforms/cell/spu_base.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

Index: cell--common--6/arch/powerpc/platforms/cell/spu_base.c
===================================================================
--- cell--common--6.orig/arch/powerpc/platforms/cell/spu_base.c
+++ cell--common--6/arch/powerpc/platforms/cell/spu_base.c
@@ -810,14 +810,14 @@
 		return ret;
 	}
 
-	sysfs_add_device_to_node(&spu->sysdev, spu->nid);
+	sysfs_add_device_to_node(&spu->sysdev, spu->node);
 
 	return 0;
 }
 
 static void spu_destroy_sysdev(struct spu *spu)
 {
-	sysfs_remove_device_from_node(&spu->sysdev, spu->nid);
+	sysfs_remove_device_from_node(&spu->sysdev, spu->node);
 	sysdev_unregister(&spu->sysdev);
 }
 

^ permalink raw reply

* [PATCH 4/16] powerpc: check for null init_early routine
From: Geoff Levand @ 2006-11-10 20:01 UTC (permalink / raw)
  To: Paul Mackerras; +Cc: linuxppc-dev

Add a check for a null ppc_md.init_early to allow platforms that
don't require an init_early routine to just set this member to null.

Signed-off-by: Geoff Levand <geoffrey.levand@am.sony.com>

---
 arch/powerpc/kernel/setup_64.c |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

Index: cell--common--6/arch/powerpc/kernel/setup_64.c
===================================================================
--- cell--common--6.orig/arch/powerpc/kernel/setup_64.c
+++ cell--common--6/arch/powerpc/kernel/setup_64.c
@@ -392,7 +392,8 @@
 	 * setting up the hash table pointers. It also sets up some interrupt-mapping
 	 * related options that will be used by finish_device_tree()
 	 */
-	ppc_md.init_early();
+	if (ppc_md.init_early)
+		ppc_md.init_early();
 
  	/*
 	 * We can discover serial ports now since the above did setup the

^ permalink raw reply

* [PATCH 3/16] cell: set ARCH_SPARSEMEM_DEFAULT in Kconfig
From: Geoff Levand @ 2006-11-10 20:00 UTC (permalink / raw)
  To: Paul Mackerras; +Cc: linuxppc-dev, Arnd Bergmann

The current cell processor support needs sparsemem, so set it as
the default memory model.

Signed-off-by: Geoff Levand <geoffrey.levand@am.sony.com>

---
As noted by Benjamin Herrenschmidt.

Index: cell--common--6/arch/powerpc/Kconfig
===================================================================
--- cell--common--6.orig/arch/powerpc/Kconfig
+++ cell--common--6/arch/powerpc/Kconfig
@@ -734,7 +734,7 @@
 
 config ARCH_SPARSEMEM_DEFAULT
 	def_bool y
-	depends on SMP && PPC_PSERIES
+	depends on (SMP && PPC_PSERIES) || PPC_CELL
 
 config ARCH_POPULATES_NODE_MAP
 	def_bool y

^ permalink raw reply


This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.