]> git.ipfire.org Git - people/pmueller/ipfire-2.x.git/blame - src/patches/suse-2.6.27.25/patches.arch/ppc-ps3-ps3vram-mtd.patch
Revert "Move xen patchset to new version's subdir."
[people/pmueller/ipfire-2.x.git] / src / patches / suse-2.6.27.25 / patches.arch / ppc-ps3-ps3vram-mtd.patch
CommitLineData
00e5a55c
BS
1From: Jim Paris <jim@jtan.com>
2Subject: Add ps3vram driver, which exposes unused video RAM on the PS3 as a MTD
3
4Add ps3vram driver, which exposes unused video RAM on the PS3 as a MTD
5device suitable for storage or swap. Fast data transfer is achieved
6using a local cache in system RAM and DMA transfers via the GPU.
7
8Signed-off-by: Vivien Chappelier <vivien.chappelier@free.fr>
9CC: Geert Uytterhoeven <Geert.Uytterhoeven@sonycom.com>
10CC: Geoff Levand <geoffrey.levand@am.sony.com>
11Signed-off-by: Jim Paris <jim@jtan.com>
12Signed-off-by: Stefan Assmann <sassmann@suse.de>
13---
14This version has been updated to work with PS3 firmware 2.50, and
15to use ps3_gpu_mutex.
16
17Please consider for 2.6.29.
18
19 MAINTAINERS | 6
20 arch/powerpc/include/asm/ps3.h | 1
21 arch/powerpc/platforms/ps3/device-init.c | 37 +
22 drivers/mtd/devices/Kconfig | 7
23 drivers/mtd/devices/Makefile | 1
24 MAINTAINERS | 6
25 arch/powerpc/include/asm/ps3.h | 1
26 arch/powerpc/platforms/ps3/device-init.c | 37 +
27 drivers/mtd/devices/Kconfig | 7
28 drivers/mtd/devices/Makefile | 1
29 drivers/mtd/devices/ps3vram.c | 776 +++++++++++++++++++++++++++++++
30 6 files changed, 828 insertions(+)
31 create mode 100644 drivers/mtd/devices/ps3vram.c
32
33--- a/arch/powerpc/include/asm/ps3.h
34+++ b/arch/powerpc/include/asm/ps3.h
35@@ -340,6 +340,7 @@ enum ps3_system_bus_device_type {
36 enum ps3_match_sub_id {
37 /* for PS3_MATCH_ID_GRAPHICS */
38 PS3_MATCH_SUB_ID_FB = 1,
39+ PS3_MATCH_SUB_ID_RAMDISK = 2,
40 };
41
42 /**
43--- a/arch/powerpc/platforms/ps3/device-init.c
44+++ b/arch/powerpc/platforms/ps3/device-init.c
45@@ -499,6 +499,41 @@ static int __init ps3_register_graphics_
46 return result;
47 }
48
49+static int __init ps3_register_ramdisk_device(void)
50+{
51+ int result;
52+ struct layout {
53+ struct ps3_system_bus_device dev;
54+ } *p;
55+
56+ pr_debug(" -> %s:%d\n", __func__, __LINE__);
57+
58+ p = kzalloc(sizeof(struct layout), GFP_KERNEL);
59+
60+ if (!p)
61+ return -ENOMEM;
62+
63+ p->dev.match_id = PS3_MATCH_ID_GRAPHICS;
64+ p->dev.match_sub_id = PS3_MATCH_SUB_ID_RAMDISK;
65+ p->dev.dev_type = PS3_DEVICE_TYPE_IOC0;
66+
67+ result = ps3_system_bus_device_register(&p->dev);
68+
69+ if (result) {
70+ pr_debug("%s:%d ps3_system_bus_device_register failed\n",
71+ __func__, __LINE__);
72+ goto fail_device_register;
73+ }
74+
75+ pr_debug(" <- %s:%d\n", __func__, __LINE__);
76+ return 0;
77+
78+fail_device_register:
79+ kfree(p);
80+ pr_debug(" <- %s:%d failed\n", __func__, __LINE__);
81+ return result;
82+}
83+
84 /**
85 * ps3_setup_dynamic_device - Setup a dynamic device from the repository
86 */
87@@ -927,6 +962,8 @@ static int __init ps3_register_devices(v
88
89 ps3_register_lpm_devices();
90
91+ ps3_register_ramdisk_device();
92+
93 pr_debug(" <- %s:%d\n", __func__, __LINE__);
94 return 0;
95 }
96--- a/drivers/mtd/devices/Kconfig
97+++ b/drivers/mtd/devices/Kconfig
98@@ -99,6 +99,13 @@ config MTD_PHRAM
99 doesn't have access to, memory beyond the mem=xxx limit, nvram,
100 memory on the video card, etc...
101
102+config MTD_PS3VRAM
103+ tristate "PS3 video RAM"
104+ depends on FB_PS3
105+ help
106+ This driver allows you to use excess PS3 video RAM as volatile
107+ storage or system swap.
108+
109 config MTD_LART
110 tristate "28F160xx flash driver for LART"
111 depends on SA1100_LART
112--- a/drivers/mtd/devices/Makefile
113+++ b/drivers/mtd/devices/Makefile
114@@ -16,3 +16,4 @@ obj-$(CONFIG_MTD_LART) += lart.o
115 obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o
116 obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o
117 obj-$(CONFIG_MTD_M25P80) += m25p80.o
118+obj-$(CONFIG_MTD_PS3VRAM) += ps3vram.o
119--- /dev/null
120+++ b/drivers/mtd/devices/ps3vram.c
121@@ -0,0 +1,776 @@
122+/**
123+ * ps3vram - Use extra PS3 video ram as MTD block device.
124+ *
125+ * Copyright (c) 2007-2008 Jim Paris <jim@jtan.com>
126+ * Added support RSX DMA Vivien Chappelier <vivien.chappelier@free.fr>
127+ */
128+
129+#include <linux/io.h>
130+#include <linux/init.h>
131+#include <linux/kernel.h>
132+#include <linux/list.h>
133+#include <linux/module.h>
134+#include <linux/moduleparam.h>
135+#include <linux/slab.h>
136+#include <linux/version.h>
137+#include <linux/gfp.h>
138+#include <linux/delay.h>
139+#include <linux/mtd/mtd.h>
140+
141+#include <asm/lv1call.h>
142+#include <asm/ps3.h>
143+
144+#define DEVICE_NAME "ps3vram"
145+
146+#define XDR_BUF_SIZE (2 * 1024 * 1024) /* XDR buffer (must be 1MB aligned) */
147+#define XDR_IOIF 0x0c000000
148+
149+#define FIFO_BASE XDR_IOIF
150+#define FIFO_SIZE (64 * 1024)
151+
152+#define DMA_PAGE_SIZE (4 * 1024)
153+
154+#define CACHE_PAGE_SIZE (256 * 1024)
155+#define CACHE_PAGE_COUNT ((XDR_BUF_SIZE - FIFO_SIZE) / CACHE_PAGE_SIZE)
156+
157+#define CACHE_OFFSET CACHE_PAGE_SIZE
158+#define FIFO_OFFSET 0
159+
160+#define CTRL_PUT 0x10
161+#define CTRL_GET 0x11
162+#define CTRL_TOP 0x15
163+
164+#define UPLOAD_SUBCH 1
165+#define DOWNLOAD_SUBCH 2
166+
167+#define NV_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN 0x0000030c
168+#define NV_MEMORY_TO_MEMORY_FORMAT_NOTIFY 0x00000104
169+
170+#define L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT 0x601
171+
172+struct mtd_info ps3vram_mtd;
173+
174+#define CACHE_PAGE_PRESENT 1
175+#define CACHE_PAGE_DIRTY 2
176+
177+#define dbg(fmt, args...) \
178+ pr_debug("%s:%d " fmt "\n", __func__, __LINE__, ## args)
179+
180+struct ps3vram_tag {
181+ unsigned int address;
182+ unsigned int flags;
183+};
184+
185+struct ps3vram_cache {
186+ unsigned int page_count;
187+ unsigned int page_size;
188+ struct ps3vram_tag *tags;
189+};
190+
191+struct ps3vram_priv {
192+ uint64_t memory_handle;
193+ uint64_t context_handle;
194+ uint8_t *base;
195+ uint32_t *ctrl;
196+ uint32_t *reports;
197+ uint8_t *xdr_buf;
198+
199+ uint32_t *fifo_base;
200+ uint32_t *fifo_ptr;
201+
202+ struct ps3vram_cache cache;
203+
204+ /* Used to serialize cache/DMA operations */
205+ struct mutex lock;
206+};
207+
208+#define DMA_NOTIFIER_HANDLE_BASE 0x66604200 /* first DMA notifier handle */
209+#define DMA_NOTIFIER_OFFSET_BASE 0x1000 /* first DMA notifier offset */
210+#define DMA_NOTIFIER_SIZE 0x40
211+
212+#define NUM_NOTIFIERS 16
213+
214+#define NOTIFIER 7 /* notifier used for completion report */
215+
216+/* A trailing '-' means to subtract off ps3fb_videomemory.size */
217+char *size = "256M-";
218+module_param(size, charp, 0);
219+MODULE_PARM_DESC(size, "memory size");
220+
221+static inline uint32_t *ps3vram_get_notifier(uint32_t *reports, int notifier)
222+{
223+ return (void *) reports +
224+ DMA_NOTIFIER_OFFSET_BASE +
225+ DMA_NOTIFIER_SIZE * notifier;
226+}
227+
228+static void ps3vram_notifier_reset(struct mtd_info *mtd)
229+{
230+ int i;
231+ struct ps3vram_priv *priv = mtd->priv;
232+ uint32_t *notify = ps3vram_get_notifier(priv->reports, NOTIFIER);
233+ for (i = 0; i < 4; i++)
234+ notify[i] = 0xffffffff;
235+}
236+
237+static int ps3vram_notifier_wait(struct mtd_info *mtd, int timeout_ms)
238+{
239+ struct ps3vram_priv *priv = mtd->priv;
240+ uint32_t *notify = ps3vram_get_notifier(priv->reports, NOTIFIER);
241+
242+ timeout_ms *= 1000;
243+
244+ do {
245+ if (notify[3] == 0)
246+ return 0;
247+
248+ if (timeout_ms)
249+ udelay(1);
250+ } while (timeout_ms--);
251+
252+ return -1;
253+}
254+
255+static void ps3vram_dump_ring(struct mtd_info *mtd)
256+{
257+ struct ps3vram_priv *priv = mtd->priv;
258+ uint32_t *fifo;
259+
260+ pr_info("PUT = %08x GET = %08x\n", priv->ctrl[CTRL_PUT],
261+ priv->ctrl[CTRL_GET]);
262+ for (fifo = priv->fifo_base; fifo < priv->fifo_ptr; fifo++)
263+ pr_info("%p: %08x\n", fifo, *fifo);
264+}
265+
266+static void ps3vram_dump_reports(struct mtd_info *mtd)
267+{
268+ struct ps3vram_priv *priv = mtd->priv;
269+ int i;
270+
271+ for (i = 0; i < NUM_NOTIFIERS; i++) {
272+ uint32_t *n = ps3vram_get_notifier(priv->reports, i);
273+ pr_info("%p: %08x\n", n, *n);
274+ }
275+}
276+
277+static void ps3vram_init_ring(struct mtd_info *mtd)
278+{
279+ struct ps3vram_priv *priv = mtd->priv;
280+
281+ priv->ctrl[CTRL_PUT] = FIFO_BASE + FIFO_OFFSET;
282+ priv->ctrl[CTRL_GET] = FIFO_BASE + FIFO_OFFSET;
283+}
284+
285+static int ps3vram_wait_ring(struct mtd_info *mtd, int timeout)
286+{
287+ struct ps3vram_priv *priv = mtd->priv;
288+
289+ /* wait until setup commands are processed */
290+ timeout *= 1000;
291+ while (--timeout) {
292+ if (priv->ctrl[CTRL_PUT] == priv->ctrl[CTRL_GET])
293+ break;
294+ udelay(1);
295+ }
296+ if (timeout == 0) {
297+ pr_err("FIFO timeout (%08x/%08x/%08x)\n", priv->ctrl[CTRL_PUT],
298+ priv->ctrl[CTRL_GET], priv->ctrl[CTRL_TOP]);
299+ return -ETIMEDOUT;
300+ }
301+
302+ return 0;
303+}
304+
305+static inline void ps3vram_out_ring(struct ps3vram_priv *priv, uint32_t data)
306+{
307+ *(priv->fifo_ptr)++ = data;
308+}
309+
310+static inline void ps3vram_begin_ring(struct ps3vram_priv *priv, uint32_t chan,
311+ uint32_t tag, uint32_t size)
312+{
313+ ps3vram_out_ring(priv, (size << 18) | (chan << 13) | tag);
314+}
315+
316+static void ps3vram_rewind_ring(struct mtd_info *mtd)
317+{
318+ struct ps3vram_priv *priv = mtd->priv;
319+ u64 status;
320+
321+ ps3vram_out_ring(priv, 0x20000000 | (FIFO_BASE + FIFO_OFFSET));
322+
323+ priv->ctrl[CTRL_PUT] = FIFO_BASE + FIFO_OFFSET;
324+
325+ /* asking the HV for a blit will kick the fifo */
326+ status = lv1_gpu_context_attribute(priv->context_handle,
327+ L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT,
328+ 0, 0, 0, 0);
329+ if (status)
330+ pr_err("ps3vram: lv1_gpu_context_attribute FB_BLIT failed\n");
331+
332+ priv->fifo_ptr = priv->fifo_base;
333+}
334+
335+static void ps3vram_fire_ring(struct mtd_info *mtd)
336+{
337+ struct ps3vram_priv *priv = mtd->priv;
338+ u64 status;
339+
340+ mutex_lock(&ps3_gpu_mutex);
341+
342+ priv->ctrl[CTRL_PUT] = FIFO_BASE + FIFO_OFFSET +
343+ (priv->fifo_ptr - priv->fifo_base) * sizeof(uint32_t);
344+
345+ /* asking the HV for a blit will kick the fifo */
346+ status = lv1_gpu_context_attribute(priv->context_handle,
347+ L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT,
348+ 0, 0, 0, 0);
349+ if (status)
350+ pr_err("ps3vram: lv1_gpu_context_attribute FB_BLIT failed\n");
351+
352+ if ((priv->fifo_ptr - priv->fifo_base) * sizeof(uint32_t) >
353+ FIFO_SIZE - 1024) {
354+ dbg("fifo full, rewinding");
355+ ps3vram_wait_ring(mtd, 200);
356+ ps3vram_rewind_ring(mtd);
357+ }
358+
359+ mutex_unlock(&ps3_gpu_mutex);
360+}
361+
362+static void ps3vram_bind(struct mtd_info *mtd)
363+{
364+ struct ps3vram_priv *priv = mtd->priv;
365+
366+ ps3vram_begin_ring(priv, UPLOAD_SUBCH, 0, 1);
367+ ps3vram_out_ring(priv, 0x31337303);
368+ ps3vram_begin_ring(priv, UPLOAD_SUBCH, 0x180, 3);
369+ ps3vram_out_ring(priv, DMA_NOTIFIER_HANDLE_BASE + NOTIFIER);
370+ ps3vram_out_ring(priv, 0xfeed0001); /* DMA system RAM instance */
371+ ps3vram_out_ring(priv, 0xfeed0000); /* DMA video RAM instance */
372+
373+ ps3vram_begin_ring(priv, DOWNLOAD_SUBCH, 0, 1);
374+ ps3vram_out_ring(priv, 0x3137c0de);
375+ ps3vram_begin_ring(priv, DOWNLOAD_SUBCH, 0x180, 3);
376+ ps3vram_out_ring(priv, DMA_NOTIFIER_HANDLE_BASE + NOTIFIER);
377+ ps3vram_out_ring(priv, 0xfeed0000); /* DMA video RAM instance */
378+ ps3vram_out_ring(priv, 0xfeed0001); /* DMA system RAM instance */
379+
380+ ps3vram_fire_ring(mtd);
381+}
382+
383+static int ps3vram_upload(struct mtd_info *mtd, unsigned int src_offset,
384+ unsigned int dst_offset, int len, int count)
385+{
386+ struct ps3vram_priv *priv = mtd->priv;
387+
388+ ps3vram_begin_ring(priv, UPLOAD_SUBCH,
389+ NV_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN, 8);
390+ ps3vram_out_ring(priv, XDR_IOIF + src_offset);
391+ ps3vram_out_ring(priv, dst_offset);
392+ ps3vram_out_ring(priv, len);
393+ ps3vram_out_ring(priv, len);
394+ ps3vram_out_ring(priv, len);
395+ ps3vram_out_ring(priv, count);
396+ ps3vram_out_ring(priv, (1 << 8) | 1);
397+ ps3vram_out_ring(priv, 0);
398+
399+ ps3vram_notifier_reset(mtd);
400+ ps3vram_begin_ring(priv, UPLOAD_SUBCH,
401+ NV_MEMORY_TO_MEMORY_FORMAT_NOTIFY, 1);
402+ ps3vram_out_ring(priv, 0);
403+ ps3vram_begin_ring(priv, UPLOAD_SUBCH, 0x100, 1);
404+ ps3vram_out_ring(priv, 0);
405+ ps3vram_fire_ring(mtd);
406+ if (ps3vram_notifier_wait(mtd, 200) < 0) {
407+ pr_err("notifier timeout\n");
408+ ps3vram_dump_ring(mtd);
409+ ps3vram_dump_reports(mtd);
410+ return -1;
411+ }
412+
413+ return 0;
414+}
415+
416+static int ps3vram_download(struct mtd_info *mtd, unsigned int src_offset,
417+ unsigned int dst_offset, int len, int count)
418+{
419+ struct ps3vram_priv *priv = mtd->priv;
420+
421+ ps3vram_begin_ring(priv, DOWNLOAD_SUBCH,
422+ NV_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN, 8);
423+ ps3vram_out_ring(priv, src_offset);
424+ ps3vram_out_ring(priv, XDR_IOIF + dst_offset);
425+ ps3vram_out_ring(priv, len);
426+ ps3vram_out_ring(priv, len);
427+ ps3vram_out_ring(priv, len);
428+ ps3vram_out_ring(priv, count);
429+ ps3vram_out_ring(priv, (1 << 8) | 1);
430+ ps3vram_out_ring(priv, 0);
431+
432+ ps3vram_notifier_reset(mtd);
433+ ps3vram_begin_ring(priv, DOWNLOAD_SUBCH,
434+ NV_MEMORY_TO_MEMORY_FORMAT_NOTIFY, 1);
435+ ps3vram_out_ring(priv, 0);
436+ ps3vram_begin_ring(priv, DOWNLOAD_SUBCH, 0x100, 1);
437+ ps3vram_out_ring(priv, 0);
438+ ps3vram_fire_ring(mtd);
439+ if (ps3vram_notifier_wait(mtd, 200) < 0) {
440+ pr_err("notifier timeout\n");
441+ ps3vram_dump_ring(mtd);
442+ ps3vram_dump_reports(mtd);
443+ return -1;
444+ }
445+
446+ return 0;
447+}
448+
449+static void ps3vram_cache_evict(struct mtd_info *mtd, int entry)
450+{
451+ struct ps3vram_priv *priv = mtd->priv;
452+ struct ps3vram_cache *cache = &priv->cache;
453+
454+ if (cache->tags[entry].flags & CACHE_PAGE_DIRTY) {
455+ dbg("flushing %d : 0x%08x", entry, cache->tags[entry].address);
456+ if (ps3vram_upload(mtd,
457+ CACHE_OFFSET + entry * cache->page_size,
458+ cache->tags[entry].address,
459+ DMA_PAGE_SIZE,
460+ cache->page_size / DMA_PAGE_SIZE) < 0) {
461+ pr_err("failed to upload from 0x%x to 0x%x size 0x%x\n",
462+ entry * cache->page_size,
463+ cache->tags[entry].address,
464+ cache->page_size);
465+ }
466+ cache->tags[entry].flags &= ~CACHE_PAGE_DIRTY;
467+ }
468+}
469+
470+static void ps3vram_cache_load(struct mtd_info *mtd, int entry,
471+ unsigned int address)
472+{
473+ struct ps3vram_priv *priv = mtd->priv;
474+ struct ps3vram_cache *cache = &priv->cache;
475+
476+ dbg("fetching %d : 0x%08x", entry, address);
477+ if (ps3vram_download(mtd,
478+ address,
479+ CACHE_OFFSET + entry * cache->page_size,
480+ DMA_PAGE_SIZE,
481+ cache->page_size / DMA_PAGE_SIZE) < 0) {
482+ pr_err("failed to download from 0x%x to 0x%x size 0x%x\n",
483+ address,
484+ entry * cache->page_size,
485+ cache->page_size);
486+ }
487+
488+ cache->tags[entry].address = address;
489+ cache->tags[entry].flags |= CACHE_PAGE_PRESENT;
490+}
491+
492+
493+static void ps3vram_cache_flush(struct mtd_info *mtd)
494+{
495+ struct ps3vram_priv *priv = mtd->priv;
496+ struct ps3vram_cache *cache = &priv->cache;
497+ int i;
498+
499+ dbg("FLUSH");
500+ for (i = 0; i < cache->page_count; i++) {
501+ ps3vram_cache_evict(mtd, i);
502+ cache->tags[i].flags = 0;
503+ }
504+}
505+
506+static unsigned int ps3vram_cache_match(struct mtd_info *mtd, loff_t address)
507+{
508+ struct ps3vram_priv *priv = mtd->priv;
509+ struct ps3vram_cache *cache = &priv->cache;
510+ unsigned int base;
511+ unsigned int offset;
512+ int i;
513+ static int counter;
514+
515+ offset = (unsigned int) (address & (cache->page_size - 1));
516+ base = (unsigned int) (address - offset);
517+
518+ /* fully associative check */
519+ for (i = 0; i < cache->page_count; i++) {
520+ if ((cache->tags[i].flags & CACHE_PAGE_PRESENT) &&
521+ cache->tags[i].address == base) {
522+ dbg("found entry %d : 0x%08x",
523+ i, cache->tags[i].address);
524+ return i;
525+ }
526+ }
527+
528+ /* choose a random entry */
529+ i = (jiffies + (counter++)) % cache->page_count;
530+ dbg("using cache entry %d", i);
531+
532+ ps3vram_cache_evict(mtd, i);
533+ ps3vram_cache_load(mtd, i, base);
534+
535+ return i;
536+}
537+
538+static int ps3vram_cache_init(struct mtd_info *mtd)
539+{
540+ struct ps3vram_priv *priv = mtd->priv;
541+
542+ pr_info("creating cache: %d entries, %d bytes pages\n",
543+ CACHE_PAGE_COUNT, CACHE_PAGE_SIZE);
544+
545+ priv->cache.page_count = CACHE_PAGE_COUNT;
546+ priv->cache.page_size = CACHE_PAGE_SIZE;
547+ priv->cache.tags = kzalloc(sizeof(struct ps3vram_tag) *
548+ CACHE_PAGE_COUNT, GFP_KERNEL);
549+ if (priv->cache.tags == NULL) {
550+ pr_err("could not allocate cache tags\n");
551+ return -ENOMEM;
552+ }
553+
554+ return 0;
555+}
556+
557+static void ps3vram_cache_cleanup(struct mtd_info *mtd)
558+{
559+ struct ps3vram_priv *priv = mtd->priv;
560+
561+ ps3vram_cache_flush(mtd);
562+ kfree(priv->cache.tags);
563+}
564+
565+static int ps3vram_erase(struct mtd_info *mtd, struct erase_info *instr)
566+{
567+ struct ps3vram_priv *priv = mtd->priv;
568+
569+ if (instr->addr + instr->len > mtd->size)
570+ return -EINVAL;
571+
572+ mutex_lock(&priv->lock);
573+
574+ ps3vram_cache_flush(mtd);
575+
576+ /* Set bytes to 0xFF */
577+ memset(priv->base + instr->addr, 0xFF, instr->len);
578+
579+ mutex_unlock(&priv->lock);
580+
581+ instr->state = MTD_ERASE_DONE;
582+ mtd_erase_callback(instr);
583+
584+ return 0;
585+}
586+
587+
588+static int ps3vram_read(struct mtd_info *mtd, loff_t from, size_t len,
589+ size_t *retlen, u_char *buf)
590+{
591+ struct ps3vram_priv *priv = mtd->priv;
592+ unsigned int cached, count;
593+
594+ dbg("from = 0x%08x len = 0x%zx", (unsigned int) from, len);
595+
596+ if (from >= mtd->size)
597+ return -EINVAL;
598+
599+ if (len > mtd->size - from)
600+ len = mtd->size - from;
601+
602+ /* Copy from vram to buf */
603+ count = len;
604+ while (count) {
605+ unsigned int offset, avail;
606+ unsigned int entry;
607+
608+ offset = (unsigned int) (from & (priv->cache.page_size - 1));
609+ avail = priv->cache.page_size - offset;
610+
611+ mutex_lock(&priv->lock);
612+
613+ entry = ps3vram_cache_match(mtd, from);
614+ cached = CACHE_OFFSET + entry * priv->cache.page_size + offset;
615+
616+ dbg("from=%08x cached=%08x offset=%08x avail=%08x count=%08x",
617+ (unsigned)from, cached, offset, avail, count);
618+
619+ if (avail > count)
620+ avail = count;
621+ memcpy(buf, priv->xdr_buf + cached, avail);
622+
623+ mutex_unlock(&priv->lock);
624+
625+ buf += avail;
626+ count -= avail;
627+ from += avail;
628+ }
629+
630+ *retlen = len;
631+ return 0;
632+}
633+
634+static int ps3vram_write(struct mtd_info *mtd, loff_t to, size_t len,
635+ size_t *retlen, const u_char *buf)
636+{
637+ struct ps3vram_priv *priv = mtd->priv;
638+ unsigned int cached, count;
639+
640+ if (to >= mtd->size)
641+ return -EINVAL;
642+
643+ if (len > mtd->size - to)
644+ len = mtd->size - to;
645+
646+ /* Copy from buf to vram */
647+ count = len;
648+ while (count) {
649+ unsigned int offset, avail;
650+ unsigned int entry;
651+
652+ offset = (unsigned int) (to & (priv->cache.page_size - 1));
653+ avail = priv->cache.page_size - offset;
654+
655+ mutex_lock(&priv->lock);
656+
657+ entry = ps3vram_cache_match(mtd, to);
658+ cached = CACHE_OFFSET + entry * priv->cache.page_size + offset;
659+
660+ dbg("to=%08x cached=%08x offset=%08x avail=%08x count=%08x",
661+ (unsigned) to, cached, offset, avail, count);
662+
663+ if (avail > count)
664+ avail = count;
665+ memcpy(priv->xdr_buf + cached, buf, avail);
666+
667+ priv->cache.tags[entry].flags |= CACHE_PAGE_DIRTY;
668+
669+ mutex_unlock(&priv->lock);
670+
671+ buf += avail;
672+ count -= avail;
673+ to += avail;
674+ }
675+
676+ *retlen = len;
677+ return 0;
678+}
679+
680+static int __devinit ps3vram_probe(struct ps3_system_bus_device *dev)
681+{
682+ struct ps3vram_priv *priv;
683+ uint64_t status;
684+ uint64_t ddr_lpar, ctrl_lpar, info_lpar, reports_lpar;
685+ int64_t ddr_size;
686+ uint64_t reports_size;
687+ int ret = -ENOMEM;
688+ char *rest;
689+
690+ ret = -EIO;
691+ ps3vram_mtd.priv = kzalloc(sizeof(struct ps3vram_priv), GFP_KERNEL);
692+ if (!ps3vram_mtd.priv)
693+ goto out;
694+ priv = ps3vram_mtd.priv;
695+
696+ mutex_init(&priv->lock);
697+
698+ /* Allocate XDR buffer (1MB aligned) */
699+ priv->xdr_buf = (uint8_t *) __get_free_pages(GFP_KERNEL,
700+ get_order(XDR_BUF_SIZE));
701+ if (priv->xdr_buf == NULL) {
702+ pr_err("ps3vram: could not allocate XDR buffer\n");
703+ ret = -ENOMEM;
704+ goto out_free_priv;
705+ }
706+
707+ /* Put FIFO at begginning of XDR buffer */
708+ priv->fifo_base = (uint32_t *) (priv->xdr_buf + FIFO_OFFSET);
709+ priv->fifo_ptr = priv->fifo_base;
710+
711+ /* XXX: Need to open GPU, in case ps3fb or snd_ps3 aren't loaded */
712+ if (ps3_open_hv_device(dev)) {
713+ pr_err("ps3vram: ps3_open_hv_device failed\n");
714+ ret = -EAGAIN;
715+ goto out_close_gpu;
716+ }
717+
718+ /* Request memory */
719+ status = -1;
720+ ddr_size = memparse(size, &rest);
721+ if (*rest == '-')
722+ ddr_size -= ps3fb_videomemory.size;
723+ ddr_size = ALIGN(ddr_size, 1024*1024);
724+ if (ddr_size <= 0) {
725+ printk(KERN_ERR "ps3vram: specified size is too small\n");
726+ ret = -EINVAL;
727+ goto out_close_gpu;
728+ }
729+
730+ while (ddr_size > 0) {
731+ status = lv1_gpu_memory_allocate(ddr_size, 0, 0, 0, 0,
732+ &priv->memory_handle,
733+ &ddr_lpar);
734+ if (status == 0)
735+ break;
736+ ddr_size -= 1024*1024;
737+ }
738+ if (status != 0 || ddr_size <= 0) {
739+ pr_err("ps3vram: lv1_gpu_memory_allocate failed\n");
740+ ret = -ENOMEM;
741+ goto out_free_xdr_buf;
742+ }
743+ pr_info("ps3vram: allocated %u MiB of DDR memory\n",
744+ (unsigned int) (ddr_size / 1024 / 1024));
745+
746+ /* Request context */
747+ status = lv1_gpu_context_allocate(priv->memory_handle,
748+ 0,
749+ &priv->context_handle,
750+ &ctrl_lpar,
751+ &info_lpar,
752+ &reports_lpar,
753+ &reports_size);
754+ if (status) {
755+ pr_err("ps3vram: lv1_gpu_context_allocate failed\n");
756+ ret = -ENOMEM;
757+ goto out_free_memory;
758+ }
759+
760+ /* Map XDR buffer to RSX */
761+ status = lv1_gpu_context_iomap(priv->context_handle, XDR_IOIF,
762+ ps3_mm_phys_to_lpar(__pa(priv->xdr_buf)),
763+ XDR_BUF_SIZE, 0);
764+ if (status) {
765+ pr_err("ps3vram: lv1_gpu_context_iomap failed\n");
766+ ret = -ENOMEM;
767+ goto out_free_context;
768+ }
769+
770+ priv->base = ioremap(ddr_lpar, ddr_size);
771+ if (!priv->base) {
772+ pr_err("ps3vram: ioremap failed\n");
773+ ret = -ENOMEM;
774+ goto out_free_context;
775+ }
776+
777+ priv->ctrl = ioremap(ctrl_lpar, 64 * 1024);
778+ if (!priv->ctrl) {
779+ pr_err("ps3vram: ioremap failed\n");
780+ ret = -ENOMEM;
781+ goto out_unmap_vram;
782+ }
783+
784+ priv->reports = ioremap(reports_lpar, reports_size);
785+ if (!priv->reports) {
786+ pr_err("ps3vram: ioremap failed\n");
787+ ret = -ENOMEM;
788+ goto out_unmap_ctrl;
789+ }
790+
791+ mutex_lock(&ps3_gpu_mutex);
792+ ps3vram_init_ring(&ps3vram_mtd);
793+ mutex_unlock(&ps3_gpu_mutex);
794+
795+ ps3vram_mtd.name = "ps3vram";
796+ ps3vram_mtd.size = ddr_size;
797+ ps3vram_mtd.flags = MTD_CAP_RAM;
798+ ps3vram_mtd.erase = ps3vram_erase;
799+ ps3vram_mtd.point = NULL;
800+ ps3vram_mtd.unpoint = NULL;
801+ ps3vram_mtd.read = ps3vram_read;
802+ ps3vram_mtd.write = ps3vram_write;
803+ ps3vram_mtd.owner = THIS_MODULE;
804+ ps3vram_mtd.type = MTD_RAM;
805+ ps3vram_mtd.erasesize = CACHE_PAGE_SIZE;
806+ ps3vram_mtd.writesize = 1;
807+
808+ ps3vram_bind(&ps3vram_mtd);
809+
810+ mutex_lock(&ps3_gpu_mutex);
811+ ret = ps3vram_wait_ring(&ps3vram_mtd, 100);
812+ mutex_unlock(&ps3_gpu_mutex);
813+ if (ret < 0) {
814+ pr_err("failed to initialize channels\n");
815+ ret = -ETIMEDOUT;
816+ goto out_unmap_reports;
817+ }
818+
819+ ps3vram_cache_init(&ps3vram_mtd);
820+
821+ if (add_mtd_device(&ps3vram_mtd)) {
822+ pr_err("ps3vram: failed to register device\n");
823+ ret = -EAGAIN;
824+ goto out_cache_cleanup;
825+ }
826+
827+ pr_info("ps3vram mtd device registered, %lu bytes\n", ddr_size);
828+ return 0;
829+
830+out_cache_cleanup:
831+ ps3vram_cache_cleanup(&ps3vram_mtd);
832+out_unmap_reports:
833+ iounmap(priv->reports);
834+out_unmap_ctrl:
835+ iounmap(priv->ctrl);
836+out_unmap_vram:
837+ iounmap(priv->base);
838+out_free_context:
839+ lv1_gpu_context_free(priv->context_handle);
840+out_free_memory:
841+ lv1_gpu_memory_free(priv->memory_handle);
842+out_close_gpu:
843+ ps3_close_hv_device(dev);
844+out_free_xdr_buf:
845+ free_pages((unsigned long) priv->xdr_buf, get_order(XDR_BUF_SIZE));
846+out_free_priv:
847+ kfree(ps3vram_mtd.priv);
848+ ps3vram_mtd.priv = NULL;
849+out:
850+ return ret;
851+}
852+
853+static int ps3vram_shutdown(struct ps3_system_bus_device *dev)
854+{
855+ struct ps3vram_priv *priv;
856+
857+ priv = ps3vram_mtd.priv;
858+
859+ del_mtd_device(&ps3vram_mtd);
860+ ps3vram_cache_cleanup(&ps3vram_mtd);
861+ iounmap(priv->reports);
862+ iounmap(priv->ctrl);
863+ iounmap(priv->base);
864+ lv1_gpu_context_free(priv->context_handle);
865+ lv1_gpu_memory_free(priv->memory_handle);
866+ ps3_close_hv_device(dev);
867+ free_pages((unsigned long) priv->xdr_buf, get_order(XDR_BUF_SIZE));
868+ kfree(priv);
869+ return 0;
870+}
871+
872+static struct ps3_system_bus_driver ps3vram_driver = {
873+ .match_id = PS3_MATCH_ID_GRAPHICS,
874+ .match_sub_id = PS3_MATCH_SUB_ID_RAMDISK,
875+ .core.name = DEVICE_NAME,
876+ .core.owner = THIS_MODULE,
877+ .probe = ps3vram_probe,
878+ .remove = ps3vram_shutdown,
879+ .shutdown = ps3vram_shutdown,
880+};
881+
882+static int __init ps3vram_init(void)
883+{
884+ return ps3_system_bus_driver_register(&ps3vram_driver);
885+}
886+
887+static void __exit ps3vram_exit(void)
888+{
889+ ps3_system_bus_driver_unregister(&ps3vram_driver);
890+}
891+
892+module_init(ps3vram_init);
893+module_exit(ps3vram_exit);
894+
895+MODULE_LICENSE("GPL");
896+MODULE_AUTHOR("Jim Paris <jim@jtan.com>");
897+MODULE_DESCRIPTION("MTD driver for PS3 video RAM");
898--- a/MAINTAINERS
899+++ b/MAINTAINERS
900@@ -3353,6 +3353,12 @@ L: linuxppc-dev@ozlabs.org
901 L: cbe-oss-dev@ozlabs.org
902 S: Supported
903
904+PS3VRAM DRIVER
905+P: Jim Paris
906+M: jim@jtan.com
907+L: cbe-oss-dev@ozlabs.org
908+S: Maintained
909+
910 PVRUSB2 VIDEO4LINUX DRIVER
911 P: Mike Isely
912 M: isely@pobox.com