* Re: [patch 2/2] powerpc: replace isync with lwsync
From: Nick Piggin @ 2009-03-04 10:15 UTC (permalink / raw)
To: Benjamin Herrenschmidt; +Cc: linuxppc-dev, paulus
In-Reply-To: <1236139451.6696.10.camel@pasglop>
On Wed, Mar 04, 2009 at 03:04:11PM +1100, Benjamin Herrenschmidt wrote:
> On Thu, 2009-02-19 at 18:21 +0100, Nick Piggin wrote:
> > OK, here is this patch again. You didn't think I'd let a 2% performance
> > improvement be forgotten? :)
> >
> > Anyway, patch won't work well on architecture without lwsync, but I won't
> > bother fixing that kind of thing and making it merge worthy until you
> > guys say something positive about it.
> >
> > 20 runs of tbench on the G5
> >
> > unpatched AVG=920.37 STD=2.36
> > patched AVG=938.89 STD=3.33
> >
> > (throughput in MB/s) This is a 1.9% throughput increase.
>
> Definitely worth it believe. We could use a macro that uses michael new
> improvements on the CPU features code pathing so that the isync gets
> changed to lwsync on some CPUs based on the availability of it.
OK. I guess the interesting part about this is that I can't find any
IBM documentation for lwsync capable CPUs that suggest using this
pattern for acquire locking. It would be interesting to know whether
it helps other CPUs...
^ permalink raw reply
* Re: [PATCH 2/2] powerpc: oprofile: enable support for ppc750 processors
From: Octavian Purdila @ 2009-03-04 12:02 UTC (permalink / raw)
To: Benjamin Herrenschmidt; +Cc: linuxppc-dev
In-Reply-To: <1236142078.6696.11.camel@pasglop>
From: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> On Tue, 2009-01-06 at 14:55 +0200, Octavian Purdila wrote:
> > Signed-off-by: Octavian Purdila <opurdila@ixiacom.com>
>
> So I'm going to merge 1/2 but this one should really be changed to
> advertise ppc/750 in oprofile_cpu_type (ie. to userspace).
>
Sure. Here is the new patch which uses ppc/750. It enables oprofile for all 3 FX variants and GX as well.
Thanks!
tavi
commit 70f4865a614e9b0ff4594ebd52b95f78e998b79f
Author: Octavian Purdila <opurdila@ixiacom.com>
Date: Tue Jan 6 12:51:43 2009 +0200
powerpc: oprofile: enable support for ppc750 processors
Signed-off-by: Octavian Purdila <opurdila@ixiacom.com>
diff --git a/arch/powerpc/kernel/cputable.c b/arch/powerpc/kernel/cputable.c
index 923f87a..c3ea72b 100644
--- a/arch/powerpc/kernel/cputable.c
+++ b/arch/powerpc/kernel/cputable.c
@@ -726,6 +726,8 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_setup = __setup_cpu_750,
.machine_check = machine_check_generic,
.platform = "ppc750",
+ .oprofile_cpu_type = "ppc/750",
+ .oprofile_type = PPC_OPROFILE_G4,
},
{ /* 750FX rev 2.0 must disable HID0[DPM] */
.pvr_mask = 0xffffffff,
@@ -741,6 +743,8 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_setup = __setup_cpu_750,
.machine_check = machine_check_generic,
.platform = "ppc750",
+ .oprofile_cpu_type = "ppc/750",
+ .oprofile_type = PPC_OPROFILE_G4,
},
{ /* 750FX (All revs except 2.0) */
.pvr_mask = 0xffff0000,
@@ -756,6 +760,8 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_setup = __setup_cpu_750fx,
.machine_check = machine_check_generic,
.platform = "ppc750",
+ .oprofile_cpu_type = "ppc/750",
+ .oprofile_type = PPC_OPROFILE_G4,
},
{ /* 750GX */
.pvr_mask = 0xffff0000,
@@ -771,6 +777,8 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_setup = __setup_cpu_750fx,
.machine_check = machine_check_generic,
.platform = "ppc750",
+ .oprofile_cpu_type = "ppc/750",
+ .oprofile_type = PPC_OPROFILE_G4,
},
{ /* 740/750 (L2CR bit need fixup for 740) */
.pvr_mask = 0xffff0000,
^ permalink raw reply related
* Re: [rtc-linux] Re: [PATCH/RFC 0/5] Generic RTC class driver
From: Alessandro Zummo @ 2009-03-04 12:47 UTC (permalink / raw)
To: rtc-linux
Cc: Linux/m68k, Richard Zidlicky, linux-parisc, Linux/PPC,
Linux Kernel Development, Kyle McMartin, Development, Paul Mundt,
Geert.Uytterhoeven, David Woodhouse
In-Reply-To: <alpine.LRH.2.00.0903040920240.2416@vixen.sonytel.be>
On Wed, 4 Mar 2009 09:26:29 +0100 (CET)
Geert Uytterhoeven <Geert.Uytterhoeven@sonycom.com> wrote:
> Thanks Paul, I wasn't aware of that thread!
>
> Yes, this is almost the same. The only part I don't agree with is the move of
> the creation of the platform device from arch-specific code to rtc-firmware.c,
> as this makes autoloading the driver more difficult.
and it's also against a proper implementation of the device/driver model.
> Seems like everybody but the RTC maintainer has an interest in having an RTC
> class driver on top of [gs]et_rtc_time()... ;-)
That's because everyone is lazy :)
Seriously, if you want to handle it in the way we wrote
in the previous emails, it's ok for me.
--
Best regards,
Alessandro Zummo,
Tower Technologies - Torino, Italy
http://www.towertech.it
^ permalink raw reply
* Re: [rtc-linux] Re: [PATCH/RFC 0/5] Generic RTC class driver
From: Geert Uytterhoeven @ 2009-03-04 12:51 UTC (permalink / raw)
To: Alessandro Zummo
Cc: Linux/m68k, Richard Zidlicky, rtc-linux, linux-parisc,
Linux Kernel Development, Kyle McMartin, Linux/PPC Development,
Paul Mundt, David Woodhouse
In-Reply-To: <20090304134705.7d9bbe5c@i1501.lan.towertech.it>
On Wed, 4 Mar 2009, Alessandro Zummo wrote:
> On Wed, 4 Mar 2009 09:26:29 +0100 (CET)
> Geert Uytterhoeven <Geert.Uytterhoeven@sonycom.com> wrote:
>
> > Thanks Paul, I wasn't aware of that thread!
> >
> > Yes, this is almost the same. The only part I don't agree with is the move of
> > the creation of the platform device from arch-specific code to rtc-firmware.c,
> > as this makes autoloading the driver more difficult.
>
> and it's also against a proper implementation of the device/driver model.
>
> > Seems like everybody but the RTC maintainer has an interest in having an RTC
> > class driver on top of [gs]et_rtc_time()... ;-)
>
> That's because everyone is lazy :)
>
> Seriously, if you want to handle it in the way we wrote
> in the previous emails, it's ok for me.
OK, I will do so. Thanks!
With kind regards,
Geert Uytterhoeven
Software Architect
Sony Techsoft Centre Europe
The Corporate Village · Da Vincilaan 7-D1 · B-1935 Zaventem · Belgium
Phone: +32 (0)2 700 8453
Fax: +32 (0)2 700 8622
E-mail: Geert.Uytterhoeven@sonycom.com
Internet: http://www.sony-europe.com/
A division of Sony Europe (Belgium) N.V.
VAT BE 0413.825.160 · RPR Brussels
Fortis · BIC GEBABEBB · IBAN BE41293037680010
^ permalink raw reply
* RE: mpc8349e-mitx 2.6.25 serial IRQ assigned wrong
From: Michael Ellerman @ 2009-03-04 13:00 UTC (permalink / raw)
To: onramp123; +Cc: linuxppc-dev
In-Reply-To: <025a01c99c8b$90afb030$6b03a8c0@sdelaney2>
[-- Attachment #1: Type: text/plain, Size: 1983 bytes --]
On Tue, 2009-03-03 at 21:39 -0800, Steve DeLaney wrote:
>
> Here's an update on this issue.
>
> After verifying the device tree was OK, the OF traces led us to the root
> cause.
>
> powerpc irq.c irq_alloc_virt() assigns a virtual IRQ as a function of the
> input hardware IRQ hint, AND NUM_ISA_INTERRUPTS. it turns out that
> asm-ppc/irq.h defines NUM_ISA_INTERRUPTS 16, so that the allocated virq
> is offset by this amount. this accounts for the remap shown below for
> i2c and serial device vectors.
>
> I didn't realize before that /proc/interrupts
> serviced by irq.c show_interrupts() displays virtual vector numbers.
>
> For the mpc8349e-mitx this must be incorrect since there is no ISA?
> Essentially all that is needed is a 1:1 mapping hirq:virq since
> each interrupt source in the system appears to have a unique vector
> in the SOC IPIC. It seems this scheme is needlessly complex, at least for
> mpc8349e.
>
> For now we simply define irq.h NUM_ISA_INTERRUPTS 0
> but this isn't a complete solution since our PCI device
> interrupt on hirq 20, ends up allocated on virq 1. To force
> this to work, the driver does an irq_create_mapping(NULL, 20),
> then assigns its own dev->irq=20 before calling request_irq()
>
> I'm sure someone has a more elegant solution but that's what
> we've been able to come up with so far.
I'm not clear on what the problem is.
The interrupt numbers in /proc/interrupts will no longer match what the
old kernel had, because as you mention the values in /proc/interrupts
are virtual irqs.
If you want to see the mapping turn on CONFIG_VIRQ_DEBUG and you'll get
a file in debugfs called virq_mapping that shows the details.
cheers
--
Michael Ellerman
OzLabs, IBM Australia Development Lab
wwweb: http://michael.ellerman.id.au
phone: +61 2 6212 1183 (tie line 70 21183)
We do not inherit the earth from our ancestors,
we borrow it from our children. - S.M.A.R.T Person
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 197 bytes --]
^ permalink raw reply
* [PATCH/RFC] ps3/block: Add ps3vram-ng driver for accessing video RAM as block device
From: Geert Uytterhoeven @ 2009-03-04 13:57 UTC (permalink / raw)
To: Benjamin Herrenschmidt, Jens Axboe, Jim Paris
Cc: Arnd Bergmann, Linux Kernel Development, Linux/PPC Development,
linux-mtd, Vivien Chappelier, David Woodhouse,
Cell Broadband Engine OSS Development
Hi,
Below is the rewrite of the PS3 Video RAM Storage Driver as a plain block
device, as requested by Arnd Bergmann.
The MTD-based PS3 Video RAM Storage Driver was integrated into the mainline
kernel in 2.6.29-rc1.
Ideally, we think it would be best if the existing MTD-based ps3vram driver
would be replaced by the new block-based ps3vram driver before 2.6.29 is
released. This would relieve the burden of supporting two different swap space
schemes on PS3 (swap on /dev/mtdblock0 vs. /dev/ps3vram) from the distro
maintainer's shoulders, as in that case there would never have been a stable
kernel version containing the MTD-based ps3vram driver.
What do you think? If this is accepted, I'll submit a patch to remove the MTD
ps3vram and add the new driver as ps3vram (instead of ps3vram-ng).
Thanks for your (review and other) comments!
---
>From e19ce619675bc150cd82701e7b272f7018c36527 Mon Sep 17 00:00:00 2001
From: Geert Uytterhoeven <Geert.Uytterhoeven@sonycom.com>
Date: Wed, 25 Feb 2009 18:32:10 +0100
Subject: [PATCH] ps3/block: Add ps3vram-ng driver for accessing video RAM as block device
Add ps3vram-ng driver, which exposes unused video RAM on the PS3 as a block
device suitable for storage or swap. Fast data transfer is achieved
using a local cache in system RAM and DMA transfers via the GPU.
Notes:
- As both the existing MTD ps3vram and ps3vram-ng bind to the same PS3 system
bus device, the driver which is loaded first by udev wins. However, you can
unload ps3vram, and load ps3vram-ng later.
- ps3vram-ng is faster than ps3vram:
o 1 MiB blocks: +50% (read), +5% (write)
o 4 KiB blocks: +50% (read), +5% (write)
o 512 B blocks: +10% (read), +10% (write)
Signed-off-by: Geert Uytterhoeven <Geert.Uytterhoeven@sonycom.com>
Cc: Jim Paris <jim@jtan.com>
Cc: Vivien Chappelier <vivien.chappelier@free.fr>
---
arch/powerpc/platforms/ps3/Kconfig | 7 +
drivers/block/Makefile | 1 +
drivers/block/ps3vram_ng.c | 916 ++++++++++++++++++++++++++++++++++++
3 files changed, 924 insertions(+), 0 deletions(-)
create mode 100644 drivers/block/ps3vram_ng.c
diff --git a/arch/powerpc/platforms/ps3/Kconfig b/arch/powerpc/platforms/ps3/Kconfig
index 920cf7a..b3b7a20 100644
--- a/arch/powerpc/platforms/ps3/Kconfig
+++ b/arch/powerpc/platforms/ps3/Kconfig
@@ -128,6 +128,13 @@ config PS3_FLASH
be disabled on the kernel command line using "ps3flash=off", to
not allocate this fixed buffer.
+config PS3_VRAM
+ tristate "PS3 Video RAM Storage Driver"
+ depends on PPC_PS3 && BLOCK
+ help
+ This driver allows you to use excess PS3 video RAM as volatile
+ storage or system swap.
+
config PS3_LPM
tristate "PS3 Logical Performance Monitor support"
depends on PPC_PS3
diff --git a/drivers/block/Makefile b/drivers/block/Makefile
index 204332b..ad3e45e 100644
--- a/drivers/block/Makefile
+++ b/drivers/block/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_MAC_FLOPPY) += swim3.o
obj-$(CONFIG_BLK_DEV_FD) += floppy.o
obj-$(CONFIG_AMIGA_FLOPPY) += amiflop.o
obj-$(CONFIG_PS3_DISK) += ps3disk.o
+obj-$(CONFIG_PS3_VRAM) += ps3vram_ng.o
obj-$(CONFIG_ATARI_FLOPPY) += ataflop.o
obj-$(CONFIG_AMIGA_Z2RAM) += z2ram.o
obj-$(CONFIG_BLK_DEV_RAM) += brd.o
diff --git a/drivers/block/ps3vram_ng.c b/drivers/block/ps3vram_ng.c
new file mode 100644
index 0000000..fa24a17
--- /dev/null
+++ b/drivers/block/ps3vram_ng.c
@@ -0,0 +1,916 @@
+/*
+ * ps3vram - Use extra PS3 video ram as MTD block device.
+ *
+ * Copyright 2009 Sony Corporation
+ *
+ * Based on the MTD ps3vram driver, which is
+ * Copyright (c) 2007-2008 Jim Paris <jim@jtan.com>
+ * Added support RSX DMA Vivien Chappelier <vivien.chappelier@free.fr>
+ */
+
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+
+#include <asm/firmware.h>
+#include <asm/lv1call.h>
+#include <asm/ps3.h>
+
+
+#define DEVICE_NAME "ps3vram"
+
+
+#define XDR_BUF_SIZE (2 * 1024 * 1024) /* XDR buffer (must be 1MiB aligned) */
+#define XDR_IOIF 0x0c000000
+
+#define FIFO_BASE XDR_IOIF
+#define FIFO_SIZE (64 * 1024)
+
+#define DMA_PAGE_SIZE (4 * 1024)
+
+#define CACHE_PAGE_SIZE (256 * 1024)
+#define CACHE_PAGE_COUNT ((XDR_BUF_SIZE - FIFO_SIZE) / CACHE_PAGE_SIZE)
+
+#define CACHE_OFFSET CACHE_PAGE_SIZE
+#define FIFO_OFFSET 0
+
+#define CTRL_PUT 0x10
+#define CTRL_GET 0x11
+#define CTRL_TOP 0x15
+
+#define UPLOAD_SUBCH 1
+#define DOWNLOAD_SUBCH 2
+
+#define NV_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN 0x0000030c
+#define NV_MEMORY_TO_MEMORY_FORMAT_NOTIFY 0x00000104
+
+#define L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT 0x601
+
+#define CACHE_PAGE_PRESENT 1
+#define CACHE_PAGE_DIRTY 2
+
+struct ps3vram_tag {
+ unsigned int address;
+ unsigned int flags;
+};
+
+struct ps3vram_cache {
+ unsigned int page_count;
+ unsigned int page_size;
+ struct ps3vram_tag *tags;
+ unsigned int hit;
+ unsigned int miss;
+};
+
+struct ps3vram_priv {
+ spinlock_t lock; /* Request queue spinlock */
+ struct task_struct *thread;
+ struct request_queue *queue;
+ struct gendisk *gendisk;
+
+ u64 size;
+
+ u64 memory_handle;
+ u64 context_handle;
+ u32 *ctrl;
+ u32 *reports;
+ u8 __iomem *ddr_base;
+ u8 *xdr_buf;
+
+ u32 *fifo_base;
+ u32 *fifo_ptr;
+
+ struct ps3vram_cache cache;
+};
+
+
+static int ps3vram_major;
+
+
+static struct block_device_operations ps3vram_fops = {
+ .owner = THIS_MODULE,
+};
+
+
+#define DMA_NOTIFIER_HANDLE_BASE 0x66604200 /* first DMA notifier handle */
+#define DMA_NOTIFIER_OFFSET_BASE 0x1000 /* first DMA notifier offset */
+#define DMA_NOTIFIER_SIZE 0x40
+#define NOTIFIER 7 /* notifier used for completion report */
+
+/* A trailing '-' means to subtract off ps3fb_videomemory.size */
+static char *size = "256M-";
+module_param(size, charp, 0);
+MODULE_PARM_DESC(size, "memory size");
+
+static u32 *ps3vram_get_notifier(u32 *reports, int notifier)
+{
+ return (void *)reports + DMA_NOTIFIER_OFFSET_BASE +
+ DMA_NOTIFIER_SIZE * notifier;
+}
+
+static void ps3vram_notifier_reset(struct ps3_system_bus_device *dev)
+{
+ struct ps3vram_priv *priv = dev->core.driver_data;
+ u32 *notify = ps3vram_get_notifier(priv->reports, NOTIFIER);
+ int i;
+
+ for (i = 0; i < 4; i++)
+ notify[i] = 0xffffffff;
+}
+
+static int ps3vram_notifier_wait(struct ps3_system_bus_device *dev,
+ unsigned int timeout_ms)
+{
+ struct ps3vram_priv *priv = dev->core.driver_data;
+ u32 *notify = ps3vram_get_notifier(priv->reports, NOTIFIER);
+ unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms);
+
+ do {
+ if (!notify[3])
+ return 0;
+ msleep(1);
+ } while (time_before(jiffies, timeout));
+
+ return -ETIMEDOUT;
+}
+
+static void ps3vram_init_ring(struct ps3_system_bus_device *dev)
+{
+ struct ps3vram_priv *priv = dev->core.driver_data;
+
+ priv->ctrl[CTRL_PUT] = FIFO_BASE + FIFO_OFFSET;
+ priv->ctrl[CTRL_GET] = FIFO_BASE + FIFO_OFFSET;
+}
+
+static int ps3vram_wait_ring(struct ps3_system_bus_device *dev,
+ unsigned int timeout_ms)
+{
+ struct ps3vram_priv *priv = dev->core.driver_data;
+ unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms);
+
+ do {
+ if (priv->ctrl[CTRL_PUT] == priv->ctrl[CTRL_GET])
+ return 0;
+ msleep(1);
+ } while (time_before(jiffies, timeout));
+
+ dev_dbg(&dev->core, "FIFO timeout (%08x/%08x/%08x)\n",
+ priv->ctrl[CTRL_PUT], priv->ctrl[CTRL_GET],
+ priv->ctrl[CTRL_TOP]);
+
+ return -ETIMEDOUT;
+}
+
+static void ps3vram_out_ring(struct ps3vram_priv *priv, u32 data)
+{
+ *(priv->fifo_ptr)++ = data;
+}
+
+static void ps3vram_begin_ring(struct ps3vram_priv *priv, u32 chan, u32 tag,
+ u32 size)
+{
+ ps3vram_out_ring(priv, (size << 18) | (chan << 13) | tag);
+}
+
+static void ps3vram_rewind_ring(struct ps3_system_bus_device *dev)
+{
+ struct ps3vram_priv *priv = dev->core.driver_data;
+ u64 status;
+
+ ps3vram_out_ring(priv, 0x20000000 | (FIFO_BASE + FIFO_OFFSET));
+
+ priv->ctrl[CTRL_PUT] = FIFO_BASE + FIFO_OFFSET;
+
+ /* asking the HV for a blit will kick the FIFO */
+ status = lv1_gpu_context_attribute(priv->context_handle,
+ L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT, 0,
+ 0, 0, 0);
+ if (status)
+ dev_err(&dev->core,
+ "%s: lv1_gpu_context_attribute failed %llu\n",
+ __func__, status);
+
+ priv->fifo_ptr = priv->fifo_base;
+}
+
+static void ps3vram_fire_ring(struct ps3_system_bus_device *dev)
+{
+ struct ps3vram_priv *priv = dev->core.driver_data;
+ u64 status;
+
+ mutex_lock(&ps3_gpu_mutex);
+
+ priv->ctrl[CTRL_PUT] = FIFO_BASE + FIFO_OFFSET +
+ (priv->fifo_ptr - priv->fifo_base) * sizeof(u32);
+
+ /* asking the HV for a blit will kick the FIFO */
+ status = lv1_gpu_context_attribute(priv->context_handle,
+ L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT, 0,
+ 0, 0, 0);
+ if (status)
+ dev_err(&dev->core,
+ "%s: lv1_gpu_context_attribute failed %llu\n",
+ __func__, status);
+
+ if ((priv->fifo_ptr - priv->fifo_base) * sizeof(u32) >
+ FIFO_SIZE - 1024) {
+ dev_dbg(&dev->core, "FIFO full, rewinding\n");
+ ps3vram_wait_ring(dev, 200);
+ ps3vram_rewind_ring(dev);
+ }
+
+ mutex_unlock(&ps3_gpu_mutex);
+}
+
+static void ps3vram_bind(struct ps3_system_bus_device *dev)
+{
+ struct ps3vram_priv *priv = dev->core.driver_data;
+
+ ps3vram_begin_ring(priv, UPLOAD_SUBCH, 0, 1);
+ ps3vram_out_ring(priv, 0x31337303);
+ ps3vram_begin_ring(priv, UPLOAD_SUBCH, 0x180, 3);
+ ps3vram_out_ring(priv, DMA_NOTIFIER_HANDLE_BASE + NOTIFIER);
+ ps3vram_out_ring(priv, 0xfeed0001); /* DMA system RAM instance */
+ ps3vram_out_ring(priv, 0xfeed0000); /* DMA video RAM instance */
+
+ ps3vram_begin_ring(priv, DOWNLOAD_SUBCH, 0, 1);
+ ps3vram_out_ring(priv, 0x3137c0de);
+ ps3vram_begin_ring(priv, DOWNLOAD_SUBCH, 0x180, 3);
+ ps3vram_out_ring(priv, DMA_NOTIFIER_HANDLE_BASE + NOTIFIER);
+ ps3vram_out_ring(priv, 0xfeed0000); /* DMA video RAM instance */
+ ps3vram_out_ring(priv, 0xfeed0001); /* DMA system RAM instance */
+
+ ps3vram_fire_ring(dev);
+}
+
+static int ps3vram_upload(struct ps3_system_bus_device *dev,
+ unsigned int src_offset, unsigned int dst_offset,
+ int len, int count)
+{
+ struct ps3vram_priv *priv = dev->core.driver_data;
+
+ ps3vram_begin_ring(priv, UPLOAD_SUBCH,
+ NV_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN, 8);
+ ps3vram_out_ring(priv, XDR_IOIF + src_offset);
+ ps3vram_out_ring(priv, dst_offset);
+ ps3vram_out_ring(priv, len);
+ ps3vram_out_ring(priv, len);
+ ps3vram_out_ring(priv, len);
+ ps3vram_out_ring(priv, count);
+ ps3vram_out_ring(priv, (1 << 8) | 1);
+ ps3vram_out_ring(priv, 0);
+
+ ps3vram_notifier_reset(dev);
+ ps3vram_begin_ring(priv, UPLOAD_SUBCH,
+ NV_MEMORY_TO_MEMORY_FORMAT_NOTIFY, 1);
+ ps3vram_out_ring(priv, 0);
+ ps3vram_begin_ring(priv, UPLOAD_SUBCH, 0x100, 1);
+ ps3vram_out_ring(priv, 0);
+ ps3vram_fire_ring(dev);
+ if (ps3vram_notifier_wait(dev, 200) < 0) {
+ dev_dbg(&dev->core, "%s: Notifier timeout\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int ps3vram_download(struct ps3_system_bus_device *dev,
+ unsigned int src_offset, unsigned int dst_offset,
+ int len, int count)
+{
+ struct ps3vram_priv *priv = dev->core.driver_data;
+
+ ps3vram_begin_ring(priv, DOWNLOAD_SUBCH,
+ NV_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN, 8);
+ ps3vram_out_ring(priv, src_offset);
+ ps3vram_out_ring(priv, XDR_IOIF + dst_offset);
+ ps3vram_out_ring(priv, len);
+ ps3vram_out_ring(priv, len);
+ ps3vram_out_ring(priv, len);
+ ps3vram_out_ring(priv, count);
+ ps3vram_out_ring(priv, (1 << 8) | 1);
+ ps3vram_out_ring(priv, 0);
+
+ ps3vram_notifier_reset(dev);
+ ps3vram_begin_ring(priv, DOWNLOAD_SUBCH,
+ NV_MEMORY_TO_MEMORY_FORMAT_NOTIFY, 1);
+ ps3vram_out_ring(priv, 0);
+ ps3vram_begin_ring(priv, DOWNLOAD_SUBCH, 0x100, 1);
+ ps3vram_out_ring(priv, 0);
+ ps3vram_fire_ring(dev);
+ if (ps3vram_notifier_wait(dev, 200) < 0) {
+ dev_dbg(&dev->core, "%s: Notifier timeout\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void ps3vram_cache_evict(struct ps3_system_bus_device *dev, int entry)
+{
+ struct ps3vram_priv *priv = dev->core.driver_data;
+ struct ps3vram_cache *cache = &priv->cache;
+
+ if (!(cache->tags[entry].flags & CACHE_PAGE_DIRTY))
+ return;
+
+ dev_dbg(&dev->core, "Flushing %d: 0x%08x\n", entry,
+ cache->tags[entry].address);
+ if (ps3vram_upload(dev, CACHE_OFFSET + entry * cache->page_size,
+ cache->tags[entry].address, DMA_PAGE_SIZE,
+ cache->page_size / DMA_PAGE_SIZE) < 0) {
+ dev_err(&dev->core,
+ "Failed to upload from 0x%x to " "0x%x size 0x%x\n",
+ entry * cache->page_size, cache->tags[entry].address,
+ cache->page_size);
+ }
+ cache->tags[entry].flags &= ~CACHE_PAGE_DIRTY;
+}
+
+static void ps3vram_cache_load(struct ps3_system_bus_device *dev, int entry,
+ unsigned int address)
+{
+ struct ps3vram_priv *priv = dev->core.driver_data;
+ struct ps3vram_cache *cache = &priv->cache;
+
+ dev_dbg(&dev->core, "Fetching %d: 0x%08x\n", entry, address);
+ if (ps3vram_download(dev, address,
+ CACHE_OFFSET + entry * cache->page_size,
+ DMA_PAGE_SIZE,
+ cache->page_size / DMA_PAGE_SIZE) < 0) {
+ dev_err(&dev->core,
+ "Failed to download from 0x%x to 0x%x size 0x%x\n",
+ address, entry * cache->page_size, cache->page_size);
+ }
+
+ cache->tags[entry].address = address;
+ cache->tags[entry].flags |= CACHE_PAGE_PRESENT;
+}
+
+
+static void ps3vram_cache_flush(struct ps3_system_bus_device *dev)
+{
+ struct ps3vram_priv *priv = dev->core.driver_data;
+ struct ps3vram_cache *cache = &priv->cache;
+ int i;
+
+ dev_dbg(&dev->core, "FLUSH\n");
+ for (i = 0; i < cache->page_count; i++) {
+ ps3vram_cache_evict(dev, i);
+ cache->tags[i].flags = 0;
+ }
+}
+
+static unsigned int ps3vram_cache_match(struct ps3_system_bus_device *dev,
+ loff_t address)
+{
+ struct ps3vram_priv *priv = dev->core.driver_data;
+ struct ps3vram_cache *cache = &priv->cache;
+ unsigned int base;
+ unsigned int offset;
+ int i;
+ static int counter;
+
+ offset = (unsigned int) (address & (cache->page_size - 1));
+ base = (unsigned int) (address - offset);
+
+ /* fully associative check */
+ for (i = 0; i < cache->page_count; i++) {
+ if ((cache->tags[i].flags & CACHE_PAGE_PRESENT) &&
+ cache->tags[i].address == base) {
+ cache->hit++;
+ dev_dbg(&dev->core, "Found entry %d: 0x%08x\n", i,
+ cache->tags[i].address);
+ return i;
+ }
+ }
+
+ /* choose a random entry */
+ i = (jiffies + (counter++)) % cache->page_count;
+ dev_dbg(&dev->core, "Using entry %d\n", i);
+
+ ps3vram_cache_evict(dev, i);
+ ps3vram_cache_load(dev, i, base);
+
+ cache->miss++;
+ return i;
+}
+
+static int ps3vram_cache_init(struct ps3_system_bus_device *dev)
+{
+ struct ps3vram_priv *priv = dev->core.driver_data;
+
+ priv->cache.page_count = CACHE_PAGE_COUNT;
+ priv->cache.page_size = CACHE_PAGE_SIZE;
+ priv->cache.tags = kzalloc(sizeof(struct ps3vram_tag) *
+ CACHE_PAGE_COUNT, GFP_KERNEL);
+ if (priv->cache.tags == NULL) {
+ dev_err(&dev->core, "Could not allocate cache tags\n");
+ return -ENOMEM;
+ }
+
+ dev_info(&dev->core, "Created ram cache: %d entries, %d KiB each\n",
+ CACHE_PAGE_COUNT, CACHE_PAGE_SIZE / 1024);
+
+ return 0;
+}
+
+static void ps3vram_cache_cleanup(struct ps3_system_bus_device *dev)
+{
+ struct ps3vram_priv *priv = dev->core.driver_data;
+
+ ps3vram_cache_flush(dev);
+ kfree(priv->cache.tags);
+}
+
+static int ps3vram_read(struct ps3_system_bus_device *dev, loff_t from,
+ size_t len, size_t *retlen, u_char *buf)
+{
+ struct ps3vram_priv *priv = dev->core.driver_data;
+ unsigned int cached, count;
+
+ dev_dbg(&dev->core, "%s: from=0x%08x len=0x%zx\n", __func__,
+ (unsigned int)from, len);
+
+ if (from >= priv->size)
+ return -EINVAL;
+
+ if (len > priv->size - from)
+ len = priv->size - from;
+
+ /* Copy from vram to buf */
+ count = len;
+ while (count) {
+ unsigned int offset, avail;
+ unsigned int entry;
+
+ offset = (unsigned int) (from & (priv->cache.page_size - 1));
+ avail = priv->cache.page_size - offset;
+
+ entry = ps3vram_cache_match(dev, from);
+ cached = CACHE_OFFSET + entry * priv->cache.page_size + offset;
+
+ dev_dbg(&dev->core, "%s: from=%08x cached=%08x offset=%08x "
+ "avail=%08x count=%08x\n", __func__,
+ (unsigned int)from, cached, offset, avail, count);
+
+ if (avail > count)
+ avail = count;
+ memcpy(buf, priv->xdr_buf + cached, avail);
+
+ buf += avail;
+ count -= avail;
+ from += avail;
+ }
+
+ *retlen = len;
+ return 0;
+}
+
+static int ps3vram_write(struct ps3_system_bus_device *dev, loff_t to,
+ size_t len, size_t *retlen, const u_char *buf)
+{
+ struct ps3vram_priv *priv = dev->core.driver_data;
+ unsigned int cached, count;
+
+ if (to >= priv->size)
+ return -EINVAL;
+
+ if (len > priv->size - to)
+ len = priv->size - to;
+
+ /* Copy from buf to vram */
+ count = len;
+ while (count) {
+ unsigned int offset, avail;
+ unsigned int entry;
+
+ offset = (unsigned int) (to & (priv->cache.page_size - 1));
+ avail = priv->cache.page_size - offset;
+
+ entry = ps3vram_cache_match(dev, to);
+ cached = CACHE_OFFSET + entry * priv->cache.page_size + offset;
+
+ dev_dbg(&dev->core, "%s: to=%08x cached=%08x offset=%08x "
+ "avail=%08x count=%08x\n", __func__, (unsigned int)to,
+ cached, offset, avail, count);
+
+ if (avail > count)
+ avail = count;
+ memcpy(priv->xdr_buf + cached, buf, avail);
+
+ priv->cache.tags[entry].flags |= CACHE_PAGE_DIRTY;
+
+ buf += avail;
+ count -= avail;
+ to += avail;
+ }
+
+ *retlen = len;
+ return 0;
+}
+
+static int ps3vram_proc_show(struct seq_file *m, void *v)
+{
+ struct ps3vram_priv *priv = m->private;
+
+ seq_printf(m, "hit:%u\nmiss:%u\n", priv->cache.hit, priv->cache.miss);
+ return 0;
+}
+
+static int ps3vram_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ps3vram_proc_show, PDE(inode)->data);
+}
+
+static const struct file_operations ps3vram_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = ps3vram_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static void __devinit ps3vram_proc_init(struct ps3_system_bus_device *dev)
+{
+ struct ps3vram_priv *priv = dev->core.driver_data;
+ struct proc_dir_entry *pde;
+
+ pde = proc_create(DEVICE_NAME, 0444, NULL, &ps3vram_proc_fops);
+ if (!pde) {
+ dev_warn(&dev->core, "failed to create /proc entry\n");
+ return;
+ }
+
+ pde->owner = THIS_MODULE;
+ pde->data = priv;
+}
+
+static void ps3vram_do_request(struct ps3_system_bus_device *dev,
+ struct request *req)
+{
+ struct ps3vram_priv *priv = dev->core.driver_data;
+ int write, res, uptodate = 0;
+ const char *op;
+ sector_t start_sector;
+ unsigned int sectors;
+ loff_t offset;
+ size_t len, retlen;
+
+ write = rq_data_dir(req);
+ op = write ? "write" : "read";
+
+ start_sector = req->sector;
+ sectors = req->current_nr_sectors;
+ dev_dbg(&dev->core, "%s %u sectors starting at %lu\n", op, sectors,
+ start_sector);
+
+ offset = start_sector << 9;
+ len = sectors << 9;
+
+ if (write)
+ res = ps3vram_write(dev, offset, len, &retlen, req->buffer);
+ else
+ res = ps3vram_read(dev, offset, len, &retlen, req->buffer);
+
+ if (res) {
+ dev_err(&dev->core, "%s failed\n", op);
+ goto out;
+ }
+
+ if (retlen != len) {
+ dev_err(&dev->core, "Short %s\n", op);
+ goto out;
+ }
+
+ dev_dbg(&dev->core, "%s completed\n", op);
+
+ uptodate = 1;
+
+out:
+ spin_lock_irq(&priv->lock);
+ end_request(req, uptodate);
+ spin_unlock_irq(&priv->lock);
+}
+
+static int ps3vram_thread(void *data)
+{
+ struct ps3_system_bus_device *dev = data;
+ struct ps3vram_priv *priv = dev->core.driver_data;
+ struct request_queue *q = priv->queue;
+ struct request *req;
+
+ dev_dbg(&dev->core, "%s init\n", __func__);
+
+ current->flags |= PF_NOFREEZE;
+
+ while (!kthread_should_stop()) {
+ spin_lock_irq(&priv->lock);
+ set_current_state(TASK_INTERRUPTIBLE);
+ req = elv_next_request(q);
+ if (!req) {
+ spin_unlock_irq(&priv->lock);
+ schedule();
+ continue;
+ }
+ if (!blk_fs_request(req)) {
+ blk_dump_rq_flags(req, DEVICE_NAME " bad request");
+ end_request(req, 0);
+ spin_unlock_irq(&priv->lock);
+ continue;
+ }
+ spin_unlock_irq(&priv->lock);
+ ps3vram_do_request(dev, req);
+ }
+
+ dev_dbg(&dev->core, "%s exit\n", __func__);
+ return 0;
+}
+
+static void ps3vram_request(struct request_queue *q)
+{
+ struct ps3_system_bus_device *dev = q->queuedata;
+ struct ps3vram_priv *priv = dev->core.driver_data;
+
+ dev_dbg(&dev->core, "Waking up thread\n");
+ wake_up_process(priv->thread);
+}
+
+
+static int __devinit ps3vram_probe(struct ps3_system_bus_device *dev)
+{
+ struct ps3vram_priv *priv;
+ int error, status;
+ struct request_queue *queue;
+ struct gendisk *gendisk;
+ u64 ddr_lpar, ctrl_lpar, info_lpar, reports_lpar, ddr_size,
+ reports_size;
+ char *rest;
+ struct task_struct *task;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ error = -ENOMEM;
+ goto fail;
+ }
+
+ dev->core.driver_data = priv;
+ spin_lock_init(&priv->lock);
+
+ priv = dev->core.driver_data;
+
+ /* Allocate XDR buffer (1MiB aligned) */
+ priv->xdr_buf = (void *)__get_free_pages(GFP_KERNEL,
+ get_order(XDR_BUF_SIZE));
+ if (priv->xdr_buf == NULL) {
+ dev_dbg(&dev->core, "Could not allocate XDR buffer\n");
+ error = -ENOMEM;
+ goto fail_free_priv;
+ }
+
+ /* Put FIFO at begginning of XDR buffer */
+ priv->fifo_base = (u32 *) (priv->xdr_buf + FIFO_OFFSET);
+ priv->fifo_ptr = priv->fifo_base;
+
+ /* XXX: Need to open GPU, in case ps3fb or snd_ps3 aren't loaded */
+ if (ps3_open_hv_device(dev)) {
+ dev_err(&dev->core, "ps3_open_hv_device failed\n");
+ error = -EAGAIN;
+ goto out_close_gpu;
+ }
+
+ /* Request memory */
+ status = -1;
+ ddr_size = memparse(size, &rest);
+ if (*rest == '-')
+ ddr_size -= ps3fb_videomemory.size;
+ ddr_size = ALIGN(ddr_size, 1024*1024);
+ if (ddr_size <= 0) {
+ dev_err(&dev->core, "Specified size is too small\n");
+ error = -EINVAL;
+ goto out_close_gpu;
+ }
+
+ while (ddr_size > 0) {
+ status = lv1_gpu_memory_allocate(ddr_size, 0, 0, 0, 0,
+ &priv->memory_handle,
+ &ddr_lpar);
+ if (!status)
+ break;
+ ddr_size -= 1024*1024;
+ }
+ if (status || ddr_size <= 0) {
+ dev_err(&dev->core, "lv1_gpu_memory_allocate failed %d\n",
+ status);
+ error = -ENOMEM;
+ goto out_free_xdr_buf;
+ }
+
+ /* Request context */
+ status = lv1_gpu_context_allocate(priv->memory_handle, 0,
+ &priv->context_handle, &ctrl_lpar,
+ &info_lpar, &reports_lpar,
+ &reports_size);
+ if (status) {
+ dev_err(&dev->core, "lv1_gpu_context_allocate failed %d\n",
+ status);
+ error = -ENOMEM;
+ goto out_free_memory;
+ }
+
+ /* Map XDR buffer to RSX */
+ status = lv1_gpu_context_iomap(priv->context_handle, XDR_IOIF,
+ ps3_mm_phys_to_lpar(__pa(priv->xdr_buf)),
+ XDR_BUF_SIZE, 0);
+ if (status) {
+ dev_err(&dev->core, "lv1_gpu_context_iomap failed %d\n",
+ status);
+ error = -ENOMEM;
+ goto out_free_context;
+ }
+
+ priv->ddr_base = ioremap_flags(ddr_lpar, ddr_size, _PAGE_NO_CACHE);
+
+ if (!priv->ddr_base) {
+ dev_err(&dev->core, "ioremap DDR failed\n");
+ error = -ENOMEM;
+ goto out_free_context;
+ }
+
+ priv->ctrl = ioremap(ctrl_lpar, 64 * 1024);
+ if (!priv->ctrl) {
+ dev_err(&dev->core, "ioremap CTRL failed\n");
+ error = -ENOMEM;
+ goto out_unmap_vram;
+ }
+
+ priv->reports = ioremap(reports_lpar, reports_size);
+ if (!priv->reports) {
+ dev_err(&dev->core, "ioremap REPORTS failed\n");
+ error = -ENOMEM;
+ goto out_unmap_ctrl;
+ }
+
+ mutex_lock(&ps3_gpu_mutex);
+ ps3vram_init_ring(dev);
+ mutex_unlock(&ps3_gpu_mutex);
+
+ priv->size = ddr_size;
+
+ ps3vram_bind(dev);
+
+ mutex_lock(&ps3_gpu_mutex);
+ error = ps3vram_wait_ring(dev, 100);
+ mutex_unlock(&ps3_gpu_mutex);
+ if (error < 0) {
+ dev_err(&dev->core, "Failed to initialize channels\n");
+ error = -ETIMEDOUT;
+ goto out_unmap_reports;
+ }
+
+ ps3vram_cache_init(dev);
+ ps3vram_proc_init(dev);
+
+ queue = blk_init_queue(ps3vram_request, &priv->lock);
+ if (!queue) {
+ dev_err(&dev->core, "blk_init_queue failed\n");
+ error = -ENOMEM;
+ goto out_cache_cleanup;
+ }
+
+ priv->queue = queue;
+ queue->queuedata = dev;
+
+ gendisk = alloc_disk(1);
+ if (!gendisk) {
+ dev_err(&dev->core, "alloc_disk failed\n");
+ error = -ENOMEM;
+ goto fail_cleanup_queue;
+ }
+
+ priv->gendisk = gendisk;
+ gendisk->major = ps3vram_major;
+ gendisk->first_minor = 0;
+ gendisk->fops = &ps3vram_fops;
+ gendisk->queue = queue;
+ gendisk->private_data = dev;
+ gendisk->driverfs_dev = &dev->core;
+ strlcpy(gendisk->disk_name, DEVICE_NAME, sizeof(gendisk->disk_name));
+ set_capacity(gendisk, priv->size >> 9);
+
+ task = kthread_run(ps3vram_thread, dev, DEVICE_NAME);
+ if (IS_ERR(task)) {
+ error = PTR_ERR(task);
+ goto fail_free_disk;
+ }
+ priv->thread = task;
+
+ dev_info(&dev->core, "%s: Using %lu MiB of GPU memory\n",
+ gendisk->disk_name, get_capacity(gendisk) >> 11);
+
+ add_disk(gendisk);
+ return 0;
+
+fail_free_disk:
+ put_disk(priv->gendisk);
+fail_cleanup_queue:
+ blk_cleanup_queue(queue);
+out_cache_cleanup:
+ remove_proc_entry(DEVICE_NAME, NULL);
+ ps3vram_cache_cleanup(dev);
+out_unmap_reports:
+ iounmap(priv->reports);
+out_unmap_ctrl:
+ iounmap(priv->ctrl);
+out_unmap_vram:
+ iounmap(priv->ddr_base);
+out_free_context:
+ lv1_gpu_context_free(priv->context_handle);
+out_free_memory:
+ lv1_gpu_memory_free(priv->memory_handle);
+out_close_gpu:
+ ps3_close_hv_device(dev);
+out_free_xdr_buf:
+ free_pages((unsigned long) priv->xdr_buf, get_order(XDR_BUF_SIZE));
+fail_free_priv:
+ kfree(priv);
+ dev->core.driver_data = NULL;
+fail:
+ return error;
+}
+
+static int ps3vram_remove(struct ps3_system_bus_device *dev)
+{
+ struct ps3vram_priv *priv = dev->core.driver_data;
+
+ BUG_ON(!elv_queue_empty(priv->queue));
+
+ kthread_stop(priv->thread);
+ del_gendisk(priv->gendisk);
+ blk_cleanup_queue(priv->queue);
+ put_disk(priv->gendisk);
+ remove_proc_entry(DEVICE_NAME, NULL);
+ ps3vram_cache_cleanup(dev);
+ iounmap(priv->reports);
+ iounmap(priv->ctrl);
+ iounmap(priv->ddr_base);
+ lv1_gpu_context_free(priv->context_handle);
+ lv1_gpu_memory_free(priv->memory_handle);
+ ps3_close_hv_device(dev);
+ free_pages((unsigned long) priv->xdr_buf, get_order(XDR_BUF_SIZE));
+ kfree(priv);
+ dev->core.driver_data = NULL;
+ return 0;
+}
+
+static struct ps3_system_bus_driver ps3vram = {
+ .match_id = PS3_MATCH_ID_GPU,
+ .match_sub_id = PS3_MATCH_SUB_ID_GPU_RAMDISK,
+ .core.name = DEVICE_NAME,
+ .core.owner = THIS_MODULE,
+ .probe = ps3vram_probe,
+ .remove = ps3vram_remove,
+ .shutdown = ps3vram_remove,
+};
+
+
+static int __init ps3vram_init(void)
+{
+ int error;
+
+ if (!firmware_has_feature(FW_FEATURE_PS3_LV1))
+ return -ENODEV;
+
+ error = register_blkdev(0, DEVICE_NAME);
+ if (error <= 0) {
+ pr_err("%s: register_blkdev failed %d\n", DEVICE_NAME, error);
+ return error;
+ }
+ ps3vram_major = error;
+
+ pr_info("%s: registered block device major %d\n", DEVICE_NAME,
+ ps3vram_major);
+
+ error = ps3_system_bus_driver_register(&ps3vram);
+ if (error)
+ unregister_blkdev(ps3vram_major, DEVICE_NAME);
+
+ return error;
+}
+
+static void __exit ps3vram_exit(void)
+{
+ ps3_system_bus_driver_unregister(&ps3vram);
+ unregister_blkdev(ps3vram_major, DEVICE_NAME);
+}
+
+module_init(ps3vram_init);
+module_exit(ps3vram_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("PS3 Video RAM Storage Driver");
+MODULE_AUTHOR("Sony Corporation");
+MODULE_ALIAS(PS3_MODULE_ALIAS_GPU_RAMDISK);
--
1.6.0.4
With kind regards,
Geert Uytterhoeven
Software Architect
Sony Techsoft Centre Europe
The Corporate Village · Da Vincilaan 7-D1 · B-1935 Zaventem · Belgium
Phone: +32 (0)2 700 8453
Fax: +32 (0)2 700 8622
E-mail: Geert.Uytterhoeven@sonycom.com
Internet: http://www.sony-europe.com/
A division of Sony Europe (Belgium) N.V.
VAT BE 0413.825.160 · RPR Brussels
Fortis · BIC GEBABEBB · IBAN BE41293037680010
^ permalink raw reply related
* Re: [PATCH] ppc: detect sbc610 boards and only fixup nec usb on them
From: Kyle McMartin @ 2009-03-04 14:30 UTC (permalink / raw)
To: Benjamin Herrenschmidt; +Cc: linuxppc-dev, Kyle McMartin, linux-kernel
In-Reply-To: <1236148940.6696.39.camel@pasglop>
On Wed, Mar 04, 2009 at 05:42:20PM +1100, Benjamin Herrenschmidt wrote:
> Thanks, I applied Tony's patch and sent a pull request to Linus.
>
Works for me. Thanks for being so quick, Ben!
cheers, Kyle
^ permalink raw reply
* Re: PHY not found after migration of gianfar driver to an of_platform_driver
From: Grant Likely @ 2009-03-04 15:57 UTC (permalink / raw)
To: Michael Guntsche, linuxppc-dev
In-Reply-To: <68DDAAA9-42AF-4E9C-907C-7D3269816184@it-loops.com>
I'm posting this back on the mailing list. You're not being dense and
there are good questions here which others might elaborate more on.
On Tue, Mar 3, 2009 at 2:56 PM, Michael Guntsche <mike@it-loops.com> wrote:
> The routeboard is already providing a device-tree albeit not a very good
> one, otherwise the board would not boot in the first please if I just use=
a
> plain kernel
I need more information here. What do you mean when you say "plain
kernel". What file from the build process do you use, and how do you
boot it?
> without an embedded tree. I need to fix this device tree since the new
> gianfar network code is expecting
>
> tbi-handle =3D <&tbi1>;
>
> Entries in the ethernet nodes to find the PHY devices. I do not know if y=
ou
> looked at the source of rbppc.c in detail. There is already code in there=
to
> init the PCI bus
> since the tree is missing data for this.
I don't see a file named rbppc.c in the current kernel tree.
> * Why can't I just add those missing values in the setup-arch section of =
the
> code?
You might be able to use of_attach_node() & prom_add_property() to
modify the tree, but I've never done it myself. Give it a try and
tell me if it works. :-)
> =A0They are needed to init the NICs correctly so I can fix this here befo=
re
> the driver is loaded
Another option is to add a workaround to the driver. This isn't
ideal, but driver authors aren't supposed to break device tree
bindings either. Drivers are supposed to remain backwards compatible
with older device trees (either by patching the device tree or with
explicit workarounds). Downside is that this type of change may be
harder to get merged. And it's just plain not very pretty.
> * How could I add those information?
> =A0Can't I just do something similar to platforms/iseries/vio.c
> =A0(add_string_property and do_device_node)?
Maybe. I don't know why those functions are tucked away in vio.c
instead of being common code. If you go that approach, refactor the
functions you use to be shared.
> This wold have the benefit of getting the rest of the data from the board
> and just patch it with the needed values.
This is definitely preferred to wholesale replacement of the available tree=
.
g.
--=20
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
^ permalink raw reply
* Re: PHY not found after migration of gianfar driver to an of_platform_driver
From: Michael Guntsche @ 2009-03-04 16:47 UTC (permalink / raw)
To: Grant Likely; +Cc: linuxppc-dev
In-Reply-To: <fa686aa40903040757n3992fec4jb6f3b75396af2958@mail.gmail.com>
On Mar 4, 2009, at 16:57, Grant Likely wrote:
> I need more information here. What do you mean when you say "plain
> kernel". What file from the build process do you use, and how do you
> boot it?
>
I build the kernel with
make ARCH="powerpc" CROSS_COMPILE="powerpc-linux-gnu-" vmlinux
I take the kernel file and add a kernparm segment to it, where I
specify my root directory.
Then I dd the file to the first partition of my CF card on the board.
As I said before, the bootloader looks at the first partition of the
CF-card with a partition type of 0x27.
It expects a standard elf kernel to be in there and boots that.
That's all there is to it.
>
> I don't see a file named rbppc.c in the current kernel tree.
>
Sorry, this patch is not in mainline yet. You can find it at
http://cynigram.com/~nfontes/rb600/
It adds the pata driver, and the board specific stuff to the stock
kernel.
The patch applies cleanly to 2.6.28.x but fails due to the gianfar
related changes in the 2.6.29 release cycle.
>> You might be able to use of_attach_node() & prom_add_property() to
> modify the tree, but I've never done it myself. Give it a try and
> tell me if it works. :-)
I am trying to do it this way right now, when I was looking at them I
was just wondering if those two functions were the correct ones to use.
As for backwards compatibility. All in-tree drivers were fixed as well
to work with the new code so you cannot call this change breakage IMHO.
Off-tree code that is affected by it (like the one I am struggling
with right now) just has to follow those changes. I do not think that
the changes broke any
defined interface standards.
It would be a lot easier if the board would use uboot or cuboot in the
first place, but there is no way I can change that, other thanflashing
the boot-loader
itself NS I am neither brave nor knowledgeable enough to try that
anyway.
Kind regards,
Michael
^ permalink raw reply
* Re: U-Boot image
From: Peter Korsgaard @ 2009-03-04 16:52 UTC (permalink / raw)
To: Günter Leonhardt; +Cc: linuxppc-dev
In-Reply-To: <D0B748CCD5DF144CA5C56A9D860A31341DE5C9@men-exch1.intra.men.de>
>>>>> "G=C3=BCnter" =3D=3D G=C3=BCnter Leonhardt <Guenter.Leonhardt@men.de>=
writes:
G=C3=BCnter> Hello,
G=C3=BCnter> I'am looking for an image to boot a embedded device with a
G=C3=BCnter> single image.
G=C3=BCnter> I though the generated uImage in arch/powerpc/boot should
G=C3=BCnter> work, but u-boot cannot but this, because the addresses of
G=C3=BCnter> initrd and dtb not found.
G=C3=BCnter> Now I'am creating the image by hand with mkimage:
G=C3=BCnter> mkimage -A ppc -n F302 -O linux \
G=C3=BCnter> -T multi -C gzip -a 0x0 -e 0x0 \
G=C3=BCnter> -d vmlinux.bin.gz:initramfs_data.cpio.gz:f302.dtb=
\
G=C3=BCnter> uImage
I posted a patch to add support for multi image uImages to the kernel
makefiles, E.G. uImage.<platform> some time ago. It got nack'ed by
wdenx though as he wants people to use the new fitimage stuff in
u-boot instead. Anyway, it might be useful to you:
http://peter.korsgaard.com/patches/linux/bootwrapper-uboot-multi-2.patch
To use it, add:
image-$(CONFIG_<your-platform>) + uImage.<your-platform>
to arch/powerpc/boot/Makefile.
G=C3=BCnter> This file is bootable, but I don't understand how a standard
G=C3=BCnter> file has to be booted.
With an external dtb / initrd.
G=C3=BCnter> Can someone explain me the different fileforamts for booting
G=C3=BCnter> a powerpc with u-boot? Which is the simplest to use?
Have a look at Documentation/powerpc/bootwrapper.txt
--=20
Bye, Peter Korsgaard
^ permalink raw reply
* Re: [PATCH 02/11] sdhci: Add support for bus-specific IO memory accessors
From: Anton Vorontsov @ 2009-03-04 17:46 UTC (permalink / raw)
To: Pierre Ossman
Cc: Ben Dooks, Arnd Bergmann, Liu Dave, linux-kernel, linuxppc-dev,
sdhci-devel
In-Reply-To: <20090221165757.22747648@mjolnir.ossman.eu>
On Sat, Feb 21, 2009 at 04:57:57PM +0100, Pierre Ossman wrote:
> On Fri, 13 Feb 2009 17:40:39 +0300
> Anton Vorontsov <avorontsov@ru.mvista.com> wrote:
>
> >
> > No, on eSDHC the registers are big-endian, 32-bit width, with, for
> > example, two 16-bit "logical" registers packed into it.
> >
> > That is,
> >
> > 0x4 0x5 0x6 0x7
> > |~~~~~~~~:~~~~~~~~|
> > | BLKCNT : BLKSZ |
> > |________:________|
> > 31 0
> >
> > ( The register looks wrong, right? BLKSZ should be at 0x4. But imagine
> > that you swapped bytes in this 32 bit register... then the registers
> > and their byte addresses will look normal. )
> >
> > So if we try to issue readw(SDHCI_BLOCK_SIZE), i.e. readw(0x4):
> >
> > - We'll read BLKCNT, while we wanted BLKSZ. This is because the
> > address bits should be translated before we try word or byte
> > reads/writes.
> > - On powerpc read{l,w}() convert the read value from little-endian
> > to big-endian byte order, which is wrong for our case (the
> > register is big-endian already).
> >
> > That means that we have to convert address, but we don't want to
> > convert the result of read/write ops.
> >
>
> *cries*
:-)
[...]
> > > as far as I'm
> > > concerned, so I'd prefer something like:
> > >
> > > if (!host->ops->writel)
> > > writel(host->ioaddr + reg, val);
> > > else
> > > host->ops->writel(host, val, reg);
> >
> > Surely the overhead isn't measurable... but why we purposely make
> > things worse?
> >
>
> We can most likely do some micro-optimisation do make the compare part
> cheaper, but the point was to avoid a function call for all the
> properly implemented controllers out there. We could have a flag so
> that it only has to check host->flags, which will most likely be in the
> cache anyway.
>
> Overhead for eSDHC is not a concern in my book, what is interesting is
> how much this change slows things down for other controllers.
OK, I see. Will the patch down below make you a little bit more happy
wrt normal controllers? Two #ifdefs, but then there is absolutely
zero overhead for the fully compliant SDHCI controllers.
(So far it's just on top of this series, but I can incorporate it
into the "sdhci: Add support for bus-specific IO memory accessors"
patch, if you like).
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 73b79e1..69bd124 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -37,6 +37,13 @@ config MMC_SDHCI
If unsure, say N.
+config MMC_SDHCI_IO_ACCESSORS
+ bool
+ depends on MMC_SDHCI
+ help
+ This is silent Kconfig symbol that is selected by the drivers that
+ need to overwrite SDHCI IO memory accessors.
+
config MMC_SDHCI_PCI
tristate "SDHCI support on PCI bus"
depends on MMC_SDHCI && PCI
@@ -68,6 +75,7 @@ config MMC_RICOH_MMC
config MMC_SDHCI_OF
tristate "SDHCI support on OpenFirmware platforms"
depends on MMC_SDHCI && PPC_OF
+ select MMC_SDHCI_IO_ACCESSORS
help
This selects the OF support for Secure Digital Host Controller
Interfaces. So far, only the Freescale eSDHC controller is known
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 284bc5d..fb08d3b 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -88,60 +88,6 @@ static void sdhci_dumpregs(struct sdhci_host *host)
* *
\*****************************************************************************/
-void sdhci_writel(struct sdhci_host *host, u32 val, int reg)
-{
- if (unlikely(host->ops->writel))
- host->ops->writel(host, val, reg);
- else
- writel(val, host->ioaddr + reg);
-}
-EXPORT_SYMBOL_GPL(sdhci_writel);
-
-void sdhci_writew(struct sdhci_host *host, u16 val, int reg)
-{
- if (unlikely(host->ops->writew))
- host->ops->writew(host, val, reg);
- else
- writew(val, host->ioaddr + reg);
-}
-EXPORT_SYMBOL_GPL(sdhci_writew);
-
-void sdhci_writeb(struct sdhci_host *host, u8 val, int reg)
-{
- if (unlikely(host->ops->writeb))
- host->ops->writeb(host, val, reg);
- else
- writeb(val, host->ioaddr + reg);
-}
-EXPORT_SYMBOL_GPL(sdhci_writeb);
-
-u32 sdhci_readl(struct sdhci_host *host, int reg)
-{
- if (unlikely(host->ops->readl))
- return host->ops->readl(host, reg);
- else
- return readl(host->ioaddr + reg);
-}
-EXPORT_SYMBOL_GPL(sdhci_readl);
-
-u16 sdhci_readw(struct sdhci_host *host, int reg)
-{
- if (unlikely(host->ops->readw))
- return host->ops->readw(host, reg);
- else
- return readw(host->ioaddr + reg);
-}
-EXPORT_SYMBOL_GPL(sdhci_readw);
-
-u8 sdhci_readb(struct sdhci_host *host, int reg)
-{
- if (unlikely(host->ops->readb))
- return host->ops->readb(host, reg);
- else
- return readb(host->ioaddr + reg);
-}
-EXPORT_SYMBOL_GPL(sdhci_readb);
-
static void sdhci_clear_set_irqs(struct sdhci_host *host, u32 clear, u32 set)
{
u32 ier;
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 1697e01..b6675df 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -281,12 +281,14 @@ struct sdhci_host {
struct sdhci_ops {
+#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
u32 (*readl)(struct sdhci_host *host, int reg);
u16 (*readw)(struct sdhci_host *host, int reg);
u8 (*readb)(struct sdhci_host *host, int reg);
void (*writel)(struct sdhci_host *host, u32 val, int reg);
void (*writew)(struct sdhci_host *host, u16 val, int reg);
void (*writeb)(struct sdhci_host *host, u8 val, int reg);
+#endif
void (*set_clock)(struct sdhci_host *host, unsigned int clock);
@@ -295,12 +297,89 @@ struct sdhci_ops {
unsigned int (*get_timeout_clock)(struct sdhci_host *host);
};
-extern void sdhci_writel(struct sdhci_host *host, u32 val, int reg);
-extern void sdhci_writew(struct sdhci_host *host, u16 val, int reg);
-extern void sdhci_writeb(struct sdhci_host *host, u8 val, int reg);
-extern u32 sdhci_readl(struct sdhci_host *host, int reg);
-extern u16 sdhci_readw(struct sdhci_host *host, int reg);
-extern u8 sdhci_readb(struct sdhci_host *host, int reg);
+#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
+
+static inline void sdhci_writel(struct sdhci_host *host, u32 val, int reg)
+{
+ if (unlikely(host->ops->writel))
+ host->ops->writel(host, val, reg);
+ else
+ writel(val, host->ioaddr + reg);
+}
+
+static inline void sdhci_writew(struct sdhci_host *host, u16 val, int reg)
+{
+ if (unlikely(host->ops->writew))
+ host->ops->writew(host, val, reg);
+ else
+ writew(val, host->ioaddr + reg);
+}
+
+static inline void sdhci_writeb(struct sdhci_host *host, u8 val, int reg)
+{
+ if (unlikely(host->ops->writeb))
+ host->ops->writeb(host, val, reg);
+ else
+ writeb(val, host->ioaddr + reg);
+}
+
+static inline u32 sdhci_readl(struct sdhci_host *host, int reg)
+{
+ if (unlikely(host->ops->readl))
+ return host->ops->readl(host, reg);
+ else
+ return readl(host->ioaddr + reg);
+}
+
+static inline u16 sdhci_readw(struct sdhci_host *host, int reg)
+{
+ if (unlikely(host->ops->readw))
+ return host->ops->readw(host, reg);
+ else
+ return readw(host->ioaddr + reg);
+}
+
+static inline u8 sdhci_readb(struct sdhci_host *host, int reg)
+{
+ if (unlikely(host->ops->readb))
+ return host->ops->readb(host, reg);
+ else
+ return readb(host->ioaddr + reg);
+}
+
+#else
+
+static inline void sdhci_writel(struct sdhci_host *host, u32 val, int reg)
+{
+ writel(val, host->ioaddr + reg);
+}
+
+static inline void sdhci_writew(struct sdhci_host *host, u16 val, int reg)
+{
+ writew(val, host->ioaddr + reg);
+}
+
+static inline void sdhci_writeb(struct sdhci_host *host, u8 val, int reg)
+{
+ writeb(val, host->ioaddr + reg);
+}
+
+static inline u32 sdhci_readl(struct sdhci_host *host, int reg)
+{
+ return readl(host->ioaddr + reg);
+}
+
+static inline u16 sdhci_readw(struct sdhci_host *host, int reg)
+{
+ return readw(host->ioaddr + reg);
+}
+
+static inline u8 sdhci_readb(struct sdhci_host *host, int reg)
+{
+ return readb(host->ioaddr + reg);
+}
+
+#endif /* CONFIG_MMC_SDHCI_IO_ACCESSORS */
extern struct sdhci_host *sdhci_alloc_host(struct device *dev,
size_t priv_size);
^ permalink raw reply related
* Re: [PATCH 12/13] sdhci: Add quirk for controllers with max. block size up to 4096 bytes
From: Anton Vorontsov @ 2009-03-04 17:47 UTC (permalink / raw)
To: Pierre Ossman
Cc: Ben Dooks, Arnd Bergmann, Liu Dave, linux-kernel, linuxppc-dev,
sdhci-devel, Pierre Ossman
In-Reply-To: <20090221165844.4f494043@mjolnir.ossman.eu>
On Sat, Feb 21, 2009 at 04:58:44PM +0100, Pierre Ossman wrote:
> On Fri, 13 Feb 2009 17:47:39 +0300
> Anton Vorontsov <avorontsov@ru.mvista.com> wrote:
>
> > @@ -831,7 +832,12 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data)
> > sdhci_set_transfer_irqs(host);
> >
> > /* We do not handle DMA boundaries, so set it to max (512 KiB) */
> > - sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, data->blksz), SDHCI_BLOCK_SIZE);
> > + if (host->quirks & SDHCI_QUIRK_MAX_BLK_SZ_4096)
> > + blksz = data->blksz;
> > + else
> > + blksz = SDHCI_MAKE_BLKSZ(7, data->blksz);
> > +
> > + sdhci_writew(host, blksz, SDHCI_BLOCK_SIZE);
> > sdhci_writew(host, data->blocks, SDHCI_BLOCK_COUNT);
> > }
> >
>
> Hmm.. I seem to have overlooked this part previously. I guess they've
> basically stripped out the DMA boundary stuff and used the bits for
> other things?
Yes, the last two "DMA boundary" bits are reserved, and the first
one is re-used for blksz of 4096 bytes.
> At this point I'm leaning more towards simply not supporting their
> extended block size.
Eh. But well, OK. We can always persuade you later. :-)
I'll get rid of this particular patch, and put some BLOCK_SIZE
magic into the writew accessor (to clean the DMA bits) instead.
Though, I'll prepare another patch to force blksz to 2048, since
eSDHC specifies "3" in the blksz capability bitfield, and that
causes SDHCI core to fall back to the 512 byte blocks.
> After all, is it ever used?
Not sure, maybe `dd bs=' can use it? A bit lazy to check this
right now, but from the quick tests, enabling/disabling "blksz
of 4096 bytes" doesn't cause any performance change. At least
with the ordinary SD cards.
--
Anton Vorontsov
email: cbouatmailru@gmail.com
irc://irc.freenode.net/bd2
^ permalink raw reply
* Re: [PATCH 07/13] sdhci: Add support for hosts with strict 32 bit addressing
From: Anton Vorontsov @ 2009-03-04 17:48 UTC (permalink / raw)
To: Pierre Ossman
Cc: Ben Dooks, Arnd Bergmann, Liu Dave, linux-kernel, linuxppc-dev,
sdhci-devel, Pierre Ossman
In-Reply-To: <20090221165833.6dec220a@mjolnir.ossman.eu>
On Sat, Feb 21, 2009 at 04:58:33PM +0100, Pierre Ossman wrote:
> On Fri, 13 Feb 2009 17:47:22 +0300
> Anton Vorontsov <avorontsov@ru.mvista.com> wrote:
>
> > SDHCI driver must take special care when working with "triggering"
> > registers on hosts with strict 32 bit addressing.
> >
> > In FSL eSDHC hosts all registers are 32 bit width, writing to the
> > first half of any register will cause [undefined?] write the second
> > half of the register. That is, 16 bit write to the TRANSFER_MODE
> > register, makes hardware see a bogus write to the COMMAND register
> > (these two registers are adjacent).
> >
> > This patch adds SDHCI_QUIRK_32BIT_REGISTERS quirk. When specified,
> > the sdhci driver will try to "pack" all dangerous writes into single
> > 32 bit write transaction.
> >
> > Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
> > ---
>
> What about the other places where we have 16 and 8 bit registers?
These are handled by the accessors:
+static void esdhc_writeb(struct sdhci_host *host, u8 val, int reg)
+{
+ int base = reg & ~0x3;
+ int shift = (reg & 0x3) * 8;
+
+ clrsetbits_be32(host->ioaddr + base , 0xff << shift, val << shift);
^^ See, we're always issuing 32bit ops.
The point of this patch is to take care of "triggering" registers,
i.e. those registers that are used trigger "send some data" command.
There is just one (from the eSDHC point of view) such register:
TRANSFER_MODE+COMMAND (which is a single 32 bit register in eSDHC,
but two independant word-sized registers in standard SDHCI).
That register must be accessed with 32bit ops just _once_ per
data transfer, not two 32bit writes (half of a register one time,
and half of a register next time -- this won't work).
But I see the point of confusion... Instead of teaching
"SDHCI core" to work with 32 bits hosts, we'd better handle this
in the eSDHC part, in the accessors.
This is relatively trivial and should not cause much overhead
(at least when using DMA), just a small state machine with
the xfer mode register shadowed in software (plus, notice that
this also handles BLOCK_SIZE, as I promised in another email):
diff --git a/drivers/mmc/host/sdhci-of.c b/drivers/mmc/host/sdhci-of.c
index 22bf006..5100d1d 100644
--- a/drivers/mmc/host/sdhci-of.c
+++ b/drivers/mmc/host/sdhci-of.c
@@ -30,6 +30,7 @@ struct sdhci_of_data {
struct sdhci_of_host {
unsigned int clock;
+ u16 xfer_mode_shadow;
};
/*
@@ -69,9 +70,31 @@ static void esdhc_writel(struct sdhci_host *host, u32 val, int reg)
static void esdhc_writew(struct sdhci_host *host, u16 val, int reg)
{
+ struct sdhci_of_host *of_host = sdhci_priv(host);
int base = reg & ~0x3;
int shift = (reg & 0x2) * 8;
+ switch (reg) {
+ case SDHCI_TRANSFER_MODE:
+ /*
+ * Postpone this write, we must do it together with a
+ * command write that is down below.
+ */
+ of_host->xfer_mode_shadow = val;
+ return;
+ case SDHCI_COMMAND:
+ esdhc_writel(host, val << 16 | of_host->xfer_mode_shadow,
+ SDHCI_TRANSFER_MODE);
+ return;
+ case SDHCI_BLOCK_SIZE:
+ /*
+ * Two last DMA bits are reserved, and first one is used for
+ * non-standard blksz of 4096 bytes that we don't support
+ * yet. So clear the DMA boundary bits.
+ */
+ val &= ~SDHCI_MAKE_BLKSZ(0x7, 0);
+ /* fall through */
+ }
clrsetbits_be32(host->ioaddr + base, 0xffff << shift, val << shift);
}
@@ -137,13 +160,11 @@ static unsigned int esdhc_get_timeout_clock(struct sdhci_host *host)
}
static struct sdhci_of_data sdhci_esdhc = {
- .quirks = SDHCI_QUIRK_32BIT_REGISTERS |
- SDHCI_QUIRK_BROKEN_CARD_DETECTION |
+ .quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION |
SDHCI_QUIRK_INVERTED_WRITE_PROTECT |
SDHCI_QUIRK_NO_BUSY_IRQ |
SDHCI_QUIRK_NONSTANDARD_CLOCK |
SDHCI_QUIRK_PIO_NEEDS_DELAY |
- SDHCI_QUIRK_MAX_BLK_SZ_4096 |
SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET |
SDHCI_QUIRK_NO_CARD_NO_RESET,
.ops = {
^ permalink raw reply related
* Re: [PATCH 05/13] sdhci: Add support for card-detection polling
From: Anton Vorontsov @ 2009-03-04 17:49 UTC (permalink / raw)
To: Pierre Ossman
Cc: Ben Dooks, Arnd Bergmann, Liu Dave, linux-kernel, linuxppc-dev,
sdhci-devel, Pierre Ossman
In-Reply-To: <20090221165821.1239aa5a@mjolnir.ossman.eu>
On Sat, Feb 21, 2009 at 04:58:21PM +0100, Pierre Ossman wrote:
> On Fri, 13 Feb 2009 17:47:18 +0300
> Anton Vorontsov <avorontsov@ru.mvista.com> wrote:
>
> > @@ -1110,13 +1113,18 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
> >
> > host->mrq = mrq;
> >
> > + if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)
> > + goto send;
> > +
> > if (!(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)
> > || (host->flags & SDHCI_DEVICE_DEAD)) {
> > host->mrq->cmd->error = -ENOMEDIUM;
> > tasklet_schedule(&host->finish_tasklet);
> > - } else
> > - sdhci_send_command(host, mrq->cmd);
> > -
> > + goto out;
> > + }
> > +send:
> > + sdhci_send_command(host, mrq->cmd);
> > +out:
> > mmiowb();
> > spin_unlock_irqrestore(&host->lock, flags);
> > }
>
> goto:s seem unnecessary here, and your patch is even incorrect as it
> ignores the SDHCI_DEVICE_DEAD flag.
Oops.
> Just modify the if-clause and
> things will work.
That would look horrid...
if ((!(host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) &&
!(sdhci_readl(host, SDHCI_PRESENT_STATE) &
SDHCI_CARD_PRESENT)) ||
(host->flags & SDHCI_DEVICE_DEAD)) {
> Might want to add a comment also to make it more obvious what the
> if-clause does.
Let's try to avoid the if-clause above? How about this:
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 0cbde8e..d71c877 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -167,6 +167,9 @@ static void sdhci_set_card_detection(struct sdhci_host *host, bool enable)
{
u32 irqs = SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT;
+ if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)
+ return;
+
if (enable)
sdhci_unmask_irqs(host, irqs);
else
@@ -1096,6 +1099,7 @@ out:
static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
{
struct sdhci_host *host;
+ bool present;
unsigned long flags;
host = mmc_priv(mmc);
@@ -1110,8 +1114,14 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
host->mrq = mrq;
- if (!(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)
- || (host->flags & SDHCI_DEVICE_DEAD)) {
+ /* If polling, assume that the card is always present. */
+ if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)
+ present = true;
+ else
+ present = sdhci_readl(host, SDHCI_PRESENT_STATE) &
+ SDHCI_CARD_PRESENT;
+
+ if (!present || host->flags & SDHCI_DEVICE_DEAD) {
host->mrq->cmd->error = -ENOMEDIUM;
tasklet_schedule(&host->finish_tasklet);
} else
@@ -1745,6 +1755,9 @@ int sdhci_add_host(struct sdhci_host *host)
if (caps & SDHCI_CAN_DO_HISPD)
mmc->caps |= MMC_CAP_SD_HIGHSPEED;
+ if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)
+ mmc->caps |= MMC_CAP_NEEDS_POLL;
+
mmc->ocr_avail = 0;
if (caps & SDHCI_CAN_VDD_330)
mmc->ocr_avail |= MMC_VDD_32_33|MMC_VDD_33_34;
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 1c29895..09a4363 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -212,6 +212,8 @@ struct sdhci_host {
#define SDHCI_QUIRK_BROKEN_SMALL_PIO (1<<13)
/* Controller does not provide transfer-complete interrupt when not busy */
#define SDHCI_QUIRK_NO_BUSY_IRQ (1<<14)
+/* Controller has unreliable card detection */
+#define SDHCI_QUIRK_BROKEN_CARD_DETECTION (1<<15)
int irq; /* Device IRQ */
void __iomem * ioaddr; /* Mapped address */
^ permalink raw reply related
* GDB problem with Xilinx GIT Linux on Virtex5FX PPC440
From: Frederic LEGER @ 2009-03-04 17:36 UTC (permalink / raw)
To: linuxppc-dev
Hi,
At the moment I can't set any breakpoint under gdb or gdbserver on ML510 and
AVNETV5FX30T, if I set one => die "continuing...".
Everything else is OK for a while on both boards: cross-compiling, dhcp,
netperf, custom apps, .... but not debugging !
If you have a working gdb (and/or gdbserver) on PPC440 Linux , could you
please anwer this message with the following piece of information:
Board
kernel revision
kernel snapshot (day/number) used from Xilinx GIT
defconfig used
toolchain (ELDK/buildroot...)
toolchain version
location of the rootfs (ramdisk, SysACE partition, flash Uboot partition)
Type of the rootfs
Thank you very much in advance.
Best regards,
Frederic
^ permalink raw reply
* [PATCH] powerpc: add fsl, fifo-depth property to Freescale SSI device nodes
From: Timur Tabi @ 2009-03-04 20:55 UTC (permalink / raw)
To: linuxppc-dev, benh
The Freescale Serial Synchronous Interface (SSI) is an audio device present on
some Freescale SOCs. Various implementations of the SSI have a different
transmit and receive FIFO depth, but are otherwise identical. To support
these variations, add a new property fsl,fifo-depth to the SSI node that
specifies the depth of the FIFOs.
Also update the MPC8610 HPCD device tree with this property.
Signed-off-by: Timur Tabi <timur@freescale.com>
---
Updates to the SSI audio driver will come later. Currently, this driver
supports only one Freescale SOC, and so it's hard-coded to use the value 8.
If/when this driver is updated to support other SOCs (e.g. the i.MX parts
that have a FIFO depth of 15), the driver will check for this property. I
just want to get this DTS change in now.
Documentation/powerpc/dts-bindings/fsl/ssi.txt | 2 ++
arch/powerpc/boot/dts/mpc8610_hpcd.dts | 2 ++
2 files changed, 4 insertions(+), 0 deletions(-)
diff --git a/Documentation/powerpc/dts-bindings/fsl/ssi.txt b/Documentation/powerpc/dts-bindings/fsl/ssi.txt
index a2d9639..7313322 100644
--- a/Documentation/powerpc/dts-bindings/fsl/ssi.txt
+++ b/Documentation/powerpc/dts-bindings/fsl/ssi.txt
@@ -30,6 +30,8 @@ Required properties:
- fsl,capture-dma: phandle to a node for the DMA channel to use for
capture (recording) of audio. This is typically dictated
by SOC design. See the notes below.
+- fsl,fifo-depth: the number of elements in the transmit and receive FIFOs.
+ This number is the maximum allowed value for SFCSR[TFWM0].
Optional properties:
- codec-handle : phandle to a 'codec' node that defines an audio
diff --git a/arch/powerpc/boot/dts/mpc8610_hpcd.dts b/arch/powerpc/boot/dts/mpc8610_hpcd.dts
index f724d72..1bd3ebe 100644
--- a/arch/powerpc/boot/dts/mpc8610_hpcd.dts
+++ b/arch/powerpc/boot/dts/mpc8610_hpcd.dts
@@ -217,6 +217,7 @@
codec-handle = <&cs4270>;
fsl,playback-dma = <&dma00>;
fsl,capture-dma = <&dma01>;
+ fsl,fifo-depth = <8>;
};
ssi@16100 {
@@ -225,6 +226,7 @@
reg = <0x16100 0x100>;
interrupt-parent = <&mpic>;
interrupts = <63 2>;
+ fsl,fifo-depth = <8>;
};
dma@21300 {
--
1.6.1.3
^ permalink raw reply related
* Re: How to bring up fs_enet on 2.6.27?
From: Scott Wood @ 2009-03-04 21:00 UTC (permalink / raw)
To: Daniel Ng; +Cc: linuxppc-dev, Mike Ditto
In-Reply-To: <547eba1b0902242309o2086dce2p49c59a9ea1b85b53@mail.gmail.com>
On Wed, Feb 25, 2009 at 06:09:32PM +1100, Daniel Ng wrote:
> On Fri, Feb 20, 2009 at 4:01 PM, Daniel Ng <daniel.ng1234@gmail.com> wrote:
> >
> > Now, I'm seeing these boot messages:
> >
> > f0010d40:00 not found
> > eth0: Could not attach to PHY
> > IP-Config: Failed to open eth0
> > IP-Config: Device `eth0' not found.
> >
> > Previous mailing list discussions suggest that I use the correct PHY,
> > which I am sure about because my 8272-based board only has the one PHY
> > ie. PHY0 with reg = <0x0>.
> >
> > Note the relevant parts of my Device Tree below. Currently, our PHY
> > attributes eg. 'auto-negotiate' are not changeable, so we aren't
> > actually using MDC+MDIO even the MDC+MDIO lines exist.
Your device tree is telling the kernel that you *are* using those lines. If
they're not connected the way the device tree describes them as being
connected, you'll have to replace it with something that accurately
describes your hardware.
> > Also, the PHY interrupt line is not wired up. Hence the PHY0 interrupts
> > field is <0 8> (or should it be removed altogether?).
If your PHY interrupt is not connected, then you must remove the
"interrupts" property altogether. "0" is a potentially valid interrupt
number.
-Scott
^ permalink raw reply
* Re: mpc8349e-mitx 2.6.25 serial IRQ assigned wrong
From: Timur Tabi @ 2009-03-04 22:21 UTC (permalink / raw)
To: onramp123; +Cc: linuxppc-dev
In-Reply-To: <025a01c99c8b$90afb030$6b03a8c0@sdelaney2>
On Tue, Mar 3, 2009 at 11:39 PM, Steve DeLaney <onramp123@yahoo.com> wrote:
> For the mpc8349e-mitx this must be incorrect since there is no ISA?
That doesn't matter. We want the code to be as common as possible.
The actual virtual IRQ value is irrelevant. The OF code gives you a
virq number, and you just use that. Any code or board design that
depends on any relationship between the virq and the hwirq is broken.
> Essentially all that is needed is a 1:1 mapping hirq:virq since
Needed for who?
> For now we simply define irq.h NUM_ISA_INTERRUPTS 0
This is a bad idea. You will just break something else.
--
Timur Tabi
Linux kernel developer at Freescale
^ permalink raw reply
* Re: mpc8349e-mitx 2.6.25 serial IRQ assigned wrong
From: Grant Likely @ 2009-03-04 23:08 UTC (permalink / raw)
To: onramp123; +Cc: linuxppc-dev
In-Reply-To: <025a01c99c8b$90afb030$6b03a8c0@sdelaney2>
On Tue, Mar 3, 2009 at 10:39 PM, Steve DeLaney <onramp123@yahoo.com> wrote:
> I didn't realize before that /proc/interrupts
> serviced by irq.c show_interrupts() displays virtual vector numbers.
>
> For the mpc8349e-mitx this must be incorrect since there is no ISA?
> Essentially all that is needed is a 1:1 mapping hirq:virq since
> each interrupt source in the system appears to have a unique vector
> in the SOC IPIC. =A0It seems this scheme is needlessly complex, at least =
for
> mpc8349e.
It may look to be needlessly complex for boards that only have one
interrupt controller, but it is far simpler than the alternative when
you have boards with multiple cascaded IRQ controllers. This is not
an uncommon circumstance any more, even on embedded boards. ie. I'm
working on two different MPC5200 platforms and each has an FPGA which
coalesces interrupts to a single MPC5200 external IRQ line.
Without VIRQs, I'd need to slice up the IRQ number space for each
board variant I work with and write the code to match. With VIRQs,
there is no need to, and all of the boards can use common support
code.
g.
.
--=20
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
^ permalink raw reply
* Re: [PATCH/RFC] ps3/block: Add ps3vram-ng driver for accessing video RAM as block device
From: Benjamin Herrenschmidt @ 2009-03-04 23:27 UTC (permalink / raw)
To: Geert Uytterhoeven
Cc: Jens Axboe, Arnd Bergmann, Linus Torvalds,
Linux Kernel Development, Jim Paris, Linux/PPC Development,
linux-mtd, Vivien Chappelier, David Woodhouse,
Cell Broadband Engine OSS Development
In-Reply-To: <alpine.LRH.2.00.0903041444300.32279@vixen.sonytel.be>
On Wed, 2009-03-04 at 14:57 +0100, Geert Uytterhoeven wrote:
> Hi,
>
> Below is the rewrite of the PS3 Video RAM Storage Driver as a plain block
> device, as requested by Arnd Bergmann.
>
> The MTD-based PS3 Video RAM Storage Driver was integrated into the mainline
> kernel in 2.6.29-rc1.
>
> Ideally, we think it would be best if the existing MTD-based ps3vram driver
> would be replaced by the new block-based ps3vram driver before 2.6.29 is
> released. This would relieve the burden of supporting two different swap space
> schemes on PS3 (swap on /dev/mtdblock0 vs. /dev/ps3vram) from the distro
> maintainer's shoulders, as in that case there would never have been a stable
> kernel version containing the MTD-based ps3vram driver.
This is very very very late ... we are at rc7, probably one rc before
final... as much as I like integrating drivers later, I'll ask Linus
opinion on this one.
Linus ? What do you reckon ? Maybe a better option is just to remove
ps3nvram from .29 and merge the new one in .30 ?
Cheers,
Ben.
> What do you think? If this is accepted, I'll submit a patch to remove the MTD
> ps3vram and add the new driver as ps3vram (instead of ps3vram-ng).
>
> Thanks for your (review and other) comments!
>
> ---
> >From e19ce619675bc150cd82701e7b272f7018c36527 Mon Sep 17 00:00:00 2001
> From: Geert Uytterhoeven <Geert.Uytterhoeven@sonycom.com>
> Date: Wed, 25 Feb 2009 18:32:10 +0100
> Subject: [PATCH] ps3/block: Add ps3vram-ng driver for accessing video RAM as block device
>
> Add ps3vram-ng driver, which exposes unused video RAM on the PS3 as a block
> device suitable for storage or swap. Fast data transfer is achieved
> using a local cache in system RAM and DMA transfers via the GPU.
>
> Notes:
> - As both the existing MTD ps3vram and ps3vram-ng bind to the same PS3 system
> bus device, the driver which is loaded first by udev wins. However, you can
> unload ps3vram, and load ps3vram-ng later.
> - ps3vram-ng is faster than ps3vram:
> o 1 MiB blocks: +50% (read), +5% (write)
> o 4 KiB blocks: +50% (read), +5% (write)
> o 512 B blocks: +10% (read), +10% (write)
>
> Signed-off-by: Geert Uytterhoeven <Geert.Uytterhoeven@sonycom.com>
> Cc: Jim Paris <jim@jtan.com>
> Cc: Vivien Chappelier <vivien.chappelier@free.fr>
> ---
> arch/powerpc/platforms/ps3/Kconfig | 7 +
> drivers/block/Makefile | 1 +
> drivers/block/ps3vram_ng.c | 916 ++++++++++++++++++++++++++++++++++++
> 3 files changed, 924 insertions(+), 0 deletions(-)
> create mode 100644 drivers/block/ps3vram_ng.c
>
> diff --git a/arch/powerpc/platforms/ps3/Kconfig b/arch/powerpc/platforms/ps3/Kconfig
> index 920cf7a..b3b7a20 100644
> --- a/arch/powerpc/platforms/ps3/Kconfig
> +++ b/arch/powerpc/platforms/ps3/Kconfig
> @@ -128,6 +128,13 @@ config PS3_FLASH
> be disabled on the kernel command line using "ps3flash=off", to
> not allocate this fixed buffer.
>
> +config PS3_VRAM
> + tristate "PS3 Video RAM Storage Driver"
> + depends on PPC_PS3 && BLOCK
> + help
> + This driver allows you to use excess PS3 video RAM as volatile
> + storage or system swap.
> +
> config PS3_LPM
> tristate "PS3 Logical Performance Monitor support"
> depends on PPC_PS3
> diff --git a/drivers/block/Makefile b/drivers/block/Makefile
> index 204332b..ad3e45e 100644
> --- a/drivers/block/Makefile
> +++ b/drivers/block/Makefile
> @@ -9,6 +9,7 @@ obj-$(CONFIG_MAC_FLOPPY) += swim3.o
> obj-$(CONFIG_BLK_DEV_FD) += floppy.o
> obj-$(CONFIG_AMIGA_FLOPPY) += amiflop.o
> obj-$(CONFIG_PS3_DISK) += ps3disk.o
> +obj-$(CONFIG_PS3_VRAM) += ps3vram_ng.o
> obj-$(CONFIG_ATARI_FLOPPY) += ataflop.o
> obj-$(CONFIG_AMIGA_Z2RAM) += z2ram.o
> obj-$(CONFIG_BLK_DEV_RAM) += brd.o
> diff --git a/drivers/block/ps3vram_ng.c b/drivers/block/ps3vram_ng.c
> new file mode 100644
> index 0000000..fa24a17
> --- /dev/null
> +++ b/drivers/block/ps3vram_ng.c
> @@ -0,0 +1,916 @@
> +/*
> + * ps3vram - Use extra PS3 video ram as MTD block device.
> + *
> + * Copyright 2009 Sony Corporation
> + *
> + * Based on the MTD ps3vram driver, which is
> + * Copyright (c) 2007-2008 Jim Paris <jim@jtan.com>
> + * Added support RSX DMA Vivien Chappelier <vivien.chappelier@free.fr>
> + */
> +
> +#include <linux/blkdev.h>
> +#include <linux/delay.h>
> +#include <linux/kthread.h>
> +#include <linux/proc_fs.h>
> +#include <linux/seq_file.h>
> +
> +#include <asm/firmware.h>
> +#include <asm/lv1call.h>
> +#include <asm/ps3.h>
> +
> +
> +#define DEVICE_NAME "ps3vram"
> +
> +
> +#define XDR_BUF_SIZE (2 * 1024 * 1024) /* XDR buffer (must be 1MiB aligned) */
> +#define XDR_IOIF 0x0c000000
> +
> +#define FIFO_BASE XDR_IOIF
> +#define FIFO_SIZE (64 * 1024)
> +
> +#define DMA_PAGE_SIZE (4 * 1024)
> +
> +#define CACHE_PAGE_SIZE (256 * 1024)
> +#define CACHE_PAGE_COUNT ((XDR_BUF_SIZE - FIFO_SIZE) / CACHE_PAGE_SIZE)
> +
> +#define CACHE_OFFSET CACHE_PAGE_SIZE
> +#define FIFO_OFFSET 0
> +
> +#define CTRL_PUT 0x10
> +#define CTRL_GET 0x11
> +#define CTRL_TOP 0x15
> +
> +#define UPLOAD_SUBCH 1
> +#define DOWNLOAD_SUBCH 2
> +
> +#define NV_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN 0x0000030c
> +#define NV_MEMORY_TO_MEMORY_FORMAT_NOTIFY 0x00000104
> +
> +#define L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT 0x601
> +
> +#define CACHE_PAGE_PRESENT 1
> +#define CACHE_PAGE_DIRTY 2
> +
> +struct ps3vram_tag {
> + unsigned int address;
> + unsigned int flags;
> +};
> +
> +struct ps3vram_cache {
> + unsigned int page_count;
> + unsigned int page_size;
> + struct ps3vram_tag *tags;
> + unsigned int hit;
> + unsigned int miss;
> +};
> +
> +struct ps3vram_priv {
> + spinlock_t lock; /* Request queue spinlock */
> + struct task_struct *thread;
> + struct request_queue *queue;
> + struct gendisk *gendisk;
> +
> + u64 size;
> +
> + u64 memory_handle;
> + u64 context_handle;
> + u32 *ctrl;
> + u32 *reports;
> + u8 __iomem *ddr_base;
> + u8 *xdr_buf;
> +
> + u32 *fifo_base;
> + u32 *fifo_ptr;
> +
> + struct ps3vram_cache cache;
> +};
> +
> +
> +static int ps3vram_major;
> +
> +
> +static struct block_device_operations ps3vram_fops = {
> + .owner = THIS_MODULE,
> +};
> +
> +
> +#define DMA_NOTIFIER_HANDLE_BASE 0x66604200 /* first DMA notifier handle */
> +#define DMA_NOTIFIER_OFFSET_BASE 0x1000 /* first DMA notifier offset */
> +#define DMA_NOTIFIER_SIZE 0x40
> +#define NOTIFIER 7 /* notifier used for completion report */
> +
> +/* A trailing '-' means to subtract off ps3fb_videomemory.size */
> +static char *size = "256M-";
> +module_param(size, charp, 0);
> +MODULE_PARM_DESC(size, "memory size");
> +
> +static u32 *ps3vram_get_notifier(u32 *reports, int notifier)
> +{
> + return (void *)reports + DMA_NOTIFIER_OFFSET_BASE +
> + DMA_NOTIFIER_SIZE * notifier;
> +}
> +
> +static void ps3vram_notifier_reset(struct ps3_system_bus_device *dev)
> +{
> + struct ps3vram_priv *priv = dev->core.driver_data;
> + u32 *notify = ps3vram_get_notifier(priv->reports, NOTIFIER);
> + int i;
> +
> + for (i = 0; i < 4; i++)
> + notify[i] = 0xffffffff;
> +}
> +
> +static int ps3vram_notifier_wait(struct ps3_system_bus_device *dev,
> + unsigned int timeout_ms)
> +{
> + struct ps3vram_priv *priv = dev->core.driver_data;
> + u32 *notify = ps3vram_get_notifier(priv->reports, NOTIFIER);
> + unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms);
> +
> + do {
> + if (!notify[3])
> + return 0;
> + msleep(1);
> + } while (time_before(jiffies, timeout));
> +
> + return -ETIMEDOUT;
> +}
> +
> +static void ps3vram_init_ring(struct ps3_system_bus_device *dev)
> +{
> + struct ps3vram_priv *priv = dev->core.driver_data;
> +
> + priv->ctrl[CTRL_PUT] = FIFO_BASE + FIFO_OFFSET;
> + priv->ctrl[CTRL_GET] = FIFO_BASE + FIFO_OFFSET;
> +}
> +
> +static int ps3vram_wait_ring(struct ps3_system_bus_device *dev,
> + unsigned int timeout_ms)
> +{
> + struct ps3vram_priv *priv = dev->core.driver_data;
> + unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms);
> +
> + do {
> + if (priv->ctrl[CTRL_PUT] == priv->ctrl[CTRL_GET])
> + return 0;
> + msleep(1);
> + } while (time_before(jiffies, timeout));
> +
> + dev_dbg(&dev->core, "FIFO timeout (%08x/%08x/%08x)\n",
> + priv->ctrl[CTRL_PUT], priv->ctrl[CTRL_GET],
> + priv->ctrl[CTRL_TOP]);
> +
> + return -ETIMEDOUT;
> +}
> +
> +static void ps3vram_out_ring(struct ps3vram_priv *priv, u32 data)
> +{
> + *(priv->fifo_ptr)++ = data;
> +}
> +
> +static void ps3vram_begin_ring(struct ps3vram_priv *priv, u32 chan, u32 tag,
> + u32 size)
> +{
> + ps3vram_out_ring(priv, (size << 18) | (chan << 13) | tag);
> +}
> +
> +static void ps3vram_rewind_ring(struct ps3_system_bus_device *dev)
> +{
> + struct ps3vram_priv *priv = dev->core.driver_data;
> + u64 status;
> +
> + ps3vram_out_ring(priv, 0x20000000 | (FIFO_BASE + FIFO_OFFSET));
> +
> + priv->ctrl[CTRL_PUT] = FIFO_BASE + FIFO_OFFSET;
> +
> + /* asking the HV for a blit will kick the FIFO */
> + status = lv1_gpu_context_attribute(priv->context_handle,
> + L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT, 0,
> + 0, 0, 0);
> + if (status)
> + dev_err(&dev->core,
> + "%s: lv1_gpu_context_attribute failed %llu\n",
> + __func__, status);
> +
> + priv->fifo_ptr = priv->fifo_base;
> +}
> +
> +static void ps3vram_fire_ring(struct ps3_system_bus_device *dev)
> +{
> + struct ps3vram_priv *priv = dev->core.driver_data;
> + u64 status;
> +
> + mutex_lock(&ps3_gpu_mutex);
> +
> + priv->ctrl[CTRL_PUT] = FIFO_BASE + FIFO_OFFSET +
> + (priv->fifo_ptr - priv->fifo_base) * sizeof(u32);
> +
> + /* asking the HV for a blit will kick the FIFO */
> + status = lv1_gpu_context_attribute(priv->context_handle,
> + L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT, 0,
> + 0, 0, 0);
> + if (status)
> + dev_err(&dev->core,
> + "%s: lv1_gpu_context_attribute failed %llu\n",
> + __func__, status);
> +
> + if ((priv->fifo_ptr - priv->fifo_base) * sizeof(u32) >
> + FIFO_SIZE - 1024) {
> + dev_dbg(&dev->core, "FIFO full, rewinding\n");
> + ps3vram_wait_ring(dev, 200);
> + ps3vram_rewind_ring(dev);
> + }
> +
> + mutex_unlock(&ps3_gpu_mutex);
> +}
> +
> +static void ps3vram_bind(struct ps3_system_bus_device *dev)
> +{
> + struct ps3vram_priv *priv = dev->core.driver_data;
> +
> + ps3vram_begin_ring(priv, UPLOAD_SUBCH, 0, 1);
> + ps3vram_out_ring(priv, 0x31337303);
> + ps3vram_begin_ring(priv, UPLOAD_SUBCH, 0x180, 3);
> + ps3vram_out_ring(priv, DMA_NOTIFIER_HANDLE_BASE + NOTIFIER);
> + ps3vram_out_ring(priv, 0xfeed0001); /* DMA system RAM instance */
> + ps3vram_out_ring(priv, 0xfeed0000); /* DMA video RAM instance */
> +
> + ps3vram_begin_ring(priv, DOWNLOAD_SUBCH, 0, 1);
> + ps3vram_out_ring(priv, 0x3137c0de);
> + ps3vram_begin_ring(priv, DOWNLOAD_SUBCH, 0x180, 3);
> + ps3vram_out_ring(priv, DMA_NOTIFIER_HANDLE_BASE + NOTIFIER);
> + ps3vram_out_ring(priv, 0xfeed0000); /* DMA video RAM instance */
> + ps3vram_out_ring(priv, 0xfeed0001); /* DMA system RAM instance */
> +
> + ps3vram_fire_ring(dev);
> +}
> +
> +static int ps3vram_upload(struct ps3_system_bus_device *dev,
> + unsigned int src_offset, unsigned int dst_offset,
> + int len, int count)
> +{
> + struct ps3vram_priv *priv = dev->core.driver_data;
> +
> + ps3vram_begin_ring(priv, UPLOAD_SUBCH,
> + NV_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN, 8);
> + ps3vram_out_ring(priv, XDR_IOIF + src_offset);
> + ps3vram_out_ring(priv, dst_offset);
> + ps3vram_out_ring(priv, len);
> + ps3vram_out_ring(priv, len);
> + ps3vram_out_ring(priv, len);
> + ps3vram_out_ring(priv, count);
> + ps3vram_out_ring(priv, (1 << 8) | 1);
> + ps3vram_out_ring(priv, 0);
> +
> + ps3vram_notifier_reset(dev);
> + ps3vram_begin_ring(priv, UPLOAD_SUBCH,
> + NV_MEMORY_TO_MEMORY_FORMAT_NOTIFY, 1);
> + ps3vram_out_ring(priv, 0);
> + ps3vram_begin_ring(priv, UPLOAD_SUBCH, 0x100, 1);
> + ps3vram_out_ring(priv, 0);
> + ps3vram_fire_ring(dev);
> + if (ps3vram_notifier_wait(dev, 200) < 0) {
> + dev_dbg(&dev->core, "%s: Notifier timeout\n", __func__);
> + return -1;
> + }
> +
> + return 0;
> +}
> +
> +static int ps3vram_download(struct ps3_system_bus_device *dev,
> + unsigned int src_offset, unsigned int dst_offset,
> + int len, int count)
> +{
> + struct ps3vram_priv *priv = dev->core.driver_data;
> +
> + ps3vram_begin_ring(priv, DOWNLOAD_SUBCH,
> + NV_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN, 8);
> + ps3vram_out_ring(priv, src_offset);
> + ps3vram_out_ring(priv, XDR_IOIF + dst_offset);
> + ps3vram_out_ring(priv, len);
> + ps3vram_out_ring(priv, len);
> + ps3vram_out_ring(priv, len);
> + ps3vram_out_ring(priv, count);
> + ps3vram_out_ring(priv, (1 << 8) | 1);
> + ps3vram_out_ring(priv, 0);
> +
> + ps3vram_notifier_reset(dev);
> + ps3vram_begin_ring(priv, DOWNLOAD_SUBCH,
> + NV_MEMORY_TO_MEMORY_FORMAT_NOTIFY, 1);
> + ps3vram_out_ring(priv, 0);
> + ps3vram_begin_ring(priv, DOWNLOAD_SUBCH, 0x100, 1);
> + ps3vram_out_ring(priv, 0);
> + ps3vram_fire_ring(dev);
> + if (ps3vram_notifier_wait(dev, 200) < 0) {
> + dev_dbg(&dev->core, "%s: Notifier timeout\n", __func__);
> + return -1;
> + }
> +
> + return 0;
> +}
> +
> +static void ps3vram_cache_evict(struct ps3_system_bus_device *dev, int entry)
> +{
> + struct ps3vram_priv *priv = dev->core.driver_data;
> + struct ps3vram_cache *cache = &priv->cache;
> +
> + if (!(cache->tags[entry].flags & CACHE_PAGE_DIRTY))
> + return;
> +
> + dev_dbg(&dev->core, "Flushing %d: 0x%08x\n", entry,
> + cache->tags[entry].address);
> + if (ps3vram_upload(dev, CACHE_OFFSET + entry * cache->page_size,
> + cache->tags[entry].address, DMA_PAGE_SIZE,
> + cache->page_size / DMA_PAGE_SIZE) < 0) {
> + dev_err(&dev->core,
> + "Failed to upload from 0x%x to " "0x%x size 0x%x\n",
> + entry * cache->page_size, cache->tags[entry].address,
> + cache->page_size);
> + }
> + cache->tags[entry].flags &= ~CACHE_PAGE_DIRTY;
> +}
> +
> +static void ps3vram_cache_load(struct ps3_system_bus_device *dev, int entry,
> + unsigned int address)
> +{
> + struct ps3vram_priv *priv = dev->core.driver_data;
> + struct ps3vram_cache *cache = &priv->cache;
> +
> + dev_dbg(&dev->core, "Fetching %d: 0x%08x\n", entry, address);
> + if (ps3vram_download(dev, address,
> + CACHE_OFFSET + entry * cache->page_size,
> + DMA_PAGE_SIZE,
> + cache->page_size / DMA_PAGE_SIZE) < 0) {
> + dev_err(&dev->core,
> + "Failed to download from 0x%x to 0x%x size 0x%x\n",
> + address, entry * cache->page_size, cache->page_size);
> + }
> +
> + cache->tags[entry].address = address;
> + cache->tags[entry].flags |= CACHE_PAGE_PRESENT;
> +}
> +
> +
> +static void ps3vram_cache_flush(struct ps3_system_bus_device *dev)
> +{
> + struct ps3vram_priv *priv = dev->core.driver_data;
> + struct ps3vram_cache *cache = &priv->cache;
> + int i;
> +
> + dev_dbg(&dev->core, "FLUSH\n");
> + for (i = 0; i < cache->page_count; i++) {
> + ps3vram_cache_evict(dev, i);
> + cache->tags[i].flags = 0;
> + }
> +}
> +
> +static unsigned int ps3vram_cache_match(struct ps3_system_bus_device *dev,
> + loff_t address)
> +{
> + struct ps3vram_priv *priv = dev->core.driver_data;
> + struct ps3vram_cache *cache = &priv->cache;
> + unsigned int base;
> + unsigned int offset;
> + int i;
> + static int counter;
> +
> + offset = (unsigned int) (address & (cache->page_size - 1));
> + base = (unsigned int) (address - offset);
> +
> + /* fully associative check */
> + for (i = 0; i < cache->page_count; i++) {
> + if ((cache->tags[i].flags & CACHE_PAGE_PRESENT) &&
> + cache->tags[i].address == base) {
> + cache->hit++;
> + dev_dbg(&dev->core, "Found entry %d: 0x%08x\n", i,
> + cache->tags[i].address);
> + return i;
> + }
> + }
> +
> + /* choose a random entry */
> + i = (jiffies + (counter++)) % cache->page_count;
> + dev_dbg(&dev->core, "Using entry %d\n", i);
> +
> + ps3vram_cache_evict(dev, i);
> + ps3vram_cache_load(dev, i, base);
> +
> + cache->miss++;
> + return i;
> +}
> +
> +static int ps3vram_cache_init(struct ps3_system_bus_device *dev)
> +{
> + struct ps3vram_priv *priv = dev->core.driver_data;
> +
> + priv->cache.page_count = CACHE_PAGE_COUNT;
> + priv->cache.page_size = CACHE_PAGE_SIZE;
> + priv->cache.tags = kzalloc(sizeof(struct ps3vram_tag) *
> + CACHE_PAGE_COUNT, GFP_KERNEL);
> + if (priv->cache.tags == NULL) {
> + dev_err(&dev->core, "Could not allocate cache tags\n");
> + return -ENOMEM;
> + }
> +
> + dev_info(&dev->core, "Created ram cache: %d entries, %d KiB each\n",
> + CACHE_PAGE_COUNT, CACHE_PAGE_SIZE / 1024);
> +
> + return 0;
> +}
> +
> +static void ps3vram_cache_cleanup(struct ps3_system_bus_device *dev)
> +{
> + struct ps3vram_priv *priv = dev->core.driver_data;
> +
> + ps3vram_cache_flush(dev);
> + kfree(priv->cache.tags);
> +}
> +
> +static int ps3vram_read(struct ps3_system_bus_device *dev, loff_t from,
> + size_t len, size_t *retlen, u_char *buf)
> +{
> + struct ps3vram_priv *priv = dev->core.driver_data;
> + unsigned int cached, count;
> +
> + dev_dbg(&dev->core, "%s: from=0x%08x len=0x%zx\n", __func__,
> + (unsigned int)from, len);
> +
> + if (from >= priv->size)
> + return -EINVAL;
> +
> + if (len > priv->size - from)
> + len = priv->size - from;
> +
> + /* Copy from vram to buf */
> + count = len;
> + while (count) {
> + unsigned int offset, avail;
> + unsigned int entry;
> +
> + offset = (unsigned int) (from & (priv->cache.page_size - 1));
> + avail = priv->cache.page_size - offset;
> +
> + entry = ps3vram_cache_match(dev, from);
> + cached = CACHE_OFFSET + entry * priv->cache.page_size + offset;
> +
> + dev_dbg(&dev->core, "%s: from=%08x cached=%08x offset=%08x "
> + "avail=%08x count=%08x\n", __func__,
> + (unsigned int)from, cached, offset, avail, count);
> +
> + if (avail > count)
> + avail = count;
> + memcpy(buf, priv->xdr_buf + cached, avail);
> +
> + buf += avail;
> + count -= avail;
> + from += avail;
> + }
> +
> + *retlen = len;
> + return 0;
> +}
> +
> +static int ps3vram_write(struct ps3_system_bus_device *dev, loff_t to,
> + size_t len, size_t *retlen, const u_char *buf)
> +{
> + struct ps3vram_priv *priv = dev->core.driver_data;
> + unsigned int cached, count;
> +
> + if (to >= priv->size)
> + return -EINVAL;
> +
> + if (len > priv->size - to)
> + len = priv->size - to;
> +
> + /* Copy from buf to vram */
> + count = len;
> + while (count) {
> + unsigned int offset, avail;
> + unsigned int entry;
> +
> + offset = (unsigned int) (to & (priv->cache.page_size - 1));
> + avail = priv->cache.page_size - offset;
> +
> + entry = ps3vram_cache_match(dev, to);
> + cached = CACHE_OFFSET + entry * priv->cache.page_size + offset;
> +
> + dev_dbg(&dev->core, "%s: to=%08x cached=%08x offset=%08x "
> + "avail=%08x count=%08x\n", __func__, (unsigned int)to,
> + cached, offset, avail, count);
> +
> + if (avail > count)
> + avail = count;
> + memcpy(priv->xdr_buf + cached, buf, avail);
> +
> + priv->cache.tags[entry].flags |= CACHE_PAGE_DIRTY;
> +
> + buf += avail;
> + count -= avail;
> + to += avail;
> + }
> +
> + *retlen = len;
> + return 0;
> +}
> +
> +static int ps3vram_proc_show(struct seq_file *m, void *v)
> +{
> + struct ps3vram_priv *priv = m->private;
> +
> + seq_printf(m, "hit:%u\nmiss:%u\n", priv->cache.hit, priv->cache.miss);
> + return 0;
> +}
> +
> +static int ps3vram_proc_open(struct inode *inode, struct file *file)
> +{
> + return single_open(file, ps3vram_proc_show, PDE(inode)->data);
> +}
> +
> +static const struct file_operations ps3vram_proc_fops = {
> + .owner = THIS_MODULE,
> + .open = ps3vram_proc_open,
> + .read = seq_read,
> + .llseek = seq_lseek,
> + .release = single_release,
> +};
> +
> +static void __devinit ps3vram_proc_init(struct ps3_system_bus_device *dev)
> +{
> + struct ps3vram_priv *priv = dev->core.driver_data;
> + struct proc_dir_entry *pde;
> +
> + pde = proc_create(DEVICE_NAME, 0444, NULL, &ps3vram_proc_fops);
> + if (!pde) {
> + dev_warn(&dev->core, "failed to create /proc entry\n");
> + return;
> + }
> +
> + pde->owner = THIS_MODULE;
> + pde->data = priv;
> +}
> +
> +static void ps3vram_do_request(struct ps3_system_bus_device *dev,
> + struct request *req)
> +{
> + struct ps3vram_priv *priv = dev->core.driver_data;
> + int write, res, uptodate = 0;
> + const char *op;
> + sector_t start_sector;
> + unsigned int sectors;
> + loff_t offset;
> + size_t len, retlen;
> +
> + write = rq_data_dir(req);
> + op = write ? "write" : "read";
> +
> + start_sector = req->sector;
> + sectors = req->current_nr_sectors;
> + dev_dbg(&dev->core, "%s %u sectors starting at %lu\n", op, sectors,
> + start_sector);
> +
> + offset = start_sector << 9;
> + len = sectors << 9;
> +
> + if (write)
> + res = ps3vram_write(dev, offset, len, &retlen, req->buffer);
> + else
> + res = ps3vram_read(dev, offset, len, &retlen, req->buffer);
> +
> + if (res) {
> + dev_err(&dev->core, "%s failed\n", op);
> + goto out;
> + }
> +
> + if (retlen != len) {
> + dev_err(&dev->core, "Short %s\n", op);
> + goto out;
> + }
> +
> + dev_dbg(&dev->core, "%s completed\n", op);
> +
> + uptodate = 1;
> +
> +out:
> + spin_lock_irq(&priv->lock);
> + end_request(req, uptodate);
> + spin_unlock_irq(&priv->lock);
> +}
> +
> +static int ps3vram_thread(void *data)
> +{
> + struct ps3_system_bus_device *dev = data;
> + struct ps3vram_priv *priv = dev->core.driver_data;
> + struct request_queue *q = priv->queue;
> + struct request *req;
> +
> + dev_dbg(&dev->core, "%s init\n", __func__);
> +
> + current->flags |= PF_NOFREEZE;
> +
> + while (!kthread_should_stop()) {
> + spin_lock_irq(&priv->lock);
> + set_current_state(TASK_INTERRUPTIBLE);
> + req = elv_next_request(q);
> + if (!req) {
> + spin_unlock_irq(&priv->lock);
> + schedule();
> + continue;
> + }
> + if (!blk_fs_request(req)) {
> + blk_dump_rq_flags(req, DEVICE_NAME " bad request");
> + end_request(req, 0);
> + spin_unlock_irq(&priv->lock);
> + continue;
> + }
> + spin_unlock_irq(&priv->lock);
> + ps3vram_do_request(dev, req);
> + }
> +
> + dev_dbg(&dev->core, "%s exit\n", __func__);
> + return 0;
> +}
> +
> +static void ps3vram_request(struct request_queue *q)
> +{
> + struct ps3_system_bus_device *dev = q->queuedata;
> + struct ps3vram_priv *priv = dev->core.driver_data;
> +
> + dev_dbg(&dev->core, "Waking up thread\n");
> + wake_up_process(priv->thread);
> +}
> +
> +
> +static int __devinit ps3vram_probe(struct ps3_system_bus_device *dev)
> +{
> + struct ps3vram_priv *priv;
> + int error, status;
> + struct request_queue *queue;
> + struct gendisk *gendisk;
> + u64 ddr_lpar, ctrl_lpar, info_lpar, reports_lpar, ddr_size,
> + reports_size;
> + char *rest;
> + struct task_struct *task;
> +
> + priv = kzalloc(sizeof(*priv), GFP_KERNEL);
> + if (!priv) {
> + error = -ENOMEM;
> + goto fail;
> + }
> +
> + dev->core.driver_data = priv;
> + spin_lock_init(&priv->lock);
> +
> + priv = dev->core.driver_data;
> +
> + /* Allocate XDR buffer (1MiB aligned) */
> + priv->xdr_buf = (void *)__get_free_pages(GFP_KERNEL,
> + get_order(XDR_BUF_SIZE));
> + if (priv->xdr_buf == NULL) {
> + dev_dbg(&dev->core, "Could not allocate XDR buffer\n");
> + error = -ENOMEM;
> + goto fail_free_priv;
> + }
> +
> + /* Put FIFO at begginning of XDR buffer */
> + priv->fifo_base = (u32 *) (priv->xdr_buf + FIFO_OFFSET);
> + priv->fifo_ptr = priv->fifo_base;
> +
> + /* XXX: Need to open GPU, in case ps3fb or snd_ps3 aren't loaded */
> + if (ps3_open_hv_device(dev)) {
> + dev_err(&dev->core, "ps3_open_hv_device failed\n");
> + error = -EAGAIN;
> + goto out_close_gpu;
> + }
> +
> + /* Request memory */
> + status = -1;
> + ddr_size = memparse(size, &rest);
> + if (*rest == '-')
> + ddr_size -= ps3fb_videomemory.size;
> + ddr_size = ALIGN(ddr_size, 1024*1024);
> + if (ddr_size <= 0) {
> + dev_err(&dev->core, "Specified size is too small\n");
> + error = -EINVAL;
> + goto out_close_gpu;
> + }
> +
> + while (ddr_size > 0) {
> + status = lv1_gpu_memory_allocate(ddr_size, 0, 0, 0, 0,
> + &priv->memory_handle,
> + &ddr_lpar);
> + if (!status)
> + break;
> + ddr_size -= 1024*1024;
> + }
> + if (status || ddr_size <= 0) {
> + dev_err(&dev->core, "lv1_gpu_memory_allocate failed %d\n",
> + status);
> + error = -ENOMEM;
> + goto out_free_xdr_buf;
> + }
> +
> + /* Request context */
> + status = lv1_gpu_context_allocate(priv->memory_handle, 0,
> + &priv->context_handle, &ctrl_lpar,
> + &info_lpar, &reports_lpar,
> + &reports_size);
> + if (status) {
> + dev_err(&dev->core, "lv1_gpu_context_allocate failed %d\n",
> + status);
> + error = -ENOMEM;
> + goto out_free_memory;
> + }
> +
> + /* Map XDR buffer to RSX */
> + status = lv1_gpu_context_iomap(priv->context_handle, XDR_IOIF,
> + ps3_mm_phys_to_lpar(__pa(priv->xdr_buf)),
> + XDR_BUF_SIZE, 0);
> + if (status) {
> + dev_err(&dev->core, "lv1_gpu_context_iomap failed %d\n",
> + status);
> + error = -ENOMEM;
> + goto out_free_context;
> + }
> +
> + priv->ddr_base = ioremap_flags(ddr_lpar, ddr_size, _PAGE_NO_CACHE);
> +
> + if (!priv->ddr_base) {
> + dev_err(&dev->core, "ioremap DDR failed\n");
> + error = -ENOMEM;
> + goto out_free_context;
> + }
> +
> + priv->ctrl = ioremap(ctrl_lpar, 64 * 1024);
> + if (!priv->ctrl) {
> + dev_err(&dev->core, "ioremap CTRL failed\n");
> + error = -ENOMEM;
> + goto out_unmap_vram;
> + }
> +
> + priv->reports = ioremap(reports_lpar, reports_size);
> + if (!priv->reports) {
> + dev_err(&dev->core, "ioremap REPORTS failed\n");
> + error = -ENOMEM;
> + goto out_unmap_ctrl;
> + }
> +
> + mutex_lock(&ps3_gpu_mutex);
> + ps3vram_init_ring(dev);
> + mutex_unlock(&ps3_gpu_mutex);
> +
> + priv->size = ddr_size;
> +
> + ps3vram_bind(dev);
> +
> + mutex_lock(&ps3_gpu_mutex);
> + error = ps3vram_wait_ring(dev, 100);
> + mutex_unlock(&ps3_gpu_mutex);
> + if (error < 0) {
> + dev_err(&dev->core, "Failed to initialize channels\n");
> + error = -ETIMEDOUT;
> + goto out_unmap_reports;
> + }
> +
> + ps3vram_cache_init(dev);
> + ps3vram_proc_init(dev);
> +
> + queue = blk_init_queue(ps3vram_request, &priv->lock);
> + if (!queue) {
> + dev_err(&dev->core, "blk_init_queue failed\n");
> + error = -ENOMEM;
> + goto out_cache_cleanup;
> + }
> +
> + priv->queue = queue;
> + queue->queuedata = dev;
> +
> + gendisk = alloc_disk(1);
> + if (!gendisk) {
> + dev_err(&dev->core, "alloc_disk failed\n");
> + error = -ENOMEM;
> + goto fail_cleanup_queue;
> + }
> +
> + priv->gendisk = gendisk;
> + gendisk->major = ps3vram_major;
> + gendisk->first_minor = 0;
> + gendisk->fops = &ps3vram_fops;
> + gendisk->queue = queue;
> + gendisk->private_data = dev;
> + gendisk->driverfs_dev = &dev->core;
> + strlcpy(gendisk->disk_name, DEVICE_NAME, sizeof(gendisk->disk_name));
> + set_capacity(gendisk, priv->size >> 9);
> +
> + task = kthread_run(ps3vram_thread, dev, DEVICE_NAME);
> + if (IS_ERR(task)) {
> + error = PTR_ERR(task);
> + goto fail_free_disk;
> + }
> + priv->thread = task;
> +
> + dev_info(&dev->core, "%s: Using %lu MiB of GPU memory\n",
> + gendisk->disk_name, get_capacity(gendisk) >> 11);
> +
> + add_disk(gendisk);
> + return 0;
> +
> +fail_free_disk:
> + put_disk(priv->gendisk);
> +fail_cleanup_queue:
> + blk_cleanup_queue(queue);
> +out_cache_cleanup:
> + remove_proc_entry(DEVICE_NAME, NULL);
> + ps3vram_cache_cleanup(dev);
> +out_unmap_reports:
> + iounmap(priv->reports);
> +out_unmap_ctrl:
> + iounmap(priv->ctrl);
> +out_unmap_vram:
> + iounmap(priv->ddr_base);
> +out_free_context:
> + lv1_gpu_context_free(priv->context_handle);
> +out_free_memory:
> + lv1_gpu_memory_free(priv->memory_handle);
> +out_close_gpu:
> + ps3_close_hv_device(dev);
> +out_free_xdr_buf:
> + free_pages((unsigned long) priv->xdr_buf, get_order(XDR_BUF_SIZE));
> +fail_free_priv:
> + kfree(priv);
> + dev->core.driver_data = NULL;
> +fail:
> + return error;
> +}
> +
> +static int ps3vram_remove(struct ps3_system_bus_device *dev)
> +{
> + struct ps3vram_priv *priv = dev->core.driver_data;
> +
> + BUG_ON(!elv_queue_empty(priv->queue));
> +
> + kthread_stop(priv->thread);
> + del_gendisk(priv->gendisk);
> + blk_cleanup_queue(priv->queue);
> + put_disk(priv->gendisk);
> + remove_proc_entry(DEVICE_NAME, NULL);
> + ps3vram_cache_cleanup(dev);
> + iounmap(priv->reports);
> + iounmap(priv->ctrl);
> + iounmap(priv->ddr_base);
> + lv1_gpu_context_free(priv->context_handle);
> + lv1_gpu_memory_free(priv->memory_handle);
> + ps3_close_hv_device(dev);
> + free_pages((unsigned long) priv->xdr_buf, get_order(XDR_BUF_SIZE));
> + kfree(priv);
> + dev->core.driver_data = NULL;
> + return 0;
> +}
> +
> +static struct ps3_system_bus_driver ps3vram = {
> + .match_id = PS3_MATCH_ID_GPU,
> + .match_sub_id = PS3_MATCH_SUB_ID_GPU_RAMDISK,
> + .core.name = DEVICE_NAME,
> + .core.owner = THIS_MODULE,
> + .probe = ps3vram_probe,
> + .remove = ps3vram_remove,
> + .shutdown = ps3vram_remove,
> +};
> +
> +
> +static int __init ps3vram_init(void)
> +{
> + int error;
> +
> + if (!firmware_has_feature(FW_FEATURE_PS3_LV1))
> + return -ENODEV;
> +
> + error = register_blkdev(0, DEVICE_NAME);
> + if (error <= 0) {
> + pr_err("%s: register_blkdev failed %d\n", DEVICE_NAME, error);
> + return error;
> + }
> + ps3vram_major = error;
> +
> + pr_info("%s: registered block device major %d\n", DEVICE_NAME,
> + ps3vram_major);
> +
> + error = ps3_system_bus_driver_register(&ps3vram);
> + if (error)
> + unregister_blkdev(ps3vram_major, DEVICE_NAME);
> +
> + return error;
> +}
> +
> +static void __exit ps3vram_exit(void)
> +{
> + ps3_system_bus_driver_unregister(&ps3vram);
> + unregister_blkdev(ps3vram_major, DEVICE_NAME);
> +}
> +
> +module_init(ps3vram_init);
> +module_exit(ps3vram_exit);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("PS3 Video RAM Storage Driver");
> +MODULE_AUTHOR("Sony Corporation");
> +MODULE_ALIAS(PS3_MODULE_ALIAS_GPU_RAMDISK);
^ permalink raw reply
* Re: [Cbe-oss-dev] [PATCH] powerpc/spufs: Fix incorrect buffer offset in regs write
From: Jeremy Kerr @ 2009-03-04 23:32 UTC (permalink / raw)
To: cbe-oss-dev; +Cc: Geert Uytterhoeven, linuxppc-dev
In-Reply-To: <alpine.LRH.2.00.0903040935560.2416@vixen.sonytel.be>
Hi Geert,
> Could this be abused by an attacker to write registers or local store
> he's not allowed to do?
It looks like the user can only overwrite fields that it already has
access to. There's struct spu_lscsa:
struct spu_lscsa {
struct spu_reg128 gprs[128];
struct spu_reg128 fpcr;
struct spu_reg128 decr;
struct spu_reg128 decr_status;
struct spu_reg128 ppu_mb;
struct spu_reg128 ppuint_mb;
struct spu_reg128 tag_mask;
struct spu_reg128 event_mask;
struct spu_reg128 srr0;
struct spu_reg128 stopped_status;
unsigned char ls[LS_SIZE] __attribute__((aligned(65536)));
};
where spu_reg128 is a u32[4].
The maximum 'allowed' write offset to the regs file is 2047. The
(incorrect) maximum offset calculated by the old code would be 8188
(2047 * 4) bytes into struct spu_lscsa.
So, 8188 bytes covers all of the registers, but ends somewhere before
the start of the ls area (within the ls alignment padding). Let's look
at the registers:
gprs: user-writable
fpcr: user-writable
decr: user-writable
decr_status: only affects user-settable SPE state
ppu_mb: only affects user-settable SPE state
ppuint_mb: only affects user-settable SPE state
tag_mask: only affects user-settable SPE state
event_mask: only affects user-settable SPE state
srr0: only affects user-settable SPE state
stopped_status: only affects user-settable SPE state
So, I think we're fine. All a user can do with this bug is mess up their
own SPE state.
> Should it be backported to stable?
Yes, I'll submit to the stable tree too.
Cheers,
Jeremy
^ permalink raw reply
* (no subject)
From: deng david @ 2009-03-05 0:45 UTC (permalink / raw)
To: linuxppc-dev
Dear Everyone:
I have read in this list that someone asked for the document of
IBM eBony board with PPC440GP. I don't know if he get it . But could
anyone send me the document or tell me how to get it ? thanks a lot.
^ permalink raw reply
* Re: [Cbe-oss-dev] [PATCH/RFC] ps3/block: Add ps3vram-ng driver for accessing video RAM as block device
From: Marcus G. Daniels @ 2009-03-05 0:21 UTC (permalink / raw)
To: Geert Uytterhoeven
Cc: Jens Axboe, Arnd Bergmann, Linux Kernel Development, Jim Paris,
Linux/PPC Development, linux-mtd, Vivien Chappelier,
Cell Broadband Engine OSS Development
In-Reply-To: <alpine.LRH.2.00.0903041444300.32279@vixen.sonytel.be>
Geert Uytterhoeven wrote:
> Ideally, we think it would be best if the existing MTD-based ps3vram driver
> would be replaced by the new block-based ps3vram driver before 2.6.29 is
> released. This would relieve the burden of supporting two different swap space
> schemes on PS3 (swap on /dev/mtdblock0 vs. /dev/ps3vram) from the distro
> maintainer's shoulders, as in that case there would never have been a stable
> kernel version containing the MTD-based ps3vram driver.
> [..]
> - ps3vram-ng is faster than ps3vram:
> o 1 MiB blocks: +50% (read), +5% (write)
> o 4 KiB blocks: +50% (read), +5% (write)
> o 512 B blocks: +10% (read), +10% (write)
>
Fwiw, it's a useful swap device, since space is so limited -- the
performance improvements are certainly appealing too.
Marcus
^ permalink raw reply
* [PATCH] powerpc/cell: Only build Axon MSI driver for IBM Cell Blades
From: Michael Ellerman @ 2009-03-05 4:41 UTC (permalink / raw)
To: linuxppc-dev
The hardware is only present on those machines, and the driver
depends on infrastructure which is selected by the Kconfig for
cell blades.
Reported-by: Mikey "Randconfig Monkey" Neuling
Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
---
arch/powerpc/platforms/cell/Makefile | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/arch/powerpc/platforms/cell/Makefile b/arch/powerpc/platforms/cell/Makefile
index 43eccb2..9330cf8 100644
--- a/arch/powerpc/platforms/cell/Makefile
+++ b/arch/powerpc/platforms/cell/Makefile
@@ -28,7 +28,9 @@ obj-$(CONFIG_SPU_BASE) += spu_callbacks.o spu_base.o \
$(spu-manage-y) \
spufs/
+ifeq ($(CONFIG_PPC_IBM_CELL_BLADE),y)
obj-$(CONFIG_PCI_MSI) += axon_msi.o
+endif
# qpace setup
obj-$(CONFIG_PPC_CELL_QPACE) += qpace_setup.o
--
1.5.6.3
^ permalink raw reply related
* [RFC/PATCH] Print linux_banner in prom_init
From: Michael Ellerman @ 2009-03-05 5:02 UTC (permalink / raw)
To: linuxppc-dev
So at least you can see what kernel you're booting if you die
before the kernel prints it mid-way through start_kernel().
Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
---
arch/powerpc/kernel/prom_init.c | 2 ++
arch/powerpc/kernel/prom_init_check.sh | 2 +-
2 files changed, 3 insertions(+), 1 deletions(-)
diff --git a/arch/powerpc/kernel/prom_init.c b/arch/powerpc/kernel/prom_init.c
index 7f1b33d..4d5ebb4 100644
--- a/arch/powerpc/kernel/prom_init.c
+++ b/arch/powerpc/kernel/prom_init.c
@@ -2283,6 +2283,8 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4,
*/
prom_init_stdout();
+ prom_printf("Preparing to boot %s", PTRRELOC((char *)linux_banner));
+
/*
* Get default machine type. At this point, we do not differentiate
* between pSeries SMP and pSeries LPAR
diff --git a/arch/powerpc/kernel/prom_init_check.sh b/arch/powerpc/kernel/prom_init_check.sh
index ea3a2ec..1ac136b 100644
--- a/arch/powerpc/kernel/prom_init_check.sh
+++ b/arch/powerpc/kernel/prom_init_check.sh
@@ -20,7 +20,7 @@ WHITELIST="add_reloc_offset __bss_start __bss_stop copy_and_flush
_end enter_prom memcpy memset reloc_offset __secondary_hold
__secondary_hold_acknowledge __secondary_hold_spinloop __start
strcmp strcpy strlcpy strlen strncmp strstr logo_linux_clut224
-reloc_got2 kernstart_addr memstart_addr"
+reloc_got2 kernstart_addr memstart_addr linux_banner"
NM="$1"
OBJ="$2"
--
1.5.6.3
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox