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> 234b16f8e2SPaul Gortmaker #include <linux/export.h> 242a08ea69SGeoff Levand #include <linux/dma-mapping.h> 252a08ea69SGeoff Levand #include <linux/err.h> 265a0e3ad6STejun Heo #include <linux/slab.h> 272a08ea69SGeoff Levand 282a08ea69SGeoff Levand #include <asm/udbg.h> 292a08ea69SGeoff Levand #include <asm/lv1call.h> 302a08ea69SGeoff Levand #include <asm/firmware.h> 319413c883SGeert Uytterhoeven #include <asm/cell-regs.h> 322a08ea69SGeoff Levand 332a08ea69SGeoff Levand #include "platform.h" 342a08ea69SGeoff Levand 356bb5cf10SGeoff Levand static struct device ps3_system_bus = { 36aab0d375SKay Sievers .init_name = "ps3_system", 376bb5cf10SGeoff Levand }; 386bb5cf10SGeoff Levand 396bb5cf10SGeoff Levand /* FIXME: need device usage counters! */ 406bb5cf10SGeoff Levand struct { 416bb5cf10SGeoff Levand struct mutex mutex; 426bb5cf10SGeoff Levand int sb_11; /* usb 0 */ 436bb5cf10SGeoff Levand int sb_12; /* usb 0 */ 446bb5cf10SGeoff Levand int gpu; 456bb5cf10SGeoff Levand } static usage_hack; 466bb5cf10SGeoff Levand 47034e0ab5SGeert Uytterhoeven static int ps3_is_device(struct ps3_system_bus_device *dev, u64 bus_id, 48034e0ab5SGeert Uytterhoeven u64 dev_id) 496bb5cf10SGeoff Levand { 506bb5cf10SGeoff Levand return dev->bus_id == bus_id && dev->dev_id == dev_id; 516bb5cf10SGeoff Levand } 526bb5cf10SGeoff Levand 536bb5cf10SGeoff Levand static int ps3_open_hv_device_sb(struct ps3_system_bus_device *dev) 546bb5cf10SGeoff Levand { 556bb5cf10SGeoff Levand int result; 566bb5cf10SGeoff Levand 576bb5cf10SGeoff Levand BUG_ON(!dev->bus_id); 586bb5cf10SGeoff Levand mutex_lock(&usage_hack.mutex); 596bb5cf10SGeoff Levand 606bb5cf10SGeoff Levand if (ps3_is_device(dev, 1, 1)) { 616bb5cf10SGeoff Levand usage_hack.sb_11++; 626bb5cf10SGeoff Levand if (usage_hack.sb_11 > 1) { 636bb5cf10SGeoff Levand result = 0; 646bb5cf10SGeoff Levand goto done; 656bb5cf10SGeoff Levand } 666bb5cf10SGeoff Levand } 676bb5cf10SGeoff Levand 686bb5cf10SGeoff Levand if (ps3_is_device(dev, 1, 2)) { 696bb5cf10SGeoff Levand usage_hack.sb_12++; 706bb5cf10SGeoff Levand if (usage_hack.sb_12 > 1) { 716bb5cf10SGeoff Levand result = 0; 726bb5cf10SGeoff Levand goto done; 736bb5cf10SGeoff Levand } 746bb5cf10SGeoff Levand } 756bb5cf10SGeoff Levand 766bb5cf10SGeoff Levand result = lv1_open_device(dev->bus_id, dev->dev_id, 0); 776bb5cf10SGeoff Levand 786bb5cf10SGeoff Levand if (result) { 796bb5cf10SGeoff Levand pr_debug("%s:%d: lv1_open_device failed: %s\n", __func__, 806bb5cf10SGeoff Levand __LINE__, ps3_result(result)); 816bb5cf10SGeoff Levand result = -EPERM; 826bb5cf10SGeoff Levand } 836bb5cf10SGeoff Levand 846bb5cf10SGeoff Levand done: 856bb5cf10SGeoff Levand mutex_unlock(&usage_hack.mutex); 866bb5cf10SGeoff Levand return result; 876bb5cf10SGeoff Levand } 886bb5cf10SGeoff Levand 896bb5cf10SGeoff Levand static int ps3_close_hv_device_sb(struct ps3_system_bus_device *dev) 906bb5cf10SGeoff Levand { 916bb5cf10SGeoff Levand int result; 926bb5cf10SGeoff Levand 936bb5cf10SGeoff Levand BUG_ON(!dev->bus_id); 946bb5cf10SGeoff Levand mutex_lock(&usage_hack.mutex); 956bb5cf10SGeoff Levand 966bb5cf10SGeoff Levand if (ps3_is_device(dev, 1, 1)) { 976bb5cf10SGeoff Levand usage_hack.sb_11--; 986bb5cf10SGeoff Levand if (usage_hack.sb_11) { 996bb5cf10SGeoff Levand result = 0; 1006bb5cf10SGeoff Levand goto done; 1016bb5cf10SGeoff Levand } 1026bb5cf10SGeoff Levand } 1036bb5cf10SGeoff Levand 1046bb5cf10SGeoff Levand if (ps3_is_device(dev, 1, 2)) { 1056bb5cf10SGeoff Levand usage_hack.sb_12--; 1066bb5cf10SGeoff Levand if (usage_hack.sb_12) { 1076bb5cf10SGeoff Levand result = 0; 1086bb5cf10SGeoff Levand goto done; 1096bb5cf10SGeoff Levand } 1106bb5cf10SGeoff Levand } 1116bb5cf10SGeoff Levand 1126bb5cf10SGeoff Levand result = lv1_close_device(dev->bus_id, dev->dev_id); 1136bb5cf10SGeoff Levand BUG_ON(result); 1146bb5cf10SGeoff Levand 1156bb5cf10SGeoff Levand done: 1166bb5cf10SGeoff Levand mutex_unlock(&usage_hack.mutex); 1176bb5cf10SGeoff Levand return result; 1186bb5cf10SGeoff Levand } 1196bb5cf10SGeoff Levand 1206bb5cf10SGeoff Levand static int ps3_open_hv_device_gpu(struct ps3_system_bus_device *dev) 1216bb5cf10SGeoff Levand { 1226bb5cf10SGeoff Levand int result; 1236bb5cf10SGeoff Levand 1246bb5cf10SGeoff Levand mutex_lock(&usage_hack.mutex); 1256bb5cf10SGeoff Levand 1266bb5cf10SGeoff Levand usage_hack.gpu++; 1276bb5cf10SGeoff Levand if (usage_hack.gpu > 1) { 1286bb5cf10SGeoff Levand result = 0; 1296bb5cf10SGeoff Levand goto done; 1306bb5cf10SGeoff Levand } 1316bb5cf10SGeoff Levand 1326bb5cf10SGeoff Levand result = lv1_gpu_open(0); 1336bb5cf10SGeoff Levand 1346bb5cf10SGeoff Levand if (result) { 1356bb5cf10SGeoff Levand pr_debug("%s:%d: lv1_gpu_open failed: %s\n", __func__, 1366bb5cf10SGeoff Levand __LINE__, ps3_result(result)); 1376bb5cf10SGeoff Levand result = -EPERM; 1386bb5cf10SGeoff Levand } 1396bb5cf10SGeoff Levand 1406bb5cf10SGeoff Levand done: 1416bb5cf10SGeoff Levand mutex_unlock(&usage_hack.mutex); 1426bb5cf10SGeoff Levand return result; 1436bb5cf10SGeoff Levand } 1446bb5cf10SGeoff Levand 1456bb5cf10SGeoff Levand static int ps3_close_hv_device_gpu(struct ps3_system_bus_device *dev) 1466bb5cf10SGeoff Levand { 1476bb5cf10SGeoff Levand int result; 1486bb5cf10SGeoff Levand 1496bb5cf10SGeoff Levand mutex_lock(&usage_hack.mutex); 1506bb5cf10SGeoff Levand 1516bb5cf10SGeoff Levand usage_hack.gpu--; 1526bb5cf10SGeoff Levand if (usage_hack.gpu) { 1536bb5cf10SGeoff Levand result = 0; 1546bb5cf10SGeoff Levand goto done; 1556bb5cf10SGeoff Levand } 1566bb5cf10SGeoff Levand 1576bb5cf10SGeoff Levand result = lv1_gpu_close(); 1586bb5cf10SGeoff Levand BUG_ON(result); 1596bb5cf10SGeoff Levand 1606bb5cf10SGeoff Levand done: 1616bb5cf10SGeoff Levand mutex_unlock(&usage_hack.mutex); 1626bb5cf10SGeoff Levand return result; 1636bb5cf10SGeoff Levand } 1646bb5cf10SGeoff Levand 1656bb5cf10SGeoff Levand int ps3_open_hv_device(struct ps3_system_bus_device *dev) 1666bb5cf10SGeoff Levand { 1676bb5cf10SGeoff Levand BUG_ON(!dev); 1686bb5cf10SGeoff Levand pr_debug("%s:%d: match_id: %u\n", __func__, __LINE__, dev->match_id); 1696bb5cf10SGeoff Levand 1706bb5cf10SGeoff Levand switch (dev->match_id) { 1716bb5cf10SGeoff Levand case PS3_MATCH_ID_EHCI: 1726bb5cf10SGeoff Levand case PS3_MATCH_ID_OHCI: 1736bb5cf10SGeoff Levand case PS3_MATCH_ID_GELIC: 1746bb5cf10SGeoff Levand case PS3_MATCH_ID_STOR_DISK: 1756bb5cf10SGeoff Levand case PS3_MATCH_ID_STOR_ROM: 1766bb5cf10SGeoff Levand case PS3_MATCH_ID_STOR_FLASH: 1776bb5cf10SGeoff Levand return ps3_open_hv_device_sb(dev); 1786bb5cf10SGeoff Levand 1796bb5cf10SGeoff Levand case PS3_MATCH_ID_SOUND: 18046d01492SGeert Uytterhoeven case PS3_MATCH_ID_GPU: 1816bb5cf10SGeoff Levand return ps3_open_hv_device_gpu(dev); 1826bb5cf10SGeoff Levand 1836bb5cf10SGeoff Levand case PS3_MATCH_ID_AV_SETTINGS: 1846bb5cf10SGeoff Levand case PS3_MATCH_ID_SYSTEM_MANAGER: 1856bb5cf10SGeoff Levand pr_debug("%s:%d: unsupported match_id: %u\n", __func__, 1866bb5cf10SGeoff Levand __LINE__, dev->match_id); 1875c949070SStephen Rothwell pr_debug("%s:%d: bus_id: %llu\n", __func__, __LINE__, 188034e0ab5SGeert Uytterhoeven dev->bus_id); 1896bb5cf10SGeoff Levand BUG(); 1906bb5cf10SGeoff Levand return -EINVAL; 1916bb5cf10SGeoff Levand 1926bb5cf10SGeoff Levand default: 1936bb5cf10SGeoff Levand break; 1946bb5cf10SGeoff Levand } 1956bb5cf10SGeoff Levand 1966bb5cf10SGeoff Levand pr_debug("%s:%d: unknown match_id: %u\n", __func__, __LINE__, 1976bb5cf10SGeoff Levand dev->match_id); 1986bb5cf10SGeoff Levand BUG(); 1996bb5cf10SGeoff Levand return -ENODEV; 2006bb5cf10SGeoff Levand } 2016bb5cf10SGeoff Levand EXPORT_SYMBOL_GPL(ps3_open_hv_device); 2026bb5cf10SGeoff Levand 2036bb5cf10SGeoff Levand int ps3_close_hv_device(struct ps3_system_bus_device *dev) 2046bb5cf10SGeoff Levand { 2056bb5cf10SGeoff Levand BUG_ON(!dev); 2066bb5cf10SGeoff Levand pr_debug("%s:%d: match_id: %u\n", __func__, __LINE__, dev->match_id); 2076bb5cf10SGeoff Levand 2086bb5cf10SGeoff Levand switch (dev->match_id) { 2096bb5cf10SGeoff Levand case PS3_MATCH_ID_EHCI: 2106bb5cf10SGeoff Levand case PS3_MATCH_ID_OHCI: 2116bb5cf10SGeoff Levand case PS3_MATCH_ID_GELIC: 2126bb5cf10SGeoff Levand case PS3_MATCH_ID_STOR_DISK: 2136bb5cf10SGeoff Levand case PS3_MATCH_ID_STOR_ROM: 2146bb5cf10SGeoff Levand case PS3_MATCH_ID_STOR_FLASH: 2156bb5cf10SGeoff Levand return ps3_close_hv_device_sb(dev); 2166bb5cf10SGeoff Levand 2176bb5cf10SGeoff Levand case PS3_MATCH_ID_SOUND: 21846d01492SGeert Uytterhoeven case PS3_MATCH_ID_GPU: 2196bb5cf10SGeoff Levand return ps3_close_hv_device_gpu(dev); 2206bb5cf10SGeoff Levand 2216bb5cf10SGeoff Levand case PS3_MATCH_ID_AV_SETTINGS: 2226bb5cf10SGeoff Levand case PS3_MATCH_ID_SYSTEM_MANAGER: 2236bb5cf10SGeoff Levand pr_debug("%s:%d: unsupported match_id: %u\n", __func__, 2246bb5cf10SGeoff Levand __LINE__, dev->match_id); 2255c949070SStephen Rothwell pr_debug("%s:%d: bus_id: %llu\n", __func__, __LINE__, 226034e0ab5SGeert Uytterhoeven dev->bus_id); 2276bb5cf10SGeoff Levand BUG(); 2286bb5cf10SGeoff Levand return -EINVAL; 2296bb5cf10SGeoff Levand 2306bb5cf10SGeoff Levand default: 2316bb5cf10SGeoff Levand break; 2326bb5cf10SGeoff Levand } 2336bb5cf10SGeoff Levand 2346bb5cf10SGeoff Levand pr_debug("%s:%d: unknown match_id: %u\n", __func__, __LINE__, 2356bb5cf10SGeoff Levand dev->match_id); 2366bb5cf10SGeoff Levand BUG(); 2376bb5cf10SGeoff Levand return -ENODEV; 2386bb5cf10SGeoff Levand } 2396bb5cf10SGeoff Levand EXPORT_SYMBOL_GPL(ps3_close_hv_device); 2406bb5cf10SGeoff Levand 2412a08ea69SGeoff Levand #define dump_mmio_region(_a) _dump_mmio_region(_a, __func__, __LINE__) 2422a08ea69SGeoff Levand static void _dump_mmio_region(const struct ps3_mmio_region* r, 2432a08ea69SGeoff Levand const char* func, int line) 2442a08ea69SGeoff Levand { 2455c949070SStephen Rothwell pr_debug("%s:%d: dev %llu:%llu\n", func, line, r->dev->bus_id, 2466bb5cf10SGeoff Levand r->dev->dev_id); 2472a08ea69SGeoff Levand pr_debug("%s:%d: bus_addr %lxh\n", func, line, r->bus_addr); 2482a08ea69SGeoff Levand pr_debug("%s:%d: len %lxh\n", func, line, r->len); 2492a08ea69SGeoff Levand pr_debug("%s:%d: lpar_addr %lxh\n", func, line, r->lpar_addr); 2502a08ea69SGeoff Levand } 2512a08ea69SGeoff Levand 2526bb5cf10SGeoff Levand static int ps3_sb_mmio_region_create(struct ps3_mmio_region *r) 2532a08ea69SGeoff Levand { 2542a08ea69SGeoff Levand int result; 255b17b3df1SStephen Rothwell u64 lpar_addr; 2562a08ea69SGeoff Levand 2576bb5cf10SGeoff Levand result = lv1_map_device_mmio_region(r->dev->bus_id, r->dev->dev_id, 258b17b3df1SStephen Rothwell r->bus_addr, r->len, r->page_size, &lpar_addr); 259b17b3df1SStephen Rothwell r->lpar_addr = lpar_addr; 2602a08ea69SGeoff Levand 2612a08ea69SGeoff Levand if (result) { 2622a08ea69SGeoff Levand pr_debug("%s:%d: lv1_map_device_mmio_region failed: %s\n", 2632a08ea69SGeoff Levand __func__, __LINE__, ps3_result(result)); 2642a08ea69SGeoff Levand r->lpar_addr = 0; 2652a08ea69SGeoff Levand } 2662a08ea69SGeoff Levand 2672a08ea69SGeoff Levand dump_mmio_region(r); 2682a08ea69SGeoff Levand return result; 2692a08ea69SGeoff Levand } 2706bb5cf10SGeoff Levand 2716bb5cf10SGeoff Levand static int ps3_ioc0_mmio_region_create(struct ps3_mmio_region *r) 2726bb5cf10SGeoff Levand { 2736bb5cf10SGeoff Levand /* device specific; do nothing currently */ 2746bb5cf10SGeoff Levand return 0; 2756bb5cf10SGeoff Levand } 2766bb5cf10SGeoff Levand 2776bb5cf10SGeoff Levand int ps3_mmio_region_create(struct ps3_mmio_region *r) 2786bb5cf10SGeoff Levand { 2796bb5cf10SGeoff Levand return r->mmio_ops->create(r); 2806bb5cf10SGeoff Levand } 2819288f5c3SAl Viro EXPORT_SYMBOL_GPL(ps3_mmio_region_create); 2822a08ea69SGeoff Levand 2836bb5cf10SGeoff Levand static int ps3_sb_free_mmio_region(struct ps3_mmio_region *r) 2842a08ea69SGeoff Levand { 2852a08ea69SGeoff Levand int result; 2862a08ea69SGeoff Levand 2876bb5cf10SGeoff Levand dump_mmio_region(r); 2886bb5cf10SGeoff Levand result = lv1_unmap_device_mmio_region(r->dev->bus_id, r->dev->dev_id, 2892a08ea69SGeoff Levand r->lpar_addr); 2902a08ea69SGeoff Levand 2912a08ea69SGeoff Levand if (result) 2922a08ea69SGeoff Levand pr_debug("%s:%d: lv1_unmap_device_mmio_region failed: %s\n", 2932a08ea69SGeoff Levand __func__, __LINE__, ps3_result(result)); 2942a08ea69SGeoff Levand 2952a08ea69SGeoff Levand r->lpar_addr = 0; 2962a08ea69SGeoff Levand return result; 2972a08ea69SGeoff Levand } 2986bb5cf10SGeoff Levand 2996bb5cf10SGeoff Levand static int ps3_ioc0_free_mmio_region(struct ps3_mmio_region *r) 3006bb5cf10SGeoff Levand { 3016bb5cf10SGeoff Levand /* device specific; do nothing currently */ 3026bb5cf10SGeoff Levand return 0; 3036bb5cf10SGeoff Levand } 3046bb5cf10SGeoff Levand 3056bb5cf10SGeoff Levand 3066bb5cf10SGeoff Levand int ps3_free_mmio_region(struct ps3_mmio_region *r) 3076bb5cf10SGeoff Levand { 3086bb5cf10SGeoff Levand return r->mmio_ops->free(r); 3096bb5cf10SGeoff Levand } 3106bb5cf10SGeoff Levand 3119288f5c3SAl Viro EXPORT_SYMBOL_GPL(ps3_free_mmio_region); 3122a08ea69SGeoff Levand 3136bb5cf10SGeoff Levand static const struct ps3_mmio_region_ops ps3_mmio_sb_region_ops = { 3146bb5cf10SGeoff Levand .create = ps3_sb_mmio_region_create, 3156bb5cf10SGeoff Levand .free = ps3_sb_free_mmio_region 3166bb5cf10SGeoff Levand }; 3176bb5cf10SGeoff Levand 3186bb5cf10SGeoff Levand static const struct ps3_mmio_region_ops ps3_mmio_ioc0_region_ops = { 3196bb5cf10SGeoff Levand .create = ps3_ioc0_mmio_region_create, 3206bb5cf10SGeoff Levand .free = ps3_ioc0_free_mmio_region 3216bb5cf10SGeoff Levand }; 3226bb5cf10SGeoff Levand 3236bb5cf10SGeoff Levand int ps3_mmio_region_init(struct ps3_system_bus_device *dev, 3246bb5cf10SGeoff Levand struct ps3_mmio_region *r, unsigned long bus_addr, unsigned long len, 3256bb5cf10SGeoff Levand enum ps3_mmio_page_size page_size) 3266bb5cf10SGeoff Levand { 3276bb5cf10SGeoff Levand r->dev = dev; 3286bb5cf10SGeoff Levand r->bus_addr = bus_addr; 3296bb5cf10SGeoff Levand r->len = len; 3306bb5cf10SGeoff Levand r->page_size = page_size; 3316bb5cf10SGeoff Levand switch (dev->dev_type) { 3326bb5cf10SGeoff Levand case PS3_DEVICE_TYPE_SB: 3336bb5cf10SGeoff Levand r->mmio_ops = &ps3_mmio_sb_region_ops; 3346bb5cf10SGeoff Levand break; 3356bb5cf10SGeoff Levand case PS3_DEVICE_TYPE_IOC0: 3366bb5cf10SGeoff Levand r->mmio_ops = &ps3_mmio_ioc0_region_ops; 3376bb5cf10SGeoff Levand break; 3386bb5cf10SGeoff Levand default: 3396bb5cf10SGeoff Levand BUG(); 3406bb5cf10SGeoff Levand return -EINVAL; 3416bb5cf10SGeoff Levand } 3426bb5cf10SGeoff Levand return 0; 3436bb5cf10SGeoff Levand } 3446bb5cf10SGeoff Levand EXPORT_SYMBOL_GPL(ps3_mmio_region_init); 3456bb5cf10SGeoff Levand 3462a08ea69SGeoff Levand static int ps3_system_bus_match(struct device *_dev, 3472a08ea69SGeoff Levand struct device_driver *_drv) 3482a08ea69SGeoff Levand { 3492a08ea69SGeoff Levand int result; 3506bb5cf10SGeoff Levand struct ps3_system_bus_driver *drv = ps3_drv_to_system_bus_drv(_drv); 3516bb5cf10SGeoff Levand struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); 3522a08ea69SGeoff Levand 353059e4938SMasakazu Mokuno if (!dev->match_sub_id) 3542a08ea69SGeoff Levand result = dev->match_id == drv->match_id; 355059e4938SMasakazu Mokuno else 356059e4938SMasakazu Mokuno result = dev->match_sub_id == drv->match_sub_id && 357059e4938SMasakazu Mokuno dev->match_id == drv->match_id; 3582a08ea69SGeoff Levand 35988b90c96SGeoff Levand if (result) 360059e4938SMasakazu Mokuno pr_info("%s:%d: dev=%u.%u(%s), drv=%u.%u(%s): match\n", 361059e4938SMasakazu Mokuno __func__, __LINE__, 362aab0d375SKay Sievers dev->match_id, dev->match_sub_id, dev_name(&dev->core), 363059e4938SMasakazu Mokuno drv->match_id, drv->match_sub_id, drv->core.name); 36488b90c96SGeoff Levand else 365059e4938SMasakazu Mokuno pr_debug("%s:%d: dev=%u.%u(%s), drv=%u.%u(%s): miss\n", 366059e4938SMasakazu Mokuno __func__, __LINE__, 367aab0d375SKay Sievers dev->match_id, dev->match_sub_id, dev_name(&dev->core), 368059e4938SMasakazu Mokuno drv->match_id, drv->match_sub_id, drv->core.name); 369059e4938SMasakazu Mokuno 3702a08ea69SGeoff Levand return result; 3712a08ea69SGeoff Levand } 3722a08ea69SGeoff Levand 3732a08ea69SGeoff Levand static int ps3_system_bus_probe(struct device *_dev) 3742a08ea69SGeoff Levand { 3756bb5cf10SGeoff Levand int result = 0; 3766bb5cf10SGeoff Levand struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); 3776bb5cf10SGeoff Levand struct ps3_system_bus_driver *drv; 3782a08ea69SGeoff Levand 3796bb5cf10SGeoff Levand BUG_ON(!dev); 38054ca5412SGreg Kroah-Hartman dev_dbg(_dev, "%s:%d\n", __func__, __LINE__); 3812a08ea69SGeoff Levand 3826bb5cf10SGeoff Levand drv = ps3_system_bus_dev_to_system_bus_drv(dev); 3832a08ea69SGeoff Levand BUG_ON(!drv); 3842a08ea69SGeoff Levand 3852a08ea69SGeoff Levand if (drv->probe) 3862a08ea69SGeoff Levand result = drv->probe(dev); 3872a08ea69SGeoff Levand else 38888b90c96SGeoff Levand pr_debug("%s:%d: %s no probe method\n", __func__, __LINE__, 389aab0d375SKay Sievers dev_name(&dev->core)); 3902a08ea69SGeoff Levand 391aab0d375SKay Sievers pr_debug(" <- %s:%d: %s\n", __func__, __LINE__, dev_name(&dev->core)); 3922a08ea69SGeoff Levand return result; 3932a08ea69SGeoff Levand } 3942a08ea69SGeoff Levand 3952a08ea69SGeoff Levand static int ps3_system_bus_remove(struct device *_dev) 3962a08ea69SGeoff Levand { 3976bb5cf10SGeoff Levand int result = 0; 3986bb5cf10SGeoff Levand struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); 3996bb5cf10SGeoff Levand struct ps3_system_bus_driver *drv; 4006bb5cf10SGeoff Levand 4016bb5cf10SGeoff Levand BUG_ON(!dev); 40254ca5412SGreg Kroah-Hartman dev_dbg(_dev, "%s:%d\n", __func__, __LINE__); 4036bb5cf10SGeoff Levand 4046bb5cf10SGeoff Levand drv = ps3_system_bus_dev_to_system_bus_drv(dev); 4056bb5cf10SGeoff Levand BUG_ON(!drv); 4062a08ea69SGeoff Levand 4072a08ea69SGeoff Levand if (drv->remove) 4086bb5cf10SGeoff Levand result = drv->remove(dev); 4092a08ea69SGeoff Levand else 4106bb5cf10SGeoff Levand dev_dbg(&dev->core, "%s:%d %s: no remove method\n", 4116bb5cf10SGeoff Levand __func__, __LINE__, drv->core.name); 4122a08ea69SGeoff Levand 413aab0d375SKay Sievers pr_debug(" <- %s:%d: %s\n", __func__, __LINE__, dev_name(&dev->core)); 4146bb5cf10SGeoff Levand return result; 4156bb5cf10SGeoff Levand } 4162a08ea69SGeoff Levand 4176bb5cf10SGeoff Levand static void ps3_system_bus_shutdown(struct device *_dev) 4186bb5cf10SGeoff Levand { 4196bb5cf10SGeoff Levand struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); 4206bb5cf10SGeoff Levand struct ps3_system_bus_driver *drv; 4216bb5cf10SGeoff Levand 4226bb5cf10SGeoff Levand BUG_ON(!dev); 4236bb5cf10SGeoff Levand 4246bb5cf10SGeoff Levand dev_dbg(&dev->core, " -> %s:%d: match_id %d\n", __func__, __LINE__, 4256bb5cf10SGeoff Levand dev->match_id); 4266bb5cf10SGeoff Levand 4276bb5cf10SGeoff Levand if (!dev->core.driver) { 4286bb5cf10SGeoff Levand dev_dbg(&dev->core, "%s:%d: no driver bound\n", __func__, 4296bb5cf10SGeoff Levand __LINE__); 4306bb5cf10SGeoff Levand return; 4316bb5cf10SGeoff Levand } 4326bb5cf10SGeoff Levand 4336bb5cf10SGeoff Levand drv = ps3_system_bus_dev_to_system_bus_drv(dev); 4346bb5cf10SGeoff Levand 4356bb5cf10SGeoff Levand BUG_ON(!drv); 4366bb5cf10SGeoff Levand 4376bb5cf10SGeoff Levand dev_dbg(&dev->core, "%s:%d: %s -> %s\n", __func__, __LINE__, 438aab0d375SKay Sievers dev_name(&dev->core), drv->core.name); 4396bb5cf10SGeoff Levand 4406bb5cf10SGeoff Levand if (drv->shutdown) 4416bb5cf10SGeoff Levand drv->shutdown(dev); 4426bb5cf10SGeoff Levand else if (drv->remove) { 4436bb5cf10SGeoff Levand dev_dbg(&dev->core, "%s:%d %s: no shutdown, calling remove\n", 4446bb5cf10SGeoff Levand __func__, __LINE__, drv->core.name); 4456bb5cf10SGeoff Levand drv->remove(dev); 4466bb5cf10SGeoff Levand } else { 4476bb5cf10SGeoff Levand dev_dbg(&dev->core, "%s:%d %s: no shutdown method\n", 4486bb5cf10SGeoff Levand __func__, __LINE__, drv->core.name); 4496bb5cf10SGeoff Levand BUG(); 4506bb5cf10SGeoff Levand } 4516bb5cf10SGeoff Levand 4526bb5cf10SGeoff Levand dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__); 4532a08ea69SGeoff Levand } 4542a08ea69SGeoff Levand 4557eff2e7aSKay Sievers static int ps3_system_bus_uevent(struct device *_dev, struct kobj_uevent_env *env) 456688b3378SDavid Woodhouse { 457688b3378SDavid Woodhouse struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); 458688b3378SDavid Woodhouse 45946d01492SGeert Uytterhoeven if (add_uevent_var(env, "MODALIAS=ps3:%d:%d", dev->match_id, 46046d01492SGeert Uytterhoeven dev->match_sub_id)) 461688b3378SDavid Woodhouse return -ENOMEM; 462688b3378SDavid Woodhouse return 0; 463688b3378SDavid Woodhouse } 464688b3378SDavid Woodhouse 4656758555dSDavid Woodhouse static ssize_t modalias_show(struct device *_dev, struct device_attribute *a, 4666758555dSDavid Woodhouse char *buf) 4676758555dSDavid Woodhouse { 4686758555dSDavid Woodhouse struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); 46946d01492SGeert Uytterhoeven int len = snprintf(buf, PAGE_SIZE, "ps3:%d:%d\n", dev->match_id, 47046d01492SGeert Uytterhoeven dev->match_sub_id); 4716758555dSDavid Woodhouse 4726758555dSDavid Woodhouse return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len; 4736758555dSDavid Woodhouse } 4746758555dSDavid Woodhouse 4756758555dSDavid Woodhouse static struct device_attribute ps3_system_bus_dev_attrs[] = { 4766758555dSDavid Woodhouse __ATTR_RO(modalias), 4776758555dSDavid Woodhouse __ATTR_NULL, 4786758555dSDavid Woodhouse }; 4796758555dSDavid Woodhouse 4802a08ea69SGeoff Levand struct bus_type ps3_system_bus_type = { 4812a08ea69SGeoff Levand .name = "ps3_system_bus", 4822a08ea69SGeoff Levand .match = ps3_system_bus_match, 483688b3378SDavid Woodhouse .uevent = ps3_system_bus_uevent, 4842a08ea69SGeoff Levand .probe = ps3_system_bus_probe, 4852a08ea69SGeoff Levand .remove = ps3_system_bus_remove, 4866bb5cf10SGeoff Levand .shutdown = ps3_system_bus_shutdown, 4876758555dSDavid Woodhouse .dev_attrs = ps3_system_bus_dev_attrs, 4882a08ea69SGeoff Levand }; 4892a08ea69SGeoff Levand 4906bb5cf10SGeoff Levand static int __init ps3_system_bus_init(void) 4912a08ea69SGeoff Levand { 4922a08ea69SGeoff Levand int result; 4932a08ea69SGeoff Levand 4942a08ea69SGeoff Levand if (!firmware_has_feature(FW_FEATURE_PS3_LV1)) 495ef596c69SGeert Uytterhoeven return -ENODEV; 4962a08ea69SGeoff Levand 4976bb5cf10SGeoff Levand pr_debug(" -> %s:%d\n", __func__, __LINE__); 4986bb5cf10SGeoff Levand 4996bb5cf10SGeoff Levand mutex_init(&usage_hack.mutex); 5006bb5cf10SGeoff Levand 5016bb5cf10SGeoff Levand result = device_register(&ps3_system_bus); 5026bb5cf10SGeoff Levand BUG_ON(result); 5036bb5cf10SGeoff Levand 5042a08ea69SGeoff Levand result = bus_register(&ps3_system_bus_type); 5052a08ea69SGeoff Levand BUG_ON(result); 5066bb5cf10SGeoff Levand 5076bb5cf10SGeoff Levand pr_debug(" <- %s:%d\n", __func__, __LINE__); 5082a08ea69SGeoff Levand return result; 5092a08ea69SGeoff Levand } 5102a08ea69SGeoff Levand 5112a08ea69SGeoff Levand core_initcall(ps3_system_bus_init); 5122a08ea69SGeoff Levand 5132a08ea69SGeoff Levand /* Allocates a contiguous real buffer and creates mappings over it. 5142a08ea69SGeoff Levand * Returns the virtual address of the buffer and sets dma_handle 5152a08ea69SGeoff Levand * to the dma address (mapping) of the first page. 5162a08ea69SGeoff Levand */ 5172a08ea69SGeoff Levand static void * ps3_alloc_coherent(struct device *_dev, size_t size, 518bfbf7d61SAndrzej Pietrasiewicz dma_addr_t *dma_handle, gfp_t flag, 51900085f1eSKrzysztof Kozlowski unsigned long attrs) 5202a08ea69SGeoff Levand { 5212a08ea69SGeoff Levand int result; 5226bb5cf10SGeoff Levand struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); 5232a08ea69SGeoff Levand unsigned long virt_addr; 5242a08ea69SGeoff Levand 5252a08ea69SGeoff Levand flag &= ~(__GFP_DMA | __GFP_HIGHMEM); 5262a08ea69SGeoff Levand flag |= __GFP_ZERO; 5272a08ea69SGeoff Levand 5282a08ea69SGeoff Levand virt_addr = __get_free_pages(flag, get_order(size)); 5292a08ea69SGeoff Levand 5302a08ea69SGeoff Levand if (!virt_addr) { 5312a08ea69SGeoff Levand pr_debug("%s:%d: get_free_pages failed\n", __func__, __LINE__); 5322a08ea69SGeoff Levand goto clean_none; 5332a08ea69SGeoff Levand } 5342a08ea69SGeoff Levand 5356bb5cf10SGeoff Levand result = ps3_dma_map(dev->d_region, virt_addr, size, dma_handle, 5365c6fc8dbSGeert Uytterhoeven CBE_IOPTE_PP_W | CBE_IOPTE_PP_R | 5375c6fc8dbSGeert Uytterhoeven CBE_IOPTE_SO_RW | CBE_IOPTE_M); 5382a08ea69SGeoff Levand 5392a08ea69SGeoff Levand if (result) { 5402a08ea69SGeoff Levand pr_debug("%s:%d: ps3_dma_map failed (%d)\n", 5412a08ea69SGeoff Levand __func__, __LINE__, result); 5422a08ea69SGeoff Levand BUG_ON("check region type"); 5432a08ea69SGeoff Levand goto clean_alloc; 5442a08ea69SGeoff Levand } 5452a08ea69SGeoff Levand 5462a08ea69SGeoff Levand return (void*)virt_addr; 5472a08ea69SGeoff Levand 5482a08ea69SGeoff Levand clean_alloc: 5492a08ea69SGeoff Levand free_pages(virt_addr, get_order(size)); 5502a08ea69SGeoff Levand clean_none: 5512a08ea69SGeoff Levand dma_handle = NULL; 5522a08ea69SGeoff Levand return NULL; 5532a08ea69SGeoff Levand } 5542a08ea69SGeoff Levand 5552a08ea69SGeoff Levand static void ps3_free_coherent(struct device *_dev, size_t size, void *vaddr, 55600085f1eSKrzysztof Kozlowski dma_addr_t dma_handle, unsigned long attrs) 5572a08ea69SGeoff Levand { 5586bb5cf10SGeoff Levand struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); 5592a08ea69SGeoff Levand 5602a08ea69SGeoff Levand ps3_dma_unmap(dev->d_region, dma_handle, size); 5612a08ea69SGeoff Levand free_pages((unsigned long)vaddr, get_order(size)); 5622a08ea69SGeoff Levand } 5632a08ea69SGeoff Levand 5642a08ea69SGeoff Levand /* Creates TCEs for a user provided buffer. The user buffer must be 565f9226d57SMark Nelson * contiguous real kernel storage (not vmalloc). The address passed here 566f9226d57SMark Nelson * comprises a page address and offset into that page. The dma_addr_t 567f9226d57SMark Nelson * returned will point to the same byte within the page as was passed in. 5682a08ea69SGeoff Levand */ 5692a08ea69SGeoff Levand 570f9226d57SMark Nelson static dma_addr_t ps3_sb_map_page(struct device *_dev, struct page *page, 571f9226d57SMark Nelson unsigned long offset, size_t size, enum dma_data_direction direction, 57200085f1eSKrzysztof Kozlowski unsigned long attrs) 5732a08ea69SGeoff Levand { 5746bb5cf10SGeoff Levand struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); 5752a08ea69SGeoff Levand int result; 576494fd07aSStephen Rothwell dma_addr_t bus_addr; 577f9226d57SMark Nelson void *ptr = page_address(page) + offset; 5782a08ea69SGeoff Levand 5792a08ea69SGeoff Levand result = ps3_dma_map(dev->d_region, (unsigned long)ptr, size, 5806bb5cf10SGeoff Levand &bus_addr, 5815c6fc8dbSGeert Uytterhoeven CBE_IOPTE_PP_R | CBE_IOPTE_PP_W | 5825c6fc8dbSGeert Uytterhoeven CBE_IOPTE_SO_RW | CBE_IOPTE_M); 5832a08ea69SGeoff Levand 5842a08ea69SGeoff Levand if (result) { 5852a08ea69SGeoff Levand pr_debug("%s:%d: ps3_dma_map failed (%d)\n", 5862a08ea69SGeoff Levand __func__, __LINE__, result); 5872a08ea69SGeoff Levand } 5882a08ea69SGeoff Levand 5892a08ea69SGeoff Levand return bus_addr; 5902a08ea69SGeoff Levand } 5912a08ea69SGeoff Levand 592f9226d57SMark Nelson static dma_addr_t ps3_ioc0_map_page(struct device *_dev, struct page *page, 593f9226d57SMark Nelson unsigned long offset, size_t size, 5943affedc4SMark Nelson enum dma_data_direction direction, 59500085f1eSKrzysztof Kozlowski unsigned long attrs) 5966bb5cf10SGeoff Levand { 5976bb5cf10SGeoff Levand struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); 5986bb5cf10SGeoff Levand int result; 599494fd07aSStephen Rothwell dma_addr_t bus_addr; 6006bb5cf10SGeoff Levand u64 iopte_flag; 601f9226d57SMark Nelson void *ptr = page_address(page) + offset; 6026bb5cf10SGeoff Levand 6035c6fc8dbSGeert Uytterhoeven iopte_flag = CBE_IOPTE_M; 6046bb5cf10SGeoff Levand switch (direction) { 6056bb5cf10SGeoff Levand case DMA_BIDIRECTIONAL: 6065c6fc8dbSGeert Uytterhoeven iopte_flag |= CBE_IOPTE_PP_R | CBE_IOPTE_PP_W | CBE_IOPTE_SO_RW; 6076bb5cf10SGeoff Levand break; 6086bb5cf10SGeoff Levand case DMA_TO_DEVICE: 6095c6fc8dbSGeert Uytterhoeven iopte_flag |= CBE_IOPTE_PP_R | CBE_IOPTE_SO_R; 6106bb5cf10SGeoff Levand break; 6116bb5cf10SGeoff Levand case DMA_FROM_DEVICE: 6125c6fc8dbSGeert Uytterhoeven iopte_flag |= CBE_IOPTE_PP_W | CBE_IOPTE_SO_RW; 6136bb5cf10SGeoff Levand break; 6146bb5cf10SGeoff Levand default: 6156bb5cf10SGeoff Levand /* not happned */ 6166bb5cf10SGeoff Levand BUG(); 6176bb5cf10SGeoff Levand }; 6186bb5cf10SGeoff Levand result = ps3_dma_map(dev->d_region, (unsigned long)ptr, size, 6196bb5cf10SGeoff Levand &bus_addr, iopte_flag); 6206bb5cf10SGeoff Levand 6216bb5cf10SGeoff Levand if (result) { 6226bb5cf10SGeoff Levand pr_debug("%s:%d: ps3_dma_map failed (%d)\n", 6236bb5cf10SGeoff Levand __func__, __LINE__, result); 6246bb5cf10SGeoff Levand } 6256bb5cf10SGeoff Levand return bus_addr; 6266bb5cf10SGeoff Levand } 6276bb5cf10SGeoff Levand 628f9226d57SMark Nelson static void ps3_unmap_page(struct device *_dev, dma_addr_t dma_addr, 62900085f1eSKrzysztof Kozlowski size_t size, enum dma_data_direction direction, unsigned long attrs) 6302a08ea69SGeoff Levand { 6316bb5cf10SGeoff Levand struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); 6322a08ea69SGeoff Levand int result; 6332a08ea69SGeoff Levand 6342a08ea69SGeoff Levand result = ps3_dma_unmap(dev->d_region, dma_addr, size); 6352a08ea69SGeoff Levand 6362a08ea69SGeoff Levand if (result) { 6372a08ea69SGeoff Levand pr_debug("%s:%d: ps3_dma_unmap failed (%d)\n", 6382a08ea69SGeoff Levand __func__, __LINE__, result); 6392a08ea69SGeoff Levand } 6402a08ea69SGeoff Levand } 6412a08ea69SGeoff Levand 642d1ed455eSJens Axboe static int ps3_sb_map_sg(struct device *_dev, struct scatterlist *sgl, 64300085f1eSKrzysztof Kozlowski int nents, enum dma_data_direction direction, unsigned long attrs) 6442a08ea69SGeoff Levand { 6452a08ea69SGeoff Levand #if defined(CONFIG_PS3_DYNAMIC_DMA) 6462a08ea69SGeoff Levand BUG_ON("do"); 64735063bb2SGeoff Levand return -EPERM; 64835063bb2SGeoff Levand #else 6496bb5cf10SGeoff Levand struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); 650d1ed455eSJens Axboe struct scatterlist *sg; 651435e0b2bSStephen Rothwell int i; 652435e0b2bSStephen Rothwell 653d1ed455eSJens Axboe for_each_sg(sgl, sg, nents, i) { 65458b053e4SJens Axboe int result = ps3_dma_map(dev->d_region, sg_phys(sg), 65558b053e4SJens Axboe sg->length, &sg->dma_address, 0); 65635063bb2SGeoff Levand 65735063bb2SGeoff Levand if (result) { 65835063bb2SGeoff Levand pr_debug("%s:%d: ps3_dma_map failed (%d)\n", 65935063bb2SGeoff Levand __func__, __LINE__, result); 66035063bb2SGeoff Levand return -EINVAL; 66135063bb2SGeoff Levand } 66235063bb2SGeoff Levand 66335063bb2SGeoff Levand sg->dma_length = sg->length; 66435063bb2SGeoff Levand } 66535063bb2SGeoff Levand 66635063bb2SGeoff Levand return nents; 6672a08ea69SGeoff Levand #endif 6682a08ea69SGeoff Levand } 6692a08ea69SGeoff Levand 6706bb5cf10SGeoff Levand static int ps3_ioc0_map_sg(struct device *_dev, struct scatterlist *sg, 6716bb5cf10SGeoff Levand int nents, 6723affedc4SMark Nelson enum dma_data_direction direction, 67300085f1eSKrzysztof Kozlowski unsigned long attrs) 6746bb5cf10SGeoff Levand { 6756bb5cf10SGeoff Levand BUG(); 6766bb5cf10SGeoff Levand return 0; 6776bb5cf10SGeoff Levand } 6786bb5cf10SGeoff Levand 6796bb5cf10SGeoff Levand static void ps3_sb_unmap_sg(struct device *_dev, struct scatterlist *sg, 68000085f1eSKrzysztof Kozlowski int nents, enum dma_data_direction direction, unsigned long attrs) 6812a08ea69SGeoff Levand { 6822a08ea69SGeoff Levand #if defined(CONFIG_PS3_DYNAMIC_DMA) 6832a08ea69SGeoff Levand BUG_ON("do"); 6842a08ea69SGeoff Levand #endif 6852a08ea69SGeoff Levand } 6862a08ea69SGeoff Levand 6876bb5cf10SGeoff Levand static void ps3_ioc0_unmap_sg(struct device *_dev, struct scatterlist *sg, 6883affedc4SMark Nelson int nents, enum dma_data_direction direction, 68900085f1eSKrzysztof Kozlowski unsigned long attrs) 6906bb5cf10SGeoff Levand { 6916bb5cf10SGeoff Levand BUG(); 6926bb5cf10SGeoff Levand } 6936bb5cf10SGeoff Levand 6942a08ea69SGeoff Levand static int ps3_dma_supported(struct device *_dev, u64 mask) 6952a08ea69SGeoff Levand { 696284901a9SYang Hongyang return mask >= DMA_BIT_MASK(32); 6972a08ea69SGeoff Levand } 6982a08ea69SGeoff Levand 699d24f9c69SMilton Miller static u64 ps3_dma_get_required_mask(struct device *_dev) 700d24f9c69SMilton Miller { 701d24f9c69SMilton Miller return DMA_BIT_MASK(32); 702d24f9c69SMilton Miller } 703d24f9c69SMilton Miller 7045299709dSBart Van Assche static const struct dma_map_ops ps3_sb_dma_ops = { 705bfbf7d61SAndrzej Pietrasiewicz .alloc = ps3_alloc_coherent, 706bfbf7d61SAndrzej Pietrasiewicz .free = ps3_free_coherent, 7076bb5cf10SGeoff Levand .map_sg = ps3_sb_map_sg, 7086bb5cf10SGeoff Levand .unmap_sg = ps3_sb_unmap_sg, 709f9226d57SMark Nelson .dma_supported = ps3_dma_supported, 710d24f9c69SMilton Miller .get_required_mask = ps3_dma_get_required_mask, 711f9226d57SMark Nelson .map_page = ps3_sb_map_page, 712f9226d57SMark Nelson .unmap_page = ps3_unmap_page, 7136bb5cf10SGeoff Levand }; 7146bb5cf10SGeoff Levand 7155299709dSBart Van Assche static const struct dma_map_ops ps3_ioc0_dma_ops = { 716bfbf7d61SAndrzej Pietrasiewicz .alloc = ps3_alloc_coherent, 717bfbf7d61SAndrzej Pietrasiewicz .free = ps3_free_coherent, 7186bb5cf10SGeoff Levand .map_sg = ps3_ioc0_map_sg, 7196bb5cf10SGeoff Levand .unmap_sg = ps3_ioc0_unmap_sg, 720f9226d57SMark Nelson .dma_supported = ps3_dma_supported, 721d24f9c69SMilton Miller .get_required_mask = ps3_dma_get_required_mask, 722f9226d57SMark Nelson .map_page = ps3_ioc0_map_page, 723f9226d57SMark Nelson .unmap_page = ps3_unmap_page, 7242a08ea69SGeoff Levand }; 7252a08ea69SGeoff Levand 7262a08ea69SGeoff Levand /** 7272a08ea69SGeoff Levand * ps3_system_bus_release_device - remove a device from the system bus 7282a08ea69SGeoff Levand */ 7292a08ea69SGeoff Levand 7302a08ea69SGeoff Levand static void ps3_system_bus_release_device(struct device *_dev) 7312a08ea69SGeoff Levand { 7326bb5cf10SGeoff Levand struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev); 7332a08ea69SGeoff Levand kfree(dev); 7342a08ea69SGeoff Levand } 7352a08ea69SGeoff Levand 7362a08ea69SGeoff Levand /** 7372a08ea69SGeoff Levand * ps3_system_bus_device_register - add a device to the system bus 7382a08ea69SGeoff Levand * 7392a08ea69SGeoff Levand * ps3_system_bus_device_register() expects the dev object to be allocated 7402a08ea69SGeoff Levand * dynamically by the caller. The system bus takes ownership of the dev 7412a08ea69SGeoff Levand * object and frees the object in ps3_system_bus_release_device(). 7422a08ea69SGeoff Levand */ 7432a08ea69SGeoff Levand 7442a08ea69SGeoff Levand int ps3_system_bus_device_register(struct ps3_system_bus_device *dev) 7452a08ea69SGeoff Levand { 7462a08ea69SGeoff Levand int result; 7476bb5cf10SGeoff Levand static unsigned int dev_ioc0_count; 7486bb5cf10SGeoff Levand static unsigned int dev_sb_count; 7496bb5cf10SGeoff Levand static unsigned int dev_vuart_count; 750ed757002SGeoff Levand static unsigned int dev_lpm_count; 7512a08ea69SGeoff Levand 7526bb5cf10SGeoff Levand if (!dev->core.parent) 7536bb5cf10SGeoff Levand dev->core.parent = &ps3_system_bus; 7542a08ea69SGeoff Levand dev->core.bus = &ps3_system_bus_type; 7552a08ea69SGeoff Levand dev->core.release = ps3_system_bus_release_device; 7562a08ea69SGeoff Levand 7576bb5cf10SGeoff Levand switch (dev->dev_type) { 7586bb5cf10SGeoff Levand case PS3_DEVICE_TYPE_IOC0: 7596bb5cf10SGeoff Levand dev->core.archdata.dma_ops = &ps3_ioc0_dma_ops; 760aab0d375SKay Sievers dev_set_name(&dev->core, "ioc0_%02x", ++dev_ioc0_count); 7616bb5cf10SGeoff Levand break; 7626bb5cf10SGeoff Levand case PS3_DEVICE_TYPE_SB: 7636bb5cf10SGeoff Levand dev->core.archdata.dma_ops = &ps3_sb_dma_ops; 764aab0d375SKay Sievers dev_set_name(&dev->core, "sb_%02x", ++dev_sb_count); 7652a08ea69SGeoff Levand 7666bb5cf10SGeoff Levand break; 7676bb5cf10SGeoff Levand case PS3_DEVICE_TYPE_VUART: 768aab0d375SKay Sievers dev_set_name(&dev->core, "vuart_%02x", ++dev_vuart_count); 7696bb5cf10SGeoff Levand break; 770ed757002SGeoff Levand case PS3_DEVICE_TYPE_LPM: 771aab0d375SKay Sievers dev_set_name(&dev->core, "lpm_%02x", ++dev_lpm_count); 772ed757002SGeoff Levand break; 7736bb5cf10SGeoff Levand default: 7746bb5cf10SGeoff Levand BUG(); 7756bb5cf10SGeoff Levand }; 7766bb5cf10SGeoff Levand 777d706c1b0SGrant Likely dev->core.of_node = NULL; 7788fae0353SBecky Bruce set_dev_node(&dev->core, 0); 7792a08ea69SGeoff Levand 780aab0d375SKay Sievers pr_debug("%s:%d add %s\n", __func__, __LINE__, dev_name(&dev->core)); 7812a08ea69SGeoff Levand 7822a08ea69SGeoff Levand result = device_register(&dev->core); 7832a08ea69SGeoff Levand return result; 7842a08ea69SGeoff Levand } 7852a08ea69SGeoff Levand 7862a08ea69SGeoff Levand EXPORT_SYMBOL_GPL(ps3_system_bus_device_register); 7872a08ea69SGeoff Levand 7882a08ea69SGeoff Levand int ps3_system_bus_driver_register(struct ps3_system_bus_driver *drv) 7892a08ea69SGeoff Levand { 7902a08ea69SGeoff Levand int result; 7912a08ea69SGeoff Levand 7926bb5cf10SGeoff Levand pr_debug(" -> %s:%d: %s\n", __func__, __LINE__, drv->core.name); 7936bb5cf10SGeoff Levand 7946bb5cf10SGeoff Levand if (!firmware_has_feature(FW_FEATURE_PS3_LV1)) 7956bb5cf10SGeoff Levand return -ENODEV; 7966bb5cf10SGeoff Levand 7972a08ea69SGeoff Levand drv->core.bus = &ps3_system_bus_type; 7982a08ea69SGeoff Levand 7992a08ea69SGeoff Levand result = driver_register(&drv->core); 8006bb5cf10SGeoff Levand pr_debug(" <- %s:%d: %s\n", __func__, __LINE__, drv->core.name); 8012a08ea69SGeoff Levand return result; 8022a08ea69SGeoff Levand } 8032a08ea69SGeoff Levand 8042a08ea69SGeoff Levand EXPORT_SYMBOL_GPL(ps3_system_bus_driver_register); 8052a08ea69SGeoff Levand 8062a08ea69SGeoff Levand void ps3_system_bus_driver_unregister(struct ps3_system_bus_driver *drv) 8072a08ea69SGeoff Levand { 8086bb5cf10SGeoff Levand pr_debug(" -> %s:%d: %s\n", __func__, __LINE__, drv->core.name); 8092a08ea69SGeoff Levand driver_unregister(&drv->core); 8106bb5cf10SGeoff Levand pr_debug(" <- %s:%d: %s\n", __func__, __LINE__, drv->core.name); 8112a08ea69SGeoff Levand } 8122a08ea69SGeoff Levand 8132a08ea69SGeoff Levand EXPORT_SYMBOL_GPL(ps3_system_bus_driver_unregister); 814