mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-07 20:33:58 -04:00
Merge "net: stmmac: Add per queue ethtool stats"
This commit is contained in:
commit
b78437cae2
9 changed files with 701 additions and 5 deletions
|
|
@ -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.
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -91,6 +91,8 @@ struct stmmac_extra_stats {
|
|||
unsigned long threshold;
|
||||
unsigned long tx_pkt_n;
|
||||
unsigned long rx_pkt_n;
|
||||
unsigned long q_tx_pkt_n[MTL_MAX_TX_QUEUES];
|
||||
unsigned long q_rx_pkt_n[MTL_MAX_RX_QUEUES];
|
||||
unsigned long normal_irq_n;
|
||||
unsigned long rx_normal_irq_n;
|
||||
unsigned long napi_poll;
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@
|
|||
#include <linux/mii.h>
|
||||
#include <linux/of_mdio.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/ipc_logging.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/dma-iommu.h>
|
||||
|
|
@ -22,6 +21,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"
|
||||
|
|
@ -113,10 +114,15 @@
|
|||
#define MICREL_LINK_UP_INTR_STATUS BIT(0)
|
||||
|
||||
bool phy_intr_en;
|
||||
void *ipc_emac_log_ctxt;
|
||||
|
||||
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,293 @@ 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(ðqos->ipv4_addr_assign_wq);
|
||||
flush_delayed_work(ðqos->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(ðqos->ipv6_addr_assign_wq);
|
||||
flush_delayed_work(ðqos->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->early_eth = ethqos->early_eth_enabled;
|
||||
qcom_ethqos_add_ipaddr(&pparams, priv->dev);
|
||||
|
||||
if (pparams.is_valid_ipv4_addr) {
|
||||
INIT_DELAYED_WORK(ðqos->ipv4_addr_assign_wq,
|
||||
ethqos_is_ipv4_NW_stack_ready);
|
||||
ret = qcom_ethqos_add_ipaddr(&pparams, priv->dev);
|
||||
if (ret)
|
||||
schedule_delayed_work(ðqos->ipv4_addr_assign_wq,
|
||||
msecs_to_jiffies(1000));
|
||||
}
|
||||
|
||||
if (pparams.is_valid_ipv6_addr) {
|
||||
INIT_DELAYED_WORK(ðqos->ipv6_addr_assign_wq,
|
||||
ethqos_is_ipv6_NW_stack_ready);
|
||||
ret = qcom_ethqos_add_ipv6addr(&pparams, priv->dev);
|
||||
if (ret)
|
||||
schedule_delayed_work(ðqos->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 ssize_t read_phy_reg_dump(struct file *file, char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct qcom_ethqos *ethqos = file->private_data;
|
||||
unsigned int len = 0, buf_len = 2000;
|
||||
char *buf;
|
||||
ssize_t ret_cnt;
|
||||
int phydata = 0;
|
||||
int i = 0;
|
||||
|
||||
struct platform_device *pdev = ethqos->pdev;
|
||||
struct net_device *dev = platform_get_drvdata(pdev);
|
||||
struct stmmac_priv *priv = netdev_priv(dev);
|
||||
|
||||
if (!ethqos || !dev->phydev) {
|
||||
ETHQOSERR("NULL Pointer\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
buf = kzalloc(buf_len, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
len += scnprintf(buf + len, buf_len - len,
|
||||
"\n************* PHY Reg dump *************\n");
|
||||
|
||||
for (i = 0; i < 32; i++) {
|
||||
phydata = ethqos_mdio_read(priv, priv->plat->phy_addr, i);
|
||||
len += scnprintf(buf + len, buf_len - len,
|
||||
"MII Register (%#x) = %#x\n",
|
||||
i, phydata);
|
||||
}
|
||||
|
||||
if (len > buf_len) {
|
||||
ETHQOSERR("(len > buf_len) buffer not sufficient\n");
|
||||
len = buf_len;
|
||||
}
|
||||
|
||||
ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
|
||||
kfree(buf);
|
||||
return ret_cnt;
|
||||
}
|
||||
|
||||
static ssize_t read_rgmii_reg_dump(struct file *file,
|
||||
char __user *user_buf, size_t count,
|
||||
loff_t *ppos)
|
||||
{
|
||||
struct qcom_ethqos *ethqos = file->private_data;
|
||||
unsigned int len = 0, buf_len = 2000;
|
||||
char *buf;
|
||||
ssize_t ret_cnt;
|
||||
int rgmii_data = 0;
|
||||
struct platform_device *pdev = ethqos->pdev;
|
||||
|
||||
struct net_device *dev = platform_get_drvdata(pdev);
|
||||
|
||||
if (!ethqos || !dev->phydev) {
|
||||
ETHQOSERR("NULL Pointer\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
buf = kzalloc(buf_len, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
len += scnprintf(buf + len, buf_len - len,
|
||||
"\n************* RGMII Reg dump *************\n");
|
||||
rgmii_data = rgmii_readl(ethqos, RGMII_IO_MACRO_CONFIG);
|
||||
len += scnprintf(buf + len, buf_len - len,
|
||||
"RGMII_IO_MACRO_CONFIG Register = %#x\n",
|
||||
rgmii_data);
|
||||
rgmii_data = rgmii_readl(ethqos, SDCC_HC_REG_DLL_CONFIG);
|
||||
len += scnprintf(buf + len, buf_len - len,
|
||||
"SDCC_HC_REG_DLL_CONFIG Register = %#x\n",
|
||||
rgmii_data);
|
||||
rgmii_data = rgmii_readl(ethqos, SDCC_HC_REG_DDR_CONFIG);
|
||||
len += scnprintf(buf + len, buf_len - len,
|
||||
"SDCC_HC_REG_DDR_CONFIG Register = %#x\n",
|
||||
rgmii_data);
|
||||
rgmii_data = rgmii_readl(ethqos, SDCC_HC_REG_DLL_CONFIG2);
|
||||
len += scnprintf(buf + len, buf_len - len,
|
||||
"SDCC_HC_REG_DLL_CONFIG2 Register = %#x\n",
|
||||
rgmii_data);
|
||||
rgmii_data = rgmii_readl(ethqos, SDC4_STATUS);
|
||||
len += scnprintf(buf + len, buf_len - len,
|
||||
"SDC4_STATUS Register = %#x\n",
|
||||
rgmii_data);
|
||||
rgmii_data = rgmii_readl(ethqos, SDCC_USR_CTL);
|
||||
len += scnprintf(buf + len, buf_len - len,
|
||||
"SDCC_USR_CTL Register = %#x\n",
|
||||
rgmii_data);
|
||||
rgmii_data = rgmii_readl(ethqos, RGMII_IO_MACRO_CONFIG2);
|
||||
len += scnprintf(buf + len, buf_len - len,
|
||||
"RGMII_IO_MACRO_CONFIG2 Register = %#x\n",
|
||||
rgmii_data);
|
||||
rgmii_data = rgmii_readl(ethqos, RGMII_IO_MACRO_DEBUG1);
|
||||
len += scnprintf(buf + len, buf_len - len,
|
||||
"RGMII_IO_MACRO_DEBUG1 Register = %#x\n",
|
||||
rgmii_data);
|
||||
rgmii_data = rgmii_readl(ethqos, EMAC_SYSTEM_LOW_POWER_DEBUG);
|
||||
len += scnprintf(buf + len, buf_len - len,
|
||||
"EMAC_SYSTEM_LOW_POWER_DEBUG Register = %#x\n",
|
||||
rgmii_data);
|
||||
|
||||
if (len > buf_len) {
|
||||
ETHQOSERR("(len > buf_len) buffer not sufficient\n");
|
||||
len = buf_len;
|
||||
}
|
||||
|
||||
ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
|
||||
kfree(buf);
|
||||
return ret_cnt;
|
||||
}
|
||||
|
||||
static const struct file_operations fops_phy_reg_dump = {
|
||||
.read = read_phy_reg_dump,
|
||||
.open = simple_open,
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
static const struct file_operations fops_rgmii_reg_dump = {
|
||||
.read = read_rgmii_reg_dump,
|
||||
.open = simple_open,
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
static int ethqos_create_debugfs(struct qcom_ethqos *ethqos)
|
||||
{
|
||||
static struct dentry *phy_reg_dump;
|
||||
static struct dentry *rgmii_reg_dump;
|
||||
|
||||
if (!ethqos) {
|
||||
ETHQOSERR("Null Param %s\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ethqos->debugfs_dir = debugfs_create_dir("eth", NULL);
|
||||
|
||||
if (!ethqos->debugfs_dir || IS_ERR(ethqos->debugfs_dir)) {
|
||||
ETHQOSERR("Can't create debugfs dir\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
phy_reg_dump = debugfs_create_file("phy_reg_dump", 0400,
|
||||
ethqos->debugfs_dir, ethqos,
|
||||
&fops_phy_reg_dump);
|
||||
if (!phy_reg_dump || IS_ERR(phy_reg_dump)) {
|
||||
ETHQOSERR("Can't create phy_dump %p\n", phy_reg_dump);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
rgmii_reg_dump = debugfs_create_file("rgmii_reg_dump", 0400,
|
||||
ethqos->debugfs_dir, ethqos,
|
||||
&fops_rgmii_reg_dump);
|
||||
if (!rgmii_reg_dump || IS_ERR(rgmii_reg_dump)) {
|
||||
ETHQOSERR("Can't create rgmii_dump %p\n", rgmii_reg_dump);
|
||||
goto fail;
|
||||
}
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
debugfs_remove_recursive(ethqos->debugfs_dir);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static int qcom_ethqos_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
|
|
@ -1056,10 +1511,23 @@ 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
|
||||
|
||||
ipc_emac_log_ctxt = ipc_log_context_create(IPCLOG_STATE_PAGES,
|
||||
"emac", 0);
|
||||
if (!ipc_emac_log_ctxt)
|
||||
ETHQOSERR("Error creating logging context for emac\n");
|
||||
else
|
||||
ETHQOSDBG("IPC logging has been enabled for emac\n");
|
||||
ret = stmmac_get_platform_resources(pdev, &stmmac_res);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
@ -1081,7 +1549,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 +1569,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 +1651,20 @@ static int qcom_ethqos_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
pethqos = ethqos;
|
||||
ethqos_create_debugfs(ethqos);
|
||||
|
||||
ndev = dev_get_drvdata(ðqos->pdev->dev);
|
||||
priv = netdev_priv(ndev);
|
||||
|
||||
if (ethqos->early_eth_enabled) {
|
||||
/* Initialize work*/
|
||||
INIT_WORK(ðqos->early_eth,
|
||||
qcom_ethqos_bringup_iface);
|
||||
/* Queue the work*/
|
||||
queue_work(system_wq, ðqos->early_eth);
|
||||
/*Set early eth parameters*/
|
||||
ethqos_set_early_eth_param(priv, ethqos);
|
||||
}
|
||||
return ret;
|
||||
|
||||
err_clk:
|
||||
|
|
@ -1276,7 +1773,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");
|
||||
|
|
|
|||
|
|
@ -5,12 +5,38 @@
|
|||
#define _DWMAC_QCOM_ETHQOS_H
|
||||
|
||||
//#include <linux/msm-bus.h>
|
||||
#include <linux/ipc_logging.h>
|
||||
|
||||
extern void *ipc_emac_log_ctxt;
|
||||
|
||||
#define IPCLOG_STATE_PAGES 50
|
||||
#define __FILENAME__ (strrchr(__FILE__, '/') ? \
|
||||
strrchr(__FILE__, '/') + 1 : __FILE__)
|
||||
|
||||
#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)
|
||||
#define ETHQOSERR(fmt, args...) \
|
||||
pr_err(DRV_NAME " %s:%d " fmt, __func__, __LINE__, ## args)
|
||||
do {\
|
||||
pr_err(DRV_NAME " %s:%d " fmt, __func__, __LINE__, ## args);\
|
||||
if (ipc_emac_log_ctxt) { \
|
||||
ipc_log_string(ipc_emac_log_ctxt, \
|
||||
"%s: %s[%u]:[emac] ERROR:" fmt, __FILENAME__,\
|
||||
__func__, __LINE__, ## args); \
|
||||
} \
|
||||
} while (0)
|
||||
#define ETHQOSINFO(fmt, args...) \
|
||||
pr_info(DRV_NAME " %s:%d " fmt, __func__, __LINE__, ## args)
|
||||
#define RGMII_IO_MACRO_CONFIG 0x0
|
||||
|
|
@ -162,6 +188,15 @@ 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 dentry *debugfs_dir;
|
||||
};
|
||||
|
||||
struct pps_cfg {
|
||||
|
|
@ -191,6 +226,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);
|
||||
|
|
|
|||
|
|
@ -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,9 @@ 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;
|
||||
bool early_eth_config_set;
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
struct dentry *dbgfs_dir;
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -82,7 +82,16 @@ static const struct stmmac_stats stmmac_gstrings_stats[] = {
|
|||
STMMAC_STAT(rx_early_irq),
|
||||
STMMAC_STAT(threshold),
|
||||
STMMAC_STAT(tx_pkt_n),
|
||||
STMMAC_STAT(q_tx_pkt_n[0]),
|
||||
STMMAC_STAT(q_tx_pkt_n[1]),
|
||||
STMMAC_STAT(q_tx_pkt_n[2]),
|
||||
STMMAC_STAT(q_tx_pkt_n[3]),
|
||||
STMMAC_STAT(q_tx_pkt_n[4]),
|
||||
STMMAC_STAT(rx_pkt_n),
|
||||
STMMAC_STAT(q_rx_pkt_n[0]),
|
||||
STMMAC_STAT(q_rx_pkt_n[1]),
|
||||
STMMAC_STAT(q_rx_pkt_n[2]),
|
||||
STMMAC_STAT(q_rx_pkt_n[3]),
|
||||
STMMAC_STAT(normal_irq_n),
|
||||
STMMAC_STAT(rx_normal_irq_n),
|
||||
STMMAC_STAT(napi_poll),
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -875,6 +877,31 @@ static void stmmac_validate(struct phylink_config *config,
|
|||
__ETHTOOL_LINK_MODE_MASK_NBITS);
|
||||
bitmap_andnot(state->advertising, state->advertising, mask,
|
||||
__ETHTOOL_LINK_MODE_MASK_NBITS);
|
||||
|
||||
/* Early ethernet settings to bring up link in 100M,
|
||||
* Auto neg Off with full duplex link.
|
||||
*/
|
||||
if (priv->early_eth && !priv->early_eth_config_set) {
|
||||
priv->phydev->autoneg = AUTONEG_DISABLE;
|
||||
priv->phydev->speed = SPEED_100;
|
||||
priv->phydev->duplex = DUPLEX_FULL;
|
||||
phylink_clear(mac_supported, 1000baseT_Full);
|
||||
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);
|
||||
/* Validate method will also be called
|
||||
* when we change speed using ethtool.
|
||||
* Add check to avoid multiple calls
|
||||
*/
|
||||
priv->early_eth_config_set = 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static int stmmac_mac_link_state(struct phylink_config *config,
|
||||
|
|
@ -970,6 +997,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 +1039,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 +1077,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 +1120,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 +1999,11 @@ static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue)
|
|||
} else {
|
||||
priv->dev->stats.tx_packets++;
|
||||
priv->xstats.tx_pkt_n++;
|
||||
priv->xstats.q_tx_pkt_n[queue]++;
|
||||
#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 +3785,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++;
|
||||
}
|
||||
|
|
@ -3733,6 +3803,7 @@ read_again:
|
|||
stmmac_rx_refill(priv, queue);
|
||||
|
||||
priv->xstats.rx_pkt_n += count;
|
||||
priv->xstats.q_rx_pkt_n[queue] += count;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in a new issue