2018-04-23 09:05:15 +01:00
// SPDX-License-Identifier: (GPL-2.0 OR MIT)
/*
* Copyright ( c ) 2018 Synopsys , Inc . and / or its affiliates .
* stmmac HW Interface Handling
*/
# include "common.h"
# include "stmmac.h"
2018-05-18 14:56:04 +01:00
# include "stmmac_ptp.h"
2018-04-23 09:05:15 +01:00
static u32 stmmac_get_id ( struct stmmac_priv * priv , u32 id_reg )
{
u32 reg = readl ( priv - > ioaddr + id_reg ) ;
if ( ! reg ) {
dev_info ( priv - > device , " Version ID not available \n " ) ;
return 0x0 ;
}
dev_info ( priv - > device , " User ID: 0x%x, Synopsys ID: 0x%x \n " ,
( unsigned int ) ( reg & GENMASK ( 15 , 8 ) ) > > 8 ,
( unsigned int ) ( reg & GENMASK ( 7 , 0 ) ) ) ;
return reg & GENMASK ( 7 , 0 ) ;
}
2020-03-17 10:18:52 +01:00
static u32 stmmac_get_dev_id ( struct stmmac_priv * priv , u32 id_reg )
{
u32 reg = readl ( priv - > ioaddr + id_reg ) ;
if ( ! reg ) {
dev_info ( priv - > device , " Version ID not available \n " ) ;
return 0x0 ;
}
return ( reg & GENMASK ( 15 , 8 ) ) > > 8 ;
}
2018-04-23 09:05:15 +01:00
static void stmmac_dwmac_mode_quirk ( struct stmmac_priv * priv )
{
struct mac_device_info * mac = priv - > hw ;
if ( priv - > chain_mode ) {
dev_info ( priv - > device , " Chain mode enabled \n " ) ;
priv - > mode = STMMAC_CHAIN_MODE ;
mac - > mode = & chain_mode_ops ;
} else {
dev_info ( priv - > device , " Ring mode enabled \n " ) ;
priv - > mode = STMMAC_RING_MODE ;
mac - > mode = & ring_mode_ops ;
}
}
static int stmmac_dwmac1_quirks ( struct stmmac_priv * priv )
{
struct mac_device_info * mac = priv - > hw ;
if ( priv - > plat - > enh_desc ) {
dev_info ( priv - > device , " Enhanced/Alternate descriptors \n " ) ;
/* GMAC older than 3.50 has no extended descriptors */
if ( priv - > synopsys_id > = DWMAC_CORE_3_50 ) {
dev_info ( priv - > device , " Enabled extended descriptors \n " ) ;
priv - > extend_desc = 1 ;
} else {
dev_warn ( priv - > device , " Extended descriptors not supported \n " ) ;
}
mac - > desc = & enh_desc_ops ;
} else {
dev_info ( priv - > device , " Normal descriptors \n " ) ;
mac - > desc = & ndesc_ops ;
}
stmmac_dwmac_mode_quirk ( priv ) ;
return 0 ;
}
static int stmmac_dwmac4_quirks ( struct stmmac_priv * priv )
{
stmmac_dwmac_mode_quirk ( priv ) ;
return 0 ;
}
2020-03-17 10:18:52 +01:00
static int stmmac_dwxlgmac_quirks ( struct stmmac_priv * priv )
{
priv - > hw - > xlgmac = true ;
return 0 ;
}
2018-04-23 09:05:15 +01:00
static const struct stmmac_hwif_entry {
bool gmac ;
bool gmac4 ;
2018-08-08 09:04:29 +01:00
bool xgmac ;
2018-04-23 09:05:15 +01:00
u32 min_id ;
2020-03-17 10:18:52 +01:00
u32 dev_id ;
2018-05-18 14:56:04 +01:00
const struct stmmac_regs_off regs ;
2018-04-23 09:05:15 +01:00
const void * desc ;
const void * dma ;
const void * mac ;
const void * hwtimestamp ;
const void * mode ;
2018-05-04 10:01:38 +01:00
const void * tc ;
2019-05-24 10:20:15 +02:00
const void * mmc ;
2018-04-23 09:05:15 +01:00
int ( * setup ) ( struct stmmac_priv * priv ) ;
int ( * quirks ) ( struct stmmac_priv * priv ) ;
} stmmac_hw [ ] = {
/* NOTE: New HW versions shall go to the end of this table */
{
. gmac = false ,
. gmac4 = false ,
2018-08-08 09:04:29 +01:00
. xgmac = false ,
2018-04-23 09:05:15 +01:00
. min_id = 0 ,
2018-05-18 14:56:04 +01:00
. regs = {
. ptp_off = PTP_GMAC3_X_OFFSET ,
. mmc_off = MMC_GMAC3_X_OFFSET ,
} ,
2018-04-23 09:05:15 +01:00
. desc = NULL ,
. dma = & dwmac100_dma_ops ,
. mac = & dwmac100_ops ,
. hwtimestamp = & stmmac_ptp ,
. mode = NULL ,
2018-05-04 10:01:38 +01:00
. tc = NULL ,
2019-05-24 10:20:15 +02:00
. mmc = & dwmac_mmc_ops ,
2018-04-23 09:05:15 +01:00
. setup = dwmac100_setup ,
. quirks = stmmac_dwmac1_quirks ,
} , {
. gmac = true ,
. gmac4 = false ,
2018-08-08 09:04:29 +01:00
. xgmac = false ,
2018-04-23 09:05:15 +01:00
. min_id = 0 ,
2018-05-18 14:56:04 +01:00
. regs = {
. ptp_off = PTP_GMAC3_X_OFFSET ,
. mmc_off = MMC_GMAC3_X_OFFSET ,
} ,
2018-04-23 09:05:15 +01:00
. desc = NULL ,
. dma = & dwmac1000_dma_ops ,
. mac = & dwmac1000_ops ,
. hwtimestamp = & stmmac_ptp ,
. mode = NULL ,
2018-05-04 10:01:38 +01:00
. tc = NULL ,
2019-05-24 10:20:15 +02:00
. mmc = & dwmac_mmc_ops ,
2018-04-23 09:05:15 +01:00
. setup = dwmac1000_setup ,
. quirks = stmmac_dwmac1_quirks ,
} , {
. gmac = false ,
. gmac4 = true ,
2018-08-08 09:04:29 +01:00
. xgmac = false ,
2018-04-23 09:05:15 +01:00
. min_id = 0 ,
2018-05-18 14:56:04 +01:00
. regs = {
. ptp_off = PTP_GMAC4_OFFSET ,
. mmc_off = MMC_GMAC4_OFFSET ,
} ,
2018-04-23 09:05:15 +01:00
. desc = & dwmac4_desc_ops ,
. dma = & dwmac4_dma_ops ,
. mac = & dwmac4_ops ,
. hwtimestamp = & stmmac_ptp ,
. mode = NULL ,
2018-09-06 13:29:30 +01:00
. tc = & dwmac510_tc_ops ,
2019-05-24 10:20:15 +02:00
. mmc = & dwmac_mmc_ops ,
2018-04-23 09:05:15 +01:00
. setup = dwmac4_setup ,
. quirks = stmmac_dwmac4_quirks ,
} , {
. gmac = false ,
. gmac4 = true ,
2018-08-08 09:04:29 +01:00
. xgmac = false ,
2018-04-23 09:05:15 +01:00
. min_id = DWMAC_CORE_4_00 ,
2018-05-18 14:56:04 +01:00
. regs = {
. ptp_off = PTP_GMAC4_OFFSET ,
. mmc_off = MMC_GMAC4_OFFSET ,
} ,
2018-04-23 09:05:15 +01:00
. desc = & dwmac4_desc_ops ,
. dma = & dwmac4_dma_ops ,
. mac = & dwmac410_ops ,
. hwtimestamp = & stmmac_ptp ,
. mode = & dwmac4_ring_mode_ops ,
2018-09-06 13:29:30 +01:00
. tc = & dwmac510_tc_ops ,
2019-05-24 10:20:15 +02:00
. mmc = & dwmac_mmc_ops ,
2018-04-23 09:05:15 +01:00
. setup = dwmac4_setup ,
. quirks = NULL ,
} , {
. gmac = false ,
. gmac4 = true ,
2018-08-08 09:04:29 +01:00
. xgmac = false ,
2018-04-23 09:05:15 +01:00
. min_id = DWMAC_CORE_4_10 ,
2018-05-18 14:56:04 +01:00
. regs = {
. ptp_off = PTP_GMAC4_OFFSET ,
. mmc_off = MMC_GMAC4_OFFSET ,
} ,
2018-04-23 09:05:15 +01:00
. desc = & dwmac4_desc_ops ,
. dma = & dwmac410_dma_ops ,
. mac = & dwmac410_ops ,
. hwtimestamp = & stmmac_ptp ,
. mode = & dwmac4_ring_mode_ops ,
2018-09-06 13:29:30 +01:00
. tc = & dwmac510_tc_ops ,
2019-05-24 10:20:15 +02:00
. mmc = & dwmac_mmc_ops ,
2018-04-23 09:05:15 +01:00
. setup = dwmac4_setup ,
. quirks = NULL ,
} , {
. gmac = false ,
. gmac4 = true ,
2018-08-08 09:04:29 +01:00
. xgmac = false ,
2018-04-23 09:05:15 +01:00
. min_id = DWMAC_CORE_5_10 ,
2018-05-18 14:56:04 +01:00
. regs = {
. ptp_off = PTP_GMAC4_OFFSET ,
. mmc_off = MMC_GMAC4_OFFSET ,
} ,
2018-04-23 09:05:15 +01:00
. desc = & dwmac4_desc_ops ,
. dma = & dwmac410_dma_ops ,
. mac = & dwmac510_ops ,
. hwtimestamp = & stmmac_ptp ,
. mode = & dwmac4_ring_mode_ops ,
2018-05-04 10:01:38 +01:00
. tc = & dwmac510_tc_ops ,
2019-05-24 10:20:15 +02:00
. mmc = & dwmac_mmc_ops ,
2018-04-23 09:05:15 +01:00
. setup = dwmac4_setup ,
. quirks = NULL ,
2018-08-08 09:04:29 +01:00
} , {
. gmac = false ,
. gmac4 = false ,
. xgmac = true ,
. min_id = DWXGMAC_CORE_2_10 ,
2020-03-17 10:18:52 +01:00
. dev_id = DWXGMAC_ID ,
2018-08-08 09:04:29 +01:00
. regs = {
2018-08-08 09:04:34 +01:00
. ptp_off = PTP_XGMAC_OFFSET ,
2019-08-07 10:03:09 +02:00
. mmc_off = MMC_XGMAC_OFFSET ,
2018-08-08 09:04:29 +01:00
} ,
2018-08-08 09:04:32 +01:00
. desc = & dwxgmac210_desc_ops ,
2018-08-08 09:04:31 +01:00
. dma = & dwxgmac210_dma_ops ,
2018-08-08 09:04:30 +01:00
. mac = & dwxgmac210_ops ,
2018-08-08 09:04:34 +01:00
. hwtimestamp = & stmmac_ptp ,
2018-08-08 09:04:29 +01:00
. mode = NULL ,
2018-08-30 15:09:48 +01:00
. tc = & dwmac510_tc_ops ,
2019-08-07 10:03:09 +02:00
. mmc = & dwxgmac_mmc_ops ,
2018-08-08 09:04:30 +01:00
. setup = dwxgmac2_setup ,
2018-08-08 09:04:29 +01:00
. quirks = NULL ,
2020-03-17 10:18:52 +01:00
} , {
. gmac = false ,
. gmac4 = false ,
. xgmac = true ,
. min_id = DWXLGMAC_CORE_2_00 ,
. dev_id = DWXLGMAC_ID ,
. regs = {
. ptp_off = PTP_XGMAC_OFFSET ,
. mmc_off = MMC_XGMAC_OFFSET ,
} ,
. desc = & dwxgmac210_desc_ops ,
. dma = & dwxgmac210_dma_ops ,
. mac = & dwxlgmac2_ops ,
. hwtimestamp = & stmmac_ptp ,
. mode = NULL ,
. tc = & dwmac510_tc_ops ,
. mmc = & dwxgmac_mmc_ops ,
. setup = dwxlgmac2_setup ,
. quirks = stmmac_dwxlgmac_quirks ,
2018-08-08 09:04:29 +01:00
} ,
2018-04-23 09:05:15 +01:00
} ;
int stmmac_hwif_init ( struct stmmac_priv * priv )
{
2018-08-08 09:04:29 +01:00
bool needs_xgmac = priv - > plat - > has_xgmac ;
2018-04-23 09:05:15 +01:00
bool needs_gmac4 = priv - > plat - > has_gmac4 ;
bool needs_gmac = priv - > plat - > has_gmac ;
const struct stmmac_hwif_entry * entry ;
struct mac_device_info * mac ;
2018-05-18 16:54:38 +01:00
bool needs_setup = true ;
2020-03-17 10:18:52 +01:00
u32 id , dev_id = 0 ;
2018-04-23 09:05:15 +01:00
int i , ret ;
if ( needs_gmac ) {
id = stmmac_get_id ( priv , GMAC_VERSION ) ;
2018-08-08 09:04:29 +01:00
} else if ( needs_gmac4 | | needs_xgmac ) {
2018-04-23 09:05:15 +01:00
id = stmmac_get_id ( priv , GMAC4_VERSION ) ;
2020-03-17 10:18:52 +01:00
if ( needs_xgmac )
dev_id = stmmac_get_dev_id ( priv , GMAC4_VERSION ) ;
2018-05-18 16:54:38 +01:00
} else {
id = 0 ;
2018-04-23 09:05:15 +01:00
}
/* Save ID for later use */
priv - > synopsys_id = id ;
2018-05-18 14:56:04 +01:00
/* Lets assume some safe values first */
priv - > ptpaddr = priv - > ioaddr +
( needs_gmac4 ? PTP_GMAC4_OFFSET : PTP_GMAC3_X_OFFSET ) ;
priv - > mmcaddr = priv - > ioaddr +
( needs_gmac4 ? MMC_GMAC4_OFFSET : MMC_GMAC3_X_OFFSET ) ;
2018-04-23 09:05:15 +01:00
/* Check for HW specific setup first */
if ( priv - > plat - > setup ) {
2018-05-18 16:54:38 +01:00
mac = priv - > plat - > setup ( priv ) ;
needs_setup = false ;
} else {
mac = devm_kzalloc ( priv - > device , sizeof ( * mac ) , GFP_KERNEL ) ;
2018-04-23 09:05:15 +01:00
}
if ( ! mac )
return - ENOMEM ;
/* Fallback to generic HW */
for ( i = ARRAY_SIZE ( stmmac_hw ) - 1 ; i > = 0 ; i - - ) {
entry = & stmmac_hw [ i ] ;
if ( needs_gmac ^ entry - > gmac )
continue ;
if ( needs_gmac4 ^ entry - > gmac4 )
continue ;
2018-08-08 09:04:29 +01:00
if ( needs_xgmac ^ entry - > xgmac )
continue ;
2018-05-18 16:54:38 +01:00
/* Use synopsys_id var because some setups can override this */
if ( priv - > synopsys_id < entry - > min_id )
2018-04-23 09:05:15 +01:00
continue ;
2020-03-17 10:18:52 +01:00
if ( needs_xgmac & & ( dev_id ^ entry - > dev_id ) )
continue ;
2018-04-23 09:05:15 +01:00
2018-05-18 16:54:38 +01:00
/* Only use generic HW helpers if needed */
mac - > desc = mac - > desc ? : entry - > desc ;
mac - > dma = mac - > dma ? : entry - > dma ;
mac - > mac = mac - > mac ? : entry - > mac ;
mac - > ptp = mac - > ptp ? : entry - > hwtimestamp ;
mac - > mode = mac - > mode ? : entry - > mode ;
mac - > tc = mac - > tc ? : entry - > tc ;
2019-05-24 10:20:15 +02:00
mac - > mmc = mac - > mmc ? : entry - > mmc ;
2018-04-23 09:05:15 +01:00
priv - > hw = mac ;
2018-05-18 14:56:04 +01:00
priv - > ptpaddr = priv - > ioaddr + entry - > regs . ptp_off ;
priv - > mmcaddr = priv - > ioaddr + entry - > regs . mmc_off ;
2018-04-23 09:05:15 +01:00
/* Entry found */
2018-05-18 16:54:38 +01:00
if ( needs_setup ) {
ret = entry - > setup ( priv ) ;
if ( ret )
return ret ;
}
2018-04-23 09:05:15 +01:00
2018-06-15 16:17:27 +01:00
/* Save quirks, if needed for posterior use */
priv - > hwif_quirks = entry - > quirks ;
2018-04-23 09:05:15 +01:00
return 0 ;
}
dev_err ( priv - > device , " Failed to find HW IF (id=0x%x, gmac=%d/%d) \n " ,
id , needs_gmac , needs_gmac4 ) ;
return - EINVAL ;
}