Commit cffb4add03b1fc83026b06dc3664279cfbf70155

Authored by Jim Paris
Committed by Benjamin Herrenschmidt
1 parent d2b4397bf8

mtd/ps3vram: Add ps3vram driver for accessing video RAM as MTD

Add ps3vram driver, which exposes unused video RAM on the PS3 as a MTD
device suitable for storage or swap.  Fast data transfer is achieved
using a local cache in system RAM and DMA transfers via the GPU.

Signed-off-by: Vivien Chappelier <vivien.chappelier@free.fr>
Signed-off-by: Jim Paris <jim@jtan.com>
Acked-by: Geoff Levand <geoffrey.levand@am.sony.com>
Acked-by: David Woodhouse <David.Woodhouse@intel.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>

Showing 6 changed files with 828 additions and 0 deletions Side-by-side Diff

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