drivers:net: Add RGMII support to the qfec driver

Added support for RGMII (1G) phys, sysfs/mdio and minor enhancements.

Change-Id: Icffb3965855369430c2d831a7aa1bd1fb73f9951
Acked-by: Kaushik Sikdar <ksikdar@qualcomm.com>
Signed-off-by: Rohit Vaswani <rvaswani@codeaurora.org>
diff --git a/arch/arm/mach-msm/board-fsm9xxx.c b/arch/arm/mach-msm/board-fsm9xxx.c
index 333d366..550e288 100644
--- a/arch/arm/mach-msm/board-fsm9xxx.c
+++ b/arch/arm/mach-msm/board-fsm9xxx.c
@@ -51,7 +51,14 @@
 #define PMIC_GPIO_SD_DET	165
 
 #define GPIO_EPHY_RST_N		37
-
+#define GPIO_MAC_TXD_3      119
+#define GPIO_MAC_TXD_2      120
+#define GPIO_MAC_TXD_1      121
+#define GPIO_MAC_TXD_0      122
+#define GPIO_MAC_TX_EN      123
+#define GPIO_MAC_MDIO       127
+#define GPIO_MAC_MDC        128
+#define GPIO_MAC_TX_CLK     133
 #define GPIO_GRFC_FTR0_0	136 /* GRFC 20 */
 #define GPIO_GRFC_FTR0_1	137 /* GRFC 21 */
 #define GPIO_GRFC_FTR1_0	145 /* GRFC 22 */
@@ -472,7 +479,23 @@
 
 static struct msm_gpio phy_config_data[] = {
 	{ GPIO_CFG(GPIO_EPHY_RST_N, 0, GPIO_CFG_OUTPUT,
-		GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "MAC_RST_N" },
+		GPIO_CFG_NO_PULL, GPIO_CFG_8MA), "MAC_RST_N" },
+	{ GPIO_CFG(GPIO_MAC_TXD_3, 0, GPIO_CFG_OUTPUT,
+		GPIO_CFG_NO_PULL, GPIO_CFG_8MA), "MAC_TXD_3"},
+	{ GPIO_CFG(GPIO_MAC_TXD_2, 0, GPIO_CFG_OUTPUT,
+		GPIO_CFG_NO_PULL, GPIO_CFG_8MA), "MAC_TXD_2"},
+	{ GPIO_CFG(GPIO_MAC_TXD_1, 0, GPIO_CFG_OUTPUT,
+		GPIO_CFG_NO_PULL, GPIO_CFG_8MA), "MAC_TXD_1"},
+	{ GPIO_CFG(GPIO_MAC_TXD_0, 0, GPIO_CFG_OUTPUT,
+		GPIO_CFG_NO_PULL, GPIO_CFG_8MA), "MAC_TXD_0"},
+	{ GPIO_CFG(GPIO_MAC_TX_EN, 0, GPIO_CFG_OUTPUT,
+		GPIO_CFG_NO_PULL, GPIO_CFG_8MA), "MAC_TX_EN"},
+	{ GPIO_CFG(GPIO_MAC_TX_CLK, 0, GPIO_CFG_OUTPUT,
+		GPIO_CFG_NO_PULL, GPIO_CFG_10MA), "MAC_TX_CLK"},
+	{ GPIO_CFG(GPIO_MAC_MDIO, 0, GPIO_CFG_OUTPUT,
+		GPIO_CFG_NO_PULL, GPIO_CFG_6MA), "MDIO_MAC_MDIO"},
+	{ GPIO_CFG(GPIO_MAC_MDC, 0, GPIO_CFG_OUTPUT,
+		GPIO_CFG_NO_PULL, GPIO_CFG_6MA), "MDC_MAC_MDC"},
 };
 
 static int __init phy_init(void)
diff --git a/drivers/net/qfec.c b/drivers/net/qfec.c
index d554fec..fc27837 100644
--- a/drivers/net/qfec.c
+++ b/drivers/net/qfec.c
@@ -33,17 +33,20 @@
 #include "qfec.h"
 
 #define QFEC_NAME       "qfec"
-#define QFEC_DRV_VER    "July 14 2011"
+#define QFEC_DRV_VER    "Nov 29 2011"
 
 #define ETH_BUF_SIZE    0x600
 #define MAX_N_BD        50
 #define MAC_ADDR_SIZE	6
 
 #define RX_TX_BD_RATIO  8
-#define RX_BD_NUM       32
-#define TX_BD_NUM       (RX_BD_NUM * RX_TX_BD_RATIO)
+#define TX_BD_NUM       256
+#define RX_BD_NUM       256
 #define TX_BD_TI_RATIO  4
+#define MAX_MDIO_REG    32
 
+#define H_DPLX     0
+#define F_DPLX     1
 /*
  * logging macros
  */
@@ -52,6 +55,8 @@
 #define QFEC_LOG_DBG2   4
 #define QFEC_LOG_MDIO_W 8
 #define QFEC_LOG_MDIO_R 16
+#define QFEC_MII_EXP_MASK (EXPANSION_LCWP | EXPANSION_ENABLENPAGE \
+							| EXPANSION_NPCAPABLE)
 
 static int qfec_debug = QFEC_LOG_PR;
 
@@ -297,13 +302,15 @@
 
 static inline void qfec_ring_head_adv(struct ring *p_ring)
 {
-	p_ring->head = ++p_ring->head % p_ring->len;
+	if (++p_ring->head == p_ring->len)
+		p_ring->head = 0;
 	p_ring->n_free--;
 };
 
 static inline void qfec_ring_tail_adv(struct ring *p_ring)
 {
-	p_ring->tail = ++p_ring->tail % p_ring->len;
+	if (++p_ring->tail == p_ring->len)
+		p_ring->tail = 0;
 	p_ring->n_free++;
 };
 
@@ -809,6 +816,13 @@
 	qfec_reg_write(priv, STATUS_REG, INTRP_EN_REG_NIE | INTRP_EN_REG_RIE
 		| INTRP_EN_REG_TIE | INTRP_EN_REG_TUE | INTRP_EN_REG_ETE);
 
+	if (priv->mii.supports_gmii) {
+		/* Clear RGMII */
+		qfec_reg_read(priv, SG_RG_SMII_STATUS_REG);
+		/* Disable RGMII int */
+		qfec_reg_write(priv, INTRP_MASK_REG, 1);
+	}
+
 	return res;
 }
 
@@ -874,9 +888,9 @@
  * configure the PHY interface and clock routing and signal bits
  */
 enum phy_intfc  {
-	intfc_mii     = 0,
-	intfc_rgmii   = 1,
-	intfc_revmii  = 2,
+	INTFC_MII     = 0,
+	INTFC_RGMII   = 1,
+	INTFC_REVMII  = 2,
 };
 
 static int qfec_intf_sel(struct qfec_priv *priv, unsigned int intfc)
@@ -885,7 +899,7 @@
 
 	QFEC_LOG(QFEC_LOG_DBG2, "%s: %d\n", __func__, intfc);
 
-	if (intfc > intfc_revmii)  {
+	if (intfc > INTFC_REVMII)  {
 		QFEC_LOG_ERR("%s: range\n", __func__);
 		return -ENXIO;
 	}
@@ -972,9 +986,9 @@
 };
 
 enum speed  {
-	spd_10   = 0,
-	spd_100  = 1,
-	spd_1000 = 2,
+	SPD_10   = 0,
+	SPD_100  = 1,
+	SPD_1000 = 2,
 };
 
 /*
@@ -988,7 +1002,7 @@
 
 	QFEC_LOG(QFEC_LOG_DBG2, "%s: %d spd, %d dplx\n", __func__, spd, dplx);
 
-	if (spd > spd_1000)  {
+	if (spd > SPD_1000)  {
 		QFEC_LOG_ERR("%s: range\n", __func__);
 		return -ENODEV;
 	}
@@ -999,7 +1013,7 @@
 	qfec_reg_write(priv, MAC_CONFIG_REG,
 	(qfec_reg_read(priv, MAC_CONFIG_REG)
 		& ~(MAC_CONFIG_REG_SPD | MAC_CONFIG_REG_DM))
-			| p->spd | (dplx ? MAC_CONFIG_REG_DM : 0));
+			| p->spd | (dplx ? MAC_CONFIG_REG_DM : H_DPLX));
 
 	qfec_clkreg_write(priv, ETH_MD_REG, p->eth_md);
 	qfec_clkreg_write(priv, ETH_NS_REG, p->eth_ns);
@@ -1146,9 +1160,33 @@
 }
 
 /*
+ * MDIO show
+ */
+static int qfec_mdio_show(struct device *dev, struct device_attribute *attr,
+					char *buf)
+{
+	struct qfec_priv        *priv = netdev_priv(to_net_dev(dev));
+	int                      n;
+	int                      l = 0;
+	int                      count = PAGE_SIZE;
+
+	QFEC_LOG(QFEC_LOG_DBG2, "%s:\n", __func__);
+
+	for (n = 0; n < MAX_MDIO_REG; n++) {
+		if (!(n % 8))
+			l += snprintf(&buf[l], count - l, "\n   %02x: ", n);
+
+		l += snprintf(&buf[l], count - l, " %04x",
+			qfec_mdio_read(to_net_dev(dev), priv->phy_id, n));
+	}
+	l += snprintf(&buf[l], count - l, "\n");
+
+	return  l;
+}
+
+/*
  * get auto-negotiation results
  */
-
 #define QFEC_100        (LPA_100HALF | LPA_100FULL | LPA_100HALF)
 #define QFEC_100_FD     (LPA_100FULL | LPA_100BASE4)
 #define QFEC_10         (LPA_10HALF  | LPA_10FULL)
@@ -1157,28 +1195,45 @@
 static void qfec_get_an(struct net_device *dev, uint32_t *spd, uint32_t *dplx)
 {
 	struct qfec_priv   *priv = netdev_priv(dev);
-	uint32_t            status;
-	uint32_t            advert;
-	uint32_t            lpa;
-	uint32_t            flow;
+	uint32_t  advert   = qfec_mdio_read(dev, priv->phy_id, MII_ADVERTISE);
+	uint32_t  lpa      = qfec_mdio_read(dev, priv->phy_id, MII_LPA);
+	uint32_t  mastCtrl = qfec_mdio_read(dev, priv->phy_id, MII_CTRL1000);
+	uint32_t  mastStat = qfec_mdio_read(dev, priv->phy_id, MII_STAT1000);
+	uint32_t  anExp    = qfec_mdio_read(dev, priv->phy_id, MII_EXPANSION);
+	uint32_t  status   = advert & lpa;
+	uint32_t  flow;
 
-	advert = qfec_mdio_read(dev, priv->phy_id, MII_ADVERTISE);
-	lpa    = qfec_mdio_read(dev, priv->phy_id, MII_LPA);
-	status = advert & lpa;
+	if (priv->mii.supports_gmii) {
+		if (((anExp & QFEC_MII_EXP_MASK) == QFEC_MII_EXP_MASK)
+			&& (mastCtrl & ADVERTISE_1000FULL)
+			&& (mastStat & LPA_1000FULL)) {
+			*spd  = SPD_1000;
+			*dplx = F_DPLX;
+			goto pause;
+		}
 
-	/* todo: check extended status register for 1G abilities */
+		else if (((anExp & QFEC_MII_EXP_MASK) == QFEC_MII_EXP_MASK)
+			&& (mastCtrl & ADVERTISE_1000HALF)
+			&& (mastStat & LPA_1000HALF)) {
+			*spd  = SPD_1000;
+			*dplx = H_DPLX;
+			goto pause;
+		}
+	}
 
+	/* mii speeds */
 	if (status & QFEC_100)  {
-		*spd  = spd_100;
-		*dplx = status & QFEC_100_FD ? 1 : 0;
+		*spd  = SPD_100;
+		*dplx = status & QFEC_100_FD ? F_DPLX : H_DPLX;
 	}
 
 	else if (status & QFEC_10)  {
-		*spd  = spd_10;
-		*dplx = status & QFEC_10_FD ? 1 : 0;
+		*spd  = SPD_10;
+		*dplx = status & QFEC_10_FD ? F_DPLX : H_DPLX;
 	}
 
 	/* check pause */
+pause:
 	flow  = qfec_reg_read(priv, FLOW_CONTROL_REG);
 	flow &= ~(FLOW_CONTROL_TFE | FLOW_CONTROL_RFE);
 
@@ -1202,8 +1257,8 @@
 {
 	struct net_device  *dev  = (struct net_device *) data;
 	struct qfec_priv   *priv = netdev_priv(dev);
-	unsigned int        spd  = 0;
-	unsigned int        dplx = 1;
+	unsigned int        spd  = H_DPLX;
+	unsigned int        dplx = F_DPLX;
 
 	mod_timer(&priv->phy_tmr, jiffies + HZ);
 
@@ -1609,15 +1664,8 @@
 	CNTR_INC(priv, rx_int);
 
 	/* check that valid interrupt occurred */
-	if (unlikely(desc_status & BUF_OWN)) {
-		char  s[100];
-
-		qfec_bd_fmt(s, sizeof(s), p_bd);
-		QFEC_LOG_ERR("%s: owned by DMA, %08x, %s\n", __func__,
-			qfec_reg_read(priv, CUR_HOST_RX_DES_REG), s);
-		CNTR_INC(priv, rx_owned);
+	if (unlikely(desc_status & BUF_OWN))
 		return;
-	}
 
 	/* accumulate missed-frame count (reg reset when read) */
 	priv->stats.rx_missed_errors += mis_fr_reg
@@ -1691,6 +1739,8 @@
 			break;
 		qfec_ring_tail_adv(p_ring);
 	}
+
+	qfec_reg_write(priv, STATUS_REG, STATUS_REG_RI);
 }
 
 /*
@@ -1735,7 +1785,7 @@
 		CNTR_INC(priv, norm_int);
 
 	/* receive interrupt */
-	if (status & STATUS_REG_RI)  {
+	if (status & STATUS_REG_RI) {
 		CNTR_INC(priv, rx_isr);
 		qfec_rx_int(dev);
 	}
@@ -1748,8 +1798,10 @@
 
 	/* gmac interrupt */
 	if (status & (STATUS_REG_GPI | STATUS_REG_GMI | STATUS_REG_GLI))  {
+		status &= ~(STATUS_REG_GPI | STATUS_REG_GMI | STATUS_REG_GLI);
 		CNTR_INC(priv, gmac_isr);
-		int_bits |= STATUS_REG_GMI;
+		int_bits |= STATUS_REG_GPI | STATUS_REG_GMI | STATUS_REG_GLI;
+		qfec_reg_read(priv, SG_RG_SMII_STATUS_REG);
 	}
 
 	/* clear interrupts */
@@ -1823,7 +1875,14 @@
 	qfec_ptp_cfg(priv);
 
 	/* configure PHY - must be set before reset/hw_init */
-	qfec_intf_sel(priv, intfc_mii);
+	priv->mii.supports_gmii = mii_check_gmii_support(&priv->mii);
+	if (priv->mii.supports_gmii) {
+		QFEC_LOG_ERR("%s: RGMII\n", __func__);
+		qfec_intf_sel(priv, INTFC_RGMII);
+	} else {
+		QFEC_LOG_ERR("%s: MII\n", __func__);
+		qfec_intf_sel(priv, INTFC_MII);
+	}
 
 	/* initialize controller after BDs allocated */
 	res = qfec_hw_init(priv);
@@ -1923,6 +1982,10 @@
 
 	spin_lock_irqsave(&priv->xmit_lock, flags);
 
+	/* If there is no room, on the ring try to free some up */
+	if (qfec_ring_room(p_ring) == 0)
+		qfec_tx_replenish(dev);
+
 	/* stop queuing if no resources available */
 	if (qfec_ring_room(p_ring) == 0)  {
 		qfec_queue_stop(dev);
@@ -2477,6 +2540,7 @@
 static DEVICE_ATTR(cmd,     0222, NULL,              qfec_cmd);
 static DEVICE_ATTR(cntrs,   0444, qfec_cntrs_show,   NULL);
 static DEVICE_ATTR(reg,     0444, qfec_reg_show,     NULL);
+static DEVICE_ATTR(mdio,    0444, qfec_mdio_show,    NULL);
 static DEVICE_ATTR(stats,   0444, qfec_stats_show,   NULL);
 static DEVICE_ATTR(tstamp,  0444, qfec_tstamp_show,  NULL);
 
@@ -2488,6 +2552,7 @@
 		device_create_file(&(dev->dev), &dev_attr_clk_reg) ||
 		device_create_file(&(dev->dev), &dev_attr_cmd) ||
 		device_create_file(&(dev->dev), &dev_attr_cntrs) ||
+		device_create_file(&(dev->dev), &dev_attr_mdio) ||
 		device_create_file(&(dev->dev), &dev_attr_reg) ||
 		device_create_file(&(dev->dev), &dev_attr_stats) ||
 		device_create_file(&(dev->dev), &dev_attr_tstamp))