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

Commit 7b2cbf75 authored by Alok Chauhan's avatar Alok Chauhan
Browse files

msm: emac: factor out phy abstraction layer



Create an abstract phy layer over the sgmii and rgmii modules.
This cleanly splits the functionality between mac and phy layers
into separate source files.

Change-Id: I7c2a052376c530659b9b35f6edf7b18c1dd60b9a
Signed-off-by: default avatarGilad Avidov <gavidov@codeaurora.org>
Signed-off-by: default avatarAlok Chauhan <alokc@codeaurora.org>
parent 0d44562c
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -5,4 +5,4 @@
obj-$(CONFIG_QCOM_EMAC) += qcom_emac.o

qcom_emac-objs := emac_main.o emac_hw.o emac_ethtool.o emac_ptp.o
qcom_emac-objs += emac_rgmii.o emac_sgmii_v1.o
qcom_emac-objs += emac_phy.o emac_rgmii.o emac_sgmii_v1.o
+2 −46
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@
#include <linux/netdevice.h>
#include <linux/clk.h>
#include <linux/platform_device.h>
#include "emac_phy.h"

/* Device IDs */
#define EMAC_DEV_ID                0x0040
@@ -119,15 +120,6 @@ enum emac_dma_req_block {
	emac_dma_req_4096 = 5
};

/* Flow Control Settings */
enum emac_fc_mode {
	emac_fc_none = 0,
	emac_fc_rx_pause,
	emac_fc_tx_pause,
	emac_fc_full,
	emac_fc_default
};

/* IEEE1588 */
enum emac_ptp_clk_mode {
	emac_ptp_clk_mode_oc_two_step,
@@ -223,31 +215,11 @@ enum emac_adapter_flags {
#define TEST_N_SET_FLAG(OBJ, FLAG) \
			test_and_set_bit(EMAC_FLAG_ ## FLAG,  &((OBJ)->flags))

struct emac_adapter;
struct emac_hw;

struct emac_phy_ops {
	int  (*config)(struct platform_device *pdev, struct emac_adapter *adpt);
	int  (*up)(struct emac_adapter *adpt);
	void (*down)(struct emac_adapter *adpt);
	int  (*init)(struct emac_adapter *adpt);
	void (*reset)(struct emac_adapter *adpt);
	int  (*init_ephy)(struct emac_hw *hw);
	int  (*link_setup_no_ephy)(struct emac_adapter *adpt, u32 speed,
				   bool autoneg);
	int  (*link_check_no_ephy)(struct emac_adapter *adpt, u32 *speed,
				   bool *link_up);
	void (*tx_clk_set_rate)(struct emac_adapter *adpt);
	void (*periodic_task)(struct emac_adapter *adpt);
};

struct emac_hw {
	void __iomem *reg_addr[NUM_EMAC_REG_BASES];

	u16     devid;
	u16     revid;
	struct emac_phy_ops	ops;
	void			*private;

	/* ring parameter */
	u8      tpd_burst;
@@ -258,25 +230,11 @@ struct emac_hw {
	enum emac_dma_req_block   dmaw_block;
	enum emac_dma_order       dma_order;

	/* PHY parameter */
	u32             phy_addr;
	u16             phy_id[2];
	bool            autoneg;
	u32             autoneg_advertised;
	u32             link_speed;
	bool            link_up;
	spinlock_t      mdio_lock; /* sync access to mdio bus */

	/* MAC parameter */
	u8      mac_addr[ETH_ALEN];
	u8      mac_perm_addr[ETH_ALEN];
	u32     mtu;

	/* flow control parameter */
	enum emac_fc_mode   cur_fc_mode; /* FC mode in effect */
	enum emac_fc_mode   req_fc_mode; /* FC mode requested by caller */
	bool                disable_fc_autoneg; /* Do not autonegotiate FC */

	/* RSS parameter */
	u8      rss_hstype;
	u8      rss_base_cpu;
@@ -742,6 +700,7 @@ struct emac_adapter {

	u32 rxbuf_size;

	struct emac_phy phy;
	struct emac_hw hw;
	struct emac_hw_stats hw_stats;

@@ -757,9 +716,6 @@ struct emac_adapter {
	unsigned long	link_jiffies;

	bool            tstamp_en;
	int             phy_mode;
	bool            no_ephy;
	bool            no_mdio_gpio;
	u32             wol;
	u16             msg_enable;
	unsigned long   flags;
+39 −38
Original line number Diff line number Diff line
@@ -78,7 +78,7 @@ static int emac_get_settings(struct net_device *netdev,
			     struct ethtool_cmd *ecmd)
{
	struct emac_adapter *adpt = netdev_priv(netdev);
	struct emac_hw *hw = &adpt->hw;
	struct emac_phy *phy = &adpt->phy;

	ecmd->supported = (SUPPORTED_10baseT_Half   |
			   SUPPORTED_10baseT_Full   |
@@ -89,20 +89,20 @@ static int emac_get_settings(struct net_device *netdev,
			   SUPPORTED_TP);

	ecmd->advertising = ADVERTISED_TP;
	if (hw->autoneg) {
	if (phy->autoneg) {
		ecmd->advertising |= ADVERTISED_Autoneg;
		ecmd->advertising |= hw->autoneg_advertised;
		ecmd->advertising |= phy->autoneg_advertised;
		ecmd->autoneg = AUTONEG_ENABLE;
	} else {
		ecmd->autoneg = AUTONEG_DISABLE;
	}

	ecmd->port = PORT_TP;
	ecmd->phy_address = hw->phy_addr;
	ecmd->phy_address = phy->addr;
	ecmd->transceiver = XCVR_INTERNAL;

	if (hw->link_up) {
		switch (hw->link_speed) {
	if (phy->link_up) {
		switch (phy->link_speed) {
		case EMAC_LINK_SPEED_10_HALF:
			ecmd->speed = SPEED_10;
			ecmd->duplex = DUPLEX_HALF;
@@ -140,7 +140,7 @@ static int emac_set_settings(struct net_device *netdev,
			     struct ethtool_cmd *ecmd)
{
	struct emac_adapter *adpt = netdev_priv(netdev);
	struct emac_hw *hw = &adpt->hw;
	struct emac_phy *phy = &adpt->phy;
	u32 advertised, old;
	int retval = 0;
	bool autoneg;
@@ -152,7 +152,7 @@ static int emac_set_settings(struct net_device *netdev,
	while (TEST_N_SET_FLAG(adpt, ADPT_STATE_RESETTING))
		msleep(20); /* Reset might take few 10s of ms */

	old = hw->autoneg_advertised;
	old = phy->autoneg_advertised;
	advertised = 0;
	if (ecmd->autoneg == AUTONEG_ENABLE) {
		advertised = EMAC_LINK_SPEED_DEFAULT;
@@ -182,26 +182,27 @@ static int emac_set_settings(struct net_device *netdev,
		}
	}

	if ((hw->autoneg == autoneg) && (hw->autoneg_advertised == advertised))
	if ((phy->autoneg == autoneg) &&
	    (phy->autoneg_advertised == advertised))
		goto done;

	/* If there is no EPHY, the EMAC internal PHY may get reset in
	 * emac_setup_phy_link_speed. Reset the MAC to avoid the memory
	 * emac_phy_setup_link_speed. Reset the MAC to avoid the memory
	 * corruption.
	 */
	if (adpt->no_ephy && if_running)
	if (!phy->external && if_running)
		emac_down(adpt, EMAC_HW_CTRL_RESET_MAC);

	retval = emac_setup_phy_link_speed(hw, advertised, autoneg,
					   !hw->disable_fc_autoneg);
	retval = emac_phy_setup_link_speed(adpt, advertised, autoneg,
					   !phy->disable_fc_autoneg);
	if (retval) {
		emac_setup_phy_link_speed(hw, old, autoneg,
					  !hw->disable_fc_autoneg);
		emac_phy_setup_link_speed(adpt, old, autoneg,
					  !phy->disable_fc_autoneg);
	}

	if (if_running) {
		/* If there is no EPHY, bring up the interface */
		if (adpt->no_ephy)
		if (!phy->external)
			emac_up(adpt);
	}

@@ -214,18 +215,18 @@ static void emac_get_pauseparam(struct net_device *netdev,
				struct ethtool_pauseparam *pause)
{
	struct emac_adapter *adpt = netdev_priv(netdev);
	struct emac_hw *hw = &adpt->hw;
	struct emac_phy *phy = &adpt->phy;

	if (hw->disable_fc_autoneg)
	if (phy->disable_fc_autoneg)
		pause->autoneg = 0;
	else
		pause->autoneg = 1;

	if (hw->cur_fc_mode == emac_fc_rx_pause) {
	if (phy->cur_fc_mode == EMAC_FC_RX_PAUSE) {
		pause->rx_pause = 1;
	} else if (hw->cur_fc_mode == emac_fc_tx_pause) {
	} else if (phy->cur_fc_mode == EMAC_FC_TX_PAUSE) {
		pause->tx_pause = 1;
	} else if (hw->cur_fc_mode == emac_fc_full) {
	} else if (phy->cur_fc_mode == EMAC_FC_FULL) {
		pause->rx_pause = 1;
		pause->tx_pause = 1;
	}
@@ -235,16 +236,16 @@ static int emac_set_pauseparam(struct net_device *netdev,
			       struct ethtool_pauseparam *pause)
{
	struct emac_adapter *adpt = netdev_priv(netdev);
	struct emac_hw *hw = &adpt->hw;
	enum emac_fc_mode req_fc_mode;
	struct emac_phy *phy = &adpt->phy;
	enum emac_flow_ctrl req_fc_mode;
	bool disable_fc_autoneg;
	int retval = 0;

	while (TEST_N_SET_FLAG(adpt, ADPT_STATE_RESETTING))
		msleep(20); /* Reset might take few 10s of ms */

	req_fc_mode        = hw->req_fc_mode;
	disable_fc_autoneg = hw->disable_fc_autoneg;
	req_fc_mode        = phy->req_fc_mode;
	disable_fc_autoneg = phy->disable_fc_autoneg;

	if (pause->autoneg != AUTONEG_ENABLE)
		disable_fc_autoneg = true;
@@ -252,29 +253,29 @@ static int emac_set_pauseparam(struct net_device *netdev,
		disable_fc_autoneg = false;

	if (pause->rx_pause && pause->tx_pause) {
		req_fc_mode = emac_fc_full;
		req_fc_mode = EMAC_FC_FULL;
	} else if (pause->rx_pause && !pause->tx_pause) {
		req_fc_mode = emac_fc_rx_pause;
		req_fc_mode = EMAC_FC_RX_PAUSE;
	} else if (!pause->rx_pause && pause->tx_pause) {
		req_fc_mode = emac_fc_tx_pause;
		req_fc_mode = EMAC_FC_TX_PAUSE;
	} else if (!pause->rx_pause && !pause->tx_pause) {
		req_fc_mode = emac_fc_none;
		req_fc_mode = EMAC_FC_NONE;
	} else {
		CLR_FLAG(adpt, ADPT_STATE_RESETTING);
		return -EINVAL;
	}

	if ((hw->req_fc_mode != req_fc_mode) ||
	    (hw->disable_fc_autoneg != disable_fc_autoneg)) {
		hw->req_fc_mode = req_fc_mode;
		hw->disable_fc_autoneg = disable_fc_autoneg;
		if (!adpt->no_ephy)
			retval = emac_setup_phy_link(hw,
						     hw->autoneg_advertised,
						     hw->autoneg,
	if ((phy->req_fc_mode != req_fc_mode) ||
	    (phy->disable_fc_autoneg != disable_fc_autoneg)) {
		phy->req_fc_mode	= req_fc_mode;
		phy->disable_fc_autoneg	= disable_fc_autoneg;
		if (phy->external)
			retval = emac_phy_setup_link(adpt,
						     phy->autoneg_advertised,
						     phy->autoneg,
						     !disable_fc_autoneg);
		if (!retval)
			emac_hw_config_fc(hw);
			emac_phy_config_fc(adpt);
	}

	CLR_FLAG(adpt, ADPT_STATE_RESETTING);
+8 −539
Original line number Diff line number Diff line
@@ -18,12 +18,9 @@
#include <linux/jiffies.h>
#include <linux/phy.h>
#include <linux/of.h>
#include <linux/of_net.h>

#include "emac_hw.h"
#include "emac_ptp.h"
#include "emac_rgmii.h"
#include "emac_sgmii_v1.h"

#define RFD_PREF_LOW_TH     0x10
#define RFD_PREF_UP_TH      0x10
@@ -68,445 +65,6 @@ u32 emac_reg_field_r32(struct emac_hw *hw, u8 base, u32 reg,
	return (data & mask) >> shift;
}

/* PHY */
static int emac_disable_mdio_autopoll(struct emac_hw *hw)
{
	u32 i, val;

	emac_reg_update32(hw, EMAC, EMAC_MDIO_CTRL, MDIO_AP_EN, 0);
	wmb(); /* ensure mdio autopoll disable is requested */

	/* wait for any mdio polling to complete */
	for (i = 0; i < MDIO_WAIT_TIMES; i++) {
		val = emac_reg_r32(hw, EMAC, EMAC_MDIO_CTRL);
		if (!(val & MDIO_BUSY))
			return 0;

		udelay(100); /* atomic context */
	}

	/* failed to disable; ensure it is enabled before returning */
	emac_reg_update32(hw, EMAC, EMAC_MDIO_CTRL, 0, MDIO_AP_EN);
	wmb(); /* ensure mdio autopoll is enabled */
	return -EBUSY;
}

static void emac_enable_mdio_autopoll(struct emac_hw *hw)
{
	emac_reg_update32(hw, EMAC, EMAC_MDIO_CTRL, 0, MDIO_AP_EN);
	wmb(); /* ensure mdio autopoll is enabled */
}

int emac_hw_read_phy_reg(struct emac_hw *hw, bool ext, u8 dev, bool fast,
			 u16 reg_addr, u16 *phy_data)
{
	u32 i, clk_sel, val = 0;
	int retval = 0;

	*phy_data = 0;
	clk_sel = fast ? MDIO_CLK_25_4 : MDIO_CLK_25_28;

	if (!emac_hw_get_adap(hw)->no_ephy) {
		retval = emac_disable_mdio_autopoll(hw);
		if (retval)
			return retval;
	}

	emac_reg_update32(hw, EMAC, EMAC_PHY_STS, PHY_ADDR_BMSK,
			  (dev << PHY_ADDR_SHFT));
	wmb(); /* ensure PHY address is set before we proceed */

	if (ext) {
		val = ((dev << DEVAD_SHFT) & DEVAD_BMSK) |
		      ((reg_addr << EX_REG_ADDR_SHFT) & EX_REG_ADDR_BMSK);
		emac_reg_w32(hw, EMAC, EMAC_MDIO_EX_CTRL, val);
		wmb(); /* ensure proper address is set before proceeding */

		val = SUP_PREAMBLE |
		      ((clk_sel << MDIO_CLK_SEL_SHFT) & MDIO_CLK_SEL_BMSK) |
		      MDIO_START | MDIO_MODE | MDIO_RD_NWR;
	} else {
		val = val & ~(MDIO_REG_ADDR_BMSK | MDIO_CLK_SEL_BMSK |
				MDIO_MODE | MDIO_PR);
		val = SUP_PREAMBLE |
		      ((clk_sel << MDIO_CLK_SEL_SHFT) & MDIO_CLK_SEL_BMSK) |
		      ((reg_addr << MDIO_REG_ADDR_SHFT) & MDIO_REG_ADDR_BMSK) |
		      MDIO_START | MDIO_RD_NWR;
	}

	emac_reg_w32(hw, EMAC, EMAC_MDIO_CTRL, val);
	mb(); /* ensure hw starts the operation before we check for result */

	for (i = 0; i < MDIO_WAIT_TIMES; i++) {
		val = emac_reg_r32(hw, EMAC, EMAC_MDIO_CTRL);
		if (!(val & (MDIO_START | MDIO_BUSY))) {
			*phy_data = (u16)((val >> MDIO_DATA_SHFT) &
					MDIO_DATA_BMSK);
			break;
		}
		udelay(100); /* atomic context */
	}

	if (i == MDIO_WAIT_TIMES)
		retval = -EIO;

	if (!emac_hw_get_adap(hw)->no_ephy)
		emac_enable_mdio_autopoll(hw);

	return retval;
}

int emac_hw_write_phy_reg(struct emac_hw *hw, bool ext, u8 dev,
			  bool fast, u16 reg_addr, u16 phy_data)
{
	u32 i, clk_sel, val = 0;
	int retval = 0;

	clk_sel = fast ? MDIO_CLK_25_4 : MDIO_CLK_25_28;

	if (!emac_hw_get_adap(hw)->no_ephy) {
		retval = emac_disable_mdio_autopoll(hw);
		if (retval)
			return retval;
	}

	emac_reg_update32(hw, EMAC, EMAC_PHY_STS, PHY_ADDR_BMSK,
			  (dev << PHY_ADDR_SHFT));
	wmb(); /* ensure PHY address is set before we proceed */

	if (ext) {
		val = ((dev << DEVAD_SHFT) & DEVAD_BMSK) |
		      ((reg_addr << EX_REG_ADDR_SHFT) & EX_REG_ADDR_BMSK);
		emac_reg_w32(hw, EMAC, EMAC_MDIO_EX_CTRL, val);
		wmb(); /* ensure proper address is set before proceeding */

		val = SUP_PREAMBLE |
			((clk_sel << MDIO_CLK_SEL_SHFT) & MDIO_CLK_SEL_BMSK) |
			((phy_data << MDIO_DATA_SHFT) & MDIO_DATA_BMSK) |
			MDIO_START | MDIO_MODE;
	} else {
		val = val & ~(MDIO_REG_ADDR_BMSK | MDIO_CLK_SEL_BMSK |
			MDIO_DATA_BMSK | MDIO_MODE | MDIO_PR);
		val = SUP_PREAMBLE |
		((clk_sel << MDIO_CLK_SEL_SHFT) & MDIO_CLK_SEL_BMSK) |
		((reg_addr << MDIO_REG_ADDR_SHFT) & MDIO_REG_ADDR_BMSK) |
		((phy_data << MDIO_DATA_SHFT) & MDIO_DATA_BMSK) |
		MDIO_START;
	}

	emac_reg_w32(hw, EMAC, EMAC_MDIO_CTRL, val);
	mb(); /* ensure hw starts the operation before we check for result */

	for (i = 0; i < MDIO_WAIT_TIMES; i++) {
		val = emac_reg_r32(hw, EMAC, EMAC_MDIO_CTRL);
		if (!(val & (MDIO_START | MDIO_BUSY)))
			break;
		udelay(100); /* atomic context */
	}

	if (i == MDIO_WAIT_TIMES)
		retval = -EIO;

	if (!emac_hw_get_adap(hw)->no_ephy)
		emac_enable_mdio_autopoll(hw);

	return retval;
}

int emac_read_phy_reg(struct emac_hw *hw, u16 phy_addr,
		      u16 reg_addr, u16 *phy_data)
{
	struct emac_adapter *adpt = emac_hw_get_adap(hw);
	unsigned long  flags;
	int  retval;

	spin_lock_irqsave(&hw->mdio_lock, flags);
	retval = emac_hw_read_phy_reg(hw, false, phy_addr, true,
				      reg_addr, phy_data);
	spin_unlock_irqrestore(&hw->mdio_lock, flags);

	if (retval)
		emac_err(adpt, "error reading phy reg 0x%02x\n", reg_addr);
	else
		emac_dbg(adpt, hw, "EMAC PHY RD: 0x%02x -> 0x%04x\n", reg_addr,
			 *phy_data);

	return retval;
}

int emac_write_phy_reg(struct emac_hw *hw, u16 phy_addr,
		       u16 reg_addr, u16 phy_data)
{
	struct emac_adapter *adpt = emac_hw_get_adap(hw);
	unsigned long  flags;
	int  retval;

	spin_lock_irqsave(&hw->mdio_lock, flags);
	retval = emac_hw_write_phy_reg(hw, false, phy_addr, true,
				       reg_addr, phy_data);
	spin_unlock_irqrestore(&hw->mdio_lock, flags);

	if (retval)
		emac_err(adpt, "error writing phy reg 0x%02x\n", reg_addr);
	else
		emac_dbg(adpt, hw, "EMAC PHY WR: 0x%02x <- 0x%04x\n", reg_addr,
			 phy_data);

	return retval;
}

int emac_hw_ack_phy_intr(struct emac_hw *hw)
{
	/* ack phy interrupt */
	return 0;
}

/* read phy configuration and initialize it */
int emac_hw_config_phy(struct platform_device *pdev, struct emac_adapter *adpt)
{
	int ret;

	/* get no_ephy attribute */
	adpt->no_ephy = of_property_read_bool(pdev->dev.of_node,
					      "qcom,no-external-phy");

	/* get phy address on MDIO bus */
	if (!adpt->no_ephy) {
		ret = of_property_read_u32(pdev->dev.of_node, "phy-addr",
					   &adpt->hw.phy_addr);
		if (ret)
			return ret;
	}

	/* get phy mode */
	ret = of_get_phy_mode(pdev->dev.of_node);
	if (ret < 0)
		return ret;

	adpt->hw.ops = (adpt->phy_mode == PHY_INTERFACE_MODE_RGMII) ?
		       emac_rgmii_ops : emac_sgmii_v1_ops;

	if (adpt->no_ephy)
		adpt->no_mdio_gpio = true;

	ret = adpt->hw.ops.config(pdev, adpt);
	if (ret)
		return ret;

	spin_lock_init(&adpt->hw.mdio_lock);

	adpt->hw.autoneg = true;
	adpt->hw.autoneg_advertised = EMAC_LINK_SPEED_DEFAULT;

	return adpt->hw.ops.init(adpt);
}

/* initialize external phy */
int emac_hw_init_ephy(struct emac_hw *hw)
{
	u16 phy_id[2];
	int retval = 0;

	if (!emac_hw_get_adap(hw)->no_ephy) {
		retval = emac_read_phy_reg(hw, hw->phy_addr,
					   MII_PHYSID1, &phy_id[0]);
		if (retval)
			return retval;
		retval = emac_read_phy_reg(hw, hw->phy_addr,
					   MII_PHYSID2, &phy_id[1]);
		if (retval)
			return retval;

		hw->phy_id[0] = phy_id[0];
		hw->phy_id[1] = phy_id[1];
	} else {
		emac_disable_mdio_autopoll(hw);
	}

	return hw->ops.init_ephy(hw);
}

static int emac_hw_setup_phy_link(struct emac_hw *hw, u32 speed, bool autoneg,
				  bool fc)
{
	u16 adv, bmcr, ctrl1000 = 0;
	int retval = 0;

	if (autoneg) {
		switch (hw->req_fc_mode) {
		case emac_fc_full:
		case emac_fc_rx_pause:
			adv = ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM;
			break;
		case emac_fc_tx_pause:
			adv = ADVERTISE_PAUSE_ASYM;
			break;
		default:
			adv = 0;
			break;
		}
		if (!fc)
			adv &= ~(ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM);

		if (speed & EMAC_LINK_SPEED_10_HALF)
			adv |= ADVERTISE_10HALF;

		if (speed & EMAC_LINK_SPEED_10_FULL)
			adv |= ADVERTISE_10HALF | ADVERTISE_10FULL;

		if (speed & EMAC_LINK_SPEED_100_HALF)
			adv |= ADVERTISE_100HALF;

		if (speed & EMAC_LINK_SPEED_100_FULL)
			adv |= ADVERTISE_100HALF | ADVERTISE_100FULL;

		if (speed & EMAC_LINK_SPEED_1GB_FULL)
			ctrl1000 |= ADVERTISE_1000FULL;

		retval |= emac_write_phy_reg(hw, hw->phy_addr,
					     MII_ADVERTISE, adv);
		retval |= emac_write_phy_reg(hw, hw->phy_addr,
					     MII_CTRL1000, ctrl1000);

		bmcr = BMCR_RESET | BMCR_ANENABLE | BMCR_ANRESTART;
		retval |= emac_write_phy_reg(hw, hw->phy_addr, MII_BMCR, bmcr);
	} else {
		bmcr = BMCR_RESET;
		switch (speed) {
		case EMAC_LINK_SPEED_10_HALF:
			bmcr |= BMCR_SPEED10;
			break;
		case EMAC_LINK_SPEED_10_FULL:
			bmcr |= BMCR_SPEED10 | BMCR_FULLDPLX;
			break;
		case EMAC_LINK_SPEED_100_HALF:
			bmcr |= BMCR_SPEED100;
			break;
		case EMAC_LINK_SPEED_100_FULL:
			bmcr |= BMCR_SPEED100 | BMCR_FULLDPLX;
			break;
		default:
			return -EINVAL;
		}

		retval |= emac_write_phy_reg(hw, hw->phy_addr, MII_BMCR, bmcr);
	}

	return retval;
}

int emac_setup_phy_link(struct emac_hw *hw, u32 speed, bool autoneg, bool fc)
{
	struct emac_adapter *adpt = emac_hw_get_adap(hw);
	int retval = 0;

	if (adpt->no_ephy)
		return adpt->hw.ops.link_setup_no_ephy(adpt, speed, autoneg);

	if (emac_hw_setup_phy_link(hw, speed, autoneg, fc)) {
		emac_err(adpt, "error when init phy speed and fc\n");
		retval = -EINVAL;
	} else {
		hw->autoneg = autoneg;
	}

	return retval;
}

int emac_setup_phy_link_speed(struct emac_hw *hw, u32 speed,
			      bool autoneg, bool fc)
{
	/* update speed based on input link speed */
	hw->autoneg_advertised = speed & EMAC_LINK_SPEED_DEFAULT;
	return emac_setup_phy_link(hw, hw->autoneg_advertised, autoneg, fc);
}

int emac_check_phy_link(struct emac_hw *hw, u32 *speed, bool *link_up)
{
	struct emac_adapter *adpt = emac_hw_get_adap(hw);
	u16 bmsr, pssr;
	int retval;

	if (adpt->no_ephy)
		return hw->ops.link_check_no_ephy(adpt, speed, link_up);

	retval = emac_read_phy_reg(hw, hw->phy_addr, MII_BMSR, &bmsr);
	if (retval)
		return retval;

	if (!(bmsr & BMSR_LSTATUS)) {
		*link_up = false;
		*speed = EMAC_LINK_SPEED_UNKNOWN;
		return 0;
	}
	*link_up = true;
	retval = emac_read_phy_reg(hw, hw->phy_addr, MII_PSSR, &pssr);
	if (retval)
		return retval;

	if (!(pssr & PSSR_SPD_DPLX_RESOLVED)) {
		emac_err(adpt, "error for speed duplex resolved\n");
		return -EINVAL;
	}

	switch (pssr & PSSR_SPEED) {
	case PSSR_1000MBS:
		if (pssr & PSSR_DPLX)
			*speed = EMAC_LINK_SPEED_1GB_FULL;
		else
			emac_err(adpt, "1000M half duplex is invalid");
		break;
	case PSSR_100MBS:
		if (pssr & PSSR_DPLX)
			*speed = EMAC_LINK_SPEED_100_FULL;
		else
			*speed = EMAC_LINK_SPEED_100_HALF;
		break;
	case PSSR_10MBS:
		if (pssr & PSSR_DPLX)
			*speed = EMAC_LINK_SPEED_10_FULL;
		else
			*speed = EMAC_LINK_SPEED_10_HALF;
		break;
	default:
		*speed = EMAC_LINK_SPEED_UNKNOWN;
		retval = -EINVAL;
		break;
	}

	return retval;
}

int emac_hw_get_lpa_speed(struct emac_hw *hw, u32 *speed)
{
	struct emac_adapter *adpt = emac_hw_get_adap(hw);
	int retval;
	u16 lpa, stat1000;
	bool link;

	if (adpt->no_ephy)
		return hw->ops.link_check_no_ephy(adpt, speed, &link);

	retval = emac_read_phy_reg(hw, hw->phy_addr, MII_LPA, &lpa);
	retval |= emac_read_phy_reg(hw, hw->phy_addr, MII_STAT1000, &stat1000);
	if (retval)
		return retval;

	*speed = EMAC_LINK_SPEED_10_HALF;
	if (lpa & LPA_10FULL)
		*speed = EMAC_LINK_SPEED_10_FULL;
	else if (lpa & LPA_10HALF)
		*speed = EMAC_LINK_SPEED_10_HALF;
	else if (lpa & LPA_100FULL)
		*speed = EMAC_LINK_SPEED_100_FULL;
	else if (lpa & LPA_100HALF)
		*speed = EMAC_LINK_SPEED_100_HALF;
	else if (stat1000 & LPA_1000FULL)
		*speed = EMAC_LINK_SPEED_1GB_FULL;

	return 0;
}

/* INTR */
void emac_hw_enable_intr(struct emac_hw *hw)
{
@@ -898,96 +456,6 @@ static void emac_hw_config_dma_ctrl(struct emac_hw *hw)
	wmb(); /* ensure that the DMA configuration is flushed to HW */
}

/* Flow Control (fc)  */
static int emac_get_fc_mode(struct emac_hw *hw, enum emac_fc_mode *mode)
{
	struct emac_adapter *adpt = emac_hw_get_adap(hw);
	u16 i, bmsr = 0, pssr = 0;
	int retval = 0;

	for (i = 0; i < EMAC_MAX_SETUP_LNK_CYCLE; i++) {
		retval = emac_read_phy_reg(hw, hw->phy_addr, MII_BMSR, &bmsr);
		if (retval)
			return retval;

		if (bmsr & BMSR_LSTATUS) {
			retval = emac_read_phy_reg(hw, hw->phy_addr,
						   MII_PSSR, &pssr);
			if (retval)
				return retval;

			if (!(pssr & PSSR_SPD_DPLX_RESOLVED)) {
				emac_err(adpt,
					 "error for speed duplex resolved\n");
				return -EINVAL;
			}

			if ((pssr & PSSR_FC_TXEN) &&
			    (pssr & PSSR_FC_RXEN)) {
				*mode = (hw->req_fc_mode == emac_fc_full) ?
					emac_fc_full : emac_fc_rx_pause;
			} else if (pssr & PSSR_FC_TXEN) {
				*mode = emac_fc_tx_pause;
			} else if (pssr & PSSR_FC_RXEN) {
				*mode = emac_fc_rx_pause;
			} else {
				*mode = emac_fc_none;
			}
			break;
		}
		msleep(100); /* link can take upto few seconds to come up */
	}

	if (i == EMAC_MAX_SETUP_LNK_CYCLE) {
		emac_err(adpt, "error when get flow control mode\n");
		retval = -EINVAL;
	}

	return retval;
}

int emac_hw_config_fc(struct emac_hw *hw)
{
	struct emac_adapter *adpt = emac_hw_get_adap(hw);
	u32 mac;
	int retval;

	if (hw->disable_fc_autoneg || adpt->no_ephy) {
		hw->cur_fc_mode = hw->req_fc_mode;
	} else {
		retval = emac_get_fc_mode(hw, &hw->cur_fc_mode);
		if (retval)
			return retval;
	}

	mac = emac_reg_r32(hw, EMAC, EMAC_MAC_CTRL);

	switch (hw->cur_fc_mode) {
	case emac_fc_none:
		mac &= ~(RXFC | TXFC);
		break;
	case emac_fc_rx_pause:
		mac &= ~TXFC;
		mac |= RXFC;
		break;
	case emac_fc_tx_pause:
		mac |= TXFC;
		mac &= ~RXFC;
		break;
	case emac_fc_full:
	case emac_fc_default:
		mac |= (TXFC | RXFC);
		break;
	default:
		emac_err(adpt, "flow control param set incorrectly\n");
		return -EINVAL;
	}

	emac_reg_w32(hw, EMAC, EMAC_MAC_CTRL, mac);
	wmb(); /* ensure that the flow-control configuration is flushed to HW */
	return 0;
}

/* Configure MAC */
void emac_hw_config_mac(struct emac_hw *hw)
{
@@ -1037,6 +505,7 @@ void emac_hw_reset_mac(struct emac_hw *hw)
void emac_hw_start_mac(struct emac_hw *hw)
{
	struct emac_adapter *adpt = emac_hw_get_adap(hw);
	struct emac_phy *phy = &adpt->phy;
	u32 mac, csr1;

	/* enable tx queue */
@@ -1054,14 +523,14 @@ void emac_hw_start_mac(struct emac_hw *hw)
	mac |= TXEN | RXEN;     /* enable RX/TX */

	/* enable RX/TX Flow Control */
	switch (hw->cur_fc_mode) {
	case emac_fc_full:
	switch (phy->cur_fc_mode) {
	case EMAC_FC_FULL:
		mac |= (TXFC | RXFC);
		break;
	case emac_fc_rx_pause:
	case EMAC_FC_RX_PAUSE:
		mac |= RXFC;
		break;
	case emac_fc_tx_pause:
	case EMAC_FC_TX_PAUSE:
		mac |= TXFC;
		break;
	default:
@@ -1070,7 +539,7 @@ void emac_hw_start_mac(struct emac_hw *hw)

	/* setup link speed */
	mac &= ~SPEED_BMSK;
	switch (hw->link_speed) {
	switch (phy->link_speed) {
	case EMAC_LINK_SPEED_1GB_FULL:
		mac |= ((emac_mac_speed_1000 << SPEED_SHFT) & SPEED_BMSK);
		csr1 |= FREQ_MODE;
@@ -1081,7 +550,7 @@ void emac_hw_start_mac(struct emac_hw *hw)
		break;
	}

	switch (hw->link_speed) {
	switch (phy->link_speed) {
	case EMAC_LINK_SPEED_1GB_FULL:
	case EMAC_LINK_SPEED_100_FULL:
	case EMAC_LINK_SPEED_10_FULL:
@@ -1112,7 +581,7 @@ void emac_hw_start_mac(struct emac_hw *hw)
		      IRQ_MODERATOR_EN | IRQ_MODERATOR2_EN));

	if (TEST_FLAG(hw, HW_PTP_CAP))
		emac_ptp_set_linkspeed(hw, hw->link_speed);
		emac_ptp_set_linkspeed(hw, phy->link_speed);

	emac_hw_config_mac_ctrl(hw);

+4 −20
Original line number Diff line number Diff line
@@ -24,26 +24,10 @@
/* REG */
u32 emac_reg_r32(struct emac_hw *hw, u8 base, u32 reg);
void emac_reg_w32(struct emac_hw *hw, u8 base, u32 reg, u32 val);
void emac_reg_update32(struct emac_hw *hw, u8 base, u32 reg, u32 mask, u32 val);
u32 emac_reg_field_r32(struct emac_hw *hw, u8 base, u32 reg, u32 mask,
		       u32 shift);
/* PHY */
int emac_hw_read_phy_reg(struct emac_hw *hw, bool ext, u8 dev, bool fast,
			 u16 reg_addr, u16 *phy_data);
int emac_hw_write_phy_reg(struct emac_hw *hw, bool ext, u8 dev, bool fast,
			  u16 reg_addr, u16 phy_data);
int emac_read_phy_reg(struct emac_hw *hw, u16 phy_addr, u16 reg_addr,
		      u16 *phy_data);
int emac_write_phy_reg(struct emac_hw *hw, u16 phy_addr, u16 reg_addr,
		       u16 phy_data);
int emac_setup_phy_link(struct emac_hw *hw, u32 speed, bool autoneg, bool fc);
int emac_setup_phy_link_speed(struct emac_hw *hw, u32 speed, bool autoneg,
			      bool fc);
int emac_check_phy_link(struct emac_hw *hw, u32 *speed, bool *link_up);
int emac_hw_get_lpa_speed(struct emac_hw *hw, u32 *speed);
int emac_hw_ack_phy_intr(struct emac_hw *hw);
int emac_hw_config_phy(struct platform_device *pdev, struct emac_adapter *adpt);
int emac_hw_init_ephy(struct emac_hw *hw);
void emac_reg_update32(struct emac_hw *hw, u8 base, u32 reg,
		       u32 mask, u32 val);
u32 emac_reg_field_r32(struct emac_hw *hw, u8 base, u32 reg,
		       u32 mask, u32 shift);
void emac_hw_config_pow_save(struct emac_hw *hw, u32 speed, bool wol_en,
			     bool rx_en);
/* MAC */
Loading