2007-01-16 07:11:47 +03:00
/*
* PS3 EHCI Host Controller driver
*
* Copyright ( C ) 2006 Sony Computer Entertainment Inc .
* Copyright 2006 Sony Corp .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; version 2 of the License .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
2007-06-06 07:04:35 +04:00
# include <asm/firmware.h>
2007-01-16 07:11:47 +03:00
# include <asm/ps3.h>
2011-11-23 06:33:45 +04:00
static void ps3_ehci_setup_insnreg ( struct ehci_hcd * ehci )
{
/* PS3 HC internal setup register offsets. */
enum ps3_ehci_hc_insnreg {
ps3_ehci_hc_insnreg01 = 0x084 ,
ps3_ehci_hc_insnreg02 = 0x088 ,
ps3_ehci_hc_insnreg03 = 0x08c ,
} ;
/* PS3 EHCI HC errata fix 316 - The PS3 EHCI HC will reset its
* internal INSNREGXX setup regs back to the chip default values
* on Host Controller Reset ( CMD_RESET ) or Light Host Controller
* Reset ( CMD_LRESET ) . The work - around for this is for the HC
* driver to re - initialise these regs when ever the HC is reset .
*/
/* Set burst transfer counts to 256 out, 32 in. */
writel_be ( 0x01000020 , ( void __iomem * ) ehci - > regs +
ps3_ehci_hc_insnreg01 ) ;
/* Enable burst transfer counts. */
writel_be ( 0x00000001 , ( void __iomem * ) ehci - > regs +
ps3_ehci_hc_insnreg03 ) ;
}
2007-01-16 07:11:47 +03:00
static int ps3_ehci_hc_reset ( struct usb_hcd * hcd )
{
int result ;
struct ehci_hcd * ehci = hcd_to_ehci ( hcd ) ;
ehci - > big_endian_mmio = 1 ;
ehci - > caps = hcd - > regs ;
2012-07-09 23:55:14 +04:00
result = ehci_setup ( hcd ) ;
2007-01-16 07:11:47 +03:00
if ( result )
return result ;
2011-11-23 06:33:45 +04:00
ps3_ehci_setup_insnreg ( ehci ) ;
2007-01-16 07:11:47 +03:00
return result ;
}
static const struct hc_driver ps3_ehci_hc_driver = {
. description = hcd_name ,
. product_desc = " PS3 EHCI Host Controller " ,
. hcd_priv_size = sizeof ( struct ehci_hcd ) ,
. irq = ehci_irq ,
2013-09-24 00:32:51 +04:00
. flags = HCD_MEMORY | HCD_USB2 | HCD_BH ,
2007-01-16 07:11:47 +03:00
. reset = ps3_ehci_hc_reset ,
. start = ehci_run ,
. stop = ehci_stop ,
. shutdown = ehci_shutdown ,
. urb_enqueue = ehci_urb_enqueue ,
. urb_dequeue = ehci_urb_dequeue ,
. endpoint_disable = ehci_endpoint_disable ,
2009-05-28 02:21:56 +04:00
. endpoint_reset = ehci_endpoint_reset ,
2007-01-16 07:11:47 +03:00
. get_frame_number = ehci_get_frame ,
. hub_status_data = ehci_hub_status_data ,
. hub_control = ehci_hub_control ,
# if defined(CONFIG_PM)
. bus_suspend = ehci_bus_suspend ,
. bus_resume = ehci_bus_resume ,
# endif
2007-11-21 23:28:14 +03:00
. relinquish_port = ehci_relinquish_port ,
2008-05-21 00:58:29 +04:00
. port_handed_over = ehci_port_handed_over ,
2009-06-29 18:47:30 +04:00
. clear_tt_buffer_complete = ehci_clear_tt_buffer_complete ,
2007-01-16 07:11:47 +03:00
} ;
2012-11-19 22:21:48 +04:00
static int ps3_ehci_probe ( struct ps3_system_bus_device * dev )
2007-01-16 07:11:47 +03:00
{
int result ;
struct usb_hcd * hcd ;
unsigned int virq ;
2009-04-07 06:01:15 +04:00
static u64 dummy_mask = DMA_BIT_MASK ( 32 ) ;
2007-01-16 07:11:47 +03:00
if ( usb_disabled ( ) ) {
result = - ENODEV ;
goto fail_start ;
}
2007-06-06 07:04:35 +04:00
result = ps3_open_hv_device ( dev ) ;
if ( result ) {
dev_dbg ( & dev - > core , " %s:%d: ps3_open_hv_device failed \n " ,
__func__ , __LINE__ ) ;
goto fail_open ;
}
result = ps3_dma_region_create ( dev - > d_region ) ;
if ( result ) {
dev_dbg ( & dev - > core , " %s:%d: ps3_dma_region_create failed: "
" (%d) \n " , __func__ , __LINE__ , result ) ;
BUG_ON ( " check region type " ) ;
goto fail_dma_region ;
}
2007-01-16 07:11:47 +03:00
result = ps3_mmio_region_create ( dev - > m_region ) ;
if ( result ) {
dev_dbg ( & dev - > core , " %s:%d: ps3_map_mmio_region failed \n " ,
__func__ , __LINE__ ) ;
result = - EPERM ;
2007-06-06 07:04:35 +04:00
goto fail_mmio_region ;
2007-01-16 07:11:47 +03:00
}
dev_dbg ( & dev - > core , " %s:%d: mmio mapped_addr %lxh \n " , __func__ ,
__LINE__ , dev - > m_region - > lpar_addr ) ;
2007-05-01 01:01:01 +04:00
result = ps3_io_irq_setup ( PS3_BINDING_CPU_ANY , dev - > interrupt_id , & virq ) ;
2007-01-16 07:11:47 +03:00
if ( result ) {
dev_dbg ( & dev - > core , " %s:%d: ps3_construct_io_irq(%d) failed. \n " ,
__func__ , __LINE__ , virq ) ;
result = - EPERM ;
goto fail_irq ;
}
dev - > core . dma_mask = & dummy_mask ; /* FIXME: for improper usb code */
2008-05-02 08:02:41 +04:00
hcd = usb_create_hcd ( & ps3_ehci_hc_driver , & dev - > core , dev_name ( & dev - > core ) ) ;
2007-01-16 07:11:47 +03:00
if ( ! hcd ) {
dev_dbg ( & dev - > core , " %s:%d: usb_create_hcd failed \n " , __func__ ,
__LINE__ ) ;
result = - ENOMEM ;
goto fail_create_hcd ;
}
hcd - > rsrc_start = dev - > m_region - > lpar_addr ;
hcd - > rsrc_len = dev - > m_region - > len ;
2007-06-06 07:04:35 +04:00
if ( ! request_mem_region ( hcd - > rsrc_start , hcd - > rsrc_len , hcd_name ) )
dev_dbg ( & dev - > core , " %s:%d: request_mem_region failed \n " ,
__func__ , __LINE__ ) ;
2007-01-16 07:11:47 +03:00
hcd - > regs = ioremap ( dev - > m_region - > lpar_addr , dev - > m_region - > len ) ;
if ( ! hcd - > regs ) {
dev_dbg ( & dev - > core , " %s:%d: ioremap failed \n " , __func__ ,
__LINE__ ) ;
result = - EPERM ;
goto fail_ioremap ;
}
dev_dbg ( & dev - > core , " %s:%d: hcd->rsrc_start %lxh \n " , __func__ , __LINE__ ,
( unsigned long ) hcd - > rsrc_start ) ;
dev_dbg ( & dev - > core , " %s:%d: hcd->rsrc_len %lxh \n " , __func__ , __LINE__ ,
( unsigned long ) hcd - > rsrc_len ) ;
dev_dbg ( & dev - > core , " %s:%d: hcd->regs %lxh \n " , __func__ , __LINE__ ,
( unsigned long ) hcd - > regs ) ;
dev_dbg ( & dev - > core , " %s:%d: virq %lu \n " , __func__ , __LINE__ ,
( unsigned long ) virq ) ;
2009-06-10 08:38:54 +04:00
ps3_system_bus_set_drvdata ( dev , hcd ) ;
2007-01-16 07:11:47 +03:00
2011-09-07 12:10:52 +04:00
result = usb_add_hcd ( hcd , virq , 0 ) ;
2007-01-16 07:11:47 +03:00
if ( result ) {
dev_dbg ( & dev - > core , " %s:%d: usb_add_hcd failed (%d) \n " ,
__func__ , __LINE__ , result ) ;
goto fail_add_hcd ;
}
return result ;
fail_add_hcd :
iounmap ( hcd - > regs ) ;
fail_ioremap :
2007-06-06 07:04:35 +04:00
release_mem_region ( hcd - > rsrc_start , hcd - > rsrc_len ) ;
2007-01-16 07:11:47 +03:00
usb_put_hcd ( hcd ) ;
fail_create_hcd :
2007-05-01 01:01:01 +04:00
ps3_io_irq_destroy ( virq ) ;
2007-01-16 07:11:47 +03:00
fail_irq :
ps3_free_mmio_region ( dev - > m_region ) ;
2007-06-06 07:04:35 +04:00
fail_mmio_region :
ps3_dma_region_free ( dev - > d_region ) ;
fail_dma_region :
ps3_close_hv_device ( dev ) ;
fail_open :
2007-01-16 07:11:47 +03:00
fail_start :
return result ;
}
2007-06-06 07:04:35 +04:00
static int ps3_ehci_remove ( struct ps3_system_bus_device * dev )
2007-01-16 07:11:47 +03:00
{
2007-06-06 07:04:35 +04:00
unsigned int tmp ;
2009-06-10 08:38:54 +04:00
struct usb_hcd * hcd = ps3_system_bus_get_drvdata ( dev ) ;
2007-01-16 07:11:47 +03:00
2007-06-06 07:04:35 +04:00
BUG_ON ( ! hcd ) ;
dev_dbg ( & dev - > core , " %s:%d: regs %p \n " , __func__ , __LINE__ , hcd - > regs ) ;
dev_dbg ( & dev - > core , " %s:%d: irq %u \n " , __func__ , __LINE__ , hcd - > irq ) ;
tmp = hcd - > irq ;
usb_remove_hcd ( hcd ) ;
2009-06-10 08:38:54 +04:00
ps3_system_bus_set_drvdata ( dev , NULL ) ;
2007-01-16 07:11:47 +03:00
2007-06-06 07:04:35 +04:00
BUG_ON ( ! hcd - > regs ) ;
iounmap ( hcd - > regs ) ;
release_mem_region ( hcd - > rsrc_start , hcd - > rsrc_len ) ;
usb_put_hcd ( hcd ) ;
ps3_io_irq_destroy ( tmp ) ;
ps3_free_mmio_region ( dev - > m_region ) ;
ps3_dma_region_free ( dev - > d_region ) ;
ps3_close_hv_device ( dev ) ;
2007-01-16 07:11:47 +03:00
return 0 ;
}
2009-06-10 08:38:59 +04:00
static int __init ps3_ehci_driver_register ( struct ps3_system_bus_driver * drv )
2007-06-06 07:04:35 +04:00
{
return firmware_has_feature ( FW_FEATURE_PS3_LV1 )
? ps3_system_bus_driver_register ( drv )
: 0 ;
}
static void ps3_ehci_driver_unregister ( struct ps3_system_bus_driver * drv )
{
if ( firmware_has_feature ( FW_FEATURE_PS3_LV1 ) )
ps3_system_bus_driver_unregister ( drv ) ;
}
MODULE_ALIAS ( PS3_MODULE_ALIAS_EHCI ) ;
2007-01-16 07:11:47 +03:00
2007-06-06 07:04:35 +04:00
static struct ps3_system_bus_driver ps3_ehci_driver = {
. core . name = " ps3-ehci-driver " ,
. core . owner = THIS_MODULE ,
2007-01-16 07:11:47 +03:00
. match_id = PS3_MATCH_ID_EHCI ,
2007-06-06 07:04:35 +04:00
. probe = ps3_ehci_probe ,
. remove = ps3_ehci_remove ,
. shutdown = ps3_ehci_remove ,
2007-01-16 07:11:47 +03:00
} ;