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 = { 34aab0d375SKay Sievers .init_name = "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: 17846d01492SGeert Uytterhoeven case PS3_MATCH_ID_GPU: 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: 21646d01492SGeert Uytterhoeven case PS3_MATCH_ID_GPU: 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__, 359aab0d375SKay Sievers dev->match_id, dev->match_sub_id, dev_name(&dev->core), 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__, 364aab0d375SKay Sievers dev->match_id, dev->match_sub_id, dev_name(&dev->core), 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__, 386aab0d375SKay Sievers dev_name(&dev->core)); 3872a08ea69SGeoff Levand 388aab0d375SKay Sievers pr_debug(" <- %s:%d: %s\n", __func__, __LINE__, dev_name(&dev->core)); 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 410aab0d375SKay Sievers pr_debug(" <- %s:%d: %s\n", __func__, __LINE__, dev_name(&dev->core)); 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__, 435aab0d375SKay Sievers dev_name(&dev->core), 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 45646d01492SGeert Uytterhoeven if (add_uevent_var(env, "MODALIAS=ps3:%d:%d", dev->match_id, 45746d01492SGeert Uytterhoeven dev->match_sub_id)) 458688b3378SDavid Woodhouse return -ENOMEM; 459688b3378SDavid Woodhouse return 0; 460688b3378SDavid Woodhouse } 461688b3378SDavid Woodhouse 4626758555dSDavid Woodhouse static ssize_t modalias_show(struct device *_dev, struct device_attribute *a, 4636758555dSDavid Woodhouse char *buf) 4646758555dSDavid Woodhouse { 4656758555dSDavid Woodhouse struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); 46646d01492SGeert Uytterhoeven int len = snprintf(buf, PAGE_SIZE, "ps3:%d:%d\n", dev->match_id, 46746d01492SGeert Uytterhoeven dev->match_sub_id); 4686758555dSDavid Woodhouse 4696758555dSDavid Woodhouse return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len; 4706758555dSDavid Woodhouse } 4716758555dSDavid Woodhouse 4726758555dSDavid Woodhouse static struct device_attribute ps3_system_bus_dev_attrs[] = { 4736758555dSDavid Woodhouse __ATTR_RO(modalias), 4746758555dSDavid Woodhouse __ATTR_NULL, 4756758555dSDavid Woodhouse }; 4766758555dSDavid Woodhouse 4772a08ea69SGeoff Levand struct bus_type ps3_system_bus_type = { 4782a08ea69SGeoff Levand .name = "ps3_system_bus", 4792a08ea69SGeoff Levand .match = ps3_system_bus_match, 480688b3378SDavid Woodhouse .uevent = ps3_system_bus_uevent, 4812a08ea69SGeoff Levand .probe = ps3_system_bus_probe, 4822a08ea69SGeoff Levand .remove = ps3_system_bus_remove, 4836bb5cf10SGeoff Levand .shutdown = ps3_system_bus_shutdown, 4846758555dSDavid Woodhouse .dev_attrs = ps3_system_bus_dev_attrs, 4852a08ea69SGeoff Levand }; 4862a08ea69SGeoff Levand 4876bb5cf10SGeoff Levand static int __init ps3_system_bus_init(void) 4882a08ea69SGeoff Levand { 4892a08ea69SGeoff Levand int result; 4902a08ea69SGeoff Levand 4912a08ea69SGeoff Levand if (!firmware_has_feature(FW_FEATURE_PS3_LV1)) 492ef596c69SGeert Uytterhoeven return -ENODEV; 4932a08ea69SGeoff Levand 4946bb5cf10SGeoff Levand pr_debug(" -> %s:%d\n", __func__, __LINE__); 4956bb5cf10SGeoff Levand 4966bb5cf10SGeoff Levand mutex_init(&usage_hack.mutex); 4976bb5cf10SGeoff Levand 4986bb5cf10SGeoff Levand result = device_register(&ps3_system_bus); 4996bb5cf10SGeoff Levand BUG_ON(result); 5006bb5cf10SGeoff Levand 5012a08ea69SGeoff Levand result = bus_register(&ps3_system_bus_type); 5022a08ea69SGeoff Levand BUG_ON(result); 5036bb5cf10SGeoff Levand 5046bb5cf10SGeoff Levand pr_debug(" <- %s:%d\n", __func__, __LINE__); 5052a08ea69SGeoff Levand return result; 5062a08ea69SGeoff Levand } 5072a08ea69SGeoff Levand 5082a08ea69SGeoff Levand core_initcall(ps3_system_bus_init); 5092a08ea69SGeoff Levand 5102a08ea69SGeoff Levand /* Allocates a contiguous real buffer and creates mappings over it. 5112a08ea69SGeoff Levand * Returns the virtual address of the buffer and sets dma_handle 5122a08ea69SGeoff Levand * to the dma address (mapping) of the first page. 5132a08ea69SGeoff Levand */ 5142a08ea69SGeoff Levand static void * ps3_alloc_coherent(struct device *_dev, size_t size, 5152a08ea69SGeoff Levand dma_addr_t *dma_handle, gfp_t flag) 5162a08ea69SGeoff Levand { 5172a08ea69SGeoff Levand int result; 5186bb5cf10SGeoff Levand struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); 5192a08ea69SGeoff Levand unsigned long virt_addr; 5202a08ea69SGeoff Levand 5212a08ea69SGeoff Levand flag &= ~(__GFP_DMA | __GFP_HIGHMEM); 5222a08ea69SGeoff Levand flag |= __GFP_ZERO; 5232a08ea69SGeoff Levand 5242a08ea69SGeoff Levand virt_addr = __get_free_pages(flag, get_order(size)); 5252a08ea69SGeoff Levand 5262a08ea69SGeoff Levand if (!virt_addr) { 5272a08ea69SGeoff Levand pr_debug("%s:%d: get_free_pages failed\n", __func__, __LINE__); 5282a08ea69SGeoff Levand goto clean_none; 5292a08ea69SGeoff Levand } 5302a08ea69SGeoff Levand 5316bb5cf10SGeoff Levand result = ps3_dma_map(dev->d_region, virt_addr, size, dma_handle, 5326bb5cf10SGeoff Levand IOPTE_PP_W | IOPTE_PP_R | IOPTE_SO_RW | IOPTE_M); 5332a08ea69SGeoff Levand 5342a08ea69SGeoff Levand if (result) { 5352a08ea69SGeoff Levand pr_debug("%s:%d: ps3_dma_map failed (%d)\n", 5362a08ea69SGeoff Levand __func__, __LINE__, result); 5372a08ea69SGeoff Levand BUG_ON("check region type"); 5382a08ea69SGeoff Levand goto clean_alloc; 5392a08ea69SGeoff Levand } 5402a08ea69SGeoff Levand 5412a08ea69SGeoff Levand return (void*)virt_addr; 5422a08ea69SGeoff Levand 5432a08ea69SGeoff Levand clean_alloc: 5442a08ea69SGeoff Levand free_pages(virt_addr, get_order(size)); 5452a08ea69SGeoff Levand clean_none: 5462a08ea69SGeoff Levand dma_handle = NULL; 5472a08ea69SGeoff Levand return NULL; 5482a08ea69SGeoff Levand } 5492a08ea69SGeoff Levand 5502a08ea69SGeoff Levand static void ps3_free_coherent(struct device *_dev, size_t size, void *vaddr, 5512a08ea69SGeoff Levand dma_addr_t dma_handle) 5522a08ea69SGeoff Levand { 5536bb5cf10SGeoff Levand struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); 5542a08ea69SGeoff Levand 5552a08ea69SGeoff Levand ps3_dma_unmap(dev->d_region, dma_handle, size); 5562a08ea69SGeoff Levand free_pages((unsigned long)vaddr, get_order(size)); 5572a08ea69SGeoff Levand } 5582a08ea69SGeoff Levand 5592a08ea69SGeoff Levand /* Creates TCEs for a user provided buffer. The user buffer must be 560f9226d57SMark Nelson * contiguous real kernel storage (not vmalloc). The address passed here 561f9226d57SMark Nelson * comprises a page address and offset into that page. The dma_addr_t 562f9226d57SMark Nelson * returned will point to the same byte within the page as was passed in. 5632a08ea69SGeoff Levand */ 5642a08ea69SGeoff Levand 565f9226d57SMark Nelson static dma_addr_t ps3_sb_map_page(struct device *_dev, struct page *page, 566f9226d57SMark Nelson unsigned long offset, size_t size, enum dma_data_direction direction, 567f9226d57SMark Nelson struct dma_attrs *attrs) 5682a08ea69SGeoff Levand { 5696bb5cf10SGeoff Levand struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); 5702a08ea69SGeoff Levand int result; 5712a08ea69SGeoff Levand unsigned long bus_addr; 572f9226d57SMark Nelson void *ptr = page_address(page) + offset; 5732a08ea69SGeoff Levand 5742a08ea69SGeoff Levand result = ps3_dma_map(dev->d_region, (unsigned long)ptr, size, 5756bb5cf10SGeoff Levand &bus_addr, 5766bb5cf10SGeoff Levand IOPTE_PP_R | IOPTE_PP_W | IOPTE_SO_RW | IOPTE_M); 5772a08ea69SGeoff Levand 5782a08ea69SGeoff Levand if (result) { 5792a08ea69SGeoff Levand pr_debug("%s:%d: ps3_dma_map failed (%d)\n", 5802a08ea69SGeoff Levand __func__, __LINE__, result); 5812a08ea69SGeoff Levand } 5822a08ea69SGeoff Levand 5832a08ea69SGeoff Levand return bus_addr; 5842a08ea69SGeoff Levand } 5852a08ea69SGeoff Levand 586f9226d57SMark Nelson static dma_addr_t ps3_ioc0_map_page(struct device *_dev, struct page *page, 587f9226d57SMark Nelson unsigned long offset, size_t size, 5883affedc4SMark Nelson enum dma_data_direction direction, 5893affedc4SMark Nelson struct dma_attrs *attrs) 5906bb5cf10SGeoff Levand { 5916bb5cf10SGeoff Levand struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); 5926bb5cf10SGeoff Levand int result; 5936bb5cf10SGeoff Levand unsigned long bus_addr; 5946bb5cf10SGeoff Levand u64 iopte_flag; 595f9226d57SMark Nelson void *ptr = page_address(page) + offset; 5966bb5cf10SGeoff Levand 5976bb5cf10SGeoff Levand iopte_flag = IOPTE_M; 5986bb5cf10SGeoff Levand switch (direction) { 5996bb5cf10SGeoff Levand case DMA_BIDIRECTIONAL: 6006bb5cf10SGeoff Levand iopte_flag |= IOPTE_PP_R | IOPTE_PP_W | IOPTE_SO_RW; 6016bb5cf10SGeoff Levand break; 6026bb5cf10SGeoff Levand case DMA_TO_DEVICE: 6036bb5cf10SGeoff Levand iopte_flag |= IOPTE_PP_R | IOPTE_SO_R; 6046bb5cf10SGeoff Levand break; 6056bb5cf10SGeoff Levand case DMA_FROM_DEVICE: 6066bb5cf10SGeoff Levand iopte_flag |= IOPTE_PP_W | IOPTE_SO_RW; 6076bb5cf10SGeoff Levand break; 6086bb5cf10SGeoff Levand default: 6096bb5cf10SGeoff Levand /* not happned */ 6106bb5cf10SGeoff Levand BUG(); 6116bb5cf10SGeoff Levand }; 6126bb5cf10SGeoff Levand result = ps3_dma_map(dev->d_region, (unsigned long)ptr, size, 6136bb5cf10SGeoff Levand &bus_addr, iopte_flag); 6146bb5cf10SGeoff Levand 6156bb5cf10SGeoff Levand if (result) { 6166bb5cf10SGeoff Levand pr_debug("%s:%d: ps3_dma_map failed (%d)\n", 6176bb5cf10SGeoff Levand __func__, __LINE__, result); 6186bb5cf10SGeoff Levand } 6196bb5cf10SGeoff Levand return bus_addr; 6206bb5cf10SGeoff Levand } 6216bb5cf10SGeoff Levand 622f9226d57SMark Nelson static void ps3_unmap_page(struct device *_dev, dma_addr_t dma_addr, 6233affedc4SMark Nelson size_t size, enum dma_data_direction direction, struct dma_attrs *attrs) 6242a08ea69SGeoff Levand { 6256bb5cf10SGeoff Levand struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); 6262a08ea69SGeoff Levand int result; 6272a08ea69SGeoff Levand 6282a08ea69SGeoff Levand result = ps3_dma_unmap(dev->d_region, dma_addr, size); 6292a08ea69SGeoff Levand 6302a08ea69SGeoff Levand if (result) { 6312a08ea69SGeoff Levand pr_debug("%s:%d: ps3_dma_unmap failed (%d)\n", 6322a08ea69SGeoff Levand __func__, __LINE__, result); 6332a08ea69SGeoff Levand } 6342a08ea69SGeoff Levand } 6352a08ea69SGeoff Levand 636d1ed455eSJens Axboe static int ps3_sb_map_sg(struct device *_dev, struct scatterlist *sgl, 6373affedc4SMark Nelson int nents, enum dma_data_direction direction, struct dma_attrs *attrs) 6382a08ea69SGeoff Levand { 6392a08ea69SGeoff Levand #if defined(CONFIG_PS3_DYNAMIC_DMA) 6402a08ea69SGeoff Levand BUG_ON("do"); 64135063bb2SGeoff Levand return -EPERM; 64235063bb2SGeoff Levand #else 6436bb5cf10SGeoff Levand struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); 644d1ed455eSJens Axboe struct scatterlist *sg; 645435e0b2bSStephen Rothwell int i; 646435e0b2bSStephen Rothwell 647d1ed455eSJens Axboe for_each_sg(sgl, sg, nents, i) { 64858b053e4SJens Axboe int result = ps3_dma_map(dev->d_region, sg_phys(sg), 64958b053e4SJens Axboe sg->length, &sg->dma_address, 0); 65035063bb2SGeoff Levand 65135063bb2SGeoff Levand if (result) { 65235063bb2SGeoff Levand pr_debug("%s:%d: ps3_dma_map failed (%d)\n", 65335063bb2SGeoff Levand __func__, __LINE__, result); 65435063bb2SGeoff Levand return -EINVAL; 65535063bb2SGeoff Levand } 65635063bb2SGeoff Levand 65735063bb2SGeoff Levand sg->dma_length = sg->length; 65835063bb2SGeoff Levand } 65935063bb2SGeoff Levand 66035063bb2SGeoff Levand return nents; 6612a08ea69SGeoff Levand #endif 6622a08ea69SGeoff Levand } 6632a08ea69SGeoff Levand 6646bb5cf10SGeoff Levand static int ps3_ioc0_map_sg(struct device *_dev, struct scatterlist *sg, 6656bb5cf10SGeoff Levand int nents, 6663affedc4SMark Nelson enum dma_data_direction direction, 6673affedc4SMark Nelson struct dma_attrs *attrs) 6686bb5cf10SGeoff Levand { 6696bb5cf10SGeoff Levand BUG(); 6706bb5cf10SGeoff Levand return 0; 6716bb5cf10SGeoff Levand } 6726bb5cf10SGeoff Levand 6736bb5cf10SGeoff Levand static void ps3_sb_unmap_sg(struct device *_dev, struct scatterlist *sg, 6743affedc4SMark Nelson int nents, enum dma_data_direction direction, struct dma_attrs *attrs) 6752a08ea69SGeoff Levand { 6762a08ea69SGeoff Levand #if defined(CONFIG_PS3_DYNAMIC_DMA) 6772a08ea69SGeoff Levand BUG_ON("do"); 6782a08ea69SGeoff Levand #endif 6792a08ea69SGeoff Levand } 6802a08ea69SGeoff Levand 6816bb5cf10SGeoff Levand static void ps3_ioc0_unmap_sg(struct device *_dev, struct scatterlist *sg, 6823affedc4SMark Nelson int nents, enum dma_data_direction direction, 6833affedc4SMark Nelson struct dma_attrs *attrs) 6846bb5cf10SGeoff Levand { 6856bb5cf10SGeoff Levand BUG(); 6866bb5cf10SGeoff Levand } 6876bb5cf10SGeoff Levand 6882a08ea69SGeoff Levand static int ps3_dma_supported(struct device *_dev, u64 mask) 6892a08ea69SGeoff Levand { 69035063bb2SGeoff Levand return mask >= DMA_32BIT_MASK; 6912a08ea69SGeoff Levand } 6922a08ea69SGeoff Levand 6936bb5cf10SGeoff Levand static struct dma_mapping_ops ps3_sb_dma_ops = { 6942a08ea69SGeoff Levand .alloc_coherent = ps3_alloc_coherent, 6952a08ea69SGeoff Levand .free_coherent = ps3_free_coherent, 6966bb5cf10SGeoff Levand .map_sg = ps3_sb_map_sg, 6976bb5cf10SGeoff Levand .unmap_sg = ps3_sb_unmap_sg, 698f9226d57SMark Nelson .dma_supported = ps3_dma_supported, 699f9226d57SMark Nelson .map_page = ps3_sb_map_page, 700f9226d57SMark Nelson .unmap_page = ps3_unmap_page, 7016bb5cf10SGeoff Levand }; 7026bb5cf10SGeoff Levand 7036bb5cf10SGeoff Levand static struct dma_mapping_ops ps3_ioc0_dma_ops = { 7046bb5cf10SGeoff Levand .alloc_coherent = ps3_alloc_coherent, 7056bb5cf10SGeoff Levand .free_coherent = ps3_free_coherent, 7066bb5cf10SGeoff Levand .map_sg = ps3_ioc0_map_sg, 7076bb5cf10SGeoff Levand .unmap_sg = ps3_ioc0_unmap_sg, 708f9226d57SMark Nelson .dma_supported = ps3_dma_supported, 709f9226d57SMark Nelson .map_page = ps3_ioc0_map_page, 710f9226d57SMark Nelson .unmap_page = ps3_unmap_page, 7112a08ea69SGeoff Levand }; 7122a08ea69SGeoff Levand 7132a08ea69SGeoff Levand /** 7142a08ea69SGeoff Levand * ps3_system_bus_release_device - remove a device from the system bus 7152a08ea69SGeoff Levand */ 7162a08ea69SGeoff Levand 7172a08ea69SGeoff Levand static void ps3_system_bus_release_device(struct device *_dev) 7182a08ea69SGeoff Levand { 7196bb5cf10SGeoff Levand struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); 7202a08ea69SGeoff Levand kfree(dev); 7212a08ea69SGeoff Levand } 7222a08ea69SGeoff Levand 7232a08ea69SGeoff Levand /** 7242a08ea69SGeoff Levand * ps3_system_bus_device_register - add a device to the system bus 7252a08ea69SGeoff Levand * 7262a08ea69SGeoff Levand * ps3_system_bus_device_register() expects the dev object to be allocated 7272a08ea69SGeoff Levand * dynamically by the caller. The system bus takes ownership of the dev 7282a08ea69SGeoff Levand * object and frees the object in ps3_system_bus_release_device(). 7292a08ea69SGeoff Levand */ 7302a08ea69SGeoff Levand 7312a08ea69SGeoff Levand int ps3_system_bus_device_register(struct ps3_system_bus_device *dev) 7322a08ea69SGeoff Levand { 7332a08ea69SGeoff Levand int result; 7346bb5cf10SGeoff Levand static unsigned int dev_ioc0_count; 7356bb5cf10SGeoff Levand static unsigned int dev_sb_count; 7366bb5cf10SGeoff Levand static unsigned int dev_vuart_count; 737ed757002SGeoff Levand static unsigned int dev_lpm_count; 7382a08ea69SGeoff Levand 7396bb5cf10SGeoff Levand if (!dev->core.parent) 7406bb5cf10SGeoff Levand dev->core.parent = &ps3_system_bus; 7412a08ea69SGeoff Levand dev->core.bus = &ps3_system_bus_type; 7422a08ea69SGeoff Levand dev->core.release = ps3_system_bus_release_device; 7432a08ea69SGeoff Levand 7446bb5cf10SGeoff Levand switch (dev->dev_type) { 7456bb5cf10SGeoff Levand case PS3_DEVICE_TYPE_IOC0: 7466bb5cf10SGeoff Levand dev->core.archdata.dma_ops = &ps3_ioc0_dma_ops; 747aab0d375SKay Sievers dev_set_name(&dev->core, "ioc0_%02x", ++dev_ioc0_count); 7486bb5cf10SGeoff Levand break; 7496bb5cf10SGeoff Levand case PS3_DEVICE_TYPE_SB: 7506bb5cf10SGeoff Levand dev->core.archdata.dma_ops = &ps3_sb_dma_ops; 751aab0d375SKay Sievers dev_set_name(&dev->core, "sb_%02x", ++dev_sb_count); 7522a08ea69SGeoff Levand 7536bb5cf10SGeoff Levand break; 7546bb5cf10SGeoff Levand case PS3_DEVICE_TYPE_VUART: 755aab0d375SKay Sievers dev_set_name(&dev->core, "vuart_%02x", ++dev_vuart_count); 7566bb5cf10SGeoff Levand break; 757ed757002SGeoff Levand case PS3_DEVICE_TYPE_LPM: 758aab0d375SKay Sievers dev_set_name(&dev->core, "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 767aab0d375SKay Sievers pr_debug("%s:%d add %s\n", __func__, __LINE__, dev_name(&dev->core)); 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