/*
* Copyright ( C ) 2015 , Bin Meng < bmeng . cn @ gmail . com >
*
* Intel Platform Controller Hub EG20T ( codename Topcliff ) GMAC Driver
*
* SPDX - License - Identifier : GPL - 2.0 +
*/
# include <common.h>
# include <dm.h>
# include <errno.h>
# include <asm/io.h>
# include <pci.h>
# include <miiphy.h>
# include "pch_gbe.h"
# if !defined(CONFIG_PHYLIB)
# error "PCH Gigabit Ethernet driver requires PHYLIB - missing CONFIG_PHYLIB"
# endif
static struct pci_device_id supported [ ] = {
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , PCI_DEVICE_ID_INTEL_TCF_GBE ) } ,
{ }
} ;
static void pch_gbe_mac_read ( struct pch_gbe_regs * mac_regs , u8 * addr )
{
u32 macid_hi , macid_lo ;
macid_hi = readl ( & mac_regs - > mac_adr [ 0 ] . high ) ;
macid_lo = readl ( & mac_regs - > mac_adr [ 0 ] . low ) & 0xffff ;
debug ( " pch_gbe: macid_hi %#x macid_lo %#x \n " , macid_hi , macid_lo ) ;
addr [ 0 ] = ( u8 ) ( macid_hi & 0xff ) ;
addr [ 1 ] = ( u8 ) ( ( macid_hi > > 8 ) & 0xff ) ;
addr [ 2 ] = ( u8 ) ( ( macid_hi > > 16 ) & 0xff ) ;
addr [ 3 ] = ( u8 ) ( ( macid_hi > > 24 ) & 0xff ) ;
addr [ 4 ] = ( u8 ) ( macid_lo & 0xff ) ;
addr [ 5 ] = ( u8 ) ( ( macid_lo > > 8 ) & 0xff ) ;
}
static int pch_gbe_mac_write ( struct pch_gbe_regs * mac_regs , u8 * addr )
{
u32 macid_hi , macid_lo ;
ulong start ;
macid_hi = addr [ 0 ] + ( addr [ 1 ] < < 8 ) + ( addr [ 2 ] < < 16 ) + ( addr [ 3 ] < < 24 ) ;
macid_lo = addr [ 4 ] + ( addr [ 5 ] < < 8 ) ;
writel ( macid_hi , & mac_regs - > mac_adr [ 0 ] . high ) ;
writel ( macid_lo , & mac_regs - > mac_adr [ 0 ] . low ) ;
writel ( 0xfffe , & mac_regs - > addr_mask ) ;
start = get_timer ( 0 ) ;
while ( get_timer ( start ) < PCH_GBE_TIMEOUT ) {
if ( ! ( readl ( & mac_regs - > addr_mask ) & PCH_GBE_BUSY ) )
return 0 ;
udelay ( 10 ) ;
}
return - ETIME ;
}
static int pch_gbe_reset ( struct udevice * dev )
{
struct pch_gbe_priv * priv = dev_get_priv ( dev ) ;
struct eth_pdata * plat = dev_get_platdata ( dev ) ;
struct pch_gbe_regs * mac_regs = priv - > mac_regs ;
ulong start ;
priv - > rx_idx = 0 ;
priv - > tx_idx = 0 ;
writel ( PCH_GBE_ALL_RST , & mac_regs - > reset ) ;
/*
* Configure the MAC to RGMII mode after reset
*
* For some unknown reason , we must do the configuration here right
* after resetting the whole MAC , otherwise the reset bit in the RESET
* register will never be cleared by the hardware . And there is another
* way of having the same magic , that is to configure the MODE register
* to have the MAC work in MII / GMII mode , which is how current Linux
* pch_gbe driver does . Since anyway we need program the MAC to RGMII
* mode in the driver , we just do it here .
*
* Note : this behavior is not documented in the hardware manual .
*/
writel ( PCH_GBE_RGMII_MODE_RGMII | PCH_GBE_CHIP_TYPE_INTERNAL ,
& mac_regs - > rgmii_ctrl ) ;
start = get_timer ( 0 ) ;
while ( get_timer ( start ) < PCH_GBE_TIMEOUT ) {
if ( ! ( readl ( & mac_regs - > reset ) & PCH_GBE_ALL_RST ) ) {
/*
* Soft reset clears hardware MAC address registers ,
* so we have to reload MAC address here in order to
* make linux pch_gbe driver happy .
*/
return pch_gbe_mac_write ( mac_regs , plat - > enetaddr ) ;
}
udelay ( 10 ) ;
}
debug ( " pch_gbe: reset timeout \n " ) ;
return - ETIME ;
}
static void pch_gbe_rx_descs_init ( struct udevice * dev )
{
struct pch_gbe_priv * priv = dev_get_priv ( dev ) ;
struct pch_gbe_regs * mac_regs = priv - > mac_regs ;
struct pch_gbe_rx_desc * rx_desc = & priv - > rx_desc [ 0 ] ;
int i ;
memset ( rx_desc , 0 , sizeof ( struct pch_gbe_rx_desc ) * PCH_GBE_DESC_NUM ) ;
for ( i = 0 ; i < PCH_GBE_DESC_NUM ; i + + )
rx_desc - > buffer_addr = dm_pci_phys_to_mem ( priv - > dev ,
( u32 ) ( priv - > rx_buff [ i ] ) ) ;
writel ( dm_pci_phys_to_mem ( priv - > dev , ( u32 ) rx_desc ) ,
& mac_regs - > rx_dsc_base ) ;
writel ( sizeof ( struct pch_gbe_rx_desc ) * ( PCH_GBE_DESC_NUM - 1 ) ,
& mac_regs - > rx_dsc_size ) ;
writel ( dm_pci_phys_to_mem ( priv - > dev , ( u32 ) ( rx_desc + 1 ) ) ,
& mac_regs - > rx_dsc_sw_p ) ;
}
static void pch_gbe_tx_descs_init ( struct udevice * dev )
{
struct pch_gbe_priv * priv = dev_get_priv ( dev ) ;
struct pch_gbe_regs * mac_regs = priv - > mac_regs ;
struct pch_gbe_tx_desc * tx_desc = & priv - > tx_desc [ 0 ] ;
memset ( tx_desc , 0 , sizeof ( struct pch_gbe_tx_desc ) * PCH_GBE_DESC_NUM ) ;
writel ( dm_pci_phys_to_mem ( priv - > dev , ( u32 ) tx_desc ) ,
& mac_regs - > tx_dsc_base ) ;
writel ( sizeof ( struct pch_gbe_tx_desc ) * ( PCH_GBE_DESC_NUM - 1 ) ,
& mac_regs - > tx_dsc_size ) ;
writel ( dm_pci_phys_to_mem ( priv - > dev , ( u32 ) ( tx_desc + 1 ) ) ,
& mac_regs - > tx_dsc_sw_p ) ;
}
static void pch_gbe_adjust_link ( struct pch_gbe_regs * mac_regs ,
struct phy_device * phydev )
{
if ( ! phydev - > link ) {
printf ( " %s: No link. \n " , phydev - > dev - > name ) ;
return ;
}
clrbits_le32 ( & mac_regs - > rgmii_ctrl ,
PCH_GBE_RGMII_RATE_2_5M | PCH_GBE_CRS_SEL ) ;
clrbits_le32 ( & mac_regs - > mode ,
PCH_GBE_MODE_GMII_ETHER | PCH_GBE_MODE_FULL_DUPLEX ) ;
switch ( phydev - > speed ) {
case 1000 :
setbits_le32 ( & mac_regs - > rgmii_ctrl , PCH_GBE_RGMII_RATE_125M ) ;
setbits_le32 ( & mac_regs - > mode , PCH_GBE_MODE_GMII_ETHER ) ;
break ;
case 100 :
setbits_le32 ( & mac_regs - > rgmii_ctrl , PCH_GBE_RGMII_RATE_25M ) ;
setbits_le32 ( & mac_regs - > mode , PCH_GBE_MODE_MII_ETHER ) ;
break ;
case 10 :
setbits_le32 ( & mac_regs - > rgmii_ctrl , PCH_GBE_RGMII_RATE_2_5M ) ;
setbits_le32 ( & mac_regs - > mode , PCH_GBE_MODE_MII_ETHER ) ;
break ;
}
if ( phydev - > duplex ) {
setbits_le32 ( & mac_regs - > rgmii_ctrl , PCH_GBE_CRS_SEL ) ;
setbits_le32 ( & mac_regs - > mode , PCH_GBE_MODE_FULL_DUPLEX ) ;
}
printf ( " Speed: %d, %s duplex \n " , phydev - > speed ,
( phydev - > duplex ) ? " full " : " half " ) ;
return ;
}
static int pch_gbe_start ( struct udevice * dev )
{
struct pch_gbe_priv * priv = dev_get_priv ( dev ) ;
struct pch_gbe_regs * mac_regs = priv - > mac_regs ;
if ( pch_gbe_reset ( dev ) )
return - 1 ;
pch_gbe_rx_descs_init ( dev ) ;
pch_gbe_tx_descs_init ( dev ) ;
/* Enable frame bursting */
writel ( PCH_GBE_MODE_FR_BST , & mac_regs - > mode ) ;
/* Disable TCP/IP accelerator */
writel ( PCH_GBE_RX_TCPIPACC_OFF , & mac_regs - > tcpip_acc ) ;
/* Disable RX flow control */
writel ( 0 , & mac_regs - > rx_fctrl ) ;
/* Configure RX/TX mode */
writel ( PCH_GBE_RH_ALM_EMP_16 | PCH_GBE_RH_ALM_FULL_16 |
PCH_GBE_RH_RD_TRG_32 , & mac_regs - > rx_mode ) ;
writel ( PCH_GBE_TM_TH_TX_STRT_32 | PCH_GBE_TM_TH_ALM_EMP_16 |
PCH_GBE_TM_TH_ALM_FULL_32 | PCH_GBE_TM_ST_AND_FD |
PCH_GBE_TM_SHORT_PKT , & mac_regs - > tx_mode ) ;
/* Start up the PHY */
if ( phy_startup ( priv - > phydev ) ) {
printf ( " Could not initialize PHY %s \n " ,
priv - > phydev - > dev - > name ) ;
return - 1 ;
}
pch_gbe_adjust_link ( mac_regs , priv - > phydev ) ;
if ( ! priv - > phydev - > link )
return - 1 ;
/* Enable TX & RX */
writel ( PCH_GBE_RX_DMA_EN | PCH_GBE_TX_DMA_EN , & mac_regs - > dma_ctrl ) ;
writel ( PCH_GBE_MRE_MAC_RX_EN , & mac_regs - > mac_rx_en ) ;
return 0 ;
}
static void pch_gbe_stop ( struct udevice * dev )
{
struct pch_gbe_priv * priv = dev_get_priv ( dev ) ;
pch_gbe_reset ( dev ) ;
phy_shutdown ( priv - > phydev ) ;
}
static int pch_gbe_send ( struct udevice * dev , void * packet , int length )
{
struct pch_gbe_priv * priv = dev_get_priv ( dev ) ;
struct pch_gbe_regs * mac_regs = priv - > mac_regs ;
struct pch_gbe_tx_desc * tx_head , * tx_desc ;
u16 frame_ctrl = 0 ;
u32 int_st ;
ulong start ;
tx_head = & priv - > tx_desc [ 0 ] ;
tx_desc = & priv - > tx_desc [ priv - > tx_idx ] ;
if ( length < 64 )
frame_ctrl | = PCH_GBE_TXD_CTRL_APAD ;
tx_desc - > buffer_addr = dm_pci_phys_to_mem ( priv - > dev , ( u32 ) packet ) ;
tx_desc - > length = length ;
tx_desc - > tx_words_eob = length + 3 ;
tx_desc - > tx_frame_ctrl = frame_ctrl ;
tx_desc - > dma_status = 0 ;
tx_desc - > gbec_status = 0 ;
/* Test the wrap-around condition */
if ( + + priv - > tx_idx > = PCH_GBE_DESC_NUM )
priv - > tx_idx = 0 ;
writel ( dm_pci_phys_to_mem ( priv - > dev , ( u32 ) ( tx_head + priv - > tx_idx ) ) ,
& mac_regs - > tx_dsc_sw_p ) ;
start = get_timer ( 0 ) ;
while ( get_timer ( start ) < PCH_GBE_TIMEOUT ) {
int_st = readl ( & mac_regs - > int_st ) ;
if ( int_st & PCH_GBE_INT_TX_CMPLT )
return 0 ;
udelay ( 10 ) ;
}
debug ( " pch_gbe: sent failed \n " ) ;
return - ETIME ;
}
static int pch_gbe_recv ( struct udevice * dev , int flags , uchar * * packetp )
{
struct pch_gbe_priv * priv = dev_get_priv ( dev ) ;
struct pch_gbe_regs * mac_regs = priv - > mac_regs ;
struct pch_gbe_rx_desc * rx_desc ;
u32 hw_desc , buffer_addr , length ;
rx_desc = & priv - > rx_desc [ priv - > rx_idx ] ;
readl ( & mac_regs - > int_st ) ;
hw_desc = readl ( & mac_regs - > rx_dsc_hw_p_hld ) ;
/* Just return if not receiving any packet */
if ( ( u32 ) rx_desc = = hw_desc )
return - EAGAIN ;
buffer_addr = dm_pci_mem_to_phys ( priv - > dev , rx_desc - > buffer_addr ) ;
* packetp = ( uchar * ) buffer_addr ;
length = rx_desc - > rx_words_eob - 3 - ETH_FCS_LEN ;
return length ;
}
static int pch_gbe_free_pkt ( struct udevice * dev , uchar * packet , int length )
{
struct pch_gbe_priv * priv = dev_get_priv ( dev ) ;
struct pch_gbe_regs * mac_regs = priv - > mac_regs ;
struct pch_gbe_rx_desc * rx_head = & priv - > rx_desc [ 0 ] ;
int rx_swp ;
/* Test the wrap-around condition */
if ( + + priv - > rx_idx > = PCH_GBE_DESC_NUM )
priv - > rx_idx = 0 ;
rx_swp = priv - > rx_idx ;
if ( + + rx_swp > = PCH_GBE_DESC_NUM )
rx_swp = 0 ;
writel ( dm_pci_phys_to_mem ( priv - > dev , ( u32 ) ( rx_head + rx_swp ) ) ,
& mac_regs - > rx_dsc_sw_p ) ;
return 0 ;
}
static int pch_gbe_mdio_ready ( struct pch_gbe_regs * mac_regs )
{
ulong start = get_timer ( 0 ) ;
while ( get_timer ( start ) < PCH_GBE_TIMEOUT ) {
if ( readl ( & mac_regs - > miim ) & PCH_GBE_MIIM_OPER_READY )
return 0 ;
udelay ( 10 ) ;
}
return - ETIME ;
}
static int pch_gbe_mdio_read ( struct mii_dev * bus , int addr , int devad , int reg )
{
struct pch_gbe_regs * mac_regs = bus - > priv ;
u32 miim ;
if ( pch_gbe_mdio_ready ( mac_regs ) )
return - ETIME ;
miim = ( addr < < PCH_GBE_MIIM_PHY_ADDR_SHIFT ) |
( reg < < PCH_GBE_MIIM_REG_ADDR_SHIFT ) |
PCH_GBE_MIIM_OPER_READ ;
writel ( miim , & mac_regs - > miim ) ;
if ( pch_gbe_mdio_ready ( mac_regs ) )
return - ETIME ;
return readl ( & mac_regs - > miim ) & 0xffff ;
}
static int pch_gbe_mdio_write ( struct mii_dev * bus , int addr , int devad ,
int reg , u16 val )
{
struct pch_gbe_regs * mac_regs = bus - > priv ;
u32 miim ;
if ( pch_gbe_mdio_ready ( mac_regs ) )
return - ETIME ;
miim = ( addr < < PCH_GBE_MIIM_PHY_ADDR_SHIFT ) |
( reg < < PCH_GBE_MIIM_REG_ADDR_SHIFT ) |
PCH_GBE_MIIM_OPER_WRITE | val ;
writel ( miim , & mac_regs - > miim ) ;
if ( pch_gbe_mdio_ready ( mac_regs ) )
return - ETIME ;
else
return 0 ;
}
static int pch_gbe_mdio_init ( const char * name , struct pch_gbe_regs * mac_regs )
{
struct mii_dev * bus ;
bus = mdio_alloc ( ) ;
if ( ! bus ) {
debug ( " pch_gbe: failed to allocate MDIO bus \n " ) ;
return - ENOMEM ;
}
bus - > read = pch_gbe_mdio_read ;
bus - > write = pch_gbe_mdio_write ;
strcpy ( bus - > name , name ) ;
bus - > priv = ( void * ) mac_regs ;
return mdio_register ( bus ) ;
}
static int pch_gbe_phy_init ( struct udevice * dev )
{
struct pch_gbe_priv * priv = dev_get_priv ( dev ) ;
struct eth_pdata * plat = dev_get_platdata ( dev ) ;
struct phy_device * phydev ;
int mask = 0xffffffff ;
phydev = phy_find_by_mask ( priv - > bus , mask , plat - > phy_interface ) ;
if ( ! phydev ) {
printf ( " pch_gbe: cannot find the phy \n " ) ;
return - 1 ;
}
phy_connect_dev ( phydev , dev ) ;
phydev - > supported & = PHY_GBIT_FEATURES ;
phydev - > advertising = phydev - > supported ;
priv - > phydev = phydev ;
phy_config ( phydev ) ;
return 0 ;
}
int pch_gbe_probe ( struct udevice * dev )
{
struct pch_gbe_priv * priv ;
struct eth_pdata * plat = dev_get_platdata ( dev ) ;
u32 iobase ;
/*
* The priv structure contains the descriptors and frame buffers which
* need a strict buswidth alignment ( 64 bytes ) . This is guaranteed by
* DM_FLAG_ALLOC_PRIV_DMA flag in the U_BOOT_DRIVER .
*/
priv = dev_get_priv ( dev ) ;
priv - > dev = dev ;
dm_pci_read_config32 ( dev , PCI_BASE_ADDRESS_1 , & iobase ) ;
iobase & = PCI_BASE_ADDRESS_MEM_MASK ;
iobase = dm_pci_mem_to_phys ( dev , iobase ) ;
plat - > iobase = iobase ;
priv - > mac_regs = ( struct pch_gbe_regs * ) iobase ;
/* Read MAC address from SROM and initialize dev->enetaddr with it */
pch_gbe_mac_read ( priv - > mac_regs , plat - > enetaddr ) ;
plat - > phy_interface = PHY_INTERFACE_MODE_RGMII ;
pch_gbe_mdio_init ( dev - > name , priv - > mac_regs ) ;
priv - > bus = miiphy_get_dev_by_name ( dev - > name ) ;
return pch_gbe_phy_init ( dev ) ;
}
int pch_gbe_remove ( struct udevice * dev )
{
struct pch_gbe_priv * priv = dev_get_priv ( dev ) ;
free ( priv - > phydev ) ;
mdio_unregister ( priv - > bus ) ;
mdio_free ( priv - > bus ) ;
return 0 ;
}
static const struct eth_ops pch_gbe_ops = {
. start = pch_gbe_start ,
. send = pch_gbe_send ,
. recv = pch_gbe_recv ,
. free_pkt = pch_gbe_free_pkt ,
. stop = pch_gbe_stop ,
} ;
static const struct udevice_id pch_gbe_ids [ ] = {
{ . compatible = " intel,pch-gbe " } ,
{ }
} ;
U_BOOT_DRIVER ( eth_pch_gbe ) = {
. name = " pch_gbe " ,
. id = UCLASS_ETH ,
. of_match = pch_gbe_ids ,
. probe = pch_gbe_probe ,
. remove = pch_gbe_remove ,
. ops = & pch_gbe_ops ,
. priv_auto_alloc_size = sizeof ( struct pch_gbe_priv ) ,
. platdata_auto_alloc_size = sizeof ( struct eth_pdata ) ,
. flags = DM_FLAG_ALLOC_PRIV_DMA ,
} ;
U_BOOT_PCI_DEVICE ( eth_pch_gbe , supported ) ;