2019-06-04 10:11:33 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2014-03-25 12:10:54 -07:00
/* 10G controller driver for Samsung SoCs
*
* Copyright ( C ) 2013 Samsung Electronics Co . , Ltd .
* http : //www.samsung.com
*
* Author : Siva Reddy Kallam < siva . kallam @ samsung . com >
*/
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2014-03-25 12:11:02 -07:00
# include <linux/clk.h>
# include <linux/interrupt.h>
2014-03-25 12:10:54 -07:00
# include <linux/kernel.h>
# include <linux/netdevice.h>
2014-03-25 12:11:02 -07:00
# include <linux/net_tstamp.h>
2014-03-25 12:10:54 -07:00
# include <linux/phy.h>
2014-03-25 12:11:02 -07:00
# include <linux/ptp_clock_kernel.h>
2014-03-25 12:10:54 -07:00
# include "sxgbe_common.h"
2014-03-25 12:11:02 -07:00
# include "sxgbe_reg.h"
# include "sxgbe_dma.h"
2014-03-25 12:10:54 -07:00
struct sxgbe_stats {
char stat_string [ ETH_GSTRING_LEN ] ;
int sizeof_stat ;
int stat_offset ;
} ;
# define SXGBE_STAT(m) \
{ \
# m, \
2019-12-09 10:31:43 -08:00
sizeof_field ( struct sxgbe_extra_stats , m ) , \
2014-03-25 12:10:54 -07:00
offsetof ( struct sxgbe_priv_data , xstats . m ) \
}
static const struct sxgbe_stats sxgbe_gstrings_stats [ ] = {
2014-03-25 12:11:02 -07:00
/* TX/RX IRQ events */
SXGBE_STAT ( tx_process_stopped_irq ) ,
SXGBE_STAT ( tx_ctxt_desc_err ) ,
SXGBE_STAT ( tx_threshold ) ,
SXGBE_STAT ( rx_threshold ) ,
SXGBE_STAT ( tx_pkt_n ) ,
SXGBE_STAT ( rx_pkt_n ) ,
SXGBE_STAT ( normal_irq_n ) ,
SXGBE_STAT ( tx_normal_irq_n ) ,
SXGBE_STAT ( rx_normal_irq_n ) ,
SXGBE_STAT ( napi_poll ) ,
SXGBE_STAT ( tx_clean ) ,
SXGBE_STAT ( tx_reset_ic_bit ) ,
SXGBE_STAT ( rx_process_stopped_irq ) ,
SXGBE_STAT ( rx_underflow_irq ) ,
/* Bus access errors */
SXGBE_STAT ( fatal_bus_error_irq ) ,
SXGBE_STAT ( tx_read_transfer_err ) ,
SXGBE_STAT ( tx_write_transfer_err ) ,
SXGBE_STAT ( tx_desc_access_err ) ,
SXGBE_STAT ( tx_buffer_access_err ) ,
SXGBE_STAT ( tx_data_transfer_err ) ,
SXGBE_STAT ( rx_read_transfer_err ) ,
SXGBE_STAT ( rx_write_transfer_err ) ,
SXGBE_STAT ( rx_desc_access_err ) ,
SXGBE_STAT ( rx_buffer_access_err ) ,
SXGBE_STAT ( rx_data_transfer_err ) ,
/* EEE-LPI stats */
2014-03-25 12:10:57 -07:00
SXGBE_STAT ( tx_lpi_entry_n ) ,
SXGBE_STAT ( tx_lpi_exit_n ) ,
SXGBE_STAT ( rx_lpi_entry_n ) ,
SXGBE_STAT ( rx_lpi_exit_n ) ,
SXGBE_STAT ( eee_wakeup_error_n ) ,
2014-03-25 12:11:02 -07:00
/* RX specific */
/* L2 error */
SXGBE_STAT ( rx_code_gmii_err ) ,
SXGBE_STAT ( rx_watchdog_err ) ,
SXGBE_STAT ( rx_crc_err ) ,
SXGBE_STAT ( rx_gaint_pkt_err ) ,
SXGBE_STAT ( ip_hdr_err ) ,
SXGBE_STAT ( ip_payload_err ) ,
SXGBE_STAT ( overflow_error ) ,
/* L2 Pkt type */
SXGBE_STAT ( len_pkt ) ,
SXGBE_STAT ( mac_ctl_pkt ) ,
SXGBE_STAT ( dcb_ctl_pkt ) ,
SXGBE_STAT ( arp_pkt ) ,
SXGBE_STAT ( oam_pkt ) ,
SXGBE_STAT ( untag_okt ) ,
SXGBE_STAT ( other_pkt ) ,
SXGBE_STAT ( svlan_tag_pkt ) ,
SXGBE_STAT ( cvlan_tag_pkt ) ,
SXGBE_STAT ( dvlan_ocvlan_icvlan_pkt ) ,
SXGBE_STAT ( dvlan_osvlan_isvlan_pkt ) ,
SXGBE_STAT ( dvlan_osvlan_icvlan_pkt ) ,
SXGBE_STAT ( dvan_ocvlan_icvlan_pkt ) ,
/* L3/L4 Pkt type */
SXGBE_STAT ( not_ip_pkt ) ,
SXGBE_STAT ( ip4_tcp_pkt ) ,
SXGBE_STAT ( ip4_udp_pkt ) ,
SXGBE_STAT ( ip4_icmp_pkt ) ,
SXGBE_STAT ( ip4_unknown_pkt ) ,
SXGBE_STAT ( ip6_tcp_pkt ) ,
SXGBE_STAT ( ip6_udp_pkt ) ,
SXGBE_STAT ( ip6_icmp_pkt ) ,
SXGBE_STAT ( ip6_unknown_pkt ) ,
/* Filter specific */
SXGBE_STAT ( vlan_filter_match ) ,
SXGBE_STAT ( sa_filter_fail ) ,
SXGBE_STAT ( da_filter_fail ) ,
SXGBE_STAT ( hash_filter_pass ) ,
SXGBE_STAT ( l3_filter_match ) ,
SXGBE_STAT ( l4_filter_match ) ,
/* RX context specific */
SXGBE_STAT ( timestamp_dropped ) ,
SXGBE_STAT ( rx_msg_type_no_ptp ) ,
SXGBE_STAT ( rx_ptp_type_sync ) ,
SXGBE_STAT ( rx_ptp_type_follow_up ) ,
SXGBE_STAT ( rx_ptp_type_delay_req ) ,
SXGBE_STAT ( rx_ptp_type_delay_resp ) ,
SXGBE_STAT ( rx_ptp_type_pdelay_req ) ,
SXGBE_STAT ( rx_ptp_type_pdelay_resp ) ,
SXGBE_STAT ( rx_ptp_type_pdelay_follow_up ) ,
SXGBE_STAT ( rx_ptp_announce ) ,
SXGBE_STAT ( rx_ptp_mgmt ) ,
SXGBE_STAT ( rx_ptp_signal ) ,
SXGBE_STAT ( rx_ptp_resv_msg_type ) ,
2014-03-25 12:10:54 -07:00
} ;
# define SXGBE_STATS_LEN ARRAY_SIZE(sxgbe_gstrings_stats)
2014-03-25 12:10:57 -07:00
static int sxgbe_get_eee ( struct net_device * dev ,
struct ethtool_eee * edata )
{
struct sxgbe_priv_data * priv = netdev_priv ( dev ) ;
if ( ! priv - > hw_cap . eee )
return - EOPNOTSUPP ;
edata - > eee_enabled = priv - > eee_enabled ;
edata - > eee_active = priv - > eee_active ;
edata - > tx_lpi_timer = priv - > tx_lpi_timer ;
2016-06-25 22:05:26 +02:00
return phy_ethtool_get_eee ( dev - > phydev , edata ) ;
2014-03-25 12:10:57 -07:00
}
static int sxgbe_set_eee ( struct net_device * dev ,
struct ethtool_eee * edata )
{
struct sxgbe_priv_data * priv = netdev_priv ( dev ) ;
priv - > eee_enabled = edata - > eee_enabled ;
if ( ! priv - > eee_enabled ) {
sxgbe_disable_eee_mode ( priv ) ;
} else {
/* We are asking for enabling the EEE but it is safe
* to verify all by invoking the eee_init function .
* In case of failure it will return an error .
*/
priv - > eee_enabled = sxgbe_eee_init ( priv ) ;
if ( ! priv - > eee_enabled )
return - EOPNOTSUPP ;
/* Do not change tx_lpi_timer in case of failure */
priv - > tx_lpi_timer = edata - > tx_lpi_timer ;
}
2016-06-25 22:05:26 +02:00
return phy_ethtool_set_eee ( dev - > phydev , edata ) ;
2014-03-25 12:10:57 -07:00
}
2014-03-25 12:11:02 -07:00
static void sxgbe_getdrvinfo ( struct net_device * dev ,
struct ethtool_drvinfo * info )
{
strlcpy ( info - > driver , KBUILD_MODNAME , sizeof ( info - > driver ) ) ;
strlcpy ( info - > version , DRV_VERSION , sizeof ( info - > version ) ) ;
}
static u32 sxgbe_getmsglevel ( struct net_device * dev )
{
struct sxgbe_priv_data * priv = netdev_priv ( dev ) ;
return priv - > msg_enable ;
}
static void sxgbe_setmsglevel ( struct net_device * dev , u32 level )
{
struct sxgbe_priv_data * priv = netdev_priv ( dev ) ;
priv - > msg_enable = level ;
}
static void sxgbe_get_strings ( struct net_device * dev , u32 stringset , u8 * data )
{
int i ;
u8 * p = data ;
switch ( stringset ) {
case ETH_SS_STATS :
for ( i = 0 ; i < SXGBE_STATS_LEN ; i + + ) {
memcpy ( p , sxgbe_gstrings_stats [ i ] . stat_string ,
ETH_GSTRING_LEN ) ;
p + = ETH_GSTRING_LEN ;
}
break ;
default :
WARN_ON ( 1 ) ;
break ;
}
}
static int sxgbe_get_sset_count ( struct net_device * netdev , int sset )
{
int len ;
switch ( sset ) {
case ETH_SS_STATS :
len = SXGBE_STATS_LEN ;
return len ;
default :
return - EINVAL ;
}
}
static void sxgbe_get_ethtool_stats ( struct net_device * dev ,
struct ethtool_stats * dummy , u64 * data )
{
struct sxgbe_priv_data * priv = netdev_priv ( dev ) ;
int i ;
char * p ;
if ( priv - > eee_enabled ) {
2016-06-25 22:05:26 +02:00
int val = phy_get_eee_err ( dev - > phydev ) ;
2014-03-25 12:11:02 -07:00
if ( val )
priv - > xstats . eee_wakeup_error_n = val ;
}
for ( i = 0 ; i < SXGBE_STATS_LEN ; i + + ) {
p = ( char * ) priv + sxgbe_gstrings_stats [ i ] . stat_offset ;
data [ i ] = ( sxgbe_gstrings_stats [ i ] . sizeof_stat = = sizeof ( u64 ) )
? ( * ( u64 * ) p ) : ( * ( u32 * ) p ) ;
}
}
static void sxgbe_get_channels ( struct net_device * dev ,
struct ethtool_channels * channel )
{
channel - > max_rx = SXGBE_MAX_RX_CHANNELS ;
channel - > max_tx = SXGBE_MAX_TX_CHANNELS ;
channel - > rx_count = SXGBE_RX_QUEUES ;
channel - > tx_count = SXGBE_TX_QUEUES ;
}
static u32 sxgbe_riwt2usec ( u32 riwt , struct sxgbe_priv_data * priv )
{
unsigned long clk = clk_get_rate ( priv - > sxgbe_clk ) ;
if ( ! clk )
return 0 ;
return ( riwt * 256 ) / ( clk / 1000000 ) ;
}
static u32 sxgbe_usec2riwt ( u32 usec , struct sxgbe_priv_data * priv )
{
unsigned long clk = clk_get_rate ( priv - > sxgbe_clk ) ;
if ( ! clk )
return 0 ;
return ( usec * ( clk / 1000000 ) ) / 256 ;
}
static int sxgbe_get_coalesce ( struct net_device * dev ,
2021-08-20 15:35:18 +08:00
struct ethtool_coalesce * ec ,
struct kernel_ethtool_coalesce * kernel_coal ,
struct netlink_ext_ack * extack )
2014-03-25 12:11:02 -07:00
{
struct sxgbe_priv_data * priv = netdev_priv ( dev ) ;
if ( priv - > use_riwt )
ec - > rx_coalesce_usecs = sxgbe_riwt2usec ( priv - > rx_riwt , priv ) ;
return 0 ;
}
static int sxgbe_set_coalesce ( struct net_device * dev ,
2021-08-20 15:35:18 +08:00
struct ethtool_coalesce * ec ,
struct kernel_ethtool_coalesce * kernel_coal ,
struct netlink_ext_ack * extack )
2014-03-25 12:11:02 -07:00
{
struct sxgbe_priv_data * priv = netdev_priv ( dev ) ;
unsigned int rx_riwt ;
if ( ! ec - > rx_coalesce_usecs )
return - EINVAL ;
rx_riwt = sxgbe_usec2riwt ( ec - > rx_coalesce_usecs , priv ) ;
if ( ( rx_riwt > SXGBE_MAX_DMA_RIWT ) | | ( rx_riwt < SXGBE_MIN_DMA_RIWT ) )
return - EINVAL ;
else if ( ! priv - > use_riwt )
return - EOPNOTSUPP ;
priv - > rx_riwt = rx_riwt ;
priv - > hw - > dma - > rx_watchdog ( priv - > ioaddr , priv - > rx_riwt ) ;
return 0 ;
}
static int sxgbe_get_rss_hash_opts ( struct sxgbe_priv_data * priv ,
struct ethtool_rxnfc * cmd )
{
cmd - > data = 0 ;
/* Report default options for RSS on sxgbe */
switch ( cmd - > flow_type ) {
case TCP_V4_FLOW :
case UDP_V4_FLOW :
cmd - > data | = RXH_L4_B_0_1 | RXH_L4_B_2_3 ;
2020-08-23 17:36:59 -05:00
fallthrough ;
2014-03-25 12:11:02 -07:00
case SCTP_V4_FLOW :
case AH_ESP_V4_FLOW :
case AH_V4_FLOW :
case ESP_V4_FLOW :
case IPV4_FLOW :
cmd - > data | = RXH_IP_SRC | RXH_IP_DST ;
break ;
case TCP_V6_FLOW :
case UDP_V6_FLOW :
cmd - > data | = RXH_L4_B_0_1 | RXH_L4_B_2_3 ;
2020-08-23 17:36:59 -05:00
fallthrough ;
2014-03-25 12:11:02 -07:00
case SCTP_V6_FLOW :
case AH_ESP_V6_FLOW :
case AH_V6_FLOW :
case ESP_V6_FLOW :
case IPV6_FLOW :
cmd - > data | = RXH_IP_SRC | RXH_IP_DST ;
break ;
default :
return - EINVAL ;
}
return 0 ;
}
static int sxgbe_get_rxnfc ( struct net_device * dev , struct ethtool_rxnfc * cmd ,
u32 * rule_locs )
{
struct sxgbe_priv_data * priv = netdev_priv ( dev ) ;
int ret = - EOPNOTSUPP ;
switch ( cmd - > cmd ) {
case ETHTOOL_GRXFH :
ret = sxgbe_get_rss_hash_opts ( priv , cmd ) ;
break ;
default :
break ;
}
return ret ;
}
static int sxgbe_set_rss_hash_opt ( struct sxgbe_priv_data * priv ,
struct ethtool_rxnfc * cmd )
{
u32 reg_val = 0 ;
/* RSS does not support anything other than hashing
* to queues on src and dst IPs and ports
*/
if ( cmd - > data & ~ ( RXH_IP_SRC | RXH_IP_DST |
RXH_L4_B_0_1 | RXH_L4_B_2_3 ) )
return - EINVAL ;
switch ( cmd - > flow_type ) {
case TCP_V4_FLOW :
case TCP_V6_FLOW :
if ( ! ( cmd - > data & RXH_IP_SRC ) | |
! ( cmd - > data & RXH_IP_DST ) | |
! ( cmd - > data & RXH_L4_B_0_1 ) | |
! ( cmd - > data & RXH_L4_B_2_3 ) )
return - EINVAL ;
reg_val = SXGBE_CORE_RSS_CTL_TCP4TE ;
break ;
case UDP_V4_FLOW :
case UDP_V6_FLOW :
if ( ! ( cmd - > data & RXH_IP_SRC ) | |
! ( cmd - > data & RXH_IP_DST ) | |
! ( cmd - > data & RXH_L4_B_0_1 ) | |
! ( cmd - > data & RXH_L4_B_2_3 ) )
return - EINVAL ;
reg_val = SXGBE_CORE_RSS_CTL_UDP4TE ;
break ;
case SCTP_V4_FLOW :
case AH_ESP_V4_FLOW :
case AH_V4_FLOW :
case ESP_V4_FLOW :
case AH_ESP_V6_FLOW :
case AH_V6_FLOW :
case ESP_V6_FLOW :
case SCTP_V6_FLOW :
case IPV4_FLOW :
case IPV6_FLOW :
if ( ! ( cmd - > data & RXH_IP_SRC ) | |
! ( cmd - > data & RXH_IP_DST ) | |
( cmd - > data & RXH_L4_B_0_1 ) | |
( cmd - > data & RXH_L4_B_2_3 ) )
return - EINVAL ;
reg_val = SXGBE_CORE_RSS_CTL_IP2TE ;
break ;
default :
return - EINVAL ;
}
/* Read SXGBE RSS control register and update */
reg_val | = readl ( priv - > ioaddr + SXGBE_CORE_RSS_CTL_REG ) ;
writel ( reg_val , priv - > ioaddr + SXGBE_CORE_RSS_CTL_REG ) ;
readl ( priv - > ioaddr + SXGBE_CORE_RSS_CTL_REG ) ;
return 0 ;
}
static int sxgbe_set_rxnfc ( struct net_device * dev , struct ethtool_rxnfc * cmd )
{
struct sxgbe_priv_data * priv = netdev_priv ( dev ) ;
int ret = - EOPNOTSUPP ;
switch ( cmd - > cmd ) {
case ETHTOOL_SRXFH :
ret = sxgbe_set_rss_hash_opt ( priv , cmd ) ;
break ;
default :
break ;
}
return ret ;
}
static void sxgbe_get_regs ( struct net_device * dev ,
struct ethtool_regs * regs , void * space )
{
struct sxgbe_priv_data * priv = netdev_priv ( dev ) ;
u32 * reg_space = ( u32 * ) space ;
int reg_offset ;
int reg_ix = 0 ;
void __iomem * ioaddr = priv - > ioaddr ;
memset ( reg_space , 0x0 , REG_SPACE_SIZE ) ;
/* MAC registers */
for ( reg_offset = START_MAC_REG_OFFSET ;
reg_offset < = MAX_MAC_REG_OFFSET ; reg_offset + = 4 ) {
reg_space [ reg_ix ] = readl ( ioaddr + reg_offset ) ;
reg_ix + + ;
}
/* MTL registers */
for ( reg_offset = START_MTL_REG_OFFSET ;
reg_offset < = MAX_MTL_REG_OFFSET ; reg_offset + = 4 ) {
reg_space [ reg_ix ] = readl ( ioaddr + reg_offset ) ;
reg_ix + + ;
}
/* DMA registers */
for ( reg_offset = START_DMA_REG_OFFSET ;
reg_offset < = MAX_DMA_REG_OFFSET ; reg_offset + = 4 ) {
reg_space [ reg_ix ] = readl ( ioaddr + reg_offset ) ;
reg_ix + + ;
}
BUG_ON ( reg_ix * 4 > REG_SPACE_SIZE ) ;
}
static int sxgbe_get_regs_len ( struct net_device * dev )
{
return REG_SPACE_SIZE ;
}
2014-03-25 12:10:54 -07:00
static const struct ethtool_ops sxgbe_ethtool_ops = {
2020-03-12 21:08:02 -07:00
. supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS ,
2014-03-25 12:11:02 -07:00
. get_drvinfo = sxgbe_getdrvinfo ,
. get_msglevel = sxgbe_getmsglevel ,
. set_msglevel = sxgbe_setmsglevel ,
. get_link = ethtool_op_get_link ,
. get_strings = sxgbe_get_strings ,
. get_ethtool_stats = sxgbe_get_ethtool_stats ,
. get_sset_count = sxgbe_get_sset_count ,
. get_channels = sxgbe_get_channels ,
. get_coalesce = sxgbe_get_coalesce ,
. set_coalesce = sxgbe_set_coalesce ,
. get_rxnfc = sxgbe_get_rxnfc ,
. set_rxnfc = sxgbe_set_rxnfc ,
. get_regs = sxgbe_get_regs ,
. get_regs_len = sxgbe_get_regs_len ,
2014-03-25 12:10:57 -07:00
. get_eee = sxgbe_get_eee ,
. set_eee = sxgbe_set_eee ,
2016-06-25 22:05:27 +02:00
. get_link_ksettings = phy_ethtool_get_link_ksettings ,
. set_link_ksettings = phy_ethtool_set_link_ksettings ,
2014-03-25 12:10:54 -07:00
} ;
void sxgbe_set_ethtool_ops ( struct net_device * netdev )
{
2014-05-11 00:12:32 +00:00
netdev - > ethtool_ops = & sxgbe_ethtool_ops ;
2014-03-25 12:10:54 -07:00
}