2018-03-29 10:40:19 +01:00
// SPDX-License-Identifier: (GPL-2.0 OR MIT)
// Copyright (c) 2017 Synopsys, Inc. and/or its affiliates.
// stmmac Support for 5.xx Ethernet QoS cores
# include <linux/bitops.h>
# include <linux/iopoll.h>
# include "common.h"
# include "dwmac4.h"
# include "dwmac5.h"
2018-05-04 10:01:38 +01:00
# include "stmmac.h"
2018-05-31 18:01:27 +01:00
# include "stmmac_ptp.h"
2018-03-29 10:40:19 +01:00
struct dwmac5_error_desc {
bool valid ;
const char * desc ;
const char * detailed_desc ;
} ;
# define STAT_OFF(field) offsetof(struct stmmac_safety_stats, field)
static void dwmac5_log_error ( struct net_device * ndev , u32 value , bool corr ,
const char * module_name , const struct dwmac5_error_desc * desc ,
unsigned long field_offset , struct stmmac_safety_stats * stats )
{
unsigned long loc , mask ;
u8 * bptr = ( u8 * ) stats ;
unsigned long * ptr ;
ptr = ( unsigned long * ) ( bptr + field_offset ) ;
mask = value ;
for_each_set_bit ( loc , & mask , 32 ) {
netdev_err ( ndev , " Found %s error in %s: '%s: %s' \n " , corr ?
" correctable " : " uncorrectable " , module_name ,
desc [ loc ] . desc , desc [ loc ] . detailed_desc ) ;
/* Update counters */
ptr [ loc ] + + ;
}
}
static const struct dwmac5_error_desc dwmac5_mac_errors [ 32 ] = {
{ true , " ATPES " , " Application Transmit Interface Parity Check Error " } ,
{ true , " TPES " , " TSO Data Path Parity Check Error " } ,
{ true , " RDPES " , " Read Descriptor Parity Check Error " } ,
{ true , " MPES " , " MTL Data Path Parity Check Error " } ,
{ true , " MTSPES " , " MTL TX Status Data Path Parity Check Error " } ,
{ true , " ARPES " , " Application Receive Interface Data Path Parity Check Error " } ,
{ true , " CWPES " , " CSR Write Data Path Parity Check Error " } ,
{ true , " ASRPES " , " AXI Slave Read Data Path Parity Check Error " } ,
{ true , " TTES " , " TX FSM Timeout Error " } ,
{ true , " RTES " , " RX FSM Timeout Error " } ,
{ true , " CTES " , " CSR FSM Timeout Error " } ,
{ true , " ATES " , " APP FSM Timeout Error " } ,
{ true , " PTES " , " PTP FSM Timeout Error " } ,
{ true , " T125ES " , " TX125 FSM Timeout Error " } ,
{ true , " R125ES " , " RX125 FSM Timeout Error " } ,
{ true , " RVCTES " , " REV MDC FSM Timeout Error " } ,
{ true , " MSTTES " , " Master Read/Write Timeout Error " } ,
{ true , " SLVTES " , " Slave Read/Write Timeout Error " } ,
{ true , " ATITES " , " Application Timeout on ATI Interface Error " } ,
{ true , " ARITES " , " Application Timeout on ARI Interface Error " } ,
{ false , " UNKNOWN " , " Unknown Error " } , /* 20 */
{ false , " UNKNOWN " , " Unknown Error " } , /* 21 */
{ false , " UNKNOWN " , " Unknown Error " } , /* 22 */
{ false , " UNKNOWN " , " Unknown Error " } , /* 23 */
{ true , " FSMPES " , " FSM State Parity Error " } ,
{ false , " UNKNOWN " , " Unknown Error " } , /* 25 */
{ false , " UNKNOWN " , " Unknown Error " } , /* 26 */
{ false , " UNKNOWN " , " Unknown Error " } , /* 27 */
{ false , " UNKNOWN " , " Unknown Error " } , /* 28 */
{ false , " UNKNOWN " , " Unknown Error " } , /* 29 */
{ false , " UNKNOWN " , " Unknown Error " } , /* 30 */
{ false , " UNKNOWN " , " Unknown Error " } , /* 31 */
} ;
static void dwmac5_handle_mac_err ( struct net_device * ndev ,
void __iomem * ioaddr , bool correctable ,
struct stmmac_safety_stats * stats )
{
u32 value ;
value = readl ( ioaddr + MAC_DPP_FSM_INT_STATUS ) ;
writel ( value , ioaddr + MAC_DPP_FSM_INT_STATUS ) ;
dwmac5_log_error ( ndev , value , correctable , " MAC " , dwmac5_mac_errors ,
STAT_OFF ( mac_errors ) , stats ) ;
}
static const struct dwmac5_error_desc dwmac5_mtl_errors [ 32 ] = {
{ true , " TXCES " , " MTL TX Memory Error " } ,
{ true , " TXAMS " , " MTL TX Memory Address Mismatch Error " } ,
{ true , " TXUES " , " MTL TX Memory Error " } ,
{ false , " UNKNOWN " , " Unknown Error " } , /* 3 */
{ true , " RXCES " , " MTL RX Memory Error " } ,
{ true , " RXAMS " , " MTL RX Memory Address Mismatch Error " } ,
{ true , " RXUES " , " MTL RX Memory Error " } ,
{ false , " UNKNOWN " , " Unknown Error " } , /* 7 */
{ true , " ECES " , " MTL EST Memory Error " } ,
{ true , " EAMS " , " MTL EST Memory Address Mismatch Error " } ,
{ true , " EUES " , " MTL EST Memory Error " } ,
{ false , " UNKNOWN " , " Unknown Error " } , /* 11 */
{ true , " RPCES " , " MTL RX Parser Memory Error " } ,
{ true , " RPAMS " , " MTL RX Parser Memory Address Mismatch Error " } ,
{ true , " RPUES " , " MTL RX Parser Memory Error " } ,
{ false , " UNKNOWN " , " Unknown Error " } , /* 15 */
{ false , " UNKNOWN " , " Unknown Error " } , /* 16 */
{ false , " UNKNOWN " , " Unknown Error " } , /* 17 */
{ false , " UNKNOWN " , " Unknown Error " } , /* 18 */
{ false , " UNKNOWN " , " Unknown Error " } , /* 19 */
{ false , " UNKNOWN " , " Unknown Error " } , /* 20 */
{ false , " UNKNOWN " , " Unknown Error " } , /* 21 */
{ false , " UNKNOWN " , " Unknown Error " } , /* 22 */
{ false , " UNKNOWN " , " Unknown Error " } , /* 23 */
{ false , " UNKNOWN " , " Unknown Error " } , /* 24 */
{ false , " UNKNOWN " , " Unknown Error " } , /* 25 */
{ false , " UNKNOWN " , " Unknown Error " } , /* 26 */
{ false , " UNKNOWN " , " Unknown Error " } , /* 27 */
{ false , " UNKNOWN " , " Unknown Error " } , /* 28 */
{ false , " UNKNOWN " , " Unknown Error " } , /* 29 */
{ false , " UNKNOWN " , " Unknown Error " } , /* 30 */
{ false , " UNKNOWN " , " Unknown Error " } , /* 31 */
} ;
static void dwmac5_handle_mtl_err ( struct net_device * ndev ,
void __iomem * ioaddr , bool correctable ,
struct stmmac_safety_stats * stats )
{
u32 value ;
value = readl ( ioaddr + MTL_ECC_INT_STATUS ) ;
writel ( value , ioaddr + MTL_ECC_INT_STATUS ) ;
dwmac5_log_error ( ndev , value , correctable , " MTL " , dwmac5_mtl_errors ,
STAT_OFF ( mtl_errors ) , stats ) ;
}
static const struct dwmac5_error_desc dwmac5_dma_errors [ 32 ] = {
{ true , " TCES " , " DMA TSO Memory Error " } ,
{ true , " TAMS " , " DMA TSO Memory Address Mismatch Error " } ,
{ true , " TUES " , " DMA TSO Memory Error " } ,
{ false , " UNKNOWN " , " Unknown Error " } , /* 3 */
{ false , " UNKNOWN " , " Unknown Error " } , /* 4 */
{ false , " UNKNOWN " , " Unknown Error " } , /* 5 */
{ false , " UNKNOWN " , " Unknown Error " } , /* 6 */
{ false , " UNKNOWN " , " Unknown Error " } , /* 7 */
{ false , " UNKNOWN " , " Unknown Error " } , /* 8 */
{ false , " UNKNOWN " , " Unknown Error " } , /* 9 */
{ false , " UNKNOWN " , " Unknown Error " } , /* 10 */
{ false , " UNKNOWN " , " Unknown Error " } , /* 11 */
{ false , " UNKNOWN " , " Unknown Error " } , /* 12 */
{ false , " UNKNOWN " , " Unknown Error " } , /* 13 */
{ false , " UNKNOWN " , " Unknown Error " } , /* 14 */
{ false , " UNKNOWN " , " Unknown Error " } , /* 15 */
{ false , " UNKNOWN " , " Unknown Error " } , /* 16 */
{ false , " UNKNOWN " , " Unknown Error " } , /* 17 */
{ false , " UNKNOWN " , " Unknown Error " } , /* 18 */
{ false , " UNKNOWN " , " Unknown Error " } , /* 19 */
{ false , " UNKNOWN " , " Unknown Error " } , /* 20 */
{ false , " UNKNOWN " , " Unknown Error " } , /* 21 */
{ false , " UNKNOWN " , " Unknown Error " } , /* 22 */
{ false , " UNKNOWN " , " Unknown Error " } , /* 23 */
{ false , " UNKNOWN " , " Unknown Error " } , /* 24 */
{ false , " UNKNOWN " , " Unknown Error " } , /* 25 */
{ false , " UNKNOWN " , " Unknown Error " } , /* 26 */
{ false , " UNKNOWN " , " Unknown Error " } , /* 27 */
{ false , " UNKNOWN " , " Unknown Error " } , /* 28 */
{ false , " UNKNOWN " , " Unknown Error " } , /* 29 */
{ false , " UNKNOWN " , " Unknown Error " } , /* 30 */
{ false , " UNKNOWN " , " Unknown Error " } , /* 31 */
} ;
static void dwmac5_handle_dma_err ( struct net_device * ndev ,
void __iomem * ioaddr , bool correctable ,
struct stmmac_safety_stats * stats )
{
u32 value ;
value = readl ( ioaddr + DMA_ECC_INT_STATUS ) ;
writel ( value , ioaddr + DMA_ECC_INT_STATUS ) ;
dwmac5_log_error ( ndev , value , correctable , " DMA " , dwmac5_dma_errors ,
STAT_OFF ( dma_errors ) , stats ) ;
}
int dwmac5_safety_feat_config ( void __iomem * ioaddr , unsigned int asp )
{
u32 value ;
if ( ! asp )
return - EINVAL ;
/* 1. Enable Safety Features */
value = readl ( ioaddr + MTL_ECC_CONTROL ) ;
value | = TSOEE ; /* TSO ECC */
value | = MRXPEE ; /* MTL RX Parser ECC */
value | = MESTEE ; /* MTL EST ECC */
value | = MRXEE ; /* MTL RX FIFO ECC */
value | = MTXEE ; /* MTL TX FIFO ECC */
writel ( value , ioaddr + MTL_ECC_CONTROL ) ;
/* 2. Enable MTL Safety Interrupts */
value = readl ( ioaddr + MTL_ECC_INT_ENABLE ) ;
value | = RPCEIE ; /* RX Parser Memory Correctable Error */
value | = ECEIE ; /* EST Memory Correctable Error */
value | = RXCEIE ; /* RX Memory Correctable Error */
value | = TXCEIE ; /* TX Memory Correctable Error */
writel ( value , ioaddr + MTL_ECC_INT_ENABLE ) ;
/* 3. Enable DMA Safety Interrupts */
value = readl ( ioaddr + DMA_ECC_INT_ENABLE ) ;
value | = TCEIE ; /* TSO Memory Correctable Error */
writel ( value , ioaddr + DMA_ECC_INT_ENABLE ) ;
/* Only ECC Protection for External Memory feature is selected */
if ( asp < = 0x1 )
return 0 ;
/* 5. Enable Parity and Timeout for FSM */
value = readl ( ioaddr + MAC_FSM_CONTROL ) ;
value | = PRTYEN ; /* FSM Parity Feature */
value | = TMOUTEN ; /* FSM Timeout Feature */
writel ( value , ioaddr + MAC_FSM_CONTROL ) ;
/* 4. Enable Data Parity Protection */
value = readl ( ioaddr + MTL_DPP_CONTROL ) ;
value | = EDPP ;
writel ( value , ioaddr + MTL_DPP_CONTROL ) ;
/*
* All the Automotive Safety features are selected without the " Parity
* Port Enable for external interface " feature.
*/
if ( asp < = 0x2 )
return 0 ;
value | = EPSI ;
writel ( value , ioaddr + MTL_DPP_CONTROL ) ;
return 0 ;
}
2018-04-16 16:08:14 +01:00
int dwmac5_safety_feat_irq_status ( struct net_device * ndev ,
2018-03-29 10:40:19 +01:00
void __iomem * ioaddr , unsigned int asp ,
struct stmmac_safety_stats * stats )
{
2018-04-16 16:08:14 +01:00
bool err , corr ;
2018-03-29 10:40:19 +01:00
u32 mtl , dma ;
2018-04-16 16:08:14 +01:00
int ret = 0 ;
2018-03-29 10:40:19 +01:00
if ( ! asp )
2018-04-16 16:08:14 +01:00
return - EINVAL ;
2018-03-29 10:40:19 +01:00
mtl = readl ( ioaddr + MTL_SAFETY_INT_STATUS ) ;
dma = readl ( ioaddr + DMA_SAFETY_INT_STATUS ) ;
err = ( mtl & MCSIS ) | | ( dma & MCSIS ) ;
corr = false ;
if ( err ) {
dwmac5_handle_mac_err ( ndev , ioaddr , corr , stats ) ;
ret | = ! corr ;
}
err = ( mtl & ( MEUIS | MECIS ) ) | | ( dma & ( MSUIS | MSCIS ) ) ;
corr = ( mtl & MECIS ) | | ( dma & MSCIS ) ;
if ( err ) {
dwmac5_handle_mtl_err ( ndev , ioaddr , corr , stats ) ;
ret | = ! corr ;
}
err = dma & ( DEUIS | DECIS ) ;
corr = dma & DECIS ;
if ( err ) {
dwmac5_handle_dma_err ( ndev , ioaddr , corr , stats ) ;
ret | = ! corr ;
}
return ret ;
}
static const struct dwmac5_error {
const struct dwmac5_error_desc * desc ;
} dwmac5_all_errors [ ] = {
{ dwmac5_mac_errors } ,
{ dwmac5_mtl_errors } ,
{ dwmac5_dma_errors } ,
} ;
2018-04-16 16:08:14 +01:00
int dwmac5_safety_feat_dump ( struct stmmac_safety_stats * stats ,
int index , unsigned long * count , const char * * desc )
2018-03-29 10:40:19 +01:00
{
int module = index / 32 , offset = index % 32 ;
unsigned long * ptr = ( unsigned long * ) stats ;
if ( module > = ARRAY_SIZE ( dwmac5_all_errors ) )
2018-04-16 16:08:14 +01:00
return - EINVAL ;
2018-03-29 10:40:19 +01:00
if ( ! dwmac5_all_errors [ module ] . desc [ offset ] . valid )
2018-04-16 16:08:14 +01:00
return - EINVAL ;
2018-03-29 10:40:19 +01:00
if ( count )
* count = * ( ptr + index ) ;
2018-04-16 16:08:14 +01:00
if ( desc )
* desc = dwmac5_all_errors [ module ] . desc [ offset ] . desc ;
return 0 ;
2018-03-29 10:40:19 +01:00
}
2018-05-04 10:01:38 +01:00
static int dwmac5_rxp_disable ( void __iomem * ioaddr )
{
u32 val ;
int ret ;
val = readl ( ioaddr + MTL_OPERATION_MODE ) ;
val & = ~ MTL_FRPE ;
writel ( val , ioaddr + MTL_OPERATION_MODE ) ;
ret = readl_poll_timeout ( ioaddr + MTL_RXP_CONTROL_STATUS , val ,
val & RXPI , 1 , 10000 ) ;
if ( ret )
return ret ;
return 0 ;
}
static void dwmac5_rxp_enable ( void __iomem * ioaddr )
{
u32 val ;
val = readl ( ioaddr + MTL_OPERATION_MODE ) ;
val | = MTL_FRPE ;
writel ( val , ioaddr + MTL_OPERATION_MODE ) ;
}
static int dwmac5_rxp_update_single_entry ( void __iomem * ioaddr ,
struct stmmac_tc_entry * entry ,
int pos )
{
int ret , i ;
for ( i = 0 ; i < ( sizeof ( entry - > val ) / sizeof ( u32 ) ) ; i + + ) {
int real_pos = pos * ( sizeof ( entry - > val ) / sizeof ( u32 ) ) + i ;
u32 val ;
/* Wait for ready */
ret = readl_poll_timeout ( ioaddr + MTL_RXP_IACC_CTRL_STATUS ,
val , ! ( val & STARTBUSY ) , 1 , 10000 ) ;
if ( ret )
return ret ;
/* Write data */
val = * ( ( u32 * ) & entry - > val + i ) ;
writel ( val , ioaddr + MTL_RXP_IACC_DATA ) ;
/* Write pos */
val = real_pos & ADDR ;
writel ( val , ioaddr + MTL_RXP_IACC_CTRL_STATUS ) ;
/* Write OP */
val | = WRRDN ;
writel ( val , ioaddr + MTL_RXP_IACC_CTRL_STATUS ) ;
/* Start Write */
val | = STARTBUSY ;
writel ( val , ioaddr + MTL_RXP_IACC_CTRL_STATUS ) ;
/* Wait for done */
ret = readl_poll_timeout ( ioaddr + MTL_RXP_IACC_CTRL_STATUS ,
val , ! ( val & STARTBUSY ) , 1 , 10000 ) ;
if ( ret )
return ret ;
}
return 0 ;
}
static struct stmmac_tc_entry *
dwmac5_rxp_get_next_entry ( struct stmmac_tc_entry * entries , unsigned int count ,
u32 curr_prio )
{
struct stmmac_tc_entry * entry ;
u32 min_prio = ~ 0x0 ;
int i , min_prio_idx ;
bool found = false ;
for ( i = count - 1 ; i > = 0 ; i - - ) {
entry = & entries [ i ] ;
/* Do not update unused entries */
if ( ! entry - > in_use )
continue ;
/* Do not update already updated entries (i.e. fragments) */
if ( entry - > in_hw )
continue ;
/* Let last entry be updated last */
if ( entry - > is_last )
continue ;
/* Do not return fragments */
if ( entry - > is_frag )
continue ;
/* Check if we already checked this prio */
if ( entry - > prio < curr_prio )
continue ;
/* Check if this is the minimum prio */
if ( entry - > prio < min_prio ) {
min_prio = entry - > prio ;
min_prio_idx = i ;
found = true ;
}
}
if ( found )
return & entries [ min_prio_idx ] ;
return NULL ;
}
int dwmac5_rxp_config ( void __iomem * ioaddr , struct stmmac_tc_entry * entries ,
unsigned int count )
{
struct stmmac_tc_entry * entry , * frag ;
int i , ret , nve = 0 ;
u32 curr_prio = 0 ;
u32 old_val , val ;
/* Force disable RX */
old_val = readl ( ioaddr + GMAC_CONFIG ) ;
val = old_val & ~ GMAC_CONFIG_RE ;
writel ( val , ioaddr + GMAC_CONFIG ) ;
/* Disable RX Parser */
ret = dwmac5_rxp_disable ( ioaddr ) ;
if ( ret )
goto re_enable ;
/* Set all entries as NOT in HW */
for ( i = 0 ; i < count ; i + + ) {
entry = & entries [ i ] ;
entry - > in_hw = false ;
}
/* Update entries by reverse order */
while ( 1 ) {
entry = dwmac5_rxp_get_next_entry ( entries , count , curr_prio ) ;
if ( ! entry )
break ;
curr_prio = entry - > prio ;
frag = entry - > frag_ptr ;
/* Set special fragment requirements */
if ( frag ) {
entry - > val . af = 0 ;
entry - > val . rf = 0 ;
entry - > val . nc = 1 ;
entry - > val . ok_index = nve + 2 ;
}
ret = dwmac5_rxp_update_single_entry ( ioaddr , entry , nve ) ;
if ( ret )
goto re_enable ;
entry - > table_pos = nve + + ;
entry - > in_hw = true ;
if ( frag & & ! frag - > in_hw ) {
ret = dwmac5_rxp_update_single_entry ( ioaddr , frag , nve ) ;
if ( ret )
goto re_enable ;
frag - > table_pos = nve + + ;
frag - > in_hw = true ;
}
}
if ( ! nve )
goto re_enable ;
/* Update all pass entry */
for ( i = 0 ; i < count ; i + + ) {
entry = & entries [ i ] ;
if ( ! entry - > is_last )
continue ;
ret = dwmac5_rxp_update_single_entry ( ioaddr , entry , nve ) ;
if ( ret )
goto re_enable ;
entry - > table_pos = nve + + ;
}
/* Assume n. of parsable entries == n. of valid entries */
val = ( nve < < 16 ) & NPE ;
val | = nve & NVE ;
writel ( val , ioaddr + MTL_RXP_CONTROL_STATUS ) ;
/* Enable RX Parser */
dwmac5_rxp_enable ( ioaddr ) ;
re_enable :
/* Re-enable RX */
writel ( old_val , ioaddr + GMAC_CONFIG ) ;
return ret ;
}
2018-05-31 18:01:27 +01:00
int dwmac5_flex_pps_config ( void __iomem * ioaddr , int index ,
struct stmmac_pps_cfg * cfg , bool enable ,
u32 sub_second_inc , u32 systime_flags )
{
u32 tnsec = readl ( ioaddr + MAC_PPSx_TARGET_TIME_NSEC ( index ) ) ;
u32 val = readl ( ioaddr + MAC_PPS_CONTROL ) ;
u64 period ;
if ( ! cfg - > available )
return - EINVAL ;
if ( tnsec & TRGTBUSY0 )
return - EBUSY ;
if ( ! sub_second_inc | | ! systime_flags )
return - EINVAL ;
val & = ~ PPSx_MASK ( index ) ;
if ( ! enable ) {
val | = PPSCMDx ( index , 0x5 ) ;
writel ( val , ioaddr + MAC_PPS_CONTROL ) ;
return 0 ;
}
val | = PPSCMDx ( index , 0x2 ) ;
val | = TRGTMODSELx ( index , 0x2 ) ;
val | = PPSEN0 ;
writel ( cfg - > start . tv_sec , ioaddr + MAC_PPSx_TARGET_TIME_SEC ( index ) ) ;
if ( ! ( systime_flags & PTP_TCR_TSCTRLSSR ) )
cfg - > start . tv_nsec = ( cfg - > start . tv_nsec * 1000 ) / 465 ;
writel ( cfg - > start . tv_nsec , ioaddr + MAC_PPSx_TARGET_TIME_NSEC ( index ) ) ;
period = cfg - > period . tv_sec * 1000000000 ;
period + = cfg - > period . tv_nsec ;
do_div ( period , sub_second_inc ) ;
if ( period < = 1 )
return - EINVAL ;
writel ( period - 1 , ioaddr + MAC_PPSx_INTERVAL ( index ) ) ;
period > > = 1 ;
if ( period < = 1 )
return - EINVAL ;
writel ( period - 1 , ioaddr + MAC_PPSx_WIDTH ( index ) ) ;
/* Finally, activate it */
writel ( val , ioaddr + MAC_PPS_CONTROL ) ;
return 0 ;
}