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>
This commit is contained in:
Rohit Vaswani
2011-12-16 13:38:02 -08:00
parent 10d87eafd6
commit 73299b46c7
2 changed files with 130 additions and 42 deletions

View File

@@ -51,7 +51,14 @@
#define PMIC_GPIO_SD_DET 165 #define PMIC_GPIO_SD_DET 165
#define GPIO_EPHY_RST_N 37 #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_0 136 /* GRFC 20 */
#define GPIO_GRFC_FTR0_1 137 /* GRFC 21 */ #define GPIO_GRFC_FTR0_1 137 /* GRFC 21 */
#define GPIO_GRFC_FTR1_0 145 /* GRFC 22 */ #define GPIO_GRFC_FTR1_0 145 /* GRFC 22 */
@@ -472,7 +479,23 @@ static int __init buses_init(void)
static struct msm_gpio phy_config_data[] = { static struct msm_gpio phy_config_data[] = {
{ GPIO_CFG(GPIO_EPHY_RST_N, 0, GPIO_CFG_OUTPUT, { 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) static int __init phy_init(void)

View File

@@ -33,17 +33,20 @@
#include "qfec.h" #include "qfec.h"
#define QFEC_NAME "qfec" #define QFEC_NAME "qfec"
#define QFEC_DRV_VER "July 14 2011" #define QFEC_DRV_VER "Nov 29 2011"
#define ETH_BUF_SIZE 0x600 #define ETH_BUF_SIZE 0x600
#define MAX_N_BD 50 #define MAX_N_BD 50
#define MAC_ADDR_SIZE 6 #define MAC_ADDR_SIZE 6
#define RX_TX_BD_RATIO 8 #define RX_TX_BD_RATIO 8
#define RX_BD_NUM 32 #define TX_BD_NUM 256
#define TX_BD_NUM (RX_BD_NUM * RX_TX_BD_RATIO) #define RX_BD_NUM 256
#define TX_BD_TI_RATIO 4 #define TX_BD_TI_RATIO 4
#define MAX_MDIO_REG 32
#define H_DPLX 0
#define F_DPLX 1
/* /*
* logging macros * logging macros
*/ */
@@ -52,6 +55,8 @@
#define QFEC_LOG_DBG2 4 #define QFEC_LOG_DBG2 4
#define QFEC_LOG_MDIO_W 8 #define QFEC_LOG_MDIO_W 8
#define QFEC_LOG_MDIO_R 16 #define QFEC_LOG_MDIO_R 16
#define QFEC_MII_EXP_MASK (EXPANSION_LCWP | EXPANSION_ENABLENPAGE \
| EXPANSION_NPCAPABLE)
static int qfec_debug = QFEC_LOG_PR; static int qfec_debug = QFEC_LOG_PR;
@@ -297,13 +302,15 @@ static inline int qfec_ring_empty(struct ring *p_ring)
static inline void qfec_ring_head_adv(struct ring *p_ring) 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--; p_ring->n_free--;
}; };
static inline void qfec_ring_tail_adv(struct ring *p_ring) 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++; p_ring->n_free++;
}; };
@@ -809,6 +816,13 @@ static int qfec_hw_init(struct qfec_priv *priv)
qfec_reg_write(priv, STATUS_REG, INTRP_EN_REG_NIE | INTRP_EN_REG_RIE 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); | 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; return res;
} }
@@ -874,9 +888,9 @@ static inline void qfec_clkreg_write(struct qfec_priv *priv,
* configure the PHY interface and clock routing and signal bits * configure the PHY interface and clock routing and signal bits
*/ */
enum phy_intfc { enum phy_intfc {
intfc_mii = 0, INTFC_MII = 0,
intfc_rgmii = 1, INTFC_RGMII = 1,
intfc_revmii = 2, INTFC_REVMII = 2,
}; };
static int qfec_intf_sel(struct qfec_priv *priv, unsigned int intfc) static int qfec_intf_sel(struct qfec_priv *priv, unsigned int intfc)
@@ -885,7 +899,7 @@ static int qfec_intf_sel(struct qfec_priv *priv, unsigned int intfc)
QFEC_LOG(QFEC_LOG_DBG2, "%s: %d\n", __func__, intfc); 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__); QFEC_LOG_ERR("%s: range\n", __func__);
return -ENXIO; return -ENXIO;
} }
@@ -972,9 +986,9 @@ static struct qfec_pll_cfg qfec_pll_cfg_tbl[] = {
}; };
enum speed { enum speed {
spd_10 = 0, SPD_10 = 0,
spd_100 = 1, SPD_100 = 1,
spd_1000 = 2, SPD_1000 = 2,
}; };
/* /*
@@ -988,7 +1002,7 @@ static int qfec_speed_cfg(struct net_device *dev, unsigned int spd,
QFEC_LOG(QFEC_LOG_DBG2, "%s: %d spd, %d dplx\n", __func__, spd, dplx); 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__); QFEC_LOG_ERR("%s: range\n", __func__);
return -ENODEV; return -ENODEV;
} }
@@ -999,7 +1013,7 @@ static int qfec_speed_cfg(struct net_device *dev, unsigned int spd,
qfec_reg_write(priv, MAC_CONFIG_REG, qfec_reg_write(priv, MAC_CONFIG_REG,
(qfec_reg_read(priv, MAC_CONFIG_REG) (qfec_reg_read(priv, MAC_CONFIG_REG)
& ~(MAC_CONFIG_REG_SPD | MAC_CONFIG_REG_DM)) & ~(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_MD_REG, p->eth_md);
qfec_clkreg_write(priv, ETH_NS_REG, p->eth_ns); qfec_clkreg_write(priv, ETH_NS_REG, p->eth_ns);
@@ -1145,10 +1159,34 @@ static void qfec_mdio_write(struct net_device *dev, int phy_id, int reg,
spin_unlock_irqrestore(&priv->mdio_lock, flags); spin_unlock_irqrestore(&priv->mdio_lock, flags);
} }
/*
* 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 * get auto-negotiation results
*/ */
#define QFEC_100 (LPA_100HALF | LPA_100FULL | LPA_100HALF) #define QFEC_100 (LPA_100HALF | LPA_100FULL | LPA_100HALF)
#define QFEC_100_FD (LPA_100FULL | LPA_100BASE4) #define QFEC_100_FD (LPA_100FULL | LPA_100BASE4)
#define QFEC_10 (LPA_10HALF | LPA_10FULL) #define QFEC_10 (LPA_10HALF | LPA_10FULL)
@@ -1157,28 +1195,45 @@ static void qfec_mdio_write(struct net_device *dev, int phy_id, int reg,
static void qfec_get_an(struct net_device *dev, uint32_t *spd, uint32_t *dplx) static void qfec_get_an(struct net_device *dev, uint32_t *spd, uint32_t *dplx)
{ {
struct qfec_priv *priv = netdev_priv(dev); struct qfec_priv *priv = netdev_priv(dev);
uint32_t status; uint32_t advert = qfec_mdio_read(dev, priv->phy_id, MII_ADVERTISE);
uint32_t advert; uint32_t lpa = qfec_mdio_read(dev, priv->phy_id, MII_LPA);
uint32_t 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; uint32_t flow;
advert = qfec_mdio_read(dev, priv->phy_id, MII_ADVERTISE); if (priv->mii.supports_gmii) {
lpa = qfec_mdio_read(dev, priv->phy_id, MII_LPA); if (((anExp & QFEC_MII_EXP_MASK) == QFEC_MII_EXP_MASK)
status = advert & lpa; && (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) { if (status & QFEC_100) {
*spd = spd_100; *spd = SPD_100;
*dplx = status & QFEC_100_FD ? 1 : 0; *dplx = status & QFEC_100_FD ? F_DPLX : H_DPLX;
} }
else if (status & QFEC_10) { else if (status & QFEC_10) {
*spd = spd_10; *spd = SPD_10;
*dplx = status & QFEC_10_FD ? 1 : 0; *dplx = status & QFEC_10_FD ? F_DPLX : H_DPLX;
} }
/* check pause */ /* check pause */
pause:
flow = qfec_reg_read(priv, FLOW_CONTROL_REG); flow = qfec_reg_read(priv, FLOW_CONTROL_REG);
flow &= ~(FLOW_CONTROL_TFE | FLOW_CONTROL_RFE); flow &= ~(FLOW_CONTROL_TFE | FLOW_CONTROL_RFE);
@@ -1202,8 +1257,8 @@ static void qfec_phy_monitor(unsigned long data)
{ {
struct net_device *dev = (struct net_device *) data; struct net_device *dev = (struct net_device *) data;
struct qfec_priv *priv = netdev_priv(dev); struct qfec_priv *priv = netdev_priv(dev);
unsigned int spd = 0; unsigned int spd = H_DPLX;
unsigned int dplx = 1; unsigned int dplx = F_DPLX;
mod_timer(&priv->phy_tmr, jiffies + HZ); mod_timer(&priv->phy_tmr, jiffies + HZ);
@@ -1609,15 +1664,8 @@ static void qfec_rx_int(struct net_device *dev)
CNTR_INC(priv, rx_int); CNTR_INC(priv, rx_int);
/* check that valid interrupt occurred */ /* check that valid interrupt occurred */
if (unlikely(desc_status & BUF_OWN)) { 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);
return; return;
}
/* accumulate missed-frame count (reg reset when read) */ /* accumulate missed-frame count (reg reset when read) */
priv->stats.rx_missed_errors += mis_fr_reg priv->stats.rx_missed_errors += mis_fr_reg
@@ -1691,6 +1739,8 @@ static void qfec_rx_int(struct net_device *dev)
break; break;
qfec_ring_tail_adv(p_ring); qfec_ring_tail_adv(p_ring);
} }
qfec_reg_write(priv, STATUS_REG, STATUS_REG_RI);
} }
/* /*
@@ -1748,8 +1798,10 @@ static irqreturn_t qfec_int(int irq, void *dev_id)
/* gmac interrupt */ /* gmac interrupt */
if (status & (STATUS_REG_GPI | STATUS_REG_GMI | STATUS_REG_GLI)) { 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); 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 */ /* clear interrupts */
@@ -1823,7 +1875,14 @@ static int qfec_open(struct net_device *dev)
qfec_ptp_cfg(priv); qfec_ptp_cfg(priv);
/* configure PHY - must be set before reset/hw_init */ /* 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 */ /* initialize controller after BDs allocated */
res = qfec_hw_init(priv); res = qfec_hw_init(priv);
@@ -1923,6 +1982,10 @@ static int qfec_xmit(struct sk_buff *skb, struct net_device *dev)
spin_lock_irqsave(&priv->xmit_lock, flags); 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 */ /* stop queuing if no resources available */
if (qfec_ring_room(p_ring) == 0) { if (qfec_ring_room(p_ring) == 0) {
qfec_queue_stop(dev); qfec_queue_stop(dev);
@@ -2477,6 +2540,7 @@ static DEVICE_ATTR(clk_reg, 0444, qfec_clk_reg_show, NULL);
static DEVICE_ATTR(cmd, 0222, NULL, qfec_cmd); static DEVICE_ATTR(cmd, 0222, NULL, qfec_cmd);
static DEVICE_ATTR(cntrs, 0444, qfec_cntrs_show, NULL); static DEVICE_ATTR(cntrs, 0444, qfec_cntrs_show, NULL);
static DEVICE_ATTR(reg, 0444, qfec_reg_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(stats, 0444, qfec_stats_show, NULL);
static DEVICE_ATTR(tstamp, 0444, qfec_tstamp_show, NULL); static DEVICE_ATTR(tstamp, 0444, qfec_tstamp_show, NULL);
@@ -2488,6 +2552,7 @@ static void qfec_sysfs_create(struct net_device *dev)
device_create_file(&(dev->dev), &dev_attr_clk_reg) || device_create_file(&(dev->dev), &dev_attr_clk_reg) ||
device_create_file(&(dev->dev), &dev_attr_cmd) || device_create_file(&(dev->dev), &dev_attr_cmd) ||
device_create_file(&(dev->dev), &dev_attr_cntrs) || 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_reg) ||
device_create_file(&(dev->dev), &dev_attr_stats) || device_create_file(&(dev->dev), &dev_attr_stats) ||
device_create_file(&(dev->dev), &dev_attr_tstamp)) device_create_file(&(dev->dev), &dev_attr_tstamp))