Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
ps3flash.c
Go to the documentation of this file.
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 
37  struct mutex mutex; /* Bounce buffer mutex */
39  int tag; /* Start sector of buffer, -1 if invalid */
40  bool dirty;
41 };
42 
43 static struct ps3_storage_device *ps3flash_dev;
44 
45 static 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);
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 
60 static 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 
76 static 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 
98 static 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 
124 out:
125  mutex_unlock(&file->f_mapping->host->i_mutex);
126  return res;
127 }
128 
129 static 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 
193 fail:
194  mutex_unlock(&priv->mutex);
195  return res;
196 }
197 
198 static 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 
268 fail:
269  mutex_unlock(&priv->mutex);
270  return res;
271 }
272 
273 static 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 
279 static 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 
285 static ssize_t ps3flash_kernel_read(void *buf, size_t count, loff_t pos)
286 {
287  return ps3flash_read(NULL, buf, count, &pos);
288 }
289 
290 static 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 
308 static int ps3flash_flush(struct file *file, fl_owner_t id)
309 {
310  return ps3flash_writeback(ps3flash_dev);
311 }
312 
313 static 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 
323 static 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 
346 static 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 
355 static const struct ps3_os_area_flash_ops ps3flash_kernel_ops = {
356  .read = ps3flash_kernel_read,
357  .write = ps3flash_kernel_write,
358 };
359 
360 static struct miscdevice ps3flash_misc = {
361  .minor = MISC_DYNAMIC_MINOR,
362  .name = DEVICE_NAME,
363  .fops = &ps3flash_fops,
364 };
365 
366 static 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 
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 
432 fail_teardown:
433  ps3stor_teardown(dev);
434 fail_free_priv:
435  kfree(priv);
436  ps3_system_bus_set_drvdata(&dev->sbd, NULL);
437 fail:
438  ps3flash_dev = NULL;
439  return error;
440 }
441 
442 static int ps3flash_remove(struct ps3_system_bus_device *_dev)
443 {
444  struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core);
445 
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 
456 static 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 
466 static int __init ps3flash_init(void)
467 {
468  return ps3_system_bus_driver_register(&ps3flash);
469 }
470 
471 static void __exit ps3flash_exit(void)
472 {
474 }
475 
476 module_init(ps3flash_init);
477 module_exit(ps3flash_exit);
478 
479 MODULE_LICENSE("GPL");
480 MODULE_DESCRIPTION("PS3 FLASH ROM Storage Driver");
481 MODULE_AUTHOR("Sony Corporation");