u-boot-brain/drivers/net/octeontx/bgx.c
Simon Glass 41575d8e4c dm: treewide: Rename auto_alloc_size members to be shorter
This construct is quite long-winded. In earlier days it made some sense
since auto-allocation was a strange concept. But with driver model now
used pretty universally, we can shorten this to 'auto'. This reduces
verbosity and makes it easier to read.

Coincidentally it also ensures that every declaration is on one line,
thus making dtoc's job easier.

Signed-off-by: Simon Glass <sjg@chromium.org>
2020-12-13 08:00:25 -07:00

1566 lines
40 KiB
C

// 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,
.priv_auto = sizeof(struct bgx),
.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);