blob: c045194db8a6da64e9fc57faca5ba3fe4e71fab7 [file] [log] [blame]
/*
* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
*
* Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <fal/fal_mib.h>
#include <fal/fal_port_ctrl.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/ethtool.h>
#include <linux/vmalloc.h>
#include <linux/spinlock.h>
#include <linux/io.h>
#include <nss_dp_arch.h>
#include "syn_dev.h"
#include "syn_mac_reg.h"
#define SYN_STAT(m) offsetof(struct nss_dp_hal_gmac_stats, m)
#define SYN_MIB_STAT(m) offsetof(fal_mib_counter_t, m)
#define HW_ERR_SIZE sizeof(uint64_t)
/*
* Array to store ethtool statistics
*/
struct syn_ethtool_stats {
uint8_t stat_string[ETH_GSTRING_LEN];
uint64_t stat_offset;
};
/*
* Array of strings describing statistics
*/
static const struct syn_ethtool_stats syn_gstrings_stats[] = {
{"rx_bytes", SYN_STAT(rx_stats.rx_bytes)},
{"rx_packets", SYN_STAT(rx_stats.rx_packets)},
{"rx_errors", SYN_STAT(rx_stats.rx_errors)},
{"rx_missed", SYN_STAT(rx_stats.rx_missed)},
{"rx_descriptor_errors", SYN_STAT(rx_stats.rx_descriptor_errors)},
{"rx_late_collision_errors", SYN_STAT(rx_stats.rx_late_collision_errors)},
{"rx_dribble_bit_errors", SYN_STAT(rx_stats.rx_dribble_bit_errors)},
{"rx_length_errors", SYN_STAT(rx_stats.rx_length_errors)},
{"rx_ip_header_errors", SYN_STAT(rx_stats.rx_ip_header_errors)},
{"rx_ip_payload_errors", SYN_STAT(rx_stats.rx_ip_payload_errors)},
{"rx_no_buffer_errors", SYN_STAT(rx_stats.rx_no_buffer_errors)},
{"rx_transport_csum_bypassed", SYN_STAT(rx_stats.rx_transport_csum_bypassed)},
{"rx_fifo_overflows", SYN_STAT(rx_stats.rx_fifo_overflows)},
{"rx_overflow_errors", SYN_STAT(rx_stats.rx_overflow_errors)},
{"rx_crc_errors", SYN_STAT(rx_stats.rx_crc_errors)},
{"rx_scatter_bytes", SYN_STAT(rx_stats.rx_scatter_bytes)},
{"rx_scatter_packets", SYN_STAT(rx_stats.rx_scatter_packets)},
{"rx_scatter_errors", SYN_STAT(rx_stats.rx_scatter_errors)},
{"tx_bytes", SYN_STAT(tx_stats.tx_bytes)},
{"tx_packets", SYN_STAT(tx_stats.tx_packets)},
{"tx_collisions", SYN_STAT(tx_stats.tx_collisions)},
{"tx_errors", SYN_STAT(tx_stats.tx_errors)},
{"tx_jabber_timeout_errors", SYN_STAT(tx_stats.tx_jabber_timeout_errors)},
{"tx_frame_flushed_errors", SYN_STAT(tx_stats.tx_frame_flushed_errors)},
{"tx_loss_of_carrier_errors", SYN_STAT(tx_stats.tx_loss_of_carrier_errors)},
{"tx_no_carrier_errors", SYN_STAT(tx_stats.tx_no_carrier_errors)},
{"tx_late_collision_errors", SYN_STAT(tx_stats.tx_late_collision_errors)},
{"tx_excessive_collision_errors", SYN_STAT(tx_stats.tx_excessive_collision_errors)},
{"tx_excessive_deferral_errors", SYN_STAT(tx_stats.tx_excessive_deferral_errors)},
{"tx_underflow_errors", SYN_STAT(tx_stats.tx_underflow_errors)},
{"tx_ip_header_errors", SYN_STAT(tx_stats.tx_ip_header_errors)},
{"tx_ip_payload_errors", SYN_STAT(tx_stats.tx_ip_payload_errors)},
{"tx_dropped", SYN_STAT(tx_stats.tx_dropped)},
{"tx_ts_create_errors", SYN_STAT(tx_stats.tx_ts_create_errors)},
{"tx_desc_not_avail", SYN_STAT(tx_stats.tx_desc_not_avail)},
{"tx_pkts_requeued", SYN_STAT(tx_stats.tx_packets_requeued)},
{"tx_nr_frags_pkts", SYN_STAT(tx_stats.tx_nr_frags_pkts)},
{"tx_fraglist_pkts", SYN_STAT(tx_stats.tx_fraglist_pkts)},
{"pmt_interrupts", SYN_STAT(hw_errs[0])},
{"mmc_interrupts", SYN_STAT(hw_errs[0]) + (1 * HW_ERR_SIZE)},
{"line_interface_interrupts", SYN_STAT(hw_errs[0]) + (2 * HW_ERR_SIZE)},
{"fatal_bus_error_interrupts", SYN_STAT(hw_errs[0]) + (3 * HW_ERR_SIZE)},
{"rx_buffer_unavailable_interrupts", SYN_STAT(hw_errs[0]) + (4 * HW_ERR_SIZE)},
{"rx_process_stopped_interrupts", SYN_STAT(hw_errs[0]) + (5 * HW_ERR_SIZE)},
{"tx_underflow_interrupts", SYN_STAT(hw_errs[0]) + (6 * HW_ERR_SIZE)},
{"rx_overflow_interrupts", SYN_STAT(hw_errs[0]) + (7 * HW_ERR_SIZE)},
{"tx_jabber_timeout_interrutps", SYN_STAT(hw_errs[0]) + (8 * HW_ERR_SIZE)},
{"tx_process_stopped_interrutps", SYN_STAT(hw_errs[0]) + (9 * HW_ERR_SIZE)},
};
/*
* Array of strings describing statistics
*/
static const struct syn_ethtool_stats syn_gstrings_mib_stats[] = {
{"rx_broadcast", SYN_MIB_STAT(RxBroad)},
{"rx_pause", SYN_MIB_STAT(RxPause)},
{"rx_multicast", SYN_MIB_STAT(RxMulti)},
{"rx_fcserr", SYN_MIB_STAT(RxFcsErr)},
{"rx_alignerr", SYN_MIB_STAT(RxAllignErr)},
{"rx_runt", SYN_MIB_STAT(RxRunt)},
{"rx_frag", SYN_MIB_STAT(RxFragment)},
{"rx_pkt64", SYN_MIB_STAT(Rx64Byte)},
{"rx_pkt65to127", SYN_MIB_STAT(Rx128Byte)},
{"rx_pkt128to255", SYN_MIB_STAT(Rx256Byte)},
{"rx_pkt256to511", SYN_MIB_STAT(Rx512Byte)},
{"rx_pkt512to1023", SYN_MIB_STAT(Rx1024Byte)},
{"rx_pkt1024to1518", SYN_MIB_STAT(Rx1518Byte)},
{"rx_pkt1519tox", SYN_MIB_STAT(RxMaxByte)},
{"rx_toolong", SYN_MIB_STAT(RxTooLong)},
{"rx_pktgoodbyte", SYN_MIB_STAT(RxGoodByte)},
{"rx_pktbadbyte", SYN_MIB_STAT(RxBadByte)},
{"rx_overflow", SYN_MIB_STAT(RxOverFlow)},
{"filtered", SYN_MIB_STAT(Filtered)},
{"tx_broadcast", SYN_MIB_STAT(TxBroad)},
{"tx_pause", SYN_MIB_STAT(TxPause)},
{"tx_multicast", SYN_MIB_STAT(TxMulti)},
{"tx_underrun", SYN_MIB_STAT(TxUnderRun)},
{"tx_pkt64", SYN_MIB_STAT(Tx64Byte)},
{"tx_pkt65to127", SYN_MIB_STAT(Tx128Byte)},
{"tx_pkt128to255", SYN_MIB_STAT(Tx256Byte)},
{"tx_pkt256to511", SYN_MIB_STAT(Tx512Byte)},
{"tx_pkt512to1023", SYN_MIB_STAT(Tx1024Byte)},
{"tx_pkt1024to1518", SYN_MIB_STAT(Tx1518Byte)},
{"tx_pkt1519tox", SYN_MIB_STAT(TxMaxByte)},
{"tx_oversize", SYN_MIB_STAT(TxOverSize)},
{"tx_pktbyte_h", SYN_MIB_STAT(TxByte)},
{"tx_collisions", SYN_MIB_STAT(TxCollision)},
{"tx_abortcol", SYN_MIB_STAT(TxAbortCol)},
{"tx_multicol", SYN_MIB_STAT(TxMultiCol)},
{"tx_singlecol", SYN_MIB_STAT(TxSingalCol)},
{"tx_exesdeffer", SYN_MIB_STAT(TxExcDefer)},
{"tx_deffer", SYN_MIB_STAT(TxDefer)},
{"tx_latecol", SYN_MIB_STAT(TxLateCol)},
{"rx_unicast", SYN_MIB_STAT(RxUniCast)},
{"tx_unicast", SYN_MIB_STAT(TxUniCast)},
{"rx_jumbofcserr", SYN_MIB_STAT(RxJumboFcsErr)},
{"rx_jumboalignerr", SYN_MIB_STAT(RxJumboAligenErr)},
{"rx_14to63", SYN_MIB_STAT(Rx14To63)},
{"rx_toolongbyte", SYN_MIB_STAT(RxTooLongByte)},
{"rx_runtbyte", SYN_MIB_STAT(RxRuntByte)},
};
#define SYN_STATS_LEN ARRAY_SIZE(syn_gstrings_stats)
#define SYN_STATS_MIB_STATS_LEN ARRAY_SIZE(syn_gstrings_mib_stats)
/*
* syn_enable_mac_cst()
* Enable stripping of MAC padding/FCS
*/
static void syn_enable_mac_cst(struct nss_gmac_hal_dev *nghd)
{
hal_set_reg_bits(nghd->mac_base, SYN_MAC_CONFIGURATION, SYN_MAC_CST_ENABLE);
}
/*
* syn_disable_mac_cst()
* Disable stripping of MAC padding/FCS
*/
static void syn_disable_mac_cst(struct nss_gmac_hal_dev *nghd)
{
hal_clear_reg_bits(nghd->mac_base, SYN_MAC_CONFIGURATION, SYN_MAC_CST_DISABLE);
}
/*
* syn_set_rx_flow_ctrl()
*/
static void syn_set_rx_flow_ctrl(struct nss_gmac_hal_dev *nghd)
{
hal_set_reg_bits(nghd->mac_base, SYN_MAC_FLOW_CONTROL,
SYN_MAC_FC_RX_FLOW_CONTROL);
}
/*
* syn_clear_rx_flow_ctrl()
*/
static void syn_clear_rx_flow_ctrl(struct nss_gmac_hal_dev *nghd)
{
hal_clear_reg_bits(nghd->mac_base, SYN_MAC_FLOW_CONTROL,
SYN_MAC_FC_RX_FLOW_CONTROL);
}
/*
* syn_set_tx_flow_ctrl()
*/
static void syn_set_tx_flow_ctrl(struct nss_gmac_hal_dev *nghd)
{
hal_set_reg_bits(nghd->mac_base, SYN_MAC_FLOW_CONTROL,
SYN_MAC_FC_TX_FLOW_CONTROL);
}
/*
* syn_send_tx_pause_frame()
*/
static void syn_send_tx_pause_frame(struct nss_gmac_hal_dev *nghd)
{
syn_set_tx_flow_ctrl(nghd);
hal_set_reg_bits(nghd->mac_base, SYN_MAC_FLOW_CONTROL,
SYN_MAC_FC_SEND_PAUSE_FRAME);
}
/*
* syn_clear_tx_flow_ctrl()
*/
static void syn_clear_tx_flow_ctrl(struct nss_gmac_hal_dev *nghd)
{
hal_clear_reg_bits(nghd->mac_base, SYN_MAC_FLOW_CONTROL,
SYN_MAC_FC_TX_FLOW_CONTROL);
}
/*
* syn_rx_enable()
*/
static void syn_rx_enable(struct nss_gmac_hal_dev *nghd)
{
hal_set_reg_bits(nghd->mac_base, SYN_MAC_CONFIGURATION, SYN_MAC_RX);
}
/*
* syn_tx_enable()
*/
static void syn_tx_enable(struct nss_gmac_hal_dev *nghd)
{
hal_set_reg_bits(nghd->mac_base, SYN_MAC_CONFIGURATION, SYN_MAC_TX);
}
/*
* syn_rx_disable()
*/
static void syn_rx_disable(struct nss_gmac_hal_dev *nghd)
{
hal_clear_reg_bits(nghd->mac_base, SYN_MAC_CONFIGURATION, SYN_MAC_RX);
}
/*
* syn_tx_disable()
*/
static void syn_tx_disable(struct nss_gmac_hal_dev *nghd)
{
hal_clear_reg_bits(nghd->mac_base, SYN_MAC_CONFIGURATION, SYN_MAC_TX);
}
/************Ip checksum offloading APIs*************/
/*
* syn_enable_rx_chksum_offload()
* Enable IPv4 header and IPv4/IPv6 TCP/UDP checksum calculation by GMAC.
*/
static void syn_enable_rx_chksum_offload(struct nss_gmac_hal_dev *nghd)
{
hal_set_reg_bits(nghd->mac_base,
SYN_MAC_CONFIGURATION, SYN_MAC_RX_IPC_OFFLOAD);
}
/*
* syn_disable_rx_chksum_offload()
* Disable the IP checksum offloading in receive path.
*/
static void syn_disable_rx_chksum_offload(struct nss_gmac_hal_dev *nghd)
{
hal_clear_reg_bits(nghd->mac_base,
SYN_MAC_CONFIGURATION, SYN_MAC_RX_IPC_OFFLOAD);
}
/*******************Ip checksum offloading APIs**********************/
/*
* syn_ipc_offload_init()
* Initialize IPC Checksum offloading.
*/
static void syn_ipc_offload_init(struct nss_gmac_hal_dev *nghd)
{
struct nss_dp_dev *dp_priv;
dp_priv = netdev_priv(nghd->netdev);
if (test_bit(__NSS_DP_RXCSUM, &dp_priv->flags)) {
/*
* Enable the offload engine in the receive path
*/
syn_enable_rx_chksum_offload(nghd);
netdev_dbg(nghd->netdev, "%s: enable Rx checksum\n", __func__);
} else {
syn_disable_rx_chksum_offload(nghd);
netdev_dbg(nghd->netdev, "%s: disable Rx checksum\n", __func__);
}
}
/*
* syn_disable_mac_interrupt()
* Disable all the interrupts.
*/
static void syn_disable_mac_interrupt(struct nss_gmac_hal_dev *nghd)
{
hal_write_relaxed_reg(nghd->mac_base, SYN_INTERRUPT_MASK, 0xffffffff);
}
/*
* syn_disable_mmc_tx_interrupt()
* Disable the MMC Tx interrupt.
*
* The MMC tx interrupts are masked out as per the mask specified.
*/
static void syn_disable_mmc_tx_interrupt(struct nss_gmac_hal_dev *nghd,
uint32_t mask)
{
hal_set_reg_bits(nghd->mac_base, SYN_MMC_TX_INTERRUPT_MASK, mask);
}
/*
* syn_disable_mmc_rx_interrupt()
* Disable the MMC Rx interrupt.
*
* The MMC rx interrupts are masked out as per the mask specified.
*/
static void syn_disable_mmc_rx_interrupt(struct nss_gmac_hal_dev *nghd,
uint32_t mask)
{
hal_set_reg_bits(nghd->mac_base, SYN_MMC_RX_INTERRUPT_MASK, mask);
}
/*
* syn_disable_mmc_ipc_rx_interrupt()
* Disable the MMC ipc rx checksum offload interrupt.
*
* The MMC ipc rx checksum offload interrupts are masked out as
* per the mask specified.
*/
static void syn_disable_mmc_ipc_rx_interrupt(struct nss_gmac_hal_dev *nghd,
uint32_t mask)
{
hal_set_reg_bits(nghd->mac_base, SYN_MMC_IPC_RX_INTR_MASK, mask);
}
/*
* syn_disable_interrupt_all()
* Disable all the interrupts.
*/
static void syn_disable_interrupt_all(struct nss_gmac_hal_dev *nghd)
{
syn_disable_mac_interrupt(nghd);
syn_disable_mmc_tx_interrupt(nghd, 0xFFFFFFFF);
syn_disable_mmc_rx_interrupt(nghd, 0xFFFFFFFF);
syn_disable_mmc_ipc_rx_interrupt(nghd, 0xFFFFFFFF);
}
/*
* syn_broadcast_enable()
* Enables Broadcast frames.
*
* When enabled Address filtering module passes all incoming broadcast frames.
*/
static void syn_broadcast_enable(struct nss_gmac_hal_dev *nghd)
{
hal_clear_reg_bits(nghd->mac_base, SYN_MAC_FRAME_FILTER, SYN_MAC_BROADCAST);
}
/*
* syn_multicast_enable()
* Enables Multicast frames.
*
* When enabled all multicast frames are passed.
*/
static void syn_multicast_enable(struct nss_gmac_hal_dev *nghd)
{
hal_set_reg_bits(nghd->mac_base, SYN_MAC_FRAME_FILTER, SYN_MAC_MULTICAST_FILTER);
}
/*
* syn_promisc_enable()
* Enables promiscous mode.
*
* When enabled Address filter modules pass all incoming frames
* regardless of their Destination and source addresses.
*/
static void syn_promisc_enable(struct nss_gmac_hal_dev *nghd)
{
hal_set_reg_bits(nghd->mac_base, SYN_MAC_FRAME_FILTER,
SYN_MAC_PROMISCUOUS_MODE_ON);
}
/*
* syn_rx_flow_control()
*/
static void syn_rx_flow_control(struct nss_gmac_hal_dev *nghd,
bool enabled)
{
BUG_ON(nghd == NULL);
if (enabled) {
syn_set_rx_flow_ctrl(nghd);
} else {
syn_clear_rx_flow_ctrl(nghd);
}
}
/*
* syn_tx_flow_control()
*/
static void syn_tx_flow_control(struct nss_gmac_hal_dev *nghd,
bool enabled)
{
BUG_ON(nghd == NULL);
if (enabled) {
syn_set_tx_flow_ctrl(nghd);
} else {
syn_clear_tx_flow_ctrl(nghd);
}
}
/*
* syn_get_max_frame_size()
*/
static int32_t syn_get_max_frame_size(struct nss_gmac_hal_dev *nghd)
{
int ret;
uint32_t mtu;
BUG_ON(nghd == NULL);
ret = fal_port_max_frame_size_get(0, nghd->mac_id, &mtu);
if (!ret) {
return mtu;
}
return ret;
}
/*
* syn_set_max_frame_size()
*/
static int32_t syn_set_max_frame_size(struct nss_gmac_hal_dev *nghd,
uint32_t val)
{
uint16_t frame_sz;
BUG_ON(nghd == NULL);
if (val > SYN_HAL_MAX_MTU_SIZE) {
netdev_warn(nghd->netdev, "Maximum allowed MTU: %d\n",
SYN_HAL_MAX_MTU_SIZE);
return -1;
}
frame_sz = val + SYN_HAL_MTU_L2_OVERHEAD;
return fal_port_max_frame_size_set(0, nghd->mac_id, frame_sz);
}
/*
* syn_get_mib_stats()
*/
int syn_get_mib_stats(struct nss_gmac_hal_dev *nghd, fal_mib_counter_t *stats)
{
if (fal_mib_counter_get(0, nghd->mac_id, stats) < 0) {
return -1;
}
return 0;
}
/*
* syn_get_netdev_stats()
*/
static int32_t syn_get_netdev_stats(struct nss_gmac_hal_dev *nghd,
struct rtnl_link_stats64 *stats)
{
fal_mib_counter_t hal_stats;
BUG_ON(nghd == NULL);
memset(&hal_stats, 0, sizeof(fal_mib_counter_t));
if (syn_get_mib_stats(nghd, &hal_stats)) {
return -1;
}
stats->rx_packets = hal_stats.RxUniCast + hal_stats.RxBroad
+ hal_stats.RxMulti;
stats->tx_packets = hal_stats.TxUniCast + hal_stats.TxBroad
+ hal_stats.TxMulti;
stats->rx_bytes = hal_stats.RxGoodByte;
stats->tx_bytes = hal_stats.TxByte;
stats->collisions = hal_stats.TxCollision;
stats->multicast = hal_stats.RxMulti;
/*
* RX errors
*/
stats->rx_crc_errors = hal_stats.RxFcsErr + hal_stats.RxJumboFcsErr;
stats->rx_frame_errors = hal_stats.RxAllignErr +
hal_stats.RxJumboAligenErr + hal_stats.RxRunt;
stats->rx_fifo_errors = hal_stats.RxOverFlow;
stats->rx_errors = stats->rx_crc_errors + stats->rx_frame_errors +
stats->rx_fifo_errors;
stats->rx_dropped = hal_stats.RxTooLong + stats->rx_errors;
/*
* TX errors
*/
stats->tx_fifo_errors = hal_stats.TxUnderRun + hal_stats.RxOverFlow;
stats->tx_aborted_errors = hal_stats.TxAbortCol;
stats->tx_errors = stats->tx_fifo_errors + stats->tx_aborted_errors;
return 0;
}
/*
* syn_get_eth_stats()
*/
static int32_t syn_get_eth_stats(struct nss_gmac_hal_dev *nghd,
uint64_t *data,
struct nss_dp_gmac_stats *stats)
{
int i, i_mib;
fal_mib_counter_t mib_stats;
uint8_t *p = NULL;
BUG_ON(nghd == NULL);
/*
* Populate data plane statistics.
*/
for (i = 0; i < SYN_STATS_LEN; i++) {
p = ((uint8_t *)(stats) + syn_gstrings_stats[i].stat_offset);
data[i] = *(uint64_t *)p;
}
/*
* Get MIB statistics
*/
memset(&mib_stats, 0, sizeof(fal_mib_counter_t));
if (syn_get_mib_stats(nghd, &mib_stats)) {
return -1;
}
/*
* Populate MIB statistics
*/
for (i_mib = 0; i_mib < SYN_STATS_MIB_STATS_LEN; i_mib++) {
p = ((uint8_t *)(&mib_stats) +
syn_gstrings_mib_stats[i_mib].stat_offset);
i = SYN_STATS_LEN + i_mib;
data[i] = *(uint64_t *)p;
}
return 0;
}
/*
* syn_get_strset_count()
*/
static int32_t syn_get_strset_count(struct nss_gmac_hal_dev *nghd,
int32_t sset)
{
struct net_device *netdev;
BUG_ON(nghd == NULL);
netdev = nghd->netdev;
switch (sset) {
case ETH_SS_STATS:
return (SYN_STATS_LEN + SYN_STATS_MIB_STATS_LEN);
}
netdev_dbg(netdev, "%s: Invalid string set\n", __func__);
return -EPERM;
}
/*
* syn_get_strings()
*/
static int32_t syn_get_strings(struct nss_gmac_hal_dev *nghd,
int32_t stringset, uint8_t *data)
{
struct net_device *netdev;
int i;
BUG_ON(nghd == NULL);
netdev = nghd->netdev;
switch (stringset) {
case ETH_SS_STATS:
for (i = 0; i < SYN_STATS_LEN; i++) {
memcpy(data, syn_gstrings_stats[i].stat_string,
ETH_GSTRING_LEN);
data += ETH_GSTRING_LEN;
}
for (i = 0; i < SYN_STATS_MIB_STATS_LEN; i++) {
memcpy(data, syn_gstrings_mib_stats[i].stat_string,
ETH_GSTRING_LEN);
data += ETH_GSTRING_LEN;
}
break;
default:
netdev_dbg(netdev, "%s: Invalid string set\n", __func__);
return -EPERM;
}
return 0;
}
/*
* syn_send_pause_frame()
*/
static void syn_send_pause_frame(struct nss_gmac_hal_dev *nghd)
{
BUG_ON(nghd == NULL);
syn_send_tx_pause_frame(nghd);
}
/*
* syn_set_mac_address()
*/
static void syn_set_mac_address(struct nss_gmac_hal_dev *nghd,
uint8_t *macaddr)
{
uint32_t data;
BUG_ON(nghd == NULL);
if (!macaddr) {
netdev_warn(nghd->netdev, "macaddr is not valid.\n");
return;
}
data = (macaddr[5] << 8) | macaddr[4] | SYN_MAC_ADDR_HIGH_AE;
hal_write_relaxed_reg(nghd->mac_base, SYN_MAC_ADDR0_HIGH, data);
data = (macaddr[3] << 24) | (macaddr[2] << 16) | (macaddr[1] << 8)
| macaddr[0];
hal_write_relaxed_reg(nghd->mac_base, SYN_MAC_ADDR0_LOW, data);
}
/*
* syn_get_mac_address()
*/
static void syn_get_mac_address(struct nss_gmac_hal_dev *nghd,
uint8_t *macaddr)
{
uint32_t data;
BUG_ON(nghd == NULL);
if (!macaddr) {
netdev_warn(nghd->netdev, "macaddr is not valid.\n");
return;
}
data = hal_read_relaxed_reg(nghd->mac_base, SYN_MAC_ADDR0_HIGH);
macaddr[5] = (data >> 8) & 0xff;
macaddr[4] = (data) & 0xff;
data = hal_read_relaxed_reg(nghd->mac_base, SYN_MAC_ADDR0_LOW);
macaddr[3] = (data >> 24) & 0xff;
macaddr[2] = (data >> 16) & 0xff;
macaddr[1] = (data >> 8) & 0xff;
macaddr[0] = (data) & 0xff;
}
/*
* syn_gmac_clk_enable
* Function to enable GCC_SNOC_GMAC_AXI_CLK.
*
* These clocks are required for GMAC operations.
*/
static void syn_gmac_clk_enable(struct nss_gmac_hal_dev *nghd)
{
struct net_device *ndev = nghd->netdev;
struct nss_dp_dev *dp_priv = netdev_priv(ndev);
struct platform_device *pdev = dp_priv->pdev;
struct device *dev = &pdev->dev;
struct clk *gmac_clk = NULL;
int err;
gmac_clk = devm_clk_get(dev, SYN_GMAC_SNOC_GMAC_AXI_CLK);
if (IS_ERR(gmac_clk)) {
pr_err("%s: cannot get clock: %s\n", __func__,
SYN_GMAC_SNOC_GMAC_AXI_CLK);
return;
}
err = clk_prepare_enable(gmac_clk);
if (err) {
pr_err("%s: cannot enable clock: %s, err: %d\n", __func__,
SYN_GMAC_SNOC_GMAC_AXI_CLK, err);
return;
}
}
/*
* syn_start()
*/
static int32_t syn_start(struct nss_gmac_hal_dev *nghd)
{
struct nss_dp_dev *dp_dev;
BUG_ON(nghd == NULL);
syn_tx_enable(nghd);
syn_rx_enable(nghd);
dp_dev = (struct nss_dp_dev *)netdev_priv(nghd->netdev);
/*
* TODO: Enable MAC CST stripping for NSS mode as well. We need
* NSS change for that.
*/
if (!(dp_dev->drv_flags & NSS_DP_PRIV_FLAG(INIT_OVERRIDE))) {
syn_enable_mac_cst(nghd);
}
netdev_dbg(nghd->netdev, "%s: mac_base:0x%px MAC Config:0x%x\n",
__func__, nghd->mac_base,
hal_read_relaxed_reg(nghd->mac_base, SYN_MAC_CONFIGURATION));
return 0;
}
/*
* syn_stop()
*/
static int32_t syn_stop(struct nss_gmac_hal_dev *nghd)
{
struct nss_dp_dev *dp_dev;
BUG_ON(nghd == NULL);
syn_tx_disable(nghd);
syn_rx_disable(nghd);
dp_dev = (struct nss_dp_dev *)netdev_priv(nghd->netdev);
/*
* TODO: Disable MAC CST stripping for NSS mode as well. We need
* NSS change for that.
*/
if (!(dp_dev->drv_flags & NSS_DP_PRIV_FLAG(INIT_OVERRIDE))) {
syn_disable_mac_cst(nghd);
}
netdev_dbg(nghd->netdev, "%s: mac_base:0x%px MAC Config:0x%x\n",
__func__, nghd->mac_base,
hal_read_relaxed_reg(nghd->mac_base, SYN_MAC_CONFIGURATION));
return 0;
}
/*
* syn_init()
*/
static void *syn_init(struct nss_gmac_hal_platform_data *gmacpdata)
{
struct syn_hal_dev *shd = NULL;
struct net_device *ndev = NULL;
struct nss_dp_dev *dp_priv = NULL;
struct resource *res;
ndev = gmacpdata->netdev;
dp_priv = netdev_priv(ndev);
res = platform_get_resource(dp_priv->pdev, IORESOURCE_MEM, 0);
if (!res) {
netdev_dbg(ndev, "Resource get failed.\n");
return NULL;
}
shd = (struct syn_hal_dev *)devm_kzalloc(&dp_priv->pdev->dev,
sizeof(struct syn_hal_dev),
GFP_KERNEL);
if (!shd) {
netdev_dbg(ndev, "kzalloc failed. Returning...\n");
return NULL;
}
shd->nghd.mac_reg_len = resource_size(res);
shd->nghd.memres = devm_request_mem_region(&dp_priv->pdev->dev,
res->start,
resource_size(res),
ndev->name);
if (!shd->nghd.memres) {
netdev_dbg(ndev, "Request mem region failed. Returning.\n");
devm_kfree(&dp_priv->pdev->dev, shd);
return NULL;
}
/*
* Save netdev context in syn HAL context
*/
shd->nghd.netdev = gmacpdata->netdev;
shd->nghd.mac_id = gmacpdata->macid;
shd->nghd.duplex_mode = DUPLEX_FULL;
set_bit(__NSS_DP_RXCSUM, &dp_priv->flags);
/*
* Populate the mac base addresses
*/
shd->nghd.mac_base =
devm_ioremap_nocache(&dp_priv->pdev->dev, res->start,
resource_size(res));
if (!shd->nghd.mac_base) {
netdev_dbg(ndev, "ioremap fail.\n");
devm_release_mem_region(&dp_priv->pdev->dev,
shd->nghd.memres->start,
shd->nghd.mac_reg_len);
devm_kfree(&dp_priv->pdev->dev, shd);
return NULL;
}
spin_lock_init(&shd->nghd.slock);
netdev_dbg(ndev, "ioremap OK.Size 0x%x Ndev base 0x%lx macbase 0x%px\n",
gmacpdata->reg_len,
ndev->base_addr,
shd->nghd.mac_base);
syn_disable_interrupt_all(&shd->nghd);
/*
* Enable SoC specific GMAC clocks.
*/
syn_gmac_clk_enable(&shd->nghd);
syn_ipc_offload_init(&shd->nghd);
syn_promisc_enable(&shd->nghd);
syn_broadcast_enable(&shd->nghd);
syn_multicast_enable(&shd->nghd);
/*
* Reset MIB Stats
*/
if (fal_mib_port_flush_counters(0, shd->nghd.mac_id)) {
netdev_dbg(ndev, "MIB stats Reset fail.\n");
}
return (struct nss_gmac_hal_dev *)shd;
}
/*
* syn_exit()
*/
static void syn_exit(struct nss_gmac_hal_dev *nghd)
{
struct nss_dp_dev *dp_priv = NULL;
struct syn_hal_dev *shd = (struct syn_hal_dev *)nghd;
netdev_dbg(nghd->netdev, "Freeing up dev memory.\n");
dp_priv = netdev_priv(nghd->netdev);
devm_iounmap(&dp_priv->pdev->dev,
(void *)nghd->mac_base);
devm_release_mem_region(&dp_priv->pdev->dev,
(nghd->memres)->start,
nghd->mac_reg_len);
nghd->memres = NULL;
nghd->mac_base = NULL;
devm_kfree(&dp_priv->pdev->dev, shd);
}
/*
* MAC hal_ops base structure
*/
struct nss_gmac_hal_ops syn_gmac_ops = {
.init = &syn_init,
.start = &syn_start,
.stop = &syn_stop,
.exit = &syn_exit,
.setmacaddr = &syn_set_mac_address,
.getmacaddr = &syn_get_mac_address,
.rxflowcontrol = &syn_rx_flow_control,
.txflowcontrol = &syn_tx_flow_control,
.setmaxframe = &syn_set_max_frame_size,
.getmaxframe = &syn_get_max_frame_size,
.getndostats = &syn_get_netdev_stats,
.getssetcount = &syn_get_strset_count,
.getstrings = &syn_get_strings,
.getethtoolstats = &syn_get_eth_stats,
.sendpause = &syn_send_pause_frame,
};