2020-08-26 12:37:33 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
/*
|
|
|
|
* Copyright (C) 2018 Marvell International Ltd.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
#include <dm.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <fdt_support.h>
|
|
|
|
#include <malloc.h>
|
|
|
|
#include <miiphy.h>
|
|
|
|
#include <misc.h>
|
|
|
|
#include <net.h>
|
|
|
|
#include <netdev.h>
|
|
|
|
#include <pci.h>
|
|
|
|
#include <pci_ids.h>
|
|
|
|
#include <asm/io.h>
|
|
|
|
#include <asm/arch/board.h>
|
|
|
|
#include <linux/delay.h>
|
|
|
|
#include <linux/libfdt.h>
|
|
|
|
|
|
|
|
#include "nic_reg.h"
|
|
|
|
#include "nic.h"
|
|
|
|
#include "bgx.h"
|
|
|
|
|
|
|
|
static const phy_interface_t if_mode[] = {
|
|
|
|
[QLM_MODE_SGMII] = PHY_INTERFACE_MODE_SGMII,
|
|
|
|
[QLM_MODE_RGMII] = PHY_INTERFACE_MODE_RGMII,
|
|
|
|
[QLM_MODE_QSGMII] = PHY_INTERFACE_MODE_QSGMII,
|
|
|
|
[QLM_MODE_XAUI] = PHY_INTERFACE_MODE_XAUI,
|
|
|
|
[QLM_MODE_RXAUI] = PHY_INTERFACE_MODE_RXAUI,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct lmac {
|
|
|
|
struct bgx *bgx;
|
|
|
|
int dmac;
|
|
|
|
u8 mac[6];
|
|
|
|
bool link_up;
|
|
|
|
bool init_pend;
|
|
|
|
int lmacid; /* ID within BGX */
|
|
|
|
int phy_addr; /* ID on board */
|
|
|
|
struct udevice *dev;
|
|
|
|
struct mii_dev *mii_bus;
|
|
|
|
struct phy_device *phydev;
|
|
|
|
unsigned int last_duplex;
|
|
|
|
unsigned int last_link;
|
|
|
|
unsigned int last_speed;
|
|
|
|
int lane_to_sds;
|
|
|
|
int use_training;
|
|
|
|
int lmac_type;
|
|
|
|
u8 qlm_mode;
|
|
|
|
int qlm;
|
|
|
|
bool is_1gx;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct bgx {
|
|
|
|
u8 bgx_id;
|
|
|
|
int node;
|
|
|
|
struct lmac lmac[MAX_LMAC_PER_BGX];
|
|
|
|
int lmac_count;
|
|
|
|
u8 max_lmac;
|
|
|
|
void __iomem *reg_base;
|
|
|
|
struct pci_dev *pdev;
|
|
|
|
bool is_rgx;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct bgx_board_info bgx_board_info[MAX_BGX_PER_NODE];
|
|
|
|
|
|
|
|
struct bgx *bgx_vnic[MAX_BGX_PER_NODE];
|
|
|
|
|
|
|
|
/* APIs to read/write BGXX CSRs */
|
|
|
|
static u64 bgx_reg_read(struct bgx *bgx, uint8_t lmac, u64 offset)
|
|
|
|
{
|
|
|
|
u64 addr = (uintptr_t)bgx->reg_base +
|
|
|
|
((uint32_t)lmac << 20) + offset;
|
|
|
|
|
|
|
|
return readq((void *)addr);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bgx_reg_write(struct bgx *bgx, uint8_t lmac,
|
|
|
|
u64 offset, u64 val)
|
|
|
|
{
|
|
|
|
u64 addr = (uintptr_t)bgx->reg_base +
|
|
|
|
((uint32_t)lmac << 20) + offset;
|
|
|
|
|
|
|
|
writeq(val, (void *)addr);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bgx_reg_modify(struct bgx *bgx, uint8_t lmac,
|
|
|
|
u64 offset, u64 val)
|
|
|
|
{
|
|
|
|
u64 addr = (uintptr_t)bgx->reg_base +
|
|
|
|
((uint32_t)lmac << 20) + offset;
|
|
|
|
|
|
|
|
writeq(val | bgx_reg_read(bgx, lmac, offset), (void *)addr);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int bgx_poll_reg(struct bgx *bgx, uint8_t lmac,
|
|
|
|
u64 reg, u64 mask, bool zero)
|
|
|
|
{
|
|
|
|
int timeout = 200;
|
|
|
|
u64 reg_val;
|
|
|
|
|
|
|
|
while (timeout) {
|
|
|
|
reg_val = bgx_reg_read(bgx, lmac, reg);
|
|
|
|
if (zero && !(reg_val & mask))
|
|
|
|
return 0;
|
|
|
|
if (!zero && (reg_val & mask))
|
|
|
|
return 0;
|
|
|
|
mdelay(1);
|
|
|
|
timeout--;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int gser_poll_reg(u64 reg, int bit, u64 mask, u64 expected_val,
|
|
|
|
int timeout)
|
|
|
|
{
|
|
|
|
u64 reg_val;
|
|
|
|
|
|
|
|
debug("%s reg = %#llx, mask = %#llx,", __func__, reg, mask);
|
|
|
|
debug(" expected_val = %#llx, bit = %d\n", expected_val, bit);
|
|
|
|
while (timeout) {
|
|
|
|
reg_val = readq(reg) >> bit;
|
|
|
|
if ((reg_val & mask) == (expected_val))
|
|
|
|
return 0;
|
|
|
|
mdelay(1);
|
|
|
|
timeout--;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool is_bgx_port_valid(int bgx, int lmac)
|
|
|
|
{
|
|
|
|
debug("%s bgx %d lmac %d valid %d\n", __func__, bgx, lmac,
|
|
|
|
bgx_board_info[bgx].lmac_reg[lmac]);
|
|
|
|
|
|
|
|
if (bgx_board_info[bgx].lmac_reg[lmac])
|
|
|
|
return 1;
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct lmac *bgx_get_lmac(int node, int bgx_idx, int lmacid)
|
|
|
|
{
|
|
|
|
struct bgx *bgx = bgx_vnic[(node * MAX_BGX_PER_NODE) + bgx_idx];
|
|
|
|
|
|
|
|
if (bgx)
|
|
|
|
return &bgx->lmac[lmacid];
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
const u8 *bgx_get_lmac_mac(int node, int bgx_idx, int lmacid)
|
|
|
|
{
|
|
|
|
struct bgx *bgx = bgx_vnic[(node * MAX_BGX_PER_NODE) + bgx_idx];
|
|
|
|
|
|
|
|
if (bgx)
|
|
|
|
return bgx->lmac[lmacid].mac;
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void bgx_set_lmac_mac(int node, int bgx_idx, int lmacid, const u8 *mac)
|
|
|
|
{
|
|
|
|
struct bgx *bgx = bgx_vnic[(node * MAX_BGX_PER_NODE) + bgx_idx];
|
|
|
|
|
|
|
|
if (!bgx)
|
|
|
|
return;
|
|
|
|
|
|
|
|
memcpy(bgx->lmac[lmacid].mac, mac, 6);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Return number of BGX present in HW */
|
|
|
|
void bgx_get_count(int node, int *bgx_count)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
struct bgx *bgx;
|
|
|
|
|
|
|
|
*bgx_count = 0;
|
|
|
|
for (i = 0; i < MAX_BGX_PER_NODE; i++) {
|
|
|
|
bgx = bgx_vnic[node * MAX_BGX_PER_NODE + i];
|
|
|
|
debug("bgx_vnic[%u]: %p\n", node * MAX_BGX_PER_NODE + i,
|
|
|
|
bgx);
|
|
|
|
if (bgx)
|
|
|
|
*bgx_count |= (1 << i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Return number of LMAC configured for this BGX */
|
|
|
|
int bgx_get_lmac_count(int node, int bgx_idx)
|
|
|
|
{
|
|
|
|
struct bgx *bgx;
|
|
|
|
|
|
|
|
bgx = bgx_vnic[(node * MAX_BGX_PER_NODE) + bgx_idx];
|
|
|
|
if (bgx)
|
|
|
|
return bgx->lmac_count;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void bgx_lmac_rx_tx_enable(int node, int bgx_idx, int lmacid, bool enable)
|
|
|
|
{
|
|
|
|
struct bgx *bgx = bgx_vnic[(node * MAX_BGX_PER_NODE) + bgx_idx];
|
|
|
|
u64 cfg;
|
|
|
|
|
|
|
|
if (!bgx)
|
|
|
|
return;
|
|
|
|
|
|
|
|
cfg = bgx_reg_read(bgx, lmacid, BGX_CMRX_CFG);
|
|
|
|
if (enable)
|
|
|
|
cfg |= CMR_PKT_RX_EN | CMR_PKT_TX_EN;
|
|
|
|
else
|
|
|
|
cfg &= ~(CMR_PKT_RX_EN | CMR_PKT_TX_EN);
|
|
|
|
bgx_reg_write(bgx, lmacid, BGX_CMRX_CFG, cfg);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bgx_flush_dmac_addrs(struct bgx *bgx, u64 lmac)
|
|
|
|
{
|
|
|
|
u64 dmac = 0x00;
|
|
|
|
u64 offset, addr;
|
|
|
|
|
|
|
|
while (bgx->lmac[lmac].dmac > 0) {
|
|
|
|
offset = ((bgx->lmac[lmac].dmac - 1) * sizeof(dmac)) +
|
|
|
|
(lmac * MAX_DMAC_PER_LMAC * sizeof(dmac));
|
|
|
|
addr = (uintptr_t)bgx->reg_base +
|
|
|
|
BGX_CMR_RX_DMACX_CAM + offset;
|
|
|
|
writeq(dmac, (void *)addr);
|
|
|
|
bgx->lmac[lmac].dmac--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Configure BGX LMAC in internal loopback mode */
|
|
|
|
void bgx_lmac_internal_loopback(int node, int bgx_idx,
|
|
|
|
int lmac_idx, bool enable)
|
|
|
|
{
|
|
|
|
struct bgx *bgx;
|
|
|
|
struct lmac *lmac;
|
|
|
|
u64 cfg;
|
|
|
|
|
|
|
|
bgx = bgx_vnic[(node * MAX_BGX_PER_NODE) + bgx_idx];
|
|
|
|
if (!bgx)
|
|
|
|
return;
|
|
|
|
|
|
|
|
lmac = &bgx->lmac[lmac_idx];
|
|
|
|
if (lmac->qlm_mode == QLM_MODE_SGMII) {
|
|
|
|
cfg = bgx_reg_read(bgx, lmac_idx, BGX_GMP_PCS_MRX_CTL);
|
|
|
|
if (enable)
|
|
|
|
cfg |= PCS_MRX_CTL_LOOPBACK1;
|
|
|
|
else
|
|
|
|
cfg &= ~PCS_MRX_CTL_LOOPBACK1;
|
|
|
|
bgx_reg_write(bgx, lmac_idx, BGX_GMP_PCS_MRX_CTL, cfg);
|
|
|
|
} else {
|
|
|
|
cfg = bgx_reg_read(bgx, lmac_idx, BGX_SPUX_CONTROL1);
|
|
|
|
if (enable)
|
|
|
|
cfg |= SPU_CTL_LOOPBACK;
|
|
|
|
else
|
|
|
|
cfg &= ~SPU_CTL_LOOPBACK;
|
|
|
|
bgx_reg_write(bgx, lmac_idx, BGX_SPUX_CONTROL1, cfg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Return the DLM used for the BGX */
|
|
|
|
static int get_qlm_for_bgx(int node, int bgx_id, int index)
|
|
|
|
{
|
|
|
|
int qlm = 0;
|
|
|
|
u64 cfg;
|
|
|
|
|
|
|
|
if (otx_is_soc(CN81XX)) {
|
|
|
|
qlm = (bgx_id) ? 2 : 0;
|
|
|
|
qlm += (index >= 2) ? 1 : 0;
|
|
|
|
} else if (otx_is_soc(CN83XX)) {
|
|
|
|
switch (bgx_id) {
|
|
|
|
case 0:
|
|
|
|
qlm = 2;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
qlm = 3;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
if (index >= 2)
|
|
|
|
qlm = 6;
|
|
|
|
else
|
|
|
|
qlm = 5;
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
qlm = 4;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
cfg = readq(GSERX_CFG(qlm)) & GSERX_CFG_BGX;
|
|
|
|
debug("%s:qlm%d: cfg = %lld\n", __func__, qlm, cfg);
|
|
|
|
|
|
|
|
/* Check if DLM is configured as BGX# */
|
|
|
|
if (cfg) {
|
|
|
|
if (readq(GSERX_PHY_CTL(qlm)))
|
|
|
|
return -1;
|
|
|
|
return qlm;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int bgx_lmac_sgmii_init(struct bgx *bgx, int lmacid)
|
|
|
|
{
|
|
|
|
u64 cfg;
|
|
|
|
struct lmac *lmac;
|
|
|
|
|
|
|
|
lmac = &bgx->lmac[lmacid];
|
|
|
|
|
|
|
|
debug("%s:bgx_id = %d, lmacid = %d\n", __func__, bgx->bgx_id, lmacid);
|
|
|
|
|
|
|
|
bgx_reg_modify(bgx, lmacid, BGX_GMP_GMI_TXX_THRESH, 0x30);
|
|
|
|
/* max packet size */
|
|
|
|
bgx_reg_modify(bgx, lmacid, BGX_GMP_GMI_RXX_JABBER, MAX_FRAME_SIZE);
|
|
|
|
|
|
|
|
/* Disable frame alignment if using preamble */
|
|
|
|
cfg = bgx_reg_read(bgx, lmacid, BGX_GMP_GMI_TXX_APPEND);
|
|
|
|
if (cfg & 1)
|
|
|
|
bgx_reg_write(bgx, lmacid, BGX_GMP_GMI_TXX_SGMII_CTL, 0);
|
|
|
|
|
|
|
|
/* Enable lmac */
|
|
|
|
bgx_reg_modify(bgx, lmacid, BGX_CMRX_CFG, CMR_EN);
|
|
|
|
|
|
|
|
/* PCS reset */
|
|
|
|
bgx_reg_modify(bgx, lmacid, BGX_GMP_PCS_MRX_CTL, PCS_MRX_CTL_RESET);
|
|
|
|
if (bgx_poll_reg(bgx, lmacid, BGX_GMP_PCS_MRX_CTL,
|
|
|
|
PCS_MRX_CTL_RESET, true)) {
|
|
|
|
printf("BGX PCS reset not completed\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* power down, reset autoneg, autoneg enable */
|
|
|
|
cfg = bgx_reg_read(bgx, lmacid, BGX_GMP_PCS_MRX_CTL);
|
|
|
|
cfg &= ~PCS_MRX_CTL_PWR_DN;
|
|
|
|
|
|
|
|
if (bgx_board_info[bgx->bgx_id].phy_info[lmacid].autoneg_dis)
|
|
|
|
cfg |= (PCS_MRX_CTL_RST_AN);
|
|
|
|
else
|
|
|
|
cfg |= (PCS_MRX_CTL_RST_AN | PCS_MRX_CTL_AN_EN);
|
|
|
|
bgx_reg_write(bgx, lmacid, BGX_GMP_PCS_MRX_CTL, cfg);
|
|
|
|
|
|
|
|
/* Disable disparity for QSGMII mode, to prevent propogation across
|
|
|
|
* ports.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (lmac->qlm_mode == QLM_MODE_QSGMII) {
|
|
|
|
cfg = bgx_reg_read(bgx, lmacid, BGX_GMP_PCS_MISCX_CTL);
|
|
|
|
cfg &= ~PCS_MISCX_CTL_DISP_EN;
|
|
|
|
bgx_reg_write(bgx, lmacid, BGX_GMP_PCS_MISCX_CTL, cfg);
|
|
|
|
return 0; /* Skip checking AN_CPT */
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lmac->is_1gx) {
|
|
|
|
cfg = bgx_reg_read(bgx, lmacid, BGX_GMP_PCS_MISCX_CTL);
|
|
|
|
cfg |= PCS_MISC_CTL_MODE;
|
|
|
|
bgx_reg_write(bgx, lmacid, BGX_GMP_PCS_MISCX_CTL, cfg);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lmac->qlm_mode == QLM_MODE_SGMII) {
|
|
|
|
if (bgx_poll_reg(bgx, lmacid, BGX_GMP_PCS_MRX_STATUS,
|
|
|
|
PCS_MRX_STATUS_AN_CPT, false)) {
|
|
|
|
printf("BGX AN_CPT not completed\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int bgx_lmac_sgmii_set_link_speed(struct lmac *lmac)
|
|
|
|
{
|
|
|
|
u64 prtx_cfg;
|
|
|
|
u64 pcs_miscx_ctl;
|
|
|
|
u64 cfg;
|
|
|
|
struct bgx *bgx = lmac->bgx;
|
|
|
|
unsigned int lmacid = lmac->lmacid;
|
|
|
|
|
|
|
|
debug("%s: lmacid %d\n", __func__, lmac->lmacid);
|
|
|
|
|
|
|
|
/* Disable LMAC before setting up speed */
|
|
|
|
cfg = bgx_reg_read(bgx, lmacid, BGX_CMRX_CFG);
|
|
|
|
cfg &= ~CMR_EN;
|
|
|
|
bgx_reg_write(bgx, lmacid, BGX_CMRX_CFG, cfg);
|
|
|
|
|
|
|
|
/* Read GMX CFG */
|
|
|
|
prtx_cfg = bgx_reg_read(bgx, lmacid,
|
|
|
|
BGX_GMP_GMI_PRTX_CFG);
|
|
|
|
/* Read PCS MISCS CTL */
|
|
|
|
pcs_miscx_ctl = bgx_reg_read(bgx, lmacid,
|
|
|
|
BGX_GMP_PCS_MISCX_CTL);
|
|
|
|
|
|
|
|
/* Use GMXENO to force the link down*/
|
|
|
|
if (lmac->link_up) {
|
|
|
|
pcs_miscx_ctl &= ~PCS_MISC_CTL_GMX_ENO;
|
|
|
|
/* change the duplex setting if the link is up */
|
|
|
|
prtx_cfg |= GMI_PORT_CFG_DUPLEX;
|
|
|
|
} else {
|
|
|
|
pcs_miscx_ctl |= PCS_MISC_CTL_GMX_ENO;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* speed based setting for GMX */
|
|
|
|
switch (lmac->last_speed) {
|
|
|
|
case 10:
|
|
|
|
prtx_cfg &= ~GMI_PORT_CFG_SPEED;
|
|
|
|
prtx_cfg |= GMI_PORT_CFG_SPEED_MSB;
|
|
|
|
prtx_cfg &= ~GMI_PORT_CFG_SLOT_TIME;
|
|
|
|
pcs_miscx_ctl |= 50; /* sampling point */
|
|
|
|
bgx_reg_write(bgx, lmacid, BGX_GMP_GMI_TXX_SLOT, 0x40);
|
|
|
|
bgx_reg_write(bgx, lmacid, BGX_GMP_GMI_TXX_BURST, 0);
|
|
|
|
break;
|
|
|
|
case 100:
|
|
|
|
prtx_cfg &= ~GMI_PORT_CFG_SPEED;
|
|
|
|
prtx_cfg &= ~GMI_PORT_CFG_SPEED_MSB;
|
|
|
|
prtx_cfg &= ~GMI_PORT_CFG_SLOT_TIME;
|
|
|
|
pcs_miscx_ctl |= 0x5; /* sampling point */
|
|
|
|
bgx_reg_write(bgx, lmacid, BGX_GMP_GMI_TXX_SLOT, 0x40);
|
|
|
|
bgx_reg_write(bgx, lmacid, BGX_GMP_GMI_TXX_BURST, 0);
|
|
|
|
break;
|
|
|
|
case 1000:
|
|
|
|
prtx_cfg |= GMI_PORT_CFG_SPEED;
|
|
|
|
prtx_cfg &= ~GMI_PORT_CFG_SPEED_MSB;
|
|
|
|
prtx_cfg |= GMI_PORT_CFG_SLOT_TIME;
|
|
|
|
pcs_miscx_ctl |= 0x1; /* sampling point */
|
|
|
|
bgx_reg_write(bgx, lmacid, BGX_GMP_GMI_TXX_SLOT, 0x200);
|
|
|
|
if (lmac->last_duplex)
|
|
|
|
bgx_reg_write(bgx, lmacid, BGX_GMP_GMI_TXX_BURST, 0);
|
|
|
|
else /* half duplex */
|
|
|
|
bgx_reg_write(bgx, lmacid, BGX_GMP_GMI_TXX_BURST,
|
|
|
|
0x2000);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* write back the new PCS misc and GMX settings */
|
|
|
|
bgx_reg_write(bgx, lmacid, BGX_GMP_PCS_MISCX_CTL, pcs_miscx_ctl);
|
|
|
|
bgx_reg_write(bgx, lmacid, BGX_GMP_GMI_PRTX_CFG, prtx_cfg);
|
|
|
|
|
|
|
|
/* read back GMX CFG again to check config completion */
|
|
|
|
bgx_reg_read(bgx, lmacid, BGX_GMP_GMI_PRTX_CFG);
|
|
|
|
|
|
|
|
/* enable BGX back */
|
|
|
|
cfg = bgx_reg_read(bgx, lmacid, BGX_CMRX_CFG);
|
|
|
|
cfg |= CMR_EN;
|
|
|
|
bgx_reg_write(bgx, lmacid, BGX_CMRX_CFG, cfg);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int bgx_lmac_xaui_init(struct bgx *bgx, int lmacid, int lmac_type)
|
|
|
|
{
|
|
|
|
u64 cfg;
|
|
|
|
struct lmac *lmac;
|
|
|
|
|
|
|
|
lmac = &bgx->lmac[lmacid];
|
|
|
|
|
|
|
|
/* Reset SPU */
|
|
|
|
bgx_reg_modify(bgx, lmacid, BGX_SPUX_CONTROL1, SPU_CTL_RESET);
|
|
|
|
if (bgx_poll_reg(bgx, lmacid, BGX_SPUX_CONTROL1, SPU_CTL_RESET, true)) {
|
|
|
|
printf("BGX SPU reset not completed\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Disable LMAC */
|
|
|
|
cfg = bgx_reg_read(bgx, lmacid, BGX_CMRX_CFG);
|
|
|
|
cfg &= ~CMR_EN;
|
|
|
|
bgx_reg_write(bgx, lmacid, BGX_CMRX_CFG, cfg);
|
|
|
|
|
|
|
|
bgx_reg_modify(bgx, lmacid, BGX_SPUX_CONTROL1, SPU_CTL_LOW_POWER);
|
|
|
|
/* Set interleaved running disparity for RXAUI */
|
|
|
|
if (lmac->qlm_mode != QLM_MODE_RXAUI)
|
|
|
|
bgx_reg_modify(bgx, lmacid,
|
|
|
|
BGX_SPUX_MISC_CONTROL, SPU_MISC_CTL_RX_DIS);
|
|
|
|
else
|
|
|
|
bgx_reg_modify(bgx, lmacid, BGX_SPUX_MISC_CONTROL,
|
|
|
|
SPU_MISC_CTL_RX_DIS | SPU_MISC_CTL_INTLV_RDISP);
|
|
|
|
|
|
|
|
/* clear all interrupts */
|
|
|
|
cfg = bgx_reg_read(bgx, lmacid, BGX_SMUX_RX_INT);
|
|
|
|
bgx_reg_write(bgx, lmacid, BGX_SMUX_RX_INT, cfg);
|
|
|
|
cfg = bgx_reg_read(bgx, lmacid, BGX_SMUX_TX_INT);
|
|
|
|
bgx_reg_write(bgx, lmacid, BGX_SMUX_TX_INT, cfg);
|
|
|
|
cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_INT);
|
|
|
|
bgx_reg_write(bgx, lmacid, BGX_SPUX_INT, cfg);
|
|
|
|
|
|
|
|
if (lmac->use_training) {
|
|
|
|
bgx_reg_write(bgx, lmacid, BGX_SPUX_BR_PMD_LP_CUP, 0x00);
|
|
|
|
bgx_reg_write(bgx, lmacid, BGX_SPUX_BR_PMD_LD_CUP, 0x00);
|
|
|
|
bgx_reg_write(bgx, lmacid, BGX_SPUX_BR_PMD_LD_REP, 0x00);
|
|
|
|
/* training enable */
|
|
|
|
bgx_reg_modify(bgx, lmacid,
|
|
|
|
BGX_SPUX_BR_PMD_CRTL, SPU_PMD_CRTL_TRAIN_EN);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Append FCS to each packet */
|
|
|
|
bgx_reg_modify(bgx, lmacid, BGX_SMUX_TX_APPEND, SMU_TX_APPEND_FCS_D);
|
|
|
|
|
|
|
|
/* Disable forward error correction */
|
|
|
|
cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_FEC_CONTROL);
|
|
|
|
cfg &= ~SPU_FEC_CTL_FEC_EN;
|
|
|
|
bgx_reg_write(bgx, lmacid, BGX_SPUX_FEC_CONTROL, cfg);
|
|
|
|
|
|
|
|
/* Disable autoneg */
|
|
|
|
cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_AN_CONTROL);
|
|
|
|
cfg = cfg & ~(SPU_AN_CTL_XNP_EN);
|
|
|
|
if (lmac->use_training)
|
|
|
|
cfg = cfg | (SPU_AN_CTL_AN_EN);
|
|
|
|
else
|
|
|
|
cfg = cfg & ~(SPU_AN_CTL_AN_EN);
|
|
|
|
bgx_reg_write(bgx, lmacid, BGX_SPUX_AN_CONTROL, cfg);
|
|
|
|
|
|
|
|
cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_AN_ADV);
|
|
|
|
/* Clear all KR bits, configure according to the mode */
|
|
|
|
cfg &= ~((0xfULL << 22) | (1ULL << 12));
|
|
|
|
if (lmac->qlm_mode == QLM_MODE_10G_KR)
|
|
|
|
cfg |= (1 << 23);
|
|
|
|
else if (lmac->qlm_mode == QLM_MODE_40G_KR4)
|
|
|
|
cfg |= (1 << 24);
|
|
|
|
bgx_reg_write(bgx, lmacid, BGX_SPUX_AN_ADV, cfg);
|
|
|
|
|
|
|
|
cfg = bgx_reg_read(bgx, 0, BGX_SPU_DBG_CONTROL);
|
|
|
|
if (lmac->use_training)
|
|
|
|
cfg |= SPU_DBG_CTL_AN_ARB_LINK_CHK_EN;
|
|
|
|
else
|
|
|
|
cfg &= ~SPU_DBG_CTL_AN_ARB_LINK_CHK_EN;
|
|
|
|
bgx_reg_write(bgx, 0, BGX_SPU_DBG_CONTROL, cfg);
|
|
|
|
|
|
|
|
/* Enable lmac */
|
|
|
|
bgx_reg_modify(bgx, lmacid, BGX_CMRX_CFG, CMR_EN);
|
|
|
|
|
|
|
|
cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_CONTROL1);
|
|
|
|
cfg &= ~SPU_CTL_LOW_POWER;
|
|
|
|
bgx_reg_write(bgx, lmacid, BGX_SPUX_CONTROL1, cfg);
|
|
|
|
|
|
|
|
cfg = bgx_reg_read(bgx, lmacid, BGX_SMUX_TX_CTL);
|
|
|
|
cfg &= ~SMU_TX_CTL_UNI_EN;
|
|
|
|
cfg |= SMU_TX_CTL_DIC_EN;
|
|
|
|
bgx_reg_write(bgx, lmacid, BGX_SMUX_TX_CTL, cfg);
|
|
|
|
|
|
|
|
/* take lmac_count into account */
|
|
|
|
bgx_reg_modify(bgx, lmacid, BGX_SMUX_TX_THRESH, (0x100 - 1));
|
|
|
|
/* max packet size */
|
|
|
|
bgx_reg_modify(bgx, lmacid, BGX_SMUX_RX_JABBER, MAX_FRAME_SIZE);
|
|
|
|
|
|
|
|
debug("xaui_init: lmacid = %d, qlm = %d, qlm_mode = %d\n",
|
|
|
|
lmacid, lmac->qlm, lmac->qlm_mode);
|
|
|
|
/* RXAUI with Marvell PHY requires some tweaking */
|
|
|
|
if (lmac->qlm_mode == QLM_MODE_RXAUI) {
|
|
|
|
char mii_name[20];
|
|
|
|
struct phy_info *phy;
|
|
|
|
|
|
|
|
phy = &bgx_board_info[bgx->bgx_id].phy_info[lmacid];
|
|
|
|
snprintf(mii_name, sizeof(mii_name), "smi%d", phy->mdio_bus);
|
|
|
|
|
|
|
|
debug("mii_name: %s\n", mii_name);
|
|
|
|
lmac->mii_bus = miiphy_get_dev_by_name(mii_name);
|
|
|
|
lmac->phy_addr = phy->phy_addr;
|
|
|
|
rxaui_phy_xs_init(lmac->mii_bus, lmac->phy_addr);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get max number of lanes present in a given QLM/DLM */
|
|
|
|
static int get_qlm_lanes(int qlm)
|
|
|
|
{
|
|
|
|
if (otx_is_soc(CN81XX))
|
|
|
|
return 2;
|
|
|
|
else if (otx_is_soc(CN83XX))
|
|
|
|
return (qlm >= 5) ? 2 : 4;
|
|
|
|
else
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int __rx_equalization(int qlm, int lane)
|
|
|
|
{
|
|
|
|
int max_lanes = get_qlm_lanes(qlm);
|
|
|
|
int l;
|
|
|
|
int fail = 0;
|
|
|
|
|
|
|
|
/* Before completing Rx equalization wait for
|
|
|
|
* GSERx_RX_EIE_DETSTS[CDRLOCK] to be set
|
|
|
|
* This ensures the rx data is valid
|
|
|
|
*/
|
|
|
|
if (lane == -1) {
|
|
|
|
if (gser_poll_reg(GSER_RX_EIE_DETSTS(qlm), GSER_CDRLOCK, 0xf,
|
|
|
|
(1 << max_lanes) - 1, 100)) {
|
|
|
|
debug("ERROR: CDR Lock not detected");
|
|
|
|
debug(" on DLM%d for 2 lanes\n", qlm);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (gser_poll_reg(GSER_RX_EIE_DETSTS(qlm), GSER_CDRLOCK,
|
|
|
|
(0xf & (1 << lane)), (1 << lane), 100)) {
|
|
|
|
debug("ERROR: DLM%d: CDR Lock not detected", qlm);
|
|
|
|
debug(" on %d lane\n", lane);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (l = 0; l < max_lanes; l++) {
|
|
|
|
u64 rctl, reer;
|
|
|
|
|
|
|
|
if (lane != -1 && lane != l)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Enable software control */
|
|
|
|
rctl = readq(GSER_BR_RXX_CTL(qlm, l));
|
|
|
|
rctl |= GSER_BR_RXX_CTL_RXT_SWM;
|
|
|
|
writeq(rctl, GSER_BR_RXX_CTL(qlm, l));
|
|
|
|
|
|
|
|
/* Clear the completion flag and initiate a new request */
|
|
|
|
reer = readq(GSER_BR_RXX_EER(qlm, l));
|
|
|
|
reer &= ~GSER_BR_RXX_EER_RXT_ESV;
|
|
|
|
reer |= GSER_BR_RXX_EER_RXT_EER;
|
|
|
|
writeq(reer, GSER_BR_RXX_EER(qlm, l));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Wait for RX equalization to complete */
|
|
|
|
for (l = 0; l < max_lanes; l++) {
|
|
|
|
u64 rctl, reer;
|
|
|
|
|
|
|
|
if (lane != -1 && lane != l)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
gser_poll_reg(GSER_BR_RXX_EER(qlm, l), EER_RXT_ESV, 1, 1, 200);
|
|
|
|
reer = readq(GSER_BR_RXX_EER(qlm, l));
|
|
|
|
|
|
|
|
/* Switch back to hardware control */
|
|
|
|
rctl = readq(GSER_BR_RXX_CTL(qlm, l));
|
|
|
|
rctl &= ~GSER_BR_RXX_CTL_RXT_SWM;
|
|
|
|
writeq(rctl, GSER_BR_RXX_CTL(qlm, l));
|
|
|
|
|
|
|
|
if (reer & GSER_BR_RXX_EER_RXT_ESV) {
|
|
|
|
debug("Rx equalization completed on DLM%d", qlm);
|
|
|
|
debug(" QLM%d rxt_esm = 0x%llx\n", l, (reer & 0x3fff));
|
|
|
|
} else {
|
|
|
|
debug("Rx equalization timedout on DLM%d", qlm);
|
|
|
|
debug(" lane %d\n", l);
|
|
|
|
fail = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return (fail) ? -1 : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int bgx_xaui_check_link(struct lmac *lmac)
|
|
|
|
{
|
|
|
|
struct bgx *bgx = lmac->bgx;
|
|
|
|
int lmacid = lmac->lmacid;
|
|
|
|
int lmac_type = lmac->lmac_type;
|
|
|
|
u64 cfg;
|
|
|
|
|
|
|
|
bgx_reg_modify(bgx, lmacid, BGX_SPUX_MISC_CONTROL, SPU_MISC_CTL_RX_DIS);
|
|
|
|
|
|
|
|
/* check if auto negotiation is complete */
|
|
|
|
cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_AN_CONTROL);
|
|
|
|
if (cfg & SPU_AN_CTL_AN_EN) {
|
|
|
|
cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_AN_STATUS);
|
|
|
|
if (!(cfg & SPU_AN_STS_AN_COMPLETE)) {
|
|
|
|
/* Restart autonegotiation */
|
|
|
|
debug("restarting auto-neg\n");
|
|
|
|
bgx_reg_modify(bgx, lmacid, BGX_SPUX_AN_CONTROL,
|
|
|
|
SPU_AN_CTL_AN_RESTART);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
debug("%s link use_training %d\n", __func__, lmac->use_training);
|
|
|
|
if (lmac->use_training) {
|
|
|
|
cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_INT);
|
|
|
|
if (!(cfg & (1ull << 13))) {
|
|
|
|
debug("waiting for link training\n");
|
|
|
|
/* Clear the training interrupts (W1C) */
|
|
|
|
cfg = (1ull << 13) | (1ull << 14);
|
|
|
|
bgx_reg_write(bgx, lmacid, BGX_SPUX_INT, cfg);
|
|
|
|
|
|
|
|
udelay(2000);
|
|
|
|
/* Restart training */
|
|
|
|
cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_BR_PMD_CRTL);
|
|
|
|
cfg |= (1ull << 0);
|
|
|
|
bgx_reg_write(bgx, lmacid, BGX_SPUX_BR_PMD_CRTL, cfg);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Perform RX Equalization. Applies to non-KR interfaces for speeds
|
|
|
|
* >= 6.25Gbps.
|
|
|
|
*/
|
|
|
|
if (!lmac->use_training) {
|
|
|
|
int qlm;
|
|
|
|
bool use_dlm = 0;
|
|
|
|
|
|
|
|
if (otx_is_soc(CN81XX) || (otx_is_soc(CN83XX) &&
|
|
|
|
bgx->bgx_id == 2))
|
|
|
|
use_dlm = 1;
|
|
|
|
switch (lmac->lmac_type) {
|
|
|
|
default:
|
|
|
|
case BGX_MODE_SGMII:
|
|
|
|
case BGX_MODE_RGMII:
|
|
|
|
case BGX_MODE_XAUI:
|
|
|
|
/* Nothing to do */
|
|
|
|
break;
|
|
|
|
case BGX_MODE_XLAUI:
|
|
|
|
if (use_dlm) {
|
|
|
|
if (__rx_equalization(lmac->qlm, -1) ||
|
|
|
|
__rx_equalization(lmac->qlm + 1, -1)) {
|
|
|
|
printf("BGX%d:%d", bgx->bgx_id, lmacid);
|
|
|
|
printf(" Waiting for RX Equalization");
|
|
|
|
printf(" on DLM%d/DLM%d\n",
|
|
|
|
lmac->qlm, lmac->qlm + 1);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (__rx_equalization(lmac->qlm, -1)) {
|
|
|
|
printf("BGX%d:%d", bgx->bgx_id, lmacid);
|
|
|
|
printf(" Waiting for RX Equalization");
|
|
|
|
printf(" on QLM%d\n", lmac->qlm);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case BGX_MODE_RXAUI:
|
|
|
|
/* RXAUI0 uses LMAC0:QLM0/QLM2 and RXAUI1 uses
|
|
|
|
* LMAC1:QLM1/QLM3 RXAUI requires 2 lanes
|
|
|
|
* for each interface
|
|
|
|
*/
|
|
|
|
qlm = lmac->qlm;
|
|
|
|
if (__rx_equalization(qlm, 0)) {
|
|
|
|
printf("BGX%d:%d", bgx->bgx_id, lmacid);
|
|
|
|
printf(" Waiting for RX Equalization");
|
|
|
|
printf(" on QLM%d, Lane0\n", qlm);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (__rx_equalization(qlm, 1)) {
|
|
|
|
printf("BGX%d:%d", bgx->bgx_id, lmacid);
|
|
|
|
printf(" Waiting for RX Equalization");
|
|
|
|
printf(" on QLM%d, Lane1\n", qlm);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case BGX_MODE_XFI:
|
|
|
|
{
|
|
|
|
int lid;
|
|
|
|
bool altpkg = otx_is_altpkg();
|
|
|
|
|
|
|
|
if (bgx->bgx_id == 0 && altpkg && lmacid)
|
|
|
|
lid = 0;
|
|
|
|
else if ((lmacid >= 2) && use_dlm)
|
|
|
|
lid = lmacid - 2;
|
|
|
|
else
|
|
|
|
lid = lmacid;
|
|
|
|
|
|
|
|
if (__rx_equalization(lmac->qlm, lid)) {
|
|
|
|
printf("BGX%d:%d", bgx->bgx_id, lid);
|
|
|
|
printf(" Waiting for RX Equalization");
|
|
|
|
printf(" on QLM%d\n", lmac->qlm);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* wait for PCS to come out of reset */
|
|
|
|
if (bgx_poll_reg(bgx, lmacid, BGX_SPUX_CONTROL1, SPU_CTL_RESET, true)) {
|
|
|
|
printf("BGX SPU reset not completed\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lmac_type == 3 || lmac_type == 4) {
|
|
|
|
if (bgx_poll_reg(bgx, lmacid, BGX_SPUX_BR_STATUS1,
|
|
|
|
SPU_BR_STATUS_BLK_LOCK, false)) {
|
|
|
|
printf("SPU_BR_STATUS_BLK_LOCK not completed\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (bgx_poll_reg(bgx, lmacid, BGX_SPUX_BX_STATUS,
|
|
|
|
SPU_BX_STATUS_RX_ALIGN, false)) {
|
|
|
|
printf("SPU_BX_STATUS_RX_ALIGN not completed\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Clear rcvflt bit (latching high) and read it back */
|
|
|
|
bgx_reg_modify(bgx, lmacid, BGX_SPUX_STATUS2, SPU_STATUS2_RCVFLT);
|
|
|
|
if (bgx_reg_read(bgx, lmacid, BGX_SPUX_STATUS2) & SPU_STATUS2_RCVFLT) {
|
|
|
|
printf("Receive fault, retry training\n");
|
|
|
|
if (lmac->use_training) {
|
|
|
|
cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_INT);
|
|
|
|
if (!(cfg & (1ull << 13))) {
|
|
|
|
cfg = (1ull << 13) | (1ull << 14);
|
|
|
|
bgx_reg_write(bgx, lmacid, BGX_SPUX_INT, cfg);
|
|
|
|
cfg = bgx_reg_read(bgx, lmacid,
|
|
|
|
BGX_SPUX_BR_PMD_CRTL);
|
|
|
|
cfg |= (1ull << 0);
|
|
|
|
bgx_reg_write(bgx, lmacid,
|
|
|
|
BGX_SPUX_BR_PMD_CRTL, cfg);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Wait for MAC RX to be ready */
|
|
|
|
if (bgx_poll_reg(bgx, lmacid, BGX_SMUX_RX_CTL,
|
|
|
|
SMU_RX_CTL_STATUS, true)) {
|
|
|
|
printf("SMU RX link not okay\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Wait for BGX RX to be idle */
|
|
|
|
if (bgx_poll_reg(bgx, lmacid, BGX_SMUX_CTL, SMU_CTL_RX_IDLE, false)) {
|
|
|
|
printf("SMU RX not idle\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Wait for BGX TX to be idle */
|
|
|
|
if (bgx_poll_reg(bgx, lmacid, BGX_SMUX_CTL, SMU_CTL_TX_IDLE, false)) {
|
|
|
|
printf("SMU TX not idle\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bgx_reg_read(bgx, lmacid, BGX_SPUX_STATUS2) & SPU_STATUS2_RCVFLT) {
|
|
|
|
printf("Receive fault\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Receive link is latching low. Force it high and verify it */
|
|
|
|
if (!(bgx_reg_read(bgx, lmacid, BGX_SPUX_STATUS1) &
|
|
|
|
SPU_STATUS1_RCV_LNK))
|
|
|
|
bgx_reg_modify(bgx, lmacid, BGX_SPUX_STATUS1,
|
|
|
|
SPU_STATUS1_RCV_LNK);
|
|
|
|
if (bgx_poll_reg(bgx, lmacid, BGX_SPUX_STATUS1,
|
|
|
|
SPU_STATUS1_RCV_LNK, false)) {
|
|
|
|
printf("SPU receive link down\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_MISC_CONTROL);
|
|
|
|
cfg &= ~SPU_MISC_CTL_RX_DIS;
|
|
|
|
bgx_reg_write(bgx, lmacid, BGX_SPUX_MISC_CONTROL, cfg);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int bgx_lmac_enable(struct bgx *bgx, int8_t lmacid)
|
|
|
|
{
|
|
|
|
struct lmac *lmac;
|
|
|
|
u64 cfg;
|
|
|
|
|
|
|
|
lmac = &bgx->lmac[lmacid];
|
|
|
|
|
|
|
|
debug("%s: lmac: %p, lmacid = %d\n", __func__, lmac, lmacid);
|
|
|
|
|
|
|
|
if (lmac->qlm_mode == QLM_MODE_SGMII ||
|
|
|
|
lmac->qlm_mode == QLM_MODE_RGMII ||
|
|
|
|
lmac->qlm_mode == QLM_MODE_QSGMII) {
|
|
|
|
if (bgx_lmac_sgmii_init(bgx, lmacid)) {
|
|
|
|
debug("bgx_lmac_sgmii_init failed\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
cfg = bgx_reg_read(bgx, lmacid, BGX_GMP_GMI_TXX_APPEND);
|
|
|
|
cfg |= ((1ull << 2) | (1ull << 1)); /* FCS and PAD */
|
|
|
|
bgx_reg_modify(bgx, lmacid, BGX_GMP_GMI_TXX_APPEND, cfg);
|
|
|
|
bgx_reg_write(bgx, lmacid, BGX_GMP_GMI_TXX_MIN_PKT, 60 - 1);
|
|
|
|
} else {
|
|
|
|
if (bgx_lmac_xaui_init(bgx, lmacid, lmac->lmac_type))
|
|
|
|
return -1;
|
|
|
|
cfg = bgx_reg_read(bgx, lmacid, BGX_SMUX_TX_APPEND);
|
|
|
|
cfg |= ((1ull << 2) | (1ull << 1)); /* FCS and PAD */
|
|
|
|
bgx_reg_modify(bgx, lmacid, BGX_SMUX_TX_APPEND, cfg);
|
|
|
|
bgx_reg_write(bgx, lmacid, BGX_SMUX_TX_MIN_PKT, 60 + 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Enable lmac */
|
|
|
|
bgx_reg_modify(bgx, lmacid, BGX_CMRX_CFG,
|
|
|
|
CMR_EN | CMR_PKT_RX_EN | CMR_PKT_TX_EN);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int bgx_poll_for_link(int node, int bgx_idx, int lmacid)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct lmac *lmac = bgx_get_lmac(node, bgx_idx, lmacid);
|
|
|
|
char mii_name[10];
|
|
|
|
struct phy_info *phy;
|
|
|
|
|
|
|
|
if (!lmac) {
|
|
|
|
printf("LMAC %d/%d/%d is disabled or doesn't exist\n",
|
|
|
|
node, bgx_idx, lmacid);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
debug("%s: %d, lmac: %d/%d/%d %p\n",
|
|
|
|
__FILE__, __LINE__,
|
|
|
|
node, bgx_idx, lmacid, lmac);
|
|
|
|
if (lmac->init_pend) {
|
|
|
|
ret = bgx_lmac_enable(lmac->bgx, lmacid);
|
|
|
|
if (ret < 0) {
|
|
|
|
printf("BGX%d LMAC%d lmac_enable failed\n", bgx_idx,
|
|
|
|
lmacid);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
lmac->init_pend = 0;
|
|
|
|
mdelay(100);
|
|
|
|
}
|
|
|
|
if (lmac->qlm_mode == QLM_MODE_SGMII ||
|
|
|
|
lmac->qlm_mode == QLM_MODE_RGMII ||
|
|
|
|
lmac->qlm_mode == QLM_MODE_QSGMII) {
|
|
|
|
if (bgx_board_info[bgx_idx].phy_info[lmacid].phy_addr == -1) {
|
|
|
|
lmac->link_up = 1;
|
|
|
|
lmac->last_speed = 1000;
|
|
|
|
lmac->last_duplex = 1;
|
|
|
|
printf("BGX%d:LMAC %u link up\n", bgx_idx, lmacid);
|
|
|
|
return lmac->link_up;
|
|
|
|
}
|
|
|
|
snprintf(mii_name, sizeof(mii_name), "smi%d",
|
|
|
|
bgx_board_info[bgx_idx].phy_info[lmacid].mdio_bus);
|
|
|
|
|
|
|
|
debug("mii_name: %s\n", mii_name);
|
|
|
|
|
|
|
|
lmac->mii_bus = miiphy_get_dev_by_name(mii_name);
|
|
|
|
phy = &bgx_board_info[bgx_idx].phy_info[lmacid];
|
|
|
|
lmac->phy_addr = phy->phy_addr;
|
|
|
|
|
|
|
|
debug("lmac->mii_bus: %p\n", lmac->mii_bus);
|
|
|
|
if (!lmac->mii_bus) {
|
|
|
|
printf("MDIO device %s not found\n", mii_name);
|
|
|
|
ret = -ENODEV;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
lmac->phydev = phy_connect(lmac->mii_bus, lmac->phy_addr,
|
|
|
|
lmac->dev,
|
|
|
|
if_mode[lmac->qlm_mode]);
|
|
|
|
|
|
|
|
if (!lmac->phydev) {
|
|
|
|
printf("%s: No PHY device\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = phy_config(lmac->phydev);
|
|
|
|
if (ret) {
|
|
|
|
printf("%s: Could not initialize PHY %s\n",
|
|
|
|
__func__, lmac->phydev->dev->name);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = phy_startup(lmac->phydev);
|
|
|
|
debug("%s: %d\n", __FILE__, __LINE__);
|
|
|
|
if (ret) {
|
|
|
|
printf("%s: Could not initialize PHY %s\n",
|
|
|
|
__func__, lmac->phydev->dev->name);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef OCTEONTX_XCV
|
|
|
|
if (lmac->qlm_mode == QLM_MODE_RGMII)
|
|
|
|
xcv_setup_link(lmac->phydev->link, lmac->phydev->speed);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
lmac->link_up = lmac->phydev->link;
|
|
|
|
lmac->last_speed = lmac->phydev->speed;
|
|
|
|
lmac->last_duplex = lmac->phydev->duplex;
|
|
|
|
|
|
|
|
debug("%s qlm_mode %d phy link status 0x%x,last speed 0x%x,",
|
|
|
|
__func__, lmac->qlm_mode, lmac->link_up,
|
|
|
|
lmac->last_speed);
|
|
|
|
debug(" duplex 0x%x\n", lmac->last_duplex);
|
|
|
|
|
|
|
|
if (lmac->qlm_mode != QLM_MODE_RGMII)
|
|
|
|
bgx_lmac_sgmii_set_link_speed(lmac);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
u64 status1;
|
|
|
|
u64 tx_ctl;
|
|
|
|
u64 rx_ctl;
|
|
|
|
|
|
|
|
status1 = bgx_reg_read(lmac->bgx, lmac->lmacid,
|
|
|
|
BGX_SPUX_STATUS1);
|
|
|
|
tx_ctl = bgx_reg_read(lmac->bgx, lmac->lmacid, BGX_SMUX_TX_CTL);
|
|
|
|
rx_ctl = bgx_reg_read(lmac->bgx, lmac->lmacid, BGX_SMUX_RX_CTL);
|
|
|
|
|
|
|
|
debug("BGX%d LMAC%d BGX_SPUX_STATUS2: %lx\n", bgx_idx, lmacid,
|
|
|
|
(unsigned long)bgx_reg_read(lmac->bgx, lmac->lmacid,
|
|
|
|
BGX_SPUX_STATUS2));
|
|
|
|
debug("BGX%d LMAC%d BGX_SPUX_STATUS1: %lx\n", bgx_idx, lmacid,
|
|
|
|
(unsigned long)bgx_reg_read(lmac->bgx, lmac->lmacid,
|
|
|
|
BGX_SPUX_STATUS1));
|
|
|
|
debug("BGX%d LMAC%d BGX_SMUX_RX_CTL: %lx\n", bgx_idx, lmacid,
|
|
|
|
(unsigned long)bgx_reg_read(lmac->bgx, lmac->lmacid,
|
|
|
|
BGX_SMUX_RX_CTL));
|
|
|
|
debug("BGX%d LMAC%d BGX_SMUX_TX_CTL: %lx\n", bgx_idx, lmacid,
|
|
|
|
(unsigned long)bgx_reg_read(lmac->bgx, lmac->lmacid,
|
|
|
|
BGX_SMUX_TX_CTL));
|
|
|
|
|
|
|
|
if ((status1 & SPU_STATUS1_RCV_LNK) &&
|
|
|
|
((tx_ctl & SMU_TX_CTL_LNK_STATUS) == 0) &&
|
|
|
|
((rx_ctl & SMU_RX_CTL_STATUS) == 0)) {
|
|
|
|
lmac->link_up = 1;
|
|
|
|
if (lmac->lmac_type == 4)
|
|
|
|
lmac->last_speed = 40000;
|
|
|
|
else
|
|
|
|
lmac->last_speed = 10000;
|
|
|
|
lmac->last_duplex = 1;
|
|
|
|
} else {
|
|
|
|
lmac->link_up = 0;
|
|
|
|
lmac->last_speed = 0;
|
|
|
|
lmac->last_duplex = 0;
|
|
|
|
return bgx_xaui_check_link(lmac);
|
|
|
|
}
|
|
|
|
|
|
|
|
lmac->last_link = lmac->link_up;
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("BGX%d:LMAC %u link %s\n", bgx_idx, lmacid,
|
|
|
|
(lmac->link_up) ? "up" : "down");
|
|
|
|
|
|
|
|
return lmac->link_up;
|
|
|
|
}
|
|
|
|
|
|
|
|
void bgx_lmac_disable(struct bgx *bgx, uint8_t lmacid)
|
|
|
|
{
|
|
|
|
struct lmac *lmac;
|
|
|
|
u64 cmrx_cfg;
|
|
|
|
|
|
|
|
lmac = &bgx->lmac[lmacid];
|
|
|
|
|
|
|
|
cmrx_cfg = bgx_reg_read(bgx, lmacid, BGX_CMRX_CFG);
|
|
|
|
cmrx_cfg &= ~(1 << 15);
|
|
|
|
bgx_reg_write(bgx, lmacid, BGX_CMRX_CFG, cmrx_cfg);
|
|
|
|
bgx_flush_dmac_addrs(bgx, lmacid);
|
|
|
|
|
|
|
|
if (lmac->phydev)
|
|
|
|
phy_shutdown(lmac->phydev);
|
|
|
|
|
|
|
|
lmac->phydev = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Program BGXX_CMRX_CONFIG.{lmac_type,lane_to_sds} for each interface.
|
|
|
|
* And the number of LMACs used by this interface. Each lmac can be in
|
|
|
|
* programmed in a different mode, so parse each lmac one at a time.
|
|
|
|
*/
|
|
|
|
static void bgx_init_hw(struct bgx *bgx)
|
|
|
|
{
|
|
|
|
struct lmac *lmac;
|
|
|
|
int i, lmacid, count = 0, inc = 0;
|
|
|
|
char buf[40];
|
|
|
|
static int qsgmii_configured;
|
|
|
|
|
|
|
|
for (lmacid = 0; lmacid < MAX_LMAC_PER_BGX; lmacid++) {
|
|
|
|
struct lmac *tlmac;
|
|
|
|
|
|
|
|
lmac = &bgx->lmac[lmacid];
|
|
|
|
debug("%s: lmacid = %d, qlm = %d, mode = %d\n",
|
|
|
|
__func__, lmacid, lmac->qlm, lmac->qlm_mode);
|
|
|
|
/* If QLM is not programmed, skip */
|
|
|
|
if (lmac->qlm == -1)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
switch (lmac->qlm_mode) {
|
|
|
|
case QLM_MODE_SGMII:
|
|
|
|
{
|
|
|
|
/* EBB8000 (alternative pkg) has only lane0 present on
|
|
|
|
* DLM0 and DLM1, skip configuring other lanes
|
|
|
|
*/
|
|
|
|
if (bgx->bgx_id == 0 && otx_is_altpkg()) {
|
|
|
|
if (lmacid % 2)
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
lmac->lane_to_sds = lmacid;
|
|
|
|
lmac->lmac_type = 0;
|
|
|
|
snprintf(buf, sizeof(buf),
|
|
|
|
"BGX%d QLM%d LMAC%d mode: %s\n",
|
|
|
|
bgx->bgx_id, lmac->qlm, lmacid,
|
|
|
|
lmac->is_1gx ? "1000Base-X" : "SGMII");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case QLM_MODE_XAUI:
|
|
|
|
if (lmacid != 0)
|
|
|
|
continue;
|
|
|
|
lmac->lmac_type = 1;
|
|
|
|
lmac->lane_to_sds = 0xE4;
|
|
|
|
snprintf(buf, sizeof(buf),
|
|
|
|
"BGX%d QLM%d LMAC%d mode: XAUI\n",
|
|
|
|
bgx->bgx_id, lmac->qlm, lmacid);
|
|
|
|
break;
|
|
|
|
case QLM_MODE_RXAUI:
|
|
|
|
if (lmacid == 0) {
|
|
|
|
lmac->lmac_type = 2;
|
|
|
|
lmac->lane_to_sds = 0x4;
|
|
|
|
} else if (lmacid == 1) {
|
|
|
|
struct lmac *tlmac;
|
|
|
|
|
|
|
|
tlmac = &bgx->lmac[2];
|
|
|
|
if (tlmac->qlm_mode == QLM_MODE_RXAUI) {
|
|
|
|
lmac->lmac_type = 2;
|
|
|
|
lmac->lane_to_sds = 0xe;
|
|
|
|
lmac->qlm = tlmac->qlm;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
snprintf(buf, sizeof(buf),
|
|
|
|
"BGX%d QLM%d LMAC%d mode: RXAUI\n",
|
|
|
|
bgx->bgx_id, lmac->qlm, lmacid);
|
|
|
|
break;
|
|
|
|
case QLM_MODE_XFI:
|
|
|
|
/* EBB8000 (alternative pkg) has only lane0 present on
|
|
|
|
* DLM0 and DLM1, skip configuring other lanes
|
|
|
|
*/
|
|
|
|
if (bgx->bgx_id == 0 && otx_is_altpkg()) {
|
|
|
|
if (lmacid % 2)
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
lmac->lane_to_sds = lmacid;
|
|
|
|
lmac->lmac_type = 3;
|
|
|
|
snprintf(buf, sizeof(buf),
|
|
|
|
"BGX%d QLM%d LMAC%d mode: XFI\n",
|
|
|
|
bgx->bgx_id, lmac->qlm, lmacid);
|
|
|
|
break;
|
|
|
|
case QLM_MODE_XLAUI:
|
|
|
|
if (lmacid != 0)
|
|
|
|
continue;
|
|
|
|
lmac->lmac_type = 4;
|
|
|
|
lmac->lane_to_sds = 0xE4;
|
|
|
|
snprintf(buf, sizeof(buf),
|
|
|
|
"BGX%d QLM%d LMAC%d mode: XLAUI\n",
|
|
|
|
bgx->bgx_id, lmac->qlm, lmacid);
|
|
|
|
break;
|
|
|
|
case QLM_MODE_10G_KR:
|
|
|
|
/* EBB8000 (alternative pkg) has only lane0 present on
|
|
|
|
* DLM0 and DLM1, skip configuring other lanes
|
|
|
|
*/
|
|
|
|
if (bgx->bgx_id == 0 && otx_is_altpkg()) {
|
|
|
|
if (lmacid % 2)
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
lmac->lane_to_sds = lmacid;
|
|
|
|
lmac->lmac_type = 3;
|
|
|
|
lmac->use_training = 1;
|
|
|
|
snprintf(buf, sizeof(buf),
|
|
|
|
"BGX%d QLM%d LMAC%d mode: 10G-KR\n",
|
|
|
|
bgx->bgx_id, lmac->qlm, lmacid);
|
|
|
|
break;
|
|
|
|
case QLM_MODE_40G_KR4:
|
|
|
|
if (lmacid != 0)
|
|
|
|
continue;
|
|
|
|
lmac->lmac_type = 4;
|
|
|
|
lmac->lane_to_sds = 0xE4;
|
|
|
|
lmac->use_training = 1;
|
|
|
|
snprintf(buf, sizeof(buf),
|
|
|
|
"BGX%d QLM%d LMAC%d mode: 40G-KR4\n",
|
|
|
|
bgx->bgx_id, lmac->qlm, lmacid);
|
|
|
|
break;
|
|
|
|
case QLM_MODE_RGMII:
|
|
|
|
if (lmacid != 0)
|
|
|
|
continue;
|
|
|
|
lmac->lmac_type = 5;
|
|
|
|
lmac->lane_to_sds = 0xE4;
|
|
|
|
snprintf(buf, sizeof(buf),
|
|
|
|
"BGX%d LMAC%d mode: RGMII\n",
|
|
|
|
bgx->bgx_id, lmacid);
|
|
|
|
break;
|
|
|
|
case QLM_MODE_QSGMII:
|
|
|
|
if (qsgmii_configured)
|
|
|
|
continue;
|
|
|
|
if (lmacid == 0 || lmacid == 2) {
|
|
|
|
count = 4;
|
|
|
|
printf("BGX%d QLM%d LMAC%d mode: QSGMII\n",
|
|
|
|
bgx->bgx_id, lmac->qlm, lmacid);
|
|
|
|
for (i = 0; i < count; i++) {
|
|
|
|
struct lmac *l;
|
|
|
|
int type;
|
|
|
|
|
|
|
|
l = &bgx->lmac[i];
|
|
|
|
l->lmac_type = 6;
|
|
|
|
type = l->lmac_type;
|
|
|
|
l->qlm_mode = QLM_MODE_QSGMII;
|
|
|
|
l->lane_to_sds = lmacid + i;
|
|
|
|
if (is_bgx_port_valid(bgx->bgx_id, i))
|
|
|
|
bgx_reg_write(bgx, i,
|
|
|
|
BGX_CMRX_CFG,
|
|
|
|
(type << 8) |
|
|
|
|
l->lane_to_sds);
|
|
|
|
}
|
|
|
|
qsgmii_configured = 1;
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
default:
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Reset lmac to the unused slot */
|
|
|
|
if (is_bgx_port_valid(bgx->bgx_id, count) &&
|
|
|
|
lmac->qlm_mode != QLM_MODE_QSGMII) {
|
|
|
|
int lmac_en = 0;
|
|
|
|
int tmp, idx;
|
|
|
|
|
|
|
|
tlmac = &bgx->lmac[count];
|
|
|
|
tlmac->lmac_type = lmac->lmac_type;
|
|
|
|
idx = bgx->bgx_id;
|
|
|
|
tmp = count + inc;
|
|
|
|
/* Adjust lane_to_sds based on BGX-ENABLE */
|
|
|
|
for (; tmp < MAX_LMAC_PER_BGX; inc++) {
|
|
|
|
lmac_en = bgx_board_info[idx].lmac_enable[tmp];
|
|
|
|
if (lmac_en)
|
|
|
|
break;
|
|
|
|
tmp = count + inc;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (inc != 0 && inc < MAX_LMAC_PER_BGX &&
|
|
|
|
lmac_en && inc != count)
|
|
|
|
tlmac->lane_to_sds =
|
|
|
|
lmac->lane_to_sds + abs(inc - count);
|
|
|
|
else
|
|
|
|
tlmac->lane_to_sds = lmac->lane_to_sds;
|
|
|
|
tlmac->qlm = lmac->qlm;
|
|
|
|
tlmac->qlm_mode = lmac->qlm_mode;
|
|
|
|
|
|
|
|
printf("%s", buf);
|
|
|
|
/* Initialize lmac_type and lane_to_sds */
|
|
|
|
bgx_reg_write(bgx, count, BGX_CMRX_CFG,
|
|
|
|
(tlmac->lmac_type << 8) |
|
|
|
|
tlmac->lane_to_sds);
|
|
|
|
|
|
|
|
if (tlmac->lmac_type == BGX_MODE_SGMII) {
|
|
|
|
if (tlmac->is_1gx) {
|
|
|
|
/* This is actually 1000BASE-X, so
|
|
|
|
* mark the LMAC as such.
|
|
|
|
*/
|
|
|
|
bgx_reg_modify(bgx, count,
|
|
|
|
BGX_GMP_PCS_MISCX_CTL,
|
|
|
|
PCS_MISC_CTL_MODE);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!bgx_board_info[bgx->bgx_id].phy_info[lmacid].autoneg_dis) {
|
|
|
|
/* The Linux DTS does not disable
|
|
|
|
* autoneg for this LMAC (in SGMII or
|
|
|
|
* 1000BASE-X mode), so that means
|
|
|
|
* enable autoneg.
|
|
|
|
*/
|
|
|
|
bgx_reg_modify(bgx, count,
|
|
|
|
BGX_GMP_PCS_MRX_CTL,
|
|
|
|
PCS_MRX_CTL_AN_EN);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
count += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Done probing all 4 lmacs, now clear qsgmii_configured */
|
|
|
|
qsgmii_configured = 0;
|
|
|
|
|
|
|
|
printf("BGX%d LMACs: %d\n", bgx->bgx_id, count);
|
|
|
|
bgx->lmac_count = count;
|
|
|
|
bgx_reg_write(bgx, 0, BGX_CMR_RX_LMACS, count);
|
|
|
|
bgx_reg_write(bgx, 0, BGX_CMR_TX_LMACS, count);
|
|
|
|
|
|
|
|
bgx_reg_modify(bgx, 0, BGX_CMR_GLOBAL_CFG, CMR_GLOBAL_CFG_FCS_STRIP);
|
|
|
|
if (bgx_reg_read(bgx, 0, BGX_CMR_BIST_STATUS))
|
|
|
|
printf("BGX%d BIST failed\n", bgx->bgx_id);
|
|
|
|
|
|
|
|
/* Set the backpressure AND mask */
|
|
|
|
for (i = 0; i < bgx->lmac_count; i++)
|
|
|
|
bgx_reg_modify(bgx, 0, BGX_CMR_CHAN_MSK_AND,
|
|
|
|
((1ULL << MAX_BGX_CHANS_PER_LMAC) - 1) <<
|
|
|
|
(i * MAX_BGX_CHANS_PER_LMAC));
|
|
|
|
|
|
|
|
/* Disable all MAC filtering */
|
|
|
|
for (i = 0; i < RX_DMAC_COUNT; i++)
|
|
|
|
bgx_reg_write(bgx, 0, BGX_CMR_RX_DMACX_CAM + (i * 8), 0x00);
|
|
|
|
|
|
|
|
/* Disable MAC steering (NCSI traffic) */
|
|
|
|
for (i = 0; i < RX_TRAFFIC_STEER_RULE_COUNT; i++)
|
|
|
|
bgx_reg_write(bgx, 0, BGX_CMR_RX_STREERING + (i * 8), 0x00);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bgx_get_qlm_mode(struct bgx *bgx)
|
|
|
|
{
|
|
|
|
struct lmac *lmac;
|
|
|
|
int lmacid;
|
|
|
|
|
|
|
|
/* Read LMACx type to figure out QLM mode
|
|
|
|
* This is configured by low level firmware
|
|
|
|
*/
|
|
|
|
for (lmacid = 0; lmacid < MAX_LMAC_PER_BGX; lmacid++) {
|
|
|
|
int lmac_type;
|
|
|
|
int train_en;
|
|
|
|
int index = 0;
|
|
|
|
|
|
|
|
if (otx_is_soc(CN81XX) || (otx_is_soc(CN83XX) &&
|
|
|
|
bgx->bgx_id == 2))
|
|
|
|
index = (lmacid < 2) ? 0 : 2;
|
|
|
|
|
|
|
|
lmac = &bgx->lmac[lmacid];
|
|
|
|
|
|
|
|
/* check if QLM is programmed, if not, skip */
|
|
|
|
if (lmac->qlm == -1)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
lmac_type = bgx_reg_read(bgx, index, BGX_CMRX_CFG);
|
|
|
|
lmac->lmac_type = (lmac_type >> 8) & 0x07;
|
|
|
|
debug("%s:%d:%d: lmac_type = %d, altpkg = %d\n", __func__,
|
|
|
|
bgx->bgx_id, lmacid, lmac->lmac_type, otx_is_altpkg());
|
|
|
|
|
|
|
|
train_en = (readq(GSERX_SCRATCH(lmac->qlm))) & 0xf;
|
|
|
|
lmac->is_1gx = bgx_reg_read(bgx, index, BGX_GMP_PCS_MISCX_CTL)
|
|
|
|
& (PCS_MISC_CTL_MODE) ? true : false;
|
|
|
|
|
|
|
|
switch (lmac->lmac_type) {
|
|
|
|
case BGX_MODE_SGMII:
|
|
|
|
if (bgx->is_rgx) {
|
|
|
|
if (lmacid == 0) {
|
|
|
|
lmac->qlm_mode = QLM_MODE_RGMII;
|
|
|
|
debug("BGX%d LMAC%d mode: RGMII\n",
|
|
|
|
bgx->bgx_id, lmacid);
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
if (bgx->bgx_id == 0 && otx_is_altpkg()) {
|
|
|
|
if (lmacid % 2)
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
lmac->qlm_mode = QLM_MODE_SGMII;
|
|
|
|
debug("BGX%d QLM%d LMAC%d mode: %s\n",
|
|
|
|
bgx->bgx_id, lmac->qlm, lmacid,
|
|
|
|
lmac->is_1gx ? "1000Base-X" : "SGMII");
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case BGX_MODE_XAUI:
|
|
|
|
if (bgx->bgx_id == 0 && otx_is_altpkg())
|
|
|
|
continue;
|
|
|
|
lmac->qlm_mode = QLM_MODE_XAUI;
|
|
|
|
if (lmacid != 0)
|
|
|
|
continue;
|
|
|
|
debug("BGX%d QLM%d LMAC%d mode: XAUI\n",
|
|
|
|
bgx->bgx_id, lmac->qlm, lmacid);
|
|
|
|
break;
|
|
|
|
case BGX_MODE_RXAUI:
|
|
|
|
if (bgx->bgx_id == 0 && otx_is_altpkg())
|
|
|
|
continue;
|
|
|
|
lmac->qlm_mode = QLM_MODE_RXAUI;
|
|
|
|
if (index == lmacid) {
|
|
|
|
debug("BGX%d QLM%d LMAC%d mode: RXAUI\n",
|
|
|
|
bgx->bgx_id, lmac->qlm, (index ? 1 : 0));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case BGX_MODE_XFI:
|
|
|
|
if (bgx->bgx_id == 0 && otx_is_altpkg()) {
|
|
|
|
if (lmacid % 2)
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if ((lmacid < 2 && (train_en & (1 << lmacid))) ||
|
|
|
|
(train_en & (1 << (lmacid - 2)))) {
|
|
|
|
lmac->qlm_mode = QLM_MODE_10G_KR;
|
|
|
|
debug("BGX%d QLM%d LMAC%d mode: 10G_KR\n",
|
|
|
|
bgx->bgx_id, lmac->qlm, lmacid);
|
|
|
|
} else {
|
|
|
|
lmac->qlm_mode = QLM_MODE_XFI;
|
|
|
|
debug("BGX%d QLM%d LMAC%d mode: XFI\n",
|
|
|
|
bgx->bgx_id, lmac->qlm, lmacid);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case BGX_MODE_XLAUI:
|
|
|
|
if (bgx->bgx_id == 0 && otx_is_altpkg())
|
|
|
|
continue;
|
|
|
|
if (train_en) {
|
|
|
|
lmac->qlm_mode = QLM_MODE_40G_KR4;
|
|
|
|
if (lmacid != 0)
|
|
|
|
break;
|
|
|
|
debug("BGX%d QLM%d LMAC%d mode: 40G_KR4\n",
|
|
|
|
bgx->bgx_id, lmac->qlm, lmacid);
|
|
|
|
} else {
|
|
|
|
lmac->qlm_mode = QLM_MODE_XLAUI;
|
|
|
|
if (lmacid != 0)
|
|
|
|
break;
|
|
|
|
debug("BGX%d QLM%d LMAC%d mode: XLAUI\n",
|
|
|
|
bgx->bgx_id, lmac->qlm, lmacid);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case BGX_MODE_QSGMII:
|
|
|
|
/* If QLM is configured as QSGMII, use lmac0 */
|
|
|
|
if (otx_is_soc(CN83XX) && lmacid == 2 &&
|
|
|
|
bgx->bgx_id != 2) {
|
|
|
|
//lmac->qlm_mode = QLM_MODE_DISABLED;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lmacid == 0 || lmacid == 2) {
|
|
|
|
lmac->qlm_mode = QLM_MODE_QSGMII;
|
|
|
|
debug("BGX%d QLM%d LMAC%d mode: QSGMII\n",
|
|
|
|
bgx->bgx_id, lmac->qlm, lmacid);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void bgx_set_board_info(int bgx_id, int *mdio_bus,
|
|
|
|
int *phy_addr, bool *autoneg_dis, bool *lmac_reg,
|
|
|
|
bool *lmac_enable)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
for (i = 0; i < MAX_LMAC_PER_BGX; i++) {
|
|
|
|
bgx_board_info[bgx_id].phy_info[i].phy_addr = phy_addr[i];
|
|
|
|
bgx_board_info[bgx_id].phy_info[i].mdio_bus = mdio_bus[i];
|
|
|
|
bgx_board_info[bgx_id].phy_info[i].autoneg_dis = autoneg_dis[i];
|
|
|
|
bgx_board_info[bgx_id].lmac_reg[i] = lmac_reg[i];
|
|
|
|
bgx_board_info[bgx_id].lmac_enable[i] = lmac_enable[i];
|
|
|
|
debug("%s bgx_id %d lmac %d\n", __func__, bgx_id, i);
|
|
|
|
debug("phy addr %x mdio bus %d autoneg_dis %d lmac_reg %d\n",
|
|
|
|
bgx_board_info[bgx_id].phy_info[i].phy_addr,
|
|
|
|
bgx_board_info[bgx_id].phy_info[i].mdio_bus,
|
|
|
|
bgx_board_info[bgx_id].phy_info[i].autoneg_dis,
|
|
|
|
bgx_board_info[bgx_id].lmac_reg[i]);
|
|
|
|
debug("lmac_enable = %x\n",
|
|
|
|
bgx_board_info[bgx_id].lmac_enable[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int octeontx_bgx_remove(struct udevice *dev)
|
|
|
|
{
|
|
|
|
int lmacid;
|
|
|
|
u64 cfg;
|
|
|
|
int count = MAX_LMAC_PER_BGX;
|
|
|
|
struct bgx *bgx = dev_get_priv(dev);
|
|
|
|
|
|
|
|
if (!bgx->reg_base)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (bgx->is_rgx)
|
|
|
|
count = 1;
|
|
|
|
|
|
|
|
for (lmacid = 0; lmacid < count; lmacid++) {
|
|
|
|
struct lmac *lmac;
|
|
|
|
|
|
|
|
lmac = &bgx->lmac[lmacid];
|
|
|
|
cfg = bgx_reg_read(bgx, lmacid, BGX_CMRX_CFG);
|
|
|
|
cfg &= ~(CMR_PKT_RX_EN | CMR_PKT_TX_EN);
|
|
|
|
bgx_reg_write(bgx, lmacid, BGX_CMRX_CFG, cfg);
|
|
|
|
|
|
|
|
/* Disable PCS for 1G interface */
|
|
|
|
if (lmac->lmac_type == BGX_MODE_SGMII ||
|
|
|
|
lmac->lmac_type == BGX_MODE_QSGMII) {
|
|
|
|
cfg = bgx_reg_read(bgx, lmacid, BGX_GMP_PCS_MRX_CTL);
|
|
|
|
cfg |= PCS_MRX_CTL_PWR_DN;
|
|
|
|
bgx_reg_write(bgx, lmacid, BGX_GMP_PCS_MRX_CTL, cfg);
|
|
|
|
}
|
|
|
|
|
|
|
|
debug("%s disabling bgx%d lmacid%d\n", __func__, bgx->bgx_id,
|
|
|
|
lmacid);
|
|
|
|
bgx_lmac_disable(bgx, lmacid);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int octeontx_bgx_probe(struct udevice *dev)
|
|
|
|
{
|
|
|
|
struct bgx *bgx = dev_get_priv(dev);
|
|
|
|
u8 lmac = 0;
|
|
|
|
int qlm[4] = {-1, -1, -1, -1};
|
|
|
|
int bgx_idx, node;
|
|
|
|
int inc = 1;
|
|
|
|
|
|
|
|
bgx->reg_base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0,
|
|
|
|
PCI_REGION_MEM);
|
|
|
|
if (!bgx->reg_base) {
|
|
|
|
debug("No PCI region found\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef OCTEONTX_XCV
|
|
|
|
/* Use FAKE BGX2 for RGX interface */
|
|
|
|
if ((((uintptr_t)bgx->reg_base >> 24) & 0xf) == 0x8) {
|
|
|
|
bgx->bgx_id = 2;
|
|
|
|
bgx->is_rgx = true;
|
|
|
|
for (lmac = 0; lmac < MAX_LMAC_PER_BGX; lmac++) {
|
|
|
|
if (lmac == 0) {
|
|
|
|
bgx->lmac[lmac].lmacid = 0;
|
|
|
|
bgx->lmac[lmac].qlm = 0;
|
|
|
|
} else {
|
|
|
|
bgx->lmac[lmac].qlm = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
xcv_init_hw();
|
|
|
|
goto skip_qlm_config;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
node = node_id(bgx->reg_base);
|
|
|
|
bgx_idx = ((uintptr_t)bgx->reg_base >> 24) & 3;
|
|
|
|
bgx->bgx_id = (node * MAX_BGX_PER_NODE) + bgx_idx;
|
|
|
|
if (otx_is_soc(CN81XX))
|
|
|
|
inc = 2;
|
|
|
|
else if (otx_is_soc(CN83XX) && (bgx_idx == 2))
|
|
|
|
inc = 2;
|
|
|
|
|
|
|
|
for (lmac = 0; lmac < MAX_LMAC_PER_BGX; lmac += inc) {
|
|
|
|
/* BGX3 (DLM4), has only 2 lanes */
|
|
|
|
if (otx_is_soc(CN83XX) && bgx_idx == 3 && lmac >= 2)
|
|
|
|
continue;
|
|
|
|
qlm[lmac + 0] = get_qlm_for_bgx(node, bgx_idx, lmac);
|
|
|
|
/* Each DLM has 2 lanes, configure both lanes with
|
|
|
|
* same qlm configuration
|
|
|
|
*/
|
|
|
|
if (inc == 2)
|
|
|
|
qlm[lmac + 1] = qlm[lmac];
|
|
|
|
debug("qlm[%d] = %d\n", lmac, qlm[lmac]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* A BGX can take 1 or 2 DLMs, if both the DLMs are not configured
|
|
|
|
* as BGX, then return, nothing to initialize
|
|
|
|
*/
|
|
|
|
if (otx_is_soc(CN81XX))
|
|
|
|
if ((qlm[0] == -1) && (qlm[2] == -1))
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
/* MAP configuration registers */
|
|
|
|
for (lmac = 0; lmac < MAX_LMAC_PER_BGX; lmac++) {
|
|
|
|
bgx->lmac[lmac].qlm = qlm[lmac];
|
|
|
|
bgx->lmac[lmac].lmacid = lmac;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef OCTEONTX_XCV
|
|
|
|
skip_qlm_config:
|
|
|
|
#endif
|
|
|
|
bgx_vnic[bgx->bgx_id] = bgx;
|
|
|
|
bgx_get_qlm_mode(bgx);
|
|
|
|
debug("bgx_vnic[%u]: %p\n", bgx->bgx_id, bgx);
|
|
|
|
|
|
|
|
bgx_init_hw(bgx);
|
|
|
|
|
|
|
|
/* Init LMACs */
|
|
|
|
for (lmac = 0; lmac < bgx->lmac_count; lmac++) {
|
|
|
|
struct lmac *tlmac = &bgx->lmac[lmac];
|
|
|
|
|
|
|
|
tlmac->dev = dev;
|
|
|
|
tlmac->init_pend = 1;
|
|
|
|
tlmac->bgx = bgx;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
U_BOOT_DRIVER(octeontx_bgx) = {
|
|
|
|
.name = "octeontx_bgx",
|
|
|
|
.id = UCLASS_MISC,
|
|
|
|
.probe = octeontx_bgx_probe,
|
|
|
|
.remove = octeontx_bgx_remove,
|
2020-12-03 23:55:17 +00:00
|
|
|
.priv_auto = sizeof(struct bgx),
|
2020-08-26 12:37:33 +00:00
|
|
|
.flags = DM_FLAG_OS_PREPARE,
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct pci_device_id octeontx_bgx_supported[] = {
|
|
|
|
{ PCI_VDEVICE(CAVIUM, PCI_DEVICE_ID_CAVIUM_BGX) },
|
|
|
|
{ PCI_VDEVICE(CAVIUM, PCI_DEVICE_ID_CAVIUM_RGX) },
|
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
|
|
|
U_BOOT_PCI_DEVICE(octeontx_bgx, octeontx_bgx_supported);
|