Files
linux-nv-oot/drivers/net/ethernet/nvidia/nvethernet/sysfs.c
Narayan Reddy d3cdce84ec nvethernet: IOCTL handling of HSIs
1) Add IOCTL support for below HSIs
 T264-EQOS_HSIv2-30
 T264-EQOS_HSIv2-9
 T264-MGBE_HSIv2-8
 T264-MGBE_HSIv2-7
 T264-MGBE_HSIv2-6
2) Add support for T26x Err Injection

JIRA NET-1946
JIRA NET-1948
Bug 4778785

Signed-off-by: Narayan Reddy <narayanr@nvidia.com>
Change-Id: Idd479983c39a7f15c875dac93c86d5bf4fd5e04c
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3258754
Tested-by: mobile promotions <svcmobile_promotions@nvidia.com>
Reviewed-by: Srinivas Ramachandran <srinivasra@nvidia.com>
Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com>
2025-07-24 10:19:18 +00:00

4193 lines
118 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
// SPDX-FileCopyrightText: Copyright (c) 2019-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
#include "ether_linux.h"
#ifdef MACSEC_SUPPORT
#include "macsec.h"
#endif
#if (IS_ENABLED(CONFIG_TEGRA_HSIERRRPTINJ))
#include <linux/tegra-hsierrrptinj.h>
#endif
#ifndef OSI_STRIPPED_LIB
#ifdef CONFIG_DEBUG_FS
/* As per IAS Docs */
#define EOQS_MAX_REGISTER_ADDRESS 0x12FC
#endif
#ifdef OSI_DEBUG
/**
* @brief Shows the current setting of descriptor dump
*
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer to store the current MAC loopback setting
*/
static ssize_t ether_desc_dump_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
struct osi_dma_priv_data *osi_dma = pdata->osi_dma;
return scnprintf(buf, PAGE_SIZE, "%s\n",
(osi_dma->enable_desc_dump == 1U) ?
"enabled" : "disabled");
}
/**
* @brief Set the user setting for enable_desc_dump
*
* Algorithm: This is used to update osi_dma->enable_desc_dump
*
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer which contains the user settings of MAC loopback
* @param[in] size: size of buffer
*
* @return size of buffer.
*/
static ssize_t ether_desc_dump_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
struct osi_dma_priv_data *osi_dma = pdata->osi_dma;
if (strncmp(buf, "enable", 6) == 0U) {
osi_dma->enable_desc_dump = 1U;
} else if (strncmp(buf, "disable", 7) == 0U) {
osi_dma->enable_desc_dump = 0U;
} else {
dev_err(pdata->dev,
"Invalid entry. Valid Entries are enable or disable\n");
}
return size;
}
/**
* @brief Sysfs attribute for enable descriptor dump
*
*/
static DEVICE_ATTR(desc_dump_enable, (S_IRUGO | S_IWUSR),
ether_desc_dump_show,
ether_desc_dump_store);
#ifdef BW_TEST
/**
* @brief Shows the current setting of tx packet dump
*
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer to store the current MAC loopback setting
*/
static ssize_t ether_test_tx_bandwidth_dump_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
return scnprintf(buf, PAGE_SIZE, "%s\n",
(pdata->test_tx_bandwidth == 1U) ?
"enabled" : "disabled");
}
/**
* @brief Extract MTU from String.
*
* @param[in] str: Input Buffer.
*
* @return MTU size.
*/
static inline int extract_mtu(const char* str)
{
int num = 0;
// Iterate through the string to find the first digit
while (*str) {
if (((*str) >= '0') && ((*str) <= '9')) {
// Adding boundary check
if ((num > (INT_MAX / 10)) ||
((num == (INT_MAX / 10)) && ((*str - '0') > (INT_MAX % 10)))) {
num = 0;
break;
}
// Convert the digit characters to an integer
num = num * 10 + (*str - '0');
}
str++;
}
if (num > OSI_MAX_MTU_SIZE) {
num = 0;
}
return num;
}
/**
* @brief Set the user setting for enable_test_tx_bandwidth
*
* Algorithm: This is used to update osi_dma->enable_desc_dump
*
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer which contains the user settings of MAC loopback
* @param[in] size: size of buffer
*
* @return size of buffer.
*/
static ssize_t ether_test_tx_bandwidth_dump_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
if (strncmp(buf, "enable", 6) == 0U) {
pdata->test_tx_bandwidth = OSI_ENABLE;
if (pdata->tx_bandwidth_pkt_size == 0U) {
pdata->tx_bandwidth_pkt_size = OSI_DFLT_MTU_SIZE;
}
queue_delayed_work_on(2, pdata->tx_bw_wq, &pdata->tx_bandwidth_work,
msecs_to_jiffies(0));
} else if (strncmp(buf, "disable", 7) == 0U) {
pdata->test_tx_bandwidth = OSI_DISABLE;
cancel_delayed_work_sync(&pdata->tx_bandwidth_work);
} else if (strncmp(buf, "rx_disable", 10) == 0U) {
pdata->test_tx_bandwidth = OSI_ENABLE;
} else if (strncmp(buf, "pkt", 3) == 0U) {
pdata->tx_bandwidth_pkt_size = extract_mtu(buf);
} else {
dev_err(pdata->dev,
"Invalid entry Valid Entries are"
" enable or disable or rx_disable or pkt1500 or pkt8192 or pkt64 or pkt256\n");
}
return size;
}
/**
* @brief Sysfs attribute for enable burst tx packet
*
*/
static DEVICE_ATTR(test_tx_bandwidth_dump_enable, (S_IRUGO | S_IWUSR),
ether_test_tx_bandwidth_dump_show,
ether_test_tx_bandwidth_dump_store);
#endif
/**
* @brief Shows current configured tx queue
*
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer to print the current Tx Q configuration
*/
static ssize_t ether_mac_tx_q_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
struct osi_core_priv_data *osi_core = pdata->osi_core;
char *start = buf;
if (osi_core->pre_sil != OSI_ENABLE) {
dev_err(pdata->dev, "Not Allowed. Not pre-sil platform\n");
return 0;
}
if (!netif_running(ndev)) {
dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n");
return 0;
}
buf += scnprintf(buf, PAGE_SIZE, "Current Tx queue: %d\n",
pdata->tx_queue_select);
return (buf - start);
}
/**
* @brief Choose dma channel for Tx traffic or Tx queue select when non-zero
*
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer which contains dma channel number or Tx Q
* @param[in] size: size of buffer
*
* @return size of buffer.
*/
static ssize_t ether_mac_tx_q_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
struct osi_core_priv_data *osi_core = pdata->osi_core;
int ret, bufp = 0, dma_chanel = 0;
if (osi_core->pre_sil != OSI_ENABLE) {
dev_err(pdata->dev, "Not Allowed. Not pre-sil platform\n");
return 0;
}
if (!netif_running(ndev)) {
dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n");
return size;
}
ret = sscanf(buf + bufp, "%d", &dma_chanel);
if (ret != 1 || dma_chanel >= OSI_MGBE_MAX_NUM_CHANS) {
dev_err(pdata->dev, "Failed to parse args or invalid dma chan");
goto exit;
}
pdata->tx_queue_select = dma_chanel;
exit:
return size;
}
/**
* @brief Sysfs attribute for MAC Tx Q
*
*/
static DEVICE_ATTR(mac_tx_q, (S_IRUGO | S_IWUSR),
ether_mac_tx_q_show,
ether_mac_tx_q_store);
#endif /* OSI_DEBUG */
/**
* @brief Shows the current setting of PCS BASE-R FEC enabled
*
* Algorithm: Display the current PCS BASE-R FEC enabled.
*
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer to store the current PCS BASE-R FEC setting
*/
static ssize_t ether_mac_base_r_fec_enable_show(
struct device *dev,
struct device_attribute *attr, char *buf)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
struct osi_core_priv_data *osi_core = pdata->osi_core;
return scnprintf(buf, PAGE_SIZE, "%s\n",
(osi_core->pcs_base_r_fec_en == OSI_ENABLE) ?
"enabled" : "disabled");
}
/**
* @brief Set the PCS BASE-R FEC enabled
*
* Algorithm: This is used to set the PCS BASE-R FEC enabled.
*
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer which contains the user settings of PCS BASE-R FEC
* @param[in] size: size of buffer
*
* @return size of buffer.
*/
static ssize_t ether_mac_base_r_fec_enable_store(
struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
struct osi_core_priv_data *osi_core = pdata->osi_core;
/* Interface is up so base-r fec change not allowed */
if (netif_running(ndev)) {
dev_err(pdata->dev, "Not Allowed. Ether interface is up\n");
return size;
}
if (strncmp(buf, "enable", 6) == 0U) {
osi_core->pcs_base_r_fec_en = OSI_ENABLE;
dev_info(pdata->dev, "Enabling PCS BASE-R FEC\n");
} else if (strncmp(buf, "disable", 7) == 0U) {
osi_core->pcs_base_r_fec_en = OSI_DISABLE;
dev_info(pdata->dev, "Disabling PCS BASE-R FEC\n");
} else {
dev_err(pdata->dev,
"Invalid entry. Valid Entries are enable or disable\n");
}
return size;
}
/**
* @brief Sysfs attribute for BASE-R FEC enable
*
*/
static DEVICE_ATTR(pcs_baser_fec, (S_IRUGO | S_IWUSR),
ether_mac_base_r_fec_enable_show,
ether_mac_base_r_fec_enable_store);
/**
* @brief Shows the current setting of MAC loopback
*
* Algorithm: Display the current MAC loopback setting.
*
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer to store the current MAC loopback setting
*
* @note MAC and PHY need to be initialized.
*/
static ssize_t ether_mac_loopback_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
return scnprintf(buf, PAGE_SIZE, "%s\n",
(pdata->mac_loopback_mode == 1U) ?
"enabled" : "disabled");
}
/**
* @brief Set the user setting of MAC loopback mode
*
* Algorithm: This is used to set the user mode settings of MAC loopback.
*
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer which contains the user settings of MAC loopback
* @param[in] size: size of buffer
*
* @note MAC and PHY need to be initialized.
*
* @return size of buffer.
*/
static ssize_t ether_mac_loopback_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct phy_device *phydev = ndev->phydev;
struct ether_priv_data *pdata = netdev_priv(ndev);
struct osi_ioctl ioctl_data = {};
int ret = -1;
/* Interface is not up so LB mode can't be set */
if (!netif_running(ndev)) {
dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n");
return size;
}
if (strncmp(buf, "enable", 6) == 0U) {
if (!phydev->link) {
/* If no phy link, then turn on carrier explicitly so
* that nw stack can send packets. If PHY link is
* present, PHY framework would have already taken care
* of netif_carrier_* status.
*/
netif_carrier_on(ndev);
}
/* Enabling the MAC Loopback Mode */
ioctl_data.arg1_u32 = OSI_ENABLE;
ioctl_data.cmd = OSI_CMD_MAC_LB;
ret = osi_handle_ioctl(pdata->osi_core, &ioctl_data);
if (ret < 0) {
dev_err(pdata->dev, "Enabling MAC Loopback failed\n");
} else {
pdata->mac_loopback_mode = 1;
dev_info(pdata->dev, "Enabling MAC Loopback\n");
}
} else if (strncmp(buf, "disable", 7) == 0U) {
if (!phydev->link) {
/* If no phy link, then turn off carrier explicitly so
* that nw stack doesn't send packets. If PHY link is
* present, PHY framework would have already taken care
* of netif_carrier_* status.
*/
netif_carrier_off(ndev);
}
/* Disabling the MAC Loopback Mode */
ioctl_data.arg1_u32 = OSI_DISABLE;
ioctl_data.cmd = OSI_CMD_MAC_LB;
ret = osi_handle_ioctl(pdata->osi_core, &ioctl_data);
if (ret < 0) {
dev_err(pdata->dev, "Disabling MAC Loopback failed\n");
} else {
pdata->mac_loopback_mode = 0;
dev_info(pdata->dev, "Disabling MAC Loopback\n");
}
} else {
dev_err(pdata->dev,
"Invalid entry. Valid Entries are enable or disable\n");
}
return size;
}
#ifdef MACSEC_SUPPORT
/**
* @brief Shows the current setting of MACsec AN status
*
* Algorithm: Display the current MACsec AN enable status
*
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer to store the current macsec an status
*/
static ssize_t macsec_an_status_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
struct macsec_priv_data *macsec_pdata = pdata->macsec_pdata;
unsigned int macsec_status = 0;
if ((macsec_pdata->macsec_tx_an_map != 0U) &&
(macsec_pdata->macsec_rx_an_map != 0U)) {
macsec_status = OSI_ENABLE;
}
return scnprintf(buf, PAGE_SIZE, "%s\n",
(macsec_status == OSI_ENABLE) ?
"1" : "0");
}
/**
* @brief Sysfs attribute for MACsec irq stats
*
*/
static DEVICE_ATTR(macsec_an_status, (S_IRUGO | S_IWUSR),
macsec_an_status_show,
NULL);
/**
* @brief Shows the current setting of MACsec controllers enabled
*
* Algorithm: Display the current MACsec controllers enabled (Tx/Rx).
*
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer to store the current MACsec loopback setting
*/
static ssize_t macsec_enable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
struct macsec_priv_data *macsec_pdata = pdata->macsec_pdata;
unsigned int enabled = macsec_pdata->enabled;
return scnprintf(buf, PAGE_SIZE, "%s\n",
(enabled == OSI_ENABLE) ? "txrx" : "None");
}
extern int macsec_open(struct macsec_priv_data *macsec_pdata,
void *const genl_info);
extern int macsec_close(struct macsec_priv_data *macsec_pdata);
/**
* @brief Set the MACsec controller enabled (Tx/Rx)
*
* Algorithm: This is used to set the Tx/Rx MACsec controller enabled.
*
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer which contains the user settings of MACsec loopback
* @param[in] size: size of buffer
*
* @return size of buffer.
*/
static ssize_t macsec_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
struct macsec_priv_data *macsec_pdata = pdata->macsec_pdata;
int ret = 0;
if (!netif_running(ndev)) {
dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n");
return size;
}
if (strncmp(buf, "0", 1) == OSI_NONE) {
ret = macsec_close(macsec_pdata);
} else if (strncmp(buf, "txrx", 4) == OSI_NONE) {
ret = macsec_open(macsec_pdata, OSI_NULL);
} else {
dev_err(pdata->dev,
"Invalid. Valid inputs are 0/txrx\n");
}
return size;
}
/**
* @brief Sysfs attribute for MACsec enable
*
*/
static DEVICE_ATTR(macsec_enable, (S_IRUGO | S_IWUSR),
macsec_enable_show,
macsec_enable_store);
/**
* @brief Shows the current setting of MACsec cipther set
*
* Algorithm: Display the current MACsec cipher setting.
*
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer to store the current MACsec cipher setting
*/
static ssize_t macsec_cipher_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
struct macsec_priv_data *macsec_pdata = pdata->macsec_pdata;
return scnprintf(buf, PAGE_SIZE, "%s\n",
(macsec_pdata->cipher == OSI_MACSEC_CIPHER_AES128) ?
"aes128" : "aes256");
}
/**
* @brief Set the user setting of MACsec AES cipher
*
* Algorithm: This is used to set the user mode settings of MACsec cipther.
*
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer which contains the user settings of MACsec cipher
* @param[in] size: size of buffer
*
* @return size of buffer.
*/
static ssize_t macsec_cipher_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
struct macsec_priv_data *macsec_pdata = pdata->macsec_pdata;
int ret = 0;
if (!netif_running(ndev)) {
dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n");
return size;
}
if (strncmp(buf, "aes128", 6) == OSI_NONE) {
ret = osi_macsec_cipher_config(pdata->osi_core,
OSI_MACSEC_CIPHER_AES128);
if (ret < 0) {
dev_err(pdata->dev, "Failed to set macsec cipher\n");
} else {
macsec_pdata->cipher = OSI_MACSEC_CIPHER_AES128;
dev_info(pdata->dev, "macsec cipher aes128 enabled\n");
}
} else if (strncmp(buf, "aes256", 6) == OSI_NONE) {
ret = osi_macsec_cipher_config(pdata->osi_core,
OSI_MACSEC_CIPHER_AES256);
if (ret < 0) {
dev_err(pdata->dev, "Failed to set macsec cipher\n");
} else {
macsec_pdata->cipher = OSI_MACSEC_CIPHER_AES256;
dev_info(pdata->dev, "macsec cipher aes256 enabled\n");
}
} else {
dev_err(pdata->dev,
"Invalid entry. Valid Entries are aes128/aes256\n");
}
return size;
}
/**
* @brief Sysfs attribute for MACsec cipher
*
*/
static DEVICE_ATTR(macsec_cipher, (S_IRUGO | S_IWUSR),
macsec_cipher_show,
macsec_cipher_store);
#ifdef DEBUG_MACSEC
/**
* @brief Shows the current setting of MACsec loopback
*
* Algorithm: Display the current MACsec loopback setting.
*
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer to store the current MACsec loopback setting
*/
static ssize_t macsec_loopback_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
struct macsec_priv_data *macsec_pdata = pdata->macsec_pdata;
return scnprintf(buf, PAGE_SIZE, "%s\n",
(macsec_pdata->loopback_mode == OSI_ENABLE) ?
"enabled" : "disabled");
}
/**
* @brief Set the user setting of MACsec loopback mode
*
* Algorithm: This is used to set the user mode settings of MACsec loopback.
*
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer which contains the user settings of MACsec loopback
* @param[in] size: size of buffer
*
* @return size of buffer.
*/
static ssize_t macsec_loopback_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
struct macsec_priv_data *macsec_pdata = pdata->macsec_pdata;
int ret = 0;
if (!netif_running(ndev)) {
dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n");
return size;
}
if (strncmp(buf, "enable", 6) == OSI_NONE) {
ret = osi_macsec_loopback(pdata->osi_core, OSI_ENABLE);
if (ret < 0) {
dev_err(pdata->dev,
"Failed to enable macsec loopback\n");
} else {
macsec_pdata->loopback_mode = OSI_ENABLE;
dev_info(pdata->dev, "Enabled macsec Loopback\n");
}
} else if (strncmp(buf, "disable", 7) == OSI_NONE) {
ret = osi_macsec_loopback(pdata->osi_core, OSI_DISABLE);
if (ret < 0) {
dev_err(pdata->dev,
"Failed to Disable macsec loopback\n");
} else {
macsec_pdata->loopback_mode = OSI_DISABLE;
dev_info(pdata->dev, "Disabled macsec Loopback\n");
}
} else if (strncmp(buf, "carrier_on", 10) == OSI_NONE) {
netif_carrier_on(ndev);
} else if (strncmp(buf, "carrier_off", 11) == OSI_NONE) {
netif_carrier_off(ndev);
} else {
dev_err(pdata->dev,
"Invalid entry. Valid Entries are "
"enable/disable/carrier_on/carrier_off\n");
}
return size;
}
/**
* @brief Sysfs attribute for MACsec loopback
*
*/
static DEVICE_ATTR(macsec_loopback, (S_IRUGO | S_IWUSR),
macsec_loopback_show,
macsec_loopback_store);
#endif /* DEBUG_MACSEC */
#define MAC_ADDR_FMT "%02x:%02x:%02x:%02x:%02x:%02x"
#define ETHTYPE_FMT "%02x%02x"
#define SCI_FMT "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x"
static void format_output(char **buf_p,
struct osi_macsec_lut_config *lut_config)
{
char *buf = *buf_p;
unsigned int flags = lut_config->flags;
struct osi_lut_inputs entry = lut_config->lut_in;
if ((flags & OSI_LUT_FLAGS_DA_VALID) == OSI_LUT_FLAGS_DA_VALID) {
buf += scnprintf(buf, PAGE_SIZE, "DA: " MAC_ADDR_FMT " ",
entry.da[5], entry.da[4], entry.da[3],
entry.da[2], entry.da[1], entry.da[0]);
} else {
buf += scnprintf(buf, PAGE_SIZE, "DA: X ");
}
if ((flags & OSI_LUT_FLAGS_SA_VALID) == OSI_LUT_FLAGS_SA_VALID) {
buf += scnprintf(buf, PAGE_SIZE, "SA: " MAC_ADDR_FMT " ",
entry.sa[5], entry.sa[4], entry.sa[3],
entry.sa[2], entry.sa[1], entry.sa[0]);
} else {
buf += scnprintf(buf, PAGE_SIZE, "SA: X ");
}
if ((flags & OSI_LUT_FLAGS_ETHTYPE_VALID) ==
OSI_LUT_FLAGS_ETHTYPE_VALID) {
buf += scnprintf(buf, PAGE_SIZE, "ethtype: " ETHTYPE_FMT " ",
entry.ethtype[1], entry.ethtype[0]);
} else {
buf += scnprintf(buf, PAGE_SIZE, "ethtype: X ");
}
if ((flags & OSI_LUT_FLAGS_VLAN_VALID) == OSI_LUT_FLAGS_VLAN_VALID) {
buf += scnprintf(buf, PAGE_SIZE, "vlan: ");
if ((flags & OSI_LUT_FLAGS_VLAN_PCP_VALID) ==
OSI_LUT_FLAGS_VLAN_PCP_VALID) {
buf += scnprintf(buf, PAGE_SIZE, "prio: %u ",
entry.vlan_pcp);
} else {
buf += scnprintf(buf, PAGE_SIZE, "prio: X ");
}
if ((flags & OSI_LUT_FLAGS_VLAN_ID_VALID) ==
OSI_LUT_FLAGS_VLAN_ID_VALID) {
buf += scnprintf(buf, PAGE_SIZE, "id: %u ",
entry.vlan_id);
} else {
buf += scnprintf(buf, PAGE_SIZE, "id: X ");
}
} else {
buf += scnprintf(buf, PAGE_SIZE, "vlan: X ");
}
if ((flags & OSI_LUT_FLAGS_DVLAN_PKT) == OSI_LUT_FLAGS_DVLAN_PKT) {
buf += scnprintf(buf, PAGE_SIZE, "dvlan: 1 ");
if ((flags & OSI_LUT_FLAGS_DVLAN_OUTER_INNER_TAG_SEL) ==
OSI_LUT_FLAGS_DVLAN_OUTER_INNER_TAG_SEL) {
buf += scnprintf(buf, PAGE_SIZE, "dvlan_outer_tag: 1 ");
} else {
buf += scnprintf(buf, PAGE_SIZE, "dvlan_outer_tag: 0 ");
}
} else {
buf += scnprintf(buf, PAGE_SIZE, "dvlan: X ");
}
if ((flags & OSI_LUT_FLAGS_BYTE0_PATTERN_VALID) ==
OSI_LUT_FLAGS_BYTE0_PATTERN_VALID) {
buf += scnprintf(buf, PAGE_SIZE, "Byte0: Pattern: %x "
"offset: %u ",
entry.byte_pattern[0],
entry.byte_pattern_offset[0]);
} else {
buf += scnprintf(buf, PAGE_SIZE, "Byte0: X ");
}
if ((flags & OSI_LUT_FLAGS_BYTE1_PATTERN_VALID) ==
OSI_LUT_FLAGS_BYTE1_PATTERN_VALID) {
buf += scnprintf(buf, PAGE_SIZE, "Byte1: Pattern: %x "
"offset: %u ",
entry.byte_pattern[1],
entry.byte_pattern_offset[1]);
} else {
buf += scnprintf(buf, PAGE_SIZE, "Byte1: X ");
}
if ((flags & OSI_LUT_FLAGS_BYTE2_PATTERN_VALID) ==
OSI_LUT_FLAGS_BYTE2_PATTERN_VALID) {
buf += scnprintf(buf, PAGE_SIZE, "Byte2: Pattern: %x "
"offset: %u ",
entry.byte_pattern[2],
entry.byte_pattern_offset[2]);
} else {
buf += scnprintf(buf, PAGE_SIZE, "Byte2: X ");
}
if ((flags & OSI_LUT_FLAGS_BYTE3_PATTERN_VALID) ==
OSI_LUT_FLAGS_BYTE3_PATTERN_VALID) {
buf += scnprintf(buf, PAGE_SIZE, "Byte3: Pattern: %x "
"offset: %u ",
entry.byte_pattern[3],
entry.byte_pattern_offset[3]);
} else {
buf += scnprintf(buf, PAGE_SIZE, "Byte3: X ");
}
if ((flags & OSI_LUT_FLAGS_PREEMPT_VALID) ==
OSI_LUT_FLAGS_PREEMPT_VALID) {
if ((flags & OSI_LUT_FLAGS_PREEMPT) == OSI_LUT_FLAGS_PREEMPT) {
buf += scnprintf(buf, PAGE_SIZE, "prempt: 1 ");
} else {
buf += scnprintf(buf, PAGE_SIZE, "prempt: 0 ");
}
} else {
buf += scnprintf(buf, PAGE_SIZE, "prempt: X ");
}
*buf_p = buf;
return;
}
static int parse_inputs(const char *buf,
struct osi_macsec_lut_config *lut_config, int *bufp)
{
struct osi_lut_inputs *lut_in;
int temp[OSI_ETH_ALEN] = {0};
int temp2[OSI_ETH_ALEN] = {0};
int temp3[OSI_LUT_BYTE_PATTERN_MAX] = {0};
int temp4[OSI_ETHTYPE_LEN] = {0};
unsigned char byte[OSI_LUT_BYTE_PATTERN_MAX] = {0};
unsigned char mac_da[OSI_ETH_ALEN] = {0};
unsigned char mac_sa[OSI_ETH_ALEN] = {0};
unsigned char ethtype[OSI_ETHTYPE_LEN] = {0};
unsigned int byte_offset[OSI_LUT_BYTE_PATTERN_MAX] = {0};
unsigned int vlan_pcp = 0, vlan_id = 0, flags = 0;
unsigned short controller;
int mac_da_valid, mac_sa_valid, ethtype_valid, vlan_valid;
int dvlan, dvlan_outer_tag;
int prempt, prempt_valid;
int byte_valid[OSI_LUT_BYTE_PATTERN_MAX];
int i, valid, index;;
i = sscanf(buf, "%d %d %hu "
"%x:%x:%x:%x:%x:%x %d "
"%x:%x:%x:%x:%x:%x %d "
"%2x%2x %d "
"%x %u %d "
"%x %u %d "
"%x %u %d "
"%x %u %d "
"%u %u %d %d %d "
"%d %d%n",
&valid, &index, &controller,
&temp[0], &temp[1], &temp[2],
&temp[3], &temp[4], &temp[5], &mac_da_valid,
&temp2[0], &temp2[1], &temp2[2],
&temp2[3], &temp2[4], &temp2[5], &mac_sa_valid,
&temp4[0], &temp4[1], &ethtype_valid,
&temp3[0], &byte_offset[0], &byte_valid[0],
&temp3[1], &byte_offset[1], &byte_valid[1],
&temp3[2], &byte_offset[2], &byte_valid[2],
&temp3[3], &byte_offset[3], &byte_valid[3],
&vlan_pcp, &vlan_id, &vlan_valid, &dvlan, &dvlan_outer_tag,
&prempt, &prempt_valid, bufp);
if (i != LUT_INPUTS_LEN) {
pr_err("%s: Invalid LUT inputs(read %d)", __func__, i);
goto err;
}
for (i = 0; i < OSI_ETH_ALEN; i++) {
mac_da[i] = (unsigned char)temp[i];
mac_sa[i] = (unsigned char)temp2[i];
}
for (i = 0; i < OSI_ETHTYPE_LEN; i++) {
ethtype[i] = (unsigned char)temp4[i];
}
for (i = 0; i < OSI_LUT_BYTE_PATTERN_MAX; i++) {
byte[i] = (unsigned char)temp3[i];
}
if (mac_sa_valid && !is_valid_ether_addr(mac_sa)) {
pr_err("%s: Invalid mac SA\n", __func__);
goto err;
}
memset(lut_config, 0, sizeof(*lut_config));
lut_config->table_config.ctlr_sel = controller;
lut_config->table_config.index = index;
lut_in = &lut_config->lut_in;
if (mac_da_valid) {
/* Reverse endianess for HW */
for (i = 0; i < OSI_ETH_ALEN; i++) {
lut_in->da[i] = mac_da[OSI_ETH_ALEN - 1 - i];
}
flags |= OSI_LUT_FLAGS_DA_VALID;
}
if (mac_sa_valid) {
/* Reverse endianess for HW */
for (i = 0; i < OSI_ETH_ALEN; i++) {
lut_in->sa[i] = mac_sa[OSI_ETH_ALEN - 1 - i];
}
flags |= OSI_LUT_FLAGS_SA_VALID;
}
if (ethtype_valid) {
/* Reverse endianess for HW */
for (i = 0; i < OSI_ETHTYPE_LEN; i++) {
lut_in->ethtype[i] = ethtype[OSI_ETHTYPE_LEN - 1 - i];
}
flags |= OSI_LUT_FLAGS_ETHTYPE_VALID;
}
for (i = 0; i < OSI_LUT_BYTE_PATTERN_MAX; i++) {
if (byte_valid[i]) {
switch (i) {
case 0:
flags |= OSI_LUT_FLAGS_BYTE0_PATTERN_VALID;
break;
case 1:
flags |= OSI_LUT_FLAGS_BYTE1_PATTERN_VALID;
break;
case 2:
flags |= OSI_LUT_FLAGS_BYTE2_PATTERN_VALID;
break;
case 3:
flags |= OSI_LUT_FLAGS_BYTE3_PATTERN_VALID;
break;
}
lut_in->byte_pattern[i] = byte[i];
lut_in->byte_pattern_offset[i] = byte_offset[i];
}
}
if (vlan_valid) {
lut_in->vlan_pcp = vlan_pcp;
lut_in->vlan_id = vlan_id;
flags |= (OSI_LUT_FLAGS_VLAN_ID_VALID |
OSI_LUT_FLAGS_VLAN_PCP_VALID |
OSI_LUT_FLAGS_VLAN_VALID);
}
if (dvlan) {
flags |= OSI_LUT_FLAGS_DVLAN_PKT;
if (dvlan_outer_tag) {
flags |= OSI_LUT_FLAGS_DVLAN_OUTER_INNER_TAG_SEL;
}
}
if (prempt_valid) {
flags |= OSI_LUT_FLAGS_PREEMPT_VALID;
if (prempt)
flags |= OSI_LUT_FLAGS_PREEMPT;
}
if (valid) {
flags |= OSI_LUT_FLAGS_ENTRY_VALID;
}
lut_config->flags = flags;
return 0;
err:
return -1;
}
static void dump_byp_lut(char **buf_p, unsigned short ctlr_sel,
struct osi_core_priv_data *osi_core)
{
struct osi_macsec_lut_config lut_config = {0};
char *buf = *buf_p;
int i;
const unsigned int byp_lut_max_index[MAX_MACSEC_IP_TYPES] = {
OSI_BYP_LUT_MAX_INDEX,
OSI_BYP_LUT_MAX_INDEX_T26X};
for (i = 0; i <= byp_lut_max_index[osi_core->macsec]; i++) {
memset(&lut_config, OSI_NONE, sizeof(lut_config));
lut_config.table_config.ctlr_sel = ctlr_sel;
lut_config.lut_sel = OSI_LUT_SEL_BYPASS;
lut_config.table_config.rw = OSI_LUT_READ;
lut_config.table_config.index = i;
if (osi_macsec_config_lut(osi_core, &lut_config) < 0) {
pr_err("%s: Failed to read BYP LUT\n", __func__);
*buf_p = buf;
return;
} else {
buf += scnprintf(buf, PAGE_SIZE, "%d.\t", i);
if ((lut_config.flags & OSI_LUT_FLAGS_ENTRY_VALID) !=
OSI_LUT_FLAGS_ENTRY_VALID) {
buf += scnprintf(buf, PAGE_SIZE, "Invalid\n");
memset(&lut_config, 0, sizeof(lut_config));
continue;
}
format_output(&buf, &lut_config);
/* BYP LUT output field */
if ((lut_config.flags & OSI_LUT_FLAGS_CONTROLLED_PORT) ==
OSI_LUT_FLAGS_CONTROLLED_PORT) {
buf += scnprintf(buf,
PAGE_SIZE, "ctrl port: 1\n");
} else {
buf += scnprintf(buf,
PAGE_SIZE, "ctrl port: 0\n");
}
}
}
*buf_p = buf;
}
/**
* @brief Shows the current BYP LUT configuration
*
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer to print the current LUT configuration
*/
static ssize_t macsec_byp_lut_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
struct osi_core_priv_data *osi_core = pdata->osi_core;
char *start = buf;
if (!netif_running(ndev)) {
dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n");
return 0;
}
buf += scnprintf(buf, PAGE_SIZE, "Tx:\n");
dump_byp_lut(&buf, OSI_CTLR_SEL_TX, osi_core);
buf += scnprintf(buf, PAGE_SIZE, "Rx:\n");
dump_byp_lut(&buf, OSI_CTLR_SEL_RX, osi_core);
return (buf - start);
}
/**
* @brief Set the BYP LUT configuration
*
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer which contains the desired LUT configuration
* @param[in] size: size of buffer
*
* @return size of buffer.
*/
static ssize_t macsec_byp_lut_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
struct osi_core_priv_data *osi_core = pdata->osi_core;
struct osi_macsec_lut_config lut_config;
int ret, bufp, ctrl_port;
unsigned int macsec = osi_core->macsec;
const unsigned int byp_lut_max_index[MAX_MACSEC_IP_TYPES] = {
OSI_BYP_LUT_MAX_INDEX,
OSI_BYP_LUT_MAX_INDEX_T26X};
if (!netif_running(ndev)) {
dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n");
return size;
}
ret = parse_inputs(buf, &lut_config, &bufp);
if (ret < 0) {
dev_err(pdata->dev, "Failed to parse inputs");
goto exit;
}
ret = sscanf(buf + bufp, " %d", &ctrl_port);
if (ret != BYP_LUT_INPUTS) {
dev_err(pdata->dev, "Failed to parse BYP LUT arguments");
goto exit;
}
if (ctrl_port)
lut_config.flags |= OSI_LUT_FLAGS_CONTROLLED_PORT;
lut_config.lut_sel = OSI_LUT_SEL_BYPASS;
lut_config.table_config.rw = OSI_LUT_WRITE;
/* Rest of LUT attributes are filled by parse_inputs() */
if (lut_config.table_config.index > byp_lut_max_index[macsec]) {
dev_err(dev, "%s: Index can't be > %d\n", __func__,
byp_lut_max_index[macsec]);
goto exit;
}
if (osi_macsec_config_lut(osi_core, &lut_config) < 0) {
dev_err(dev, "%s: Failed to config BYP LUT\n", __func__);
goto exit;
} else {
dev_err(dev, "%s: Added BYP LUT idx: %d", __func__,
lut_config.table_config.index);
}
exit:
return size;
}
/**
* @brief Sysfs attribute for MACsec BYP LUT config
*
*/
static DEVICE_ATTR(macsec_byp_lut, (S_IRUGO | S_IWUSR),
macsec_byp_lut_show,
macsec_byp_lut_store);
/**
* @brief Shows the current Rx macsec statitics counters
*
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer to print the current counters
*/
static ssize_t macsec_mmc_counters_show_rx(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
struct osi_core_priv_data *osi_core = pdata->osi_core;
struct osi_macsec_mmc_counters *mmc = &osi_core->macsec_mmc;
unsigned short i;
char *start = buf;
unsigned int macsec = osi_core->macsec;
const unsigned int sc_idx_max[MAX_MACSEC_IP_TYPES] = {
OSI_SC_INDEX_MAX, OSI_SC_INDEX_MAX_T26X};
if (!netif_running(ndev)) {
dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n");
return 0;
}
osi_macsec_read_mmc(osi_core);
buf += scnprintf(buf, PAGE_SIZE, "rx_pkts_no_tag: \t%llu\n",
mmc->rx_pkts_no_tag);
buf += scnprintf(buf, PAGE_SIZE, "rx_pkts_untagged:\t%llu\n",
mmc->rx_pkts_untagged);
buf += scnprintf(buf, PAGE_SIZE, "rx_pkts_bad_tag:\t%llu\n",
mmc->rx_pkts_bad_tag);
buf += scnprintf(buf, PAGE_SIZE, "rx_pkts_no_sa_err:\t%llu\n",
mmc->rx_pkts_no_sa_err);
buf += scnprintf(buf, PAGE_SIZE, "rx_pkts_no_sa: \t%llu\n",
mmc->rx_pkts_no_sa);
buf += scnprintf(buf, PAGE_SIZE, "rx_pkts_overrun:\t%llu\n",
mmc->rx_pkts_overrun);
buf += scnprintf(buf, PAGE_SIZE, "rx_octets_validated:\t%llu\n",
mmc->rx_octets_validated);
buf += scnprintf(buf, PAGE_SIZE, "rx_octets_decrypted:\t%llu\n",
mmc->rx_octets_decrypted);
for (i = 0; i <= sc_idx_max[macsec]; i++) {
buf += scnprintf(buf, PAGE_SIZE, "rx_pkts_invalid sc%d:\t%u\n",
i, mmc->in_pkts_invalid[i]);
}
for (i = 0; i <= sc_idx_max[macsec]; i++) {
buf += scnprintf(buf, PAGE_SIZE, "rx_pkts_delayed sc%d:\t%u\n",
i, mmc->rx_pkts_delayed[i]);
}
for (i = 0; i <= sc_idx_max[macsec]; i++) {
buf += scnprintf(buf, PAGE_SIZE, "rx_pkts_ok sc%d: \t%u\n",
i, mmc->rx_pkts_ok[i]);
}
return (buf - start);
}
/**
* @brief Sysfs attribute for MACsec rx mmc counters
*
*/
static DEVICE_ATTR(macsec_mmc_counters_rx, (S_IRUGO | S_IWUSR),
macsec_mmc_counters_show_rx,
NULL);
/**
* @brief Shows the current Tx macsec statitics counters
*
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer to print the current counters
*/
static ssize_t macsec_mmc_counters_show_tx(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
struct osi_core_priv_data *osi_core = pdata->osi_core;
struct osi_macsec_mmc_counters *mmc = &osi_core->macsec_mmc;
unsigned short i;
char *start = buf;
unsigned int macsec = osi_core->macsec;
const unsigned int sc_idx_max[MAX_MACSEC_IP_TYPES] = {
OSI_SC_INDEX_MAX, OSI_SC_INDEX_MAX_T26X};
if (!netif_running(ndev)) {
dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n");
return 0;
}
osi_macsec_read_mmc(osi_core);
buf += scnprintf(buf, PAGE_SIZE, "tx_pkts_untaged:\t%llu\n",
mmc->tx_pkts_untaged);
buf += scnprintf(buf, PAGE_SIZE, "tx_pkts_too_long:\t%llu\n",
mmc->tx_pkts_too_long);
buf += scnprintf(buf, PAGE_SIZE, "tx_octets_protected:\t%llu\n",
mmc->tx_octets_protected);
buf += scnprintf(buf, PAGE_SIZE, "tx_octets_encrypted:\t%llu\n",
mmc->tx_octets_encrypted);
for (i = 0; i <= sc_idx_max[macsec]; i++) {
buf += scnprintf(buf, PAGE_SIZE, "tx_pkts_protected sc%d:\t%u\n",
i, mmc->tx_pkts_protected[i]);
buf += scnprintf(buf, PAGE_SIZE, "tx_pkts_encrypted sc%d:\t%u\n",
i, mmc->tx_pkts_encrypted[i]);
}
return (buf - start);
}
/**
* @brief Sysfs attribute for MACsec tx mmc counters
*
*/
static DEVICE_ATTR(macsec_mmc_counters_tx, (S_IRUGO | S_IWUSR),
macsec_mmc_counters_show_tx,
NULL);
#ifdef DEBUG_MACSEC
static void dump_dbg_buffers(char **buf_p, unsigned short ctlr_sel,
struct osi_core_priv_data *osi_core)
{
struct osi_macsec_dbg_buf_config dbg_buf_config = {0};
char *buf = *buf_p;
int i;
unsigned int idx_max;
if (ctlr_sel == OSI_CTLR_SEL_TX) {
idx_max = OSI_TX_DBG_BUF_IDX_MAX;
} else {
idx_max = OSI_RX_DBG_BUF_IDX_MAX;
}
for (i = 0; i < idx_max; i++) {
memset(&dbg_buf_config, OSI_NONE, sizeof(dbg_buf_config));
dbg_buf_config.rw = OSI_LUT_READ;
dbg_buf_config.ctlr_sel = ctlr_sel;
dbg_buf_config.index = i;
if (osi_macsec_config_dbg_buf(osi_core, &dbg_buf_config) < 0) {
pr_err("%s: Failed to read debug buffers\n", __func__);
*buf_p = buf;
return;
} else {
buf += scnprintf(buf, PAGE_SIZE, "%d.\t", i);
buf += scnprintf(buf, PAGE_SIZE, " 0x%08X\t 0x%08X\t 0x%08X\t 0x%08X\n",
dbg_buf_config.dbg_buf[3], dbg_buf_config.dbg_buf[2],
dbg_buf_config.dbg_buf[1], dbg_buf_config.dbg_buf[0]);
}
}
*buf_p = buf;
/* reset debug buffer after buf read */
for (i = 0; i < idx_max; i++) {
memset(&dbg_buf_config, OSI_NONE, sizeof(dbg_buf_config));
dbg_buf_config.rw = OSI_LUT_WRITE;
dbg_buf_config.ctlr_sel = ctlr_sel;
dbg_buf_config.index = i;
if (osi_macsec_config_dbg_buf(osi_core, &dbg_buf_config) < 0) {
pr_err("%s: Failed to write debug buffers\n", __func__);
return;
}
}
}
/**
* @brief Shows the current tx/rx debug buffers
*
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer to print the current debug buffers
*/
static ssize_t macsec_dbg_buffer_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
struct osi_core_priv_data *osi_core = pdata->osi_core;
char *start = buf;
if (!netif_running(ndev)) {
dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n");
return 0;
}
if (osi_core->macsec == OSI_MACSEC_T26X) {
dev_err(pdata->dev, "T264 Doesn't support MACSEC debug buffer feature\n");
return 0;
}
buf += scnprintf(buf, PAGE_SIZE, "Tx Dbg Buffers:\n");
dump_dbg_buffers(&buf, OSI_CTLR_SEL_TX, osi_core);
buf += scnprintf(buf, PAGE_SIZE, "Rx Dbg Buffers:\n");
dump_dbg_buffers(&buf, OSI_CTLR_SEL_RX, osi_core);
return (buf - start);
}
/**
* @brief Sysfs attribute for MACsec irq stats
*
*/
static DEVICE_ATTR(macsec_dbg_buffers, (S_IRUGO | S_IWUSR),
macsec_dbg_buffer_show,
NULL);
#define DBG_EVENTS_LEN 13
/**
* @brief Set the debug buffer trigger events
*
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer which contains the desired debug buffer events
* configuration
* @param[in] size: size of buffer
*
* @return size of buffer.
*/
static ssize_t macsec_dbg_events_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
struct osi_core_priv_data *osi_core = pdata->osi_core;
struct osi_macsec_dbg_buf_config dbg_buf_config = {0};
int ret, events[12], i;
unsigned short controller;
if (!netif_running(ndev)) {
dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n");
return size;
}
if (osi_core->macsec == OSI_MACSEC_T26X) {
dev_err(pdata->dev, "T264 Doesn't support MACSEC debug buffer feature\n");
return size;
}
ret = sscanf(buf, "%hu %1x%1x%1x%1x%1x%1x%1x%1x%1x%1x%1x%1x",
&controller, &events[11], &events[10], &events[9], &events[8],
&events[7], &events[6], &events[5], &events[4],
&events[3], &events[2], &events[1], &events[0]);
if (ret != DBG_EVENTS_LEN) {
pr_err("%s: Invalid DBG inputs(read %d)", __func__, ret);
goto exit;
}
/** parse all 12 trigger events */
for (i = 0; i < 12; i++) {
if (events[i] > OSI_ENABLE) {
dev_err(dev, "%s: events bitmap error\n", __func__);
goto exit;
} else {
dbg_buf_config.flags |= (events[i] << i);
}
}
dbg_buf_config.ctlr_sel = controller;
dbg_buf_config.rw = OSI_LUT_WRITE;
if (osi_macsec_dbg_events_config(osi_core, &dbg_buf_config) < 0) {
dev_err(dev, "%s: Failed to config dbg trigger events\n", __func__);
goto exit;
} else {
dev_err(dev, "%s: Updated dbg trigger events: %x", __func__,
dbg_buf_config.flags);
}
exit:
return size;
}
/**
* @brief Sysfs attribute for MACsec debug events config
*
*/
static DEVICE_ATTR(macsec_dbg_events, (S_IRUGO | S_IWUSR),
NULL,
macsec_dbg_events_store);
#endif /* DEBUG_MACSEC */
/**
* @brief Shows the current SCI LUT configuration for RX
*
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer to print the current SCI LUT configuration
*/
static ssize_t macsec_sci_lut_show_rx(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
struct osi_core_priv_data *osi_core = pdata->osi_core;
struct osi_macsec_lut_config lut_config = {0};
int i;
char *start = buf;
unsigned int macsec = osi_core->macsec;
const unsigned int sc_lut_max_index[MAX_MACSEC_IP_TYPES] = {
OSI_SC_INDEX_MAX,
OSI_SC_INDEX_MAX_T26X};
if (!netif_running(ndev)) {
dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n");
return 0;
}
buf += scnprintf(buf, PAGE_SIZE, "Rx:\n");
for (i = 0; i <= sc_lut_max_index[macsec]; i++) {
memset(&lut_config, OSI_NONE, sizeof(lut_config));
lut_config.table_config.ctlr_sel = OSI_CTLR_SEL_RX;
lut_config.lut_sel = OSI_LUT_SEL_SCI;
lut_config.table_config.rw = OSI_LUT_READ;
lut_config.table_config.index = i;
if (osi_macsec_config_lut(osi_core, &lut_config) < 0) {
dev_err(dev, "%s: Failed to read BYP LUT\n", __func__);
goto exit;
} else {
buf += scnprintf(buf, PAGE_SIZE, "%d.\t", i);
if ((lut_config.flags & OSI_LUT_FLAGS_ENTRY_VALID) !=
OSI_LUT_FLAGS_ENTRY_VALID) {
buf += scnprintf(buf, PAGE_SIZE, "Invalid\n");
memset(&lut_config, 0, sizeof(lut_config));
continue;
}
buf += scnprintf(buf, PAGE_SIZE,
"SCI: " SCI_FMT
" sc_index: %d\n",
lut_config.sci_lut_out.sci[7],
lut_config.sci_lut_out.sci[6],
lut_config.sci_lut_out.sci[5],
lut_config.sci_lut_out.sci[4],
lut_config.sci_lut_out.sci[3],
lut_config.sci_lut_out.sci[2],
lut_config.sci_lut_out.sci[1],
lut_config.sci_lut_out.sci[0],
lut_config.sci_lut_out.sc_index);
memset(&lut_config, 0, sizeof(lut_config));
}
}
exit:
return (buf - start);
}
/**
* @brief Sysfs attribute for MACsec SCI LUT config
*
*/
static DEVICE_ATTR(macsec_sci_lut_rx, (S_IRUGO | S_IWUSR),
macsec_sci_lut_show_rx,
NULL);
/**
* @brief Shows the current SCI LUT configuration for TX from Index 0 to 23
*
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer to print the current SCI LUT configuration
*/
static ssize_t macsec_sci_lut_show_tx(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
struct osi_core_priv_data *osi_core = pdata->osi_core;
struct osi_macsec_lut_config lut_config = {0};
unsigned int an_valid;
int i;
char *start = buf;
unsigned int macsec = osi_core->macsec;
const unsigned int sc_lut_max_index[MAX_MACSEC_IP_TYPES] = {
OSI_SC_INDEX_MAX,
OSI_SC_INDEX_MAX_T26X};
if (!netif_running(ndev)) {
dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n");
return 0;
}
buf += scnprintf(buf, PAGE_SIZE, "Tx:\n");
for (i = 0; i < (sc_lut_max_index[macsec] > MAX_ENTRIES_PER_SYSFS_NODE?
MAX_ENTRIES_PER_SYSFS_NODE: sc_lut_max_index[macsec]);
i++) {
memset(&lut_config, OSI_NONE, sizeof(lut_config));
lut_config.table_config.ctlr_sel = OSI_CTLR_SEL_TX;
lut_config.lut_sel = OSI_LUT_SEL_SCI;
lut_config.table_config.rw = OSI_LUT_READ;
lut_config.table_config.index = i;
if (osi_macsec_config_lut(osi_core, &lut_config) < 0) {
dev_err(dev, "%s: Failed to read SCI LUT\n", __func__);
goto exit;
} else {
buf += scnprintf(buf, PAGE_SIZE, "%d.\t", i);
if ((lut_config.flags & OSI_LUT_FLAGS_ENTRY_VALID) !=
OSI_LUT_FLAGS_ENTRY_VALID) {
buf += scnprintf(buf, PAGE_SIZE, "Invalid\n");
memset(&lut_config, 0, sizeof(lut_config));
continue;
}
format_output(&buf, &lut_config);
/* Tx SCI LUT output field */
an_valid = lut_config.sci_lut_out.an_valid;
buf += scnprintf(buf, PAGE_SIZE, "AN3: %d AN2: %d "
"AN1: %d AN0: %d ",
an_valid & OSI_AN3_VALID ? 1 : 0,
an_valid & OSI_AN2_VALID ? 1 : 0,
an_valid & OSI_AN1_VALID ? 1 : 0,
an_valid & OSI_AN0_VALID ? 1 : 0);
buf += scnprintf(buf, PAGE_SIZE, "sc_index: %d\n",
lut_config.sci_lut_out.sc_index);
memset(&lut_config, 0, sizeof(lut_config));
}
}
exit:
return (buf - start);
}
/**
* @brief Sysfs attribute for MACsec SCI LUT config
*
*/
static DEVICE_ATTR(macsec_sci_lut_tx, (S_IRUGO | S_IWUSR),
macsec_sci_lut_show_tx,
NULL);
/**
* @brief Shows the current SCI LUT configuration for TX from Index 24
*
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer to print the current SCI LUT configuration
*/
static ssize_t macsec_sci_lut_show_tx_2(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
struct osi_core_priv_data *osi_core = pdata->osi_core;
struct osi_macsec_lut_config lut_config = {0};
unsigned int an_valid;
int i;
char *start = buf;
unsigned int macsec = osi_core->macsec;
const unsigned int sc_lut_max_index[MAX_MACSEC_IP_TYPES] = {
OSI_SC_INDEX_MAX,
OSI_SC_INDEX_MAX_T26X};
if (!netif_running(ndev)) {
dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n");
return 0;
}
buf += scnprintf(buf, PAGE_SIZE, "Tx:\n");
for (i = MAX_ENTRIES_PER_SYSFS_NODE; i <= sc_lut_max_index[macsec]; i++) {
memset(&lut_config, OSI_NONE, sizeof(lut_config));
lut_config.table_config.ctlr_sel = OSI_CTLR_SEL_TX;
lut_config.lut_sel = OSI_LUT_SEL_SCI;
lut_config.table_config.rw = OSI_LUT_READ;
lut_config.table_config.index = i;
if (osi_macsec_config_lut(osi_core, &lut_config) < 0) {
dev_err(dev, "%s: Failed to read SCI LUT\n", __func__);
goto exit;
} else {
buf += scnprintf(buf, PAGE_SIZE, "%d.\t", i);
if ((lut_config.flags & OSI_LUT_FLAGS_ENTRY_VALID) !=
OSI_LUT_FLAGS_ENTRY_VALID) {
buf += scnprintf(buf, PAGE_SIZE, "Invalid\n");
memset(&lut_config, 0, sizeof(lut_config));
continue;
}
format_output(&buf, &lut_config);
/* Tx SCI LUT output field */
an_valid = lut_config.sci_lut_out.an_valid;
buf += scnprintf(buf, PAGE_SIZE, "AN3: %d AN2: %d "
"AN1: %d AN0: %d ",
an_valid & OSI_AN3_VALID ? 1 : 0,
an_valid & OSI_AN2_VALID ? 1 : 0,
an_valid & OSI_AN1_VALID ? 1 : 0,
an_valid & OSI_AN0_VALID ? 1 : 0);
buf += scnprintf(buf, PAGE_SIZE, "sc_index: %d\n",
lut_config.sci_lut_out.sc_index);
memset(&lut_config, 0, sizeof(lut_config));
}
}
exit:
return (buf - start);
}
/**
* @brief Sysfs attribute for MACsec SCI LUT config
*
*/
static DEVICE_ATTR(macsec_sci_lut_tx_2, (S_IRUGO | S_IWUSR),
macsec_sci_lut_show_tx_2,
NULL);
#define SCI_LUT_INPUTS 13
/**
* @brief Set the SCI LUT configuration
*
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer which contains the desired LUT configuration
* @param[in] size: size of buffer
*
* @return size of buffer.
*/
static ssize_t macsec_sci_lut_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
struct osi_core_priv_data *osi_core = pdata->osi_core;
struct osi_macsec_lut_config lut_config;
int an_valid[OSI_MAX_NUM_SA] = {0};
int ret, bufp;
int temp[OSI_SCI_LEN];
int i;
int sc_index;
unsigned int macsec = osi_core->macsec;
const unsigned int sc_lut_max_index[MAX_MACSEC_IP_TYPES] = {
OSI_SC_INDEX_MAX,
OSI_SC_INDEX_MAX_T26X};
if (!netif_running(ndev)) {
dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n");
return size;
}
ret = parse_inputs(buf, &lut_config, &bufp);
if (ret < 0) {
dev_err(pdata->dev, "Failed to parse inputs");
goto exit;
}
ret = sscanf(buf+bufp, " %1x%1x%1x%1x %x:%x:%x:%x:%x:%x:%x:%x %d",
&an_valid[3], &an_valid[2], &an_valid[1], &an_valid[0],
&temp[7], &temp[6], &temp[5], &temp[4],
&temp[3], &temp[2], &temp[1], &temp[0],
&sc_index);
if (ret != SCI_LUT_INPUTS) {
dev_err(pdata->dev, "Failed to parse SCI LUT arguments");
goto exit;
}
lut_config.lut_sel = OSI_LUT_SEL_SCI;
lut_config.table_config.rw = OSI_LUT_WRITE;
/* Rest of LUT attributes are filled by parse_inputs() */
if (lut_config.table_config.index > sc_lut_max_index[macsec]) {
dev_err(dev, "%s: Index can't be > %d\n", __func__,
sc_lut_max_index[macsec]);
goto exit;
}
if (sc_index > sc_lut_max_index[macsec]) {
dev_err(dev, "%s: SC Index can't be > %d\n", __func__,
sc_lut_max_index[macsec]);
goto exit;
}
/* Configure the outputs */
for (i = 0; i < OSI_SCI_LEN; i++) {
lut_config.sci_lut_out.sci[i] = (unsigned char)temp[i];
}
for (i = 0; i < OSI_MAX_NUM_SA; i++) {
if (an_valid[i] > OSI_ENABLE) {
dev_err(dev, "%s: an_valid bitmap error\n", __func__);
goto exit;
} else {
lut_config.sci_lut_out.an_valid |= (an_valid[i] << i);
}
}
lut_config.sci_lut_out.sc_index = sc_index;
if (osi_macsec_config_lut(osi_core, &lut_config) < 0) {
dev_err(dev, "%s: Failed to config SCI LUT\n", __func__);
goto exit;
} else {
dev_err(dev, "%s: Added SCI LUT idx: %d", __func__,
lut_config.table_config.index);
}
exit:
return size;
}
/**
* @brief Sysfs attribute for MACsec SCI LUT config
*
*/
static DEVICE_ATTR(macsec_sci_lut, (S_IRUGO | S_IWUSR),
NULL,
macsec_sci_lut_store);
#ifdef MACSEC_KEY_PROGRAM
static void dump_kt(char **buf_p, unsigned short ctlr_sel,
struct osi_core_priv_data *osi_core,
unsigned short start_index,
unsigned short end_index)
{
struct osi_macsec_kt_config kt_config = {0};
char *buf = *buf_p;
int i, j;
for (i = start_index; i <= end_index; i++) {
memset(&kt_config, OSI_NONE, sizeof(kt_config));
kt_config.table_config.ctlr_sel = ctlr_sel;
kt_config.table_config.rw = OSI_LUT_READ;
kt_config.table_config.index = i;
if (osi_macsec_config_kt(osi_core, &kt_config) < 0) {
pr_err("%s: Failed to read KT\n", __func__);
*buf_p = buf;
return;
} else {
buf += scnprintf(buf, PAGE_SIZE, "%d.\t", i);
if ((kt_config.flags & OSI_LUT_FLAGS_ENTRY_VALID) !=
OSI_LUT_FLAGS_ENTRY_VALID) {
buf += scnprintf(buf, PAGE_SIZE, "Invalid\n");
memset(&kt_config, 0, sizeof(kt_config));
continue;
}
buf += scnprintf(buf, PAGE_SIZE, "SAK: 0x");
for (j = 0; j < OSI_KEY_LEN_256; j++) {
buf += scnprintf(buf, PAGE_SIZE, "%02x",
kt_config.entry.sak[OSI_KEY_LEN_256 - 1 - j]);
}
buf += scnprintf(buf, PAGE_SIZE, " H: 0x");
for (j = 0; j < OSI_KEY_LEN_128; j++) {
buf += scnprintf(buf, PAGE_SIZE, "%02x",
kt_config.entry.h[OSI_KEY_LEN_128 - 1 - j]);
}
buf += scnprintf(buf, PAGE_SIZE, "\n");
}
}
*buf_p = buf;
}
/**
* @brief Shows the current macsec Tx key table
*
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer to print the current LUT configuration
*/
static ssize_t macsec_tx_kt_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
struct osi_core_priv_data *osi_core = pdata->osi_core;
char *start = buf;
const nveu32_t lut_max_index[MAX_MACSEC_IP_TYPES] = {
OSI_SA_LUT_MAX_INDEX,
OSI_SA_LUT_MAX_INDEX_T26X};
if (!netif_running(ndev)) {
dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n");
return 0;
}
buf += scnprintf(buf, PAGE_SIZE, "Tx:\n");
dump_kt(&buf, OSI_CTLR_SEL_TX, osi_core, 0U,
(lut_max_index[osi_core->macsec] <= MAX_SA_ENTRIES_PER_SYSFS_NODE?
lut_max_index[osi_core->macsec]:
MAX_SA_ENTRIES_PER_SYSFS_NODE - 1U));
return (buf - start);
}
/**
* @brief Shows the current macsec Tx key table from 33 to 65 indices
*
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer to print the current LUT configuration
*/
static ssize_t macsec_tx_kt_show_2(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
struct osi_core_priv_data *osi_core = pdata->osi_core;
char *start = buf;
const nveu32_t lut_max_index[MAX_MACSEC_IP_TYPES] = {
OSI_SA_LUT_MAX_INDEX,
OSI_SA_LUT_MAX_INDEX_T26X};
if (!netif_running(ndev)) {
dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n");
return 0;
}
if (lut_max_index[osi_core->macsec] <= MAX_SA_ENTRIES_PER_SYSFS_NODE) {
dev_err(pdata->dev, "2nd node of KT lut is not allowed for this platform\n");
return 0;
}
buf += scnprintf(buf, PAGE_SIZE, "Tx:\n");
dump_kt(&buf, OSI_CTLR_SEL_TX, osi_core, MAX_SA_ENTRIES_PER_SYSFS_NODE,
(2U * MAX_SA_ENTRIES_PER_SYSFS_NODE) - 1U);
return (buf - start);
}
/**
* @brief Shows the current macsec Tx key table from 66 to 96 indices
*
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer to print the current LUT configuration
*/
static ssize_t macsec_tx_kt_show_3(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
struct osi_core_priv_data *osi_core = pdata->osi_core;
char *start = buf;
const nveu32_t lut_max_index[MAX_MACSEC_IP_TYPES] = {
OSI_SA_LUT_MAX_INDEX,
OSI_SA_LUT_MAX_INDEX_T26X};
if (!netif_running(ndev)) {
dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n");
return 0;
}
if (lut_max_index[osi_core->macsec] <= (2U * MAX_SA_ENTRIES_PER_SYSFS_NODE)) {
dev_err(pdata->dev, "3rd node of KT lut is not allowed for this platform\n");
return 0;
}
buf += scnprintf(buf, PAGE_SIZE, "Tx:\n");
dump_kt(&buf, OSI_CTLR_SEL_TX, osi_core, (2U * MAX_SA_ENTRIES_PER_SYSFS_NODE),
lut_max_index[osi_core->macsec]);
return (buf - start);
}
/**
* @brief Shows the current macsec Rx key table
*
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer to print the current LUT configuration
*/
static ssize_t macsec_rx_kt_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
struct osi_core_priv_data *osi_core = pdata->osi_core;
char *start = buf;
const nveu32_t lut_max_index[MAX_MACSEC_IP_TYPES] = {
OSI_SA_LUT_MAX_INDEX,
OSI_SA_LUT_MAX_INDEX_T26X};
if (!netif_running(ndev)) {
dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n");
return 0;
}
buf += scnprintf(buf, PAGE_SIZE, "Rx:\n");
dump_kt(&buf, OSI_CTLR_SEL_RX, osi_core, 0U,
(lut_max_index[osi_core->macsec] <= MAX_SA_ENTRIES_PER_SYSFS_NODE?
lut_max_index[osi_core->macsec]:
MAX_SA_ENTRIES_PER_SYSFS_NODE - 1U));
return (buf - start);
}
/**
* @brief Shows the current macsec Rx key table from 33 to 65 indices
*
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer to print the current LUT configuration
*/
static ssize_t macsec_rx_kt_show_2(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
struct osi_core_priv_data *osi_core = pdata->osi_core;
char *start = buf;
const nveu32_t lut_max_index[MAX_MACSEC_IP_TYPES] = {
OSI_SA_LUT_MAX_INDEX,
OSI_SA_LUT_MAX_INDEX_T26X};
if (!netif_running(ndev)) {
dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n");
return 0;
}
if (lut_max_index[osi_core->macsec] <= MAX_SA_ENTRIES_PER_SYSFS_NODE) {
dev_err(pdata->dev, "2nd node of KT lut is not allowed for this platform\n");
return 0;
}
buf += scnprintf(buf, PAGE_SIZE, "Rx:\n");
dump_kt(&buf, OSI_CTLR_SEL_RX, osi_core, MAX_SA_ENTRIES_PER_SYSFS_NODE,
(2U * MAX_SA_ENTRIES_PER_SYSFS_NODE) - 1U);
return (buf - start);
}
/**
* @brief Shows the current macsec Rx key table from 66 to 96 indices
*
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer to print the current LUT configuration
*/
static ssize_t macsec_rx_kt_show_3(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
struct osi_core_priv_data *osi_core = pdata->osi_core;
char *start = buf;
const nveu32_t lut_max_index[MAX_MACSEC_IP_TYPES] = {
OSI_SA_LUT_MAX_INDEX,
OSI_SA_LUT_MAX_INDEX_T26X};
if (!netif_running(ndev)) {
dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n");
return 0;
}
if (lut_max_index[osi_core->macsec] <= (2U * MAX_SA_ENTRIES_PER_SYSFS_NODE)) {
dev_err(pdata->dev, "3rd node of KT lut is not allowed for this platform\n");
return 0;
}
buf += scnprintf(buf, PAGE_SIZE, "Rx:\n");
dump_kt(&buf, OSI_CTLR_SEL_RX, osi_core, (2U * MAX_SA_ENTRIES_PER_SYSFS_NODE),
lut_max_index[osi_core->macsec]);
return (buf - start);
}
#define KEY_FMT "%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x"
/**
* @brief Set the Key table
*
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer which contains the desired LUT configuration
* @param[in] size: size of buffer
*
* @return size of buffer.
*/
static ssize_t macsec_kt_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
struct osi_core_priv_data *osi_core = pdata->osi_core;
struct crypto_cipher *tfm;
unsigned char hkey[OSI_KEY_LEN_128];
unsigned char zeros[OSI_KEY_LEN_128] = {0};
struct osi_macsec_kt_config kt_config = {0};
int temp[OSI_KEY_LEN_256] = {0};
unsigned char sak[OSI_KEY_LEN_256] = {0};
const nveu32_t lut_max_index[MAX_MACSEC_IP_TYPES] = {
OSI_SA_LUT_MAX_INDEX,
OSI_SA_LUT_MAX_INDEX_T26X};
int valid, index, ctlr, key256bit;
int i, ret, bufp = 0;
if (!netif_running(ndev)) {
dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n");
return size;
}
ret = sscanf(buf, "%d %d %d %d" KEY_FMT"%n",
&valid, &index, &ctlr, &key256bit,
&temp[0], &temp[1], &temp[2], &temp[3],
&temp[4], &temp[5], &temp[6], &temp[7],
&temp[8], &temp[9], &temp[10], &temp[11],
&temp[12], &temp[13], &temp[14], &temp[15], &bufp);
if (ret != 20) {
dev_err(pdata->dev, "Failed to parse key table arguments\n");
goto exit;
}
if (key256bit == 1) {
ret = sscanf(buf+bufp, KEY_FMT,
&temp[16], &temp[17], &temp[18], &temp[19],
&temp[20], &temp[21], &temp[22], &temp[23],
&temp[24], &temp[25], &temp[26], &temp[27],
&temp[28], &temp[29], &temp[30], &temp[31]);
if (ret != 16) {
dev_err(pdata->dev, "Failed to parse key table arguments\n");
goto exit;
}
}
if ((index > lut_max_index[osi_core->macsec]) ||
(valid != OSI_ENABLE && valid != OSI_DISABLE) ||
(ctlr != OSI_CTLR_SEL_TX && ctlr != OSI_CTLR_SEL_RX)) {
dev_err(pdata->dev, "%s: Invalid inputs\n", __func__);
goto exit;
}
kt_config.table_config.ctlr_sel = ctlr;
kt_config.table_config.rw = OSI_LUT_WRITE;
kt_config.table_config.index = index;
for (i = 0; i < OSI_KEY_LEN_128; i++) {
sak[i] = (unsigned char)temp[i];
}
if (key256bit == 1) {
for (i = OSI_KEY_LEN_128; i < OSI_KEY_LEN_256; i++) {
sak[i] = (unsigned char)temp[i];
}
}
/* HKEY GENERATION */
tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC);
if (crypto_cipher_setkey(tfm, sak, OSI_KEY_LEN_128)) {
dev_err(pdata->dev,"%s: Failed to set cipher key for H generation",
__func__);
goto exit;
}
crypto_cipher_encrypt_one(tfm, hkey, zeros);
crypto_free_cipher(tfm);
for (i = 0; i < OSI_KEY_LEN_128; i++) {
kt_config.entry.h[i] = hkey[OSI_KEY_LEN_128 - 1 - i];
}
if (key256bit == 1) {
for (i = 0; i < OSI_KEY_LEN_256; i++) {
kt_config.entry.sak[i] = sak[OSI_KEY_LEN_256 - 1 - i];
}
} else {
for (i = 0; i < OSI_KEY_LEN_128; i++) {
kt_config.entry.sak[i] = sak[OSI_KEY_LEN_128 - 1 - i];
}
}
if (valid) {
kt_config.flags |= OSI_LUT_FLAGS_ENTRY_VALID;
}
ret = osi_macsec_config_kt(osi_core, &kt_config);
if (ret < 0) {
dev_err(pdata->dev,"%s: Failed to set SAK", __func__);
goto exit;
} else {
dev_err(pdata->dev,"%s: Added KT LUT idx: %d", __func__,
kt_config.table_config.index);
}
exit:
return size;
}
/**
* @brief Sysfs attribute for MACsec key table (Store new key)
*
*/
static DEVICE_ATTR(macsec_kt, (S_IRUGO | S_IWUSR),
NULL,
macsec_kt_store);
/**
* @brief Sysfs attribute for MACsec key table (show Tx table)
*
*/
static DEVICE_ATTR(macsec_tx_kt, (S_IRUGO | S_IWUSR),
macsec_tx_kt_show,
NULL);
/**
* @brief Sysfs attribute for MACsec key table (show Rx table)
*
*/
static DEVICE_ATTR(macsec_rx_kt, (S_IRUGO | S_IWUSR),
macsec_rx_kt_show,
NULL);
/**
* @brief Sysfs attribute for MACsec key table (show Tx table)
*
*/
static DEVICE_ATTR(macsec_tx_kt_2, (S_IRUGO | S_IWUSR),
macsec_tx_kt_show_2,
NULL);
/**
* @brief Sysfs attribute for MACsec key table (show Rx table)
*
*/
static DEVICE_ATTR(macsec_rx_kt_2, (S_IRUGO | S_IWUSR),
macsec_rx_kt_show_2,
NULL);
/**
* @brief Sysfs attribute for MACsec key table (show Tx table)
*
*/
static DEVICE_ATTR(macsec_tx_kt_3, (S_IRUGO | S_IWUSR),
macsec_tx_kt_show_3,
NULL);
/**
* @brief Sysfs attribute for MACsec key table (show Rx table)
*
*/
static DEVICE_ATTR(macsec_rx_kt_3, (S_IRUGO | S_IWUSR),
macsec_rx_kt_show_3,
NULL);
#endif /* MACSEC_KEY_PROGRAM */
static void dump_sc_state_lut(char **buf_p, unsigned short ctlr_sel,
struct osi_core_priv_data *osi_core)
{
struct osi_macsec_lut_config lut_config = {0};
char *buf = *buf_p;
int i;
const unsigned int sc_lut_max_index[MAX_MACSEC_IP_TYPES] = {
OSI_SC_INDEX_MAX,
OSI_SC_INDEX_MAX_T26X};
for (i = 0; i <= sc_lut_max_index[osi_core->macsec]; i++) {
memset(&lut_config, OSI_NONE, sizeof(lut_config));
lut_config.table_config.ctlr_sel = ctlr_sel;
lut_config.table_config.rw = OSI_LUT_READ;
lut_config.table_config.index = i;
lut_config.lut_sel = OSI_LUT_SEL_SC_STATE;
if (osi_macsec_config_lut(osi_core, &lut_config) < 0) {
pr_err("%s: Failed to read BYP LUT\n", __func__);
*buf_p = buf;
return;
} else {
buf += scnprintf(buf, PAGE_SIZE, "%d.\tcurr_an: %d\n",
i, lut_config.sc_state_out.curr_an);
}
}
*buf_p = buf;
}
/**
* @brief Shows the current SC state LUT configuration
*
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer to print the current LUT configuration
*/
static ssize_t macsec_sc_state_lut_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
struct osi_core_priv_data *osi_core = pdata->osi_core;
char *start = buf;
if (!netif_running(ndev)) {
dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n");
return 0;
}
buf += scnprintf(buf, PAGE_SIZE, "Tx:\n");
dump_sc_state_lut(&buf, OSI_CTLR_SEL_TX, osi_core);
buf += scnprintf(buf, PAGE_SIZE, "Rx:\n");
dump_sc_state_lut(&buf, OSI_CTLR_SEL_RX, osi_core);
return (buf - start);
}
/**
* @brief Set the SC state LUT configuration
*
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer which contains the desired LUT configuration
* @param[in] size: size of buffer
*
* @return size of buffer.
*/
static ssize_t macsec_sc_state_lut_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
struct osi_core_priv_data *osi_core = pdata->osi_core;
struct osi_macsec_lut_config lut_config;
int index, ctlr;
int ret;
nveu32_t curr_an;
const unsigned int sc_lut_max_index[MAX_MACSEC_IP_TYPES] = {
OSI_SC_INDEX_MAX,
OSI_SC_INDEX_MAX_T26X};
if (!netif_running(ndev)) {
dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n");
return size;
}
ret = sscanf(buf, "%d %d %d", &index, &ctlr, &curr_an);
if (ret < 3) {
dev_err(pdata->dev, "%s: Failed to parse inputs", __func__);
goto exit;
}
if ((index > sc_lut_max_index[osi_core->macsec]) ||
(ctlr != OSI_CTLR_SEL_TX && ctlr != OSI_CTLR_SEL_RX) ||
(curr_an >= OSI_MAX_NUM_SA)) {
dev_err(pdata->dev, "%s:Invalid inputs", __func__);
goto exit;
}
lut_config.table_config.ctlr_sel = ctlr;
lut_config.table_config.rw = OSI_LUT_WRITE;
lut_config.table_config.index = index;
lut_config.lut_sel = OSI_LUT_SEL_SC_STATE;
lut_config.sc_state_out.curr_an = curr_an;
if (osi_macsec_config_lut(osi_core, &lut_config) < 0) {
dev_err(dev, "%s: Failed to config SC STATE LUT\n", __func__);
goto exit;
} else {
dev_err(dev, "%s: Added SC STATE LUT idx: %d", __func__,
lut_config.table_config.index);
}
exit:
return size;
}
/**
* @brief Sysfs attribute for SC state LUT configuration
*
*/
static DEVICE_ATTR(macsec_sc_state_lut, (S_IRUGO | S_IWUSR),
macsec_sc_state_lut_show,
macsec_sc_state_lut_store);
static void dump_sa_state_lut(char **buf_p, unsigned short ctlr_sel,
struct osi_core_priv_data *osi_core)
{
struct osi_macsec_lut_config lut_config = {0};
char *buf = *buf_p;
int i;
const nveu32_t lut_max_index[MAX_MACSEC_IP_TYPES] = {
OSI_SA_LUT_MAX_INDEX,
OSI_SA_LUT_MAX_INDEX_T26X};
for (i = 0; i <= lut_max_index[osi_core->macsec]; i++) {
memset(&lut_config, OSI_NONE, sizeof(lut_config));
lut_config.table_config.ctlr_sel = ctlr_sel;
lut_config.table_config.rw = OSI_LUT_READ;
lut_config.table_config.index = i;
lut_config.lut_sel = OSI_LUT_SEL_SA_STATE;
if (osi_macsec_config_lut(osi_core, &lut_config) < 0) {
pr_err("%s: Failed to read BYP LUT\n", __func__);
goto exit;
}
switch (ctlr_sel) {
case OSI_CTLR_SEL_TX:
if ((lut_config.flags & OSI_LUT_FLAGS_ENTRY_VALID) ==
OSI_LUT_FLAGS_ENTRY_VALID) {
buf += scnprintf(buf, PAGE_SIZE,
"%d.\tnext_pn: %u\n", i,
lut_config.sa_state_out.next_pn);
} else {
buf += scnprintf(buf, PAGE_SIZE,
"%d.\tInvalid\n", i);
}
break;
case OSI_CTLR_SEL_RX:
buf += scnprintf(buf, PAGE_SIZE,
"%d.\tnext_pn: %u lowest_pn: %u\n", i,
lut_config.sa_state_out.next_pn,
lut_config.sa_state_out.lowest_pn);
break;
default:
goto exit;
}
}
exit:
*buf_p = buf;
}
/**
* @brief Shows the current SA state LUT configuration
*
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer to print the current LUT configuration
*/
static ssize_t macsec_sa_state_lut_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
struct osi_core_priv_data *osi_core = pdata->osi_core;
char *start = buf;
if (!netif_running(ndev)) {
dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n");
return 0;
}
buf += scnprintf(buf, PAGE_SIZE, "Tx:\n");
dump_sa_state_lut(&buf, OSI_CTLR_SEL_TX, osi_core);
buf += scnprintf(buf, PAGE_SIZE, "Rx:\n");
dump_sa_state_lut(&buf, OSI_CTLR_SEL_RX, osi_core);
return (buf - start);
}
/**
* @brief Set the SA state LUT configuration
*
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer which contains the desired LUT configuration
* @param[in] size: size of buffer
*
* @return size of buffer.
*/
static ssize_t macsec_sa_state_lut_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
struct osi_core_priv_data *osi_core = pdata->osi_core;
struct osi_macsec_lut_config lut_config;
int index, ctlr;
int ret;
unsigned int next_pn, lowest_pn;
const nveu32_t lut_max_index[MAX_MACSEC_IP_TYPES] = {
OSI_SA_LUT_MAX_INDEX,
OSI_SA_LUT_MAX_INDEX_T26X};
if (!netif_running(ndev)) {
dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n");
return size;
}
ret = sscanf(buf, "%d %d %u %u", &index, &ctlr, &next_pn, &lowest_pn);
if (ret < 4) {
dev_err(pdata->dev, "%s: Failed to parse inputs", __func__);
goto exit;
}
if ((index > lut_max_index[osi_core->macsec]) ||
(ctlr != OSI_CTLR_SEL_TX && ctlr != OSI_CTLR_SEL_RX)) {
dev_err(pdata->dev, "%s:Invalid inputs", __func__);
goto exit;
}
lut_config.flags = OSI_LUT_FLAGS_ENTRY_VALID;
lut_config.table_config.ctlr_sel = ctlr;
lut_config.table_config.rw = OSI_LUT_WRITE;
lut_config.table_config.index = index;
lut_config.sa_state_out.next_pn = next_pn;
lut_config.sa_state_out.lowest_pn = lowest_pn;
lut_config.lut_sel = OSI_LUT_SEL_SA_STATE;
if (osi_macsec_config_lut(osi_core, &lut_config) < 0) {
dev_err(dev, "%s: Failed to config SA STATE LUT\n", __func__);
goto exit;
} else {
dev_err(dev, "%s: Added SA STATE LUT idx: %d", __func__,
lut_config.table_config.index);
}
exit:
return size;
}
/**
* @brief Sysfs attribute for SA state LUT configuration
*
*/
static DEVICE_ATTR(macsec_sa_state_lut, (S_IRUGO | S_IWUSR),
macsec_sa_state_lut_show,
macsec_sa_state_lut_store);
static void dump_sc_param_lut(char **buf_p, unsigned short ctlr_sel,
struct osi_core_priv_data *osi_core,
unsigned short start_index,
unsigned short end_index)
{
struct osi_macsec_lut_config lut_config = {0};
char *buf = *buf_p;
char sci_zero[8] = {0};
unsigned int i;
for (i = start_index; i <= end_index; i++) {
memset(&lut_config, OSI_NONE, sizeof(lut_config));
lut_config.table_config.ctlr_sel = ctlr_sel;
lut_config.table_config.rw = OSI_LUT_READ;
lut_config.table_config.index = i;
lut_config.lut_sel = OSI_LUT_SEL_SC_PARAM;
if (osi_macsec_config_lut(osi_core, &lut_config) < 0) {
pr_err("%s: Failed to read SC PARAM LUT\n", __func__);
goto exit;
}
switch (ctlr_sel) {
case OSI_CTLR_SEL_TX:
if (memcmp(lut_config.sc_param_out.sci, sci_zero, 8) != 0) {
buf += scnprintf(buf, PAGE_SIZE,
"%d.\tkey_idx: %d pn_max: %u "
"pn_threshold: %u tci %01x vlan_clr %01x "
"encrypt %01x offset %01x sci: " SCI_FMT,
i, lut_config.sc_param_out.key_index_start,
lut_config.sc_param_out.pn_max,
lut_config.sc_param_out.pn_threshold,
lut_config.sc_param_out.tci,
lut_config.sc_param_out.vlan_in_clear,
lut_config.sc_param_out.encrypt,
lut_config.sc_param_out.conf_offset,
lut_config.sc_param_out.sci[7],
lut_config.sc_param_out.sci[6],
lut_config.sc_param_out.sci[5],
lut_config.sc_param_out.sci[4],
lut_config.sc_param_out.sci[3],
lut_config.sc_param_out.sci[2],
lut_config.sc_param_out.sci[1],
lut_config.sc_param_out.sci[0]);
} else {
buf += scnprintf(buf, PAGE_SIZE,
"%d.\tkey_idx: %d pn_max: %u "
"pn_threshold: %u tci %01x vlan_clr %01x "
"encrypt %01x offset %01x sci: X",
i, lut_config.sc_param_out.key_index_start,
lut_config.sc_param_out.pn_max,
lut_config.sc_param_out.pn_threshold,
lut_config.sc_param_out.tci,
lut_config.sc_param_out.vlan_in_clear,
lut_config.sc_param_out.encrypt,
lut_config.sc_param_out.conf_offset);
}
buf += scnprintf(buf, PAGE_SIZE, "\n");
break;
case OSI_CTLR_SEL_RX:
buf += scnprintf(buf, PAGE_SIZE,
"%d.\tkey_idx: %d pn_max: %u pn_window: %u "
"encrypt %01x offset %01x\n", i,
lut_config.sc_param_out.key_index_start,
lut_config.sc_param_out.pn_max,
lut_config.sc_param_out.pn_window,
lut_config.sc_param_out.encrypt,
lut_config.sc_param_out.conf_offset);
break;
default:
goto exit;
}
}
exit:
*buf_p = buf;
}
/**
* @brief Shows the current SC parameters Tx LUT configuration for
* indices above 24
*
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer to print the current LUT configuration
*/
static ssize_t macsec_sc_param_tx_lut_show_2(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
struct osi_core_priv_data *osi_core = pdata->osi_core;
char *start = buf;
const unsigned int sc_lut_max_index[MAX_MACSEC_IP_TYPES] = {
OSI_SC_INDEX_MAX,
OSI_SC_INDEX_MAX_T26X};
if (!netif_running(ndev)) {
dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n");
return 0;
}
if (sc_lut_max_index[osi_core->macsec] <= MAX_ENTRIES_PER_SYSFS_NODE) {
dev_err(pdata->dev, "2nd node of sc param lut is not allowed for this platform\n");
return 0;
}
buf += scnprintf(buf, PAGE_SIZE, "Tx:\n");
dump_sc_param_lut(&buf, OSI_CTLR_SEL_TX, osi_core, MAX_ENTRIES_PER_SYSFS_NODE + 1U,
sc_lut_max_index[osi_core->macsec]);
return (buf - start);
}
/**
* @brief Shows the current SC parameters Tx LUT configuration
*
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer to print the current LUT configuration
*/
static ssize_t macsec_sc_param_tx_lut_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
struct osi_core_priv_data *osi_core = pdata->osi_core;
char *start = buf;
const unsigned int sc_lut_max_index[MAX_MACSEC_IP_TYPES] = {
OSI_SC_INDEX_MAX,
OSI_SC_INDEX_MAX_T26X};
if (!netif_running(ndev)) {
dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n");
return 0;
}
buf += scnprintf(buf, PAGE_SIZE, "Tx:\n");
dump_sc_param_lut(&buf, OSI_CTLR_SEL_TX, osi_core, 0U,
((sc_lut_max_index[osi_core->macsec] > MAX_ENTRIES_PER_SYSFS_NODE) ?
MAX_ENTRIES_PER_SYSFS_NODE: sc_lut_max_index[osi_core->macsec]));
return (buf - start);
}
/**
* @brief Shows the current SC parameters Rx LUT configuration
*
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer to print the current LUT configuration
*/
static ssize_t macsec_sc_param_rx_lut_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
struct osi_core_priv_data *osi_core = pdata->osi_core;
char *start = buf;
const unsigned int sc_lut_max_index[MAX_MACSEC_IP_TYPES] = {
OSI_SC_INDEX_MAX,
OSI_SC_INDEX_MAX_T26X};
if (!netif_running(ndev)) {
dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n");
return 0;
}
buf += scnprintf(buf, PAGE_SIZE, "Rx:\n");
dump_sc_param_lut(&buf, OSI_CTLR_SEL_RX, osi_core, 0U,
sc_lut_max_index[osi_core->macsec]);
return (buf - start);
}
#define SC_PARAM_INPUTS_LEN 18
/**
* @brief Set the SC parameters LUT configuration
*
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer which contains the desired LUT configuration
* @param[in] size: size of buffer
*
* @return size of buffer.
*/
static ssize_t macsec_sc_param_lut_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
struct osi_core_priv_data *osi_core = pdata->osi_core;
struct osi_macsec_lut_config lut_config = {0};
int index, ctlr;
int ret, i, tci, vlan_clear, encrypt, offset;
int sci[OSI_SCI_LEN] = {0};
unsigned int pn_max, pn_threshold, key_index_start, pn_window;
const unsigned int sc_lut_max_index[MAX_MACSEC_IP_TYPES] = {
OSI_SC_INDEX_MAX,
OSI_SC_INDEX_MAX_T26X};
const unsigned int key_idx_max[MAX_MACSEC_IP_TYPES] = {
OSI_KEY_INDEX_MAX, OSI_KEY_INDEX_MAX_T26X };
if (!netif_running(ndev)) {
dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n");
return size;
}
ret = sscanf(buf, "%d %d %u %u %u %u %d %d %d %d" SCI_FMT,
&index, &ctlr,
&key_index_start, &pn_max, &pn_threshold, &pn_window,
&tci, &vlan_clear, &encrypt, &offset,
&sci[7], &sci[6], &sci[5], &sci[4],
&sci[3], &sci[2], &sci[1], &sci[0]);
if (ret < SC_PARAM_INPUTS_LEN) {
dev_err(pdata->dev, "%s: Failed to parse inputs", __func__);
goto exit;
}
if ((index > sc_lut_max_index[osi_core->macsec]) ||
(ctlr != OSI_CTLR_SEL_TX && ctlr != OSI_CTLR_SEL_RX) ||
(key_index_start > key_idx_max[osi_core->macsec]) ||
(pn_threshold > pn_max) || (encrypt > 1) ||
(offset > 2)) {
dev_err(pdata->dev, "%s:Invalid inputs", __func__);
goto exit;
}
lut_config.table_config.ctlr_sel = ctlr;
lut_config.table_config.rw = OSI_LUT_WRITE;
lut_config.table_config.index = index;
lut_config.lut_sel = OSI_LUT_SEL_SC_PARAM;
lut_config.sc_param_out.key_index_start = key_index_start;
lut_config.sc_param_out.pn_max = pn_max;
lut_config.sc_param_out.pn_threshold = pn_threshold;
lut_config.sc_param_out.pn_window = pn_window;
lut_config.sc_param_out.tci = (unsigned char)tci;
lut_config.sc_param_out.vlan_in_clear = (unsigned char)vlan_clear;
lut_config.sc_param_out.encrypt = (unsigned char)encrypt;
lut_config.sc_param_out.conf_offset = (unsigned char)offset;
for (i = 0; i < OSI_SCI_LEN; i++) {
lut_config.sc_param_out.sci[i] = (unsigned char)sci[i];
}
if (osi_macsec_config_lut(osi_core, &lut_config) < 0) {
dev_err(dev, "%s: Failed to config SC PARAM LUT\n", __func__);
goto exit;
} else {
dev_err(dev, "%s: Added SC PARAM LUT idx: %d", __func__,
lut_config.table_config.index);
}
exit:
return size;
}
/**
* @brief Sysfs attribute for SC param LUT configuration
*
*/
static DEVICE_ATTR(macsec_sc_param_lut, (S_IRUGO | S_IWUSR),
NULL,
macsec_sc_param_lut_store);
/**
* @brief Sysfs attribute for SC param Tx LUT configuration
*
*/
static DEVICE_ATTR(macsec_sc_param_tx_lut, (S_IRUGO | S_IWUSR),
macsec_sc_param_tx_lut_show,
NULL);
/**
* @brief Sysfs attribute for SC param Tx LUT configuration
*
*/
static DEVICE_ATTR(macsec_sc_param_tx_lut_2, (S_IRUGO | S_IWUSR),
macsec_sc_param_tx_lut_show_2,
NULL);
/**
* @brief Sysfs attribute for SC param Rx LUT configuration
*
*/
static DEVICE_ATTR(macsec_sc_param_rx_lut, (S_IRUGO | S_IWUSR),
macsec_sc_param_rx_lut_show,
NULL);
/**
* @brief Shows the current MACsec irq stats
*
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer to print the current MACsec irq stats
*/
static ssize_t macsec_irq_stats_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
struct osi_core_priv_data *osi_core = pdata->osi_core;
struct osi_macsec_irq_stats *stats = &osi_core->macsec_irq_stats;
return scnprintf(buf, PAGE_SIZE,
"tx_dbg_capture_done:\t%llu\n"
"tx_mtu_check_fail :\t%llu\n"
"tx_mac_crc_error :\t%llu\n"
"tx_sc_an_not_valid :\t%llu\n"
"tx_aes_gcm_buf_ovf :\t%llu\n"
"tx_lkup_miss :\t%llu\n"
"tx_uninit_key_slot :\t%llu\n"
"tx_pn_threshold :\t%llu\n"
"tx_pn_exhausted :\t%llu\n"
"rx_dbg_capture_done:\t%llu\n"
"rx_icv_err_threshold :\t%llu\n"
"rx_replay_error :\t%llu\n"
"rx_mtu_check_fail :\t%llu\n"
"rx_mac_crc_error :\t%llu\n"
"rx_aes_gcm_buf_ovf :\t%llu\n"
"rx_lkup_miss :\t%llu\n"
"rx_uninit_key_slot :\t%llu\n"
"rx_pn_exhausted :\t%llu\n"
"secure_reg_viol :\t%llu\n",
stats->tx_dbg_capture_done,
stats->tx_mtu_check_fail,
stats->tx_mac_crc_error,
stats->tx_sc_an_not_valid,
stats->tx_aes_gcm_buf_ovf,
stats->tx_lkup_miss,
stats->tx_uninit_key_slot,
stats->tx_pn_threshold,
stats->tx_pn_exhausted,
stats->rx_dbg_capture_done,
stats->rx_icv_err_threshold,
stats->rx_replay_error,
stats->rx_mtu_check_fail,
stats->rx_mac_crc_error,
stats->rx_aes_gcm_buf_ovf,
stats->rx_lkup_miss,
stats->rx_uninit_key_slot,
stats->rx_pn_exhausted,
stats->secure_reg_viol);
}
/**
* @brief Sysfs attribute for MACsec irq stats
*
*/
static DEVICE_ATTR(macsec_irq_stats, (S_IRUGO | S_IWUSR),
macsec_irq_stats_show,
NULL);
#endif /* MACSEC_SUPPORT */
/**
* @brief Shows the current driver setting for PHY iface mode
*
* Algorithm: Display the current PHY iface mode setting.
*
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer to store the current PHY iface mode
*
* @note MAC and PHY need to be initialized.
*/
static ssize_t ether_phy_iface_mode_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
struct osi_core_priv_data *osi_core = pdata->osi_core;
switch (osi_core->phy_iface_mode) {
case OSI_XFI_MODE_10G:
return scnprintf(buf, PAGE_SIZE, "XFI-10G\n");
case OSI_XFI_MODE_5G:
return scnprintf(buf, PAGE_SIZE, "XFI-5G\n");
case OSI_USXGMII_MODE_10G:
return scnprintf(buf, PAGE_SIZE, "USX-10G\n");
case OSI_USXGMII_MODE_5G:
return scnprintf(buf, PAGE_SIZE, "USX-5G\n");
default:
return scnprintf(buf, PAGE_SIZE, "XFI-10G\n");
}
}
/**
* @brief Set the user setting of PHY iface mode.
*
* Algorithm: This is used to set the user mode settings of PHY iface mode
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer which contains the user settings of PHY iface mode
* @param[in] size: size of buffer
*
* @note MAC and PHY need to be initialized.
*
* @return size of buffer.
*/
static ssize_t ether_phy_iface_mode_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
struct osi_core_priv_data *osi_core = pdata->osi_core;
if (netif_running(ndev)) {
dev_err(pdata->dev, "Not Allowed. Ether interface is up\n");
return size;
}
if (strncmp(buf, "XFI-10G", 7) == 0U) {
osi_core->phy_iface_mode = OSI_XFI_MODE_10G;
} else if (strncmp(buf, "XFI-5G", 6) == 0U) {
osi_core->phy_iface_mode = OSI_XFI_MODE_5G;
} else if (strncmp(buf, "USX-10G", 7) == 0U) {
osi_core->phy_iface_mode = OSI_USXGMII_MODE_10G;
} else if (strncmp(buf, "USX-5G", 6) == 0U) {
osi_core->phy_iface_mode = OSI_USXGMII_MODE_5G;
} else {
dev_err(pdata->dev,
"Invalid value passed. Valid values are XFI-10G/XFI-5G/USX-10G/USX-5G\n");
}
return size;
}
/**
* @brief Sysfs attribute for PHY iface Mode
*
*/
static DEVICE_ATTR(phy_iface_mode, (S_IRUGO | S_IWUSR),
ether_phy_iface_mode_show,
ether_phy_iface_mode_store);
/**
* @brief Shows the current driver setting for UPHY GBE mocd
*
* Algorithm: Display the current PTP mode setting.
*
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer to store the current PTP mode
*
* @note MAC and PHY need to be initialized.
*/
static ssize_t ether_uphy_gbe_mode_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
struct osi_core_priv_data *osi_core = pdata->osi_core;
switch (osi_core->uphy_gbe_mode) {
case OSI_GBE_MODE_25G:
return scnprintf(buf, PAGE_SIZE, "25G\n");
case OSI_GBE_MODE_10G:
return scnprintf(buf, PAGE_SIZE, "10G\n");
case OSI_GBE_MODE_5G:
return scnprintf(buf, PAGE_SIZE, "5G\n");
case OSI_GBE_MODE_2_5G:
return scnprintf(buf, PAGE_SIZE, "2_5G\n");
default:
return scnprintf(buf, PAGE_SIZE, "1G\n");
}
}
/**
* @brief Set the user setting of UPHY GBE mode.
*
* Algorithm: This is used to set the user mode settings of UPHY GBE mode
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer which contains the user settings of UPHY GBE mode
* @param[in] size: size of buffer
*
* @note MAC and PHY need to be initialized.
*
* @return size of buffer.
*/
static ssize_t ether_uphy_gbe_mode_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
struct osi_core_priv_data *osi_core = pdata->osi_core;
if (netif_running(ndev)) {
dev_err(pdata->dev, "Not Allowed. Ether interface is up\n");
return size;
}
if (strncmp(buf, "25G", 3) == 0U) {
osi_core->uphy_gbe_mode = OSI_GBE_MODE_25G;
} else if (strncmp(buf, "10G", 3) == 0U) {
osi_core->uphy_gbe_mode = OSI_GBE_MODE_10G;
} else if (strncmp(buf, "5G", 2) == 0U) {
osi_core->uphy_gbe_mode = OSI_GBE_MODE_5G;
} else if (strncmp(buf, "2_5G", 4) == 0U) {
osi_core->uphy_gbe_mode = OSI_GBE_MODE_2_5G;
} else if (strncmp(buf, "1G", 2) == 0U) {
osi_core->uphy_gbe_mode = OSI_GBE_MODE_1G;
} else {
dev_err(pdata->dev,
"Invalid value passed. Valid values are 10G or 5G\n");
}
return size;
}
/**
* @brief Sysfs attribute for UPHY GBE Mode
*
*/
static DEVICE_ATTR(uphy_gbe_mode, (S_IRUGO | S_IWUSR),
ether_uphy_gbe_mode_show,
ether_uphy_gbe_mode_store);
/**
* @brief Sysfs attribute for MAC loopback
*
*/
static DEVICE_ATTR(mac_loopback, (S_IRUGO | S_IWUSR),
ether_mac_loopback_show,
ether_mac_loopback_store);
/**
* @brief Shows the current setting of FRP Table
*
* Algorithm: Display the FRP table
*
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer to store the current MAC loopback setting
*
* @note MAC and PHY need to be initialized.
*/
static ssize_t ether_mac_frp_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
struct osi_core_priv_data *osi_core = pdata->osi_core;
struct osi_core_frp_entry *entry = NULL;
struct osi_core_frp_data *data = NULL;
int i = 0, j = 0, written = 0;
/* Write FRP table entries */
for (i = 0, j = 0; ((i < osi_core->frp_cnt) && (j < PAGE_SIZE)); i++) {
entry = &osi_core->frp_table[i];
data = &entry->data;
written = scnprintf((buf + j), (PAGE_SIZE - j),
"[%d] ID:%d MD:0x%x ME:0x%x AF:%d "
"RF:%d IM:%d NIC:%d FO:%d OKI:%d DCH:x%llx\n",
i, entry->frp_id, data->match_data,
data->match_en, data->accept_frame,
data->reject_frame, data->inverse_match,
data->next_ins_ctrl, data->frame_offset,
data->ok_index, data->dma_chsel);
//Ensure `written` is non-negative and within bounds
if ((written < 0) || (((unsigned long)j + (unsigned long)written) >= PAGE_SIZE)) {
// Prevent overflow and exit loop
break;
}
j += written;
}
return j;
}
/**
* @brief Sysfs attribute for FRP table show
*
*/
static DEVICE_ATTR(frp, 0644, ether_mac_frp_show, NULL);
/**
* @brief Shows the current setting of PTP mode
*
* Algorithm: Display the current PTP mode setting.
*
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer to store the current PTP mode
*
* @note MAC and PHY need to be initialized.
*/
static ssize_t ether_ptp_mode_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
return scnprintf(buf, PAGE_SIZE, "%s\n",
((pdata->osi_dma->ptp_flag & OSI_PTP_SYNC_MASTER) ==
OSI_PTP_SYNC_MASTER) ? "master" :
((pdata->osi_dma->ptp_flag & OSI_PTP_SYNC_SLAVE) ==
OSI_PTP_SYNC_SLAVE) ? "slave" : " ");
}
/**
* @brief Set the user setting of PTP mode
*
* Algorithm: This is used to set the user mode settings of PTP mode
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer which contains the user settings of PTP mode
* @param[in] size: size of buffer
*
* @note MAC and PHY need to be initialized.
*
* @return size of buffer.
*/
static ssize_t ether_ptp_mode_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
if (!netif_running(ndev)) {
dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n");
return size;
}
if (strncmp(buf, "master", 6) == 0U) {
pdata->osi_dma->ptp_flag &= ~(OSI_PTP_SYNC_MASTER |
OSI_PTP_SYNC_SLAVE);
pdata->osi_dma->ptp_flag |= OSI_PTP_SYNC_MASTER;
} else if (strncmp(buf, "slave", 5) == 0U) {
pdata->osi_dma->ptp_flag &= ~(OSI_PTP_SYNC_MASTER |
OSI_PTP_SYNC_SLAVE);
pdata->osi_dma->ptp_flag |= OSI_PTP_SYNC_SLAVE;
} else {
dev_err(pdata->dev,
"Invalid entry. Valid Entries are master or slave\n");
}
return size;
}
/**
* @brief Sysfs attribute for PTP MODE
*
*/
static DEVICE_ATTR(ptp_mode, (S_IRUGO | S_IWUSR),
ether_ptp_mode_show,
ether_ptp_mode_store);
/**
* @brief Shows the current setting of PTP sync method
*
* Algorithm: Display the current PTP sync method.
*
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer to store the current ptp sync method
*
* @note MAC and PHY need to be initialized.
*/
static ssize_t ether_ptp_sync_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
return scnprintf(buf, PAGE_SIZE, "%s\n",
((pdata->osi_dma->ptp_flag & OSI_PTP_SYNC_TWOSTEP) ==
OSI_PTP_SYNC_TWOSTEP) ? "twostep" :
((pdata->osi_dma->ptp_flag & OSI_PTP_SYNC_ONESTEP) ==
OSI_PTP_SYNC_ONESTEP) ? "onestep" : " ");
}
/**
* @brief Set the user setting of PTP sync method
*
* Algorithm: This is used to set the user mode settings of PTP sync method
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer which contains the user settings of PTP sync method
* @param[in] size: size of buffer
*
* @note MAC and PHY need to be initialized.
*
* @return size of buffer.
*/
static ssize_t ether_ptp_sync_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
if (!netif_running(ndev)) {
dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n");
return size;
}
if (strncmp(buf, "onestep", 7) == 0U) {
pdata->osi_dma->ptp_flag &= ~(OSI_PTP_SYNC_ONESTEP |
OSI_PTP_SYNC_TWOSTEP);
pdata->osi_dma->ptp_flag |= OSI_PTP_SYNC_ONESTEP;
} else if (strncmp(buf, "twostep", 7) == 0U) {
pdata->osi_dma->ptp_flag &= ~(OSI_PTP_SYNC_ONESTEP |
OSI_PTP_SYNC_TWOSTEP);
pdata->osi_dma->ptp_flag |= OSI_PTP_SYNC_TWOSTEP;
} else {
dev_err(pdata->dev,
"Invalid entry. Valid Entries are onestep or twostep\n");
}
return size;
}
/**
* @brief Sysfs attribute for PTP sync method
*
*/
static DEVICE_ATTR(ptp_sync, (S_IRUGO | S_IWUSR),
ether_ptp_sync_show,
ether_ptp_sync_store);
#ifdef ETHER_NVGRO
/**
* @brief Shows the current setting of NVGRO packet age threshold.
*
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer to store the current ptp sync method
*/
static ssize_t ether_nvgro_pkt_age_msec_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
return scnprintf(buf, PAGE_SIZE, "%u\n", pdata->pkt_age_msec);
}
/**
* @brief Set the user setting of NVGRO packet age threshold.
*
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer which contains the user settings of MAC loopback
* @param[in] size: size of buffer
*
* @return size of buffer.
*/
static ssize_t ether_nvgro_pkt_age_msec_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
int ret;
ret = kstrtou32(buf, 0, &pdata->pkt_age_msec);
if (ret < 0) {
dev_err(pdata->dev,
"Invalid nvgro pkt age msec input\n");
return -EINVAL;
}
return size;
}
/**
* @brief Sysfs attribute for NVGRO packet age threshold
*
*/
static DEVICE_ATTR(nvgro_pkt_age_msec, 0644,
ether_nvgro_pkt_age_msec_show,
ether_nvgro_pkt_age_msec_store);
/**
* @brief Shows the current setting of NVGRO purge timer interval..
*
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer to store the current ptp sync method
*/
static ssize_t ether_nvgro_timer_interval_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
return scnprintf(buf, PAGE_SIZE, "%u\n", pdata->nvgro_timer_intrvl);
}
/**
* @brief Set the user setting of NVGRO purge timer interval.
*
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer which contains the user settings of MAC loopback
* @param[in] size: size of buffer
*
* @return size of buffer.
*/
static ssize_t ether_nvgro_timer_interval_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
int ret;
ret = kstrtou32(buf, 0, &pdata->nvgro_timer_intrvl);
if (ret < 0) {
dev_err(pdata->dev,
"Invalid nvgro timer interval input\n");
return -EINVAL;
}
return size;
}
/**
* @brief Sysfs attribute for NVGRO purge timer interval
*
*/
static DEVICE_ATTR(nvgro_timer_interval, 0644,
ether_nvgro_timer_interval_show,
ether_nvgro_timer_interval_store);
/**
* @brief Shows NVGRO stats
*
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer to store the current ptp sync method
*/
static ssize_t ether_nvgro_stats_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
return scnprintf(buf, PAGE_SIZE, "dropped = %llu\n",
pdata->nvgro_dropped);
}
/**
* @brief Sysfs attribute for NVGRO stats.
*
*/
static DEVICE_ATTR(nvgro_stats, 0644,
ether_nvgro_stats_show, NULL);
/**
* @brief Dumps NVGRO queues.
*
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer to store the current ptp sync method
*/
static ssize_t ether_nvgro_dump_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
struct sk_buff *p, *pp;
char *start = buf;
buf += scnprintf(buf, PAGE_SIZE, "MQ: ");
skb_queue_walk_safe(&pdata->mq, p, pp) {
buf += scnprintf(buf, PAGE_SIZE, "skb %p TTL %d IPID %u\n",
p, NAPI_GRO_CB(p)->free,
NAPI_GRO_CB(p)->flush_id);
}
buf += scnprintf(buf, PAGE_SIZE, "FQ: ");
skb_queue_walk_safe(&pdata->fq, p, pp) {
buf += scnprintf(buf, PAGE_SIZE, "skb %p TTL %d IPID %u\n",
p, NAPI_GRO_CB(p)->free,
NAPI_GRO_CB(p)->flush_id);
}
return (buf - start);
}
/**
* @brief Sysfs attribute for NVGRO queue dump.
*
*/
static DEVICE_ATTR(nvgro_dump, 0644,
ether_nvgro_dump_show, NULL);
#endif
#if defined HSI_SUPPORT && defined(NV_VLTEST_BUILD) && (IS_ENABLED(CONFIG_TEGRA_HSIERRRPTINJ))
static int hsi_inject_err_fsi(unsigned int inst_id,
struct epl_error_report_frame error_report,
void *data)
{
struct ether_priv_data *pdata = (struct ether_priv_data *)data;
struct osi_core_priv_data *osi_core = pdata->osi_core;
struct osi_ioctl ioctl_data = {};
int ret;
ioctl_data.cmd = OSI_CMD_HSI_INJECT_ERR;
ioctl_data.arg1_u32 = error_report.error_code;
ret = osi_handle_ioctl(osi_core, &ioctl_data);
if (ret < 0)
dev_err(pdata->dev, "Fail to inject error\n");
return ret;
}
/**
* @brief Shows HSI feature enabled status
*
* Algorithm: Shows HSI feature enabled status
*
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer to store the current status
*/
static ssize_t hsi_enable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
struct osi_core_priv_data *osi_core = pdata->osi_core;
return scnprintf(buf, PAGE_SIZE, "%s\n",
(osi_core->hsi.enabled == OSI_ENABLE) ?
"enabled" : "disabled");
}
/**
* @brief Set HSI enabled status
*
* Algorithm: This is used to set HSI feature enable status
*
* @param[in] dev: Device data.
* @param[in] attr: Device attribute
* @param[in] buf: Buffer which contains the user input
* @param[in] size: size of buffer
*
* @return size of buffer.
*/
static ssize_t hsi_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct net_device *ndev = (struct net_device *)dev_get_drvdata(dev);
struct ether_priv_data *pdata = netdev_priv(ndev);
struct osi_core_priv_data *osi_core = pdata->osi_core;
struct osi_ioctl ioctl_data = {};
int ret = 0;
u32 inst_id = osi_core->instance_id;
u32 ip_type[3] = {IP_EQOS, IP_MGBE, IP_MGBE};
if (!netif_running(ndev)) {
dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n");
return size;
}
ioctl_data.cmd = OSI_CMD_HSI_CONFIGURE;
if (strncmp(buf, "enable", 6) == OSI_NONE) {
if (osi_core->use_virtualization != OSI_ENABLE) {
ioctl_data.arg1_u32 = OSI_ENABLE;
ret = osi_handle_ioctl(pdata->osi_core, &ioctl_data);
if (ret < 0) {
dev_err(pdata->dev, "Failed to enable HSI\n");
}
}
if (ret == 0) {
osi_core->hsi.enabled = OSI_ENABLE;
dev_info(pdata->dev, "HSI Enabled\n");
ret = hsierrrpt_reg_cb(ip_type[osi_core->mac], inst_id,
hsi_inject_err_fsi, pdata);
if (ret != 0) {
dev_err(pdata->dev, "Err inj callback registration failed: %d",
ret);
}
}
} else if (strncmp(buf, "disable", 7) == OSI_NONE) {
if (osi_core->use_virtualization != OSI_ENABLE) {
ioctl_data.arg1_u32 = OSI_DISABLE;
ret = osi_handle_ioctl(pdata->osi_core, &ioctl_data);
if (ret < 0) {
dev_err(pdata->dev, "Failed to disable HSI\n");
}
}
if (ret == 0) {
osi_core->hsi.enabled = OSI_DISABLE;
dev_info(pdata->dev, "HSI Disabled\n");
ret = hsierrrpt_dereg_cb(ip_type[osi_core->mac], inst_id);
if (ret != 0) {
dev_err(pdata->dev, "Err inj callback deregistration failed: %d",
ret);
}
}
} else {
dev_err(pdata->dev,
"Invalid entry. Valid Entries are enable/disable\n");
}
return size;
}
/**
* @brief Sysfs attribute for HSI enable
*
*/
static DEVICE_ATTR(hsi_enable, 0644,
hsi_enable_show,
hsi_enable_store);
#endif
#endif /* OSI_STRIPPED_LIB */
/**
* @brief Attributes for nvethernet sysfs
*/
static struct attribute *ether_sysfs_attrs[] = {
#ifndef OSI_STRIPPED_LIB
#ifdef OSI_DEBUG
&dev_attr_desc_dump_enable.attr,
#endif /* OSI_DEBUG */
#ifdef BW_TEST
&dev_attr_test_tx_bandwidth_dump_enable.attr,
#endif
&dev_attr_mac_loopback.attr,
&dev_attr_pcs_baser_fec.attr,
&dev_attr_ptp_mode.attr,
&dev_attr_ptp_sync.attr,
&dev_attr_frp.attr,
#ifdef MACSEC_SUPPORT
&dev_attr_macsec_irq_stats.attr,
&dev_attr_macsec_byp_lut.attr,
&dev_attr_macsec_sci_lut.attr,
&dev_attr_macsec_sci_lut_rx.attr,
&dev_attr_macsec_sci_lut_tx.attr,
&dev_attr_macsec_sci_lut_tx_2.attr,
#ifdef MACSEC_KEY_PROGRAM
&dev_attr_macsec_kt.attr,
&dev_attr_macsec_tx_kt.attr,
&dev_attr_macsec_rx_kt.attr,
&dev_attr_macsec_tx_kt_2.attr,
&dev_attr_macsec_rx_kt_2.attr,
&dev_attr_macsec_tx_kt_3.attr,
&dev_attr_macsec_rx_kt_3.attr,
#endif /* MACSEC_KEY_PROGRAM */
&dev_attr_macsec_sc_state_lut.attr,
&dev_attr_macsec_sa_state_lut.attr,
&dev_attr_macsec_sc_param_lut.attr,
&dev_attr_macsec_sc_param_tx_lut.attr,
&dev_attr_macsec_sc_param_tx_lut_2.attr,
&dev_attr_macsec_sc_param_rx_lut.attr,
&dev_attr_macsec_cipher.attr,
&dev_attr_macsec_enable.attr,
&dev_attr_macsec_an_status.attr,
&dev_attr_macsec_mmc_counters_tx.attr,
&dev_attr_macsec_mmc_counters_rx.attr,
#ifdef DEBUG_MACSEC
&dev_attr_macsec_loopback.attr,
&dev_attr_macsec_dbg_buffers.attr,
&dev_attr_macsec_dbg_events.attr,
#endif /* DEBUG_MACSEC */
#endif /* MACSEC_SUPPORT */
&dev_attr_uphy_gbe_mode.attr,
&dev_attr_phy_iface_mode.attr,
#ifdef ETHER_NVGRO
&dev_attr_nvgro_pkt_age_msec.attr,
&dev_attr_nvgro_timer_interval.attr,
&dev_attr_nvgro_stats.attr,
&dev_attr_nvgro_dump.attr,
#endif
#if defined HSI_SUPPORT && defined(NV_VLTEST_BUILD) && (IS_ENABLED(CONFIG_TEGRA_HSIERRRPTINJ))
&dev_attr_hsi_enable.attr,
#endif
#endif /* OSI_STRIPPED_LIB */
NULL
};
/**
* @brief Attributes for nvethernet sysfs without MACSEC
*/
static struct attribute *ether_sysfs_attrs_without_macsec[] = {
#ifndef OSI_STRIPPED_LIB
#ifdef OSI_DEBUG
&dev_attr_desc_dump_enable.attr,
#endif /* OSI_DEBUG */
#ifdef BW_TEST
&dev_attr_test_tx_bandwidth_dump_enable.attr,
#endif
&dev_attr_mac_loopback.attr,
&dev_attr_ptp_mode.attr,
&dev_attr_ptp_sync.attr,
&dev_attr_frp.attr,
&dev_attr_uphy_gbe_mode.attr,
&dev_attr_phy_iface_mode.attr,
#ifdef ETHER_NVGRO
&dev_attr_nvgro_pkt_age_msec.attr,
&dev_attr_nvgro_timer_interval.attr,
&dev_attr_nvgro_stats.attr,
&dev_attr_nvgro_dump.attr,
#endif
#if defined HSI_SUPPORT && defined(NV_VLTEST_BUILD) && (IS_ENABLED(CONFIG_TEGRA_HSIERRRPTINJ))
&dev_attr_hsi_enable.attr,
#endif
&dev_attr_mac_tx_q.attr,
#endif /* OSI_STRIPPED_LIB */
NULL
};
/**
* @brief Ethernet sysfs attribute group
*/
static struct attribute_group ether_attribute_group = {
.name = "nvethernet",
.attrs = ether_sysfs_attrs,
};
/**
* @brief Ethernet sysfs attribute group without macsec
*/
static struct attribute_group ether_attribute_group_wo_macsec = {
.name = "nvethernet",
.attrs = ether_sysfs_attrs_without_macsec,
};
#ifndef OSI_STRIPPED_LIB
#ifdef CONFIG_DEBUG_FS
static char *timestamp_system_source(unsigned int source)
{
switch (source) {
case 1:
return "Internal";
case 2:
return "External";
case 3:
return "Internal and External";
case 0:
return "Reserved";
}
return "None";
}
static char *active_phy_selected_interface(unsigned int act_phy_sel)
{
switch (act_phy_sel) {
case 0:
return "GMII or MII";
case 1:
return "RGMII";
case 2:
return "SGMII";
case 3:
return "TBI";
case 4:
return "RMII";
case 5:
return "RTBI";
case 6:
return "SMII";
case 7:
return "RevMII";
}
return "None";
}
static char *mtl_fifo_size(unsigned int fifo_size)
{
switch (fifo_size) {
case 0:
return "128 Bytes";
case 1:
return "256 Bytes";
case 2:
return "512 Bytes";
case 3:
return "1KB";
case 4:
return "2KB";
case 5:
return "4KB";
case 6:
return "8KB";
case 7:
return "16KB";
case 8:
return "32KB";
case 9:
return "64KB";
case 10:
return "128KB";
case 11:
return "256KB";
default:
return "Reserved";
}
}
static char *address_width(unsigned int val)
{
switch (val) {
case 0:
return "32";
case 1:
return "40";
case 2:
return "48";
default:
return "Reserved";
}
}
static char *hash_table_size(unsigned int size)
{
switch (size) {
case 0:
return "No Hash Table";
case 1:
return "64";
case 2:
return "128";
case 3:
return "256";
default:
return "Invalid size";
}
}
static char *num_vlan_filters(unsigned int filters)
{
switch (filters) {
case 0:
return "Zero";
case 1:
return "4";
case 2:
return "8";
case 3:
return "16";
case 4:
return "24";
case 5:
return "32";
default:
return "Unknown";
}
}
static char *max_frp_bytes(unsigned int bytes)
{
switch (bytes) {
case 0:
return "64 Bytes";
case 1:
return "128 Bytes";
case 2:
return "256 Bytes";
case 3:
return "Reserved";
default:
return "Invalid";
}
}
static char *max_frp_instructions(unsigned int entries)
{
switch (entries) {
case 0:
return "64";
case 1:
return "128";
case 2:
return "256";
case 3:
return "Reserved";
default:
return "Invalid";
}
}
static char *auto_safety_package(unsigned int pkg)
{
switch (pkg) {
case 0:
return "No Safety features selected";
case 1:
return "Only 'ECC protection for external memory' feature is selected";
case 2:
return "All the Automotive Safety features are selected without the 'Parity Port Enable for external interface' feature";
case 3:
return "All the Automotive Safety features are selected with the 'Parity Port Enable for external interface' feature";
default:
return "Invalid";
}
}
static char *tts_fifo_depth(unsigned int depth)
{
switch (depth) {
case 1:
return "1";
case 2:
return "2";
case 3:
return "4";
case 4:
return "8";
case 5:
return "16";
default:
return "Reserved";
}
}
static char *gate_ctl_depth(unsigned int depth)
{
switch (depth) {
case 0:
return "No Depth Configured";
case 1:
return "64";
case 2:
return "128";
case 3:
return "256";
case 4:
return "512";
case 5:
return "1024";
default:
return "Reserved";
}
}
static char *gate_ctl_width(unsigned int width)
{
switch (width) {
case 0:
return "Width not configured";
case 1:
return "16";
case 2:
return "20";
case 3:
return "24";
default:
return "Invalid";
}
}
static int ether_hw_features_read(struct seq_file *seq, void *v)
{
struct net_device *ndev = seq->private;
struct ether_priv_data *pdata = netdev_priv(ndev);
struct osi_core_priv_data *osi_core = pdata->osi_core;
struct osi_hw_features *hw_feat = &pdata->hw_feat;
if (!netif_running(ndev)) {
dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n");
return 0;
}
seq_printf(seq, "==============================\n");
seq_printf(seq, "\tHW features\n");
seq_printf(seq, "==============================\n");
seq_printf(seq, "\t10/100 Mbps: %s\n",
(hw_feat->mii_sel) ? "Y" : "N");
seq_printf(seq, "\tRGMII Mode: %s\n",
(hw_feat->rgmii_sel) ? "Y" : "N");
seq_printf(seq, "\tRMII Mode: %s\n",
(hw_feat->rmii_sel) ? "Y" : "N");
seq_printf(seq, "\t1000 Mpbs: %s\n",
(hw_feat->gmii_sel) ? "Y" : "N");
seq_printf(seq, "\tHalf duplex support: %s\n",
(hw_feat->hd_sel) ? "Y" : "N");
seq_printf(seq, "\tTBI/SGMII/RTBI PHY interface: %s\n",
(hw_feat->pcs_sel) ? "Y" : "N");
seq_printf(seq, "\tVLAN Hash Filtering: %s\n",
(hw_feat->vlan_hash_en) ? "Y" : "N");
seq_printf(seq, "\tMDIO interface: %s\n",
(hw_feat->sma_sel) ? "Y" : "N");
seq_printf(seq, "\tRemote Wake-Up Packet Detection: %s\n",
(hw_feat->rwk_sel) ? "Y" : "N");
seq_printf(seq, "\tMagic Packet Detection: %s\n",
(hw_feat->mgk_sel) ? "Y" : "N");
seq_printf(seq, "\tMAC Management Counters (MMC): %s\n",
(hw_feat->mmc_sel) ? "Y" : "N");
seq_printf(seq, "\tARP Offload: %s\n",
(hw_feat->arp_offld_en) ? "Y" : "N");
seq_printf(seq, "\tIEEE 1588 Timestamp Support: %s\n",
(hw_feat->ts_sel) ? "Y" : "N");
seq_printf(seq, "\tEnergy Efficient Ethernet (EEE) Support: %s\n",
(hw_feat->eee_sel) ? "Y" : "N");
seq_printf(seq, "\tTransmit TCP/IP Checksum Insertion Support: %s\n",
(hw_feat->tx_coe_sel) ? "Y" : "N");
seq_printf(seq, "\tReceive TCP/IP Checksum Support: %s\n",
(hw_feat->rx_coe_sel) ? "Y" : "N");
seq_printf(seq, "\t (1 - 31) MAC Address registers: %s\n",
(hw_feat->mac_addr_sel) ? "Y" : "N");
seq_printf(seq, "\t(32 - 63) MAC Address Registers: %s\n",
(hw_feat->mac_addr32_sel) ? "Y" : "N");
seq_printf(seq, "\t(64 - 127) MAC Address Registers: %s\n",
(hw_feat->mac_addr64_sel) ? "Y" : "N");
seq_printf(seq, "\tTimestamp System Time Source: %s\n",
timestamp_system_source(hw_feat->tsstssel));
seq_printf(seq, "\tSource Address or VLAN Insertion Enable: %s\n",
(hw_feat->sa_vlan_ins) ? "Y" : "N");
seq_printf(seq, "\tActive PHY selected Interface: %s\n",
active_phy_selected_interface(hw_feat->sa_vlan_ins));
seq_printf(seq, "\tVxLAN/NVGRE Support: %s\n",
(hw_feat->vxn) ? "Y" : "N");
seq_printf(seq, "\tDifferent Descriptor Cache Support: %s\n",
(hw_feat->ediffc) ? "Y" : "N");
seq_printf(seq, "\tEnhanced DMA Support: %s\n",
(hw_feat->edma) ? "Y" : "N");
seq_printf(seq, "\tMTL Receive FIFO Size: %s\n",
mtl_fifo_size(hw_feat->rx_fifo_size));
seq_printf(seq, "\tMTL Transmit FIFO Size: %s\n",
mtl_fifo_size(hw_feat->tx_fifo_size));
seq_printf(seq, "\tPFC Enable: %s\n",
(hw_feat->pfc_en) ? "Y" : "N");
seq_printf(seq, "\tOne-Step Timestamping Support: %s\n",
(hw_feat->ost_en) ? "Y" : "N");
seq_printf(seq, "\tPTP Offload Enable: %s\n",
(hw_feat->pto_en) ? "Y" : "N");
seq_printf(seq, "\tIEEE 1588 High Word Register Enable: %s\n",
(hw_feat->adv_ts_hword) ? "Y" : "N");
seq_printf(seq, "\tAXI Address width: %s\n",
address_width(hw_feat->addr_64));
seq_printf(seq, "\tDCB Feature Support: %s\n",
(hw_feat->dcb_en) ? "Y" : "N");
seq_printf(seq, "\tSplit Header Feature Support: %s\n",
(hw_feat->sph_en) ? "Y" : "N");
seq_printf(seq, "\tTCP Segmentation Offload Support: %s\n",
(hw_feat->tso_en) ? "Y" : "N");
seq_printf(seq, "\tDMA Debug Registers Enable: %s\n",
(hw_feat->dma_debug_gen) ? "Y" : "N");
seq_printf(seq, "\tAV Feature Enable: %s\n",
(hw_feat->av_sel) ? "Y" : "N");
seq_printf(seq, "\tRx Side Only AV Feature Enable: %s\n",
(hw_feat->rav_sel) ? "Y" : "N");
seq_printf(seq, "\tHash Table Size: %s\n",
hash_table_size(hw_feat->hash_tbl_sz));
seq_printf(seq, "\tTotal number of L3 or L4 Filters: %u\n",
hw_feat->l3l4_filter_num);
seq_printf(seq, "\tNumber of MTL Receive Queues: %u\n",
(hw_feat->rx_q_cnt + 1));
seq_printf(seq, "\tNumber of MTL Transmit Queues: %u\n",
(hw_feat->tx_q_cnt + 1));
seq_printf(seq, "\tNumber of Receive DMA channels: %u\n",
(hw_feat->rx_ch_cnt + 1));
seq_printf(seq, "\tNumber of Transmit DMA channels: %u\n",
(hw_feat->tx_ch_cnt + 1));
seq_printf(seq, "\tNumber of PPS outputs: %u\n",
hw_feat->pps_out_num);
seq_printf(seq, "\tNumber of Auxiliary Snapshot Inputs: %u\n",
hw_feat->aux_snap_num);
seq_printf(seq, "\tRSS Feature Enabled: %s\n",
(hw_feat->rss_en) ? "Y" : "N");
seq_printf(seq, "\tNumber of Traffic Classes: %u\n",
(hw_feat->num_tc + 1));
seq_printf(seq, "\tNumber of VLAN filters: %s\n",
num_vlan_filters(hw_feat->num_vlan_filters));
seq_printf(seq,
"\tQueue/Channel based VLAN tag insert on Tx Enable: %s\n",
(hw_feat->cbti_sel) ? "Y" : "N");
seq_printf(seq, "\tOne-Step for PTP over UDP/IP Feature Enable: %s\n",
(hw_feat->ost_over_udp) ? "Y" : "N");
seq_printf(seq, "\tDouble VLAN processing support: %s\n",
(hw_feat->double_vlan_en) ? "Y" : "N");
if (osi_core->mac_ver > OSI_EQOS_MAC_5_00) {
seq_printf(seq, "\tSupported Flexible Receive Parser: %s\n",
(hw_feat->frp_sel) ? "Y" : "N");
seq_printf(seq, "\tNumber of FRP Pipes: %u\n",
(hw_feat->num_frp_pipes + 1));
seq_printf(seq, "\tNumber of FRP Parsable Bytes: %s\n",
max_frp_bytes(hw_feat->max_frp_bytes));
seq_printf(seq, "\tNumber of FRP Instructions: %s\n",
max_frp_instructions(hw_feat->max_frp_entries));
seq_printf(seq, "\tAutomotive Safety Package: %s\n",
auto_safety_package(hw_feat->auto_safety_pkg));
seq_printf(seq, "\tTx Timestamp FIFO Depth: %s\n",
tts_fifo_depth(hw_feat->tts_fifo_depth));
seq_printf(seq, "\tEnhancements to Scheduling Traffic Support: %s\n",
(hw_feat->est_sel) ? "Y" : "N");
seq_printf(seq, "\tDepth of the Gate Control List: %s\n",
gate_ctl_depth(hw_feat->gcl_depth));
seq_printf(seq, "\tWidth of the Time Interval field in GCL: %s\n",
gate_ctl_width(hw_feat->gcl_width));
seq_printf(seq, "\tFrame Preemption Enable: %s\n",
(hw_feat->fpe_sel) ? "Y" : "N");
seq_printf(seq, "\tTime Based Scheduling Enable: %s\n",
(hw_feat->tbs_sel) ? "Y" : "N");
seq_printf(seq, "\tNumber of DMA channels enabled for TBS: %u\n",
(hw_feat->num_tbs_ch + 1));
}
return 0;
}
static int ether_hw_feat_open(struct inode *inode, struct file *file)
{
return single_open(file, ether_hw_features_read, inode->i_private);
}
static const struct file_operations ether_hw_features_fops = {
.owner = THIS_MODULE,
.open = ether_hw_feat_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int ether_desc_dump_read(struct seq_file *seq, void *v)
{
struct net_device *ndev = seq->private;
struct ether_priv_data *pdata = netdev_priv(ndev);
struct osi_dma_priv_data *osi_dma = pdata->osi_dma;
unsigned int num_chan = osi_dma->num_dma_chans;
struct osi_tx_ring *tx_ring = NULL;
struct osi_rx_ring *rx_ring = NULL;
struct osi_tx_desc *tx_desc = NULL;
struct osi_rx_desc *rx_desc = NULL;
unsigned int chan;
unsigned int i;
unsigned int j;
if (!netif_running(ndev)) {
dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n");
return 0;
}
for (i = 0; i < num_chan; i++) {
chan = osi_dma->dma_chans[i];
tx_ring = osi_dma->tx_ring[chan];
rx_ring = osi_dma->rx_ring[chan];
seq_printf(seq, "\n\tDMA Tx channel %u descriptor dump\n",
chan);
seq_printf(seq, "\tcurrent Tx idx = %u, clean idx = %u\n",
tx_ring->cur_tx_idx, tx_ring->clean_idx);
for (j = 0; j < osi_dma->tx_ring_sz; j++) {
tx_desc = tx_ring->tx_desc + j;
seq_printf(seq, "[%03u %p %#llx] = %#x:%#x:%#x:%#x\n",
j, tx_desc, virt_to_phys(tx_desc),
tx_desc->tdes3, tx_desc->tdes2,
tx_desc->tdes1, tx_desc->tdes0);
}
seq_printf(seq, "\n\tDMA Rx channel %u descriptor dump\n",
chan);
seq_printf(seq, "\tcurrent Rx idx = %u, refill idx = %u\n",
rx_ring->cur_rx_idx, rx_ring->refill_idx);
for (j = 0; j < osi_dma->rx_ring_sz; j++) {
rx_desc = rx_ring->rx_desc + j;
seq_printf(seq, "[%03u %p %#llx] = %#x:%#x:%#x:%#x\n",
j, rx_desc, virt_to_phys(rx_desc),
rx_desc->rdes3, rx_desc->rdes2,
rx_desc->rdes1, rx_desc->rdes0);
}
}
return 0;
}
static int ether_desc_dump_open(struct inode *inode, struct file *file)
{
return single_open(file, ether_desc_dump_read, inode->i_private);
}
static const struct file_operations ether_desc_dump_fops = {
.owner = THIS_MODULE,
.open = ether_desc_dump_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int ether_register_dump_read(struct seq_file *seq, void *v)
{
struct net_device *ndev = seq->private;
struct ether_priv_data *pdata = netdev_priv(ndev);
struct osi_core_priv_data *osi_core = pdata->osi_core;
int max_address = 0x0;
int start_addr = 0x0;
struct osi_ioctl ioctl_data;
int ret = 0;
max_address = EOQS_MAX_REGISTER_ADDRESS;
/* Interface is not up so register dump not allowed */
if (!netif_running(ndev)) {
dev_err(pdata->dev, "Not Allowed. Ether interface is not up\n");
return -EBUSY;
}
while (1) {
ioctl_data.cmd = OSI_CMD_READ_REG;
ioctl_data.arg1_u32 = start_addr;
ret = osi_handle_ioctl(osi_core, &ioctl_data);
seq_printf(seq,
"\t Register offset %x value 0x%x\n",
start_addr,
ret);
start_addr += 4;
if (start_addr > max_address)
break;
}
return 0;
}
static int ether_register_dump_open(struct inode *inode, struct file *file)
{
return single_open(file, ether_register_dump_read, inode->i_private);
}
static const struct file_operations ether_register_dump_fops = {
.owner = THIS_MODULE,
.open = ether_register_dump_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int ether_create_debugfs(struct ether_priv_data *pdata)
{
char *buf;
int ret = 0;
buf = kasprintf(GFP_KERNEL, "nvethernet-%s", pdata->ndev->name);
if (!buf)
return -ENOMEM;
pdata->dbgfs_dir = debugfs_create_dir(buf, NULL);
if (!pdata->dbgfs_dir || IS_ERR(pdata->dbgfs_dir)) {
netdev_err(pdata->ndev,
"failed to create debugfs directory\n");
ret = -ENOMEM;
goto exit;
}
pdata->dbgfs_hw_feat = debugfs_create_file("hw_features", S_IRUGO,
pdata->dbgfs_dir,
pdata->ndev,
&ether_hw_features_fops);
if (!pdata->dbgfs_hw_feat) {
netdev_err(pdata->ndev,
"failed to create HW features debugfs\n");
debugfs_remove_recursive(pdata->dbgfs_dir);
ret = -ENOMEM;
goto exit;
}
pdata->dbgfs_desc_dump = debugfs_create_file("descriptors_dump",
S_IRUGO,
pdata->dbgfs_dir,
pdata->ndev,
&ether_desc_dump_fops);
if (!pdata->dbgfs_desc_dump) {
netdev_err(pdata->ndev,
"failed to create descriptor dump debugfs\n");
debugfs_remove_recursive(pdata->dbgfs_dir);
ret = -ENOMEM;
goto exit;
}
pdata->dbgfs_reg_dump = debugfs_create_file("register_dump", S_IRUGO,
pdata->dbgfs_dir,
pdata->ndev,
&ether_register_dump_fops);
if (!pdata->dbgfs_reg_dump) {
netdev_err(pdata->ndev,
"failed to create rgister dump debugfs\n");
debugfs_remove_recursive(pdata->dbgfs_dir);
ret = -ENOMEM;
goto exit;
}
debugfs_create_u64("suspend_profile_time", 0644, pdata->dbgfs_dir,
&pdata->suspend_profile_time);
debugfs_create_u64("resume_profile_time", 0644, pdata->dbgfs_dir,
&pdata->resume_profile_time);
exit:
kfree(buf);
return ret;
}
static void ether_remove_debugfs(struct ether_priv_data *pdata)
{
debugfs_remove_recursive(pdata->dbgfs_dir);
}
#endif /* CONFIG_DEBUG_FS */
#endif /* OSI_STRIPPED_LIB */
int ether_sysfs_register(struct ether_priv_data *pdata)
{
struct device *dev = pdata->dev;
int ret = 0;
#ifndef OSI_STRIPPED_LIB
#ifdef CONFIG_DEBUG_FS
if (debugfs_initialized()) {
ret = ether_create_debugfs(pdata);
if (ret < 0)
return ret;
}
#endif
#endif /* OSI_STRIPPED_LIB */
/* Create nvethernet sysfs group under /sys/devices/<ether_device>/ */
if (pdata->macsec_pdata != NULL) {
ret = sysfs_create_group(&dev->kobj, &ether_attribute_group);
} else {
ret = sysfs_create_group(&dev->kobj, &ether_attribute_group_wo_macsec);
}
return ret;
}
void ether_sysfs_unregister(struct ether_priv_data *pdata)
{
struct device *dev = pdata->dev;
#ifndef OSI_STRIPPED_LIB
#ifdef CONFIG_DEBUG_FS
ether_remove_debugfs(pdata);
#endif
#endif /* OSI_STRIPPED_LIB */
/* Remove nvethernet sysfs group under /sys/devices/<ether_device>/ */
if (pdata->macsec_pdata != NULL) {
sysfs_remove_group(&dev->kobj, &ether_attribute_group);
} else {
sysfs_remove_group(&dev->kobj, &ether_attribute_group_wo_macsec);
}
}
MODULE_AUTHOR("NVIDIA Corporation");
MODULE_DESCRIPTION("Mac/Macsec Sysfs driver");
MODULE_LICENSE("GPL");
#if defined(NV_MODULE_IMPORT_NS_CALLS_STRINGIFY)
MODULE_IMPORT_NS(CRYPTO_INTERNAL);
#else
MODULE_IMPORT_NS("CRYPTO_INTERNAL");
#endif