All of lore.kernel.org
 help / color / mirror / Atom feed
From: Stefan Roese <sr@denx.de>
To: u-boot@lists.denx.de
Cc: daniel.schwierzeck@gmail.com, awilliams@marvell.com, cchavva@marvell.com
Subject: [PATCH v2 30/52] mips: octeon: Add cvmx-ilk.c
Date: Thu,  7 Apr 2022 09:11:32 +0200	[thread overview]
Message-ID: <20220407071154.51997-31-sr@denx.de> (raw)
In-Reply-To: <20220407071154.51997-1-sr@denx.de>

From: Aaron Williams <awilliams@marvell.com>

Import cvmx-ilk.c from 2013 U-Boot. It will be used by the later
added drivers to support networking on the MIPS Octeon II / III
platforms.

Signed-off-by: Aaron Williams <awilliams@marvell.com>
Signed-off-by: Stefan Roese <sr@denx.de>
---
 arch/mips/mach-octeon/cvmx-ilk.c | 1199 ++++++++++++++++++++++++++++++
 1 file changed, 1199 insertions(+)
 create mode 100644 arch/mips/mach-octeon/cvmx-ilk.c

diff --git a/arch/mips/mach-octeon/cvmx-ilk.c b/arch/mips/mach-octeon/cvmx-ilk.c
new file mode 100644
index 000000000000..6223ff2c84b2
--- /dev/null
+++ b/arch/mips/mach-octeon/cvmx-ilk.c
@@ -0,0 +1,1199 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018-2022 Marvell International Ltd.
+ *
+ * Support library for the ILK
+ */
+
+#include <time.h>
+#include <log.h>
+#include <linux/delay.h>
+
+#include <mach/cvmx-regs.h>
+#include <mach/cvmx-csr.h>
+#include <mach/cvmx-bootmem.h>
+#include <mach/octeon-model.h>
+#include <mach/cvmx-fuse.h>
+#include <mach/octeon-feature.h>
+#include <mach/cvmx-qlm.h>
+#include <mach/octeon_qlm.h>
+#include <mach/cvmx-pcie.h>
+#include <mach/cvmx-coremask.h>
+
+#include <mach/cvmx-agl-defs.h>
+#include <mach/cvmx-bgxx-defs.h>
+#include <mach/cvmx-ciu-defs.h>
+#include <mach/cvmx-gmxx-defs.h>
+#include <mach/cvmx-gserx-defs.h>
+#include <mach/cvmx-ilk-defs.h>
+#include <mach/cvmx-ipd-defs.h>
+#include <mach/cvmx-pcsx-defs.h>
+#include <mach/cvmx-pcsxx-defs.h>
+#include <mach/cvmx-pki-defs.h>
+#include <mach/cvmx-pko-defs.h>
+#include <mach/cvmx-xcv-defs.h>
+
+#include <mach/cvmx-hwpko.h>
+#include <mach/cvmx-ilk.h>
+#include <mach/cvmx-pki.h>
+
+#include <mach/cvmx-helper.h>
+#include <mach/cvmx-helper-board.h>
+#include <mach/cvmx-helper-cfg.h>
+
+/*
+ * global configurations.
+ *
+ * for cn68, the default is {0xf, 0xf0}. to disable the 2nd ILK, set
+ * cvmx_ilk_lane_mask[CVMX_NUM_ILK_INTF] = {0xff, 0x0} and
+ * cvmx_ilk_chans[CVMX_NUM_ILK_INTF] = {8, 0}
+ */
+unsigned short cvmx_ilk_lane_mask[CVMX_MAX_NODES][CVMX_NUM_ILK_INTF] = {
+	[0 ... CVMX_MAX_NODES - 1] = { 0x000f, 0x00f0 }
+};
+
+int cvmx_ilk_chans[CVMX_MAX_NODES][CVMX_NUM_ILK_INTF] = {
+	[0 ... CVMX_MAX_NODES - 1] = { 8, 8 }
+};
+
+static cvmx_ilk_intf_t cvmx_ilk_intf_cfg[CVMX_MAX_NODES][CVMX_NUM_ILK_INTF];
+
+cvmx_ilk_LA_mode_t cvmx_ilk_LA_mode[CVMX_NUM_ILK_INTF] = { { 0, 0 }, { 0, 0 } };
+/**
+ * User-overrideable callback function that returns whether or not an interface
+ * should use look-aside mode.
+ *
+ * @param interface - interface being checked
+ * @param channel - channel number, can be 0 or 1 or -1 to see if LA mode
+ *                  should be enabled for the interface.
+ * @return 0 to not use LA-mode, 1 to use LA-mode.
+ */
+int cvmx_ilk_use_la_mode(int interface, int channel)
+{
+	if (OCTEON_IS_MODEL(OCTEON_CN68XX_PASS1_0))
+		return 0;
+
+	if (interface >= CVMX_NUM_ILK_INTF) {
+		debug("ERROR: invalid interface=%d in %s\n",
+		      interface, __func__);
+		return -1;
+	}
+	return cvmx_ilk_LA_mode[interface].ilk_LA_mode;
+}
+
+/**
+ * User-overrideable callback function that returns whether or not an interface
+ * in look-aside mode should enable the RX calendar.
+ *
+ * @param interface - interface to check
+ * @return 1 to enable RX calendar, 0 to disable RX calendar.
+ *
+ * NOTE: For the CN68XX pass 2.0 this will enable the RX calendar for interface
+ * 0 and not interface 1.  It is up to the customer to override this behavior.
+ */
+int cvmx_ilk_la_mode_enable_rx_calendar(int interface)
+{
+	/* There is an errata in the CN68XX pass 2.0 where if connected
+	 * in a loopback configuration or back to back then only one interface
+	 * can have the RX calendar enabled.
+	 */
+	if (interface >= CVMX_NUM_ILK_INTF) {
+		debug("ERROR: invalid interface=%d in %s\n",
+		      interface, __func__);
+		return -1;
+	}
+	return cvmx_ilk_LA_mode[interface].ilk_LA_mode_cal_ena;
+}
+
+/**
+ * Initialize and start the ILK interface.
+ *
+ * @param interface The identifier of the packet interface to configure and
+ *                  use as a ILK interface. cn68xx has 2 interfaces: ilk0 and
+ *                  ilk1.
+ *
+ * @param lane_mask the lane group for this interface
+ *
+ * @return Zero on success, negative on failure.
+ */
+int cvmx_ilk_start_interface(int interface, unsigned short lane_mask)
+{
+	int res = -1;
+	int other_intf, this_qlm, other_qlm;
+	unsigned short uni_mask;
+	cvmx_ilk_txx_cfg0_t ilk_txx_cfg0;
+	cvmx_ilk_rxx_cfg0_t ilk_rxx_cfg0;
+	cvmx_ilk_ser_cfg_t ilk_ser_cfg;
+	int node = (interface >> 4) & 0xf;
+
+	interface &= 0xf;
+
+	if (!octeon_has_feature(OCTEON_FEATURE_ILK))
+		return res;
+
+	if (interface >= CVMX_NUM_ILK_INTF)
+		return res;
+
+	if (lane_mask == 0)
+		return res;
+
+	/* check conflicts between 2 ilk interfaces. 1 lane can be assigned to 1
+	 * interface only
+	 */
+	other_intf = !interface;
+	if (cvmx_ilk_lane_mask[node][other_intf] & lane_mask) {
+		debug("ILK%d:%d: %s: lane assignment conflict\n", node,
+		      interface, __func__);
+		return res;
+	}
+
+	/* check the legality of the lane mask. interface 0 can have 8 lanes,
+	 * while interface 1 can have 4 lanes at most
+	 */
+	uni_mask = lane_mask >> (interface * 4);
+	if (OCTEON_IS_MODEL(OCTEON_CN68XX)) {
+		cvmx_mio_qlmx_cfg_t mio_qlmx_cfg, other_mio_qlmx_cfg;
+
+		if ((uni_mask != 0x1 && uni_mask != 0x3 && uni_mask != 0xf &&
+		     uni_mask != 0xff) ||
+		    (interface == 1 && lane_mask > 0xf0)) {
+			debug("ILK%d: %s: incorrect lane mask: 0x%x\n",
+			      interface, __func__, uni_mask);
+			return res;
+		}
+		/* check the availability of qlms. qlm_cfg = 001 means the chip
+		 * is fused to give this qlm to ilk
+		 */
+		this_qlm = interface + CVMX_ILK_QLM_BASE();
+		other_qlm = other_intf + CVMX_ILK_QLM_BASE();
+		mio_qlmx_cfg.u64 = csr_rd(CVMX_MIO_QLMX_CFG(this_qlm));
+		other_mio_qlmx_cfg.u64 = csr_rd(CVMX_MIO_QLMX_CFG(other_qlm));
+		if (mio_qlmx_cfg.s.qlm_cfg != 1 ||
+		    (uni_mask == 0xff && other_mio_qlmx_cfg.s.qlm_cfg != 1)) {
+			debug("ILK%d: %s: qlm unavailable\n", interface,
+			      __func__);
+			return res;
+		}
+		/* Has 8 lanes */
+		lane_mask &= 0xff;
+	}
+
+	if (OCTEON_IS_MODEL(OCTEON_CN78XX)) {
+		int qlm;
+		unsigned short lane_mask_all = 0;
+
+		/* QLM 4 - QLM 7 can be configured for ILK. Get the lane mask
+		 * of all the qlms that are configured for ilk
+		 */
+		for (qlm = 4; qlm < 8; qlm++) {
+			cvmx_gserx_cfg_t gserx_cfg;
+			cvmx_gserx_phy_ctl_t phy_ctl;
+
+			/* Make sure QLM is powered and out of reset */
+			phy_ctl.u64 =
+				csr_rd_node(node, CVMX_GSERX_PHY_CTL(qlm));
+			if (phy_ctl.s.phy_pd || phy_ctl.s.phy_reset)
+				continue;
+
+			/* Make sure QLM is in ILK mode */
+			gserx_cfg.u64 = csr_rd_node(node, CVMX_GSERX_CFG(qlm));
+			if (gserx_cfg.s.ila)
+				lane_mask_all |= ((1 << 4) - 1)
+						 << (4 * (qlm - 4));
+		}
+
+		if ((lane_mask_all & lane_mask) != lane_mask) {
+			debug("ILK%d: %s: incorrect lane mask: 0x%x\n",
+			      interface, __func__, lane_mask);
+			return res;
+		}
+	}
+
+	/* power up the serdes */
+	ilk_ser_cfg.u64 = csr_rd_node(node, CVMX_ILK_SER_CFG);
+	if (OCTEON_IS_MODEL(OCTEON_CN68XX)) {
+		if (ilk_ser_cfg.cn68xx.ser_pwrup == 0) {
+			ilk_ser_cfg.cn68xx.ser_rxpol_auto = 1;
+			ilk_ser_cfg.cn68xx.ser_rxpol = 0;
+			ilk_ser_cfg.cn68xx.ser_txpol = 0;
+			ilk_ser_cfg.cn68xx.ser_reset_n = 0xff;
+			ilk_ser_cfg.cn68xx.ser_haul = 0;
+		}
+		ilk_ser_cfg.cn68xx.ser_pwrup |=
+			((interface == 0) && (lane_mask > 0xf)) ?
+				0x3 :
+				      (1 << interface);
+	}
+	if (OCTEON_IS_MODEL(OCTEON_CN78XX)) {
+		ilk_ser_cfg.cn78xx.ser_rxpol_auto = 1;
+		ilk_ser_cfg.cn78xx.ser_rxpol = 0;
+		ilk_ser_cfg.cn78xx.ser_txpol = 0;
+		ilk_ser_cfg.cn78xx.ser_reset_n = 0xffff;
+	}
+	csr_wr_node(node, CVMX_ILK_SER_CFG, ilk_ser_cfg.u64);
+
+	if (OCTEON_IS_MODEL(OCTEON_CN68XX_PASS2_X)) {
+		/* Workaround for Errata (G-16467) */
+		int qlm = (interface) ? 2 : 1;
+		int start_qlm, end_qlm;
+
+		/* Apply the workaround to both the QLMs if configured for x8 lanes */
+		if (cvmx_pop(lane_mask) > 4) {
+			start_qlm = 1;
+			end_qlm = 2;
+		} else {
+			start_qlm = qlm;
+			end_qlm = qlm;
+		}
+
+		for (qlm = start_qlm; qlm <= end_qlm; qlm++) {
+#ifdef CVMX_QLM_DUMP_STATE
+			debug("%s:%d: ILK%d: Applying workaround for Errata G-16467\n",
+			      __func__, __LINE__, qlm);
+			cvmx_qlm_display_registers(qlm);
+			debug("\n");
+#endif
+			/* This workaround only applies to QLMs running ILK at 6.25Ghz */
+			if ((cvmx_qlm_get_gbaud_mhz(qlm) == 6250) &&
+			    (cvmx_qlm_jtag_get(qlm, 0, "clkf_byp") != 20)) {
+				udelay(100); /* Wait 100us for links to stabalize */
+				cvmx_qlm_jtag_set(qlm, -1, "clkf_byp", 20);
+				/* Allow the QLM to exit reset */
+				cvmx_qlm_jtag_set(qlm, -1, "cfg_rst_n_clr", 0);
+				udelay(100); /* Wait 100us for links to stabalize */
+				/* Allow TX on QLM */
+				cvmx_qlm_jtag_set(qlm, -1, "cfg_tx_idle_set",
+						  0);
+			}
+#ifdef CVMX_QLM_DUMP_STATE
+			debug("%s:%d: ILK%d: Done applying workaround for Errata G-16467\n",
+			      __func__, __LINE__, qlm);
+			cvmx_qlm_display_registers(qlm);
+			debug("\n\n");
+#endif
+		}
+	}
+
+	/* Initialize all calendar entries to xoff state */
+	__cvmx_ilk_clear_cal((node << 4) | interface);
+
+	/* Enable ILK LA mode if configured. */
+	if (OCTEON_IS_MODEL(OCTEON_CN68XX)) {
+		if (cvmx_ilk_use_la_mode(interface, 0)) {
+			cvmx_ilk_txx_cfg1_t ilk_txx_cfg1;
+			cvmx_ilk_rxx_cfg1_t ilk_rxx_cfg1;
+
+			ilk_txx_cfg1.u64 = csr_rd(CVMX_ILK_TXX_CFG1(interface));
+			ilk_rxx_cfg1.u64 = csr_rd(CVMX_ILK_RXX_CFG1(interface));
+			ilk_txx_cfg1.s.la_mode = 1;
+			ilk_txx_cfg1.s.tx_link_fc_jam = 1;
+			ilk_txx_cfg1.s.rx_link_fc_ign = 1;
+			ilk_rxx_cfg1.s.la_mode = 1;
+			csr_wr(CVMX_ILK_TXX_CFG1(interface), ilk_txx_cfg1.u64);
+			csr_wr(CVMX_ILK_RXX_CFG1(interface), ilk_rxx_cfg1.u64);
+			cvmx_ilk_intf_cfg[node][interface].la_mode =
+				1; /* Enable look-aside mode */
+		} else {
+			cvmx_ilk_intf_cfg[node][interface].la_mode =
+				0; /* Disable look-aside mode */
+		}
+	}
+	if (OCTEON_IS_MODEL(OCTEON_CN78XX))
+		cvmx_ilk_intf_cfg[node][interface].la_mode = 0;
+
+	/* configure the lane enable of the interface */
+	ilk_txx_cfg0.u64 = csr_rd_node(node, CVMX_ILK_TXX_CFG0(interface));
+	ilk_rxx_cfg0.u64 = csr_rd_node(node, CVMX_ILK_RXX_CFG0(interface));
+	ilk_rxx_cfg0.s.lane_ena = lane_mask;
+	ilk_txx_cfg0.s.lane_ena = lane_mask;
+	csr_wr_node(node, CVMX_ILK_TXX_CFG0(interface), ilk_txx_cfg0.u64);
+	csr_wr_node(node, CVMX_ILK_RXX_CFG0(interface), ilk_rxx_cfg0.u64);
+
+	/* For 10.3125Gbs data rate, set SER_LIMIT to 0x3ff for x8 & x12 mode */
+	if (OCTEON_IS_MODEL(OCTEON_CN78XX)) {
+		cvmx_gserx_lane_mode_t lmode0, lmode1;
+
+		lmode0.u64 = csr_rd_node(node, CVMX_GSERX_LANE_MODE(5));
+		lmode1.u64 = csr_rd_node(node, CVMX_GSERX_LANE_MODE(7));
+		if ((lmode0.s.lmode == 5 || lmode1.s.lmode == 5) &&
+		    (lane_mask == 0xfff || lane_mask == 0xfff0 ||
+		     lane_mask == 0xff || lane_mask == 0xff00)) {
+			cvmx_ilk_txx_cfg1_t ilk_txx_cfg1;
+
+			ilk_txx_cfg1.u64 =
+				csr_rd_node(node, CVMX_ILK_TXX_CFG1(interface));
+			ilk_txx_cfg1.s.ser_limit = 0x3ff;
+			csr_wr_node(node, CVMX_ILK_TXX_CFG1(interface),
+				    ilk_txx_cfg1.u64);
+		}
+	}
+
+	/* write to local cache. for lane speed, if interface 0 has 8 lanes,
+	 * assume both qlms have the same speed
+	 */
+	cvmx_ilk_intf_cfg[node][interface].intf_en = 1;
+	res = 0;
+
+	return res;
+}
+
+/**
+ * set pipe group base and length for the interface
+ *
+ * @param xiface    The identifier of the packet interface to configure and
+ *                  use as a ILK interface. cn68xx has 2 interfaces: ilk0 and
+ *                  ilk1.
+ *
+ * @param pipe_base the base of the pipe group
+ * @param pipe_len  the length of the pipe group
+ *
+ * @return Zero on success, negative on failure.
+ */
+int cvmx_ilk_set_pipe(int xiface, int pipe_base, unsigned int pipe_len)
+{
+	int res = -1;
+	cvmx_ilk_txx_pipe_t ilk_txx_pipe;
+	struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface);
+	int interface = xi.interface - CVMX_ILK_GBL_BASE();
+
+	if (!(OCTEON_IS_MODEL(OCTEON_CN68XX)))
+		return res;
+
+	if (interface >= CVMX_NUM_ILK_INTF)
+		return res;
+
+	/* set them in ilk tx section */
+	ilk_txx_pipe.u64 = csr_rd_node(xi.node, CVMX_ILK_TXX_PIPE(interface));
+	ilk_txx_pipe.s.base = pipe_base;
+	ilk_txx_pipe.s.nump = pipe_len;
+	csr_wr_node(xi.node, CVMX_ILK_TXX_PIPE(interface), ilk_txx_pipe.u64);
+	res = 0;
+
+	return res;
+}
+
+/**
+ * set logical channels for tx
+ *
+ * @param interface The identifier of the packet interface to configure and
+ *                  use as a ILK interface. cn68xx has 2 interfaces: ilk0 and
+ *                  ilk1.
+ *
+ * @param pch     pointer to an array of pipe-channel pair
+ * @param num_chs the number of entries in the pipe-channel array
+ *
+ * @return Zero on success, negative on failure.
+ */
+int cvmx_ilk_tx_set_channel(int interface, cvmx_ilk_pipe_chan_t *pch,
+			    unsigned int num_chs)
+{
+	int res = -1;
+	cvmx_ilk_txx_idx_pmap_t ilk_txx_idx_pmap;
+	cvmx_ilk_txx_mem_pmap_t ilk_txx_mem_pmap;
+	unsigned int i;
+
+	if (!(OCTEON_IS_MODEL(OCTEON_CN68XX)))
+		return res;
+
+	if (interface >= CVMX_NUM_ILK_INTF)
+		return res;
+
+	if (!pch || num_chs > CVMX_ILK_MAX_PIPES)
+		return res;
+
+	if (cvmx_ilk_use_la_mode(interface, 0)) {
+		ilk_txx_idx_pmap.u64 = 0;
+		ilk_txx_mem_pmap.u64 = 0;
+		for (i = 0; i < num_chs; i++) {
+			ilk_txx_idx_pmap.s.index = pch->pipe;
+			ilk_txx_mem_pmap.s.channel = pch->chan;
+			ilk_txx_mem_pmap.s.remap = 1;
+			csr_wr(CVMX_ILK_TXX_IDX_PMAP(interface),
+			       ilk_txx_idx_pmap.u64);
+			csr_wr(CVMX_ILK_TXX_MEM_PMAP(interface),
+			       ilk_txx_mem_pmap.u64);
+			pch++;
+		}
+	} else {
+		/* write the pair to ilk tx */
+		ilk_txx_mem_pmap.u64 = 0;
+		ilk_txx_idx_pmap.u64 = 0;
+		for (i = 0; i < num_chs; i++) {
+			ilk_txx_idx_pmap.s.index = pch->pipe;
+			ilk_txx_mem_pmap.s.channel = pch->chan;
+			csr_wr(CVMX_ILK_TXX_IDX_PMAP(interface),
+			       ilk_txx_idx_pmap.u64);
+			csr_wr(CVMX_ILK_TXX_MEM_PMAP(interface),
+			       ilk_txx_mem_pmap.u64);
+			pch++;
+		}
+	}
+	res = 0;
+
+	return res;
+}
+
+/**
+ * set pkind for rx
+ *
+ * @param xiface    The identifier of the packet interface to configure and
+ *                  use as a ILK interface. cn68xx has 2 interfaces: ilk0 and
+ *                  ilk1.
+ *
+ * @param chpknd    pointer to an array of channel-pkind pair
+ * @param num_pknd the number of entries in the channel-pkind array
+ *
+ * @return Zero on success, negative on failure.
+ */
+int cvmx_ilk_rx_set_pknd(int xiface, cvmx_ilk_chan_pknd_t *chpknd,
+			 unsigned int num_pknd)
+{
+	int res = -1;
+	cvmx_ilk_rxf_idx_pmap_t ilk_rxf_idx_pmap;
+	unsigned int i;
+	struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface);
+	int interface = xi.interface - CVMX_ILK_GBL_BASE();
+
+	if (!(OCTEON_IS_MODEL(OCTEON_CN68XX)))
+		return res;
+
+	if (interface >= CVMX_NUM_ILK_INTF)
+		return res;
+
+	if (!chpknd || num_pknd > CVMX_ILK_MAX_PKNDS)
+		return res;
+
+	res = 0;
+
+	for (i = 0; i < num_pknd; i++) {
+		ilk_rxf_idx_pmap.u64 = 0;
+		/* write the pair to ilk rx. note the channels for different
+		 * interfaces are given in *chpknd and interface is not used
+		 * as a param
+		 */
+		if (chpknd->chan < 2 &&
+		    cvmx_ilk_use_la_mode(interface, chpknd->chan)) {
+			ilk_rxf_idx_pmap.s.index =
+				interface * 256 + 128 + chpknd->chan;
+			csr_wr(CVMX_ILK_RXF_IDX_PMAP, ilk_rxf_idx_pmap.u64);
+			csr_wr(CVMX_ILK_RXF_MEM_PMAP, chpknd->pknd);
+		}
+		ilk_rxf_idx_pmap.s.index = interface * 256 + chpknd->chan;
+		csr_wr(CVMX_ILK_RXF_IDX_PMAP, ilk_rxf_idx_pmap.u64);
+		csr_wr(CVMX_ILK_RXF_MEM_PMAP, chpknd->pknd);
+		chpknd++;
+	}
+
+	return res;
+}
+
+/**
+ * configure calendar for rx
+ *
+ * @param intf The identifier of the packet interface to configure and
+ *                  use as a ILK interface. cn68xx has 2 interfaces: ilk0 and
+ *                  ilk1.
+ *
+ * @param cal_depth the number of calendar entries
+ * @param pent      pointer to calendar entries
+ *
+ * @return Zero on success, negative on failure.
+ */
+int cvmx_ilk_rx_cal_conf(int intf, int cal_depth, cvmx_ilk_cal_entry_t *pent)
+{
+	int res = -1, i;
+	cvmx_ilk_rxx_cfg0_t ilk_rxx_cfg0;
+	int num_entries;
+	int node = (intf >> 4) & 0xf;
+	int interface = intf & 0xf;
+
+	if (!octeon_has_feature(OCTEON_FEATURE_ILK))
+		return res;
+
+	if (interface >= CVMX_NUM_ILK_INTF)
+		return res;
+
+	if (cal_depth < CVMX_ILK_RX_MIN_CAL || cal_depth > CVMX_ILK_MAX_CAL ||
+	    (OCTEON_IS_MODEL(OCTEON_CN68XX) && !pent))
+		return res;
+
+	/* mandatory link-level fc as workarounds for ILK-15397 and
+	 * ILK-15479
+	 */
+	/* TODO: test effectiveness */
+
+	if (OCTEON_IS_MODEL(OCTEON_CN68XX)) {
+		/* Update the calendar for each channel */
+		if ((cvmx_ilk_use_la_mode(interface, 0) == 0) ||
+		    (cvmx_ilk_use_la_mode(interface, 0) &&
+		     cvmx_ilk_la_mode_enable_rx_calendar(interface))) {
+			for (i = 0; i < cal_depth; i++) {
+				__cvmx_ilk_write_rx_cal_entry(
+					interface, i, pent[i].pipe_bpid);
+			}
+		}
+
+		/* Update the depth */
+		ilk_rxx_cfg0.u64 = csr_rd(CVMX_ILK_RXX_CFG0(interface));
+		num_entries = 1 + cal_depth + (cal_depth - 1) / 15;
+		ilk_rxx_cfg0.s.cal_depth = num_entries;
+		if (cvmx_ilk_use_la_mode(interface, 0)) {
+			ilk_rxx_cfg0.s.mproto_ign = 1;
+			ilk_rxx_cfg0.s.lnk_stats_ena = 1;
+			ilk_rxx_cfg0.s.lnk_stats_wrap = 1;
+		}
+		csr_wr(CVMX_ILK_RXX_CFG0(interface), ilk_rxx_cfg0.u64);
+	}
+
+	if (OCTEON_IS_MODEL(OCTEON_CN78XX)) {
+		ilk_rxx_cfg0.u64 =
+			csr_rd_node(node, CVMX_ILK_RXX_CFG0(interface));
+		/*
+		 * Make sure cal_ena is 0 for programming the calendar table,
+		 * as per Errata ILK-19398
+		 */
+		ilk_rxx_cfg0.s.cal_ena = 0;
+		csr_wr_node(node, CVMX_ILK_RXX_CFG0(interface),
+			    ilk_rxx_cfg0.u64);
+
+		for (i = 0; i < cal_depth; i++)
+			__cvmx_ilk_write_rx_cal_entry(intf, i, 0);
+
+		ilk_rxx_cfg0.u64 =
+			csr_rd_node(node, CVMX_ILK_RXX_CFG0(interface));
+		num_entries = 1 + cal_depth + (cal_depth - 1) / 15;
+		ilk_rxx_cfg0.s.cal_depth = num_entries;
+		csr_wr_node(node, CVMX_ILK_RXX_CFG0(interface),
+			    ilk_rxx_cfg0.u64);
+	}
+
+	return 0;
+}
+
+/**
+ * set high water mark for rx
+ *
+ * @param intf      The identifier of the packet interface to configure and
+ *                  use as a ILK interface. cn68xx has 2 interfaces: ilk0 and
+ *                  ilk1.
+ *
+ * @param hi_wm     high water mark for this interface
+ *
+ * @return Zero on success, negative on failure.
+ */
+int cvmx_ilk_rx_set_hwm(int intf, int hi_wm)
+{
+	int res = -1;
+	cvmx_ilk_rxx_cfg1_t ilk_rxx_cfg1;
+	int node = (intf >> 4) & 0xf;
+	int interface = intf & 0xf;
+
+	if (!octeon_has_feature(OCTEON_FEATURE_ILK))
+		return res;
+
+	if (interface >= CVMX_NUM_ILK_INTF)
+		return res;
+
+	if (hi_wm <= 0)
+		return res;
+
+	/* set the hwm */
+	ilk_rxx_cfg1.u64 = csr_rd_node(node, CVMX_ILK_RXX_CFG1(interface));
+	ilk_rxx_cfg1.s.rx_fifo_hwm = hi_wm;
+	csr_wr_node(node, CVMX_ILK_RXX_CFG1(interface), ilk_rxx_cfg1.u64);
+	res = 0;
+
+	return res;
+}
+
+/**
+ * enable calendar for rx
+ *
+ * @param intf      The identifier of the packet interface to configure and
+ *                  use as a ILK interface. cn68xx has 2 interfaces: ilk0 and
+ *                  ilk1.
+ *
+ * @param cal_ena   enable or disable calendar
+ *
+ * @return Zero on success, negative on failure.
+ */
+int cvmx_ilk_rx_cal_ena(int intf, unsigned char cal_ena)
+{
+	int res = -1;
+	cvmx_ilk_rxx_cfg0_t ilk_rxx_cfg0;
+	int node = (intf >> 4) & 0xf;
+	int interface = intf & 0xf;
+
+	if (!octeon_has_feature(OCTEON_FEATURE_ILK))
+		return res;
+
+	if (interface >= CVMX_NUM_ILK_INTF)
+		return res;
+
+	if (cvmx_ilk_use_la_mode(interface, 0) &&
+	    !cvmx_ilk_la_mode_enable_rx_calendar(interface))
+		return 0;
+
+	/* set the enable */
+	ilk_rxx_cfg0.u64 = csr_rd_node(node, CVMX_ILK_RXX_CFG0(interface));
+	ilk_rxx_cfg0.s.cal_ena = cal_ena;
+	csr_wr_node(node, CVMX_ILK_RXX_CFG0(interface), ilk_rxx_cfg0.u64);
+	csr_rd_node(node, CVMX_ILK_RXX_CFG0(interface));
+	res = 0;
+
+	return res;
+}
+
+/**
+ * set up calendar for rx
+ *
+ * @param intf      The identifier of the packet interface to configure and
+ *                  use as a ILK interface. cn68xx has 2 interfaces: ilk0 and
+ *                  ilk1.
+ *
+ * @param cal_depth the number of calendar entries
+ * @param pent      pointer to calendar entries
+ * @param hi_wm     high water mark for this interface
+ * @param cal_ena   enable or disable calendar
+ *
+ * @return Zero on success, negative on failure.
+ */
+int cvmx_ilk_cal_setup_rx(int intf, int cal_depth, cvmx_ilk_cal_entry_t *pent,
+			  int hi_wm, unsigned char cal_ena)
+{
+	int res = -1;
+
+	if (!octeon_has_feature(OCTEON_FEATURE_ILK))
+		return res;
+
+	res = cvmx_ilk_rx_cal_conf(intf, cal_depth, pent);
+	if (res < 0)
+		return res;
+
+	res = cvmx_ilk_rx_set_hwm(intf, hi_wm);
+	if (res < 0)
+		return res;
+
+	res = cvmx_ilk_rx_cal_ena(intf, cal_ena);
+	return res;
+}
+
+/**
+ * configure calendar for tx
+ *
+ * @param intf      The identifier of the packet interface to configure and
+ *                  use as a ILK interface. cn68xx has 2 interfaces: ilk0 and
+ *                  ilk1.
+ *
+ * @param cal_depth the number of calendar entries
+ * @param pent      pointer to calendar entries
+ *
+ * @return Zero on success, negative on failure.
+ */
+int cvmx_ilk_tx_cal_conf(int intf, int cal_depth, cvmx_ilk_cal_entry_t *pent)
+{
+	int res = -1, i;
+	cvmx_ilk_txx_cfg0_t ilk_txx_cfg0;
+	int num_entries;
+	int node = (intf >> 4) & 0xf;
+	int interface = intf & 0xf;
+
+	if (!octeon_has_feature(OCTEON_FEATURE_ILK))
+		return res;
+
+	if (interface >= CVMX_NUM_ILK_INTF)
+		return res;
+
+	if (cal_depth < CVMX_ILK_TX_MIN_CAL || cal_depth > CVMX_ILK_MAX_CAL ||
+	    (OCTEON_IS_MODEL(OCTEON_CN68XX) && !pent))
+		return res;
+
+	/* mandatory link-level fc as workarounds for ILK-15397 and
+	 * ILK-15479
+	 */
+	/* TODO: test effectiveness */
+
+	if (OCTEON_IS_MODEL(OCTEON_CN68XX)) {
+		/* Update the calendar for each channel */
+		for (i = 0; i < cal_depth; i++) {
+			__cvmx_ilk_write_tx_cal_entry(interface, i,
+						      pent[i].pipe_bpid);
+		}
+
+		/* Set the depth (must be multiple of 8)*/
+		ilk_txx_cfg0.u64 = csr_rd(CVMX_ILK_TXX_CFG0(interface));
+		num_entries = 1 + cal_depth + (cal_depth - 1) / 15;
+		ilk_txx_cfg0.s.cal_depth = (num_entries + 7) & ~7;
+		csr_wr(CVMX_ILK_TXX_CFG0(interface), ilk_txx_cfg0.u64);
+	}
+
+	if (OCTEON_IS_MODEL(OCTEON_CN78XX)) {
+		ilk_txx_cfg0.u64 =
+			csr_rd_node(node, CVMX_ILK_TXX_CFG0(interface));
+		/*
+		 * Make sure cal_ena is 0 for programming the calendar table,
+		 * as per Errata ILK-19398
+		 */
+		ilk_txx_cfg0.s.cal_ena = 0;
+		csr_wr_node(node, CVMX_ILK_TXX_CFG0(interface),
+			    ilk_txx_cfg0.u64);
+
+		for (i = 0; i < cal_depth; i++)
+			__cvmx_ilk_write_tx_cal_entry(intf, i, 0);
+
+		ilk_txx_cfg0.u64 =
+			csr_rd_node(node, CVMX_ILK_TXX_CFG0(interface));
+		num_entries = 1 + cal_depth + (cal_depth - 1) / 15;
+		/* cal_depth[2:0] needs to be zero, round up */
+		ilk_txx_cfg0.s.cal_depth = (num_entries + 7) & 0x1f8;
+		csr_wr_node(node, CVMX_ILK_TXX_CFG0(interface),
+			    ilk_txx_cfg0.u64);
+	}
+
+	return 0;
+}
+
+/**
+ * enable calendar for tx
+ *
+ * @param intf	    The identifier of the packet interface to configure and
+ *                  use as a ILK interface. cn68xx has 2 interfaces: ilk0 and
+ *                  ilk1.
+ *
+ * @param cal_ena   enable or disable calendar
+ *
+ * @return Zero on success, negative on failure.
+ */
+int cvmx_ilk_tx_cal_ena(int intf, unsigned char cal_ena)
+{
+	int res = -1;
+	cvmx_ilk_txx_cfg0_t ilk_txx_cfg0;
+	int node = (intf >> 4) & 0xf;
+	int interface = intf & 0xf;
+
+	if (!octeon_has_feature(OCTEON_FEATURE_ILK))
+		return res;
+
+	if (interface >= CVMX_NUM_ILK_INTF)
+		return res;
+
+	/* set the enable */
+	ilk_txx_cfg0.u64 = csr_rd_node(node, CVMX_ILK_TXX_CFG0(interface));
+	ilk_txx_cfg0.s.cal_ena = cal_ena;
+	csr_wr_node(node, CVMX_ILK_TXX_CFG0(interface), ilk_txx_cfg0.u64);
+	csr_rd_node(node, CVMX_ILK_TXX_CFG0(interface));
+	res = 0;
+
+	return res;
+}
+
+/**
+ * set up calendar for tx
+ *
+ * @param intf      The identifier of the packet interface to configure and
+ *                  use as a ILK interface. cn68xx has 2 interfaces: ilk0 and
+ *                  ilk1.
+ *
+ * @param cal_depth the number of calendar entries
+ * @param pent      pointer to calendar entries
+ * @param cal_ena   enable or disable calendar
+ *
+ * @return Zero on success, negative on failure.
+ */
+int cvmx_ilk_cal_setup_tx(int intf, int cal_depth, cvmx_ilk_cal_entry_t *pent,
+			  unsigned char cal_ena)
+{
+	int res = -1;
+
+	if (!octeon_has_feature(OCTEON_FEATURE_ILK))
+		return res;
+
+	res = cvmx_ilk_tx_cal_conf(intf, cal_depth, pent);
+	if (res < 0)
+		return res;
+
+	res = cvmx_ilk_tx_cal_ena(intf, cal_ena);
+	return res;
+}
+
+/* #define CVMX_ILK_STATS_ENA 1 */
+#ifdef CVMX_ILK_STATS_ENA
+static void cvmx_ilk_reg_dump_rx(int intf)
+{
+	int i;
+	cvmx_ilk_rxx_cfg0_t ilk_rxx_cfg0;
+	cvmx_ilk_rxx_cfg1_t ilk_rxx_cfg1;
+	cvmx_ilk_rxx_int_t ilk_rxx_int;
+	cvmx_ilk_rxx_jabber_t ilk_rxx_jabber;
+	cvmx_ilk_rx_lnex_cfg_t ilk_rx_lnex_cfg;
+	cvmx_ilk_rx_lnex_int_t ilk_rx_lnex_int;
+	cvmx_ilk_gbl_cfg_t ilk_gbl_cfg;
+	cvmx_ilk_ser_cfg_t ilk_ser_cfg;
+	cvmx_ilk_rxf_idx_pmap_t ilk_rxf_idx_pmap;
+	cvmx_ilk_rxf_mem_pmap_t ilk_rxf_mem_pmap;
+	cvmx_ilk_rxx_idx_cal_t ilk_rxx_idx_cal;
+	cvmx_ilk_rxx_mem_cal0_t ilk_rxx_mem_cal0;
+	cvmx_ilk_rxx_mem_cal1_t ilk_rxx_mem_cal1;
+	int node = (intf >> 4) & 0xf;
+	int interface = intf & 0xf;
+
+	ilk_rxx_cfg0.u64 = csr_rd_node(node, CVMX_ILK_RXX_CFG0(interface));
+	debug("ilk rxx cfg0: 0x%16lx\n", ilk_rxx_cfg0.u64);
+
+	ilk_rxx_cfg1.u64 = csr_rd_node(node, CVMX_ILK_RXX_CFG1(interface));
+	debug("ilk rxx cfg1: 0x%16lx\n", ilk_rxx_cfg1.u64);
+
+	ilk_rxx_int.u64 = csr_rd_node(node, CVMX_ILK_RXX_INT(interface));
+	debug("ilk rxx int: 0x%16lx\n", ilk_rxx_int.u64);
+	csr_wr_node(node, CVMX_ILK_RXX_INT(interface), ilk_rxx_int.u64);
+
+	ilk_rxx_jabber.u64 = csr_rd_node(node, CVMX_ILK_RXX_JABBER(interface));
+	debug("ilk rxx jabber: 0x%16lx\n", ilk_rxx_jabber.u64);
+
+#define LNE_NUM_DBG 4
+	for (i = 0; i < LNE_NUM_DBG; i++) {
+		ilk_rx_lnex_cfg.u64 =
+			csr_rd_node(node, CVMX_ILK_RX_LNEX_CFG(i));
+		debug("ilk rx lnex cfg lane: %d  0x%16lx\n", i,
+		      ilk_rx_lnex_cfg.u64);
+	}
+
+	for (i = 0; i < LNE_NUM_DBG; i++) {
+		ilk_rx_lnex_int.u64 =
+			csr_rd_node(node, CVMX_ILK_RX_LNEX_INT(i));
+		debug("ilk rx lnex int lane: %d  0x%16lx\n", i,
+		      ilk_rx_lnex_int.u64);
+		csr_wr_node(node, CVMX_ILK_RX_LNEX_INT(i), ilk_rx_lnex_int.u64);
+	}
+
+	ilk_gbl_cfg.u64 = csr_rd_node(node, CVMX_ILK_GBL_CFG);
+	debug("ilk gbl cfg: 0x%16lx\n", ilk_gbl_cfg.u64);
+
+	ilk_ser_cfg.u64 = csr_rd_node(node, CVMX_ILK_SER_CFG);
+	debug("ilk ser cfg: 0x%16lx\n", ilk_ser_cfg.u64);
+
+#define CHAN_NUM_DBG 8
+	if (OCTEON_IS_MODEL(OCTEON_CN68XX)) {
+		ilk_rxf_idx_pmap.u64 = 0;
+		ilk_rxf_idx_pmap.s.index = interface * 256;
+		ilk_rxf_idx_pmap.s.inc = 1;
+		csr_wr(CVMX_ILK_RXF_IDX_PMAP, ilk_rxf_idx_pmap.u64);
+		for (i = 0; i < CHAN_NUM_DBG; i++) {
+			ilk_rxf_mem_pmap.u64 = csr_rd(CVMX_ILK_RXF_MEM_PMAP);
+			debug("ilk rxf mem pmap chan: %3d  0x%16lx\n", i,
+			      ilk_rxf_mem_pmap.u64);
+		}
+	}
+	if (OCTEON_IS_MODEL(OCTEON_CN78XX)) {
+		cvmx_ilk_rxx_chax_t rxx_chax;
+
+		for (i = 0; i < CHAN_NUM_DBG; i++) {
+			rxx_chax.u64 = csr_rd_node(
+				node, CVMX_ILK_RXX_CHAX(i, interface));
+			debug("ilk chan: %d  pki chan: 0x%x\n", i,
+			      rxx_chax.s.port_kind);
+		}
+	}
+
+#define CAL_NUM_DBG 2
+	if (OCTEON_IS_MODEL(OCTEON_CN68XX)) {
+		ilk_rxx_idx_cal.u64 = 0;
+		ilk_rxx_idx_cal.s.inc = 1;
+		csr_wr(CVMX_ILK_RXX_IDX_CAL(interface), ilk_rxx_idx_cal.u64);
+		for (i = 0; i < CAL_NUM_DBG; i++) {
+			ilk_rxx_idx_cal.u64 =
+				csr_rd(CVMX_ILK_RXX_IDX_CAL(interface));
+			debug("ilk rxx idx cal: 0x%16lx\n",
+			      ilk_rxx_idx_cal.u64);
+
+			ilk_rxx_mem_cal0.u64 =
+				csr_rd(CVMX_ILK_RXX_MEM_CAL0(interface));
+			debug("ilk rxx mem cal0: 0x%16lx\n",
+			      ilk_rxx_mem_cal0.u64);
+			ilk_rxx_mem_cal1.u64 =
+				csr_rd(CVMX_ILK_RXX_MEM_CAL1(interface));
+			debug("ilk rxx mem cal1: 0x%16lx\n",
+			      ilk_rxx_mem_cal1.u64);
+		}
+	}
+	if (OCTEON_IS_MODEL(OCTEON_CN78XX)) {
+		cvmx_ilk_rxx_cal_entryx_t rxx_cal_entryx;
+
+		for (i = 0; i < CAL_NUM_DBG; i++) {
+			rxx_cal_entryx.u64 = csr_rd_node(
+				node, CVMX_ILK_RXX_CAL_ENTRYX(i, interface));
+			debug("ilk rxx cal idx: %d\n", i);
+			debug("ilk rxx cal ctl: 0x%x\n", rxx_cal_entryx.s.ctl);
+			debug("ilk rxx cal pko chan: 0x%x\n",
+			      rxx_cal_entryx.s.channel);
+		}
+	}
+}
+
+static void cvmx_ilk_reg_dump_tx(int intf)
+{
+	int i;
+	cvmx_ilk_txx_cfg0_t ilk_txx_cfg0;
+	cvmx_ilk_txx_cfg1_t ilk_txx_cfg1;
+	cvmx_ilk_txx_idx_pmap_t ilk_txx_idx_pmap;
+	cvmx_ilk_txx_mem_pmap_t ilk_txx_mem_pmap;
+	cvmx_ilk_txx_int_t ilk_txx_int;
+	cvmx_ilk_txx_pipe_t ilk_txx_pipe;
+	cvmx_ilk_txx_idx_cal_t ilk_txx_idx_cal;
+	cvmx_ilk_txx_mem_cal0_t ilk_txx_mem_cal0;
+	cvmx_ilk_txx_mem_cal1_t ilk_txx_mem_cal1;
+	int node = (intf >> 4) & 0xf;
+	int interface = intf & 0xf;
+
+	ilk_txx_cfg0.u64 = csr_rd_node(node, CVMX_ILK_TXX_CFG0(interface));
+	debug("ilk txx cfg0: 0x%16lx\n", ilk_txx_cfg0.u64);
+
+	ilk_txx_cfg1.u64 = csr_rd_node(node, CVMX_ILK_TXX_CFG1(interface));
+	debug("ilk txx cfg1: 0x%16lx\n", ilk_txx_cfg1.u64);
+
+	if (OCTEON_IS_MODEL(OCTEON_CN68XX)) {
+		ilk_txx_pipe.u64 = csr_rd(CVMX_ILK_TXX_PIPE(interface));
+		debug("ilk txx pipe: 0x%16lx\n", ilk_txx_pipe.u64);
+
+		ilk_txx_idx_pmap.u64 = 0;
+		ilk_txx_idx_pmap.s.index = ilk_txx_pipe.s.base;
+		ilk_txx_idx_pmap.s.inc = 1;
+		csr_wr(CVMX_ILK_TXX_IDX_PMAP(interface), ilk_txx_idx_pmap.u64);
+		for (i = 0; i < CHAN_NUM_DBG; i++) {
+			ilk_txx_mem_pmap.u64 =
+				csr_rd(CVMX_ILK_TXX_MEM_PMAP(interface));
+			debug("ilk txx mem pmap pipe: %3d  0x%16lx\n",
+			      ilk_txx_pipe.s.base + i, ilk_txx_mem_pmap.u64);
+		}
+	}
+
+	ilk_txx_int.u64 = csr_rd_node(node, CVMX_ILK_TXX_INT(interface));
+	debug("ilk txx int: 0x%16lx\n", ilk_txx_int.u64);
+
+	if (OCTEON_IS_MODEL(OCTEON_CN68XX)) {
+		ilk_txx_idx_cal.u64 = 0;
+		ilk_txx_idx_cal.s.inc = 1;
+		csr_wr(CVMX_ILK_TXX_IDX_CAL(interface), ilk_txx_idx_cal.u64);
+		for (i = 0; i < CAL_NUM_DBG; i++) {
+			ilk_txx_idx_cal.u64 =
+				csr_rd(CVMX_ILK_TXX_IDX_CAL(interface));
+			debug("ilk txx idx cal: 0x%16lx\n",
+			      ilk_txx_idx_cal.u64);
+
+			ilk_txx_mem_cal0.u64 =
+				csr_rd(CVMX_ILK_TXX_MEM_CAL0(interface));
+			debug("ilk txx mem cal0: 0x%16lx\n",
+			      ilk_txx_mem_cal0.u64);
+			ilk_txx_mem_cal1.u64 =
+				csr_rd(CVMX_ILK_TXX_MEM_CAL1(interface));
+			debug("ilk txx mem cal1: 0x%16lx\n",
+			      ilk_txx_mem_cal1.u64);
+		}
+	}
+	if (OCTEON_IS_MODEL(OCTEON_CN78XX)) {
+		cvmx_ilk_txx_cal_entryx_t txx_cal_entryx;
+
+		for (i = 0; i < CAL_NUM_DBG; i++) {
+			txx_cal_entryx.u64 = csr_rd_node(
+				node, CVMX_ILK_TXX_CAL_ENTRYX(i, interface));
+			debug("ilk txx cal idx: %d\n", i);
+			debug("ilk txx cal ctl: 0x%x\n", txx_cal_entryx.s.ctl);
+			debug("ilk txx cal pki chan: 0x%x\n",
+			      txx_cal_entryx.s.channel);
+		}
+	}
+}
+#endif
+
+/**
+ * show run time status
+ *
+ * @param interface The identifier of the packet interface to enable. cn68xx
+ *                  has 2 interfaces: ilk0 and ilk1.
+ *
+ * @return nothing
+ */
+#ifdef CVMX_ILK_RUNTIME_DBG
+void cvmx_ilk_runtime_status(int interface)
+{
+	cvmx_ilk_txx_cfg1_t ilk_txx_cfg1;
+	cvmx_ilk_txx_flow_ctl0_t ilk_txx_flow_ctl0;
+	cvmx_ilk_rxx_cfg1_t ilk_rxx_cfg1;
+	cvmx_ilk_rxx_int_t ilk_rxx_int;
+	cvmx_ilk_rxx_flow_ctl0_t ilk_rxx_flow_ctl0;
+	cvmx_ilk_rxx_flow_ctl1_t ilk_rxx_flow_ctl1;
+	cvmx_ilk_gbl_int_t ilk_gbl_int;
+
+	debug("\nilk run-time status: interface: %d\n", interface);
+
+	ilk_txx_cfg1.u64 = csr_rd_node(node, CVMX_ILK_TXX_CFG1(interface));
+	debug("\nilk txx cfg1: 0x%16lx\n", ilk_txx_cfg1.u64);
+	if (ilk_txx_cfg1.s.rx_link_fc)
+		debug("link flow control received\n");
+	if (ilk_txx_cfg1.s.tx_link_fc)
+		debug("link flow control sent\n");
+
+	if (OCTEON_IS_MODEL(OCTEON_CN68XX)) {
+		ilk_txx_flow_ctl0.u64 =
+			csr_rd(CVMX_ILK_TXX_FLOW_CTL0(interface));
+		debug("\nilk txx flow ctl0: 0x%16lx\n", ilk_txx_flow_ctl0.u64);
+	}
+	if (OCTEON_IS_MODEL(OCTEON_CN78XX)) {
+		int i;
+		cvmx_ilk_txx_cha_xonx_t txx_cha_xonx;
+
+		for (i = 0; i < 4; i++) {
+			txx_cha_xonx.u64 = csr_rd_node(
+				node, CVMX_ILK_TXX_CHA_XONX(i, interface));
+			debug("\nilk txx cha xon: 0x%16lx\n", txx_cha_xonx.u64);
+		}
+	}
+
+	ilk_rxx_cfg1.u64 = csr_rd_node(node, CVMX_ILK_RXX_CFG1(interface));
+	debug("\nilk rxx cfg1: 0x%16lx\n", ilk_rxx_cfg1.u64);
+	debug("rx fifo count: %d\n", ilk_rxx_cfg1.s.rx_fifo_cnt);
+
+	ilk_rxx_int.u64 = csr_rd_node(node, CVMX_ILK_RXX_INT(interface));
+	debug("\nilk rxx int: 0x%16lx\n", ilk_rxx_int.u64);
+	if (ilk_rxx_int.s.pkt_drop_rxf)
+		debug("rx fifo packet drop\n");
+	if (ilk_rxx_int.u64)
+		csr_wr_node(node, CVMX_ILK_RXX_INT(interface), ilk_rxx_int.u64);
+
+	if (OCTEON_IS_MODEL(OCTEON_CN68XX)) {
+		ilk_rxx_flow_ctl0.u64 =
+			csr_rd(CVMX_ILK_RXX_FLOW_CTL0(interface));
+		debug("\nilk rxx flow ctl0: 0x%16lx\n", ilk_rxx_flow_ctl0.u64);
+
+		ilk_rxx_flow_ctl1.u64 =
+			csr_rd(CVMX_ILK_RXX_FLOW_CTL1(interface));
+		debug("\nilk rxx flow ctl1: 0x%16lx\n", ilk_rxx_flow_ctl1.u64);
+	}
+	if (OCTEON_IS_MODEL(OCTEON_CN78XX)) {
+		int i;
+		cvmx_ilk_rxx_cha_xonx_t rxx_cha_xonx;
+
+		for (i = 0; i < 4; i++) {
+			rxx_cha_xonx.u64 = csr_rd_node(
+				node, CVMX_ILK_RXX_CHA_XONX(i, interface));
+			debug("\nilk rxx cha xon: 0x%16lx\n", rxx_cha_xonx.u64);
+		}
+	}
+
+	ilk_gbl_int.u64 = csr_rd_node(node, CVMX_ILK_GBL_INT);
+	debug("\nilk gbl int: 0x%16lx\n", ilk_gbl_int.u64);
+	if (ilk_gbl_int.s.rxf_push_full)
+		debug("rx fifo overflow\n");
+	if (ilk_gbl_int.u64)
+		csr_wr_node(node, CVMX_ILK_GBL_INT, ilk_gbl_int.u64);
+}
+#endif
+
+/**
+ * enable interface
+ *
+ * @param xiface    The identifier of the packet interface to enable. cn68xx
+ *                  has 2 interfaces: ilk0 and ilk1.
+ *
+ * @return Zero on success, negative on failure.
+ */
+int cvmx_ilk_enable(int xiface)
+{
+	int res = -1;
+	int retry_count = 0;
+	cvmx_helper_link_info_t result;
+	cvmx_ilk_txx_cfg1_t ilk_txx_cfg1;
+	cvmx_ilk_rxx_cfg1_t ilk_rxx_cfg1;
+#ifdef CVMX_ILK_STATS_ENA
+	cvmx_ilk_rxx_cfg0_t ilk_rxx_cfg0;
+	cvmx_ilk_txx_cfg0_t ilk_txx_cfg0;
+#endif
+	struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface);
+	int node = xi.node;
+	int interface = xi.interface - CVMX_ILK_GBL_BASE();
+
+	if (!octeon_has_feature(OCTEON_FEATURE_ILK))
+		return res;
+
+	if (interface >= CVMX_NUM_ILK_INTF)
+		return res;
+
+	result.u64 = 0;
+
+#ifdef CVMX_ILK_STATS_ENA
+	debug("\n");
+	debug("<<<< ILK%d: Before enabling ilk\n", interface);
+	cvmx_ilk_reg_dump_rx(intf);
+	cvmx_ilk_reg_dump_tx(intf);
+#endif
+
+	/* RX packet will be enabled only if link is up */
+
+	/* TX side */
+	ilk_txx_cfg1.u64 = csr_rd_node(node, CVMX_ILK_TXX_CFG1(interface));
+	ilk_txx_cfg1.s.pkt_ena = 1;
+	if (OCTEON_IS_MODEL(OCTEON_CN68XX)) {
+		if (cvmx_ilk_use_la_mode(interface, 0)) {
+			ilk_txx_cfg1.s.la_mode = 1;
+			ilk_txx_cfg1.s.tx_link_fc_jam = 1;
+		}
+	}
+	csr_wr_node(node, CVMX_ILK_TXX_CFG1(interface), ilk_txx_cfg1.u64);
+	csr_rd_node(node, CVMX_ILK_TXX_CFG1(interface));
+
+#ifdef CVMX_ILK_STATS_ENA
+	/* RX side stats */
+	ilk_rxx_cfg0.u64 = csr_rd_node(node, CVMX_ILK_RXX_CFG0(interface));
+	ilk_rxx_cfg0.s.lnk_stats_ena = 1;
+	csr_wr_node(node, CVMX_ILK_RXX_CFG0(interface), ilk_rxx_cfg0.u64);
+
+	/* TX side stats */
+	ilk_txx_cfg0.u64 = csr_rd_node(node, CVMX_ILK_TXX_CFG0(interface));
+	ilk_txx_cfg0.s.lnk_stats_ena = 1;
+	csr_wr_node(node, CVMX_ILK_TXX_CFG0(interface), ilk_txx_cfg0.u64);
+#endif
+
+retry:
+	retry_count++;
+	if (retry_count > 10)
+		goto out;
+
+	/* Make sure the link is up, so that packets can be sent. */
+	result = __cvmx_helper_ilk_link_get(
+		cvmx_helper_get_ipd_port((interface + CVMX_ILK_GBL_BASE()), 0));
+
+	/* Small delay before another retry. */
+	udelay(100);
+
+	ilk_rxx_cfg1.u64 = csr_rd_node(node, CVMX_ILK_RXX_CFG1(interface));
+	if (ilk_rxx_cfg1.s.pkt_ena == 0)
+		goto retry;
+
+out:
+
+#ifdef CVMX_ILK_STATS_ENA
+	debug(">>>> ILK%d: After ILK is enabled\n", interface);
+	cvmx_ilk_reg_dump_rx(intf);
+	cvmx_ilk_reg_dump_tx(intf);
+#endif
+
+	if (result.s.link_up)
+		return 0;
+
+	return -1;
+}
+
+/**
+ * Provide interface enable status
+ *
+ * @param xiface The identifier of the packet xiface to disable. cn68xx
+ *                  has 2 interfaces: ilk0 and ilk1.
+ *
+ * @return Zero, not enabled; One, enabled.
+ */
+int cvmx_ilk_get_intf_ena(int xiface)
+{
+	struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface);
+	int interface = xi.interface - CVMX_ILK_GBL_BASE();
+	return cvmx_ilk_intf_cfg[xi.node][interface].intf_en;
+}
-- 
2.35.1


  parent reply	other threads:[~2022-04-07  7:23 UTC|newest]

Thread overview: 48+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-04-07  7:11 [PATCH v2 00/52] mips: octeon: Add ethernet support Stefan Roese
2022-04-07  7:11 ` [PATCH v2 01/52] mips: octeon: Add misc cvmx-* header files Stefan Roese
2022-04-07  7:11 ` [PATCH v2 02/52] mips: octeon: Add cvmx-ilk-defs.h header file Stefan Roese
2022-04-07  7:11 ` [PATCH v2 03/52] mips: octeon: Add cvmx-iob-defs.h " Stefan Roese
2022-04-07  7:11 ` [PATCH v2 04/52] mips: octeon: Add cvmx-lbk-defs.h " Stefan Roese
2022-04-07  7:11 ` [PATCH v2 05/52] mips: octeon: Add cvmx-npei-defs.h " Stefan Roese
2022-04-07  7:11 ` [PATCH v2 06/52] mips: octeon: Add cvmx-pcsxx-defs.h " Stefan Roese
2022-04-07  7:11 ` [PATCH v2 07/52] mips: octeon: Add cvmx-xcv-defs.h " Stefan Roese
2022-04-07  7:11 ` [PATCH v2 08/52] mips: octeon: Misc changes to existing headers for upcoming eth support Stefan Roese
2022-04-07  7:11 ` [PATCH v2 09/52] mips: octeon: Add cvmx-helper-agl.c Stefan Roese
2022-04-07  7:11 ` [PATCH v2 10/52] mips: octeon: Add cvmx-helper-bgx.c Stefan Roese
2022-04-07  7:11 ` [PATCH v2 11/52] mips: octeon: Add cvmx-helper-board.c Stefan Roese
2022-04-07  7:11 ` [PATCH v2 12/52] mips: octeon: Add cvmx-helper-fpa.c Stefan Roese
2022-04-07  7:11 ` [PATCH v2 13/52] mips: octeon: Add cvmx-helper-ilk.c Stefan Roese
2022-04-07  7:11 ` [PATCH v2 14/52] mips: octeon: Add cvmx-helper-ipd.c Stefan Roese
2022-04-07  7:11 ` [PATCH v2 15/52] mips: octeon: Add cvmx-helper-loop.c Stefan Roese
2022-04-07  7:11 ` [PATCH v2 16/52] mips: octeon: Add cvmx-helper-npi.c Stefan Roese
2022-04-07  7:11 ` [PATCH v2 17/52] mips: octeon: Add cvmx-helper-pki.c Stefan Roese
2022-04-07  7:11 ` [PATCH v2 18/52] mips: octeon: Add cvmx-helper-pko.c Stefan Roese
2022-04-07  7:11 ` [PATCH v2 19/52] mips: octeon: Add cvmx-helper-pko3.c Stefan Roese
2022-04-07  7:11 ` [PATCH v2 20/52] mips: octeon: Add cvmx-helper-rgmii.c Stefan Roese
2022-04-07  7:11 ` [PATCH v2 21/52] mips: octeon: Add cvmx-helper-sgmii.c Stefan Roese
2022-04-07  7:11 ` [PATCH v2 23/52] mips: octeon: Add cvmx-helper-xaui.c Stefan Roese
2022-04-07  7:11 ` [PATCH v2 24/52] mips: octeon: Add cvmx-agl.c Stefan Roese
2022-04-07  7:11 ` [PATCH v2 26/52] mips: octeon: Add cvmx-fau-compat.c Stefan Roese
2022-04-07  7:11 ` [PATCH v2 27/52] mips: octeon: Add cvmx-fpa.c Stefan Roese
2022-04-07  7:11 ` Stefan Roese [this message]
2022-04-07  7:11 ` [PATCH v2 33/52] mips: octeon: Add cvmx-pki-resources.c Stefan Roese
2022-04-07  7:11 ` [PATCH v2 34/52] mips: octeon: Add cvmx-pko.c Stefan Roese
2022-04-07  7:11 ` [PATCH v2 35/52] mips: octeon: Add cvmx-pko3.c Stefan Roese
2022-04-07  7:11 ` [PATCH v2 37/52] mips: octeon: Add cvmx-pko3-compat.c Stefan Roese
2022-04-07  7:11 ` [PATCH v2 38/52] mips: octeon: Add cvmx-pko3-resources.c Stefan Roese
2022-04-07  7:11 ` [PATCH v2 39/52] mips: octeon: Add cvmx-pko-internal-ports-range.c Stefan Roese
2022-04-07  7:11 ` [PATCH v2 40/52] mips: octeon: Add cvmx-qlm-tables.c Stefan Roese
2022-04-07  7:11 ` [PATCH v2 42/52] mips: octeon: Misc changes to existing C files for upcoming eth support Stefan Roese
2022-04-07  7:11 ` [PATCH v2 43/52] mips: octeon: Makefile: Enable building of the newly added C files Stefan Roese
2022-04-07  7:11 ` [PATCH v2 44/52] mips: octeon: cpu.c: Move bootmem init to arch_early_init_r() Stefan Roese
2022-04-07  7:11 ` [PATCH v2 45/52] mips: octeon: cpu.c: Implement configure_lmtdma_window() Stefan Roese
2022-04-07  7:11 ` [PATCH v2 46/52] mips: octeon: octeon_common.h: Move init SP because of increased image size Stefan Roese
2022-04-07  7:11 ` [PATCH v2 47/52] mips: octeon: mrvl, cn73xx.dtsi: Add ethernet (BGX) and SMI DT nodes Stefan Roese
2022-04-07  7:11 ` [PATCH v2 48/52] mips: octeon: mrvl, octeon-ebb7304.dts: Add ethernet DT support Stefan Roese
2022-04-07  7:11 ` [PATCH v2 49/52] mips: octeon: mrvl, octeon-nic23.dts: " Stefan Roese
2022-04-07  7:11 ` [PATCH v2 50/52] net: Add ethernet support for MIPS Octeon Stefan Roese
2022-04-07  7:11 ` [PATCH v2 51/52] mips: octeon: ebb7304: Enable ethernet support Stefan Roese
2022-04-07  7:11 ` [PATCH v2 52/52] mips: octeon: nic23: " Stefan Roese
2022-05-02 16:00 ` [PATCH v2 00/52] mips: octeon: Add " Stefan Roese
2022-05-02 18:54   ` Daniel Schwierzeck
2022-05-04  9:25 ` Stefan Roese

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20220407071154.51997-31-sr@denx.de \
    --to=sr@denx.de \
    --cc=awilliams@marvell.com \
    --cc=cchavva@marvell.com \
    --cc=daniel.schwierzeck@gmail.com \
    --cc=u-boot@lists.denx.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.