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

Commit 6144846a authored by Alok Chauhan's avatar Alok Chauhan
Browse files

msm: emac: Improve external phy connect sequence



Currently PHY connect and disconnect are part of EMAC
driver Up and Down sequence.  Frequent PHY connect and
disconnect happens in error scenario which leads to
instability of Ethernet Link to external PHY.

So corrected below things as part of this patch:
(1) Avoid frequent phy connect and disconnect sequence
(2) Move EMAC regulators/clock control from Runtime to
system level PM framework. This will also reduce SGMII
link establishment time.
(3) Don’t allow SGMII PHY to go into power saving mode
in case it is connected to QCA8337 external switch
(4) Enable QCA8337 auto negotiation on Port0

Change-Id: I5435e4bde30f65e1e2a0062783b189160aa85c1b
Signed-off-by: default avatarAlok Chauhan <alokc@codeaurora.org>
parent c7402fa4
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -108,7 +108,7 @@ bool emac_hw_read_tx_tstamp(struct emac_hw *hw, struct emac_hwtxtstamp *ts);
#define DMAR_DLY_CNT_DEF                15
#define DMAW_DLY_CNT_DEF                4

#define MDIO_CLK_25_4                   0
#define MDIO_CLK_25_8                   3
#define MDIO_CLK_25_28                  7

#define RXQ0_RSS_HSTYP_IPV6_TCP_EN      0x20
+39 −26
Original line number Diff line number Diff line
@@ -196,11 +196,14 @@ static int emac_acpi_get_resources(struct platform_device *pdev,
static int emac_clk_prepare_enable(struct emac_adapter *adpt,
				   enum emac_clk_id id)
{
	int ret;
	int ret = 0;

	if (ACPI_HANDLE(adpt->netdev->dev.parent))
		return 0;

	if (adpt->clk[id].enabled)
		return 0;

	ret = clk_prepare_enable(adpt->clk[id].clk);
	if (ret)
		emac_err(adpt, "error:%d on clk_prepare_enable(%s)\n", ret,
@@ -1910,12 +1913,15 @@ int emac_mac_up(struct emac_adapter *adpt)
	for (i = 0; i < adpt->num_rxques; i++)
		emac_refresh_rx_buffer(&adpt->rx_queue[i]);

	if (!adpt->phy.is_ext_phy_connect) {
		ret = phy_connect_direct(netdev, adpt->phydev, emac_adjust_link,
					 phy->phy_interface);
		if (ret) {
			netdev_err(adpt->netdev, "could not connect phy\n");
			goto err_request_irq;
		}
		adpt->phy.is_ext_phy_connect = 1;
	}

	/* enable mac irq */
	emac_enable_intr(adpt);
@@ -1980,7 +1986,11 @@ void emac_mac_down(struct emac_adapter *adpt, u32 ctrl)
		if (adpt->irq[i].irq)
			free_irq(adpt->irq[i].irq, &adpt->irq[i]);

	if ((ATH8030_PHY_ID == adpt->phydev->phy_id) &&
	    (adpt->phy.is_ext_phy_connect)) {
		phy_disconnect(adpt->phydev);
		adpt->phy.is_ext_phy_connect = 0;
	}

	CLR_FLAG(adpt, ADPT_TASK_LSC_REQ);
	CLR_FLAG(adpt, ADPT_TASK_REINIT_REQ);
@@ -2443,6 +2453,8 @@ static void emac_init_adapter(struct emac_adapter *adpt)
	/* others */
	hw->preamble = EMAC_PREAMBLE_DEF;
	adpt->wol = EMAC_WOL_MAGIC | EMAC_WOL_PHY;

	adpt->phy.is_ext_phy_connect = 0;
}

/* Get the clock */
@@ -2729,6 +2741,9 @@ static int emac_enable_regulator(struct emac_adapter *adpt, u8 start, u8 end)
	u8 i;

	for (i = start; i <= end; i++) {
		if (adpt->vreg[i].enabled)
			continue;

		if (adpt->vreg[i].voltage_uv) {
			retval = emac_set_voltage(adpt, i,
						  adpt->vreg[i].voltage_uv,
@@ -2758,10 +2773,11 @@ static void emac_disable_regulator(struct emac_adapter *adpt, u8 start, u8 end)
	for (i = start; i <= end; i++) {
		struct emac_regulator *vreg = &adpt->vreg[i];

		if (vreg->enabled) {
		if (!vreg->enabled)
			continue;

		regulator_disable(vreg->vreg);
		vreg->enabled = false;
		}

		if (adpt->vreg[i].voltage_uv) {
			emac_set_voltage(adpt, i,
@@ -2816,6 +2832,7 @@ static int emac_pm_suspend(struct device *device, bool wol_enable)

	phy_suspend(adpt->phydev);
	flush_delayed_work(&adpt->phydev->state_queue);
	if (QCA8337_PHY_ID != adpt->phydev->phy_id)
		emac_hw_config_pow_save(hw, adpt->phydev->speed, !!wufc,
					!!(wufc & EMAC_WOL_MAGIC));

@@ -2831,9 +2848,6 @@ static int emac_pm_suspend(struct device *device, bool wol_enable)
			value &= ~BMCR_PDOWN;
			mdiobus_write(adpt->phydev->bus, i, MII_BMCR, value);

			/* Clear EPHY interrupts */
			mdiobus_read(adpt->phydev->bus, i, MII_INT_STATUS);

			/* Enable EPHY Link UP interrupt */
			mdiobus_write(adpt->phydev->bus, i, MII_INT_ENABLE,
				      LINK_SUCCESS_INTERRUPT |
@@ -2850,8 +2864,6 @@ static int emac_pm_suspend(struct device *device, bool wol_enable)
	}

	adpt->gpio_off(adpt, true, false);
	emac_disable_clks(adpt);
	emac_disable_regulator(adpt, EMAC_VREG1, EMAC_VREG2);
	return 0;
}

@@ -2865,9 +2877,6 @@ static int emac_pm_resume(struct device *device)
	int retval = 0, i;

	adpt->gpio_on(adpt, true, false);
	emac_enable_regulator(adpt, EMAC_VREG1, EMAC_VREG2);
	emac_clks_phase1_init(pdev, adpt);
	emac_clks_phase2_init(adpt);
	emac_hw_reset_mac(hw);

	/* Disable EPHY Link UP interrupt */
@@ -2939,6 +2948,8 @@ static int emac_pm_sys_suspend(struct device *device)
	}

	netif_device_detach(netdev);
	emac_disable_clks(adpt);
	emac_disable_regulator(adpt, EMAC_VREG1, EMAC_VREG2);
	return 0;
}

@@ -2949,6 +2960,9 @@ static int emac_pm_sys_resume(struct device *device)
	struct emac_adapter *adpt = netdev_priv(netdev);
	struct emac_phy *phy = &adpt->phy;

	emac_enable_regulator(adpt, EMAC_VREG1, EMAC_VREG2);
	emac_clks_phase1_init(pdev, adpt);
	emac_clks_phase2_init(adpt);
	netif_device_attach(netdev);

	if (!pm_runtime_enabled(device) || !pm_runtime_suspended(device)) {
@@ -3075,9 +3089,6 @@ static int emac_probe(struct platform_device *pdev)
	/* reset mac */
	emac_hw_reset_mac(hw);

	/* disable emac core and phy regulator */
	emac_disable_clks(adpt);
	emac_disable_regulator(adpt, EMAC_VREG1, EMAC_VREG2);

	/* set hw features */
	netdev->features = NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_RXCSUM |
@@ -3152,8 +3163,10 @@ err_undo_napi:
err_init_mdio_gpio:
	adpt->gpio_off(adpt, true, true);
err_clk_init:
	if (ATH8030_PHY_ID == adpt->phydev->phy_id)
		emac_disable_clks(adpt);
err_ldo_init:
	if (ATH8030_PHY_ID == adpt->phydev->phy_id)
		emac_disable_regulator(adpt, EMAC_VREG1, EMAC_VREG5);
err_get_resource:
	free_netdev(netdev);
+2 −2
Original line number Diff line number Diff line
@@ -100,7 +100,7 @@ static int emac_mdio_read(struct mii_bus *bus, int addr, int regnum)
	reg = reg & ~(MDIO_REG_ADDR_BMSK | MDIO_CLK_SEL_BMSK |
			MDIO_MODE | MDIO_PR);
	reg = SUP_PREAMBLE |
	      ((MDIO_CLK_25_4 << MDIO_CLK_SEL_SHFT) & MDIO_CLK_SEL_BMSK) |
	      ((MDIO_CLK_25_8 << MDIO_CLK_SEL_SHFT) & MDIO_CLK_SEL_BMSK) |
	      ((regnum << MDIO_REG_ADDR_SHFT) & MDIO_REG_ADDR_BMSK) |
	      MDIO_START | MDIO_RD_NWR;

@@ -156,7 +156,7 @@ static int emac_mdio_write(struct mii_bus *bus, int addr, int regnum, u16 val)
	reg = reg & ~(MDIO_REG_ADDR_BMSK | MDIO_CLK_SEL_BMSK |
		MDIO_DATA_BMSK | MDIO_MODE | MDIO_PR);
	reg = SUP_PREAMBLE |
	((MDIO_CLK_25_4 << MDIO_CLK_SEL_SHFT) & MDIO_CLK_SEL_BMSK) |
	((MDIO_CLK_25_8 << MDIO_CLK_SEL_SHFT) & MDIO_CLK_SEL_BMSK) |
	((regnum << MDIO_REG_ADDR_SHFT) & MDIO_REG_ADDR_BMSK) |
	((val << MDIO_DATA_SHFT) & MDIO_DATA_BMSK) |
	MDIO_START;
+2 −1
Original line number Diff line number Diff line
/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -74,6 +74,7 @@ struct emac_phy {
	bool	is_wol_irq_reg;
	bool	is_wol_enabled;
	spinlock_t	wol_irq_lock; /* lock for wol irq gpio enablement */
	bool	is_ext_phy_connect;
};

int emac_phy_config_internal(struct platform_device *pdev,
+1 −1
Original line number Diff line number Diff line
@@ -182,7 +182,7 @@ static void qca8337_vlan_config(struct qca8337_priv *priv)
	priv->ops->write(priv, QCA8337_REG_PORT_VLAN0(4), 0x10001);

	priv->ops->write(priv, QCA8337_REG_PORT_LOOKUP(5), 0x0014005f);
	priv->ops->write(priv, QCA8337_REG_PORT_VLAN0(5), 0x20001);
	priv->ops->write(priv, QCA8337_REG_PORT_VLAN0(5), 0x10001);

	priv->ops->write(priv, QCA8337_REG_PORT_LOOKUP(6), 0x0014001e);
	priv->ops->write(priv, QCA8337_REG_PORT_VLAN0(6), 0x10001);
Loading