Commit | Line | Data |
---|---|---|
ffbdd246 GL |
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> | |
a5c631b1 | 29 | #include <asm/ps3stor.h> |
ffbdd246 GL |
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 | ||
a5c631b1 GU |
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 | { | |
edd2a9d1 | 276 | int error = -ENODEV; |
a5c631b1 GU |
277 | int result; |
278 | const u64 notification_dev_id = (u64)-1LL; | |
279 | const unsigned int timeout = HZ; | |
280 | u64 lpar; | |
281 | u64 tag; | |
edd2a9d1 GU |
282 | void *buf; |
283 | enum ps3_notify_type { | |
284 | notify_device_ready = 0, | |
285 | notify_region_probe = 1, | |
286 | notify_region_update = 2, | |
287 | }; | |
a5c631b1 GU |
288 | struct { |
289 | u64 operation_code; /* must be zero */ | |
edd2a9d1 | 290 | u64 event_mask; /* OR of 1UL << enum ps3_notify_type */ |
a5c631b1 GU |
291 | } *notify_cmd; |
292 | struct { | |
edd2a9d1 | 293 | u64 event_type; /* enum ps3_notify_type */ |
a5c631b1 GU |
294 | u64 bus_id; |
295 | u64 dev_id; | |
296 | u64 dev_type; | |
297 | u64 dev_port; | |
298 | } *notify_event; | |
a5c631b1 GU |
299 | |
300 | pr_debug(" -> %s:%u: bus_id %u, dev_id %u, dev_type %u\n", __func__, | |
301 | __LINE__, repo->bus_id, repo->dev_id, repo->dev_type); | |
302 | ||
edd2a9d1 GU |
303 | buf = kzalloc(512, GFP_KERNEL); |
304 | if (!buf) | |
a5c631b1 GU |
305 | return -ENOMEM; |
306 | ||
edd2a9d1 GU |
307 | lpar = ps3_mm_phys_to_lpar(__pa(buf)); |
308 | notify_cmd = buf; | |
309 | notify_event = buf; | |
a5c631b1 GU |
310 | |
311 | result = lv1_open_device(repo->bus_id, notification_dev_id, 0); | |
312 | if (result) { | |
313 | printk(KERN_ERR "%s:%u: lv1_open_device %s\n", __func__, | |
314 | __LINE__, ps3_result(result)); | |
a5c631b1 GU |
315 | goto fail_free; |
316 | } | |
317 | ||
318 | /* Setup and write the request for device notification. */ | |
319 | ||
edd2a9d1 GU |
320 | notify_cmd->operation_code = 0; /* must be zero */ |
321 | notify_cmd->event_mask = 1UL << notify_region_probe; | |
a5c631b1 GU |
322 | |
323 | result = lv1_storage_write(notification_dev_id, 0, 0, 1, 0, lpar, | |
324 | &tag); | |
325 | if (result) { | |
326 | printk(KERN_ERR "%s:%u: write failed %s\n", __func__, __LINE__, | |
327 | ps3_result(result)); | |
a5c631b1 GU |
328 | goto fail_close; |
329 | } | |
330 | ||
331 | /* Wait for the write completion */ | |
332 | ||
333 | result = ps3stor_wait_for_completion(notification_dev_id, tag, | |
334 | timeout); | |
335 | if (result) { | |
336 | printk(KERN_ERR "%s:%u: write not completed %s\n", __func__, | |
337 | __LINE__, ps3_result(result)); | |
a5c631b1 GU |
338 | goto fail_close; |
339 | } | |
340 | ||
341 | /* Loop here processing the requested notification events. */ | |
342 | ||
a5c631b1 GU |
343 | while (1) { |
344 | memset(notify_event, 0, sizeof(*notify_event)); | |
345 | ||
346 | result = lv1_storage_read(notification_dev_id, 0, 0, 1, 0, | |
347 | lpar, &tag); | |
348 | if (result) { | |
349 | printk(KERN_ERR "%s:%u: write failed %s\n", __func__, | |
350 | __LINE__, ps3_result(result)); | |
351 | break; | |
352 | } | |
353 | ||
354 | result = ps3stor_wait_for_completion(notification_dev_id, tag, | |
355 | timeout); | |
356 | if (result) { | |
357 | printk(KERN_ERR "%s:%u: read not completed %s\n", | |
358 | __func__, __LINE__, ps3_result(result)); | |
359 | break; | |
360 | } | |
361 | ||
edd2a9d1 | 362 | if (notify_event->event_type != notify_region_probe || |
a5c631b1 GU |
363 | notify_event->bus_id != repo->bus_id) { |
364 | pr_debug("%s:%u: bad notify_event: event %lu, " | |
365 | "dev_id %lu, dev_type %lu\n", | |
366 | __func__, __LINE__, notify_event->event_type, | |
367 | notify_event->dev_id, notify_event->dev_type); | |
368 | break; | |
369 | } | |
370 | ||
371 | if (notify_event->dev_id == repo->dev_id && | |
372 | notify_event->dev_type == repo->dev_type) { | |
373 | pr_debug("%s:%u: device ready: dev_id %u\n", __func__, | |
374 | __LINE__, repo->dev_id); | |
2de69124 | 375 | error = 0; |
a5c631b1 GU |
376 | break; |
377 | } | |
378 | ||
379 | if (notify_event->dev_id == repo->dev_id && | |
380 | notify_event->dev_type == PS3_DEV_TYPE_NOACCESS) { | |
381 | pr_debug("%s:%u: no access: dev_id %u\n", __func__, | |
382 | __LINE__, repo->dev_id); | |
383 | break; | |
384 | } | |
385 | } | |
386 | ||
387 | fail_close: | |
388 | lv1_close_device(repo->bus_id, notification_dev_id); | |
389 | fail_free: | |
edd2a9d1 | 390 | kfree(buf); |
a5c631b1 | 391 | pr_debug(" <- %s:%u\n", __func__, __LINE__); |
edd2a9d1 | 392 | return error; |
a5c631b1 GU |
393 | } |
394 | ||
395 | static int ps3_setup_storage_dev(const struct ps3_repository_device *repo, | |
396 | enum ps3_match_id match_id) | |
397 | { | |
398 | int result; | |
399 | struct ps3_storage_device *p; | |
400 | u64 port, blk_size, num_blocks; | |
401 | unsigned int num_regions, i; | |
402 | ||
403 | pr_debug(" -> %s:%u: match_id %u\n", __func__, __LINE__, match_id); | |
404 | ||
405 | result = ps3_repository_read_stor_dev_info(repo->bus_index, | |
406 | repo->dev_index, &port, | |
407 | &blk_size, &num_blocks, | |
408 | &num_regions); | |
409 | if (result) { | |
410 | printk(KERN_ERR "%s:%u: _read_stor_dev_info failed %d\n", | |
411 | __func__, __LINE__, result); | |
412 | return -ENODEV; | |
413 | } | |
414 | ||
415 | pr_debug("%s:%u: index %u:%u: port %lu blk_size %lu num_blocks %lu " | |
416 | "num_regions %u\n", __func__, __LINE__, repo->bus_index, | |
417 | repo->dev_index, port, blk_size, num_blocks, num_regions); | |
418 | ||
419 | p = kzalloc(sizeof(struct ps3_storage_device) + | |
420 | num_regions * sizeof(struct ps3_storage_region), | |
421 | GFP_KERNEL); | |
422 | if (!p) { | |
423 | result = -ENOMEM; | |
424 | goto fail_malloc; | |
425 | } | |
426 | ||
427 | p->sbd.match_id = match_id; | |
428 | p->sbd.dev_type = PS3_DEVICE_TYPE_SB; | |
429 | p->sbd.bus_id = repo->bus_id; | |
430 | p->sbd.dev_id = repo->dev_id; | |
431 | p->sbd.d_region = &p->dma_region; | |
432 | p->blk_size = blk_size; | |
433 | p->num_regions = num_regions; | |
434 | ||
435 | result = ps3_repository_find_interrupt(repo, | |
436 | PS3_INTERRUPT_TYPE_EVENT_PORT, | |
437 | &p->sbd.interrupt_id); | |
438 | if (result) { | |
439 | printk(KERN_ERR "%s:%u: find_interrupt failed %d\n", __func__, | |
440 | __LINE__, result); | |
441 | result = -ENODEV; | |
442 | goto fail_find_interrupt; | |
443 | } | |
444 | ||
445 | /* FIXME: Arrange to only do this on a 'cold' boot */ | |
446 | ||
447 | result = ps3_storage_wait_for_device(repo); | |
448 | if (result) { | |
449 | printk(KERN_ERR "%s:%u: storage_notification failed %d\n", | |
450 | __func__, __LINE__, result); | |
451 | result = -ENODEV; | |
452 | goto fail_probe_notification; | |
453 | } | |
454 | ||
455 | for (i = 0; i < num_regions; i++) { | |
456 | unsigned int id; | |
457 | u64 start, size; | |
458 | ||
459 | result = ps3_repository_read_stor_dev_region(repo->bus_index, | |
460 | repo->dev_index, | |
461 | i, &id, &start, | |
462 | &size); | |
463 | if (result) { | |
464 | printk(KERN_ERR | |
465 | "%s:%u: read_stor_dev_region failed %d\n", | |
466 | __func__, __LINE__, result); | |
467 | result = -ENODEV; | |
468 | goto fail_read_region; | |
469 | } | |
470 | pr_debug("%s:%u: region %u: id %u start %lu size %lu\n", | |
471 | __func__, __LINE__, i, id, start, size); | |
472 | ||
473 | p->regions[i].id = id; | |
474 | p->regions[i].start = start; | |
475 | p->regions[i].size = size; | |
476 | } | |
477 | ||
478 | result = ps3_system_bus_device_register(&p->sbd); | |
479 | if (result) { | |
480 | pr_debug("%s:%u ps3_system_bus_device_register failed\n", | |
481 | __func__, __LINE__); | |
482 | goto fail_device_register; | |
483 | } | |
484 | ||
485 | pr_debug(" <- %s:%u\n", __func__, __LINE__); | |
486 | return 0; | |
487 | ||
488 | fail_device_register: | |
489 | fail_read_region: | |
490 | fail_probe_notification: | |
491 | fail_find_interrupt: | |
492 | kfree(p); | |
493 | fail_malloc: | |
494 | pr_debug(" <- %s:%u: fail.\n", __func__, __LINE__); | |
495 | return result; | |
496 | } | |
497 | ||
ffbdd246 GL |
498 | static int __init ps3_register_vuart_devices(void) |
499 | { | |
500 | int result; | |
501 | unsigned int port_number; | |
502 | ||
503 | pr_debug(" -> %s:%d\n", __func__, __LINE__); | |
504 | ||
505 | result = ps3_repository_read_vuart_av_port(&port_number); | |
506 | if (result) | |
507 | port_number = 0; /* av default */ | |
508 | ||
509 | result = ps3_setup_vuart_device(PS3_MATCH_ID_AV_SETTINGS, port_number); | |
510 | WARN_ON(result); | |
511 | ||
512 | result = ps3_repository_read_vuart_sysmgr_port(&port_number); | |
513 | if (result) | |
514 | port_number = 2; /* sysmgr default */ | |
515 | ||
516 | result = ps3_setup_vuart_device(PS3_MATCH_ID_SYSTEM_MANAGER, | |
517 | port_number); | |
518 | WARN_ON(result); | |
519 | ||
520 | pr_debug(" <- %s:%d\n", __func__, __LINE__); | |
521 | return result; | |
522 | } | |
523 | ||
524 | static int __init ps3_register_sound_devices(void) | |
525 | { | |
526 | int result; | |
527 | struct layout { | |
528 | struct ps3_system_bus_device dev; | |
529 | struct ps3_dma_region d_region; | |
530 | struct ps3_mmio_region m_region; | |
531 | } *p; | |
532 | ||
533 | pr_debug(" -> %s:%d\n", __func__, __LINE__); | |
534 | ||
535 | p = kzalloc(sizeof(*p), GFP_KERNEL); | |
536 | if (!p) | |
537 | return -ENOMEM; | |
538 | ||
539 | p->dev.match_id = PS3_MATCH_ID_SOUND; | |
540 | p->dev.dev_type = PS3_DEVICE_TYPE_IOC0; | |
541 | p->dev.d_region = &p->d_region; | |
542 | p->dev.m_region = &p->m_region; | |
543 | ||
544 | result = ps3_system_bus_device_register(&p->dev); | |
545 | ||
546 | if (result) | |
547 | pr_debug("%s:%d ps3_system_bus_device_register failed\n", | |
548 | __func__, __LINE__); | |
549 | ||
550 | pr_debug(" <- %s:%d\n", __func__, __LINE__); | |
551 | return result; | |
552 | } | |
553 | ||
554 | static int __init ps3_register_graphics_devices(void) | |
555 | { | |
556 | int result; | |
557 | struct layout { | |
558 | struct ps3_system_bus_device dev; | |
559 | } *p; | |
560 | ||
561 | pr_debug(" -> %s:%d\n", __func__, __LINE__); | |
562 | ||
563 | p = kzalloc(sizeof(struct layout), GFP_KERNEL); | |
564 | ||
565 | if (!p) | |
566 | return -ENOMEM; | |
567 | ||
568 | p->dev.match_id = PS3_MATCH_ID_GRAPHICS; | |
569 | p->dev.dev_type = PS3_DEVICE_TYPE_IOC0; | |
570 | ||
571 | result = ps3_system_bus_device_register(&p->dev); | |
572 | ||
573 | if (result) | |
574 | pr_debug("%s:%d ps3_system_bus_device_register failed\n", | |
575 | __func__, __LINE__); | |
576 | ||
577 | pr_debug(" <- %s:%d\n", __func__, __LINE__); | |
578 | return result; | |
579 | } | |
580 | ||
581 | /** | |
582 | * ps3_register_repository_device - Register a device from the repositiory info. | |
583 | * | |
584 | */ | |
585 | ||
586 | static int ps3_register_repository_device( | |
587 | const struct ps3_repository_device *repo) | |
588 | { | |
589 | int result; | |
590 | ||
591 | switch (repo->dev_type) { | |
592 | case PS3_DEV_TYPE_SB_GELIC: | |
593 | result = ps3_setup_gelic_device(repo); | |
594 | if (result) { | |
595 | pr_debug("%s:%d ps3_setup_gelic_device failed\n", | |
596 | __func__, __LINE__); | |
597 | } | |
598 | break; | |
599 | case PS3_DEV_TYPE_SB_USB: | |
600 | ||
601 | /* Each USB device has both an EHCI and an OHCI HC */ | |
602 | ||
603 | result = ps3_setup_ehci_device(repo); | |
604 | ||
605 | if (result) { | |
606 | pr_debug("%s:%d ps3_setup_ehci_device failed\n", | |
607 | __func__, __LINE__); | |
608 | } | |
609 | ||
610 | result = ps3_setup_ohci_device(repo); | |
611 | ||
612 | if (result) { | |
613 | pr_debug("%s:%d ps3_setup_ohci_device failed\n", | |
614 | __func__, __LINE__); | |
615 | } | |
616 | break; | |
a5c631b1 GU |
617 | case PS3_DEV_TYPE_STOR_DISK: |
618 | result = ps3_setup_storage_dev(repo, PS3_MATCH_ID_STOR_DISK); | |
619 | ||
620 | /* Some devices are not accessable from the Other OS lpar. */ | |
621 | if (result == -ENODEV) { | |
622 | result = 0; | |
623 | pr_debug("%s:%u: not accessable\n", __func__, | |
624 | __LINE__); | |
625 | } | |
626 | ||
627 | if (result) | |
628 | pr_debug("%s:%u ps3_setup_storage_dev failed\n", | |
629 | __func__, __LINE__); | |
630 | break; | |
631 | ||
632 | case PS3_DEV_TYPE_STOR_ROM: | |
633 | result = ps3_setup_storage_dev(repo, PS3_MATCH_ID_STOR_ROM); | |
634 | if (result) | |
635 | pr_debug("%s:%u ps3_setup_storage_dev failed\n", | |
636 | __func__, __LINE__); | |
637 | break; | |
638 | ||
639 | case PS3_DEV_TYPE_STOR_FLASH: | |
640 | result = ps3_setup_storage_dev(repo, PS3_MATCH_ID_STOR_FLASH); | |
641 | if (result) | |
642 | pr_debug("%s:%u ps3_setup_storage_dev failed\n", | |
643 | __func__, __LINE__); | |
644 | break; | |
645 | ||
ffbdd246 GL |
646 | default: |
647 | result = 0; | |
648 | pr_debug("%s:%u: unsupported dev_type %u\n", __func__, __LINE__, | |
649 | repo->dev_type); | |
650 | } | |
651 | ||
652 | return result; | |
653 | } | |
654 | ||
655 | /** | |
656 | * ps3_probe_thread - Background repository probing at system startup. | |
657 | * | |
658 | * This implementation only supports background probing on a single bus. | |
659 | */ | |
660 | ||
661 | static int ps3_probe_thread(void *data) | |
662 | { | |
663 | struct ps3_repository_device *repo = data; | |
664 | int result; | |
665 | unsigned int ms = 250; | |
666 | ||
667 | pr_debug(" -> %s:%u: kthread started\n", __func__, __LINE__); | |
668 | ||
669 | do { | |
670 | try_to_freeze(); | |
671 | ||
672 | pr_debug("%s:%u: probing...\n", __func__, __LINE__); | |
673 | ||
674 | do { | |
675 | result = ps3_repository_find_device(repo); | |
676 | ||
677 | if (result == -ENODEV) | |
678 | pr_debug("%s:%u: nothing new\n", __func__, | |
679 | __LINE__); | |
680 | else if (result) | |
681 | pr_debug("%s:%u: find device error.\n", | |
682 | __func__, __LINE__); | |
683 | else { | |
684 | pr_debug("%s:%u: found device\n", __func__, | |
685 | __LINE__); | |
686 | ps3_register_repository_device(repo); | |
687 | ps3_repository_bump_device(repo); | |
688 | ms = 250; | |
689 | } | |
690 | } while (!result); | |
691 | ||
692 | pr_debug("%s:%u: ms %u\n", __func__, __LINE__, ms); | |
693 | ||
694 | if ( ms > 60000) | |
695 | break; | |
696 | ||
697 | msleep_interruptible(ms); | |
698 | ||
699 | /* An exponential backoff. */ | |
700 | ms <<= 1; | |
701 | ||
702 | } while (!kthread_should_stop()); | |
703 | ||
704 | pr_debug(" <- %s:%u: kthread finished\n", __func__, __LINE__); | |
705 | ||
706 | return 0; | |
707 | } | |
708 | ||
709 | /** | |
710 | * ps3_start_probe_thread - Starts the background probe thread. | |
711 | * | |
712 | */ | |
713 | ||
714 | static int __init ps3_start_probe_thread(enum ps3_bus_type bus_type) | |
715 | { | |
716 | int result; | |
717 | struct task_struct *task; | |
718 | static struct ps3_repository_device repo; /* must be static */ | |
719 | ||
720 | pr_debug(" -> %s:%d\n", __func__, __LINE__); | |
721 | ||
722 | memset(&repo, 0, sizeof(repo)); | |
723 | ||
724 | repo.bus_type = bus_type; | |
725 | ||
726 | result = ps3_repository_find_bus(repo.bus_type, 0, &repo.bus_index); | |
727 | ||
728 | if (result) { | |
729 | printk(KERN_ERR "%s: Cannot find bus (%d)\n", __func__, result); | |
730 | return -ENODEV; | |
731 | } | |
732 | ||
733 | result = ps3_repository_read_bus_id(repo.bus_index, &repo.bus_id); | |
734 | ||
735 | if (result) { | |
736 | printk(KERN_ERR "%s: read_bus_id failed %d\n", __func__, | |
737 | result); | |
738 | return -ENODEV; | |
739 | } | |
740 | ||
741 | task = kthread_run(ps3_probe_thread, &repo, "ps3-probe-%u", bus_type); | |
742 | ||
743 | if (IS_ERR(task)) { | |
744 | result = PTR_ERR(task); | |
745 | printk(KERN_ERR "%s: kthread_run failed %d\n", __func__, | |
746 | result); | |
747 | return result; | |
748 | } | |
749 | ||
750 | pr_debug(" <- %s:%d\n", __func__, __LINE__); | |
751 | return 0; | |
752 | } | |
753 | ||
754 | /** | |
755 | * ps3_register_devices - Probe the system and register devices found. | |
756 | * | |
757 | * A device_initcall() routine. | |
758 | */ | |
759 | ||
760 | static int __init ps3_register_devices(void) | |
761 | { | |
762 | int result; | |
763 | ||
764 | if (!firmware_has_feature(FW_FEATURE_PS3_LV1)) | |
765 | return -ENODEV; | |
766 | ||
767 | pr_debug(" -> %s:%d\n", __func__, __LINE__); | |
768 | ||
769 | /* ps3_repository_dump_bus_info(); */ | |
770 | ||
771 | result = ps3_start_probe_thread(PS3_BUS_TYPE_STORAGE); | |
772 | ||
773 | ps3_register_vuart_devices(); | |
774 | ||
775 | ps3_register_graphics_devices(); | |
776 | ||
777 | ps3_repository_find_devices(PS3_BUS_TYPE_SB, | |
778 | ps3_register_repository_device); | |
779 | ||
780 | ps3_register_sound_devices(); | |
781 | ||
782 | pr_debug(" <- %s:%d\n", __func__, __LINE__); | |
783 | return 0; | |
784 | } | |
785 | ||
786 | device_initcall(ps3_register_devices); |