446 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * PS3 FLASH ROM Storage Driver4 *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