Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8f76fef0 authored by Sunil Paidimarri's avatar Sunil Paidimarri Committed by Gerrit - the friendly Code Review server
Browse files

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: default avatarAbhishek Chauhan <abchauha@qti.qualcomm.com>
Signed-off-by: default avatarSunil Paidimarri <hisunil@codeaurora.org>
parent 91aa9b06
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -209,3 +209,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.
 No newline at end of file
+6 −0
Original line number Diff line number Diff line
@@ -5010,3 +5010,9 @@
	xirc2ps_cs=	[NET,PCMCIA]
			Format:
			<irq>,<irq_mask>,<io>,<full_duplex>,<do_sound>,<lockup_hack>[,<irq2>[,<irq3>[,<irq4>]]]

	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.
 No newline at end of file
+384 −4
Original line number Diff line number Diff line
@@ -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"
@@ -33,10 +35,13 @@ bool phy_intr_en;

struct qcom_ethqos *pethqos;
struct emac_emb_smmu_cb_ctx emac_emb_smmu_ctx = {0};
static unsigned char dev_addr[ETH_ALEN] = {
	0, 0x55, 0x7b, 0xb5, 0x7d, 0xf7};

void *ipc_emac_log_ctxt;
static struct qmp_pkt pkt;
static char qmp_buf[MAX_QMP_MSG_SIZE + 1] = {0};
static struct ip_params pparams = {"", "", "", ""};

static inline unsigned int dwmac_qcom_get_eth_type(unsigned char *buf)
{
@@ -208,6 +213,169 @@ 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)
{
	int ret = 1;
	bool valid_mac = false;

	pparams.is_valid_mac_addr = false;
	if (!mac_addr)
		return ret;

	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 ret;

fail:
	ETHQOSERR("Invalid Mac address programmed: %s\n", mac_addr);
	return ret;
}

__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);
@@ -1270,6 +1438,37 @@ static void qcom_ethqos_phy_resume_clks(struct qcom_ethqos *ethqos)
	ETHQOSINFO("Exit\n");
}

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) < 0)
		ETHQOSINFO("ERROR\n");

	rtnl_unlock();

	ETHQOSINFO("exit\n");
}

void qcom_ethqos_request_phy_wol(struct plat_stmmacenet_data *plat)
{
	struct qcom_ethqos *ethqos = plat->bsp_priv;
@@ -1317,6 +1516,104 @@ void qcom_ethqos_request_phy_wol(struct plat_stmmacenet_data *plat)
	}
}

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;
@@ -1328,12 +1625,17 @@ static int qcom_ethqos_probe(struct platform_device *pdev)
	struct stmmac_priv *priv;
	int ret;

#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");
		ETHQOSINFO("Error creating logging context for emac\n");
	else
		ETHQOSDBG("IPC logging has been enabled for emac\n");
		ETHQOSINFO("IPC logging has been enabled for emac\n");

	if (of_device_is_compatible(pdev->dev.of_node,
				    "qcom,emac-smmu-embedded"))
		return emac_emb_smmu_cb_probe(pdev);
@@ -1358,7 +1660,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)) {
@@ -1379,6 +1680,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 = 1;
		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);
@@ -1460,11 +1777,22 @@ static int qcom_ethqos_probe(struct platform_device *pdev)

	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);
	}
#ifdef CONFIG_ETH_IPA_OFFLOAD
	ethqos->ipa_enabled = true;
	priv->rx_queue[IPA_DMA_RX_CH].skip_sw = true;
	priv->tx_queue[IPA_DMA_TX_CH].skip_sw = true;
	ethqos_ipa_offload_event_handler(ethqos, EV_PROBE_INIT);
	priv->hw->mac->map_mtl_to_dma(priv->hw, 0, 1); //change
#endif
	return ret;

@@ -1590,7 +1918,59 @@ 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);

	if (!ipc_emac_log_ctxt)
		ipc_log_context_destroy(ipc_emac_log_ctxt);

	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 Technologies, Inc. ETHQOS driver");
MODULE_LICENSE("GPL v2");
+33 −0
Original line number Diff line number Diff line
@@ -18,8 +18,20 @@
#include <linux/mailbox/qmp.h>
#include <linux/mailbox_controller.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>

extern void *ipc_emac_log_ctxt;

#define QCOM_ETH_QOS_MAC_ADDR_LEN 6
#define QCOM_ETH_QOS_MAC_ADDR_STR_LEN 18

#define IPCLOG_STATE_PAGES 50
#define MAX_QMP_MSG_SIZE 96
#define __FILENAME__ (strrchr(__FILE__, '/') ? \
@@ -397,12 +409,20 @@ struct qcom_ethqos {
	struct mbox_chan *qmp_mbox_chan;
	struct mbox_client *qmp_mbox_client;
	struct work_struct qmp_mailbox_work;
	/* 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;

	int disable_ctile_pc;

	u32 emac_mem_base;
	u32 emac_mem_size;

	bool ipa_enabled;
	/* Key Performance Indicators */
	bool print_kpi;
};

struct pps_cfg {
@@ -430,6 +450,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);
+12 −1
Original line number Diff line number Diff line
@@ -45,6 +45,15 @@ static struct ethqos_prv_ipa_data eth_ipa_ctx;

static void __ipa_eth_free_msg(void *buff, u32 len, u32 type) {}

static inline void *ethqos_get_priv(struct qcom_ethqos *ethqos)
{
	struct platform_device *pdev = ethqos->pdev;
	struct net_device *dev = platform_get_drvdata(pdev);
	struct stmmac_priv *priv = netdev_priv(dev);

	return priv;
}

static int eth_ipa_send_msg(struct qcom_ethqos *ethqos,
			    enum ipa_peripheral_event event)
{
@@ -981,10 +990,12 @@ static void ethqos_configure_ipa_rx_dma_channel(unsigned int QINX,

static int ethqos_init_offload(struct qcom_ethqos *ethqos)
{
	struct stmmac_priv *priv = ethqos_get_priv(ethqos);

	ETHQOSDBG("\n");

	ethqos_configure_ipa_tx_dma_channel(IPA_DMA_TX_CH, ethqos);
	MTL_RQDCM0R_RGWR(0x3020100);
	priv->hw->mac->map_mtl_to_dma(priv->hw, 0, 0);
	ethqos_configure_ipa_rx_dma_channel(IPA_DMA_RX_CH, ethqos);

	ETHQOSDBG("\n");
Loading