diff options
author | Geert Uytterhoeven <Geert.Uytterhoeven@sonycom.com> | 2007-07-21 07:37:45 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-07-21 20:49:16 -0400 |
commit | c6131fa528c4fc57605c474bf8c83821aff164c0 (patch) | |
tree | 1d6fbeca1785ac149b9ce6bd18018849ebf0625f /drivers/block/ps3disk.c | |
parent | 97d22d26b4a5aefc5a18ac81ef4e7b46da451426 (diff) |
ps3: Disk Storage Driver
Add a Disk Storage Driver for the PS3:
- Implemented as a block device driver with a dynamic major
- Disk names (and partitions) are of the format ps3d%c(%u)
- Uses software scatter-gather with a 64 KiB bounce buffer as the hypervisor
doesn't support scatter-gather
Cc: Geoff Levand <geoffrey.levand@am.sony.com>
Signed-off-by: Geert Uytterhoeven <Geert.Uytterhoeven@sonycom.com>
Acked-by: Jens Axboe <jens.axboe@oracle.com>
Cc: James Bottomley <James.Bottomley@steeleye.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/block/ps3disk.c')
-rw-r--r-- | drivers/block/ps3disk.c | 630 |
1 files changed, 630 insertions, 0 deletions
diff --git a/drivers/block/ps3disk.c b/drivers/block/ps3disk.c new file mode 100644 index 000000000000..170fb33dba97 --- /dev/null +++ b/drivers/block/ps3disk.c | |||
@@ -0,0 +1,630 @@ | |||
1 | /* | ||
2 | * PS3 Disk 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/ata.h> | ||
22 | #include <linux/blkdev.h> | ||
23 | |||
24 | #include <asm/lv1call.h> | ||
25 | #include <asm/ps3stor.h> | ||
26 | #include <asm/firmware.h> | ||
27 | |||
28 | |||
29 | #define DEVICE_NAME "ps3disk" | ||
30 | |||
31 | #define BOUNCE_SIZE (64*1024) | ||
32 | |||
33 | #define PS3DISK_MAX_DISKS 16 | ||
34 | #define PS3DISK_MINORS 16 | ||
35 | |||
36 | |||
37 | #define PS3DISK_NAME "ps3d%c" | ||
38 | |||
39 | |||
40 | struct ps3disk_private { | ||
41 | spinlock_t lock; /* Request queue spinlock */ | ||
42 | struct request_queue *queue; | ||
43 | struct gendisk *gendisk; | ||
44 | unsigned int blocking_factor; | ||
45 | struct request *req; | ||
46 | u64 raw_capacity; | ||
47 | unsigned char model[ATA_ID_PROD_LEN+1]; | ||
48 | }; | ||
49 | |||
50 | |||
51 | #define LV1_STORAGE_SEND_ATA_COMMAND (2) | ||
52 | #define LV1_STORAGE_ATA_HDDOUT (0x23) | ||
53 | |||
54 | struct lv1_ata_cmnd_block { | ||
55 | u16 features; | ||
56 | u16 sector_count; | ||
57 | u16 LBA_low; | ||
58 | u16 LBA_mid; | ||
59 | u16 LBA_high; | ||
60 | u8 device; | ||
61 | u8 command; | ||
62 | u32 is_ext; | ||
63 | u32 proto; | ||
64 | u32 in_out; | ||
65 | u32 size; | ||
66 | u64 buffer; | ||
67 | u32 arglen; | ||
68 | }; | ||
69 | |||
70 | enum lv1_ata_proto { | ||
71 | NON_DATA_PROTO = 0, | ||
72 | PIO_DATA_IN_PROTO = 1, | ||
73 | PIO_DATA_OUT_PROTO = 2, | ||
74 | DMA_PROTO = 3 | ||
75 | }; | ||
76 | |||
77 | enum lv1_ata_in_out { | ||
78 | DIR_WRITE = 0, /* memory -> device */ | ||
79 | DIR_READ = 1 /* device -> memory */ | ||
80 | }; | ||
81 | |||
82 | static int ps3disk_major; | ||
83 | |||
84 | |||
85 | static struct block_device_operations ps3disk_fops = { | ||
86 | .owner = THIS_MODULE, | ||
87 | }; | ||
88 | |||
89 | |||
90 | static void ps3disk_scatter_gather(struct ps3_storage_device *dev, | ||
91 | struct request *req, int gather) | ||
92 | { | ||
93 | unsigned int offset = 0; | ||
94 | struct bio *bio; | ||
95 | sector_t sector; | ||
96 | struct bio_vec *bvec; | ||
97 | unsigned int i = 0, j; | ||
98 | size_t size; | ||
99 | void *buf; | ||
100 | |||
101 | rq_for_each_bio(bio, req) { | ||
102 | sector = bio->bi_sector; | ||
103 | dev_dbg(&dev->sbd.core, | ||
104 | "%s:%u: bio %u: %u segs %u sectors from %lu\n", | ||
105 | __func__, __LINE__, i, bio_segments(bio), | ||
106 | bio_sectors(bio), sector); | ||
107 | bio_for_each_segment(bvec, bio, j) { | ||
108 | size = bvec->bv_len; | ||
109 | buf = __bio_kmap_atomic(bio, j, KM_IRQ0); | ||
110 | if (gather) | ||
111 | memcpy(dev->bounce_buf+offset, buf, size); | ||
112 | else | ||
113 | memcpy(buf, dev->bounce_buf+offset, size); | ||
114 | offset += size; | ||
115 | flush_kernel_dcache_page(bio_iovec_idx(bio, j)->bv_page); | ||
116 | __bio_kunmap_atomic(bio, KM_IRQ0); | ||
117 | } | ||
118 | i++; | ||
119 | } | ||
120 | } | ||
121 | |||
122 | static int ps3disk_submit_request_sg(struct ps3_storage_device *dev, | ||
123 | struct request *req) | ||
124 | { | ||
125 | struct ps3disk_private *priv = dev->sbd.core.driver_data; | ||
126 | int write = rq_data_dir(req), res; | ||
127 | const char *op = write ? "write" : "read"; | ||
128 | u64 start_sector, sectors; | ||
129 | unsigned int region_id = dev->regions[dev->region_idx].id; | ||
130 | |||
131 | #ifdef DEBUG | ||
132 | unsigned int n = 0; | ||
133 | struct bio *bio; | ||
134 | |||
135 | rq_for_each_bio(bio, req) | ||
136 | n++; | ||
137 | dev_dbg(&dev->sbd.core, | ||
138 | "%s:%u: %s req has %u bios for %lu sectors %lu hard sectors\n", | ||
139 | __func__, __LINE__, op, n, req->nr_sectors, | ||
140 | req->hard_nr_sectors); | ||
141 | #endif | ||
142 | |||
143 | start_sector = req->sector * priv->blocking_factor; | ||
144 | sectors = req->nr_sectors * priv->blocking_factor; | ||
145 | dev_dbg(&dev->sbd.core, "%s:%u: %s %lu sectors starting at %lu\n", | ||
146 | __func__, __LINE__, op, sectors, start_sector); | ||
147 | |||
148 | if (write) { | ||
149 | ps3disk_scatter_gather(dev, req, 1); | ||
150 | |||
151 | res = lv1_storage_write(dev->sbd.dev_id, region_id, | ||
152 | start_sector, sectors, 0, | ||
153 | dev->bounce_lpar, &dev->tag); | ||
154 | } else { | ||
155 | res = lv1_storage_read(dev->sbd.dev_id, region_id, | ||
156 | start_sector, sectors, 0, | ||
157 | dev->bounce_lpar, &dev->tag); | ||
158 | } | ||
159 | if (res) { | ||
160 | dev_err(&dev->sbd.core, "%s:%u: %s failed %d\n", __func__, | ||
161 | __LINE__, op, res); | ||
162 | end_request(req, 0); | ||
163 | return 0; | ||
164 | } | ||
165 | |||
166 | priv->req = req; | ||
167 | return 1; | ||
168 | } | ||
169 | |||
170 | static int ps3disk_submit_flush_request(struct ps3_storage_device *dev, | ||
171 | struct request *req) | ||
172 | { | ||
173 | struct ps3disk_private *priv = dev->sbd.core.driver_data; | ||
174 | u64 res; | ||
175 | |||
176 | dev_dbg(&dev->sbd.core, "%s:%u: flush request\n", __func__, __LINE__); | ||
177 | |||
178 | res = lv1_storage_send_device_command(dev->sbd.dev_id, | ||
179 | LV1_STORAGE_ATA_HDDOUT, 0, 0, 0, | ||
180 | 0, &dev->tag); | ||
181 | if (res) { | ||
182 | dev_err(&dev->sbd.core, "%s:%u: sync cache failed 0x%lx\n", | ||
183 | __func__, __LINE__, res); | ||
184 | end_request(req, 0); | ||
185 | return 0; | ||
186 | } | ||
187 | |||
188 | priv->req = req; | ||
189 | return 1; | ||
190 | } | ||
191 | |||
192 | static void ps3disk_do_request(struct ps3_storage_device *dev, | ||
193 | request_queue_t *q) | ||
194 | { | ||
195 | struct request *req; | ||
196 | |||
197 | dev_dbg(&dev->sbd.core, "%s:%u\n", __func__, __LINE__); | ||
198 | |||
199 | while ((req = elv_next_request(q))) { | ||
200 | if (blk_fs_request(req)) { | ||
201 | if (ps3disk_submit_request_sg(dev, req)) | ||
202 | break; | ||
203 | } else if (req->cmd_type == REQ_TYPE_FLUSH) { | ||
204 | if (ps3disk_submit_flush_request(dev, req)) | ||
205 | break; | ||
206 | } else { | ||
207 | blk_dump_rq_flags(req, DEVICE_NAME " bad request"); | ||
208 | end_request(req, 0); | ||
209 | continue; | ||
210 | } | ||
211 | } | ||
212 | } | ||
213 | |||
214 | static void ps3disk_request(request_queue_t *q) | ||
215 | { | ||
216 | struct ps3_storage_device *dev = q->queuedata; | ||
217 | struct ps3disk_private *priv = dev->sbd.core.driver_data; | ||
218 | |||
219 | if (priv->req) { | ||
220 | dev_dbg(&dev->sbd.core, "%s:%u busy\n", __func__, __LINE__); | ||
221 | return; | ||
222 | } | ||
223 | |||
224 | ps3disk_do_request(dev, q); | ||
225 | } | ||
226 | |||
227 | static irqreturn_t ps3disk_interrupt(int irq, void *data) | ||
228 | { | ||
229 | struct ps3_storage_device *dev = data; | ||
230 | struct ps3disk_private *priv; | ||
231 | struct request *req; | ||
232 | int res, read, uptodate; | ||
233 | u64 tag, status; | ||
234 | unsigned long num_sectors; | ||
235 | const char *op; | ||
236 | |||
237 | res = lv1_storage_get_async_status(dev->sbd.dev_id, &tag, &status); | ||
238 | |||
239 | if (tag != dev->tag) | ||
240 | dev_err(&dev->sbd.core, | ||
241 | "%s:%u: tag mismatch, got %lx, expected %lx\n", | ||
242 | __func__, __LINE__, tag, dev->tag); | ||
243 | |||
244 | if (res) { | ||
245 | dev_err(&dev->sbd.core, "%s:%u: res=%d status=0x%lx\n", | ||
246 | __func__, __LINE__, res, status); | ||
247 | return IRQ_HANDLED; | ||
248 | } | ||
249 | |||
250 | priv = dev->sbd.core.driver_data; | ||
251 | req = priv->req; | ||
252 | if (!req) { | ||
253 | dev_dbg(&dev->sbd.core, | ||
254 | "%s:%u non-block layer request completed\n", __func__, | ||
255 | __LINE__); | ||
256 | dev->lv1_status = status; | ||
257 | complete(&dev->done); | ||
258 | return IRQ_HANDLED; | ||
259 | } | ||
260 | |||
261 | if (req->cmd_type == REQ_TYPE_FLUSH) { | ||
262 | read = 0; | ||
263 | num_sectors = req->hard_cur_sectors; | ||
264 | op = "flush"; | ||
265 | } else { | ||
266 | read = !rq_data_dir(req); | ||
267 | num_sectors = req->nr_sectors; | ||
268 | op = read ? "read" : "write"; | ||
269 | } | ||
270 | if (status) { | ||
271 | dev_dbg(&dev->sbd.core, "%s:%u: %s failed 0x%lx\n", __func__, | ||
272 | __LINE__, op, status); | ||
273 | uptodate = 0; | ||
274 | } else { | ||
275 | dev_dbg(&dev->sbd.core, "%s:%u: %s completed\n", __func__, | ||
276 | __LINE__, op); | ||
277 | uptodate = 1; | ||
278 | if (read) | ||
279 | ps3disk_scatter_gather(dev, req, 0); | ||
280 | } | ||
281 | |||
282 | spin_lock(&priv->lock); | ||
283 | if (!end_that_request_first(req, uptodate, num_sectors)) { | ||
284 | add_disk_randomness(req->rq_disk); | ||
285 | blkdev_dequeue_request(req); | ||
286 | end_that_request_last(req, uptodate); | ||
287 | } | ||
288 | priv->req = NULL; | ||
289 | ps3disk_do_request(dev, priv->queue); | ||
290 | spin_unlock(&priv->lock); | ||
291 | |||
292 | return IRQ_HANDLED; | ||
293 | } | ||
294 | |||
295 | static int ps3disk_sync_cache(struct ps3_storage_device *dev) | ||
296 | { | ||
297 | u64 res; | ||
298 | |||
299 | dev_dbg(&dev->sbd.core, "%s:%u: sync cache\n", __func__, __LINE__); | ||
300 | |||
301 | res = ps3stor_send_command(dev, LV1_STORAGE_ATA_HDDOUT, 0, 0, 0, 0); | ||
302 | if (res) { | ||
303 | dev_err(&dev->sbd.core, "%s:%u: sync cache failed 0x%lx\n", | ||
304 | __func__, __LINE__, res); | ||
305 | return -EIO; | ||
306 | } | ||
307 | return 0; | ||
308 | } | ||
309 | |||
310 | |||
311 | /* ATA helpers copied from drivers/ata/libata-core.c */ | ||
312 | |||
313 | static void swap_buf_le16(u16 *buf, unsigned int buf_words) | ||
314 | { | ||
315 | #ifdef __BIG_ENDIAN | ||
316 | unsigned int i; | ||
317 | |||
318 | for (i = 0; i < buf_words; i++) | ||
319 | buf[i] = le16_to_cpu(buf[i]); | ||
320 | #endif /* __BIG_ENDIAN */ | ||
321 | } | ||
322 | |||
323 | static u64 ata_id_n_sectors(const u16 *id) | ||
324 | { | ||
325 | if (ata_id_has_lba(id)) { | ||
326 | if (ata_id_has_lba48(id)) | ||
327 | return ata_id_u64(id, 100); | ||
328 | else | ||
329 | return ata_id_u32(id, 60); | ||
330 | } else { | ||
331 | if (ata_id_current_chs_valid(id)) | ||
332 | return ata_id_u32(id, 57); | ||
333 | else | ||
334 | return id[1] * id[3] * id[6]; | ||
335 | } | ||
336 | } | ||
337 | |||
338 | static void ata_id_string(const u16 *id, unsigned char *s, unsigned int ofs, | ||
339 | unsigned int len) | ||
340 | { | ||
341 | unsigned int c; | ||
342 | |||
343 | while (len > 0) { | ||
344 | c = id[ofs] >> 8; | ||
345 | *s = c; | ||
346 | s++; | ||
347 | |||
348 | c = id[ofs] & 0xff; | ||
349 | *s = c; | ||
350 | s++; | ||
351 | |||
352 | ofs++; | ||
353 | len -= 2; | ||
354 | } | ||
355 | } | ||
356 | |||
357 | static void ata_id_c_string(const u16 *id, unsigned char *s, unsigned int ofs, | ||
358 | unsigned int len) | ||
359 | { | ||
360 | unsigned char *p; | ||
361 | |||
362 | WARN_ON(!(len & 1)); | ||
363 | |||
364 | ata_id_string(id, s, ofs, len - 1); | ||
365 | |||
366 | p = s + strnlen(s, len - 1); | ||
367 | while (p > s && p[-1] == ' ') | ||
368 | p--; | ||
369 | *p = '\0'; | ||
370 | } | ||
371 | |||
372 | static int ps3disk_identify(struct ps3_storage_device *dev) | ||
373 | { | ||
374 | struct ps3disk_private *priv = dev->sbd.core.driver_data; | ||
375 | struct lv1_ata_cmnd_block ata_cmnd; | ||
376 | u16 *id = dev->bounce_buf; | ||
377 | u64 res; | ||
378 | |||
379 | dev_dbg(&dev->sbd.core, "%s:%u: identify disk\n", __func__, __LINE__); | ||
380 | |||
381 | memset(&ata_cmnd, 0, sizeof(struct lv1_ata_cmnd_block)); | ||
382 | ata_cmnd.command = ATA_CMD_ID_ATA; | ||
383 | ata_cmnd.sector_count = 1; | ||
384 | ata_cmnd.size = ata_cmnd.arglen = ATA_ID_WORDS * 2; | ||
385 | ata_cmnd.buffer = dev->bounce_lpar; | ||
386 | ata_cmnd.proto = PIO_DATA_IN_PROTO; | ||
387 | ata_cmnd.in_out = DIR_READ; | ||
388 | |||
389 | res = ps3stor_send_command(dev, LV1_STORAGE_SEND_ATA_COMMAND, | ||
390 | ps3_mm_phys_to_lpar(__pa(&ata_cmnd)), | ||
391 | sizeof(ata_cmnd), ata_cmnd.buffer, | ||
392 | ata_cmnd.arglen); | ||
393 | if (res) { | ||
394 | dev_err(&dev->sbd.core, "%s:%u: identify disk failed 0x%lx\n", | ||
395 | __func__, __LINE__, res); | ||
396 | return -EIO; | ||
397 | } | ||
398 | |||
399 | swap_buf_le16(id, ATA_ID_WORDS); | ||
400 | |||
401 | /* All we're interested in are raw capacity and model name */ | ||
402 | priv->raw_capacity = ata_id_n_sectors(id); | ||
403 | ata_id_c_string(id, priv->model, ATA_ID_PROD, sizeof(priv->model)); | ||
404 | return 0; | ||
405 | } | ||
406 | |||
407 | static void ps3disk_prepare_flush(request_queue_t *q, struct request *req) | ||
408 | { | ||
409 | struct ps3_storage_device *dev = q->queuedata; | ||
410 | |||
411 | dev_dbg(&dev->sbd.core, "%s:%u\n", __func__, __LINE__); | ||
412 | |||
413 | memset(req->cmd, 0, sizeof(req->cmd)); | ||
414 | req->cmd_type = REQ_TYPE_FLUSH; | ||
415 | } | ||
416 | |||
417 | static int ps3disk_issue_flush(request_queue_t *q, struct gendisk *gendisk, | ||
418 | sector_t *sector) | ||
419 | { | ||
420 | struct ps3_storage_device *dev = q->queuedata; | ||
421 | struct request *req; | ||
422 | int res; | ||
423 | |||
424 | dev_dbg(&dev->sbd.core, "%s:%u\n", __func__, __LINE__); | ||
425 | |||
426 | req = blk_get_request(q, WRITE, __GFP_WAIT); | ||
427 | ps3disk_prepare_flush(q, req); | ||
428 | res = blk_execute_rq(q, gendisk, req, 0); | ||
429 | if (res) | ||
430 | dev_err(&dev->sbd.core, "%s:%u: flush request failed %d\n", | ||
431 | __func__, __LINE__, res); | ||
432 | blk_put_request(req); | ||
433 | return res; | ||
434 | } | ||
435 | |||
436 | |||
437 | static unsigned long ps3disk_mask; | ||
438 | |||
439 | static DEFINE_MUTEX(ps3disk_mask_mutex); | ||
440 | |||
441 | static int __devinit ps3disk_probe(struct ps3_system_bus_device *_dev) | ||
442 | { | ||
443 | struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core); | ||
444 | struct ps3disk_private *priv; | ||
445 | int error; | ||
446 | unsigned int devidx; | ||
447 | struct request_queue *queue; | ||
448 | struct gendisk *gendisk; | ||
449 | |||
450 | if (dev->blk_size < 512) { | ||
451 | dev_err(&dev->sbd.core, | ||
452 | "%s:%u: cannot handle block size %lu\n", __func__, | ||
453 | __LINE__, dev->blk_size); | ||
454 | return -EINVAL; | ||
455 | } | ||
456 | |||
457 | BUILD_BUG_ON(PS3DISK_MAX_DISKS > BITS_PER_LONG); | ||
458 | mutex_lock(&ps3disk_mask_mutex); | ||
459 | devidx = find_first_zero_bit(&ps3disk_mask, PS3DISK_MAX_DISKS); | ||
460 | if (devidx >= PS3DISK_MAX_DISKS) { | ||
461 | dev_err(&dev->sbd.core, "%s:%u: Too many disks\n", __func__, | ||
462 | __LINE__); | ||
463 | mutex_unlock(&ps3disk_mask_mutex); | ||
464 | return -ENOSPC; | ||
465 | } | ||
466 | __set_bit(devidx, &ps3disk_mask); | ||
467 | mutex_unlock(&ps3disk_mask_mutex); | ||
468 | |||
469 | priv = kzalloc(sizeof(*priv), GFP_KERNEL); | ||
470 | if (!priv) { | ||
471 | error = -ENOMEM; | ||
472 | goto fail; | ||
473 | } | ||
474 | |||
475 | dev->sbd.core.driver_data = priv; | ||
476 | spin_lock_init(&priv->lock); | ||
477 | |||
478 | dev->bounce_size = BOUNCE_SIZE; | ||
479 | dev->bounce_buf = kmalloc(BOUNCE_SIZE, GFP_DMA); | ||
480 | if (!dev->bounce_buf) { | ||
481 | error = -ENOMEM; | ||
482 | goto fail_free_priv; | ||
483 | } | ||
484 | |||
485 | error = ps3stor_setup(dev, ps3disk_interrupt); | ||
486 | if (error) | ||
487 | goto fail_free_bounce; | ||
488 | |||
489 | ps3disk_identify(dev); | ||
490 | |||
491 | queue = blk_init_queue(ps3disk_request, &priv->lock); | ||
492 | if (!queue) { | ||
493 | dev_err(&dev->sbd.core, "%s:%u: blk_init_queue failed\n", | ||
494 | __func__, __LINE__); | ||
495 | error = -ENOMEM; | ||
496 | goto fail_teardown; | ||
497 | } | ||
498 | |||
499 | priv->queue = queue; | ||
500 | queue->queuedata = dev; | ||
501 | |||
502 | blk_queue_bounce_limit(queue, BLK_BOUNCE_HIGH); | ||
503 | |||
504 | blk_queue_max_sectors(queue, dev->bounce_size >> 9); | ||
505 | blk_queue_segment_boundary(queue, -1UL); | ||
506 | blk_queue_dma_alignment(queue, dev->blk_size-1); | ||
507 | blk_queue_hardsect_size(queue, dev->blk_size); | ||
508 | |||
509 | blk_queue_issue_flush_fn(queue, ps3disk_issue_flush); | ||
510 | blk_queue_ordered(queue, QUEUE_ORDERED_DRAIN_FLUSH, | ||
511 | ps3disk_prepare_flush); | ||
512 | |||
513 | blk_queue_max_phys_segments(queue, -1); | ||
514 | blk_queue_max_hw_segments(queue, -1); | ||
515 | blk_queue_max_segment_size(queue, dev->bounce_size); | ||
516 | |||
517 | gendisk = alloc_disk(PS3DISK_MINORS); | ||
518 | if (!gendisk) { | ||
519 | dev_err(&dev->sbd.core, "%s:%u: alloc_disk failed\n", __func__, | ||
520 | __LINE__); | ||
521 | error = -ENOMEM; | ||
522 | goto fail_cleanup_queue; | ||
523 | } | ||
524 | |||
525 | priv->gendisk = gendisk; | ||
526 | gendisk->major = ps3disk_major; | ||
527 | gendisk->first_minor = devidx * PS3DISK_MINORS; | ||
528 | gendisk->fops = &ps3disk_fops; | ||
529 | gendisk->queue = queue; | ||
530 | gendisk->private_data = dev; | ||
531 | gendisk->driverfs_dev = &dev->sbd.core; | ||
532 | snprintf(gendisk->disk_name, sizeof(gendisk->disk_name), PS3DISK_NAME, | ||
533 | devidx+'a'); | ||
534 | priv->blocking_factor = dev->blk_size >> 9; | ||
535 | set_capacity(gendisk, | ||
536 | dev->regions[dev->region_idx].size*priv->blocking_factor); | ||
537 | |||
538 | dev_info(&dev->sbd.core, | ||
539 | "%s is a %s (%lu MiB total, %lu MiB for OtherOS)\n", | ||
540 | gendisk->disk_name, priv->model, priv->raw_capacity >> 11, | ||
541 | get_capacity(gendisk) >> 11); | ||
542 | |||
543 | add_disk(gendisk); | ||
544 | return 0; | ||
545 | |||
546 | fail_cleanup_queue: | ||
547 | blk_cleanup_queue(queue); | ||
548 | fail_teardown: | ||
549 | ps3stor_teardown(dev); | ||
550 | fail_free_bounce: | ||
551 | kfree(dev->bounce_buf); | ||
552 | fail_free_priv: | ||
553 | kfree(priv); | ||
554 | dev->sbd.core.driver_data = NULL; | ||
555 | fail: | ||
556 | mutex_lock(&ps3disk_mask_mutex); | ||
557 | __clear_bit(devidx, &ps3disk_mask); | ||
558 | mutex_unlock(&ps3disk_mask_mutex); | ||
559 | return error; | ||
560 | } | ||
561 | |||
562 | static int ps3disk_remove(struct ps3_system_bus_device *_dev) | ||
563 | { | ||
564 | struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core); | ||
565 | struct ps3disk_private *priv = dev->sbd.core.driver_data; | ||
566 | |||
567 | mutex_lock(&ps3disk_mask_mutex); | ||
568 | __clear_bit(priv->gendisk->first_minor / PS3DISK_MINORS, | ||
569 | &ps3disk_mask); | ||
570 | mutex_unlock(&ps3disk_mask_mutex); | ||
571 | del_gendisk(priv->gendisk); | ||
572 | blk_cleanup_queue(priv->queue); | ||
573 | put_disk(priv->gendisk); | ||
574 | dev_notice(&dev->sbd.core, "Synchronizing disk cache\n"); | ||
575 | ps3disk_sync_cache(dev); | ||
576 | ps3stor_teardown(dev); | ||
577 | kfree(dev->bounce_buf); | ||
578 | kfree(priv); | ||
579 | dev->sbd.core.driver_data = NULL; | ||
580 | return 0; | ||
581 | } | ||
582 | |||
583 | static struct ps3_system_bus_driver ps3disk = { | ||
584 | .match_id = PS3_MATCH_ID_STOR_DISK, | ||
585 | .core.name = DEVICE_NAME, | ||
586 | .core.owner = THIS_MODULE, | ||
587 | .probe = ps3disk_probe, | ||
588 | .remove = ps3disk_remove, | ||
589 | .shutdown = ps3disk_remove, | ||
590 | }; | ||
591 | |||
592 | |||
593 | static int __init ps3disk_init(void) | ||
594 | { | ||
595 | int error; | ||
596 | |||
597 | if (!firmware_has_feature(FW_FEATURE_PS3_LV1)) | ||
598 | return -ENODEV; | ||
599 | |||
600 | error = register_blkdev(0, DEVICE_NAME); | ||
601 | if (error <= 0) { | ||
602 | printk(KERN_ERR "%s:%u: register_blkdev failed %d\n", __func__, | ||
603 | __LINE__, error); | ||
604 | return error; | ||
605 | } | ||
606 | ps3disk_major = error; | ||
607 | |||
608 | pr_info("%s:%u: registered block device major %d\n", __func__, | ||
609 | __LINE__, ps3disk_major); | ||
610 | |||
611 | error = ps3_system_bus_driver_register(&ps3disk); | ||
612 | if (error) | ||
613 | unregister_blkdev(ps3disk_major, DEVICE_NAME); | ||
614 | |||
615 | return error; | ||
616 | } | ||
617 | |||
618 | static void __exit ps3disk_exit(void) | ||
619 | { | ||
620 | ps3_system_bus_driver_unregister(&ps3disk); | ||
621 | unregister_blkdev(ps3disk_major, DEVICE_NAME); | ||
622 | } | ||
623 | |||
624 | module_init(ps3disk_init); | ||
625 | module_exit(ps3disk_exit); | ||
626 | |||
627 | MODULE_LICENSE("GPL"); | ||
628 | MODULE_DESCRIPTION("PS3 Disk Storage Driver"); | ||
629 | MODULE_AUTHOR("Sony Corporation"); | ||
630 | MODULE_ALIAS(PS3_MODULE_ALIAS_STOR_DISK); | ||