|
@@ -37,6 +37,7 @@
|
|
|
#include <linux/io.h>
|
|
|
#include <linux/kernel.h>
|
|
|
#include <linux/module.h>
|
|
|
+#include <linux/mii.h>
|
|
|
#include <linux/netdevice.h>
|
|
|
#include <linux/of_device.h>
|
|
|
#include <linux/of_mdio.h>
|
|
@@ -96,6 +97,27 @@ static inline u32 tse_tx_avail(struct altera_tse_private *priv)
|
|
|
return priv->tx_cons + priv->tx_ring_size - priv->tx_prod - 1;
|
|
|
}
|
|
|
|
|
|
+/* PCS Register read/write functions
|
|
|
+ */
|
|
|
+static u16 sgmii_pcs_read(struct altera_tse_private *priv, int regnum)
|
|
|
+{
|
|
|
+ return csrrd32(priv->mac_dev,
|
|
|
+ tse_csroffs(mdio_phy0) + regnum * 4) & 0xffff;
|
|
|
+}
|
|
|
+
|
|
|
+static void sgmii_pcs_write(struct altera_tse_private *priv, int regnum,
|
|
|
+ u16 value)
|
|
|
+{
|
|
|
+ csrwr32(value, priv->mac_dev, tse_csroffs(mdio_phy0) + regnum * 4);
|
|
|
+}
|
|
|
+
|
|
|
+/* Check PCS scratch memory */
|
|
|
+static int sgmii_pcs_scratch_test(struct altera_tse_private *priv, u16 value)
|
|
|
+{
|
|
|
+ sgmii_pcs_write(priv, SGMII_PCS_SCRATCH, value);
|
|
|
+ return (sgmii_pcs_read(priv, SGMII_PCS_SCRATCH) == value);
|
|
|
+}
|
|
|
+
|
|
|
/* MDIO specific functions
|
|
|
*/
|
|
|
static int altera_tse_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
|
|
@@ -1083,6 +1105,66 @@ static void tse_set_rx_mode(struct net_device *dev)
|
|
|
spin_unlock(&priv->mac_cfg_lock);
|
|
|
}
|
|
|
|
|
|
+/* Initialise (if necessary) the SGMII PCS component
|
|
|
+ */
|
|
|
+static int init_sgmii_pcs(struct net_device *dev)
|
|
|
+{
|
|
|
+ struct altera_tse_private *priv = netdev_priv(dev);
|
|
|
+ int n;
|
|
|
+ unsigned int tmp_reg = 0;
|
|
|
+
|
|
|
+ if (priv->phy_iface != PHY_INTERFACE_MODE_SGMII)
|
|
|
+ return 0; /* Nothing to do, not in SGMII mode */
|
|
|
+
|
|
|
+ /* The TSE SGMII PCS block looks a little like a PHY, it is
|
|
|
+ * mapped into the zeroth MDIO space of the MAC and it has
|
|
|
+ * ID registers like a PHY would. Sadly this is often
|
|
|
+ * configured to zeroes, so don't be surprised if it does
|
|
|
+ * show 0x00000000.
|
|
|
+ */
|
|
|
+
|
|
|
+ if (sgmii_pcs_scratch_test(priv, 0x0000) &&
|
|
|
+ sgmii_pcs_scratch_test(priv, 0xffff) &&
|
|
|
+ sgmii_pcs_scratch_test(priv, 0xa5a5) &&
|
|
|
+ sgmii_pcs_scratch_test(priv, 0x5a5a)) {
|
|
|
+ netdev_info(dev, "PCS PHY ID: 0x%04x%04x\n",
|
|
|
+ sgmii_pcs_read(priv, MII_PHYSID1),
|
|
|
+ sgmii_pcs_read(priv, MII_PHYSID2));
|
|
|
+ } else {
|
|
|
+ netdev_err(dev, "SGMII PCS Scratch memory test failed.\n");
|
|
|
+ return -ENOMEM;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Starting on page 5-29 of the MegaCore Function User Guide
|
|
|
+ * Set SGMII Link timer to 1.6ms
|
|
|
+ */
|
|
|
+ sgmii_pcs_write(priv, SGMII_PCS_LINK_TIMER_0, 0x0D40);
|
|
|
+ sgmii_pcs_write(priv, SGMII_PCS_LINK_TIMER_1, 0x03);
|
|
|
+
|
|
|
+ /* Enable SGMII Interface and Enable SGMII Auto Negotiation */
|
|
|
+ sgmii_pcs_write(priv, SGMII_PCS_IF_MODE, 0x3);
|
|
|
+
|
|
|
+ /* Enable Autonegotiation */
|
|
|
+ tmp_reg = sgmii_pcs_read(priv, MII_BMCR);
|
|
|
+ tmp_reg |= (BMCR_SPEED1000 | BMCR_FULLDPLX | BMCR_ANENABLE);
|
|
|
+ sgmii_pcs_write(priv, MII_BMCR, tmp_reg);
|
|
|
+
|
|
|
+ /* Reset PCS block */
|
|
|
+ tmp_reg |= BMCR_RESET;
|
|
|
+ sgmii_pcs_write(priv, MII_BMCR, tmp_reg);
|
|
|
+ for (n = 0; n < SGMII_PCS_SW_RESET_TIMEOUT; n++) {
|
|
|
+ if (!(sgmii_pcs_read(priv, MII_BMCR) & BMCR_RESET)) {
|
|
|
+ netdev_info(dev, "SGMII PCS block initialised OK\n");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ udelay(1);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* We failed to reset the block, return a timeout */
|
|
|
+ netdev_err(dev, "SGMII PCS block reset failed.\n");
|
|
|
+ return -ETIMEDOUT;
|
|
|
+}
|
|
|
+
|
|
|
/* Open and initialize the interface
|
|
|
*/
|
|
|
static int tse_open(struct net_device *dev)
|
|
@@ -1107,6 +1189,15 @@ static int tse_open(struct net_device *dev)
|
|
|
netdev_warn(dev, "TSE revision %x\n", priv->revision);
|
|
|
|
|
|
spin_lock(&priv->mac_cfg_lock);
|
|
|
+ /* no-op if MAC not operating in SGMII mode*/
|
|
|
+ ret = init_sgmii_pcs(dev);
|
|
|
+ if (ret) {
|
|
|
+ netdev_err(dev,
|
|
|
+ "Cannot init the SGMII PCS (error: %d)\n", ret);
|
|
|
+ spin_unlock(&priv->mac_cfg_lock);
|
|
|
+ goto phy_error;
|
|
|
+ }
|
|
|
+
|
|
|
ret = reset_mac(priv);
|
|
|
/* Note that reset_mac will fail if the clocks are gated by the PHY
|
|
|
* due to the PHY being put into isolation or power down mode.
|