2007-09-18 23:12:50 +04:00
/*
* Sonics Silicon Backplane
* Broadcom PCI - core driver
*
* Copyright 2005 , Broadcom Corporation
2011-07-04 22:50:05 +04:00
* Copyright 2006 , 2007 , Michael Buesch < m @ bues . ch >
2007-09-18 23:12:50 +04:00
*
* Licensed under the GNU / GPL . See COPYING for details .
*/
# include <linux/ssb/ssb.h>
# include <linux/pci.h>
2011-07-28 06:07:02 +04:00
# include <linux/export.h>
2007-09-18 23:12:50 +04:00
# include <linux/delay.h>
2008-02-19 19:46:48 +03:00
# include <linux/ssb/ssb_embedded.h>
2007-09-18 23:12:50 +04:00
# include "ssb_private.h"
2011-04-01 15:26:52 +04:00
static u32 ssb_pcie_read ( struct ssb_pcicore * pc , u32 address ) ;
static void ssb_pcie_write ( struct ssb_pcicore * pc , u32 address , u32 data ) ;
static u16 ssb_pcie_mdio_read ( struct ssb_pcicore * pc , u8 device , u8 address ) ;
static void ssb_pcie_mdio_write ( struct ssb_pcicore * pc , u8 device ,
u8 address , u16 data ) ;
2007-09-18 23:12:50 +04:00
static inline
u32 pcicore_read32 ( struct ssb_pcicore * pc , u16 offset )
{
return ssb_read32 ( pc - > dev , offset ) ;
}
static inline
void pcicore_write32 ( struct ssb_pcicore * pc , u16 offset , u32 value )
{
ssb_write32 ( pc - > dev , offset , value ) ;
}
2008-02-19 19:46:48 +03:00
static inline
u16 pcicore_read16 ( struct ssb_pcicore * pc , u16 offset )
{
return ssb_read16 ( pc - > dev , offset ) ;
}
static inline
void pcicore_write16 ( struct ssb_pcicore * pc , u16 offset , u16 value )
{
ssb_write16 ( pc - > dev , offset , value ) ;
}
2007-09-18 23:12:50 +04:00
/**************************************************
* Code for hostmode operation .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# ifdef CONFIG_SSB_PCICORE_HOSTMODE
# include <asm/paccess.h>
/* Probe a 32bit value on the bus and catch bus exceptions.
* Returns nonzero on a bus exception .
* This is MIPS specific */
# define mips_busprobe32(val, addr) get_dbe((val), ((u32 *)(addr)))
/* Assume one-hot slot wiring */
# define SSB_PCI_SLOT_MAX 16
/* Global lock is OK, as we won't have more than one extpci anyway. */
static DEFINE_SPINLOCK ( cfgspace_lock ) ;
/* Core to access the external PCI config space. Can only have one. */
static struct ssb_pcicore * extpci_core ;
static u32 get_cfgspace_addr ( struct ssb_pcicore * pc ,
unsigned int bus , unsigned int dev ,
unsigned int func , unsigned int off )
{
u32 addr = 0 ;
u32 tmp ;
2008-02-19 19:46:48 +03:00
/* We do only have one cardbus device behind the bridge. */
2012-02-01 03:13:53 +04:00
if ( pc - > cardbusmode & & ( dev > 1 ) )
2007-09-18 23:12:50 +04:00
goto out ;
2008-02-19 19:46:48 +03:00
2007-09-18 23:12:50 +04:00
if ( bus = = 0 ) {
/* Type 0 transaction */
if ( unlikely ( dev > = SSB_PCI_SLOT_MAX ) )
goto out ;
/* Slide the window */
tmp = SSB_PCICORE_SBTOPCI_CFG0 ;
tmp | = ( ( 1 < < ( dev + 16 ) ) & SSB_PCICORE_SBTOPCI1_MASK ) ;
pcicore_write32 ( pc , SSB_PCICORE_SBTOPCI1 , tmp ) ;
/* Calculate the address */
addr = SSB_PCI_CFG ;
addr | = ( ( 1 < < ( dev + 16 ) ) & ~ SSB_PCICORE_SBTOPCI1_MASK ) ;
addr | = ( func < < 8 ) ;
addr | = ( off & ~ 3 ) ;
} else {
/* Type 1 transaction */
pcicore_write32 ( pc , SSB_PCICORE_SBTOPCI1 ,
SSB_PCICORE_SBTOPCI_CFG1 ) ;
/* Calculate the address */
addr = SSB_PCI_CFG ;
addr | = ( bus < < 16 ) ;
addr | = ( dev < < 11 ) ;
addr | = ( func < < 8 ) ;
addr | = ( off & ~ 3 ) ;
}
out :
return addr ;
}
static int ssb_extpci_read_config ( struct ssb_pcicore * pc ,
unsigned int bus , unsigned int dev ,
unsigned int func , unsigned int off ,
void * buf , int len )
{
int err = - EINVAL ;
u32 addr , val ;
void __iomem * mmio ;
SSB_WARN_ON ( ! pc - > hostmode ) ;
if ( unlikely ( len ! = 1 & & len ! = 2 & & len ! = 4 ) )
goto out ;
addr = get_cfgspace_addr ( pc , bus , dev , func , off ) ;
if ( unlikely ( ! addr ) )
goto out ;
err = - ENOMEM ;
mmio = ioremap_nocache ( addr , len ) ;
if ( ! mmio )
goto out ;
if ( mips_busprobe32 ( val , mmio ) ) {
val = 0xffffffff ;
goto unmap ;
}
val = readl ( mmio ) ;
val > > = ( 8 * ( off & 3 ) ) ;
switch ( len ) {
case 1 :
* ( ( u8 * ) buf ) = ( u8 ) val ;
break ;
case 2 :
* ( ( u16 * ) buf ) = ( u16 ) val ;
break ;
case 4 :
* ( ( u32 * ) buf ) = ( u32 ) val ;
break ;
}
err = 0 ;
unmap :
iounmap ( mmio ) ;
out :
return err ;
}
static int ssb_extpci_write_config ( struct ssb_pcicore * pc ,
unsigned int bus , unsigned int dev ,
unsigned int func , unsigned int off ,
const void * buf , int len )
{
int err = - EINVAL ;
u32 addr , val = 0 ;
void __iomem * mmio ;
SSB_WARN_ON ( ! pc - > hostmode ) ;
if ( unlikely ( len ! = 1 & & len ! = 2 & & len ! = 4 ) )
goto out ;
addr = get_cfgspace_addr ( pc , bus , dev , func , off ) ;
if ( unlikely ( ! addr ) )
goto out ;
err = - ENOMEM ;
mmio = ioremap_nocache ( addr , len ) ;
if ( ! mmio )
goto out ;
if ( mips_busprobe32 ( val , mmio ) ) {
val = 0xffffffff ;
goto unmap ;
}
switch ( len ) {
case 1 :
val = readl ( mmio ) ;
val & = ~ ( 0xFF < < ( 8 * ( off & 3 ) ) ) ;
val | = * ( ( const u8 * ) buf ) < < ( 8 * ( off & 3 ) ) ;
break ;
case 2 :
val = readl ( mmio ) ;
val & = ~ ( 0xFFFF < < ( 8 * ( off & 3 ) ) ) ;
val | = * ( ( const u16 * ) buf ) < < ( 8 * ( off & 3 ) ) ;
break ;
case 4 :
val = * ( ( const u32 * ) buf ) ;
break ;
}
writel ( val , mmio ) ;
err = 0 ;
unmap :
iounmap ( mmio ) ;
out :
return err ;
}
static int ssb_pcicore_read_config ( struct pci_bus * bus , unsigned int devfn ,
int reg , int size , u32 * val )
{
unsigned long flags ;
int err ;
spin_lock_irqsave ( & cfgspace_lock , flags ) ;
err = ssb_extpci_read_config ( extpci_core , bus - > number , PCI_SLOT ( devfn ) ,
PCI_FUNC ( devfn ) , reg , val , size ) ;
spin_unlock_irqrestore ( & cfgspace_lock , flags ) ;
return err ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL ;
}
static int ssb_pcicore_write_config ( struct pci_bus * bus , unsigned int devfn ,
int reg , int size , u32 val )
{
unsigned long flags ;
int err ;
spin_lock_irqsave ( & cfgspace_lock , flags ) ;
err = ssb_extpci_write_config ( extpci_core , bus - > number , PCI_SLOT ( devfn ) ,
PCI_FUNC ( devfn ) , reg , & val , size ) ;
spin_unlock_irqrestore ( & cfgspace_lock , flags ) ;
return err ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL ;
}
static struct pci_ops ssb_pcicore_pciops = {
. read = ssb_pcicore_read_config ,
. write = ssb_pcicore_write_config ,
} ;
static struct resource ssb_pcicore_mem_resource = {
. name = " SSB PCIcore external memory " ,
. start = SSB_PCI_DMA ,
. end = SSB_PCI_DMA + SSB_PCI_DMA_SZ - 1 ,
2008-02-16 20:13:36 +03:00
. flags = IORESOURCE_MEM | IORESOURCE_PCI_FIXED ,
2007-09-18 23:12:50 +04:00
} ;
static struct resource ssb_pcicore_io_resource = {
. name = " SSB PCIcore external I/O " ,
. start = 0x100 ,
. end = 0x7FF ,
2008-02-16 20:13:36 +03:00
. flags = IORESOURCE_IO | IORESOURCE_PCI_FIXED ,
2007-09-18 23:12:50 +04:00
} ;
static struct pci_controller ssb_pcicore_controller = {
. pci_ops = & ssb_pcicore_pciops ,
. io_resource = & ssb_pcicore_io_resource ,
. mem_resource = & ssb_pcicore_mem_resource ,
} ;
2008-02-29 13:36:12 +03:00
/* This function is called when doing a pci_enable_device().
* We must first check if the device is a device on the PCI - core bridge . */
int ssb_pcicore_plat_dev_init ( struct pci_dev * d )
{
if ( d - > bus - > ops ! = & ssb_pcicore_pciops ) {
/* This is not a device on the PCI-core bridge. */
return - ENODEV ;
}
ssb_printk ( KERN_INFO " PCI: Fixing up device %s \n " ,
pci_name ( d ) ) ;
/* Fix up interrupt lines */
d - > irq = ssb_mips_irq ( extpci_core - > dev ) + 2 ;
pci_write_config_byte ( d , PCI_INTERRUPT_LINE , d - > irq ) ;
return 0 ;
}
/* Early PCI fixup for a device on the PCI-core bridge. */
static void ssb_pcicore_fixup_pcibridge ( struct pci_dev * dev )
{
u8 lat ;
if ( dev - > bus - > ops ! = & ssb_pcicore_pciops ) {
/* This is not a device on the PCI-core bridge. */
return ;
}
if ( dev - > bus - > number ! = 0 | | PCI_SLOT ( dev - > devfn ) ! = 0 )
return ;
ssb_printk ( KERN_INFO " PCI: Fixing up bridge %s \n " , pci_name ( dev ) ) ;
/* Enable PCI bridge bus mastering and memory space */
pci_set_master ( dev ) ;
if ( pcibios_enable_device ( dev , ~ 0 ) < 0 ) {
ssb_printk ( KERN_ERR " PCI: SSB bridge enable failed \n " ) ;
return ;
}
/* Enable PCI bridge BAR1 prefetch and burst */
pci_write_config_dword ( dev , SSB_BAR1_CONTROL , 3 ) ;
/* Make sure our latency is high enough to handle the devices behind us */
lat = 168 ;
ssb_printk ( KERN_INFO " PCI: Fixing latency timer of device %s to %u \n " ,
pci_name ( dev ) , lat ) ;
pci_write_config_byte ( dev , PCI_LATENCY_TIMER , lat ) ;
}
DECLARE_PCI_FIXUP_EARLY ( PCI_ANY_ID , PCI_ANY_ID , ssb_pcicore_fixup_pcibridge ) ;
/* PCI device IRQ mapping. */
int ssb_pcicore_pcibios_map_irq ( const struct pci_dev * dev , u8 slot , u8 pin )
{
if ( dev - > bus - > ops ! = & ssb_pcicore_pciops ) {
/* This is not a device on the PCI-core bridge. */
return - ENODEV ;
}
return ssb_mips_irq ( extpci_core - > dev ) + 2 ;
}
2012-12-22 03:10:52 +04:00
static void ssb_pcicore_init_hostmode ( struct ssb_pcicore * pc )
2007-09-18 23:12:50 +04:00
{
u32 val ;
if ( WARN_ON ( extpci_core ) )
return ;
extpci_core = pc ;
ssb_dprintk ( KERN_INFO PFX " PCIcore in host mode found \n " ) ;
/* Reset devices on the external PCI bus */
val = SSB_PCICORE_CTL_RST_OE ;
val | = SSB_PCICORE_CTL_CLK_OE ;
pcicore_write32 ( pc , SSB_PCICORE_CTL , val ) ;
val | = SSB_PCICORE_CTL_CLK ; /* Clock on */
pcicore_write32 ( pc , SSB_PCICORE_CTL , val ) ;
udelay ( 150 ) ; /* Assertion time demanded by the PCI standard */
val | = SSB_PCICORE_CTL_RST ; /* Deassert RST# */
pcicore_write32 ( pc , SSB_PCICORE_CTL , val ) ;
val = SSB_PCICORE_ARBCTL_INTERN ;
pcicore_write32 ( pc , SSB_PCICORE_ARBCTL , val ) ;
udelay ( 1 ) ; /* Assertion time demanded by the PCI standard */
2008-02-19 19:46:48 +03:00
if ( pc - > dev - > bus - > has_cardbus_slot ) {
ssb_dprintk ( KERN_INFO PFX " CardBus slot detected \n " ) ;
pc - > cardbusmode = 1 ;
/* GPIO 1 resets the bridge */
ssb_gpio_out ( pc - > dev - > bus , 1 , 1 ) ;
ssb_gpio_outen ( pc - > dev - > bus , 1 , 1 ) ;
pcicore_write16 ( pc , SSB_PCICORE_SPROM ( 0 ) ,
pcicore_read16 ( pc , SSB_PCICORE_SPROM ( 0 ) )
| 0x0400 ) ;
}
2007-09-18 23:12:50 +04:00
/* 64MB I/O window */
pcicore_write32 ( pc , SSB_PCICORE_SBTOPCI0 ,
SSB_PCICORE_SBTOPCI_IO ) ;
/* 64MB config space */
pcicore_write32 ( pc , SSB_PCICORE_SBTOPCI1 ,
SSB_PCICORE_SBTOPCI_CFG0 ) ;
/* 1GB memory window */
pcicore_write32 ( pc , SSB_PCICORE_SBTOPCI2 ,
SSB_PCICORE_SBTOPCI_MEM | SSB_PCI_DMA ) ;
/* Enable PCI bridge BAR0 prefetch and burst */
val = PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY ;
ssb_extpci_write_config ( pc , 0 , 0 , 0 , PCI_COMMAND , & val , 2 ) ;
/* Clear error conditions */
val = 0 ;
ssb_extpci_write_config ( pc , 0 , 0 , 0 , PCI_STATUS , & val , 2 ) ;
/* Enable PCI interrupts */
pcicore_write32 ( pc , SSB_PCICORE_IMASK ,
SSB_PCICORE_IMASK_INTA ) ;
/* Ok, ready to run, register it to the system.
* The following needs change , if we want to port hostmode
* to non - MIPS platform . */
2008-02-16 20:13:36 +03:00
ssb_pcicore_controller . io_map_base = ( unsigned long ) ioremap_nocache ( SSB_PCI_MEM , 0x04000000 ) ;
set_io_port_base ( ssb_pcicore_controller . io_map_base ) ;
2007-09-18 23:12:50 +04:00
/* Give some time to the PCI controller to configure itself with the new
* values . Not waiting at this point causes crashes of the machine . */
mdelay ( 10 ) ;
register_pci_controller ( & ssb_pcicore_controller ) ;
}
2012-12-22 03:10:52 +04:00
static int pcicore_is_in_hostmode ( struct ssb_pcicore * pc )
2007-09-18 23:12:50 +04:00
{
struct ssb_bus * bus = pc - > dev - > bus ;
u16 chipid_top ;
u32 tmp ;
chipid_top = ( bus - > chip_id & 0xFF00 ) ;
if ( chipid_top ! = 0x4700 & &
chipid_top ! = 0x5300 )
return 0 ;
2008-02-28 17:11:26 +03:00
if ( bus - > sprom . boardflags_lo & SSB_PCICORE_BFL_NOPCI )
2007-09-18 23:12:50 +04:00
return 0 ;
/* The 200-pin BCM4712 package does not bond out PCI. Even when
* PCI is bonded out , some boards may leave the pins floating . */
if ( bus - > chip_id = = 0x4712 ) {
if ( bus - > chip_package = = SSB_CHIPPACK_BCM4712S )
return 0 ;
if ( bus - > chip_package = = SSB_CHIPPACK_BCM4712M )
return 0 ;
}
if ( bus - > chip_id = = 0x5350 )
return 0 ;
return ! mips_busprobe32 ( tmp , ( bus - > mmio + ( pc - > dev - > core_index * SSB_CORE_SIZE ) ) ) ;
}
# endif /* CONFIG_SSB_PCICORE_HOSTMODE */
2011-04-01 15:26:52 +04:00
/**************************************************
* Workarounds .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2012-12-22 03:10:52 +04:00
static void ssb_pcicore_fix_sprom_core_index ( struct ssb_pcicore * pc )
2011-04-27 20:21:34 +04:00
{
u16 tmp = pcicore_read16 ( pc , SSB_PCICORE_SPROM ( 0 ) ) ;
if ( ( ( tmp & 0xF000 ) > > 12 ) ! = pc - > dev - > core_index ) {
tmp & = ~ 0xF000 ;
tmp | = ( pc - > dev - > core_index < < 12 ) ;
pcicore_write16 ( pc , SSB_PCICORE_SPROM ( 0 ) , tmp ) ;
}
}
2011-04-01 15:26:52 +04:00
static u8 ssb_pcicore_polarity_workaround ( struct ssb_pcicore * pc )
{
return ( ssb_pcie_read ( pc , 0x204 ) & 0x10 ) ? 0xC0 : 0x80 ;
}
static void ssb_pcicore_serdes_workaround ( struct ssb_pcicore * pc )
{
const u8 serdes_pll_device = 0x1D ;
const u8 serdes_rx_device = 0x1F ;
u16 tmp ;
ssb_pcie_mdio_write ( pc , serdes_rx_device , 1 /* Control */ ,
ssb_pcicore_polarity_workaround ( pc ) ) ;
tmp = ssb_pcie_mdio_read ( pc , serdes_pll_device , 1 /* Control */ ) ;
if ( tmp & 0x4000 )
ssb_pcie_mdio_write ( pc , serdes_pll_device , 1 , tmp & ~ 0x4000 ) ;
}
2007-09-18 23:12:50 +04:00
2011-04-27 19:39:47 +04:00
static void ssb_pcicore_pci_setup_workarounds ( struct ssb_pcicore * pc )
{
struct ssb_device * pdev = pc - > dev ;
struct ssb_bus * bus = pdev - > bus ;
u32 tmp ;
tmp = pcicore_read32 ( pc , SSB_PCICORE_SBTOPCI2 ) ;
tmp | = SSB_PCICORE_SBTOPCI_PREF ;
tmp | = SSB_PCICORE_SBTOPCI_BURST ;
pcicore_write32 ( pc , SSB_PCICORE_SBTOPCI2 , tmp ) ;
if ( pdev - > id . revision < 5 ) {
tmp = ssb_read32 ( pdev , SSB_IMCFGLO ) ;
tmp & = ~ SSB_IMCFGLO_SERTO ;
tmp | = 2 ;
tmp & = ~ SSB_IMCFGLO_REQTO ;
tmp | = 3 < < SSB_IMCFGLO_REQTO_SHIFT ;
ssb_write32 ( pdev , SSB_IMCFGLO , tmp ) ;
ssb_commit_settings ( bus ) ;
} else if ( pdev - > id . revision > = 11 ) {
tmp = pcicore_read32 ( pc , SSB_PCICORE_SBTOPCI2 ) ;
tmp | = SSB_PCICORE_SBTOPCI_MRM ;
pcicore_write32 ( pc , SSB_PCICORE_SBTOPCI2 , tmp ) ;
}
}
static void ssb_pcicore_pcie_setup_workarounds ( struct ssb_pcicore * pc )
{
u32 tmp ;
2011-04-27 19:39:48 +04:00
u8 rev = pc - > dev - > id . revision ;
2011-04-27 19:39:47 +04:00
2011-04-27 19:39:48 +04:00
if ( rev = = 0 | | rev = = 1 ) {
2011-04-27 19:39:47 +04:00
/* TLP Workaround register. */
tmp = ssb_pcie_read ( pc , 0x4 ) ;
tmp | = 0x8 ;
ssb_pcie_write ( pc , 0x4 , tmp ) ;
}
2011-04-27 19:39:48 +04:00
if ( rev = = 1 ) {
/* DLLP Link Control register. */
tmp = ssb_pcie_read ( pc , 0x100 ) ;
tmp | = 0x40 ;
ssb_pcie_write ( pc , 0x100 , tmp ) ;
}
if ( rev = = 0 ) {
2011-04-27 19:39:47 +04:00
const u8 serdes_rx_device = 0x1F ;
ssb_pcie_mdio_write ( pc , serdes_rx_device ,
2 /* Timer */ , 0x8128 ) ;
ssb_pcie_mdio_write ( pc , serdes_rx_device ,
6 /* CDR */ , 0x0100 ) ;
ssb_pcie_mdio_write ( pc , serdes_rx_device ,
7 /* CDR BW */ , 0x1466 ) ;
2011-04-27 19:39:48 +04:00
} else if ( rev = = 3 | | rev = = 4 | | rev = = 5 ) {
/* TODO: DLLP Power Management Threshold */
ssb_pcicore_serdes_workaround ( pc ) ;
/* TODO: ASPM */
} else if ( rev = = 7 ) {
/* TODO: No PLL down */
}
if ( rev > = 6 ) {
/* Miscellaneous Configuration Fixup */
tmp = pcicore_read16 ( pc , SSB_PCICORE_SPROM ( 5 ) ) ;
if ( ! ( tmp & 0x8000 ) )
pcicore_write16 ( pc , SSB_PCICORE_SPROM ( 5 ) ,
tmp | 0x8000 ) ;
2011-04-27 19:39:47 +04:00
}
}
2007-09-18 23:12:50 +04:00
/**************************************************
* Generic and Clientmode operation code .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2012-12-22 03:10:52 +04:00
static void ssb_pcicore_init_clientmode ( struct ssb_pcicore * pc )
2007-09-18 23:12:50 +04:00
{
2011-12-06 02:19:51 +04:00
struct ssb_device * pdev = pc - > dev ;
struct ssb_bus * bus = pdev - > bus ;
if ( bus - > bustype = = SSB_BUSTYPE_PCI )
ssb_pcicore_fix_sprom_core_index ( pc ) ;
2011-07-05 19:25:32 +04:00
2007-09-18 23:12:50 +04:00
/* Disable PCI interrupts. */
2011-12-06 02:19:51 +04:00
ssb_write32 ( pdev , SSB_INTVEC , 0 ) ;
2011-07-05 19:25:32 +04:00
/* Additional PCIe always once-executed workarounds */
if ( pc - > dev - > id . coreid = = SSB_DEV_PCIE ) {
ssb_pcicore_serdes_workaround ( pc ) ;
/* TODO: ASPM */
/* TODO: Clock Request Update */
}
2007-09-18 23:12:50 +04:00
}
2012-12-22 03:10:52 +04:00
void ssb_pcicore_init ( struct ssb_pcicore * pc )
2007-09-18 23:12:50 +04:00
{
struct ssb_device * dev = pc - > dev ;
if ( ! dev )
return ;
if ( ! ssb_device_is_enabled ( dev ) )
ssb_device_enable ( dev , 0 ) ;
# ifdef CONFIG_SSB_PCICORE_HOSTMODE
pc - > hostmode = pcicore_is_in_hostmode ( pc ) ;
if ( pc - > hostmode )
ssb_pcicore_init_hostmode ( pc ) ;
# endif /* CONFIG_SSB_PCICORE_HOSTMODE */
if ( ! pc - > hostmode )
ssb_pcicore_init_clientmode ( pc ) ;
}
static u32 ssb_pcie_read ( struct ssb_pcicore * pc , u32 address )
{
pcicore_write32 ( pc , 0x130 , address ) ;
return pcicore_read32 ( pc , 0x134 ) ;
}
static void ssb_pcie_write ( struct ssb_pcicore * pc , u32 address , u32 data )
{
pcicore_write32 ( pc , 0x130 , address ) ;
pcicore_write32 ( pc , 0x134 , data ) ;
}
2011-04-01 14:07:33 +04:00
static void ssb_pcie_mdio_set_phy ( struct ssb_pcicore * 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 ) ;
}
}
2011-04-01 14:07:34 +04:00
static u16 ssb_pcie_mdio_read ( struct ssb_pcicore * 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 - > dev - > id . revision > = 10 ) {
max_retries = 200 ;
ssb_pcie_mdio_set_phy ( pc , device ) ;
}
v = ( 1 < < 30 ) ; /* Start of Transaction */
v | = ( 1 < < 29 ) ; /* Read Transaction */
v | = ( 1 < < 17 ) ; /* Turnaround */
if ( pc - > dev - > id . revision < 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-04-20 00:40:22 +04:00
for ( i = 0 ; i < max_retries ; i + + ) {
2011-04-01 14:07:34 +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 ;
}
2007-09-18 23:12:50 +04:00
static void ssb_pcie_mdio_write ( struct ssb_pcicore * pc , u8 device ,
u8 address , u16 data )
{
const u16 mdio_control = 0x128 ;
const u16 mdio_data = 0x12C ;
2011-04-01 14:07:33 +04:00
int max_retries = 10 ;
2007-09-18 23:12:50 +04:00
u32 v ;
int i ;
v = 0x80 ; /* Enable Preamble Sequence */
v | = 0x2 ; /* MDIO Clock Divisor */
pcicore_write32 ( pc , mdio_control , v ) ;
2011-04-01 14:07:33 +04:00
if ( pc - > dev - > id . revision > = 10 ) {
max_retries = 200 ;
ssb_pcie_mdio_set_phy ( pc , device ) ;
}
2007-09-18 23:12:50 +04:00
v = ( 1 < < 30 ) ; /* Start of Transaction */
v | = ( 1 < < 28 ) ; /* Write Transaction */
v | = ( 1 < < 17 ) ; /* Turnaround */
2011-04-01 14:07:33 +04:00
if ( pc - > dev - > id . revision < 10 )
v | = ( u32 ) device < < 22 ;
2007-09-18 23:12:50 +04:00
v | = ( u32 ) address < < 18 ;
v | = data ;
pcicore_write32 ( pc , mdio_data , v ) ;
/* Wait for the device to complete the transaction */
udelay ( 10 ) ;
2011-04-01 14:07:33 +04:00
for ( i = 0 ; i < max_retries ; i + + ) {
2007-09-18 23:12:50 +04:00
v = pcicore_read32 ( pc , mdio_control ) ;
if ( v & 0x100 /* Trans complete */ )
break ;
msleep ( 1 ) ;
}
pcicore_write32 ( pc , mdio_control , 0 ) ;
}
int ssb_pcicore_dev_irqvecs_enable ( struct ssb_pcicore * pc ,
struct ssb_device * dev )
{
struct ssb_device * pdev = pc - > dev ;
struct ssb_bus * bus ;
int err = 0 ;
u32 tmp ;
2008-07-05 01:44:37 +04:00
if ( dev - > bus - > bustype ! = SSB_BUSTYPE_PCI ) {
/* This SSB 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 ;
}
2007-09-18 23:12:50 +04:00
if ( ! pdev )
goto out ;
bus = pdev - > bus ;
2008-06-02 18:15:23 +04:00
might_sleep_if ( pdev - > id . coreid ! = SSB_DEV_PCI ) ;
2007-09-18 23:12:50 +04:00
/* Enable interrupts for this device. */
2009-10-09 22:32:10 +04:00
if ( ( pdev - > id . revision > = 6 ) | | ( pdev - > id . coreid = = SSB_DEV_PCIE ) ) {
2007-09-18 23:12:50 +04:00
u32 coremask ;
/* Calculate the "coremask" for the device. */
coremask = ( 1 < < dev - > core_index ) ;
2009-10-09 22:32:10 +04:00
SSB_WARN_ON ( bus - > bustype ! = SSB_BUSTYPE_PCI ) ;
2007-09-18 23:12:50 +04:00
err = pci_read_config_dword ( bus - > host_pci , SSB_PCI_IRQMASK , & tmp ) ;
if ( err )
goto out ;
tmp | = coremask < < 8 ;
err = pci_write_config_dword ( bus - > host_pci , SSB_PCI_IRQMASK , tmp ) ;
if ( err )
goto out ;
} else {
u32 intvec ;
intvec = ssb_read32 ( pdev , SSB_INTVEC ) ;
2008-04-08 12:31:22 +04:00
tmp = ssb_read32 ( dev , SSB_TPSFLAG ) ;
tmp & = SSB_TPSFLAG_BPFLAG ;
intvec | = ( 1 < < tmp ) ;
2007-09-18 23:12:50 +04:00
ssb_write32 ( pdev , SSB_INTVEC , intvec ) ;
}
/* Setup PCIcore operation. */
if ( pc - > setup_done )
goto out ;
if ( pdev - > id . coreid = = SSB_DEV_PCI ) {
2011-04-27 19:39:47 +04:00
ssb_pcicore_pci_setup_workarounds ( pc ) ;
2007-09-18 23:12:50 +04:00
} else {
WARN_ON ( pdev - > id . coreid ! = SSB_DEV_PCIE ) ;
2011-04-27 19:39:47 +04:00
ssb_pcicore_pcie_setup_workarounds ( pc ) ;
2007-09-18 23:12:50 +04:00
}
pc - > setup_done = 1 ;
out :
return err ;
}
EXPORT_SYMBOL ( ssb_pcicore_dev_irqvecs_enable ) ;