linux/drivers/char/ps3flash.c
<<
>>
Prefs
   1/*
   2 * PS3 FLASH ROM Storage Driver
   3 *
   4 * Copyright (C) 2007 Sony Computer Entertainment Inc.
   5 * Copyright 2007 Sony Corp.
   6 *
   7 * This program is free software; you can redistribute it and/or modify it
   8 * under the terms of the GNU General Public License as published
   9 * by the Free Software Foundation; version 2 of the License.
  10 *
  11 * This program is distributed in the hope that it will be useful, but
  12 * WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14 * General Public License for more details.
  15 *
  16 * You should have received a copy of the GNU General Public License along
  17 * with this program; if not, write to the Free Software Foundation, Inc.,
  18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  19 */
  20
  21#include <linux/fs.h>
  22#include <linux/miscdevice.h>
  23#include <linux/slab.h>
  24#include <linux/uaccess.h>
  25#include <linux/module.h>
  26
  27#include <asm/lv1call.h>
  28#include <asm/ps3stor.h>
  29
  30
  31#define DEVICE_NAME             "ps3flash"
  32
  33#define FLASH_BLOCK_SIZE        (256*1024)
  34
  35
  36struct ps3flash_private {
  37        struct mutex mutex;     /* Bounce buffer mutex */
  38        u64 chunk_sectors;
  39        int tag;                /* Start sector of buffer, -1 if invalid */
  40        bool dirty;
  41};
  42
  43static struct ps3_storage_device *ps3flash_dev;
  44
  45static int ps3flash_read_write_sectors(struct ps3_storage_device *dev,
  46                                       u64 start_sector, int write)
  47{
  48        struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
  49        u64 res = ps3stor_read_write_sectors(dev, dev->bounce_lpar,
  50                                             start_sector, priv->chunk_sectors,
  51                                             write);
  52        if (res) {
  53                dev_err(&dev->sbd.core, "%s:%u: %s failed 0x%llx\n", __func__,
  54                        __LINE__, write ? "write" : "read", res);
  55                return -EIO;
  56        }
  57        return 0;
  58}
  59
  60static int ps3flash_writeback(struct ps3_storage_device *dev)
  61{
  62        struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
  63        int res;
  64
  65        if (!priv->dirty || priv->tag < 0)
  66                return 0;
  67
  68        res = ps3flash_read_write_sectors(dev, priv->tag, 1);
  69        if (res)
  70                return res;
  71
  72        priv->dirty = false;
  73        return 0;
  74}
  75
  76static int ps3flash_fetch(struct ps3_storage_device *dev, u64 start_sector)
  77{
  78        struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
  79        int res;
  80
  81        if (start_sector == priv->tag)
  82                return 0;
  83
  84        res = ps3flash_writeback(dev);
  85        if (res)
  86                return res;
  87
  88        priv->tag = -1;
  89
  90        res = ps3flash_read_write_sectors(dev, start_sector, 0);
  91        if (res)
  92                return res;
  93
  94        priv->tag = start_sector;
  95        return 0;
  96}
  97
  98static loff_t ps3flash_llseek(struct file *file, loff_t offset, int origin)
  99{
 100        struct ps3_storage_device *dev = ps3flash_dev;
 101        loff_t res;
 102
 103        mutex_lock(&file->f_mapping->host->i_mutex);
 104        switch (origin) {
 105        case 0:
 106                break;
 107        case 1:
 108                offset += file->f_pos;
 109                break;
 110        case 2:
 111                offset += dev->regions[dev->region_idx].size*dev->blk_size;
 112                break;
 113        default:
 114                offset = -1;
 115        }
 116        if (offset < 0) {
 117                res = -EINVAL;
 118                goto out;
 119        }
 120
 121        file->f_pos = offset;
 122        res = file->f_pos;
 123
 124out:
 125        mutex_unlock(&file->f_mapping->host->i_mutex);
 126        return res;
 127}
 128
 129static ssize_t ps3flash_read(char __user *userbuf, void *kernelbuf,
 130                             size_t count, loff_t *pos)
 131{
 132        struct ps3_storage_device *dev = ps3flash_dev;
 133        struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
 134        u64 size, sector, offset;
 135        int res;
 136        size_t remaining, n;
 137        const void *src;
 138
 139        dev_dbg(&dev->sbd.core,
 140                "%s:%u: Reading %zu bytes at position %lld to U0x%p/K0x%p\n",
 141                __func__, __LINE__, count, *pos, userbuf, kernelbuf);
 142
 143        size = dev->regions[dev->region_idx].size*dev->blk_size;
 144        if (*pos >= size || !count)
 145                return 0;
 146
 147        if (*pos + count > size) {
 148                dev_dbg(&dev->sbd.core,
 149                        "%s:%u Truncating count from %zu to %llu\n", __func__,
 150                        __LINE__, count, size - *pos);
 151                count = size - *pos;
 152        }
 153
 154        sector = *pos / dev->bounce_size * priv->chunk_sectors;
 155        offset = *pos % dev->bounce_size;
 156
 157        remaining = count;
 158        do {
 159                n = min_t(u64, remaining, dev->bounce_size - offset);
 160                src = dev->bounce_buf + offset;
 161
 162                mutex_lock(&priv->mutex);
 163
 164                res = ps3flash_fetch(dev, sector);
 165                if (res)
 166                        goto fail;
 167
 168                dev_dbg(&dev->sbd.core,
 169                        "%s:%u: copy %lu bytes from 0x%p to U0x%p/K0x%p\n",
 170                        __func__, __LINE__, n, src, userbuf, kernelbuf);
 171                if (userbuf) {
 172                        if (copy_to_user(userbuf, src, n)) {
 173                                res = -EFAULT;
 174                                goto fail;
 175                        }
 176                        userbuf += n;
 177                }
 178                if (kernelbuf) {
 179                        memcpy(kernelbuf, src, n);
 180                        kernelbuf += n;
 181                }
 182
 183                mutex_unlock(&priv->mutex);
 184
 185                *pos += n;
 186                remaining -= n;
 187                sector += priv->chunk_sectors;
 188                offset = 0;
 189        } while (remaining > 0);
 190
 191        return count;
 192
 193fail:
 194        mutex_unlock(&priv->mutex);
 195        return res;
 196}
 197
 198static ssize_t ps3flash_write(const char __user *userbuf,
 199                              const void *kernelbuf, size_t count, loff_t *pos)
 200{
 201        struct ps3_storage_device *dev = ps3flash_dev;
 202        struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
 203        u64 size, sector, offset;
 204        int res = 0;
 205        size_t remaining, n;
 206        void *dst;
 207
 208        dev_dbg(&dev->sbd.core,
 209                "%s:%u: Writing %zu bytes at position %lld from U0x%p/K0x%p\n",
 210                __func__, __LINE__, count, *pos, userbuf, kernelbuf);
 211
 212        size = dev->regions[dev->region_idx].size*dev->blk_size;
 213        if (*pos >= size || !count)
 214                return 0;
 215
 216        if (*pos + count > size) {
 217                dev_dbg(&dev->sbd.core,
 218                        "%s:%u Truncating count from %zu to %llu\n", __func__,
 219                        __LINE__, count, size - *pos);
 220                count = size - *pos;
 221        }
 222
 223        sector = *pos / dev->bounce_size * priv->chunk_sectors;
 224        offset = *pos % dev->bounce_size;
 225
 226        remaining = count;
 227        do {
 228                n = min_t(u64, remaining, dev->bounce_size - offset);
 229                dst = dev->bounce_buf + offset;
 230
 231                mutex_lock(&priv->mutex);
 232
 233                if (n != dev->bounce_size)
 234                        res = ps3flash_fetch(dev, sector);
 235                else if (sector != priv->tag)
 236                        res = ps3flash_writeback(dev);
 237                if (res)
 238                        goto fail;
 239
 240                dev_dbg(&dev->sbd.core,
 241                        "%s:%u: copy %lu bytes from U0x%p/K0x%p to 0x%p\n",
 242                        __func__, __LINE__, n, userbuf, kernelbuf, dst);
 243                if (userbuf) {
 244                        if (copy_from_user(dst, userbuf, n)) {
 245                                res = -EFAULT;
 246                                goto fail;
 247                        }
 248                        userbuf += n;
 249                }
 250                if (kernelbuf) {
 251                        memcpy(dst, kernelbuf, n);
 252                        kernelbuf += n;
 253                }
 254
 255                priv->tag = sector;
 256                priv->dirty = true;
 257
 258                mutex_unlock(&priv->mutex);
 259
 260                *pos += n;
 261                remaining -= n;
 262                sector += priv->chunk_sectors;
 263                offset = 0;
 264        } while (remaining > 0);
 265
 266        return count;
 267
 268fail:
 269        mutex_unlock(&priv->mutex);
 270        return res;
 271}
 272
 273static ssize_t ps3flash_user_read(struct file *file, char __user *buf,
 274                                  size_t count, loff_t *pos)
 275{
 276        return ps3flash_read(buf, NULL, count, pos);
 277}
 278
 279static ssize_t ps3flash_user_write(struct file *file, const char __user *buf,
 280                                   size_t count, loff_t *pos)
 281{
 282        return ps3flash_write(buf, NULL, count, pos);
 283}
 284
 285static ssize_t ps3flash_kernel_read(void *buf, size_t count, loff_t pos)
 286{
 287        return ps3flash_read(NULL, buf, count, &pos);
 288}
 289
 290static ssize_t ps3flash_kernel_write(const void *buf, size_t count,
 291                                     loff_t pos)
 292{
 293        ssize_t res;
 294        int wb;
 295
 296        res = ps3flash_write(NULL, buf, count, &pos);
 297        if (res < 0)
 298                return res;
 299
 300        /* Make kernel writes synchronous */
 301        wb = ps3flash_writeback(ps3flash_dev);
 302        if (wb)
 303                return wb;
 304
 305        return res;
 306}
 307
 308static int ps3flash_flush(struct file *file, fl_owner_t id)
 309{
 310        return ps3flash_writeback(ps3flash_dev);
 311}
 312
 313static int ps3flash_fsync(struct file *file, loff_t start, loff_t end, int datasync)
 314{
 315        struct inode *inode = file->f_path.dentry->d_inode;
 316        int err;
 317        mutex_lock(&inode->i_mutex);
 318        err = ps3flash_writeback(ps3flash_dev);
 319        mutex_unlock(&inode->i_mutex);
 320        return err;
 321}
 322
 323static irqreturn_t ps3flash_interrupt(int irq, void *data)
 324{
 325        struct ps3_storage_device *dev = data;
 326        int res;
 327        u64 tag, status;
 328
 329        res = lv1_storage_get_async_status(dev->sbd.dev_id, &tag, &status);
 330
 331        if (tag != dev->tag)
 332                dev_err(&dev->sbd.core,
 333                        "%s:%u: tag mismatch, got %llx, expected %llx\n",
 334                        __func__, __LINE__, tag, dev->tag);
 335
 336        if (res) {
 337                dev_err(&dev->sbd.core, "%s:%u: res=%d status=0x%llx\n",
 338                        __func__, __LINE__, res, status);
 339        } else {
 340                dev->lv1_status = status;
 341                complete(&dev->done);
 342        }
 343        return IRQ_HANDLED;
 344}
 345
 346static const struct file_operations ps3flash_fops = {
 347        .owner  = THIS_MODULE,
 348        .llseek = ps3flash_llseek,
 349        .read   = ps3flash_user_read,
 350        .write  = ps3flash_user_write,
 351        .flush  = ps3flash_flush,
 352        .fsync  = ps3flash_fsync,
 353};
 354
 355static const struct ps3_os_area_flash_ops ps3flash_kernel_ops = {
 356        .read   = ps3flash_kernel_read,
 357        .write  = ps3flash_kernel_write,
 358};
 359
 360static struct miscdevice ps3flash_misc = {
 361        .minor  = MISC_DYNAMIC_MINOR,
 362        .name   = DEVICE_NAME,
 363        .fops   = &ps3flash_fops,
 364};
 365
 366static int __devinit ps3flash_probe(struct ps3_system_bus_device *_dev)
 367{
 368        struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core);
 369        struct ps3flash_private *priv;
 370        int error;
 371        unsigned long tmp;
 372
 373        tmp = dev->regions[dev->region_idx].start*dev->blk_size;
 374        if (tmp % FLASH_BLOCK_SIZE) {
 375                dev_err(&dev->sbd.core,
 376                        "%s:%u region start %lu is not aligned\n", __func__,
 377                        __LINE__, tmp);
 378                return -EINVAL;
 379        }
 380        tmp = dev->regions[dev->region_idx].size*dev->blk_size;
 381        if (tmp % FLASH_BLOCK_SIZE) {
 382                dev_err(&dev->sbd.core,
 383                        "%s:%u region size %lu is not aligned\n", __func__,
 384                        __LINE__, tmp);
 385                return -EINVAL;
 386        }
 387
 388        /* use static buffer, kmalloc cannot allocate 256 KiB */
 389        if (!ps3flash_bounce_buffer.address)
 390                return -ENODEV;
 391
 392        if (ps3flash_dev) {
 393                dev_err(&dev->sbd.core,
 394                        "Only one FLASH device is supported\n");
 395                return -EBUSY;
 396        }
 397
 398        ps3flash_dev = dev;
 399
 400        priv = kzalloc(sizeof(*priv), GFP_KERNEL);
 401        if (!priv) {
 402                error = -ENOMEM;
 403                goto fail;
 404        }
 405
 406        ps3_system_bus_set_drvdata(&dev->sbd, priv);
 407        mutex_init(&priv->mutex);
 408        priv->tag = -1;
 409
 410        dev->bounce_size = ps3flash_bounce_buffer.size;
 411        dev->bounce_buf = ps3flash_bounce_buffer.address;
 412        priv->chunk_sectors = dev->bounce_size / dev->blk_size;
 413
 414        error = ps3stor_setup(dev, ps3flash_interrupt);
 415        if (error)
 416                goto fail_free_priv;
 417
 418        ps3flash_misc.parent = &dev->sbd.core;
 419        error = misc_register(&ps3flash_misc);
 420        if (error) {
 421                dev_err(&dev->sbd.core, "%s:%u: misc_register failed %d\n",
 422                        __func__, __LINE__, error);
 423                goto fail_teardown;
 424        }
 425
 426        dev_info(&dev->sbd.core, "%s:%u: registered misc device %d\n",
 427                 __func__, __LINE__, ps3flash_misc.minor);
 428
 429        ps3_os_area_flash_register(&ps3flash_kernel_ops);
 430        return 0;
 431
 432fail_teardown:
 433        ps3stor_teardown(dev);
 434fail_free_priv:
 435        kfree(priv);
 436        ps3_system_bus_set_drvdata(&dev->sbd, NULL);
 437fail:
 438        ps3flash_dev = NULL;
 439        return error;
 440}
 441
 442static int ps3flash_remove(struct ps3_system_bus_device *_dev)
 443{
 444        struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core);
 445
 446        ps3_os_area_flash_register(NULL);
 447        misc_deregister(&ps3flash_misc);
 448        ps3stor_teardown(dev);
 449        kfree(ps3_system_bus_get_drvdata(&dev->sbd));
 450        ps3_system_bus_set_drvdata(&dev->sbd, NULL);
 451        ps3flash_dev = NULL;
 452        return 0;
 453}
 454
 455
 456static struct ps3_system_bus_driver ps3flash = {
 457        .match_id       = PS3_MATCH_ID_STOR_FLASH,
 458        .core.name      = DEVICE_NAME,
 459        .core.owner     = THIS_MODULE,
 460        .probe          = ps3flash_probe,
 461        .remove         = ps3flash_remove,
 462        .shutdown       = ps3flash_remove,
 463};
 464
 465
 466static int __init ps3flash_init(void)
 467{
 468        return ps3_system_bus_driver_register(&ps3flash);
 469}
 470
 471static void __exit ps3flash_exit(void)
 472{
 473        ps3_system_bus_driver_unregister(&ps3flash);
 474}
 475
 476module_init(ps3flash_init);
 477module_exit(ps3flash_exit);
 478
 479MODULE_LICENSE("GPL");
 480MODULE_DESCRIPTION("PS3 FLASH ROM Storage Driver");
 481MODULE_AUTHOR("Sony Corporation");
 482MODULE_ALIAS(PS3_MODULE_ALIAS_STOR_FLASH);
 483
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.