#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>
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)
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)
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.