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 350059e4938SMasakazu Mokuno if (!dev->match_sub_id) 3512a08ea69SGeoff Levand result = dev->match_id == drv->match_id; 352059e4938SMasakazu Mokuno else 353059e4938SMasakazu Mokuno result = dev->match_sub_id == drv->match_sub_id && 354059e4938SMasakazu Mokuno dev->match_id == drv->match_id; 3552a08ea69SGeoff Levand 35688b90c96SGeoff Levand if (result) 357059e4938SMasakazu Mokuno pr_info("%s:%d: dev=%u.%u(%s), drv=%u.%u(%s): match\n", 358059e4938SMasakazu Mokuno __func__, __LINE__, 359059e4938SMasakazu Mokuno dev->match_id, dev->match_sub_id, dev->core.bus_id, 360059e4938SMasakazu Mokuno drv->match_id, drv->match_sub_id, drv->core.name); 36188b90c96SGeoff Levand else 362059e4938SMasakazu Mokuno pr_debug("%s:%d: dev=%u.%u(%s), drv=%u.%u(%s): miss\n", 363059e4938SMasakazu Mokuno __func__, __LINE__, 364059e4938SMasakazu Mokuno dev->match_id, dev->match_sub_id, dev->core.bus_id, 365059e4938SMasakazu Mokuno drv->match_id, drv->match_sub_id, drv->core.name); 366059e4938SMasakazu Mokuno 3672a08ea69SGeoff Levand return result; 3682a08ea69SGeoff Levand } 3692a08ea69SGeoff Levand 3702a08ea69SGeoff Levand static int ps3_system_bus_probe(struct device *_dev) 3712a08ea69SGeoff Levand { 3726bb5cf10SGeoff Levand int result = 0; 3736bb5cf10SGeoff Levand struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); 3746bb5cf10SGeoff Levand struct ps3_system_bus_driver *drv; 3752a08ea69SGeoff Levand 3766bb5cf10SGeoff Levand BUG_ON(!dev); 37788b90c96SGeoff Levand pr_debug(" -> %s:%d: %s\n", __func__, __LINE__, _dev->bus_id); 3782a08ea69SGeoff Levand 3796bb5cf10SGeoff Levand drv = ps3_system_bus_dev_to_system_bus_drv(dev); 3802a08ea69SGeoff Levand BUG_ON(!drv); 3812a08ea69SGeoff Levand 3822a08ea69SGeoff Levand if (drv->probe) 3832a08ea69SGeoff Levand result = drv->probe(dev); 3842a08ea69SGeoff Levand else 38588b90c96SGeoff Levand pr_debug("%s:%d: %s no probe method\n", __func__, __LINE__, 3862a08ea69SGeoff Levand dev->core.bus_id); 3872a08ea69SGeoff Levand 38888b90c96SGeoff Levand pr_debug(" <- %s:%d: %s\n", __func__, __LINE__, dev->core.bus_id); 3892a08ea69SGeoff Levand return result; 3902a08ea69SGeoff Levand } 3912a08ea69SGeoff Levand 3922a08ea69SGeoff Levand static int ps3_system_bus_remove(struct device *_dev) 3932a08ea69SGeoff Levand { 3946bb5cf10SGeoff Levand int result = 0; 3956bb5cf10SGeoff Levand struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); 3966bb5cf10SGeoff Levand struct ps3_system_bus_driver *drv; 3976bb5cf10SGeoff Levand 3986bb5cf10SGeoff Levand BUG_ON(!dev); 39988b90c96SGeoff Levand pr_debug(" -> %s:%d: %s\n", __func__, __LINE__, _dev->bus_id); 4006bb5cf10SGeoff Levand 4016bb5cf10SGeoff Levand drv = ps3_system_bus_dev_to_system_bus_drv(dev); 4026bb5cf10SGeoff Levand BUG_ON(!drv); 4032a08ea69SGeoff Levand 4042a08ea69SGeoff Levand if (drv->remove) 4056bb5cf10SGeoff Levand result = drv->remove(dev); 4062a08ea69SGeoff Levand else 4076bb5cf10SGeoff Levand dev_dbg(&dev->core, "%s:%d %s: no remove method\n", 4086bb5cf10SGeoff Levand __func__, __LINE__, drv->core.name); 4092a08ea69SGeoff Levand 41088b90c96SGeoff Levand pr_debug(" <- %s:%d: %s\n", __func__, __LINE__, dev->core.bus_id); 4116bb5cf10SGeoff Levand return result; 4126bb5cf10SGeoff Levand } 4132a08ea69SGeoff Levand 4146bb5cf10SGeoff Levand static void ps3_system_bus_shutdown(struct device *_dev) 4156bb5cf10SGeoff Levand { 4166bb5cf10SGeoff Levand struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); 4176bb5cf10SGeoff Levand struct ps3_system_bus_driver *drv; 4186bb5cf10SGeoff Levand 4196bb5cf10SGeoff Levand BUG_ON(!dev); 4206bb5cf10SGeoff Levand 4216bb5cf10SGeoff Levand dev_dbg(&dev->core, " -> %s:%d: match_id %d\n", __func__, __LINE__, 4226bb5cf10SGeoff Levand dev->match_id); 4236bb5cf10SGeoff Levand 4246bb5cf10SGeoff Levand if (!dev->core.driver) { 4256bb5cf10SGeoff Levand dev_dbg(&dev->core, "%s:%d: no driver bound\n", __func__, 4266bb5cf10SGeoff Levand __LINE__); 4276bb5cf10SGeoff Levand return; 4286bb5cf10SGeoff Levand } 4296bb5cf10SGeoff Levand 4306bb5cf10SGeoff Levand drv = ps3_system_bus_dev_to_system_bus_drv(dev); 4316bb5cf10SGeoff Levand 4326bb5cf10SGeoff Levand BUG_ON(!drv); 4336bb5cf10SGeoff Levand 4346bb5cf10SGeoff Levand dev_dbg(&dev->core, "%s:%d: %s -> %s\n", __func__, __LINE__, 4356bb5cf10SGeoff Levand dev->core.bus_id, drv->core.name); 4366bb5cf10SGeoff Levand 4376bb5cf10SGeoff Levand if (drv->shutdown) 4386bb5cf10SGeoff Levand drv->shutdown(dev); 4396bb5cf10SGeoff Levand else if (drv->remove) { 4406bb5cf10SGeoff Levand dev_dbg(&dev->core, "%s:%d %s: no shutdown, calling remove\n", 4416bb5cf10SGeoff Levand __func__, __LINE__, drv->core.name); 4426bb5cf10SGeoff Levand drv->remove(dev); 4436bb5cf10SGeoff Levand } else { 4446bb5cf10SGeoff Levand dev_dbg(&dev->core, "%s:%d %s: no shutdown method\n", 4456bb5cf10SGeoff Levand __func__, __LINE__, drv->core.name); 4466bb5cf10SGeoff Levand BUG(); 4476bb5cf10SGeoff Levand } 4486bb5cf10SGeoff Levand 4496bb5cf10SGeoff Levand dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__); 4502a08ea69SGeoff Levand } 4512a08ea69SGeoff Levand 4527eff2e7aSKay Sievers static int ps3_system_bus_uevent(struct device *_dev, struct kobj_uevent_env *env) 453688b3378SDavid Woodhouse { 454688b3378SDavid Woodhouse struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); 455688b3378SDavid Woodhouse 4567eff2e7aSKay Sievers if (add_uevent_var(env, "MODALIAS=ps3:%d", dev->match_id)) 457688b3378SDavid Woodhouse return -ENOMEM; 458688b3378SDavid Woodhouse return 0; 459688b3378SDavid Woodhouse } 460688b3378SDavid Woodhouse 4616758555dSDavid Woodhouse static ssize_t modalias_show(struct device *_dev, struct device_attribute *a, 4626758555dSDavid Woodhouse char *buf) 4636758555dSDavid Woodhouse { 4646758555dSDavid Woodhouse struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); 4656758555dSDavid Woodhouse int len = snprintf(buf, PAGE_SIZE, "ps3:%d\n", dev->match_id); 4666758555dSDavid Woodhouse 4676758555dSDavid Woodhouse return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len; 4686758555dSDavid Woodhouse } 4696758555dSDavid Woodhouse 4706758555dSDavid Woodhouse static struct device_attribute ps3_system_bus_dev_attrs[] = { 4716758555dSDavid Woodhouse __ATTR_RO(modalias), 4726758555dSDavid Woodhouse __ATTR_NULL, 4736758555dSDavid Woodhouse }; 4746758555dSDavid Woodhouse 4752a08ea69SGeoff Levand struct bus_type ps3_system_bus_type = { 4762a08ea69SGeoff Levand .name = "ps3_system_bus", 4772a08ea69SGeoff Levand .match = ps3_system_bus_match, 478688b3378SDavid Woodhouse .uevent = ps3_system_bus_uevent, 4792a08ea69SGeoff Levand .probe = ps3_system_bus_probe, 4802a08ea69SGeoff Levand .remove = ps3_system_bus_remove, 4816bb5cf10SGeoff Levand .shutdown = ps3_system_bus_shutdown, 4826758555dSDavid Woodhouse .dev_attrs = ps3_system_bus_dev_attrs, 4832a08ea69SGeoff Levand }; 4842a08ea69SGeoff Levand 4856bb5cf10SGeoff Levand static int __init ps3_system_bus_init(void) 4862a08ea69SGeoff Levand { 4872a08ea69SGeoff Levand int result; 4882a08ea69SGeoff Levand 4892a08ea69SGeoff Levand if (!firmware_has_feature(FW_FEATURE_PS3_LV1)) 490ef596c69SGeert Uytterhoeven return -ENODEV; 4912a08ea69SGeoff Levand 4926bb5cf10SGeoff Levand pr_debug(" -> %s:%d\n", __func__, __LINE__); 4936bb5cf10SGeoff Levand 4946bb5cf10SGeoff Levand mutex_init(&usage_hack.mutex); 4956bb5cf10SGeoff Levand 4966bb5cf10SGeoff Levand result = device_register(&ps3_system_bus); 4976bb5cf10SGeoff Levand BUG_ON(result); 4986bb5cf10SGeoff Levand 4992a08ea69SGeoff Levand result = bus_register(&ps3_system_bus_type); 5002a08ea69SGeoff Levand BUG_ON(result); 5016bb5cf10SGeoff Levand 5026bb5cf10SGeoff Levand pr_debug(" <- %s:%d\n", __func__, __LINE__); 5032a08ea69SGeoff Levand return result; 5042a08ea69SGeoff Levand } 5052a08ea69SGeoff Levand 5062a08ea69SGeoff Levand core_initcall(ps3_system_bus_init); 5072a08ea69SGeoff Levand 5082a08ea69SGeoff Levand /* Allocates a contiguous real buffer and creates mappings over it. 5092a08ea69SGeoff Levand * Returns the virtual address of the buffer and sets dma_handle 5102a08ea69SGeoff Levand * to the dma address (mapping) of the first page. 5112a08ea69SGeoff Levand */ 5122a08ea69SGeoff Levand static void * ps3_alloc_coherent(struct device *_dev, size_t size, 5132a08ea69SGeoff Levand dma_addr_t *dma_handle, gfp_t flag) 5142a08ea69SGeoff Levand { 5152a08ea69SGeoff Levand int result; 5166bb5cf10SGeoff Levand struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); 5172a08ea69SGeoff Levand unsigned long virt_addr; 5182a08ea69SGeoff Levand 5192a08ea69SGeoff Levand flag &= ~(__GFP_DMA | __GFP_HIGHMEM); 5202a08ea69SGeoff Levand flag |= __GFP_ZERO; 5212a08ea69SGeoff Levand 5222a08ea69SGeoff Levand virt_addr = __get_free_pages(flag, get_order(size)); 5232a08ea69SGeoff Levand 5242a08ea69SGeoff Levand if (!virt_addr) { 5252a08ea69SGeoff Levand pr_debug("%s:%d: get_free_pages failed\n", __func__, __LINE__); 5262a08ea69SGeoff Levand goto clean_none; 5272a08ea69SGeoff Levand } 5282a08ea69SGeoff Levand 5296bb5cf10SGeoff Levand result = ps3_dma_map(dev->d_region, virt_addr, size, dma_handle, 5306bb5cf10SGeoff Levand IOPTE_PP_W | IOPTE_PP_R | IOPTE_SO_RW | IOPTE_M); 5312a08ea69SGeoff Levand 5322a08ea69SGeoff Levand if (result) { 5332a08ea69SGeoff Levand pr_debug("%s:%d: ps3_dma_map failed (%d)\n", 5342a08ea69SGeoff Levand __func__, __LINE__, result); 5352a08ea69SGeoff Levand BUG_ON("check region type"); 5362a08ea69SGeoff Levand goto clean_alloc; 5372a08ea69SGeoff Levand } 5382a08ea69SGeoff Levand 5392a08ea69SGeoff Levand return (void*)virt_addr; 5402a08ea69SGeoff Levand 5412a08ea69SGeoff Levand clean_alloc: 5422a08ea69SGeoff Levand free_pages(virt_addr, get_order(size)); 5432a08ea69SGeoff Levand clean_none: 5442a08ea69SGeoff Levand dma_handle = NULL; 5452a08ea69SGeoff Levand return NULL; 5462a08ea69SGeoff Levand } 5472a08ea69SGeoff Levand 5482a08ea69SGeoff Levand static void ps3_free_coherent(struct device *_dev, size_t size, void *vaddr, 5492a08ea69SGeoff Levand dma_addr_t dma_handle) 5502a08ea69SGeoff Levand { 5516bb5cf10SGeoff Levand struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); 5522a08ea69SGeoff Levand 5532a08ea69SGeoff Levand ps3_dma_unmap(dev->d_region, dma_handle, size); 5542a08ea69SGeoff Levand free_pages((unsigned long)vaddr, get_order(size)); 5552a08ea69SGeoff Levand } 5562a08ea69SGeoff Levand 5572a08ea69SGeoff Levand /* Creates TCEs for a user provided buffer. The user buffer must be 5582a08ea69SGeoff Levand * contiguous real kernel storage (not vmalloc). The address of the buffer 5592a08ea69SGeoff Levand * passed here is the kernel (virtual) address of the buffer. The buffer 5602a08ea69SGeoff Levand * need not be page aligned, the dma_addr_t returned will point to the same 5612a08ea69SGeoff Levand * byte within the page as vaddr. 5622a08ea69SGeoff Levand */ 5632a08ea69SGeoff Levand 5646bb5cf10SGeoff Levand static dma_addr_t ps3_sb_map_single(struct device *_dev, void *ptr, size_t size, 5653affedc4SMark Nelson enum dma_data_direction direction, struct dma_attrs *attrs) 5662a08ea69SGeoff Levand { 5676bb5cf10SGeoff Levand struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); 5682a08ea69SGeoff Levand int result; 5692a08ea69SGeoff Levand unsigned long bus_addr; 5702a08ea69SGeoff Levand 5712a08ea69SGeoff Levand result = ps3_dma_map(dev->d_region, (unsigned long)ptr, size, 5726bb5cf10SGeoff Levand &bus_addr, 5736bb5cf10SGeoff Levand IOPTE_PP_R | IOPTE_PP_W | IOPTE_SO_RW | IOPTE_M); 5742a08ea69SGeoff Levand 5752a08ea69SGeoff Levand if (result) { 5762a08ea69SGeoff Levand pr_debug("%s:%d: ps3_dma_map failed (%d)\n", 5772a08ea69SGeoff Levand __func__, __LINE__, result); 5782a08ea69SGeoff Levand } 5792a08ea69SGeoff Levand 5802a08ea69SGeoff Levand return bus_addr; 5812a08ea69SGeoff Levand } 5822a08ea69SGeoff Levand 5836bb5cf10SGeoff Levand static dma_addr_t ps3_ioc0_map_single(struct device *_dev, void *ptr, 5846bb5cf10SGeoff Levand size_t size, 5853affedc4SMark Nelson enum dma_data_direction direction, 5863affedc4SMark Nelson struct dma_attrs *attrs) 5876bb5cf10SGeoff Levand { 5886bb5cf10SGeoff Levand struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); 5896bb5cf10SGeoff Levand int result; 5906bb5cf10SGeoff Levand unsigned long bus_addr; 5916bb5cf10SGeoff Levand u64 iopte_flag; 5926bb5cf10SGeoff Levand 5936bb5cf10SGeoff Levand iopte_flag = IOPTE_M; 5946bb5cf10SGeoff Levand switch (direction) { 5956bb5cf10SGeoff Levand case DMA_BIDIRECTIONAL: 5966bb5cf10SGeoff Levand iopte_flag |= IOPTE_PP_R | IOPTE_PP_W | IOPTE_SO_RW; 5976bb5cf10SGeoff Levand break; 5986bb5cf10SGeoff Levand case DMA_TO_DEVICE: 5996bb5cf10SGeoff Levand iopte_flag |= IOPTE_PP_R | IOPTE_SO_R; 6006bb5cf10SGeoff Levand break; 6016bb5cf10SGeoff Levand case DMA_FROM_DEVICE: 6026bb5cf10SGeoff Levand iopte_flag |= IOPTE_PP_W | IOPTE_SO_RW; 6036bb5cf10SGeoff Levand break; 6046bb5cf10SGeoff Levand default: 6056bb5cf10SGeoff Levand /* not happned */ 6066bb5cf10SGeoff Levand BUG(); 6076bb5cf10SGeoff Levand }; 6086bb5cf10SGeoff Levand result = ps3_dma_map(dev->d_region, (unsigned long)ptr, size, 6096bb5cf10SGeoff Levand &bus_addr, iopte_flag); 6106bb5cf10SGeoff Levand 6116bb5cf10SGeoff Levand if (result) { 6126bb5cf10SGeoff Levand pr_debug("%s:%d: ps3_dma_map failed (%d)\n", 6136bb5cf10SGeoff Levand __func__, __LINE__, result); 6146bb5cf10SGeoff Levand } 6156bb5cf10SGeoff Levand return bus_addr; 6166bb5cf10SGeoff Levand } 6176bb5cf10SGeoff Levand 6182a08ea69SGeoff Levand static void ps3_unmap_single(struct device *_dev, dma_addr_t dma_addr, 6193affedc4SMark Nelson size_t size, enum dma_data_direction direction, struct dma_attrs *attrs) 6202a08ea69SGeoff Levand { 6216bb5cf10SGeoff Levand struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); 6222a08ea69SGeoff Levand int result; 6232a08ea69SGeoff Levand 6242a08ea69SGeoff Levand result = ps3_dma_unmap(dev->d_region, dma_addr, size); 6252a08ea69SGeoff Levand 6262a08ea69SGeoff Levand if (result) { 6272a08ea69SGeoff Levand pr_debug("%s:%d: ps3_dma_unmap failed (%d)\n", 6282a08ea69SGeoff Levand __func__, __LINE__, result); 6292a08ea69SGeoff Levand } 6302a08ea69SGeoff Levand } 6312a08ea69SGeoff Levand 632d1ed455eSJens Axboe static int ps3_sb_map_sg(struct device *_dev, struct scatterlist *sgl, 6333affedc4SMark Nelson int nents, enum dma_data_direction direction, struct dma_attrs *attrs) 6342a08ea69SGeoff Levand { 6352a08ea69SGeoff Levand #if defined(CONFIG_PS3_DYNAMIC_DMA) 6362a08ea69SGeoff Levand BUG_ON("do"); 63735063bb2SGeoff Levand return -EPERM; 63835063bb2SGeoff Levand #else 6396bb5cf10SGeoff Levand struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); 640d1ed455eSJens Axboe struct scatterlist *sg; 641435e0b2bSStephen Rothwell int i; 642435e0b2bSStephen Rothwell 643d1ed455eSJens Axboe for_each_sg(sgl, sg, nents, i) { 64458b053e4SJens Axboe int result = ps3_dma_map(dev->d_region, sg_phys(sg), 64558b053e4SJens Axboe sg->length, &sg->dma_address, 0); 64635063bb2SGeoff Levand 64735063bb2SGeoff Levand if (result) { 64835063bb2SGeoff Levand pr_debug("%s:%d: ps3_dma_map failed (%d)\n", 64935063bb2SGeoff Levand __func__, __LINE__, result); 65035063bb2SGeoff Levand return -EINVAL; 65135063bb2SGeoff Levand } 65235063bb2SGeoff Levand 65335063bb2SGeoff Levand sg->dma_length = sg->length; 65435063bb2SGeoff Levand } 65535063bb2SGeoff Levand 65635063bb2SGeoff Levand return nents; 6572a08ea69SGeoff Levand #endif 6582a08ea69SGeoff Levand } 6592a08ea69SGeoff Levand 6606bb5cf10SGeoff Levand static int ps3_ioc0_map_sg(struct device *_dev, struct scatterlist *sg, 6616bb5cf10SGeoff Levand int nents, 6623affedc4SMark Nelson enum dma_data_direction direction, 6633affedc4SMark Nelson struct dma_attrs *attrs) 6646bb5cf10SGeoff Levand { 6656bb5cf10SGeoff Levand BUG(); 6666bb5cf10SGeoff Levand return 0; 6676bb5cf10SGeoff Levand } 6686bb5cf10SGeoff Levand 6696bb5cf10SGeoff Levand static void ps3_sb_unmap_sg(struct device *_dev, struct scatterlist *sg, 6703affedc4SMark Nelson int nents, enum dma_data_direction direction, struct dma_attrs *attrs) 6712a08ea69SGeoff Levand { 6722a08ea69SGeoff Levand #if defined(CONFIG_PS3_DYNAMIC_DMA) 6732a08ea69SGeoff Levand BUG_ON("do"); 6742a08ea69SGeoff Levand #endif 6752a08ea69SGeoff Levand } 6762a08ea69SGeoff Levand 6776bb5cf10SGeoff Levand static void ps3_ioc0_unmap_sg(struct device *_dev, struct scatterlist *sg, 6783affedc4SMark Nelson int nents, enum dma_data_direction direction, 6793affedc4SMark Nelson struct dma_attrs *attrs) 6806bb5cf10SGeoff Levand { 6816bb5cf10SGeoff Levand BUG(); 6826bb5cf10SGeoff Levand } 6836bb5cf10SGeoff Levand 6842a08ea69SGeoff Levand static int ps3_dma_supported(struct device *_dev, u64 mask) 6852a08ea69SGeoff Levand { 68635063bb2SGeoff Levand return mask >= DMA_32BIT_MASK; 6872a08ea69SGeoff Levand } 6882a08ea69SGeoff Levand 6896bb5cf10SGeoff Levand static struct dma_mapping_ops ps3_sb_dma_ops = { 6902a08ea69SGeoff Levand .alloc_coherent = ps3_alloc_coherent, 6912a08ea69SGeoff Levand .free_coherent = ps3_free_coherent, 6926bb5cf10SGeoff Levand .map_single = ps3_sb_map_single, 6932a08ea69SGeoff Levand .unmap_single = ps3_unmap_single, 6946bb5cf10SGeoff Levand .map_sg = ps3_sb_map_sg, 6956bb5cf10SGeoff Levand .unmap_sg = ps3_sb_unmap_sg, 6966bb5cf10SGeoff Levand .dma_supported = ps3_dma_supported 6976bb5cf10SGeoff Levand }; 6986bb5cf10SGeoff Levand 6996bb5cf10SGeoff Levand static struct dma_mapping_ops ps3_ioc0_dma_ops = { 7006bb5cf10SGeoff Levand .alloc_coherent = ps3_alloc_coherent, 7016bb5cf10SGeoff Levand .free_coherent = ps3_free_coherent, 7026bb5cf10SGeoff Levand .map_single = ps3_ioc0_map_single, 7036bb5cf10SGeoff Levand .unmap_single = ps3_unmap_single, 7046bb5cf10SGeoff Levand .map_sg = ps3_ioc0_map_sg, 7056bb5cf10SGeoff Levand .unmap_sg = ps3_ioc0_unmap_sg, 7062a08ea69SGeoff Levand .dma_supported = ps3_dma_supported 7072a08ea69SGeoff Levand }; 7082a08ea69SGeoff Levand 7092a08ea69SGeoff Levand /** 7102a08ea69SGeoff Levand * ps3_system_bus_release_device - remove a device from the system bus 7112a08ea69SGeoff Levand */ 7122a08ea69SGeoff Levand 7132a08ea69SGeoff Levand static void ps3_system_bus_release_device(struct device *_dev) 7142a08ea69SGeoff Levand { 7156bb5cf10SGeoff Levand struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); 7162a08ea69SGeoff Levand kfree(dev); 7172a08ea69SGeoff Levand } 7182a08ea69SGeoff Levand 7192a08ea69SGeoff Levand /** 7202a08ea69SGeoff Levand * ps3_system_bus_device_register - add a device to the system bus 7212a08ea69SGeoff Levand * 7222a08ea69SGeoff Levand * ps3_system_bus_device_register() expects the dev object to be allocated 7232a08ea69SGeoff Levand * dynamically by the caller. The system bus takes ownership of the dev 7242a08ea69SGeoff Levand * object and frees the object in ps3_system_bus_release_device(). 7252a08ea69SGeoff Levand */ 7262a08ea69SGeoff Levand 7272a08ea69SGeoff Levand int ps3_system_bus_device_register(struct ps3_system_bus_device *dev) 7282a08ea69SGeoff Levand { 7292a08ea69SGeoff Levand int result; 7306bb5cf10SGeoff Levand static unsigned int dev_ioc0_count; 7316bb5cf10SGeoff Levand static unsigned int dev_sb_count; 7326bb5cf10SGeoff Levand static unsigned int dev_vuart_count; 733ed757002SGeoff Levand static unsigned int dev_lpm_count; 7342a08ea69SGeoff Levand 7356bb5cf10SGeoff Levand if (!dev->core.parent) 7366bb5cf10SGeoff Levand dev->core.parent = &ps3_system_bus; 7372a08ea69SGeoff Levand dev->core.bus = &ps3_system_bus_type; 7382a08ea69SGeoff Levand dev->core.release = ps3_system_bus_release_device; 7392a08ea69SGeoff Levand 7406bb5cf10SGeoff Levand switch (dev->dev_type) { 7416bb5cf10SGeoff Levand case PS3_DEVICE_TYPE_IOC0: 7426bb5cf10SGeoff Levand dev->core.archdata.dma_ops = &ps3_ioc0_dma_ops; 7436bb5cf10SGeoff Levand snprintf(dev->core.bus_id, sizeof(dev->core.bus_id), 7446bb5cf10SGeoff Levand "ioc0_%02x", ++dev_ioc0_count); 7456bb5cf10SGeoff Levand break; 7466bb5cf10SGeoff Levand case PS3_DEVICE_TYPE_SB: 7476bb5cf10SGeoff Levand dev->core.archdata.dma_ops = &ps3_sb_dma_ops; 7486bb5cf10SGeoff Levand snprintf(dev->core.bus_id, sizeof(dev->core.bus_id), 7496bb5cf10SGeoff Levand "sb_%02x", ++dev_sb_count); 7502a08ea69SGeoff Levand 7516bb5cf10SGeoff Levand break; 7526bb5cf10SGeoff Levand case PS3_DEVICE_TYPE_VUART: 7536bb5cf10SGeoff Levand snprintf(dev->core.bus_id, sizeof(dev->core.bus_id), 7546bb5cf10SGeoff Levand "vuart_%02x", ++dev_vuart_count); 7556bb5cf10SGeoff Levand break; 756ed757002SGeoff Levand case PS3_DEVICE_TYPE_LPM: 757ed757002SGeoff Levand snprintf(dev->core.bus_id, sizeof(dev->core.bus_id), 758ed757002SGeoff Levand "lpm_%02x", ++dev_lpm_count); 759ed757002SGeoff Levand break; 7606bb5cf10SGeoff Levand default: 7616bb5cf10SGeoff Levand BUG(); 7626bb5cf10SGeoff Levand }; 7636bb5cf10SGeoff Levand 7646bb5cf10SGeoff Levand dev->core.archdata.of_node = NULL; 7658fae0353SBecky Bruce set_dev_node(&dev->core, 0); 7662a08ea69SGeoff Levand 7672a08ea69SGeoff Levand pr_debug("%s:%d add %s\n", __func__, __LINE__, dev->core.bus_id); 7682a08ea69SGeoff Levand 7692a08ea69SGeoff Levand result = device_register(&dev->core); 7702a08ea69SGeoff Levand return result; 7712a08ea69SGeoff Levand } 7722a08ea69SGeoff Levand 7732a08ea69SGeoff Levand EXPORT_SYMBOL_GPL(ps3_system_bus_device_register); 7742a08ea69SGeoff Levand 7752a08ea69SGeoff Levand int ps3_system_bus_driver_register(struct ps3_system_bus_driver *drv) 7762a08ea69SGeoff Levand { 7772a08ea69SGeoff Levand int result; 7782a08ea69SGeoff Levand 7796bb5cf10SGeoff Levand pr_debug(" -> %s:%d: %s\n", __func__, __LINE__, drv->core.name); 7806bb5cf10SGeoff Levand 7816bb5cf10SGeoff Levand if (!firmware_has_feature(FW_FEATURE_PS3_LV1)) 7826bb5cf10SGeoff Levand return -ENODEV; 7836bb5cf10SGeoff Levand 7842a08ea69SGeoff Levand drv->core.bus = &ps3_system_bus_type; 7852a08ea69SGeoff Levand 7862a08ea69SGeoff Levand result = driver_register(&drv->core); 7876bb5cf10SGeoff Levand pr_debug(" <- %s:%d: %s\n", __func__, __LINE__, drv->core.name); 7882a08ea69SGeoff Levand return result; 7892a08ea69SGeoff Levand } 7902a08ea69SGeoff Levand 7912a08ea69SGeoff Levand EXPORT_SYMBOL_GPL(ps3_system_bus_driver_register); 7922a08ea69SGeoff Levand 7932a08ea69SGeoff Levand void ps3_system_bus_driver_unregister(struct ps3_system_bus_driver *drv) 7942a08ea69SGeoff Levand { 7956bb5cf10SGeoff Levand pr_debug(" -> %s:%d: %s\n", __func__, __LINE__, drv->core.name); 7962a08ea69SGeoff Levand driver_unregister(&drv->core); 7976bb5cf10SGeoff Levand pr_debug(" <- %s:%d: %s\n", __func__, __LINE__, drv->core.name); 7982a08ea69SGeoff Levand } 7992a08ea69SGeoff Levand 8002a08ea69SGeoff Levand EXPORT_SYMBOL_GPL(ps3_system_bus_driver_unregister); 801