1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * PS3 system bus driver. |
4 | * |
5 | * Copyright (C) 2006 Sony Computer Entertainment Inc. |
6 | * Copyright 2006 Sony Corp. |
7 | */ |
8 | |
9 | #include <linux/kernel.h> |
10 | #include <linux/init.h> |
11 | #include <linux/export.h> |
12 | #include <linux/dma-map-ops.h> |
13 | #include <linux/err.h> |
14 | #include <linux/slab.h> |
15 | |
16 | #include <asm/udbg.h> |
17 | #include <asm/lv1call.h> |
18 | #include <asm/firmware.h> |
19 | #include <asm/cell-regs.h> |
20 | |
21 | #include "platform.h" |
22 | |
23 | static struct device ps3_system_bus = { |
24 | .init_name = "ps3_system" , |
25 | }; |
26 | |
27 | /* FIXME: need device usage counters! */ |
28 | static struct { |
29 | struct mutex mutex; |
30 | int sb_11; /* usb 0 */ |
31 | int sb_12; /* usb 0 */ |
32 | int gpu; |
33 | } usage_hack; |
34 | |
35 | static int ps3_is_device(struct ps3_system_bus_device *dev, u64 bus_id, |
36 | u64 dev_id) |
37 | { |
38 | return dev->bus_id == bus_id && dev->dev_id == dev_id; |
39 | } |
40 | |
41 | static int ps3_open_hv_device_sb(struct ps3_system_bus_device *dev) |
42 | { |
43 | int result; |
44 | |
45 | BUG_ON(!dev->bus_id); |
46 | mutex_lock(&usage_hack.mutex); |
47 | |
48 | if (ps3_is_device(dev, bus_id: 1, dev_id: 1)) { |
49 | usage_hack.sb_11++; |
50 | if (usage_hack.sb_11 > 1) { |
51 | result = 0; |
52 | goto done; |
53 | } |
54 | } |
55 | |
56 | if (ps3_is_device(dev, bus_id: 1, dev_id: 2)) { |
57 | usage_hack.sb_12++; |
58 | if (usage_hack.sb_12 > 1) { |
59 | result = 0; |
60 | goto done; |
61 | } |
62 | } |
63 | |
64 | result = lv1_open_device(dev->bus_id, dev->dev_id, 0); |
65 | |
66 | if (result) { |
67 | pr_warn("%s:%d: lv1_open_device dev=%u.%u(%s) failed: %s\n" , |
68 | __func__, __LINE__, dev->match_id, dev->match_sub_id, |
69 | dev_name(&dev->core), ps3_result(result)); |
70 | result = -EPERM; |
71 | } |
72 | |
73 | done: |
74 | mutex_unlock(lock: &usage_hack.mutex); |
75 | return result; |
76 | } |
77 | |
78 | static int ps3_close_hv_device_sb(struct ps3_system_bus_device *dev) |
79 | { |
80 | int result; |
81 | |
82 | BUG_ON(!dev->bus_id); |
83 | mutex_lock(&usage_hack.mutex); |
84 | |
85 | if (ps3_is_device(dev, bus_id: 1, dev_id: 1)) { |
86 | usage_hack.sb_11--; |
87 | if (usage_hack.sb_11) { |
88 | result = 0; |
89 | goto done; |
90 | } |
91 | } |
92 | |
93 | if (ps3_is_device(dev, bus_id: 1, dev_id: 2)) { |
94 | usage_hack.sb_12--; |
95 | if (usage_hack.sb_12) { |
96 | result = 0; |
97 | goto done; |
98 | } |
99 | } |
100 | |
101 | result = lv1_close_device(dev->bus_id, dev->dev_id); |
102 | BUG_ON(result); |
103 | |
104 | done: |
105 | mutex_unlock(lock: &usage_hack.mutex); |
106 | return result; |
107 | } |
108 | |
109 | static int ps3_open_hv_device_gpu(struct ps3_system_bus_device *dev) |
110 | { |
111 | int result; |
112 | |
113 | mutex_lock(&usage_hack.mutex); |
114 | |
115 | usage_hack.gpu++; |
116 | if (usage_hack.gpu > 1) { |
117 | result = 0; |
118 | goto done; |
119 | } |
120 | |
121 | result = lv1_gpu_open(0); |
122 | |
123 | if (result) { |
124 | pr_warn("%s:%d: lv1_gpu_open failed: %s\n" , __func__, |
125 | __LINE__, ps3_result(result)); |
126 | result = -EPERM; |
127 | } |
128 | |
129 | done: |
130 | mutex_unlock(lock: &usage_hack.mutex); |
131 | return result; |
132 | } |
133 | |
134 | static int ps3_close_hv_device_gpu(struct ps3_system_bus_device *dev) |
135 | { |
136 | int result; |
137 | |
138 | mutex_lock(&usage_hack.mutex); |
139 | |
140 | usage_hack.gpu--; |
141 | if (usage_hack.gpu) { |
142 | result = 0; |
143 | goto done; |
144 | } |
145 | |
146 | result = lv1_gpu_close(); |
147 | BUG_ON(result); |
148 | |
149 | done: |
150 | mutex_unlock(lock: &usage_hack.mutex); |
151 | return result; |
152 | } |
153 | |
154 | int ps3_open_hv_device(struct ps3_system_bus_device *dev) |
155 | { |
156 | BUG_ON(!dev); |
157 | pr_debug("%s:%d: match_id: %u\n" , __func__, __LINE__, dev->match_id); |
158 | |
159 | switch (dev->match_id) { |
160 | case PS3_MATCH_ID_EHCI: |
161 | case PS3_MATCH_ID_OHCI: |
162 | case PS3_MATCH_ID_GELIC: |
163 | case PS3_MATCH_ID_STOR_DISK: |
164 | case PS3_MATCH_ID_STOR_ROM: |
165 | case PS3_MATCH_ID_STOR_FLASH: |
166 | return ps3_open_hv_device_sb(dev); |
167 | |
168 | case PS3_MATCH_ID_SOUND: |
169 | case PS3_MATCH_ID_GPU: |
170 | return ps3_open_hv_device_gpu(dev); |
171 | |
172 | case PS3_MATCH_ID_AV_SETTINGS: |
173 | case PS3_MATCH_ID_SYSTEM_MANAGER: |
174 | pr_debug("%s:%d: unsupported match_id: %u\n" , __func__, |
175 | __LINE__, dev->match_id); |
176 | pr_debug("%s:%d: bus_id: %llu\n" , __func__, __LINE__, |
177 | dev->bus_id); |
178 | BUG(); |
179 | return -EINVAL; |
180 | |
181 | default: |
182 | break; |
183 | } |
184 | |
185 | pr_debug("%s:%d: unknown match_id: %u\n" , __func__, __LINE__, |
186 | dev->match_id); |
187 | BUG(); |
188 | return -ENODEV; |
189 | } |
190 | EXPORT_SYMBOL_GPL(ps3_open_hv_device); |
191 | |
192 | int ps3_close_hv_device(struct ps3_system_bus_device *dev) |
193 | { |
194 | BUG_ON(!dev); |
195 | pr_debug("%s:%d: match_id: %u\n" , __func__, __LINE__, dev->match_id); |
196 | |
197 | switch (dev->match_id) { |
198 | case PS3_MATCH_ID_EHCI: |
199 | case PS3_MATCH_ID_OHCI: |
200 | case PS3_MATCH_ID_GELIC: |
201 | case PS3_MATCH_ID_STOR_DISK: |
202 | case PS3_MATCH_ID_STOR_ROM: |
203 | case PS3_MATCH_ID_STOR_FLASH: |
204 | return ps3_close_hv_device_sb(dev); |
205 | |
206 | case PS3_MATCH_ID_SOUND: |
207 | case PS3_MATCH_ID_GPU: |
208 | return ps3_close_hv_device_gpu(dev); |
209 | |
210 | case PS3_MATCH_ID_AV_SETTINGS: |
211 | case PS3_MATCH_ID_SYSTEM_MANAGER: |
212 | pr_debug("%s:%d: unsupported match_id: %u\n" , __func__, |
213 | __LINE__, dev->match_id); |
214 | pr_debug("%s:%d: bus_id: %llu\n" , __func__, __LINE__, |
215 | dev->bus_id); |
216 | BUG(); |
217 | return -EINVAL; |
218 | |
219 | default: |
220 | break; |
221 | } |
222 | |
223 | pr_debug("%s:%d: unknown match_id: %u\n" , __func__, __LINE__, |
224 | dev->match_id); |
225 | BUG(); |
226 | return -ENODEV; |
227 | } |
228 | EXPORT_SYMBOL_GPL(ps3_close_hv_device); |
229 | |
230 | #define dump_mmio_region(_a) _dump_mmio_region(_a, __func__, __LINE__) |
231 | static void _dump_mmio_region(const struct ps3_mmio_region* r, |
232 | const char* func, int line) |
233 | { |
234 | pr_debug("%s:%d: dev %llu:%llu\n" , func, line, r->dev->bus_id, |
235 | r->dev->dev_id); |
236 | pr_debug("%s:%d: bus_addr %lxh\n" , func, line, r->bus_addr); |
237 | pr_debug("%s:%d: len %lxh\n" , func, line, r->len); |
238 | pr_debug("%s:%d: lpar_addr %lxh\n" , func, line, r->lpar_addr); |
239 | } |
240 | |
241 | static int ps3_sb_mmio_region_create(struct ps3_mmio_region *r) |
242 | { |
243 | int result; |
244 | u64 lpar_addr; |
245 | |
246 | result = lv1_map_device_mmio_region(r->dev->bus_id, r->dev->dev_id, |
247 | r->bus_addr, r->len, r->page_size, &lpar_addr); |
248 | r->lpar_addr = lpar_addr; |
249 | |
250 | if (result) { |
251 | pr_debug("%s:%d: lv1_map_device_mmio_region failed: %s\n" , |
252 | __func__, __LINE__, ps3_result(result)); |
253 | r->lpar_addr = 0; |
254 | } |
255 | |
256 | dump_mmio_region(r); |
257 | return result; |
258 | } |
259 | |
260 | static int ps3_ioc0_mmio_region_create(struct ps3_mmio_region *r) |
261 | { |
262 | /* device specific; do nothing currently */ |
263 | return 0; |
264 | } |
265 | |
266 | int ps3_mmio_region_create(struct ps3_mmio_region *r) |
267 | { |
268 | return r->mmio_ops->create(r); |
269 | } |
270 | EXPORT_SYMBOL_GPL(ps3_mmio_region_create); |
271 | |
272 | static int ps3_sb_free_mmio_region(struct ps3_mmio_region *r) |
273 | { |
274 | int result; |
275 | |
276 | dump_mmio_region(r); |
277 | result = lv1_unmap_device_mmio_region(r->dev->bus_id, r->dev->dev_id, |
278 | r->lpar_addr); |
279 | |
280 | if (result) |
281 | pr_debug("%s:%d: lv1_unmap_device_mmio_region failed: %s\n" , |
282 | __func__, __LINE__, ps3_result(result)); |
283 | |
284 | r->lpar_addr = 0; |
285 | return result; |
286 | } |
287 | |
288 | static int ps3_ioc0_free_mmio_region(struct ps3_mmio_region *r) |
289 | { |
290 | /* device specific; do nothing currently */ |
291 | return 0; |
292 | } |
293 | |
294 | |
295 | int ps3_free_mmio_region(struct ps3_mmio_region *r) |
296 | { |
297 | return r->mmio_ops->free(r); |
298 | } |
299 | |
300 | EXPORT_SYMBOL_GPL(ps3_free_mmio_region); |
301 | |
302 | static const struct ps3_mmio_region_ops ps3_mmio_sb_region_ops = { |
303 | .create = ps3_sb_mmio_region_create, |
304 | .free = ps3_sb_free_mmio_region |
305 | }; |
306 | |
307 | static const struct ps3_mmio_region_ops ps3_mmio_ioc0_region_ops = { |
308 | .create = ps3_ioc0_mmio_region_create, |
309 | .free = ps3_ioc0_free_mmio_region |
310 | }; |
311 | |
312 | int ps3_mmio_region_init(struct ps3_system_bus_device *dev, |
313 | struct ps3_mmio_region *r, unsigned long bus_addr, unsigned long len, |
314 | enum ps3_mmio_page_size page_size) |
315 | { |
316 | r->dev = dev; |
317 | r->bus_addr = bus_addr; |
318 | r->len = len; |
319 | r->page_size = page_size; |
320 | switch (dev->dev_type) { |
321 | case PS3_DEVICE_TYPE_SB: |
322 | r->mmio_ops = &ps3_mmio_sb_region_ops; |
323 | break; |
324 | case PS3_DEVICE_TYPE_IOC0: |
325 | r->mmio_ops = &ps3_mmio_ioc0_region_ops; |
326 | break; |
327 | default: |
328 | BUG(); |
329 | return -EINVAL; |
330 | } |
331 | return 0; |
332 | } |
333 | EXPORT_SYMBOL_GPL(ps3_mmio_region_init); |
334 | |
335 | static int ps3_system_bus_match(struct device *_dev, |
336 | struct device_driver *_drv) |
337 | { |
338 | int result; |
339 | struct ps3_system_bus_driver *drv = ps3_drv_to_system_bus_drv(_drv); |
340 | struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); |
341 | |
342 | if (!dev->match_sub_id) |
343 | result = dev->match_id == drv->match_id; |
344 | else |
345 | result = dev->match_sub_id == drv->match_sub_id && |
346 | dev->match_id == drv->match_id; |
347 | |
348 | if (result) |
349 | pr_info("%s:%d: dev=%u.%u(%s), drv=%u.%u(%s): match\n" , |
350 | __func__, __LINE__, |
351 | dev->match_id, dev->match_sub_id, dev_name(&dev->core), |
352 | drv->match_id, drv->match_sub_id, drv->core.name); |
353 | else |
354 | pr_debug("%s:%d: dev=%u.%u(%s), drv=%u.%u(%s): miss\n" , |
355 | __func__, __LINE__, |
356 | dev->match_id, dev->match_sub_id, dev_name(&dev->core), |
357 | drv->match_id, drv->match_sub_id, drv->core.name); |
358 | |
359 | return result; |
360 | } |
361 | |
362 | static int ps3_system_bus_probe(struct device *_dev) |
363 | { |
364 | int result = 0; |
365 | struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); |
366 | struct ps3_system_bus_driver *drv; |
367 | |
368 | BUG_ON(!dev); |
369 | dev_dbg(_dev, "%s:%d\n" , __func__, __LINE__); |
370 | |
371 | drv = ps3_system_bus_dev_to_system_bus_drv(dev); |
372 | BUG_ON(!drv); |
373 | |
374 | if (drv->probe) |
375 | result = drv->probe(dev); |
376 | else |
377 | pr_debug("%s:%d: %s no probe method\n" , __func__, __LINE__, |
378 | dev_name(&dev->core)); |
379 | |
380 | pr_debug(" <- %s:%d: %s\n" , __func__, __LINE__, dev_name(&dev->core)); |
381 | return result; |
382 | } |
383 | |
384 | static void ps3_system_bus_remove(struct device *_dev) |
385 | { |
386 | struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); |
387 | struct ps3_system_bus_driver *drv; |
388 | |
389 | BUG_ON(!dev); |
390 | dev_dbg(_dev, "%s:%d\n" , __func__, __LINE__); |
391 | |
392 | drv = ps3_system_bus_dev_to_system_bus_drv(dev); |
393 | BUG_ON(!drv); |
394 | |
395 | if (drv->remove) |
396 | drv->remove(dev); |
397 | else |
398 | dev_dbg(&dev->core, "%s:%d %s: no remove method\n" , |
399 | __func__, __LINE__, drv->core.name); |
400 | |
401 | pr_debug(" <- %s:%d: %s\n" , __func__, __LINE__, dev_name(&dev->core)); |
402 | } |
403 | |
404 | static void ps3_system_bus_shutdown(struct device *_dev) |
405 | { |
406 | struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); |
407 | struct ps3_system_bus_driver *drv; |
408 | |
409 | BUG_ON(!dev); |
410 | |
411 | dev_dbg(&dev->core, " -> %s:%d: match_id %d\n" , __func__, __LINE__, |
412 | dev->match_id); |
413 | |
414 | if (!dev->core.driver) { |
415 | dev_dbg(&dev->core, "%s:%d: no driver bound\n" , __func__, |
416 | __LINE__); |
417 | return; |
418 | } |
419 | |
420 | drv = ps3_system_bus_dev_to_system_bus_drv(dev); |
421 | |
422 | BUG_ON(!drv); |
423 | |
424 | dev_dbg(&dev->core, "%s:%d: %s -> %s\n" , __func__, __LINE__, |
425 | dev_name(&dev->core), drv->core.name); |
426 | |
427 | if (drv->shutdown) |
428 | drv->shutdown(dev); |
429 | else if (drv->remove) { |
430 | dev_dbg(&dev->core, "%s:%d %s: no shutdown, calling remove\n" , |
431 | __func__, __LINE__, drv->core.name); |
432 | drv->remove(dev); |
433 | } else { |
434 | dev_dbg(&dev->core, "%s:%d %s: no shutdown method\n" , |
435 | __func__, __LINE__, drv->core.name); |
436 | BUG(); |
437 | } |
438 | |
439 | dev_dbg(&dev->core, " <- %s:%d\n" , __func__, __LINE__); |
440 | } |
441 | |
442 | static int ps3_system_bus_uevent(const struct device *_dev, struct kobj_uevent_env *env) |
443 | { |
444 | struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); |
445 | |
446 | if (add_uevent_var(env, format: "MODALIAS=ps3:%d:%d" , dev->match_id, |
447 | dev->match_sub_id)) |
448 | return -ENOMEM; |
449 | return 0; |
450 | } |
451 | |
452 | static ssize_t modalias_show(struct device *_dev, struct device_attribute *a, |
453 | char *buf) |
454 | { |
455 | struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); |
456 | int len = snprintf(buf, PAGE_SIZE, fmt: "ps3:%d:%d\n" , dev->match_id, |
457 | dev->match_sub_id); |
458 | |
459 | return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len; |
460 | } |
461 | static DEVICE_ATTR_RO(modalias); |
462 | |
463 | static struct attribute *ps3_system_bus_dev_attrs[] = { |
464 | &dev_attr_modalias.attr, |
465 | NULL, |
466 | }; |
467 | ATTRIBUTE_GROUPS(ps3_system_bus_dev); |
468 | |
469 | static struct bus_type ps3_system_bus_type = { |
470 | .name = "ps3_system_bus" , |
471 | .match = ps3_system_bus_match, |
472 | .uevent = ps3_system_bus_uevent, |
473 | .probe = ps3_system_bus_probe, |
474 | .remove = ps3_system_bus_remove, |
475 | .shutdown = ps3_system_bus_shutdown, |
476 | .dev_groups = ps3_system_bus_dev_groups, |
477 | }; |
478 | |
479 | static int __init ps3_system_bus_init(void) |
480 | { |
481 | int result; |
482 | |
483 | if (!firmware_has_feature(FW_FEATURE_PS3_LV1)) |
484 | return -ENODEV; |
485 | |
486 | pr_debug(" -> %s:%d\n" , __func__, __LINE__); |
487 | |
488 | mutex_init(&usage_hack.mutex); |
489 | |
490 | result = device_register(dev: &ps3_system_bus); |
491 | BUG_ON(result); |
492 | |
493 | result = bus_register(bus: &ps3_system_bus_type); |
494 | BUG_ON(result); |
495 | |
496 | pr_debug(" <- %s:%d\n" , __func__, __LINE__); |
497 | return result; |
498 | } |
499 | |
500 | core_initcall(ps3_system_bus_init); |
501 | |
502 | /* Allocates a contiguous real buffer and creates mappings over it. |
503 | * Returns the virtual address of the buffer and sets dma_handle |
504 | * to the dma address (mapping) of the first page. |
505 | */ |
506 | static void * ps3_alloc_coherent(struct device *_dev, size_t size, |
507 | dma_addr_t *dma_handle, gfp_t flag, |
508 | unsigned long attrs) |
509 | { |
510 | int result; |
511 | struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); |
512 | unsigned long virt_addr; |
513 | |
514 | flag &= ~(__GFP_DMA | __GFP_HIGHMEM); |
515 | flag |= __GFP_ZERO; |
516 | |
517 | virt_addr = __get_free_pages(gfp_mask: flag, order: get_order(size)); |
518 | |
519 | if (!virt_addr) { |
520 | pr_debug("%s:%d: get_free_pages failed\n" , __func__, __LINE__); |
521 | goto clean_none; |
522 | } |
523 | |
524 | result = ps3_dma_map(dev->d_region, virt_addr, size, dma_handle, |
525 | CBE_IOPTE_PP_W | CBE_IOPTE_PP_R | |
526 | CBE_IOPTE_SO_RW | CBE_IOPTE_M); |
527 | |
528 | if (result) { |
529 | pr_debug("%s:%d: ps3_dma_map failed (%d)\n" , |
530 | __func__, __LINE__, result); |
531 | BUG_ON("check region type" ); |
532 | goto clean_alloc; |
533 | } |
534 | |
535 | return (void*)virt_addr; |
536 | |
537 | clean_alloc: |
538 | free_pages(addr: virt_addr, order: get_order(size)); |
539 | clean_none: |
540 | dma_handle = NULL; |
541 | return NULL; |
542 | } |
543 | |
544 | static void ps3_free_coherent(struct device *_dev, size_t size, void *vaddr, |
545 | dma_addr_t dma_handle, unsigned long attrs) |
546 | { |
547 | struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); |
548 | |
549 | ps3_dma_unmap(dev->d_region, dma_handle, size); |
550 | free_pages(addr: (unsigned long)vaddr, order: get_order(size)); |
551 | } |
552 | |
553 | /* Creates TCEs for a user provided buffer. The user buffer must be |
554 | * contiguous real kernel storage (not vmalloc). The address passed here |
555 | * comprises a page address and offset into that page. The dma_addr_t |
556 | * returned will point to the same byte within the page as was passed in. |
557 | */ |
558 | |
559 | static dma_addr_t ps3_sb_map_page(struct device *_dev, struct page *page, |
560 | unsigned long offset, size_t size, enum dma_data_direction direction, |
561 | unsigned long attrs) |
562 | { |
563 | struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); |
564 | int result; |
565 | dma_addr_t bus_addr; |
566 | void *ptr = page_address(page) + offset; |
567 | |
568 | result = ps3_dma_map(dev->d_region, (unsigned long)ptr, size, |
569 | &bus_addr, |
570 | CBE_IOPTE_PP_R | CBE_IOPTE_PP_W | |
571 | CBE_IOPTE_SO_RW | CBE_IOPTE_M); |
572 | |
573 | if (result) { |
574 | pr_debug("%s:%d: ps3_dma_map failed (%d)\n" , |
575 | __func__, __LINE__, result); |
576 | } |
577 | |
578 | return bus_addr; |
579 | } |
580 | |
581 | static dma_addr_t ps3_ioc0_map_page(struct device *_dev, struct page *page, |
582 | unsigned long offset, size_t size, |
583 | enum dma_data_direction direction, |
584 | unsigned long attrs) |
585 | { |
586 | struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); |
587 | int result; |
588 | dma_addr_t bus_addr; |
589 | u64 iopte_flag; |
590 | void *ptr = page_address(page) + offset; |
591 | |
592 | iopte_flag = CBE_IOPTE_M; |
593 | switch (direction) { |
594 | case DMA_BIDIRECTIONAL: |
595 | iopte_flag |= CBE_IOPTE_PP_R | CBE_IOPTE_PP_W | CBE_IOPTE_SO_RW; |
596 | break; |
597 | case DMA_TO_DEVICE: |
598 | iopte_flag |= CBE_IOPTE_PP_R | CBE_IOPTE_SO_R; |
599 | break; |
600 | case DMA_FROM_DEVICE: |
601 | iopte_flag |= CBE_IOPTE_PP_W | CBE_IOPTE_SO_RW; |
602 | break; |
603 | default: |
604 | /* not happened */ |
605 | BUG(); |
606 | } |
607 | result = ps3_dma_map(dev->d_region, (unsigned long)ptr, size, |
608 | &bus_addr, iopte_flag); |
609 | |
610 | if (result) { |
611 | pr_debug("%s:%d: ps3_dma_map failed (%d)\n" , |
612 | __func__, __LINE__, result); |
613 | } |
614 | return bus_addr; |
615 | } |
616 | |
617 | static void ps3_unmap_page(struct device *_dev, dma_addr_t dma_addr, |
618 | size_t size, enum dma_data_direction direction, unsigned long attrs) |
619 | { |
620 | struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); |
621 | int result; |
622 | |
623 | result = ps3_dma_unmap(dev->d_region, dma_addr, size); |
624 | |
625 | if (result) { |
626 | pr_debug("%s:%d: ps3_dma_unmap failed (%d)\n" , |
627 | __func__, __LINE__, result); |
628 | } |
629 | } |
630 | |
631 | static int ps3_sb_map_sg(struct device *_dev, struct scatterlist *sgl, |
632 | int nents, enum dma_data_direction direction, unsigned long attrs) |
633 | { |
634 | #if defined(CONFIG_PS3_DYNAMIC_DMA) |
635 | BUG_ON("do" ); |
636 | return -EPERM; |
637 | #else |
638 | struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); |
639 | struct scatterlist *sg; |
640 | int i; |
641 | |
642 | for_each_sg(sgl, sg, nents, i) { |
643 | int result = ps3_dma_map(dev->d_region, sg_phys(sg), |
644 | sg->length, &sg->dma_address, 0); |
645 | |
646 | if (result) { |
647 | pr_debug("%s:%d: ps3_dma_map failed (%d)\n" , |
648 | __func__, __LINE__, result); |
649 | return -EINVAL; |
650 | } |
651 | |
652 | sg->dma_length = sg->length; |
653 | } |
654 | |
655 | return nents; |
656 | #endif |
657 | } |
658 | |
659 | static int ps3_ioc0_map_sg(struct device *_dev, struct scatterlist *sg, |
660 | int nents, |
661 | enum dma_data_direction direction, |
662 | unsigned long attrs) |
663 | { |
664 | BUG(); |
665 | return -EINVAL; |
666 | } |
667 | |
668 | static void ps3_sb_unmap_sg(struct device *_dev, struct scatterlist *sg, |
669 | int nents, enum dma_data_direction direction, unsigned long attrs) |
670 | { |
671 | #if defined(CONFIG_PS3_DYNAMIC_DMA) |
672 | BUG_ON("do" ); |
673 | #endif |
674 | } |
675 | |
676 | static void ps3_ioc0_unmap_sg(struct device *_dev, struct scatterlist *sg, |
677 | int nents, enum dma_data_direction direction, |
678 | unsigned long attrs) |
679 | { |
680 | BUG(); |
681 | } |
682 | |
683 | static int ps3_dma_supported(struct device *_dev, u64 mask) |
684 | { |
685 | return mask >= DMA_BIT_MASK(32); |
686 | } |
687 | |
688 | static const struct dma_map_ops ps3_sb_dma_ops = { |
689 | .alloc = ps3_alloc_coherent, |
690 | .free = ps3_free_coherent, |
691 | .map_sg = ps3_sb_map_sg, |
692 | .unmap_sg = ps3_sb_unmap_sg, |
693 | .dma_supported = ps3_dma_supported, |
694 | .map_page = ps3_sb_map_page, |
695 | .unmap_page = ps3_unmap_page, |
696 | .mmap = dma_common_mmap, |
697 | .get_sgtable = dma_common_get_sgtable, |
698 | .alloc_pages = dma_common_alloc_pages, |
699 | .free_pages = dma_common_free_pages, |
700 | }; |
701 | |
702 | static const struct dma_map_ops ps3_ioc0_dma_ops = { |
703 | .alloc = ps3_alloc_coherent, |
704 | .free = ps3_free_coherent, |
705 | .map_sg = ps3_ioc0_map_sg, |
706 | .unmap_sg = ps3_ioc0_unmap_sg, |
707 | .dma_supported = ps3_dma_supported, |
708 | .map_page = ps3_ioc0_map_page, |
709 | .unmap_page = ps3_unmap_page, |
710 | .mmap = dma_common_mmap, |
711 | .get_sgtable = dma_common_get_sgtable, |
712 | .alloc_pages = dma_common_alloc_pages, |
713 | .free_pages = dma_common_free_pages, |
714 | }; |
715 | |
716 | /** |
717 | * ps3_system_bus_release_device - remove a device from the system bus |
718 | */ |
719 | |
720 | static void ps3_system_bus_release_device(struct device *_dev) |
721 | { |
722 | struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); |
723 | kfree(objp: dev); |
724 | } |
725 | |
726 | /** |
727 | * ps3_system_bus_device_register - add a device to the system bus |
728 | * |
729 | * ps3_system_bus_device_register() expects the dev object to be allocated |
730 | * dynamically by the caller. The system bus takes ownership of the dev |
731 | * object and frees the object in ps3_system_bus_release_device(). |
732 | */ |
733 | |
734 | int ps3_system_bus_device_register(struct ps3_system_bus_device *dev) |
735 | { |
736 | int result; |
737 | static unsigned int dev_ioc0_count; |
738 | static unsigned int dev_sb_count; |
739 | static unsigned int dev_vuart_count; |
740 | static unsigned int dev_lpm_count; |
741 | |
742 | if (!dev->core.parent) |
743 | dev->core.parent = &ps3_system_bus; |
744 | dev->core.bus = &ps3_system_bus_type; |
745 | dev->core.release = ps3_system_bus_release_device; |
746 | |
747 | switch (dev->dev_type) { |
748 | case PS3_DEVICE_TYPE_IOC0: |
749 | dev->core.dma_ops = &ps3_ioc0_dma_ops; |
750 | dev_set_name(dev: &dev->core, name: "ioc0_%02x" , ++dev_ioc0_count); |
751 | break; |
752 | case PS3_DEVICE_TYPE_SB: |
753 | dev->core.dma_ops = &ps3_sb_dma_ops; |
754 | dev_set_name(dev: &dev->core, name: "sb_%02x" , ++dev_sb_count); |
755 | |
756 | break; |
757 | case PS3_DEVICE_TYPE_VUART: |
758 | dev_set_name(dev: &dev->core, name: "vuart_%02x" , ++dev_vuart_count); |
759 | break; |
760 | case PS3_DEVICE_TYPE_LPM: |
761 | dev_set_name(dev: &dev->core, name: "lpm_%02x" , ++dev_lpm_count); |
762 | break; |
763 | default: |
764 | BUG(); |
765 | } |
766 | |
767 | dev->core.of_node = NULL; |
768 | set_dev_node(dev: &dev->core, node: 0); |
769 | |
770 | pr_debug("%s:%d add %s\n" , __func__, __LINE__, dev_name(&dev->core)); |
771 | |
772 | result = device_register(dev: &dev->core); |
773 | return result; |
774 | } |
775 | |
776 | EXPORT_SYMBOL_GPL(ps3_system_bus_device_register); |
777 | |
778 | int ps3_system_bus_driver_register(struct ps3_system_bus_driver *drv) |
779 | { |
780 | int result; |
781 | |
782 | pr_debug(" -> %s:%d: %s\n" , __func__, __LINE__, drv->core.name); |
783 | |
784 | if (!firmware_has_feature(FW_FEATURE_PS3_LV1)) |
785 | return -ENODEV; |
786 | |
787 | drv->core.bus = &ps3_system_bus_type; |
788 | |
789 | result = driver_register(drv: &drv->core); |
790 | pr_debug(" <- %s:%d: %s\n" , __func__, __LINE__, drv->core.name); |
791 | return result; |
792 | } |
793 | |
794 | EXPORT_SYMBOL_GPL(ps3_system_bus_driver_register); |
795 | |
796 | void ps3_system_bus_driver_unregister(struct ps3_system_bus_driver *drv) |
797 | { |
798 | pr_debug(" -> %s:%d: %s\n" , __func__, __LINE__, drv->core.name); |
799 | driver_unregister(drv: &drv->core); |
800 | pr_debug(" <- %s:%d: %s\n" , __func__, __LINE__, drv->core.name); |
801 | } |
802 | |
803 | EXPORT_SYMBOL_GPL(ps3_system_bus_driver_unregister); |
804 | |