diff options
Diffstat (limited to 'arch/powerpc/platforms/ps3/device-init.c')
-rw-r--r-- | arch/powerpc/platforms/ps3/device-init.c | 785 |
1 files changed, 785 insertions, 0 deletions
diff --git a/arch/powerpc/platforms/ps3/device-init.c b/arch/powerpc/platforms/ps3/device-init.c new file mode 100644 index 000000000000..825ebb2cbc2a --- /dev/null +++ b/arch/powerpc/platforms/ps3/device-init.c | |||
@@ -0,0 +1,785 @@ | |||
1 | /* | ||
2 | * PS3 device registration routines. | ||
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 | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; version 2 of the License. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | */ | ||
20 | |||
21 | #include <linux/delay.h> | ||
22 | #include <linux/freezer.h> | ||
23 | #include <linux/kernel.h> | ||
24 | #include <linux/kthread.h> | ||
25 | #include <linux/init.h> | ||
26 | |||
27 | #include <asm/firmware.h> | ||
28 | #include <asm/lv1call.h> | ||
29 | #include <asm/ps3stor.h> | ||
30 | |||
31 | #include "platform.h" | ||
32 | |||
33 | /** | ||
34 | * ps3_setup_gelic_device - Setup and register a gelic device instance. | ||
35 | * | ||
36 | * Allocates memory for a struct ps3_system_bus_device instance, initialises the | ||
37 | * structure members, and registers the device instance with the system bus. | ||
38 | */ | ||
39 | |||
40 | static int __init ps3_setup_gelic_device( | ||
41 | const struct ps3_repository_device *repo) | ||
42 | { | ||
43 | int result; | ||
44 | struct layout { | ||
45 | struct ps3_system_bus_device dev; | ||
46 | struct ps3_dma_region d_region; | ||
47 | } *p; | ||
48 | |||
49 | pr_debug(" -> %s:%d\n", __func__, __LINE__); | ||
50 | |||
51 | BUG_ON(repo->bus_type != PS3_BUS_TYPE_SB); | ||
52 | BUG_ON(repo->dev_type != PS3_DEV_TYPE_SB_GELIC); | ||
53 | |||
54 | p = kzalloc(sizeof(struct layout), GFP_KERNEL); | ||
55 | |||
56 | if (!p) { | ||
57 | result = -ENOMEM; | ||
58 | goto fail_malloc; | ||
59 | } | ||
60 | |||
61 | p->dev.match_id = PS3_MATCH_ID_GELIC; | ||
62 | p->dev.dev_type = PS3_DEVICE_TYPE_SB; | ||
63 | p->dev.bus_id = repo->bus_id; | ||
64 | p->dev.dev_id = repo->dev_id; | ||
65 | p->dev.d_region = &p->d_region; | ||
66 | |||
67 | result = ps3_repository_find_interrupt(repo, | ||
68 | PS3_INTERRUPT_TYPE_EVENT_PORT, &p->dev.interrupt_id); | ||
69 | |||
70 | if (result) { | ||
71 | pr_debug("%s:%d ps3_repository_find_interrupt failed\n", | ||
72 | __func__, __LINE__); | ||
73 | goto fail_find_interrupt; | ||
74 | } | ||
75 | |||
76 | BUG_ON(p->dev.interrupt_id != 0); | ||
77 | |||
78 | result = ps3_dma_region_init(&p->dev, p->dev.d_region, PS3_DMA_64K, | ||
79 | PS3_DMA_OTHER, NULL, 0); | ||
80 | |||
81 | if (result) { | ||
82 | pr_debug("%s:%d ps3_dma_region_init failed\n", | ||
83 | __func__, __LINE__); | ||
84 | goto fail_dma_init; | ||
85 | } | ||
86 | |||
87 | result = ps3_system_bus_device_register(&p->dev); | ||
88 | |||
89 | if (result) { | ||
90 | pr_debug("%s:%d ps3_system_bus_device_register failed\n", | ||
91 | __func__, __LINE__); | ||
92 | goto fail_device_register; | ||
93 | } | ||
94 | |||
95 | pr_debug(" <- %s:%d\n", __func__, __LINE__); | ||
96 | return result; | ||
97 | |||
98 | fail_device_register: | ||
99 | fail_dma_init: | ||
100 | fail_find_interrupt: | ||
101 | kfree(p); | ||
102 | fail_malloc: | ||
103 | pr_debug(" <- %s:%d: fail.\n", __func__, __LINE__); | ||
104 | return result; | ||
105 | } | ||
106 | |||
107 | static int __init_refok ps3_setup_uhc_device( | ||
108 | const struct ps3_repository_device *repo, enum ps3_match_id match_id, | ||
109 | enum ps3_interrupt_type interrupt_type, enum ps3_reg_type reg_type) | ||
110 | { | ||
111 | int result; | ||
112 | struct layout { | ||
113 | struct ps3_system_bus_device dev; | ||
114 | struct ps3_dma_region d_region; | ||
115 | struct ps3_mmio_region m_region; | ||
116 | } *p; | ||
117 | u64 bus_addr; | ||
118 | u64 len; | ||
119 | |||
120 | pr_debug(" -> %s:%d\n", __func__, __LINE__); | ||
121 | |||
122 | BUG_ON(repo->bus_type != PS3_BUS_TYPE_SB); | ||
123 | BUG_ON(repo->dev_type != PS3_DEV_TYPE_SB_USB); | ||
124 | |||
125 | p = kzalloc(sizeof(struct layout), GFP_KERNEL); | ||
126 | |||
127 | if (!p) { | ||
128 | result = -ENOMEM; | ||
129 | goto fail_malloc; | ||
130 | } | ||
131 | |||
132 | p->dev.match_id = match_id; | ||
133 | p->dev.dev_type = PS3_DEVICE_TYPE_SB; | ||
134 | p->dev.bus_id = repo->bus_id; | ||
135 | p->dev.dev_id = repo->dev_id; | ||
136 | p->dev.d_region = &p->d_region; | ||
137 | p->dev.m_region = &p->m_region; | ||
138 | |||
139 | result = ps3_repository_find_interrupt(repo, | ||
140 | interrupt_type, &p->dev.interrupt_id); | ||
141 | |||
142 | if (result) { | ||
143 | pr_debug("%s:%d ps3_repository_find_interrupt failed\n", | ||
144 | __func__, __LINE__); | ||
145 | goto fail_find_interrupt; | ||
146 | } | ||
147 | |||
148 | result = ps3_repository_find_reg(repo, reg_type, | ||
149 | &bus_addr, &len); | ||
150 | |||
151 | if (result) { | ||
152 | pr_debug("%s:%d ps3_repository_find_reg failed\n", | ||
153 | __func__, __LINE__); | ||
154 | goto fail_find_reg; | ||
155 | } | ||
156 | |||
157 | result = ps3_dma_region_init(&p->dev, p->dev.d_region, PS3_DMA_64K, | ||
158 | PS3_DMA_INTERNAL, NULL, 0); | ||
159 | |||
160 | if (result) { | ||
161 | pr_debug("%s:%d ps3_dma_region_init failed\n", | ||
162 | __func__, __LINE__); | ||
163 | goto fail_dma_init; | ||
164 | } | ||
165 | |||
166 | result = ps3_mmio_region_init(&p->dev, p->dev.m_region, bus_addr, len, | ||
167 | PS3_MMIO_4K); | ||
168 | |||
169 | if (result) { | ||
170 | pr_debug("%s:%d ps3_mmio_region_init failed\n", | ||
171 | __func__, __LINE__); | ||
172 | goto fail_mmio_init; | ||
173 | } | ||
174 | |||
175 | result = ps3_system_bus_device_register(&p->dev); | ||
176 | |||
177 | if (result) { | ||
178 | pr_debug("%s:%d ps3_system_bus_device_register failed\n", | ||
179 | __func__, __LINE__); | ||
180 | goto fail_device_register; | ||
181 | } | ||
182 | |||
183 | pr_debug(" <- %s:%d\n", __func__, __LINE__); | ||
184 | return result; | ||
185 | |||
186 | fail_device_register: | ||
187 | fail_mmio_init: | ||
188 | fail_dma_init: | ||
189 | fail_find_reg: | ||
190 | fail_find_interrupt: | ||
191 | kfree(p); | ||
192 | fail_malloc: | ||
193 | pr_debug(" <- %s:%d: fail.\n", __func__, __LINE__); | ||
194 | return result; | ||
195 | } | ||
196 | |||
197 | static int __init ps3_setup_ehci_device( | ||
198 | const struct ps3_repository_device *repo) | ||
199 | { | ||
200 | return ps3_setup_uhc_device(repo, PS3_MATCH_ID_EHCI, | ||
201 | PS3_INTERRUPT_TYPE_SB_EHCI, PS3_REG_TYPE_SB_EHCI); | ||
202 | } | ||
203 | |||
204 | static int __init ps3_setup_ohci_device( | ||
205 | const struct ps3_repository_device *repo) | ||
206 | { | ||
207 | return ps3_setup_uhc_device(repo, PS3_MATCH_ID_OHCI, | ||
208 | PS3_INTERRUPT_TYPE_SB_OHCI, PS3_REG_TYPE_SB_OHCI); | ||
209 | } | ||
210 | |||
211 | static int __init ps3_setup_vuart_device(enum ps3_match_id match_id, | ||
212 | unsigned int port_number) | ||
213 | { | ||
214 | int result; | ||
215 | struct layout { | ||
216 | struct ps3_system_bus_device dev; | ||
217 | } *p; | ||
218 | |||
219 | pr_debug(" -> %s:%d: match_id %u, port %u\n", __func__, __LINE__, | ||
220 | match_id, port_number); | ||
221 | |||
222 | p = kzalloc(sizeof(struct layout), GFP_KERNEL); | ||
223 | |||
224 | if (!p) | ||
225 | return -ENOMEM; | ||
226 | |||
227 | p->dev.match_id = match_id; | ||
228 | p->dev.dev_type = PS3_DEVICE_TYPE_VUART; | ||
229 | p->dev.port_number = port_number; | ||
230 | |||
231 | result = ps3_system_bus_device_register(&p->dev); | ||
232 | |||
233 | if (result) | ||
234 | pr_debug("%s:%d ps3_system_bus_device_register failed\n", | ||
235 | __func__, __LINE__); | ||
236 | |||
237 | pr_debug(" <- %s:%d\n", __func__, __LINE__); | ||
238 | return result; | ||
239 | } | ||
240 | |||
241 | static int ps3stor_wait_for_completion(u64 dev_id, u64 tag, | ||
242 | unsigned int timeout) | ||
243 | { | ||
244 | int result = -1; | ||
245 | unsigned int retries = 0; | ||
246 | u64 status; | ||
247 | |||
248 | for (retries = 0; retries < timeout; retries++) { | ||
249 | result = lv1_storage_check_async_status(dev_id, tag, &status); | ||
250 | if (!result) | ||
251 | break; | ||
252 | |||
253 | msleep(1); | ||
254 | } | ||
255 | |||
256 | if (result) | ||
257 | pr_debug("%s:%u: check_async_status: %s, status %lx\n", | ||
258 | __func__, __LINE__, ps3_result(result), status); | ||
259 | |||
260 | return result; | ||
261 | } | ||
262 | |||
263 | /** | ||
264 | * ps3_storage_wait_for_device - Wait for a storage device to become ready. | ||
265 | * @repo: The repository device to wait for. | ||
266 | * | ||
267 | * Uses the hypervisor's storage device notification mechanism to wait until | ||
268 | * a storage device is ready. The device notification mechanism uses a | ||
269 | * psuedo device (id = -1) to asynchronously notify the guest when storage | ||
270 | * devices become ready. The notification device has a block size of 512 | ||
271 | * bytes. | ||
272 | */ | ||
273 | |||
274 | static int ps3_storage_wait_for_device(const struct ps3_repository_device *repo) | ||
275 | { | ||
276 | int result; | ||
277 | const u64 notification_dev_id = (u64)-1LL; | ||
278 | const unsigned int timeout = HZ; | ||
279 | u64 lpar; | ||
280 | u64 tag; | ||
281 | struct { | ||
282 | u64 operation_code; /* must be zero */ | ||
283 | u64 event_mask; /* 1 = device ready */ | ||
284 | } *notify_cmd; | ||
285 | struct { | ||
286 | u64 event_type; /* notify_device_ready */ | ||
287 | u64 bus_id; | ||
288 | u64 dev_id; | ||
289 | u64 dev_type; | ||
290 | u64 dev_port; | ||
291 | } *notify_event; | ||
292 | enum { | ||
293 | notify_device_ready = 1 | ||
294 | }; | ||
295 | |||
296 | pr_debug(" -> %s:%u: bus_id %u, dev_id %u, dev_type %u\n", __func__, | ||
297 | __LINE__, repo->bus_id, repo->dev_id, repo->dev_type); | ||
298 | |||
299 | notify_cmd = kzalloc(512, GFP_KERNEL); | ||
300 | notify_event = (void *)notify_cmd; | ||
301 | if (!notify_cmd) | ||
302 | return -ENOMEM; | ||
303 | |||
304 | lpar = ps3_mm_phys_to_lpar(__pa(notify_cmd)); | ||
305 | |||
306 | result = lv1_open_device(repo->bus_id, notification_dev_id, 0); | ||
307 | if (result) { | ||
308 | printk(KERN_ERR "%s:%u: lv1_open_device %s\n", __func__, | ||
309 | __LINE__, ps3_result(result)); | ||
310 | result = -ENODEV; | ||
311 | goto fail_free; | ||
312 | } | ||
313 | |||
314 | /* Setup and write the request for device notification. */ | ||
315 | |||
316 | notify_cmd->operation_code = 0; /* must be zero */ | ||
317 | notify_cmd->event_mask = 0x01; /* device ready */ | ||
318 | |||
319 | result = lv1_storage_write(notification_dev_id, 0, 0, 1, 0, lpar, | ||
320 | &tag); | ||
321 | if (result) { | ||
322 | printk(KERN_ERR "%s:%u: write failed %s\n", __func__, __LINE__, | ||
323 | ps3_result(result)); | ||
324 | result = -ENODEV; | ||
325 | goto fail_close; | ||
326 | } | ||
327 | |||
328 | /* Wait for the write completion */ | ||
329 | |||
330 | result = ps3stor_wait_for_completion(notification_dev_id, tag, | ||
331 | timeout); | ||
332 | if (result) { | ||
333 | printk(KERN_ERR "%s:%u: write not completed %s\n", __func__, | ||
334 | __LINE__, ps3_result(result)); | ||
335 | result = -ENODEV; | ||
336 | goto fail_close; | ||
337 | } | ||
338 | |||
339 | /* Loop here processing the requested notification events. */ | ||
340 | |||
341 | result = -ENODEV; | ||
342 | while (1) { | ||
343 | memset(notify_event, 0, sizeof(*notify_event)); | ||
344 | |||
345 | result = lv1_storage_read(notification_dev_id, 0, 0, 1, 0, | ||
346 | lpar, &tag); | ||
347 | if (result) { | ||
348 | printk(KERN_ERR "%s:%u: write failed %s\n", __func__, | ||
349 | __LINE__, ps3_result(result)); | ||
350 | break; | ||
351 | } | ||
352 | |||
353 | result = ps3stor_wait_for_completion(notification_dev_id, tag, | ||
354 | timeout); | ||
355 | if (result) { | ||
356 | printk(KERN_ERR "%s:%u: read not completed %s\n", | ||
357 | __func__, __LINE__, ps3_result(result)); | ||
358 | break; | ||
359 | } | ||
360 | |||
361 | if (notify_event->event_type != notify_device_ready || | ||
362 | notify_event->bus_id != repo->bus_id) { | ||
363 | pr_debug("%s:%u: bad notify_event: event %lu, " | ||
364 | "dev_id %lu, dev_type %lu\n", | ||
365 | __func__, __LINE__, notify_event->event_type, | ||
366 | notify_event->dev_id, notify_event->dev_type); | ||
367 | break; | ||
368 | } | ||
369 | |||
370 | if (notify_event->dev_id == repo->dev_id && | ||
371 | notify_event->dev_type == repo->dev_type) { | ||
372 | pr_debug("%s:%u: device ready: dev_id %u\n", __func__, | ||
373 | __LINE__, repo->dev_id); | ||
374 | result = 0; | ||
375 | break; | ||
376 | } | ||
377 | |||
378 | if (notify_event->dev_id == repo->dev_id && | ||
379 | notify_event->dev_type == PS3_DEV_TYPE_NOACCESS) { | ||
380 | pr_debug("%s:%u: no access: dev_id %u\n", __func__, | ||
381 | __LINE__, repo->dev_id); | ||
382 | break; | ||
383 | } | ||
384 | } | ||
385 | |||
386 | fail_close: | ||
387 | lv1_close_device(repo->bus_id, notification_dev_id); | ||
388 | fail_free: | ||
389 | kfree(notify_cmd); | ||
390 | pr_debug(" <- %s:%u\n", __func__, __LINE__); | ||
391 | return result; | ||
392 | } | ||
393 | |||
394 | static int ps3_setup_storage_dev(const struct ps3_repository_device *repo, | ||
395 | enum ps3_match_id match_id) | ||
396 | { | ||
397 | int result; | ||
398 | struct ps3_storage_device *p; | ||
399 | u64 port, blk_size, num_blocks; | ||
400 | unsigned int num_regions, i; | ||
401 | |||
402 | pr_debug(" -> %s:%u: match_id %u\n", __func__, __LINE__, match_id); | ||
403 | |||
404 | result = ps3_repository_read_stor_dev_info(repo->bus_index, | ||
405 | repo->dev_index, &port, | ||
406 | &blk_size, &num_blocks, | ||
407 | &num_regions); | ||
408 | if (result) { | ||
409 | printk(KERN_ERR "%s:%u: _read_stor_dev_info failed %d\n", | ||
410 | __func__, __LINE__, result); | ||
411 | return -ENODEV; | ||
412 | } | ||
413 | |||
414 | pr_debug("%s:%u: index %u:%u: port %lu blk_size %lu num_blocks %lu " | ||
415 | "num_regions %u\n", __func__, __LINE__, repo->bus_index, | ||
416 | repo->dev_index, port, blk_size, num_blocks, num_regions); | ||
417 | |||
418 | p = kzalloc(sizeof(struct ps3_storage_device) + | ||
419 | num_regions * sizeof(struct ps3_storage_region), | ||
420 | GFP_KERNEL); | ||
421 | if (!p) { | ||
422 | result = -ENOMEM; | ||
423 | goto fail_malloc; | ||
424 | } | ||
425 | |||
426 | p->sbd.match_id = match_id; | ||
427 | p->sbd.dev_type = PS3_DEVICE_TYPE_SB; | ||
428 | p->sbd.bus_id = repo->bus_id; | ||
429 | p->sbd.dev_id = repo->dev_id; | ||
430 | p->sbd.d_region = &p->dma_region; | ||
431 | p->blk_size = blk_size; | ||
432 | p->num_regions = num_regions; | ||
433 | |||
434 | result = ps3_repository_find_interrupt(repo, | ||
435 | PS3_INTERRUPT_TYPE_EVENT_PORT, | ||
436 | &p->sbd.interrupt_id); | ||
437 | if (result) { | ||
438 | printk(KERN_ERR "%s:%u: find_interrupt failed %d\n", __func__, | ||
439 | __LINE__, result); | ||
440 | result = -ENODEV; | ||
441 | goto fail_find_interrupt; | ||
442 | } | ||
443 | |||
444 | /* FIXME: Arrange to only do this on a 'cold' boot */ | ||
445 | |||
446 | result = ps3_storage_wait_for_device(repo); | ||
447 | if (result) { | ||
448 | printk(KERN_ERR "%s:%u: storage_notification failed %d\n", | ||
449 | __func__, __LINE__, result); | ||
450 | result = -ENODEV; | ||
451 | goto fail_probe_notification; | ||
452 | } | ||
453 | |||
454 | for (i = 0; i < num_regions; i++) { | ||
455 | unsigned int id; | ||
456 | u64 start, size; | ||
457 | |||
458 | result = ps3_repository_read_stor_dev_region(repo->bus_index, | ||
459 | repo->dev_index, | ||
460 | i, &id, &start, | ||
461 | &size); | ||
462 | if (result) { | ||
463 | printk(KERN_ERR | ||
464 | "%s:%u: read_stor_dev_region failed %d\n", | ||
465 | __func__, __LINE__, result); | ||
466 | result = -ENODEV; | ||
467 | goto fail_read_region; | ||
468 | } | ||
469 | pr_debug("%s:%u: region %u: id %u start %lu size %lu\n", | ||
470 | __func__, __LINE__, i, id, start, size); | ||
471 | |||
472 | p->regions[i].id = id; | ||
473 | p->regions[i].start = start; | ||
474 | p->regions[i].size = size; | ||
475 | } | ||
476 | |||
477 | result = ps3_system_bus_device_register(&p->sbd); | ||
478 | if (result) { | ||
479 | pr_debug("%s:%u ps3_system_bus_device_register failed\n", | ||
480 | __func__, __LINE__); | ||
481 | goto fail_device_register; | ||
482 | } | ||
483 | |||
484 | pr_debug(" <- %s:%u\n", __func__, __LINE__); | ||
485 | return 0; | ||
486 | |||
487 | fail_device_register: | ||
488 | fail_read_region: | ||
489 | fail_probe_notification: | ||
490 | fail_find_interrupt: | ||
491 | kfree(p); | ||
492 | fail_malloc: | ||
493 | pr_debug(" <- %s:%u: fail.\n", __func__, __LINE__); | ||
494 | return result; | ||
495 | } | ||
496 | |||
497 | static int __init ps3_register_vuart_devices(void) | ||
498 | { | ||
499 | int result; | ||
500 | unsigned int port_number; | ||
501 | |||
502 | pr_debug(" -> %s:%d\n", __func__, __LINE__); | ||
503 | |||
504 | result = ps3_repository_read_vuart_av_port(&port_number); | ||
505 | if (result) | ||
506 | port_number = 0; /* av default */ | ||
507 | |||
508 | result = ps3_setup_vuart_device(PS3_MATCH_ID_AV_SETTINGS, port_number); | ||
509 | WARN_ON(result); | ||
510 | |||
511 | result = ps3_repository_read_vuart_sysmgr_port(&port_number); | ||
512 | if (result) | ||
513 | port_number = 2; /* sysmgr default */ | ||
514 | |||
515 | result = ps3_setup_vuart_device(PS3_MATCH_ID_SYSTEM_MANAGER, | ||
516 | port_number); | ||
517 | WARN_ON(result); | ||
518 | |||
519 | pr_debug(" <- %s:%d\n", __func__, __LINE__); | ||
520 | return result; | ||
521 | } | ||
522 | |||
523 | static int __init ps3_register_sound_devices(void) | ||
524 | { | ||
525 | int result; | ||
526 | struct layout { | ||
527 | struct ps3_system_bus_device dev; | ||
528 | struct ps3_dma_region d_region; | ||
529 | struct ps3_mmio_region m_region; | ||
530 | } *p; | ||
531 | |||
532 | pr_debug(" -> %s:%d\n", __func__, __LINE__); | ||
533 | |||
534 | p = kzalloc(sizeof(*p), GFP_KERNEL); | ||
535 | if (!p) | ||
536 | return -ENOMEM; | ||
537 | |||
538 | p->dev.match_id = PS3_MATCH_ID_SOUND; | ||
539 | p->dev.dev_type = PS3_DEVICE_TYPE_IOC0; | ||
540 | p->dev.d_region = &p->d_region; | ||
541 | p->dev.m_region = &p->m_region; | ||
542 | |||
543 | result = ps3_system_bus_device_register(&p->dev); | ||
544 | |||
545 | if (result) | ||
546 | pr_debug("%s:%d ps3_system_bus_device_register failed\n", | ||
547 | __func__, __LINE__); | ||
548 | |||
549 | pr_debug(" <- %s:%d\n", __func__, __LINE__); | ||
550 | return result; | ||
551 | } | ||
552 | |||
553 | static int __init ps3_register_graphics_devices(void) | ||
554 | { | ||
555 | int result; | ||
556 | struct layout { | ||
557 | struct ps3_system_bus_device dev; | ||
558 | } *p; | ||
559 | |||
560 | pr_debug(" -> %s:%d\n", __func__, __LINE__); | ||
561 | |||
562 | p = kzalloc(sizeof(struct layout), GFP_KERNEL); | ||
563 | |||
564 | if (!p) | ||
565 | return -ENOMEM; | ||
566 | |||
567 | p->dev.match_id = PS3_MATCH_ID_GRAPHICS; | ||
568 | p->dev.dev_type = PS3_DEVICE_TYPE_IOC0; | ||
569 | |||
570 | result = ps3_system_bus_device_register(&p->dev); | ||
571 | |||
572 | if (result) | ||
573 | pr_debug("%s:%d ps3_system_bus_device_register failed\n", | ||
574 | __func__, __LINE__); | ||
575 | |||
576 | pr_debug(" <- %s:%d\n", __func__, __LINE__); | ||
577 | return result; | ||
578 | } | ||
579 | |||
580 | /** | ||
581 | * ps3_register_repository_device - Register a device from the repositiory info. | ||
582 | * | ||
583 | */ | ||
584 | |||
585 | static int ps3_register_repository_device( | ||
586 | const struct ps3_repository_device *repo) | ||
587 | { | ||
588 | int result; | ||
589 | |||
590 | switch (repo->dev_type) { | ||
591 | case PS3_DEV_TYPE_SB_GELIC: | ||
592 | result = ps3_setup_gelic_device(repo); | ||
593 | if (result) { | ||
594 | pr_debug("%s:%d ps3_setup_gelic_device failed\n", | ||
595 | __func__, __LINE__); | ||
596 | } | ||
597 | break; | ||
598 | case PS3_DEV_TYPE_SB_USB: | ||
599 | |||
600 | /* Each USB device has both an EHCI and an OHCI HC */ | ||
601 | |||
602 | result = ps3_setup_ehci_device(repo); | ||
603 | |||
604 | if (result) { | ||
605 | pr_debug("%s:%d ps3_setup_ehci_device failed\n", | ||
606 | __func__, __LINE__); | ||
607 | } | ||
608 | |||
609 | result = ps3_setup_ohci_device(repo); | ||
610 | |||
611 | if (result) { | ||
612 | pr_debug("%s:%d ps3_setup_ohci_device failed\n", | ||
613 | __func__, __LINE__); | ||
614 | } | ||
615 | break; | ||
616 | case PS3_DEV_TYPE_STOR_DISK: | ||
617 | result = ps3_setup_storage_dev(repo, PS3_MATCH_ID_STOR_DISK); | ||
618 | |||
619 | /* Some devices are not accessable from the Other OS lpar. */ | ||
620 | if (result == -ENODEV) { | ||
621 | result = 0; | ||
622 | pr_debug("%s:%u: not accessable\n", __func__, | ||
623 | __LINE__); | ||
624 | } | ||
625 | |||
626 | if (result) | ||
627 | pr_debug("%s:%u ps3_setup_storage_dev failed\n", | ||
628 | __func__, __LINE__); | ||
629 | break; | ||
630 | |||
631 | case PS3_DEV_TYPE_STOR_ROM: | ||
632 | result = ps3_setup_storage_dev(repo, PS3_MATCH_ID_STOR_ROM); | ||
633 | if (result) | ||
634 | pr_debug("%s:%u ps3_setup_storage_dev failed\n", | ||
635 | __func__, __LINE__); | ||
636 | break; | ||
637 | |||
638 | case PS3_DEV_TYPE_STOR_FLASH: | ||
639 | result = ps3_setup_storage_dev(repo, PS3_MATCH_ID_STOR_FLASH); | ||
640 | if (result) | ||
641 | pr_debug("%s:%u ps3_setup_storage_dev failed\n", | ||
642 | __func__, __LINE__); | ||
643 | break; | ||
644 | |||
645 | default: | ||
646 | result = 0; | ||
647 | pr_debug("%s:%u: unsupported dev_type %u\n", __func__, __LINE__, | ||
648 | repo->dev_type); | ||
649 | } | ||
650 | |||
651 | return result; | ||
652 | } | ||
653 | |||
654 | /** | ||
655 | * ps3_probe_thread - Background repository probing at system startup. | ||
656 | * | ||
657 | * This implementation only supports background probing on a single bus. | ||
658 | */ | ||
659 | |||
660 | static int ps3_probe_thread(void *data) | ||
661 | { | ||
662 | struct ps3_repository_device *repo = data; | ||
663 | int result; | ||
664 | unsigned int ms = 250; | ||
665 | |||
666 | pr_debug(" -> %s:%u: kthread started\n", __func__, __LINE__); | ||
667 | |||
668 | do { | ||
669 | try_to_freeze(); | ||
670 | |||
671 | pr_debug("%s:%u: probing...\n", __func__, __LINE__); | ||
672 | |||
673 | do { | ||
674 | result = ps3_repository_find_device(repo); | ||
675 | |||
676 | if (result == -ENODEV) | ||
677 | pr_debug("%s:%u: nothing new\n", __func__, | ||
678 | __LINE__); | ||
679 | else if (result) | ||
680 | pr_debug("%s:%u: find device error.\n", | ||
681 | __func__, __LINE__); | ||
682 | else { | ||
683 | pr_debug("%s:%u: found device\n", __func__, | ||
684 | __LINE__); | ||
685 | ps3_register_repository_device(repo); | ||
686 | ps3_repository_bump_device(repo); | ||
687 | ms = 250; | ||
688 | } | ||
689 | } while (!result); | ||
690 | |||
691 | pr_debug("%s:%u: ms %u\n", __func__, __LINE__, ms); | ||
692 | |||
693 | if ( ms > 60000) | ||
694 | break; | ||
695 | |||
696 | msleep_interruptible(ms); | ||
697 | |||
698 | /* An exponential backoff. */ | ||
699 | ms <<= 1; | ||
700 | |||
701 | } while (!kthread_should_stop()); | ||
702 | |||
703 | pr_debug(" <- %s:%u: kthread finished\n", __func__, __LINE__); | ||
704 | |||
705 | return 0; | ||
706 | } | ||
707 | |||
708 | /** | ||
709 | * ps3_start_probe_thread - Starts the background probe thread. | ||
710 | * | ||
711 | */ | ||
712 | |||
713 | static int __init ps3_start_probe_thread(enum ps3_bus_type bus_type) | ||
714 | { | ||
715 | int result; | ||
716 | struct task_struct *task; | ||
717 | static struct ps3_repository_device repo; /* must be static */ | ||
718 | |||
719 | pr_debug(" -> %s:%d\n", __func__, __LINE__); | ||
720 | |||
721 | memset(&repo, 0, sizeof(repo)); | ||
722 | |||
723 | repo.bus_type = bus_type; | ||
724 | |||
725 | result = ps3_repository_find_bus(repo.bus_type, 0, &repo.bus_index); | ||
726 | |||
727 | if (result) { | ||
728 | printk(KERN_ERR "%s: Cannot find bus (%d)\n", __func__, result); | ||
729 | return -ENODEV; | ||
730 | } | ||
731 | |||
732 | result = ps3_repository_read_bus_id(repo.bus_index, &repo.bus_id); | ||
733 | |||
734 | if (result) { | ||
735 | printk(KERN_ERR "%s: read_bus_id failed %d\n", __func__, | ||
736 | result); | ||
737 | return -ENODEV; | ||
738 | } | ||
739 | |||
740 | task = kthread_run(ps3_probe_thread, &repo, "ps3-probe-%u", bus_type); | ||
741 | |||
742 | if (IS_ERR(task)) { | ||
743 | result = PTR_ERR(task); | ||
744 | printk(KERN_ERR "%s: kthread_run failed %d\n", __func__, | ||
745 | result); | ||
746 | return result; | ||
747 | } | ||
748 | |||
749 | pr_debug(" <- %s:%d\n", __func__, __LINE__); | ||
750 | return 0; | ||
751 | } | ||
752 | |||
753 | /** | ||
754 | * ps3_register_devices - Probe the system and register devices found. | ||
755 | * | ||
756 | * A device_initcall() routine. | ||
757 | */ | ||
758 | |||
759 | static int __init ps3_register_devices(void) | ||
760 | { | ||
761 | int result; | ||
762 | |||
763 | if (!firmware_has_feature(FW_FEATURE_PS3_LV1)) | ||
764 | return -ENODEV; | ||
765 | |||
766 | pr_debug(" -> %s:%d\n", __func__, __LINE__); | ||
767 | |||
768 | /* ps3_repository_dump_bus_info(); */ | ||
769 | |||
770 | result = ps3_start_probe_thread(PS3_BUS_TYPE_STORAGE); | ||
771 | |||
772 | ps3_register_vuart_devices(); | ||
773 | |||
774 | ps3_register_graphics_devices(); | ||
775 | |||
776 | ps3_repository_find_devices(PS3_BUS_TYPE_SB, | ||
777 | ps3_register_repository_device); | ||
778 | |||
779 | ps3_register_sound_devices(); | ||
780 | |||
781 | pr_debug(" <- %s:%d\n", __func__, __LINE__); | ||
782 | return 0; | ||
783 | } | ||
784 | |||
785 | device_initcall(ps3_register_devices); | ||