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:
@@ -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 int __init buses_init(void)
|
||||
|
||||
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)
|
||||
|
||||
@@ -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 int qfec_ring_empty(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--;
|
||||
};
|
||||
|
||||
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 @@ static int qfec_hw_init(struct qfec_priv *priv)
|
||||
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 @@ static inline void qfec_clkreg_write(struct qfec_priv *priv,
|
||||
* 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 @@ static int qfec_intf_sel(struct qfec_priv *priv, unsigned int 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__);
|
||||
return -ENXIO;
|
||||
}
|
||||
@@ -972,9 +986,9 @@ static struct qfec_pll_cfg qfec_pll_cfg_tbl[] = {
|
||||
};
|
||||
|
||||
enum speed {
|
||||
spd_10 = 0,
|
||||
spd_100 = 1,
|
||||
spd_1000 = 2,
|
||||
SPD_10 = 0,
|
||||
SPD_100 = 1,
|
||||
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);
|
||||
|
||||
if (spd > spd_1000) {
|
||||
if (spd > SPD_1000) {
|
||||
QFEC_LOG_ERR("%s: range\n", __func__);
|
||||
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_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);
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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_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)
|
||||
{
|
||||
struct qfec_priv *priv = netdev_priv(dev);
|
||||
uint32_t status;
|
||||
uint32_t advert;
|
||||
uint32_t lpa;
|
||||
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 @@ static void qfec_phy_monitor(unsigned long data)
|
||||
{
|
||||
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 @@ static void qfec_rx_int(struct net_device *dev)
|
||||
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 @@ static void qfec_rx_int(struct net_device *dev)
|
||||
break;
|
||||
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 */
|
||||
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 @@ static int qfec_open(struct net_device *dev)
|
||||
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 @@ static int qfec_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
|
||||
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(clk_reg, 0444, qfec_clk_reg_show, NULL);
|
||||
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 @@ 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_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))
|
||||
|
||||
Reference in New Issue
Block a user