net: thunderx: Fix PHY autoneg for SGMII QLM mode

This patch fixes the case where there is no phydev attached
to a LMAC in DT due to non-existance of a PHY driver or due
to usage of non-stanadard PHY which doesn't support autoneg.
Changes dependeds on firmware to send correct info w.r.t
PHY and autoneg capability.

This patch also covers a case where a 10G/40G interface is used
as a 1G with convertors with Cortina PHY in between.

Signed-off-by: Thanneeru Srinivasulu <tsrinivasulu@cavium.com>
Signed-off-by: Sunil Goutham <sgoutham@cavium.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Thanneeru Srinivasulu 2017-02-08 18:09:00 +05:30 committed by David S. Miller
parent 382e1eea2d
commit 075ad765ef
2 changed files with 101 additions and 12 deletions

View File

@ -31,6 +31,7 @@ struct lmac {
u8 lmac_type;
u8 lane_to_sds;
bool use_training;
bool autoneg;
bool link_up;
int lmacid; /* ID within BGX */
int lmacid_bd; /* ID on board */
@ -461,7 +462,17 @@ static int bgx_lmac_sgmii_init(struct bgx *bgx, struct lmac *lmac)
/* power down, reset autoneg, autoneg enable */
cfg = bgx_reg_read(bgx, lmacid, BGX_GMP_PCS_MRX_CTL);
cfg &= ~PCS_MRX_CTL_PWR_DN;
cfg |= (PCS_MRX_CTL_RST_AN | PCS_MRX_CTL_AN_EN);
cfg |= PCS_MRX_CTL_RST_AN;
if (lmac->phydev) {
cfg |= PCS_MRX_CTL_AN_EN;
} else {
/* In scenarios where PHY driver is not present or it's a
* non-standard PHY, FW sets AN_EN to inform Linux driver
* to do auto-neg and link polling or not.
*/
if (cfg & PCS_MRX_CTL_AN_EN)
lmac->autoneg = true;
}
bgx_reg_write(bgx, lmacid, BGX_GMP_PCS_MRX_CTL, cfg);
if (lmac->lmac_type == BGX_MODE_QSGMII) {
@ -472,7 +483,7 @@ static int bgx_lmac_sgmii_init(struct bgx *bgx, struct lmac *lmac)
return 0;
}
if (lmac->lmac_type == BGX_MODE_SGMII) {
if ((lmac->lmac_type == BGX_MODE_SGMII) && lmac->phydev) {
if (bgx_poll_reg(bgx, lmacid, BGX_GMP_PCS_MRX_STATUS,
PCS_MRX_STATUS_AN_CPT, false)) {
dev_err(&bgx->pdev->dev, "BGX AN_CPT not completed\n");
@ -678,12 +689,71 @@ static int bgx_xaui_check_link(struct lmac *lmac)
return -1;
}
static void bgx_poll_for_sgmii_link(struct lmac *lmac)
{
u64 pcs_link, an_result;
u8 speed;
pcs_link = bgx_reg_read(lmac->bgx, lmac->lmacid,
BGX_GMP_PCS_MRX_STATUS);
/*Link state bit is sticky, read it again*/
if (!(pcs_link & PCS_MRX_STATUS_LINK))
pcs_link = bgx_reg_read(lmac->bgx, lmac->lmacid,
BGX_GMP_PCS_MRX_STATUS);
if (bgx_poll_reg(lmac->bgx, lmac->lmacid, BGX_GMP_PCS_MRX_STATUS,
PCS_MRX_STATUS_AN_CPT, false)) {
lmac->link_up = false;
lmac->last_speed = SPEED_UNKNOWN;
lmac->last_duplex = DUPLEX_UNKNOWN;
goto next_poll;
}
lmac->link_up = ((pcs_link & PCS_MRX_STATUS_LINK) != 0) ? true : false;
an_result = bgx_reg_read(lmac->bgx, lmac->lmacid,
BGX_GMP_PCS_ANX_AN_RESULTS);
speed = (an_result >> 3) & 0x3;
lmac->last_duplex = (an_result >> 1) & 0x1;
switch (speed) {
case 0:
lmac->last_speed = 10;
break;
case 1:
lmac->last_speed = 100;
break;
case 2:
lmac->last_speed = 1000;
break;
default:
lmac->link_up = false;
lmac->last_speed = SPEED_UNKNOWN;
lmac->last_duplex = DUPLEX_UNKNOWN;
break;
}
next_poll:
if (lmac->last_link != lmac->link_up) {
if (lmac->link_up)
bgx_sgmii_change_link_state(lmac);
lmac->last_link = lmac->link_up;
}
queue_delayed_work(lmac->check_link, &lmac->dwork, HZ * 3);
}
static void bgx_poll_for_link(struct work_struct *work)
{
struct lmac *lmac;
u64 spu_link, smu_link;
lmac = container_of(work, struct lmac, dwork.work);
if (lmac->is_sgmii) {
bgx_poll_for_sgmii_link(lmac);
return;
}
/* Receive link is latching low. Force it high and verify it */
bgx_reg_modify(lmac->bgx, lmac->lmacid,
@ -775,9 +845,21 @@ static int bgx_lmac_enable(struct bgx *bgx, u8 lmacid)
(lmac->lmac_type != BGX_MODE_XLAUI) &&
(lmac->lmac_type != BGX_MODE_40G_KR) &&
(lmac->lmac_type != BGX_MODE_10G_KR)) {
if (!lmac->phydev)
return -ENODEV;
if (!lmac->phydev) {
if (lmac->autoneg) {
bgx_reg_write(bgx, lmacid,
BGX_GMP_PCS_LINKX_TIMER,
PCS_LINKX_TIMER_COUNT);
goto poll;
} else {
/* Default to below link speed and duplex */
lmac->link_up = true;
lmac->last_speed = 1000;
lmac->last_duplex = 1;
bgx_sgmii_change_link_state(lmac);
return 0;
}
}
lmac->phydev->dev_flags = 0;
if (phy_connect_direct(&lmac->netdev, lmac->phydev,
@ -786,15 +868,17 @@ static int bgx_lmac_enable(struct bgx *bgx, u8 lmacid)
return -ENODEV;
phy_start_aneg(lmac->phydev);
} else {
lmac->check_link = alloc_workqueue("check_link", WQ_UNBOUND |
WQ_MEM_RECLAIM, 1);
if (!lmac->check_link)
return -ENOMEM;
INIT_DELAYED_WORK(&lmac->dwork, bgx_poll_for_link);
queue_delayed_work(lmac->check_link, &lmac->dwork, 0);
return 0;
}
poll:
lmac->check_link = alloc_workqueue("check_link", WQ_UNBOUND |
WQ_MEM_RECLAIM, 1);
if (!lmac->check_link)
return -ENOMEM;
INIT_DELAYED_WORK(&lmac->dwork, bgx_poll_for_link);
queue_delayed_work(lmac->check_link, &lmac->dwork, 0);
return 0;
}

View File

@ -153,10 +153,15 @@
#define PCS_MRX_CTL_LOOPBACK1 BIT_ULL(14)
#define PCS_MRX_CTL_RESET BIT_ULL(15)
#define BGX_GMP_PCS_MRX_STATUS 0x30008
#define PCS_MRX_STATUS_LINK BIT_ULL(2)
#define PCS_MRX_STATUS_AN_CPT BIT_ULL(5)
#define BGX_GMP_PCS_ANX_ADV 0x30010
#define BGX_GMP_PCS_ANX_AN_RESULTS 0x30020
#define BGX_GMP_PCS_LINKX_TIMER 0x30040
#define PCS_LINKX_TIMER_COUNT 0x1E84
#define BGX_GMP_PCS_SGM_AN_ADV 0x30068
#define BGX_GMP_PCS_MISCX_CTL 0x30078
#define PCS_MISC_CTL_MODE BIT_ULL(8)
#define PCS_MISC_CTL_DISP_EN BIT_ULL(13)
#define PCS_MISC_CTL_GMX_ENO BIT_ULL(11)
#define PCS_MISC_CTL_SAMP_PT_MASK 0x7Full