aboutsummaryrefslogtreecommitdiff
path: root/drivers/net
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2020-10-14 13:51:56 -0400
committerTom Rini <trini@konsulko.com>2020-10-14 13:51:56 -0400
commit0f35d96bfd8565da2c5ba0f540827fff0e82eb32 (patch)
tree753688442ed5b0dbed5f5e56c0c56398775ed6f8 /drivers/net
parent6731c5a5ef30e1581e3042b11893627f42949cd7 (diff)
parent8369886b401d85e49d52ff5f877cf14b1619bd43 (diff)
downloadu-boot-0f35d96bfd8565da2c5ba0f540827fff0e82eb32.zip
u-boot-0f35d96bfd8565da2c5ba0f540827fff0e82eb32.tar.gz
u-boot-0f35d96bfd8565da2c5ba0f540827fff0e82eb32.tar.bz2
Merge https://gitlab.denx.de/u-boot/custodians/u-boot-marvell
- Octeon TX: Add NAND driver (Suneel) - Octeon TX: Add NIC driver driver (Suneel) - Octeon TX2: Add NIC driver driver (Suneel) - Armada 8040: Add iEi Puzzle-M80 board support (Luka) - Armada A37xx SPI: Add support for CS-GPIO (George) - Espressobin: Use Linux model/compatible strings (Andre) - Espressobin: Add armada-3720-espressobin-emmc.dts from Linux (Andre) - Armada A37xx: Small cleanup of config header (Pali)
Diffstat (limited to 'drivers/net')
-rw-r--r--drivers/net/Kconfig31
-rw-r--r--drivers/net/Makefile4
-rw-r--r--drivers/net/octeontx/Makefile7
-rw-r--r--drivers/net/octeontx/bgx.c1565
-rw-r--r--drivers/net/octeontx/bgx.h259
-rw-r--r--drivers/net/octeontx/nic.h508
-rw-r--r--drivers/net/octeontx/nic_main.c778
-rw-r--r--drivers/net/octeontx/nic_reg.h250
-rw-r--r--drivers/net/octeontx/nicvf_main.c581
-rw-r--r--drivers/net/octeontx/nicvf_queues.c1140
-rw-r--r--drivers/net/octeontx/nicvf_queues.h353
-rw-r--r--drivers/net/octeontx/q_struct.h695
-rw-r--r--drivers/net/octeontx/smi.c380
-rw-r--r--drivers/net/octeontx/xcv.c124
-rw-r--r--drivers/net/octeontx2/Makefile8
-rw-r--r--drivers/net/octeontx2/cgx.c296
-rw-r--r--drivers/net/octeontx2/cgx.h105
-rw-r--r--drivers/net/octeontx2/cgx_intf.c715
-rw-r--r--drivers/net/octeontx2/cgx_intf.h448
-rw-r--r--drivers/net/octeontx2/lmt.h49
-rw-r--r--drivers/net/octeontx2/nix.c831
-rw-r--r--drivers/net/octeontx2/nix.h353
-rw-r--r--drivers/net/octeontx2/nix_af.c1102
-rw-r--r--drivers/net/octeontx2/npc.h90
-rw-r--r--drivers/net/octeontx2/rvu.h119
-rw-r--r--drivers/net/octeontx2/rvu_af.c171
-rw-r--r--drivers/net/octeontx2/rvu_common.c71
-rw-r--r--drivers/net/octeontx2/rvu_pf.c116
28 files changed, 11149 insertions, 0 deletions
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index a0d2d21..2beec2d 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -407,6 +407,37 @@ 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 NET_OCTEONTX2
+ bool "OcteonTX2 Ethernet support"
+ depends on ARCH_OCTEONTX2
+ select OCTEONTX2_CGX_INTF
+ help
+ You must select Y to enable network device support for
+ OcteonTX2 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 OCTEONTX2_CGX_INTF
+ bool "OcteonTX2 CGX ATF interface support"
+ depends on ARCH_OCTEONTX2
+ default y if ARCH_OCTEONTX2
+ help
+ You must select Y to enable CGX ATF interface support for
+ 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 03f0192..9f7a79e 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -65,6 +65,10 @@ 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_NET_OCTEONTX2) += octeontx2/
+obj-$(CONFIG_OCTEONTX_SMI) += octeontx/smi.o
+obj-$(CONFIG_OCTEONTX2_CGX_INTF) += octeontx2/cgx_intf.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 0000000..d4adb7c
--- /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 0000000..fbe2e2c
--- /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 0000000..8402630
--- /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 0000000..af3576c
--- /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 0000000..1a805f7
--- /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 0000000..c214ebb
--- /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 0000000..e13c8b9
--- /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 0000000..c7f262f
--- /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 0000000..833b2a1
--- /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 0000000..87abb13
--- /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 0000000..8e2c3ca
--- /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 0000000..8dd558b
--- /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));
+ }
+}
diff --git a/drivers/net/octeontx2/Makefile b/drivers/net/octeontx2/Makefile
new file mode 100644
index 0000000..c930072
--- /dev/null
+++ b/drivers/net/octeontx2/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (C) 2018 Marvell International Ltd.
+#
+
+obj-$(CONFIG_NET_OCTEONTX2) += cgx.o nix_af.o nix.o rvu_pf.o \
+ rvu_af.o rvu_common.o
+
diff --git a/drivers/net/octeontx2/cgx.c b/drivers/net/octeontx2/cgx.c
new file mode 100644
index 0000000..ff2ebc2
--- /dev/null
+++ b/drivers/net/octeontx2/cgx.c
@@ -0,0 +1,296 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018 Marvell International Ltd.
+ */
+
+#include <dm.h>
+#include <errno.h>
+#include <malloc.h>
+#include <misc.h>
+#include <net.h>
+#include <pci_ids.h>
+#include <linux/list.h>
+#include <asm/arch/board.h>
+#include <asm/arch/csrs/csrs-cgx.h>
+#include <asm/io.h>
+
+#include "cgx.h"
+
+char lmac_type_to_str[][8] = {
+ "SGMII",
+ "XAUI",
+ "RXAUI",
+ "10G_R",
+ "40G_R",
+ "RGMII",
+ "QSGMII",
+ "25G_R",
+ "50G_R",
+ "100G_R",
+ "USXGMII",
+};
+
+char lmac_speed_to_str[][8] = {
+ "0",
+ "10M",
+ "100M",
+ "1G",
+ "2.5G",
+ "5G",
+ "10G",
+ "20G",
+ "25G",
+ "40G",
+ "50G",
+ "80G",
+ "100G",
+};
+
+/**
+ * Given an LMAC/PF instance number, return the lmac
+ * Per design, each PF has only one LMAC mapped.
+ *
+ * @param instance instance to find
+ *
+ * @return pointer to lmac data structure or NULL if not found
+ */
+struct lmac *nix_get_cgx_lmac(int lmac_instance)
+{
+ struct cgx *cgx;
+ struct udevice *dev;
+ int i, idx, err;
+
+ for (i = 0; i < CGX_PER_NODE; i++) {
+ err = dm_pci_find_device(PCI_VENDOR_ID_CAVIUM,
+ PCI_DEVICE_ID_OCTEONTX2_CGX, i,
+ &dev);
+ if (err)
+ continue;
+
+ cgx = dev_get_priv(dev);
+ debug("%s udev %p cgx %p instance %d\n", __func__, dev, cgx,
+ lmac_instance);
+ for (idx = 0; idx < cgx->lmac_count; idx++) {
+ if (cgx->lmac[idx]->instance == lmac_instance)
+ return cgx->lmac[idx];
+ }
+ }
+ return NULL;
+}
+
+void cgx_lmac_mac_filter_clear(struct lmac *lmac)
+{
+ union cgxx_cmrx_rx_dmac_ctl0 dmac_ctl0;
+ union cgxx_cmr_rx_dmacx_cam0 dmac_cam0;
+ void *reg_addr;
+
+ dmac_cam0.u = 0x0;
+ reg_addr = lmac->cgx->reg_base +
+ CGXX_CMR_RX_DMACX_CAM0(lmac->lmac_id * 8);
+ writeq(dmac_cam0.u, reg_addr);
+ debug("%s: reg %p dmac_cam0 %llx\n", __func__, reg_addr, dmac_cam0.u);
+
+ dmac_ctl0.u = 0x0;
+ dmac_ctl0.s.bcst_accept = 1;
+ dmac_ctl0.s.mcst_mode = 1;
+ dmac_ctl0.s.cam_accept = 0;
+ reg_addr = lmac->cgx->reg_base +
+ CGXX_CMRX_RX_DMAC_CTL0(lmac->lmac_id);
+ writeq(dmac_ctl0.u, reg_addr);
+ debug("%s: reg %p dmac_ctl0 %llx\n", __func__, reg_addr, dmac_ctl0.u);
+}
+
+void cgx_lmac_mac_filter_setup(struct lmac *lmac)
+{
+ union cgxx_cmrx_rx_dmac_ctl0 dmac_ctl0;
+ union cgxx_cmr_rx_dmacx_cam0 dmac_cam0;
+ u64 mac, tmp;
+ void *reg_addr;
+
+ memcpy((void *)&tmp, lmac->mac_addr, 6);
+ debug("%s: tmp %llx\n", __func__, tmp);
+ debug("%s: swab tmp %llx\n", __func__, swab64(tmp));
+ mac = swab64(tmp) >> 16;
+ debug("%s: mac %llx\n", __func__, mac);
+ dmac_cam0.u = 0x0;
+ dmac_cam0.s.id = lmac->lmac_id;
+ dmac_cam0.s.adr = mac;
+ dmac_cam0.s.en = 1;
+ reg_addr = lmac->cgx->reg_base +
+ CGXX_CMR_RX_DMACX_CAM0(lmac->lmac_id * 8);
+ writeq(dmac_cam0.u, reg_addr);
+ debug("%s: reg %p dmac_cam0 %llx\n", __func__, reg_addr, dmac_cam0.u);
+ dmac_ctl0.u = 0x0;
+ dmac_ctl0.s.bcst_accept = 1;
+ dmac_ctl0.s.mcst_mode = 0;
+ dmac_ctl0.s.cam_accept = 1;
+ reg_addr = lmac->cgx->reg_base +
+ CGXX_CMRX_RX_DMAC_CTL0(lmac->lmac_id);
+ writeq(dmac_ctl0.u, reg_addr);
+ debug("%s: reg %p dmac_ctl0 %llx\n", __func__, reg_addr, dmac_ctl0.u);
+}
+
+int cgx_lmac_set_pkind(struct lmac *lmac, u8 lmac_id, int pkind)
+{
+ cgx_write(lmac->cgx, lmac_id, CGXX_CMRX_RX_ID_MAP(0),
+ (pkind & 0x3f));
+ return 0;
+}
+
+int cgx_lmac_link_status(struct lmac *lmac, int lmac_id, u64 *status)
+{
+ int ret = 0;
+
+ ret = cgx_intf_get_link_sts(lmac->cgx->cgx_id, lmac_id, status);
+ if (ret) {
+ debug("%s request failed for cgx%d lmac%d\n",
+ __func__, lmac->cgx->cgx_id, lmac->lmac_id);
+ ret = -1;
+ }
+ return ret;
+}
+
+int cgx_lmac_rx_tx_enable(struct lmac *lmac, int lmac_id, bool enable)
+{
+ struct cgx *cgx = lmac->cgx;
+ union cgxx_cmrx_config cmrx_config;
+
+ if (!cgx || lmac_id >= cgx->lmac_count)
+ return -ENODEV;
+
+ cmrx_config.u = cgx_read(cgx, lmac_id, CGXX_CMRX_CONFIG(0));
+ cmrx_config.s.data_pkt_rx_en =
+ cmrx_config.s.data_pkt_tx_en = enable ? 1 : 0;
+ cgx_write(cgx, lmac_id, CGXX_CMRX_CONFIG(0), cmrx_config.u);
+ return 0;
+}
+
+int cgx_lmac_link_enable(struct lmac *lmac, int lmac_id, bool enable,
+ u64 *status)
+{
+ int ret = 0;
+
+ ret = cgx_intf_link_up_dwn(lmac->cgx->cgx_id, lmac_id, enable,
+ status);
+ if (ret) {
+ debug("%s request failed for cgx%d lmac%d\n",
+ __func__, lmac->cgx->cgx_id, lmac->lmac_id);
+ ret = -1;
+ }
+ return ret;
+}
+
+int cgx_lmac_internal_loopback(struct lmac *lmac, int lmac_id, bool enable)
+{
+ struct cgx *cgx = lmac->cgx;
+ union cgxx_cmrx_config cmrx_cfg;
+ union cgxx_gmp_pcs_mrx_control mrx_control;
+ union cgxx_spux_control1 spux_control1;
+ enum lmac_type lmac_type;
+
+ if (!cgx || lmac_id >= cgx->lmac_count)
+ return -ENODEV;
+
+ cmrx_cfg.u = cgx_read(cgx, lmac_id, CGXX_CMRX_CONFIG(0));
+ lmac_type = cmrx_cfg.s.lmac_type;
+ if (lmac_type == LMAC_MODE_SGMII || lmac_type == LMAC_MODE_QSGMII) {
+ mrx_control.u = cgx_read(cgx, lmac_id,
+ CGXX_GMP_PCS_MRX_CONTROL(0));
+ mrx_control.s.loopbck1 = enable ? 1 : 0;
+ cgx_write(cgx, lmac_id, CGXX_GMP_PCS_MRX_CONTROL(0),
+ mrx_control.u);
+ } else {
+ spux_control1.u = cgx_read(cgx, lmac_id,
+ CGXX_SPUX_CONTROL1(0));
+ spux_control1.s.loopbck = enable ? 1 : 0;
+ cgx_write(cgx, lmac_id, CGXX_SPUX_CONTROL1(0),
+ spux_control1.u);
+ }
+ return 0;
+}
+
+static int cgx_lmac_init(struct cgx *cgx)
+{
+ struct lmac *lmac;
+ union cgxx_cmrx_config cmrx_cfg;
+ static int instance = 1;
+ int i;
+
+ cgx->lmac_count = cgx_read(cgx, 0, CGXX_CMR_RX_LMACS());
+ debug("%s: Found %d lmacs for cgx %d@%p\n", __func__, cgx->lmac_count,
+ cgx->cgx_id, cgx->reg_base);
+
+ for (i = 0; i < cgx->lmac_count; i++) {
+ lmac = calloc(1, sizeof(*lmac));
+ if (!lmac)
+ return -ENOMEM;
+ lmac->instance = instance++;
+ snprintf(lmac->name, sizeof(lmac->name), "cgx_fwi_%d_%d",
+ cgx->cgx_id, i);
+ /* Get LMAC type */
+ cmrx_cfg.u = cgx_read(cgx, i, CGXX_CMRX_CONFIG(0));
+ lmac->lmac_type = cmrx_cfg.s.lmac_type;
+
+ lmac->lmac_id = i;
+ lmac->cgx = cgx;
+ cgx->lmac[i] = lmac;
+ debug("%s: map id %d to lmac %p (%s), type:%d instance %d\n",
+ __func__, i, lmac, lmac->name, lmac->lmac_type,
+ lmac->instance);
+ lmac->init_pend = 1;
+ printf("CGX%d LMAC%d [%s]\n", lmac->cgx->cgx_id,
+ lmac->lmac_id, lmac_type_to_str[lmac->lmac_type]);
+ octeontx2_board_get_mac_addr((lmac->instance - 1),
+ lmac->mac_addr);
+ debug("%s: MAC %pM\n", __func__, lmac->mac_addr);
+ cgx_lmac_mac_filter_setup(lmac);
+ }
+ return 0;
+}
+
+int cgx_probe(struct udevice *dev)
+{
+ struct cgx *cgx = dev_get_priv(dev);
+ int err;
+
+ cgx->reg_base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0,
+ PCI_REGION_MEM);
+ cgx->dev = dev;
+ cgx->cgx_id = ((u64)(cgx->reg_base) >> 24) & 0x7;
+
+ debug("%s CGX BAR %p, id: %d\n", __func__, cgx->reg_base,
+ cgx->cgx_id);
+ debug("%s CGX %p, udev: %p\n", __func__, cgx, dev);
+
+ err = cgx_lmac_init(cgx);
+
+ return err;
+}
+
+int cgx_remove(struct udevice *dev)
+{
+ struct cgx *cgx = dev_get_priv(dev);
+ int i;
+
+ debug("%s: cgx remove reg_base %p cgx_id %d",
+ __func__, cgx->reg_base, cgx->cgx_id);
+ for (i = 0; i < cgx->lmac_count; i++)
+ cgx_lmac_mac_filter_clear(cgx->lmac[i]);
+
+ return 0;
+}
+
+U_BOOT_DRIVER(cgx) = {
+ .name = "cgx",
+ .id = UCLASS_MISC,
+ .probe = cgx_probe,
+ .remove = cgx_remove,
+ .priv_auto_alloc_size = sizeof(struct cgx),
+};
+
+static struct pci_device_id cgx_supported[] = {
+ {PCI_VDEVICE(CAVIUM, PCI_DEVICE_ID_CAVIUM_CGX) },
+ {}
+};
+
+U_BOOT_PCI_DEVICE(cgx, cgx_supported);
diff --git a/drivers/net/octeontx2/cgx.h b/drivers/net/octeontx2/cgx.h
new file mode 100644
index 0000000..f287692
--- /dev/null
+++ b/drivers/net/octeontx2/cgx.h
@@ -0,0 +1,105 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2018 Marvell International Ltd.
+ */
+
+#ifndef __CGX_H__
+#define __CGX_H__
+
+#include "cgx_intf.h"
+
+#define PCI_DEVICE_ID_OCTEONTX2_CGX 0xA059
+
+#define MAX_LMAC_PER_CGX 4
+#define CGX_PER_NODE 3
+
+enum lmac_type {
+ LMAC_MODE_SGMII = 0,
+ LMAC_MODE_XAUI = 1,
+ LMAC_MODE_RXAUI = 2,
+ LMAC_MODE_10G_R = 3,
+ LMAC_MODE_40G_R = 4,
+ LMAC_MODE_QSGMII = 6,
+ LMAC_MODE_25G_R = 7,
+ LMAC_MODE_50G_R = 8,
+ LMAC_MODE_100G_R = 9,
+ LMAC_MODE_USXGMII = 10,
+};
+
+extern char lmac_type_to_str[][8];
+
+extern char lmac_speed_to_str[][8];
+
+struct lmac_priv {
+ u8 enable:1;
+ u8 full_duplex:1;
+ u8 speed:4;
+ u8 mode:1;
+ u8 rsvd:1;
+ u8 mac_addr[6];
+};
+
+struct cgx;
+struct nix;
+struct nix_af;
+
+struct lmac {
+ struct cgx *cgx;
+ struct nix *nix;
+ char name[16];
+ enum lmac_type lmac_type;
+ bool init_pend;
+ u8 instance;
+ u8 lmac_id;
+ u8 pknd;
+ u8 link_num;
+ u32 chan_num;
+ u8 mac_addr[6];
+};
+
+struct cgx {
+ struct nix_af *nix_af;
+ void __iomem *reg_base;
+ struct udevice *dev;
+ struct lmac *lmac[MAX_LMAC_PER_CGX];
+ u8 cgx_id;
+ u8 lmac_count;
+};
+
+static inline void cgx_write(struct cgx *cgx, u8 lmac, u64 offset, u64 val)
+{
+ writeq(val, cgx->reg_base + CMR_SHIFT(lmac) + offset);
+}
+
+static inline u64 cgx_read(struct cgx *cgx, u8 lmac, u64 offset)
+{
+ return readq(cgx->reg_base + CMR_SHIFT(lmac) + offset);
+}
+
+/**
+ * Given an LMAC/PF instance number, return the lmac
+ * Per design, each PF has only one LMAC mapped.
+ *
+ * @param instance instance to find
+ *
+ * @return pointer to lmac data structure or NULL if not found
+ */
+struct lmac *nix_get_cgx_lmac(int lmac_instance);
+
+int cgx_lmac_set_pkind(struct lmac *lmac, u8 lmac_id, int pkind);
+int cgx_lmac_internal_loopback(struct lmac *lmac, int lmac_id, bool enable);
+int cgx_lmac_rx_tx_enable(struct lmac *lmac, int lmac_id, bool enable);
+int cgx_lmac_link_enable(struct lmac *lmac, int lmac_id, bool enable,
+ u64 *status);
+int cgx_lmac_link_status(struct lmac *lmac, int lmac_id, u64 *status);
+void cgx_lmac_mac_filter_setup(struct lmac *lmac);
+
+int cgx_intf_get_link_sts(u8 cgx, u8 lmac, u64 *lnk_sts);
+int cgx_intf_link_up_dwn(u8 cgx, u8 lmac, u8 up_dwn, u64 *lnk_sts);
+int cgx_intf_get_mac_addr(u8 cgx, u8 lmac, u8 *mac);
+int cgx_intf_set_macaddr(struct udevice *dev);
+int cgx_intf_prbs(u8 qlm, u8 mode, u32 time, u8 lane);
+int cgx_intf_display_eye(u8 qlm, u8 lane);
+int cgx_intf_display_serdes(u8 qlm, u8 lane);
+
+#endif /* __CGX_H__ */
diff --git a/drivers/net/octeontx2/cgx_intf.c b/drivers/net/octeontx2/cgx_intf.c
new file mode 100644
index 0000000..37d9a2b
--- /dev/null
+++ b/drivers/net/octeontx2/cgx_intf.c
@@ -0,0 +1,715 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018 Marvell International Ltd.
+ */
+
+#include <dm.h>
+#include <errno.h>
+#include <malloc.h>
+#include <misc.h>
+#include <net.h>
+
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/list.h>
+
+#include <asm/arch/board.h>
+#include <asm/io.h>
+
+#include "cgx_intf.h"
+#include "cgx.h"
+#include "nix.h"
+
+static u64 cgx_rd_scrx(u8 cgx, u8 lmac, u8 index)
+{
+ u64 addr;
+
+ addr = (index == 1) ? CGX_CMR_SCRATCH1 : CGX_CMR_SCRATCH0;
+ addr += CGX_SHIFT(cgx) + CMR_SHIFT(lmac);
+ return readq(addr);
+}
+
+static void cgx_wr_scrx(u8 cgx, u8 lmac, u8 index, u64 val)
+{
+ u64 addr;
+
+ addr = (index == 1) ? CGX_CMR_SCRATCH1 : CGX_CMR_SCRATCH0;
+ addr += CGX_SHIFT(cgx) + CMR_SHIFT(lmac);
+ writeq(val, addr);
+}
+
+static u64 cgx_rd_scr0(u8 cgx, u8 lmac)
+{
+ return cgx_rd_scrx(cgx, lmac, 0);
+}
+
+static u64 cgx_rd_scr1(u8 cgx, u8 lmac)
+{
+ return cgx_rd_scrx(cgx, lmac, 1);
+}
+
+static void cgx_wr_scr0(u8 cgx, u8 lmac, u64 val)
+{
+ return cgx_wr_scrx(cgx, lmac, 0, val);
+}
+
+static void cgx_wr_scr1(u8 cgx, u8 lmac, u64 val)
+{
+ return cgx_wr_scrx(cgx, lmac, 1, val);
+}
+
+static inline void set_ownership(u8 cgx, u8 lmac, u8 val)
+{
+ union cgx_scratchx1 scr1;
+
+ scr1.u = cgx_rd_scr1(cgx, lmac);
+ scr1.s.own_status = val;
+ cgx_wr_scr1(cgx, lmac, scr1.u);
+}
+
+static int wait_for_ownership(u8 cgx, u8 lmac)
+{
+ union cgx_scratchx1 scr1;
+ union cgx_scratchx0 scr0;
+ u64 cmrx_int;
+ int timeout = 5000;
+
+ do {
+ scr1.u = cgx_rd_scr1(cgx, lmac);
+ scr0.u = cgx_rd_scr0(cgx, lmac);
+ /* clear async events if any */
+ if (scr0.s.evt_sts.evt_type == CGX_EVT_ASYNC &&
+ scr0.s.evt_sts.ack) {
+ /* clear interrupt */
+ cmrx_int = readq(CGX_CMR_INT +
+ CGX_SHIFT(cgx) + CMR_SHIFT(lmac));
+ cmrx_int |= 0x2; // Overflw bit
+ writeq(cmrx_int, CGX_CMR_INT +
+ CGX_SHIFT(cgx) + CMR_SHIFT(lmac));
+
+ /* clear ack */
+ scr0.s.evt_sts.ack = 0;
+ cgx_wr_scr0(cgx, lmac, scr0.u);
+ }
+
+ if (timeout-- < 0) {
+ debug("timeout waiting for ownership\n");
+ return -ETIMEDOUT;
+ }
+ mdelay(1);
+ } while ((scr1.s.own_status == CGX_OWN_FIRMWARE) &&
+ scr0.s.evt_sts.ack);
+
+ return 0;
+}
+
+int cgx_intf_req(u8 cgx, u8 lmac, union cgx_cmd_s cmd_args, u64 *rsp,
+ int use_cmd_id_only)
+{
+ union cgx_scratchx1 scr1;
+ union cgx_scratchx0 scr0;
+ u64 cmrx_int;
+ int timeout = 500;
+ int err = 0;
+ u8 cmd = cmd_args.cmd.id;
+
+ if (wait_for_ownership(cgx, lmac)) {
+ err = -ETIMEDOUT;
+ goto error;
+ }
+
+ /* send command */
+ scr1.u = cgx_rd_scr1(cgx, lmac);
+
+ if (use_cmd_id_only) {
+ scr1.s.cmd.id = cmd;
+ } else {
+ cmd_args.own_status = scr1.s.own_status;
+ scr1.s = cmd_args;
+ }
+ cgx_wr_scr1(cgx, lmac, scr1.u);
+
+ set_ownership(cgx, lmac, CGX_OWN_FIRMWARE);
+
+ /* wait for response and ownership */
+ do {
+ scr0.u = cgx_rd_scr0(cgx, lmac);
+ scr1.u = cgx_rd_scr1(cgx, lmac);
+ mdelay(10);
+ } while (timeout-- && (!scr0.s.evt_sts.ack) &&
+ (scr1.s.own_status == CGX_OWN_FIRMWARE));
+ if (timeout < 0) {
+ debug("%s timeout waiting for ack\n", __func__);
+ err = -ETIMEDOUT;
+ goto error;
+ }
+
+ if (cmd == CGX_CMD_INTF_SHUTDOWN)
+ goto error;
+
+ if (scr0.s.evt_sts.evt_type != CGX_EVT_CMD_RESP) {
+ debug("%s received async event instead of cmd resp event\n",
+ __func__);
+ err = -1;
+ goto error;
+ }
+ if (scr0.s.evt_sts.id != cmd) {
+ debug("%s received resp for cmd %d expected cmd %d\n",
+ __func__, scr0.s.evt_sts.id, cmd);
+ err = -1;
+ goto error;
+ }
+ if (scr0.s.evt_sts.stat != CGX_STAT_SUCCESS) {
+ debug("%s cmd%d failed on cgx%u lmac%u with errcode %d\n",
+ __func__, cmd, cgx, lmac, scr0.s.link_sts.err_type);
+ err = -1;
+ }
+
+error:
+ /* clear interrupt */
+ cmrx_int = readq(CGX_CMR_INT + CGX_SHIFT(cgx) + CMR_SHIFT(lmac));
+ cmrx_int |= 0x2; // Overflw bit
+ writeq(cmrx_int, CGX_CMR_INT + CGX_SHIFT(cgx) + CMR_SHIFT(lmac));
+
+ /* clear ownership and ack */
+ scr0.s.evt_sts.ack = 0;
+ cgx_wr_scr0(cgx, lmac, scr0.u);
+
+ *rsp = err ? 0 : scr0.u;
+
+ return err;
+}
+
+int cgx_intf_get_mac_addr(u8 cgx, u8 lmac, u8 *mac)
+{
+ union cgx_scratchx0 scr0;
+ int ret;
+ union cgx_cmd_s cmd;
+
+ cmd.cmd.id = CGX_CMD_GET_MAC_ADDR;
+
+ ret = cgx_intf_req(cgx, lmac, cmd, &scr0.u, 1);
+ if (ret)
+ return -1;
+
+ scr0.u >>= 9;
+ memcpy(mac, &scr0.u, 6);
+
+ return 0;
+}
+
+int cgx_intf_get_ver(u8 cgx, u8 lmac, u8 *ver)
+{
+ union cgx_scratchx0 scr0;
+ int ret;
+ union cgx_cmd_s cmd;
+
+ cmd.cmd.id = CGX_CMD_GET_FW_VER;
+
+ ret = cgx_intf_req(cgx, lmac, cmd, &scr0.u, 1);
+ if (ret)
+ return -1;
+
+ scr0.u >>= 9;
+ *ver = scr0.u & 0xFFFF;
+
+ return 0;
+}
+
+int cgx_intf_get_link_sts(u8 cgx, u8 lmac, u64 *lnk_sts)
+{
+ union cgx_scratchx0 scr0;
+ int ret;
+ union cgx_cmd_s cmd;
+
+ cmd.cmd.id = CGX_CMD_GET_LINK_STS;
+
+ ret = cgx_intf_req(cgx, lmac, cmd, &scr0.u, 1);
+ if (ret)
+ return -1;
+
+ scr0.u >>= 9;
+ /* pass the same format as cgx_lnk_sts_s
+ * err_type:10, speed:4, full_duplex:1, link_up:1
+ */
+ *lnk_sts = scr0.u & 0xFFFF;
+ return 0;
+}
+
+int cgx_intf_link_up_dwn(u8 cgx, u8 lmac, u8 up_dwn, u64 *lnk_sts)
+{
+ union cgx_scratchx0 scr0;
+ int ret;
+ union cgx_cmd_s cmd;
+
+ cmd.cmd.id = up_dwn ? CGX_CMD_LINK_BRING_UP : CGX_CMD_LINK_BRING_DOWN;
+
+ ret = cgx_intf_req(cgx, lmac, cmd, &scr0.u, 1);
+ if (ret)
+ return -1;
+
+ scr0.u >>= 9;
+ /* pass the same format as cgx_lnk_sts_s
+ * err_type:10, speed:4, full_duplex:1, link_up:1
+ */
+ *lnk_sts = scr0.u & 0xFFFF;
+ return 0;
+}
+
+void cgx_intf_shutdown(void)
+{
+ union cgx_scratchx0 scr0;
+ union cgx_cmd_s cmd;
+
+ cmd.cmd.id = CGX_CMD_INTF_SHUTDOWN;
+
+ cgx_intf_req(0, 0, cmd, &scr0.u, 1);
+}
+
+int cgx_intf_prbs(u8 qlm, u8 mode, u32 time, u8 lane)
+{
+ union cgx_scratchx0 scr0;
+ int ret;
+ union cgx_cmd_s cmd;
+
+ cmd.cmd.id = CGX_CMD_PRBS;
+
+ cmd.prbs_args.qlm = qlm;
+ cmd.prbs_args.mode = mode;
+ cmd.prbs_args.time = time;
+ cmd.prbs_args.lane = lane;
+
+ ret = cgx_intf_req(0, 0, cmd, &scr0.u, 0);
+ if (ret)
+ return -1;
+
+ return 0;
+}
+
+enum cgx_mode {
+ MODE_10G_C2C,
+ MODE_10G_C2M,
+ MODE_10G_KR,
+ MODE_25G_C2C,
+ MODE_25G_2_C2C,
+ MODE_50G_C2C,
+ MODE_50G_4_C2C
+};
+
+static char intf_speed_to_str[][8] = {
+ "10M",
+ "100M",
+ "1G",
+ "2.5G",
+ "5G",
+ "10G",
+ "20G",
+ "25G",
+ "40G",
+ "50G",
+ "80G",
+ "100G",
+};
+
+static void mode_to_args(int mode, struct cgx_mode_change_args *args)
+{
+ args->an = 0;
+ args->duplex = 0;
+ args->port = 0;
+
+ switch (mode) {
+ case MODE_10G_C2C:
+ args->speed = CGX_LINK_10G;
+ args->mode = BIT_ULL(CGX_MODE_10G_C2C_BIT);
+ break;
+ case MODE_10G_C2M:
+ args->speed = CGX_LINK_10G;
+ args->mode = BIT_ULL(CGX_MODE_10G_C2M_BIT);
+ break;
+ case MODE_10G_KR:
+ args->speed = CGX_LINK_10G;
+ args->mode = BIT_ULL(CGX_MODE_10G_KR_BIT);
+ args->an = 1;
+ break;
+ case MODE_25G_C2C:
+ args->speed = CGX_LINK_25G;
+ args->mode = BIT_ULL(CGX_MODE_25G_C2C_BIT);
+ break;
+ case MODE_25G_2_C2C:
+ args->speed = CGX_LINK_25G;
+ args->mode = BIT_ULL(CGX_MODE_25G_2_C2C_BIT);
+ break;
+ case MODE_50G_C2C:
+ args->speed = CGX_LINK_50G;
+ args->mode = BIT_ULL(CGX_MODE_50G_C2C_BIT);
+ break;
+ case MODE_50G_4_C2C:
+ args->speed = CGX_LINK_50G;
+ args->mode = BIT_ULL(CGX_MODE_50G_4_C2C_BIT);
+ }
+}
+
+int cgx_intf_set_mode(struct udevice *ethdev, int mode)
+{
+ struct rvu_pf *rvu = dev_get_priv(ethdev);
+ struct nix *nix = rvu->nix;
+ union cgx_scratchx0 scr0;
+ int ret;
+ union cgx_cmd_s cmd;
+
+ cmd.cmd.id = CGX_CMD_MODE_CHANGE;
+
+ mode_to_args(mode, &cmd.mode_change_args);
+
+ ret = cgx_intf_req(nix->lmac->cgx->cgx_id, nix->lmac->lmac_id,
+ cmd, &scr0.u, 0);
+ if (ret) {
+ printf("Mode change command failed for %s\n", ethdev->name);
+ return -1;
+ }
+
+ cmd.cmd.id = CGX_CMD_GET_LINK_STS;
+ ret = cgx_intf_req(nix->lmac->cgx->cgx_id, nix->lmac->lmac_id,
+ cmd, &scr0.u, 1);
+ if (ret) {
+ printf("Get Link Status failed for %s\n", ethdev->name);
+ return -1;
+ }
+
+ printf("Current Link Status: ");
+ if (scr0.s.link_sts.speed) {
+ printf("%s\n", intf_speed_to_str[scr0.s.link_sts.speed]);
+ switch (scr0.s.link_sts.fec) {
+ case 0:
+ printf("FEC_NONE\n");
+ break;
+ case 1:
+ printf("FEC_BASE_R\n");
+ break;
+ case 2:
+ printf("FEC_RS\n");
+ break;
+ }
+ printf("Auto Negotiation %sabled\n",
+ scr0.s.link_sts.an ? "En" : "Dis");
+ printf("%s Duplex\n",
+ scr0.s.link_sts.full_duplex ? "Full" : "Half");
+ } else {
+ printf("Down\n");
+ }
+ return 0;
+}
+
+int cgx_intf_get_mode(struct udevice *ethdev)
+{
+ struct rvu_pf *rvu = dev_get_priv(ethdev);
+ struct nix *nix = rvu->nix;
+ union cgx_scratchx0 scr0;
+ int ret;
+ union cgx_cmd_s cmd;
+
+ cmd.cmd.id = CGX_CMD_GET_LINK_STS;
+ ret = cgx_intf_req(nix->lmac->cgx->cgx_id, nix->lmac->lmac_id,
+ cmd, &scr0.u, 1);
+ if (ret) {
+ printf("Get link status failed for %s\n", ethdev->name);
+ return -1;
+ }
+ printf("Current Interface Mode: ");
+ switch (scr0.s.link_sts.mode) {
+ case CGX_MODE_10G_C2C_BIT:
+ printf("10G_C2C\n");
+ break;
+ case CGX_MODE_10G_C2M_BIT:
+ printf("10G_C2M\n");
+ break;
+ case CGX_MODE_10G_KR_BIT:
+ printf("10G_KR\n");
+ break;
+ case CGX_MODE_25G_C2C_BIT:
+ printf("25G_C2C\n");
+ break;
+ case CGX_MODE_25G_2_C2C_BIT:
+ printf("25G_2_C2C\n");
+ break;
+ case CGX_MODE_50G_C2C_BIT:
+ printf("50G_C2C\n");
+ break;
+ case CGX_MODE_50G_4_C2C_BIT:
+ printf("50G_4_C2C\n");
+ break;
+ default:
+ printf("Unknown\n");
+ break;
+ }
+ return 0;
+}
+
+int cgx_intf_get_fec(struct udevice *ethdev)
+{
+ struct rvu_pf *rvu = dev_get_priv(ethdev);
+ struct nix *nix = rvu->nix;
+ union cgx_scratchx0 scr0;
+ int ret;
+ union cgx_cmd_s cmd;
+
+ cmd.cmd.id = CGX_CMD_GET_SUPPORTED_FEC;
+
+ ret = cgx_intf_req(nix->lmac->cgx->cgx_id, nix->lmac->lmac_id,
+ cmd, &scr0.u, 1);
+ if (ret) {
+ printf("Get supported FEC failed for %s\n", ethdev->name);
+ return -1;
+ }
+
+ printf("Supported FEC type: ");
+ switch (scr0.s.supported_fec.fec) {
+ case 0:
+ printf("FEC_NONE\n");
+ break;
+ case 1:
+ printf("FEC_BASE_R\n");
+ break;
+ case 2:
+ printf("FEC_RS\n");
+ break;
+ case 3:
+ printf("FEC_BASE_R FEC_RS\n");
+ break;
+ }
+
+ cmd.cmd.id = CGX_CMD_GET_LINK_STS;
+ ret = cgx_intf_req(nix->lmac->cgx->cgx_id, nix->lmac->lmac_id,
+ cmd, &scr0.u, 1);
+ if (ret) {
+ printf("Get active fec failed for %s\n", ethdev->name);
+ return -1;
+ }
+ printf("Active FEC type: ");
+ switch (scr0.s.link_sts.fec) {
+ case 0:
+ printf("FEC_NONE\n");
+ break;
+ case 1:
+ printf("FEC_BASE_R\n");
+ break;
+ case 2:
+ printf("FEC_RS\n");
+ break;
+ }
+ return 0;
+}
+
+int cgx_intf_set_fec(struct udevice *ethdev, int type)
+{
+ struct rvu_pf *rvu = dev_get_priv(ethdev);
+ struct nix *nix = rvu->nix;
+ union cgx_scratchx0 scr0;
+ int ret;
+ union cgx_cmd_s cmd;
+
+ cmd.cmd.id = CGX_CMD_SET_FEC;
+ cmd.fec_args.fec = type;
+
+ ret = cgx_intf_req(nix->lmac->cgx->cgx_id, nix->lmac->lmac_id,
+ cmd, &scr0.u, 0);
+ if (ret) {
+ printf("Set FEC type %d failed for %s\n", type, ethdev->name);
+ return -1;
+ }
+ return 0;
+}
+
+int cgx_intf_get_phy_mod_type(struct udevice *ethdev)
+{
+ struct rvu_pf *rvu = dev_get_priv(ethdev);
+ struct nix *nix = rvu->nix;
+ union cgx_scratchx0 scr0;
+ int ret;
+ union cgx_cmd_s cmd;
+
+ cmd.cmd.id = CGX_CMD_GET_PHY_MOD_TYPE;
+
+ ret = cgx_intf_req(nix->lmac->cgx->cgx_id, nix->lmac->lmac_id,
+ cmd, &scr0.u, 1);
+ if (ret) {
+ printf("Get PHYMOD type failed for %s\n", ethdev->name);
+ return -1;
+ }
+ printf("Current phy mod type %s\n",
+ scr0.s.phy_mod_type.mod ? "PAM4" : "NRZ");
+ return 0;
+}
+
+int cgx_intf_set_phy_mod_type(struct udevice *ethdev, int type)
+{
+ struct rvu_pf *rvu = dev_get_priv(ethdev);
+ struct nix *nix = rvu->nix;
+ union cgx_scratchx0 scr0;
+ int ret;
+ union cgx_cmd_s cmd;
+
+ cmd.cmd.id = CGX_CMD_SET_PHY_MOD_TYPE;
+ cmd.phy_mod_args.mod = type;
+
+ ret = cgx_intf_req(nix->lmac->cgx->cgx_id, nix->lmac->lmac_id,
+ cmd, &scr0.u, 0);
+ if (ret) {
+ printf("Set PHYMOD type %d failed for %s\n", type,
+ ethdev->name);
+ return -1;
+ }
+
+ return 0;
+}
+
+int cgx_intf_set_an_lbk(struct udevice *ethdev, int enable)
+{
+ struct rvu_pf *rvu = dev_get_priv(ethdev);
+ struct nix *nix = rvu->nix;
+ union cgx_scratchx0 scr0;
+ int ret;
+ union cgx_cmd_s cmd;
+
+ cmd.cmd.id = CGX_CMD_AN_LOOPBACK;
+ cmd.cmd_args.enable = enable;
+
+ ret = cgx_intf_req(nix->lmac->cgx->cgx_id, nix->lmac->lmac_id,
+ cmd, &scr0.u, 0);
+ if (ret) {
+ printf("Set AN loopback command failed on %s\n", ethdev->name);
+ return -1;
+ }
+ printf("AN loopback %s for %s\n", enable ? "set" : "clear",
+ ethdev->name);
+
+ return 0;
+}
+
+int cgx_intf_get_ignore(struct udevice *ethdev, int cgx, int lmac)
+{
+ struct rvu_pf *rvu;
+ struct nix *nix;
+ union cgx_scratchx0 scr0;
+ int ret, cgx_id = cgx, lmac_id = lmac;
+ union cgx_cmd_s cmd;
+
+ if (ethdev) {
+ rvu = dev_get_priv(ethdev);
+ nix = rvu->nix;
+ cgx_id = nix->lmac->cgx->cgx_id;
+ lmac_id = nix->lmac->lmac_id;
+ }
+ cmd.cmd.id = CGX_CMD_GET_PERSIST_IGNORE;
+
+ ret = cgx_intf_req(cgx_id, lmac_id, cmd, &scr0.u, 1);
+ if (ret) {
+ if (ethdev)
+ printf("Get ignore command failed for %s\n",
+ ethdev->name);
+ else
+ printf("Get ignore command failed for CGX%d LMAC%d\n",
+ cgx_id, lmac_id);
+ return -1;
+ }
+ if (ethdev)
+ printf("Persist settings %signored for %s\n",
+ scr0.s.persist.ignore ? "" : "not ", ethdev->name);
+ else
+ printf("Persist settings %signored for CGX%d LMAC%d\n",
+ scr0.s.persist.ignore ? "" : "not ", cgx_id, lmac_id);
+
+ return 0;
+}
+
+int cgx_intf_set_ignore(struct udevice *ethdev, int cgx, int lmac, int ignore)
+{
+ struct rvu_pf *rvu;
+ struct nix *nix;
+ union cgx_scratchx0 scr0;
+ int ret, cgx_id = cgx, lmac_id = lmac;
+ union cgx_cmd_s cmd;
+
+ if (ethdev) {
+ rvu = dev_get_priv(ethdev);
+ nix = rvu->nix;
+ cgx_id = nix->lmac->cgx->cgx_id;
+ lmac_id = nix->lmac->lmac_id;
+ }
+ cmd.cmd.id = CGX_CMD_SET_PERSIST_IGNORE;
+ cmd.persist_args.ignore = ignore;
+
+ ret = cgx_intf_req(cgx_id, lmac_id, cmd, &scr0.u, 0);
+ if (ret) {
+ if (ethdev)
+ printf("Set ignore command failed for %s\n",
+ ethdev->name);
+ else
+ printf("Set ignore command failed for CGX%d LMAC%d\n",
+ cgx_id, lmac_id);
+ return -1;
+ }
+
+ return 0;
+}
+
+int cgx_intf_set_macaddr(struct udevice *ethdev)
+{
+ struct rvu_pf *rvu = dev_get_priv(ethdev);
+ struct nix *nix = rvu->nix;
+ union cgx_scratchx0 scr0;
+ int ret;
+ union cgx_cmd_s cmd;
+ u64 mac, tmp;
+
+ memcpy((void *)&tmp, nix->lmac->mac_addr, 6);
+ mac = swab64(tmp) >> 16;
+ cmd.cmd.id = CGX_CMD_SET_MAC_ADDR;
+ cmd.mac_args.addr = mac;
+ cmd.mac_args.pf_id = rvu->pfid;
+
+ ret = cgx_intf_req(nix->lmac->cgx->cgx_id, nix->lmac->lmac_id,
+ cmd, &scr0.u, 0);
+ if (ret) {
+ printf("Set user mac addr failed for %s\n", ethdev->name);
+ return -1;
+ }
+
+ return 0;
+}
+
+int cgx_intf_display_eye(u8 qlm, u8 lane)
+{
+ union cgx_scratchx0 scr0;
+ int ret;
+ union cgx_cmd_s cmd;
+
+ cmd.cmd.id = CGX_CMD_DISPLAY_EYE;
+
+ cmd.dsp_eye_args.qlm = qlm;
+ cmd.dsp_eye_args.lane = lane;
+
+ ret = cgx_intf_req(0, 0, cmd, &scr0.u, 0);
+ if (ret)
+ return -1;
+
+ return 0;
+}
+
+int cgx_intf_display_serdes(u8 qlm, u8 lane)
+{
+ union cgx_scratchx0 scr0;
+ int ret;
+ union cgx_cmd_s cmd;
+
+ cmd.cmd.id = CGX_CMD_DISPLAY_SERDES;
+
+ cmd.dsp_eye_args.qlm = qlm;
+ cmd.dsp_eye_args.lane = lane;
+
+ ret = cgx_intf_req(0, 0, cmd, &scr0.u, 0);
+ if (ret)
+ return -1;
+
+ return 0;
+}
diff --git a/drivers/net/octeontx2/cgx_intf.h b/drivers/net/octeontx2/cgx_intf.h
new file mode 100644
index 0000000..62a7203
--- /dev/null
+++ b/drivers/net/octeontx2/cgx_intf.h
@@ -0,0 +1,448 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2018 Marvell International Ltd.
+ */
+
+#ifndef __CGX_INTF_H__
+#define __CGX_INTF_H__
+
+#define CGX_FIRMWARE_MAJOR_VER 1
+#define CGX_FIRMWARE_MINOR_VER 0
+
+/* Register offsets */
+#define CGX_CMR_INT 0x87e0e0000040
+#define CGX_CMR_SCRATCH0 0x87e0e0001050
+#define CGX_CMR_SCRATCH1 0x87e0e0001058
+
+#define CGX_SHIFT(x) (0x1000000 * ((x) & 0x3))
+#define CMR_SHIFT(x) (0x40000 * ((x) & 0x3))
+
+/* CGX error types. set for cmd response status as CGX_STAT_FAIL */
+enum cgx_error_type {
+ CGX_ERR_NONE,
+ CGX_ERR_LMAC_NOT_ENABLED,
+ CGX_ERR_LMAC_MODE_INVALID,
+ CGX_ERR_REQUEST_ID_INVALID,
+ CGX_ERR_PREV_ACK_NOT_CLEAR,
+ CGX_ERR_PHY_LINK_DOWN,
+ CGX_ERR_PCS_RESET_FAIL,
+ CGX_ERR_AN_CPT_FAIL,
+ CGX_ERR_TX_NOT_IDLE,
+ CGX_ERR_RX_NOT_IDLE,
+ CGX_ERR_SPUX_BR_BLKLOCK_FAIL,
+ CGX_ERR_SPUX_RX_ALIGN_FAIL,
+ CGX_ERR_SPUX_TX_FAULT,
+ CGX_ERR_SPUX_RX_FAULT,
+ CGX_ERR_SPUX_RESET_FAIL,
+ CGX_ERR_SPUX_AN_RESET_FAIL,
+ CGX_ERR_SPUX_USX_AN_RESET_FAIL,
+ CGX_ERR_SMUX_RX_LINK_NOT_OK,
+ CGX_ERR_PCS_LINK_FAIL,
+ CGX_ERR_TRAINING_FAIL,
+ CGX_ERR_RX_EQU_FAIL,
+ CGX_ERR_SPUX_BER_FAIL,
+ CGX_ERR_SPUX_RSFEC_ALGN_FAIL,
+ CGX_ERR_SPUX_MARKER_LOCK_FAIL,
+ CGX_ERR_SET_FEC_INVALID,
+ CGX_ERR_SET_FEC_FAIL,
+ CGX_ERR_MODULE_INVALID,
+ CGX_ERR_MODULE_NOT_PRESENT,
+ CGX_ERR_SPEED_CHANGE_INVALID, /* = 28 */
+ /* FIXME : add more error types when adding support for new modes */
+};
+
+/* LINK speed types */
+enum cgx_link_speed {
+ CGX_LINK_NONE,
+ CGX_LINK_10M,
+ CGX_LINK_100M,
+ CGX_LINK_1G,
+ CGX_LINK_2HG, /* 2.5 Gbps */
+ CGX_LINK_5G,
+ CGX_LINK_10G,
+ CGX_LINK_20G,
+ CGX_LINK_25G,
+ CGX_LINK_40G,
+ CGX_LINK_50G,
+ CGX_LINK_80G,
+ CGX_LINK_100G,
+ CGX_LINK_MAX,
+};
+
+/* REQUEST ID types. Input to firmware */
+enum cgx_cmd_id {
+ CGX_CMD_NONE = 0,
+ CGX_CMD_GET_FW_VER,
+ CGX_CMD_GET_MAC_ADDR,
+ CGX_CMD_SET_MTU,
+ CGX_CMD_GET_LINK_STS, /* optional to user */
+ CGX_CMD_LINK_BRING_UP, /* = 5 */
+ CGX_CMD_LINK_BRING_DOWN,
+ CGX_CMD_INTERNAL_LBK,
+ CGX_CMD_EXTERNAL_LBK,
+ CGX_CMD_HIGIG,
+ CGX_CMD_LINK_STAT_CHANGE, /* = 10 */
+ CGX_CMD_MODE_CHANGE, /* hot plug support */
+ CGX_CMD_INTF_SHUTDOWN,
+ CGX_CMD_GET_MKEX_SIZE,
+ CGX_CMD_GET_MKEX_PROFILE,
+ CGX_CMD_GET_FWD_BASE, /* get base address of shared FW data */
+ CGX_CMD_GET_LINK_MODES, /* Supported Link Modes */
+ CGX_CMD_SET_LINK_MODE,
+ CGX_CMD_GET_SUPPORTED_FEC,
+ CGX_CMD_SET_FEC,
+ CGX_CMD_GET_AN, /* = 20 */
+ CGX_CMD_SET_AN,
+ CGX_CMD_GET_ADV_LINK_MODES,
+ CGX_CMD_GET_ADV_FEC,
+ CGX_CMD_GET_PHY_MOD_TYPE, /* line-side modulation type: NRZ or PAM4 */
+ CGX_CMD_SET_PHY_MOD_TYPE, /* = 25 */
+ CGX_CMD_PRBS,
+ CGX_CMD_DISPLAY_EYE,
+ CGX_CMD_GET_PHY_FEC_STATS,
+ CGX_CMD_DISPLAY_SERDES,
+ CGX_CMD_AN_LOOPBACK, /* = 30 */
+ CGX_CMD_GET_PERSIST_IGNORE,
+ CGX_CMD_SET_PERSIST_IGNORE,
+ CGX_CMD_SET_MAC_ADDR,
+};
+
+/* async event ids */
+enum cgx_evt_id {
+ CGX_EVT_NONE,
+ CGX_EVT_LINK_CHANGE,
+};
+
+/* event types - cause of interrupt */
+enum cgx_evt_type {
+ CGX_EVT_ASYNC,
+ CGX_EVT_CMD_RESP
+};
+
+enum cgx_stat {
+ CGX_STAT_SUCCESS,
+ CGX_STAT_FAIL
+};
+
+enum cgx_cmd_own {
+ /* default ownership with kernel/uefi/u-boot */
+ CGX_OWN_NON_SECURE_SW,
+ /* set by kernel/uefi/u-boot after posting a new request to ATF */
+ CGX_OWN_FIRMWARE,
+};
+
+/* Supported LINK MODE enums
+ * Each link mode is a bit mask of these
+ * enums which are represented as bits
+ */
+enum cgx_mode_t {
+ CGX_MODE_SGMII_BIT = 0,
+ CGX_MODE_1000_BASEX_BIT,
+ CGX_MODE_QSGMII_BIT,
+ CGX_MODE_10G_C2C_BIT,
+ CGX_MODE_10G_C2M_BIT,
+ CGX_MODE_10G_KR_BIT,
+ CGX_MODE_20G_C2C_BIT,
+ CGX_MODE_25G_C2C_BIT,
+ CGX_MODE_25G_C2M_BIT,
+ CGX_MODE_25G_2_C2C_BIT,
+ CGX_MODE_25G_CR_BIT,
+ CGX_MODE_25G_KR_BIT,
+ CGX_MODE_40G_C2C_BIT,
+ CGX_MODE_40G_C2M_BIT,
+ CGX_MODE_40G_CR4_BIT,
+ CGX_MODE_40G_KR4_BIT,
+ CGX_MODE_40GAUI_C2C_BIT,
+ CGX_MODE_50G_C2C_BIT,
+ CGX_MODE_50G_C2M_BIT,
+ CGX_MODE_50G_4_C2C_BIT,
+ CGX_MODE_50G_CR_BIT,
+ CGX_MODE_50G_KR_BIT,
+ CGX_MODE_80GAUI_C2C_BIT,
+ CGX_MODE_100G_C2C_BIT,
+ CGX_MODE_100G_C2M_BIT,
+ CGX_MODE_100G_CR4_BIT,
+ CGX_MODE_100G_KR4_BIT,
+ CGX_MODE_MAX_BIT /* = 29 */
+};
+
+/* scratchx(0) CSR used for ATF->non-secure SW communication.
+ * This acts as the status register
+ * Provides details on command ack/status, link status, error details
+ */
+
+/* CAUTION : below structures are placed in order based on the bit positions
+ * For any updates/new bitfields, corresponding structures needs to be updated
+ */
+struct cgx_evt_sts_s { /* start from bit 0 */
+ u64 ack:1;
+ u64 evt_type:1; /* cgx_evt_type */
+ u64 stat:1; /* cgx_stat */
+ u64 id:6; /* cgx_evt_id/cgx_cmd_id */
+ u64 reserved:55;
+};
+
+/* all the below structures are in the same memory location of SCRATCHX(0)
+ * value can be read/written based on command ID
+ */
+
+/* Resp to command IDs with command status as CGX_STAT_FAIL
+ * Not applicable for commands :
+ * CGX_CMD_LINK_BRING_UP/DOWN/CGX_EVT_LINK_CHANGE
+ * check struct cgx_lnk_sts_s comments
+ */
+struct cgx_err_sts_s { /* start from bit 9 */
+ u64 reserved1:9;
+ u64 type:10; /* cgx_error_type */
+ u64 reserved2:35;
+};
+
+/* Resp to cmd ID as CGX_CMD_GET_FW_VER with cmd status as CGX_STAT_SUCCESS */
+struct cgx_ver_s { /* start from bit 9 */
+ u64 reserved1:9;
+ u64 major_ver:4;
+ u64 minor_ver:4;
+ u64 reserved2:47;
+};
+
+/* Resp to cmd ID as CGX_CMD_GET_MAC_ADDR with cmd status as CGX_STAT_SUCCESS
+ * Returns each byte of MAC address in a separate bit field
+ */
+struct cgx_mac_addr_s { /* start from bit 9 */
+ u64 reserved1:9;
+ u64 addr_0:8;
+ u64 addr_1:8;
+ u64 addr_2:8;
+ u64 addr_3:8;
+ u64 addr_4:8;
+ u64 addr_5:8;
+ u64 reserved2:7;
+};
+
+/* Resp to cmd ID - CGX_CMD_LINK_BRING_UP/DOWN, event ID CGX_EVT_LINK_CHANGE
+ * status can be either CGX_STAT_FAIL or CGX_STAT_SUCCESS
+ * In case of CGX_STAT_FAIL, it indicates CGX configuration failed when
+ * processing link up/down/change command. Both err_type and current link status
+ * will be updated
+ * In case of CGX_STAT_SUCCESS, err_type will be CGX_ERR_NONE and current
+ * link status will be updated
+ */
+struct cgx_lnk_sts_s {
+ u64 reserved1:9;
+ u64 link_up:1;
+ u64 full_duplex:1;
+ u64 speed:4; /* cgx_link_speed */
+ u64 err_type:10;
+ u64 an:1; /* Current AN state : enabled/disabled */
+ u64 fec:2; /* Current FEC type if enabled, if not 0 */
+ u64 port:8; /* Share the current port info if required */
+ u64 mode:8; /* cgx_mode_t enum integer value */
+ u64 reserved2:20;
+};
+
+struct sh_fwd_base_s {
+ u64 reserved1:9;
+ u64 addr:55;
+};
+
+struct cgx_link_modes_s {
+ u64 reserved1:9;
+ u64 modes:55;
+};
+
+/* Resp to cmd ID - CGX_CMD_GET_ADV_FEC/CGX_CMD_GET_SUPPORTED_FEC
+ * fec : 2 bits
+ * typedef enum cgx_fec_type {
+ * CGX_FEC_NONE,
+ * CGX_FEC_BASE_R,
+ * CGX_FEC_RS
+ * } fec_type_t;
+ */
+struct cgx_fec_types_s {
+ u64 reserved1:9;
+ u64 fec:2;
+ u64 reserved2:53;
+};
+
+/* Resp to cmd ID - CGX_CMD_GET_AN */
+struct cgx_get_an_s {
+ u64 reserved1:9;
+ u64 an:1;
+ u64 reserved2:54;
+};
+
+/* Resp to cmd ID - CGX_CMD_GET_PHY_MOD_TYPE */
+struct cgx_get_phy_mod_type_s {
+ u64 reserved1:9;
+ u64 mod:1; /* 0=NRZ, 1=PAM4 */
+ u64 reserved2:54;
+};
+
+/* Resp to cmd ID - CGX_CMD_GET_PERSIST_IGNORE */
+struct cgx_get_flash_ignore_s {
+ uint64_t reserved1:9;
+ uint64_t ignore:1;
+ uint64_t reserved2:54;
+};
+
+union cgx_rsp_sts {
+ /* Fixed, applicable for all commands/events */
+ struct cgx_evt_sts_s evt_sts;
+ /* response to CGX_CMD_LINK_BRINGUP/DOWN/LINK_CHANGE */
+ struct cgx_lnk_sts_s link_sts;
+ /* response to CGX_CMD_GET_FW_VER */
+ struct cgx_ver_s ver;
+ /* response to CGX_CMD_GET_MAC_ADDR */
+ struct cgx_mac_addr_s mac_s;
+ /* response to CGX_CMD_GET_FWD_BASE */
+ struct sh_fwd_base_s fwd_base_s;
+ /* response if evt_status = CMD_FAIL */
+ struct cgx_err_sts_s err;
+ /* response to CGX_CMD_GET_SUPPORTED_FEC */
+ struct cgx_fec_types_s supported_fec;
+ /* response to CGX_CMD_GET_LINK_MODES */
+ struct cgx_link_modes_s supported_modes;
+ /* response to CGX_CMD_GET_ADV_LINK_MODES */
+ struct cgx_link_modes_s adv_modes;
+ /* response to CGX_CMD_GET_ADV_FEC */
+ struct cgx_fec_types_s adv_fec;
+ /* response to CGX_CMD_GET_AN */
+ struct cgx_get_an_s an;
+ /* response to CGX_CMD_GET_PHY_MOD_TYPE */
+ struct cgx_get_phy_mod_type_s phy_mod_type;
+ /* response to CGX_CMD_GET_PERSIST_IGNORE */
+ struct cgx_get_flash_ignore_s persist;
+#ifdef NT_FW_CONFIG
+ /* response to CGX_CMD_GET_MKEX_SIZE */
+ struct cgx_mcam_profile_sz_s prfl_sz;
+ /* response to CGX_CMD_GET_MKEX_PROFILE */
+ struct cgx_mcam_profile_addr_s prfl_addr;
+#endif
+};
+
+union cgx_scratchx0 {
+ u64 u;
+ union cgx_rsp_sts s;
+};
+
+/* scratchx(1) CSR used for non-secure SW->ATF communication
+ * This CSR acts as a command register
+ */
+struct cgx_cmd { /* start from bit 2 */
+ u64 reserved1:2;
+ u64 id:6; /* cgx_request_id */
+ u64 reserved2:56;
+};
+
+/* all the below structures are in the same memory location of SCRATCHX(1)
+ * corresponding arguments for command Id needs to be updated
+ */
+
+/* Any command using enable/disable as an argument need
+ * to pass the option via this structure.
+ * Ex: Loopback, HiGig...
+ */
+struct cgx_ctl_args { /* start from bit 8 */
+ u64 reserved1:8;
+ u64 enable:1;
+ u64 reserved2:55;
+};
+
+/* command argument to be passed for cmd ID - CGX_CMD_SET_MTU */
+struct cgx_mtu_args {
+ u64 reserved1:8;
+ u64 size:16;
+ u64 reserved2:40;
+};
+
+/* command argument to be passed for cmd ID - CGX_CMD_MODE_CHANGE */
+struct cgx_mode_change_args {
+ uint64_t reserved1:8;
+ uint64_t speed:4; /* cgx_link_speed enum */
+ uint64_t duplex:1; /* 0 - full duplex, 1 - half duplex */
+ uint64_t an:1; /* 0 - disable AN, 1 - enable AN */
+ uint64_t port:8; /* device port */
+ uint64_t mode:42;
+};
+
+/* command argument to be passed for cmd ID - CGX_CMD_LINK_CHANGE */
+struct cgx_link_change_args { /* start from bit 8 */
+ u64 reserved1:8;
+ u64 link_up:1;
+ u64 full_duplex:1;
+ u64 speed:4; /* cgx_link_speed */
+ u64 reserved2:50;
+};
+
+/* command argument to be passed for cmd ID - CGX_CMD_SET_LINK_MODE */
+struct cgx_set_mode_args {
+ u64 reserved1:8;
+ u64 mode:56;
+};
+
+/* command argument to be passed for cmd ID - CGX_CMD_SET_FEC */
+struct cgx_set_fec_args {
+ u64 reserved1:8;
+ u64 fec:2;
+ u64 reserved2:54;
+};
+
+/* command argument to be passed for cmd ID - CGX_CMD_SET_PHY_MOD_TYPE */
+struct cgx_set_phy_mod_args {
+ u64 reserved1:8;
+ u64 mod:1; /* 0=NRZ, 1=PAM4 */
+ u64 reserved2:55;
+};
+
+/* command argument to be passed for cmd ID - CGX_CMD_SET_PERSIST_IGNORE */
+struct cgx_set_flash_ignore_args {
+ uint64_t reserved1:8;
+ uint64_t ignore:1;
+ uint64_t reserved2:55;
+};
+
+/* command argument to be passed for cmd ID - CGX_CMD_SET_MAC_ADDR */
+struct cgx_mac_addr_args {
+ uint64_t reserved1:8;
+ uint64_t addr:48;
+ uint64_t pf_id:8;
+};
+
+struct cgx_prbs_args {
+ u64 reserved1:8; /* start from bit 8 */
+ u64 lane:8;
+ u64 qlm:8;
+ u64 stop_on_error:1;
+ u64 mode:8;
+ u64 time:31;
+};
+
+struct cgx_display_eye_args {
+ u64 reserved1:8; /* start from bit 8 */
+ u64 qlm:8;
+ u64 lane:47;
+};
+
+union cgx_cmd_s {
+ u64 own_status:2; /* cgx_cmd_own */
+ struct cgx_cmd cmd;
+ struct cgx_ctl_args cmd_args;
+ struct cgx_mtu_args mtu_size;
+ struct cgx_link_change_args lnk_args; /* Input to CGX_CMD_LINK_CHANGE */
+ struct cgx_set_mode_args mode_args;
+ struct cgx_mode_change_args mode_change_args;
+ struct cgx_set_fec_args fec_args;
+ struct cgx_set_phy_mod_args phy_mod_args;
+ struct cgx_set_flash_ignore_args persist_args;
+ struct cgx_mac_addr_args mac_args;
+ /* any other arg for command id * like : mtu, dmac filtering control */
+ struct cgx_prbs_args prbs_args;
+ struct cgx_display_eye_args dsp_eye_args;
+};
+
+union cgx_scratchx1 {
+ u64 u;
+ union cgx_cmd_s s;
+};
+
+#endif /* __CGX_INTF_H__ */
diff --git a/drivers/net/octeontx2/lmt.h b/drivers/net/octeontx2/lmt.h
new file mode 100644
index 0000000..84a7eab
--- /dev/null
+++ b/drivers/net/octeontx2/lmt.h
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2018 Marvell International Ltd.
+ */
+
+/**
+ * Atomically adds a signed value to a 64 bit (aligned) memory location,
+ * and returns previous value.
+ *
+ * This version does not perform 'sync' operations to enforce memory
+ * operations. This should only be used when there are no memory operation
+ * ordering constraints. (This should NOT be used for reference counting -
+ * use the standard version instead.)
+ *
+ * @param ptr address in memory to add incr to
+ * @param incr amount to increment memory location by (signed)
+ *
+ * @return Value of memory location before increment
+ */
+static inline s64 atomic_fetch_and_add64_nosync(s64 *ptr, s64 incr)
+{
+ s64 result;
+ /* Atomic add with no ordering */
+ asm volatile("ldadd %x[i], %x[r], [%[b]]"
+ : [r] "=r" (result), "+m" (*ptr)
+ : [i] "r" (incr), [b] "r" (ptr)
+ : "memory");
+ return result;
+}
+
+static inline void lmt_cancel(const struct nix *nix)
+{
+ writeq(0, nix->lmt_base + LMT_LF_LMTCANCEL());
+}
+
+static inline u64 *lmt_store_ptr(struct nix *nix)
+{
+ return (u64 *)((u8 *)(nix->lmt_base) +
+ LMT_LF_LMTLINEX(0));
+}
+
+static inline s64 lmt_submit(u64 io_address)
+{
+ s64 result = 0;
+
+ asm volatile("ldeor xzr, %x[rf],[%[rs]]"
+ : [rf] "=r"(result) : [rs] "r"(io_address));
+ return result;
+}
diff --git a/drivers/net/octeontx2/nix.c b/drivers/net/octeontx2/nix.c
new file mode 100644
index 0000000..0a3e8e4
--- /dev/null
+++ b/drivers/net/octeontx2/nix.c
@@ -0,0 +1,831 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018 Marvell International Ltd.
+ */
+
+#include <dm.h>
+#include <errno.h>
+#include <log.h>
+#include <malloc.h>
+#include <memalign.h>
+#include <misc.h>
+#include <net.h>
+#include <pci.h>
+#include <watchdog.h>
+
+#include <asm/arch/board.h>
+#include <asm/arch/csrs/csrs-lmt.h>
+#include <asm/io.h>
+#include <asm/types.h>
+
+#include <linux/delay.h>
+#include <linux/log2.h>
+#include <linux/types.h>
+
+#include "nix.h"
+#include "lmt.h"
+#include "cgx.h"
+
+/**
+ * NIX needs a lot of memory areas. Rather than handle all the failure cases,
+ * we'll use a wrapper around alloc that prints an error if a memory
+ * allocation fails.
+ *
+ * @param num_elements
+ * Number of elements to allocate
+ * @param elem_size Size of each element
+ * @param msg Text string to show when allocation fails
+ *
+ * @return A valid memory location or NULL on failure
+ */
+static void *nix_memalloc(int num_elements, size_t elem_size, const char *msg)
+{
+ size_t alloc_size = num_elements * elem_size;
+ void *base = memalign(CONFIG_SYS_CACHELINE_SIZE, alloc_size);
+
+ if (!base)
+ printf("NIX: Mem alloc failed for %s (%d * %zu = %zu bytes)\n",
+ msg ? msg : __func__, num_elements, elem_size,
+ alloc_size);
+ else
+ memset(base, 0, alloc_size);
+
+ debug("NIX: Memory alloc for %s (%d * %zu = %zu bytes) at %p\n",
+ msg ? msg : __func__, num_elements, elem_size, alloc_size, base);
+ return base;
+}
+
+int npc_lf_setup(struct nix *nix)
+{
+ int err;
+
+ err = npc_lf_admin_setup(nix);
+ if (err) {
+ printf("%s: Error setting up npc lf admin\n", __func__);
+ return err;
+ }
+
+ return 0;
+}
+
+static int npa_setup_pool(struct npa *npa, u32 pool_id,
+ size_t buffer_size, u32 queue_length, void *buffers[])
+{
+ struct {
+ union npa_lf_aura_op_free0 f0;
+ union npa_lf_aura_op_free1 f1;
+ } aura_descr;
+ int index;
+
+ for (index = 0; index < queue_length; index++) {
+ buffers[index] = memalign(CONFIG_SYS_CACHELINE_SIZE,
+ buffer_size);
+ if (!buffers[index]) {
+ printf("%s: Out of memory %d, size: %zu\n",
+ __func__, index, buffer_size);
+ return -ENOMEM;
+ }
+ debug("%s: allocating buffer %d, addr %p size: %zu\n",
+ __func__, index, buffers[index], buffer_size);
+
+ /* Add the newly obtained pointer to the pool. 128 bit
+ * writes only.
+ */
+ aura_descr.f0.s.addr = (u64)buffers[index];
+ aura_descr.f1.u = 0;
+ aura_descr.f1.s.aura = pool_id;
+ st128(npa->npa_base + NPA_LF_AURA_OP_FREE0(),
+ aura_descr.f0.u, aura_descr.f1.u);
+ }
+
+ return 0;
+}
+
+int npa_lf_setup(struct nix *nix)
+{
+ struct rvu_pf *rvu = dev_get_priv(nix->dev);
+ struct nix_af *nix_af = nix->nix_af;
+ struct npa *npa;
+ union npa_af_const npa_af_const;
+ union npa_aura_s *aura;
+ union npa_pool_s *pool;
+ union rvu_func_addr_s block_addr;
+ int idx;
+ int stack_page_pointers;
+ int stack_page_bytes;
+ int err;
+
+ npa = (struct npa *)calloc(1, sizeof(struct npa));
+ if (!npa) {
+ printf("%s: out of memory for npa instance\n", __func__);
+ return -ENOMEM;
+ }
+ block_addr.u = 0;
+ block_addr.s.block = RVU_BLOCK_ADDR_E_NPA;
+ npa->npa_base = rvu->pf_base + block_addr.u;
+ npa->npa_af = nix_af->npa_af;
+ nix->npa = npa;
+
+ npa_af_const.u = npa_af_reg_read(npa->npa_af, NPA_AF_CONST());
+ stack_page_pointers = npa_af_const.s.stack_page_ptrs;
+ stack_page_bytes = npa_af_const.s.stack_page_bytes;
+
+ npa->stack_pages[NPA_POOL_RX] = (RQ_QLEN + stack_page_pointers - 1) /
+ stack_page_pointers;
+ npa->stack_pages[NPA_POOL_TX] = (SQ_QLEN + stack_page_pointers - 1) /
+ stack_page_pointers;
+ npa->stack_pages[NPA_POOL_SQB] = (SQB_QLEN + stack_page_pointers - 1) /
+ stack_page_pointers;
+ npa->pool_stack_pointers = stack_page_pointers;
+
+ npa->q_len[NPA_POOL_RX] = RQ_QLEN;
+ npa->q_len[NPA_POOL_TX] = SQ_QLEN;
+ npa->q_len[NPA_POOL_SQB] = SQB_QLEN;
+
+ npa->buf_size[NPA_POOL_RX] = MAX_MTU + CONFIG_SYS_CACHELINE_SIZE;
+ npa->buf_size[NPA_POOL_TX] = MAX_MTU + CONFIG_SYS_CACHELINE_SIZE;
+ npa->buf_size[NPA_POOL_SQB] = nix_af->sqb_size;
+
+ npa->aura_ctx = nix_memalloc(NPA_POOL_COUNT,
+ sizeof(union npa_aura_s),
+ "aura context");
+ if (!npa->aura_ctx) {
+ printf("%s: Out of memory for aura context\n", __func__);
+ return -ENOMEM;
+ }
+
+ for (idx = 0; idx < NPA_POOL_COUNT; idx++) {
+ npa->pool_ctx[idx] = nix_memalloc(1,
+ sizeof(union npa_pool_s),
+ "pool context");
+ if (!npa->pool_ctx[idx]) {
+ printf("%s: Out of memory for pool context\n",
+ __func__);
+ return -ENOMEM;
+ }
+ npa->pool_stack[idx] = nix_memalloc(npa->stack_pages[idx],
+ stack_page_bytes,
+ "pool stack");
+ if (!npa->pool_stack[idx]) {
+ printf("%s: Out of memory for pool stack\n", __func__);
+ return -ENOMEM;
+ }
+ }
+
+ err = npa_lf_admin_setup(npa, nix->lf, (dma_addr_t)npa->aura_ctx);
+ if (err) {
+ printf("%s: Error setting up NPA LF admin for lf %d\n",
+ __func__, nix->lf);
+ return err;
+ }
+
+ /* Set up the auras */
+ for (idx = 0; idx < NPA_POOL_COUNT; idx++) {
+ aura = npa->aura_ctx + (idx * sizeof(union npa_aura_s));
+ pool = npa->pool_ctx[idx];
+ debug("%s aura %p pool %p\n", __func__, aura, pool);
+ memset(aura, 0, sizeof(union npa_aura_s));
+ aura->s.fc_ena = 0;
+ aura->s.pool_addr = (u64)npa->pool_ctx[idx];
+ debug("%s aura.s.pool_addr %llx pool_addr %p\n", __func__,
+ aura->s.pool_addr, npa->pool_ctx[idx]);
+ aura->s.shift = 64 - __builtin_clzll(npa->q_len[idx]) - 8;
+ aura->s.count = npa->q_len[idx];
+ aura->s.limit = npa->q_len[idx];
+ aura->s.ena = 1;
+ err = npa_attach_aura(nix_af, nix->lf, aura, idx);
+ if (err)
+ return err;
+
+ memset(pool, 0, sizeof(*pool));
+ pool->s.fc_ena = 0;
+ pool->s.nat_align = 1;
+ pool->s.stack_base = (u64)(npa->pool_stack[idx]);
+ debug("%s pool.s.stack_base %llx stack_base %p\n", __func__,
+ pool->s.stack_base, npa->pool_stack[idx]);
+ pool->s.buf_size =
+ npa->buf_size[idx] / CONFIG_SYS_CACHELINE_SIZE;
+ pool->s.stack_max_pages = npa->stack_pages[idx];
+ pool->s.shift =
+ 64 - __builtin_clzll(npa->pool_stack_pointers) - 8;
+ pool->s.ptr_start = 0;
+ pool->s.ptr_end = (1ULL << 40) - 1;
+ pool->s.ena = 1;
+ err = npa_attach_pool(nix_af, nix->lf, pool, idx);
+ if (err)
+ return err;
+ }
+
+ for (idx = 0; idx < NPA_POOL_COUNT; idx++) {
+ npa->buffers[idx] = nix_memalloc(npa->q_len[idx],
+ sizeof(void *),
+ "buffers");
+ if (!npa->buffers[idx]) {
+ printf("%s: Out of memory\n", __func__);
+ return -ENOMEM;
+ }
+ }
+
+ for (idx = 0; idx < NPA_POOL_COUNT; idx++) {
+ err = npa_setup_pool(npa, idx, npa->buf_size[idx],
+ npa->q_len[idx], npa->buffers[idx]);
+ if (err) {
+ printf("%s: Error setting up pool %d\n",
+ __func__, idx);
+ return err;
+ }
+ }
+ return 0;
+}
+
+int npa_lf_shutdown(struct nix *nix)
+{
+ struct npa *npa = nix->npa;
+ int err;
+ int pool;
+
+ err = npa_lf_admin_shutdown(nix->nix_af, nix->lf, NPA_POOL_COUNT);
+ if (err) {
+ printf("%s: Error %d shutting down NPA LF admin\n",
+ __func__, err);
+ return err;
+ }
+ free(npa->aura_ctx);
+ npa->aura_ctx = NULL;
+
+ for (pool = 0; pool < NPA_POOL_COUNT; pool++) {
+ free(npa->pool_ctx[pool]);
+ npa->pool_ctx[pool] = NULL;
+ free(npa->pool_stack[pool]);
+ npa->pool_stack[pool] = NULL;
+ free(npa->buffers[pool]);
+ npa->buffers[pool] = NULL;
+ }
+
+ return 0;
+}
+
+int nix_lf_setup(struct nix *nix)
+{
+ struct nix_af *nix_af = nix->nix_af;
+ int idx;
+ int err = -1;
+
+ /* Alloc NIX RQ HW context memory */
+ nix->rq_ctx_base = nix_memalloc(nix->rq_cnt, nix_af->rq_ctx_sz,
+ "RQ CTX");
+ if (!nix->rq_ctx_base)
+ goto error;
+ memset(nix->rq_ctx_base, 0, nix_af->rq_ctx_sz);
+
+ /* Alloc NIX SQ HW context memory */
+ nix->sq_ctx_base = nix_memalloc(nix->sq_cnt, nix_af->sq_ctx_sz,
+ "SQ CTX");
+ if (!nix->sq_ctx_base)
+ goto error;
+ memset(nix->sq_ctx_base, 0, nix_af->sq_ctx_sz);
+
+ /* Alloc NIX CQ HW context memory */
+ nix->cq_ctx_base = nix_memalloc(nix->cq_cnt, nix_af->cq_ctx_sz,
+ "CQ CTX");
+ if (!nix->cq_ctx_base)
+ goto error;
+ memset(nix->cq_ctx_base, 0, nix_af->cq_ctx_sz * NIX_CQ_COUNT);
+ /* Alloc NIX CQ Ring memory */
+ for (idx = 0; idx < NIX_CQ_COUNT; idx++) {
+ err = qmem_alloc(&nix->cq[idx], CQ_ENTRIES, CQ_ENTRY_SIZE);
+ if (err)
+ goto error;
+ }
+
+ /* Alloc memory for Qints HW contexts */
+ nix->qint_base = nix_memalloc(nix_af->qints, nix_af->qint_ctx_sz,
+ "Qint CTX");
+ if (!nix->qint_base)
+ goto error;
+ /* Alloc memory for CQints HW contexts */
+ nix->cint_base = nix_memalloc(nix_af->cints, nix_af->cint_ctx_sz,
+ "Cint CTX");
+ if (!nix->cint_base)
+ goto error;
+ /* Alloc NIX RSS HW context memory and config the base */
+ nix->rss_base = nix_memalloc(nix->rss_grps, nix_af->rsse_ctx_sz,
+ "RSS CTX");
+ if (!nix->rss_base)
+ goto error;
+
+ err = nix_lf_admin_setup(nix);
+ if (err) {
+ printf("%s: Error setting up LF\n", __func__);
+ goto error;
+ }
+
+ return 0;
+
+error:
+ if (nix->rq_ctx_base)
+ free(nix->rq_ctx_base);
+ nix->rq_ctx_base = NULL;
+ if (nix->rq_ctx_base)
+ free(nix->rq_ctx_base);
+ nix->rq_ctx_base = NULL;
+ if (nix->sq_ctx_base)
+ free(nix->sq_ctx_base);
+ nix->sq_ctx_base = NULL;
+ if (nix->cq_ctx_base)
+ free(nix->cq_ctx_base);
+ nix->cq_ctx_base = NULL;
+
+ for (idx = 0; idx < NIX_CQ_COUNT; idx++)
+ qmem_free(&nix->cq[idx]);
+
+ return err;
+}
+
+int nix_lf_shutdown(struct nix *nix)
+{
+ struct nix_af *nix_af = nix->nix_af;
+ int index;
+ int err;
+
+ err = nix_lf_admin_shutdown(nix_af, nix->lf, nix->cq_cnt,
+ nix->rq_cnt, nix->sq_cnt);
+ if (err) {
+ printf("%s: Error shutting down LF admin\n", __func__);
+ return err;
+ }
+
+ if (nix->rq_ctx_base)
+ free(nix->rq_ctx_base);
+ nix->rq_ctx_base = NULL;
+ if (nix->rq_ctx_base)
+ free(nix->rq_ctx_base);
+ nix->rq_ctx_base = NULL;
+ if (nix->sq_ctx_base)
+ free(nix->sq_ctx_base);
+ nix->sq_ctx_base = NULL;
+ if (nix->cq_ctx_base)
+ free(nix->cq_ctx_base);
+ nix->cq_ctx_base = NULL;
+
+ for (index = 0; index < NIX_CQ_COUNT; index++)
+ qmem_free(&nix->cq[index]);
+
+ debug("%s: nix lf %d reset --\n", __func__, nix->lf);
+ return 0;
+}
+
+struct nix *nix_lf_alloc(struct udevice *dev)
+{
+ union rvu_func_addr_s block_addr;
+ struct nix *nix;
+ struct rvu_pf *rvu = dev_get_priv(dev);
+ struct rvu_af *rvu_af = dev_get_priv(rvu->afdev);
+ union rvu_pf_func_s pf_func;
+ int err;
+
+ debug("%s(%s )\n", __func__, dev->name);
+
+ nix = (struct nix *)calloc(1, sizeof(*nix));
+ if (!nix) {
+ printf("%s: Out of memory for nix instance\n", __func__);
+ return NULL;
+ }
+ nix->nix_af = rvu_af->nix_af;
+
+ block_addr.u = 0;
+ block_addr.s.block = RVU_BLOCK_ADDR_E_NIXX(0);
+ nix->nix_base = rvu->pf_base + block_addr.u;
+ block_addr.u = 0;
+ block_addr.s.block = RVU_BLOCK_ADDR_E_NPC;
+ nix->npc_base = rvu->pf_base + block_addr.u;
+ block_addr.u = 0;
+ block_addr.s.block = RVU_BLOCK_ADDR_E_LMT;
+ nix->lmt_base = rvu->pf_base + block_addr.u;
+
+ pf_func.u = 0;
+ pf_func.s.pf = rvu->pfid;
+ nix->pf_func = pf_func.u;
+ nix->lf = rvu->nix_lfid;
+ nix->pf = rvu->pfid;
+ nix->dev = dev;
+ nix->sq_cnt = 1;
+ nix->rq_cnt = 1;
+ nix->rss_grps = 1;
+ nix->cq_cnt = 2;
+ nix->xqe_sz = NIX_CQE_SIZE_W16;
+
+ nix->lmac = nix_get_cgx_lmac(nix->pf);
+ if (!nix->lmac) {
+ printf("%s: Error: could not find lmac for pf %d\n",
+ __func__, nix->pf);
+ free(nix);
+ return NULL;
+ }
+ nix->lmac->link_num =
+ NIX_LINK_E_CGXX_LMACX(nix->lmac->cgx->cgx_id,
+ nix->lmac->lmac_id);
+ nix->lmac->chan_num =
+ NIX_CHAN_E_CGXX_LMACX_CHX(nix->lmac->cgx->cgx_id,
+ nix->lmac->lmac_id, 0);
+ /* This is rx pkind in 1:1 mapping to NIX_LINK_E */
+ nix->lmac->pknd = nix->lmac->link_num;
+
+ cgx_lmac_set_pkind(nix->lmac, nix->lmac->lmac_id, nix->lmac->pknd);
+ debug("%s(%s CGX%x LMAC%x)\n", __func__, dev->name,
+ nix->lmac->cgx->cgx_id, nix->lmac->lmac_id);
+ debug("%s(%s Link %x Chan %x Pknd %x)\n", __func__, dev->name,
+ nix->lmac->link_num, nix->lmac->chan_num, nix->lmac->pknd);
+
+ err = npa_lf_setup(nix);
+ if (err)
+ return NULL;
+
+ err = npc_lf_setup(nix);
+ if (err)
+ return NULL;
+
+ err = nix_lf_setup(nix);
+ if (err)
+ return NULL;
+
+ return nix;
+}
+
+u64 npa_aura_op_alloc(struct npa *npa, u64 aura_id)
+{
+ union npa_lf_aura_op_allocx op_allocx;
+
+ op_allocx.u = atomic_fetch_and_add64_nosync(npa->npa_base +
+ NPA_LF_AURA_OP_ALLOCX(0), aura_id);
+ return op_allocx.s.addr;
+}
+
+u64 nix_cq_op_status(struct nix *nix, u64 cq_id)
+{
+ union nixx_lf_cq_op_status op_status;
+ s64 *reg = nix->nix_base + NIXX_LF_CQ_OP_STATUS();
+
+ op_status.u = atomic_fetch_and_add64_nosync(reg, cq_id << 32);
+ return op_status.u;
+}
+
+/* TX */
+static inline void nix_write_lmt(struct nix *nix, void *buffer,
+ int num_words)
+{
+ int i;
+
+ u64 *lmt_ptr = lmt_store_ptr(nix);
+ u64 *ptr = buffer;
+
+ debug("%s lmt_ptr %p %p\n", __func__, nix->lmt_base, lmt_ptr);
+ for (i = 0; i < num_words; i++) {
+ debug("%s data %llx lmt_ptr %p\n", __func__, ptr[i],
+ lmt_ptr + i);
+ lmt_ptr[i] = ptr[i];
+ }
+}
+
+void nix_cqe_tx_pkt_handler(struct nix *nix, void *cqe)
+{
+ union nix_cqe_hdr_s *txcqe = (union nix_cqe_hdr_s *)cqe;
+
+ debug("%s: txcqe: %p\n", __func__, txcqe);
+
+ if (txcqe->s.cqe_type != NIX_XQE_TYPE_E_SEND) {
+ printf("%s: Error: Unsupported CQ header type %d\n",
+ __func__, txcqe->s.cqe_type);
+ return;
+ }
+ nix_pf_reg_write(nix, NIXX_LF_CQ_OP_DOOR(),
+ (NIX_CQ_TX << 32) | 1);
+}
+
+void nix_lf_flush_tx(struct udevice *dev)
+{
+ struct rvu_pf *rvu = dev_get_priv(dev);
+ struct nix *nix = rvu->nix;
+ union nixx_lf_cq_op_status op_status;
+ u32 head, tail;
+ void *cq_tx_base = nix->cq[NIX_CQ_TX].base;
+ union nix_cqe_hdr_s *cqe;
+
+ /* ack tx cqe entries */
+ op_status.u = nix_cq_op_status(nix, NIX_CQ_TX);
+ head = op_status.s.head;
+ tail = op_status.s.tail;
+ head &= (nix->cq[NIX_CQ_TX].qsize - 1);
+ tail &= (nix->cq[NIX_CQ_TX].qsize - 1);
+
+ debug("%s cq tx head %d tail %d\n", __func__, head, tail);
+ while (head != tail) {
+ cqe = cq_tx_base + head * nix->cq[NIX_CQ_TX].entry_sz;
+ nix_cqe_tx_pkt_handler(nix, cqe);
+ op_status.u = nix_cq_op_status(nix, NIX_CQ_TX);
+ head = op_status.s.head;
+ tail = op_status.s.tail;
+ head &= (nix->cq[NIX_CQ_TX].qsize - 1);
+ tail &= (nix->cq[NIX_CQ_TX].qsize - 1);
+ debug("%s cq tx head %d tail %d\n", __func__, head, tail);
+ }
+}
+
+int nix_lf_xmit(struct udevice *dev, void *pkt, int pkt_len)
+{
+ struct rvu_pf *rvu = dev_get_priv(dev);
+ struct nix *nix = rvu->nix;
+ struct nix_tx_dr tx_dr;
+ int dr_sz = (sizeof(struct nix_tx_dr) + 15) / 16 - 1;
+ s64 result;
+ void *packet;
+
+ nix_lf_flush_tx(dev);
+ memset((void *)&tx_dr, 0, sizeof(struct nix_tx_dr));
+ /* Dump TX packet in to NPA buffer */
+ packet = (void *)npa_aura_op_alloc(nix->npa, NPA_POOL_TX);
+ if (!packet) {
+ printf("%s TX buffers unavailable\n", __func__);
+ return -1;
+ }
+ memcpy(packet, pkt, pkt_len);
+ debug("%s TX buffer %p\n", __func__, packet);
+
+ tx_dr.hdr.s.aura = NPA_POOL_TX;
+ tx_dr.hdr.s.df = 0;
+ tx_dr.hdr.s.pnc = 1;
+ tx_dr.hdr.s.sq = 0;
+ tx_dr.hdr.s.total = pkt_len;
+ tx_dr.hdr.s.sizem1 = dr_sz - 2; /* FIXME - for now hdr+sg+sg1addr */
+ debug("%s dr_sz %d\n", __func__, dr_sz);
+
+ tx_dr.tx_sg.s.segs = 1;
+ tx_dr.tx_sg.s.subdc = NIX_SUBDC_E_SG;
+ tx_dr.tx_sg.s.seg1_size = pkt_len;
+ tx_dr.tx_sg.s.ld_type = NIX_SENDLDTYPE_E_LDT;
+ tx_dr.sg1_addr = (dma_addr_t)packet;
+
+#define DEBUG_PKT
+#ifdef DEBUG_PKT
+ debug("TX PKT Data\n");
+ for (int i = 0; i < pkt_len; i++) {
+ if (i && (i % 8 == 0))
+ debug("\n");
+ debug("%02x ", *((u8 *)pkt + i));
+ }
+ debug("\n");
+#endif
+ do {
+ nix_write_lmt(nix, &tx_dr, (dr_sz - 1) * 2);
+ __iowmb();
+ result = lmt_submit((u64)(nix->nix_base +
+ NIXX_LF_OP_SENDX(0)));
+ WATCHDOG_RESET();
+ } while (result == 0);
+
+ return 0;
+}
+
+/* RX */
+void nix_lf_flush_rx(struct udevice *dev)
+{
+ struct rvu_pf *rvu = dev_get_priv(dev);
+ struct nix *nix = rvu->nix;
+ union nixx_lf_cq_op_status op_status;
+ void *cq_rx_base = nix->cq[NIX_CQ_RX].base;
+ struct nix_rx_dr *rx_dr;
+ union nix_rx_parse_s *rxparse;
+ u32 head, tail;
+ u32 rx_cqe_sz = nix->cq[NIX_CQ_RX].entry_sz;
+ u64 *seg;
+
+ /* flush rx cqe entries */
+ op_status.u = nix_cq_op_status(nix, NIX_CQ_RX);
+ head = op_status.s.head;
+ tail = op_status.s.tail;
+ head &= (nix->cq[NIX_CQ_RX].qsize - 1);
+ tail &= (nix->cq[NIX_CQ_RX].qsize - 1);
+
+ debug("%s cq rx head %d tail %d\n", __func__, head, tail);
+ while (head != tail) {
+ rx_dr = (struct nix_rx_dr *)cq_rx_base + head * rx_cqe_sz;
+ rxparse = &rx_dr->rx_parse;
+
+ debug("%s: rx parse: %p\n", __func__, rxparse);
+ debug("%s: rx parse: desc_sizem1 %x pkt_lenm1 %x\n",
+ __func__, rxparse->s.desc_sizem1, rxparse->s.pkt_lenm1);
+
+ seg = (dma_addr_t *)(&rx_dr->rx_sg + 1);
+
+ st128(nix->npa->npa_base + NPA_LF_AURA_OP_FREE0(),
+ seg[0], (1ULL << 63) | NPA_POOL_RX);
+
+ debug("%s return %llx to NPA\n", __func__, seg[0]);
+ nix_pf_reg_write(nix, NIXX_LF_CQ_OP_DOOR(),
+ (NIX_CQ_RX << 32) | 1);
+
+ op_status.u = nix_cq_op_status(nix, NIX_CQ_RX);
+ head = op_status.s.head;
+ tail = op_status.s.tail;
+ head &= (nix->cq[NIX_CQ_RX].qsize - 1);
+ tail &= (nix->cq[NIX_CQ_RX].qsize - 1);
+ debug("%s cq rx head %d tail %d\n", __func__, head, tail);
+ }
+}
+
+int nix_lf_free_pkt(struct udevice *dev, uchar *pkt, int pkt_len)
+{
+ struct rvu_pf *rvu = dev_get_priv(dev);
+ struct nix *nix = rvu->nix;
+
+ /* Return rx packet to NPA */
+ debug("%s return %p to NPA\n", __func__, pkt);
+ st128(nix->npa->npa_base + NPA_LF_AURA_OP_FREE0(), (u64)pkt,
+ (1ULL << 63) | NPA_POOL_RX);
+ nix_pf_reg_write(nix, NIXX_LF_CQ_OP_DOOR(),
+ (NIX_CQ_RX << 32) | 1);
+
+ nix_lf_flush_tx(dev);
+ return 0;
+}
+
+int nix_lf_recv(struct udevice *dev, int flags, uchar **packetp)
+{
+ struct rvu_pf *rvu = dev_get_priv(dev);
+ struct nix *nix = rvu->nix;
+ union nixx_lf_cq_op_status op_status;
+ void *cq_rx_base = nix->cq[NIX_CQ_RX].base;
+ struct nix_rx_dr *rx_dr;
+ union nix_rx_parse_s *rxparse;
+ void *pkt, *cqe;
+ int pkt_len = 0;
+ u64 *addr;
+ u32 head, tail;
+
+ /* fetch rx cqe entries */
+ op_status.u = nix_cq_op_status(nix, NIX_CQ_RX);
+ head = op_status.s.head;
+ tail = op_status.s.tail;
+ head &= (nix->cq[NIX_CQ_RX].qsize - 1);
+ tail &= (nix->cq[NIX_CQ_RX].qsize - 1);
+ debug("%s cq rx head %d tail %d\n", __func__, head, tail);
+ if (head == tail)
+ return -EAGAIN;
+
+ debug("%s: rx_base %p head %d sz %d\n", __func__, cq_rx_base, head,
+ nix->cq[NIX_CQ_RX].entry_sz);
+ cqe = cq_rx_base + head * nix->cq[NIX_CQ_RX].entry_sz;
+ rx_dr = (struct nix_rx_dr *)cqe;
+ rxparse = &rx_dr->rx_parse;
+
+ debug("%s: rx completion: %p\n", __func__, cqe);
+ debug("%s: rx dr: %p\n", __func__, rx_dr);
+ debug("%s: rx parse: %p\n", __func__, rxparse);
+ debug("%s: rx parse: desc_sizem1 %x pkt_lenm1 %x\n",
+ __func__, rxparse->s.desc_sizem1, rxparse->s.pkt_lenm1);
+ debug("%s: rx parse: pkind %x chan %x\n",
+ __func__, rxparse->s.pkind, rxparse->s.chan);
+
+ if (rx_dr->hdr.s.cqe_type != NIX_XQE_TYPE_E_RX) {
+ printf("%s: Error: Unsupported CQ header type in Rx %d\n",
+ __func__, rx_dr->hdr.s.cqe_type);
+ return -1;
+ }
+
+ pkt_len = rxparse->s.pkt_lenm1 + 1;
+ addr = (dma_addr_t *)(&rx_dr->rx_sg + 1);
+ pkt = (void *)addr[0];
+
+ debug("%s: segs: %d (%d@0x%llx, %d@0x%llx, %d@0x%llx)\n", __func__,
+ rx_dr->rx_sg.s.segs, rx_dr->rx_sg.s.seg1_size, addr[0],
+ rx_dr->rx_sg.s.seg2_size, addr[1],
+ rx_dr->rx_sg.s.seg3_size, addr[2]);
+ if (pkt_len < rx_dr->rx_sg.s.seg1_size + rx_dr->rx_sg.s.seg2_size +
+ rx_dr->rx_sg.s.seg3_size) {
+ debug("%s: Error: rx buffer size too small\n", __func__);
+ return -1;
+ }
+
+ __iowmb();
+#define DEBUG_PKT
+#ifdef DEBUG_PKT
+ debug("RX PKT Data\n");
+ for (int i = 0; i < pkt_len; i++) {
+ if (i && (i % 8 == 0))
+ debug("\n");
+ debug("%02x ", *((u8 *)pkt + i));
+ }
+ debug("\n");
+#endif
+
+ *packetp = (uchar *)pkt;
+
+ return pkt_len;
+}
+
+int nix_lf_setup_mac(struct udevice *dev)
+{
+ struct rvu_pf *rvu = dev_get_priv(dev);
+ struct nix *nix = rvu->nix;
+ struct eth_pdata *pdata = dev_get_platdata(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 cgx lmac
+ * and call mac filter setup to update new address.
+ */
+ if (memcmp(nix->lmac->mac_addr, pdata->enetaddr, ARP_HLEN)) {
+ memcpy(nix->lmac->mac_addr, pdata->enetaddr, 6);
+ eth_env_set_enetaddr_by_index("eth", rvu->dev->seq,
+ pdata->enetaddr);
+ cgx_lmac_mac_filter_setup(nix->lmac);
+ /* Update user given MAC address to ATF for update
+ * in sh_fwdata to use in Linux.
+ */
+ cgx_intf_set_macaddr(dev);
+ debug("%s: lMAC %pM\n", __func__, nix->lmac->mac_addr);
+ debug("%s: pMAC %pM\n", __func__, pdata->enetaddr);
+ }
+ debug("%s: setupMAC %pM\n", __func__, pdata->enetaddr);
+ return 0;
+}
+
+void nix_lf_halt(struct udevice *dev)
+{
+ struct rvu_pf *rvu = dev_get_priv(dev);
+ struct nix *nix = rvu->nix;
+
+ cgx_lmac_rx_tx_enable(nix->lmac, nix->lmac->lmac_id, false);
+
+ mdelay(1);
+
+ /* Flush tx and rx descriptors */
+ nix_lf_flush_rx(dev);
+ nix_lf_flush_tx(dev);
+}
+
+int nix_lf_init(struct udevice *dev)
+{
+ struct rvu_pf *rvu = dev_get_priv(dev);
+ struct nix *nix = rvu->nix;
+ struct lmac *lmac = nix->lmac;
+ int ret;
+ u64 link_sts;
+ u8 link, speed;
+ u16 errcode;
+
+ printf("Waiting for CGX%d LMAC%d [%s] link status...",
+ lmac->cgx->cgx_id, lmac->lmac_id,
+ lmac_type_to_str[lmac->lmac_type]);
+
+ if (lmac->init_pend) {
+ /* Bring up LMAC */
+ ret = cgx_lmac_link_enable(lmac, lmac->lmac_id,
+ true, &link_sts);
+ lmac->init_pend = 0;
+ } else {
+ ret = cgx_lmac_link_status(lmac, lmac->lmac_id, &link_sts);
+ }
+
+ if (ret) {
+ printf(" [Down]\n");
+ return -1;
+ }
+
+ link = link_sts & 0x1;
+ speed = (link_sts >> 2) & 0xf;
+ errcode = (link_sts >> 6) & 0x2ff;
+ debug("%s: link %x speed %x errcode %x\n",
+ __func__, link, speed, errcode);
+
+ /* Print link status */
+ printf(" [%s]\n", link ? lmac_speed_to_str[speed] : "Down");
+ if (!link)
+ return -1;
+
+ if (!lmac->init_pend)
+ cgx_lmac_rx_tx_enable(lmac, lmac->lmac_id, true);
+
+ return 0;
+}
+
+void nix_get_cgx_lmac_id(struct udevice *dev, int *cgxid, int *lmacid)
+{
+ struct rvu_pf *rvu = dev_get_priv(dev);
+ struct nix *nix = rvu->nix;
+ struct lmac *lmac = nix->lmac;
+
+ *cgxid = lmac->cgx->cgx_id;
+ *lmacid = lmac->lmac_id;
+}
+
+void nix_print_mac_info(struct udevice *dev)
+{
+ struct rvu_pf *rvu = dev_get_priv(dev);
+ struct nix *nix = rvu->nix;
+ struct lmac *lmac = nix->lmac;
+
+ printf(" CGX%d LMAC%d [%s]", lmac->cgx->cgx_id, lmac->lmac_id,
+ lmac_type_to_str[lmac->lmac_type]);
+}
+
diff --git a/drivers/net/octeontx2/nix.h b/drivers/net/octeontx2/nix.h
new file mode 100644
index 0000000..03260dd
--- /dev/null
+++ b/drivers/net/octeontx2/nix.h
@@ -0,0 +1,353 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2018 Marvell International Ltd.
+ */
+
+#ifndef __NIX_H__
+#define __NIX_H__
+
+#include <asm/arch/csrs/csrs-npa.h>
+#include <asm/arch/csrs/csrs-nix.h>
+#include "rvu.h"
+
+/** Maximum number of LMACs supported */
+#define MAX_LMAC 12
+
+/* NIX RX action operation*/
+#define NIX_RX_ACTIONOP_DROP (0x0ull)
+#define NIX_RX_ACTIONOP_UCAST (0x1ull)
+#define NIX_RX_ACTIONOP_UCAST_IPSEC (0x2ull)
+#define NIX_RX_ACTIONOP_MCAST (0x3ull)
+#define NIX_RX_ACTIONOP_RSS (0x4ull)
+
+/* NIX TX action operation*/
+#define NIX_TX_ACTIONOP_DROP (0x0ull)
+#define NIX_TX_ACTIONOP_UCAST_DEFAULT (0x1ull)
+#define NIX_TX_ACTIONOP_UCAST_CHAN (0x2ull)
+#define NIX_TX_ACTIONOP_MCAST (0x3ull)
+#define NIX_TX_ACTIONOP_DROP_VIOL (0x5ull)
+
+#define NIX_INTF_RX 0
+#define NIX_INTF_TX 1
+
+#define NIX_INTF_TYPE_CGX 0
+#define NIX_INTF_TYPE_LBK 1
+#define NIX_MAX_HW_MTU 9212
+#define NIX_MIN_HW_MTU 40
+#define MAX_MTU 1536
+
+#define NPA_POOL_COUNT 3
+#define NPA_AURA_COUNT(x) (1ULL << ((x) + 6))
+#define NPA_POOL_RX 0ULL
+#define NPA_POOL_TX 1ULL
+#define NPA_POOL_SQB 2ULL
+#define RQ_QLEN Q_COUNT(Q_SIZE_1K)
+#define SQ_QLEN Q_COUNT(Q_SIZE_1K)
+#define SQB_QLEN Q_COUNT(Q_SIZE_16)
+
+#define NIX_CQ_RX 0ULL
+#define NIX_CQ_TX 1ULL
+#define NIX_CQ_COUNT 2ULL
+#define NIX_CQE_SIZE_W16 (16 * sizeof(u64))
+#define NIX_CQE_SIZE_W64 (64 * sizeof(u64))
+
+/** Size of aura hardware context */
+#define NPA_AURA_HW_CTX_SIZE 48
+/** Size of pool hardware context */
+#define NPA_POOL_HW_CTX_SIZE 64
+
+#define NPA_DEFAULT_PF_FUNC 0xffff
+
+#define NIX_CHAN_CGX_LMAC_CHX(a, b, c) (0x800 + 0x100 * (a) + 0x10 * (b) + (c))
+#define NIX_LINK_CGX_LMAC(a, b) (0 + 4 * (a) + (b))
+#define NIX_LINK_LBK(a) (12 + (a))
+#define NIX_CHAN_LBK_CHX(a, b) (0 + 0x100 * (a) + (b))
+#define MAX_LMAC_PKIND 12
+
+/** Number of Admin queue entries */
+#define AQ_RING_SIZE Q_COUNT(Q_SIZE_16)
+
+/** Each completion queue contains 256 entries, see NIC_CQ_CTX_S[qsize] */
+#define CQS_QSIZE Q_SIZE_256
+#define CQ_ENTRIES Q_COUNT(CQS_QSIZE)
+/**
+ * Each completion queue entry contains 128 bytes, see
+ * NIXX_AF_LFX_CFG[xqe_size]
+ */
+#define CQ_ENTRY_SIZE NIX_CQE_SIZE_W16
+
+enum npa_aura_size {
+ NPA_AURA_SZ_0,
+ NPA_AURA_SZ_128,
+ NPA_AURA_SZ_256,
+ NPA_AURA_SZ_512,
+ NPA_AURA_SZ_1K,
+ NPA_AURA_SZ_2K,
+ NPA_AURA_SZ_4K,
+ NPA_AURA_SZ_8K,
+ NPA_AURA_SZ_16K,
+ NPA_AURA_SZ_32K,
+ NPA_AURA_SZ_64K,
+ NPA_AURA_SZ_128K,
+ NPA_AURA_SZ_256K,
+ NPA_AURA_SZ_512K,
+ NPA_AURA_SZ_1M,
+ NPA_AURA_SZ_MAX,
+};
+
+#define NPA_AURA_SIZE_DEFAULT NPA_AURA_SZ_128
+
+/* NIX Transmit schedulers */
+enum nix_scheduler {
+ NIX_TXSCH_LVL_SMQ = 0x0,
+ NIX_TXSCH_LVL_MDQ = 0x0,
+ NIX_TXSCH_LVL_TL4 = 0x1,
+ NIX_TXSCH_LVL_TL3 = 0x2,
+ NIX_TXSCH_LVL_TL2 = 0x3,
+ NIX_TXSCH_LVL_TL1 = 0x4,
+ NIX_TXSCH_LVL_CNT = 0x5,
+};
+
+struct cgx;
+
+struct nix_stats {
+ u64 num_packets;
+ u64 num_bytes;
+};
+
+struct nix;
+struct lmac;
+
+struct npa_af {
+ void __iomem *npa_af_base;
+ struct admin_queue aq;
+ u32 aura;
+};
+
+struct npa {
+ struct npa_af *npa_af;
+ void __iomem *npa_base;
+ void __iomem *npc_base;
+ void __iomem *lmt_base;
+ /** Hardware aura context */
+ void *aura_ctx;
+ /** Hardware pool context */
+ void *pool_ctx[NPA_POOL_COUNT];
+ void *pool_stack[NPA_POOL_COUNT];
+ void **buffers[NPA_POOL_COUNT];
+ u32 pool_stack_pages[NPA_POOL_COUNT];
+ u32 pool_stack_pointers;
+ u32 q_len[NPA_POOL_COUNT];
+ u32 buf_size[NPA_POOL_COUNT];
+ u32 stack_pages[NPA_POOL_COUNT];
+};
+
+struct nix_af {
+ struct udevice *dev;
+ struct nix *lmacs[MAX_LMAC];
+ struct npa_af *npa_af;
+ void __iomem *nix_af_base;
+ void __iomem *npc_af_base;
+ struct admin_queue aq;
+ u8 num_lmacs;
+ s8 index;
+ u8 xqe_size;
+ u32 sqb_size;
+ u32 qints;
+ u32 cints;
+ u32 sq_ctx_sz;
+ u32 rq_ctx_sz;
+ u32 cq_ctx_sz;
+ u32 rsse_ctx_sz;
+ u32 cint_ctx_sz;
+ u32 qint_ctx_sz;
+};
+
+struct nix_tx_dr {
+ union nix_send_hdr_s hdr;
+ union nix_send_sg_s tx_sg;
+ dma_addr_t sg1_addr;
+ dma_addr_t sg2_addr;
+ dma_addr_t sg3_addr;
+ u64 in_use;
+};
+
+struct nix_rx_dr {
+ union nix_cqe_hdr_s hdr;
+ union nix_rx_parse_s rx_parse;
+ union nix_rx_sg_s rx_sg;
+};
+
+struct nix {
+ struct udevice *dev;
+ struct eth_device *netdev;
+ struct nix_af *nix_af;
+ struct npa *npa;
+ struct lmac *lmac;
+ union nix_cint_hw_s *cint_base;
+ union nix_cq_ctx_s *cq_ctx_base;
+ union nix_qint_hw_s *qint_base;
+ union nix_rq_ctx_s *rq_ctx_base;
+ union nix_rsse_s *rss_base;
+ union nix_sq_ctx_s *sq_ctx_base;
+ void *cqe_base;
+ struct qmem sq;
+ struct qmem cq[NIX_CQ_COUNT];
+ struct qmem rq;
+ struct qmem rss;
+ struct qmem cq_ints;
+ struct qmem qints;
+ char name[16];
+ void __iomem *nix_base; /** PF reg base */
+ void __iomem *npc_base;
+ void __iomem *lmt_base;
+ struct nix_stats tx_stats;
+ struct nix_stats rx_stats;
+ u32 aura;
+ int pknd;
+ int lf;
+ int pf;
+ u16 pf_func;
+ u32 rq_cnt; /** receive queues count */
+ u32 sq_cnt; /** send queues count */
+ u32 cq_cnt; /** completion queues count */
+ u16 rss_sz;
+ u16 sqb_size;
+ u8 rss_grps;
+ u8 xqe_sz;
+};
+
+struct nix_aq_cq_dis {
+ union nix_aq_res_s resp ALIGNED;
+ union nix_cq_ctx_s cq ALIGNED;
+ union nix_cq_ctx_s mcq ALIGNED;
+};
+
+struct nix_aq_rq_dis {
+ union nix_aq_res_s resp ALIGNED;
+ union nix_rq_ctx_s rq ALIGNED;
+ union nix_rq_ctx_s mrq ALIGNED;
+};
+
+struct nix_aq_sq_dis {
+ union nix_aq_res_s resp ALIGNED;
+ union nix_sq_ctx_s sq ALIGNED;
+ union nix_sq_ctx_s msq ALIGNED;
+};
+
+struct nix_aq_cq_request {
+ union nix_aq_res_s resp ALIGNED;
+ union nix_cq_ctx_s cq ALIGNED;
+};
+
+struct nix_aq_rq_request {
+ union nix_aq_res_s resp ALIGNED;
+ union nix_rq_ctx_s rq ALIGNED;
+};
+
+struct nix_aq_sq_request {
+ union nix_aq_res_s resp ALIGNED;
+ union nix_sq_ctx_s sq ALIGNED;
+};
+
+static inline u64 nix_af_reg_read(struct nix_af *nix_af, u64 offset)
+{
+ u64 val = readq(nix_af->nix_af_base + offset);
+
+ debug("%s reg %p val %llx\n", __func__, nix_af->nix_af_base + offset,
+ val);
+ return val;
+}
+
+static inline void nix_af_reg_write(struct nix_af *nix_af, u64 offset,
+ u64 val)
+{
+ debug("%s reg %p val %llx\n", __func__, nix_af->nix_af_base + offset,
+ val);
+ writeq(val, nix_af->nix_af_base + offset);
+}
+
+static inline u64 nix_pf_reg_read(struct nix *nix, u64 offset)
+{
+ u64 val = readq(nix->nix_base + offset);
+
+ debug("%s reg %p val %llx\n", __func__, nix->nix_base + offset,
+ val);
+ return val;
+}
+
+static inline void nix_pf_reg_write(struct nix *nix, u64 offset,
+ u64 val)
+{
+ debug("%s reg %p val %llx\n", __func__, nix->nix_base + offset,
+ val);
+ writeq(val, nix->nix_base + offset);
+}
+
+static inline u64 npa_af_reg_read(struct npa_af *npa_af, u64 offset)
+{
+ u64 val = readq(npa_af->npa_af_base + offset);
+
+ debug("%s reg %p val %llx\n", __func__, npa_af->npa_af_base + offset,
+ val);
+ return val;
+}
+
+static inline void npa_af_reg_write(struct npa_af *npa_af, u64 offset,
+ u64 val)
+{
+ debug("%s reg %p val %llx\n", __func__, npa_af->npa_af_base + offset,
+ val);
+ writeq(val, npa_af->npa_af_base + offset);
+}
+
+static inline u64 npc_af_reg_read(struct nix_af *nix_af, u64 offset)
+{
+ u64 val = readq(nix_af->npc_af_base + offset);
+
+ debug("%s reg %p val %llx\n", __func__, nix_af->npc_af_base + offset,
+ val);
+ return val;
+}
+
+static inline void npc_af_reg_write(struct nix_af *nix_af, u64 offset,
+ u64 val)
+{
+ debug("%s reg %p val %llx\n", __func__, nix_af->npc_af_base + offset,
+ val);
+ writeq(val, nix_af->npc_af_base + offset);
+}
+
+int npa_attach_aura(struct nix_af *nix_af, int lf,
+ const union npa_aura_s *desc, u32 aura_id);
+int npa_attach_pool(struct nix_af *nix_af, int lf,
+ const union npa_pool_s *desc, u32 pool_id);
+int npa_af_setup(struct npa_af *npa_af);
+int npa_af_shutdown(struct npa_af *npa_af);
+int npa_lf_setup(struct nix *nix);
+int npa_lf_shutdown(struct nix *nix);
+int npa_lf_admin_setup(struct npa *npa, int lf, dma_addr_t aura_base);
+int npa_lf_admin_shutdown(struct nix_af *nix_af, int lf, u32 pool_count);
+
+int npc_lf_admin_setup(struct nix *nix);
+int npc_af_shutdown(struct nix_af *nix_af);
+
+int nix_af_setup(struct nix_af *nix_af);
+int nix_af_shutdown(struct nix_af *nix_af);
+int nix_lf_setup(struct nix *nix);
+int nix_lf_shutdown(struct nix *nix);
+struct nix *nix_lf_alloc(struct udevice *dev);
+int nix_lf_admin_setup(struct nix *nix);
+int nix_lf_admin_shutdown(struct nix_af *nix_af, int lf,
+ u32 cq_count, u32 rq_count, u32 sq_count);
+struct rvu_af *get_af(void);
+
+int nix_lf_setup_mac(struct udevice *dev);
+int nix_lf_read_rom_mac(struct udevice *dev);
+void nix_lf_halt(struct udevice *dev);
+int nix_lf_free_pkt(struct udevice *dev, uchar *pkt, int pkt_len);
+int nix_lf_recv(struct udevice *dev, int flags, uchar **packetp);
+int nix_lf_init(struct udevice *dev);
+int nix_lf_xmit(struct udevice *dev, void *pkt, int pkt_len);
+
+#endif /* __NIX_H__ */
diff --git a/drivers/net/octeontx2/nix_af.c b/drivers/net/octeontx2/nix_af.c
new file mode 100644
index 0000000..d513917
--- /dev/null
+++ b/drivers/net/octeontx2/nix_af.c
@@ -0,0 +1,1102 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018 Marvell International Ltd.
+ */
+
+#include <dm.h>
+#include <errno.h>
+#include <malloc.h>
+#include <memalign.h>
+#include <misc.h>
+#include <net.h>
+#include <pci.h>
+#include <watchdog.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/log2.h>
+#include <asm/arch/board.h>
+#include <asm/arch/csrs/csrs-npc.h>
+#include <asm/arch/csrs/csrs-lmt.h>
+#include <asm/io.h>
+
+#include "nix.h"
+#include "lmt.h"
+#include "cgx.h"
+
+static struct nix_aq_cq_dis cq_dis ALIGNED;
+static struct nix_aq_rq_dis rq_dis ALIGNED;
+static struct nix_aq_sq_dis sq_dis ALIGNED;
+
+/***************
+ * NPA API
+ ***************/
+int npa_attach_aura(struct nix_af *nix_af, int lf,
+ const union npa_aura_s *desc, u32 aura_id)
+{
+ struct npa_af *npa = nix_af->npa_af;
+ union npa_aq_inst_s *inst;
+ union npa_aq_res_s *res;
+ union npa_af_aq_status aq_stat;
+ union npa_aura_s *context;
+ u64 head;
+ ulong start;
+
+ debug("%s(%p, %d, %p, %u)\n", __func__, nix_af, lf, desc, aura_id);
+ aq_stat.u = npa_af_reg_read(npa, NPA_AF_AQ_STATUS());
+ head = aq_stat.s.head_ptr;
+ inst = (union npa_aq_inst_s *)(npa->aq.inst.base) + head;
+ res = (union npa_aq_res_s *)(npa->aq.res.base);
+
+ memset(inst, 0, sizeof(*inst));
+ inst->s.lf = lf;
+ inst->s.doneint = 0;
+ inst->s.ctype = NPA_AQ_CTYPE_E_AURA;
+ inst->s.op = NPA_AQ_INSTOP_E_INIT;
+ inst->s.res_addr = npa->aq.res.iova;
+ inst->s.cindex = aura_id;
+
+ context = (union npa_aura_s *)(npa->aq.res.base +
+ CONFIG_SYS_CACHELINE_SIZE);
+ memset(npa->aq.res.base, 0, npa->aq.res.entry_sz);
+ memcpy(context, desc, sizeof(union npa_aura_s));
+ __iowmb();
+ npa_af_reg_write(npa, NPA_AF_AQ_DOOR(), 1);
+
+ start = get_timer(0);
+ while ((res->s.compcode == NPA_AQ_COMP_E_NOTDONE) &&
+ (get_timer(start) < 1000))
+ WATCHDOG_RESET();
+ if (res->s.compcode != NPA_AQ_COMP_E_GOOD) {
+ printf("%s: Error: result 0x%x not good\n",
+ __func__, res->s.compcode);
+ return -1;
+ }
+
+ return 0;
+}
+
+int npa_attach_pool(struct nix_af *nix_af, int lf,
+ const union npa_pool_s *desc, u32 pool_id)
+{
+ union npa_aq_inst_s *inst;
+ union npa_aq_res_s *res;
+ union npa_af_aq_status aq_stat;
+ struct npa_af *npa = nix_af->npa_af;
+ union npa_aura_s *context;
+ u64 head;
+ ulong start;
+
+ debug("%s(%p, %d, %p, %u)\n", __func__, nix_af, lf, desc, pool_id);
+ aq_stat.u = npa_af_reg_read(npa, NPA_AF_AQ_STATUS());
+ head = aq_stat.s.head_ptr;
+
+ inst = (union npa_aq_inst_s *)(npa->aq.inst.base) + head;
+ res = (union npa_aq_res_s *)(npa->aq.res.base);
+
+ memset(inst, 0, sizeof(*inst));
+ inst->s.cindex = pool_id;
+ inst->s.lf = lf;
+ inst->s.doneint = 0;
+ inst->s.ctype = NPA_AQ_CTYPE_E_POOL;
+ inst->s.op = NPA_AQ_INSTOP_E_INIT;
+ inst->s.res_addr = npa->aq.res.iova;
+
+ context = (union npa_aura_s *)(npa->aq.res.base +
+ CONFIG_SYS_CACHELINE_SIZE);
+ memset(npa->aq.res.base, 0, npa->aq.res.entry_sz);
+ memcpy(context, desc, sizeof(union npa_aura_s));
+ __iowmb();
+ npa_af_reg_write(npa, NPA_AF_AQ_DOOR(), 1);
+
+ start = get_timer(0);
+ while ((res->s.compcode == NPA_AQ_COMP_E_NOTDONE) &&
+ (get_timer(start) < 1000))
+ WATCHDOG_RESET();
+
+ if (res->s.compcode != NPA_AQ_COMP_E_GOOD) {
+ printf("%s: Error: result 0x%x not good\n",
+ __func__, res->s.compcode);
+ return -1;
+ }
+
+ return 0;
+}
+
+int npa_lf_admin_setup(struct npa *npa, int lf, dma_addr_t aura_base)
+{
+ union npa_af_lf_rst lf_rst;
+ union npa_af_lfx_auras_cfg auras_cfg;
+ struct npa_af *npa_af = npa->npa_af;
+
+ debug("%s(%p, %d, 0x%llx)\n", __func__, npa_af, lf, aura_base);
+ lf_rst.u = 0;
+ lf_rst.s.exec = 1;
+ lf_rst.s.lf = lf;
+ npa_af_reg_write(npa_af, NPA_AF_LF_RST(), lf_rst.u);
+
+ do {
+ lf_rst.u = npa_af_reg_read(npa_af, NPA_AF_LF_RST());
+ WATCHDOG_RESET();
+ } while (lf_rst.s.exec);
+
+ /* Set Aura size and enable caching of contexts */
+ auras_cfg.u = npa_af_reg_read(npa_af, NPA_AF_LFX_AURAS_CFG(lf));
+ auras_cfg.s.loc_aura_size = NPA_AURA_SIZE_DEFAULT; //FIXME aura_size;
+ auras_cfg.s.caching = 1;
+ auras_cfg.s.rmt_aura_size = 0;
+ auras_cfg.s.rmt_aura_offset = 0;
+ auras_cfg.s.rmt_lf = 0;
+ npa_af_reg_write(npa_af, NPA_AF_LFX_AURAS_CFG(lf), auras_cfg.u);
+ /* Configure aura HW context base */
+ npa_af_reg_write(npa_af, NPA_AF_LFX_LOC_AURAS_BASE(lf),
+ aura_base);
+
+ return 0;
+}
+
+int npa_lf_admin_shutdown(struct nix_af *nix_af, int lf, u32 pool_count)
+{
+ int pool_id;
+ u32 head;
+ union npa_aq_inst_s *inst;
+ union npa_aq_res_s *res;
+ struct npa_aq_pool_request {
+ union npa_aq_res_s resp ALIGNED;
+ union npa_pool_s p0 ALIGNED;
+ union npa_pool_s p1 ALIGNED;
+ } pool_req ALIGNED;
+ struct npa_aq_aura_request {
+ union npa_aq_res_s resp ALIGNED;
+ union npa_aura_s a0 ALIGNED;
+ union npa_aura_s a1 ALIGNED;
+ } aura_req ALIGNED;
+ union npa_af_aq_status aq_stat;
+ union npa_af_lf_rst lf_rst;
+ struct npa_af *npa = nix_af->npa_af;
+ ulong start;
+
+ for (pool_id = 0; pool_id < pool_count; pool_id++) {
+ aq_stat.u = npa_af_reg_read(npa, NPA_AF_AQ_STATUS());
+ head = aq_stat.s.head_ptr;
+ inst = (union npa_aq_inst_s *)(npa->aq.inst.base) + head;
+ res = &pool_req.resp;
+
+ memset(inst, 0, sizeof(*inst));
+ inst->s.cindex = pool_id;
+ inst->s.lf = lf;
+ inst->s.doneint = 0;
+ inst->s.ctype = NPA_AQ_CTYPE_E_POOL;
+ inst->s.op = NPA_AQ_INSTOP_E_WRITE;
+ inst->s.res_addr = (u64)&pool_req.resp;
+
+ memset((void *)&pool_req, 0, sizeof(pool_req));
+ pool_req.p0.s.ena = 0;
+ pool_req.p1.s.ena = 1; /* Write mask */
+ __iowmb();
+
+ npa_af_reg_write(npa, NPA_AF_AQ_DOOR(), 1);
+
+ start = get_timer(0);
+ while ((res->s.compcode == NPA_AQ_COMP_E_NOTDONE) &&
+ (get_timer(start) < 1000))
+ WATCHDOG_RESET();
+
+ if (res->s.compcode != NPA_AQ_COMP_E_GOOD) {
+ printf("%s: Error: result 0x%x not good for lf %d\n"
+ " aura id %d", __func__, res->s.compcode, lf,
+ pool_id);
+ return -1;
+ }
+ debug("%s(LF %d, pool id %d) disabled\n", __func__, lf,
+ pool_id);
+ }
+
+ for (pool_id = 0; pool_id < pool_count; pool_id++) {
+ aq_stat.u = npa_af_reg_read(npa, NPA_AF_AQ_STATUS());
+ head = aq_stat.s.head_ptr;
+ inst = (union npa_aq_inst_s *)(npa->aq.inst.base) + head;
+ res = &aura_req.resp;
+
+ memset(inst, 0, sizeof(*inst));
+ inst->s.cindex = pool_id;
+ inst->s.lf = lf;
+ inst->s.doneint = 0;
+ inst->s.ctype = NPA_AQ_CTYPE_E_AURA;
+ inst->s.op = NPA_AQ_INSTOP_E_WRITE;
+ inst->s.res_addr = (u64)&aura_req.resp;
+
+ memset((void *)&aura_req, 0, sizeof(aura_req));
+ aura_req.a0.s.ena = 0;
+ aura_req.a1.s.ena = 1; /* Write mask */
+ __iowmb();
+
+ npa_af_reg_write(npa, NPA_AF_AQ_DOOR(), 1);
+
+ start = get_timer(0);
+ while ((res->s.compcode == NPA_AQ_COMP_E_NOTDONE) &&
+ (get_timer(start) < 1000))
+ WATCHDOG_RESET();
+
+ if (res->s.compcode != NPA_AQ_COMP_E_GOOD) {
+ printf("%s: Error: result 0x%x not good for lf %d\n"
+ " aura id %d", __func__, res->s.compcode, lf,
+ pool_id);
+ return -1;
+ }
+ debug("%s(LF %d, aura id %d) disabled\n", __func__, lf,
+ pool_id);
+ }
+
+ /* Reset the LF */
+ lf_rst.u = 0;
+ lf_rst.s.exec = 1;
+ lf_rst.s.lf = lf;
+ npa_af_reg_write(npa, NPA_AF_LF_RST(), lf_rst.u);
+
+ do {
+ lf_rst.u = npa_af_reg_read(npa, NPA_AF_LF_RST());
+ WATCHDOG_RESET();
+ } while (lf_rst.s.exec);
+
+ return 0;
+}
+
+int npa_af_setup(struct npa_af *npa_af)
+{
+ int err;
+ union npa_af_gen_cfg npa_cfg;
+ union npa_af_ndc_cfg ndc_cfg;
+ union npa_af_aq_cfg aq_cfg;
+ union npa_af_blk_rst blk_rst;
+
+ err = rvu_aq_alloc(&npa_af->aq, Q_COUNT(AQ_SIZE),
+ sizeof(union npa_aq_inst_s),
+ sizeof(union npa_aq_res_s));
+ if (err) {
+ printf("%s: Error %d allocating admin queue\n", __func__, err);
+ return err;
+ }
+ debug("%s: NPA admin queue allocated at %p %llx\n", __func__,
+ npa_af->aq.inst.base, npa_af->aq.inst.iova);
+
+ blk_rst.u = 0;
+ blk_rst.s.rst = 1;
+ npa_af_reg_write(npa_af, NPA_AF_BLK_RST(), blk_rst.u);
+
+ /* Wait for reset to complete */
+ do {
+ blk_rst.u = npa_af_reg_read(npa_af, NPA_AF_BLK_RST());
+ WATCHDOG_RESET();
+ } while (blk_rst.s.busy);
+
+ /* Set little Endian */
+ npa_cfg.u = npa_af_reg_read(npa_af, NPA_AF_GEN_CFG());
+ npa_cfg.s.af_be = 0;
+ npa_af_reg_write(npa_af, NPA_AF_GEN_CFG(), npa_cfg.u);
+ /* Enable NDC cache */
+ ndc_cfg.u = npa_af_reg_read(npa_af, NPA_AF_NDC_CFG());
+ ndc_cfg.s.ndc_bypass = 0;
+ npa_af_reg_write(npa_af, NPA_AF_NDC_CFG(), ndc_cfg.u);
+ /* Set up queue size */
+ aq_cfg.u = npa_af_reg_read(npa_af, NPA_AF_AQ_CFG());
+ aq_cfg.s.qsize = AQ_SIZE;
+ npa_af_reg_write(npa_af, NPA_AF_AQ_CFG(), aq_cfg.u);
+ /* Set up queue base address */
+ npa_af_reg_write(npa_af, NPA_AF_AQ_BASE(), npa_af->aq.inst.iova);
+
+ return 0;
+}
+
+int npa_af_shutdown(struct npa_af *npa_af)
+{
+ union npa_af_blk_rst blk_rst;
+
+ blk_rst.u = 0;
+ blk_rst.s.rst = 1;
+ npa_af_reg_write(npa_af, NPA_AF_BLK_RST(), blk_rst.u);
+
+ /* Wait for reset to complete */
+ do {
+ blk_rst.u = npa_af_reg_read(npa_af, NPA_AF_BLK_RST());
+ WATCHDOG_RESET();
+ } while (blk_rst.s.busy);
+
+ rvu_aq_free(&npa_af->aq);
+
+ debug("%s: npa af reset --\n", __func__);
+
+ return 0;
+}
+
+/***************
+ * NIX API
+ ***************/
+/**
+ * Setup SMQ -> TL4 -> TL3 -> TL2 -> TL1 -> MAC mapping
+ *
+ * @param nix Handle to setup
+ *
+ * @return 0, or negative on failure
+ */
+static int nix_af_setup_sq(struct nix *nix)
+{
+ union nixx_af_tl1x_schedule tl1_sched;
+ union nixx_af_tl2x_parent tl2_parent;
+ union nixx_af_tl3x_parent tl3_parent;
+ union nixx_af_tl3_tl2x_cfg tl3_tl2_cfg;
+ union nixx_af_tl3_tl2x_linkx_cfg tl3_tl2_link_cfg;
+ union nixx_af_tl4x_parent tl4_parent;
+ union nixx_af_tl4x_sdp_link_cfg tl4_sdp_link_cfg;
+ union nixx_af_smqx_cfg smq_cfg;
+ union nixx_af_mdqx_schedule mdq_sched;
+ union nixx_af_mdqx_parent mdq_parent;
+ union nixx_af_rx_linkx_cfg link_cfg;
+ int tl1_index = nix->lmac->link_num; /* NIX_LINK_E enum */
+ int tl2_index = tl1_index;
+ int tl3_index = tl2_index;
+ int tl4_index = tl3_index;
+ int smq_index = tl4_index;
+ struct nix_af *nix_af = nix->nix_af;
+ u64 offset = 0;
+
+ tl1_sched.u = nix_af_reg_read(nix_af,
+ NIXX_AF_TL1X_SCHEDULE(tl1_index));
+ tl1_sched.s.rr_quantum = MAX_MTU;
+ nix_af_reg_write(nix_af, NIXX_AF_TL1X_SCHEDULE(tl1_index),
+ tl1_sched.u);
+
+ tl2_parent.u = nix_af_reg_read(nix_af,
+ NIXX_AF_TL2X_PARENT(tl2_index));
+ tl2_parent.s.parent = tl1_index;
+ nix_af_reg_write(nix_af, NIXX_AF_TL2X_PARENT(tl2_index),
+ tl2_parent.u);
+
+ tl3_parent.u = nix_af_reg_read(nix_af,
+ NIXX_AF_TL3X_PARENT(tl3_index));
+ tl3_parent.s.parent = tl2_index;
+ nix_af_reg_write(nix_af, NIXX_AF_TL3X_PARENT(tl3_index),
+ tl3_parent.u);
+ tl3_tl2_cfg.u = nix_af_reg_read(nix_af,
+ NIXX_AF_TL3_TL2X_CFG(tl3_index));
+ tl3_tl2_cfg.s.express = 0;
+ nix_af_reg_write(nix_af, NIXX_AF_TL3_TL2X_CFG(tl3_index),
+ tl3_tl2_cfg.u);
+
+ offset = NIXX_AF_TL3_TL2X_LINKX_CFG(tl3_index,
+ nix->lmac->link_num);
+ tl3_tl2_link_cfg.u = nix_af_reg_read(nix_af, offset);
+ tl3_tl2_link_cfg.s.bp_ena = 1;
+ tl3_tl2_link_cfg.s.ena = 1;
+ tl3_tl2_link_cfg.s.relchan = 0;
+ offset = NIXX_AF_TL3_TL2X_LINKX_CFG(tl3_index,
+ nix->lmac->link_num);
+ nix_af_reg_write(nix_af, offset, tl3_tl2_link_cfg.u);
+
+ tl4_parent.u = nix_af_reg_read(nix_af,
+ NIXX_AF_TL4X_PARENT(tl4_index));
+ tl4_parent.s.parent = tl3_index;
+ nix_af_reg_write(nix_af, NIXX_AF_TL4X_PARENT(tl4_index),
+ tl4_parent.u);
+
+ offset = NIXX_AF_TL4X_SDP_LINK_CFG(tl4_index);
+ tl4_sdp_link_cfg.u = nix_af_reg_read(nix_af, offset);
+ tl4_sdp_link_cfg.s.bp_ena = 0;
+ tl4_sdp_link_cfg.s.ena = 0;
+ tl4_sdp_link_cfg.s.relchan = 0;
+ offset = NIXX_AF_TL4X_SDP_LINK_CFG(tl4_index);
+ nix_af_reg_write(nix_af, offset, tl4_sdp_link_cfg.u);
+
+ smq_cfg.u = nix_af_reg_read(nix_af, NIXX_AF_SMQX_CFG(smq_index));
+ smq_cfg.s.express = 0;
+ smq_cfg.s.lf = nix->lf;
+ smq_cfg.s.desc_shp_ctl_dis = 1;
+ smq_cfg.s.maxlen = MAX_MTU;
+ smq_cfg.s.minlen = NIX_MIN_HW_MTU;
+ nix_af_reg_write(nix_af, NIXX_AF_SMQX_CFG(smq_index), smq_cfg.u);
+
+ mdq_sched.u = nix_af_reg_read(nix_af,
+ NIXX_AF_MDQX_SCHEDULE(smq_index));
+ mdq_sched.s.rr_quantum = MAX_MTU;
+ offset = NIXX_AF_MDQX_SCHEDULE(smq_index);
+ nix_af_reg_write(nix_af, offset, mdq_sched.u);
+ mdq_parent.u = nix_af_reg_read(nix_af,
+ NIXX_AF_MDQX_PARENT(smq_index));
+ mdq_parent.s.parent = tl4_index;
+ nix_af_reg_write(nix_af, NIXX_AF_MDQX_PARENT(smq_index),
+ mdq_parent.u);
+
+ link_cfg.u = 0;
+ link_cfg.s.maxlen = NIX_MAX_HW_MTU;
+ link_cfg.s.minlen = NIX_MIN_HW_MTU;
+ nix_af_reg_write(nix->nix_af,
+ NIXX_AF_RX_LINKX_CFG(nix->lmac->link_num),
+ link_cfg.u);
+
+ return 0;
+}
+
+/**
+ * Issue a command to the NIX AF Admin Queue
+ *
+ * @param nix nix handle
+ * @param lf Logical function number for command
+ * @param op Operation
+ * @param ctype Context type
+ * @param cindex Context index
+ * @param resp Result pointer
+ *
+ * @return 0 for success, -EBUSY on failure
+ */
+static int nix_aq_issue_command(struct nix_af *nix_af,
+ int lf,
+ int op,
+ int ctype,
+ int cindex, union nix_aq_res_s *resp)
+{
+ union nixx_af_aq_status aq_status;
+ union nix_aq_inst_s *aq_inst;
+ union nix_aq_res_s *result = resp;
+ ulong start;
+
+ debug("%s(%p, 0x%x, 0x%x, 0x%x, 0x%x, %p)\n", __func__, nix_af, lf,
+ op, ctype, cindex, resp);
+ aq_status.u = nix_af_reg_read(nix_af, NIXX_AF_AQ_STATUS());
+ aq_inst = (union nix_aq_inst_s *)(nix_af->aq.inst.base) +
+ aq_status.s.head_ptr;
+ aq_inst->u[0] = 0;
+ aq_inst->u[1] = 0;
+ aq_inst->s.op = op;
+ aq_inst->s.ctype = ctype;
+ aq_inst->s.lf = lf;
+ aq_inst->s.cindex = cindex;
+ aq_inst->s.doneint = 0;
+ aq_inst->s.res_addr = (u64)resp;
+ debug("%s: inst@%p: 0x%llx 0x%llx\n", __func__, aq_inst,
+ aq_inst->u[0], aq_inst->u[1]);
+ __iowmb();
+
+ /* Ring doorbell and wait for result */
+ nix_af_reg_write(nix_af, NIXX_AF_AQ_DOOR(), 1);
+
+ start = get_timer(0);
+ /* Wait for completion */
+ do {
+ WATCHDOG_RESET();
+ dsb();
+ } while (result->s.compcode == 0 && get_timer(start) < 2);
+
+ if (result->s.compcode != NIX_AQ_COMP_E_GOOD) {
+ printf("NIX:AQ fail or time out with code %d after %ld ms\n",
+ result->s.compcode, get_timer(start));
+ return -EBUSY;
+ }
+ return 0;
+}
+
+static int nix_attach_receive_queue(struct nix_af *nix_af, int lf)
+{
+ struct nix_aq_rq_request rq_req ALIGNED;
+ int err;
+
+ debug("%s(%p, %d)\n", __func__, nix_af, lf);
+
+ memset(&rq_req, 0, sizeof(struct nix_aq_rq_request));
+
+ rq_req.rq.s.ena = 1;
+ rq_req.rq.s.spb_ena = 1;
+ rq_req.rq.s.ipsech_ena = 0;
+ rq_req.rq.s.ena_wqwd = 0;
+ rq_req.rq.s.cq = NIX_CQ_RX;
+ rq_req.rq.s.substream = 0; /* FIXME: Substream IDs? */
+ rq_req.rq.s.wqe_aura = -1; /* No WQE aura */
+ rq_req.rq.s.spb_aura = NPA_POOL_RX;
+ rq_req.rq.s.lpb_aura = NPA_POOL_RX;
+ /* U-Boot doesn't use WQE group for anything */
+ rq_req.rq.s.pb_caching = 1;
+ rq_req.rq.s.xqe_drop_ena = 0; /* Disable RED dropping */
+ rq_req.rq.s.spb_drop_ena = 0;
+ rq_req.rq.s.lpb_drop_ena = 0;
+ rq_req.rq.s.spb_sizem1 = (MAX_MTU / (3 * 8)) - 1; /* 512 bytes */
+ rq_req.rq.s.lpb_sizem1 = (MAX_MTU / 8) - 1;
+ rq_req.rq.s.first_skip = 0;
+ rq_req.rq.s.later_skip = 0;
+ rq_req.rq.s.xqe_imm_copy = 0;
+ rq_req.rq.s.xqe_hdr_split = 0;
+ rq_req.rq.s.xqe_drop = 0;
+ rq_req.rq.s.xqe_pass = 0;
+ rq_req.rq.s.wqe_pool_drop = 0; /* No WQE pool */
+ rq_req.rq.s.wqe_pool_pass = 0; /* No WQE pool */
+ rq_req.rq.s.spb_aura_drop = 255;
+ rq_req.rq.s.spb_aura_pass = 255;
+ rq_req.rq.s.spb_pool_drop = 0;
+ rq_req.rq.s.spb_pool_pass = 0;
+ rq_req.rq.s.lpb_aura_drop = 255;
+ rq_req.rq.s.lpb_aura_pass = 255;
+ rq_req.rq.s.lpb_pool_drop = 0;
+ rq_req.rq.s.lpb_pool_pass = 0;
+ rq_req.rq.s.qint_idx = 0;
+
+ err = nix_aq_issue_command(nix_af, lf,
+ NIX_AQ_INSTOP_E_INIT,
+ NIX_AQ_CTYPE_E_RQ,
+ 0, &rq_req.resp);
+ if (err) {
+ printf("%s: Error requesting send queue\n", __func__);
+ return err;
+ }
+
+ return 0;
+}
+
+static int nix_attach_send_queue(struct nix *nix)
+{
+ struct nix_af *nix_af = nix->nix_af;
+ struct nix_aq_sq_request sq_req ALIGNED;
+ int err;
+
+ debug("%s(%p)\n", __func__, nix_af);
+ err = nix_af_setup_sq(nix);
+
+ memset(&sq_req, 0, sizeof(sq_req));
+
+ sq_req.sq.s.ena = 1;
+ sq_req.sq.s.cq_ena = 1;
+ sq_req.sq.s.max_sqe_size = NIX_MAXSQESZ_E_W16;
+ sq_req.sq.s.substream = 0; // FIXME: Substream IDs?
+ sq_req.sq.s.sdp_mcast = 0;
+ sq_req.sq.s.cq = NIX_CQ_TX;
+ sq_req.sq.s.cq_limit = 0;
+ sq_req.sq.s.smq = nix->lmac->link_num; // scheduling index
+ sq_req.sq.s.sso_ena = 0;
+ sq_req.sq.s.smq_rr_quantum = MAX_MTU / 4;
+ sq_req.sq.s.default_chan = nix->lmac->chan_num;
+ sq_req.sq.s.sqe_stype = NIX_STYPE_E_STP;
+ sq_req.sq.s.qint_idx = 0;
+ sq_req.sq.s.sqb_aura = NPA_POOL_SQB;
+
+ err = nix_aq_issue_command(nix_af, nix->lf,
+ NIX_AQ_INSTOP_E_INIT,
+ NIX_AQ_CTYPE_E_SQ,
+ 0, &sq_req.resp);
+ if (err) {
+ printf("%s: Error requesting send queue\n", __func__);
+ return err;
+ }
+
+ return 0;
+}
+
+static int nix_attach_completion_queue(struct nix *nix, int cq_idx)
+{
+ struct nix_af *nix_af = nix->nix_af;
+ struct nix_aq_cq_request cq_req ALIGNED;
+ int err;
+
+ debug("%s(%p)\n", __func__, nix_af);
+ memset(&cq_req, 0, sizeof(cq_req));
+ cq_req.cq.s.ena = 1;
+ cq_req.cq.s.bpid = nix->lmac->pknd;
+ cq_req.cq.s.substream = 0; /* FIXME: Substream IDs? */
+ cq_req.cq.s.drop_ena = 0;
+ cq_req.cq.s.caching = 1;
+ cq_req.cq.s.qsize = CQS_QSIZE;
+ cq_req.cq.s.drop = 255 * 7 / 8;
+ cq_req.cq.s.qint_idx = 0;
+ cq_req.cq.s.cint_idx = 0;
+ cq_req.cq.s.base = nix->cq[cq_idx].iova;
+ debug("%s: CQ(%d) base %p\n", __func__, cq_idx,
+ nix->cq[cq_idx].base);
+
+ err = nix_aq_issue_command(nix_af, nix->lf,
+ NIX_AQ_INSTOP_E_INIT,
+ NIX_AQ_CTYPE_E_CQ,
+ cq_idx, &cq_req.resp);
+ if (err) {
+ printf("%s: Error requesting completion queue\n", __func__);
+ return err;
+ }
+ debug("%s: CQ(%d) allocated, base %p\n", __func__, cq_idx,
+ nix->cq[cq_idx].base);
+
+ return 0;
+}
+
+int nix_lf_admin_setup(struct nix *nix)
+{
+ union nixx_af_lfx_rqs_cfg rqs_cfg;
+ union nixx_af_lfx_sqs_cfg sqs_cfg;
+ union nixx_af_lfx_cqs_cfg cqs_cfg;
+ union nixx_af_lfx_rss_cfg rss_cfg;
+ union nixx_af_lfx_cints_cfg cints_cfg;
+ union nixx_af_lfx_qints_cfg qints_cfg;
+ union nixx_af_lfx_rss_grpx rss_grp;
+ union nixx_af_lfx_tx_cfg2 tx_cfg2;
+ union nixx_af_lfx_cfg lfx_cfg;
+ union nixx_af_lf_rst lf_rst;
+ u32 index;
+ struct nix_af *nix_af = nix->nix_af;
+ int err;
+
+ /* Reset the LF */
+ lf_rst.u = 0;
+ lf_rst.s.lf = nix->lf;
+ lf_rst.s.exec = 1;
+ nix_af_reg_write(nix_af, NIXX_AF_LF_RST(), lf_rst.u);
+
+ do {
+ lf_rst.u = nix_af_reg_read(nix_af, NIXX_AF_LF_RST());
+ WATCHDOG_RESET();
+ } while (lf_rst.s.exec);
+
+ /* Config NIX RQ HW context and base*/
+ nix_af_reg_write(nix_af, NIXX_AF_LFX_RQS_BASE(nix->lf),
+ (u64)nix->rq_ctx_base);
+ /* Set caching and queue count in HW */
+ rqs_cfg.u = nix_af_reg_read(nix_af, NIXX_AF_LFX_RQS_CFG(nix->lf));
+ rqs_cfg.s.caching = 1;
+ rqs_cfg.s.max_queuesm1 = nix->rq_cnt - 1;
+ nix_af_reg_write(nix_af, NIXX_AF_LFX_RQS_CFG(nix->lf), rqs_cfg.u);
+
+ /* Config NIX SQ HW context and base*/
+ nix_af_reg_write(nix_af, NIXX_AF_LFX_SQS_BASE(nix->lf),
+ (u64)nix->sq_ctx_base);
+ sqs_cfg.u = nix_af_reg_read(nix_af, NIXX_AF_LFX_SQS_CFG(nix->lf));
+ sqs_cfg.s.caching = 1;
+ sqs_cfg.s.max_queuesm1 = nix->sq_cnt - 1;
+ nix_af_reg_write(nix_af, NIXX_AF_LFX_SQS_CFG(nix->lf), sqs_cfg.u);
+
+ /* Config NIX CQ HW context and base*/
+ nix_af_reg_write(nix_af, NIXX_AF_LFX_CQS_BASE(nix->lf),
+ (u64)nix->cq_ctx_base);
+ cqs_cfg.u = nix_af_reg_read(nix_af, NIXX_AF_LFX_CQS_CFG(nix->lf));
+ cqs_cfg.s.caching = 1;
+ cqs_cfg.s.max_queuesm1 = nix->cq_cnt - 1;
+ nix_af_reg_write(nix_af, NIXX_AF_LFX_CQS_CFG(nix->lf), cqs_cfg.u);
+
+ /* Config NIX RSS HW context and base */
+ nix_af_reg_write(nix_af, NIXX_AF_LFX_RSS_BASE(nix->lf),
+ (u64)nix->rss_base);
+ rss_cfg.u = nix_af_reg_read(nix_af, NIXX_AF_LFX_RSS_CFG(nix->lf));
+ rss_cfg.s.ena = 1;
+ rss_cfg.s.size = ilog2(nix->rss_sz) / 256;
+ nix_af_reg_write(nix_af, NIXX_AF_LFX_RSS_CFG(nix->lf), rss_cfg.u);
+
+ for (index = 0; index < nix->rss_grps; index++) {
+ rss_grp.u = 0;
+ rss_grp.s.sizem1 = 0x7;
+ rss_grp.s.offset = nix->rss_sz * index;
+ nix_af_reg_write(nix_af,
+ NIXX_AF_LFX_RSS_GRPX(nix->lf, index),
+ rss_grp.u);
+ }
+
+ /* Config CQints HW contexts and base */
+ nix_af_reg_write(nix_af, NIXX_AF_LFX_CINTS_BASE(nix->lf),
+ (u64)nix->cint_base);
+ cints_cfg.u = nix_af_reg_read(nix_af,
+ NIXX_AF_LFX_CINTS_CFG(nix->lf));
+ cints_cfg.s.caching = 1;
+ nix_af_reg_write(nix_af, NIXX_AF_LFX_CINTS_CFG(nix->lf),
+ cints_cfg.u);
+
+ /* Config Qints HW context and base */
+ nix_af_reg_write(nix_af, NIXX_AF_LFX_QINTS_BASE(nix->lf),
+ (u64)nix->qint_base);
+ qints_cfg.u = nix_af_reg_read(nix_af,
+ NIXX_AF_LFX_QINTS_CFG(nix->lf));
+ qints_cfg.s.caching = 1;
+ nix_af_reg_write(nix_af, NIXX_AF_LFX_QINTS_CFG(nix->lf),
+ qints_cfg.u);
+
+ debug("%s(%p, %d, %d)\n", __func__, nix_af, nix->lf, nix->pf);
+
+ /* Enable LMTST for this NIX LF */
+ tx_cfg2.u = nix_af_reg_read(nix_af, NIXX_AF_LFX_TX_CFG2(nix->lf));
+ tx_cfg2.s.lmt_ena = 1;
+ nix_af_reg_write(nix_af, NIXX_AF_LFX_TX_CFG2(nix->lf), tx_cfg2.u);
+
+ /* Use 16-word XQEs, write the npa pf_func number only */
+ lfx_cfg.u = nix_af_reg_read(nix_af, NIXX_AF_LFX_CFG(nix->lf));
+ lfx_cfg.s.xqe_size = NIX_XQESZ_E_W16;
+ lfx_cfg.s.npa_pf_func = nix->pf_func;
+ nix_af_reg_write(nix_af, NIXX_AF_LFX_CFG(nix->lf), lfx_cfg.u);
+
+ nix_af_reg_write(nix_af, NIXX_AF_LFX_RX_CFG(nix->lf), 0);
+
+ for (index = 0; index < nix->cq_cnt; index++) {
+ err = nix_attach_completion_queue(nix, index);
+ if (err) {
+ printf("%s: Error attaching completion queue %d\n",
+ __func__, index);
+ return err;
+ }
+ }
+
+ for (index = 0; index < nix->rq_cnt; index++) {
+ err = nix_attach_receive_queue(nix_af, nix->lf);
+ if (err) {
+ printf("%s: Error attaching receive queue %d\n",
+ __func__, index);
+ return err;
+ }
+ }
+
+ for (index = 0; index < nix->sq_cnt; index++) {
+ err = nix_attach_send_queue(nix);
+ if (err) {
+ printf("%s: Error attaching send queue %d\n",
+ __func__, index);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+int nix_lf_admin_shutdown(struct nix_af *nix_af, int lf,
+ u32 cq_count, u32 rq_count, u32 sq_count)
+{
+ union nixx_af_rx_sw_sync sw_sync;
+ union nixx_af_lf_rst lf_rst;
+ int index, err;
+
+ /* Flush all tx packets */
+ sw_sync.u = 0;
+ sw_sync.s.ena = 1;
+ nix_af_reg_write(nix_af, NIXX_AF_RX_SW_SYNC(), sw_sync.u);
+
+ do {
+ sw_sync.u = nix_af_reg_read(nix_af, NIXX_AF_RX_SW_SYNC());
+ WATCHDOG_RESET();
+ } while (sw_sync.s.ena);
+
+ for (index = 0; index < rq_count; index++) {
+ memset((void *)&rq_dis, 0, sizeof(rq_dis));
+ rq_dis.rq.s.ena = 0; /* Context */
+ rq_dis.mrq.s.ena = 1; /* Mask */
+ __iowmb();
+
+ err = nix_aq_issue_command(nix_af, lf,
+ NIX_AQ_INSTOP_E_WRITE,
+ NIX_AQ_CTYPE_E_RQ,
+ index, &rq_dis.resp);
+ if (err) {
+ printf("%s: Error disabling LF %d RQ(%d)\n",
+ __func__, lf, index);
+ return err;
+ }
+ debug("%s: LF %d RQ(%d) disabled\n", __func__, lf, index);
+ }
+
+ for (index = 0; index < sq_count; index++) {
+ memset((void *)&sq_dis, 0, sizeof(sq_dis));
+ sq_dis.sq.s.ena = 0; /* Context */
+ sq_dis.msq.s.ena = 1; /* Mask */
+ __iowmb();
+
+ err = nix_aq_issue_command(nix_af, lf,
+ NIX_AQ_INSTOP_E_WRITE,
+ NIX_AQ_CTYPE_E_SQ,
+ index, &sq_dis.resp);
+ if (err) {
+ printf("%s: Error disabling LF %d SQ(%d)\n",
+ __func__, lf, index);
+ return err;
+ }
+ debug("%s: LF %d SQ(%d) disabled\n", __func__, lf, index);
+ }
+
+ for (index = 0; index < cq_count; index++) {
+ memset((void *)&cq_dis, 0, sizeof(cq_dis));
+ cq_dis.cq.s.ena = 0; /* Context */
+ cq_dis.mcq.s.ena = 1; /* Mask */
+ __iowmb();
+
+ err = nix_aq_issue_command(nix_af, lf,
+ NIX_AQ_INSTOP_E_WRITE,
+ NIX_AQ_CTYPE_E_CQ,
+ index, &cq_dis.resp);
+ if (err) {
+ printf("%s: Error disabling LF %d CQ(%d)\n",
+ __func__, lf, index);
+ return err;
+ }
+ debug("%s: LF %d CQ(%d) disabled\n", __func__, lf, index);
+ }
+
+ /* Reset the LF */
+ lf_rst.u = 0;
+ lf_rst.s.lf = lf;
+ lf_rst.s.exec = 1;
+ nix_af_reg_write(nix_af, NIXX_AF_LF_RST(), lf_rst.u);
+
+ do {
+ lf_rst.u = nix_af_reg_read(nix_af, NIXX_AF_LF_RST());
+ WATCHDOG_RESET();
+ } while (lf_rst.s.exec);
+
+ return 0;
+}
+
+int npc_lf_admin_setup(struct nix *nix)
+{
+ union npc_af_const af_const;
+ union npc_af_pkindx_action0 action0;
+ union npc_af_pkindx_action1 action1;
+ union npc_af_intfx_kex_cfg kex_cfg;
+ union npc_af_intfx_miss_stat_act intfx_stat_act;
+ union npc_af_mcamex_bankx_camx_intf camx_intf;
+ union npc_af_mcamex_bankx_camx_w0 camx_w0;
+ union npc_af_mcamex_bankx_cfg bankx_cfg;
+ union npc_af_mcamex_bankx_stat_act mcamex_stat_act;
+
+ union nix_rx_action_s rx_action;
+ union nix_tx_action_s tx_action;
+
+ struct nix_af *nix_af = nix->nix_af;
+ u32 kpus;
+ int pkind = nix->lmac->link_num;
+ int index;
+ u64 offset;
+
+ debug("%s(%p, pkind 0x%x)\n", __func__, nix_af, pkind);
+ af_const.u = npc_af_reg_read(nix_af, NPC_AF_CONST());
+ kpus = af_const.s.kpus;
+
+ action0.u = 0;
+ action0.s.parse_done = 1;
+ npc_af_reg_write(nix_af, NPC_AF_PKINDX_ACTION0(pkind), action0.u);
+
+ action1.u = 0;
+ npc_af_reg_write(nix_af, NPC_AF_PKINDX_ACTION1(pkind), action1.u);
+
+ kex_cfg.u = 0;
+ kex_cfg.s.keyw = NPC_MCAMKEYW_E_X1;
+ kex_cfg.s.parse_nibble_ena = 0x7;
+ npc_af_reg_write(nix_af,
+ NPC_AF_INTFX_KEX_CFG(NPC_INTF_E_NIXX_RX(0)),
+ kex_cfg.u);
+
+ /* HW Issue */
+ kex_cfg.u = 0;
+ kex_cfg.s.parse_nibble_ena = 0x7;
+ npc_af_reg_write(nix_af,
+ NPC_AF_INTFX_KEX_CFG(NPC_INTF_E_NIXX_TX(0)),
+ kex_cfg.u);
+
+ camx_intf.u = 0;
+ camx_intf.s.intf = ~NPC_INTF_E_NIXX_RX(0);
+ npc_af_reg_write(nix_af,
+ NPC_AF_MCAMEX_BANKX_CAMX_INTF(pkind, 0, 0),
+ camx_intf.u);
+
+ camx_intf.u = 0;
+ camx_intf.s.intf = NPC_INTF_E_NIXX_RX(0);
+ npc_af_reg_write(nix_af,
+ NPC_AF_MCAMEX_BANKX_CAMX_INTF(pkind, 0, 1),
+ camx_intf.u);
+
+ camx_w0.u = 0;
+ camx_w0.s.md = ~(nix->lmac->chan_num) & (~((~0x0ull) << 12));
+ debug("NPC LF ADMIN camx_w0.u %llx\n", camx_w0.u);
+ npc_af_reg_write(nix_af,
+ NPC_AF_MCAMEX_BANKX_CAMX_W0(pkind, 0, 0),
+ camx_w0.u);
+
+ camx_w0.u = 0;
+ camx_w0.s.md = nix->lmac->chan_num;
+ npc_af_reg_write(nix_af,
+ NPC_AF_MCAMEX_BANKX_CAMX_W0(pkind, 0, 1),
+ camx_w0.u);
+
+ npc_af_reg_write(nix_af, NPC_AF_MCAMEX_BANKX_CAMX_W1(pkind, 0, 0),
+ 0);
+
+ npc_af_reg_write(nix_af, NPC_AF_MCAMEX_BANKX_CAMX_W1(pkind, 0, 1),
+ 0);
+
+ /* Enable stats for NPC INTF RX */
+ mcamex_stat_act.u = 0;
+ mcamex_stat_act.s.ena = 1;
+ mcamex_stat_act.s.stat_sel = pkind;
+ npc_af_reg_write(nix_af,
+ NPC_AF_MCAMEX_BANKX_STAT_ACT(pkind, 0),
+ mcamex_stat_act.u);
+ intfx_stat_act.u = 0;
+ intfx_stat_act.s.ena = 1;
+ intfx_stat_act.s.stat_sel = 16;
+ offset = NPC_AF_INTFX_MISS_STAT_ACT(NPC_INTF_E_NIXX_RX(0));
+ npc_af_reg_write(nix_af, offset, intfx_stat_act.u);
+ rx_action.u = 0;
+ rx_action.s.pf_func = nix->pf_func;
+ rx_action.s.op = NIX_RX_ACTIONOP_E_UCAST;
+ npc_af_reg_write(nix_af, NPC_AF_MCAMEX_BANKX_ACTION(pkind, 0),
+ rx_action.u);
+
+ for (index = 0; index < kpus; index++)
+ npc_af_reg_write(nix_af, NPC_AF_KPUX_CFG(index), 0);
+
+ rx_action.u = 0;
+ rx_action.s.pf_func = nix->pf_func;
+ rx_action.s.op = NIX_RX_ACTIONOP_E_DROP;
+ npc_af_reg_write(nix_af,
+ NPC_AF_INTFX_MISS_ACT(NPC_INTF_E_NIXX_RX(0)),
+ rx_action.u);
+ bankx_cfg.u = 0;
+ bankx_cfg.s.ena = 1;
+ npc_af_reg_write(nix_af, NPC_AF_MCAMEX_BANKX_CFG(pkind, 0),
+ bankx_cfg.u);
+
+ tx_action.u = 0;
+ tx_action.s.op = NIX_TX_ACTIONOP_E_UCAST_DEFAULT;
+ npc_af_reg_write(nix_af,
+ NPC_AF_INTFX_MISS_ACT(NPC_INTF_E_NIXX_TX(0)),
+ tx_action.u);
+
+#ifdef DEBUG
+ /* Enable debug capture on RX intf */
+ npc_af_reg_write(nix_af, NPC_AF_DBG_CTL(), 0x4);
+#endif
+
+ return 0;
+}
+
+int npc_af_shutdown(struct nix_af *nix_af)
+{
+ union npc_af_blk_rst blk_rst;
+
+ blk_rst.u = 0;
+ blk_rst.s.rst = 1;
+ npc_af_reg_write(nix_af, NPC_AF_BLK_RST(), blk_rst.u);
+
+ /* Wait for reset to complete */
+ do {
+ blk_rst.u = npc_af_reg_read(nix_af, NPC_AF_BLK_RST());
+ WATCHDOG_RESET();
+ } while (blk_rst.s.busy);
+
+ debug("%s: npc af reset --\n", __func__);
+
+ return 0;
+}
+
+int nix_af_setup(struct nix_af *nix_af)
+{
+ int err;
+ union nixx_af_const2 af_const2;
+ union nixx_af_const3 af_const3;
+ union nixx_af_sq_const sq_const;
+ union nixx_af_cfg af_cfg;
+ union nixx_af_status af_status;
+ union nixx_af_ndc_cfg ndc_cfg;
+ union nixx_af_aq_cfg aq_cfg;
+ union nixx_af_blk_rst blk_rst;
+
+ debug("%s(%p)\n", __func__, nix_af);
+ err = rvu_aq_alloc(&nix_af->aq, Q_COUNT(AQ_SIZE),
+ sizeof(union nix_aq_inst_s),
+ sizeof(union nix_aq_res_s));
+ if (err) {
+ printf("%s: Error allocating nix admin queue\n", __func__);
+ return err;
+ }
+
+ blk_rst.u = 0;
+ blk_rst.s.rst = 1;
+ nix_af_reg_write(nix_af, NIXX_AF_BLK_RST(), blk_rst.u);
+
+ /* Wait for reset to complete */
+ do {
+ blk_rst.u = nix_af_reg_read(nix_af, NIXX_AF_BLK_RST());
+ WATCHDOG_RESET();
+ } while (blk_rst.s.busy);
+
+ /* Put in LE mode */
+ af_cfg.u = nix_af_reg_read(nix_af, NIXX_AF_CFG());
+ if (af_cfg.s.force_cond_clk_en || af_cfg.s.calibrate_x2p ||
+ af_cfg.s.force_intf_clk_en) {
+ printf("%s: Error: Invalid NIX_AF_CFG value 0x%llx\n",
+ __func__, af_cfg.u);
+ return -1;
+ }
+ af_cfg.s.af_be = 0;
+ af_cfg.u |= 0x5E; /* HW Issue */
+ nix_af_reg_write(nix_af, NIXX_AF_CFG(), af_cfg.u);
+
+ /* Perform Calibration */
+ af_cfg.u = nix_af_reg_read(nix_af, NIXX_AF_CFG());
+ af_cfg.s.calibrate_x2p = 1;
+ nix_af_reg_write(nix_af, NIXX_AF_CFG(), af_cfg.u);
+
+ /* Wait for calibration to complete */
+ do {
+ af_status.u = nix_af_reg_read(nix_af, NIXX_AF_STATUS());
+ WATCHDOG_RESET();
+ } while (af_status.s.calibrate_done == 0);
+
+ af_cfg.u = nix_af_reg_read(nix_af, NIXX_AF_CFG());
+ af_cfg.s.calibrate_x2p = 0;
+ nix_af_reg_write(nix_af, NIXX_AF_CFG(), af_cfg.u);
+
+ /* Enable NDC cache */
+ ndc_cfg.u = nix_af_reg_read(nix_af, NIXX_AF_NDC_CFG());
+ ndc_cfg.s.ndc_ign_pois = 0;
+ ndc_cfg.s.byp_sq = 0;
+ ndc_cfg.s.byp_sqb = 0;
+ ndc_cfg.s.byp_cqs = 0;
+ ndc_cfg.s.byp_cints = 0;
+ ndc_cfg.s.byp_dyno = 0;
+ ndc_cfg.s.byp_mce = 0;
+ ndc_cfg.s.byp_rqc = 0;
+ ndc_cfg.s.byp_rsse = 0;
+ ndc_cfg.s.byp_mc_data = 0;
+ ndc_cfg.s.byp_mc_wqe = 0;
+ ndc_cfg.s.byp_mr_data = 0;
+ ndc_cfg.s.byp_mr_wqe = 0;
+ ndc_cfg.s.byp_qints = 0;
+ nix_af_reg_write(nix_af, NIXX_AF_NDC_CFG(), ndc_cfg.u);
+
+ /* Set up queue size */
+ aq_cfg.u = 0;
+ aq_cfg.s.qsize = AQ_SIZE;
+ nix_af_reg_write(nix_af, NIXX_AF_AQ_CFG(), aq_cfg.u);
+
+ /* Set up queue base address */
+ nix_af_reg_write(nix_af, NIXX_AF_AQ_BASE(), nix_af->aq.inst.iova);
+
+ af_const3.u = nix_af_reg_read(nix_af, NIXX_AF_CONST3());
+ af_const2.u = nix_af_reg_read(nix_af, NIXX_AF_CONST2());
+ sq_const.u = nix_af_reg_read(nix_af, NIXX_AF_SQ_CONST());
+ nix_af->rq_ctx_sz = 1ULL << af_const3.s.rq_ctx_log2bytes;
+ nix_af->sq_ctx_sz = 1ULL << af_const3.s.sq_ctx_log2bytes;
+ nix_af->cq_ctx_sz = 1ULL << af_const3.s.cq_ctx_log2bytes;
+ nix_af->rsse_ctx_sz = 1ULL << af_const3.s.rsse_log2bytes;
+ nix_af->qints = af_const2.s.qints;
+ nix_af->cints = af_const2.s.cints;
+ nix_af->cint_ctx_sz = 1ULL << af_const3.s.cint_log2bytes;
+ nix_af->qint_ctx_sz = 1ULL << af_const3.s.qint_log2bytes;
+ nix_af->sqb_size = sq_const.s.sqb_size;
+
+ return 0;
+}
+
+int nix_af_shutdown(struct nix_af *nix_af)
+{
+ union nixx_af_blk_rst blk_rst;
+
+ blk_rst.u = 0;
+ blk_rst.s.rst = 1;
+ nix_af_reg_write(nix_af, NIXX_AF_BLK_RST(), blk_rst.u);
+
+ /* Wait for reset to complete */
+ do {
+ blk_rst.u = nix_af_reg_read(nix_af, NIXX_AF_BLK_RST());
+ WATCHDOG_RESET();
+ } while (blk_rst.s.busy);
+
+ rvu_aq_free(&nix_af->aq);
+
+ debug("%s: nix af reset --\n", __func__);
+
+ return 0;
+}
diff --git a/drivers/net/octeontx2/npc.h b/drivers/net/octeontx2/npc.h
new file mode 100644
index 0000000..6e645cd
--- /dev/null
+++ b/drivers/net/octeontx2/npc.h
@@ -0,0 +1,90 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2018 Marvell International Ltd.
+ */
+
+#ifndef __NPC_H__
+#define __NPC_H__
+
+#define RSVD_MCAM_ENTRIES_PER_PF 2 /** Ucast and Bcast */
+#define RSVD_MCAM_ENTRIES_PER_NIXLF 1 /** Ucast for VFs */
+
+struct npc_kpu_profile_cam {
+ u8 state;
+ u8 state_mask;
+ u16 dp0;
+ u16 dp0_mask;
+ u16 dp1;
+ u16 dp1_mask;
+ u16 dp2;
+ u16 dp2_mask;
+};
+
+struct npc_kpu_profile_action {
+ u8 errlev;
+ u8 errcode;
+ u8 dp0_offset;
+ u8 dp1_offset;
+ u8 dp2_offset;
+ u8 bypass_count;
+ u8 parse_done;
+ u8 next_state;
+ u8 ptr_advance;
+ u8 cap_ena;
+ u8 lid;
+ u8 ltype;
+ u8 flags;
+ u8 offset;
+ u8 mask;
+ u8 right;
+ u8 shift;
+};
+
+struct npc_kpu_profile {
+ int cam_entries;
+ int action_entries;
+ struct npc_kpu_profile_cam *cam;
+ struct npc_kpu_profile_action *action;
+};
+
+struct npc_pkind {
+ struct rsrc_bmap rsrc;
+ u32 *pfchan_map;
+};
+
+struct npc_mcam {
+ struct rsrc_bmap rsrc;
+ u16 *pfvf_map;
+ u16 total_entries; /* Total number of MCAM entries */
+ u16 entries; /* Total - reserved for NIX LFs */
+ u8 banks_per_entry; /* Number of keywords in key */
+ u8 keysize;
+ u8 banks; /* Number of MCAM banks */
+ u16 banksize; /* Number of MCAM entries in each bank */
+ u16 counters; /* Number of match counters */
+ u16 nixlf_offset;
+ u16 pf_offset;
+};
+
+struct nix_af_handle;
+struct nix_handle;
+struct rvu_hwinfo;
+
+struct npc_af {
+ struct nix_af_handle *nix_af;
+ struct npc_pkind pkind;
+ void __iomem *npc_af_base;
+ u8 npc_kpus; /** Number of parser units */
+ struct npc_mcam mcam;
+ struct rvu_block block;
+ struct rvu_hwinfo *hw;
+};
+
+struct npc {
+ struct npc_af *npc_af;
+ void __iomem *npc_base;
+ struct nix_handle *nix;
+}
+
+#endif /* __NPC_H__ */
+
diff --git a/drivers/net/octeontx2/rvu.h b/drivers/net/octeontx2/rvu.h
new file mode 100644
index 0000000..f455260
--- /dev/null
+++ b/drivers/net/octeontx2/rvu.h
@@ -0,0 +1,119 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2018 Marvell International Ltd.
+ */
+
+#ifndef __RVU_H__
+#define __RVU_H__
+
+#include <asm/arch/csrs/csrs-rvu.h>
+
+#define ALIGNED __aligned(CONFIG_SYS_CACHELINE_SIZE)
+
+#define Q_SIZE_16 0ULL /* 16 entries */
+#define Q_SIZE_64 1ULL /* 64 entries */
+#define Q_SIZE_256 2ULL
+#define Q_SIZE_1K 3ULL
+#define Q_SIZE_4K 4ULL
+#define Q_SIZE_16K 5ULL
+#define Q_SIZE_64K 6ULL
+#define Q_SIZE_256K 7ULL
+#define Q_SIZE_1M 8ULL /* Million entries */
+#define Q_SIZE_MIN Q_SIZE_16
+#define Q_SIZE_MAX Q_SIZE_1M
+
+#define Q_COUNT(x) (16ULL << (2 * (x)))
+#define Q_SIZE(x, n) ((ilog2(x) - (n)) / 2)
+
+/* Admin queue info */
+
+/* Since we intend to add only one instruction at a time,
+ * keep queue size to it's minimum.
+ */
+#define AQ_SIZE Q_SIZE_16
+/* HW head & tail pointer mask */
+#define AQ_PTR_MASK 0xFFFFF
+
+struct qmem {
+ void *base;
+ dma_addr_t iova;
+ size_t alloc_sz;
+ u32 qsize;
+ u8 entry_sz;
+};
+
+struct admin_queue {
+ struct qmem inst;
+ struct qmem res;
+};
+
+struct rvu_af {
+ struct udevice *dev;
+ void __iomem *af_base;
+ struct nix_af *nix_af;
+};
+
+struct rvu_pf {
+ struct udevice *dev;
+ struct udevice *afdev;
+ void __iomem *pf_base;
+ struct nix *nix;
+ u8 pfid;
+ int nix_lfid;
+ int npa_lfid;
+};
+
+/**
+ * Store 128 bit value
+ *
+ * @param[out] dest pointer to destination address
+ * @param val0 first 64 bits to write
+ * @param val1 second 64 bits to write
+ */
+static inline void st128(void *dest, u64 val0, u64 val1)
+{
+ __asm__ __volatile__("stp %x[x0], %x[x1], [%[pm]]" :
+ : [x0]"r"(val0), [x1]"r"(val1), [pm]"r"(dest)
+ : "memory");
+}
+
+/**
+ * Load 128 bit value
+ *
+ * @param[in] source pointer to 128 bits of data to load
+ * @param[out] val0 first 64 bits of data
+ * @param[out] val1 second 64 bits of data
+ */
+static inline void ld128(const u64 *src, u64 *val0, u64 *val1)
+{
+ __asm__ __volatile__ ("ldp %x[x0], %x[x1], [%[pm]]" :
+ : [x0]"r"(*val0), [x1]"r"(*val1), [pm]"r"(src));
+}
+
+void qmem_free(struct qmem *q);
+int qmem_alloc(struct qmem *q, u32 qsize, size_t entry_sz);
+
+/**
+ * Allocates an admin queue for instructions and results
+ *
+ * @param aq admin queue to allocate for
+ * @param qsize Number of entries in the queue
+ * @param inst_size Size of each instruction
+ * @param res_size Size of each result
+ *
+ * @return -ENOMEM on error, 0 on success
+ */
+int rvu_aq_alloc(struct admin_queue *aq, unsigned int qsize,
+ size_t inst_size, size_t res_size);
+
+/**
+ * Frees an admin queue
+ *
+ * @param aq Admin queue to free
+ */
+void rvu_aq_free(struct admin_queue *aq);
+
+void rvu_get_lfid_for_pf(int pf, int *nixid, int *npaid);
+
+#endif /* __RVU_H__ */
+
diff --git a/drivers/net/octeontx2/rvu_af.c b/drivers/net/octeontx2/rvu_af.c
new file mode 100644
index 0000000..7750089
--- /dev/null
+++ b/drivers/net/octeontx2/rvu_af.c
@@ -0,0 +1,171 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018 Marvell International Ltd.
+ */
+
+#include <dm.h>
+#include <errno.h>
+#include <malloc.h>
+#include <misc.h>
+#include <net.h>
+#include <pci_ids.h>
+#include <linux/list.h>
+#include <asm/io.h>
+#include <asm/arch/board.h>
+#include <asm/arch/csrs/csrs-npa.h>
+
+#include "nix.h"
+
+struct udevice *rvu_af_dev;
+
+inline struct rvu_af *get_af(void)
+{
+ return rvu_af_dev ? dev_get_priv(rvu_af_dev) : NULL;
+}
+
+void rvu_get_lfid_for_pf(int pf, int *nixid, int *npaid)
+{
+ union nixx_af_rvu_lf_cfg_debug nix_lf_dbg;
+ union npa_af_rvu_lf_cfg_debug npa_lf_dbg;
+ union rvu_pf_func_s pf_func;
+ struct rvu_af *af = dev_get_priv(rvu_af_dev);
+ struct nix_af *nix_af = af->nix_af;
+
+ pf_func.u = 0;
+ pf_func.s.pf = pf;
+
+ nix_lf_dbg.u = 0;
+ nix_lf_dbg.s.pf_func = pf_func.u & 0xFFFF;
+ nix_lf_dbg.s.exec = 1;
+ nix_af_reg_write(nix_af, NIXX_AF_RVU_LF_CFG_DEBUG(),
+ nix_lf_dbg.u);
+ do {
+ nix_lf_dbg.u = nix_af_reg_read(nix_af,
+ NIXX_AF_RVU_LF_CFG_DEBUG());
+ } while (nix_lf_dbg.s.exec);
+
+ if (nix_lf_dbg.s.lf_valid)
+ *nixid = nix_lf_dbg.s.lf;
+
+ debug("%s: nix lf_valid %d lf %d nixid %d\n", __func__,
+ nix_lf_dbg.s.lf_valid, nix_lf_dbg.s.lf, *nixid);
+
+ npa_lf_dbg.u = 0;
+ npa_lf_dbg.s.pf_func = pf_func.u & 0xFFFF;
+ npa_lf_dbg.s.exec = 1;
+ npa_af_reg_write(nix_af->npa_af, NPA_AF_RVU_LF_CFG_DEBUG(),
+ npa_lf_dbg.u);
+ do {
+ npa_lf_dbg.u = npa_af_reg_read(nix_af->npa_af,
+ NPA_AF_RVU_LF_CFG_DEBUG());
+ } while (npa_lf_dbg.s.exec);
+
+ if (npa_lf_dbg.s.lf_valid)
+ *npaid = npa_lf_dbg.s.lf;
+ debug("%s: npa lf_valid %d lf %d npaid %d\n", __func__,
+ npa_lf_dbg.s.lf_valid, npa_lf_dbg.s.lf, *npaid);
+}
+
+struct nix_af *rvu_af_init(struct rvu_af *rvu_af)
+{
+ struct nix_af *nix_af;
+ union rvu_af_addr_s block_addr;
+ int err;
+
+ nix_af = (struct nix_af *)calloc(1, sizeof(struct nix_af));
+ if (!nix_af) {
+ printf("%s: out of memory\n", __func__);
+ goto error;
+ }
+
+ nix_af->dev = rvu_af->dev;
+
+ block_addr.u = 0;
+ block_addr.s.block = RVU_BLOCK_ADDR_E_NIXX(0);
+ nix_af->nix_af_base = rvu_af->af_base + block_addr.u;
+
+ nix_af->npa_af = (struct npa_af *)calloc(1, sizeof(struct npa_af));
+ if (!nix_af->npa_af) {
+ printf("%s: out of memory\n", __func__);
+ goto error;
+ }
+
+ block_addr.u = 0;
+ block_addr.s.block = RVU_BLOCK_ADDR_E_NPA;
+ nix_af->npa_af->npa_af_base = rvu_af->af_base + block_addr.u;
+
+ block_addr.u = 0;
+ block_addr.s.block = RVU_BLOCK_ADDR_E_NPC;
+ nix_af->npc_af_base = rvu_af->af_base + block_addr.u;
+
+ debug("%s: Setting up npa admin\n", __func__);
+ err = npa_af_setup(nix_af->npa_af);
+ if (err) {
+ printf("%s: Error %d setting up NPA admin\n", __func__, err);
+ goto error;
+ }
+ debug("%s: Setting up nix af\n", __func__);
+ err = nix_af_setup(nix_af);
+ if (err) {
+ printf("%s: Error %d setting up NIX admin\n", __func__, err);
+ goto error;
+ }
+ debug("%s: nix_af: %p\n", __func__, nix_af);
+ return nix_af;
+
+error:
+ if (nix_af->npa_af) {
+ free(nix_af->npa_af);
+ memset(nix_af, 0, sizeof(*nix_af));
+ }
+ if (nix_af)
+ free(nix_af);
+ return NULL;
+}
+
+int rvu_af_probe(struct udevice *dev)
+{
+ struct rvu_af *af_ptr = dev_get_priv(dev);
+
+ af_ptr->af_base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0,
+ PCI_REGION_MEM);
+ debug("%s RVU AF BAR %p\n", __func__, af_ptr->af_base);
+ af_ptr->dev = dev;
+ rvu_af_dev = dev;
+
+ af_ptr->nix_af = rvu_af_init(af_ptr);
+ if (!af_ptr->nix_af) {
+ printf("%s: Error: could not initialize NIX AF\n", __func__);
+ return -1;
+ }
+ debug("%s: Done\n", __func__);
+
+ return 0;
+}
+
+int rvu_af_remove(struct udevice *dev)
+{
+ struct rvu_af *rvu_af = dev_get_priv(dev);
+
+ nix_af_shutdown(rvu_af->nix_af);
+ npa_af_shutdown(rvu_af->nix_af->npa_af);
+ npc_af_shutdown(rvu_af->nix_af);
+
+ debug("%s: rvu af down --\n", __func__);
+ return 0;
+}
+
+U_BOOT_DRIVER(rvu_af) = {
+ .name = "rvu_af",
+ .id = UCLASS_MISC,
+ .probe = rvu_af_probe,
+ .remove = rvu_af_remove,
+ .priv_auto_alloc_size = sizeof(struct rvu_af),
+};
+
+static struct pci_device_id rvu_af_supported[] = {
+ { PCI_VDEVICE(CAVIUM, PCI_DEVICE_ID_CAVIUM_RVU_AF) },
+ {}
+};
+
+U_BOOT_PCI_DEVICE(rvu_af, rvu_af_supported);
diff --git a/drivers/net/octeontx2/rvu_common.c b/drivers/net/octeontx2/rvu_common.c
new file mode 100644
index 0000000..173b28b
--- /dev/null
+++ b/drivers/net/octeontx2/rvu_common.c
@@ -0,0 +1,71 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018 Marvell International Ltd.
+ */
+
+#include <dm.h>
+#include <errno.h>
+#include <malloc.h>
+#include <misc.h>
+#include <net.h>
+#include <asm/io.h>
+
+#include "rvu.h"
+
+int qmem_alloc(struct qmem *q, u32 qsize, size_t entry_sz)
+{
+ q->base = memalign(CONFIG_SYS_CACHELINE_SIZE, qsize * entry_sz);
+ if (!q->base)
+ return -ENOMEM;
+ q->entry_sz = entry_sz;
+ q->qsize = qsize;
+ q->alloc_sz = (size_t)qsize * entry_sz;
+ q->iova = (dma_addr_t)(q->base);
+ debug("NIX: qmem alloc for (%d * %d = %ld bytes) at %p\n",
+ q->qsize, q->entry_sz, q->alloc_sz, q->base);
+ return 0;
+}
+
+void qmem_free(struct qmem *q)
+{
+ if (q->base)
+ free(q->base);
+ memset(q, 0, sizeof(*q));
+}
+
+/**
+ * Allocates an admin queue for instructions and results
+ *
+ * @param aq admin queue to allocate for
+ * @param qsize Number of entries in the queue
+ * @param inst_size Size of each instruction
+ * @param res_size Size of each result
+ *
+ * @return -ENOMEM on error, 0 on success
+ */
+int rvu_aq_alloc(struct admin_queue *aq, unsigned int qsize,
+ size_t inst_size, size_t res_size)
+{
+ int err;
+
+ err = qmem_alloc(&aq->inst, qsize, inst_size);
+ if (err)
+ return err;
+ err = qmem_alloc(&aq->res, qsize, res_size);
+ if (err)
+ qmem_free(&aq->inst);
+
+ return err;
+}
+
+/**
+ * Frees an admin queue
+ *
+ * @param aq Admin queue to free
+ */
+void rvu_aq_free(struct admin_queue *aq)
+{
+ qmem_free(&aq->inst);
+ qmem_free(&aq->res);
+ memset(aq, 0, sizeof(*aq));
+}
diff --git a/drivers/net/octeontx2/rvu_pf.c b/drivers/net/octeontx2/rvu_pf.c
new file mode 100644
index 0000000..201ecf2
--- /dev/null
+++ b/drivers/net/octeontx2/rvu_pf.c
@@ -0,0 +1,116 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018 Marvell International Ltd.
+ */
+
+#include <dm.h>
+#include <errno.h>
+#include <malloc.h>
+#include <misc.h>
+#include <net.h>
+#include <pci_ids.h>
+#include <asm/io.h>
+#include <asm/types.h>
+#include <asm/arch/board.h>
+#include "cgx.h"
+#include "nix.h"
+
+extern struct udevice *rvu_af_dev;
+
+int rvu_pf_init(struct rvu_pf *rvu)
+{
+ struct nix *nix;
+ struct eth_pdata *pdata = dev_get_platdata(rvu->dev);
+
+ debug("%s: Allocating nix lf\n", __func__);
+ nix = nix_lf_alloc(rvu->dev);
+ if (!nix) {
+ printf("%s: Error allocating lf for pf %d\n",
+ __func__, rvu->pfid);
+ return -1;
+ }
+ rvu->nix = nix;
+
+ /* to make post_probe happy */
+ if (is_valid_ethaddr(nix->lmac->mac_addr)) {
+ memcpy(pdata->enetaddr, nix->lmac->mac_addr, 6);
+ eth_env_set_enetaddr_by_index("eth", rvu->dev->seq,
+ pdata->enetaddr);
+ }
+
+ return 0;
+}
+
+static const struct eth_ops nix_eth_ops = {
+ .start = nix_lf_init,
+ .send = nix_lf_xmit,
+ .recv = nix_lf_recv,
+ .free_pkt = nix_lf_free_pkt,
+ .stop = nix_lf_halt,
+ .write_hwaddr = nix_lf_setup_mac,
+};
+
+int rvu_pf_probe(struct udevice *dev)
+{
+ struct rvu_pf *rvu = dev_get_priv(dev);
+ int err;
+ char name[16];
+
+ debug("%s: name: %s\n", __func__, dev->name);
+
+ rvu->pf_base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_2, PCI_REGION_MEM);
+ rvu->pfid = dev->seq + 1; // RVU PF's start from 1;
+ rvu->dev = dev;
+ if (!rvu_af_dev) {
+ printf("%s: Error: Could not find RVU AF device\n",
+ __func__);
+ return -1;
+ }
+ rvu->afdev = rvu_af_dev;
+
+ debug("RVU PF %u BAR2 %p\n", rvu->pfid, rvu->pf_base);
+
+ rvu_get_lfid_for_pf(rvu->pfid, &rvu->nix_lfid, &rvu->npa_lfid);
+
+ err = rvu_pf_init(rvu);
+ if (err)
+ printf("%s: Error %d adding nix\n", __func__, err);
+
+ /*
+ * modify device name to include index/sequence number,
+ * for better readability, this is 1:1 mapping with eth0/1/2.. names.
+ */
+ sprintf(name, "rvu_pf#%d", dev->seq);
+ device_set_name(dev, name);
+ debug("%s: name: %s\n", __func__, dev->name);
+ return err;
+}
+
+int rvu_pf_remove(struct udevice *dev)
+{
+ struct rvu_pf *rvu = dev_get_priv(dev);
+
+ nix_lf_shutdown(rvu->nix);
+ npa_lf_shutdown(rvu->nix);
+
+ debug("%s: rvu pf%d down --\n", __func__, rvu->pfid);
+
+ return 0;
+}
+
+U_BOOT_DRIVER(rvu_pf) = {
+ .name = "rvu_pf",
+ .id = UCLASS_ETH,
+ .probe = rvu_pf_probe,
+ .remove = rvu_pf_remove,
+ .ops = &nix_eth_ops,
+ .priv_auto_alloc_size = sizeof(struct rvu_pf),
+ .platdata_auto_alloc_size = sizeof(struct eth_pdata),
+};
+
+static struct pci_device_id rvu_pf_supported[] = {
+ { PCI_VDEVICE(CAVIUM, PCI_DEVICE_ID_CAVIUM_RVU_PF) },
+ {}
+};
+
+U_BOOT_PCI_DEVICE(rvu_pf, rvu_pf_supported);