2011-05-09 20:56:46 +04:00
/*
* Broadcom specific AMBA
* PCI Core
*
2012-01-31 03:03:35 +04:00
* Copyright 2005 , 2011 , Broadcom Corporation
2011-07-04 22:50:05 +04:00
* Copyright 2006 , 2007 , Michael Buesch < m @ bues . ch >
2012-01-31 03:03:32 +04:00
* Copyright 2011 , 2012 , Hauke Mehrtens < hauke @ hauke - m . de >
2011-05-09 20:56:46 +04:00
*
* Licensed under the GNU / GPL . See COPYING for details .
*/
# include "bcma_private.h"
2011-07-28 05:21:04 +04:00
# include <linux/export.h>
2011-05-09 20:56:46 +04:00
# include <linux/bcma/bcma.h>
/**************************************************
* R / W ops .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2012-01-31 03:03:33 +04:00
u32 bcma_pcie_read ( struct bcma_drv_pci * pc , u32 address )
2011-05-09 20:56:46 +04:00
{
2012-01-31 03:03:32 +04:00
pcicore_write32 ( pc , BCMA_CORE_PCI_PCIEIND_ADDR , address ) ;
pcicore_read32 ( pc , BCMA_CORE_PCI_PCIEIND_ADDR ) ;
return pcicore_read32 ( pc , BCMA_CORE_PCI_PCIEIND_DATA ) ;
2011-05-09 20:56:46 +04:00
}
static void bcma_pcie_write ( struct bcma_drv_pci * pc , u32 address , u32 data )
{
2012-01-31 03:03:32 +04:00
pcicore_write32 ( pc , BCMA_CORE_PCI_PCIEIND_ADDR , address ) ;
pcicore_read32 ( pc , BCMA_CORE_PCI_PCIEIND_ADDR ) ;
pcicore_write32 ( pc , BCMA_CORE_PCI_PCIEIND_DATA , data ) ;
2011-05-09 20:56:46 +04:00
}
2013-08-24 02:32:33 +04:00
static void bcma_pcie_mdio_set_phy ( struct bcma_drv_pci * pc , u16 phy )
2011-05-09 20:56:46 +04:00
{
u32 v ;
int i ;
2012-01-31 03:03:32 +04:00
v = BCMA_CORE_PCI_MDIODATA_START ;
v | = BCMA_CORE_PCI_MDIODATA_WRITE ;
v | = ( BCMA_CORE_PCI_MDIODATA_DEV_ADDR < <
BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF ) ;
v | = ( BCMA_CORE_PCI_MDIODATA_BLK_ADDR < <
BCMA_CORE_PCI_MDIODATA_REGADDR_SHF ) ;
v | = BCMA_CORE_PCI_MDIODATA_TA ;
2011-05-09 20:56:46 +04:00
v | = ( phy < < 4 ) ;
2012-01-31 03:03:32 +04:00
pcicore_write32 ( pc , BCMA_CORE_PCI_MDIO_DATA , v ) ;
2011-05-09 20:56:46 +04:00
udelay ( 10 ) ;
for ( i = 0 ; i < 200 ; i + + ) {
2012-01-31 03:03:32 +04:00
v = pcicore_read32 ( pc , BCMA_CORE_PCI_MDIO_CONTROL ) ;
if ( v & BCMA_CORE_PCI_MDIOCTL_ACCESS_DONE )
2011-05-09 20:56:46 +04:00
break ;
2012-09-25 12:17:22 +04:00
usleep_range ( 1000 , 2000 ) ;
2011-05-09 20:56:46 +04:00
}
}
2013-08-24 02:32:33 +04:00
static u16 bcma_pcie_mdio_read ( struct bcma_drv_pci * pc , u16 device , u8 address )
2011-05-09 20:56:46 +04:00
{
int max_retries = 10 ;
u16 ret = 0 ;
u32 v ;
int i ;
2012-01-31 03:03:32 +04:00
/* enable mdio access to SERDES */
v = BCMA_CORE_PCI_MDIOCTL_PREAM_EN ;
v | = BCMA_CORE_PCI_MDIOCTL_DIVISOR_VAL ;
pcicore_write32 ( pc , BCMA_CORE_PCI_MDIO_CONTROL , v ) ;
2011-05-09 20:56:46 +04:00
if ( pc - > core - > id . rev > = 10 ) {
max_retries = 200 ;
bcma_pcie_mdio_set_phy ( pc , device ) ;
2012-01-31 03:03:32 +04:00
v = ( BCMA_CORE_PCI_MDIODATA_DEV_ADDR < <
BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF ) ;
v | = ( address < < BCMA_CORE_PCI_MDIODATA_REGADDR_SHF ) ;
} else {
v = ( device < < BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF_OLD ) ;
v | = ( address < < BCMA_CORE_PCI_MDIODATA_REGADDR_SHF_OLD ) ;
2011-05-09 20:56:46 +04:00
}
2019-08-27 11:16:20 +03:00
v | = BCMA_CORE_PCI_MDIODATA_START ;
2012-01-31 03:03:32 +04:00
v | = BCMA_CORE_PCI_MDIODATA_READ ;
v | = BCMA_CORE_PCI_MDIODATA_TA ;
pcicore_write32 ( pc , BCMA_CORE_PCI_MDIO_DATA , v ) ;
2011-05-09 20:56:46 +04:00
/* Wait for the device to complete the transaction */
udelay ( 10 ) ;
2011-05-12 02:01:47 +04:00
for ( i = 0 ; i < max_retries ; i + + ) {
2012-01-31 03:03:32 +04:00
v = pcicore_read32 ( pc , BCMA_CORE_PCI_MDIO_CONTROL ) ;
if ( v & BCMA_CORE_PCI_MDIOCTL_ACCESS_DONE ) {
2011-05-09 20:56:46 +04:00
udelay ( 10 ) ;
2012-01-31 03:03:32 +04:00
ret = pcicore_read32 ( pc , BCMA_CORE_PCI_MDIO_DATA ) ;
2011-05-09 20:56:46 +04:00
break ;
}
2012-09-25 12:17:22 +04:00
usleep_range ( 1000 , 2000 ) ;
2011-05-09 20:56:46 +04:00
}
2012-01-31 03:03:32 +04:00
pcicore_write32 ( pc , BCMA_CORE_PCI_MDIO_CONTROL , 0 ) ;
2011-05-09 20:56:46 +04:00
return ret ;
}
2013-08-24 02:32:33 +04:00
static void bcma_pcie_mdio_write ( struct bcma_drv_pci * pc , u16 device ,
2011-05-09 20:56:46 +04:00
u8 address , u16 data )
{
int max_retries = 10 ;
u32 v ;
int i ;
2012-01-31 03:03:32 +04:00
/* enable mdio access to SERDES */
v = BCMA_CORE_PCI_MDIOCTL_PREAM_EN ;
v | = BCMA_CORE_PCI_MDIOCTL_DIVISOR_VAL ;
pcicore_write32 ( pc , BCMA_CORE_PCI_MDIO_CONTROL , v ) ;
2011-05-09 20:56:46 +04:00
if ( pc - > core - > id . rev > = 10 ) {
max_retries = 200 ;
bcma_pcie_mdio_set_phy ( pc , device ) ;
2012-01-31 03:03:32 +04:00
v = ( BCMA_CORE_PCI_MDIODATA_DEV_ADDR < <
BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF ) ;
v | = ( address < < BCMA_CORE_PCI_MDIODATA_REGADDR_SHF ) ;
} else {
v = ( device < < BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF_OLD ) ;
v | = ( address < < BCMA_CORE_PCI_MDIODATA_REGADDR_SHF_OLD ) ;
2011-05-09 20:56:46 +04:00
}
2019-08-27 11:16:20 +03:00
v | = BCMA_CORE_PCI_MDIODATA_START ;
2012-01-31 03:03:32 +04:00
v | = BCMA_CORE_PCI_MDIODATA_WRITE ;
v | = BCMA_CORE_PCI_MDIODATA_TA ;
2011-05-09 20:56:46 +04:00
v | = data ;
2012-01-31 03:03:32 +04:00
pcicore_write32 ( pc , BCMA_CORE_PCI_MDIO_DATA , v ) ;
2011-05-09 20:56:46 +04:00
/* Wait for the device to complete the transaction */
udelay ( 10 ) ;
for ( i = 0 ; i < max_retries ; i + + ) {
2012-01-31 03:03:32 +04:00
v = pcicore_read32 ( pc , BCMA_CORE_PCI_MDIO_CONTROL ) ;
if ( v & BCMA_CORE_PCI_MDIOCTL_ACCESS_DONE )
2011-05-09 20:56:46 +04:00
break ;
2012-09-25 12:17:22 +04:00
usleep_range ( 1000 , 2000 ) ;
2011-05-09 20:56:46 +04:00
}
2012-01-31 03:03:32 +04:00
pcicore_write32 ( pc , BCMA_CORE_PCI_MDIO_CONTROL , 0 ) ;
2011-05-09 20:56:46 +04:00
}
2013-08-24 02:32:33 +04:00
static u16 bcma_pcie_mdio_writeread ( struct bcma_drv_pci * pc , u16 device ,
u8 address , u16 data )
{
bcma_pcie_mdio_write ( pc , device , address , data ) ;
return bcma_pcie_mdio_read ( pc , device , address ) ;
}
2015-01-25 13:11:14 +03:00
/**************************************************
* Early init .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void bcma_core_pci_fixcfg ( struct bcma_drv_pci * pc )
{
struct bcma_device * core = pc - > core ;
u16 val16 , core_index ;
uint regoff ;
regoff = BCMA_CORE_PCI_SPROM ( BCMA_CORE_PCI_SPROM_PI_OFFSET ) ;
core_index = ( u16 ) core - > core_index ;
val16 = pcicore_read16 ( pc , regoff ) ;
if ( ( ( val16 & BCMA_CORE_PCI_SPROM_PI_MASK ) > > BCMA_CORE_PCI_SPROM_PI_SHIFT )
! = core_index ) {
val16 = ( core_index < < BCMA_CORE_PCI_SPROM_PI_SHIFT ) |
( val16 & ~ BCMA_CORE_PCI_SPROM_PI_MASK ) ;
pcicore_write16 ( pc , regoff , val16 ) ;
}
}
/*
* Apply some early fixes required before accessing SPROM .
* See also si_pci_fixcfg .
*/
void bcma_core_pci_early_init ( struct bcma_drv_pci * pc )
{
if ( pc - > early_setup_done )
return ;
pc - > hostmode = bcma_core_pci_is_in_hostmode ( pc ) ;
if ( pc - > hostmode )
goto out ;
bcma_core_pci_fixcfg ( pc ) ;
out :
pc - > early_setup_done = true ;
}
2011-05-09 20:56:46 +04:00
/**************************************************
* Workarounds .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static u8 bcma_pcicore_polarity_workaround ( struct bcma_drv_pci * pc )
{
2012-01-31 03:03:32 +04:00
u32 tmp ;
tmp = bcma_pcie_read ( pc , BCMA_CORE_PCI_PLP_STATUSREG ) ;
if ( tmp & BCMA_CORE_PCI_PLP_POLARITYINV_STAT )
return BCMA_CORE_PCI_SERDES_RX_CTRL_FORCE |
BCMA_CORE_PCI_SERDES_RX_CTRL_POLARITY ;
else
return BCMA_CORE_PCI_SERDES_RX_CTRL_FORCE ;
2011-05-09 20:56:46 +04:00
}
static void bcma_pcicore_serdes_workaround ( struct bcma_drv_pci * pc )
{
u16 tmp ;
2012-01-31 03:03:32 +04:00
bcma_pcie_mdio_write ( pc , BCMA_CORE_PCI_MDIODATA_DEV_RX ,
BCMA_CORE_PCI_SERDES_RX_CTRL ,
bcma_pcicore_polarity_workaround ( pc ) ) ;
tmp = bcma_pcie_mdio_read ( pc , BCMA_CORE_PCI_MDIODATA_DEV_PLL ,
BCMA_CORE_PCI_SERDES_PLL_CTRL ) ;
if ( tmp & BCMA_CORE_PCI_PLL_CTRL_FREQDET_EN )
bcma_pcie_mdio_write ( pc , BCMA_CORE_PCI_MDIODATA_DEV_PLL ,
BCMA_CORE_PCI_SERDES_PLL_CTRL ,
tmp & ~ BCMA_CORE_PCI_PLL_CTRL_FREQDET_EN ) ;
2011-05-09 20:56:46 +04:00
}
2012-04-29 04:18:52 +04:00
/* Fix MISC config to allow coming out of L2/L3-Ready state w/o PRST */
/* Needs to happen when coming out of 'standby'/'hibernate' */
static void bcma_core_pci_config_fixup ( struct bcma_drv_pci * pc )
{
u16 val16 ;
uint regoff ;
regoff = BCMA_CORE_PCI_SPROM ( BCMA_CORE_PCI_SPROM_MISC_CONFIG ) ;
val16 = pcicore_read16 ( pc , regoff ) ;
if ( ! ( val16 & BCMA_CORE_PCI_SPROM_L23READY_EXIT_NOPERST ) ) {
val16 | = BCMA_CORE_PCI_SPROM_L23READY_EXIT_NOPERST ;
pcicore_write16 ( pc , regoff , val16 ) ;
}
}
2011-05-09 20:56:46 +04:00
/**************************************************
* Init .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2012-12-22 03:12:59 +04:00
static void bcma_core_pci_clientmode_init ( struct bcma_drv_pci * pc )
2011-05-09 20:56:46 +04:00
{
bcma_pcicore_serdes_workaround ( pc ) ;
2012-04-29 04:18:52 +04:00
bcma_core_pci_config_fixup ( pc ) ;
2011-05-09 20:56:46 +04:00
}
2011-05-19 16:08:22 +04:00
2012-12-22 03:12:59 +04:00
void bcma_core_pci_init ( struct bcma_drv_pci * pc )
2011-07-05 21:48:26 +04:00
{
2011-07-23 03:20:07 +04:00
if ( pc - > setup_done )
return ;
2015-01-25 13:11:14 +03:00
bcma_core_pci_early_init ( pc ) ;
2012-01-31 03:03:35 +04:00
if ( pc - > hostmode )
2011-07-05 21:48:26 +04:00
bcma_core_pci_hostmode_init ( pc ) ;
2015-01-25 13:11:14 +03:00
else
2012-01-31 03:03:35 +04:00
bcma_core_pci_clientmode_init ( pc ) ;
2011-07-05 21:48:26 +04:00
}
2013-09-25 14:11:02 +04:00
void bcma_core_pci_power_save ( struct bcma_bus * bus , bool up )
{
struct bcma_drv_pci * pc ;
u16 data ;
if ( bus - > hosttype ! = BCMA_HOSTTYPE_PCI )
return ;
pc = & bus - > drv_pci [ 0 ] ;
if ( pc - > core - > id . rev > = 15 & & pc - > core - > id . rev < = 20 ) {
data = up ? 0x74 : 0x7C ;
bcma_pcie_mdio_writeread ( pc , BCMA_CORE_PCI_MDIO_BLK1 ,
BCMA_CORE_PCI_MDIO_BLK1_MGMT1 , 0x7F64 ) ;
bcma_pcie_mdio_writeread ( pc , BCMA_CORE_PCI_MDIO_BLK1 ,
BCMA_CORE_PCI_MDIO_BLK1_MGMT3 , data ) ;
} else if ( pc - > core - > id . rev > = 21 & & pc - > core - > id . rev < = 22 ) {
data = up ? 0x75 : 0x7D ;
bcma_pcie_mdio_writeread ( pc , BCMA_CORE_PCI_MDIO_BLK1 ,
BCMA_CORE_PCI_MDIO_BLK1_MGMT1 , 0x7E65 ) ;
bcma_pcie_mdio_writeread ( pc , BCMA_CORE_PCI_MDIO_BLK1 ,
BCMA_CORE_PCI_MDIO_BLK1_MGMT3 , data ) ;
}
}
EXPORT_SYMBOL_GPL ( bcma_core_pci_power_save ) ;
2013-08-24 02:32:32 +04:00
static void bcma_core_pci_extend_L1timer ( struct bcma_drv_pci * pc , bool extend )
2012-04-29 04:18:50 +04:00
{
u32 w ;
w = bcma_pcie_read ( pc , BCMA_CORE_PCI_DLLP_PMTHRESHREG ) ;
if ( extend )
w | = BCMA_CORE_PCI_ASPMTIMER_EXTEND ;
else
w & = ~ BCMA_CORE_PCI_ASPMTIMER_EXTEND ;
bcma_pcie_write ( pc , BCMA_CORE_PCI_DLLP_PMTHRESHREG , w ) ;
bcma_pcie_read ( pc , BCMA_CORE_PCI_DLLP_PMTHRESHREG ) ;
}
2013-08-24 02:32:30 +04:00
2015-02-08 19:11:47 +03:00
void bcma_core_pci_up ( struct bcma_drv_pci * pc )
2013-08-24 02:32:30 +04:00
{
bcma_core_pci_extend_L1timer ( pc , true ) ;
}
2015-02-08 19:11:47 +03:00
void bcma_core_pci_down ( struct bcma_drv_pci * pc )
2013-08-24 02:32:30 +04:00
{
bcma_core_pci_extend_L1timer ( pc , false ) ;
}