diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/Kconfig | 14 | ||||
-rw-r--r-- | drivers/net/Makefile | 2 | ||||
-rw-r--r-- | drivers/net/octeontx/Makefile | 7 | ||||
-rw-r--r-- | drivers/net/octeontx/bgx.c | 1565 | ||||
-rw-r--r-- | drivers/net/octeontx/bgx.h | 259 | ||||
-rw-r--r-- | drivers/net/octeontx/nic.h | 508 | ||||
-rw-r--r-- | drivers/net/octeontx/nic_main.c | 778 | ||||
-rw-r--r-- | drivers/net/octeontx/nic_reg.h | 250 | ||||
-rw-r--r-- | drivers/net/octeontx/nicvf_main.c | 581 | ||||
-rw-r--r-- | drivers/net/octeontx/nicvf_queues.c | 1140 | ||||
-rw-r--r-- | drivers/net/octeontx/nicvf_queues.h | 353 | ||||
-rw-r--r-- | drivers/net/octeontx/q_struct.h | 695 | ||||
-rw-r--r-- | drivers/net/octeontx/smi.c | 380 | ||||
-rw-r--r-- | drivers/net/octeontx/xcv.c | 124 |
14 files changed, 6656 insertions, 0 deletions
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index a0d2d21a556..96f23d3d7cc 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -407,6 +407,20 @@ config MT7628_ETH The MediaTek MT7628 ethernet interface is used on MT7628 and MT7688 based boards. +config NET_OCTEONTX + bool "OcteonTX Ethernet support" + depends on ARCH_OCTEONTX + depends on PCI_SRIOV + help + You must select Y to enable network device support for + OcteonTX SoCs. If unsure, say n +config OCTEONTX_SMI + bool "OcteonTX SMI Device support" + depends on ARCH_OCTEONTX || ARCH_OCTEONTX2 + help + You must select Y to enable SMI controller support for + OcteonTX or OcteonTX2 SoCs. If unsure, say n + config PCH_GBE bool "Intel Platform Controller Hub EG20T GMAC driver" depends on DM_ETH && DM_PCI diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 03f01921ead..c193953b0e2 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -65,6 +65,8 @@ obj-$(CONFIG_RENESAS_RAVB) += ravb.o obj-$(CONFIG_SMC91111) += smc91111.o obj-$(CONFIG_SMC911X) += smc911x.o obj-$(CONFIG_TSEC_ENET) += tsec.o fsl_mdio.o +obj-$(CONFIG_NET_OCTEONTX) += octeontx/ +obj-$(CONFIG_OCTEONTX_SMI) += octeontx/smi.o obj-$(CONFIG_FMAN_ENET) += fsl_mdio.o obj-$(CONFIG_ULI526X) += uli526x.o obj-$(CONFIG_VSC7385_ENET) += vsc7385.o diff --git a/drivers/net/octeontx/Makefile b/drivers/net/octeontx/Makefile new file mode 100644 index 00000000000..d4adb7cdba2 --- /dev/null +++ b/drivers/net/octeontx/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Copyright (C) 2018 Marvell International Ltd. +# + +obj-$(CONFIG_NET_OCTEONTX) += bgx.o nic_main.o nicvf_queues.o nicvf_main.o \ + xcv.o diff --git a/drivers/net/octeontx/bgx.c b/drivers/net/octeontx/bgx.c new file mode 100644 index 00000000000..fbe2e2c073e --- /dev/null +++ b/drivers/net/octeontx/bgx.c @@ -0,0 +1,1565 @@ +// 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_alloc_size = 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); diff --git a/drivers/net/octeontx/bgx.h b/drivers/net/octeontx/bgx.h new file mode 100644 index 00000000000..8402630bf5a --- /dev/null +++ b/drivers/net/octeontx/bgx.h @@ -0,0 +1,259 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (C) 2018 Marvell International Ltd. + */ + +#ifndef BGX_H +#define BGX_H + +#include <asm/arch/board.h> + +/* PCI device IDs */ +#define PCI_DEVICE_ID_OCTEONTX_BGX 0xA026 +#define PCI_DEVICE_ID_OCTEONTX_RGX 0xA054 + +#define MAX_LMAC_PER_BGX 4 +#define MAX_BGX_CHANS_PER_LMAC 16 +#define MAX_DMAC_PER_LMAC 8 +#define MAX_FRAME_SIZE 9216 + +#define MAX_DMAC_PER_LMAC_TNS_BYPASS_MODE 2 + +#define MAX_LMAC (MAX_BGX_PER_NODE * MAX_LMAC_PER_BGX) + +#define NODE_ID_MASK 0x300000000000 +#define NODE_ID(x) (((x) & NODE_ID_MASK) >> 44) + +/* Registers */ +#define GSERX_CFG(x) (0x87E090000080ull + (x) * 0x1000000ull) +#define GSERX_SCRATCH(x) (0x87E090000020ull + (x) * 0x1000000ull) +#define GSERX_PHY_CTL(x) (0x87E090000000ull + (x) * 0x1000000ull) +#define GSERX_CFG_BGX BIT(2) +#define GSER_RX_EIE_DETSTS(x) (0x87E090000150ull + (x) * 0x1000000ull) +#define GSER_CDRLOCK (8) +#define GSER_BR_RXX_CTL(x, y) (0x87E090000400ull + (x) * 0x1000000ull + \ + (y) * 0x80) +#define GSER_BR_RXX_CTL_RXT_SWM BIT(2) +#define GSER_BR_RXX_EER(x, y) (0x87E090000418ull + (x) * 0x1000000ull + \ + (y) * 0x80) +#define GSER_BR_RXX_EER_RXT_ESV BIT(14) +#define GSER_BR_RXX_EER_RXT_EER BIT(15) +#define EER_RXT_ESV (14) + +#define BGX_CMRX_CFG 0x00 +#define CMR_PKT_TX_EN BIT_ULL(13) +#define CMR_PKT_RX_EN BIT_ULL(14) +#define CMR_EN BIT_ULL(15) +#define BGX_CMR_GLOBAL_CFG 0x08 +#define CMR_GLOBAL_CFG_FCS_STRIP BIT_ULL(6) +#define BGX_CMRX_RX_ID_MAP 0x60 +#define BGX_CMRX_RX_STAT0 0x70 +#define BGX_CMRX_RX_STAT1 0x78 +#define BGX_CMRX_RX_STAT2 0x80 +#define BGX_CMRX_RX_STAT3 0x88 +#define BGX_CMRX_RX_STAT4 0x90 +#define BGX_CMRX_RX_STAT5 0x98 +#define BGX_CMRX_RX_STAT6 0xA0 +#define BGX_CMRX_RX_STAT7 0xA8 +#define BGX_CMRX_RX_STAT8 0xB0 +#define BGX_CMRX_RX_STAT9 0xB8 +#define BGX_CMRX_RX_STAT10 0xC0 +#define BGX_CMRX_RX_BP_DROP 0xC8 +#define BGX_CMRX_RX_DMAC_CTL 0x0E8 +#define BGX_CMR_RX_DMACX_CAM 0x200 +#define RX_DMACX_CAM_EN BIT_ULL(48) +#define RX_DMACX_CAM_LMACID(x) ((x) << 49) +#define RX_DMAC_COUNT 32 +#define BGX_CMR_RX_STREERING 0x300 +#define RX_TRAFFIC_STEER_RULE_COUNT 8 +#define BGX_CMR_CHAN_MSK_AND 0x450 +#define BGX_CMR_BIST_STATUS 0x460 +#define BGX_CMR_RX_LMACS 0x468 +#define BGX_CMRX_TX_STAT0 0x600 +#define BGX_CMRX_TX_STAT1 0x608 +#define BGX_CMRX_TX_STAT2 0x610 +#define BGX_CMRX_TX_STAT3 0x618 +#define BGX_CMRX_TX_STAT4 0x620 +#define BGX_CMRX_TX_STAT5 0x628 +#define BGX_CMRX_TX_STAT6 0x630 +#define BGX_CMRX_TX_STAT7 0x638 +#define BGX_CMRX_TX_STAT8 0x640 +#define BGX_CMRX_TX_STAT9 0x648 +#define BGX_CMRX_TX_STAT10 0x650 +#define BGX_CMRX_TX_STAT11 0x658 +#define BGX_CMRX_TX_STAT12 0x660 +#define BGX_CMRX_TX_STAT13 0x668 +#define BGX_CMRX_TX_STAT14 0x670 +#define BGX_CMRX_TX_STAT15 0x678 +#define BGX_CMRX_TX_STAT16 0x680 +#define BGX_CMRX_TX_STAT17 0x688 +#define BGX_CMR_TX_LMACS 0x1000 + +#define BGX_SPUX_CONTROL1 0x10000 +#define SPU_CTL_LOW_POWER BIT_ULL(11) +#define SPU_CTL_LOOPBACK BIT_ULL(14) +#define SPU_CTL_RESET BIT_ULL(15) +#define BGX_SPUX_STATUS1 0x10008 +#define SPU_STATUS1_RCV_LNK BIT_ULL(2) +#define BGX_SPUX_STATUS2 0x10020 +#define SPU_STATUS2_RCVFLT BIT_ULL(10) +#define BGX_SPUX_BX_STATUS 0x10028 +#define SPU_BX_STATUS_RX_ALIGN BIT_ULL(12) +#define BGX_SPUX_BR_STATUS1 0x10030 +#define SPU_BR_STATUS_BLK_LOCK BIT_ULL(0) +#define SPU_BR_STATUS_RCV_LNK BIT_ULL(12) +#define BGX_SPUX_BR_PMD_CRTL 0x10068 +#define SPU_PMD_CRTL_TRAIN_EN BIT_ULL(1) +#define BGX_SPUX_BR_PMD_LP_CUP 0x10078 +#define BGX_SPUX_BR_PMD_LD_CUP 0x10088 +#define BGX_SPUX_BR_PMD_LD_REP 0x10090 +#define BGX_SPUX_FEC_CONTROL 0x100A0 +#define SPU_FEC_CTL_FEC_EN BIT_ULL(0) +#define SPU_FEC_CTL_ERR_EN BIT_ULL(1) +#define BGX_SPUX_AN_CONTROL 0x100C8 +#define SPU_AN_CTL_AN_EN BIT_ULL(12) +#define SPU_AN_CTL_XNP_EN BIT_ULL(13) +#define SPU_AN_CTL_AN_RESTART BIT_ULL(15) +#define BGX_SPUX_AN_STATUS 0x100D0 +#define SPU_AN_STS_AN_COMPLETE BIT_ULL(5) +#define BGX_SPUX_AN_ADV 0x100D8 +#define BGX_SPUX_MISC_CONTROL 0x10218 +#define SPU_MISC_CTL_INTLV_RDISP BIT_ULL(10) +#define SPU_MISC_CTL_RX_DIS BIT_ULL(12) +#define BGX_SPUX_INT 0x10220 /* +(0..3) << 20 */ +#define BGX_SPUX_INT_W1S 0x10228 +#define BGX_SPUX_INT_ENA_W1C 0x10230 +#define BGX_SPUX_INT_ENA_W1S 0x10238 +#define BGX_SPU_DBG_CONTROL 0x10300 +#define SPU_DBG_CTL_AN_ARB_LINK_CHK_EN BIT_ULL(18) +#define SPU_DBG_CTL_AN_NONCE_MCT_DIS BIT_ULL(29) + +#define BGX_SMUX_RX_INT 0x20000 +#define BGX_SMUX_RX_JABBER 0x20030 +#define BGX_SMUX_RX_CTL 0x20048 +#define SMU_RX_CTL_STATUS (3ull << 0) +#define BGX_SMUX_TX_APPEND 0x20100 +#define SMU_TX_APPEND_FCS_D BIT_ULL(2) +#define BGX_SMUX_TX_MIN_PKT 0x20118 +#define BGX_SMUX_TX_INT 0x20140 +#define BGX_SMUX_TX_CTL 0x20178 +#define SMU_TX_CTL_DIC_EN BIT_ULL(0) +#define SMU_TX_CTL_UNI_EN BIT_ULL(1) +#define SMU_TX_CTL_LNK_STATUS (3ull << 4) +#define BGX_SMUX_TX_THRESH 0x20180 +#define BGX_SMUX_CTL 0x20200 +#define SMU_CTL_RX_IDLE BIT_ULL(0) +#define SMU_CTL_TX_IDLE BIT_ULL(1) + +#define BGX_GMP_PCS_MRX_CTL 0x30000 +#define PCS_MRX_CTL_RST_AN BIT_ULL(9) +#define PCS_MRX_CTL_PWR_DN BIT_ULL(11) +#define PCS_MRX_CTL_AN_EN BIT_ULL(12) +#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_AN_CPT BIT_ULL(5) +#define BGX_GMP_PCS_ANX_AN_RESULTS 0x30020 +#define BGX_GMP_PCS_SGM_AN_ADV 0x30068 +#define BGX_GMP_PCS_MISCX_CTL 0x30078 +#define PCS_MISCX_CTL_DISP_EN BIT_ULL(13) +#define PCS_MISC_CTL_GMX_ENO BIT_ULL(11) +#define PCS_MISC_CTL_SAMP_PT_MASK 0x7Full +#define PCS_MISC_CTL_MODE BIT_ULL(8) +#define BGX_GMP_GMI_PRTX_CFG 0x38020 +#define GMI_PORT_CFG_SPEED BIT_ULL(1) +#define GMI_PORT_CFG_DUPLEX BIT_ULL(2) +#define GMI_PORT_CFG_SLOT_TIME BIT_ULL(3) +#define GMI_PORT_CFG_SPEED_MSB BIT_ULL(8) +#define BGX_GMP_GMI_RXX_JABBER 0x38038 +#define BGX_GMP_GMI_TXX_THRESH 0x38210 +#define BGX_GMP_GMI_TXX_APPEND 0x38218 +#define BGX_GMP_GMI_TXX_SLOT 0x38220 +#define BGX_GMP_GMI_TXX_BURST 0x38228 +#define BGX_GMP_GMI_TXX_MIN_PKT 0x38240 +#define BGX_GMP_GMI_TXX_SGMII_CTL 0x38300 + +#define BGX_MSIX_VEC_0_29_ADDR 0x400000 /* +(0..29) << 4 */ +#define BGX_MSIX_VEC_0_29_CTL 0x400008 +#define BGX_MSIX_PBA_0 0x4F0000 + +/* MSI-X interrupts */ +#define BGX_MSIX_VECTORS 30 +#define BGX_LMAC_VEC_OFFSET 7 +#define BGX_MSIX_VEC_SHIFT 4 + +#define CMRX_INT 0 +#define SPUX_INT 1 +#define SMUX_RX_INT 2 +#define SMUX_TX_INT 3 +#define GMPX_PCS_INT 4 +#define GMPX_GMI_RX_INT 5 +#define GMPX_GMI_TX_INT 6 +#define CMR_MEM_INT 28 +#define SPU_MEM_INT 29 + +#define LMAC_INTR_LINK_UP BIT(0) +#define LMAC_INTR_LINK_DOWN BIT(1) + +/* RX_DMAC_CTL configuration*/ +enum MCAST_MODE { + MCAST_MODE_REJECT, + MCAST_MODE_ACCEPT, + MCAST_MODE_CAM_FILTER, + RSVD +}; + +#define BCAST_ACCEPT 1 +#define CAM_ACCEPT 1 + +int octeontx_bgx_initialize(unsigned int bgx_idx, unsigned int node); +void bgx_add_dmac_addr(u64 dmac, int node, int bgx_idx, int lmac); +void bgx_get_count(int node, int *bgx_count); +int bgx_get_lmac_count(int node, int bgx); +void bgx_print_stats(int bgx_idx, int lmac); +void xcv_init_hw(void); +void xcv_setup_link(bool link_up, int link_speed); + +#undef LINK_INTR_ENABLE + +enum qlm_mode { + QLM_MODE_SGMII, /* SGMII, each lane independent */ + QLM_MODE_XAUI, /* 1 XAUI or DXAUI, 4 lanes */ + QLM_MODE_RXAUI, /* 2 RXAUI, 2 lanes each */ + QLM_MODE_XFI, /* 4 XFI, 1 lane each */ + QLM_MODE_XLAUI, /* 1 XLAUI, 4 lanes each */ + QLM_MODE_10G_KR, /* 4 10GBASE-KR, 1 lane each */ + QLM_MODE_40G_KR4, /* 1 40GBASE-KR4, 4 lanes each */ + QLM_MODE_QSGMII, /* 4 QSGMII, each lane independent */ + QLM_MODE_RGMII, /* 1 RGX */ +}; + +struct phy_info { + int mdio_bus; + int phy_addr; + bool autoneg_dis; +}; + +struct bgx_board_info { + struct phy_info phy_info[MAX_LMAC_PER_BGX]; + bool lmac_reg[MAX_LMAC_PER_BGX]; + bool lmac_enable[MAX_LMAC_PER_BGX]; +}; + +enum LMAC_TYPE { + BGX_MODE_SGMII = 0, /* 1 lane, 1.250 Gbaud */ + BGX_MODE_XAUI = 1, /* 4 lanes, 3.125 Gbaud */ + BGX_MODE_DXAUI = 1, /* 4 lanes, 6.250 Gbaud */ + BGX_MODE_RXAUI = 2, /* 2 lanes, 6.250 Gbaud */ + BGX_MODE_XFI = 3, /* 1 lane, 10.3125 Gbaud */ + BGX_MODE_XLAUI = 4, /* 4 lanes, 10.3125 Gbaud */ + BGX_MODE_10G_KR = 3,/* 1 lane, 10.3125 Gbaud */ + BGX_MODE_40G_KR = 4,/* 4 lanes, 10.3125 Gbaud */ + BGX_MODE_RGMII = 5, + BGX_MODE_QSGMII = 6, + BGX_MODE_INVALID = 7, +}; + +int rxaui_phy_xs_init(struct mii_dev *bus, int phy_addr); + +#endif /* BGX_H */ diff --git a/drivers/net/octeontx/nic.h b/drivers/net/octeontx/nic.h new file mode 100644 index 00000000000..af3576cfbba --- /dev/null +++ b/drivers/net/octeontx/nic.h @@ -0,0 +1,508 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (C) 2018 Marvell International Ltd. + */ + +#ifndef NIC_H +#define NIC_H + +#include <linux/netdevice.h> +#include "bgx.h" + +#define PCI_DEVICE_ID_CAVIUM_NICVF_1 0x0011 + +/* Subsystem device IDs */ +#define PCI_SUBSYS_DEVID_88XX_NIC_PF 0xA11E +#define PCI_SUBSYS_DEVID_81XX_NIC_PF 0xA21E +#define PCI_SUBSYS_DEVID_83XX_NIC_PF 0xA31E + +#define PCI_SUBSYS_DEVID_88XX_PASS1_NIC_VF 0xA11E +#define PCI_SUBSYS_DEVID_88XX_NIC_VF 0xA134 +#define PCI_SUBSYS_DEVID_81XX_NIC_VF 0xA234 +#define PCI_SUBSYS_DEVID_83XX_NIC_VF 0xA334 + +#define NIC_INTF_COUNT 2 /* Interfaces btw VNIC and TNS/BGX */ +#define NIC_CHANS_PER_INF 128 +#define NIC_MAX_CHANS (NIC_INTF_COUNT * NIC_CHANS_PER_INF) + +/* PCI BAR nos */ +#define PCI_CFG_REG_BAR_NUM 0 +#define PCI_MSIX_REG_BAR_NUM 4 + +/* NIC SRIOV VF count */ +#define MAX_NUM_VFS_SUPPORTED 128 +#define DEFAULT_NUM_VF_ENABLED 8 + +#define NIC_TNS_BYPASS_MODE 0 +#define NIC_TNS_MODE 1 + +/* NIC priv flags */ +#define NIC_SRIOV_ENABLED BIT(0) +#define NIC_TNS_ENABLED BIT(1) + +/* VNIC HW optimiation features */ +#define VNIC_RX_CSUM_OFFLOAD_SUPPORT +#undef VNIC_TX_CSUM_OFFLOAD_SUPPORT +#undef VNIC_SG_SUPPORT +#undef VNIC_TSO_SUPPORT +#undef VNIC_LRO_SUPPORT +#undef VNIC_RSS_SUPPORT + +/* TSO not supported in Thunder pass1 */ +#ifdef VNIC_TSO_SUPPORT +#define VNIC_SW_TSO_SUPPORT +#undef VNIC_HW_TSO_SUPPORT +#endif + +/* ETHTOOL enable or disable, undef this to disable */ +#define NICVF_ETHTOOL_ENABLE + +/* Min/Max packet size */ +#define NIC_HW_MIN_FRS 64 +#define NIC_HW_MAX_FRS 9200 /* 9216 max packet including FCS */ + +/* Max pkinds */ +#define NIC_MAX_PKIND 16 + +/* Max when CPI_ALG is IP diffserv */ +#define NIC_MAX_CPI_PER_LMAC 64 + +/* NIC VF Interrupts */ +#define NICVF_INTR_CQ 0 +#define NICVF_INTR_SQ 1 +#define NICVF_INTR_RBDR 2 +#define NICVF_INTR_PKT_DROP 3 +#define NICVF_INTR_TCP_TIMER 4 +#define NICVF_INTR_MBOX 5 +#define NICVF_INTR_QS_ERR 6 + +#define NICVF_INTR_CQ_SHIFT 0 +#define NICVF_INTR_SQ_SHIFT 8 +#define NICVF_INTR_RBDR_SHIFT 16 +#define NICVF_INTR_PKT_DROP_SHIFT 20 +#define NICVF_INTR_TCP_TIMER_SHIFT 21 +#define NICVF_INTR_MBOX_SHIFT 22 +#define NICVF_INTR_QS_ERR_SHIFT 23 + +#define NICVF_INTR_CQ_MASK (0xFF << NICVF_INTR_CQ_SHIFT) +#define NICVF_INTR_SQ_MASK (0xFF << NICVF_INTR_SQ_SHIFT) +#define NICVF_INTR_RBDR_MASK (0x03 << NICVF_INTR_RBDR_SHIFT) +#define NICVF_INTR_PKT_DROP_MASK BIT(NICVF_INTR_PKT_DROP_SHIFT) +#define NICVF_INTR_TCP_TIMER_MASK BIT(NICVF_INTR_TCP_TIMER_SHIFT) +#define NICVF_INTR_MBOX_MASK BIT(NICVF_INTR_MBOX_SHIFT) +#define NICVF_INTR_QS_ERR_MASK BIT(NICVF_INTR_QS_ERR_SHIFT) + +/* MSI-X interrupts */ +#define NIC_PF_MSIX_VECTORS 10 +#define NIC_VF_MSIX_VECTORS 20 + +#define NIC_PF_INTR_ID_ECC0_SBE 0 +#define NIC_PF_INTR_ID_ECC0_DBE 1 +#define NIC_PF_INTR_ID_ECC1_SBE 2 +#define NIC_PF_INTR_ID_ECC1_DBE 3 +#define NIC_PF_INTR_ID_ECC2_SBE 4 +#define NIC_PF_INTR_ID_ECC2_DBE 5 +#define NIC_PF_INTR_ID_ECC3_SBE 6 +#define NIC_PF_INTR_ID_ECC3_DBE 7 +#define NIC_PF_INTR_ID_MBOX0 8 +#define NIC_PF_INTR_ID_MBOX1 9 + +/* Global timer for CQ timer thresh interrupts + * Calculated for SCLK of 700Mhz + * value written should be a 1/16thof what is expected + * + * 1 tick per ms + */ +#define NICPF_CLK_PER_INT_TICK 43750 + +struct nicvf_cq_poll { + u8 cq_idx; /* Completion queue index */ +}; + +#define NIC_MAX_RSS_HASH_BITS 8 +#define NIC_MAX_RSS_IDR_TBL_SIZE BIT(NIC_MAX_RSS_HASH_BITS) +#define RSS_HASH_KEY_SIZE 5 /* 320 bit key */ + +#ifdef VNIC_RSS_SUPPORT +struct nicvf_rss_info { + bool enable; +#define RSS_L2_EXTENDED_HASH_ENA BIT(0) +#define RSS_IP_HASH_ENA BIT(1) +#define RSS_TCP_HASH_ENA BIT(2) +#define RSS_TCP_SYN_DIS BIT(3) +#define RSS_UDP_HASH_ENA BIT(4) +#define RSS_L4_EXTENDED_HASH_ENA BIT(5) +#define RSS_ROCE_ENA BIT(6) +#define RSS_L3_BI_DIRECTION_ENA BIT(7) +#define RSS_L4_BI_DIRECTION_ENA BIT(8) + u64 cfg; + u8 hash_bits; + u16 rss_size; + u8 ind_tbl[NIC_MAX_RSS_IDR_TBL_SIZE]; + u64 key[RSS_HASH_KEY_SIZE]; +}; +#endif + +enum rx_stats_reg_offset { + RX_OCTS = 0x0, + RX_UCAST = 0x1, + RX_BCAST = 0x2, + RX_MCAST = 0x3, + RX_RED = 0x4, + RX_RED_OCTS = 0x5, + RX_ORUN = 0x6, + RX_ORUN_OCTS = 0x7, + RX_FCS = 0x8, + RX_L2ERR = 0x9, + RX_DRP_BCAST = 0xa, + RX_DRP_MCAST = 0xb, + RX_DRP_L3BCAST = 0xc, + RX_DRP_L3MCAST = 0xd, + RX_STATS_ENUM_LAST, +}; + +enum tx_stats_reg_offset { + TX_OCTS = 0x0, + TX_UCAST = 0x1, + TX_BCAST = 0x2, + TX_MCAST = 0x3, + TX_DROP = 0x4, + TX_STATS_ENUM_LAST, +}; + +struct nicvf_hw_stats { + u64 rx_bytes_ok; + u64 rx_ucast_frames_ok; + u64 rx_bcast_frames_ok; + u64 rx_mcast_frames_ok; + u64 rx_fcs_errors; + u64 rx_l2_errors; + u64 rx_drop_red; + u64 rx_drop_red_bytes; + u64 rx_drop_overrun; + u64 rx_drop_overrun_bytes; + u64 rx_drop_bcast; + u64 rx_drop_mcast; + u64 rx_drop_l3_bcast; + u64 rx_drop_l3_mcast; + u64 tx_bytes_ok; + u64 tx_ucast_frames_ok; + u64 tx_bcast_frames_ok; + u64 tx_mcast_frames_ok; + u64 tx_drops; +}; + +struct nicvf_drv_stats { + /* Rx */ + u64 rx_frames_ok; + u64 rx_frames_64; + u64 rx_frames_127; + u64 rx_frames_255; + u64 rx_frames_511; + u64 rx_frames_1023; + u64 rx_frames_1518; + u64 rx_frames_jumbo; + u64 rx_drops; + /* Tx */ + u64 tx_frames_ok; + u64 tx_drops; + u64 tx_busy; + u64 tx_tso; +}; + +struct hw_info { + u8 bgx_cnt; + u8 chans_per_lmac; + u8 chans_per_bgx; /* Rx/Tx chans */ + u8 chans_per_rgx; + u8 chans_per_lbk; + u16 cpi_cnt; + u16 rssi_cnt; + u16 rss_ind_tbl_size; + u16 tl4_cnt; + u16 tl3_cnt; + u8 tl2_cnt; + u8 tl1_cnt; + bool tl1_per_bgx; /* TL1 per BGX or per LMAC */ + u8 model_id; +}; + +struct nicvf { + struct udevice *dev; + u8 vf_id; + bool sqs_mode:1; + bool loopback_supported:1; + u8 tns_mode; + u8 node; + u16 mtu; + struct queue_set *qs; +#define MAX_SQS_PER_VF_SINGLE_NODE 5 +#define MAX_SQS_PER_VF 11 + u8 num_qs; + void *addnl_qs; + u16 vf_mtu; + void __iomem *reg_base; +#define MAX_QUEUES_PER_QSET 8 + struct nicvf_cq_poll *napi[8]; + + u8 cpi_alg; + + struct nicvf_hw_stats stats; + struct nicvf_drv_stats drv_stats; + + struct nicpf *nicpf; + + /* VF <-> PF mailbox communication */ + bool pf_acked; + bool pf_nacked; + bool set_mac_pending; + + bool link_up; + u8 duplex; + u32 speed; + u8 rev_id; + u8 rx_queues; + u8 tx_queues; + + bool open; + bool rb_alloc_fail; + void *rcv_buf; + bool hw_tso; +}; + +static inline int node_id(void *addr) +{ + return ((uintptr_t)addr >> 44) & 0x3; +} + +struct nicpf { + struct udevice *udev; + struct hw_info *hw; + u8 node; + unsigned int flags; + u16 total_vf_cnt; /* Total num of VF supported */ + u16 num_vf_en; /* No of VF enabled */ + void __iomem *reg_base; /* Register start address */ + u16 rss_ind_tbl_size; + u8 num_sqs_en; /* Secondary qsets enabled */ + u64 nicvf[MAX_NUM_VFS_SUPPORTED]; + u8 vf_sqs[MAX_NUM_VFS_SUPPORTED][MAX_SQS_PER_VF]; + u8 pqs_vf[MAX_NUM_VFS_SUPPORTED]; + bool sqs_used[MAX_NUM_VFS_SUPPORTED]; + struct pkind_cfg pkind; + u8 bgx_cnt; + u8 rev_id; +#define NIC_SET_VF_LMAC_MAP(bgx, lmac) ((((bgx) & 0xF) << 4) | ((lmac) & 0xF)) +#define NIC_GET_BGX_FROM_VF_LMAC_MAP(map) (((map) >> 4) & 0xF) +#define NIC_GET_LMAC_FROM_VF_LMAC_MAP(map) ((map) & 0xF) + u8 vf_lmac_map[MAX_LMAC]; + u16 cpi_base[MAX_NUM_VFS_SUPPORTED]; + u64 mac[MAX_NUM_VFS_SUPPORTED]; + bool mbx_lock[MAX_NUM_VFS_SUPPORTED]; + u8 link[MAX_LMAC]; + u8 duplex[MAX_LMAC]; + u32 speed[MAX_LMAC]; + bool vf_enabled[MAX_NUM_VFS_SUPPORTED]; + u16 rssi_base[MAX_NUM_VFS_SUPPORTED]; + u8 lmac_cnt; +}; + +/* PF <--> VF Mailbox communication + * Eight 64bit registers are shared between PF and VF. + * Separate set for each VF. + * Writing '1' into last register mbx7 means end of message. + */ + +/* PF <--> VF mailbox communication */ +#define NIC_PF_VF_MAILBOX_SIZE 2 +#define NIC_PF_VF_MBX_TIMEOUT 2000 /* ms */ + +/* Mailbox message types */ +#define NIC_MBOX_MSG_READY 0x01 /* Is PF ready to rcv msgs */ +#define NIC_MBOX_MSG_ACK 0x02 /* ACK the message received */ +#define NIC_MBOX_MSG_NACK 0x03 /* NACK the message received */ +#define NIC_MBOX_MSG_QS_CFG 0x04 /* Configure Qset */ +#define NIC_MBOX_MSG_RQ_CFG 0x05 /* Configure receive queue */ +#define NIC_MBOX_MSG_SQ_CFG 0x06 /* Configure Send queue */ +#define NIC_MBOX_MSG_RQ_DROP_CFG 0x07 /* Configure receive queue */ +#define NIC_MBOX_MSG_SET_MAC 0x08 /* Add MAC ID to DMAC filter */ +#define NIC_MBOX_MSG_SET_MAX_FRS 0x09 /* Set max frame size */ +#define NIC_MBOX_MSG_CPI_CFG 0x0A /* Config CPI, RSSI */ +#define NIC_MBOX_MSG_RSS_SIZE 0x0B /* Get RSS indir_tbl size */ +#define NIC_MBOX_MSG_RSS_CFG 0x0C /* Config RSS table */ +#define NIC_MBOX_MSG_RSS_CFG_CONT 0x0D /* RSS config continuation */ +#define NIC_MBOX_MSG_RQ_BP_CFG 0x0E /* RQ backpressure config */ +#define NIC_MBOX_MSG_RQ_SW_SYNC 0x0F /* Flush inflight pkts to RQ */ +#define NIC_MBOX_MSG_BGX_STATS 0x10 /* Get stats from BGX */ +#define NIC_MBOX_MSG_BGX_LINK_CHANGE 0x11 /* BGX:LMAC link status */ +#define NIC_MBOX_MSG_ALLOC_SQS 0x12 /* Allocate secondary Qset */ +#define NIC_MBOX_MSG_NICVF_PTR 0x13 /* Send nicvf ptr to PF */ +#define NIC_MBOX_MSG_PNICVF_PTR 0x14 /* Get primary qset nicvf ptr */ +#define NIC_MBOX_MSG_SNICVF_PTR 0x15 /* Send sqet nicvf ptr to PVF */ +#define NIC_MBOX_MSG_LOOPBACK 0x16 /* Set interface in loopback */ +#define NIC_MBOX_MSG_CFG_DONE 0xF0 /* VF configuration done */ +#define NIC_MBOX_MSG_SHUTDOWN 0xF1 /* VF is being shutdown */ + +struct nic_cfg_msg { + u8 msg; + u8 vf_id; + u8 node_id; + bool tns_mode:1; + bool sqs_mode:1; + bool loopback_supported:1; + u8 mac_addr[6]; +}; + +/* Qset configuration */ +struct qs_cfg_msg { + u8 msg; + u8 num; + u8 sqs_count; + u64 cfg; +}; + +/* Receive queue configuration */ +struct rq_cfg_msg { + u8 msg; + u8 qs_num; + u8 rq_num; + u64 cfg; +}; + +/* Send queue configuration */ +struct sq_cfg_msg { + u8 msg; + u8 qs_num; + u8 sq_num; + bool sqs_mode; + u64 cfg; +}; + +/* Set VF's MAC address */ +struct set_mac_msg { + u8 msg; + u8 vf_id; + u8 mac_addr[6]; +}; + +/* Set Maximum frame size */ +struct set_frs_msg { + u8 msg; + u8 vf_id; + u16 max_frs; +}; + +/* Set CPI algorithm type */ +struct cpi_cfg_msg { + u8 msg; + u8 vf_id; + u8 rq_cnt; + u8 cpi_alg; +}; + +/* Get RSS table size */ +struct rss_sz_msg { + u8 msg; + u8 vf_id; + u16 ind_tbl_size; +}; + +/* Set RSS configuration */ +struct rss_cfg_msg { + u8 msg; + u8 vf_id; + u8 hash_bits; + u8 tbl_len; + u8 tbl_offset; +#define RSS_IND_TBL_LEN_PER_MBX_MSG 8 + u8 ind_tbl[RSS_IND_TBL_LEN_PER_MBX_MSG]; +}; + +struct bgx_stats_msg { + u8 msg; + u8 vf_id; + u8 rx; + u8 idx; + u64 stats; +}; + +/* Physical interface link status */ +struct bgx_link_status { + u8 msg; + u8 link_up; + u8 duplex; + u32 speed; +}; + +#ifdef VNIC_MULTI_QSET_SUPPORT +/* Get Extra Qset IDs */ +struct sqs_alloc { + u8 msg; + u8 vf_id; + u8 qs_count; +}; + +struct nicvf_ptr { + u8 msg; + u8 vf_id; + bool sqs_mode; + u8 sqs_id; + u64 nicvf; +}; +#endif + +/* Set interface in loopback mode */ +struct set_loopback { + u8 msg; + u8 vf_id; + bool enable; +}; + +/* 128 bit shared memory between PF and each VF */ +union nic_mbx { + struct { u8 msg; } msg; + struct nic_cfg_msg nic_cfg; + struct qs_cfg_msg qs; + struct rq_cfg_msg rq; + struct sq_cfg_msg sq; + struct set_mac_msg mac; + struct set_frs_msg frs; + struct cpi_cfg_msg cpi_cfg; + struct rss_sz_msg rss_size; + struct rss_cfg_msg rss_cfg; + struct bgx_stats_msg bgx_stats; + struct bgx_link_status link_status; +#ifdef VNIC_MULTI_QSET_SUPPORT + struct sqs_alloc sqs_alloc; + struct nicvf_ptr nicvf; +#endif + struct set_loopback lbk; +}; + +int nicvf_set_real_num_queues(struct udevice *dev, + int tx_queues, int rx_queues); +int nicvf_open(struct udevice *dev); +void nicvf_stop(struct udevice *dev); +int nicvf_send_msg_to_pf(struct nicvf *vf, union nic_mbx *mbx); +void nicvf_update_stats(struct nicvf *nic); + +void nic_handle_mbx_intr(struct nicpf *nic, int vf); + +int bgx_poll_for_link(int node, int bgx_idx, int lmacid); +const u8 *bgx_get_lmac_mac(int node, int bgx_idx, int lmacid); +void bgx_set_lmac_mac(int node, int bgx_idx, int lmacid, const u8 *mac); +void bgx_lmac_rx_tx_enable(int node, int bgx_idx, int lmacid, bool enable); +void bgx_lmac_internal_loopback(int node, int bgx_idx, + int lmac_idx, bool enable); + +static inline bool pass1_silicon(unsigned int revision, int model_id) +{ + return ((revision < 8) && (model_id == 0x88)); +} + +static inline bool pass2_silicon(unsigned int revision, int model_id) +{ + return ((revision >= 8) && (model_id == 0x88)); +} + +#endif /* NIC_H */ diff --git a/drivers/net/octeontx/nic_main.c b/drivers/net/octeontx/nic_main.c new file mode 100644 index 00000000000..1a805f7a468 --- /dev/null +++ b/drivers/net/octeontx/nic_main.c @@ -0,0 +1,778 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Marvell International Ltd. + */ + +#include <config.h> +#include <net.h> +#include <netdev.h> +#include <malloc.h> +#include <miiphy.h> +#include <dm.h> +#include <misc.h> +#include <pci.h> +#include <pci_ids.h> +#include <asm/io.h> +#include <linux/delay.h> + +#include "nic_reg.h" +#include "nic.h" +#include "q_struct.h" + +unsigned long rounddown_pow_of_two(unsigned long n) +{ + n |= n >> 1; + n |= n >> 2; + n |= n >> 4; + n |= n >> 8; + n |= n >> 16; + n |= n >> 32; + + return(n + 1); +} + +static void nic_config_cpi(struct nicpf *nic, struct cpi_cfg_msg *cfg); +static void nic_tx_channel_cfg(struct nicpf *nic, u8 vnic, + struct sq_cfg_msg *sq); +static int nic_update_hw_frs(struct nicpf *nic, int new_frs, int vf); +static int nic_rcv_queue_sw_sync(struct nicpf *nic); + +/* Register read/write APIs */ +static void nic_reg_write(struct nicpf *nic, u64 offset, u64 val) +{ + writeq(val, nic->reg_base + offset); +} + +static u64 nic_reg_read(struct nicpf *nic, u64 offset) +{ + return readq(nic->reg_base + offset); +} + +static u64 nic_get_mbx_addr(int vf) +{ + return NIC_PF_VF_0_127_MAILBOX_0_1 + (vf << NIC_VF_NUM_SHIFT); +} + +static void nic_send_msg_to_vf(struct nicpf *nic, int vf, union nic_mbx *mbx) +{ + void __iomem *mbx_addr = (void *)(nic->reg_base + nic_get_mbx_addr(vf)); + u64 *msg = (u64 *)mbx; + + /* In first revision HW, mbox interrupt is triggerred + * when PF writes to MBOX(1), in next revisions when + * PF writes to MBOX(0) + */ + if (pass1_silicon(nic->rev_id, nic->hw->model_id)) { + /* see the comment for nic_reg_write()/nic_reg_read() + * functions above + */ + writeq(msg[0], mbx_addr); + writeq(msg[1], mbx_addr + 8); + } else { + writeq(msg[1], mbx_addr + 8); + writeq(msg[0], mbx_addr); + } +} + +static void nic_mbx_send_ready(struct nicpf *nic, int vf) +{ + union nic_mbx mbx = {}; + int bgx_idx, lmac, timeout = 5, link = -1; + const u8 *mac; + + mbx.nic_cfg.msg = NIC_MBOX_MSG_READY; + mbx.nic_cfg.vf_id = vf; + + if (nic->flags & NIC_TNS_ENABLED) + mbx.nic_cfg.tns_mode = NIC_TNS_MODE; + else + mbx.nic_cfg.tns_mode = NIC_TNS_BYPASS_MODE; + + if (vf < nic->num_vf_en) { + bgx_idx = NIC_GET_BGX_FROM_VF_LMAC_MAP(nic->vf_lmac_map[vf]); + lmac = NIC_GET_LMAC_FROM_VF_LMAC_MAP(nic->vf_lmac_map[vf]); + + mac = bgx_get_lmac_mac(nic->node, bgx_idx, lmac); + if (mac) + memcpy((u8 *)&mbx.nic_cfg.mac_addr, mac, 6); + + while (timeout-- && (link <= 0)) { + link = bgx_poll_for_link(nic->node, bgx_idx, lmac); + debug("Link status: %d\n", link); + if (link <= 0) + mdelay(2000); + } + } +#ifdef VNIC_MULTI_QSET_SUPPORT + mbx.nic_cfg.sqs_mode = (vf >= nic->num_vf_en) ? true : false; +#endif + mbx.nic_cfg.node_id = nic->node; + + mbx.nic_cfg.loopback_supported = vf < nic->num_vf_en; + + nic_send_msg_to_vf(nic, vf, &mbx); +} + +/* ACKs VF's mailbox message + * @vf: VF to which ACK to be sent + */ +static void nic_mbx_send_ack(struct nicpf *nic, int vf) +{ + union nic_mbx mbx = {}; + + mbx.msg.msg = NIC_MBOX_MSG_ACK; + nic_send_msg_to_vf(nic, vf, &mbx); +} + +/* NACKs VF's mailbox message that PF is not able to + * complete the action + * @vf: VF to which ACK to be sent + */ +static void nic_mbx_send_nack(struct nicpf *nic, int vf) +{ + union nic_mbx mbx = {}; + + mbx.msg.msg = NIC_MBOX_MSG_NACK; + nic_send_msg_to_vf(nic, vf, &mbx); +} + +static int nic_config_loopback(struct nicpf *nic, struct set_loopback *lbk) +{ + int bgx_idx, lmac_idx; + + if (lbk->vf_id > nic->num_vf_en) + return -1; + + bgx_idx = NIC_GET_BGX_FROM_VF_LMAC_MAP(nic->vf_lmac_map[lbk->vf_id]); + lmac_idx = NIC_GET_LMAC_FROM_VF_LMAC_MAP(nic->vf_lmac_map[lbk->vf_id]); + + bgx_lmac_internal_loopback(nic->node, bgx_idx, lmac_idx, lbk->enable); + + return 0; +} + +/* Interrupt handler to handle mailbox messages from VFs */ +void nic_handle_mbx_intr(struct nicpf *nic, int vf) +{ + union nic_mbx mbx = {}; + u64 *mbx_data; + u64 mbx_addr; + u64 reg_addr; + u64 cfg; + int bgx, lmac; + int i; + int ret = 0; + + nic->mbx_lock[vf] = true; + + mbx_addr = nic_get_mbx_addr(vf); + mbx_data = (u64 *)&mbx; + + for (i = 0; i < NIC_PF_VF_MAILBOX_SIZE; i++) { + *mbx_data = nic_reg_read(nic, mbx_addr); + mbx_data++; + mbx_addr += sizeof(u64); + } + + debug("%s: Mailbox msg %d from VF%d\n", __func__, mbx.msg.msg, vf); + switch (mbx.msg.msg) { + case NIC_MBOX_MSG_READY: + nic_mbx_send_ready(nic, vf); + if (vf < nic->num_vf_en) { + nic->link[vf] = 0; + nic->duplex[vf] = 0; + nic->speed[vf] = 0; + } + ret = 1; + break; + case NIC_MBOX_MSG_QS_CFG: + reg_addr = NIC_PF_QSET_0_127_CFG | + (mbx.qs.num << NIC_QS_ID_SHIFT); + cfg = mbx.qs.cfg; +#ifdef VNIC_MULTI_QSET_SUPPORT + /* Check if its a secondary Qset */ + if (vf >= nic->num_vf_en) { + cfg = cfg & (~0x7FULL); + /* Assign this Qset to primary Qset's VF */ + cfg |= nic->pqs_vf[vf]; + } +#endif + nic_reg_write(nic, reg_addr, cfg); + break; + case NIC_MBOX_MSG_RQ_CFG: + reg_addr = NIC_PF_QSET_0_127_RQ_0_7_CFG | + (mbx.rq.qs_num << NIC_QS_ID_SHIFT) | + (mbx.rq.rq_num << NIC_Q_NUM_SHIFT); + nic_reg_write(nic, reg_addr, mbx.rq.cfg); + /* Enable CQE_RX2_S extension in CQE_RX descriptor. + * This gets appended by default on 81xx/83xx chips, + * for consistency enabling the same on 88xx pass2 + * where this is introduced. + */ + if (pass2_silicon(nic->rev_id, nic->hw->model_id)) + nic_reg_write(nic, NIC_PF_RX_CFG, 0x01); + break; + case NIC_MBOX_MSG_RQ_BP_CFG: + reg_addr = NIC_PF_QSET_0_127_RQ_0_7_BP_CFG | + (mbx.rq.qs_num << NIC_QS_ID_SHIFT) | + (mbx.rq.rq_num << NIC_Q_NUM_SHIFT); + nic_reg_write(nic, reg_addr, mbx.rq.cfg); + break; + case NIC_MBOX_MSG_RQ_SW_SYNC: + ret = nic_rcv_queue_sw_sync(nic); + break; + case NIC_MBOX_MSG_RQ_DROP_CFG: + reg_addr = NIC_PF_QSET_0_127_RQ_0_7_DROP_CFG | + (mbx.rq.qs_num << NIC_QS_ID_SHIFT) | + (mbx.rq.rq_num << NIC_Q_NUM_SHIFT); + nic_reg_write(nic, reg_addr, mbx.rq.cfg); + break; + case NIC_MBOX_MSG_SQ_CFG: + reg_addr = NIC_PF_QSET_0_127_SQ_0_7_CFG | + (mbx.sq.qs_num << NIC_QS_ID_SHIFT) | + (mbx.sq.sq_num << NIC_Q_NUM_SHIFT); + nic_reg_write(nic, reg_addr, mbx.sq.cfg); + nic_tx_channel_cfg(nic, mbx.qs.num, + (struct sq_cfg_msg *)&mbx.sq); + break; + case NIC_MBOX_MSG_SET_MAC: +#ifdef VNIC_MULTI_QSET_SUPPORT + if (vf >= nic->num_vf_en) + break; +#endif + lmac = mbx.mac.vf_id; + bgx = NIC_GET_BGX_FROM_VF_LMAC_MAP(nic->vf_lmac_map[lmac]); + lmac = NIC_GET_LMAC_FROM_VF_LMAC_MAP(nic->vf_lmac_map[lmac]); + bgx_set_lmac_mac(nic->node, bgx, lmac, mbx.mac.mac_addr); + break; + case NIC_MBOX_MSG_SET_MAX_FRS: + ret = nic_update_hw_frs(nic, mbx.frs.max_frs, + mbx.frs.vf_id); + break; + case NIC_MBOX_MSG_CPI_CFG: + nic_config_cpi(nic, &mbx.cpi_cfg); + break; +#ifdef VNIC_RSS_SUPPORT + case NIC_MBOX_MSG_RSS_SIZE: + nic_send_rss_size(nic, vf); + goto unlock; + case NIC_MBOX_MSG_RSS_CFG: + case NIC_MBOX_MSG_RSS_CFG_CONT: + nic_config_rss(nic, &mbx.rss_cfg); + break; +#endif + case NIC_MBOX_MSG_CFG_DONE: + /* Last message of VF config msg sequence */ + nic->vf_enabled[vf] = true; + if (vf >= nic->lmac_cnt) + goto unlock; + + bgx = NIC_GET_BGX_FROM_VF_LMAC_MAP(nic->vf_lmac_map[vf]); + lmac = NIC_GET_LMAC_FROM_VF_LMAC_MAP(nic->vf_lmac_map[vf]); + + bgx_lmac_rx_tx_enable(nic->node, bgx, lmac, true); + goto unlock; + case NIC_MBOX_MSG_SHUTDOWN: + /* First msg in VF teardown sequence */ + nic->vf_enabled[vf] = false; +#ifdef VNIC_MULTI_QSET_SUPPORT + if (vf >= nic->num_vf_en) + nic->sqs_used[vf - nic->num_vf_en] = false; + nic->pqs_vf[vf] = 0; +#endif + if (vf >= nic->lmac_cnt) + break; + + bgx = NIC_GET_BGX_FROM_VF_LMAC_MAP(nic->vf_lmac_map[vf]); + lmac = NIC_GET_LMAC_FROM_VF_LMAC_MAP(nic->vf_lmac_map[vf]); + + bgx_lmac_rx_tx_enable(nic->node, bgx, lmac, false); + break; +#ifdef VNIC_MULTI_QSET_SUPPORT + case NIC_MBOX_MSG_ALLOC_SQS: + nic_alloc_sqs(nic, &mbx.sqs_alloc); + goto unlock; + case NIC_MBOX_MSG_NICVF_PTR: + nic->nicvf[vf] = mbx.nicvf.nicvf; + break; + case NIC_MBOX_MSG_PNICVF_PTR: + nic_send_pnicvf(nic, vf); + goto unlock; + case NIC_MBOX_MSG_SNICVF_PTR: + nic_send_snicvf(nic, &mbx.nicvf); + goto unlock; +#endif + case NIC_MBOX_MSG_LOOPBACK: + ret = nic_config_loopback(nic, &mbx.lbk); + break; + default: + printf("Invalid msg from VF%d, msg 0x%x\n", vf, mbx.msg.msg); + break; + } + + if (!ret) + nic_mbx_send_ack(nic, vf); + else if (mbx.msg.msg != NIC_MBOX_MSG_READY) + nic_mbx_send_nack(nic, vf); +unlock: + nic->mbx_lock[vf] = false; +} + +static int nic_rcv_queue_sw_sync(struct nicpf *nic) +{ + int timeout = 20; + + nic_reg_write(nic, NIC_PF_SW_SYNC_RX, 0x01); + while (timeout) { + if (nic_reg_read(nic, NIC_PF_SW_SYNC_RX_DONE) & 0x1) + break; + udelay(2000); + timeout--; + } + nic_reg_write(nic, NIC_PF_SW_SYNC_RX, 0x00); + if (!timeout) { + printf("Recevie queue software sync failed"); + return 1; + } + return 0; +} + +static int nic_update_hw_frs(struct nicpf *nic, int new_frs, int vf) +{ + u64 *pkind = (u64 *)&nic->pkind; + + if (new_frs > NIC_HW_MAX_FRS || new_frs < NIC_HW_MIN_FRS) { + printf("Invalid MTU setting from VF%d rejected,", vf); + printf(" should be between %d and %d\n", NIC_HW_MIN_FRS, + NIC_HW_MAX_FRS); + return 1; + } + new_frs += ETH_HLEN; + if (new_frs <= nic->pkind.maxlen) + return 0; + + nic->pkind.maxlen = new_frs; + + nic_reg_write(nic, NIC_PF_PKIND_0_15_CFG, *pkind); + return 0; +} + +/* Set minimum transmit packet size */ +static void nic_set_tx_pkt_pad(struct nicpf *nic, int size) +{ + int lmac; + u64 lmac_cfg; + struct hw_info *hw = nic->hw; + int max_lmac = nic->hw->bgx_cnt * MAX_LMAC_PER_BGX; + + /* Max value that can be set is 60 */ + if (size > 52) + size = 52; + + /* CN81XX has RGX configured as FAKE BGX, adjust mac_lmac accordingly */ + if (hw->chans_per_rgx) + max_lmac = ((nic->hw->bgx_cnt - 1) * MAX_LMAC_PER_BGX) + 1; + + for (lmac = 0; lmac < max_lmac; lmac++) { + lmac_cfg = nic_reg_read(nic, NIC_PF_LMAC_0_7_CFG | (lmac << 3)); + lmac_cfg &= ~(0xF << 2); + lmac_cfg |= ((size / 4) << 2); + nic_reg_write(nic, NIC_PF_LMAC_0_7_CFG | (lmac << 3), lmac_cfg); + } +} + +/* Function to check number of LMACs present and set VF to LMAC mapping. + * Mapping will be used while initializing channels. + */ +static void nic_set_lmac_vf_mapping(struct nicpf *nic) +{ + int bgx, bgx_count, next_bgx_lmac = 0; + int lmac, lmac_cnt = 0; + u64 lmac_credit; + + nic->num_vf_en = 0; + if (nic->flags & NIC_TNS_ENABLED) { + nic->num_vf_en = DEFAULT_NUM_VF_ENABLED; + return; + } + + bgx_get_count(nic->node, &bgx_count); + debug("bgx_count: %d\n", bgx_count); + + for (bgx = 0; bgx < nic->hw->bgx_cnt; bgx++) { + if (!(bgx_count & (1 << bgx))) + continue; + nic->bgx_cnt++; + lmac_cnt = bgx_get_lmac_count(nic->node, bgx); + debug("lmac_cnt: %d for BGX%d\n", lmac_cnt, bgx); + for (lmac = 0; lmac < lmac_cnt; lmac++) + nic->vf_lmac_map[next_bgx_lmac++] = + NIC_SET_VF_LMAC_MAP(bgx, lmac); + nic->num_vf_en += lmac_cnt; + + /* Program LMAC credits */ + lmac_credit = (1ull << 1); /* chennel credit enable */ + lmac_credit |= (0x1ff << 2); + lmac_credit |= (((((48 * 1024) / lmac_cnt) - + NIC_HW_MAX_FRS) / 16) << 12); + lmac = bgx * MAX_LMAC_PER_BGX; + for (; lmac < lmac_cnt + (bgx * MAX_LMAC_PER_BGX); lmac++) + nic_reg_write(nic, NIC_PF_LMAC_0_7_CREDIT + (lmac * 8), + lmac_credit); + } +} + +static void nic_get_hw_info(struct nicpf *nic) +{ + u16 sdevid; + struct hw_info *hw = nic->hw; + + dm_pci_read_config16(nic->udev, PCI_SUBSYSTEM_ID, &sdevid); + + switch (sdevid) { + case PCI_SUBSYS_DEVID_88XX_NIC_PF: + hw->bgx_cnt = MAX_BGX_PER_NODE; + hw->chans_per_lmac = 16; + hw->chans_per_bgx = 128; + hw->cpi_cnt = 2048; + hw->rssi_cnt = 4096; + hw->rss_ind_tbl_size = NIC_MAX_RSS_IDR_TBL_SIZE; + hw->tl3_cnt = 256; + hw->tl2_cnt = 64; + hw->tl1_cnt = 2; + hw->tl1_per_bgx = true; + hw->model_id = 0x88; + break; + case PCI_SUBSYS_DEVID_81XX_NIC_PF: + hw->bgx_cnt = MAX_BGX_PER_NODE; + hw->chans_per_lmac = 8; + hw->chans_per_bgx = 32; + hw->chans_per_rgx = 8; + hw->chans_per_lbk = 24; + hw->cpi_cnt = 512; + hw->rssi_cnt = 256; + hw->rss_ind_tbl_size = 32; /* Max RSSI / Max interfaces */ + hw->tl3_cnt = 64; + hw->tl2_cnt = 16; + hw->tl1_cnt = 10; + hw->tl1_per_bgx = false; + hw->model_id = 0x81; + break; + case PCI_SUBSYS_DEVID_83XX_NIC_PF: + hw->bgx_cnt = MAX_BGX_PER_NODE; + hw->chans_per_lmac = 8; + hw->chans_per_bgx = 32; + hw->chans_per_lbk = 64; + hw->cpi_cnt = 2048; + hw->rssi_cnt = 1024; + hw->rss_ind_tbl_size = 64; /* Max RSSI / Max interfaces */ + hw->tl3_cnt = 256; + hw->tl2_cnt = 64; + hw->tl1_cnt = 18; + hw->tl1_per_bgx = false; + hw->model_id = 0x83; + break; + } + + hw->tl4_cnt = MAX_QUEUES_PER_QSET * pci_sriov_get_totalvfs(nic->udev); +} + +static void nic_init_hw(struct nicpf *nic) +{ + int i; + u64 reg; + u64 *pkind = (u64 *)&nic->pkind; + + /* Get HW capability info */ + nic_get_hw_info(nic); + + /* Enable NIC HW block */ + nic_reg_write(nic, NIC_PF_CFG, 0x3); + + /* Enable backpressure */ + nic_reg_write(nic, NIC_PF_BP_CFG, (1ULL << 6) | 0x03); + nic_reg_write(nic, NIC_PF_INTF_0_1_BP_CFG, (1ULL << 63) | 0x08); + nic_reg_write(nic, NIC_PF_INTF_0_1_BP_CFG + (1 << 8), + (1ULL << 63) | 0x09); + + for (i = 0; i < NIC_MAX_CHANS; i++) + nic_reg_write(nic, NIC_PF_CHAN_0_255_TX_CFG | (i << 3), 1); + + if (nic->flags & NIC_TNS_ENABLED) { + reg = NIC_TNS_MODE << 7; + reg |= 0x06; + nic_reg_write(nic, NIC_PF_INTF_0_1_SEND_CFG, reg); + reg &= ~0xFull; + reg |= 0x07; + nic_reg_write(nic, NIC_PF_INTF_0_1_SEND_CFG | (1 << 8), reg); + } else { + /* Disable TNS mode on both interfaces */ + reg = NIC_TNS_BYPASS_MODE << 7; + reg |= 0x08; /* Block identifier */ + nic_reg_write(nic, NIC_PF_INTF_0_1_SEND_CFG, reg); + reg &= ~0xFull; + reg |= 0x09; + nic_reg_write(nic, NIC_PF_INTF_0_1_SEND_CFG | (1 << 8), reg); + } + + /* PKIND configuration */ + nic->pkind.minlen = 0; + nic->pkind.maxlen = NIC_HW_MAX_FRS + ETH_HLEN; + nic->pkind.lenerr_en = 1; + nic->pkind.rx_hdr = 0; + nic->pkind.hdr_sl = 0; + + for (i = 0; i < NIC_MAX_PKIND; i++) + nic_reg_write(nic, NIC_PF_PKIND_0_15_CFG | (i << 3), *pkind); + + nic_set_tx_pkt_pad(nic, NIC_HW_MIN_FRS); + + /* Timer config */ + nic_reg_write(nic, NIC_PF_INTR_TIMER_CFG, NICPF_CLK_PER_INT_TICK); +} + +/* Channel parse index configuration */ +static void nic_config_cpi(struct nicpf *nic, struct cpi_cfg_msg *cfg) +{ + struct hw_info *hw = nic->hw; + u32 vnic, bgx, lmac, chan; + u32 padd, cpi_count = 0; + u64 cpi_base, cpi, rssi_base, rssi; + u8 qset, rq_idx = 0; + + vnic = cfg->vf_id; + bgx = NIC_GET_BGX_FROM_VF_LMAC_MAP(nic->vf_lmac_map[vnic]); + lmac = NIC_GET_LMAC_FROM_VF_LMAC_MAP(nic->vf_lmac_map[vnic]); + + chan = (lmac * hw->chans_per_lmac) + (bgx * hw->chans_per_bgx); + cpi_base = vnic * NIC_MAX_CPI_PER_LMAC; + rssi_base = vnic * hw->rss_ind_tbl_size; + + /* Rx channel configuration */ + nic_reg_write(nic, NIC_PF_CHAN_0_255_RX_BP_CFG | (chan << 3), + (1ull << 63) | (vnic << 0)); + nic_reg_write(nic, NIC_PF_CHAN_0_255_RX_CFG | (chan << 3), + ((u64)cfg->cpi_alg << 62) | (cpi_base << 48)); + + if (cfg->cpi_alg == CPI_ALG_NONE) + cpi_count = 1; + else if (cfg->cpi_alg == CPI_ALG_VLAN) /* 3 bits of PCP */ + cpi_count = 8; + else if (cfg->cpi_alg == CPI_ALG_VLAN16) /* 3 bits PCP + DEI */ + cpi_count = 16; + else if (cfg->cpi_alg == CPI_ALG_DIFF) /* 6bits DSCP */ + cpi_count = NIC_MAX_CPI_PER_LMAC; + + /* RSS Qset, Qidx mapping */ + qset = cfg->vf_id; + rssi = rssi_base; + for (; rssi < (rssi_base + cfg->rq_cnt); rssi++) { + nic_reg_write(nic, NIC_PF_RSSI_0_4097_RQ | (rssi << 3), + (qset << 3) | rq_idx); + rq_idx++; + } + + rssi = 0; + cpi = cpi_base; + for (; cpi < (cpi_base + cpi_count); cpi++) { + /* Determine port to channel adder */ + if (cfg->cpi_alg != CPI_ALG_DIFF) + padd = cpi % cpi_count; + else + padd = cpi % 8; /* 3 bits CS out of 6bits DSCP */ + + /* Leave RSS_SIZE as '0' to disable RSS */ + if (pass1_silicon(nic->rev_id, nic->hw->model_id)) { + nic_reg_write(nic, NIC_PF_CPI_0_2047_CFG | (cpi << 3), + (vnic << 24) | (padd << 16) | + (rssi_base + rssi)); + } else { + /* Set MPI_ALG to '0' to disable MCAM parsing */ + nic_reg_write(nic, NIC_PF_CPI_0_2047_CFG | (cpi << 3), + (padd << 16)); + /* MPI index is same as CPI if MPI_ALG is not enabled */ + nic_reg_write(nic, NIC_PF_MPI_0_2047_CFG | (cpi << 3), + (vnic << 24) | (rssi_base + rssi)); + } + + if ((rssi + 1) >= cfg->rq_cnt) + continue; + + if (cfg->cpi_alg == CPI_ALG_VLAN) + rssi++; + else if (cfg->cpi_alg == CPI_ALG_VLAN16) + rssi = ((cpi - cpi_base) & 0xe) >> 1; + else if (cfg->cpi_alg == CPI_ALG_DIFF) + rssi = ((cpi - cpi_base) & 0x38) >> 3; + } + nic->cpi_base[cfg->vf_id] = cpi_base; + nic->rssi_base[cfg->vf_id] = rssi_base; +} + +/* Transmit channel configuration (TL4 -> TL3 -> Chan) + * VNIC0-SQ0 -> TL4(0) -> TL4A(0) -> TL3[0] -> BGX0/LMAC0/Chan0 + * VNIC1-SQ0 -> TL4(8) -> TL4A(2) -> TL3[2] -> BGX0/LMAC1/Chan0 + * VNIC2-SQ0 -> TL4(16) -> TL4A(4) -> TL3[4] -> BGX0/LMAC2/Chan0 + * VNIC3-SQ0 -> TL4(32) -> TL4A(6) -> TL3[6] -> BGX0/LMAC3/Chan0 + * VNIC4-SQ0 -> TL4(512) -> TL4A(128) -> TL3[128] -> BGX1/LMAC0/Chan0 + * VNIC5-SQ0 -> TL4(520) -> TL4A(130) -> TL3[130] -> BGX1/LMAC1/Chan0 + * VNIC6-SQ0 -> TL4(528) -> TL4A(132) -> TL3[132] -> BGX1/LMAC2/Chan0 + * VNIC7-SQ0 -> TL4(536) -> TL4A(134) -> TL3[134] -> BGX1/LMAC3/Chan0 + */ +static void nic_tx_channel_cfg(struct nicpf *nic, u8 vnic, + struct sq_cfg_msg *sq) +{ + struct hw_info *hw = nic->hw; + u32 bgx, lmac, chan; + u32 tl2, tl3, tl4; + u32 rr_quantum; + u8 sq_idx = sq->sq_num; + u8 pqs_vnic = vnic; + int svf; + u16 sdevid; + + dm_pci_read_config16(nic->udev, PCI_SUBSYSTEM_ID, &sdevid); + + bgx = NIC_GET_BGX_FROM_VF_LMAC_MAP(nic->vf_lmac_map[pqs_vnic]); + lmac = NIC_GET_LMAC_FROM_VF_LMAC_MAP(nic->vf_lmac_map[pqs_vnic]); + + /* 24 bytes for FCS, IPG and preamble */ + rr_quantum = ((NIC_HW_MAX_FRS + 24) / 4); + + /* For 88xx 0-511 TL4 transmits via BGX0 and + * 512-1023 TL4s transmit via BGX1. + */ + if (hw->tl1_per_bgx) { + tl4 = bgx * (hw->tl4_cnt / hw->bgx_cnt); + if (!sq->sqs_mode) { + tl4 += (lmac * MAX_QUEUES_PER_QSET); + } else { + for (svf = 0; svf < MAX_SQS_PER_VF_SINGLE_NODE; svf++) { + if (nic->vf_sqs[pqs_vnic][svf] == vnic) + break; + } + tl4 += (MAX_LMAC_PER_BGX * MAX_QUEUES_PER_QSET); + tl4 += (lmac * MAX_QUEUES_PER_QSET * + MAX_SQS_PER_VF_SINGLE_NODE); + tl4 += (svf * MAX_QUEUES_PER_QSET); + } + } else { + tl4 = (vnic * MAX_QUEUES_PER_QSET); + } + + tl4 += sq_idx; + + tl3 = tl4 / (hw->tl4_cnt / hw->tl3_cnt); + nic_reg_write(nic, NIC_PF_QSET_0_127_SQ_0_7_CFG2 | + ((u64)vnic << NIC_QS_ID_SHIFT) | + ((u32)sq_idx << NIC_Q_NUM_SHIFT), tl4); + nic_reg_write(nic, NIC_PF_TL4_0_1023_CFG | (tl4 << 3), + ((u64)vnic << 27) | ((u32)sq_idx << 24) | rr_quantum); + + nic_reg_write(nic, NIC_PF_TL3_0_255_CFG | (tl3 << 3), rr_quantum); + + /* On 88xx 0-127 channels are for BGX0 and + * 127-255 channels for BGX1. + * + * On 81xx/83xx TL3_CHAN reg should be configured with channel + * within LMAC i.e 0-7 and not the actual channel number like on 88xx + */ + chan = (lmac * hw->chans_per_lmac) + (bgx * hw->chans_per_bgx); + if (hw->tl1_per_bgx) + nic_reg_write(nic, NIC_PF_TL3_0_255_CHAN | (tl3 << 3), chan); + else + nic_reg_write(nic, NIC_PF_TL3_0_255_CHAN | (tl3 << 3), 0); + + /* Enable backpressure on the channel */ + nic_reg_write(nic, NIC_PF_CHAN_0_255_TX_CFG | (chan << 3), 1); + + tl2 = tl3 >> 2; + nic_reg_write(nic, NIC_PF_TL3A_0_63_CFG | (tl2 << 3), tl2); + nic_reg_write(nic, NIC_PF_TL2_0_63_CFG | (tl2 << 3), rr_quantum); + /* No priorities as of now */ + nic_reg_write(nic, NIC_PF_TL2_0_63_PRI | (tl2 << 3), 0x00); + + /* Unlike 88xx where TL2s 0-31 transmits to TL1 '0' and rest to TL1 '1' + * on 81xx/83xx TL2 needs to be configured to transmit to one of the + * possible LMACs. + * + * This register doesn't exist on 88xx. + */ + if (!hw->tl1_per_bgx) + nic_reg_write(nic, NIC_PF_TL2_LMAC | (tl2 << 3), + lmac + (bgx * MAX_LMAC_PER_BGX)); +} + +int nic_initialize(struct udevice *dev) +{ + struct nicpf *nic = dev_get_priv(dev); + + nic->udev = dev; + nic->hw = calloc(1, sizeof(struct hw_info)); + if (!nic->hw) + return -ENOMEM; + + /* MAP PF's configuration registers */ + nic->reg_base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0, + PCI_REGION_MEM); + if (!nic->reg_base) { + printf("Cannot map config register space, aborting\n"); + goto exit; + } + + nic->node = node_id(nic->reg_base); + dm_pci_read_config8(dev, PCI_REVISION_ID, &nic->rev_id); + + /* By default set NIC in TNS bypass mode */ + nic->flags &= ~NIC_TNS_ENABLED; + + /* Initialize hardware */ + nic_init_hw(nic); + + nic_set_lmac_vf_mapping(nic); + + /* Set RSS TBL size for each VF */ + nic->rss_ind_tbl_size = NIC_MAX_RSS_IDR_TBL_SIZE; + + nic->rss_ind_tbl_size = rounddown_pow_of_two(nic->rss_ind_tbl_size); + + return 0; +exit: + free(nic->hw); + return -ENODEV; +} + +int octeontx_nic_probe(struct udevice *dev) +{ + int ret = 0; + struct nicpf *nicpf = dev_get_priv(dev); + + nicpf->udev = dev; + ret = nic_initialize(dev); + if (ret < 0) { + printf("couldn't initialize NIC PF\n"); + return ret; + } + + ret = pci_sriov_init(dev, nicpf->num_vf_en); + if (ret < 0) + printf("enabling SRIOV failed for num VFs %d\n", + nicpf->num_vf_en); + + return ret; +} + +U_BOOT_DRIVER(octeontx_nic) = { + .name = "octeontx_nic", + .id = UCLASS_MISC, + .probe = octeontx_nic_probe, + .priv_auto_alloc_size = sizeof(struct nicpf), +}; + +static struct pci_device_id octeontx_nic_supported[] = { + { PCI_VDEVICE(CAVIUM, PCI_DEVICE_ID_CAVIUM_NIC) }, + {} +}; + +U_BOOT_PCI_DEVICE(octeontx_nic, octeontx_nic_supported); + diff --git a/drivers/net/octeontx/nic_reg.h b/drivers/net/octeontx/nic_reg.h new file mode 100644 index 00000000000..c214ebb6799 --- /dev/null +++ b/drivers/net/octeontx/nic_reg.h @@ -0,0 +1,250 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (C) 2018 Marvell International Ltd. + */ + +#ifndef NIC_REG_H +#define NIC_REG_H + +#define NIC_PF_REG_COUNT 29573 +#define NIC_VF_REG_COUNT 249 + +/* Physical function register offsets */ +#define NIC_PF_CFG (0x0000) +#define NIC_PF_STATUS (0x0010) + +#define NIC_PF_INTR_TIMER_CFG (0x0030) +#define NIC_PF_BIST_STATUS (0x0040) +#define NIC_PF_SOFT_RESET (0x0050) + +#define NIC_PF_TCP_TIMER (0x0060) +#define NIC_PF_BP_CFG (0x0080) +#define NIC_PF_RRM_CFG (0x0088) +#define NIC_PF_CQM_CF (0x00A0) +#define NIC_PF_CNM_CF (0x00A8) +#define NIC_PF_CNM_STATUS (0x00B0) +#define NIC_PF_CQ_AVG_CFG (0x00C0) +#define NIC_PF_RRM_AVG_CFG (0x00C8) + +#define NIC_PF_INTF_0_1_SEND_CFG (0x0200) +#define NIC_PF_INTF_0_1_BP_CFG (0x0208) +#define NIC_PF_INTF_0_1_BP_DIS_0_1 (0x0210) +#define NIC_PF_INTF_0_1_BP_SW_0_1 (0x0220) +#define NIC_PF_RBDR_BP_STATE_0_3 (0x0240) + +#define NIC_PF_MAILBOX_INT (0x0410) +#define NIC_PF_MAILBOX_INT_W1S (0x0430) +#define NIC_PF_MAILBOX_ENA_W1C (0x0450) +#define NIC_PF_MAILBOX_ENA_W1S (0x0470) + +#define NIC_PF_RX_ETYPE_0_7 (0x0500) +#define NIC_PF_RX_CFG (0x05D0) +#define NIC_PF_PKIND_0_15_CFG (0x0600) + +#define NIC_PF_ECC0_FLIP0 (0x1000) +#define NIC_PF_ECC1_FLIP0 (0x1008) +#define NIC_PF_ECC2_FLIP0 (0x1010) +#define NIC_PF_ECC3_FLIP0 (0x1018) +#define NIC_PF_ECC0_FLIP1 (0x1080) +#define NIC_PF_ECC1_FLIP1 (0x1088) +#define NIC_PF_ECC2_FLIP1 (0x1090) +#define NIC_PF_ECC3_FLIP1 (0x1098) +#define NIC_PF_ECC0_CDIS (0x1100) +#define NIC_PF_ECC1_CDIS (0x1108) +#define NIC_PF_ECC2_CDIS (0x1110) +#define NIC_PF_ECC3_CDIS (0x1118) +#define NIC_PF_BIST0_STATUS (0x1280) +#define NIC_PF_BIST1_STATUS (0x1288) +#define NIC_PF_BIST2_STATUS (0x1290) +#define NIC_PF_BIST3_STATUS (0x1298) + +#define NIC_PF_ECC0_SBE_INT (0x2000) +#define NIC_PF_ECC0_SBE_INT_W1S (0x2008) +#define NIC_PF_ECC0_SBE_ENA_W1C (0x2010) +#define NIC_PF_ECC0_SBE_ENA_W1S (0x2018) +#define NIC_PF_ECC0_DBE_INT (0x2100) +#define NIC_PF_ECC0_DBE_INT_W1S (0x2108) +#define NIC_PF_ECC0_DBE_ENA_W1C (0x2110) +#define NIC_PF_ECC0_DBE_ENA_W1S (0x2118) + +#define NIC_PF_ECC1_SBE_INT (0x2200) +#define NIC_PF_ECC1_SBE_INT_W1S (0x2208) +#define NIC_PF_ECC1_SBE_ENA_W1C (0x2210) +#define NIC_PF_ECC1_SBE_ENA_W1S (0x2218) +#define NIC_PF_ECC1_DBE_INT (0x2300) +#define NIC_PF_ECC1_DBE_INT_W1S (0x2308) +#define NIC_PF_ECC1_DBE_ENA_W1C (0x2310) +#define NIC_PF_ECC1_DBE_ENA_W1S (0x2318) + +#define NIC_PF_ECC2_SBE_INT (0x2400) +#define NIC_PF_ECC2_SBE_INT_W1S (0x2408) +#define NIC_PF_ECC2_SBE_ENA_W1C (0x2410) +#define NIC_PF_ECC2_SBE_ENA_W1S (0x2418) +#define NIC_PF_ECC2_DBE_INT (0x2500) +#define NIC_PF_ECC2_DBE_INT_W1S (0x2508) +#define NIC_PF_ECC2_DBE_ENA_W1C (0x2510) +#define NIC_PF_ECC2_DBE_ENA_W1S (0x2518) + +#define NIC_PF_ECC3_SBE_INT (0x2600) +#define NIC_PF_ECC3_SBE_INT_W1S (0x2608) +#define NIC_PF_ECC3_SBE_ENA_W1C (0x2610) +#define NIC_PF_ECC3_SBE_ENA_W1S (0x2618) +#define NIC_PF_ECC3_DBE_INT (0x2700) +#define NIC_PF_ECC3_DBE_INT_W1S (0x2708) +#define NIC_PF_ECC3_DBE_ENA_W1C (0x2710) +#define NIC_PF_ECC3_DBE_ENA_W1S (0x2718) + +#define NIC_PF_CPI_0_2047_CFG (0x200000) +#define NIC_PF_MPI_0_2047_CFG (0x210000) +#define NIC_PF_RSSI_0_4097_RQ (0x220000) +#define NIC_PF_LMAC_0_7_CFG (0x240000) +#define NIC_PF_LMAC_0_7_SW_XOFF (0x242000) +#define NIC_PF_LMAC_0_7_CREDIT (0x244000) +#define NIC_PF_CHAN_0_255_TX_CFG (0x400000) +#define NIC_PF_CHAN_0_255_RX_CFG (0x420000) +#define NIC_PF_CHAN_0_255_SW_XOFF (0x440000) +#define NIC_PF_CHAN_0_255_CREDIT (0x460000) +#define NIC_PF_CHAN_0_255_RX_BP_CFG (0x480000) + +#define NIC_PF_SW_SYNC_RX (0x490000) + +#define NIC_PF_SW_SYNC_RX_DONE (0x490008) +#define NIC_PF_TL2_0_63_CFG (0x500000) +#define NIC_PF_TL2_0_63_PRI (0x520000) +#define NIC_PF_TL2_LMAC (0x540000) +#define NIC_PF_TL2_0_63_SH_STATUS (0x580000) +#define NIC_PF_TL3A_0_63_CFG (0x5F0000) +#define NIC_PF_TL3_0_255_CFG (0x600000) +#define NIC_PF_TL3_0_255_CHAN (0x620000) +#define NIC_PF_TL3_0_255_PIR (0x640000) +#define NIC_PF_TL3_0_255_SW_XOFF (0x660000) +#define NIC_PF_TL3_0_255_CNM_RATE (0x680000) +#define NIC_PF_TL3_0_255_SH_STATUS (0x6A0000) +#define NIC_PF_TL4A_0_255_CFG (0x6F0000) +#define NIC_PF_TL4_0_1023_CFG (0x800000) +#define NIC_PF_TL4_0_1023_SW_XOFF (0x820000) +#define NIC_PF_TL4_0_1023_SH_STATUS (0x840000) +#define NIC_PF_TL4A_0_1023_CNM_RATE (0x880000) +#define NIC_PF_TL4A_0_1023_CNM_STATUS (0x8A0000) + +#define NIC_PF_VF_0_127_MAILBOX_0_1 (0x20002030) +#define NIC_PF_VNIC_0_127_TX_STAT_0_4 (0x20004000) +#define NIC_PF_VNIC_0_127_RX_STAT_0_13 (0x20004100) +#define NIC_PF_QSET_0_127_LOCK_0_15 (0x20006000) +#define NIC_PF_QSET_0_127_CFG (0x20010000) +#define NIC_PF_QSET_0_127_RQ_0_7_CFG (0x20010400) +#define NIC_PF_QSET_0_127_RQ_0_7_DROP_CFG (0x20010420) +#define NIC_PF_QSET_0_127_RQ_0_7_BP_CFG (0x20010500) +#define NIC_PF_QSET_0_127_RQ_0_7_STAT_0_1 (0x20010600) +#define NIC_PF_QSET_0_127_SQ_0_7_CFG (0x20010C00) +#define NIC_PF_QSET_0_127_SQ_0_7_CFG2 (0x20010C08) +#define NIC_PF_QSET_0_127_SQ_0_7_STAT_0_1 (0x20010D00) + +#define NIC_PF_MSIX_VEC_0_18_ADDR (0x000000) +#define NIC_PF_MSIX_VEC_0_CTL (0x000008) +#define NIC_PF_MSIX_PBA_0 (0x0F0000) + +/* Virtual function register offsets */ +#define NIC_VNIC_CFG (0x000020) +#define NIC_VF_PF_MAILBOX_0_1 (0x000130) +#define NIC_VF_INT (0x000200) +#define NIC_VF_INT_W1S (0x000220) +#define NIC_VF_ENA_W1C (0x000240) +#define NIC_VF_ENA_W1S (0x000260) + +#define NIC_VNIC_RSS_CFG (0x0020E0) +#define NIC_VNIC_RSS_KEY_0_4 (0x002200) +#define NIC_VNIC_TX_STAT_0_4 (0x004000) +#define NIC_VNIC_RX_STAT_0_13 (0x004100) +#define NIC_QSET_RQ_GEN_CFG (0x010010) + +#define NIC_QSET_CQ_0_7_CFG (0x010400) +#define NIC_QSET_CQ_0_7_CFG2 (0x010408) +#define NIC_QSET_CQ_0_7_THRESH (0x010410) +#define NIC_QSET_CQ_0_7_BASE (0x010420) +#define NIC_QSET_CQ_0_7_HEAD (0x010428) +#define NIC_QSET_CQ_0_7_TAIL (0x010430) +#define NIC_QSET_CQ_0_7_DOOR (0x010438) +#define NIC_QSET_CQ_0_7_STATUS (0x010440) +#define NIC_QSET_CQ_0_7_STATUS2 (0x010448) +#define NIC_QSET_CQ_0_7_DEBUG (0x010450) + +#define NIC_QSET_RQ_0_7_CFG (0x010600) +#define NIC_QSET_RQ_0_7_STAT_0_1 (0x010700) + +#define NIC_QSET_SQ_0_7_CFG (0x010800) +#define NIC_QSET_SQ_0_7_THRESH (0x010810) +#define NIC_QSET_SQ_0_7_BASE (0x010820) +#define NIC_QSET_SQ_0_7_HEAD (0x010828) +#define NIC_QSET_SQ_0_7_TAIL (0x010830) +#define NIC_QSET_SQ_0_7_DOOR (0x010838) +#define NIC_QSET_SQ_0_7_STATUS (0x010840) +#define NIC_QSET_SQ_0_7_DEBUG (0x010848) +#define NIC_QSET_SQ_0_7_CNM_CHG (0x010860) +#define NIC_QSET_SQ_0_7_STAT_0_1 (0x010900) + +#define NIC_QSET_RBDR_0_1_CFG (0x010C00) +#define NIC_QSET_RBDR_0_1_THRESH (0x010C10) +#define NIC_QSET_RBDR_0_1_BASE (0x010C20) +#define NIC_QSET_RBDR_0_1_HEAD (0x010C28) +#define NIC_QSET_RBDR_0_1_TAIL (0x010C30) +#define NIC_QSET_RBDR_0_1_DOOR (0x010C38) +#define NIC_QSET_RBDR_0_1_STATUS0 (0x010C40) +#define NIC_QSET_RBDR_0_1_STATUS1 (0x010C48) +#define NIC_QSET_RBDR_0_1_PREFETCH_STATUS (0x010C50) + +#define NIC_VF_MSIX_VECTOR_0_19_ADDR (0x000000) +#define NIC_VF_MSIX_VECTOR_0_19_CTL (0x000008) +#define NIC_VF_MSIX_PBA (0x0F0000) + +/* Offsets within registers */ +#define NIC_MSIX_VEC_SHIFT 4 +#define NIC_Q_NUM_SHIFT 18 +#define NIC_QS_ID_SHIFT 21 +#define NIC_VF_NUM_SHIFT 21 + +/* Port kind configuration register */ +struct pkind_cfg { +#if defined(__BIG_ENDIAN_BITFIELD) + uint64_t reserved_42_63:22; + uint64_t hdr_sl:5; /* Header skip length */ + uint64_t rx_hdr:3; /* TNS Receive header present */ + uint64_t lenerr_en:1; /* L2 length error check enable */ + uint64_t reserved_32_32:1; + uint64_t maxlen:16; /* Max frame size */ + uint64_t minlen:16; /* Min frame size */ +#elif defined(__LITTLE_ENDIAN_BITFIELD) + uint64_t minlen:16; + uint64_t maxlen:16; + uint64_t reserved_32_32:1; + uint64_t lenerr_en:1; + uint64_t rx_hdr:3; + uint64_t hdr_sl:5; + uint64_t reserved_42_63:22; +#endif +}; + +static inline uint64_t BGXX_PF_BAR0(unsigned long param1) + __attribute__ ((pure, always_inline)); +static inline uint64_t BGXX_PF_BAR0(unsigned long param1) +{ + assert(param1 <= 1); + return 0x87E0E0000000 + (param1 << 24); +} + +#define BGXX_PF_BAR0_SIZE 0x400000 +#define NIC_PF_BAR0 0x843000000000 +#define NIC_PF_BAR0_SIZE 0x40000000 + +static inline uint64_t NIC_VFX_BAR0(unsigned long param1) + __attribute__ ((pure, always_inline)); +static inline uint64_t NIC_VFX_BAR0(unsigned long param1) +{ + assert(param1 <= 127); + + return 0x8430A0000000 + (param1 << 21); +} + +#define NIC_VFX_BAR0_SIZE 0x200000 + +#endif /* NIC_REG_H */ diff --git a/drivers/net/octeontx/nicvf_main.c b/drivers/net/octeontx/nicvf_main.c new file mode 100644 index 00000000000..e13c8b95569 --- /dev/null +++ b/drivers/net/octeontx/nicvf_main.c @@ -0,0 +1,581 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Marvell International Ltd. + */ + +#include <dm.h> +#include <malloc.h> +#include <misc.h> +#include <net.h> +#include <pci.h> +#include <pci_ids.h> +#include <phy.h> +#include <asm/io.h> +#include <linux/delay.h> + +#include "nic_reg.h" +#include "nic.h" +#include "nicvf_queues.h" + +/* Register read/write APIs */ +void nicvf_reg_write(struct nicvf *nic, u64 offset, u64 val) +{ + writeq(val, nic->reg_base + offset); +} + +u64 nicvf_reg_read(struct nicvf *nic, u64 offset) +{ + return readq(nic->reg_base + offset); +} + +void nicvf_queue_reg_write(struct nicvf *nic, u64 offset, + u64 qidx, u64 val) +{ + void *addr = nic->reg_base + offset; + + writeq(val, (void *)(addr + (qidx << NIC_Q_NUM_SHIFT))); +} + +u64 nicvf_queue_reg_read(struct nicvf *nic, u64 offset, u64 qidx) +{ + void *addr = nic->reg_base + offset; + + return readq((void *)(addr + (qidx << NIC_Q_NUM_SHIFT))); +} + +static void nicvf_handle_mbx_intr(struct nicvf *nic); + +/* VF -> PF mailbox communication */ +static void nicvf_write_to_mbx(struct nicvf *nic, union nic_mbx *mbx) +{ + u64 *msg = (u64 *)mbx; + + nicvf_reg_write(nic, NIC_VF_PF_MAILBOX_0_1 + 0, msg[0]); + nicvf_reg_write(nic, NIC_VF_PF_MAILBOX_0_1 + 8, msg[1]); +} + +int nicvf_send_msg_to_pf(struct nicvf *nic, union nic_mbx *mbx) +{ + int timeout = NIC_PF_VF_MBX_TIMEOUT; + int sleep = 10; + + nic->pf_acked = false; + nic->pf_nacked = false; + + nicvf_write_to_mbx(nic, mbx); + + nic_handle_mbx_intr(nic->nicpf, nic->vf_id); + + /* Wait for previous message to be acked, timeout 2sec */ + while (!nic->pf_acked) { + if (nic->pf_nacked) + return -1; + mdelay(sleep); + nicvf_handle_mbx_intr(nic); + + if (nic->pf_acked) + break; + timeout -= sleep; + if (!timeout) { + printf("PF didn't ack to mbox msg %d from VF%d\n", + (mbx->msg.msg & 0xFF), nic->vf_id); + return -1; + } + } + + return 0; +} + +/* Checks if VF is able to comminicate with PF + * and also gets the VNIC number this VF is associated to. + */ +static int nicvf_check_pf_ready(struct nicvf *nic) +{ + union nic_mbx mbx = {}; + + mbx.msg.msg = NIC_MBOX_MSG_READY; + if (nicvf_send_msg_to_pf(nic, &mbx)) { + printf("PF didn't respond to READY msg\n"); + return 0; + } + + return 1; +} + +static void nicvf_handle_mbx_intr(struct nicvf *nic) +{ + union nic_mbx mbx = {}; + struct eth_pdata *pdata = dev_get_platdata(nic->dev); + u64 *mbx_data; + u64 mbx_addr; + int i; + + mbx_addr = NIC_VF_PF_MAILBOX_0_1; + mbx_data = (u64 *)&mbx; + + for (i = 0; i < NIC_PF_VF_MAILBOX_SIZE; i++) { + *mbx_data = nicvf_reg_read(nic, mbx_addr); + mbx_data++; + mbx_addr += sizeof(u64); + } + + debug("Mbox message: msg: 0x%x\n", mbx.msg.msg); + switch (mbx.msg.msg) { + case NIC_MBOX_MSG_READY: + nic->pf_acked = true; + nic->vf_id = mbx.nic_cfg.vf_id & 0x7F; + nic->tns_mode = mbx.nic_cfg.tns_mode & 0x7F; + nic->node = mbx.nic_cfg.node_id; + if (!nic->set_mac_pending) + memcpy(pdata->enetaddr, + mbx.nic_cfg.mac_addr, 6); + nic->loopback_supported = mbx.nic_cfg.loopback_supported; + nic->link_up = false; + nic->duplex = 0; + nic->speed = 0; + break; + case NIC_MBOX_MSG_ACK: + nic->pf_acked = true; + break; + case NIC_MBOX_MSG_NACK: + nic->pf_nacked = true; + break; + case NIC_MBOX_MSG_BGX_LINK_CHANGE: + nic->pf_acked = true; + nic->link_up = mbx.link_status.link_up; + nic->duplex = mbx.link_status.duplex; + nic->speed = mbx.link_status.speed; + if (nic->link_up) { + printf("%s: Link is Up %d Mbps %s\n", + nic->dev->name, nic->speed, + nic->duplex == 1 ? + "Full duplex" : "Half duplex"); + } else { + printf("%s: Link is Down\n", nic->dev->name); + } + break; + default: + printf("Invalid message from PF, msg 0x%x\n", mbx.msg.msg); + break; + } + + nicvf_clear_intr(nic, NICVF_INTR_MBOX, 0); +} + +static int nicvf_hw_set_mac_addr(struct nicvf *nic, struct udevice *dev) +{ + union nic_mbx mbx = {}; + struct eth_pdata *pdata = dev_get_platdata(dev); + + mbx.mac.msg = NIC_MBOX_MSG_SET_MAC; + mbx.mac.vf_id = nic->vf_id; + memcpy(mbx.mac.mac_addr, pdata->enetaddr, 6); + + return nicvf_send_msg_to_pf(nic, &mbx); +} + +static void nicvf_config_cpi(struct nicvf *nic) +{ + union nic_mbx mbx = {}; + + mbx.cpi_cfg.msg = NIC_MBOX_MSG_CPI_CFG; + mbx.cpi_cfg.vf_id = nic->vf_id; + mbx.cpi_cfg.cpi_alg = nic->cpi_alg; + mbx.cpi_cfg.rq_cnt = nic->qs->rq_cnt; + + nicvf_send_msg_to_pf(nic, &mbx); +} + +static int nicvf_init_resources(struct nicvf *nic) +{ + int err; + + nic->num_qs = 1; + + /* Enable Qset */ + nicvf_qset_config(nic, true); + + /* Initialize queues and HW for data transfer */ + err = nicvf_config_data_transfer(nic, true); + + if (err) { + printf("Failed to alloc/config VF's QSet resources\n"); + return err; + } + return 0; +} + +static void nicvf_snd_pkt_handler(struct nicvf *nic, + struct cmp_queue *cq, + void *cq_desc, int cqe_type) +{ + struct cqe_send_t *cqe_tx; + struct snd_queue *sq; + struct sq_hdr_subdesc *hdr; + + cqe_tx = (struct cqe_send_t *)cq_desc; + sq = &nic->qs->sq[cqe_tx->sq_idx]; + + hdr = (struct sq_hdr_subdesc *)GET_SQ_DESC(sq, cqe_tx->sqe_ptr); + if (hdr->subdesc_type != SQ_DESC_TYPE_HEADER) + return; + + nicvf_check_cqe_tx_errs(nic, cq, cq_desc); + nicvf_put_sq_desc(sq, hdr->subdesc_cnt + 1); +} + +static int nicvf_rcv_pkt_handler(struct nicvf *nic, + struct cmp_queue *cq, void *cq_desc, + void **ppkt, int cqe_type) +{ + void *pkt; + + size_t pkt_len; + struct cqe_rx_t *cqe_rx = (struct cqe_rx_t *)cq_desc; + int err = 0; + + /* Check for errors */ + err = nicvf_check_cqe_rx_errs(nic, cq, cq_desc); + if (err && !cqe_rx->rb_cnt) + return -1; + + pkt = nicvf_get_rcv_pkt(nic, cq_desc, &pkt_len); + if (!pkt) { + debug("Packet not received\n"); + return -1; + } + + if (pkt) + *ppkt = pkt; + + return pkt_len; +} + +int nicvf_cq_handler(struct nicvf *nic, void **ppkt, int *pkt_len) +{ + int cq_qnum = 0; + int processed_sq_cqe = 0; + int processed_rq_cqe = 0; + int processed_cqe = 0; + + unsigned long cqe_count, cqe_head; + struct queue_set *qs = nic->qs; + struct cmp_queue *cq = &qs->cq[cq_qnum]; + struct cqe_rx_t *cq_desc; + + /* Get num of valid CQ entries expect next one to be SQ completion */ + cqe_count = nicvf_queue_reg_read(nic, NIC_QSET_CQ_0_7_STATUS, cq_qnum); + cqe_count &= 0xFFFF; + if (!cqe_count) + return 0; + + /* Get head of the valid CQ entries */ + cqe_head = nicvf_queue_reg_read(nic, NIC_QSET_CQ_0_7_HEAD, cq_qnum); + cqe_head >>= 9; + cqe_head &= 0xFFFF; + + if (cqe_count) { + /* Get the CQ descriptor */ + cq_desc = (struct cqe_rx_t *)GET_CQ_DESC(cq, cqe_head); + cqe_head++; + cqe_head &= (cq->dmem.q_len - 1); + /* Initiate prefetch for next descriptor */ + prefetch((struct cqe_rx_t *)GET_CQ_DESC(cq, cqe_head)); + + switch (cq_desc->cqe_type) { + case CQE_TYPE_RX: + debug("%s: Got Rx CQE\n", nic->dev->name); + *pkt_len = nicvf_rcv_pkt_handler(nic, cq, cq_desc, + ppkt, CQE_TYPE_RX); + processed_rq_cqe++; + break; + case CQE_TYPE_SEND: + debug("%s: Got Tx CQE\n", nic->dev->name); + nicvf_snd_pkt_handler(nic, cq, cq_desc, CQE_TYPE_SEND); + processed_sq_cqe++; + break; + default: + debug("%s: Got CQ type %u\n", nic->dev->name, + cq_desc->cqe_type); + break; + } + processed_cqe++; + } + + /* Dequeue CQE */ + nicvf_queue_reg_write(nic, NIC_QSET_CQ_0_7_DOOR, + cq_qnum, processed_cqe); + + asm volatile ("dsb sy"); + + return (processed_sq_cqe | processed_rq_cqe); +} + +/* Qset error interrupt handler + * + * As of now only CQ errors are handled + */ +void nicvf_handle_qs_err(struct nicvf *nic) +{ + struct queue_set *qs = nic->qs; + int qidx; + u64 status; + + /* Check if it is CQ err */ + for (qidx = 0; qidx < qs->cq_cnt; qidx++) { + status = nicvf_queue_reg_read(nic, NIC_QSET_CQ_0_7_STATUS, + qidx); + if (!(status & CQ_ERR_MASK)) + continue; + /* Process already queued CQEs and reconfig CQ */ + nicvf_sq_disable(nic, qidx); + nicvf_cmp_queue_config(nic, qs, qidx, true); + nicvf_sq_free_used_descs(nic->dev, &qs->sq[qidx], qidx); + nicvf_sq_enable(nic, &qs->sq[qidx], qidx); + } +} + +static int nicvf_free_pkt(struct udevice *dev, uchar *pkt, int pkt_len) +{ + struct nicvf *nic = dev_get_priv(dev); + + if (pkt && pkt_len) + free(pkt); + nicvf_refill_rbdr(nic); + return 0; +} + +static int nicvf_xmit(struct udevice *dev, void *pkt, int pkt_len) +{ + struct nicvf *nic = dev_get_priv(dev); + int ret = 0; + int rcv_len = 0; + unsigned int timeout = 5000; + void *rpkt = NULL; + + if (!nicvf_sq_append_pkt(nic, pkt, pkt_len)) { + printf("VF%d: TX ring full\n", nic->vf_id); + return -1; + } + + /* check and update CQ for pkt sent */ + while (!ret && timeout--) { + ret = nicvf_cq_handler(nic, &rpkt, &rcv_len); + if (!ret) { + debug("%s: %d, Not sent\n", __func__, __LINE__); + udelay(10); + } + } + + return 0; +} + +static int nicvf_recv(struct udevice *dev, int flags, uchar **packetp) +{ + struct nicvf *nic = dev_get_priv(dev); + void *pkt; + int pkt_len = 0; +#ifdef DEBUG + u8 *dpkt; + int i, j; +#endif + + nicvf_cq_handler(nic, &pkt, &pkt_len); + + if (pkt_len) { +#ifdef DEBUG + dpkt = pkt; + printf("RX packet contents:\n"); + for (i = 0; i < 8; i++) { + puts("\t"); + for (j = 0; j < 10; j++) + printf("%02x ", dpkt[i * 10 + j]); + puts("\n"); + } +#endif + *packetp = pkt; + } + + return pkt_len; +} + +void nicvf_stop(struct udevice *dev) +{ + struct nicvf *nic = dev_get_priv(dev); + + if (!nic->open) + return; + + /* Free resources */ + nicvf_config_data_transfer(nic, false); + + /* Disable HW Qset */ + nicvf_qset_config(nic, false); + + nic->open = false; +} + +int nicvf_open(struct udevice *dev) +{ + int err; + struct nicvf *nic = dev_get_priv(dev); + + nicvf_hw_set_mac_addr(nic, dev); + + /* Configure CPI alorithm */ + nic->cpi_alg = CPI_ALG_NONE; + nicvf_config_cpi(nic); + + /* Initialize the queues */ + err = nicvf_init_resources(nic); + if (err) + return -1; + + if (!nicvf_check_pf_ready(nic)) + return -1; + + nic->open = true; + + /* Make sure queue initialization is written */ + asm volatile("dsb sy"); + + return 0; +} + +int nicvf_write_hwaddr(struct udevice *dev) +{ + unsigned char ethaddr[ARP_HLEN]; + struct eth_pdata *pdata = dev_get_platdata(dev); + struct nicvf *nic = dev_get_priv(dev); + + /* If lower level firmware fails to set proper MAC + * u-boot framework updates MAC to random address. + * Use this hook to update mac address in environment. + */ + if (!eth_env_get_enetaddr_by_index("eth", dev->seq, ethaddr)) { + eth_env_set_enetaddr_by_index("eth", dev->seq, pdata->enetaddr); + debug("%s: pMAC %pM\n", __func__, pdata->enetaddr); + } + eth_env_get_enetaddr_by_index("eth", dev->seq, ethaddr); + if (memcmp(ethaddr, pdata->enetaddr, ARP_HLEN)) { + debug("%s: pMAC %pM\n", __func__, pdata->enetaddr); + nicvf_hw_set_mac_addr(nic, dev); + } + return 0; +} + +static void nicvf_probe_mdio_devices(void) +{ + struct udevice *pdev; + int err; + static int probed; + + if (probed) + return; + + err = dm_pci_find_device(PCI_VENDOR_ID_CAVIUM, + PCI_DEVICE_ID_CAVIUM_SMI, 0, + &pdev); + if (err) + debug("%s couldn't find SMI device\n", __func__); + probed = 1; +} + +int nicvf_initialize(struct udevice *dev) +{ + struct nicvf *nicvf = dev_get_priv(dev); + struct eth_pdata *pdata = dev_get_platdata(dev); + int ret = 0, bgx, lmac; + char name[16]; + unsigned char ethaddr[ARP_HLEN]; + struct udevice *pfdev; + struct nicpf *pf; + static int vfid; + + if (dm_pci_find_device(PCI_VENDOR_ID_CAVIUM, + PCI_DEVICE_ID_CAVIUM_NIC, 0, &pfdev)) { + printf("%s NIC PF device not found..VF probe failed\n", + __func__); + return -1; + } + pf = dev_get_priv(pfdev); + nicvf->vf_id = vfid++; + nicvf->dev = dev; + nicvf->nicpf = pf; + + nicvf_probe_mdio_devices(); + + /* Enable TSO support */ + nicvf->hw_tso = true; + + nicvf->reg_base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0, + PCI_REGION_MEM); + + debug("nicvf->reg_base: %p\n", nicvf->reg_base); + + if (!nicvf->reg_base) { + printf("Cannot map config register space, aborting\n"); + ret = -1; + goto fail; + } + + ret = nicvf_set_qset_resources(nicvf); + if (ret) + return -1; + + sprintf(name, "vnic%u", nicvf->vf_id); + debug("%s name %s\n", __func__, name); + device_set_name(dev, name); + + bgx = NIC_GET_BGX_FROM_VF_LMAC_MAP(pf->vf_lmac_map[nicvf->vf_id]); + lmac = NIC_GET_LMAC_FROM_VF_LMAC_MAP(pf->vf_lmac_map[nicvf->vf_id]); + debug("%s VF %d BGX %d LMAC %d\n", __func__, nicvf->vf_id, bgx, lmac); + debug("%s PF %p pfdev %p VF %p vfdev %p vf->pdata %p\n", + __func__, nicvf->nicpf, nicvf->nicpf->udev, nicvf, nicvf->dev, + pdata); + + fdt_board_get_ethaddr(bgx, lmac, ethaddr); + + debug("%s bgx %d lmac %d ethaddr %pM\n", __func__, bgx, lmac, ethaddr); + + if (is_valid_ethaddr(ethaddr)) { + memcpy(pdata->enetaddr, ethaddr, ARP_HLEN); + eth_env_set_enetaddr_by_index("eth", dev->seq, ethaddr); + } + debug("%s enetaddr %pM ethaddr %pM\n", __func__, + pdata->enetaddr, ethaddr); + +fail: + return ret; +} + +int octeontx_vnic_probe(struct udevice *dev) +{ + return nicvf_initialize(dev); +} + +static const struct eth_ops octeontx_vnic_ops = { + .start = nicvf_open, + .stop = nicvf_stop, + .send = nicvf_xmit, + .recv = nicvf_recv, + .free_pkt = nicvf_free_pkt, + .write_hwaddr = nicvf_write_hwaddr, +}; + +U_BOOT_DRIVER(octeontx_vnic) = { + .name = "vnic", + .id = UCLASS_ETH, + .probe = octeontx_vnic_probe, + .ops = &octeontx_vnic_ops, + .priv_auto_alloc_size = sizeof(struct nicvf), + .platdata_auto_alloc_size = sizeof(struct eth_pdata), +}; + +static struct pci_device_id octeontx_vnic_supported[] = { + { PCI_VDEVICE(CAVIUM, PCI_DEVICE_ID_CAVIUM_NICVF) }, + { PCI_VDEVICE(CAVIUM, PCI_DEVICE_ID_CAVIUM_NICVF_1) }, + {} +}; + +U_BOOT_PCI_DEVICE(octeontx_vnic, octeontx_vnic_supported); diff --git a/drivers/net/octeontx/nicvf_queues.c b/drivers/net/octeontx/nicvf_queues.c new file mode 100644 index 00000000000..c7f262f440d --- /dev/null +++ b/drivers/net/octeontx/nicvf_queues.c @@ -0,0 +1,1140 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Marvell International Ltd. + */ + +#include <cpu_func.h> +#include <dm/device.h> +#include <malloc.h> +#include <net.h> +#include <phy.h> +#include <linux/delay.h> + +#include "nic_reg.h" +#include "nic.h" +#include "q_struct.h" +#include "nicvf_queues.h" + +static int nicvf_poll_reg(struct nicvf *nic, int qidx, + u64 reg, int bit_pos, int bits, int val) +{ + u64 bit_mask; + u64 reg_val; + int timeout = 10; + + bit_mask = (1ULL << bits) - 1; + bit_mask = (bit_mask << bit_pos); + + while (timeout) { + reg_val = nicvf_queue_reg_read(nic, reg, qidx); + if (((reg_val & bit_mask) >> bit_pos) == val) + return 0; + udelay(2000); + timeout--; + } + printf("Poll on reg 0x%llx failed\n", reg); + return 1; +} + +static int nicvf_alloc_q_desc_mem(struct nicvf *nic, struct q_desc_mem *dmem, + int q_len, int desc_size, int align_bytes) +{ + dmem->q_len = q_len; + dmem->size = (desc_size * q_len) + align_bytes; + /* Save address, need it while freeing */ + dmem->unalign_base = calloc(1, dmem->size); + dmem->dma = (uintptr_t)dmem->unalign_base; + + if (!dmem->unalign_base) + return -1; + + /* Align memory address for 'align_bytes' */ + dmem->phys_base = NICVF_ALIGNED_ADDR((u64)dmem->dma, align_bytes); + dmem->base = dmem->unalign_base + (dmem->phys_base - dmem->dma); + + return 0; +} + +static void nicvf_free_q_desc_mem(struct nicvf *nic, struct q_desc_mem *dmem) +{ + if (!dmem) + return; + + free(dmem->unalign_base); + + dmem->unalign_base = NULL; + dmem->base = NULL; +} + +static void *nicvf_rb_ptr_to_pkt(struct nicvf *nic, uintptr_t rb_ptr) +{ + return (void *)rb_ptr; +} + +static int nicvf_init_rbdr(struct nicvf *nic, struct rbdr *rbdr, + int ring_len, int buf_size) +{ + int idx; + uintptr_t rbuf; + struct rbdr_entry_t *desc; + + if (nicvf_alloc_q_desc_mem(nic, &rbdr->dmem, ring_len, + sizeof(struct rbdr_entry_t), + NICVF_RCV_BUF_ALIGN_BYTES)) { + printf("Unable to allocate memory for rcv buffer ring\n"); + return -1; + } + + rbdr->desc = rbdr->dmem.base; + /* Buffer size has to be in multiples of 128 bytes */ + rbdr->dma_size = buf_size; + rbdr->enable = true; + rbdr->thresh = RBDR_THRESH; + + debug("%s: %d: allocating %lld bytes for rcv buffers\n", + __func__, __LINE__, + ring_len * buf_size + NICVF_RCV_BUF_ALIGN_BYTES); + rbdr->buf_mem = (uintptr_t)calloc(1, ring_len * buf_size + + NICVF_RCV_BUF_ALIGN_BYTES); + + if (!rbdr->buf_mem) { + printf("Unable to allocate memory for rcv buffers\n"); + return -1; + } + + rbdr->buffers = NICVF_ALIGNED_ADDR(rbdr->buf_mem, + NICVF_RCV_BUF_ALIGN_BYTES); + + debug("%s: %d: rbdr->buf_mem: %lx, rbdr->buffers: %lx\n", + __func__, __LINE__, rbdr->buf_mem, rbdr->buffers); + + for (idx = 0; idx < ring_len; idx++) { + rbuf = rbdr->buffers + DMA_BUFFER_LEN * idx; + desc = GET_RBDR_DESC(rbdr, idx); + desc->buf_addr = rbuf >> NICVF_RCV_BUF_ALIGN; + flush_dcache_range((uintptr_t)desc, + (uintptr_t)desc + sizeof(desc)); + } + return 0; +} + +static void nicvf_free_rbdr(struct nicvf *nic, struct rbdr *rbdr) +{ + if (!rbdr) + return; + + rbdr->enable = false; + if (!rbdr->dmem.base) + return; + + debug("%s: %d: rbdr->buf_mem: %p\n", __func__, + __LINE__, (void *)rbdr->buf_mem); + free((void *)rbdr->buf_mem); + + /* Free RBDR ring */ + nicvf_free_q_desc_mem(nic, &rbdr->dmem); +} + +/* Refill receive buffer descriptors with new buffers. + * This runs in softirq context . + */ +void nicvf_refill_rbdr(struct nicvf *nic) +{ + struct queue_set *qs = nic->qs; + int rbdr_idx = qs->rbdr_cnt; + unsigned long qcount, head, tail, rb_cnt; + struct rbdr *rbdr; + + if (!rbdr_idx) + return; + rbdr_idx--; + rbdr = &qs->rbdr[rbdr_idx]; + /* Check if it's enabled */ + if (!rbdr->enable) { + printf("Receive queue %d is disabled\n", rbdr_idx); + return; + } + + /* check if valid descs reached or crossed threshold level */ + qcount = nicvf_queue_reg_read(nic, NIC_QSET_RBDR_0_1_STATUS0, rbdr_idx); + head = nicvf_queue_reg_read(nic, NIC_QSET_RBDR_0_1_HEAD, rbdr_idx); + tail = nicvf_queue_reg_read(nic, NIC_QSET_RBDR_0_1_TAIL, rbdr_idx); + + qcount &= 0x7FFFF; + + rb_cnt = qs->rbdr_len - qcount - 1; + + debug("%s: %d: qcount: %lu, head: %lx, tail: %lx, rb_cnt: %lu\n", + __func__, __LINE__, qcount, head, tail, rb_cnt); + + /* Notify HW */ + nicvf_queue_reg_write(nic, NIC_QSET_RBDR_0_1_DOOR, rbdr_idx, rb_cnt); + + asm volatile ("dsb sy"); +} + +/* TBD: how to handle full packets received in CQ + * i.e conversion of buffers into SKBs + */ +static int nicvf_init_cmp_queue(struct nicvf *nic, + struct cmp_queue *cq, int q_len) +{ + if (nicvf_alloc_q_desc_mem(nic, &cq->dmem, q_len, + CMP_QUEUE_DESC_SIZE, + NICVF_CQ_BASE_ALIGN_BYTES)) { + printf("Unable to allocate memory for completion queue\n"); + return -1; + } + cq->desc = cq->dmem.base; + if (!pass1_silicon(nic->rev_id, nic->nicpf->hw->model_id)) + cq->thresh = CMP_QUEUE_CQE_THRESH; + else + cq->thresh = 0; + cq->intr_timer_thresh = CMP_QUEUE_TIMER_THRESH; + + return 0; +} + +static void nicvf_free_cmp_queue(struct nicvf *nic, struct cmp_queue *cq) +{ + if (!cq) + return; + if (!cq->dmem.base) + return; + + nicvf_free_q_desc_mem(nic, &cq->dmem); +} + +static int nicvf_init_snd_queue(struct nicvf *nic, + struct snd_queue *sq, int q_len) +{ + if (nicvf_alloc_q_desc_mem(nic, &sq->dmem, q_len, + SND_QUEUE_DESC_SIZE, + NICVF_SQ_BASE_ALIGN_BYTES)) { + printf("Unable to allocate memory for send queue\n"); + return -1; + } + + sq->desc = sq->dmem.base; + sq->skbuff = calloc(q_len, sizeof(u64)); + sq->head = 0; + sq->tail = 0; + sq->free_cnt = q_len - 1; + sq->thresh = SND_QUEUE_THRESH; + + return 0; +} + +static void nicvf_free_snd_queue(struct nicvf *nic, struct snd_queue *sq) +{ + if (!sq) + return; + if (!sq->dmem.base) + return; + + debug("%s: %d\n", __func__, __LINE__); + free(sq->skbuff); + + nicvf_free_q_desc_mem(nic, &sq->dmem); +} + +static void nicvf_reclaim_snd_queue(struct nicvf *nic, + struct queue_set *qs, int qidx) +{ + /* Disable send queue */ + nicvf_queue_reg_write(nic, NIC_QSET_SQ_0_7_CFG, qidx, 0); + /* Check if SQ is stopped */ + if (nicvf_poll_reg(nic, qidx, NIC_QSET_SQ_0_7_STATUS, 21, 1, 0x01)) + return; + /* Reset send queue */ + nicvf_queue_reg_write(nic, NIC_QSET_SQ_0_7_CFG, qidx, NICVF_SQ_RESET); +} + +static void nicvf_reclaim_rcv_queue(struct nicvf *nic, + struct queue_set *qs, int qidx) +{ + union nic_mbx mbx = {}; + + /* Make sure all packets in the pipeline are written back into mem */ + mbx.msg.msg = NIC_MBOX_MSG_RQ_SW_SYNC; + nicvf_send_msg_to_pf(nic, &mbx); +} + +static void nicvf_reclaim_cmp_queue(struct nicvf *nic, + struct queue_set *qs, int qidx) +{ + /* Disable timer threshold (doesn't get reset upon CQ reset */ + nicvf_queue_reg_write(nic, NIC_QSET_CQ_0_7_CFG2, qidx, 0); + /* Disable completion queue */ + nicvf_queue_reg_write(nic, NIC_QSET_CQ_0_7_CFG, qidx, 0); + /* Reset completion queue */ + nicvf_queue_reg_write(nic, NIC_QSET_CQ_0_7_CFG, qidx, NICVF_CQ_RESET); +} + +static void nicvf_reclaim_rbdr(struct nicvf *nic, + struct rbdr *rbdr, int qidx) +{ + u64 tmp, fifo_state; + int timeout = 10; + + /* Save head and tail pointers for feeing up buffers */ + rbdr->head = nicvf_queue_reg_read(nic, + NIC_QSET_RBDR_0_1_HEAD, + qidx) >> 3; + rbdr->tail = nicvf_queue_reg_read(nic, + NIC_QSET_RBDR_0_1_TAIL, + qidx) >> 3; + + /* If RBDR FIFO is in 'FAIL' state then do a reset first + * before relaiming. + */ + fifo_state = nicvf_queue_reg_read(nic, NIC_QSET_RBDR_0_1_STATUS0, qidx); + if (((fifo_state >> 62) & 0x03) == 0x3) + nicvf_queue_reg_write(nic, NIC_QSET_RBDR_0_1_CFG, + qidx, NICVF_RBDR_RESET); + + /* Disable RBDR */ + nicvf_queue_reg_write(nic, NIC_QSET_RBDR_0_1_CFG, qidx, 0); + if (nicvf_poll_reg(nic, qidx, NIC_QSET_RBDR_0_1_STATUS0, 62, 2, 0x00)) + return; + while (1) { + tmp = nicvf_queue_reg_read(nic, + NIC_QSET_RBDR_0_1_PREFETCH_STATUS, + qidx); + if ((tmp & 0xFFFFFFFF) == ((tmp >> 32) & 0xFFFFFFFF)) + break; + mdelay(2000); + timeout--; + if (!timeout) { + printf("Failed polling on prefetch status\n"); + return; + } + } + nicvf_queue_reg_write(nic, NIC_QSET_RBDR_0_1_CFG, + qidx, NICVF_RBDR_RESET); + + if (nicvf_poll_reg(nic, qidx, NIC_QSET_RBDR_0_1_STATUS0, 62, 2, 0x02)) + return; + nicvf_queue_reg_write(nic, NIC_QSET_RBDR_0_1_CFG, qidx, 0x00); + if (nicvf_poll_reg(nic, qidx, NIC_QSET_RBDR_0_1_STATUS0, 62, 2, 0x00)) + return; +} + +/* Configures receive queue */ +static void nicvf_rcv_queue_config(struct nicvf *nic, struct queue_set *qs, + int qidx, bool enable) +{ + union nic_mbx mbx = {}; + struct rcv_queue *rq; + union { + struct rq_cfg s; + u64 u; + } rq_cfg; + + rq = &qs->rq[qidx]; + rq->enable = enable; + + /* Disable receive queue */ + nicvf_queue_reg_write(nic, NIC_QSET_RQ_0_7_CFG, qidx, 0); + + if (!rq->enable) { + nicvf_reclaim_rcv_queue(nic, qs, qidx); + return; + } + + rq->cq_qs = qs->vnic_id; + rq->cq_idx = qidx; + rq->start_rbdr_qs = qs->vnic_id; + rq->start_qs_rbdr_idx = qs->rbdr_cnt - 1; + rq->cont_rbdr_qs = qs->vnic_id; + rq->cont_qs_rbdr_idx = qs->rbdr_cnt - 1; + /* all writes of RBDR data to be loaded into L2 Cache as well*/ + rq->caching = 1; + + /* Send a mailbox msg to PF to config RQ */ + mbx.rq.msg = NIC_MBOX_MSG_RQ_CFG; + mbx.rq.qs_num = qs->vnic_id; + mbx.rq.rq_num = qidx; + mbx.rq.cfg = (rq->caching << 26) | (rq->cq_qs << 19) | + (rq->cq_idx << 16) | (rq->cont_rbdr_qs << 9) | + (rq->cont_qs_rbdr_idx << 8) | + (rq->start_rbdr_qs << 1) | (rq->start_qs_rbdr_idx); + nicvf_send_msg_to_pf(nic, &mbx); + + mbx.rq.msg = NIC_MBOX_MSG_RQ_BP_CFG; + mbx.rq.cfg = (1ULL << 63) | (1ULL << 62) | (qs->vnic_id << 0); + nicvf_send_msg_to_pf(nic, &mbx); + + /* RQ drop config + * Enable CQ drop to reserve sufficient CQEs for all tx packets + */ + mbx.rq.msg = NIC_MBOX_MSG_RQ_DROP_CFG; + mbx.rq.cfg = (1ULL << 62) | (RQ_CQ_DROP << 8); + nicvf_send_msg_to_pf(nic, &mbx); + nicvf_queue_reg_write(nic, NIC_QSET_RQ_GEN_CFG, 0, 0x00); + + /* Enable Receive queue */ + rq_cfg.s.ena = 1; + rq_cfg.s.tcp_ena = 0; + nicvf_queue_reg_write(nic, NIC_QSET_RQ_0_7_CFG, qidx, rq_cfg.u); +} + +void nicvf_cmp_queue_config(struct nicvf *nic, struct queue_set *qs, + int qidx, bool enable) +{ + struct cmp_queue *cq; + union { + u64 u; + struct cq_cfg s; + } cq_cfg; + + cq = &qs->cq[qidx]; + cq->enable = enable; + + if (!cq->enable) { + nicvf_reclaim_cmp_queue(nic, qs, qidx); + return; + } + + /* Reset completion queue */ + nicvf_queue_reg_write(nic, NIC_QSET_CQ_0_7_CFG, qidx, NICVF_CQ_RESET); + + if (!cq->enable) + return; + + /* Set completion queue base address */ + nicvf_queue_reg_write(nic, NIC_QSET_CQ_0_7_BASE, + qidx, (u64)(cq->dmem.phys_base)); + + /* Enable Completion queue */ + cq_cfg.s.ena = 1; + cq_cfg.s.reset = 0; + cq_cfg.s.caching = 0; + cq_cfg.s.qsize = CMP_QSIZE; + cq_cfg.s.avg_con = 0; + nicvf_queue_reg_write(nic, NIC_QSET_CQ_0_7_CFG, qidx, cq_cfg.u); + + /* Set threshold value for interrupt generation */ + nicvf_queue_reg_write(nic, NIC_QSET_CQ_0_7_THRESH, qidx, cq->thresh); + nicvf_queue_reg_write(nic, NIC_QSET_CQ_0_7_CFG2, qidx, + cq->intr_timer_thresh); +} + +/* Configures transmit queue */ +static void nicvf_snd_queue_config(struct nicvf *nic, struct queue_set *qs, + int qidx, bool enable) +{ + union nic_mbx mbx = {}; + struct snd_queue *sq; + + union { + struct sq_cfg s; + u64 u; + } sq_cfg; + + sq = &qs->sq[qidx]; + sq->enable = enable; + + if (!sq->enable) { + nicvf_reclaim_snd_queue(nic, qs, qidx); + return; + } + + /* Reset send queue */ + nicvf_queue_reg_write(nic, NIC_QSET_SQ_0_7_CFG, qidx, NICVF_SQ_RESET); + + sq->cq_qs = qs->vnic_id; + sq->cq_idx = qidx; + + /* Send a mailbox msg to PF to config SQ */ + mbx.sq.msg = NIC_MBOX_MSG_SQ_CFG; + mbx.sq.qs_num = qs->vnic_id; + mbx.sq.sq_num = qidx; + mbx.sq.sqs_mode = nic->sqs_mode; + mbx.sq.cfg = (sq->cq_qs << 3) | sq->cq_idx; + nicvf_send_msg_to_pf(nic, &mbx); + + /* Set queue base address */ + nicvf_queue_reg_write(nic, NIC_QSET_SQ_0_7_BASE, + qidx, (u64)(sq->dmem.phys_base)); + + /* Enable send queue & set queue size */ + sq_cfg.s.ena = 1; + sq_cfg.s.reset = 0; + sq_cfg.s.ldwb = 0; + sq_cfg.s.qsize = SND_QSIZE; + sq_cfg.s.tstmp_bgx_intf = 0; + nicvf_queue_reg_write(nic, NIC_QSET_SQ_0_7_CFG, qidx, sq_cfg.u); + + /* Set threshold value for interrupt generation */ + nicvf_queue_reg_write(nic, NIC_QSET_SQ_0_7_THRESH, qidx, sq->thresh); +} + +/* Configures receive buffer descriptor ring */ +static void nicvf_rbdr_config(struct nicvf *nic, struct queue_set *qs, + int qidx, bool enable) +{ + struct rbdr *rbdr; + union { + struct rbdr_cfg s; + u64 u; + } rbdr_cfg; + + rbdr = &qs->rbdr[qidx]; + nicvf_reclaim_rbdr(nic, rbdr, qidx); + if (!enable) + return; + + /* Set descriptor base address */ + nicvf_queue_reg_write(nic, NIC_QSET_RBDR_0_1_BASE, + qidx, (u64)(rbdr->dmem.phys_base)); + + /* Enable RBDR & set queue size */ + /* Buffer size should be in multiples of 128 bytes */ + rbdr_cfg.s.ena = 1; + rbdr_cfg.s.reset = 0; + rbdr_cfg.s.ldwb = 0; + rbdr_cfg.s.qsize = RBDR_SIZE; + rbdr_cfg.s.avg_con = 0; + rbdr_cfg.s.lines = rbdr->dma_size / 128; + nicvf_queue_reg_write(nic, NIC_QSET_RBDR_0_1_CFG, + qidx, rbdr_cfg.u); + + /* Notify HW */ + nicvf_queue_reg_write(nic, NIC_QSET_RBDR_0_1_DOOR, + qidx, qs->rbdr_len - 1); + + /* Set threshold value for interrupt generation */ + nicvf_queue_reg_write(nic, NIC_QSET_RBDR_0_1_THRESH, + qidx, rbdr->thresh - 1); +} + +/* Requests PF to assign and enable Qset */ +void nicvf_qset_config(struct nicvf *nic, bool enable) +{ + union nic_mbx mbx = {}; + struct queue_set *qs = nic->qs; + struct qs_cfg *qs_cfg; + + if (!qs) { + printf("Qset is still not allocated, don't init queues\n"); + return; + } + + qs->enable = enable; + qs->vnic_id = nic->vf_id; + + /* Send a mailbox msg to PF to config Qset */ + mbx.qs.msg = NIC_MBOX_MSG_QS_CFG; + mbx.qs.num = qs->vnic_id; +#ifdef VNIC_MULTI_QSET_SUPPORT + mbx.qs.sqs_count = nic->sqs_count; +#endif + + mbx.qs.cfg = 0; + qs_cfg = (struct qs_cfg *)&mbx.qs.cfg; + if (qs->enable) { + qs_cfg->ena = 1; +#ifdef __BIG_ENDIAN + qs_cfg->be = 1; +#endif + qs_cfg->vnic = qs->vnic_id; + } + nicvf_send_msg_to_pf(nic, &mbx); +} + +static void nicvf_free_resources(struct nicvf *nic) +{ + int qidx; + struct queue_set *qs = nic->qs; + + /* Free receive buffer descriptor ring */ + for (qidx = 0; qidx < qs->rbdr_cnt; qidx++) + nicvf_free_rbdr(nic, &qs->rbdr[qidx]); + + /* Free completion queue */ + for (qidx = 0; qidx < qs->cq_cnt; qidx++) + nicvf_free_cmp_queue(nic, &qs->cq[qidx]); + + /* Free send queue */ + for (qidx = 0; qidx < qs->sq_cnt; qidx++) + nicvf_free_snd_queue(nic, &qs->sq[qidx]); +} + +static int nicvf_alloc_resources(struct nicvf *nic) +{ + int qidx; + struct queue_set *qs = nic->qs; + + /* Alloc receive buffer descriptor ring */ + for (qidx = 0; qidx < qs->rbdr_cnt; qidx++) { + if (nicvf_init_rbdr(nic, &qs->rbdr[qidx], qs->rbdr_len, + DMA_BUFFER_LEN)) + goto alloc_fail; + } + + /* Alloc send queue */ + for (qidx = 0; qidx < qs->sq_cnt; qidx++) { + if (nicvf_init_snd_queue(nic, &qs->sq[qidx], qs->sq_len)) + goto alloc_fail; + } + + /* Alloc completion queue */ + for (qidx = 0; qidx < qs->cq_cnt; qidx++) { + if (nicvf_init_cmp_queue(nic, &qs->cq[qidx], qs->cq_len)) + goto alloc_fail; + } + + return 0; +alloc_fail: + nicvf_free_resources(nic); + return -1; +} + +int nicvf_set_qset_resources(struct nicvf *nic) +{ + struct queue_set *qs; + + qs = calloc(1, sizeof(struct queue_set)); + if (!qs) + return -1; + nic->qs = qs; + + /* Set count of each queue */ + qs->rbdr_cnt = RBDR_CNT; + qs->rq_cnt = 1; + qs->sq_cnt = SND_QUEUE_CNT; + qs->cq_cnt = CMP_QUEUE_CNT; + + /* Set queue lengths */ + qs->rbdr_len = RCV_BUF_COUNT; + qs->sq_len = SND_QUEUE_LEN; + qs->cq_len = CMP_QUEUE_LEN; + + nic->rx_queues = qs->rq_cnt; + nic->tx_queues = qs->sq_cnt; + + return 0; +} + +int nicvf_config_data_transfer(struct nicvf *nic, bool enable) +{ + bool disable = false; + struct queue_set *qs = nic->qs; + int qidx; + + if (!qs) + return 0; + + if (enable) { + if (nicvf_alloc_resources(nic)) + return -1; + + for (qidx = 0; qidx < qs->sq_cnt; qidx++) + nicvf_snd_queue_config(nic, qs, qidx, enable); + for (qidx = 0; qidx < qs->cq_cnt; qidx++) + nicvf_cmp_queue_config(nic, qs, qidx, enable); + for (qidx = 0; qidx < qs->rbdr_cnt; qidx++) + nicvf_rbdr_config(nic, qs, qidx, enable); + for (qidx = 0; qidx < qs->rq_cnt; qidx++) + nicvf_rcv_queue_config(nic, qs, qidx, enable); + } else { + for (qidx = 0; qidx < qs->rq_cnt; qidx++) + nicvf_rcv_queue_config(nic, qs, qidx, disable); + for (qidx = 0; qidx < qs->rbdr_cnt; qidx++) + nicvf_rbdr_config(nic, qs, qidx, disable); + for (qidx = 0; qidx < qs->sq_cnt; qidx++) + nicvf_snd_queue_config(nic, qs, qidx, disable); + for (qidx = 0; qidx < qs->cq_cnt; qidx++) + nicvf_cmp_queue_config(nic, qs, qidx, disable); + + nicvf_free_resources(nic); + } + + return 0; +} + +/* Get a free desc from SQ + * returns descriptor ponter & descriptor number + */ +static int nicvf_get_sq_desc(struct snd_queue *sq, int desc_cnt) +{ + int qentry; + + qentry = sq->tail; + sq->free_cnt -= desc_cnt; + sq->tail += desc_cnt; + sq->tail &= (sq->dmem.q_len - 1); + + return qentry; +} + +/* Free descriptor back to SQ for future use */ +void nicvf_put_sq_desc(struct snd_queue *sq, int desc_cnt) +{ + sq->free_cnt += desc_cnt; + sq->head += desc_cnt; + sq->head &= (sq->dmem.q_len - 1); +} + +static int nicvf_get_nxt_sqentry(struct snd_queue *sq, int qentry) +{ + qentry++; + qentry &= (sq->dmem.q_len - 1); + return qentry; +} + +void nicvf_sq_enable(struct nicvf *nic, struct snd_queue *sq, int qidx) +{ + u64 sq_cfg; + + sq_cfg = nicvf_queue_reg_read(nic, NIC_QSET_SQ_0_7_CFG, qidx); + sq_cfg |= NICVF_SQ_EN; + nicvf_queue_reg_write(nic, NIC_QSET_SQ_0_7_CFG, qidx, sq_cfg); + /* Ring doorbell so that H/W restarts processing SQEs */ + nicvf_queue_reg_write(nic, NIC_QSET_SQ_0_7_DOOR, qidx, 0); +} + +void nicvf_sq_disable(struct nicvf *nic, int qidx) +{ + u64 sq_cfg; + + sq_cfg = nicvf_queue_reg_read(nic, NIC_QSET_SQ_0_7_CFG, qidx); + sq_cfg &= ~NICVF_SQ_EN; + nicvf_queue_reg_write(nic, NIC_QSET_SQ_0_7_CFG, qidx, sq_cfg); +} + +void nicvf_sq_free_used_descs(struct udevice *dev, struct snd_queue *sq, + int qidx) +{ + u64 head; + struct nicvf *nic = dev_get_priv(dev); + struct sq_hdr_subdesc *hdr; + + head = nicvf_queue_reg_read(nic, NIC_QSET_SQ_0_7_HEAD, qidx) >> 4; + + while (sq->head != head) { + hdr = (struct sq_hdr_subdesc *)GET_SQ_DESC(sq, sq->head); + if (hdr->subdesc_type != SQ_DESC_TYPE_HEADER) { + nicvf_put_sq_desc(sq, 1); + continue; + } + nicvf_put_sq_desc(sq, hdr->subdesc_cnt + 1); + } +} + +/* Get the number of SQ descriptors needed to xmit this skb */ +static int nicvf_sq_subdesc_required(struct nicvf *nic) +{ + int subdesc_cnt = MIN_SQ_DESC_PER_PKT_XMIT; + + return subdesc_cnt; +} + +/* Add SQ HEADER subdescriptor. + * First subdescriptor for every send descriptor. + */ +static inline void +nicvf_sq_add_hdr_subdesc(struct nicvf *nic, struct snd_queue *sq, int qentry, + int subdesc_cnt, void *pkt, size_t pkt_len) +{ + struct sq_hdr_subdesc *hdr; + + hdr = (struct sq_hdr_subdesc *)GET_SQ_DESC(sq, qentry); + sq->skbuff[qentry] = (uintptr_t)pkt; + + memset(hdr, 0, SND_QUEUE_DESC_SIZE); + hdr->subdesc_type = SQ_DESC_TYPE_HEADER; + /* Enable notification via CQE after processing SQE */ + hdr->post_cqe = 1; + /* No of subdescriptors following this */ + hdr->subdesc_cnt = subdesc_cnt; + hdr->tot_len = pkt_len; + + flush_dcache_range((uintptr_t)hdr, + (uintptr_t)hdr + sizeof(struct sq_hdr_subdesc)); +} + +/* SQ GATHER subdescriptor + * Must follow HDR descriptor + */ +static inline void nicvf_sq_add_gather_subdesc(struct snd_queue *sq, int qentry, + size_t size, uintptr_t data) +{ + struct sq_gather_subdesc *gather; + + qentry &= (sq->dmem.q_len - 1); + gather = (struct sq_gather_subdesc *)GET_SQ_DESC(sq, qentry); + + memset(gather, 0, SND_QUEUE_DESC_SIZE); + gather->subdesc_type = SQ_DESC_TYPE_GATHER; + gather->ld_type = NIC_SEND_LD_TYPE_E_LDD; + gather->size = size; + gather->addr = data; + + flush_dcache_range((uintptr_t)gather, + (uintptr_t)gather + sizeof(struct sq_hdr_subdesc)); +} + +/* Append an skb to a SQ for packet transfer. */ +int nicvf_sq_append_pkt(struct nicvf *nic, void *pkt, size_t pkt_size) +{ + int subdesc_cnt; + int sq_num = 0, qentry; + struct queue_set *qs; + struct snd_queue *sq; + + qs = nic->qs; + sq = &qs->sq[sq_num]; + + subdesc_cnt = nicvf_sq_subdesc_required(nic); + if (subdesc_cnt > sq->free_cnt) + goto append_fail; + + qentry = nicvf_get_sq_desc(sq, subdesc_cnt); + + /* Add SQ header subdesc */ + nicvf_sq_add_hdr_subdesc(nic, sq, qentry, subdesc_cnt - 1, + pkt, pkt_size); + + /* Add SQ gather subdescs */ + qentry = nicvf_get_nxt_sqentry(sq, qentry); + nicvf_sq_add_gather_subdesc(sq, qentry, pkt_size, (uintptr_t)(pkt)); + + flush_dcache_range((uintptr_t)pkt, + (uintptr_t)pkt + pkt_size); + + /* make sure all memory stores are done before ringing doorbell */ + asm volatile ("dsb sy"); + + /* Inform HW to xmit new packet */ + nicvf_queue_reg_write(nic, NIC_QSET_SQ_0_7_DOOR, + sq_num, subdesc_cnt); + return 1; + +append_fail: + printf("Not enough SQ descriptors to xmit pkt\n"); + return 0; +} + +static unsigned int frag_num(unsigned int i) +{ +#ifdef __BIG_ENDIAN + return (i & ~3) + 3 - (i & 3); +#else + return i; +#endif +} + +void *nicvf_get_rcv_pkt(struct nicvf *nic, void *cq_desc, size_t *pkt_len) +{ + int frag; + int payload_len = 0, tot_len; + void *pkt = NULL, *pkt_buf = NULL, *buffer; + struct cqe_rx_t *cqe_rx; + struct rbdr *rbdr; + struct rcv_queue *rq; + struct queue_set *qs = nic->qs; + u16 *rb_lens = NULL; + u64 *rb_ptrs = NULL; + + cqe_rx = (struct cqe_rx_t *)cq_desc; + + rq = &qs->rq[cqe_rx->rq_idx]; + rbdr = &qs->rbdr[rq->start_qs_rbdr_idx]; + rb_lens = cq_desc + (3 * sizeof(u64)); /* Use offsetof */ + /* Except 88xx pass1 on all other chips CQE_RX2_S is added to + * CQE_RX at word6, hence buffer pointers move by word + * + * Use existing 'hw_tso' flag which will be set for all chips + * except 88xx pass1 instead of a additional cache line + * access (or miss) by using pci dev's revision. + */ + if (!nic->hw_tso) + rb_ptrs = (void *)cqe_rx + (6 * sizeof(u64)); + else + rb_ptrs = (void *)cqe_rx + (7 * sizeof(u64)); + + /* + * Figure out packet length to create packet buffer + */ + for (frag = 0; frag < cqe_rx->rb_cnt; frag++) + payload_len += rb_lens[frag_num(frag)]; + *pkt_len = payload_len; + /* round up size to 8 byte multiple */ + tot_len = (payload_len & (~0x7)) + 8; + buffer = calloc(1, tot_len); + if (!buffer) { + printf("%s - Failed to allocate packet buffer\n", __func__); + return NULL; + } + pkt_buf = buffer; + debug("total pkt buf %p len %ld tot_len %d\n", pkt_buf, *pkt_len, + tot_len); + for (frag = 0; frag < cqe_rx->rb_cnt; frag++) { + payload_len = rb_lens[frag_num(frag)]; + + invalidate_dcache_range((uintptr_t)(*rb_ptrs), + (uintptr_t)(*rb_ptrs) + rbdr->dma_size); + + /* First fragment */ + *rb_ptrs = *rb_ptrs - cqe_rx->align_pad; + + pkt = nicvf_rb_ptr_to_pkt(nic, *rb_ptrs); + + invalidate_dcache_range((uintptr_t)pkt, + (uintptr_t)pkt + payload_len); + + if (cqe_rx->align_pad) + pkt += cqe_rx->align_pad; + debug("pkt_buf %p, pkt %p payload_len %d\n", pkt_buf, pkt, + payload_len); + memcpy(buffer, pkt, payload_len); + buffer += payload_len; + /* Next buffer pointer */ + rb_ptrs++; + } + return pkt_buf; +} + +/* Clear interrupt */ +void nicvf_clear_intr(struct nicvf *nic, int int_type, int q_idx) +{ + u64 reg_val = 0; + + switch (int_type) { + case NICVF_INTR_CQ: + reg_val = ((1ULL << q_idx) << NICVF_INTR_CQ_SHIFT); + break; + case NICVF_INTR_SQ: + reg_val = ((1ULL << q_idx) << NICVF_INTR_SQ_SHIFT); + break; + case NICVF_INTR_RBDR: + reg_val = ((1ULL << q_idx) << NICVF_INTR_RBDR_SHIFT); + break; + case NICVF_INTR_PKT_DROP: + reg_val = (1ULL << NICVF_INTR_PKT_DROP_SHIFT); + break; + case NICVF_INTR_TCP_TIMER: + reg_val = (1ULL << NICVF_INTR_TCP_TIMER_SHIFT); + break; + case NICVF_INTR_MBOX: + reg_val = (1ULL << NICVF_INTR_MBOX_SHIFT); + break; + case NICVF_INTR_QS_ERR: + reg_val |= (1ULL << NICVF_INTR_QS_ERR_SHIFT); + break; + default: + printf("Failed to clear interrupt: unknown type\n"); + break; + } + + nicvf_reg_write(nic, NIC_VF_INT, reg_val); +} + +void nicvf_update_rq_stats(struct nicvf *nic, int rq_idx) +{ + struct rcv_queue *rq; + +#define GET_RQ_STATS(reg) \ + nicvf_reg_read(nic, NIC_QSET_RQ_0_7_STAT_0_1 |\ + (rq_idx << NIC_Q_NUM_SHIFT) | ((reg) << 3)) + + rq = &nic->qs->rq[rq_idx]; + rq->stats.bytes = GET_RQ_STATS(RQ_SQ_STATS_OCTS); + rq->stats.pkts = GET_RQ_STATS(RQ_SQ_STATS_PKTS); +} + +void nicvf_update_sq_stats(struct nicvf *nic, int sq_idx) +{ + struct snd_queue *sq; + +#define GET_SQ_STATS(reg) \ + nicvf_reg_read(nic, NIC_QSET_SQ_0_7_STAT_0_1 |\ + (sq_idx << NIC_Q_NUM_SHIFT) | ((reg) << 3)) + + sq = &nic->qs->sq[sq_idx]; + sq->stats.bytes = GET_SQ_STATS(RQ_SQ_STATS_OCTS); + sq->stats.pkts = GET_SQ_STATS(RQ_SQ_STATS_PKTS); +} + +/* Check for errors in the receive cmp.queue entry */ +int nicvf_check_cqe_rx_errs(struct nicvf *nic, + struct cmp_queue *cq, void *cq_desc) +{ + struct cqe_rx_t *cqe_rx; + struct cmp_queue_stats *stats = &cq->stats; + + cqe_rx = (struct cqe_rx_t *)cq_desc; + if (!cqe_rx->err_level && !cqe_rx->err_opcode) { + stats->rx.errop.good++; + return 0; + } + + switch (cqe_rx->err_level) { + case CQ_ERRLVL_MAC: + stats->rx.errlvl.mac_errs++; + break; + case CQ_ERRLVL_L2: + stats->rx.errlvl.l2_errs++; + break; + case CQ_ERRLVL_L3: + stats->rx.errlvl.l3_errs++; + break; + case CQ_ERRLVL_L4: + stats->rx.errlvl.l4_errs++; + break; + } + + switch (cqe_rx->err_opcode) { + case CQ_RX_ERROP_RE_PARTIAL: + stats->rx.errop.partial_pkts++; + break; + case CQ_RX_ERROP_RE_JABBER: + stats->rx.errop.jabber_errs++; + break; + case CQ_RX_ERROP_RE_FCS: + stats->rx.errop.fcs_errs++; + break; + case CQ_RX_ERROP_RE_TERMINATE: + stats->rx.errop.terminate_errs++; + break; + case CQ_RX_ERROP_RE_RX_CTL: + stats->rx.errop.bgx_rx_errs++; + break; + case CQ_RX_ERROP_PREL2_ERR: + stats->rx.errop.prel2_errs++; + break; + case CQ_RX_ERROP_L2_FRAGMENT: + stats->rx.errop.l2_frags++; + break; + case CQ_RX_ERROP_L2_OVERRUN: + stats->rx.errop.l2_overruns++; + break; + case CQ_RX_ERROP_L2_PFCS: + stats->rx.errop.l2_pfcs++; + break; + case CQ_RX_ERROP_L2_PUNY: + stats->rx.errop.l2_puny++; + break; + case CQ_RX_ERROP_L2_MAL: + stats->rx.errop.l2_hdr_malformed++; + break; + case CQ_RX_ERROP_L2_OVERSIZE: + stats->rx.errop.l2_oversize++; + break; + case CQ_RX_ERROP_L2_UNDERSIZE: + stats->rx.errop.l2_undersize++; + break; + case CQ_RX_ERROP_L2_LENMISM: + stats->rx.errop.l2_len_mismatch++; + break; + case CQ_RX_ERROP_L2_PCLP: + stats->rx.errop.l2_pclp++; + break; + case CQ_RX_ERROP_IP_NOT: + stats->rx.errop.non_ip++; + break; + case CQ_RX_ERROP_IP_CSUM_ERR: + stats->rx.errop.ip_csum_err++; + break; + case CQ_RX_ERROP_IP_MAL: + stats->rx.errop.ip_hdr_malformed++; + break; + case CQ_RX_ERROP_IP_MALD: + stats->rx.errop.ip_payload_malformed++; + break; + case CQ_RX_ERROP_IP_HOP: + stats->rx.errop.ip_hop_errs++; + break; + case CQ_RX_ERROP_L3_ICRC: + stats->rx.errop.l3_icrc_errs++; + break; + case CQ_RX_ERROP_L3_PCLP: + stats->rx.errop.l3_pclp++; + break; + case CQ_RX_ERROP_L4_MAL: + stats->rx.errop.l4_malformed++; + break; + case CQ_RX_ERROP_L4_CHK: + stats->rx.errop.l4_csum_errs++; + break; + case CQ_RX_ERROP_UDP_LEN: + stats->rx.errop.udp_len_err++; + break; + case CQ_RX_ERROP_L4_PORT: + stats->rx.errop.bad_l4_port++; + break; + case CQ_RX_ERROP_TCP_FLAG: + stats->rx.errop.bad_tcp_flag++; + break; + case CQ_RX_ERROP_TCP_OFFSET: + stats->rx.errop.tcp_offset_errs++; + break; + case CQ_RX_ERROP_L4_PCLP: + stats->rx.errop.l4_pclp++; + break; + case CQ_RX_ERROP_RBDR_TRUNC: + stats->rx.errop.pkt_truncated++; + break; + } + + return 1; +} + +/* Check for errors in the send cmp.queue entry */ +int nicvf_check_cqe_tx_errs(struct nicvf *nic, + struct cmp_queue *cq, void *cq_desc) +{ + struct cqe_send_t *cqe_tx; + struct cmp_queue_stats *stats = &cq->stats; + + cqe_tx = (struct cqe_send_t *)cq_desc; + switch (cqe_tx->send_status) { + case CQ_TX_ERROP_GOOD: + stats->tx.good++; + return 0; + break; + case CQ_TX_ERROP_DESC_FAULT: + stats->tx.desc_fault++; + break; + case CQ_TX_ERROP_HDR_CONS_ERR: + stats->tx.hdr_cons_err++; + break; + case CQ_TX_ERROP_SUBDC_ERR: + stats->tx.subdesc_err++; + break; + case CQ_TX_ERROP_IMM_SIZE_OFLOW: + stats->tx.imm_size_oflow++; + break; + case CQ_TX_ERROP_DATA_SEQUENCE_ERR: + stats->tx.data_seq_err++; + break; + case CQ_TX_ERROP_MEM_SEQUENCE_ERR: + stats->tx.mem_seq_err++; + break; + case CQ_TX_ERROP_LOCK_VIOL: + stats->tx.lock_viol++; + break; + case CQ_TX_ERROP_DATA_FAULT: + stats->tx.data_fault++; + break; + case CQ_TX_ERROP_TSTMP_CONFLICT: + stats->tx.tstmp_conflict++; + break; + case CQ_TX_ERROP_TSTMP_TIMEOUT: + stats->tx.tstmp_timeout++; + break; + case CQ_TX_ERROP_MEM_FAULT: + stats->tx.mem_fault++; + break; + case CQ_TX_ERROP_CK_OVERLAP: + stats->tx.csum_overlap++; + break; + case CQ_TX_ERROP_CK_OFLOW: + stats->tx.csum_overflow++; + break; + } + + return 1; +} diff --git a/drivers/net/octeontx/nicvf_queues.h b/drivers/net/octeontx/nicvf_queues.h new file mode 100644 index 00000000000..833b2a1dd47 --- /dev/null +++ b/drivers/net/octeontx/nicvf_queues.h @@ -0,0 +1,353 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (C) 2018 Marvell International Ltd. + */ + +#ifndef NICVF_QUEUES_H +#define NICVF_QUEUES_H + +#include "q_struct.h" + +#define MAX_QUEUE_SET 128 +#define MAX_RCV_QUEUES_PER_QS 8 +#define MAX_RCV_BUF_DESC_RINGS_PER_QS 2 +#define MAX_SND_QUEUES_PER_QS 8 +#define MAX_CMP_QUEUES_PER_QS 8 + +/* VF's queue interrupt ranges */ +#define NICVF_INTR_ID_CQ 0 +#define NICVF_INTR_ID_SQ 8 +#define NICVF_INTR_ID_RBDR 16 +#define NICVF_INTR_ID_MISC 18 +#define NICVF_INTR_ID_QS_ERR 19 + +#define RBDR_SIZE0 0ULL /* 8K entries */ +#define RBDR_SIZE1 1ULL /* 16K entries */ +#define RBDR_SIZE2 2ULL /* 32K entries */ +#define RBDR_SIZE3 3ULL /* 64K entries */ +#define RBDR_SIZE4 4ULL /* 126K entries */ +#define RBDR_SIZE5 5ULL /* 256K entries */ +#define RBDR_SIZE6 6ULL /* 512K entries */ + +#define SND_QUEUE_SIZE0 0ULL /* 1K entries */ +#define SND_QUEUE_SIZE1 1ULL /* 2K entries */ +#define SND_QUEUE_SIZE2 2ULL /* 4K entries */ +#define SND_QUEUE_SIZE3 3ULL /* 8K entries */ +#define SND_QUEUE_SIZE4 4ULL /* 16K entries */ +#define SND_QUEUE_SIZE5 5ULL /* 32K entries */ +#define SND_QUEUE_SIZE6 6ULL /* 64K entries */ + +#define CMP_QUEUE_SIZE0 0ULL /* 1K entries */ +#define CMP_QUEUE_SIZE1 1ULL /* 2K entries */ +#define CMP_QUEUE_SIZE2 2ULL /* 4K entries */ +#define CMP_QUEUE_SIZE3 3ULL /* 8K entries */ +#define CMP_QUEUE_SIZE4 4ULL /* 16K entries */ +#define CMP_QUEUE_SIZE5 5ULL /* 32K entries */ +#define CMP_QUEUE_SIZE6 6ULL /* 64K entries */ + +/* Default queue count per QS, its lengths and threshold values */ +#define RBDR_CNT 1 +#define RCV_QUEUE_CNT 1 +#define SND_QUEUE_CNT 1 +#define CMP_QUEUE_CNT 1 /* Max of RCV and SND qcount */ + +#define SND_QSIZE SND_QUEUE_SIZE0 +#define SND_QUEUE_LEN BIT_ULL((SND_QSIZE + 10)) +#define SND_QUEUE_THRESH 2ULL +#define MIN_SQ_DESC_PER_PKT_XMIT 2 +#define MAX_CQE_PER_PKT_XMIT 2 + +#define CMP_QSIZE CMP_QUEUE_SIZE0 +#define CMP_QUEUE_LEN BIT_ULL((CMP_QSIZE + 10)) +#define CMP_QUEUE_CQE_THRESH 0 +#define CMP_QUEUE_TIMER_THRESH 1 /* 1 ms */ + +#define RBDR_SIZE RBDR_SIZE0 +#define RCV_BUF_COUNT BIT_ULL((RBDR_SIZE + 13)) +#define RBDR_THRESH (RCV_BUF_COUNT / 2) +#define DMA_BUFFER_LEN 2048 /* In multiples of 128bytes */ +#define RCV_FRAG_LEN DMA_BUFFER_LEN + +#define MAX_CQES_FOR_TX ((SND_QUEUE_LEN / MIN_SQ_DESC_PER_PKT_XMIT) *\ + MAX_CQE_PER_PKT_XMIT) +#define RQ_CQ_DROP ((CMP_QUEUE_LEN - MAX_CQES_FOR_TX) / 256) + +/* Descriptor size */ +#define SND_QUEUE_DESC_SIZE 16 /* 128 bits */ +#define CMP_QUEUE_DESC_SIZE 512 + +/* Buffer / descriptor alignments */ +#define NICVF_RCV_BUF_ALIGN 7 +#define NICVF_RCV_BUF_ALIGN_BYTES BIT_ULL(NICVF_RCV_BUF_ALIGN) +#define NICVF_CQ_BASE_ALIGN_BYTES 512 /* 9 bits */ +#define NICVF_SQ_BASE_ALIGN_BYTES 128 /* 7 bits */ + +#define NICVF_ALIGNED_ADDR(ADDR, ALIGN_BYTES) ALIGN(ADDR, ALIGN_BYTES) + +/* Queue enable/disable */ +#define NICVF_SQ_EN BIT_ULL(19) + +/* Queue reset */ +#define NICVF_CQ_RESET BIT_ULL(41) +#define NICVF_SQ_RESET BIT_ULL(17) +#define NICVF_RBDR_RESET BIT_ULL(43) + +enum CQ_RX_ERRLVL_E { + CQ_ERRLVL_MAC, + CQ_ERRLVL_L2, + CQ_ERRLVL_L3, + CQ_ERRLVL_L4, +}; + +enum CQ_RX_ERROP_E { + CQ_RX_ERROP_RE_NONE = 0x0, + CQ_RX_ERROP_RE_PARTIAL = 0x1, + CQ_RX_ERROP_RE_JABBER = 0x2, + CQ_RX_ERROP_RE_FCS = 0x7, + CQ_RX_ERROP_RE_TERMINATE = 0x9, + CQ_RX_ERROP_RE_RX_CTL = 0xb, + CQ_RX_ERROP_PREL2_ERR = 0x1f, + CQ_RX_ERROP_L2_FRAGMENT = 0x20, + CQ_RX_ERROP_L2_OVERRUN = 0x21, + CQ_RX_ERROP_L2_PFCS = 0x22, + CQ_RX_ERROP_L2_PUNY = 0x23, + CQ_RX_ERROP_L2_MAL = 0x24, + CQ_RX_ERROP_L2_OVERSIZE = 0x25, + CQ_RX_ERROP_L2_UNDERSIZE = 0x26, + CQ_RX_ERROP_L2_LENMISM = 0x27, + CQ_RX_ERROP_L2_PCLP = 0x28, + CQ_RX_ERROP_IP_NOT = 0x41, + CQ_RX_ERROP_IP_CSUM_ERR = 0x42, + CQ_RX_ERROP_IP_MAL = 0x43, + CQ_RX_ERROP_IP_MALD = 0x44, + CQ_RX_ERROP_IP_HOP = 0x45, + CQ_RX_ERROP_L3_ICRC = 0x46, + CQ_RX_ERROP_L3_PCLP = 0x47, + CQ_RX_ERROP_L4_MAL = 0x61, + CQ_RX_ERROP_L4_CHK = 0x62, + CQ_RX_ERROP_UDP_LEN = 0x63, + CQ_RX_ERROP_L4_PORT = 0x64, + CQ_RX_ERROP_TCP_FLAG = 0x65, + CQ_RX_ERROP_TCP_OFFSET = 0x66, + CQ_RX_ERROP_L4_PCLP = 0x67, + CQ_RX_ERROP_RBDR_TRUNC = 0x70, +}; + +enum CQ_TX_ERROP_E { + CQ_TX_ERROP_GOOD = 0x0, + CQ_TX_ERROP_DESC_FAULT = 0x10, + CQ_TX_ERROP_HDR_CONS_ERR = 0x11, + CQ_TX_ERROP_SUBDC_ERR = 0x12, + CQ_TX_ERROP_IMM_SIZE_OFLOW = 0x80, + CQ_TX_ERROP_DATA_SEQUENCE_ERR = 0x81, + CQ_TX_ERROP_MEM_SEQUENCE_ERR = 0x82, + CQ_TX_ERROP_LOCK_VIOL = 0x83, + CQ_TX_ERROP_DATA_FAULT = 0x84, + CQ_TX_ERROP_TSTMP_CONFLICT = 0x85, + CQ_TX_ERROP_TSTMP_TIMEOUT = 0x86, + CQ_TX_ERROP_MEM_FAULT = 0x87, + CQ_TX_ERROP_CK_OVERLAP = 0x88, + CQ_TX_ERROP_CK_OFLOW = 0x89, + CQ_TX_ERROP_ENUM_LAST = 0x8a, +}; + +struct cmp_queue_stats { + struct rx_stats { + struct { + u64 mac_errs; + u64 l2_errs; + u64 l3_errs; + u64 l4_errs; + } errlvl; + struct { + u64 good; + u64 partial_pkts; + u64 jabber_errs; + u64 fcs_errs; + u64 terminate_errs; + u64 bgx_rx_errs; + u64 prel2_errs; + u64 l2_frags; + u64 l2_overruns; + u64 l2_pfcs; + u64 l2_puny; + u64 l2_hdr_malformed; + u64 l2_oversize; + u64 l2_undersize; + u64 l2_len_mismatch; + u64 l2_pclp; + u64 non_ip; + u64 ip_csum_err; + u64 ip_hdr_malformed; + u64 ip_payload_malformed; + u64 ip_hop_errs; + u64 l3_icrc_errs; + u64 l3_pclp; + u64 l4_malformed; + u64 l4_csum_errs; + u64 udp_len_err; + u64 bad_l4_port; + u64 bad_tcp_flag; + u64 tcp_offset_errs; + u64 l4_pclp; + u64 pkt_truncated; + } errop; + } rx; + struct tx_stats { + u64 good; + u64 desc_fault; + u64 hdr_cons_err; + u64 subdesc_err; + u64 imm_size_oflow; + u64 data_seq_err; + u64 mem_seq_err; + u64 lock_viol; + u64 data_fault; + u64 tstmp_conflict; + u64 tstmp_timeout; + u64 mem_fault; + u64 csum_overlap; + u64 csum_overflow; + } tx; +}; + +enum RQ_SQ_STATS { + RQ_SQ_STATS_OCTS, + RQ_SQ_STATS_PKTS, +}; + +struct rx_tx_queue_stats { + u64 bytes; + u64 pkts; +}; + +struct q_desc_mem { + uintptr_t dma; + u64 size; + u16 q_len; + uintptr_t phys_base; + void *base; + void *unalign_base; + bool allocated; +}; + +struct rbdr { + bool enable; + u32 dma_size; + u32 thresh; /* Threshold level for interrupt */ + void *desc; + u32 head; + u32 tail; + struct q_desc_mem dmem; + uintptr_t buf_mem; + uintptr_t buffers; +}; + +struct rcv_queue { + bool enable; + struct rbdr *rbdr_start; + struct rbdr *rbdr_cont; + bool en_tcp_reassembly; + u8 cq_qs; /* CQ's QS to which this RQ is assigned */ + u8 cq_idx; /* CQ index (0 to 7) in the QS */ + u8 cont_rbdr_qs; /* Continue buffer ptrs - QS num */ + u8 cont_qs_rbdr_idx; /* RBDR idx in the cont QS */ + u8 start_rbdr_qs; /* First buffer ptrs - QS num */ + u8 start_qs_rbdr_idx; /* RBDR idx in the above QS */ + u8 caching; + struct rx_tx_queue_stats stats; +}; + +struct cmp_queue { + bool enable; + u16 intr_timer_thresh; + u16 thresh; + void *desc; + struct q_desc_mem dmem; + struct cmp_queue_stats stats; +}; + +struct snd_queue { + bool enable; + u8 cq_qs; /* CQ's QS to which this SQ is pointing */ + u8 cq_idx; /* CQ index (0 to 7) in the above QS */ + u16 thresh; + u32 free_cnt; + u32 head; + u32 tail; + u64 *skbuff; + void *desc; + struct q_desc_mem dmem; + struct rx_tx_queue_stats stats; +}; + +struct queue_set { + bool enable; + bool be_en; + u8 vnic_id; + u8 rq_cnt; + u8 cq_cnt; + u64 cq_len; + u8 sq_cnt; + u64 sq_len; + u8 rbdr_cnt; + u64 rbdr_len; + struct rcv_queue rq[MAX_RCV_QUEUES_PER_QS]; + struct cmp_queue cq[MAX_CMP_QUEUES_PER_QS]; + struct snd_queue sq[MAX_SND_QUEUES_PER_QS]; + struct rbdr rbdr[MAX_RCV_BUF_DESC_RINGS_PER_QS]; +}; + +#define GET_RBDR_DESC(RING, idx)\ + (&(((struct rbdr_entry_t *)((RING)->desc))[idx])) +#define GET_SQ_DESC(RING, idx)\ + (&(((struct sq_hdr_subdesc *)((RING)->desc))[idx])) +#define GET_CQ_DESC(RING, idx)\ + (&(((union cq_desc_t *)((RING)->desc))[idx])) + +/* CQ status bits */ +#define CQ_WR_FULL BIT(26) +#define CQ_WR_DISABLE BIT(25) +#define CQ_WR_FAULT BIT(24) +#define CQ_CQE_COUNT (0xFFFF << 0) + +#define CQ_ERR_MASK (CQ_WR_FULL | CQ_WR_DISABLE | CQ_WR_FAULT) + +int nicvf_set_qset_resources(struct nicvf *nic); +int nicvf_config_data_transfer(struct nicvf *nic, bool enable); +void nicvf_qset_config(struct nicvf *nic, bool enable); +void nicvf_cmp_queue_config(struct nicvf *nic, struct queue_set *qs, + int qidx, bool enable); + +void nicvf_sq_enable(struct nicvf *nic, struct snd_queue *sq, int qidx); +void nicvf_sq_disable(struct nicvf *nic, int qidx); +void nicvf_put_sq_desc(struct snd_queue *sq, int desc_cnt); +void nicvf_sq_free_used_descs(struct udevice *dev, + struct snd_queue *sq, int qidx); +int nicvf_sq_append_pkt(struct nicvf *nic, void *pkt, size_t pkt_len); + +void *nicvf_get_rcv_pkt(struct nicvf *nic, void *cq_desc, size_t *pkt_len); +void nicvf_refill_rbdr(struct nicvf *nic); + +void nicvf_enable_intr(struct nicvf *nic, int int_type, int q_idx); +void nicvf_disable_intr(struct nicvf *nic, int int_type, int q_idx); +void nicvf_clear_intr(struct nicvf *nic, int int_type, int q_idx); +int nicvf_is_intr_enabled(struct nicvf *nic, int int_type, int q_idx); + +/* Register access APIs */ +void nicvf_reg_write(struct nicvf *nic, u64 offset, u64 val); +u64 nicvf_reg_read(struct nicvf *nic, u64 offset); +void nicvf_qset_reg_write(struct nicvf *nic, u64 offset, u64 val); +u64 nicvf_qset_reg_read(struct nicvf *nic, u64 offset); +void nicvf_queue_reg_write(struct nicvf *nic, u64 offset, + u64 qidx, u64 val); +u64 nicvf_queue_reg_read(struct nicvf *nic, u64 offset, u64 qidx); + +/* Stats */ +void nicvf_update_rq_stats(struct nicvf *nic, int rq_idx); +void nicvf_update_sq_stats(struct nicvf *nic, int sq_idx); +int nicvf_check_cqe_rx_errs(struct nicvf *nic, + struct cmp_queue *cq, void *cq_desc); +int nicvf_check_cqe_tx_errs(struct nicvf *nic, + struct cmp_queue *cq, void *cq_desc); +#endif /* NICVF_QUEUES_H */ diff --git a/drivers/net/octeontx/q_struct.h b/drivers/net/octeontx/q_struct.h new file mode 100644 index 00000000000..87abb132f50 --- /dev/null +++ b/drivers/net/octeontx/q_struct.h @@ -0,0 +1,695 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (C) 2018 Marvell International Ltd. + */ + +#ifndef Q_STRUCT_H +#define Q_STRUCT_H + +/* Load transaction types for reading segment bytes specified by + * NIC_SEND_GATHER_S[LD_TYPE]. + */ +enum nic_send_ld_type_e { + NIC_SEND_LD_TYPE_E_LDD = 0x0, + NIC_SEND_LD_TYPE_E_LDT = 0x1, + NIC_SEND_LD_TYPE_E_LDWB = 0x2, + NIC_SEND_LD_TYPE_E_ENUM_LAST = 0x3, +}; + +enum ether_type_algorithm { + ETYPE_ALG_NONE = 0x0, + ETYPE_ALG_SKIP = 0x1, + ETYPE_ALG_ENDPARSE = 0x2, + ETYPE_ALG_VLAN = 0x3, + ETYPE_ALG_VLAN_STRIP = 0x4, +}; + +enum layer3_type { + L3TYPE_NONE = 0x00, + L3TYPE_GRH = 0x01, + L3TYPE_IPV4 = 0x04, + L3TYPE_IPV4_OPTIONS = 0x05, + L3TYPE_IPV6 = 0x06, + L3TYPE_IPV6_OPTIONS = 0x07, + L3TYPE_ET_STOP = 0x0D, + L3TYPE_OTHER = 0x0E, +}; + +enum layer4_type { + L4TYPE_NONE = 0x00, + L4TYPE_IPSEC_ESP = 0x01, + L4TYPE_IPFRAG = 0x02, + L4TYPE_IPCOMP = 0x03, + L4TYPE_TCP = 0x04, + L4TYPE_UDP = 0x05, + L4TYPE_SCTP = 0x06, + L4TYPE_GRE = 0x07, + L4TYPE_ROCE_BTH = 0x08, + L4TYPE_OTHER = 0x0E, +}; + +/* CPI and RSSI configuration */ +enum cpi_algorithm_type { + CPI_ALG_NONE = 0x0, + CPI_ALG_VLAN = 0x1, + CPI_ALG_VLAN16 = 0x2, + CPI_ALG_DIFF = 0x3, +}; + +enum rss_algorithm_type { + RSS_ALG_NONE = 0x00, + RSS_ALG_PORT = 0x01, + RSS_ALG_IP = 0x02, + RSS_ALG_TCP_IP = 0x03, + RSS_ALG_UDP_IP = 0x04, + RSS_ALG_SCTP_IP = 0x05, + RSS_ALG_GRE_IP = 0x06, + RSS_ALG_ROCE = 0x07, +}; + +enum rss_hash_cfg { + RSS_HASH_L2ETC = 0x00, + RSS_HASH_IP = 0x01, + RSS_HASH_TCP = 0x02, + RSS_TCP_SYN_DIS = 0x03, + RSS_HASH_UDP = 0x04, + RSS_HASH_L4ETC = 0x05, + RSS_HASH_ROCE = 0x06, + RSS_L3_BIDI = 0x07, + RSS_L4_BIDI = 0x08, +}; + +/* Completion queue entry types */ +enum cqe_type { + CQE_TYPE_INVALID = 0x0, + CQE_TYPE_RX = 0x2, + CQE_TYPE_RX_SPLIT = 0x3, + CQE_TYPE_RX_TCP = 0x4, + CQE_TYPE_SEND = 0x8, + CQE_TYPE_SEND_PTP = 0x9, +}; + +enum cqe_rx_tcp_status { + CQE_RX_STATUS_VALID_TCP_CNXT = 0x00, + CQE_RX_STATUS_INVALID_TCP_CNXT = 0x0F, +}; + +enum cqe_send_status { + CQE_SEND_STATUS_GOOD = 0x00, + CQE_SEND_STATUS_DESC_FAULT = 0x01, + CQE_SEND_STATUS_HDR_CONS_ERR = 0x11, + CQE_SEND_STATUS_SUBDESC_ERR = 0x12, + CQE_SEND_STATUS_IMM_SIZE_OFLOW = 0x80, + CQE_SEND_STATUS_CRC_SEQ_ERR = 0x81, + CQE_SEND_STATUS_DATA_SEQ_ERR = 0x82, + CQE_SEND_STATUS_MEM_SEQ_ERR = 0x83, + CQE_SEND_STATUS_LOCK_VIOL = 0x84, + CQE_SEND_STATUS_LOCK_UFLOW = 0x85, + CQE_SEND_STATUS_DATA_FAULT = 0x86, + CQE_SEND_STATUS_TSTMP_CONFLICT = 0x87, + CQE_SEND_STATUS_TSTMP_TIMEOUT = 0x88, + CQE_SEND_STATUS_MEM_FAULT = 0x89, + CQE_SEND_STATUS_CSUM_OVERLAP = 0x8A, + CQE_SEND_STATUS_CSUM_OVERFLOW = 0x8B, +}; + +enum cqe_rx_tcp_end_reason { + CQE_RX_TCP_END_FIN_FLAG_DET = 0, + CQE_RX_TCP_END_INVALID_FLAG = 1, + CQE_RX_TCP_END_TIMEOUT = 2, + CQE_RX_TCP_END_OUT_OF_SEQ = 3, + CQE_RX_TCP_END_PKT_ERR = 4, + CQE_RX_TCP_END_QS_DISABLED = 0x0F, +}; + +/* Packet protocol level error enumeration */ +enum cqe_rx_err_level { + CQE_RX_ERRLVL_RE = 0x0, + CQE_RX_ERRLVL_L2 = 0x1, + CQE_RX_ERRLVL_L3 = 0x2, + CQE_RX_ERRLVL_L4 = 0x3, +}; + +/* Packet protocol level error type enumeration */ +enum cqe_rx_err_opcode { + CQE_RX_ERR_RE_NONE = 0x0, + CQE_RX_ERR_RE_PARTIAL = 0x1, + CQE_RX_ERR_RE_JABBER = 0x2, + CQE_RX_ERR_RE_FCS = 0x7, + CQE_RX_ERR_RE_TERMINATE = 0x9, + CQE_RX_ERR_RE_RX_CTL = 0xb, + CQE_RX_ERR_PREL2_ERR = 0x1f, + CQE_RX_ERR_L2_FRAGMENT = 0x20, + CQE_RX_ERR_L2_OVERRUN = 0x21, + CQE_RX_ERR_L2_PFCS = 0x22, + CQE_RX_ERR_L2_PUNY = 0x23, + CQE_RX_ERR_L2_MAL = 0x24, + CQE_RX_ERR_L2_OVERSIZE = 0x25, + CQE_RX_ERR_L2_UNDERSIZE = 0x26, + CQE_RX_ERR_L2_LENMISM = 0x27, + CQE_RX_ERR_L2_PCLP = 0x28, + CQE_RX_ERR_IP_NOT = 0x41, + CQE_RX_ERR_IP_CHK = 0x42, + CQE_RX_ERR_IP_MAL = 0x43, + CQE_RX_ERR_IP_MALD = 0x44, + CQE_RX_ERR_IP_HOP = 0x45, + CQE_RX_ERR_L3_ICRC = 0x46, + CQE_RX_ERR_L3_PCLP = 0x47, + CQE_RX_ERR_L4_MAL = 0x61, + CQE_RX_ERR_L4_CHK = 0x62, + CQE_RX_ERR_UDP_LEN = 0x63, + CQE_RX_ERR_L4_PORT = 0x64, + CQE_RX_ERR_TCP_FLAG = 0x65, + CQE_RX_ERR_TCP_OFFSET = 0x66, + CQE_RX_ERR_L4_PCLP = 0x67, + CQE_RX_ERR_RBDR_TRUNC = 0x70, +}; + +struct cqe_rx_t { +#if defined(__BIG_ENDIAN_BITFIELD) + u64 cqe_type:4; /* W0 */ + u64 stdn_fault:1; + u64 rsvd0:1; + u64 rq_qs:7; + u64 rq_idx:3; + u64 rsvd1:12; + u64 rss_alg:4; + u64 rsvd2:4; + u64 rb_cnt:4; + u64 vlan_found:1; + u64 vlan_stripped:1; + u64 vlan2_found:1; + u64 vlan2_stripped:1; + u64 l4_type:4; + u64 l3_type:4; + u64 l2_present:1; + u64 err_level:3; + u64 err_opcode:8; + + u64 pkt_len:16; /* W1 */ + u64 l2_ptr:8; + u64 l3_ptr:8; + u64 l4_ptr:8; + u64 cq_pkt_len:8; + u64 align_pad:3; + u64 rsvd3:1; + u64 chan:12; + + u64 rss_tag:32; /* W2 */ + u64 vlan_tci:16; + u64 vlan_ptr:8; + u64 vlan2_ptr:8; + + u64 rb3_sz:16; /* W3 */ + u64 rb2_sz:16; + u64 rb1_sz:16; + u64 rb0_sz:16; + + u64 rb7_sz:16; /* W4 */ + u64 rb6_sz:16; + u64 rb5_sz:16; + u64 rb4_sz:16; + + u64 rb11_sz:16; /* W5 */ + u64 rb10_sz:16; + u64 rb9_sz:16; + u64 rb8_sz:16; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + u64 err_opcode:8; + u64 err_level:3; + u64 l2_present:1; + u64 l3_type:4; + u64 l4_type:4; + u64 vlan2_stripped:1; + u64 vlan2_found:1; + u64 vlan_stripped:1; + u64 vlan_found:1; + u64 rb_cnt:4; + u64 rsvd2:4; + u64 rss_alg:4; + u64 rsvd1:12; + u64 rq_idx:3; + u64 rq_qs:7; + u64 rsvd0:1; + u64 stdn_fault:1; + u64 cqe_type:4; /* W0 */ + u64 chan:12; + u64 rsvd3:1; + u64 align_pad:3; + u64 cq_pkt_len:8; + u64 l4_ptr:8; + u64 l3_ptr:8; + u64 l2_ptr:8; + u64 pkt_len:16; /* W1 */ + u64 vlan2_ptr:8; + u64 vlan_ptr:8; + u64 vlan_tci:16; + u64 rss_tag:32; /* W2 */ + u64 rb0_sz:16; + u64 rb1_sz:16; + u64 rb2_sz:16; + u64 rb3_sz:16; /* W3 */ + u64 rb4_sz:16; + u64 rb5_sz:16; + u64 rb6_sz:16; + u64 rb7_sz:16; /* W4 */ + u64 rb8_sz:16; + u64 rb9_sz:16; + u64 rb10_sz:16; + u64 rb11_sz:16; /* W5 */ +#endif + u64 rb0_ptr:64; + u64 rb1_ptr:64; + u64 rb2_ptr:64; + u64 rb3_ptr:64; + u64 rb4_ptr:64; + u64 rb5_ptr:64; + u64 rb6_ptr:64; + u64 rb7_ptr:64; + u64 rb8_ptr:64; + u64 rb9_ptr:64; + u64 rb10_ptr:64; + u64 rb11_ptr:64; +}; + +struct cqe_rx_tcp_err_t { +#if defined(__BIG_ENDIAN_BITFIELD) + u64 cqe_type:4; /* W0 */ + u64 rsvd0:60; + + u64 rsvd1:4; /* W1 */ + u64 partial_first:1; + u64 rsvd2:27; + u64 rbdr_bytes:8; + u64 rsvd3:24; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + u64 rsvd0:60; + u64 cqe_type:4; + + u64 rsvd3:24; + u64 rbdr_bytes:8; + u64 rsvd2:27; + u64 partial_first:1; + u64 rsvd1:4; +#endif +}; + +struct cqe_rx_tcp_t { +#if defined(__BIG_ENDIAN_BITFIELD) + u64 cqe_type:4; /* W0 */ + u64 rsvd0:52; + u64 cq_tcp_status:8; + + u64 rsvd1:32; /* W1 */ + u64 tcp_cntx_bytes:8; + u64 rsvd2:8; + u64 tcp_err_bytes:16; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + u64 cq_tcp_status:8; + u64 rsvd0:52; + u64 cqe_type:4; /* W0 */ + + u64 tcp_err_bytes:16; + u64 rsvd2:8; + u64 tcp_cntx_bytes:8; + u64 rsvd1:32; /* W1 */ +#endif +}; + +struct cqe_send_t { +#if defined(__BIG_ENDIAN_BITFIELD) + u64 cqe_type:4; /* W0 */ + u64 rsvd0:4; + u64 sqe_ptr:16; + u64 rsvd1:4; + u64 rsvd2:10; + u64 sq_qs:7; + u64 sq_idx:3; + u64 rsvd3:8; + u64 send_status:8; + + u64 ptp_timestamp:64; /* W1 */ +#elif defined(__LITTLE_ENDIAN_BITFIELD) + u64 send_status:8; + u64 rsvd3:8; + u64 sq_idx:3; + u64 sq_qs:7; + u64 rsvd2:10; + u64 rsvd1:4; + u64 sqe_ptr:16; + u64 rsvd0:4; + u64 cqe_type:4; /* W0 */ + + u64 ptp_timestamp:64; /* W1 */ +#endif +}; + +union cq_desc_t { + u64 u[64]; + struct cqe_send_t snd_hdr; + struct cqe_rx_t rx_hdr; + struct cqe_rx_tcp_t rx_tcp_hdr; + struct cqe_rx_tcp_err_t rx_tcp_err_hdr; +}; + +struct rbdr_entry_t { +#if defined(__BIG_ENDIAN_BITFIELD) + u64 rsvd0:15; + u64 buf_addr:42; + u64 cache_align:7; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + u64 cache_align:7; + u64 buf_addr:42; + u64 rsvd0:15; +#endif +}; + +/* TCP reassembly context */ +struct rbe_tcp_cnxt_t { +#if defined(__BIG_ENDIAN_BITFIELD) + u64 tcp_pkt_cnt:12; + u64 rsvd1:4; + u64 align_hdr_bytes:4; + u64 align_ptr_bytes:4; + u64 ptr_bytes:16; + u64 rsvd2:24; + u64 cqe_type:4; + u64 rsvd0:54; + u64 tcp_end_reason:2; + u64 tcp_status:4; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + u64 tcp_status:4; + u64 tcp_end_reason:2; + u64 rsvd0:54; + u64 cqe_type:4; + u64 rsvd2:24; + u64 ptr_bytes:16; + u64 align_ptr_bytes:4; + u64 align_hdr_bytes:4; + u64 rsvd1:4; + u64 tcp_pkt_cnt:12; +#endif +}; + +/* Always Big endian */ +struct rx_hdr_t { + u64 opaque:32; + u64 rss_flow:8; + u64 skip_length:6; + u64 disable_rss:1; + u64 disable_tcp_reassembly:1; + u64 nodrop:1; + u64 dest_alg:2; + u64 rsvd0:2; + u64 dest_rq:11; +}; + +enum send_l4_csum_type { + SEND_L4_CSUM_DISABLE = 0x00, + SEND_L4_CSUM_UDP = 0x01, + SEND_L4_CSUM_TCP = 0x02, + SEND_L4_CSUM_SCTP = 0x03, +}; + +enum send_crc_alg { + SEND_CRCALG_CRC32 = 0x00, + SEND_CRCALG_CRC32C = 0x01, + SEND_CRCALG_ICRC = 0x02, +}; + +enum send_load_type { + SEND_LD_TYPE_LDD = 0x00, + SEND_LD_TYPE_LDT = 0x01, + SEND_LD_TYPE_LDWB = 0x02, +}; + +enum send_mem_alg_type { + SEND_MEMALG_SET = 0x00, + SEND_MEMALG_ADD = 0x08, + SEND_MEMALG_SUB = 0x09, + SEND_MEMALG_ADDLEN = 0x0A, + SEND_MEMALG_SUBLEN = 0x0B, +}; + +enum send_mem_dsz_type { + SEND_MEMDSZ_B64 = 0x00, + SEND_MEMDSZ_B32 = 0x01, + SEND_MEMDSZ_B8 = 0x03, +}; + +enum sq_subdesc_type { + SQ_DESC_TYPE_INVALID = 0x00, + SQ_DESC_TYPE_HEADER = 0x01, + SQ_DESC_TYPE_CRC = 0x02, + SQ_DESC_TYPE_IMMEDIATE = 0x03, + SQ_DESC_TYPE_GATHER = 0x04, + SQ_DESC_TYPE_MEMORY = 0x05, +}; + +struct sq_crc_subdesc { +#if defined(__BIG_ENDIAN_BITFIELD) + u64 rsvd1:32; + u64 crc_ival:32; + u64 subdesc_type:4; + u64 crc_alg:2; + u64 rsvd0:10; + u64 crc_insert_pos:16; + u64 hdr_start:16; + u64 crc_len:16; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + u64 crc_len:16; + u64 hdr_start:16; + u64 crc_insert_pos:16; + u64 rsvd0:10; + u64 crc_alg:2; + u64 subdesc_type:4; + u64 crc_ival:32; + u64 rsvd1:32; +#endif +}; + +struct sq_gather_subdesc { +#if defined(__BIG_ENDIAN_BITFIELD) + u64 subdesc_type:4; /* W0 */ + u64 ld_type:2; + u64 rsvd0:42; + u64 size:16; + + u64 rsvd1:15; /* W1 */ + u64 addr:49; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + u64 size:16; + u64 rsvd0:42; + u64 ld_type:2; + u64 subdesc_type:4; /* W0 */ + + u64 addr:49; + u64 rsvd1:15; /* W1 */ +#endif +}; + +/* SQ immediate subdescriptor */ +struct sq_imm_subdesc { +#if defined(__BIG_ENDIAN_BITFIELD) + u64 subdesc_type:4; /* W0 */ + u64 rsvd0:46; + u64 len:14; + + u64 data:64; /* W1 */ +#elif defined(__LITTLE_ENDIAN_BITFIELD) + u64 len:14; + u64 rsvd0:46; + u64 subdesc_type:4; /* W0 */ + + u64 data:64; /* W1 */ +#endif +}; + +struct sq_mem_subdesc { +#if defined(__BIG_ENDIAN_BITFIELD) + u64 subdesc_type:4; /* W0 */ + u64 mem_alg:4; + u64 mem_dsz:2; + u64 wmem:1; + u64 rsvd0:21; + u64 offset:32; + + u64 rsvd1:15; /* W1 */ + u64 addr:49; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + u64 offset:32; + u64 rsvd0:21; + u64 wmem:1; + u64 mem_dsz:2; + u64 mem_alg:4; + u64 subdesc_type:4; /* W0 */ + + u64 addr:49; + u64 rsvd1:15; /* W1 */ +#endif +}; + +struct sq_hdr_subdesc { +#if defined(__BIG_ENDIAN_BITFIELD) + u64 subdesc_type:4; + u64 tso:1; + u64 post_cqe:1; /* Post CQE on no error also */ + u64 dont_send:1; + u64 tstmp:1; + u64 subdesc_cnt:8; + u64 csum_l4:2; + u64 csum_l3:1; + u64 rsvd0:5; + u64 l4_offset:8; + u64 l3_offset:8; + u64 rsvd1:4; + u64 tot_len:20; /* W0 */ + + u64 tso_sdc_cont:8; + u64 tso_sdc_first:8; + u64 tso_l4_offset:8; + u64 tso_flags_last:12; + u64 tso_flags_first:12; + u64 rsvd2:2; + u64 tso_max_paysize:14; /* W1 */ +#elif defined(__LITTLE_ENDIAN_BITFIELD) + u64 tot_len:20; + u64 rsvd1:4; + u64 l3_offset:8; + u64 l4_offset:8; + u64 rsvd0:5; + u64 csum_l3:1; + u64 csum_l4:2; + u64 subdesc_cnt:8; + u64 tstmp:1; + u64 dont_send:1; + u64 post_cqe:1; /* Post CQE on no error also */ + u64 tso:1; + u64 subdesc_type:4; /* W0 */ + + u64 tso_max_paysize:14; + u64 rsvd2:2; + u64 tso_flags_first:12; + u64 tso_flags_last:12; + u64 tso_l4_offset:8; + u64 tso_sdc_first:8; + u64 tso_sdc_cont:8; /* W1 */ +#endif +}; + +/* Queue config register formats */ +struct rq_cfg { +#if defined(__BIG_ENDIAN_BITFIELD) + u64 reserved_2_63:62; + u64 ena:1; + u64 tcp_ena:1; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + u64 tcp_ena:1; + u64 ena:1; + u64 reserved_2_63:62; +#endif +}; + +struct cq_cfg { +#if defined(__BIG_ENDIAN_BITFIELD) + u64 reserved_43_63:21; + u64 ena:1; + u64 reset:1; + u64 caching:1; + u64 reserved_35_39:5; + u64 qsize:3; + u64 reserved_25_31:7; + u64 avg_con:9; + u64 reserved_0_15:16; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + u64 reserved_0_15:16; + u64 avg_con:9; + u64 reserved_25_31:7; + u64 qsize:3; + u64 reserved_35_39:5; + u64 caching:1; + u64 reset:1; + u64 ena:1; + u64 reserved_43_63:21; +#endif +}; + +struct sq_cfg { +#if defined(__BIG_ENDIAN_BITFIELD) + u64 reserved_20_63:44; + u64 ena:1; + u64 reserved_18_18:1; + u64 reset:1; + u64 ldwb:1; + u64 reserved_11_15:5; + u64 qsize:3; + u64 reserved_3_7:5; + u64 tstmp_bgx_intf:3; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + u64 tstmp_bgx_intf:3; + u64 reserved_3_7:5; + u64 qsize:3; + u64 reserved_11_15:5; + u64 ldwb:1; + u64 reset:1; + u64 reserved_18_18:1; + u64 ena:1; + u64 reserved_20_63:44; +#endif +}; + +struct rbdr_cfg { +#if defined(__BIG_ENDIAN_BITFIELD) + u64 reserved_45_63:19; + u64 ena:1; + u64 reset:1; + u64 ldwb:1; + u64 reserved_36_41:6; + u64 qsize:4; + u64 reserved_25_31:7; + u64 avg_con:9; + u64 reserved_12_15:4; + u64 lines:12; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + u64 lines:12; + u64 reserved_12_15:4; + u64 avg_con:9; + u64 reserved_25_31:7; + u64 qsize:4; + u64 reserved_36_41:6; + u64 ldwb:1; + u64 reset:1; + u64 ena: 1; + u64 reserved_45_63:19; +#endif +}; + +struct qs_cfg { +#if defined(__BIG_ENDIAN_BITFIELD) + u64 reserved_32_63:32; + u64 ena:1; + u64 reserved_27_30:4; + u64 sq_ins_ena:1; + u64 sq_ins_pos:6; + u64 lock_ena:1; + u64 lock_viol_cqe_ena:1; + u64 send_tstmp_ena:1; + u64 be:1; + u64 reserved_7_15:9; + u64 vnic:7; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + u64 vnic:7; + u64 reserved_7_15:9; + u64 be:1; + u64 send_tstmp_ena:1; + u64 lock_viol_cqe_ena:1; + u64 lock_ena:1; + u64 sq_ins_pos:6; + u64 sq_ins_ena:1; + u64 reserved_27_30:4; + u64 ena:1; + u64 reserved_32_63:32; +#endif +}; + +#endif /* Q_STRUCT_H */ diff --git a/drivers/net/octeontx/smi.c b/drivers/net/octeontx/smi.c new file mode 100644 index 00000000000..8e2c3ca5a30 --- /dev/null +++ b/drivers/net/octeontx/smi.c @@ -0,0 +1,380 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Marvell International Ltd. + */ + +#include <dm.h> +#include <malloc.h> +#include <miiphy.h> +#include <misc.h> +#include <pci.h> +#include <pci_ids.h> +#include <phy.h> +#include <asm/io.h> +#include <linux/ctype.h> +#include <linux/delay.h> + +#define PCI_DEVICE_ID_OCTEONTX_SMI 0xA02B + +DECLARE_GLOBAL_DATA_PTR; + +enum octeontx_smi_mode { + CLAUSE22 = 0, + CLAUSE45 = 1, +}; + +enum { + SMI_OP_C22_WRITE = 0, + SMI_OP_C22_READ = 1, + + SMI_OP_C45_ADDR = 0, + SMI_OP_C45_WRITE = 1, + SMI_OP_C45_PRIA = 2, + SMI_OP_C45_READ = 3, +}; + +union smi_x_clk { + u64 u; + struct smi_x_clk_s { + int phase:8; + int sample:4; + int preamble:1; + int clk_idle:1; + int reserved_14_14:1; + int sample_mode:1; + int sample_hi:5; + int reserved_21_23:3; + int mode:1; + } s; +}; + +union smi_x_cmd { + u64 u; + struct smi_x_cmd_s { + int reg_adr:5; + int reserved_5_7:3; + int phy_adr:5; + int reserved_13_15:3; + int phy_op:2; + } s; +}; + +union smi_x_wr_dat { + u64 u; + struct smi_x_wr_dat_s { + unsigned int dat:16; + int val:1; + int pending:1; + } s; +}; + +union smi_x_rd_dat { + u64 u; + struct smi_x_rd_dat_s { + unsigned int dat:16; + int val:1; + int pending:1; + } s; +}; + +union smi_x_en { + u64 u; + struct smi_x_en_s { + int en:1; + } s; +}; + +#define SMI_X_RD_DAT 0x10ull +#define SMI_X_WR_DAT 0x08ull +#define SMI_X_CMD 0x00ull +#define SMI_X_CLK 0x18ull +#define SMI_X_EN 0x20ull + +struct octeontx_smi_priv { + void __iomem *baseaddr; + enum octeontx_smi_mode mode; +}; + +#define MDIO_TIMEOUT 10000 + +void octeontx_smi_setmode(struct mii_dev *bus, enum octeontx_smi_mode mode) +{ + struct octeontx_smi_priv *priv = bus->priv; + union smi_x_clk smix_clk; + + smix_clk.u = readq(priv->baseaddr + SMI_X_CLK); + smix_clk.s.mode = mode; + smix_clk.s.preamble = mode == CLAUSE45; + writeq(smix_clk.u, priv->baseaddr + SMI_X_CLK); + + priv->mode = mode; +} + +int octeontx_c45_addr(struct mii_dev *bus, int addr, int devad, int regnum) +{ + struct octeontx_smi_priv *priv = bus->priv; + + union smi_x_cmd smix_cmd; + union smi_x_wr_dat smix_wr_dat; + unsigned long timeout = MDIO_TIMEOUT; + + smix_wr_dat.u = 0; + smix_wr_dat.s.dat = regnum; + + writeq(smix_wr_dat.u, priv->baseaddr + SMI_X_WR_DAT); + + smix_cmd.u = 0; + smix_cmd.s.phy_op = SMI_OP_C45_ADDR; + smix_cmd.s.phy_adr = addr; + smix_cmd.s.reg_adr = devad; + + writeq(smix_cmd.u, priv->baseaddr + SMI_X_CMD); + + do { + smix_wr_dat.u = readq(priv->baseaddr + SMI_X_WR_DAT); + udelay(100); + timeout--; + } while (smix_wr_dat.s.pending && timeout); + + return timeout == 0; +} + +int octeontx_phy_read(struct mii_dev *bus, int addr, int devad, int regnum) +{ + struct octeontx_smi_priv *priv = bus->priv; + union smi_x_cmd smix_cmd; + union smi_x_rd_dat smix_rd_dat; + unsigned long timeout = MDIO_TIMEOUT; + int ret; + + enum octeontx_smi_mode mode = (devad < 0) ? CLAUSE22 : CLAUSE45; + + debug("RD: Mode: %u, baseaddr: %p, addr: %d, devad: %d, reg: %d\n", + mode, priv->baseaddr, addr, devad, regnum); + + octeontx_smi_setmode(bus, mode); + + if (mode == CLAUSE45) { + ret = octeontx_c45_addr(bus, addr, devad, regnum); + + debug("RD: ret: %u\n", ret); + + if (ret) + return 0; + } + + smix_cmd.u = 0; + smix_cmd.s.phy_adr = addr; + + if (mode == CLAUSE45) { + smix_cmd.s.reg_adr = devad; + smix_cmd.s.phy_op = SMI_OP_C45_READ; + } else { + smix_cmd.s.reg_adr = regnum; + smix_cmd.s.phy_op = SMI_OP_C22_READ; + } + + writeq(smix_cmd.u, priv->baseaddr + SMI_X_CMD); + + do { + smix_rd_dat.u = readq(priv->baseaddr + SMI_X_RD_DAT); + udelay(10); + timeout--; + } while (smix_rd_dat.s.pending && timeout); + + debug("SMIX_RD_DAT: %lx\n", (unsigned long)smix_rd_dat.u); + + return smix_rd_dat.s.dat; +} + +int octeontx_phy_write(struct mii_dev *bus, int addr, int devad, int regnum, + u16 value) +{ + struct octeontx_smi_priv *priv = bus->priv; + union smi_x_cmd smix_cmd; + union smi_x_wr_dat smix_wr_dat; + unsigned long timeout = MDIO_TIMEOUT; + int ret; + + enum octeontx_smi_mode mode = (devad < 0) ? CLAUSE22 : CLAUSE45; + + debug("WR: Mode: %u, baseaddr: %p, addr: %d, devad: %d, reg: %d\n", + mode, priv->baseaddr, addr, devad, regnum); + + if (mode == CLAUSE45) { + ret = octeontx_c45_addr(bus, addr, devad, regnum); + + debug("WR: ret: %u\n", ret); + + if (ret) + return ret; + } + + smix_wr_dat.u = 0; + smix_wr_dat.s.dat = value; + + writeq(smix_wr_dat.u, priv->baseaddr + SMI_X_WR_DAT); + + smix_cmd.u = 0; + smix_cmd.s.phy_adr = addr; + + if (mode == CLAUSE45) { + smix_cmd.s.reg_adr = devad; + smix_cmd.s.phy_op = SMI_OP_C45_WRITE; + } else { + smix_cmd.s.reg_adr = regnum; + smix_cmd.s.phy_op = SMI_OP_C22_WRITE; + } + + writeq(smix_cmd.u, priv->baseaddr + SMI_X_CMD); + + do { + smix_wr_dat.u = readq(priv->baseaddr + SMI_X_WR_DAT); + udelay(10); + timeout--; + } while (smix_wr_dat.s.pending && timeout); + + debug("SMIX_WR_DAT: %lx\n", (unsigned long)smix_wr_dat.u); + + return timeout == 0; +} + +int octeontx_smi_reset(struct mii_dev *bus) +{ + struct octeontx_smi_priv *priv = bus->priv; + + union smi_x_en smi_en; + + smi_en.s.en = 0; + writeq(smi_en.u, priv->baseaddr + SMI_X_EN); + + smi_en.s.en = 1; + writeq(smi_en.u, priv->baseaddr + SMI_X_EN); + + octeontx_smi_setmode(bus, CLAUSE22); + + return 0; +} + +/* PHY XS initialization, primarily for RXAUI + * + */ +int rxaui_phy_xs_init(struct mii_dev *bus, int phy_addr) +{ + int reg; + ulong start_time; + int phy_id1, phy_id2; + int oui, model_number; + + phy_id1 = octeontx_phy_read(bus, phy_addr, 1, 0x2); + phy_id2 = octeontx_phy_read(bus, phy_addr, 1, 0x3); + model_number = (phy_id2 >> 4) & 0x3F; + debug("%s model %x\n", __func__, model_number); + oui = phy_id1; + oui <<= 6; + oui |= (phy_id2 >> 10) & 0x3F; + debug("%s oui %x\n", __func__, oui); + switch (oui) { + case 0x5016: + if (model_number == 9) { + debug("%s +\n", __func__); + /* Perform hardware reset in XGXS control */ + reg = octeontx_phy_read(bus, phy_addr, 4, 0x0); + if ((reg & 0xffff) < 0) + goto read_error; + reg |= 0x8000; + octeontx_phy_write(bus, phy_addr, 4, 0x0, reg); + + start_time = get_timer(0); + do { + reg = octeontx_phy_read(bus, phy_addr, 4, 0x0); + if ((reg & 0xffff) < 0) + goto read_error; + } while ((reg & 0x8000) && get_timer(start_time) < 500); + if (reg & 0x8000) { + printf("HW reset for M88X3120 PHY failed"); + printf("MII_BMCR: 0x%x\n", reg); + return -1; + } + /* program 4.49155 with 0x5 */ + octeontx_phy_write(bus, phy_addr, 4, 0xc003, 0x5); + } + break; + default: + break; + } + + return 0; + +read_error: + debug("M88X3120 PHY config read failed\n"); + return -1; +} + +int octeontx_smi_probe(struct udevice *dev) +{ + int ret, subnode, cnt = 0, node = dev->node.of_offset; + struct mii_dev *bus; + struct octeontx_smi_priv *priv; + pci_dev_t bdf = dm_pci_get_bdf(dev); + + debug("SMI PCI device: %x\n", bdf); + dev->req_seq = PCI_FUNC(bdf); + if (!dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0, PCI_REGION_MEM)) { + printf("Failed to map PCI region for bdf %x\n", bdf); + return -1; + } + + fdt_for_each_subnode(subnode, gd->fdt_blob, node) { + ret = fdt_node_check_compatible(gd->fdt_blob, subnode, + "cavium,thunder-8890-mdio"); + if (ret) + continue; + + bus = mdio_alloc(); + priv = malloc(sizeof(*priv)); + if (!bus || !priv) { + printf("Failed to allocate OcteonTX MDIO bus # %u\n", + dev->seq); + return -1; + } + + bus->read = octeontx_phy_read; + bus->write = octeontx_phy_write; + bus->reset = octeontx_smi_reset; + bus->priv = priv; + + priv->mode = CLAUSE22; + priv->baseaddr = (void __iomem *)fdtdec_get_addr(gd->fdt_blob, + subnode, + "reg"); + debug("mdio base addr %p\n", priv->baseaddr); + + /* use given name or generate its own unique name */ + snprintf(bus->name, MDIO_NAME_LEN, "smi%d", cnt++); + + ret = mdio_register(bus); + if (ret) + return ret; + } + return 0; +} + +static const struct udevice_id octeontx_smi_ids[] = { + { .compatible = "cavium,thunder-8890-mdio-nexus" }, + {} +}; + +U_BOOT_DRIVER(octeontx_smi) = { + .name = "octeontx_smi", + .id = UCLASS_MISC, + .probe = octeontx_smi_probe, + .of_match = octeontx_smi_ids, +}; + +static struct pci_device_id octeontx_smi_supported[] = { + { PCI_VDEVICE(CAVIUM, PCI_DEVICE_ID_CAVIUM_SMI) }, + {} +}; + +U_BOOT_PCI_DEVICE(octeontx_smi, octeontx_smi_supported); diff --git a/drivers/net/octeontx/xcv.c b/drivers/net/octeontx/xcv.c new file mode 100644 index 00000000000..8dd558b1bf2 --- /dev/null +++ b/drivers/net/octeontx/xcv.c @@ -0,0 +1,124 @@ +// 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 <pci.h> +#include <malloc.h> +#include <miiphy.h> +#include <misc.h> +#include <net.h> +#include <netdev.h> +#include <asm/io.h> +#include <linux/delay.h> +#include <linux/libfdt.h> + +#include <asm/arch/csrs/csrs-xcv.h> + +#define XCVX_BASE 0x87E0DB000000ULL + +/* Initialize XCV block */ +void xcv_init_hw(void) +{ + union xcvx_reset reset; + union xcvx_dll_ctl xcv_dll_ctl; + + /* Take the DLL out of reset */ + reset.u = readq(XCVX_BASE + XCVX_RESET(0)); + reset.s.dllrst = 0; + writeq(reset.u, XCVX_BASE + XCVX_RESET(0)); + + /* Take the clock tree out of reset */ + reset.u = readq(XCVX_BASE + XCVX_RESET(0)); + reset.s.clkrst = 0; + writeq(reset.u, XCVX_BASE + XCVX_RESET(0)); + + /* Once the 125MHz ref clock is stable, wait 10us for DLL to lock */ + udelay(10); + + /* Optionally, bypass the DLL setting */ + xcv_dll_ctl.u = readq(XCVX_BASE + XCVX_DLL_CTL(0)); + xcv_dll_ctl.s.clkrx_set = 0; + xcv_dll_ctl.s.clkrx_byp = 1; + xcv_dll_ctl.s.clktx_byp = 0; + writeq(xcv_dll_ctl.u, XCVX_BASE + XCVX_DLL_CTL(0)); + + /* Enable the compensation controller */ + reset.u = readq(XCVX_BASE + XCVX_RESET(0)); + reset.s.comp = 1; + writeq(reset.u, XCVX_BASE + XCVX_RESET(0)); + reset.u = readq(XCVX_BASE + XCVX_RESET(0)); + + /* Wait for 1040 reference clock cycles for the compensation state + * machine lock. + */ + udelay(100); + + /* Enable the XCV block */ + reset.u = readq(XCVX_BASE + XCVX_RESET(0)); + reset.s.enable = 1; + writeq(reset.u, XCVX_BASE + XCVX_RESET(0)); + + /* set XCV(0)_RESET[CLKRST] to 1 */ + reset.u = readq(XCVX_BASE + XCVX_RESET(0)); + reset.s.clkrst = 1; + writeq(reset.u, XCVX_BASE + XCVX_RESET(0)); +} + +/* + * Configure XCV link based on the speed + * link_up : Set to 1 when link is up otherwise 0 + * link_speed: The speed of the link. + */ +void xcv_setup_link(bool link_up, int link_speed) +{ + union xcvx_ctl xcv_ctl; + union xcvx_reset reset; + union xcvx_batch_crd_ret xcv_crd_ret; + int speed = 2; + + /* Check RGMII link */ + if (link_speed == 100) + speed = 1; + else if (link_speed == 10) + speed = 0; + + if (link_up) { + /* Set operating speed */ + xcv_ctl.u = readq(XCVX_BASE + XCVX_CTL(0)); + xcv_ctl.s.speed = speed; + writeq(xcv_ctl.u, XCVX_BASE + XCVX_CTL(0)); + + /* Datapaths come out of reset + * - The datapath resets will disengage BGX from the + * RGMII interface + * - XCV will continue to return TX credits for each tick + * that is sent on the TX data path + */ + reset.u = readq(XCVX_BASE + XCVX_RESET(0)); + reset.s.tx_dat_rst_n = 1; + reset.s.rx_dat_rst_n = 1; + writeq(reset.u, XCVX_BASE + XCVX_RESET(0)); + + /* Enable packet flow */ + reset.u = readq(XCVX_BASE + XCVX_RESET(0)); + reset.s.tx_pkt_rst_n = 1; + reset.s.rx_pkt_rst_n = 1; + writeq(reset.u, XCVX_BASE + XCVX_RESET(0)); + + xcv_crd_ret.u = readq(XCVX_BASE + XCVX_BATCH_CRD_RET(0)); + xcv_crd_ret.s.crd_ret = 1; + writeq(xcv_crd_ret.u, XCVX_BASE + XCVX_BATCH_CRD_RET(0)); + } else { + /* Enable packet flow */ + reset.u = readq(XCVX_BASE + XCVX_RESET(0)); + reset.s.tx_pkt_rst_n = 0; + reset.s.rx_pkt_rst_n = 0; + writeq(reset.u, XCVX_BASE + XCVX_RESET(0)); + reset.u = readq(XCVX_BASE + XCVX_RESET(0)); + } +} |