1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * PS3 device registration routines. |
4 | * |
5 | * Copyright (C) 2007 Sony Computer Entertainment Inc. |
6 | * Copyright 2007 Sony Corp. |
7 | */ |
8 | |
9 | #include <linux/delay.h> |
10 | #include <linux/freezer.h> |
11 | #include <linux/kernel.h> |
12 | #include <linux/kthread.h> |
13 | #include <linux/init.h> |
14 | #include <linux/slab.h> |
15 | #include <linux/reboot.h> |
16 | #include <linux/rcuwait.h> |
17 | |
18 | #include <asm/firmware.h> |
19 | #include <asm/lv1call.h> |
20 | #include <asm/ps3stor.h> |
21 | |
22 | #include "platform.h" |
23 | |
24 | static int __init ps3_register_lpm_devices(void) |
25 | { |
26 | int result; |
27 | u64 tmp1; |
28 | u64 tmp2; |
29 | struct ps3_system_bus_device *dev; |
30 | |
31 | pr_debug(" -> %s:%d\n" , __func__, __LINE__); |
32 | |
33 | dev = kzalloc(sizeof(*dev), GFP_KERNEL); |
34 | if (!dev) |
35 | return -ENOMEM; |
36 | |
37 | dev->match_id = PS3_MATCH_ID_LPM; |
38 | dev->dev_type = PS3_DEVICE_TYPE_LPM; |
39 | |
40 | /* The current lpm driver only supports a single BE processor. */ |
41 | |
42 | result = ps3_repository_read_be_node_id(be_index: 0, node_id: &dev->lpm.node_id); |
43 | |
44 | if (result) { |
45 | pr_debug("%s:%d: ps3_repository_read_be_node_id failed \n" , |
46 | __func__, __LINE__); |
47 | goto fail_read_repo; |
48 | } |
49 | |
50 | result = ps3_repository_read_lpm_privileges(be_index: dev->lpm.node_id, lpar: &tmp1, |
51 | rights: &dev->lpm.rights); |
52 | |
53 | if (result) { |
54 | pr_debug("%s:%d: ps3_repository_read_lpm_privileges failed\n" , |
55 | __func__, __LINE__); |
56 | goto fail_read_repo; |
57 | } |
58 | |
59 | lv1_get_logical_partition_id(&tmp2); |
60 | |
61 | if (tmp1 != tmp2) { |
62 | pr_debug("%s:%d: wrong lpar\n" , |
63 | __func__, __LINE__); |
64 | result = -ENODEV; |
65 | goto fail_rights; |
66 | } |
67 | |
68 | if (!(dev->lpm.rights & PS3_LPM_RIGHTS_USE_LPM)) { |
69 | pr_debug("%s:%d: don't have rights to use lpm\n" , |
70 | __func__, __LINE__); |
71 | result = -EPERM; |
72 | goto fail_rights; |
73 | } |
74 | |
75 | pr_debug("%s:%d: pu_id %llu, rights %llu(%llxh)\n" , |
76 | __func__, __LINE__, dev->lpm.pu_id, dev->lpm.rights, |
77 | dev->lpm.rights); |
78 | |
79 | result = ps3_repository_read_pu_id(pu_index: 0, pu_id: &dev->lpm.pu_id); |
80 | |
81 | if (result) { |
82 | pr_debug("%s:%d: ps3_repository_read_pu_id failed \n" , |
83 | __func__, __LINE__); |
84 | goto fail_read_repo; |
85 | } |
86 | |
87 | result = ps3_system_bus_device_register(dev); |
88 | |
89 | if (result) { |
90 | pr_debug("%s:%d ps3_system_bus_device_register failed\n" , |
91 | __func__, __LINE__); |
92 | goto fail_register; |
93 | } |
94 | |
95 | pr_debug(" <- %s:%d\n" , __func__, __LINE__); |
96 | return 0; |
97 | |
98 | |
99 | fail_register: |
100 | fail_rights: |
101 | fail_read_repo: |
102 | kfree(objp: dev); |
103 | pr_debug(" <- %s:%d: failed\n" , __func__, __LINE__); |
104 | return result; |
105 | } |
106 | |
107 | /** |
108 | * ps3_setup_gelic_device - Setup and register a gelic device instance. |
109 | * |
110 | * Allocates memory for a struct ps3_system_bus_device instance, initialises the |
111 | * structure members, and registers the device instance with the system bus. |
112 | */ |
113 | |
114 | static int __init ps3_setup_gelic_device( |
115 | const struct ps3_repository_device *repo) |
116 | { |
117 | int result; |
118 | struct layout { |
119 | struct ps3_system_bus_device dev; |
120 | struct ps3_dma_region d_region; |
121 | } *p; |
122 | |
123 | pr_debug(" -> %s:%d\n" , __func__, __LINE__); |
124 | |
125 | BUG_ON(repo->bus_type != PS3_BUS_TYPE_SB); |
126 | BUG_ON(repo->dev_type != PS3_DEV_TYPE_SB_GELIC); |
127 | |
128 | p = kzalloc(size: sizeof(struct layout), GFP_KERNEL); |
129 | |
130 | if (!p) { |
131 | result = -ENOMEM; |
132 | goto fail_malloc; |
133 | } |
134 | |
135 | p->dev.match_id = PS3_MATCH_ID_GELIC; |
136 | p->dev.dev_type = PS3_DEVICE_TYPE_SB; |
137 | p->dev.bus_id = repo->bus_id; |
138 | p->dev.dev_id = repo->dev_id; |
139 | p->dev.d_region = &p->d_region; |
140 | |
141 | result = ps3_repository_find_interrupt(repo, |
142 | intr_type: PS3_INTERRUPT_TYPE_EVENT_PORT, interrupt_id: &p->dev.interrupt_id); |
143 | |
144 | if (result) { |
145 | pr_debug("%s:%d ps3_repository_find_interrupt failed\n" , |
146 | __func__, __LINE__); |
147 | goto fail_find_interrupt; |
148 | } |
149 | |
150 | BUG_ON(p->dev.interrupt_id != 0); |
151 | |
152 | result = ps3_dma_region_init(&p->dev, p->dev.d_region, PS3_DMA_64K, |
153 | PS3_DMA_OTHER, NULL, 0); |
154 | |
155 | if (result) { |
156 | pr_debug("%s:%d ps3_dma_region_init failed\n" , |
157 | __func__, __LINE__); |
158 | goto fail_dma_init; |
159 | } |
160 | |
161 | result = ps3_system_bus_device_register(&p->dev); |
162 | |
163 | if (result) { |
164 | pr_debug("%s:%d ps3_system_bus_device_register failed\n" , |
165 | __func__, __LINE__); |
166 | goto fail_device_register; |
167 | } |
168 | |
169 | pr_debug(" <- %s:%d\n" , __func__, __LINE__); |
170 | return result; |
171 | |
172 | fail_device_register: |
173 | fail_dma_init: |
174 | fail_find_interrupt: |
175 | kfree(objp: p); |
176 | fail_malloc: |
177 | pr_debug(" <- %s:%d: fail.\n" , __func__, __LINE__); |
178 | return result; |
179 | } |
180 | |
181 | static int __ref ps3_setup_uhc_device( |
182 | const struct ps3_repository_device *repo, enum ps3_match_id match_id, |
183 | enum ps3_interrupt_type interrupt_type, enum ps3_reg_type reg_type) |
184 | { |
185 | int result; |
186 | struct layout { |
187 | struct ps3_system_bus_device dev; |
188 | struct ps3_dma_region d_region; |
189 | struct ps3_mmio_region m_region; |
190 | } *p; |
191 | u64 bus_addr; |
192 | u64 len; |
193 | |
194 | pr_debug(" -> %s:%d\n" , __func__, __LINE__); |
195 | |
196 | BUG_ON(repo->bus_type != PS3_BUS_TYPE_SB); |
197 | BUG_ON(repo->dev_type != PS3_DEV_TYPE_SB_USB); |
198 | |
199 | p = kzalloc(size: sizeof(struct layout), GFP_KERNEL); |
200 | |
201 | if (!p) { |
202 | result = -ENOMEM; |
203 | goto fail_malloc; |
204 | } |
205 | |
206 | p->dev.match_id = match_id; |
207 | p->dev.dev_type = PS3_DEVICE_TYPE_SB; |
208 | p->dev.bus_id = repo->bus_id; |
209 | p->dev.dev_id = repo->dev_id; |
210 | p->dev.d_region = &p->d_region; |
211 | p->dev.m_region = &p->m_region; |
212 | |
213 | result = ps3_repository_find_interrupt(repo, |
214 | intr_type: interrupt_type, interrupt_id: &p->dev.interrupt_id); |
215 | |
216 | if (result) { |
217 | pr_debug("%s:%d ps3_repository_find_interrupt failed\n" , |
218 | __func__, __LINE__); |
219 | goto fail_find_interrupt; |
220 | } |
221 | |
222 | result = ps3_repository_find_reg(repo, reg_type, |
223 | bus_addr: &bus_addr, len: &len); |
224 | |
225 | if (result) { |
226 | pr_debug("%s:%d ps3_repository_find_reg failed\n" , |
227 | __func__, __LINE__); |
228 | goto fail_find_reg; |
229 | } |
230 | |
231 | result = ps3_dma_region_init(&p->dev, p->dev.d_region, PS3_DMA_64K, |
232 | PS3_DMA_INTERNAL, NULL, 0); |
233 | |
234 | if (result) { |
235 | pr_debug("%s:%d ps3_dma_region_init failed\n" , |
236 | __func__, __LINE__); |
237 | goto fail_dma_init; |
238 | } |
239 | |
240 | result = ps3_mmio_region_init(&p->dev, p->dev.m_region, bus_addr, len, |
241 | PS3_MMIO_4K); |
242 | |
243 | if (result) { |
244 | pr_debug("%s:%d ps3_mmio_region_init failed\n" , |
245 | __func__, __LINE__); |
246 | goto fail_mmio_init; |
247 | } |
248 | |
249 | result = ps3_system_bus_device_register(&p->dev); |
250 | |
251 | if (result) { |
252 | pr_debug("%s:%d ps3_system_bus_device_register failed\n" , |
253 | __func__, __LINE__); |
254 | goto fail_device_register; |
255 | } |
256 | |
257 | pr_debug(" <- %s:%d\n" , __func__, __LINE__); |
258 | return result; |
259 | |
260 | fail_device_register: |
261 | fail_mmio_init: |
262 | fail_dma_init: |
263 | fail_find_reg: |
264 | fail_find_interrupt: |
265 | kfree(objp: p); |
266 | fail_malloc: |
267 | pr_debug(" <- %s:%d: fail.\n" , __func__, __LINE__); |
268 | return result; |
269 | } |
270 | |
271 | static int __init ps3_setup_ehci_device( |
272 | const struct ps3_repository_device *repo) |
273 | { |
274 | return ps3_setup_uhc_device(repo, match_id: PS3_MATCH_ID_EHCI, |
275 | interrupt_type: PS3_INTERRUPT_TYPE_SB_EHCI, reg_type: PS3_REG_TYPE_SB_EHCI); |
276 | } |
277 | |
278 | static int __init ps3_setup_ohci_device( |
279 | const struct ps3_repository_device *repo) |
280 | { |
281 | return ps3_setup_uhc_device(repo, match_id: PS3_MATCH_ID_OHCI, |
282 | interrupt_type: PS3_INTERRUPT_TYPE_SB_OHCI, reg_type: PS3_REG_TYPE_SB_OHCI); |
283 | } |
284 | |
285 | static int __init ps3_setup_vuart_device(enum ps3_match_id match_id, |
286 | unsigned int port_number) |
287 | { |
288 | int result; |
289 | struct layout { |
290 | struct ps3_system_bus_device dev; |
291 | } *p; |
292 | |
293 | pr_debug(" -> %s:%d: match_id %u, port %u\n" , __func__, __LINE__, |
294 | match_id, port_number); |
295 | |
296 | p = kzalloc(size: sizeof(struct layout), GFP_KERNEL); |
297 | |
298 | if (!p) |
299 | return -ENOMEM; |
300 | |
301 | p->dev.match_id = match_id; |
302 | p->dev.dev_type = PS3_DEVICE_TYPE_VUART; |
303 | p->dev.port_number = port_number; |
304 | |
305 | result = ps3_system_bus_device_register(&p->dev); |
306 | |
307 | if (result) { |
308 | pr_debug("%s:%d ps3_system_bus_device_register failed\n" , |
309 | __func__, __LINE__); |
310 | goto fail_device_register; |
311 | } |
312 | pr_debug(" <- %s:%d\n" , __func__, __LINE__); |
313 | return 0; |
314 | |
315 | fail_device_register: |
316 | kfree(objp: p); |
317 | pr_debug(" <- %s:%d fail\n" , __func__, __LINE__); |
318 | return result; |
319 | } |
320 | |
321 | static int ps3_setup_storage_dev(const struct ps3_repository_device *repo, |
322 | enum ps3_match_id match_id) |
323 | { |
324 | int result; |
325 | struct ps3_storage_device *p; |
326 | u64 port, blk_size, num_blocks; |
327 | unsigned int num_regions, i; |
328 | |
329 | pr_debug(" -> %s:%u: match_id %u\n" , __func__, __LINE__, match_id); |
330 | |
331 | result = ps3_repository_read_stor_dev_info(bus_index: repo->bus_index, |
332 | dev_index: repo->dev_index, port: &port, |
333 | blk_size: &blk_size, num_blocks: &num_blocks, |
334 | num_regions: &num_regions); |
335 | if (result) { |
336 | printk(KERN_ERR "%s:%u: _read_stor_dev_info failed %d\n" , |
337 | __func__, __LINE__, result); |
338 | return -ENODEV; |
339 | } |
340 | |
341 | pr_debug("%s:%u: (%u:%u:%u): port %llu blk_size %llu num_blocks %llu " |
342 | "num_regions %u\n" , __func__, __LINE__, repo->bus_index, |
343 | repo->dev_index, repo->dev_type, port, blk_size, num_blocks, |
344 | num_regions); |
345 | |
346 | p = kzalloc(struct_size(p, regions, num_regions), GFP_KERNEL); |
347 | if (!p) { |
348 | result = -ENOMEM; |
349 | goto fail_malloc; |
350 | } |
351 | |
352 | p->sbd.match_id = match_id; |
353 | p->sbd.dev_type = PS3_DEVICE_TYPE_SB; |
354 | p->sbd.bus_id = repo->bus_id; |
355 | p->sbd.dev_id = repo->dev_id; |
356 | p->sbd.d_region = &p->dma_region; |
357 | p->blk_size = blk_size; |
358 | p->num_regions = num_regions; |
359 | |
360 | result = ps3_repository_find_interrupt(repo, |
361 | intr_type: PS3_INTERRUPT_TYPE_EVENT_PORT, |
362 | interrupt_id: &p->sbd.interrupt_id); |
363 | if (result) { |
364 | printk(KERN_ERR "%s:%u: find_interrupt failed %d\n" , __func__, |
365 | __LINE__, result); |
366 | result = -ENODEV; |
367 | goto fail_find_interrupt; |
368 | } |
369 | |
370 | for (i = 0; i < num_regions; i++) { |
371 | unsigned int id; |
372 | u64 start, size; |
373 | |
374 | result = ps3_repository_read_stor_dev_region(bus_index: repo->bus_index, |
375 | dev_index: repo->dev_index, |
376 | region_index: i, region_id: &id, region_start: &start, |
377 | region_size: &size); |
378 | if (result) { |
379 | printk(KERN_ERR |
380 | "%s:%u: read_stor_dev_region failed %d\n" , |
381 | __func__, __LINE__, result); |
382 | result = -ENODEV; |
383 | goto fail_read_region; |
384 | } |
385 | pr_debug("%s:%u: region %u: id %u start %llu size %llu\n" , |
386 | __func__, __LINE__, i, id, start, size); |
387 | |
388 | p->regions[i].id = id; |
389 | p->regions[i].start = start; |
390 | p->regions[i].size = size; |
391 | } |
392 | |
393 | result = ps3_system_bus_device_register(&p->sbd); |
394 | if (result) { |
395 | pr_debug("%s:%u ps3_system_bus_device_register failed\n" , |
396 | __func__, __LINE__); |
397 | goto fail_device_register; |
398 | } |
399 | |
400 | pr_debug(" <- %s:%u\n" , __func__, __LINE__); |
401 | return 0; |
402 | |
403 | fail_device_register: |
404 | fail_read_region: |
405 | fail_find_interrupt: |
406 | kfree(objp: p); |
407 | fail_malloc: |
408 | pr_debug(" <- %s:%u: fail.\n" , __func__, __LINE__); |
409 | return result; |
410 | } |
411 | |
412 | static int __init ps3_register_vuart_devices(void) |
413 | { |
414 | int result; |
415 | unsigned int port_number; |
416 | |
417 | pr_debug(" -> %s:%d\n" , __func__, __LINE__); |
418 | |
419 | result = ps3_repository_read_vuart_av_port(port: &port_number); |
420 | if (result) |
421 | port_number = 0; /* av default */ |
422 | |
423 | result = ps3_setup_vuart_device(match_id: PS3_MATCH_ID_AV_SETTINGS, port_number); |
424 | WARN_ON(result); |
425 | |
426 | result = ps3_repository_read_vuart_sysmgr_port(port: &port_number); |
427 | if (result) |
428 | port_number = 2; /* sysmgr default */ |
429 | |
430 | result = ps3_setup_vuart_device(match_id: PS3_MATCH_ID_SYSTEM_MANAGER, |
431 | port_number); |
432 | WARN_ON(result); |
433 | |
434 | pr_debug(" <- %s:%d\n" , __func__, __LINE__); |
435 | return result; |
436 | } |
437 | |
438 | static int __init ps3_register_sound_devices(void) |
439 | { |
440 | int result; |
441 | struct layout { |
442 | struct ps3_system_bus_device dev; |
443 | struct ps3_dma_region d_region; |
444 | struct ps3_mmio_region m_region; |
445 | } *p; |
446 | |
447 | pr_debug(" -> %s:%d\n" , __func__, __LINE__); |
448 | |
449 | p = kzalloc(size: sizeof(*p), GFP_KERNEL); |
450 | if (!p) |
451 | return -ENOMEM; |
452 | |
453 | p->dev.match_id = PS3_MATCH_ID_SOUND; |
454 | p->dev.dev_type = PS3_DEVICE_TYPE_IOC0; |
455 | p->dev.d_region = &p->d_region; |
456 | p->dev.m_region = &p->m_region; |
457 | |
458 | result = ps3_system_bus_device_register(&p->dev); |
459 | |
460 | if (result) { |
461 | pr_debug("%s:%d ps3_system_bus_device_register failed\n" , |
462 | __func__, __LINE__); |
463 | goto fail_device_register; |
464 | } |
465 | pr_debug(" <- %s:%d\n" , __func__, __LINE__); |
466 | return 0; |
467 | |
468 | fail_device_register: |
469 | kfree(objp: p); |
470 | pr_debug(" <- %s:%d failed\n" , __func__, __LINE__); |
471 | return result; |
472 | } |
473 | |
474 | static int __init ps3_register_graphics_devices(void) |
475 | { |
476 | int result; |
477 | struct layout { |
478 | struct ps3_system_bus_device dev; |
479 | } *p; |
480 | |
481 | pr_debug(" -> %s:%d\n" , __func__, __LINE__); |
482 | |
483 | p = kzalloc(size: sizeof(struct layout), GFP_KERNEL); |
484 | |
485 | if (!p) |
486 | return -ENOMEM; |
487 | |
488 | p->dev.match_id = PS3_MATCH_ID_GPU; |
489 | p->dev.match_sub_id = PS3_MATCH_SUB_ID_GPU_FB; |
490 | p->dev.dev_type = PS3_DEVICE_TYPE_IOC0; |
491 | |
492 | result = ps3_system_bus_device_register(&p->dev); |
493 | |
494 | if (result) { |
495 | pr_debug("%s:%d ps3_system_bus_device_register failed\n" , |
496 | __func__, __LINE__); |
497 | goto fail_device_register; |
498 | } |
499 | |
500 | pr_debug(" <- %s:%d\n" , __func__, __LINE__); |
501 | return 0; |
502 | |
503 | fail_device_register: |
504 | kfree(objp: p); |
505 | pr_debug(" <- %s:%d failed\n" , __func__, __LINE__); |
506 | return result; |
507 | } |
508 | |
509 | static int __init ps3_register_ramdisk_device(void) |
510 | { |
511 | int result; |
512 | struct layout { |
513 | struct ps3_system_bus_device dev; |
514 | } *p; |
515 | |
516 | pr_debug(" -> %s:%d\n" , __func__, __LINE__); |
517 | |
518 | p = kzalloc(size: sizeof(struct layout), GFP_KERNEL); |
519 | |
520 | if (!p) |
521 | return -ENOMEM; |
522 | |
523 | p->dev.match_id = PS3_MATCH_ID_GPU; |
524 | p->dev.match_sub_id = PS3_MATCH_SUB_ID_GPU_RAMDISK; |
525 | p->dev.dev_type = PS3_DEVICE_TYPE_IOC0; |
526 | |
527 | result = ps3_system_bus_device_register(&p->dev); |
528 | |
529 | if (result) { |
530 | pr_debug("%s:%d ps3_system_bus_device_register failed\n" , |
531 | __func__, __LINE__); |
532 | goto fail_device_register; |
533 | } |
534 | |
535 | pr_debug(" <- %s:%d\n" , __func__, __LINE__); |
536 | return 0; |
537 | |
538 | fail_device_register: |
539 | kfree(objp: p); |
540 | pr_debug(" <- %s:%d failed\n" , __func__, __LINE__); |
541 | return result; |
542 | } |
543 | |
544 | /** |
545 | * ps3_setup_dynamic_device - Setup a dynamic device from the repository |
546 | */ |
547 | |
548 | static int ps3_setup_dynamic_device(const struct ps3_repository_device *repo) |
549 | { |
550 | int result; |
551 | |
552 | switch (repo->dev_type) { |
553 | case PS3_DEV_TYPE_STOR_DISK: |
554 | result = ps3_setup_storage_dev(repo, PS3_MATCH_ID_STOR_DISK); |
555 | |
556 | /* Some devices are not accessible from the Other OS lpar. */ |
557 | if (result == -ENODEV) { |
558 | result = 0; |
559 | pr_debug("%s:%u: not accessible\n" , __func__, |
560 | __LINE__); |
561 | } |
562 | |
563 | if (result) |
564 | pr_debug("%s:%u ps3_setup_storage_dev failed\n" , |
565 | __func__, __LINE__); |
566 | break; |
567 | |
568 | case PS3_DEV_TYPE_STOR_ROM: |
569 | result = ps3_setup_storage_dev(repo, PS3_MATCH_ID_STOR_ROM); |
570 | if (result) |
571 | pr_debug("%s:%u ps3_setup_storage_dev failed\n" , |
572 | __func__, __LINE__); |
573 | break; |
574 | |
575 | case PS3_DEV_TYPE_STOR_FLASH: |
576 | result = ps3_setup_storage_dev(repo, PS3_MATCH_ID_STOR_FLASH); |
577 | if (result) |
578 | pr_debug("%s:%u ps3_setup_storage_dev failed\n" , |
579 | __func__, __LINE__); |
580 | break; |
581 | |
582 | default: |
583 | result = 0; |
584 | pr_debug("%s:%u: unsupported dev_type %u\n" , __func__, __LINE__, |
585 | repo->dev_type); |
586 | } |
587 | |
588 | return result; |
589 | } |
590 | |
591 | /** |
592 | * ps3_setup_static_device - Setup a static device from the repository |
593 | */ |
594 | |
595 | static int __init ps3_setup_static_device(const struct ps3_repository_device *repo) |
596 | { |
597 | int result; |
598 | |
599 | switch (repo->dev_type) { |
600 | case PS3_DEV_TYPE_SB_GELIC: |
601 | result = ps3_setup_gelic_device(repo); |
602 | if (result) { |
603 | pr_debug("%s:%d ps3_setup_gelic_device failed\n" , |
604 | __func__, __LINE__); |
605 | } |
606 | break; |
607 | case PS3_DEV_TYPE_SB_USB: |
608 | |
609 | /* Each USB device has both an EHCI and an OHCI HC */ |
610 | |
611 | result = ps3_setup_ehci_device(repo); |
612 | |
613 | if (result) { |
614 | pr_debug("%s:%d ps3_setup_ehci_device failed\n" , |
615 | __func__, __LINE__); |
616 | } |
617 | |
618 | result = ps3_setup_ohci_device(repo); |
619 | |
620 | if (result) { |
621 | pr_debug("%s:%d ps3_setup_ohci_device failed\n" , |
622 | __func__, __LINE__); |
623 | } |
624 | break; |
625 | |
626 | default: |
627 | return ps3_setup_dynamic_device(repo); |
628 | } |
629 | |
630 | return result; |
631 | } |
632 | |
633 | static void ps3_find_and_add_device(u64 bus_id, u64 dev_id) |
634 | { |
635 | struct ps3_repository_device repo; |
636 | int res; |
637 | unsigned int retries; |
638 | unsigned long rem; |
639 | |
640 | /* |
641 | * On some firmware versions (e.g. 1.90), the device may not show up |
642 | * in the repository immediately |
643 | */ |
644 | for (retries = 0; retries < 10; retries++) { |
645 | res = ps3_repository_find_device_by_id(repo: &repo, bus_id, dev_id); |
646 | if (!res) |
647 | goto found; |
648 | |
649 | rem = msleep_interruptible(msecs: 100); |
650 | if (rem) |
651 | break; |
652 | } |
653 | pr_warn("%s:%u: device %llu:%llu not found\n" , |
654 | __func__, __LINE__, bus_id, dev_id); |
655 | return; |
656 | |
657 | found: |
658 | if (retries) |
659 | pr_debug("%s:%u: device %llu:%llu found after %u retries\n" , |
660 | __func__, __LINE__, bus_id, dev_id, retries); |
661 | |
662 | ps3_setup_dynamic_device(repo: &repo); |
663 | return; |
664 | } |
665 | |
666 | #define PS3_NOTIFICATION_DEV_ID ULONG_MAX |
667 | #define PS3_NOTIFICATION_INTERRUPT_ID 0 |
668 | |
669 | struct ps3_notification_device { |
670 | struct ps3_system_bus_device sbd; |
671 | spinlock_t lock; |
672 | u64 tag; |
673 | u64 lv1_status; |
674 | struct rcuwait wait; |
675 | bool done; |
676 | }; |
677 | |
678 | enum ps3_notify_type { |
679 | notify_device_ready = 0, |
680 | notify_region_probe = 1, |
681 | notify_region_update = 2, |
682 | }; |
683 | |
684 | struct ps3_notify_cmd { |
685 | u64 operation_code; /* must be zero */ |
686 | u64 event_mask; /* OR of 1UL << enum ps3_notify_type */ |
687 | }; |
688 | |
689 | struct ps3_notify_event { |
690 | u64 event_type; /* enum ps3_notify_type */ |
691 | u64 bus_id; |
692 | u64 dev_id; |
693 | u64 dev_type; |
694 | u64 dev_port; |
695 | }; |
696 | |
697 | static irqreturn_t ps3_notification_interrupt(int irq, void *data) |
698 | { |
699 | struct ps3_notification_device *dev = data; |
700 | int res; |
701 | u64 tag, status; |
702 | |
703 | spin_lock(lock: &dev->lock); |
704 | res = lv1_storage_get_async_status(PS3_NOTIFICATION_DEV_ID, &tag, |
705 | &status); |
706 | if (tag != dev->tag) |
707 | pr_err("%s:%u: tag mismatch, got %llx, expected %llx\n" , |
708 | __func__, __LINE__, tag, dev->tag); |
709 | |
710 | if (res) { |
711 | pr_err("%s:%u: res %d status 0x%llx\n" , __func__, __LINE__, res, |
712 | status); |
713 | } else { |
714 | pr_debug("%s:%u: completed, status 0x%llx\n" , __func__, |
715 | __LINE__, status); |
716 | dev->lv1_status = status; |
717 | dev->done = true; |
718 | rcuwait_wake_up(w: &dev->wait); |
719 | } |
720 | spin_unlock(lock: &dev->lock); |
721 | return IRQ_HANDLED; |
722 | } |
723 | |
724 | static int ps3_notification_read_write(struct ps3_notification_device *dev, |
725 | u64 lpar, int write) |
726 | { |
727 | const char *op = write ? "write" : "read" ; |
728 | unsigned long flags; |
729 | int res; |
730 | |
731 | spin_lock_irqsave(&dev->lock, flags); |
732 | res = write ? lv1_storage_write(dev->sbd.dev_id, 0, 0, 1, 0, lpar, |
733 | &dev->tag) |
734 | : lv1_storage_read(dev->sbd.dev_id, 0, 0, 1, 0, lpar, |
735 | &dev->tag); |
736 | dev->done = false; |
737 | spin_unlock_irqrestore(lock: &dev->lock, flags); |
738 | if (res) { |
739 | pr_err("%s:%u: %s failed %d\n" , __func__, __LINE__, op, res); |
740 | return -EPERM; |
741 | } |
742 | pr_debug("%s:%u: notification %s issued\n" , __func__, __LINE__, op); |
743 | |
744 | rcuwait_wait_event(&dev->wait, dev->done || kthread_should_stop(), TASK_IDLE); |
745 | |
746 | if (kthread_should_stop()) |
747 | res = -EINTR; |
748 | |
749 | if (dev->lv1_status) { |
750 | pr_err("%s:%u: %s not completed, status 0x%llx\n" , __func__, |
751 | __LINE__, op, dev->lv1_status); |
752 | return -EIO; |
753 | } |
754 | pr_debug("%s:%u: notification %s completed\n" , __func__, __LINE__, op); |
755 | |
756 | return 0; |
757 | } |
758 | |
759 | static struct task_struct *probe_task; |
760 | |
761 | /** |
762 | * ps3_probe_thread - Background repository probing at system startup. |
763 | * |
764 | * This implementation only supports background probing on a single bus. |
765 | * It uses the hypervisor's storage device notification mechanism to wait until |
766 | * a storage device is ready. The device notification mechanism uses a |
767 | * pseudo device to asynchronously notify the guest when storage devices become |
768 | * ready. The notification device has a block size of 512 bytes. |
769 | */ |
770 | |
771 | static int ps3_probe_thread(void *data) |
772 | { |
773 | struct ps3_notification_device dev; |
774 | int res; |
775 | unsigned int irq; |
776 | u64 lpar; |
777 | void *buf; |
778 | struct ps3_notify_cmd *notify_cmd; |
779 | struct ps3_notify_event *notify_event; |
780 | |
781 | pr_debug(" -> %s:%u: kthread started\n" , __func__, __LINE__); |
782 | |
783 | buf = kzalloc(size: 512, GFP_KERNEL); |
784 | if (!buf) |
785 | return -ENOMEM; |
786 | |
787 | lpar = ps3_mm_phys_to_lpar(__pa(buf)); |
788 | notify_cmd = buf; |
789 | notify_event = buf; |
790 | |
791 | /* dummy system bus device */ |
792 | dev.sbd.bus_id = (u64)data; |
793 | dev.sbd.dev_id = PS3_NOTIFICATION_DEV_ID; |
794 | dev.sbd.interrupt_id = PS3_NOTIFICATION_INTERRUPT_ID; |
795 | |
796 | res = lv1_open_device(dev.sbd.bus_id, dev.sbd.dev_id, 0); |
797 | if (res) { |
798 | pr_err("%s:%u: lv1_open_device failed %s\n" , __func__, |
799 | __LINE__, ps3_result(res)); |
800 | goto fail_free; |
801 | } |
802 | |
803 | res = ps3_sb_event_receive_port_setup(&dev.sbd, PS3_BINDING_CPU_ANY, |
804 | &irq); |
805 | if (res) { |
806 | pr_err("%s:%u: ps3_sb_event_receive_port_setup failed %d\n" , |
807 | __func__, __LINE__, res); |
808 | goto fail_close_device; |
809 | } |
810 | |
811 | spin_lock_init(&dev.lock); |
812 | rcuwait_init(w: &dev.wait); |
813 | |
814 | res = request_irq(irq, handler: ps3_notification_interrupt, flags: 0, |
815 | name: "ps3_notification" , dev: &dev); |
816 | if (res) { |
817 | pr_err("%s:%u: request_irq failed %d\n" , __func__, __LINE__, |
818 | res); |
819 | goto fail_sb_event_receive_port_destroy; |
820 | } |
821 | |
822 | /* Setup and write the request for device notification. */ |
823 | notify_cmd->operation_code = 0; /* must be zero */ |
824 | notify_cmd->event_mask = 1UL << notify_region_probe; |
825 | |
826 | res = ps3_notification_read_write(dev: &dev, lpar, write: 1); |
827 | if (res) |
828 | goto fail_free_irq; |
829 | |
830 | /* Loop here processing the requested notification events. */ |
831 | do { |
832 | try_to_freeze(); |
833 | |
834 | memset(notify_event, 0, sizeof(*notify_event)); |
835 | |
836 | res = ps3_notification_read_write(dev: &dev, lpar, write: 0); |
837 | if (res) |
838 | break; |
839 | |
840 | pr_debug("%s:%u: notify event type 0x%llx bus id %llu dev id %llu" |
841 | " type %llu port %llu\n" , __func__, __LINE__, |
842 | notify_event->event_type, notify_event->bus_id, |
843 | notify_event->dev_id, notify_event->dev_type, |
844 | notify_event->dev_port); |
845 | |
846 | if (notify_event->event_type != notify_region_probe || |
847 | notify_event->bus_id != dev.sbd.bus_id) { |
848 | pr_warn("%s:%u: bad notify_event: event %llu, dev_id %llu, dev_type %llu\n" , |
849 | __func__, __LINE__, notify_event->event_type, |
850 | notify_event->dev_id, notify_event->dev_type); |
851 | continue; |
852 | } |
853 | |
854 | ps3_find_and_add_device(bus_id: dev.sbd.bus_id, dev_id: notify_event->dev_id); |
855 | |
856 | } while (!kthread_should_stop()); |
857 | |
858 | fail_free_irq: |
859 | free_irq(irq, &dev); |
860 | fail_sb_event_receive_port_destroy: |
861 | ps3_sb_event_receive_port_destroy(&dev.sbd, irq); |
862 | fail_close_device: |
863 | lv1_close_device(dev.sbd.bus_id, dev.sbd.dev_id); |
864 | fail_free: |
865 | kfree(objp: buf); |
866 | |
867 | probe_task = NULL; |
868 | |
869 | pr_debug(" <- %s:%u: kthread finished\n" , __func__, __LINE__); |
870 | |
871 | return 0; |
872 | } |
873 | |
874 | /** |
875 | * ps3_stop_probe_thread - Stops the background probe thread. |
876 | * |
877 | */ |
878 | |
879 | static int ps3_stop_probe_thread(struct notifier_block *nb, unsigned long code, |
880 | void *data) |
881 | { |
882 | if (probe_task) |
883 | kthread_stop(k: probe_task); |
884 | return 0; |
885 | } |
886 | |
887 | static struct notifier_block nb = { |
888 | .notifier_call = ps3_stop_probe_thread |
889 | }; |
890 | |
891 | /** |
892 | * ps3_start_probe_thread - Starts the background probe thread. |
893 | * |
894 | */ |
895 | |
896 | static int __init ps3_start_probe_thread(enum ps3_bus_type bus_type) |
897 | { |
898 | int result; |
899 | struct task_struct *task; |
900 | struct ps3_repository_device repo; |
901 | |
902 | pr_debug(" -> %s:%d\n" , __func__, __LINE__); |
903 | |
904 | memset(&repo, 0, sizeof(repo)); |
905 | |
906 | repo.bus_type = bus_type; |
907 | |
908 | result = ps3_repository_find_bus(bus_type: repo.bus_type, from: 0, bus_index: &repo.bus_index); |
909 | |
910 | if (result) { |
911 | printk(KERN_ERR "%s: Cannot find bus (%d)\n" , __func__, result); |
912 | return -ENODEV; |
913 | } |
914 | |
915 | result = ps3_repository_read_bus_id(bus_index: repo.bus_index, bus_id: &repo.bus_id); |
916 | |
917 | if (result) { |
918 | printk(KERN_ERR "%s: read_bus_id failed %d\n" , __func__, |
919 | result); |
920 | return -ENODEV; |
921 | } |
922 | |
923 | task = kthread_run(ps3_probe_thread, (void *)repo.bus_id, |
924 | "ps3-probe-%u" , bus_type); |
925 | |
926 | if (IS_ERR(ptr: task)) { |
927 | result = PTR_ERR(ptr: task); |
928 | printk(KERN_ERR "%s: kthread_run failed %d\n" , __func__, |
929 | result); |
930 | return result; |
931 | } |
932 | |
933 | probe_task = task; |
934 | register_reboot_notifier(&nb); |
935 | |
936 | pr_debug(" <- %s:%d\n" , __func__, __LINE__); |
937 | return 0; |
938 | } |
939 | |
940 | /** |
941 | * ps3_register_devices - Probe the system and register devices found. |
942 | * |
943 | * A device_initcall() routine. |
944 | */ |
945 | |
946 | static int __init ps3_register_devices(void) |
947 | { |
948 | int result; |
949 | |
950 | if (!firmware_has_feature(FW_FEATURE_PS3_LV1)) |
951 | return -ENODEV; |
952 | |
953 | pr_debug(" -> %s:%d\n" , __func__, __LINE__); |
954 | |
955 | /* ps3_repository_dump_bus_info(); */ |
956 | |
957 | result = ps3_start_probe_thread(bus_type: PS3_BUS_TYPE_STORAGE); |
958 | |
959 | ps3_register_vuart_devices(); |
960 | |
961 | ps3_register_graphics_devices(); |
962 | |
963 | ps3_repository_find_devices(bus_type: PS3_BUS_TYPE_SB, callback: ps3_setup_static_device); |
964 | |
965 | ps3_register_sound_devices(); |
966 | |
967 | ps3_register_lpm_devices(); |
968 | |
969 | ps3_register_ramdisk_device(); |
970 | |
971 | pr_debug(" <- %s:%d\n" , __func__, __LINE__); |
972 | return 0; |
973 | } |
974 | |
975 | device_initcall(ps3_register_devices); |
976 | |