2011-05-09 20:56:46 +04:00
/*
* Broadcom specific AMBA
* PCI Core
*
* Copyright 2005 , 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
}
#if 0
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
}
# endif
static void bcma_pcie_mdio_set_phy ( struct bcma_drv_pci * pc , u8 phy )
{
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 ;
msleep ( 1 ) ;
}
}
static u16 bcma_pcie_mdio_read ( struct bcma_drv_pci * pc , u8 device , u8 address )
{
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
}
2012-01-31 03:03:32 +04:00
v = BCMA_CORE_PCI_MDIODATA_START ;
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 ;
}
msleep ( 1 ) ;
}
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 ;
}
static void bcma_pcie_mdio_write ( struct bcma_drv_pci * pc , u8 device ,
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
}
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_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 ;
msleep ( 1 ) ;
}
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
}
/**************************************************
* 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
}
/**************************************************
* Init .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2011-07-05 21:48:26 +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 ) ;
}
2011-05-19 16:08:22 +04:00
2011-07-05 21:48:26 +04:00
static bool bcma_core_pci_is_in_hostmode ( struct bcma_drv_pci * pc )
{
struct bcma_bus * bus = pc - > core - > bus ;
u16 chipid_top ;
chipid_top = ( bus - > chipinfo . id & 0xFF00 ) ;
if ( chipid_top ! = 0x4700 & &
chipid_top ! = 0x5300 )
return false ;
2011-07-23 00:22:14 +04:00
# ifdef CONFIG_SSB_DRIVER_PCICORE
2011-07-23 13:10:12 +04:00
if ( bus - > sprom . boardflags_lo & SSB_BFL_NOPCI )
2011-07-05 21:48:26 +04:00
return false ;
2011-07-23 00:22:14 +04:00
# endif /* CONFIG_SSB_DRIVER_PCICORE */
2011-07-05 21:48:26 +04:00
#if 0
/* TODO: on BCMA we use address from EROM instead of magic formula */
u32 tmp ;
return ! mips_busprobe32 ( tmp , ( bus - > mmio +
( pc - > core - > core_index * BCMA_CORE_SIZE ) ) ) ;
# endif
return true ;
}
void bcma_core_pci_init ( struct bcma_drv_pci * pc )
{
2011-07-23 03:20:07 +04:00
if ( pc - > setup_done )
return ;
2011-07-05 21:48:26 +04:00
if ( bcma_core_pci_is_in_hostmode ( pc ) ) {
# ifdef CONFIG_BCMA_DRIVER_PCI_HOSTMODE
bcma_core_pci_hostmode_init ( pc ) ;
# else
pr_err ( " Driver compiled without support for hostmode PCI \n " ) ;
# endif /* CONFIG_BCMA_DRIVER_PCI_HOSTMODE */
} else {
bcma_core_pci_clientmode_init ( pc ) ;
}
2011-07-23 03:20:07 +04:00
pc - > setup_done = true ;
2011-07-05 21:48:26 +04:00
}
2011-05-19 16:08:22 +04:00
int bcma_core_pci_irq_ctl ( struct bcma_drv_pci * pc , struct bcma_device * core ,
bool enable )
{
struct pci_dev * pdev = pc - > core - > bus - > host_pci ;
u32 coremask , tmp ;
2011-07-23 03:20:08 +04:00
int err = 0 ;
if ( core - > bus - > hosttype ! = BCMA_HOSTTYPE_PCI ) {
/* This bcma device is not on a PCI host-bus. So the IRQs are
* not routed through the PCI core .
* So we must not enable routing through the PCI core . */
goto out ;
}
2011-05-19 16:08:22 +04:00
err = pci_read_config_dword ( pdev , BCMA_PCI_IRQMASK , & tmp ) ;
if ( err )
goto out ;
coremask = BIT ( core - > core_index ) < < 8 ;
if ( enable )
tmp | = coremask ;
else
tmp & = ~ coremask ;
err = pci_write_config_dword ( pdev , BCMA_PCI_IRQMASK , tmp ) ;
out :
return err ;
}
2011-06-18 03:01:59 +04:00
EXPORT_SYMBOL_GPL ( bcma_core_pci_irq_ctl ) ;