12a08ea69SGeoff Levand /* 22a08ea69SGeoff Levand * PS3 system bus driver. 32a08ea69SGeoff Levand * 42a08ea69SGeoff Levand * Copyright (C) 2006 Sony Computer Entertainment Inc. 52a08ea69SGeoff Levand * Copyright 2006 Sony Corp. 62a08ea69SGeoff Levand * 72a08ea69SGeoff Levand * This program is free software; you can redistribute it and/or modify 82a08ea69SGeoff Levand * it under the terms of the GNU General Public License as published by 92a08ea69SGeoff Levand * the Free Software Foundation; version 2 of the License. 102a08ea69SGeoff Levand * 112a08ea69SGeoff Levand * This program is distributed in the hope that it will be useful, 122a08ea69SGeoff Levand * but WITHOUT ANY WARRANTY; without even the implied warranty of 132a08ea69SGeoff Levand * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 142a08ea69SGeoff Levand * GNU General Public License for more details. 152a08ea69SGeoff Levand * 162a08ea69SGeoff Levand * You should have received a copy of the GNU General Public License 172a08ea69SGeoff Levand * along with this program; if not, write to the Free Software 182a08ea69SGeoff Levand * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 192a08ea69SGeoff Levand */ 202a08ea69SGeoff Levand 212a08ea69SGeoff Levand #include <linux/kernel.h> 222a08ea69SGeoff Levand #include <linux/init.h> 232a08ea69SGeoff Levand #include <linux/module.h> 242a08ea69SGeoff Levand #include <linux/dma-mapping.h> 252a08ea69SGeoff Levand #include <linux/err.h> 262a08ea69SGeoff Levand 272a08ea69SGeoff Levand #include <asm/udbg.h> 282a08ea69SGeoff Levand #include <asm/lv1call.h> 292a08ea69SGeoff Levand #include <asm/firmware.h> 302a08ea69SGeoff Levand 312a08ea69SGeoff Levand #include "platform.h" 322a08ea69SGeoff Levand 336bb5cf10SGeoff Levand static struct device ps3_system_bus = { 346bb5cf10SGeoff Levand .bus_id = "ps3_system", 356bb5cf10SGeoff Levand }; 366bb5cf10SGeoff Levand 376bb5cf10SGeoff Levand /* FIXME: need device usage counters! */ 386bb5cf10SGeoff Levand struct { 396bb5cf10SGeoff Levand struct mutex mutex; 406bb5cf10SGeoff Levand int sb_11; /* usb 0 */ 416bb5cf10SGeoff Levand int sb_12; /* usb 0 */ 426bb5cf10SGeoff Levand int gpu; 436bb5cf10SGeoff Levand } static usage_hack; 446bb5cf10SGeoff Levand 45034e0ab5SGeert Uytterhoeven static int ps3_is_device(struct ps3_system_bus_device *dev, u64 bus_id, 46034e0ab5SGeert Uytterhoeven u64 dev_id) 476bb5cf10SGeoff Levand { 486bb5cf10SGeoff Levand return dev->bus_id == bus_id && dev->dev_id == dev_id; 496bb5cf10SGeoff Levand } 506bb5cf10SGeoff Levand 516bb5cf10SGeoff Levand static int ps3_open_hv_device_sb(struct ps3_system_bus_device *dev) 526bb5cf10SGeoff Levand { 536bb5cf10SGeoff Levand int result; 546bb5cf10SGeoff Levand 556bb5cf10SGeoff Levand BUG_ON(!dev->bus_id); 566bb5cf10SGeoff Levand mutex_lock(&usage_hack.mutex); 576bb5cf10SGeoff Levand 586bb5cf10SGeoff Levand if (ps3_is_device(dev, 1, 1)) { 596bb5cf10SGeoff Levand usage_hack.sb_11++; 606bb5cf10SGeoff Levand if (usage_hack.sb_11 > 1) { 616bb5cf10SGeoff Levand result = 0; 626bb5cf10SGeoff Levand goto done; 636bb5cf10SGeoff Levand } 646bb5cf10SGeoff Levand } 656bb5cf10SGeoff Levand 666bb5cf10SGeoff Levand if (ps3_is_device(dev, 1, 2)) { 676bb5cf10SGeoff Levand usage_hack.sb_12++; 686bb5cf10SGeoff Levand if (usage_hack.sb_12 > 1) { 696bb5cf10SGeoff Levand result = 0; 706bb5cf10SGeoff Levand goto done; 716bb5cf10SGeoff Levand } 726bb5cf10SGeoff Levand } 736bb5cf10SGeoff Levand 746bb5cf10SGeoff Levand result = lv1_open_device(dev->bus_id, dev->dev_id, 0); 756bb5cf10SGeoff Levand 766bb5cf10SGeoff Levand if (result) { 776bb5cf10SGeoff Levand pr_debug("%s:%d: lv1_open_device failed: %s\n", __func__, 786bb5cf10SGeoff Levand __LINE__, ps3_result(result)); 796bb5cf10SGeoff Levand result = -EPERM; 806bb5cf10SGeoff Levand } 816bb5cf10SGeoff Levand 826bb5cf10SGeoff Levand done: 836bb5cf10SGeoff Levand mutex_unlock(&usage_hack.mutex); 846bb5cf10SGeoff Levand return result; 856bb5cf10SGeoff Levand } 866bb5cf10SGeoff Levand 876bb5cf10SGeoff Levand static int ps3_close_hv_device_sb(struct ps3_system_bus_device *dev) 886bb5cf10SGeoff Levand { 896bb5cf10SGeoff Levand int result; 906bb5cf10SGeoff Levand 916bb5cf10SGeoff Levand BUG_ON(!dev->bus_id); 926bb5cf10SGeoff Levand mutex_lock(&usage_hack.mutex); 936bb5cf10SGeoff Levand 946bb5cf10SGeoff Levand if (ps3_is_device(dev, 1, 1)) { 956bb5cf10SGeoff Levand usage_hack.sb_11--; 966bb5cf10SGeoff Levand if (usage_hack.sb_11) { 976bb5cf10SGeoff Levand result = 0; 986bb5cf10SGeoff Levand goto done; 996bb5cf10SGeoff Levand } 1006bb5cf10SGeoff Levand } 1016bb5cf10SGeoff Levand 1026bb5cf10SGeoff Levand if (ps3_is_device(dev, 1, 2)) { 1036bb5cf10SGeoff Levand usage_hack.sb_12--; 1046bb5cf10SGeoff Levand if (usage_hack.sb_12) { 1056bb5cf10SGeoff Levand result = 0; 1066bb5cf10SGeoff Levand goto done; 1076bb5cf10SGeoff Levand } 1086bb5cf10SGeoff Levand } 1096bb5cf10SGeoff Levand 1106bb5cf10SGeoff Levand result = lv1_close_device(dev->bus_id, dev->dev_id); 1116bb5cf10SGeoff Levand BUG_ON(result); 1126bb5cf10SGeoff Levand 1136bb5cf10SGeoff Levand done: 1146bb5cf10SGeoff Levand mutex_unlock(&usage_hack.mutex); 1156bb5cf10SGeoff Levand return result; 1166bb5cf10SGeoff Levand } 1176bb5cf10SGeoff Levand 1186bb5cf10SGeoff Levand static int ps3_open_hv_device_gpu(struct ps3_system_bus_device *dev) 1196bb5cf10SGeoff Levand { 1206bb5cf10SGeoff Levand int result; 1216bb5cf10SGeoff Levand 1226bb5cf10SGeoff Levand mutex_lock(&usage_hack.mutex); 1236bb5cf10SGeoff Levand 1246bb5cf10SGeoff Levand usage_hack.gpu++; 1256bb5cf10SGeoff Levand if (usage_hack.gpu > 1) { 1266bb5cf10SGeoff Levand result = 0; 1276bb5cf10SGeoff Levand goto done; 1286bb5cf10SGeoff Levand } 1296bb5cf10SGeoff Levand 1306bb5cf10SGeoff Levand result = lv1_gpu_open(0); 1316bb5cf10SGeoff Levand 1326bb5cf10SGeoff Levand if (result) { 1336bb5cf10SGeoff Levand pr_debug("%s:%d: lv1_gpu_open failed: %s\n", __func__, 1346bb5cf10SGeoff Levand __LINE__, ps3_result(result)); 1356bb5cf10SGeoff Levand result = -EPERM; 1366bb5cf10SGeoff Levand } 1376bb5cf10SGeoff Levand 1386bb5cf10SGeoff Levand done: 1396bb5cf10SGeoff Levand mutex_unlock(&usage_hack.mutex); 1406bb5cf10SGeoff Levand return result; 1416bb5cf10SGeoff Levand } 1426bb5cf10SGeoff Levand 1436bb5cf10SGeoff Levand static int ps3_close_hv_device_gpu(struct ps3_system_bus_device *dev) 1446bb5cf10SGeoff Levand { 1456bb5cf10SGeoff Levand int result; 1466bb5cf10SGeoff Levand 1476bb5cf10SGeoff Levand mutex_lock(&usage_hack.mutex); 1486bb5cf10SGeoff Levand 1496bb5cf10SGeoff Levand usage_hack.gpu--; 1506bb5cf10SGeoff Levand if (usage_hack.gpu) { 1516bb5cf10SGeoff Levand result = 0; 1526bb5cf10SGeoff Levand goto done; 1536bb5cf10SGeoff Levand } 1546bb5cf10SGeoff Levand 1556bb5cf10SGeoff Levand result = lv1_gpu_close(); 1566bb5cf10SGeoff Levand BUG_ON(result); 1576bb5cf10SGeoff Levand 1586bb5cf10SGeoff Levand done: 1596bb5cf10SGeoff Levand mutex_unlock(&usage_hack.mutex); 1606bb5cf10SGeoff Levand return result; 1616bb5cf10SGeoff Levand } 1626bb5cf10SGeoff Levand 1636bb5cf10SGeoff Levand int ps3_open_hv_device(struct ps3_system_bus_device *dev) 1646bb5cf10SGeoff Levand { 1656bb5cf10SGeoff Levand BUG_ON(!dev); 1666bb5cf10SGeoff Levand pr_debug("%s:%d: match_id: %u\n", __func__, __LINE__, dev->match_id); 1676bb5cf10SGeoff Levand 1686bb5cf10SGeoff Levand switch (dev->match_id) { 1696bb5cf10SGeoff Levand case PS3_MATCH_ID_EHCI: 1706bb5cf10SGeoff Levand case PS3_MATCH_ID_OHCI: 1716bb5cf10SGeoff Levand case PS3_MATCH_ID_GELIC: 1726bb5cf10SGeoff Levand case PS3_MATCH_ID_STOR_DISK: 1736bb5cf10SGeoff Levand case PS3_MATCH_ID_STOR_ROM: 1746bb5cf10SGeoff Levand case PS3_MATCH_ID_STOR_FLASH: 1756bb5cf10SGeoff Levand return ps3_open_hv_device_sb(dev); 1766bb5cf10SGeoff Levand 1776bb5cf10SGeoff Levand case PS3_MATCH_ID_SOUND: 1786bb5cf10SGeoff Levand case PS3_MATCH_ID_GRAPHICS: 1796bb5cf10SGeoff Levand return ps3_open_hv_device_gpu(dev); 1806bb5cf10SGeoff Levand 1816bb5cf10SGeoff Levand case PS3_MATCH_ID_AV_SETTINGS: 1826bb5cf10SGeoff Levand case PS3_MATCH_ID_SYSTEM_MANAGER: 1836bb5cf10SGeoff Levand pr_debug("%s:%d: unsupported match_id: %u\n", __func__, 1846bb5cf10SGeoff Levand __LINE__, dev->match_id); 185034e0ab5SGeert Uytterhoeven pr_debug("%s:%d: bus_id: %lu\n", __func__, __LINE__, 186034e0ab5SGeert Uytterhoeven dev->bus_id); 1876bb5cf10SGeoff Levand BUG(); 1886bb5cf10SGeoff Levand return -EINVAL; 1896bb5cf10SGeoff Levand 1906bb5cf10SGeoff Levand default: 1916bb5cf10SGeoff Levand break; 1926bb5cf10SGeoff Levand } 1936bb5cf10SGeoff Levand 1946bb5cf10SGeoff Levand pr_debug("%s:%d: unknown match_id: %u\n", __func__, __LINE__, 1956bb5cf10SGeoff Levand dev->match_id); 1966bb5cf10SGeoff Levand BUG(); 1976bb5cf10SGeoff Levand return -ENODEV; 1986bb5cf10SGeoff Levand } 1996bb5cf10SGeoff Levand EXPORT_SYMBOL_GPL(ps3_open_hv_device); 2006bb5cf10SGeoff Levand 2016bb5cf10SGeoff Levand int ps3_close_hv_device(struct ps3_system_bus_device *dev) 2026bb5cf10SGeoff Levand { 2036bb5cf10SGeoff Levand BUG_ON(!dev); 2046bb5cf10SGeoff Levand pr_debug("%s:%d: match_id: %u\n", __func__, __LINE__, dev->match_id); 2056bb5cf10SGeoff Levand 2066bb5cf10SGeoff Levand switch (dev->match_id) { 2076bb5cf10SGeoff Levand case PS3_MATCH_ID_EHCI: 2086bb5cf10SGeoff Levand case PS3_MATCH_ID_OHCI: 2096bb5cf10SGeoff Levand case PS3_MATCH_ID_GELIC: 2106bb5cf10SGeoff Levand case PS3_MATCH_ID_STOR_DISK: 2116bb5cf10SGeoff Levand case PS3_MATCH_ID_STOR_ROM: 2126bb5cf10SGeoff Levand case PS3_MATCH_ID_STOR_FLASH: 2136bb5cf10SGeoff Levand return ps3_close_hv_device_sb(dev); 2146bb5cf10SGeoff Levand 2156bb5cf10SGeoff Levand case PS3_MATCH_ID_SOUND: 2166bb5cf10SGeoff Levand case PS3_MATCH_ID_GRAPHICS: 2176bb5cf10SGeoff Levand return ps3_close_hv_device_gpu(dev); 2186bb5cf10SGeoff Levand 2196bb5cf10SGeoff Levand case PS3_MATCH_ID_AV_SETTINGS: 2206bb5cf10SGeoff Levand case PS3_MATCH_ID_SYSTEM_MANAGER: 2216bb5cf10SGeoff Levand pr_debug("%s:%d: unsupported match_id: %u\n", __func__, 2226bb5cf10SGeoff Levand __LINE__, dev->match_id); 223034e0ab5SGeert Uytterhoeven pr_debug("%s:%d: bus_id: %lu\n", __func__, __LINE__, 224034e0ab5SGeert Uytterhoeven dev->bus_id); 2256bb5cf10SGeoff Levand BUG(); 2266bb5cf10SGeoff Levand return -EINVAL; 2276bb5cf10SGeoff Levand 2286bb5cf10SGeoff Levand default: 2296bb5cf10SGeoff Levand break; 2306bb5cf10SGeoff Levand } 2316bb5cf10SGeoff Levand 2326bb5cf10SGeoff Levand pr_debug("%s:%d: unknown match_id: %u\n", __func__, __LINE__, 2336bb5cf10SGeoff Levand dev->match_id); 2346bb5cf10SGeoff Levand BUG(); 2356bb5cf10SGeoff Levand return -ENODEV; 2366bb5cf10SGeoff Levand } 2376bb5cf10SGeoff Levand EXPORT_SYMBOL_GPL(ps3_close_hv_device); 2386bb5cf10SGeoff Levand 2392a08ea69SGeoff Levand #define dump_mmio_region(_a) _dump_mmio_region(_a, __func__, __LINE__) 2402a08ea69SGeoff Levand static void _dump_mmio_region(const struct ps3_mmio_region* r, 2412a08ea69SGeoff Levand const char* func, int line) 2422a08ea69SGeoff Levand { 243034e0ab5SGeert Uytterhoeven pr_debug("%s:%d: dev %lu:%lu\n", func, line, r->dev->bus_id, 2446bb5cf10SGeoff Levand r->dev->dev_id); 2452a08ea69SGeoff Levand pr_debug("%s:%d: bus_addr %lxh\n", func, line, r->bus_addr); 2462a08ea69SGeoff Levand pr_debug("%s:%d: len %lxh\n", func, line, r->len); 2472a08ea69SGeoff Levand pr_debug("%s:%d: lpar_addr %lxh\n", func, line, r->lpar_addr); 2482a08ea69SGeoff Levand } 2492a08ea69SGeoff Levand 2506bb5cf10SGeoff Levand static int ps3_sb_mmio_region_create(struct ps3_mmio_region *r) 2512a08ea69SGeoff Levand { 2522a08ea69SGeoff Levand int result; 2532a08ea69SGeoff Levand 2546bb5cf10SGeoff Levand result = lv1_map_device_mmio_region(r->dev->bus_id, r->dev->dev_id, 2552a08ea69SGeoff Levand r->bus_addr, r->len, r->page_size, &r->lpar_addr); 2562a08ea69SGeoff Levand 2572a08ea69SGeoff Levand if (result) { 2582a08ea69SGeoff Levand pr_debug("%s:%d: lv1_map_device_mmio_region failed: %s\n", 2592a08ea69SGeoff Levand __func__, __LINE__, ps3_result(result)); 2602a08ea69SGeoff Levand r->lpar_addr = 0; 2612a08ea69SGeoff Levand } 2622a08ea69SGeoff Levand 2632a08ea69SGeoff Levand dump_mmio_region(r); 2642a08ea69SGeoff Levand return result; 2652a08ea69SGeoff Levand } 2666bb5cf10SGeoff Levand 2676bb5cf10SGeoff Levand static int ps3_ioc0_mmio_region_create(struct ps3_mmio_region *r) 2686bb5cf10SGeoff Levand { 2696bb5cf10SGeoff Levand /* device specific; do nothing currently */ 2706bb5cf10SGeoff Levand return 0; 2716bb5cf10SGeoff Levand } 2726bb5cf10SGeoff Levand 2736bb5cf10SGeoff Levand int ps3_mmio_region_create(struct ps3_mmio_region *r) 2746bb5cf10SGeoff Levand { 2756bb5cf10SGeoff Levand return r->mmio_ops->create(r); 2766bb5cf10SGeoff Levand } 2779288f5c3SAl Viro EXPORT_SYMBOL_GPL(ps3_mmio_region_create); 2782a08ea69SGeoff Levand 2796bb5cf10SGeoff Levand static int ps3_sb_free_mmio_region(struct ps3_mmio_region *r) 2802a08ea69SGeoff Levand { 2812a08ea69SGeoff Levand int result; 2822a08ea69SGeoff Levand 2836bb5cf10SGeoff Levand dump_mmio_region(r); 2846bb5cf10SGeoff Levand ; 2856bb5cf10SGeoff Levand result = lv1_unmap_device_mmio_region(r->dev->bus_id, r->dev->dev_id, 2862a08ea69SGeoff Levand r->lpar_addr); 2872a08ea69SGeoff Levand 2882a08ea69SGeoff Levand if (result) 2892a08ea69SGeoff Levand pr_debug("%s:%d: lv1_unmap_device_mmio_region failed: %s\n", 2902a08ea69SGeoff Levand __func__, __LINE__, ps3_result(result)); 2912a08ea69SGeoff Levand 2922a08ea69SGeoff Levand r->lpar_addr = 0; 2932a08ea69SGeoff Levand return result; 2942a08ea69SGeoff Levand } 2956bb5cf10SGeoff Levand 2966bb5cf10SGeoff Levand static int ps3_ioc0_free_mmio_region(struct ps3_mmio_region *r) 2976bb5cf10SGeoff Levand { 2986bb5cf10SGeoff Levand /* device specific; do nothing currently */ 2996bb5cf10SGeoff Levand return 0; 3006bb5cf10SGeoff Levand } 3016bb5cf10SGeoff Levand 3026bb5cf10SGeoff Levand 3036bb5cf10SGeoff Levand int ps3_free_mmio_region(struct ps3_mmio_region *r) 3046bb5cf10SGeoff Levand { 3056bb5cf10SGeoff Levand return r->mmio_ops->free(r); 3066bb5cf10SGeoff Levand } 3076bb5cf10SGeoff Levand 3089288f5c3SAl Viro EXPORT_SYMBOL_GPL(ps3_free_mmio_region); 3092a08ea69SGeoff Levand 3106bb5cf10SGeoff Levand static const struct ps3_mmio_region_ops ps3_mmio_sb_region_ops = { 3116bb5cf10SGeoff Levand .create = ps3_sb_mmio_region_create, 3126bb5cf10SGeoff Levand .free = ps3_sb_free_mmio_region 3136bb5cf10SGeoff Levand }; 3146bb5cf10SGeoff Levand 3156bb5cf10SGeoff Levand static const struct ps3_mmio_region_ops ps3_mmio_ioc0_region_ops = { 3166bb5cf10SGeoff Levand .create = ps3_ioc0_mmio_region_create, 3176bb5cf10SGeoff Levand .free = ps3_ioc0_free_mmio_region 3186bb5cf10SGeoff Levand }; 3196bb5cf10SGeoff Levand 3206bb5cf10SGeoff Levand int ps3_mmio_region_init(struct ps3_system_bus_device *dev, 3216bb5cf10SGeoff Levand struct ps3_mmio_region *r, unsigned long bus_addr, unsigned long len, 3226bb5cf10SGeoff Levand enum ps3_mmio_page_size page_size) 3236bb5cf10SGeoff Levand { 3246bb5cf10SGeoff Levand r->dev = dev; 3256bb5cf10SGeoff Levand r->bus_addr = bus_addr; 3266bb5cf10SGeoff Levand r->len = len; 3276bb5cf10SGeoff Levand r->page_size = page_size; 3286bb5cf10SGeoff Levand switch (dev->dev_type) { 3296bb5cf10SGeoff Levand case PS3_DEVICE_TYPE_SB: 3306bb5cf10SGeoff Levand r->mmio_ops = &ps3_mmio_sb_region_ops; 3316bb5cf10SGeoff Levand break; 3326bb5cf10SGeoff Levand case PS3_DEVICE_TYPE_IOC0: 3336bb5cf10SGeoff Levand r->mmio_ops = &ps3_mmio_ioc0_region_ops; 3346bb5cf10SGeoff Levand break; 3356bb5cf10SGeoff Levand default: 3366bb5cf10SGeoff Levand BUG(); 3376bb5cf10SGeoff Levand return -EINVAL; 3386bb5cf10SGeoff Levand } 3396bb5cf10SGeoff Levand return 0; 3406bb5cf10SGeoff Levand } 3416bb5cf10SGeoff Levand EXPORT_SYMBOL_GPL(ps3_mmio_region_init); 3426bb5cf10SGeoff Levand 3432a08ea69SGeoff Levand static int ps3_system_bus_match(struct device *_dev, 3442a08ea69SGeoff Levand struct device_driver *_drv) 3452a08ea69SGeoff Levand { 3462a08ea69SGeoff Levand int result; 3476bb5cf10SGeoff Levand struct ps3_system_bus_driver *drv = ps3_drv_to_system_bus_drv(_drv); 3486bb5cf10SGeoff Levand struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); 3492a08ea69SGeoff Levand 3502a08ea69SGeoff Levand result = dev->match_id == drv->match_id; 3512a08ea69SGeoff Levand 3522a08ea69SGeoff Levand pr_info("%s:%d: dev=%u(%s), drv=%u(%s): %s\n", __func__, __LINE__, 3532a08ea69SGeoff Levand dev->match_id, dev->core.bus_id, drv->match_id, drv->core.name, 3542a08ea69SGeoff Levand (result ? "match" : "miss")); 3552a08ea69SGeoff Levand return result; 3562a08ea69SGeoff Levand } 3572a08ea69SGeoff Levand 3582a08ea69SGeoff Levand static int ps3_system_bus_probe(struct device *_dev) 3592a08ea69SGeoff Levand { 3606bb5cf10SGeoff Levand int result = 0; 3616bb5cf10SGeoff Levand struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); 3626bb5cf10SGeoff Levand struct ps3_system_bus_driver *drv; 3632a08ea69SGeoff Levand 3646bb5cf10SGeoff Levand BUG_ON(!dev); 3656bb5cf10SGeoff Levand pr_info(" -> %s:%d: %s\n", __func__, __LINE__, _dev->bus_id); 3662a08ea69SGeoff Levand 3676bb5cf10SGeoff Levand drv = ps3_system_bus_dev_to_system_bus_drv(dev); 3682a08ea69SGeoff Levand BUG_ON(!drv); 3692a08ea69SGeoff Levand 3702a08ea69SGeoff Levand if (drv->probe) 3712a08ea69SGeoff Levand result = drv->probe(dev); 3722a08ea69SGeoff Levand else 3732a08ea69SGeoff Levand pr_info("%s:%d: %s no probe method\n", __func__, __LINE__, 3742a08ea69SGeoff Levand dev->core.bus_id); 3752a08ea69SGeoff Levand 3766bb5cf10SGeoff Levand pr_info(" <- %s:%d: %s\n", __func__, __LINE__, dev->core.bus_id); 3772a08ea69SGeoff Levand return result; 3782a08ea69SGeoff Levand } 3792a08ea69SGeoff Levand 3802a08ea69SGeoff Levand static int ps3_system_bus_remove(struct device *_dev) 3812a08ea69SGeoff Levand { 3826bb5cf10SGeoff Levand int result = 0; 3836bb5cf10SGeoff Levand struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); 3846bb5cf10SGeoff Levand struct ps3_system_bus_driver *drv; 3856bb5cf10SGeoff Levand 3866bb5cf10SGeoff Levand BUG_ON(!dev); 3876bb5cf10SGeoff Levand pr_info(" -> %s:%d: %s\n", __func__, __LINE__, _dev->bus_id); 3886bb5cf10SGeoff Levand 3896bb5cf10SGeoff Levand drv = ps3_system_bus_dev_to_system_bus_drv(dev); 3906bb5cf10SGeoff Levand BUG_ON(!drv); 3912a08ea69SGeoff Levand 3922a08ea69SGeoff Levand if (drv->remove) 3936bb5cf10SGeoff Levand result = drv->remove(dev); 3942a08ea69SGeoff Levand else 3956bb5cf10SGeoff Levand dev_dbg(&dev->core, "%s:%d %s: no remove method\n", 3966bb5cf10SGeoff Levand __func__, __LINE__, drv->core.name); 3972a08ea69SGeoff Levand 3986bb5cf10SGeoff Levand pr_info(" <- %s:%d: %s\n", __func__, __LINE__, dev->core.bus_id); 3996bb5cf10SGeoff Levand return result; 4006bb5cf10SGeoff Levand } 4012a08ea69SGeoff Levand 4026bb5cf10SGeoff Levand static void ps3_system_bus_shutdown(struct device *_dev) 4036bb5cf10SGeoff Levand { 4046bb5cf10SGeoff Levand struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); 4056bb5cf10SGeoff Levand struct ps3_system_bus_driver *drv; 4066bb5cf10SGeoff Levand 4076bb5cf10SGeoff Levand BUG_ON(!dev); 4086bb5cf10SGeoff Levand 4096bb5cf10SGeoff Levand dev_dbg(&dev->core, " -> %s:%d: match_id %d\n", __func__, __LINE__, 4106bb5cf10SGeoff Levand dev->match_id); 4116bb5cf10SGeoff Levand 4126bb5cf10SGeoff Levand if (!dev->core.driver) { 4136bb5cf10SGeoff Levand dev_dbg(&dev->core, "%s:%d: no driver bound\n", __func__, 4146bb5cf10SGeoff Levand __LINE__); 4156bb5cf10SGeoff Levand return; 4166bb5cf10SGeoff Levand } 4176bb5cf10SGeoff Levand 4186bb5cf10SGeoff Levand drv = ps3_system_bus_dev_to_system_bus_drv(dev); 4196bb5cf10SGeoff Levand 4206bb5cf10SGeoff Levand BUG_ON(!drv); 4216bb5cf10SGeoff Levand 4226bb5cf10SGeoff Levand dev_dbg(&dev->core, "%s:%d: %s -> %s\n", __func__, __LINE__, 4236bb5cf10SGeoff Levand dev->core.bus_id, drv->core.name); 4246bb5cf10SGeoff Levand 4256bb5cf10SGeoff Levand if (drv->shutdown) 4266bb5cf10SGeoff Levand drv->shutdown(dev); 4276bb5cf10SGeoff Levand else if (drv->remove) { 4286bb5cf10SGeoff Levand dev_dbg(&dev->core, "%s:%d %s: no shutdown, calling remove\n", 4296bb5cf10SGeoff Levand __func__, __LINE__, drv->core.name); 4306bb5cf10SGeoff Levand drv->remove(dev); 4316bb5cf10SGeoff Levand } else { 4326bb5cf10SGeoff Levand dev_dbg(&dev->core, "%s:%d %s: no shutdown method\n", 4336bb5cf10SGeoff Levand __func__, __LINE__, drv->core.name); 4346bb5cf10SGeoff Levand BUG(); 4356bb5cf10SGeoff Levand } 4366bb5cf10SGeoff Levand 4376bb5cf10SGeoff Levand dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__); 4382a08ea69SGeoff Levand } 4392a08ea69SGeoff Levand 4407eff2e7aSKay Sievers static int ps3_system_bus_uevent(struct device *_dev, struct kobj_uevent_env *env) 441688b3378SDavid Woodhouse { 442688b3378SDavid Woodhouse struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); 443688b3378SDavid Woodhouse 4447eff2e7aSKay Sievers if (add_uevent_var(env, "MODALIAS=ps3:%d", dev->match_id)) 445688b3378SDavid Woodhouse return -ENOMEM; 446688b3378SDavid Woodhouse return 0; 447688b3378SDavid Woodhouse } 448688b3378SDavid Woodhouse 4496758555dSDavid Woodhouse static ssize_t modalias_show(struct device *_dev, struct device_attribute *a, 4506758555dSDavid Woodhouse char *buf) 4516758555dSDavid Woodhouse { 4526758555dSDavid Woodhouse struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); 4536758555dSDavid Woodhouse int len = snprintf(buf, PAGE_SIZE, "ps3:%d\n", dev->match_id); 4546758555dSDavid Woodhouse 4556758555dSDavid Woodhouse return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len; 4566758555dSDavid Woodhouse } 4576758555dSDavid Woodhouse 4586758555dSDavid Woodhouse static struct device_attribute ps3_system_bus_dev_attrs[] = { 4596758555dSDavid Woodhouse __ATTR_RO(modalias), 4606758555dSDavid Woodhouse __ATTR_NULL, 4616758555dSDavid Woodhouse }; 4626758555dSDavid Woodhouse 4632a08ea69SGeoff Levand struct bus_type ps3_system_bus_type = { 4642a08ea69SGeoff Levand .name = "ps3_system_bus", 4652a08ea69SGeoff Levand .match = ps3_system_bus_match, 466688b3378SDavid Woodhouse .uevent = ps3_system_bus_uevent, 4672a08ea69SGeoff Levand .probe = ps3_system_bus_probe, 4682a08ea69SGeoff Levand .remove = ps3_system_bus_remove, 4696bb5cf10SGeoff Levand .shutdown = ps3_system_bus_shutdown, 4706758555dSDavid Woodhouse .dev_attrs = ps3_system_bus_dev_attrs, 4712a08ea69SGeoff Levand }; 4722a08ea69SGeoff Levand 4736bb5cf10SGeoff Levand static int __init ps3_system_bus_init(void) 4742a08ea69SGeoff Levand { 4752a08ea69SGeoff Levand int result; 4762a08ea69SGeoff Levand 4772a08ea69SGeoff Levand if (!firmware_has_feature(FW_FEATURE_PS3_LV1)) 478ef596c69SGeert Uytterhoeven return -ENODEV; 4792a08ea69SGeoff Levand 4806bb5cf10SGeoff Levand pr_debug(" -> %s:%d\n", __func__, __LINE__); 4816bb5cf10SGeoff Levand 4826bb5cf10SGeoff Levand mutex_init(&usage_hack.mutex); 4836bb5cf10SGeoff Levand 4846bb5cf10SGeoff Levand result = device_register(&ps3_system_bus); 4856bb5cf10SGeoff Levand BUG_ON(result); 4866bb5cf10SGeoff Levand 4872a08ea69SGeoff Levand result = bus_register(&ps3_system_bus_type); 4882a08ea69SGeoff Levand BUG_ON(result); 4896bb5cf10SGeoff Levand 4906bb5cf10SGeoff Levand pr_debug(" <- %s:%d\n", __func__, __LINE__); 4912a08ea69SGeoff Levand return result; 4922a08ea69SGeoff Levand } 4932a08ea69SGeoff Levand 4942a08ea69SGeoff Levand core_initcall(ps3_system_bus_init); 4952a08ea69SGeoff Levand 4962a08ea69SGeoff Levand /* Allocates a contiguous real buffer and creates mappings over it. 4972a08ea69SGeoff Levand * Returns the virtual address of the buffer and sets dma_handle 4982a08ea69SGeoff Levand * to the dma address (mapping) of the first page. 4992a08ea69SGeoff Levand */ 5002a08ea69SGeoff Levand static void * ps3_alloc_coherent(struct device *_dev, size_t size, 5012a08ea69SGeoff Levand dma_addr_t *dma_handle, gfp_t flag) 5022a08ea69SGeoff Levand { 5032a08ea69SGeoff Levand int result; 5046bb5cf10SGeoff Levand struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); 5052a08ea69SGeoff Levand unsigned long virt_addr; 5062a08ea69SGeoff Levand 5072a08ea69SGeoff Levand flag &= ~(__GFP_DMA | __GFP_HIGHMEM); 5082a08ea69SGeoff Levand flag |= __GFP_ZERO; 5092a08ea69SGeoff Levand 5102a08ea69SGeoff Levand virt_addr = __get_free_pages(flag, get_order(size)); 5112a08ea69SGeoff Levand 5122a08ea69SGeoff Levand if (!virt_addr) { 5132a08ea69SGeoff Levand pr_debug("%s:%d: get_free_pages failed\n", __func__, __LINE__); 5142a08ea69SGeoff Levand goto clean_none; 5152a08ea69SGeoff Levand } 5162a08ea69SGeoff Levand 5176bb5cf10SGeoff Levand result = ps3_dma_map(dev->d_region, virt_addr, size, dma_handle, 5186bb5cf10SGeoff Levand IOPTE_PP_W | IOPTE_PP_R | IOPTE_SO_RW | IOPTE_M); 5192a08ea69SGeoff Levand 5202a08ea69SGeoff Levand if (result) { 5212a08ea69SGeoff Levand pr_debug("%s:%d: ps3_dma_map failed (%d)\n", 5222a08ea69SGeoff Levand __func__, __LINE__, result); 5232a08ea69SGeoff Levand BUG_ON("check region type"); 5242a08ea69SGeoff Levand goto clean_alloc; 5252a08ea69SGeoff Levand } 5262a08ea69SGeoff Levand 5272a08ea69SGeoff Levand return (void*)virt_addr; 5282a08ea69SGeoff Levand 5292a08ea69SGeoff Levand clean_alloc: 5302a08ea69SGeoff Levand free_pages(virt_addr, get_order(size)); 5312a08ea69SGeoff Levand clean_none: 5322a08ea69SGeoff Levand dma_handle = NULL; 5332a08ea69SGeoff Levand return NULL; 5342a08ea69SGeoff Levand } 5352a08ea69SGeoff Levand 5362a08ea69SGeoff Levand static void ps3_free_coherent(struct device *_dev, size_t size, void *vaddr, 5372a08ea69SGeoff Levand dma_addr_t dma_handle) 5382a08ea69SGeoff Levand { 5396bb5cf10SGeoff Levand struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); 5402a08ea69SGeoff Levand 5412a08ea69SGeoff Levand ps3_dma_unmap(dev->d_region, dma_handle, size); 5422a08ea69SGeoff Levand free_pages((unsigned long)vaddr, get_order(size)); 5432a08ea69SGeoff Levand } 5442a08ea69SGeoff Levand 5452a08ea69SGeoff Levand /* Creates TCEs for a user provided buffer. The user buffer must be 5462a08ea69SGeoff Levand * contiguous real kernel storage (not vmalloc). The address of the buffer 5472a08ea69SGeoff Levand * passed here is the kernel (virtual) address of the buffer. The buffer 5482a08ea69SGeoff Levand * need not be page aligned, the dma_addr_t returned will point to the same 5492a08ea69SGeoff Levand * byte within the page as vaddr. 5502a08ea69SGeoff Levand */ 5512a08ea69SGeoff Levand 5526bb5cf10SGeoff Levand static dma_addr_t ps3_sb_map_single(struct device *_dev, void *ptr, size_t size, 5532a08ea69SGeoff Levand enum dma_data_direction direction) 5542a08ea69SGeoff Levand { 5556bb5cf10SGeoff Levand struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); 5562a08ea69SGeoff Levand int result; 5572a08ea69SGeoff Levand unsigned long bus_addr; 5582a08ea69SGeoff Levand 5592a08ea69SGeoff Levand result = ps3_dma_map(dev->d_region, (unsigned long)ptr, size, 5606bb5cf10SGeoff Levand &bus_addr, 5616bb5cf10SGeoff Levand IOPTE_PP_R | IOPTE_PP_W | IOPTE_SO_RW | IOPTE_M); 5622a08ea69SGeoff Levand 5632a08ea69SGeoff Levand if (result) { 5642a08ea69SGeoff Levand pr_debug("%s:%d: ps3_dma_map failed (%d)\n", 5652a08ea69SGeoff Levand __func__, __LINE__, result); 5662a08ea69SGeoff Levand } 5672a08ea69SGeoff Levand 5682a08ea69SGeoff Levand return bus_addr; 5692a08ea69SGeoff Levand } 5702a08ea69SGeoff Levand 5716bb5cf10SGeoff Levand static dma_addr_t ps3_ioc0_map_single(struct device *_dev, void *ptr, 5726bb5cf10SGeoff Levand size_t size, 5736bb5cf10SGeoff Levand enum dma_data_direction direction) 5746bb5cf10SGeoff Levand { 5756bb5cf10SGeoff Levand struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); 5766bb5cf10SGeoff Levand int result; 5776bb5cf10SGeoff Levand unsigned long bus_addr; 5786bb5cf10SGeoff Levand u64 iopte_flag; 5796bb5cf10SGeoff Levand 5806bb5cf10SGeoff Levand iopte_flag = IOPTE_M; 5816bb5cf10SGeoff Levand switch (direction) { 5826bb5cf10SGeoff Levand case DMA_BIDIRECTIONAL: 5836bb5cf10SGeoff Levand iopte_flag |= IOPTE_PP_R | IOPTE_PP_W | IOPTE_SO_RW; 5846bb5cf10SGeoff Levand break; 5856bb5cf10SGeoff Levand case DMA_TO_DEVICE: 5866bb5cf10SGeoff Levand iopte_flag |= IOPTE_PP_R | IOPTE_SO_R; 5876bb5cf10SGeoff Levand break; 5886bb5cf10SGeoff Levand case DMA_FROM_DEVICE: 5896bb5cf10SGeoff Levand iopte_flag |= IOPTE_PP_W | IOPTE_SO_RW; 5906bb5cf10SGeoff Levand break; 5916bb5cf10SGeoff Levand default: 5926bb5cf10SGeoff Levand /* not happned */ 5936bb5cf10SGeoff Levand BUG(); 5946bb5cf10SGeoff Levand }; 5956bb5cf10SGeoff Levand result = ps3_dma_map(dev->d_region, (unsigned long)ptr, size, 5966bb5cf10SGeoff Levand &bus_addr, iopte_flag); 5976bb5cf10SGeoff Levand 5986bb5cf10SGeoff Levand if (result) { 5996bb5cf10SGeoff Levand pr_debug("%s:%d: ps3_dma_map failed (%d)\n", 6006bb5cf10SGeoff Levand __func__, __LINE__, result); 6016bb5cf10SGeoff Levand } 6026bb5cf10SGeoff Levand return bus_addr; 6036bb5cf10SGeoff Levand } 6046bb5cf10SGeoff Levand 6052a08ea69SGeoff Levand static void ps3_unmap_single(struct device *_dev, dma_addr_t dma_addr, 6062a08ea69SGeoff Levand size_t size, enum dma_data_direction direction) 6072a08ea69SGeoff Levand { 6086bb5cf10SGeoff Levand struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); 6092a08ea69SGeoff Levand int result; 6102a08ea69SGeoff Levand 6112a08ea69SGeoff Levand result = ps3_dma_unmap(dev->d_region, dma_addr, size); 6122a08ea69SGeoff Levand 6132a08ea69SGeoff Levand if (result) { 6142a08ea69SGeoff Levand pr_debug("%s:%d: ps3_dma_unmap failed (%d)\n", 6152a08ea69SGeoff Levand __func__, __LINE__, result); 6162a08ea69SGeoff Levand } 6172a08ea69SGeoff Levand } 6182a08ea69SGeoff Levand 619d1ed455eSJens Axboe static int ps3_sb_map_sg(struct device *_dev, struct scatterlist *sgl, 620d1ed455eSJens Axboe int nents, enum dma_data_direction direction) 6212a08ea69SGeoff Levand { 6222a08ea69SGeoff Levand #if defined(CONFIG_PS3_DYNAMIC_DMA) 6232a08ea69SGeoff Levand BUG_ON("do"); 62435063bb2SGeoff Levand return -EPERM; 62535063bb2SGeoff Levand #else 6266bb5cf10SGeoff Levand struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); 627d1ed455eSJens Axboe struct scatterlist *sg; 628435e0b2bSStephen Rothwell int i; 629435e0b2bSStephen Rothwell 630d1ed455eSJens Axboe for_each_sg(sgl, sg, nents, i) { 63158b053e4SJens Axboe int result = ps3_dma_map(dev->d_region, sg_phys(sg), 63258b053e4SJens Axboe sg->length, &sg->dma_address, 0); 63335063bb2SGeoff Levand 63435063bb2SGeoff Levand if (result) { 63535063bb2SGeoff Levand pr_debug("%s:%d: ps3_dma_map failed (%d)\n", 63635063bb2SGeoff Levand __func__, __LINE__, result); 63735063bb2SGeoff Levand return -EINVAL; 63835063bb2SGeoff Levand } 63935063bb2SGeoff Levand 64035063bb2SGeoff Levand sg->dma_length = sg->length; 64135063bb2SGeoff Levand } 64235063bb2SGeoff Levand 64335063bb2SGeoff Levand return nents; 6442a08ea69SGeoff Levand #endif 6452a08ea69SGeoff Levand } 6462a08ea69SGeoff Levand 6476bb5cf10SGeoff Levand static int ps3_ioc0_map_sg(struct device *_dev, struct scatterlist *sg, 6486bb5cf10SGeoff Levand int nents, 6496bb5cf10SGeoff Levand enum dma_data_direction direction) 6506bb5cf10SGeoff Levand { 6516bb5cf10SGeoff Levand BUG(); 6526bb5cf10SGeoff Levand return 0; 6536bb5cf10SGeoff Levand } 6546bb5cf10SGeoff Levand 6556bb5cf10SGeoff Levand static void ps3_sb_unmap_sg(struct device *_dev, struct scatterlist *sg, 6562a08ea69SGeoff Levand int nents, enum dma_data_direction direction) 6572a08ea69SGeoff Levand { 6582a08ea69SGeoff Levand #if defined(CONFIG_PS3_DYNAMIC_DMA) 6592a08ea69SGeoff Levand BUG_ON("do"); 6602a08ea69SGeoff Levand #endif 6612a08ea69SGeoff Levand } 6622a08ea69SGeoff Levand 6636bb5cf10SGeoff Levand static void ps3_ioc0_unmap_sg(struct device *_dev, struct scatterlist *sg, 6646bb5cf10SGeoff Levand int nents, enum dma_data_direction direction) 6656bb5cf10SGeoff Levand { 6666bb5cf10SGeoff Levand BUG(); 6676bb5cf10SGeoff Levand } 6686bb5cf10SGeoff Levand 6692a08ea69SGeoff Levand static int ps3_dma_supported(struct device *_dev, u64 mask) 6702a08ea69SGeoff Levand { 67135063bb2SGeoff Levand return mask >= DMA_32BIT_MASK; 6722a08ea69SGeoff Levand } 6732a08ea69SGeoff Levand 6746bb5cf10SGeoff Levand static struct dma_mapping_ops ps3_sb_dma_ops = { 6752a08ea69SGeoff Levand .alloc_coherent = ps3_alloc_coherent, 6762a08ea69SGeoff Levand .free_coherent = ps3_free_coherent, 6776bb5cf10SGeoff Levand .map_single = ps3_sb_map_single, 6782a08ea69SGeoff Levand .unmap_single = ps3_unmap_single, 6796bb5cf10SGeoff Levand .map_sg = ps3_sb_map_sg, 6806bb5cf10SGeoff Levand .unmap_sg = ps3_sb_unmap_sg, 6816bb5cf10SGeoff Levand .dma_supported = ps3_dma_supported 6826bb5cf10SGeoff Levand }; 6836bb5cf10SGeoff Levand 6846bb5cf10SGeoff Levand static struct dma_mapping_ops ps3_ioc0_dma_ops = { 6856bb5cf10SGeoff Levand .alloc_coherent = ps3_alloc_coherent, 6866bb5cf10SGeoff Levand .free_coherent = ps3_free_coherent, 6876bb5cf10SGeoff Levand .map_single = ps3_ioc0_map_single, 6886bb5cf10SGeoff Levand .unmap_single = ps3_unmap_single, 6896bb5cf10SGeoff Levand .map_sg = ps3_ioc0_map_sg, 6906bb5cf10SGeoff Levand .unmap_sg = ps3_ioc0_unmap_sg, 6912a08ea69SGeoff Levand .dma_supported = ps3_dma_supported 6922a08ea69SGeoff Levand }; 6932a08ea69SGeoff Levand 6942a08ea69SGeoff Levand /** 6952a08ea69SGeoff Levand * ps3_system_bus_release_device - remove a device from the system bus 6962a08ea69SGeoff Levand */ 6972a08ea69SGeoff Levand 6982a08ea69SGeoff Levand static void ps3_system_bus_release_device(struct device *_dev) 6992a08ea69SGeoff Levand { 7006bb5cf10SGeoff Levand struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); 7012a08ea69SGeoff Levand kfree(dev); 7022a08ea69SGeoff Levand } 7032a08ea69SGeoff Levand 7042a08ea69SGeoff Levand /** 7052a08ea69SGeoff Levand * ps3_system_bus_device_register - add a device to the system bus 7062a08ea69SGeoff Levand * 7072a08ea69SGeoff Levand * ps3_system_bus_device_register() expects the dev object to be allocated 7082a08ea69SGeoff Levand * dynamically by the caller. The system bus takes ownership of the dev 7092a08ea69SGeoff Levand * object and frees the object in ps3_system_bus_release_device(). 7102a08ea69SGeoff Levand */ 7112a08ea69SGeoff Levand 7122a08ea69SGeoff Levand int ps3_system_bus_device_register(struct ps3_system_bus_device *dev) 7132a08ea69SGeoff Levand { 7142a08ea69SGeoff Levand int result; 7156bb5cf10SGeoff Levand static unsigned int dev_ioc0_count; 7166bb5cf10SGeoff Levand static unsigned int dev_sb_count; 7176bb5cf10SGeoff Levand static unsigned int dev_vuart_count; 718ed757002SGeoff Levand static unsigned int dev_lpm_count; 7192a08ea69SGeoff Levand 7206bb5cf10SGeoff Levand if (!dev->core.parent) 7216bb5cf10SGeoff Levand dev->core.parent = &ps3_system_bus; 7222a08ea69SGeoff Levand dev->core.bus = &ps3_system_bus_type; 7232a08ea69SGeoff Levand dev->core.release = ps3_system_bus_release_device; 7242a08ea69SGeoff Levand 7256bb5cf10SGeoff Levand switch (dev->dev_type) { 7266bb5cf10SGeoff Levand case PS3_DEVICE_TYPE_IOC0: 7276bb5cf10SGeoff Levand dev->core.archdata.dma_ops = &ps3_ioc0_dma_ops; 7286bb5cf10SGeoff Levand snprintf(dev->core.bus_id, sizeof(dev->core.bus_id), 7296bb5cf10SGeoff Levand "ioc0_%02x", ++dev_ioc0_count); 7306bb5cf10SGeoff Levand break; 7316bb5cf10SGeoff Levand case PS3_DEVICE_TYPE_SB: 7326bb5cf10SGeoff Levand dev->core.archdata.dma_ops = &ps3_sb_dma_ops; 7336bb5cf10SGeoff Levand snprintf(dev->core.bus_id, sizeof(dev->core.bus_id), 7346bb5cf10SGeoff Levand "sb_%02x", ++dev_sb_count); 7352a08ea69SGeoff Levand 7366bb5cf10SGeoff Levand break; 7376bb5cf10SGeoff Levand case PS3_DEVICE_TYPE_VUART: 7386bb5cf10SGeoff Levand snprintf(dev->core.bus_id, sizeof(dev->core.bus_id), 7396bb5cf10SGeoff Levand "vuart_%02x", ++dev_vuart_count); 7406bb5cf10SGeoff Levand break; 741ed757002SGeoff Levand case PS3_DEVICE_TYPE_LPM: 742ed757002SGeoff Levand snprintf(dev->core.bus_id, sizeof(dev->core.bus_id), 743ed757002SGeoff Levand "lpm_%02x", ++dev_lpm_count); 744ed757002SGeoff Levand break; 7456bb5cf10SGeoff Levand default: 7466bb5cf10SGeoff Levand BUG(); 7476bb5cf10SGeoff Levand }; 7486bb5cf10SGeoff Levand 7496bb5cf10SGeoff Levand dev->core.archdata.of_node = NULL; 7506bb5cf10SGeoff Levand dev->core.archdata.numa_node = 0; 7512a08ea69SGeoff Levand 7522a08ea69SGeoff Levand pr_debug("%s:%d add %s\n", __func__, __LINE__, dev->core.bus_id); 7532a08ea69SGeoff Levand 7542a08ea69SGeoff Levand result = device_register(&dev->core); 7552a08ea69SGeoff Levand return result; 7562a08ea69SGeoff Levand } 7572a08ea69SGeoff Levand 7582a08ea69SGeoff Levand EXPORT_SYMBOL_GPL(ps3_system_bus_device_register); 7592a08ea69SGeoff Levand 7602a08ea69SGeoff Levand int ps3_system_bus_driver_register(struct ps3_system_bus_driver *drv) 7612a08ea69SGeoff Levand { 7622a08ea69SGeoff Levand int result; 7632a08ea69SGeoff Levand 7646bb5cf10SGeoff Levand pr_debug(" -> %s:%d: %s\n", __func__, __LINE__, drv->core.name); 7656bb5cf10SGeoff Levand 7666bb5cf10SGeoff Levand if (!firmware_has_feature(FW_FEATURE_PS3_LV1)) 7676bb5cf10SGeoff Levand return -ENODEV; 7686bb5cf10SGeoff Levand 7692a08ea69SGeoff Levand drv->core.bus = &ps3_system_bus_type; 7702a08ea69SGeoff Levand 7712a08ea69SGeoff Levand result = driver_register(&drv->core); 7726bb5cf10SGeoff Levand pr_debug(" <- %s:%d: %s\n", __func__, __LINE__, drv->core.name); 7732a08ea69SGeoff Levand return result; 7742a08ea69SGeoff Levand } 7752a08ea69SGeoff Levand 7762a08ea69SGeoff Levand EXPORT_SYMBOL_GPL(ps3_system_bus_driver_register); 7772a08ea69SGeoff Levand 7782a08ea69SGeoff Levand void ps3_system_bus_driver_unregister(struct ps3_system_bus_driver *drv) 7792a08ea69SGeoff Levand { 7806bb5cf10SGeoff Levand pr_debug(" -> %s:%d: %s\n", __func__, __LINE__, drv->core.name); 7812a08ea69SGeoff Levand driver_unregister(&drv->core); 7826bb5cf10SGeoff Levand pr_debug(" <- %s:%d: %s\n", __func__, __LINE__, drv->core.name); 7832a08ea69SGeoff Levand } 7842a08ea69SGeoff Levand 7852a08ea69SGeoff Levand EXPORT_SYMBOL_GPL(ps3_system_bus_driver_unregister); 786