diff options
-rw-r--r-- | arch/powerpc/platforms/ps3/Makefile | 1 | ||||
-rw-r--r-- | arch/powerpc/platforms/ps3/device-init.c | 499 |
2 files changed, 500 insertions, 0 deletions
diff --git a/arch/powerpc/platforms/ps3/Makefile b/arch/powerpc/platforms/ps3/Makefile index a0048fcf0866..ac1bdf844eca 100644 --- a/arch/powerpc/platforms/ps3/Makefile +++ b/arch/powerpc/platforms/ps3/Makefile | |||
@@ -4,3 +4,4 @@ obj-y += system-bus.o | |||
4 | 4 | ||
5 | obj-$(CONFIG_SMP) += smp.o | 5 | obj-$(CONFIG_SMP) += smp.o |
6 | obj-$(CONFIG_SPU_BASE) += spu.o | 6 | obj-$(CONFIG_SPU_BASE) += spu.o |
7 | obj-y += device-init.o | ||
diff --git a/arch/powerpc/platforms/ps3/device-init.c b/arch/powerpc/platforms/ps3/device-init.c new file mode 100644 index 000000000000..864f313be8de --- /dev/null +++ b/arch/powerpc/platforms/ps3/device-init.c | |||
@@ -0,0 +1,499 @@ | |||
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 | |||
30 | #include "platform.h" | ||
31 | |||
32 | /** | ||
33 | * ps3_setup_gelic_device - Setup and register a gelic device instance. | ||
34 | * | ||
35 | * Allocates memory for a struct ps3_system_bus_device instance, initialises the | ||
36 | * structure members, and registers the device instance with the system bus. | ||
37 | */ | ||
38 | |||
39 | static int __init ps3_setup_gelic_device( | ||
40 | const struct ps3_repository_device *repo) | ||
41 | { | ||
42 | int result; | ||
43 | struct layout { | ||
44 | struct ps3_system_bus_device dev; | ||
45 | struct ps3_dma_region d_region; | ||
46 | } *p; | ||
47 | |||
48 | pr_debug(" -> %s:%d\n", __func__, __LINE__); | ||
49 | |||
50 | BUG_ON(repo->bus_type != PS3_BUS_TYPE_SB); | ||
51 | BUG_ON(repo->dev_type != PS3_DEV_TYPE_SB_GELIC); | ||
52 | |||
53 | p = kzalloc(sizeof(struct layout), GFP_KERNEL); | ||
54 | |||
55 | if (!p) { | ||
56 | result = -ENOMEM; | ||
57 | goto fail_malloc; | ||
58 | } | ||
59 | |||
60 | p->dev.match_id = PS3_MATCH_ID_GELIC; | ||
61 | p->dev.dev_type = PS3_DEVICE_TYPE_SB; | ||
62 | p->dev.bus_id = repo->bus_id; | ||
63 | p->dev.dev_id = repo->dev_id; | ||
64 | p->dev.d_region = &p->d_region; | ||
65 | |||
66 | result = ps3_repository_find_interrupt(repo, | ||
67 | PS3_INTERRUPT_TYPE_EVENT_PORT, &p->dev.interrupt_id); | ||
68 | |||
69 | if (result) { | ||
70 | pr_debug("%s:%d ps3_repository_find_interrupt failed\n", | ||
71 | __func__, __LINE__); | ||
72 | goto fail_find_interrupt; | ||
73 | } | ||
74 | |||
75 | BUG_ON(p->dev.interrupt_id != 0); | ||
76 | |||
77 | result = ps3_dma_region_init(&p->dev, p->dev.d_region, PS3_DMA_64K, | ||
78 | PS3_DMA_OTHER, NULL, 0); | ||
79 | |||
80 | if (result) { | ||
81 | pr_debug("%s:%d ps3_dma_region_init failed\n", | ||
82 | __func__, __LINE__); | ||
83 | goto fail_dma_init; | ||
84 | } | ||
85 | |||
86 | result = ps3_system_bus_device_register(&p->dev); | ||
87 | |||
88 | if (result) { | ||
89 | pr_debug("%s:%d ps3_system_bus_device_register failed\n", | ||
90 | __func__, __LINE__); | ||
91 | goto fail_device_register; | ||
92 | } | ||
93 | |||
94 | pr_debug(" <- %s:%d\n", __func__, __LINE__); | ||
95 | return result; | ||
96 | |||
97 | fail_device_register: | ||
98 | fail_dma_init: | ||
99 | fail_find_interrupt: | ||
100 | kfree(p); | ||
101 | fail_malloc: | ||
102 | pr_debug(" <- %s:%d: fail.\n", __func__, __LINE__); | ||
103 | return result; | ||
104 | } | ||
105 | |||
106 | static int __init_refok ps3_setup_uhc_device( | ||
107 | const struct ps3_repository_device *repo, enum ps3_match_id match_id, | ||
108 | enum ps3_interrupt_type interrupt_type, enum ps3_reg_type reg_type) | ||
109 | { | ||
110 | int result; | ||
111 | struct layout { | ||
112 | struct ps3_system_bus_device dev; | ||
113 | struct ps3_dma_region d_region; | ||
114 | struct ps3_mmio_region m_region; | ||
115 | } *p; | ||
116 | u64 bus_addr; | ||
117 | u64 len; | ||
118 | |||
119 | pr_debug(" -> %s:%d\n", __func__, __LINE__); | ||
120 | |||
121 | BUG_ON(repo->bus_type != PS3_BUS_TYPE_SB); | ||
122 | BUG_ON(repo->dev_type != PS3_DEV_TYPE_SB_USB); | ||
123 | |||
124 | p = kzalloc(sizeof(struct layout), GFP_KERNEL); | ||
125 | |||
126 | if (!p) { | ||
127 | result = -ENOMEM; | ||
128 | goto fail_malloc; | ||
129 | } | ||
130 | |||
131 | p->dev.match_id = match_id; | ||
132 | p->dev.dev_type = PS3_DEVICE_TYPE_SB; | ||
133 | p->dev.bus_id = repo->bus_id; | ||
134 | p->dev.dev_id = repo->dev_id; | ||
135 | p->dev.d_region = &p->d_region; | ||
136 | p->dev.m_region = &p->m_region; | ||
137 | |||
138 | result = ps3_repository_find_interrupt(repo, | ||
139 | interrupt_type, &p->dev.interrupt_id); | ||
140 | |||
141 | if (result) { | ||
142 | pr_debug("%s:%d ps3_repository_find_interrupt failed\n", | ||
143 | __func__, __LINE__); | ||
144 | goto fail_find_interrupt; | ||
145 | } | ||
146 | |||
147 | result = ps3_repository_find_reg(repo, reg_type, | ||
148 | &bus_addr, &len); | ||
149 | |||
150 | if (result) { | ||
151 | pr_debug("%s:%d ps3_repository_find_reg failed\n", | ||
152 | __func__, __LINE__); | ||
153 | goto fail_find_reg; | ||
154 | } | ||
155 | |||
156 | result = ps3_dma_region_init(&p->dev, p->dev.d_region, PS3_DMA_64K, | ||
157 | PS3_DMA_INTERNAL, NULL, 0); | ||
158 | |||
159 | if (result) { | ||
160 | pr_debug("%s:%d ps3_dma_region_init failed\n", | ||
161 | __func__, __LINE__); | ||
162 | goto fail_dma_init; | ||
163 | } | ||
164 | |||
165 | result = ps3_mmio_region_init(&p->dev, p->dev.m_region, bus_addr, len, | ||
166 | PS3_MMIO_4K); | ||
167 | |||
168 | if (result) { | ||
169 | pr_debug("%s:%d ps3_mmio_region_init failed\n", | ||
170 | __func__, __LINE__); | ||
171 | goto fail_mmio_init; | ||
172 | } | ||
173 | |||
174 | result = ps3_system_bus_device_register(&p->dev); | ||
175 | |||
176 | if (result) { | ||
177 | pr_debug("%s:%d ps3_system_bus_device_register failed\n", | ||
178 | __func__, __LINE__); | ||
179 | goto fail_device_register; | ||
180 | } | ||
181 | |||
182 | pr_debug(" <- %s:%d\n", __func__, __LINE__); | ||
183 | return result; | ||
184 | |||
185 | fail_device_register: | ||
186 | fail_mmio_init: | ||
187 | fail_dma_init: | ||
188 | fail_find_reg: | ||
189 | fail_find_interrupt: | ||
190 | kfree(p); | ||
191 | fail_malloc: | ||
192 | pr_debug(" <- %s:%d: fail.\n", __func__, __LINE__); | ||
193 | return result; | ||
194 | } | ||
195 | |||
196 | static int __init ps3_setup_ehci_device( | ||
197 | const struct ps3_repository_device *repo) | ||
198 | { | ||
199 | return ps3_setup_uhc_device(repo, PS3_MATCH_ID_EHCI, | ||
200 | PS3_INTERRUPT_TYPE_SB_EHCI, PS3_REG_TYPE_SB_EHCI); | ||
201 | } | ||
202 | |||
203 | static int __init ps3_setup_ohci_device( | ||
204 | const struct ps3_repository_device *repo) | ||
205 | { | ||
206 | return ps3_setup_uhc_device(repo, PS3_MATCH_ID_OHCI, | ||
207 | PS3_INTERRUPT_TYPE_SB_OHCI, PS3_REG_TYPE_SB_OHCI); | ||
208 | } | ||
209 | |||
210 | static int __init ps3_setup_vuart_device(enum ps3_match_id match_id, | ||
211 | unsigned int port_number) | ||
212 | { | ||
213 | int result; | ||
214 | struct layout { | ||
215 | struct ps3_system_bus_device dev; | ||
216 | } *p; | ||
217 | |||
218 | pr_debug(" -> %s:%d: match_id %u, port %u\n", __func__, __LINE__, | ||
219 | match_id, port_number); | ||
220 | |||
221 | p = kzalloc(sizeof(struct layout), GFP_KERNEL); | ||
222 | |||
223 | if (!p) | ||
224 | return -ENOMEM; | ||
225 | |||
226 | p->dev.match_id = match_id; | ||
227 | p->dev.dev_type = PS3_DEVICE_TYPE_VUART; | ||
228 | p->dev.port_number = port_number; | ||
229 | |||
230 | result = ps3_system_bus_device_register(&p->dev); | ||
231 | |||
232 | if (result) | ||
233 | pr_debug("%s:%d ps3_system_bus_device_register failed\n", | ||
234 | __func__, __LINE__); | ||
235 | |||
236 | pr_debug(" <- %s:%d\n", __func__, __LINE__); | ||
237 | return result; | ||
238 | } | ||
239 | |||
240 | static int __init ps3_register_vuart_devices(void) | ||
241 | { | ||
242 | int result; | ||
243 | unsigned int port_number; | ||
244 | |||
245 | pr_debug(" -> %s:%d\n", __func__, __LINE__); | ||
246 | |||
247 | result = ps3_repository_read_vuart_av_port(&port_number); | ||
248 | if (result) | ||
249 | port_number = 0; /* av default */ | ||
250 | |||
251 | result = ps3_setup_vuart_device(PS3_MATCH_ID_AV_SETTINGS, port_number); | ||
252 | WARN_ON(result); | ||
253 | |||
254 | result = ps3_repository_read_vuart_sysmgr_port(&port_number); | ||
255 | if (result) | ||
256 | port_number = 2; /* sysmgr default */ | ||
257 | |||
258 | result = ps3_setup_vuart_device(PS3_MATCH_ID_SYSTEM_MANAGER, | ||
259 | port_number); | ||
260 | WARN_ON(result); | ||
261 | |||
262 | pr_debug(" <- %s:%d\n", __func__, __LINE__); | ||
263 | return result; | ||
264 | } | ||
265 | |||
266 | static int __init ps3_register_sound_devices(void) | ||
267 | { | ||
268 | int result; | ||
269 | struct layout { | ||
270 | struct ps3_system_bus_device dev; | ||
271 | struct ps3_dma_region d_region; | ||
272 | struct ps3_mmio_region m_region; | ||
273 | } *p; | ||
274 | |||
275 | pr_debug(" -> %s:%d\n", __func__, __LINE__); | ||
276 | |||
277 | p = kzalloc(sizeof(*p), GFP_KERNEL); | ||
278 | if (!p) | ||
279 | return -ENOMEM; | ||
280 | |||
281 | p->dev.match_id = PS3_MATCH_ID_SOUND; | ||
282 | p->dev.dev_type = PS3_DEVICE_TYPE_IOC0; | ||
283 | p->dev.d_region = &p->d_region; | ||
284 | p->dev.m_region = &p->m_region; | ||
285 | |||
286 | result = ps3_system_bus_device_register(&p->dev); | ||
287 | |||
288 | if (result) | ||
289 | pr_debug("%s:%d ps3_system_bus_device_register failed\n", | ||
290 | __func__, __LINE__); | ||
291 | |||
292 | pr_debug(" <- %s:%d\n", __func__, __LINE__); | ||
293 | return result; | ||
294 | } | ||
295 | |||
296 | static int __init ps3_register_graphics_devices(void) | ||
297 | { | ||
298 | int result; | ||
299 | struct layout { | ||
300 | struct ps3_system_bus_device dev; | ||
301 | } *p; | ||
302 | |||
303 | pr_debug(" -> %s:%d\n", __func__, __LINE__); | ||
304 | |||
305 | p = kzalloc(sizeof(struct layout), GFP_KERNEL); | ||
306 | |||
307 | if (!p) | ||
308 | return -ENOMEM; | ||
309 | |||
310 | p->dev.match_id = PS3_MATCH_ID_GRAPHICS; | ||
311 | p->dev.dev_type = PS3_DEVICE_TYPE_IOC0; | ||
312 | |||
313 | result = ps3_system_bus_device_register(&p->dev); | ||
314 | |||
315 | if (result) | ||
316 | pr_debug("%s:%d ps3_system_bus_device_register failed\n", | ||
317 | __func__, __LINE__); | ||
318 | |||
319 | pr_debug(" <- %s:%d\n", __func__, __LINE__); | ||
320 | return result; | ||
321 | } | ||
322 | |||
323 | /** | ||
324 | * ps3_register_repository_device - Register a device from the repositiory info. | ||
325 | * | ||
326 | */ | ||
327 | |||
328 | static int ps3_register_repository_device( | ||
329 | const struct ps3_repository_device *repo) | ||
330 | { | ||
331 | int result; | ||
332 | |||
333 | switch (repo->dev_type) { | ||
334 | case PS3_DEV_TYPE_SB_GELIC: | ||
335 | result = ps3_setup_gelic_device(repo); | ||
336 | if (result) { | ||
337 | pr_debug("%s:%d ps3_setup_gelic_device failed\n", | ||
338 | __func__, __LINE__); | ||
339 | } | ||
340 | break; | ||
341 | case PS3_DEV_TYPE_SB_USB: | ||
342 | |||
343 | /* Each USB device has both an EHCI and an OHCI HC */ | ||
344 | |||
345 | result = ps3_setup_ehci_device(repo); | ||
346 | |||
347 | if (result) { | ||
348 | pr_debug("%s:%d ps3_setup_ehci_device failed\n", | ||
349 | __func__, __LINE__); | ||
350 | } | ||
351 | |||
352 | result = ps3_setup_ohci_device(repo); | ||
353 | |||
354 | if (result) { | ||
355 | pr_debug("%s:%d ps3_setup_ohci_device failed\n", | ||
356 | __func__, __LINE__); | ||
357 | } | ||
358 | break; | ||
359 | default: | ||
360 | result = 0; | ||
361 | pr_debug("%s:%u: unsupported dev_type %u\n", __func__, __LINE__, | ||
362 | repo->dev_type); | ||
363 | } | ||
364 | |||
365 | return result; | ||
366 | } | ||
367 | |||
368 | /** | ||
369 | * ps3_probe_thread - Background repository probing at system startup. | ||
370 | * | ||
371 | * This implementation only supports background probing on a single bus. | ||
372 | */ | ||
373 | |||
374 | static int ps3_probe_thread(void *data) | ||
375 | { | ||
376 | struct ps3_repository_device *repo = data; | ||
377 | int result; | ||
378 | unsigned int ms = 250; | ||
379 | |||
380 | pr_debug(" -> %s:%u: kthread started\n", __func__, __LINE__); | ||
381 | |||
382 | do { | ||
383 | try_to_freeze(); | ||
384 | |||
385 | pr_debug("%s:%u: probing...\n", __func__, __LINE__); | ||
386 | |||
387 | do { | ||
388 | result = ps3_repository_find_device(repo); | ||
389 | |||
390 | if (result == -ENODEV) | ||
391 | pr_debug("%s:%u: nothing new\n", __func__, | ||
392 | __LINE__); | ||
393 | else if (result) | ||
394 | pr_debug("%s:%u: find device error.\n", | ||
395 | __func__, __LINE__); | ||
396 | else { | ||
397 | pr_debug("%s:%u: found device\n", __func__, | ||
398 | __LINE__); | ||
399 | ps3_register_repository_device(repo); | ||
400 | ps3_repository_bump_device(repo); | ||
401 | ms = 250; | ||
402 | } | ||
403 | } while (!result); | ||
404 | |||
405 | pr_debug("%s:%u: ms %u\n", __func__, __LINE__, ms); | ||
406 | |||
407 | if ( ms > 60000) | ||
408 | break; | ||
409 | |||
410 | msleep_interruptible(ms); | ||
411 | |||
412 | /* An exponential backoff. */ | ||
413 | ms <<= 1; | ||
414 | |||
415 | } while (!kthread_should_stop()); | ||
416 | |||
417 | pr_debug(" <- %s:%u: kthread finished\n", __func__, __LINE__); | ||
418 | |||
419 | return 0; | ||
420 | } | ||
421 | |||
422 | /** | ||
423 | * ps3_start_probe_thread - Starts the background probe thread. | ||
424 | * | ||
425 | */ | ||
426 | |||
427 | static int __init ps3_start_probe_thread(enum ps3_bus_type bus_type) | ||
428 | { | ||
429 | int result; | ||
430 | struct task_struct *task; | ||
431 | static struct ps3_repository_device repo; /* must be static */ | ||
432 | |||
433 | pr_debug(" -> %s:%d\n", __func__, __LINE__); | ||
434 | |||
435 | memset(&repo, 0, sizeof(repo)); | ||
436 | |||
437 | repo.bus_type = bus_type; | ||
438 | |||
439 | result = ps3_repository_find_bus(repo.bus_type, 0, &repo.bus_index); | ||
440 | |||
441 | if (result) { | ||
442 | printk(KERN_ERR "%s: Cannot find bus (%d)\n", __func__, result); | ||
443 | return -ENODEV; | ||
444 | } | ||
445 | |||
446 | result = ps3_repository_read_bus_id(repo.bus_index, &repo.bus_id); | ||
447 | |||
448 | if (result) { | ||
449 | printk(KERN_ERR "%s: read_bus_id failed %d\n", __func__, | ||
450 | result); | ||
451 | return -ENODEV; | ||
452 | } | ||
453 | |||
454 | task = kthread_run(ps3_probe_thread, &repo, "ps3-probe-%u", bus_type); | ||
455 | |||
456 | if (IS_ERR(task)) { | ||
457 | result = PTR_ERR(task); | ||
458 | printk(KERN_ERR "%s: kthread_run failed %d\n", __func__, | ||
459 | result); | ||
460 | return result; | ||
461 | } | ||
462 | |||
463 | pr_debug(" <- %s:%d\n", __func__, __LINE__); | ||
464 | return 0; | ||
465 | } | ||
466 | |||
467 | /** | ||
468 | * ps3_register_devices - Probe the system and register devices found. | ||
469 | * | ||
470 | * A device_initcall() routine. | ||
471 | */ | ||
472 | |||
473 | static int __init ps3_register_devices(void) | ||
474 | { | ||
475 | int result; | ||
476 | |||
477 | if (!firmware_has_feature(FW_FEATURE_PS3_LV1)) | ||
478 | return -ENODEV; | ||
479 | |||
480 | pr_debug(" -> %s:%d\n", __func__, __LINE__); | ||
481 | |||
482 | /* ps3_repository_dump_bus_info(); */ | ||
483 | |||
484 | result = ps3_start_probe_thread(PS3_BUS_TYPE_STORAGE); | ||
485 | |||
486 | ps3_register_vuart_devices(); | ||
487 | |||
488 | ps3_register_graphics_devices(); | ||
489 | |||
490 | ps3_repository_find_devices(PS3_BUS_TYPE_SB, | ||
491 | ps3_register_repository_device); | ||
492 | |||
493 | ps3_register_sound_devices(); | ||
494 | |||
495 | pr_debug(" <- %s:%d\n", __func__, __LINE__); | ||
496 | return 0; | ||
497 | } | ||
498 | |||
499 | device_initcall(ps3_register_devices); | ||