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