2011-05-09 20:56:46 +04:00
/*
* Broadcom specific AMBA
* PCI Host
*
* Licensed under the GNU / GPL . See COPYING for details .
*/
# include "bcma_private.h"
2011-05-27 03:24:57 +04:00
# include <linux/slab.h>
2011-05-09 20:56:46 +04:00
# include <linux/bcma/bcma.h>
# include <linux/pci.h>
2011-07-02 00:06:37 +04:00
# include <linux/module.h>
2011-05-09 20:56:46 +04:00
static void bcma_host_pci_switch_core ( struct bcma_device * core )
{
pci_write_config_dword ( core - > bus - > host_pci , BCMA_PCI_BAR0_WIN ,
core - > addr ) ;
pci_write_config_dword ( core - > bus - > host_pci , BCMA_PCI_BAR0_WIN2 ,
core - > wrap ) ;
core - > bus - > mapped_core = core ;
2012-07-06 00:07:32 +04:00
bcma_debug ( core - > bus , " Switched to core: 0x%X \n " , core - > id . id ) ;
2011-05-09 20:56:46 +04:00
}
2011-12-05 22:13:39 +04:00
/* Provides access to the requested core. Returns base offset that has to be
* used . It makes use of fixed windows when possible . */
static u16 bcma_host_pci_provide_access_to_core ( struct bcma_device * core )
2011-05-09 20:56:46 +04:00
{
2011-12-05 22:13:39 +04:00
switch ( core - > id . id ) {
case BCMA_CORE_CHIPCOMMON :
return 3 * BCMA_CORE_SIZE ;
case BCMA_CORE_PCIE :
return 2 * BCMA_CORE_SIZE ;
}
2011-05-09 20:56:46 +04:00
if ( core - > bus - > mapped_core ! = core )
bcma_host_pci_switch_core ( core ) ;
2011-12-05 22:13:39 +04:00
return 0 ;
}
static u8 bcma_host_pci_read8 ( struct bcma_device * core , u16 offset )
{
offset + = bcma_host_pci_provide_access_to_core ( core ) ;
2011-05-09 20:56:46 +04:00
return ioread8 ( core - > bus - > mmio + offset ) ;
}
static u16 bcma_host_pci_read16 ( struct bcma_device * core , u16 offset )
{
2011-12-05 22:13:39 +04:00
offset + = bcma_host_pci_provide_access_to_core ( core ) ;
2011-05-09 20:56:46 +04:00
return ioread16 ( core - > bus - > mmio + offset ) ;
}
static u32 bcma_host_pci_read32 ( struct bcma_device * core , u16 offset )
{
2011-12-05 22:13:39 +04:00
offset + = bcma_host_pci_provide_access_to_core ( core ) ;
2011-05-09 20:56:46 +04:00
return ioread32 ( core - > bus - > mmio + offset ) ;
}
static void bcma_host_pci_write8 ( struct bcma_device * core , u16 offset ,
u8 value )
{
2011-12-05 22:13:39 +04:00
offset + = bcma_host_pci_provide_access_to_core ( core ) ;
2011-05-09 20:56:46 +04:00
iowrite8 ( value , core - > bus - > mmio + offset ) ;
}
static void bcma_host_pci_write16 ( struct bcma_device * core , u16 offset ,
u16 value )
{
2011-12-05 22:13:39 +04:00
offset + = bcma_host_pci_provide_access_to_core ( core ) ;
2011-05-09 20:56:46 +04:00
iowrite16 ( value , core - > bus - > mmio + offset ) ;
}
static void bcma_host_pci_write32 ( struct bcma_device * core , u16 offset ,
u32 value )
{
2011-12-05 22:13:39 +04:00
offset + = bcma_host_pci_provide_access_to_core ( core ) ;
2011-05-09 20:56:46 +04:00
iowrite32 ( value , core - > bus - > mmio + offset ) ;
}
2011-05-20 05:27:06 +04:00
# ifdef CONFIG_BCMA_BLOCKIO
2012-08-05 18:54:41 +04:00
static void bcma_host_pci_block_read ( struct bcma_device * core , void * buffer ,
size_t count , u16 offset , u8 reg_width )
2011-05-20 05:27:06 +04:00
{
void __iomem * addr = core - > bus - > mmio + offset ;
if ( core - > bus - > mapped_core ! = core )
bcma_host_pci_switch_core ( core ) ;
switch ( reg_width ) {
case sizeof ( u8 ) :
ioread8_rep ( addr , buffer , count ) ;
break ;
case sizeof ( u16 ) :
WARN_ON ( count & 1 ) ;
ioread16_rep ( addr , buffer , count > > 1 ) ;
break ;
case sizeof ( u32 ) :
WARN_ON ( count & 3 ) ;
ioread32_rep ( addr , buffer , count > > 2 ) ;
break ;
default :
WARN_ON ( 1 ) ;
}
}
2012-08-05 18:54:41 +04:00
static void bcma_host_pci_block_write ( struct bcma_device * core ,
const void * buffer , size_t count ,
u16 offset , u8 reg_width )
2011-05-20 05:27:06 +04:00
{
void __iomem * addr = core - > bus - > mmio + offset ;
if ( core - > bus - > mapped_core ! = core )
bcma_host_pci_switch_core ( core ) ;
switch ( reg_width ) {
case sizeof ( u8 ) :
iowrite8_rep ( addr , buffer , count ) ;
break ;
case sizeof ( u16 ) :
WARN_ON ( count & 1 ) ;
iowrite16_rep ( addr , buffer , count > > 1 ) ;
break ;
case sizeof ( u32 ) :
WARN_ON ( count & 3 ) ;
iowrite32_rep ( addr , buffer , count > > 2 ) ;
break ;
default :
WARN_ON ( 1 ) ;
}
}
# endif
2011-05-09 20:56:46 +04:00
static u32 bcma_host_pci_aread32 ( struct bcma_device * core , u16 offset )
{
if ( core - > bus - > mapped_core ! = core )
bcma_host_pci_switch_core ( core ) ;
return ioread32 ( core - > bus - > mmio + ( 1 * BCMA_CORE_SIZE ) + offset ) ;
}
static void bcma_host_pci_awrite32 ( struct bcma_device * core , u16 offset ,
u32 value )
{
if ( core - > bus - > mapped_core ! = core )
bcma_host_pci_switch_core ( core ) ;
iowrite32 ( value , core - > bus - > mmio + ( 1 * BCMA_CORE_SIZE ) + offset ) ;
}
2012-08-05 18:54:41 +04:00
static const struct bcma_host_ops bcma_host_pci_ops = {
2011-05-09 20:56:46 +04:00
. read8 = bcma_host_pci_read8 ,
. read16 = bcma_host_pci_read16 ,
. read32 = bcma_host_pci_read32 ,
. write8 = bcma_host_pci_write8 ,
. write16 = bcma_host_pci_write16 ,
. write32 = bcma_host_pci_write32 ,
2011-05-20 05:27:06 +04:00
# ifdef CONFIG_BCMA_BLOCKIO
. block_read = bcma_host_pci_block_read ,
. block_write = bcma_host_pci_block_write ,
# endif
2011-05-09 20:56:46 +04:00
. aread32 = bcma_host_pci_aread32 ,
. awrite32 = bcma_host_pci_awrite32 ,
} ;
2012-12-22 03:12:59 +04:00
static int bcma_host_pci_probe ( struct pci_dev * dev ,
const struct pci_device_id * id )
2011-05-09 20:56:46 +04:00
{
struct bcma_bus * bus ;
int err = - ENOMEM ;
const char * name ;
u32 val ;
/* Alloc */
bus = kzalloc ( sizeof ( * bus ) , GFP_KERNEL ) ;
if ( ! bus )
goto out ;
/* Basic PCI configuration */
err = pci_enable_device ( dev ) ;
if ( err )
goto err_kfree_bus ;
name = dev_name ( & dev - > dev ) ;
if ( dev - > driver & & dev - > driver - > name )
name = dev - > driver - > name ;
err = pci_request_regions ( dev , name ) ;
if ( err )
goto err_pci_disable ;
pci_set_master ( dev ) ;
/* Disable the RETRY_TIMEOUT register (0x41) to keep
* PCI Tx retries from interfering with C3 CPU state */
pci_read_config_dword ( dev , 0x40 , & val ) ;
if ( ( val & 0x0000ff00 ) ! = 0 )
pci_write_config_dword ( dev , 0x40 , val & 0xffff00ff ) ;
/* SSB needed additional powering up, do we have any AMBA PCI cards? */
if ( ! pci_is_pcie ( dev ) )
2012-07-06 00:07:32 +04:00
bcma_err ( bus , " PCI card detected, report problems. \n " ) ;
2011-05-09 20:56:46 +04:00
/* Map MMIO */
err = - ENOMEM ;
bus - > mmio = pci_iomap ( dev , 0 , ~ 0UL ) ;
if ( ! bus - > mmio )
goto err_pci_release_regions ;
/* Host specific */
bus - > host_pci = dev ;
bus - > hosttype = BCMA_HOSTTYPE_PCI ;
bus - > ops = & bcma_host_pci_ops ;
2012-04-29 04:04:08 +04:00
bus - > boardinfo . vendor = bus - > host_pci - > subsystem_vendor ;
bus - > boardinfo . type = bus - > host_pci - > subsystem_device ;
2011-05-09 20:56:46 +04:00
/* Register */
err = bcma_bus_register ( bus ) ;
if ( err )
goto err_pci_unmap_mmio ;
pci_set_drvdata ( dev , bus ) ;
out :
return err ;
err_pci_unmap_mmio :
pci_iounmap ( dev , bus - > mmio ) ;
err_pci_release_regions :
pci_release_regions ( dev ) ;
err_pci_disable :
pci_disable_device ( dev ) ;
err_kfree_bus :
kfree ( bus ) ;
return err ;
}
2012-12-22 03:12:59 +04:00
static void bcma_host_pci_remove ( struct pci_dev * dev )
2011-05-09 20:56:46 +04:00
{
struct bcma_bus * bus = pci_get_drvdata ( dev ) ;
bcma_bus_unregister ( bus ) ;
pci_iounmap ( dev , bus - > mmio ) ;
pci_release_regions ( dev ) ;
pci_disable_device ( dev ) ;
kfree ( bus ) ;
pci_set_drvdata ( dev , NULL ) ;
}
2012-10-16 18:59:02 +04:00
# ifdef CONFIG_PM_SLEEP
2012-01-14 02:58:39 +04:00
static int bcma_host_pci_suspend ( struct device * dev )
2011-12-10 01:16:07 +04:00
{
2012-01-14 02:58:39 +04:00
struct pci_dev * pdev = to_pci_dev ( dev ) ;
struct bcma_bus * bus = pci_get_drvdata ( pdev ) ;
2011-12-10 01:16:07 +04:00
2012-01-14 02:58:38 +04:00
bus - > mapped_core = NULL ;
2012-01-14 02:58:39 +04:00
2012-01-14 02:58:40 +04:00
return bcma_bus_suspend ( bus ) ;
2011-12-10 01:16:07 +04:00
}
2012-01-14 02:58:39 +04:00
static int bcma_host_pci_resume ( struct device * dev )
2011-12-10 01:16:07 +04:00
{
2012-01-14 02:58:39 +04:00
struct pci_dev * pdev = to_pci_dev ( dev ) ;
struct bcma_bus * bus = pci_get_drvdata ( pdev ) ;
2011-12-10 01:16:07 +04:00
2012-01-14 02:58:39 +04:00
return bcma_bus_resume ( bus ) ;
}
2011-12-10 01:16:07 +04:00
2012-01-14 02:58:39 +04:00
static SIMPLE_DEV_PM_OPS ( bcma_pm_ops , bcma_host_pci_suspend ,
bcma_host_pci_resume ) ;
# define BCMA_PM_OPS (&bcma_pm_ops)
2011-12-10 01:16:07 +04:00
2012-10-16 18:59:02 +04:00
# else /* CONFIG_PM_SLEEP */
2012-01-14 02:58:39 +04:00
# define BCMA_PM_OPS NULL
2012-10-16 18:59:02 +04:00
# endif /* CONFIG_PM_SLEEP */
2011-12-10 01:16:07 +04:00
2011-05-09 20:56:46 +04:00
static DEFINE_PCI_DEVICE_TABLE ( bcma_pci_bridge_tbl ) = {
2011-05-14 12:31:46 +04:00
{ PCI_DEVICE ( PCI_VENDOR_ID_BROADCOM , 0x0576 ) } ,
2012-06-30 03:44:40 +04:00
{ PCI_DEVICE ( PCI_VENDOR_ID_BROADCOM , 43224 ) } ,
2011-05-09 20:56:46 +04:00
{ PCI_DEVICE ( PCI_VENDOR_ID_BROADCOM , 0x4331 ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_BROADCOM , 0x4353 ) } ,
2011-06-17 15:15:23 +04:00
{ PCI_DEVICE ( PCI_VENDOR_ID_BROADCOM , 0x4357 ) } ,
2012-09-21 10:38:38 +04:00
{ PCI_DEVICE ( PCI_VENDOR_ID_BROADCOM , 0x4358 ) } ,
2012-07-23 20:20:12 +04:00
{ PCI_DEVICE ( PCI_VENDOR_ID_BROADCOM , 0x4359 ) } ,
2013-06-26 12:02:11 +04:00
{ PCI_DEVICE ( PCI_VENDOR_ID_BROADCOM , 0x4365 ) } ,
2011-05-09 20:56:46 +04:00
{ PCI_DEVICE ( PCI_VENDOR_ID_BROADCOM , 0x4727 ) } ,
{ 0 , } ,
} ;
MODULE_DEVICE_TABLE ( pci , bcma_pci_bridge_tbl ) ;
static struct pci_driver bcma_pci_bridge_driver = {
. name = " bcma-pci-bridge " ,
. id_table = bcma_pci_bridge_tbl ,
. probe = bcma_host_pci_probe ,
2012-12-22 03:12:59 +04:00
. remove = bcma_host_pci_remove ,
2012-01-14 02:58:39 +04:00
. driver . pm = BCMA_PM_OPS ,
2011-05-09 20:56:46 +04:00
} ;
int __init bcma_host_pci_init ( void )
{
return pci_register_driver ( & bcma_pci_bridge_driver ) ;
}
void __exit bcma_host_pci_exit ( void )
{
pci_unregister_driver ( & bcma_pci_bridge_driver ) ;
}