2011-05-09 20:56:46 +04:00
/*
* Broadcom specific AMBA
* PCI Core
*
* Copyright 2005 , Broadcom Corporation
* Copyright 2006 , 2007 , Michael Buesch < mb @ bu3sch . de >
*
* Licensed under the GNU / GPL . See COPYING for details .
*/
# include "bcma_private.h"
# include <linux/bcma/bcma.h>
/**************************************************
* R / W ops .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static u32 bcma_pcie_read ( struct bcma_drv_pci * pc , u32 address )
{
pcicore_write32 ( pc , 0x130 , address ) ;
pcicore_read32 ( pc , 0x130 ) ;
return pcicore_read32 ( pc , 0x134 ) ;
}
#if 0
static void bcma_pcie_write ( struct bcma_drv_pci * pc , u32 address , u32 data )
{
pcicore_write32 ( pc , 0x130 , address ) ;
pcicore_read32 ( pc , 0x130 ) ;
pcicore_write32 ( pc , 0x134 , data ) ;
}
# endif
static void bcma_pcie_mdio_set_phy ( struct bcma_drv_pci * pc , u8 phy )
{
const u16 mdio_control = 0x128 ;
const u16 mdio_data = 0x12C ;
u32 v ;
int i ;
v = ( 1 < < 30 ) ; /* Start of Transaction */
v | = ( 1 < < 28 ) ; /* Write Transaction */
v | = ( 1 < < 17 ) ; /* Turnaround */
v | = ( 0x1F < < 18 ) ;
v | = ( phy < < 4 ) ;
pcicore_write32 ( pc , mdio_data , v ) ;
udelay ( 10 ) ;
for ( i = 0 ; i < 200 ; i + + ) {
v = pcicore_read32 ( pc , mdio_control ) ;
if ( v & 0x100 /* Trans complete */ )
break ;
msleep ( 1 ) ;
}
}
static u16 bcma_pcie_mdio_read ( struct bcma_drv_pci * pc , u8 device , u8 address )
{
const u16 mdio_control = 0x128 ;
const u16 mdio_data = 0x12C ;
int max_retries = 10 ;
u16 ret = 0 ;
u32 v ;
int i ;
v = 0x80 ; /* Enable Preamble Sequence */
v | = 0x2 ; /* MDIO Clock Divisor */
pcicore_write32 ( pc , mdio_control , v ) ;
if ( pc - > core - > id . rev > = 10 ) {
max_retries = 200 ;
bcma_pcie_mdio_set_phy ( pc , device ) ;
}
v = ( 1 < < 30 ) ; /* Start of Transaction */
v | = ( 1 < < 29 ) ; /* Read Transaction */
v | = ( 1 < < 17 ) ; /* Turnaround */
if ( pc - > core - > id . rev < 10 )
v | = ( u32 ) device < < 22 ;
v | = ( u32 ) address < < 18 ;
pcicore_write32 ( pc , mdio_data , v ) ;
/* 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 + + ) {
2011-05-09 20:56:46 +04:00
v = pcicore_read32 ( pc , mdio_control ) ;
if ( v & 0x100 /* Trans complete */ ) {
udelay ( 10 ) ;
ret = pcicore_read32 ( pc , mdio_data ) ;
break ;
}
msleep ( 1 ) ;
}
pcicore_write32 ( pc , mdio_control , 0 ) ;
return ret ;
}
static void bcma_pcie_mdio_write ( struct bcma_drv_pci * pc , u8 device ,
u8 address , u16 data )
{
const u16 mdio_control = 0x128 ;
const u16 mdio_data = 0x12C ;
int max_retries = 10 ;
u32 v ;
int i ;
v = 0x80 ; /* Enable Preamble Sequence */
v | = 0x2 ; /* MDIO Clock Divisor */
pcicore_write32 ( pc , mdio_control , v ) ;
if ( pc - > core - > id . rev > = 10 ) {
max_retries = 200 ;
bcma_pcie_mdio_set_phy ( pc , device ) ;
}
v = ( 1 < < 30 ) ; /* Start of Transaction */
v | = ( 1 < < 28 ) ; /* Write Transaction */
v | = ( 1 < < 17 ) ; /* Turnaround */
if ( pc - > core - > id . rev < 10 )
v | = ( u32 ) device < < 22 ;
v | = ( u32 ) address < < 18 ;
v | = data ;
pcicore_write32 ( pc , mdio_data , v ) ;
/* Wait for the device to complete the transaction */
udelay ( 10 ) ;
for ( i = 0 ; i < max_retries ; i + + ) {
v = pcicore_read32 ( pc , mdio_control ) ;
if ( v & 0x100 /* Trans complete */ )
break ;
msleep ( 1 ) ;
}
pcicore_write32 ( pc , mdio_control , 0 ) ;
}
/**************************************************
* Workarounds .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static u8 bcma_pcicore_polarity_workaround ( struct bcma_drv_pci * pc )
{
return ( bcma_pcie_read ( pc , 0x204 ) & 0x10 ) ? 0xC0 : 0x80 ;
}
static void bcma_pcicore_serdes_workaround ( struct bcma_drv_pci * pc )
{
const u8 serdes_pll_device = 0x1D ;
const u8 serdes_rx_device = 0x1F ;
u16 tmp ;
bcma_pcie_mdio_write ( pc , serdes_rx_device , 1 /* Control */ ,
bcma_pcicore_polarity_workaround ( pc ) ) ;
tmp = bcma_pcie_mdio_read ( pc , serdes_pll_device , 1 /* Control */ ) ;
if ( tmp & 0x4000 )
bcma_pcie_mdio_write ( pc , serdes_pll_device , 1 , tmp & ~ 0x4000 ) ;
}
/**************************************************
* Init .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void bcma_core_pci_init ( struct bcma_drv_pci * pc )
{
bcma_pcicore_serdes_workaround ( pc ) ;
}