Path: blob/master/arch/powerpc/platforms/ps3/system-bus.c
10818 views
/*1* PS3 system bus driver.2*3* Copyright (C) 2006 Sony Computer Entertainment Inc.4* Copyright 2006 Sony Corp.5*6* This program is free software; you can redistribute it and/or modify7* it under the terms of the GNU General Public License as published by8* the Free Software Foundation; version 2 of the License.9*10* This program is distributed in the hope that it will be useful,11* but WITHOUT ANY WARRANTY; without even the implied warranty of12* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the13* GNU General Public License for more details.14*15* You should have received a copy of the GNU General Public License16* along with this program; if not, write to the Free Software17* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA18*/1920#include <linux/kernel.h>21#include <linux/init.h>22#include <linux/module.h>23#include <linux/dma-mapping.h>24#include <linux/err.h>25#include <linux/slab.h>2627#include <asm/udbg.h>28#include <asm/lv1call.h>29#include <asm/firmware.h>30#include <asm/cell-regs.h>3132#include "platform.h"3334static struct device ps3_system_bus = {35.init_name = "ps3_system",36};3738/* FIXME: need device usage counters! */39struct {40struct mutex mutex;41int sb_11; /* usb 0 */42int sb_12; /* usb 0 */43int gpu;44} static usage_hack;4546static int ps3_is_device(struct ps3_system_bus_device *dev, u64 bus_id,47u64 dev_id)48{49return dev->bus_id == bus_id && dev->dev_id == dev_id;50}5152static int ps3_open_hv_device_sb(struct ps3_system_bus_device *dev)53{54int result;5556BUG_ON(!dev->bus_id);57mutex_lock(&usage_hack.mutex);5859if (ps3_is_device(dev, 1, 1)) {60usage_hack.sb_11++;61if (usage_hack.sb_11 > 1) {62result = 0;63goto done;64}65}6667if (ps3_is_device(dev, 1, 2)) {68usage_hack.sb_12++;69if (usage_hack.sb_12 > 1) {70result = 0;71goto done;72}73}7475result = lv1_open_device(dev->bus_id, dev->dev_id, 0);7677if (result) {78pr_debug("%s:%d: lv1_open_device failed: %s\n", __func__,79__LINE__, ps3_result(result));80result = -EPERM;81}8283done:84mutex_unlock(&usage_hack.mutex);85return result;86}8788static int ps3_close_hv_device_sb(struct ps3_system_bus_device *dev)89{90int result;9192BUG_ON(!dev->bus_id);93mutex_lock(&usage_hack.mutex);9495if (ps3_is_device(dev, 1, 1)) {96usage_hack.sb_11--;97if (usage_hack.sb_11) {98result = 0;99goto done;100}101}102103if (ps3_is_device(dev, 1, 2)) {104usage_hack.sb_12--;105if (usage_hack.sb_12) {106result = 0;107goto done;108}109}110111result = lv1_close_device(dev->bus_id, dev->dev_id);112BUG_ON(result);113114done:115mutex_unlock(&usage_hack.mutex);116return result;117}118119static int ps3_open_hv_device_gpu(struct ps3_system_bus_device *dev)120{121int result;122123mutex_lock(&usage_hack.mutex);124125usage_hack.gpu++;126if (usage_hack.gpu > 1) {127result = 0;128goto done;129}130131result = lv1_gpu_open(0);132133if (result) {134pr_debug("%s:%d: lv1_gpu_open failed: %s\n", __func__,135__LINE__, ps3_result(result));136result = -EPERM;137}138139done:140mutex_unlock(&usage_hack.mutex);141return result;142}143144static int ps3_close_hv_device_gpu(struct ps3_system_bus_device *dev)145{146int result;147148mutex_lock(&usage_hack.mutex);149150usage_hack.gpu--;151if (usage_hack.gpu) {152result = 0;153goto done;154}155156result = lv1_gpu_close();157BUG_ON(result);158159done:160mutex_unlock(&usage_hack.mutex);161return result;162}163164int ps3_open_hv_device(struct ps3_system_bus_device *dev)165{166BUG_ON(!dev);167pr_debug("%s:%d: match_id: %u\n", __func__, __LINE__, dev->match_id);168169switch (dev->match_id) {170case PS3_MATCH_ID_EHCI:171case PS3_MATCH_ID_OHCI:172case PS3_MATCH_ID_GELIC:173case PS3_MATCH_ID_STOR_DISK:174case PS3_MATCH_ID_STOR_ROM:175case PS3_MATCH_ID_STOR_FLASH:176return ps3_open_hv_device_sb(dev);177178case PS3_MATCH_ID_SOUND:179case PS3_MATCH_ID_GPU:180return ps3_open_hv_device_gpu(dev);181182case PS3_MATCH_ID_AV_SETTINGS:183case PS3_MATCH_ID_SYSTEM_MANAGER:184pr_debug("%s:%d: unsupported match_id: %u\n", __func__,185__LINE__, dev->match_id);186pr_debug("%s:%d: bus_id: %llu\n", __func__, __LINE__,187dev->bus_id);188BUG();189return -EINVAL;190191default:192break;193}194195pr_debug("%s:%d: unknown match_id: %u\n", __func__, __LINE__,196dev->match_id);197BUG();198return -ENODEV;199}200EXPORT_SYMBOL_GPL(ps3_open_hv_device);201202int ps3_close_hv_device(struct ps3_system_bus_device *dev)203{204BUG_ON(!dev);205pr_debug("%s:%d: match_id: %u\n", __func__, __LINE__, dev->match_id);206207switch (dev->match_id) {208case PS3_MATCH_ID_EHCI:209case PS3_MATCH_ID_OHCI:210case PS3_MATCH_ID_GELIC:211case PS3_MATCH_ID_STOR_DISK:212case PS3_MATCH_ID_STOR_ROM:213case PS3_MATCH_ID_STOR_FLASH:214return ps3_close_hv_device_sb(dev);215216case PS3_MATCH_ID_SOUND:217case PS3_MATCH_ID_GPU:218return ps3_close_hv_device_gpu(dev);219220case PS3_MATCH_ID_AV_SETTINGS:221case PS3_MATCH_ID_SYSTEM_MANAGER:222pr_debug("%s:%d: unsupported match_id: %u\n", __func__,223__LINE__, dev->match_id);224pr_debug("%s:%d: bus_id: %llu\n", __func__, __LINE__,225dev->bus_id);226BUG();227return -EINVAL;228229default:230break;231}232233pr_debug("%s:%d: unknown match_id: %u\n", __func__, __LINE__,234dev->match_id);235BUG();236return -ENODEV;237}238EXPORT_SYMBOL_GPL(ps3_close_hv_device);239240#define dump_mmio_region(_a) _dump_mmio_region(_a, __func__, __LINE__)241static void _dump_mmio_region(const struct ps3_mmio_region* r,242const char* func, int line)243{244pr_debug("%s:%d: dev %llu:%llu\n", func, line, r->dev->bus_id,245r->dev->dev_id);246pr_debug("%s:%d: bus_addr %lxh\n", func, line, r->bus_addr);247pr_debug("%s:%d: len %lxh\n", func, line, r->len);248pr_debug("%s:%d: lpar_addr %lxh\n", func, line, r->lpar_addr);249}250251static int ps3_sb_mmio_region_create(struct ps3_mmio_region *r)252{253int result;254u64 lpar_addr;255256result = lv1_map_device_mmio_region(r->dev->bus_id, r->dev->dev_id,257r->bus_addr, r->len, r->page_size, &lpar_addr);258r->lpar_addr = lpar_addr;259260if (result) {261pr_debug("%s:%d: lv1_map_device_mmio_region failed: %s\n",262__func__, __LINE__, ps3_result(result));263r->lpar_addr = 0;264}265266dump_mmio_region(r);267return result;268}269270static int ps3_ioc0_mmio_region_create(struct ps3_mmio_region *r)271{272/* device specific; do nothing currently */273return 0;274}275276int ps3_mmio_region_create(struct ps3_mmio_region *r)277{278return r->mmio_ops->create(r);279}280EXPORT_SYMBOL_GPL(ps3_mmio_region_create);281282static int ps3_sb_free_mmio_region(struct ps3_mmio_region *r)283{284int result;285286dump_mmio_region(r);287result = lv1_unmap_device_mmio_region(r->dev->bus_id, r->dev->dev_id,288r->lpar_addr);289290if (result)291pr_debug("%s:%d: lv1_unmap_device_mmio_region failed: %s\n",292__func__, __LINE__, ps3_result(result));293294r->lpar_addr = 0;295return result;296}297298static int ps3_ioc0_free_mmio_region(struct ps3_mmio_region *r)299{300/* device specific; do nothing currently */301return 0;302}303304305int ps3_free_mmio_region(struct ps3_mmio_region *r)306{307return r->mmio_ops->free(r);308}309310EXPORT_SYMBOL_GPL(ps3_free_mmio_region);311312static const struct ps3_mmio_region_ops ps3_mmio_sb_region_ops = {313.create = ps3_sb_mmio_region_create,314.free = ps3_sb_free_mmio_region315};316317static const struct ps3_mmio_region_ops ps3_mmio_ioc0_region_ops = {318.create = ps3_ioc0_mmio_region_create,319.free = ps3_ioc0_free_mmio_region320};321322int ps3_mmio_region_init(struct ps3_system_bus_device *dev,323struct ps3_mmio_region *r, unsigned long bus_addr, unsigned long len,324enum ps3_mmio_page_size page_size)325{326r->dev = dev;327r->bus_addr = bus_addr;328r->len = len;329r->page_size = page_size;330switch (dev->dev_type) {331case PS3_DEVICE_TYPE_SB:332r->mmio_ops = &ps3_mmio_sb_region_ops;333break;334case PS3_DEVICE_TYPE_IOC0:335r->mmio_ops = &ps3_mmio_ioc0_region_ops;336break;337default:338BUG();339return -EINVAL;340}341return 0;342}343EXPORT_SYMBOL_GPL(ps3_mmio_region_init);344345static int ps3_system_bus_match(struct device *_dev,346struct device_driver *_drv)347{348int result;349struct ps3_system_bus_driver *drv = ps3_drv_to_system_bus_drv(_drv);350struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev);351352if (!dev->match_sub_id)353result = dev->match_id == drv->match_id;354else355result = dev->match_sub_id == drv->match_sub_id &&356dev->match_id == drv->match_id;357358if (result)359pr_info("%s:%d: dev=%u.%u(%s), drv=%u.%u(%s): match\n",360__func__, __LINE__,361dev->match_id, dev->match_sub_id, dev_name(&dev->core),362drv->match_id, drv->match_sub_id, drv->core.name);363else364pr_debug("%s:%d: dev=%u.%u(%s), drv=%u.%u(%s): miss\n",365__func__, __LINE__,366dev->match_id, dev->match_sub_id, dev_name(&dev->core),367drv->match_id, drv->match_sub_id, drv->core.name);368369return result;370}371372static int ps3_system_bus_probe(struct device *_dev)373{374int result = 0;375struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev);376struct ps3_system_bus_driver *drv;377378BUG_ON(!dev);379dev_dbg(_dev, "%s:%d\n", __func__, __LINE__);380381drv = ps3_system_bus_dev_to_system_bus_drv(dev);382BUG_ON(!drv);383384if (drv->probe)385result = drv->probe(dev);386else387pr_debug("%s:%d: %s no probe method\n", __func__, __LINE__,388dev_name(&dev->core));389390pr_debug(" <- %s:%d: %s\n", __func__, __LINE__, dev_name(&dev->core));391return result;392}393394static int ps3_system_bus_remove(struct device *_dev)395{396int result = 0;397struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev);398struct ps3_system_bus_driver *drv;399400BUG_ON(!dev);401dev_dbg(_dev, "%s:%d\n", __func__, __LINE__);402403drv = ps3_system_bus_dev_to_system_bus_drv(dev);404BUG_ON(!drv);405406if (drv->remove)407result = drv->remove(dev);408else409dev_dbg(&dev->core, "%s:%d %s: no remove method\n",410__func__, __LINE__, drv->core.name);411412pr_debug(" <- %s:%d: %s\n", __func__, __LINE__, dev_name(&dev->core));413return result;414}415416static void ps3_system_bus_shutdown(struct device *_dev)417{418struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev);419struct ps3_system_bus_driver *drv;420421BUG_ON(!dev);422423dev_dbg(&dev->core, " -> %s:%d: match_id %d\n", __func__, __LINE__,424dev->match_id);425426if (!dev->core.driver) {427dev_dbg(&dev->core, "%s:%d: no driver bound\n", __func__,428__LINE__);429return;430}431432drv = ps3_system_bus_dev_to_system_bus_drv(dev);433434BUG_ON(!drv);435436dev_dbg(&dev->core, "%s:%d: %s -> %s\n", __func__, __LINE__,437dev_name(&dev->core), drv->core.name);438439if (drv->shutdown)440drv->shutdown(dev);441else if (drv->remove) {442dev_dbg(&dev->core, "%s:%d %s: no shutdown, calling remove\n",443__func__, __LINE__, drv->core.name);444drv->remove(dev);445} else {446dev_dbg(&dev->core, "%s:%d %s: no shutdown method\n",447__func__, __LINE__, drv->core.name);448BUG();449}450451dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__);452}453454static int ps3_system_bus_uevent(struct device *_dev, struct kobj_uevent_env *env)455{456struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev);457458if (add_uevent_var(env, "MODALIAS=ps3:%d:%d", dev->match_id,459dev->match_sub_id))460return -ENOMEM;461return 0;462}463464static ssize_t modalias_show(struct device *_dev, struct device_attribute *a,465char *buf)466{467struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev);468int len = snprintf(buf, PAGE_SIZE, "ps3:%d:%d\n", dev->match_id,469dev->match_sub_id);470471return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len;472}473474static struct device_attribute ps3_system_bus_dev_attrs[] = {475__ATTR_RO(modalias),476__ATTR_NULL,477};478479struct bus_type ps3_system_bus_type = {480.name = "ps3_system_bus",481.match = ps3_system_bus_match,482.uevent = ps3_system_bus_uevent,483.probe = ps3_system_bus_probe,484.remove = ps3_system_bus_remove,485.shutdown = ps3_system_bus_shutdown,486.dev_attrs = ps3_system_bus_dev_attrs,487};488489static int __init ps3_system_bus_init(void)490{491int result;492493if (!firmware_has_feature(FW_FEATURE_PS3_LV1))494return -ENODEV;495496pr_debug(" -> %s:%d\n", __func__, __LINE__);497498mutex_init(&usage_hack.mutex);499500result = device_register(&ps3_system_bus);501BUG_ON(result);502503result = bus_register(&ps3_system_bus_type);504BUG_ON(result);505506pr_debug(" <- %s:%d\n", __func__, __LINE__);507return result;508}509510core_initcall(ps3_system_bus_init);511512/* Allocates a contiguous real buffer and creates mappings over it.513* Returns the virtual address of the buffer and sets dma_handle514* to the dma address (mapping) of the first page.515*/516static void * ps3_alloc_coherent(struct device *_dev, size_t size,517dma_addr_t *dma_handle, gfp_t flag)518{519int result;520struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev);521unsigned long virt_addr;522523flag &= ~(__GFP_DMA | __GFP_HIGHMEM);524flag |= __GFP_ZERO;525526virt_addr = __get_free_pages(flag, get_order(size));527528if (!virt_addr) {529pr_debug("%s:%d: get_free_pages failed\n", __func__, __LINE__);530goto clean_none;531}532533result = ps3_dma_map(dev->d_region, virt_addr, size, dma_handle,534CBE_IOPTE_PP_W | CBE_IOPTE_PP_R |535CBE_IOPTE_SO_RW | CBE_IOPTE_M);536537if (result) {538pr_debug("%s:%d: ps3_dma_map failed (%d)\n",539__func__, __LINE__, result);540BUG_ON("check region type");541goto clean_alloc;542}543544return (void*)virt_addr;545546clean_alloc:547free_pages(virt_addr, get_order(size));548clean_none:549dma_handle = NULL;550return NULL;551}552553static void ps3_free_coherent(struct device *_dev, size_t size, void *vaddr,554dma_addr_t dma_handle)555{556struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev);557558ps3_dma_unmap(dev->d_region, dma_handle, size);559free_pages((unsigned long)vaddr, get_order(size));560}561562/* Creates TCEs for a user provided buffer. The user buffer must be563* contiguous real kernel storage (not vmalloc). The address passed here564* comprises a page address and offset into that page. The dma_addr_t565* returned will point to the same byte within the page as was passed in.566*/567568static dma_addr_t ps3_sb_map_page(struct device *_dev, struct page *page,569unsigned long offset, size_t size, enum dma_data_direction direction,570struct dma_attrs *attrs)571{572struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev);573int result;574dma_addr_t bus_addr;575void *ptr = page_address(page) + offset;576577result = ps3_dma_map(dev->d_region, (unsigned long)ptr, size,578&bus_addr,579CBE_IOPTE_PP_R | CBE_IOPTE_PP_W |580CBE_IOPTE_SO_RW | CBE_IOPTE_M);581582if (result) {583pr_debug("%s:%d: ps3_dma_map failed (%d)\n",584__func__, __LINE__, result);585}586587return bus_addr;588}589590static dma_addr_t ps3_ioc0_map_page(struct device *_dev, struct page *page,591unsigned long offset, size_t size,592enum dma_data_direction direction,593struct dma_attrs *attrs)594{595struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev);596int result;597dma_addr_t bus_addr;598u64 iopte_flag;599void *ptr = page_address(page) + offset;600601iopte_flag = CBE_IOPTE_M;602switch (direction) {603case DMA_BIDIRECTIONAL:604iopte_flag |= CBE_IOPTE_PP_R | CBE_IOPTE_PP_W | CBE_IOPTE_SO_RW;605break;606case DMA_TO_DEVICE:607iopte_flag |= CBE_IOPTE_PP_R | CBE_IOPTE_SO_R;608break;609case DMA_FROM_DEVICE:610iopte_flag |= CBE_IOPTE_PP_W | CBE_IOPTE_SO_RW;611break;612default:613/* not happned */614BUG();615};616result = ps3_dma_map(dev->d_region, (unsigned long)ptr, size,617&bus_addr, iopte_flag);618619if (result) {620pr_debug("%s:%d: ps3_dma_map failed (%d)\n",621__func__, __LINE__, result);622}623return bus_addr;624}625626static void ps3_unmap_page(struct device *_dev, dma_addr_t dma_addr,627size_t size, enum dma_data_direction direction, struct dma_attrs *attrs)628{629struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev);630int result;631632result = ps3_dma_unmap(dev->d_region, dma_addr, size);633634if (result) {635pr_debug("%s:%d: ps3_dma_unmap failed (%d)\n",636__func__, __LINE__, result);637}638}639640static int ps3_sb_map_sg(struct device *_dev, struct scatterlist *sgl,641int nents, enum dma_data_direction direction, struct dma_attrs *attrs)642{643#if defined(CONFIG_PS3_DYNAMIC_DMA)644BUG_ON("do");645return -EPERM;646#else647struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev);648struct scatterlist *sg;649int i;650651for_each_sg(sgl, sg, nents, i) {652int result = ps3_dma_map(dev->d_region, sg_phys(sg),653sg->length, &sg->dma_address, 0);654655if (result) {656pr_debug("%s:%d: ps3_dma_map failed (%d)\n",657__func__, __LINE__, result);658return -EINVAL;659}660661sg->dma_length = sg->length;662}663664return nents;665#endif666}667668static int ps3_ioc0_map_sg(struct device *_dev, struct scatterlist *sg,669int nents,670enum dma_data_direction direction,671struct dma_attrs *attrs)672{673BUG();674return 0;675}676677static void ps3_sb_unmap_sg(struct device *_dev, struct scatterlist *sg,678int nents, enum dma_data_direction direction, struct dma_attrs *attrs)679{680#if defined(CONFIG_PS3_DYNAMIC_DMA)681BUG_ON("do");682#endif683}684685static void ps3_ioc0_unmap_sg(struct device *_dev, struct scatterlist *sg,686int nents, enum dma_data_direction direction,687struct dma_attrs *attrs)688{689BUG();690}691692static int ps3_dma_supported(struct device *_dev, u64 mask)693{694return mask >= DMA_BIT_MASK(32);695}696697static struct dma_map_ops ps3_sb_dma_ops = {698.alloc_coherent = ps3_alloc_coherent,699.free_coherent = ps3_free_coherent,700.map_sg = ps3_sb_map_sg,701.unmap_sg = ps3_sb_unmap_sg,702.dma_supported = ps3_dma_supported,703.map_page = ps3_sb_map_page,704.unmap_page = ps3_unmap_page,705};706707static struct dma_map_ops ps3_ioc0_dma_ops = {708.alloc_coherent = ps3_alloc_coherent,709.free_coherent = ps3_free_coherent,710.map_sg = ps3_ioc0_map_sg,711.unmap_sg = ps3_ioc0_unmap_sg,712.dma_supported = ps3_dma_supported,713.map_page = ps3_ioc0_map_page,714.unmap_page = ps3_unmap_page,715};716717/**718* ps3_system_bus_release_device - remove a device from the system bus719*/720721static void ps3_system_bus_release_device(struct device *_dev)722{723struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev);724kfree(dev);725}726727/**728* ps3_system_bus_device_register - add a device to the system bus729*730* ps3_system_bus_device_register() expects the dev object to be allocated731* dynamically by the caller. The system bus takes ownership of the dev732* object and frees the object in ps3_system_bus_release_device().733*/734735int ps3_system_bus_device_register(struct ps3_system_bus_device *dev)736{737int result;738static unsigned int dev_ioc0_count;739static unsigned int dev_sb_count;740static unsigned int dev_vuart_count;741static unsigned int dev_lpm_count;742743if (!dev->core.parent)744dev->core.parent = &ps3_system_bus;745dev->core.bus = &ps3_system_bus_type;746dev->core.release = ps3_system_bus_release_device;747748switch (dev->dev_type) {749case PS3_DEVICE_TYPE_IOC0:750dev->core.archdata.dma_ops = &ps3_ioc0_dma_ops;751dev_set_name(&dev->core, "ioc0_%02x", ++dev_ioc0_count);752break;753case PS3_DEVICE_TYPE_SB:754dev->core.archdata.dma_ops = &ps3_sb_dma_ops;755dev_set_name(&dev->core, "sb_%02x", ++dev_sb_count);756757break;758case PS3_DEVICE_TYPE_VUART:759dev_set_name(&dev->core, "vuart_%02x", ++dev_vuart_count);760break;761case PS3_DEVICE_TYPE_LPM:762dev_set_name(&dev->core, "lpm_%02x", ++dev_lpm_count);763break;764default:765BUG();766};767768dev->core.of_node = NULL;769set_dev_node(&dev->core, 0);770771pr_debug("%s:%d add %s\n", __func__, __LINE__, dev_name(&dev->core));772773result = device_register(&dev->core);774return result;775}776777EXPORT_SYMBOL_GPL(ps3_system_bus_device_register);778779int ps3_system_bus_driver_register(struct ps3_system_bus_driver *drv)780{781int result;782783pr_debug(" -> %s:%d: %s\n", __func__, __LINE__, drv->core.name);784785if (!firmware_has_feature(FW_FEATURE_PS3_LV1))786return -ENODEV;787788drv->core.bus = &ps3_system_bus_type;789790result = driver_register(&drv->core);791pr_debug(" <- %s:%d: %s\n", __func__, __LINE__, drv->core.name);792return result;793}794795EXPORT_SYMBOL_GPL(ps3_system_bus_driver_register);796797void ps3_system_bus_driver_unregister(struct ps3_system_bus_driver *drv)798{799pr_debug(" -> %s:%d: %s\n", __func__, __LINE__, drv->core.name);800driver_unregister(&drv->core);801pr_debug(" <- %s:%d: %s\n", __func__, __LINE__, drv->core.name);802}803804EXPORT_SYMBOL_GPL(ps3_system_bus_driver_unregister);805806807