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