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