net: stmmac: boot up KPI changes

Read early ethernet dtsi flag to enable this feature.
Disable autoneg and configure MAC and PHY with 100 Mbps
link speed.
Read MAC addr, IPv4 and IPv6 addresses from kernel cmd
line parameters in order to assign these to ethernet
interface.

Change-Id: Ibeeebb73b4cbbd0950a1bff065297c4e58be671d
Acked-by: Nagarjuna Chaganti <nchagant@qti.qualcomm.com>
Signed-off-by: Suraj Jaiswal <jsuraj@codeaurora.org>
This commit is contained in:
Suraj Jaiswal 2021-03-26 12:59:48 +05:30
parent 14bc214f53
commit 36d251b783
7 changed files with 502 additions and 3 deletions

View File

@ -211,3 +211,9 @@ Todo
----
Add more DRM drivers.
eipv4= [KNL] Sets ipv4 address at boot up for early ethernet.
eipv6= [KNL] Sets ipv6 address at boot up for early ethernet.
ermac= [KNL] Sets mac address at boot up for early ethernet.

View File

@ -5508,3 +5508,9 @@
memory, and other data can't be written using
xmon commands.
off xmon is disabled.
eipv4= [KNL] Sets ipv4 address at boot up for early ethernet.
eipv6= [KNL] Sets ipv6 address at boot up for early ethernet.
ermac= [KNL] Sets mac address at boot up for early ethernet.

View File

@ -22,6 +22,8 @@
#include <linux/tcp.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/rtnetlink.h>
#include "stmmac.h"
#include "stmmac_platform.h"
#include "dwmac-qcom-ethqos.h"
@ -117,6 +119,10 @@ bool phy_intr_en;
struct emac_emb_smmu_cb_ctx emac_emb_smmu_ctx = {0};
struct qcom_ethqos *pethqos;
static unsigned char dev_addr[ETH_ALEN] = {
0, 0x55, 0x7b, 0xb5, 0x7d, 0xf7};
static struct ip_params pparams = {"", "", "", ""};
static inline unsigned int dwmac_qcom_get_eth_type(unsigned char *buf)
{
return
@ -283,6 +289,168 @@ int ethqos_handle_prv_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
return ret;
}
static int __init set_early_ethernet_ipv4(char *ipv4_addr_in)
{
int ret = 1;
pparams.is_valid_ipv4_addr = false;
if (!ipv4_addr_in)
return ret;
strlcpy(pparams.ipv4_addr_str,
ipv4_addr_in, sizeof(pparams.ipv4_addr_str));
ETHQOSDBG("Early ethernet IPv4 addr: %s\n", pparams.ipv4_addr_str);
ret = in4_pton(pparams.ipv4_addr_str, -1,
(u8 *)&pparams.ipv4_addr.s_addr, -1, NULL);
if (ret != 1 || pparams.ipv4_addr.s_addr == 0) {
ETHQOSERR("Invalid ipv4 address programmed: %s\n",
ipv4_addr_in);
return ret;
}
pparams.is_valid_ipv4_addr = true;
return ret;
}
__setup("eipv4=", set_early_ethernet_ipv4);
static int __init set_early_ethernet_ipv6(char *ipv6_addr_in)
{
int ret = 1;
pparams.is_valid_ipv6_addr = false;
if (!ipv6_addr_in)
return ret;
strlcpy(pparams.ipv6_addr_str,
ipv6_addr_in, sizeof(pparams.ipv6_addr_str));
ETHQOSDBG("Early ethernet IPv6 addr: %s\n", pparams.ipv6_addr_str);
ret = in6_pton(pparams.ipv6_addr_str, -1,
(u8 *)&pparams.ipv6_addr.ifr6_addr.s6_addr32, -1, NULL);
if (ret != 1 || !pparams.ipv6_addr.ifr6_addr.s6_addr32) {
ETHQOSERR("Invalid ipv6 address programmed: %s\n",
ipv6_addr_in);
return ret;
}
pparams.is_valid_ipv6_addr = true;
return ret;
}
__setup("eipv6=", set_early_ethernet_ipv6);
static int __init set_early_ethernet_mac(char *mac_addr)
{
bool valid_mac = false;
pparams.is_valid_mac_addr = false;
if (!mac_addr)
return 1;
valid_mac = mac_pton(mac_addr, pparams.mac_addr);
if (!valid_mac)
goto fail;
valid_mac = is_valid_ether_addr(pparams.mac_addr);
if (!valid_mac)
goto fail;
pparams.is_valid_mac_addr = true;
return 0;
fail:
ETHQOSERR("Invalid Mac address programmed: %s\n", mac_addr);
return 1;
}
__setup("ermac=", set_early_ethernet_mac);
static int qcom_ethqos_add_ipaddr(struct ip_params *ip_info,
struct net_device *dev)
{
int res = 0;
struct ifreq ir;
struct sockaddr_in *sin = (void *)&ir.ifr_ifru.ifru_addr;
struct net *net = dev_net(dev);
if (!net || !net->genl_sock || !net->genl_sock->sk_socket) {
ETHQOSINFO("Sock is null, unable to assign ipv4 address\n");
return res;
}
/*For valid Ipv4 address*/
memset(&ir, 0, sizeof(ir));
memcpy(&sin->sin_addr.s_addr, &ip_info->ipv4_addr,
sizeof(sin->sin_addr.s_addr));
strlcpy(ir.ifr_ifrn.ifrn_name,
dev->name, sizeof(ir.ifr_ifrn.ifrn_name));
sin->sin_family = AF_INET;
sin->sin_port = 0;
res = inet_ioctl(net->genl_sock->sk_socket,
SIOCSIFADDR, (unsigned long)(void *)&ir);
if (res) {
ETHQOSERR("can't setup IPv4 address!: %d\r\n", res);
} else {
ETHQOSINFO("Assigned IPv4 address: %s\r\n",
ip_info->ipv4_addr_str);
#ifdef CONFIG_MSM_BOOT_TIME_MARKER
place_marker("M - Etherent Assigned IPv4 address");
#endif
}
return res;
}
static int qcom_ethqos_add_ipv6addr(struct ip_params *ip_info,
struct net_device *dev)
{
int ret = -EFAULT;
struct in6_ifreq ir6;
char *prefix;
struct net *net = dev_net(dev);
/*For valid IPv6 address*/
if (!net || !net->genl_sock || !net->genl_sock->sk_socket)
ETHQOSERR("Sock is null, unable to assign ipv6 address\n");
if (!net->ipv6.devconf_dflt) {
ETHQOSERR("ipv6.devconf_dflt is null, schedule wq\n");
schedule_delayed_work(&pethqos->ipv6_addr_assign_wq,
msecs_to_jiffies(1000));
return ret;
}
memset(&ir6, 0, sizeof(ir6));
memcpy(&ir6, &ip_info->ipv6_addr, sizeof(struct in6_ifreq));
ir6.ifr6_ifindex = dev->ifindex;
prefix = strnchr(ip_info->ipv6_addr_str,
strlen(ip_info->ipv6_addr_str), '/');
if (!prefix) {
ir6.ifr6_prefixlen = 0;
} else {
kstrtoul(prefix + 1, 0, (unsigned long *)&ir6.ifr6_prefixlen);
if (ir6.ifr6_prefixlen > 128)
ir6.ifr6_prefixlen = 0;
}
ret = inet6_ioctl(net->genl_sock->sk_socket,
SIOCSIFADDR, (unsigned long)(void *)&ir6);
if (ret) {
ETHQOSDBG("Can't setup IPv6 address!\r\n");
} else {
ETHQOSDBG("Assigned IPv6 address: %s\r\n",
ip_info->ipv6_addr_str);
#ifdef CONFIG_MSM_BOOT_TIME_MARKER
place_marker("M - Ethernet Assigned IPv6 address");
#endif
}
return ret;
}
static int rgmii_readl(struct qcom_ethqos *ethqos, unsigned int offset)
{
return readl(ethqos->rgmii_base + offset);
@ -1048,6 +1216,127 @@ void qcom_ethqos_request_phy_wol(struct plat_stmmacenet_data *plat)
}
}
static void qcom_ethqos_bringup_iface(struct work_struct *work)
{
struct platform_device *pdev = NULL;
struct net_device *ndev = NULL;
struct qcom_ethqos *ethqos =
container_of(work, struct qcom_ethqos, early_eth);
ETHQOSINFO("entry\n");
if (!ethqos)
return;
pdev = ethqos->pdev;
if (!pdev)
return;
ndev = platform_get_drvdata(pdev);
if (!ndev || netif_running(ndev))
return;
rtnl_lock();
if (dev_change_flags(ndev, ndev->flags | IFF_UP, NULL) < 0)
ETHQOSINFO("ERROR\n");
rtnl_unlock();
ETHQOSINFO("exit\n");
}
static void ethqos_is_ipv4_NW_stack_ready(struct work_struct *work)
{
struct delayed_work *dwork;
struct qcom_ethqos *ethqos;
struct platform_device *pdev = NULL;
struct net_device *ndev = NULL;
int ret;
ETHQOSINFO("\n");
dwork = container_of(work, struct delayed_work, work);
ethqos = container_of(dwork, struct qcom_ethqos, ipv4_addr_assign_wq);
if (!ethqos)
return;
pdev = ethqos->pdev;
if (!pdev)
return;
ndev = platform_get_drvdata(pdev);
ret = qcom_ethqos_add_ipaddr(&pparams, ndev);
if (ret)
return;
cancel_delayed_work_sync(&ethqos->ipv4_addr_assign_wq);
flush_delayed_work(&ethqos->ipv4_addr_assign_wq);
}
static void ethqos_is_ipv6_NW_stack_ready(struct work_struct *work)
{
struct delayed_work *dwork;
struct qcom_ethqos *ethqos;
struct platform_device *pdev = NULL;
struct net_device *ndev = NULL;
int ret;
ETHQOSINFO("\n");
dwork = container_of(work, struct delayed_work, work);
ethqos = container_of(dwork, struct qcom_ethqos, ipv6_addr_assign_wq);
if (!ethqos)
return;
pdev = ethqos->pdev;
if (!pdev)
return;
ndev = platform_get_drvdata(pdev);
ret = qcom_ethqos_add_ipv6addr(&pparams, ndev);
if (ret)
return;
cancel_delayed_work_sync(&ethqos->ipv6_addr_assign_wq);
flush_delayed_work(&ethqos->ipv6_addr_assign_wq);
}
static int ethqos_set_early_eth_param(struct stmmac_priv *priv,
struct qcom_ethqos *ethqos)
{
int ret = 0;
if (priv->plat && priv->plat->mdio_bus_data)
priv->plat->mdio_bus_data->phy_mask =
priv->plat->mdio_bus_data->phy_mask | DUPLEX_FULL | SPEED_100;
priv->plat->max_speed = SPEED_100;
priv->early_eth = ethqos->early_eth_enabled;
qcom_ethqos_add_ipaddr(&pparams, priv->dev);
if (pparams.is_valid_ipv4_addr) {
INIT_DELAYED_WORK(&ethqos->ipv4_addr_assign_wq,
ethqos_is_ipv4_NW_stack_ready);
ret = qcom_ethqos_add_ipaddr(&pparams, priv->dev);
if (ret)
schedule_delayed_work(&ethqos->ipv4_addr_assign_wq,
msecs_to_jiffies(1000));
}
if (pparams.is_valid_ipv6_addr) {
INIT_DELAYED_WORK(&ethqos->ipv6_addr_assign_wq,
ethqos_is_ipv6_NW_stack_ready);
ret = qcom_ethqos_add_ipv6addr(&pparams, priv->dev);
if (ret)
schedule_delayed_work(&ethqos->ipv6_addr_assign_wq,
msecs_to_jiffies(1000));
}
if (pparams.is_valid_mac_addr) {
ether_addr_copy(dev_addr, pparams.mac_addr);
memcpy(priv->dev->dev_addr, dev_addr, ETH_ALEN);
}
return ret;
}
static int qcom_ethqos_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
@ -1056,10 +1345,16 @@ static int qcom_ethqos_probe(struct platform_device *pdev)
struct qcom_ethqos *ethqos = NULL;
struct resource *res = NULL;
int ret;
struct net_device *ndev;
struct stmmac_priv *priv;
if (of_device_is_compatible(pdev->dev.of_node,
"qcom,emac-smmu-embedded"))
return emac_emb_smmu_cb_probe(pdev);
#ifdef CONFIG_MSM_BOOT_TIME_MARKER
place_marker("M - Ethernet probe start");
#endif
ret = stmmac_get_platform_resources(pdev, &stmmac_res);
if (ret)
return ret;
@ -1081,7 +1376,6 @@ static int qcom_ethqos_probe(struct platform_device *pdev)
return PTR_ERR(plat_dat);
}
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rgmii");
ethqos->rgmii_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(ethqos->rgmii_base)) {
@ -1102,6 +1396,22 @@ static int qcom_ethqos_probe(struct platform_device *pdev)
if (ret)
goto err_mem;
/*Initialize Early ethernet to false*/
ethqos->early_eth_enabled = false;
/*Check for valid mac, ip address to enable Early eth*/
if (pparams.is_valid_mac_addr &&
(pparams.is_valid_ipv4_addr || pparams.is_valid_ipv6_addr)) {
/* For 1000BASE-T mode, auto-negotiation is required and
* always used to establish a link.
* Configure phy and MAC in 100Mbps mode with autoneg
* disable as link up takes more time with autoneg
* enabled.
*/
ethqos->early_eth_enabled = true;
ETHQOSINFO("Early ethernet is enabled\n");
}
ethqos->speed = SPEED_10;
ethqos_update_rgmii_clk_and_bus_cfg(ethqos, SPEED_10);
ethqos_set_func_clk_en(ethqos);
@ -1168,6 +1478,19 @@ static int qcom_ethqos_probe(struct platform_device *pdev)
}
pethqos = ethqos;
ndev = dev_get_drvdata(&ethqos->pdev->dev);
priv = netdev_priv(ndev);
if (ethqos->early_eth_enabled) {
/* Initialize work*/
INIT_WORK(&ethqos->early_eth,
qcom_ethqos_bringup_iface);
/* Queue the work*/
queue_work(system_wq, &ethqos->early_eth);
/*Set early eth parameters*/
ethqos_set_early_eth_param(priv, ethqos);
}
return ret;
err_clk:
@ -1276,7 +1599,56 @@ static struct platform_driver qcom_ethqos_driver = {
.of_match_table = of_match_ptr(qcom_ethqos_match),
},
};
module_platform_driver(qcom_ethqos_driver);
static int __init qcom_ethqos_init_module(void)
{
int ret = 0;
ETHQOSINFO("\n");
ret = platform_driver_register(&qcom_ethqos_driver);
if (ret < 0) {
ETHQOSINFO("qcom-ethqos: Driver registration failed");
return ret;
}
ETHQOSINFO("\n");
return ret;
}
static void __exit qcom_ethqos_exit_module(void)
{
ETHQOSINFO("\n");
platform_driver_unregister(&qcom_ethqos_driver);
ETHQOSINFO("\n");
}
/*!
* \brief Macro to register the driver registration function.
*
* \details A module always begin with either the init_module or the function
* you specify with module_init call. This is the entry function for modules;
* it tells the kernel what functionality the module provides and sets up the
* kernel to run the module's functions when they're needed. Once it does this,
* entry function returns and the module does nothing until the kernel wants
* to do something with the code that the module provides.
*/
module_init(qcom_ethqos_init_module)
/*!
* \brief Macro to register the driver un-registration function.
*
* \details All modules end by calling either cleanup_module or the function
* you specify with the module_exit call. This is the exit function for modules;
* it undoes whatever entry function did. It unregisters the functionality
* that the entry function registered.
*/
module_exit(qcom_ethqos_exit_module)
MODULE_DESCRIPTION("Qualcomm ETHQOS driver");
MODULE_LICENSE("GPL v2");

View File

@ -6,6 +6,18 @@
//#include <linux/msm-bus.h>
#include <linux/inetdevice.h>
#include <linux/inet.h>
#include <net/addrconf.h>
#include <net/ipv6.h>
#include <net/inet_common.h>
#include <linux/uaccess.h>
#define QCOM_ETH_QOS_MAC_ADDR_LEN 6
#define QCOM_ETH_QOS_MAC_ADDR_STR_LEN 18
#define DRV_NAME "qcom-ethqos"
#define ETHQOSDBG(fmt, args...) \
pr_debug(DRV_NAME " %s:%d " fmt, __func__, __LINE__, ## args)
@ -162,6 +174,13 @@ struct qcom_ethqos {
int clks_suspended;
/* Structure which holds done and wait members */
struct completion clk_enable_done;
/* early ethernet parameters */
struct work_struct early_eth;
struct delayed_work ipv4_addr_assign_wq;
struct delayed_work ipv6_addr_assign_wq;
bool early_eth_enabled;
/* Key Performance Indicators */
bool print_kpi;
};
struct pps_cfg {
@ -191,6 +210,19 @@ struct pps_info {
int channel_no;
};
struct ip_params {
unsigned char mac_addr[QCOM_ETH_QOS_MAC_ADDR_LEN];
bool is_valid_mac_addr;
char link_speed[32];
bool is_valid_link_speed;
char ipv4_addr_str[32];
struct in_addr ipv4_addr;
bool is_valid_ipv4_addr;
char ipv6_addr_str[48];
struct in6_ifreq ipv6_addr;
bool is_valid_ipv6_addr;
};
int ethqos_init_reqgulators(struct qcom_ethqos *ethqos);
void ethqos_disable_regulators(struct qcom_ethqos *ethqos);
int ethqos_init_gpio(struct qcom_ethqos *ethqos);

View File

@ -22,6 +22,9 @@
#include <linux/net_tstamp.h>
#include <linux/reset.h>
#include <net/page_pool.h>
#ifdef CONFIG_MSM_BOOT_TIME_MARKER
#include <soc/qcom/boot_stats.h>
#endif
struct stmmac_resources {
void __iomem *addr;
@ -215,7 +218,8 @@ struct stmmac_priv {
void __iomem *mmcaddr;
void __iomem *ptpaddr;
unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)];
bool boot_kpi;
bool early_eth;
#ifdef CONFIG_DEBUG_FS
struct dentry *dbgfs_dir;
#endif

View File

@ -117,6 +117,8 @@ static void stmmac_exit_fs(struct net_device *dev);
#define STMMAC_COAL_TIMER(x) (jiffies + usecs_to_jiffies(x))
static void stmmac_set_speed100(struct phy_device *phydev);
/**
* stmmac_verify_args - verify the driver parameters.
* Description: it checks the driver parameters and set a default in case of
@ -867,14 +869,46 @@ static void stmmac_validate(struct phylink_config *config,
phylink_set(mask, 1000baseT_Half);
}
/* Early ethernet settings to bring up link in 100M,
* Auto neg Off with full duplex link.
*/
if (max_speed == SPEED_100 && priv->early_eth) {
priv->phydev->autoneg = AUTONEG_DISABLE;
priv->phydev->speed = SPEED_100;
priv->phydev->duplex = DUPLEX_FULL;
phylink_set(mac_supported, 100baseT_Full);
phylink_set(mac_supported, TP);
phylink_set(mac_supported, MII);
phylink_set(mac_supported, 10baseT_Full);
phylink_clear(mac_supported, Autoneg);
bitmap_and(supported, supported, mac_supported,
__ETHTOOL_LINK_MODE_MASK_NBITS);
bitmap_andnot(supported, supported, mask,
__ETHTOOL_LINK_MODE_MASK_NBITS);
phylink_clear(mac_supported, Autoneg);
bitmap_and(state->advertising, state->advertising, mac_supported,
__ETHTOOL_LINK_MODE_MASK_NBITS);
bitmap_andnot(state->advertising, state->advertising, mask,
__ETHTOOL_LINK_MODE_MASK_NBITS);
pr_info(" qcom-ethqos: %s early eth setting successful\n",
__func__);
stmmac_set_speed100(priv->phydev);
} else {
bitmap_and(supported, supported, mac_supported,
__ETHTOOL_LINK_MODE_MASK_NBITS);
bitmap_andnot(supported, supported, mask,
__ETHTOOL_LINK_MODE_MASK_NBITS);
bitmap_and(state->advertising, state->advertising,
mac_supported,
__ETHTOOL_LINK_MODE_MASK_NBITS);
bitmap_andnot(state->advertising, state->advertising, mask,
__ETHTOOL_LINK_MODE_MASK_NBITS);
}
}
static int stmmac_mac_link_state(struct phylink_config *config,
@ -970,6 +1004,12 @@ static void stmmac_mac_link_up(struct phylink_config *config,
priv->eee_enabled = stmmac_eee_init(priv);
stmmac_set_eee_pls(priv, priv->hw, true);
}
#ifdef CONFIG_MSM_BOOT_TIME_MARKER
if (phy->link == 1 && !priv->boot_kpi) {
place_marker("M - Ethernet is Ready.Link is UP");
priv->boot_kpi = true;
}
#endif
}
static const struct phylink_mac_ops stmmac_phylink_mac_ops = {
@ -1006,6 +1046,30 @@ static void stmmac_check_pcs_mode(struct stmmac_priv *priv)
}
}
static void stmmac_set_speed100(struct phy_device *phydev)
{
u16 bmcr_val, ctrl1000_val, adv_val;
/* Disable 1000M mode */
ctrl1000_val = phy_read(phydev, MII_CTRL1000);
ctrl1000_val &= ~(ADVERTISE_1000HALF | ADVERTISE_1000FULL);
phy_write(phydev, MII_CTRL1000, ctrl1000_val);
/* Disable 100M mode */
adv_val = phy_read(phydev, MII_ADVERTISE);
adv_val &= ~(ADVERTISE_100HALF);
phy_write(phydev, MII_ADVERTISE, adv_val);
/* Disable autoneg */
bmcr_val = phy_read(phydev, MII_BMCR);
bmcr_val &= ~(BMCR_ANENABLE);
phy_write(phydev, MII_BMCR, bmcr_val);
bmcr_val = phy_read(phydev, MII_BMCR);
bmcr_val |= BMCR_ANRESTART;
phy_write(phydev, MII_BMCR, bmcr_val);
}
/**
* stmmac_init_phy - PHY initialization
* @dev: net device structure
@ -1020,6 +1084,8 @@ static int stmmac_init_phy(struct net_device *dev)
struct device_node *node;
int ret;
priv->boot_kpi = false;
node = priv->plat->phylink_node;
if (node)
@ -1061,6 +1127,8 @@ static int stmmac_init_phy(struct net_device *dev)
priv->phydev->irq = PHY_POLL;
}
}
pr_info(" qcom-ethqos: %s early eth setting stmmac init\n",
__func__);
return ret;
}
@ -1938,6 +2006,10 @@ static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue)
} else {
priv->dev->stats.tx_packets++;
priv->xstats.tx_pkt_n++;
#ifdef CONFIG_MSM_BOOT_TIME_MARKER
if (priv->dev->stats.tx_packets == 1)
place_marker("M - Ethernet first packet transmitted");
#endif
}
stmmac_get_tx_hwtstamp(priv, p, skb);
}
@ -3719,6 +3791,10 @@ read_again:
napi_gro_receive(&ch->rx_napi, skb);
priv->dev->stats.rx_packets++;
#ifdef CONFIG_MSM_BOOT_TIME_MARKER
if (priv->dev->stats.rx_packets == 1)
place_marker("M - Ethernet first packet received");
#endif
priv->dev->stats.rx_bytes += len;
count++;
}

View File

@ -269,6 +269,9 @@ int stmmac_mdio_reset(struct mii_bus *bus)
#ifdef CONFIG_DWMAC_QCOM_ETHQOS
active_high = false;
#endif
if (priv->early_eth)
return 0;
#ifdef CONFIG_OF
if (priv->device->of_node) {
struct gpio_desc *reset_gpio;