diff options
author | Tom Rini <trini@konsulko.com> | 2022-05-04 09:05:03 -0400 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2022-05-04 09:05:03 -0400 |
commit | c3d451d5e6b7c2ea6d83397d5b6c986ff6ab4ee3 (patch) | |
tree | 9101cb7a943fe6d055c08b64de8c8e05f09c591b | |
parent | 4209f7444541b67bf0505c5f1feccc585fb42583 (diff) | |
parent | 95a3a6ee086d47a8e0cd573e327fd48a97d87439 (diff) | |
download | u-boot-c3d451d5e6b7c2ea6d83397d5b6c986ff6ab4ee3.zip u-boot-c3d451d5e6b7c2ea6d83397d5b6c986ff6ab4ee3.tar.gz u-boot-c3d451d5e6b7c2ea6d83397d5b6c986ff6ab4ee3.tar.bz2 |
Merge https://source.denx.de/u-boot/custodians/u-boot-marvell
- mips: octeon: Add ethernet support (Aaron & Stefan)
- Misc mvneta changes, cleanups, fixes (Marek)
97 files changed, 33853 insertions, 2867 deletions
diff --git a/arch/arm/dts/armada-3720-turris-mox-u-boot.dtsi b/arch/arm/dts/armada-3720-turris-mox-u-boot.dtsi index 28a36a6..71d5e70 100644 --- a/arch/arm/dts/armada-3720-turris-mox-u-boot.dtsi +++ b/arch/arm/dts/armada-3720-turris-mox-u-boot.dtsi @@ -3,25 +3,6 @@ * 2022 by Marek Behún <kabel@kernel.org> */ -/ { - mdio { - #address-cells = <1>; - #size-cells = <0>; - - old_binding_phy1: ethernet-phy@1 { - reg = <1>; - }; - }; -}; - -ð0 { - pinctrl-0 = <&rgmii_pins>, <&smi_pins>; - /delete-property/ phy-handle; - phy = <&old_binding_phy1>; -}; - -/delete-node/ &mdio; - &usb3 { vbus-supply = <&exp_usb3_vbus>; }; diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 06cae68..9b62764 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -101,6 +101,7 @@ config ARCH_JZ47XX config ARCH_OCTEON bool "Support Marvell Octeon CN7xxx platforms" + select ARCH_EARLY_INIT_R select CPU_CAVIUM_OCTEON select DISPLAY_CPUINFO select DMA_ADDR_T_64BIT diff --git a/arch/mips/dts/mrvl,cn73xx.dtsi b/arch/mips/dts/mrvl,cn73xx.dtsi index 2a17f7a..77f3548 100644 --- a/arch/mips/dts/mrvl,cn73xx.dtsi +++ b/arch/mips/dts/mrvl,cn73xx.dtsi @@ -267,5 +267,40 @@ interrupts = <0x6c010 4>; }; }; + + /* SMI1 */ + smi1: mdio@1180000003880 { + compatible = "cavium,octeon-3860-mdio"; + reg = <0x11800 0x00003880 0x0 0x40>; + #address-cells = <1>; + #size-cells = <0>; + }; + + /* BGX 0 */ + bgx0: ethernet-mac-nexus@11800e0000000 { + compatible = "cavium,octeon-7890-bgx"; + reg = <0x11800 0xe0000000 0x0 0x1000000>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + /* BGX 1 */ + bgx1: ethernet-mac-nexus@11800e1000000 { + compatible = "cavium,octeon-7890-bgx"; + reg = <0x11800 0xe1000000 0x0 0x1000000>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + /* BGX 2*/ + bgx2: ethernet-mac-nexus@11800e2000000 { + compatible = "cavium,octeon-7890-bgx"; + reg = <0x11800 0xe2000000 0x0 0x1000000>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; }; }; diff --git a/arch/mips/dts/mrvl,octeon-ebb7304.dts b/arch/mips/dts/mrvl,octeon-ebb7304.dts index fda559d..08247eb 100644 --- a/arch/mips/dts/mrvl,octeon-ebb7304.dts +++ b/arch/mips/dts/mrvl,octeon-ebb7304.dts @@ -201,3 +201,48 @@ cd-gpios = <&gpio 25 1>; /* active low */ }; }; + +/* SMI_1 -- Available on rev 2 and later boards */ +&smi1 { + /** + * The phy names are broken down as follows: + * (m)phyxxyzzs + * where: + * xx = 01 for SGMII, 10 for DXAUI, 20 for RXAUI + * and 40 for XFI/LXAUI + * y = QLM/DLM number + * zz = PHY address (decimal) + * s = sub-phy number in the case of the Cortina + * PHY + * a mphy is a nexus phy that contains one or more + * sub-phys, for example the Cortina CS4223. + */ + + /* QLM 2 */ + phy01208: ethernet-phy@01208 { + reg = <8>; + compatible = "marvell,88e1240", "ethernet-phy-ieee802.3-c22"; + + marvell,reg-init = <3 0x10 0 0x8665>, + <3 0x11 0 0x00aa>, + <3 0x12 0 0x4105>, + <3 0x13 0 0x8a08>; + + interrupt-parent = <&gpio>; + interrupts = <12 8>; /* Pin 12, active low */ + }; +}; + +/* BGX 0 */ +&bgx0 { + status = "okay"; + phy-handle = <&phy01208>; /* put phy-handle in BGX node and MAC node */ + + /* SerDes 0, may differ from PCS Lane/LMAC */ + eth0: ethernet-mac@D { + compatible = "cavium,octeon-7890-bgx-port"; + reg = <0>; + local-mac-address = [ 00 00 00 00 00 00 ]; + phy-handle = <&phy01208>; + }; +}; diff --git a/arch/mips/dts/mrvl,octeon-nic23.dts b/arch/mips/dts/mrvl,octeon-nic23.dts index 72ef56d..dfbd51c 100644 --- a/arch/mips/dts/mrvl,octeon-nic23.dts +++ b/arch/mips/dts/mrvl,octeon-nic23.dts @@ -118,11 +118,208 @@ &i2c0 { u-boot,dm-pre-reloc; /* Needed early for DDR SPD EEPROM */ clock-frequency = <100000>; + + sfp0eeprom: eeprom@50 { + compatible = "atmel,24c01"; + reg = <0x50>; + }; + + sfp0alerts: eeprom@51 { + compatible = "atmel,24c01"; + reg = <0x51>; + }; }; &i2c1 { u-boot,dm-pre-reloc; /* Needed early for DDR SPD EEPROM */ clock-frequency = <100000>; + + vitesse@10 { + compatible = "vitesse,vsc7224"; + reg = <0x10>; + #address-cells = <1>; + #size-cells = <0>; + + /* Note that reset is active high with this device */ + reset = <&gpio 7 0>; + + /* LoS pin can be pulled low when there is a loss of signal */ + los = <&gpio 6 0>; + + vitesse,reg-init = + /* Clear all masks */ + /* Page select FSYNC0 (0x30) */ + <0x7f 0x0030>, + /* Set FSYNC0 for 10.3125Gbps */ + <0x80 0x2841>, /* See Table 3. */ + <0x81 0x0008>, + <0x82 0xc000>, + <0x83 0x0010>, + <0x84 0x1d00>, + + /* All channels Rx settings set equally */ + <0x7f 0x0050>, + /* Shrink EQ_BUFF */ + <0x82 0x0014>, + /* Set EQVGA_ADAP = 1 (enable EQVGA circuitry), + * USE_UNIT_GAIN = 1 (EQVGA is in unity gain), + * USE_LPF = 0 (VGA adapt not using LPF), + * USE_EQVGA = 1 + <0x89 0x7f13>, + /* Select min DFE Delay (DFE_DELAY) */ + <0x90 0x5785>, + /* Set DFE 1-3 limit (DXMAX) = 32dec, + * AP Max limit = 127 decimal + */ + <0x92 0x207f>, + /* Set AP Min limit = 32 decimal */ + <0x93 0x2000>, + /* Set DFE Averaging to the slowest (DFE_AVG) */ + <0x94 0x0031>, + /* Set Inductor Bypass OD_IND_BYP = 0 & fastest Rise/Fall */ + <0x9c 0x0000>, + /* Setting DFE Boost = none. Must set for + * rev C (if DFE in adapt mode) + */ + <0xaa 0x0888>, + /* Setting EQ Min = 8 & Max limit = 72 dec. + * Must set for rev C, otherwise EQ is 0 + * (if EQ is in adaptive mode) + */ + <0xa8 0x2408>, + /* Setting EQVGA = 96, when in EQVGA manual mode */ + <0xa9 0x0060>, + /* Setting SW_BFOCM, bits 15:14 to 01 */ + <0x87 0x4021>, + /* Turn off adaptive input equalization + * and VGA adaptive algorithm control. + */ + <0x89 0x7313>, + /* Turn on adaptive input equalization + * and VGA adaptive algorithm control. + */ + <0x89 0x7f13>; + + vitesse-channel@0 { + compatible = "vitesse,vsc7224-channel"; + reg = <0>; + direction-tx; + sfp-mac = <ð0>; + + /* TAP settings. The format of this is as + * follows: + * - cable length in meters, 0 = active or + * optical module + * - maintap value + * - pretap value + * - posttap value + * + * For the cable length, the value will apply + * for that cable length and greater until the + * next largest cable length specified. These + * values must be ordered first by channel mask + * then by cable length. These are typically + * set for the transmit channels, not the + * receive channels. + */ + taps = <0 0x0013 0x000f 0x0000>, + <1 0x001f 0x000f 0x0004>, + <3 0x0014 0x000b 0x0004>, + <5 0x0014 0x0009 0x0006>, + <7 0x0014 0x000f 0x0000>, + <10 0x0012 0x000b 0x0013>; + }; + + vitesse-channel@1 { + compatible = "vitesse,vsc7224-channel"; + reg = <1>; + /* Ignore mod_abs and module */ + direction-rx; + sfp-mac = <ð0>; + + /* Disable pre-tap */ + pretap-disable; + + /* Disable post-tap */ + posttap-disable; + + /* Taps has the following fields: + * - cable length (ignored for rx) + * - main tap value + * - pre tap value + * - post tap value + * + * NOTE: if taps are disabled then they + * are not programmed. + */ + taps = <0 0x0a 0x0b 0x10>; + }; + + vitesse-channel@2 { + compatible = "vitesse,vsc7224-channel"; + reg = <2>; + direction-tx; + sfp-mac = <ð1>; + + /* TAP settings. The format of this is as + * follows: + * - cable length in meters, 0 = active or + * optical module + * - maintap value + * - pretap value + * - posttap value + * + * For the cable length, the value will apply + * for that cable length and greater until the + * next largest cable length specified. These + * values must be ordered first by channel mask + * then by cable length. These are typically + * set for the transmit channels, not the + * receive channels. + */ + taps = <0 0x0013 0x000f 0x0000>, + <1 0x001f 0x000f 0x0004>, + <3 0x0014 0x000b 0x0004>, + <5 0x0014 0x0009 0x0006>, + <7 0x0014 0x000f 0x0000>, + <10 0x0012 0x000b 0x0013>; + }; + + vitesse-channel@3 { + compatible = "vitesse,vsc7224-channel"; + reg = <3>; + /* Ignore mod_abs and module */ + direction-rx; + sfp-mac = <ð1>; + + /* Disable pre-tap */ + pretap-disable; + + /* Disable post-tap */ + posttap-disable; + + /* Taps has the following fields: + * - cable length (ignored for rx) + * - main tap value + * - pre tap value + * - post tap value + * + * NOTE: if taps are disabled then they + * are not programmed. + */ + taps = <0 0x0a 0x0b 0x10>; + }; + }; + + sfp1eeprom: eeprom@50 { + compatible = "atmel,24c01"; + reg = <0x50>; + }; + + sfp1alerts: eeprom@51 { + compatible = "atmel,24c01"; + reg = <0x51>; + }; }; &mmc { @@ -151,6 +348,26 @@ compatible = "marvell,pci-bootcmd"; status = "okay"; }; + + sfp0: sfp-slot@0 { + compatible = "ethernet,sfp-slot"; + tx_disable = <&gpio 16 0>; + mod_abs = <&gpio 17 0>; + tx_error = <&gpio 19 0>; + rx_los = <&gpio 18 0>; + eeprom = <&sfp0eeprom>; + diag = <&sfp0alerts>; + }; + + sfp1: sfp-slot@1 { + compatible = "ethernet,sfp-slot"; + tx_disable = <&gpio 21 0>; + mod_abs = <&gpio 22 0>; + tx_error = <&gpio 24 0>; + rx_los = <&gpio 23 0>; + eeprom = <&sfp1eeprom>; + diag = <&sfp1alerts>; + }; }; &spi { @@ -160,3 +377,24 @@ reg = <0>; }; }; + +/* BGX 2 */ +&bgx2 { + status = "okay"; + + /* SerDes 0, may differ from PCS Lane/LMAC */ + eth0: ethernet-mac@0 { + compatible = "cavium,octeon-7890-bgx-port"; + reg = <0>; + local-mac-address = [ 00 00 00 00 00 00 ]; + sfp-slot = <&sfp0>; + }; + + /* SerDes 1, may differ from PCS Lane/LMAC */ + eth1: ethernet-mac@1 { + compatible = "cavium,octeon-7890-bgx-port"; + reg = <1>; + local-mac-address = [ 00 00 00 00 00 00 ]; + sfp-slot = <&sfp1>; + }; +}; diff --git a/arch/mips/mach-octeon/Makefile b/arch/mips/mach-octeon/Makefile index 40ddab2..6aa7b36 100644 --- a/arch/mips/mach-octeon/Makefile +++ b/arch/mips/mach-octeon/Makefile @@ -12,13 +12,46 @@ obj-y += cvmx-coremask.o obj-y += cvmx-bootmem.o obj-y += bootoctlinux.o -# QLM related code +# Misc Octeon C files, mostly for QLM & ethernet support +obj-y += cvmx-agl.o +obj-y += cvmx-fpa.o +obj-y += cvmx-fpa-resource.o +obj-y += cvmx-fau-compat.o +obj-y += cvmx-global-resources.o +obj-y += cvmx-cmd-queue.o +obj-y += cvmx-helper-agl.o +obj-y += cvmx-helper-bgx.o +obj-y += cvmx-helper-board.o obj-y += cvmx-helper-cfg.o obj-y += cvmx-helper-fdt.o +obj-y += cvmx-helper-fpa.o +obj-y += cvmx-helper-ilk.o +obj-y += cvmx-helper-ipd.o obj-y += cvmx-helper-jtag.o +obj-y += cvmx-helper-loop.o +obj-y += cvmx-helper-npi.o +obj-y += cvmx-helper-pki.o +obj-y += cvmx-helper-pko.o +obj-y += cvmx-helper-pko3.o +obj-y += cvmx-helper-rgmii.o +obj-y += cvmx-helper-sfp.o +obj-y += cvmx-helper-sgmii.o obj-y += cvmx-helper-util.o +obj-y += cvmx-helper-xaui.o obj-y += cvmx-helper.o +obj-y += cvmx-ilk.o +obj-y += cvmx-ipd.o obj-y += cvmx-pcie.o +obj-y += cvmx-pki.o +obj-y += cvmx-pki-resources.o +obj-y += cvmx-pko.o +obj-y += cvmx-pko-internal-ports-range.o +obj-y += cvmx-pko3.o +obj-y += cvmx-pko3-compat.o +obj-y += cvmx-pko3-resources.o +obj-y += cvmx-pko3-queue.o +obj-y += cvmx-range.o obj-y += cvmx-qlm.o +obj-y += cvmx-qlm-tables.o obj-y += octeon_fdt.o obj-y += octeon_qlm.o diff --git a/arch/mips/mach-octeon/cpu.c b/arch/mips/mach-octeon/cpu.c index 6cfcc3e..1bdc6cd 100644 --- a/arch/mips/mach-octeon/cpu.c +++ b/arch/mips/mach-octeon/cpu.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * Copyright (C) 2020 Marvell International Ltd. + * Copyright (C) 2020-2022 Marvell International Ltd. */ #include <dm.h> @@ -17,6 +17,8 @@ #include <mach/cvmx-bootmem.h> #include <mach/cvmx-regs.h> #include <mach/cvmx-sata-defs.h> +#include <mach/octeon-model.h> +#include <mach/octeon-feature.h> DECLARE_GLOBAL_DATA_PTR; @@ -393,14 +395,55 @@ static int init_bootcmd_console(void) return ret; } -int arch_misc_init(void) +static void configure_lmtdma_window(void) +{ + u64 tmp; + u64 addr; + u64 end_addr; + + CVMX_MF_CVM_MEM_CTL(tmp); + tmp &= ~0x1ffull; + tmp |= 0x104ull; + + /* enable LMTDMA */ + tmp |= (1ull << 51); + /* configure scratch line 2 for LMT */ + /* TODO: reserve this scratch line, so that others will not use it */ + /* TODO: store LMTLINE in global var */ + tmp |= (CVMX_PKO_LMTLINE << 45); + /* clear LMTLINE in scratch */ + addr = CVMX_PKO_LMTLINE * CVMX_CACHE_LINE_SIZE; + end_addr = addr + CVMX_CACHE_LINE_SIZE; + + while (addr < end_addr) { + *CASTPTR(volatile u64, addr + CVMX_SCRATCH_BASE) = (u64)0; + addr += 8; + } + CVMX_MT_CVM_MEM_CTL(tmp); +} + +int arch_early_init_r(void) { int ret; + /* + * Needs to be called pretty early, so that e.g. networking etc + * can access the bootmem infrastructure + */ ret = octeon_bootmem_init(); if (ret) return ret; + if (octeon_has_feature(OCTEON_FEATURE_PKO3)) + configure_lmtdma_window(); + + return 0; +} + +int arch_misc_init(void) +{ + int ret; + ret = octeon_configure_load_memory(); if (ret) return ret; diff --git a/arch/mips/mach-octeon/cvmx-agl.c b/arch/mips/mach-octeon/cvmx-agl.c new file mode 100644 index 0000000..9eea857 --- /dev/null +++ b/arch/mips/mach-octeon/cvmx-agl.c @@ -0,0 +1,216 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018-2022 Marvell International Ltd. + * + * Functions for RGMII (MGMT) initialization, configuration, + * and monitoring. + */ + +#include <log.h> +#include <time.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-pki.h> +#include <mach/cvmx-helper.h> +#include <mach/cvmx-helper-board.h> +#include <mach/cvmx-helper-cfg.h> + +#include <mach/cvmx-agl.h> +#include <mach/cvmx-agl-defs.h> + +/* + * @param port to enable + * + * @return Zero on success, negative on failure + */ +int cvmx_agl_enable(int port) +{ + cvmx_agl_gmx_rxx_frm_ctl_t rxx_frm_ctl; + + rxx_frm_ctl.u64 = 0; + rxx_frm_ctl.s.pre_align = 1; + /* When set, disables the length check for non-min sized pkts with + * padding in the client data + */ + rxx_frm_ctl.s.pad_len = 1; + /* When set, disables the length check for VLAN pkts */ + rxx_frm_ctl.s.vlan_len = 1; + /* When set, PREAMBLE checking is less strict */ + rxx_frm_ctl.s.pre_free = 1; + /* Control Pause Frames can match station SMAC */ + rxx_frm_ctl.s.ctl_smac = 0; + /* Control Pause Frames can match globally assign Multicast address */ + rxx_frm_ctl.s.ctl_mcst = 1; + rxx_frm_ctl.s.ctl_bck = 1; /* Forward pause information to TX block */ + rxx_frm_ctl.s.ctl_drp = 1; /* Drop Control Pause Frames */ + rxx_frm_ctl.s.pre_strp = 1; /* Strip off the preamble */ + /* This port is configured to send PREAMBLE+SFD to begin every frame. + * GMX checks that the PREAMBLE is sent correctly + */ + rxx_frm_ctl.s.pre_chk = 1; + csr_wr(CVMX_AGL_GMX_RXX_FRM_CTL(port), rxx_frm_ctl.u64); + + return 0; +} + +cvmx_helper_link_info_t cvmx_agl_link_get(int port) +{ + cvmx_helper_link_info_t result; + int interface, port_index; + + /* Fake IPD port is used on some older models. */ + if (port < 0) + return __cvmx_helper_board_link_get(port); + + /* Simulator does not have PHY, use some defaults. */ + interface = cvmx_helper_get_interface_num(port); + port_index = cvmx_helper_get_interface_index_num(port); + if (cvmx_helper_get_port_force_link_up(interface, port_index)) { + result.u64 = 0; + result.s.full_duplex = 1; + result.s.link_up = 1; + result.s.speed = 1000; + return result; + } + + return __cvmx_helper_board_link_get(port); +} + +/* + * Set MII/RGMII link based on mode. + * + * @param port interface port to set the link. + * @param link_info Link status + * + * @return 0 on success and 1 on failure + */ +int cvmx_agl_link_set(int port, cvmx_helper_link_info_t link_info) +{ + cvmx_agl_gmx_prtx_cfg_t agl_gmx_prtx; + + /* Disable GMX before we make any changes. */ + agl_gmx_prtx.u64 = csr_rd(CVMX_AGL_GMX_PRTX_CFG(port)); + agl_gmx_prtx.s.en = 0; + agl_gmx_prtx.s.tx_en = 0; + agl_gmx_prtx.s.rx_en = 0; + csr_wr(CVMX_AGL_GMX_PRTX_CFG(port), agl_gmx_prtx.u64); + + if (OCTEON_IS_MODEL(OCTEON_CN6XXX) || OCTEON_IS_OCTEON3()) { + u64 one_second = 0x1000000; /* todo: this needs checking */ + + /* Wait for GMX to be idle */ + if (CVMX_WAIT_FOR_FIELD64(CVMX_AGL_GMX_PRTX_CFG(port), + cvmx_agl_gmx_prtx_cfg_t, rx_idle, ==, + 1, one_second) || + CVMX_WAIT_FOR_FIELD64(CVMX_AGL_GMX_PRTX_CFG(port), + cvmx_agl_gmx_prtx_cfg_t, tx_idle, ==, + 1, one_second)) { + debug("AGL%d: Timeout waiting for GMX to be idle\n", + port); + return -1; + } + } + + agl_gmx_prtx.u64 = csr_rd(CVMX_AGL_GMX_PRTX_CFG(port)); + + /* Set duplex mode */ + if (!link_info.s.link_up) + agl_gmx_prtx.s.duplex = 1; /* Force full duplex on down links */ + else + agl_gmx_prtx.s.duplex = link_info.s.full_duplex; + + switch (link_info.s.speed) { + case 10: + agl_gmx_prtx.s.speed = 0; + agl_gmx_prtx.s.slottime = 0; + if (OCTEON_IS_MODEL(OCTEON_CN6XXX) || OCTEON_IS_OCTEON3()) { + agl_gmx_prtx.s.speed_msb = 1; + agl_gmx_prtx.s.burst = 1; + } + break; + + case 100: + agl_gmx_prtx.s.speed = 0; + agl_gmx_prtx.s.slottime = 0; + if (OCTEON_IS_MODEL(OCTEON_CN6XXX) || OCTEON_IS_OCTEON3()) { + agl_gmx_prtx.s.speed_msb = 0; + agl_gmx_prtx.s.burst = 1; + } + break; + + case 1000: + /* 1000 MBits is only supported on 6XXX chips */ + if (OCTEON_IS_MODEL(OCTEON_CN6XXX) || OCTEON_IS_OCTEON3()) { + agl_gmx_prtx.s.speed_msb = 0; + agl_gmx_prtx.s.speed = 1; + agl_gmx_prtx.s.slottime = + 1; /* Only matters for half-duplex */ + agl_gmx_prtx.s.burst = agl_gmx_prtx.s.duplex; + } + break; + + /* No link */ + case 0: + default: + break; + } + + /* Write the new GMX setting with the port still disabled. */ + csr_wr(CVMX_AGL_GMX_PRTX_CFG(port), agl_gmx_prtx.u64); + + /* Read GMX CFG again to make sure the config is completed. */ + agl_gmx_prtx.u64 = csr_rd(CVMX_AGL_GMX_PRTX_CFG(port)); + + if (OCTEON_IS_MODEL(OCTEON_CN6XXX) || OCTEON_IS_OCTEON3()) { + cvmx_agl_gmx_txx_clk_t agl_clk; + cvmx_agl_prtx_ctl_t prt_ctl; + + prt_ctl.u64 = csr_rd(CVMX_AGL_PRTX_CTL(port)); + agl_clk.u64 = csr_rd(CVMX_AGL_GMX_TXX_CLK(port)); + /* MII (both speeds) and RGMII 1000 setting */ + agl_clk.s.clk_cnt = 1; + /* Check other speeds for RGMII mode */ + if (prt_ctl.s.mode == 0 || OCTEON_IS_OCTEON3()) { + if (link_info.s.speed == 10) + agl_clk.s.clk_cnt = 50; + else if (link_info.s.speed == 100) + agl_clk.s.clk_cnt = 5; + } + csr_wr(CVMX_AGL_GMX_TXX_CLK(port), agl_clk.u64); + } + + /* Enable transmit and receive ports */ + agl_gmx_prtx.s.tx_en = 1; + agl_gmx_prtx.s.rx_en = 1; + csr_wr(CVMX_AGL_GMX_PRTX_CFG(port), agl_gmx_prtx.u64); + + /* Enable the link. */ + agl_gmx_prtx.s.en = 1; + csr_wr(CVMX_AGL_GMX_PRTX_CFG(port), agl_gmx_prtx.u64); + + if (OCTEON_IS_OCTEON3()) { + union cvmx_agl_prtx_ctl agl_prtx_ctl; + /* Enable the interface, set clkrst */ + agl_prtx_ctl.u64 = csr_rd(CVMX_AGL_PRTX_CTL(port)); + agl_prtx_ctl.s.clkrst = 1; + csr_wr(CVMX_AGL_PRTX_CTL(port), agl_prtx_ctl.u64); + csr_rd(CVMX_AGL_PRTX_CTL(port)); + agl_prtx_ctl.s.enable = 1; + csr_wr(CVMX_AGL_PRTX_CTL(port), agl_prtx_ctl.u64); + /* Read the value back to force the previous write */ + csr_rd(CVMX_AGL_PRTX_CTL(port)); + } + + return 0; +} diff --git a/arch/mips/mach-octeon/cvmx-bootmem.c b/arch/mips/mach-octeon/cvmx-bootmem.c index 9bd644d..52e58b4 100644 --- a/arch/mips/mach-octeon/cvmx-bootmem.c +++ b/arch/mips/mach-octeon/cvmx-bootmem.c @@ -1189,7 +1189,7 @@ s64 cvmx_bootmem_phy_mem_list_init(u64 mem_size, if (mem_size > OCTEON_DDR1_SIZE) { __cvmx_bootmem_phy_free(OCTEON_DDR1_BASE, OCTEON_DDR1_SIZE, 0); __cvmx_bootmem_phy_free(OCTEON_DDR2_BASE, - mem_size - OCTEON_DDR1_SIZE, 0); + mem_size - OCTEON_DDR2_BASE, 0); } else { __cvmx_bootmem_phy_free(OCTEON_DDR1_BASE, mem_size, 0); } @@ -1349,7 +1349,6 @@ s64 cvmx_bootmem_phy_mem_list_init_multi(u8 node_mask, addr += sizeof(struct cvmx_bootmem_named_block_desc); } - // test-only: DEBUG ifdef??? cvmx_bootmem_phy_list_print(); return 1; diff --git a/arch/mips/mach-octeon/cvmx-cmd-queue.c b/arch/mips/mach-octeon/cvmx-cmd-queue.c new file mode 100644 index 0000000..c4b49f9 --- /dev/null +++ b/arch/mips/mach-octeon/cvmx-cmd-queue.c @@ -0,0 +1,355 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018-2022 Marvell International Ltd. + * + * Support functions for managing command queues used for + * various hardware blocks. + */ + +#include <errno.h> +#include <log.h> +#include <time.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-fpa.h> +#include <mach/cvmx-cmd-queue.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-pko3.h> +#include <mach/cvmx-pko3-queue.h> +#include <mach/cvmx-pko3-resources.h> + +#include <mach/cvmx-helper.h> +#include <mach/cvmx-helper-board.h> +#include <mach/cvmx-helper-cfg.h> + +#include <mach/cvmx-helper-bgx.h> +#include <mach/cvmx-helper-cfg.h> +#include <mach/cvmx-helper-util.h> +#include <mach/cvmx-helper-pki.h> + +#include <mach/cvmx-helper-util.h> +#include <mach/cvmx-dpi-defs.h> +#include <mach/cvmx-npei-defs.h> +#include <mach/cvmx-pexp-defs.h> + +/** + * This application uses this pointer to access the global queue + * state. It points to a bootmem named block. + */ +__cvmx_cmd_queue_all_state_t *__cvmx_cmd_queue_state_ptrs[CVMX_MAX_NODES]; + +/** + * @INTERNAL + * Initialize the Global queue state pointer. + * + * @return CVMX_CMD_QUEUE_SUCCESS or a failure code + */ +cvmx_cmd_queue_result_t __cvmx_cmd_queue_init_state_ptr(unsigned int node) +{ + const char *alloc_name = "cvmx_cmd_queues\0\0"; + char s[4] = "_0"; + const struct cvmx_bootmem_named_block_desc *block_desc = NULL; + unsigned int size; + u64 paddr_min = 0, paddr_max = 0; + void *ptr; + + if (cvmx_likely(__cvmx_cmd_queue_state_ptrs[node])) + return CVMX_CMD_QUEUE_SUCCESS; + + /* Add node# to block name */ + if (node > 0) { + s[1] += node; + strcat((char *)alloc_name, s); + } + + /* Find the named block in case it has been created already */ + block_desc = cvmx_bootmem_find_named_block(alloc_name); + if (block_desc) { + __cvmx_cmd_queue_state_ptrs[node] = + (__cvmx_cmd_queue_all_state_t *)cvmx_phys_to_ptr( + block_desc->base_addr); + return CVMX_CMD_QUEUE_SUCCESS; + } + + size = sizeof(*__cvmx_cmd_queue_state_ptrs[node]); + + /* Rest f the code is to allocate a new named block */ + + /* Atomically allocate named block once, and zero it by default */ + ptr = cvmx_bootmem_alloc_named_range_once(size, paddr_min, paddr_max, + 128, alloc_name, NULL); + + if (ptr) { + __cvmx_cmd_queue_state_ptrs[node] = + (__cvmx_cmd_queue_all_state_t *)ptr; + } else { + debug("ERROR: %s: Unable to get named block %s.\n", __func__, + alloc_name); + return CVMX_CMD_QUEUE_NO_MEMORY; + } + return CVMX_CMD_QUEUE_SUCCESS; +} + +/** + * Initialize a command queue for use. The initial FPA buffer is + * allocated and the hardware unit is configured to point to the + * new command queue. + * + * @param queue_id Hardware command queue to initialize. + * @param max_depth Maximum outstanding commands that can be queued. + * @param fpa_pool FPA pool the command queues should come from. + * @param pool_size Size of each buffer in the FPA pool (bytes) + * + * @return CVMX_CMD_QUEUE_SUCCESS or a failure code + */ +cvmx_cmd_queue_result_t cvmx_cmd_queue_initialize(cvmx_cmd_queue_id_t queue_id, + int max_depth, int fpa_pool, + int pool_size) +{ + __cvmx_cmd_queue_state_t *qstate; + cvmx_cmd_queue_result_t result; + unsigned int node; + unsigned int index; + int fpa_pool_min, fpa_pool_max; + union cvmx_fpa_ctl_status status; + void *buffer; + + node = __cvmx_cmd_queue_get_node(queue_id); + + index = __cvmx_cmd_queue_get_index(queue_id); + if (index >= NUM_ELEMENTS(__cvmx_cmd_queue_state_ptrs[node]->state)) { + printf("ERROR: %s: queue %#x out of range\n", __func__, + queue_id); + return CVMX_CMD_QUEUE_INVALID_PARAM; + } + + result = __cvmx_cmd_queue_init_state_ptr(node); + if (result != CVMX_CMD_QUEUE_SUCCESS) + return result; + + qstate = __cvmx_cmd_queue_get_state(queue_id); + if (!qstate) + return CVMX_CMD_QUEUE_INVALID_PARAM; + + /* + * We artificially limit max_depth to 1<<20 words. It is an + * arbitrary limit. + */ + if (CVMX_CMD_QUEUE_ENABLE_MAX_DEPTH) { + if (max_depth < 0 || max_depth > 1 << 20) + return CVMX_CMD_QUEUE_INVALID_PARAM; + } else if (max_depth != 0) { + return CVMX_CMD_QUEUE_INVALID_PARAM; + } + + /* CVMX_FPA_NUM_POOLS maps to cvmx_fpa3_num_auras for FPA3 */ + fpa_pool_min = node << 10; + fpa_pool_max = fpa_pool_min + CVMX_FPA_NUM_POOLS; + + if (fpa_pool < fpa_pool_min || fpa_pool >= fpa_pool_max) + return CVMX_CMD_QUEUE_INVALID_PARAM; + + if (pool_size < 128 || pool_size > (1 << 17)) + return CVMX_CMD_QUEUE_INVALID_PARAM; + + if (pool_size & 3) + debug("WARNING: %s: pool_size %d not multiple of 8\n", __func__, + pool_size); + + /* See if someone else has already initialized the queue */ + if (qstate->base_paddr) { + int depth; + static const char emsg[] = /* Common error message part */ + "Queue already initialized with different "; + + depth = (max_depth + qstate->pool_size_m1 - 1) / + qstate->pool_size_m1; + if (depth != qstate->max_depth) { + depth = qstate->max_depth * qstate->pool_size_m1; + debug("ERROR: %s: %s max_depth (%d).\n", __func__, emsg, + depth); + return CVMX_CMD_QUEUE_INVALID_PARAM; + } + if (fpa_pool != qstate->fpa_pool) { + debug("ERROR: %s: %s FPA pool (%d).\n", __func__, emsg, + (int)qstate->fpa_pool); + return CVMX_CMD_QUEUE_INVALID_PARAM; + } + if ((pool_size >> 3) - 1 != qstate->pool_size_m1) { + debug("ERROR: %s: %s FPA pool size (%u).\n", __func__, + emsg, (qstate->pool_size_m1 + 1) << 3); + return CVMX_CMD_QUEUE_INVALID_PARAM; + } + return CVMX_CMD_QUEUE_ALREADY_SETUP; + } + + if (!(octeon_has_feature(OCTEON_FEATURE_FPA3))) { + status.u64 = csr_rd(CVMX_FPA_CTL_STATUS); + if (!status.s.enb) { + debug("ERROR: %s: FPA is not enabled.\n", + __func__); + return CVMX_CMD_QUEUE_NO_MEMORY; + } + } + buffer = cvmx_fpa_alloc(fpa_pool); + if (!buffer) { + debug("ERROR: %s: allocating first buffer.\n", __func__); + return CVMX_CMD_QUEUE_NO_MEMORY; + } + + index = (pool_size >> 3) - 1; + qstate->pool_size_m1 = index; + qstate->max_depth = (max_depth + index - 1) / index; + qstate->index = 0; + qstate->fpa_pool = fpa_pool; + qstate->base_paddr = cvmx_ptr_to_phys(buffer); + + /* Initialize lock */ + __cvmx_cmd_queue_lock_init(queue_id); + return CVMX_CMD_QUEUE_SUCCESS; +} + +/** + * Return the command buffer to be written to. The purpose of this + * function is to allow CVMX routine access to the low level buffer + * for initial hardware setup. User applications should not call this + * function directly. + * + * @param queue_id Command queue to query + * + * @return Command buffer or NULL on failure + */ +void *cvmx_cmd_queue_buffer(cvmx_cmd_queue_id_t queue_id) +{ + __cvmx_cmd_queue_state_t *qptr = __cvmx_cmd_queue_get_state(queue_id); + + if (qptr && qptr->base_paddr) + return cvmx_phys_to_ptr((u64)qptr->base_paddr); + else + return NULL; +} + +static u64 *__cvmx_cmd_queue_add_blk(__cvmx_cmd_queue_state_t *qptr) +{ + u64 *cmd_ptr; + u64 *new_buffer; + u64 new_paddr; + + /* Get base vaddr of current (full) block */ + cmd_ptr = (u64 *)cvmx_phys_to_ptr((u64)qptr->base_paddr); + + /* Allocate a new block from the per-queue pool */ + new_buffer = (u64 *)cvmx_fpa_alloc(qptr->fpa_pool); + + /* Check for allocation failure */ + if (cvmx_unlikely(!new_buffer)) + return NULL; + + /* Zero out the new block link pointer, + * in case this block will be filled to the rim + */ + new_buffer[qptr->pool_size_m1] = ~0ull; + + /* Get physical address of the new buffer */ + new_paddr = cvmx_ptr_to_phys(new_buffer); + + /* Store the physical link address at the end of current full block */ + cmd_ptr[qptr->pool_size_m1] = new_paddr; + + /* Store the physical address in the queue state structure */ + qptr->base_paddr = new_paddr; + qptr->index = 0; + + /* Return the virtual base of the new block */ + return new_buffer; +} + +/** + * @INTERNAL + * Add command words into a queue, handles all the corener cases + * where only some of the words might fit into the current block, + * and a new block may need to be allocated. + * Locking and argument checks are done in the front-end in-line + * functions that call this one for the rare corner cases. + */ +cvmx_cmd_queue_result_t +__cvmx_cmd_queue_write_raw(cvmx_cmd_queue_id_t queue_id, + __cvmx_cmd_queue_state_t *qptr, int cmd_count, + const u64 *cmds) +{ + u64 *cmd_ptr; + unsigned int index; + + cmd_ptr = (u64 *)cvmx_phys_to_ptr((u64)qptr->base_paddr); + index = qptr->index; + + /* Enforce queue depth limit, if enabled, once per block */ + if (CVMX_CMD_QUEUE_ENABLE_MAX_DEPTH && cvmx_unlikely(qptr->max_depth)) { + unsigned int depth = cvmx_cmd_queue_length(queue_id); + + depth /= qptr->pool_size_m1; + + if (cvmx_unlikely(depth > qptr->max_depth)) + return CVMX_CMD_QUEUE_FULL; + } + + /* + * If the block allocation fails, even the words that we wrote + * to the current block will not count because the 'index' will + * not be comitted. + * The loop is run 'count + 1' times to take care of the tail + * case, where the buffer is full to the rim, so the link + * pointer must be filled with a valid address. + */ + while (cmd_count >= 0) { + if (index >= qptr->pool_size_m1) { + /* Block is full, get another one and proceed */ + cmd_ptr = __cvmx_cmd_queue_add_blk(qptr); + + /* Baul on allocation error w/o comitting anything */ + if (cvmx_unlikely(!cmd_ptr)) + return CVMX_CMD_QUEUE_NO_MEMORY; + + /* Reset index for start of new block */ + index = 0; + } + /* Exit Loop on 'count + 1' iterations */ + if (cmd_count <= 0) + break; + /* Store commands into queue block while there is space */ + cmd_ptr[index++] = *cmds++; + cmd_count--; + } /* while cmd_count */ + + /* Commit added words if all is well */ + qptr->index = index; + + return CVMX_CMD_QUEUE_SUCCESS; +} diff --git a/arch/mips/mach-octeon/cvmx-fau-compat.c b/arch/mips/mach-octeon/cvmx-fau-compat.c new file mode 100644 index 0000000..9c2ff76 --- /dev/null +++ b/arch/mips/mach-octeon/cvmx-fau-compat.c @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018-2022 Marvell International Ltd. + */ + +#include <log.h> +#include <time.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-global-resources.h> + +#include <mach/cvmx-pki.h> +#include <mach/cvmx-helper.h> +#include <mach/cvmx-helper-board.h> +#include <mach/cvmx-helper-cfg.h> + +#include <mach/cvmx-hwfau.h> + +u8 *cvmx_fau_regs_ptr; + +void cvmx_fau_bootmem_init(void *bootmem) +{ + memset(bootmem, 0, CVMX_FAU_MAX_REGISTERS_8); +} + +/** + * Initializes FAU region for devices without FAU unit. + * @return 0 on success -1 on failure + */ +int cvmx_fau_init(void) +{ + cvmx_fau_regs_ptr = (u8 *)cvmx_bootmem_alloc_named_range_once( + CVMX_FAU_MAX_REGISTERS_8, 0, 1ull << 31, 128, + "cvmx_fau_registers", cvmx_fau_bootmem_init); + + if (cvmx_fau_regs_ptr == 0ull) { + debug("ERROR: Failed to alloc named block for software FAU.\n"); + return -1; + } + + return 0; +} diff --git a/arch/mips/mach-octeon/cvmx-fpa-resource.c b/arch/mips/mach-octeon/cvmx-fpa-resource.c new file mode 100644 index 0000000..e502564 --- /dev/null +++ b/arch/mips/mach-octeon/cvmx-fpa-resource.c @@ -0,0 +1,219 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018-2022 Marvell International Ltd. + */ + +#include <errno.h> +#include <log.h> +#include <time.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-range.h> +#include <mach/cvmx-global-resources.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-ipd.h> +#include <mach/cvmx-pki.h> +#include <mach/cvmx-pko3.h> +#include <mach/cvmx-pko3-queue.h> +#include <mach/cvmx-pko3-resources.h> + +#include <mach/cvmx-helper.h> +#include <mach/cvmx-helper-board.h> +#include <mach/cvmx-helper-cfg.h> + +#include <mach/cvmx-helper-bgx.h> +#include <mach/cvmx-helper-cfg.h> +#include <mach/cvmx-helper-util.h> +#include <mach/cvmx-helper-pki.h> + +static struct global_resource_tag get_fpa1_resource_tag(void) +{ + return CVMX_GR_TAG_FPA; +} + +static struct global_resource_tag get_fpa3_aura_resource_tag(int node) +{ + return cvmx_get_gr_tag('c', 'v', 'm', '_', 'a', 'u', 'r', 'a', '_', + node + '0', '.', '.', '.', '.', '.', '.'); +} + +static struct global_resource_tag get_fpa3_pool_resource_tag(int node) +{ + return cvmx_get_gr_tag('c', 'v', 'm', '_', 'p', 'o', 'o', 'l', '_', + node + '0', '.', '.', '.', '.', '.', '.'); +} + +int cvmx_fpa_get_max_pools(void) +{ + if (octeon_has_feature(OCTEON_FEATURE_FPA3)) + return cvmx_fpa3_num_auras(); + else if (OCTEON_IS_MODEL(OCTEON_CN68XX)) + /* 68xx pool 8 is not available via API */ + return CVMX_FPA1_NUM_POOLS; + else + return CVMX_FPA1_NUM_POOLS; +} + +cvmx_fpa3_gaura_t cvmx_fpa3_reserve_aura(int node, int desired_aura_num) +{ + u64 owner = cvmx_get_app_id(); + int rv = 0; + struct global_resource_tag tag; + cvmx_fpa3_gaura_t aura; + + if (node == -1) + node = cvmx_get_node_num(); + + tag = get_fpa3_aura_resource_tag(node); + + if (cvmx_create_global_resource_range(tag, cvmx_fpa3_num_auras()) != + 0) { + printf("ERROR: %s: global resource create node=%u\n", __func__, + node); + return CVMX_FPA3_INVALID_GAURA; + } + + if (desired_aura_num >= 0) + rv = cvmx_reserve_global_resource_range(tag, owner, + desired_aura_num, 1); + else + rv = cvmx_resource_alloc_reverse(tag, owner); + + if (rv < 0) { + printf("ERROR: %s: node=%u desired aura=%d\n", __func__, node, + desired_aura_num); + return CVMX_FPA3_INVALID_GAURA; + } + + aura = __cvmx_fpa3_gaura(node, rv); + + return aura; +} + +int cvmx_fpa3_release_aura(cvmx_fpa3_gaura_t aura) +{ + struct global_resource_tag tag = get_fpa3_aura_resource_tag(aura.node); + int laura = aura.laura; + + if (!__cvmx_fpa3_aura_valid(aura)) + return -1; + + return cvmx_free_global_resource_range_multiple(tag, &laura, 1); +} + +/** + */ +cvmx_fpa3_pool_t cvmx_fpa3_reserve_pool(int node, int desired_pool_num) +{ + u64 owner = cvmx_get_app_id(); + int rv = 0; + struct global_resource_tag tag; + cvmx_fpa3_pool_t pool; + + if (node == -1) + node = cvmx_get_node_num(); + + tag = get_fpa3_pool_resource_tag(node); + + if (cvmx_create_global_resource_range(tag, cvmx_fpa3_num_pools()) != + 0) { + printf("ERROR: %s: global resource create node=%u\n", __func__, + node); + return CVMX_FPA3_INVALID_POOL; + } + + if (desired_pool_num >= 0) + rv = cvmx_reserve_global_resource_range(tag, owner, + desired_pool_num, 1); + else + rv = cvmx_resource_alloc_reverse(tag, owner); + + if (rv < 0) { + /* Desired pool is already in use */ + return CVMX_FPA3_INVALID_POOL; + } + + pool = __cvmx_fpa3_pool(node, rv); + + return pool; +} + +int cvmx_fpa3_release_pool(cvmx_fpa3_pool_t pool) +{ + struct global_resource_tag tag = get_fpa3_pool_resource_tag(pool.node); + int lpool = pool.lpool; + + if (!__cvmx_fpa3_pool_valid(pool)) + return -1; + + if (cvmx_create_global_resource_range(tag, cvmx_fpa3_num_pools()) != + 0) { + printf("ERROR: %s: global resource create node=%u\n", __func__, + pool.node); + return -1; + } + + return cvmx_free_global_resource_range_multiple(tag, &lpool, 1); +} + +cvmx_fpa1_pool_t cvmx_fpa1_reserve_pool(int desired_pool_num) +{ + u64 owner = cvmx_get_app_id(); + struct global_resource_tag tag; + int rv; + + tag = get_fpa1_resource_tag(); + + if (cvmx_create_global_resource_range(tag, CVMX_FPA1_NUM_POOLS) != 0) { + printf("ERROR: %s: global resource not created\n", __func__); + return -1; + } + + if (desired_pool_num >= 0) { + rv = cvmx_reserve_global_resource_range(tag, owner, + desired_pool_num, 1); + } else { + rv = cvmx_resource_alloc_reverse(tag, owner); + } + + if (rv < 0) { + printf("ERROR: %s: FPA_POOL %d unavailable\n", __func__, + desired_pool_num); + return CVMX_RESOURCE_ALREADY_RESERVED; + } + return (cvmx_fpa1_pool_t)rv; +} + +int cvmx_fpa1_release_pool(cvmx_fpa1_pool_t pool) +{ + struct global_resource_tag tag; + + tag = get_fpa1_resource_tag(); + + return cvmx_free_global_resource_range_multiple(tag, &pool, 1); +} diff --git a/arch/mips/mach-octeon/cvmx-fpa.c b/arch/mips/mach-octeon/cvmx-fpa.c new file mode 100644 index 0000000..14fe87c --- /dev/null +++ b/arch/mips/mach-octeon/cvmx-fpa.c @@ -0,0 +1,1127 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018-2022 Marvell International Ltd. + * + * Support library for the hardware Free Pool Allocator. + */ + +#include <errno.h> +#include <log.h> +#include <time.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-range.h> +#include <mach/cvmx-global-resources.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-pko3.h> +#include <mach/cvmx-pko3-queue.h> +#include <mach/cvmx-pko3-resources.h> + +#include <mach/cvmx-helper.h> +#include <mach/cvmx-helper-board.h> +#include <mach/cvmx-helper-cfg.h> + +#include <mach/cvmx-helper-bgx.h> +#include <mach/cvmx-helper-cfg.h> +#include <mach/cvmx-helper-util.h> +#include <mach/cvmx-helper-pki.h> + +static const int debug; + +/* Due to suspected errata, we may not be able to let the FPA_AURAX_CNT + * get too close to 0, to avoid a spurious wrap-around error + */ +const unsigned int __cvmx_fpa3_cnt_offset = 32; + +/* For advanced checks, a guard-band is created around the internal + * stack, to make sure the stack is not overwritten. + */ +const u64 magic_pattern = 0xbab4faced095f00d; +const unsigned int guard_band_size = 0 << 10; /* 1KiB default*/ + +#define CVMX_CACHE_LINE_SHIFT (7) + +#define CVMX_FPA3_NAME_LEN (16) + +typedef struct { + char name[CVMX_FPA3_NAME_LEN]; + u64 stack_paddr; /* Internal stack storage */ + u64 bufs_paddr; /* Buffer pool base address */ + u64 stack_psize; /* Internal stack storage size */ + u64 bufs_psize; /* Buffer pool raw size */ + u64 buf_count; /* Number of buffer filled */ + u64 buf_size; /* Buffer size */ +} cvmx_fpa3_poolx_info_t; + +typedef struct { + char name[CVMX_FPA3_NAME_LEN]; + unsigned int buf_size; /* Buffer size */ +} cvmx_fpa3_aurax_info_t; + +typedef struct { + char name[CVMX_FPA1_NAME_SIZE]; + u64 size; /* Block size of pool buffers */ + u64 buffer_count; + u64 base_paddr; /* Base physical addr */ + /* if buffer is allocated at initialization */ +} cvmx_fpa1_pool_info_t; + +/** + * FPA1/FPA3 info structure is stored in a named block + * that is allocated once and shared among applications. + */ +static cvmx_fpa1_pool_info_t *cvmx_fpa1_pool_info; +static cvmx_fpa3_poolx_info_t *cvmx_fpa3_pool_info[CVMX_MAX_NODES]; +static cvmx_fpa3_aurax_info_t *cvmx_fpa3_aura_info[CVMX_MAX_NODES]; + +/** + * Return the size of buffers held in a POOL + * + * @param pool is the POOL handle + * @return buffer size in bytes + * + */ +int cvmx_fpa3_get_pool_buf_size(cvmx_fpa3_pool_t pool) +{ + cvmx_fpa_poolx_cfg_t pool_cfg; + + if (!__cvmx_fpa3_pool_valid(pool)) + return -1; + + pool_cfg.u64 = csr_rd_node(pool.node, CVMX_FPA_POOLX_CFG(pool.lpool)); + return pool_cfg.cn78xx.buf_size << CVMX_CACHE_LINE_SHIFT; +} + +/** + * Return the size of buffers held in a buffer pool + * + * @param pool is the pool number + * + * This function will work with CN78XX models in backward-compatible mode + */ +unsigned int cvmx_fpa_get_block_size(int pool) +{ + if (octeon_has_feature(OCTEON_FEATURE_FPA3)) { + return cvmx_fpa3_get_pool_buf_size(cvmx_fpa3_aura_to_pool( + cvmx_fpa1_pool_to_fpa3_aura(pool))); + } else { + if ((unsigned int)pool >= CVMX_FPA1_NUM_POOLS) + return 0; + if (!cvmx_fpa1_pool_info) + cvmx_fpa_global_init_node(0); + return cvmx_fpa1_pool_info[pool].size; + } +} + +static void cvmx_fpa3_set_aura_name(cvmx_fpa3_gaura_t aura, const char *name) +{ + cvmx_fpa3_aurax_info_t *pinfo; + + pinfo = cvmx_fpa3_aura_info[aura.node]; + if (!pinfo) + return; + pinfo += aura.laura; + memset(pinfo->name, 0, sizeof(pinfo->name)); + if (name) + strlcpy(pinfo->name, name, sizeof(pinfo->name)); +} + +static void cvmx_fpa3_set_pool_name(cvmx_fpa3_pool_t pool, const char *name) +{ + cvmx_fpa3_poolx_info_t *pinfo; + + pinfo = cvmx_fpa3_pool_info[pool.node]; + if (!pinfo) + return; + pinfo += pool.lpool; + memset(pinfo->name, 0, sizeof(pinfo->name)); + if (name) + strlcpy(pinfo->name, name, sizeof(pinfo->name)); +} + +static void cvmx_fpa_set_name(int pool_num, const char *name) +{ + if (octeon_has_feature(OCTEON_FEATURE_FPA3)) { + cvmx_fpa3_set_aura_name(cvmx_fpa1_pool_to_fpa3_aura(pool_num), + name); + } else { + cvmx_fpa1_pool_info_t *pinfo; + + if ((unsigned int)pool_num >= CVMX_FPA1_NUM_POOLS) + return; + if (!cvmx_fpa1_pool_info) + cvmx_fpa_global_init_node(0); + pinfo = &cvmx_fpa1_pool_info[pool_num]; + memset(pinfo->name, 0, sizeof(pinfo->name)); + if (name) + strlcpy(pinfo->name, name, sizeof(pinfo->name)); + } +} + +static int cvmx_fpa3_aura_cfg(cvmx_fpa3_gaura_t aura, cvmx_fpa3_pool_t pool, + u64 limit, u64 threshold, int ptr_dis) +{ + cvmx_fpa3_aurax_info_t *pinfo; + cvmx_fpa_aurax_cfg_t aura_cfg; + cvmx_fpa_poolx_cfg_t pool_cfg; + cvmx_fpa_aurax_cnt_t cnt_reg; + cvmx_fpa_aurax_cnt_limit_t limit_reg; + cvmx_fpa_aurax_cnt_threshold_t thresh_reg; + cvmx_fpa_aurax_int_t int_reg; + unsigned int block_size; + + if (debug) + debug("%s: AURA %u:%u POOL %u:%u\n", __func__, aura.node, + aura.laura, pool.node, pool.lpool); + + if (aura.node != pool.node) { + printf("ERROR: %s: AURA/POOL node mismatch\n", __func__); + return -1; + } + + if (!__cvmx_fpa3_aura_valid(aura)) { + printf("ERROR: %s: AURA invalid\n", __func__); + return -1; + } + + if (!__cvmx_fpa3_pool_valid(pool)) { + printf("ERROR: %s: POOL invalid\n", __func__); + return -1; + } + + /* Record POOL block size in AURA info entry */ + pool_cfg.u64 = csr_rd_node(pool.node, CVMX_FPA_POOLX_CFG(pool.lpool)); + + block_size = pool_cfg.cn78xx.buf_size << 7; + pinfo = cvmx_fpa3_aura_info[aura.node]; + if (!pinfo) + return -1; + pinfo += aura.laura; + + pinfo->buf_size = block_size; + + /* block_size should be >0 except for POOL=0 which is never enabled*/ + if (pool_cfg.cn78xx.ena && block_size == 0) { + printf("ERROR; %s: POOL buf_size invalid\n", __func__); + return -1; + } + + /* Initialize AURA count, limit and threshold registers */ + cnt_reg.u64 = 0; + cnt_reg.cn78xx.cnt = 0 + __cvmx_fpa3_cnt_offset; + + limit_reg.u64 = 0; + limit_reg.cn78xx.limit = limit; + /* Apply count offset, unless it cases a wrap-around */ + if ((limit + __cvmx_fpa3_cnt_offset) < CVMX_FPA3_AURAX_LIMIT_MAX) + limit_reg.cn78xx.limit += __cvmx_fpa3_cnt_offset; + + thresh_reg.u64 = 0; + thresh_reg.cn78xx.thresh = threshold; + /* Apply count offset, unless it cases a wrap-around */ + if ((threshold + __cvmx_fpa3_cnt_offset) < CVMX_FPA3_AURAX_LIMIT_MAX) + thresh_reg.cn78xx.thresh += __cvmx_fpa3_cnt_offset; + + csr_wr_node(aura.node, CVMX_FPA_AURAX_CNT(aura.laura), cnt_reg.u64); + csr_wr_node(aura.node, CVMX_FPA_AURAX_CNT_LIMIT(aura.laura), + limit_reg.u64); + csr_wr_node(aura.node, CVMX_FPA_AURAX_CNT_THRESHOLD(aura.laura), + thresh_reg.u64); + + /* Clear any pending error interrupts */ + int_reg.u64 = 0; + int_reg.cn78xx.thresh = 1; + + /* Follow a write to clear FPA_AURAX_INT[THRESH] with a read as + * a workaround to Errata FPA-23410. If FPA_AURAX_INT[THRESH] + * isn't clear, try again. + */ + do { + csr_wr_node(aura.node, CVMX_FPA_AURAX_INT(aura.laura), + int_reg.u64); + int_reg.u64 = + csr_rd_node(aura.node, CVMX_FPA_AURAX_INT(aura.laura)); + } while (int_reg.s.thresh); + + /* Disable backpressure etc.*/ + csr_wr_node(aura.node, CVMX_FPA_AURAX_CNT_LEVELS(aura.laura), 0); + csr_wr_node(aura.node, CVMX_FPA_AURAX_POOL_LEVELS(aura.laura), 0); + + aura_cfg.u64 = 0; + aura_cfg.s.ptr_dis = ptr_dis; + csr_wr_node(aura.node, CVMX_FPA_AURAX_CFG(aura.laura), aura_cfg.u64); + csr_wr_node(aura.node, CVMX_FPA_AURAX_POOL(aura.laura), pool.lpool); + + return 0; +} + +/** + * @INTERNAL + * + * Fill a newly created FPA3 POOL with buffers + * using a temporary AURA. + */ +static int cvmx_fpa3_pool_populate(cvmx_fpa3_pool_t pool, unsigned int buf_cnt, + unsigned int buf_sz, void *mem_ptr, + unsigned int mem_node) +{ + cvmx_fpa3_poolx_info_t *pinfo; + cvmx_fpa3_gaura_t aura; + cvmx_fpa3_pool_t zero_pool; + cvmx_fpa_poolx_cfg_t pool_cfg; + cvmx_fpa_poolx_start_addr_t pool_start_reg; + cvmx_fpa_poolx_end_addr_t pool_end_reg; + cvmx_fpa_poolx_available_t avail_reg; + cvmx_fpa_poolx_threshold_t thresh_reg; + cvmx_fpa_poolx_int_t int_reg; + unsigned int block_size, align; + unsigned long long mem_size; + u64 paddr; + unsigned int i; + + if (debug) + debug("%s: POOL %u:%u buf_sz=%u count=%d\n", __func__, + pool.node, pool.lpool, buf_sz, buf_cnt); + + if (!__cvmx_fpa3_pool_valid(pool)) + return -1; + + zero_pool = __cvmx_fpa3_pool(pool.node, 0); + + pool_cfg.u64 = csr_rd_node(pool.node, CVMX_FPA_POOLX_CFG(pool.lpool)); + + block_size = pool_cfg.cn78xx.buf_size << 7; + + if (pool_cfg.cn78xx.nat_align) { + /* Assure block_size is legit */ + if (block_size > (1 << 17)) { + printf("ERROR: %s: POOL %u:%u block size %u is not valid\n", + __func__, pool.node, pool.lpool, block_size); + return -1; + } + } + align = CVMX_CACHE_LINE_SIZE; + + pinfo = cvmx_fpa3_pool_info[pool.node]; + if (!pinfo) + return -1; + pinfo += pool.lpool; + + if (pinfo->buf_size != block_size || block_size != buf_sz) { + printf("ERROR: %s: POOL %u:%u buffer size mismatch\n", __func__, + pool.node, pool.lpool); + return -1; + } + + if (!mem_ptr) { + /* When allocating our own memory + * make sure at least 'buf_cnt' blocks + * will fit into it. + */ + mem_size = (long long)buf_cnt * block_size + (block_size - 128); + + mem_ptr = cvmx_helper_mem_alloc(mem_node, mem_size, align); + + if (!mem_ptr) { + printf("ERROR: %s: POOL %u:%u out of memory, could not allocate %llu bytes\n", + __func__, pool.node, pool.lpool, mem_size); + return -1; + } + + /* Record memory base for use in shutdown */ + pinfo->bufs_paddr = cvmx_ptr_to_phys(mem_ptr); + } else { + /* caller-allocated memory is sized simply, may reduce count */ + mem_size = (long long)buf_cnt * block_size; + /* caller responsable to free this memory too */ + } + + /* Recalculate buf_cnt after possible alignment adjustment */ + buf_cnt = mem_size / block_size; + + /* Get temporary AURA */ + aura = cvmx_fpa3_reserve_aura(pool.node, -1); + if (!__cvmx_fpa3_aura_valid(aura)) + return -1; + + /* Attach the temporary AURA to the POOL */ + (void)cvmx_fpa3_aura_cfg(aura, pool, buf_cnt, buf_cnt + 1, 0); + + /* Set AURA count to buffer count to avoid wrap-around */ + csr_wr_node(aura.node, CVMX_FPA_AURAX_CNT(aura.laura), buf_cnt); + + /* Set POOL threshold just above buf count so it does not misfire */ + thresh_reg.u64 = 0; + thresh_reg.cn78xx.thresh = buf_cnt + 1; + csr_wr_node(pool.node, CVMX_FPA_POOLX_THRESHOLD(pool.lpool), + thresh_reg.u64); + + /* Set buffer memory region bounds checking */ + paddr = (cvmx_ptr_to_phys(mem_ptr) >> 7) << 7; + pool_start_reg.u64 = 0; + pool_end_reg.u64 = 0; + pool_start_reg.cn78xx.addr = paddr >> 7; + pool_end_reg.cn78xx.addr = (paddr + mem_size + 127) >> 7; + + csr_wr_node(pool.node, CVMX_FPA_POOLX_START_ADDR(pool.lpool), + pool_start_reg.u64); + csr_wr_node(pool.node, CVMX_FPA_POOLX_END_ADDR(pool.lpool), + pool_end_reg.u64); + + /* Make sure 'paddr' is divisible by 'block_size' */ + i = (paddr % block_size); + if (i > 0) { + i = block_size - i; + paddr += i; + mem_size -= i; + } + + /* The above alignment mimics how the FPA3 hardware + * aligns pointers to the buffer size, which only + * needs to be multiple of cache line size + */ + + if (debug && paddr != cvmx_ptr_to_phys(mem_ptr)) + debug("%s: pool mem paddr %#llx adjusted to %#llx for block size %#x\n", + __func__, CAST_ULL(cvmx_ptr_to_phys(mem_ptr)), + CAST_ULL(paddr), block_size); + + for (i = 0; i < buf_cnt; i++) { + void *ptr = cvmx_phys_to_ptr(paddr); + + cvmx_fpa3_free_nosync(ptr, aura, 0); + + paddr += block_size; + + if ((paddr + block_size - 1) >= (paddr + mem_size)) + break; + } + + if (debug && i < buf_cnt) { + debug("%s: buffer count reduced from %u to %u\n", __func__, + buf_cnt, i); + buf_cnt = i; + } + + /* Wait for all buffers to reach the POOL before removing temp AURA */ + do { + CVMX_SYNC; + avail_reg.u64 = csr_rd_node( + pool.node, CVMX_FPA_POOLX_AVAILABLE(pool.lpool)); + } while (avail_reg.cn78xx.count < buf_cnt); + + /* Detach the temporary AURA */ + (void)cvmx_fpa3_aura_cfg(aura, zero_pool, 0, 0, 0); + + /* Release temporary AURA */ + (void)cvmx_fpa3_release_aura(aura); + + /* Clear all POOL interrupts */ + int_reg.u64 = 0; + int_reg.cn78xx.ovfls = 1; + int_reg.cn78xx.crcerr = 1; + int_reg.cn78xx.range = 1; + int_reg.cn78xx.thresh = 1; + csr_wr_node(pool.node, CVMX_FPA_POOLX_INT(pool.lpool), int_reg.u64); + + /* Record buffer count for shutdown */ + pinfo->buf_count = buf_cnt; + + return buf_cnt; +} + +/** + * @INTERNAL + * + * Fill a legacy FPA pool with buffers + */ +static int cvmx_fpa1_fill_pool(cvmx_fpa1_pool_t pool, int num_blocks, + void *buffer) +{ + cvmx_fpa_poolx_start_addr_t pool_start_reg; + cvmx_fpa_poolx_end_addr_t pool_end_reg; + unsigned int block_size = cvmx_fpa_get_block_size(pool); + unsigned int mem_size; + char *bufp; + + if ((unsigned int)pool >= CVMX_FPA1_NUM_POOLS) + return -1; + + mem_size = block_size * num_blocks; + + if (!buffer) { + buffer = cvmx_helper_mem_alloc(0, mem_size, + CVMX_CACHE_LINE_SIZE); + + cvmx_fpa1_pool_info[pool].base_paddr = cvmx_ptr_to_phys(buffer); + } else { + /* Align user-supplied buffer to cache line size */ + unsigned int off = + (CVMX_CACHE_LINE_SIZE - 1) & cvmx_ptr_to_phys(buffer); + if (off > 0) { + // buffer += CVMX_CACHE_LINE_SIZE - off; + buffer = (char *)buffer + CVMX_CACHE_LINE_SIZE - off; + mem_size -= CVMX_CACHE_LINE_SIZE - off; + num_blocks = mem_size / block_size; + } + } + + if (debug) + debug("%s: memory at %p size %#x\n", __func__, buffer, + mem_size); + + pool_start_reg.u64 = 0; + pool_end_reg.u64 = 0; + + /* buffer pointer range checks are highly recommended, but optional */ + pool_start_reg.cn61xx.addr = 1; /* catch NULL pointers */ + pool_end_reg.cn61xx.addr = (1ull << (40 - 7)) - 1; /* max paddr */ + if (!OCTEON_IS_MODEL(OCTEON_CN63XX)) { + csr_wr(CVMX_FPA_POOLX_START_ADDR(pool), pool_start_reg.u64); + csr_wr(CVMX_FPA_POOLX_END_ADDR(pool), pool_end_reg.u64); + } + + bufp = (char *)buffer; + while (num_blocks--) { + cvmx_fpa1_free(bufp, pool, 0); + cvmx_fpa1_pool_info[pool].buffer_count++; + bufp += block_size; + } + return 0; +} + +/** + * @INTERNAL + * + * Setup a legacy FPA pool + */ +static int cvmx_fpa1_pool_init(cvmx_fpa1_pool_t pool_id, int num_blocks, + int block_size, void *buffer) +{ + int max_pool = cvmx_fpa_get_max_pools(); + + if (pool_id < 0 || pool_id >= max_pool) { + printf("ERROR: %s pool %d invalid\n", __func__, pool_id); + return -1; + } + + if (!cvmx_fpa1_pool_info) + cvmx_fpa_global_init_node(0); + + if (debug) + debug("%s: initializing info pool %d\n", __func__, pool_id); + + cvmx_fpa1_pool_info[pool_id].size = block_size; + cvmx_fpa1_pool_info[pool_id].buffer_count = 0; + + if (debug) + debug("%s: enabling unit for pool %d\n", __func__, pool_id); + + return 0; +} + +/** + * Initialize global configuration for FPA block for specified node. + * + * @param node is the node number + * + * @note this function sets the initial QoS averaging timing parameters, + * for the entire FPA unit (per node), which may be overridden on a + * per AURA basis. + */ +int cvmx_fpa_global_init_node(int node) +{ + /* There are just the initial parameter values */ +#define FPA_RED_AVG_DLY 1 +#define FPA_RED_LVL_DLY 3 +#define FPA_QOS_AVRG 0 + /* Setting up avg_dly and prb_dly, enable bits */ + if (octeon_has_feature(OCTEON_FEATURE_FPA3)) { + char pool_info_name[32] = "cvmx_fpa3_pools_"; + char aura_info_name[32] = "cvmx_fpa3_auras_"; + char ns[2] = "0"; + + ns[0] += node; + strcat(pool_info_name, ns); + strcat(aura_info_name, ns); + + cvmx_fpa3_config_red_params(node, FPA_QOS_AVRG, FPA_RED_LVL_DLY, + FPA_RED_AVG_DLY); + + /* Allocate the pinfo named block */ + cvmx_fpa3_pool_info[node] = (cvmx_fpa3_poolx_info_t *) + cvmx_bootmem_alloc_named_range_once( + sizeof(cvmx_fpa3_pool_info[0][0]) * + cvmx_fpa3_num_pools(), + 0, 0, 0, pool_info_name, NULL); + + cvmx_fpa3_aura_info[node] = (cvmx_fpa3_aurax_info_t *) + cvmx_bootmem_alloc_named_range_once( + sizeof(cvmx_fpa3_aura_info[0][0]) * + cvmx_fpa3_num_auras(), + 0, 0, 0, aura_info_name, NULL); + + //XXX add allocation error check + + /* Setup zero_pool on this node */ + cvmx_fpa3_reserve_pool(node, 0); + cvmx_fpa3_pool_info[node][0].buf_count = 0; + } else { + char pool_info_name[32] = "cvmx_fpa_pool"; + + /* Allocate the pinfo named block */ + cvmx_fpa1_pool_info = (cvmx_fpa1_pool_info_t *) + cvmx_bootmem_alloc_named_range_once( + sizeof(cvmx_fpa1_pool_info[0]) * + CVMX_FPA1_NUM_POOLS, + 0, 0, 0, pool_info_name, NULL); + + cvmx_fpa1_enable(); + } + + return 0; +} + +static void __memset_u64(u64 *ptr, u64 pattern, unsigned int words) +{ + while (words--) + *ptr++ = pattern; +} + +/** + * @INTERNAL + * Initialize pool pointer-storage memory + * + * Unlike legacy FPA, which used free buffers to store pointers that + * exceed on-chip memory, FPA3 requires a dedicated memory buffer for + * free pointer stack back-store. + * + * @param pool - pool to initialize + * @param mem_node - if memory should be allocated from a different node + * @param max_buffer_cnt - maximum block capacity of pool + * @param align - buffer alignment mode, + * current FPA_NATURAL_ALIGNMENT is supported + * @param buffer_sz - size of buffers in pool + */ +static int cvmx_fpa3_pool_stack_init(cvmx_fpa3_pool_t pool, + unsigned int mem_node, + unsigned int max_buffer_cnt, + enum cvmx_fpa3_pool_alignment_e align, + unsigned int buffer_sz) +{ + cvmx_fpa3_poolx_info_t *pinfo; + u64 stack_paddr; + void *mem_ptr; + unsigned int stack_memory_size; + cvmx_fpa_poolx_cfg_t pool_cfg; + cvmx_fpa_poolx_fpf_marks_t pool_fpf_marks; + + if (debug) + debug("%s: POOL %u:%u bufsz=%u maxbuf=%u\n", __func__, + pool.node, pool.lpool, buffer_sz, max_buffer_cnt); + + if (!__cvmx_fpa3_pool_valid(pool)) { + printf("ERROR: %s: POOL invalid\n", __func__); + return -1; + } + + pinfo = cvmx_fpa3_pool_info[pool.node]; + if (!pinfo) { + printf("ERROR: %s: FPA on node#%u is not initialized\n", + __func__, pool.node); + return -1; + } + pinfo += pool.lpool; + + /* Calculate stack size based on buffer count with one line to spare */ + stack_memory_size = (max_buffer_cnt * 128) / 29 + 128 + 127; + + /* Increase stack size by band guard */ + stack_memory_size += guard_band_size << 1; + + /* Align size to cache line */ + stack_memory_size = (stack_memory_size >> 7) << 7; + + /* Allocate internal stack */ + mem_ptr = cvmx_helper_mem_alloc(mem_node, stack_memory_size, + CVMX_CACHE_LINE_SIZE); + + if (debug) + debug("%s: stack_mem=%u ptr=%p\n", __func__, stack_memory_size, + mem_ptr); + + if (!mem_ptr) { + debug("ERROR: %sFailed to allocate stack for POOL %u:%u\n", + __func__, pool.node, pool.lpool); + return -1; + } + + /* Initialize guard bands */ + if (guard_band_size > 0) { + __memset_u64((u64 *)mem_ptr, magic_pattern, + guard_band_size >> 3); + __memset_u64((u64 *)((char *)mem_ptr + stack_memory_size - + guard_band_size), + magic_pattern, guard_band_size >> 3); + } + + pinfo->stack_paddr = cvmx_ptr_to_phys(mem_ptr); + pinfo->stack_psize = stack_memory_size; + + /* Calculate usable stack start */ + stack_paddr = cvmx_ptr_to_phys((char *)mem_ptr + guard_band_size); + + csr_wr_node(pool.node, CVMX_FPA_POOLX_STACK_BASE(pool.lpool), + stack_paddr); + csr_wr_node(pool.node, CVMX_FPA_POOLX_STACK_ADDR(pool.lpool), + stack_paddr); + + /* Calculate usable stack end - start of last cache line */ + stack_paddr = stack_paddr + stack_memory_size - (guard_band_size << 1); + + csr_wr_node(pool.node, CVMX_FPA_POOLX_STACK_END(pool.lpool), + stack_paddr); + + if (debug) + debug("%s: Stack paddr %#llx - %#llx\n", __func__, + CAST_ULL(csr_rd_node(pool.node, CVMX_FPA_POOLX_STACK_BASE( + pool.lpool))), + CAST_ULL(csr_rd_node(pool.node, CVMX_FPA_POOLX_STACK_END( + pool.lpool)))); + + /* Setup buffer size for this pool until it is shutdown */ + pinfo->buf_size = buffer_sz; + + pool_cfg.u64 = 0; + pool_cfg.cn78xx.buf_size = buffer_sz >> 7; + pool_cfg.cn78xx.l_type = 0x2; + pool_cfg.cn78xx.ena = 0; + if (align == FPA_NATURAL_ALIGNMENT) + pool_cfg.cn78xx.nat_align = 1; + + /* FPA-26117, FPA-22443 */ + pool_fpf_marks.u64 = + csr_rd_node(pool.node, CVMX_FPA_POOLX_FPF_MARKS(pool.lpool)); + pool_fpf_marks.s.fpf_rd = 0x80; + csr_wr_node(pool.node, CVMX_FPA_POOLX_FPF_MARKS(pool.lpool), + pool_fpf_marks.u64); + + csr_wr_node(pool.node, CVMX_FPA_POOLX_CFG(pool.lpool), pool_cfg.u64); + pool_cfg.cn78xx.ena = 1; + csr_wr_node(pool.node, CVMX_FPA_POOLX_CFG(pool.lpool), pool_cfg.u64); + + /* Pool is now ready to be filled up */ + return 0; +} + +/** + * Create an FPA POOL and fill it up with buffers + * + * @param node is the node number for the pool and memory location + * @param desired_pool is the local pool number desired + * or -1 for first available + * @param name is the symbolic name to assign the POOL + * @param block_size is the size of all buffers held in this POOL + * @param num_blocks is the number of free buffers to fill into the POOL + * @param buffer is an optionally caller-supplied memory for the buffers + * or NULL to cause the buffer memory to be allocated automatically. + * @return the POOL handle + * + * Note: if the buffer memory is supplied by caller, the application + * will be responsable to free this memory. + * + * Only supported on CN78XX. + */ +cvmx_fpa3_pool_t cvmx_fpa3_setup_fill_pool(int node, int desired_pool, + const char *name, + unsigned int block_size, + unsigned int num_blocks, + void *buffer) +{ + cvmx_fpa3_pool_t pool; + unsigned int mem_node; + int rc; + + if (node < 0) + node = cvmx_get_node_num(); + + if (debug) + debug("%s: desired pool=%d bufsize=%u cnt=%u '%s'\n", __func__, + desired_pool, block_size, num_blocks, name); + + /* Use memory from the node local to the AURA/POOL */ + mem_node = node; + + if (num_blocks == 0 || num_blocks > 1 << 30) { + printf("ERROR: %s: invalid block count %u\n", __func__, + num_blocks); + return CVMX_FPA3_INVALID_POOL; + } + + /* + * Check for block size validity: + * With user-supplied buffer, can't increase block size, + * so make sure it is at least 128, and is aligned to 128 + * For all cases make sure it is not too big + */ + if ((buffer && (block_size < CVMX_CACHE_LINE_SIZE || + (block_size & (CVMX_CACHE_LINE_SIZE - 1)))) || + (block_size > (1 << 17))) { + printf("ERROR: %s: invalid block size %u\n", __func__, + block_size); + return CVMX_FPA3_INVALID_POOL; + } + + if (block_size < CVMX_CACHE_LINE_SIZE) + block_size = CVMX_CACHE_LINE_SIZE; + + /* Reserve POOL */ + pool = cvmx_fpa3_reserve_pool(node, desired_pool); + + if (!__cvmx_fpa3_pool_valid(pool)) { + printf("ERROR: %s: POOL %u:%d not available\n", __func__, node, + desired_pool); + return CVMX_FPA3_INVALID_POOL; + } + + /* Initialize POOL with stack storage */ + rc = cvmx_fpa3_pool_stack_init(pool, mem_node, num_blocks, + FPA_NATURAL_ALIGNMENT, block_size); + if (rc < 0) { + printf("ERROR: %s: POOL %u:%u stack setup failed\n", __func__, + pool.node, pool.lpool); + cvmx_fpa3_release_pool(pool); + return CVMX_FPA3_INVALID_POOL; + } + + /* Populate the POOL with buffers */ + rc = cvmx_fpa3_pool_populate(pool, num_blocks, block_size, buffer, + mem_node); + if (rc < 0) { + printf("ERROR: %s: POOL %u:%u memory fill failed\n", __func__, + pool.node, pool.lpool); + cvmx_fpa3_release_pool(pool); + return CVMX_FPA3_INVALID_POOL; + } + + cvmx_fpa3_set_pool_name(pool, name); + + return pool; +} + +/** + * Attach an AURA to an existing POOL + * + * @param pool is the handle of the POOL to be attached + * @param desired_aura is the number of the AURA resired + * or -1 for the AURA to be automatically assigned + * @param name is a symbolic name for the new AURA + * @param block_size is the size of all buffers that will be handed + * out by this AURA + * @param num_blocks is the maximum number of buffers that can be + * handed out by this AURA, and can not exceed the number + * of buffers filled into the attached POOL + * @return the AURA handle + * + * Only supported on CN78XX. + */ +cvmx_fpa3_gaura_t cvmx_fpa3_set_aura_for_pool(cvmx_fpa3_pool_t pool, + int desired_aura, + const char *name, + unsigned int block_size, + unsigned int num_blocks) +{ + cvmx_fpa3_gaura_t aura; + cvmx_fpa_poolx_available_t avail_reg; + const char *emsg; + int rc; + + if (debug) + debug("%s: aura=%d bufsize=%u cnt=%u '%s'\n", __func__, + desired_aura, block_size, num_blocks, name); + + if (!__cvmx_fpa3_pool_valid(pool)) { + printf("ERROR: %s: POOL argument invalid\n", __func__); + return CVMX_FPA3_INVALID_GAURA; + } + + /* Verify the AURA buffer count limit is not above POOL buffer count */ + avail_reg.u64 = + csr_rd_node(pool.node, CVMX_FPA_POOLX_AVAILABLE(pool.lpool)); + if (avail_reg.cn78xx.count < num_blocks) { + printf("WARNING: %s: AURA %u:%u buffer count limit %u reduced to POOL available count %u\n", + __func__, aura.node, aura.laura, num_blocks, + (unsigned int)avail_reg.cn78xx.count); + num_blocks = avail_reg.cn78xx.count; + } + + /* Reserve an AURA number, follow desired number */ + aura = cvmx_fpa3_reserve_aura(pool.node, desired_aura); + + if (!__cvmx_fpa3_aura_valid(aura)) { + printf("ERROR: %s: AURA %u:%d not available\n", __func__, + pool.node, desired_aura); + return CVMX_FPA3_INVALID_GAURA; + } + + /* Initialize AURA attached to the above POOL */ + rc = cvmx_fpa3_aura_cfg(aura, pool, num_blocks, num_blocks + 1, 0); + if (rc < 0) { + emsg = "AURA configuration"; + goto _fail; + } + + cvmx_fpa3_set_aura_name(aura, name); + + return aura; + +_fail: + printf("ERROR: %s: %s\n", __func__, emsg); + cvmx_fpa3_release_aura(aura); + return CVMX_FPA3_INVALID_GAURA; +} + +/** + * Create a combination of an AURA and a POOL + * + * @param node is the node number for the pool and memory location + * @param desired_aura is the number of the AURA resired + * or -1 for the AURA to be automatically assigned + * @param name is a symbolic name for the new AURA + * @param block_size is the size of all buffers that will be handed + * out by this AURA + * @param num_blocks is the maximum number of buffers that can be + * handed out by this AURA, and can not exceed the number + * of buffers filled into the attached POOL + * @param buffer is an optionally caller-supplied memory for the buffers + * or NULL to cause the buffer memory to be allocated automatically. + * + * @return the AURA handle + * + * Note: if the buffer memory is supplied by caller, the application + * will be responsable to free this memory. + * The POOL number is always automatically assigned. + * + * Only supported on CN78XX. + */ +cvmx_fpa3_gaura_t cvmx_fpa3_setup_aura_and_pool(int node, int desired_aura, + const char *name, void *buffer, + unsigned int block_size, + unsigned int num_blocks) +{ + cvmx_fpa3_gaura_t aura = CVMX_FPA3_INVALID_GAURA; + cvmx_fpa3_pool_t pool = CVMX_FPA3_INVALID_POOL; + const char *emsg = ""; + unsigned int mem_node; + int rc; + + if (debug) + debug("%s: aura=%d size=%u cnt=%u '%s'\n", __func__, + desired_aura, block_size, num_blocks, name); + + if (node < 0) + node = cvmx_get_node_num(); + + if (num_blocks == 0 || num_blocks > 1 << 30) { + printf("ERROR: %s: invalid block count %u\n", __func__, + num_blocks); + return CVMX_FPA3_INVALID_GAURA; + } + + /* Use memory from the node local to the AURA/POOL */ + mem_node = node; + + /* Reserve an AURA number, follow desired number */ + aura = cvmx_fpa3_reserve_aura(node, desired_aura); + + if (!__cvmx_fpa3_aura_valid(aura)) { + emsg = "AURA not available"; + goto _fail; + } + + /* Reserve POOL dynamically to underpin this AURA */ + pool = cvmx_fpa3_reserve_pool(node, -1); + + if (!__cvmx_fpa3_pool_valid(pool)) { + emsg = "POOL not available"; + goto _fail; + } + + /* + * Check for block size validity: + * With user-supplied buffer, can't increase block size, + * so make sure it is at least 128, and is aligned to 128 + * For all cases make sure it is not too big + */ + if ((buffer && (block_size < CVMX_CACHE_LINE_SIZE || + (block_size & (CVMX_CACHE_LINE_SIZE - 1)))) || + block_size > (1 << 17)) { + printf("ERROR: %s: invalid block size %u\n", __func__, + block_size); + emsg = "invalid block size"; + goto _fail; + } + + if (block_size < CVMX_CACHE_LINE_SIZE) + block_size = CVMX_CACHE_LINE_SIZE; + + /* Initialize POOL with stack storage */ + rc = cvmx_fpa3_pool_stack_init(pool, mem_node, num_blocks, + FPA_NATURAL_ALIGNMENT, block_size); + if (rc < 0) { + emsg = "POOL Stack setup"; + goto _fail; + } + + /* Populate the AURA/POOL with buffers */ + rc = cvmx_fpa3_pool_populate(pool, num_blocks, block_size, buffer, + mem_node); + if (rc < 0) { + emsg = "POOL buffer memory"; + goto _fail; + } + + /* Initialize AURA attached to the above POOL */ + rc = cvmx_fpa3_aura_cfg(aura, pool, num_blocks, num_blocks + 1, 0); + if (rc < 0) { + emsg = "AURA configuration"; + goto _fail; + } + + cvmx_fpa3_set_aura_name(aura, name); + cvmx_fpa3_set_pool_name(pool, name); + + if (debug) + debug("%s: AURA %u:%u ready, avail=%lld\n", __func__, aura.node, + aura.laura, cvmx_fpa3_get_available(aura)); + + return aura; + +_fail: + printf("ERROR: %s: Failed in %s\n", __func__, emsg); + /* These will silently fail if POOL/AURA is not valid */ + cvmx_fpa3_release_aura(aura); + cvmx_fpa3_release_pool(pool); + return CVMX_FPA3_INVALID_GAURA; +} + +/** + * Setup a legacy FPA pool + * + * @param desired_pool is the POOL number desired or -1 for automatic + * assignment + * @param name is the symbolic POOL name + * @param block_size is the size of all buffers held in this POOL + * @param num_blocks is the number of free buffers to fill into the POOL + * @param buffer is an optionally caller-supplied memory for the buffers + * or NULL to cause the buffer memory to be allocated automatically. + * @return pool number or -1 on error. + * + * Note: if the buffer memory is supplied by caller, the application + * will be responsable to free this memory. + */ +int cvmx_fpa1_setup_pool(int desired_pool, const char *name, void *buffer, + unsigned int block_size, unsigned int num_blocks) +{ + cvmx_fpa1_pool_t pool = CVMX_FPA1_INVALID_POOL; + int rc; + + if (debug) + debug("%s: desired pool %d, name '%s', mem %p size %u count %u\n", + __func__, desired_pool, name, buffer, block_size, + num_blocks); + + /* Reserve desired pool or get one dynamically */ + pool = cvmx_fpa1_reserve_pool(desired_pool); + + /* Validate reserved pool, if successful */ + if (pool < 0 || pool >= cvmx_fpa_get_max_pools()) { + /* global resources would have printed an error message here */ + return CVMX_FPA1_INVALID_POOL; + } + + /* Initialize the pool */ + rc = cvmx_fpa1_pool_init(pool, num_blocks, block_size, buffer); + if (rc < 0) { + printf("ERROR: %s: failed pool %u init\n", __func__, pool); + cvmx_fpa1_release_pool(pool); + return CVMX_FPA1_INVALID_POOL; + } + + rc = cvmx_fpa1_fill_pool(pool, num_blocks, buffer); + if (rc < 0) { + printf("ERROR: %s: failed pool %u memory\n", __func__, pool); + cvmx_fpa1_release_pool(pool); + return CVMX_FPA1_INVALID_POOL; + } + + if (debug) + debug("%s: pool %d filled up\b", __func__, pool); + + cvmx_fpa_set_name(pool, name); + return pool; +} + +/** + * Setup an FPA pool with buffers + * + * @param pool is the POOL number desired or -1 for automatic assignment + * @param name is the symbolic POOL name + * @param buffer is an optionally caller-supplied memory for the buffers + * or NULL to cause the buffer memory to be allocated automatically. + * @param block_size is the size of all buffers held in this POOL + * @param num_blocks is the number of free buffers to fill into the POOL + * @param buffer is an optionally caller-supplied memory for the buffers + * or NULL to cause the buffer memory to be allocated automatically. + * + * @return pool number or -1 on error. + * + * Note: if the buffer memory is supplied by caller, the application + * will be responsable to free this memory. + * This function will work with CN78XX models in backward-compatible mode + */ +int cvmx_fpa_setup_pool(int pool, const char *name, void *buffer, + u64 block_size, u64 num_blocks) +{ + if (octeon_has_feature(OCTEON_FEATURE_FPA3)) { + cvmx_fpa3_gaura_t aura; + + aura = cvmx_fpa3_setup_aura_and_pool(-1, pool, name, buffer, + block_size, num_blocks); + if (!__cvmx_fpa3_aura_valid(aura)) + return -1; + if (aura.laura >= CVMX_FPA1_NUM_POOLS && pool >= 0) + printf("WARNING: %s: AURA %u is out of range for backward-compatible operation\n", + __func__, aura.laura); + return aura.laura; + } else { + return cvmx_fpa1_setup_pool(pool, name, buffer, block_size, + num_blocks); + } +} diff --git a/arch/mips/mach-octeon/cvmx-global-resources.c b/arch/mips/mach-octeon/cvmx-global-resources.c new file mode 100644 index 0000000..a3da17c --- /dev/null +++ b/arch/mips/mach-octeon/cvmx-global-resources.c @@ -0,0 +1,517 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018-2022 Marvell International Ltd. + * + */ + +#include <log.h> +#include <time.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-range.h> + +#include <mach/cvmx-global-resources.h> +#include <mach/cvmx-bootmem.h> + +#include <mach/cvmx-pki.h> +#include <mach/cvmx-helper.h> +#include <mach/cvmx-helper-board.h> +#include <mach/cvmx-helper-cfg.h> + +#define CVMX_MAX_GLOBAL_RESOURCES 128 +#define CVMX_RESOURCES_ENTRIES_SIZE \ + (sizeof(struct cvmx_global_resource_entry) * CVMX_MAX_GLOBAL_RESOURCES) + +/** + * This macro returns a member of the data + * structure. The argument "field" is the member name of the + * structure to read. The return type is a u64. + */ +#define CVMX_GLOBAL_RESOURCES_GET_FIELD(field) \ + __cvmx_struct_get_unsigned_field( \ + __cvmx_global_resources_addr, \ + offsetof(struct cvmx_global_resources, field), \ + SIZEOF_FIELD(struct cvmx_global_resources, field)) + +/** + * This macro writes a member of the struct cvmx_global_resourcest + * structure. The argument "field" is the member name of the + * struct cvmx_global_resources to write. + */ +#define CVMX_GLOBAL_RESOURCES_SET_FIELD(field, value) \ + __cvmx_struct_set_unsigned_field( \ + __cvmx_global_resources_addr, \ + offsetof(struct cvmx_global_resources, field), \ + SIZEOF_FIELD(struct cvmx_global_resources, field), value) + +/** + * This macro returns a member of the struct cvmx_global_resource_entry. + * The argument "field" is the member name of this structure. + * the return type is a u64. The "addr" parameter is the physical + * address of the structure. + */ +#define CVMX_RESOURCE_ENTRY_GET_FIELD(addr, field) \ + __cvmx_struct_get_unsigned_field( \ + addr, offsetof(struct cvmx_global_resource_entry, field), \ + SIZEOF_FIELD(struct cvmx_global_resource_entry, field)) + +/** + * This macro writes a member of the struct cvmx_global_resource_entry + * structure. The argument "field" is the member name of the + * struct cvmx_global_resource_entry to write. The "addr" parameter + * is the physical address of the structure. + */ +#define CVMX_RESOURCE_ENTRY_SET_FIELD(addr, field, value) \ + __cvmx_struct_set_unsigned_field( \ + addr, offsetof(struct cvmx_global_resource_entry, field), \ + SIZEOF_FIELD(struct cvmx_global_resource_entry, field), value) + +#define CVMX_GET_RESOURCE_ENTRY(count) \ + (__cvmx_global_resources_addr + \ + offsetof(struct cvmx_global_resources, resource_entry) + \ + (count * sizeof(struct cvmx_global_resource_entry))) + +#define CVMX_RESOURCE_TAG_SET_FIELD(addr, field, value) \ + __cvmx_struct_set_unsigned_field( \ + addr, offsetof(struct global_resource_tag, field), \ + SIZEOF_FIELD(struct global_resource_tag, field), value) + +#define CVMX_RESOURCE_TAG_GET_FIELD(addr, field) \ + __cvmx_struct_get_unsigned_field( \ + addr, offsetof(struct global_resource_tag, field), \ + SIZEOF_FIELD(struct global_resource_tag, field)) + +#define MAX_RESOURCE_TAG_LEN 16 +#define CVMX_GLOBAL_RESOURCE_NO_LOCKING (1) + +struct cvmx_global_resource_entry { + struct global_resource_tag tag; + u64 phys_addr; + u64 size; +}; + +struct cvmx_global_resources { + u32 pad; + u32 rlock; + u64 entry_cnt; + struct cvmx_global_resource_entry resource_entry[]; +}; + +/* Not the right place, putting it here for now */ +u64 cvmx_app_id; + +/* + * Global named memory can be accessed anywhere even in 32-bit mode + */ +static u64 __cvmx_global_resources_addr; + +/** + * This macro returns the size of a member of a structure. + */ +#define SIZEOF_FIELD(s, field) sizeof(((s *)NULL)->field) + +/** + * This function is the implementation of the get macros defined + * for individual structure members. The argument are generated + * by the macros inorder to read only the needed memory. + * + * @param base 64bit physical address of the complete structure + * @param offset from the beginning of the structure to the member being + * accessed. + * @param size Size of the structure member. + * + * @return Value of the structure member promoted into a u64. + */ +static inline u64 __cvmx_struct_get_unsigned_field(u64 base, int offset, + int size) +{ + base = (1ull << 63) | (base + offset); + switch (size) { + case 4: + return cvmx_read64_uint32(base); + case 8: + return cvmx_read64_uint64(base); + default: + return 0; + } +} + +/** + * This function is the implementation of the set macros defined + * for individual structure members. The argument are generated + * by the macros in order to write only the needed memory. + * + * @param base 64bit physical address of the complete structure + * @param offset from the beginning of the structure to the member being + * accessed. + * @param size Size of the structure member. + * @param value Value to write into the structure + */ +static inline void __cvmx_struct_set_unsigned_field(u64 base, int offset, + int size, u64 value) +{ + base = (1ull << 63) | (base + offset); + switch (size) { + case 4: + cvmx_write64_uint32(base, value); + break; + case 8: + cvmx_write64_uint64(base, value); + break; + default: + break; + } +} + +/* Get the global resource lock. */ +static inline void __cvmx_global_resource_lock(void) +{ + u64 lock_addr = + (1ull << 63) | (__cvmx_global_resources_addr + + offsetof(struct cvmx_global_resources, rlock)); + unsigned int tmp; + + __asm__ __volatile__(".set noreorder\n" + "1: ll %[tmp], 0(%[addr])\n" + " bnez %[tmp], 1b\n" + " li %[tmp], 1\n" + " sc %[tmp], 0(%[addr])\n" + " beqz %[tmp], 1b\n" + " nop\n" + ".set reorder\n" + : [tmp] "=&r"(tmp) + : [addr] "r"(lock_addr) + : "memory"); +} + +/* Release the global resource lock. */ +static inline void __cvmx_global_resource_unlock(void) +{ + u64 lock_addr = + (1ull << 63) | (__cvmx_global_resources_addr + + offsetof(struct cvmx_global_resources, rlock)); + CVMX_SYNCW; + __asm__ __volatile__("sw $0, 0(%[addr])\n" + : + : [addr] "r"(lock_addr) + : "memory"); + CVMX_SYNCW; +} + +static u64 __cvmx_alloc_bootmem_for_global_resources(int sz) +{ + void *tmp; + + tmp = cvmx_bootmem_alloc_range(sz, CVMX_CACHE_LINE_SIZE, 0, 0); + return cvmx_ptr_to_phys(tmp); +} + +static inline void __cvmx_get_tagname(struct global_resource_tag *rtag, + char *tagname) +{ + int i, j, k; + + j = 0; + k = 8; + for (i = 7; i >= 0; i--, j++, k++) { + tagname[j] = (rtag->lo >> (i * 8)) & 0xff; + tagname[k] = (rtag->hi >> (i * 8)) & 0xff; + } +} + +static u64 __cvmx_global_resources_init(void) +{ + struct cvmx_bootmem_named_block_desc *block_desc; + int sz = sizeof(struct cvmx_global_resources) + + CVMX_RESOURCES_ENTRIES_SIZE; + s64 tmp_phys; + int count = 0; + u64 base = 0; + + cvmx_bootmem_lock(); + + block_desc = (struct cvmx_bootmem_named_block_desc *) + __cvmx_bootmem_find_named_block_flags( + CVMX_GLOBAL_RESOURCES_DATA_NAME, + CVMX_BOOTMEM_FLAG_NO_LOCKING); + if (!block_desc) { + debug("%s: allocating global resources\n", __func__); + + tmp_phys = cvmx_bootmem_phy_named_block_alloc( + sz, 0, 0, CVMX_CACHE_LINE_SIZE, + CVMX_GLOBAL_RESOURCES_DATA_NAME, + CVMX_BOOTMEM_FLAG_NO_LOCKING); + if (tmp_phys < 0) { + cvmx_printf( + "ERROR: %s: failed to allocate global resource name block. sz=%d\n", + __func__, sz); + goto end; + } + __cvmx_global_resources_addr = (u64)tmp_phys; + + debug("%s: memset global resources %llu\n", __func__, + CAST_ULL(__cvmx_global_resources_addr)); + + base = (1ull << 63) | __cvmx_global_resources_addr; + for (count = 0; count < (sz / 8); count++) { + cvmx_write64_uint64(base, 0); + base += 8; + } + } else { + debug("%s:found global resource\n", __func__); + __cvmx_global_resources_addr = block_desc->base_addr; + } +end: + cvmx_bootmem_unlock(); + debug("__cvmx_global_resources_addr=%llu sz=%d\n", + CAST_ULL(__cvmx_global_resources_addr), sz); + return __cvmx_global_resources_addr; +} + +u64 cvmx_get_global_resource(struct global_resource_tag tag, int no_lock) +{ + u64 entry_cnt = 0; + u64 resource_entry_addr = 0; + int count = 0; + u64 rphys_addr = 0; + u64 tag_lo = 0, tag_hi = 0; + + if (__cvmx_global_resources_addr == 0) + __cvmx_global_resources_init(); + if (!no_lock) + __cvmx_global_resource_lock(); + + entry_cnt = CVMX_GLOBAL_RESOURCES_GET_FIELD(entry_cnt); + while (entry_cnt > 0) { + resource_entry_addr = CVMX_GET_RESOURCE_ENTRY(count); + tag_lo = CVMX_RESOURCE_TAG_GET_FIELD(resource_entry_addr, lo); + tag_hi = CVMX_RESOURCE_TAG_GET_FIELD(resource_entry_addr, hi); + + if (tag_lo == tag.lo && tag_hi == tag.hi) { + debug("%s: Found global resource entry\n", __func__); + break; + } + entry_cnt--; + count++; + } + + if (entry_cnt == 0) { + debug("%s: no matching global resource entry found\n", + __func__); + if (!no_lock) + __cvmx_global_resource_unlock(); + return 0; + } + rphys_addr = + CVMX_RESOURCE_ENTRY_GET_FIELD(resource_entry_addr, phys_addr); + if (!no_lock) + __cvmx_global_resource_unlock(); + + return rphys_addr; +} + +u64 cvmx_create_global_resource(struct global_resource_tag tag, u64 size, + int no_lock, int *_new_) +{ + u64 entry_count = 0; + u64 resource_entry_addr = 0; + u64 phys_addr; + + if (__cvmx_global_resources_addr == 0) + __cvmx_global_resources_init(); + + if (!no_lock) + __cvmx_global_resource_lock(); + + phys_addr = + cvmx_get_global_resource(tag, CVMX_GLOBAL_RESOURCE_NO_LOCKING); + if (phys_addr != 0) { + /* we already have the resource, return it */ + *_new_ = 0; + goto end; + } + + *_new_ = 1; + entry_count = CVMX_GLOBAL_RESOURCES_GET_FIELD(entry_cnt); + if (entry_count >= CVMX_MAX_GLOBAL_RESOURCES) { + char tagname[MAX_RESOURCE_TAG_LEN + 1]; + + __cvmx_get_tagname(&tag, tagname); + cvmx_printf( + "ERROR: %s: reached global resources limit for %s\n", + __func__, tagname); + phys_addr = 0; + goto end; + } + + /* Allocate bootmem for the resource*/ + phys_addr = __cvmx_alloc_bootmem_for_global_resources(size); + if (!phys_addr) { + char tagname[MAX_RESOURCE_TAG_LEN + 1]; + + __cvmx_get_tagname(&tag, tagname); + debug("ERROR: %s: out of memory %s, size=%d\n", __func__, + tagname, (int)size); + goto end; + } + + resource_entry_addr = CVMX_GET_RESOURCE_ENTRY(entry_count); + CVMX_RESOURCE_ENTRY_SET_FIELD(resource_entry_addr, phys_addr, + phys_addr); + CVMX_RESOURCE_ENTRY_SET_FIELD(resource_entry_addr, size, size); + CVMX_RESOURCE_TAG_SET_FIELD(resource_entry_addr, lo, tag.lo); + CVMX_RESOURCE_TAG_SET_FIELD(resource_entry_addr, hi, tag.hi); + /* update entry_cnt */ + entry_count += 1; + CVMX_GLOBAL_RESOURCES_SET_FIELD(entry_cnt, entry_count); + +end: + if (!no_lock) + __cvmx_global_resource_unlock(); + + return phys_addr; +} + +int cvmx_create_global_resource_range(struct global_resource_tag tag, + int nelements) +{ + int sz = cvmx_range_memory_size(nelements); + int _new_; + u64 addr; + int rv = 0; + + if (__cvmx_global_resources_addr == 0) + __cvmx_global_resources_init(); + + __cvmx_global_resource_lock(); + addr = cvmx_create_global_resource(tag, sz, 1, &_new_); + if (!addr) { + __cvmx_global_resource_unlock(); + return -1; + } + if (_new_) + rv = cvmx_range_init(addr, nelements); + __cvmx_global_resource_unlock(); + return rv; +} + +int cvmx_allocate_global_resource_range(struct global_resource_tag tag, + u64 owner, int nelements, int alignment) +{ + u64 addr = cvmx_get_global_resource(tag, 1); + int base; + + if (addr == 0) { + char tagname[256]; + + __cvmx_get_tagname(&tag, tagname); + cvmx_printf("ERROR: %s: cannot find resource %s\n", __func__, + tagname); + return -1; + } + __cvmx_global_resource_lock(); + base = cvmx_range_alloc(addr, owner, nelements, alignment); + __cvmx_global_resource_unlock(); + return base; +} + +int cvmx_resource_alloc_reverse(struct global_resource_tag tag, u64 owner) +{ + u64 addr = cvmx_get_global_resource(tag, 1); + int rv; + + if (addr == 0) { + char tagname[256]; + + __cvmx_get_tagname(&tag, tagname); + debug("ERROR: cannot find resource %s\n", tagname); + return -1; + } + __cvmx_global_resource_lock(); + rv = cvmx_range_alloc_ordered(addr, owner, 1, 1, 1); + __cvmx_global_resource_unlock(); + return rv; +} + +int cvmx_reserve_global_resource_range(struct global_resource_tag tag, + u64 owner, int base, int nelements) +{ + u64 addr = cvmx_get_global_resource(tag, 1); + int start; + + __cvmx_global_resource_lock(); + start = cvmx_range_reserve(addr, owner, base, nelements); + __cvmx_global_resource_unlock(); + return start; +} + +int cvmx_free_global_resource_range_with_base(struct global_resource_tag tag, + int base, int nelements) +{ + u64 addr = cvmx_get_global_resource(tag, 1); + int rv; + + /* Resource was not created, nothing to release */ + if (addr == 0) + return 0; + + __cvmx_global_resource_lock(); + rv = cvmx_range_free_with_base(addr, base, nelements); + __cvmx_global_resource_unlock(); + return rv; +} + +int cvmx_free_global_resource_range_multiple(struct global_resource_tag tag, + int bases[], int nelements) +{ + u64 addr = cvmx_get_global_resource(tag, 1); + int rv; + + /* Resource was not created, nothing to release */ + if (addr == 0) + return 0; + + __cvmx_global_resource_lock(); + rv = cvmx_range_free_mutiple(addr, bases, nelements); + __cvmx_global_resource_unlock(); + return rv; +} + +void cvmx_app_id_init(void *bootmem) +{ + u64 *p = (u64 *)bootmem; + + *p = 0; +} + +u64 cvmx_allocate_app_id(void) +{ + u64 *vptr; + + vptr = (u64 *)cvmx_bootmem_alloc_named_range_once(sizeof(cvmx_app_id), + 0, 1 << 31, 128, + "cvmx_app_id", + cvmx_app_id_init); + + cvmx_app_id = __atomic_add_fetch(vptr, 1, __ATOMIC_SEQ_CST); + + debug("CVMX_APP_ID = %lx\n", (unsigned long)cvmx_app_id); + return cvmx_app_id; +} + +u64 cvmx_get_app_id(void) +{ + if (cvmx_app_id == 0) + cvmx_allocate_app_id(); + return cvmx_app_id; +} diff --git a/arch/mips/mach-octeon/cvmx-helper-agl.c b/arch/mips/mach-octeon/cvmx-helper-agl.c new file mode 100644 index 0000000..7eb99ac --- /dev/null +++ b/arch/mips/mach-octeon/cvmx-helper-agl.c @@ -0,0 +1,231 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018-2022 Marvell International Ltd. + * + * Functions for AGL (RGMII) initialization, configuration, + * and monitoring. + */ + +#include <log.h> +#include <time.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-helper.h> +#include <mach/cvmx-helper-cfg.h> + +#include <mach/cvmx-agl.h> +#include <mach/cvmx-pki.h> + +#include <mach/cvmx-agl-defs.h> +#include <mach/cvmx-pko-defs.h> + +int __cvmx_helper_agl_enumerate(int xiface) +{ + if (OCTEON_IS_MODEL(OCTEON_CN70XX)) { + union cvmx_agl_prtx_ctl agl_prtx_ctl; + + agl_prtx_ctl.u64 = csr_rd(CVMX_AGL_PRTX_CTL(0)); + if (agl_prtx_ctl.s.mode == 0) /* RGMII */ + return 1; + } + return 0; +} + +/** + * @INTERNAL + * Convert interface to port to assess CSRs. + * + * @param xiface Interface to probe + * @return The port corresponding to the interface + */ +int cvmx_helper_agl_get_port(int xiface) +{ + struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); + + if (OCTEON_IS_MODEL(OCTEON_CN70XX)) + return xi.interface - 4; + return -1; +} + +/** + * @INTERNAL + * Probe a RGMII interface and determine the number of ports + * connected to it. The RGMII interface should still be down + * after this call. + * + * @param interface to probe + * + * @return Number of ports on the interface. Zero to disable. + */ +int __cvmx_helper_agl_probe(int interface) +{ + int port = cvmx_helper_agl_get_port(interface); + union cvmx_agl_gmx_bist gmx_bist; + union cvmx_agl_gmx_prtx_cfg gmx_prtx_cfg; + union cvmx_agl_prtx_ctl agl_prtx_ctl; + int result; + + result = __cvmx_helper_agl_enumerate(interface); + if (result == 0) + return 0; + + /* Check BIST status */ + gmx_bist.u64 = csr_rd(CVMX_AGL_GMX_BIST); + if (gmx_bist.u64) + printf("Management port AGL failed BIST (0x%016llx) on AGL%d\n", + CAST64(gmx_bist.u64), port); + + /* Disable the external input/output */ + gmx_prtx_cfg.u64 = csr_rd(CVMX_AGL_GMX_PRTX_CFG(port)); + gmx_prtx_cfg.s.en = 0; + csr_wr(CVMX_AGL_GMX_PRTX_CFG(port), gmx_prtx_cfg.u64); + + /* Set the rgx_ref_clk MUX with AGL_PRTx_CTL[REFCLK_SEL]. Default value + * is 0 (RGMII REFCLK). Recommended to use RGMII RXC(1) or sclk/4 (2) + * to save cost. + */ + + agl_prtx_ctl.u64 = csr_rd(CVMX_AGL_PRTX_CTL(port)); + agl_prtx_ctl.s.clkrst = 0; + agl_prtx_ctl.s.dllrst = 0; + agl_prtx_ctl.s.clktx_byp = 0; + + if (OCTEON_IS_MODEL(OCTEON_CN70XX)) { + bool tx_enable_bypass; + int tx_delay; + + agl_prtx_ctl.s.refclk_sel = + cvmx_helper_get_agl_refclk_sel(interface, port); + agl_prtx_ctl.s.clkrx_set = + cvmx_helper_get_agl_rx_clock_skew(interface, port); + agl_prtx_ctl.s.clkrx_byp = + cvmx_helper_get_agl_rx_clock_delay_bypass(interface, + port); + cvmx_helper_cfg_get_rgmii_tx_clk_delay( + interface, port, &tx_enable_bypass, &tx_delay); + agl_prtx_ctl.s.clktx_byp = tx_enable_bypass; + agl_prtx_ctl.s.clktx_set = tx_delay; + } + csr_wr(CVMX_AGL_PRTX_CTL(port), agl_prtx_ctl.u64); + /* Force write out before wait */ + csr_rd(CVMX_AGL_PRTX_CTL(port)); + udelay(500); + + /* Enable the componsation controller */ + agl_prtx_ctl.u64 = csr_rd(CVMX_AGL_PRTX_CTL(port)); + agl_prtx_ctl.s.drv_byp = 0; + csr_wr(CVMX_AGL_PRTX_CTL(port), agl_prtx_ctl.u64); + /* Force write out before wait */ + csr_rd(CVMX_AGL_PRTX_CTL(port)); + + if (!OCTEON_IS_OCTEON3()) { + /* Enable the interface */ + agl_prtx_ctl.u64 = csr_rd(CVMX_AGL_PRTX_CTL(port)); + agl_prtx_ctl.s.enable = 1; + csr_wr(CVMX_AGL_PRTX_CTL(port), agl_prtx_ctl.u64); + /* Read the value back to force the previous write */ + agl_prtx_ctl.u64 = csr_rd(CVMX_AGL_PRTX_CTL(port)); + } + + /* Enable the compensation controller */ + agl_prtx_ctl.u64 = csr_rd(CVMX_AGL_PRTX_CTL(port)); + agl_prtx_ctl.s.comp = 1; + csr_wr(CVMX_AGL_PRTX_CTL(port), agl_prtx_ctl.u64); + /* Force write out before wait */ + csr_rd(CVMX_AGL_PRTX_CTL(port)); + + /* for componsation state to lock. */ + udelay(500); + + return result; +} + +/** + * @INTERNAL + * Bringup and enable a RGMII interface. After this call packet + * I/O should be fully functional. This is called with IPD + * enabled but PKO disabled. + * + * @param interface to bring up + * + * @return Zero on success, negative on failure + */ +int __cvmx_helper_agl_enable(int interface) +{ + int port = cvmx_helper_agl_get_port(interface); + int ipd_port = cvmx_helper_get_ipd_port(interface, port); + union cvmx_pko_mem_port_ptrs pko_mem_port_ptrs; + union cvmx_pko_reg_read_idx read_idx; + int do_link_set = 1; + int i; + + /* Setup PKO for AGL interface. Back pressure is not supported. */ + pko_mem_port_ptrs.u64 = 0; + read_idx.u64 = 0; + read_idx.s.inc = 1; + csr_wr(CVMX_PKO_REG_READ_IDX, read_idx.u64); + + for (i = 0; i < 40; i++) { + pko_mem_port_ptrs.u64 = csr_rd(CVMX_PKO_MEM_PORT_PTRS); + if (pko_mem_port_ptrs.s.pid == 24) { + pko_mem_port_ptrs.s.eid = 10; + pko_mem_port_ptrs.s.bp_port = 40; + csr_wr(CVMX_PKO_MEM_PORT_PTRS, pko_mem_port_ptrs.u64); + break; + } + } + + cvmx_agl_enable(port); + if (do_link_set) + cvmx_agl_link_set(port, cvmx_agl_link_get(ipd_port)); + + return 0; +} + +/** + * @INTERNAL + * Return the link state of an IPD/PKO port as returned by + * auto negotiation. The result of this function may not match + * Octeon's link config if auto negotiation has changed since + * the last call to cvmx_helper_link_set(). + * + * @param ipd_port IPD/PKO port to query + * + * @return Link state + */ +cvmx_helper_link_info_t __cvmx_helper_agl_link_get(int ipd_port) +{ + return cvmx_agl_link_get(ipd_port); +} + +/** + * @INTERNAL + * Configure an IPD/PKO port for the specified link state. This + * function does not influence auto negotiation at the PHY level. + * The passed link state must always match the link state returned + * by cvmx_helper_link_get(). It is normally best to use + * cvmx_helper_link_autoconf() instead. + * + * @param ipd_port IPD/PKO port to configure + * @param link_info The new link state + * + * @return Zero on success, negative on failure + */ +int __cvmx_helper_agl_link_set(int ipd_port, cvmx_helper_link_info_t link_info) +{ + int interface = cvmx_helper_get_interface_num(ipd_port); + int port = cvmx_helper_agl_get_port(interface); + + return cvmx_agl_link_set(port, link_info); +} diff --git a/arch/mips/mach-octeon/cvmx-helper-bgx.c b/arch/mips/mach-octeon/cvmx-helper-bgx.c new file mode 100644 index 0000000..7d6e178 --- /dev/null +++ b/arch/mips/mach-octeon/cvmx-helper-bgx.c @@ -0,0 +1,2737 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018-2022 Marvell International Ltd. + * + * Functions to configure the BGX MAC. + */ + +#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-gmxx-defs.h> +#include <mach/cvmx-ipd-defs.h> +#include <mach/cvmx-pki-defs.h> +#include <mach/cvmx-xcv-defs.h> + +#include <mach/cvmx-helper.h> +#include <mach/cvmx-helper-board.h> +#include <mach/cvmx-helper-fdt.h> +#include <mach/cvmx-helper-bgx.h> +#include <mach/cvmx-helper-cfg.h> +#include <mach/cvmx-helper-util.h> +#include <mach/cvmx-helper-pki.h> + +#include <mach/cvmx-global-resources.h> +#include <mach/cvmx-pko-internal-ports-range.h> +#include <mach/cvmx-ilk.h> +#include <mach/cvmx-pip.h> + +/* Enable this define to see BGX error messages */ +/*#define DEBUG_BGX */ + +/* Enable this variable to trace functions called for initializing BGX */ +static const int debug; + +/** + * cvmx_helper_bgx_override_autoneg(int xiface, int index) is a function pointer + * to override enabling/disabling of autonegotiation for SGMII, 10G-KR or 40G-KR4 + * interfaces. This function is called when interface is initialized. + */ +int (*cvmx_helper_bgx_override_autoneg)(int xiface, int index) = NULL; + +/* + * cvmx_helper_bgx_override_fec(int xiface) is a function pointer + * to override enabling/disabling of FEC for 10G interfaces. This function + * is called when interface is initialized. + */ +int (*cvmx_helper_bgx_override_fec)(int xiface, int index) = NULL; + +/** + * Delay after enabling an interface based on the mode. Different modes take + * different amounts of time. + */ +static void +__cvmx_helper_bgx_interface_enable_delay(cvmx_helper_interface_mode_t mode) +{ + switch (mode) { + case CVMX_HELPER_INTERFACE_MODE_10G_KR: + case CVMX_HELPER_INTERFACE_MODE_40G_KR4: + case CVMX_HELPER_INTERFACE_MODE_XLAUI: + case CVMX_HELPER_INTERFACE_MODE_XFI: + mdelay(250); + break; + case CVMX_HELPER_INTERFACE_MODE_RXAUI: + case CVMX_HELPER_INTERFACE_MODE_XAUI: + mdelay(100); + break; + case CVMX_HELPER_INTERFACE_MODE_SGMII: + mdelay(50); + break; + default: + mdelay(50); + break; + } +} + +/** + * @INTERNAL + * + * Returns number of ports based on interface + * @param xiface Which xiface + * @return Number of ports based on xiface + */ +int __cvmx_helper_bgx_enumerate(int xiface) +{ + cvmx_bgxx_cmr_tx_lmacs_t lmacs; + struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); + + lmacs.u64 = csr_rd_node(xi.node, CVMX_BGXX_CMR_TX_LMACS(xi.interface)); + return lmacs.s.lmacs; +} + +/** + * @INTERNAL + * + * Returns mode of each BGX LMAC (port). + * This is different than 'cvmx_helper_interface_get_mode()' which + * provides mode of an entire interface, but when BGX is in "mixed" + * mode this function should be called instead to get the protocol + * for each port (BGX LMAC) individually. + * Both function return the same enumerated mode. + * + * @param xiface is the global interface identifier + * @param index is the interface port index + * @returns mode of the individual port + */ +cvmx_helper_interface_mode_t cvmx_helper_bgx_get_mode(int xiface, int index) +{ + struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); + cvmx_bgxx_cmrx_config_t cmr_config; + cvmx_bgxx_spux_br_pmd_control_t pmd_control; + + cmr_config.u64 = csr_rd_node( + xi.node, CVMX_BGXX_CMRX_CONFIG(index, xi.interface)); + + switch (cmr_config.s.lmac_type) { + case 0: + return CVMX_HELPER_INTERFACE_MODE_SGMII; + case 1: + return CVMX_HELPER_INTERFACE_MODE_XAUI; + case 2: + return CVMX_HELPER_INTERFACE_MODE_RXAUI; + case 3: + if (OCTEON_IS_MODEL(OCTEON_CN78XX_PASS1_X)) + return cvmx_helper_interface_get_mode(xiface); + pmd_control.u64 = csr_rd_node( + xi.node, + CVMX_BGXX_SPUX_BR_PMD_CONTROL(index, xi.interface)); + if (pmd_control.s.train_en) + return CVMX_HELPER_INTERFACE_MODE_10G_KR; + else + return CVMX_HELPER_INTERFACE_MODE_XFI; + break; + case 4: + if (OCTEON_IS_MODEL(OCTEON_CN78XX_PASS1_X)) + return cvmx_helper_interface_get_mode(xiface); + pmd_control.u64 = csr_rd_node( + xi.node, + CVMX_BGXX_SPUX_BR_PMD_CONTROL(index, xi.interface)); + if (pmd_control.s.train_en) + return CVMX_HELPER_INTERFACE_MODE_40G_KR4; + else + return CVMX_HELPER_INTERFACE_MODE_XLAUI; + break; + case 5: + return CVMX_HELPER_INTERFACE_MODE_RGMII; + default: + return CVMX_HELPER_INTERFACE_MODE_DISABLED; + } +} + +static int __cvmx_helper_bgx_rgmii_speed(cvmx_helper_link_info_t link_info) +{ + cvmx_xcv_reset_t xcv_reset; + cvmx_xcv_ctl_t xcv_ctl; + cvmx_xcv_batch_crd_ret_t crd_ret; + cvmx_xcv_dll_ctl_t dll_ctl; + cvmx_xcv_comp_ctl_t comp_ctl; + int speed; + int up = link_info.s.link_up; + int do_credits; + + if (link_info.s.speed == 100) + speed = 1; + else if (link_info.s.speed == 10) + speed = 0; + else + speed = 2; + + xcv_reset.u64 = csr_rd(CVMX_XCV_RESET); + xcv_ctl.u64 = csr_rd(CVMX_XCV_CTL); + do_credits = up && !xcv_reset.s.enable; + + if (xcv_ctl.s.lpbk_int) { + xcv_reset.s.clkrst = 0; + csr_wr(CVMX_XCV_RESET, xcv_reset.u64); + } + + if (up && (!xcv_reset.s.enable || xcv_ctl.s.speed != speed)) { + if (debug) + debug("%s: *** Enabling XCV block\n", __func__); + /* Enable the XCV block */ + xcv_reset.u64 = csr_rd(CVMX_XCV_RESET); + xcv_reset.s.enable = 1; + csr_wr(CVMX_XCV_RESET, xcv_reset.u64); + + /* Set operating mode */ + xcv_ctl.u64 = csr_rd(CVMX_XCV_CTL); + xcv_ctl.s.speed = speed; + csr_wr(CVMX_XCV_CTL, xcv_ctl.u64); + + /* Configure DLL - enable or bypass */ + /* TX no bypass, RX bypass */ + dll_ctl.u64 = csr_rd(CVMX_XCV_DLL_CTL); + dll_ctl.s.clkrx_set = 0; + dll_ctl.s.clkrx_byp = 1; + dll_ctl.s.clktx_byp = 0; + csr_wr(CVMX_XCV_DLL_CTL, dll_ctl.u64); + + /* Enable */ + dll_ctl.u64 = csr_rd(CVMX_XCV_DLL_CTL); + dll_ctl.s.refclk_sel = 0; + csr_wr(CVMX_XCV_DLL_CTL, dll_ctl.u64); + xcv_reset.u64 = csr_rd(CVMX_XCV_RESET); + xcv_reset.s.dllrst = 0; + csr_wr(CVMX_XCV_RESET, xcv_reset.u64); + + /* Delay deems to be need so XCV_DLL_CTL[CLK_SET] works */ + udelay(10); + + comp_ctl.u64 = csr_rd(CVMX_XCV_COMP_CTL); + //comp_ctl.s.drv_pctl = 0; + //comp_ctl.s.drv_nctl = 0; + comp_ctl.s.drv_byp = 0; + csr_wr(CVMX_XCV_COMP_CTL, comp_ctl.u64); + + /* enable */ + xcv_reset.u64 = csr_rd(CVMX_XCV_RESET); + xcv_reset.s.comp = 1; + csr_wr(CVMX_XCV_RESET, xcv_reset.u64); + + /* setup the RXC */ + xcv_reset.u64 = csr_rd(CVMX_XCV_RESET); + xcv_reset.s.clkrst = !xcv_ctl.s.lpbk_int; + csr_wr(CVMX_XCV_RESET, xcv_reset.u64); + + /* datapaths come out of the 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 + */ + xcv_reset.u64 = csr_rd(CVMX_XCV_RESET); + xcv_reset.s.tx_dat_rst_n = 1; + xcv_reset.s.rx_dat_rst_n = 1; + csr_wr(CVMX_XCV_RESET, xcv_reset.u64); + } else if (debug) { + debug("%s: *** Not enabling XCV\n", __func__); + debug(" up: %s, xcv_reset.s.enable: %d, xcv_ctl.s.speed: %d, speed: %d\n", + up ? "true" : "false", (unsigned int)xcv_reset.s.enable, + (unsigned int)xcv_ctl.s.speed, speed); + } + + /* enable the packet flow + * - The packet resets will be only disengage on packet boundaries + * - XCV will continue to return TX credits for each tick that is + * sent on the TX datapath + */ + xcv_reset.u64 = csr_rd(CVMX_XCV_RESET); + xcv_reset.s.tx_pkt_rst_n = up; + xcv_reset.s.rx_pkt_rst_n = up; + csr_wr(CVMX_XCV_RESET, xcv_reset.u64); + + /* Full reset when link is down */ + if (!up) { + if (debug) + debug("%s: *** Disabling XCV reset\n", __func__); + /* wait 2*MTU in time */ + mdelay(10); + /* reset the world */ + csr_wr(CVMX_XCV_RESET, 0); + } + + /* grant PKO TX credits */ + if (do_credits) { + crd_ret.u64 = csr_rd(CVMX_XCV_BATCH_CRD_RET); + crd_ret.s.crd_ret = 1; + csr_wr(CVMX_XCV_BATCH_CRD_RET, crd_ret.u64); + } + + return 0; +} + +static void __cvmx_bgx_common_init_pknd(int xiface, int index) +{ + int num_ports; + int num_chl = 16; + struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); + int node = xi.node; + int pknd; + cvmx_bgxx_cmrx_rx_bp_on_t bgx_rx_bp_on; + cvmx_bgxx_cmrx_rx_id_map_t cmr_rx_id_map; + cvmx_bgxx_cmr_chan_msk_and_t chan_msk_and; + cvmx_bgxx_cmr_chan_msk_or_t chan_msk_or; + + if (debug) + debug("%s: interface %u:%d/%d\n", __func__, xi.node, + xi.interface, index); + + num_ports = cvmx_helper_ports_on_interface(xiface); + /* Modify bp_on mark, depending on number of LMACS on that interface + * and write it for every port + */ + bgx_rx_bp_on.u64 = 0; + bgx_rx_bp_on.s.mark = (CVMX_BGX_RX_FIFO_SIZE / (num_ports * 4 * 16)); + + /* Setup pkind */ + pknd = cvmx_helper_get_pknd(xiface, index); + cmr_rx_id_map.u64 = csr_rd_node( + node, CVMX_BGXX_CMRX_RX_ID_MAP(index, xi.interface)); + cmr_rx_id_map.s.pknd = pknd; + /* Change the default reassembly id (RID), as max 14 RIDs allowed */ + if (OCTEON_IS_MODEL(OCTEON_CN73XX)) + cmr_rx_id_map.s.rid = ((4 * xi.interface) + 2 + index); + csr_wr_node(node, CVMX_BGXX_CMRX_RX_ID_MAP(index, xi.interface), + cmr_rx_id_map.u64); + /* Set backpressure channel mask AND/OR registers */ + chan_msk_and.u64 = + csr_rd_node(node, CVMX_BGXX_CMR_CHAN_MSK_AND(xi.interface)); + chan_msk_or.u64 = + csr_rd_node(node, CVMX_BGXX_CMR_CHAN_MSK_OR(xi.interface)); + chan_msk_and.s.msk_and |= ((1 << num_chl) - 1) << (16 * index); + chan_msk_or.s.msk_or |= ((1 << num_chl) - 1) << (16 * index); + csr_wr_node(node, CVMX_BGXX_CMR_CHAN_MSK_AND(xi.interface), + chan_msk_and.u64); + csr_wr_node(node, CVMX_BGXX_CMR_CHAN_MSK_OR(xi.interface), + chan_msk_or.u64); + /* set rx back pressure (bp_on) on value */ + csr_wr_node(node, CVMX_BGXX_CMRX_RX_BP_ON(index, xi.interface), + bgx_rx_bp_on.u64); +} + +/** + * @INTERNAL + * Probe a SGMII interface and determine the number of ports + * connected to it. The SGMII interface should still be down after + * this call. This is used by interfaces using the bgx mac. + * + * @param xiface Interface to probe + * + * @return Number of ports on the interface. Zero to disable. + */ +int __cvmx_helper_bgx_probe(int xiface) +{ + return __cvmx_helper_bgx_enumerate(xiface); +} + +/** + * @INTERNAL + * Return the size of the BGX TX_FIFO for a given LMAC, + * or 0 if the requested LMAC is inactive. + * + * TBD: Need also to add a "__cvmx_helper_bgx_speed()" function to + * return the speed of each LMAC. + */ +int __cvmx_helper_bgx_fifo_size(int xiface, unsigned int lmac) +{ + cvmx_bgxx_cmr_tx_lmacs_t lmacs; + struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); + unsigned int tx_fifo_size = CVMX_BGX_TX_FIFO_SIZE; + + /* FIXME: Add validation for interface# < BGX_count */ + lmacs.u64 = csr_rd_node(xi.node, CVMX_BGXX_CMR_TX_LMACS(xi.interface)); + + switch (lmacs.s.lmacs) { + case 1: + if (lmac > 0) + return 0; + else + return tx_fifo_size; + case 2: + if (lmac > 1) + return 0; + else + return tx_fifo_size >> 1; + case 3: + if (lmac > 2) + return 0; + else + return tx_fifo_size >> 2; + case 4: + if (lmac > 3) + return 0; + else + return tx_fifo_size >> 2; + default: + return 0; + } +} + +/** + * @INTERNAL + * Perform initialization required only once for an SGMII port. + * + * @param xiface Interface to init + * @param index Index of prot on the interface + * + * @return Zero on success, negative on failure + */ +static int __cvmx_helper_bgx_sgmii_hardware_init_one_time(int xiface, int index) +{ + struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); + int node = xi.node; + const u64 clock_mhz = 1200; /* todo: fixme */ + cvmx_bgxx_gmp_pcs_miscx_ctl_t gmp_misc_ctl; + cvmx_bgxx_gmp_pcs_linkx_timer_t gmp_timer; + + if (!cvmx_helper_is_port_valid(xi.interface, index)) + return 0; + + if (debug) + debug("%s: interface %u:%d/%d\n", __func__, xi.node, + xi.interface, index); + + /* + * Write PCS*_LINK*_TIMER_COUNT_REG[COUNT] with the + * appropriate value. 1000BASE-X specifies a 10ms + * interval. SGMII specifies a 1.6ms interval. + */ + gmp_misc_ctl.u64 = csr_rd_node( + node, CVMX_BGXX_GMP_PCS_MISCX_CTL(index, xi.interface)); + /* Adjust the MAC mode if requested by device tree */ + gmp_misc_ctl.s.mac_phy = cvmx_helper_get_mac_phy_mode(xiface, index); + gmp_misc_ctl.s.mode = cvmx_helper_get_1000x_mode(xiface, index); + csr_wr_node(node, CVMX_BGXX_GMP_PCS_MISCX_CTL(index, xi.interface), + gmp_misc_ctl.u64); + + gmp_timer.u64 = csr_rd_node( + node, CVMX_BGXX_GMP_PCS_LINKX_TIMER(index, xi.interface)); + if (gmp_misc_ctl.s.mode) + /* 1000BASE-X */ + gmp_timer.s.count = (10000ull * clock_mhz) >> 10; + else + /* SGMII */ + gmp_timer.s.count = (1600ull * clock_mhz) >> 10; + + csr_wr_node(node, CVMX_BGXX_GMP_PCS_LINKX_TIMER(index, xi.interface), + gmp_timer.u64); + + /* + * Write the advertisement register to be used as the + * tx_Config_Reg<D15:D0> of the autonegotiation. In + * 1000BASE-X mode, tx_Config_Reg<D15:D0> is PCS*_AN*_ADV_REG. + * In SGMII PHY mode, tx_Config_Reg<D15:D0> is + * PCS*_SGM*_AN_ADV_REG. In SGMII MAC mode, + * tx_Config_Reg<D15:D0> is the fixed value 0x4001, so this + * step can be skipped. + */ + if (gmp_misc_ctl.s.mode) { + /* 1000BASE-X */ + cvmx_bgxx_gmp_pcs_anx_adv_t gmp_an_adv; + + gmp_an_adv.u64 = csr_rd_node( + node, CVMX_BGXX_GMP_PCS_ANX_ADV(index, xi.interface)); + gmp_an_adv.s.rem_flt = 0; + gmp_an_adv.s.pause = 3; + gmp_an_adv.s.hfd = 1; + gmp_an_adv.s.fd = 1; + csr_wr_node(node, + CVMX_BGXX_GMP_PCS_ANX_ADV(index, xi.interface), + gmp_an_adv.u64); + } else { + if (gmp_misc_ctl.s.mac_phy) { + /* PHY Mode */ + cvmx_bgxx_gmp_pcs_sgmx_an_adv_t gmp_sgmx_an_adv; + + gmp_sgmx_an_adv.u64 = + csr_rd_node(node, CVMX_BGXX_GMP_PCS_SGMX_AN_ADV( + index, xi.interface)); + gmp_sgmx_an_adv.s.dup = 1; + gmp_sgmx_an_adv.s.speed = 2; + csr_wr_node(node, + CVMX_BGXX_GMP_PCS_SGMX_AN_ADV(index, + xi.interface), + gmp_sgmx_an_adv.u64); + } else { + /* MAC Mode - Nothing to do */ + } + } + return 0; +} + +/** + * @INTERNAL + * Bring up the SGMII interface to be ready for packet I/O but + * leave I/O disabled using the GMX override. This function + * follows the bringup documented in 10.6.3 of the manual. + * + * @param xiface Interface to bringup + * @param num_ports Number of ports on the interface + * + * @return Zero on success, negative on failure + */ +static int __cvmx_helper_bgx_sgmii_hardware_init(int xiface, int num_ports) +{ + int index; + int do_link_set = 1; + + for (index = 0; index < num_ports; index++) { + int xipd_port = cvmx_helper_get_ipd_port(xiface, index); + cvmx_helper_interface_mode_t mode; + + if (!cvmx_helper_is_port_valid(xiface, index)) + continue; + + __cvmx_helper_bgx_port_init(xipd_port, 0); + + mode = cvmx_helper_bgx_get_mode(xiface, index); + if (mode == CVMX_HELPER_INTERFACE_MODE_RGMII) + continue; + + if (do_link_set) + __cvmx_helper_bgx_sgmii_link_set( + xipd_port, + __cvmx_helper_bgx_sgmii_link_get(xipd_port)); + } + + return 0; +} + +/** + * @INTERNAL + * Bringup and enable a SGMII interface. After this call packet + * I/O should be fully functional. This is called with IPD + * enabled but PKO disabled. This is used by interfaces using + * the bgx mac. + * + * @param xiface Interface to bring up + * + * @return Zero on success, negative on failure + */ +int __cvmx_helper_bgx_sgmii_enable(int xiface) +{ + int num_ports; + + num_ports = cvmx_helper_ports_on_interface(xiface); + __cvmx_helper_bgx_sgmii_hardware_init(xiface, num_ports); + + return 0; +} + +/** + * @INTERNAL + * Initialize the SERDES link for the first time or after a loss + * of link. + * + * @param xiface Interface to init + * @param index Index of prot on the interface + * + * @return Zero on success, negative on failure + */ +static int __cvmx_helper_bgx_sgmii_hardware_init_link(int xiface, int index) +{ + cvmx_bgxx_gmp_pcs_mrx_control_t gmp_control; + cvmx_bgxx_gmp_pcs_miscx_ctl_t gmp_misc_ctl; + cvmx_bgxx_cmrx_config_t cmr_config; + int phy_mode, mode_1000x; + struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); + int interface = xi.interface; + int node = xi.node; + int autoneg = 0; + + if (!cvmx_helper_is_port_valid(xiface, index)) + return 0; + + if (debug) + debug("%s: interface %u:%d/%d\n", __func__, xi.node, + xi.interface, index); + + gmp_control.u64 = csr_rd_node( + node, CVMX_BGXX_GMP_PCS_MRX_CONTROL(index, xi.interface)); + /* Take PCS through a reset sequence */ + gmp_control.s.reset = 1; + csr_wr_node(node, CVMX_BGXX_GMP_PCS_MRX_CONTROL(index, xi.interface), + gmp_control.u64); + + /* Wait until GMP_PCS_MRX_CONTROL[reset] comes out of reset */ + if (CVMX_WAIT_FOR_FIELD64_NODE( + node, CVMX_BGXX_GMP_PCS_MRX_CONTROL(index, xi.interface), + cvmx_bgxx_gmp_pcs_mrx_control_t, reset, ==, 0, 10000)) { + debug("SGMII%d: Timeout waiting for port %d to finish reset\n", + interface, index); + return -1; + } + + cmr_config.u64 = + csr_rd_node(node, CVMX_BGXX_CMRX_CONFIG(index, xi.interface)); + + gmp_control.u64 = csr_rd_node( + node, CVMX_BGXX_GMP_PCS_MRX_CONTROL(index, xi.interface)); + if (cvmx_helper_get_port_phy_present(xiface, index)) { + gmp_control.s.pwr_dn = 0; + } else { + gmp_control.s.spdmsb = 1; + gmp_control.s.spdlsb = 0; + gmp_control.s.pwr_dn = 0; + } + /* Write GMP_PCS_MR*_CONTROL[RST_AN]=1 to ensure a fresh SGMII + * negotiation starts. + */ + autoneg = cvmx_helper_get_port_autonegotiation(xiface, index); + gmp_control.s.rst_an = 1; + gmp_control.s.an_en = (cmr_config.s.lmac_type != 5) && autoneg; + csr_wr_node(node, CVMX_BGXX_GMP_PCS_MRX_CONTROL(index, xi.interface), + gmp_control.u64); + + phy_mode = cvmx_helper_get_mac_phy_mode(xiface, index); + mode_1000x = cvmx_helper_get_1000x_mode(xiface, index); + + gmp_misc_ctl.u64 = csr_rd_node( + node, CVMX_BGXX_GMP_PCS_MISCX_CTL(index, xi.interface)); + gmp_misc_ctl.s.mac_phy = phy_mode; + gmp_misc_ctl.s.mode = mode_1000x; + csr_wr_node(node, CVMX_BGXX_GMP_PCS_MISCX_CTL(index, xi.interface), + gmp_misc_ctl.u64); + + if (phy_mode || !autoneg) + /* In PHY mode we can't query the link status so we just + * assume that the link is up + */ + return 0; + + /* Wait for GMP_PCS_MRX_CONTROL[an_cpt] to be set, indicating that + * SGMII autonegotiation is complete. In MAC mode this isn't an + * ethernet link, but a link between OCTEON and PHY. + */ + if (cmr_config.s.lmac_type != 5 && + CVMX_WAIT_FOR_FIELD64_NODE( + node, CVMX_BGXX_GMP_PCS_MRX_STATUS(index, xi.interface), + cvmx_bgxx_gmp_pcs_mrx_status_t, an_cpt, ==, 1, 10000)) { + debug("SGMII%d: Port %d link timeout\n", interface, index); + return -1; + } + + return 0; +} + +/** + * @INTERNAL + * Configure an SGMII link to the specified speed after the SERDES + * link is up. + * + * @param xiface Interface to init + * @param index Index of prot on the interface + * @param link_info Link state to configure + * + * @return Zero on success, negative on failure + */ +static int __cvmx_helper_bgx_sgmii_hardware_init_link_speed( + int xiface, int index, cvmx_helper_link_info_t link_info) +{ + cvmx_bgxx_cmrx_config_t cmr_config; + cvmx_bgxx_gmp_pcs_miscx_ctl_t gmp_miscx_ctl; + cvmx_bgxx_gmp_gmi_prtx_cfg_t gmp_prtx_cfg; + struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); + int node = xi.node; + + if (!cvmx_helper_is_port_valid(xiface, index)) + return 0; + + if (debug) + debug("%s: interface %u:%d/%d\n", __func__, xi.node, + xi.interface, index); + + /* Disable GMX before we make any changes. */ + cmr_config.u64 = + csr_rd_node(node, CVMX_BGXX_CMRX_CONFIG(index, xi.interface)); + cmr_config.s.data_pkt_tx_en = 0; + cmr_config.s.data_pkt_rx_en = 0; + csr_wr_node(node, CVMX_BGXX_CMRX_CONFIG(index, xi.interface), + cmr_config.u64); + + /* Wait for GMX to be idle */ + if (CVMX_WAIT_FOR_FIELD64_NODE( + node, CVMX_BGXX_GMP_GMI_PRTX_CFG(index, xi.interface), + cvmx_bgxx_gmp_gmi_prtx_cfg_t, rx_idle, ==, 1, 10000) || + CVMX_WAIT_FOR_FIELD64_NODE( + node, CVMX_BGXX_GMP_GMI_PRTX_CFG(index, xi.interface), + cvmx_bgxx_gmp_gmi_prtx_cfg_t, tx_idle, ==, 1, 10000)) { + debug("SGMII%d:%d: Timeout waiting for port %d to be idle\n", + node, xi.interface, index); + return -1; + } + + /* Read GMX CFG again to make sure the disable completed */ + gmp_prtx_cfg.u64 = csr_rd_node( + node, CVMX_BGXX_GMP_GMI_PRTX_CFG(index, xi.interface)); + + /* + * Get the misc control for PCS. We will need to set the + * duplication amount. + */ + gmp_miscx_ctl.u64 = csr_rd_node( + node, CVMX_BGXX_GMP_PCS_MISCX_CTL(index, xi.interface)); + + /* + * Use GMXENO to force the link down if the status we get says + * it should be down. + */ + gmp_miscx_ctl.s.gmxeno = !link_info.s.link_up; + + /* Only change the duplex setting if the link is up */ + if (link_info.s.link_up) + gmp_prtx_cfg.s.duplex = link_info.s.full_duplex; + + /* Do speed based setting for GMX */ + switch (link_info.s.speed) { + case 10: + gmp_prtx_cfg.s.speed = 0; + gmp_prtx_cfg.s.speed_msb = 1; + gmp_prtx_cfg.s.slottime = 0; + /* Setting from GMX-603 */ + gmp_miscx_ctl.s.samp_pt = 25; + csr_wr_node(node, + CVMX_BGXX_GMP_GMI_TXX_SLOT(index, xi.interface), + 64); + csr_wr_node(node, + CVMX_BGXX_GMP_GMI_TXX_BURST(index, xi.interface), + 0); + break; + case 100: + gmp_prtx_cfg.s.speed = 0; + gmp_prtx_cfg.s.speed_msb = 0; + gmp_prtx_cfg.s.slottime = 0; + gmp_miscx_ctl.s.samp_pt = 0x5; + csr_wr_node(node, + CVMX_BGXX_GMP_GMI_TXX_SLOT(index, xi.interface), + 64); + csr_wr_node(node, + CVMX_BGXX_GMP_GMI_TXX_BURST(index, xi.interface), + 0); + break; + case 1000: + gmp_prtx_cfg.s.speed = 1; + gmp_prtx_cfg.s.speed_msb = 0; + gmp_prtx_cfg.s.slottime = 1; + gmp_miscx_ctl.s.samp_pt = 1; + csr_wr_node(node, + CVMX_BGXX_GMP_GMI_TXX_SLOT(index, xi.interface), + 512); + if (gmp_prtx_cfg.s.duplex) + /* full duplex */ + csr_wr_node(node, + CVMX_BGXX_GMP_GMI_TXX_BURST(index, + xi.interface), + 0); + else + /* half duplex */ + csr_wr_node(node, + CVMX_BGXX_GMP_GMI_TXX_BURST(index, + xi.interface), + 8192); + break; + default: + break; + } + + /* Write the new misc control for PCS */ + csr_wr_node(node, CVMX_BGXX_GMP_PCS_MISCX_CTL(index, xi.interface), + gmp_miscx_ctl.u64); + + /* Write the new GMX settings with the port still disabled */ + csr_wr_node(node, CVMX_BGXX_GMP_GMI_PRTX_CFG(index, xi.interface), + gmp_prtx_cfg.u64); + + /* Read GMX CFG again to make sure the config completed */ + csr_rd_node(node, CVMX_BGXX_GMP_GMI_PRTX_CFG(index, xi.interface)); + + /* Enable back BGX. */ + cmr_config.u64 = + csr_rd_node(node, CVMX_BGXX_CMRX_CONFIG(index, xi.interface)); + if (debug) + debug("%s: Enabling tx and rx packets on %d:%d\n", __func__, + xi.interface, index); + cmr_config.s.data_pkt_tx_en = 1; + cmr_config.s.data_pkt_rx_en = 1; + csr_wr_node(node, CVMX_BGXX_CMRX_CONFIG(index, xi.interface), + cmr_config.u64); + + return 0; +} + +/** + * @INTERNAL + * Return the link state of an IPD/PKO port as returned by + * auto negotiation. The result of this function may not match + * Octeon's link config if auto negotiation has changed since + * the last call to cvmx_helper_link_set(). This is used by + * interfaces using the bgx mac. + * + * @param xipd_port IPD/PKO port to query + * + * @return Link state + */ +cvmx_helper_link_info_t __cvmx_helper_bgx_sgmii_link_get(int xipd_port) +{ + cvmx_helper_link_info_t result; + cvmx_bgxx_gmp_pcs_mrx_control_t gmp_control; + cvmx_bgxx_gmp_pcs_miscx_ctl_t gmp_misc_ctl; + int xiface = cvmx_helper_get_interface_num(xipd_port); + struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); + struct cvmx_xport xp = cvmx_helper_ipd_port_to_xport(xipd_port); + int node = xi.node; + int index = cvmx_helper_get_interface_index_num(xp.port); + + result.u64 = 0; + + if (!cvmx_helper_is_port_valid(xiface, index)) + return result; + + if (debug) + debug("%s: interface %u:%d/%d\n", __func__, xi.node, + xi.interface, index); + + gmp_control.u64 = csr_rd_node( + node, CVMX_BGXX_GMP_PCS_MRX_CONTROL(index, xi.interface)); + if (gmp_control.s.loopbck1) { + int qlm = cvmx_qlm_lmac(xiface, index); + int speed; + + if (OCTEON_IS_MODEL(OCTEON_CN78XX)) + speed = cvmx_qlm_get_gbaud_mhz_node(node, qlm); + else + speed = cvmx_qlm_get_gbaud_mhz(qlm); + /* Force 1Gbps full duplex link for internal loopback */ + result.s.link_up = 1; + result.s.full_duplex = 1; + result.s.speed = speed * 8 / 10; + return result; + } + + gmp_misc_ctl.u64 = csr_rd_node( + node, CVMX_BGXX_GMP_PCS_MISCX_CTL(index, xi.interface)); + if (gmp_misc_ctl.s.mac_phy || + cvmx_helper_get_port_force_link_up(xiface, index)) { + int qlm = cvmx_qlm_lmac(xiface, index); + int speed; + + if (OCTEON_IS_MODEL(OCTEON_CN78XX)) + speed = cvmx_qlm_get_gbaud_mhz_node(node, qlm); + else + speed = cvmx_qlm_get_gbaud_mhz(qlm); + /* PHY Mode */ + /* Note that this also works for 1000base-X mode */ + + result.s.speed = speed * 8 / 10; + result.s.full_duplex = 1; + result.s.link_up = 1; + return result; + } + + /* MAC Mode */ + return __cvmx_helper_board_link_get(xipd_port); +} + +/** + * This sequence brings down the link for the XCV RGMII interface + * + * @param interface Interface (BGX) number. Port index is always 0 + */ +static void __cvmx_helper_bgx_rgmii_link_set_down(int interface) +{ + union cvmx_xcv_reset xcv_reset; + union cvmx_bgxx_cmrx_config cmr_config; + union cvmx_bgxx_gmp_pcs_mrx_control mr_control; + union cvmx_bgxx_cmrx_rx_fifo_len rx_fifo_len; + union cvmx_bgxx_cmrx_tx_fifo_len tx_fifo_len; + + xcv_reset.u64 = csr_rd(CVMX_XCV_RESET); + xcv_reset.s.rx_pkt_rst_n = 0; + csr_wr(CVMX_XCV_RESET, xcv_reset.u64); + csr_rd(CVMX_XCV_RESET); + mdelay(10); /* Wait for 1 MTU */ + + cmr_config.u64 = csr_rd(CVMX_BGXX_CMRX_CONFIG(0, interface)); + cmr_config.s.data_pkt_rx_en = 0; + csr_wr(CVMX_BGXX_CMRX_CONFIG(0, interface), cmr_config.u64); + + /* Wait for RX and TX to be idle */ + do { + rx_fifo_len.u64 = + csr_rd(CVMX_BGXX_CMRX_RX_FIFO_LEN(0, interface)); + tx_fifo_len.u64 = + csr_rd(CVMX_BGXX_CMRX_TX_FIFO_LEN(0, interface)); + } while (rx_fifo_len.s.fifo_len > 0 && tx_fifo_len.s.lmac_idle != 1); + + cmr_config.u64 = csr_rd(CVMX_BGXX_CMRX_CONFIG(0, interface)); + cmr_config.s.data_pkt_tx_en = 0; + csr_wr(CVMX_BGXX_CMRX_CONFIG(0, interface), cmr_config.u64); + + xcv_reset.u64 = csr_rd(CVMX_XCV_RESET); + xcv_reset.s.tx_pkt_rst_n = 0; + csr_wr(CVMX_XCV_RESET, xcv_reset.u64); + mr_control.u64 = csr_rd(CVMX_BGXX_GMP_PCS_MRX_CONTROL(0, interface)); + mr_control.s.pwr_dn = 1; + csr_wr(CVMX_BGXX_GMP_PCS_MRX_CONTROL(0, interface), mr_control.u64); +} + +/** + * Sets a BGS SGMII link down. + * + * @param node Octeon node number + * @param iface BGX interface number + * @param index BGX port index + */ +static void __cvmx_helper_bgx_sgmii_link_set_down(int node, int iface, + int index) +{ + union cvmx_bgxx_gmp_pcs_miscx_ctl gmp_misc_ctl; + union cvmx_bgxx_gmp_pcs_mrx_control gmp_control; + union cvmx_bgxx_cmrx_config cmr_config; + + cmr_config.u64 = csr_rd_node(node, CVMX_BGXX_CMRX_CONFIG(index, iface)); + cmr_config.s.data_pkt_tx_en = 0; + cmr_config.s.data_pkt_rx_en = 0; + csr_wr_node(node, CVMX_BGXX_CMRX_CONFIG(index, iface), cmr_config.u64); + + gmp_misc_ctl.u64 = + csr_rd_node(node, CVMX_BGXX_GMP_PCS_MISCX_CTL(index, iface)); + + /* Disable autonegotiation only when in MAC mode. */ + if (gmp_misc_ctl.s.mac_phy == 0) { + gmp_control.u64 = csr_rd_node( + node, CVMX_BGXX_GMP_PCS_MRX_CONTROL(index, iface)); + gmp_control.s.an_en = 0; + csr_wr_node(node, CVMX_BGXX_GMP_PCS_MRX_CONTROL(index, iface), + gmp_control.u64); + } + + /* Use GMXENO to force the link down. It will get reenabled later... */ + gmp_misc_ctl.s.gmxeno = 1; + csr_wr_node(node, CVMX_BGXX_GMP_PCS_MISCX_CTL(index, iface), + gmp_misc_ctl.u64); + csr_rd_node(node, CVMX_BGXX_GMP_PCS_MISCX_CTL(index, iface)); +} + +/** + * @INTERNAL + * Configure an IPD/PKO port for the specified link state. This + * function does not influence auto negotiation at the PHY level. + * The passed link state must always match the link state returned + * by cvmx_helper_link_get(). It is normally best to use + * cvmx_helper_link_autoconf() instead. This is used by interfaces + * using the bgx mac. + * + * @param xipd_port IPD/PKO port to configure + * @param link_info The new link state + * + * @return Zero on success, negative on failure + */ +int __cvmx_helper_bgx_sgmii_link_set(int xipd_port, + cvmx_helper_link_info_t link_info) +{ + cvmx_bgxx_cmrx_config_t cmr_config; + int xiface = cvmx_helper_get_interface_num(xipd_port); + struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); + struct cvmx_xport xp = cvmx_helper_ipd_port_to_xport(xipd_port); + int node = xi.node; + int index = cvmx_helper_get_interface_index_num(xp.port); + const int iface = xi.interface; + int rc = 0; + + if (!cvmx_helper_is_port_valid(xiface, index)) + return 0; + + if (debug) + debug("%s: interface %u:%d/%d\n", __func__, xi.node, + xi.interface, index); + + cmr_config.u64 = csr_rd_node(node, CVMX_BGXX_CMRX_CONFIG(index, iface)); + if (link_info.s.link_up) { + cmr_config.s.enable = 1; + csr_wr_node(node, CVMX_BGXX_CMRX_CONFIG(index, iface), + cmr_config.u64); + /* Apply workaround for errata BGX-22429 */ + if (OCTEON_IS_MODEL(OCTEON_CN78XX_PASS1_X) && index) { + cvmx_bgxx_cmrx_config_t cmr0; + + cmr0.u64 = csr_rd_node(node, + CVMX_BGXX_CMRX_CONFIG(0, iface)); + cmr0.s.enable = 1; + csr_wr_node(node, CVMX_BGXX_CMRX_CONFIG(0, iface), + cmr0.u64); + } + __cvmx_helper_bgx_sgmii_hardware_init_link(xiface, index); + } else if (cvmx_helper_bgx_is_rgmii(xi.interface, index)) { + if (debug) + debug("%s: Bringing down XCV RGMII interface %d\n", + __func__, xi.interface); + __cvmx_helper_bgx_rgmii_link_set_down(xi.interface); + } else { /* Link is down, not RGMII */ + __cvmx_helper_bgx_sgmii_link_set_down(node, iface, index); + return 0; + } + rc = __cvmx_helper_bgx_sgmii_hardware_init_link_speed(xiface, index, + link_info); + if (cvmx_helper_bgx_is_rgmii(xiface, index)) + rc = __cvmx_helper_bgx_rgmii_speed(link_info); + + return rc; +} + +/** + * @INTERNAL + * Bringup XAUI interface. After this call packet I/O should be + * fully functional. + * + * @param index port on interface to bring up + * @param xiface Interface to bring up + * + * @return Zero on success, negative on failure + */ +static int __cvmx_helper_bgx_xaui_init(int index, int xiface) +{ + cvmx_bgxx_cmrx_config_t cmr_config; + cvmx_bgxx_spux_br_pmd_control_t pmd_control; + cvmx_bgxx_spux_misc_control_t spu_misc_control; + cvmx_bgxx_spux_control1_t spu_control1; + cvmx_bgxx_spux_an_control_t spu_an_control; + cvmx_bgxx_spux_an_adv_t spu_an_adv; + cvmx_bgxx_spux_fec_control_t spu_fec_control; + cvmx_bgxx_spu_dbg_control_t spu_dbg_control; + cvmx_bgxx_smux_tx_append_t smu_tx_append; + cvmx_bgxx_smux_tx_ctl_t smu_tx_ctl; + cvmx_helper_interface_mode_t mode; + struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); + int interface = xi.interface; + int node = xi.node; + int use_auto_neg = 0; + int kr_mode = 0; + + if (debug) + debug("%s: interface %u:%d/%d\n", __func__, xi.node, + xi.interface, index); + + mode = cvmx_helper_bgx_get_mode(xiface, index); + + if (mode == CVMX_HELPER_INTERFACE_MODE_10G_KR || + mode == CVMX_HELPER_INTERFACE_MODE_40G_KR4) { + kr_mode = 1; + if (cvmx_helper_bgx_override_autoneg) + use_auto_neg = + cvmx_helper_bgx_override_autoneg(xiface, index); + else + use_auto_neg = cvmx_helper_get_port_autonegotiation( + xiface, index); + } + + /* NOTE: This code was moved first, out of order compared to the HRM + * because the RESET causes all SPU registers to loose their value + */ + /* 4. Next, bring up the SMU/SPU and the BGX reconciliation layer + * logic: + */ + /* 4a. Take SMU/SPU through a reset sequence. Write + * BGX(0..5)_SPU(0..3)_CONTROL1[RESET] = 1. Read + * BGX(0..5)_SPU(0..3)_CONTROL1[RESET] until it changes value to 0. Keep + * BGX(0..5)_SPU(0..3)_MISC_CONTROL[RX_PACKET_DIS] = 1 to disable + * reception. + */ + spu_control1.u64 = + csr_rd_node(node, CVMX_BGXX_SPUX_CONTROL1(index, xi.interface)); + spu_control1.s.reset = 1; + csr_wr_node(node, CVMX_BGXX_SPUX_CONTROL1(index, xi.interface), + spu_control1.u64); + + /* 1. Wait for PCS to come out of reset */ + if (CVMX_WAIT_FOR_FIELD64_NODE( + node, CVMX_BGXX_SPUX_CONTROL1(index, xi.interface), + cvmx_bgxx_spux_control1_t, reset, ==, 0, 10000)) { + debug("BGX%d:%d: SPU stuck in reset\n", node, interface); + return -1; + } + + /* 2. Write BGX(0..5)_CMR(0..3)_CONFIG[ENABLE] to 0, + * BGX(0..5)_SPU(0..3)_CONTROL1[LO_PWR] = 1 and + * BGX(0..5)_SPU(0..3)_MISC_CONTROL[RX_PACKET_DIS] = 1. + */ + spu_control1.u64 = + csr_rd_node(node, CVMX_BGXX_SPUX_CONTROL1(index, xi.interface)); + spu_control1.s.lo_pwr = 1; + csr_wr_node(node, CVMX_BGXX_SPUX_CONTROL1(index, xi.interface), + spu_control1.u64); + + spu_misc_control.u64 = csr_rd_node( + node, CVMX_BGXX_SPUX_MISC_CONTROL(index, xi.interface)); + spu_misc_control.s.rx_packet_dis = 1; + csr_wr_node(node, CVMX_BGXX_SPUX_MISC_CONTROL(index, xi.interface), + spu_misc_control.u64); + + /* 3. At this point, it may be appropriate to disable all BGX and + * SMU/SPU interrupts, as a number of them will occur during bring-up + * of the Link. + * - zero BGX(0..5)_SMU(0..3)_RX_INT + * - zero BGX(0..5)_SMU(0..3)_TX_INT + * - zero BGX(0..5)_SPU(0..3)_INT + */ + csr_wr_node(node, CVMX_BGXX_SMUX_RX_INT(index, xi.interface), + csr_rd_node(node, + CVMX_BGXX_SMUX_RX_INT(index, xi.interface))); + csr_wr_node(node, CVMX_BGXX_SMUX_TX_INT(index, xi.interface), + csr_rd_node(node, + CVMX_BGXX_SMUX_TX_INT(index, xi.interface))); + csr_wr_node(node, CVMX_BGXX_SPUX_INT(index, xi.interface), + csr_rd_node(node, CVMX_BGXX_SPUX_INT(index, xi.interface))); + + /* 4. Configure the BGX LMAC. */ + /* 4a. Configure the LMAC type (40GBASE-R/10GBASE-R/RXAUI/XAUI) and + * SerDes selection in the BGX(0..5)_CMR(0..3)_CONFIG register, but keep + * the ENABLE, DATA_PKT_TX_EN and DATA_PKT_RX_EN bits clear. + */ + /* Already done in bgx_setup_one_time */ + + /* 4b. Write BGX(0..5)_SPU(0..3)_CONTROL1[LO_PWR] = 1 and + * BGX(0..5)_SPU(0..3)_MISC_CONTROL[RX_PACKET_DIS] = 1. + */ + /* 4b. Initialize the selected SerDes lane(s) in the QLM. See Section + * 28.1.2.2 in the GSER chapter. + */ + /* Already done in QLM setup */ + + /* 4c. For 10GBASE-KR or 40GBASE-KR, enable link training by writing + * BGX(0..5)_SPU(0..3)_BR_PMD_CONTROL[TRAIN_EN] = 1. + */ + + if (kr_mode && !OCTEON_IS_MODEL(OCTEON_CN78XX_PASS1_X)) { + csr_wr_node(node, + CVMX_BGXX_SPUX_BR_PMD_LP_CUP(index, interface), 0); + csr_wr_node(node, + CVMX_BGXX_SPUX_BR_PMD_LD_CUP(index, interface), 0); + csr_wr_node(node, + CVMX_BGXX_SPUX_BR_PMD_LD_REP(index, interface), 0); + pmd_control.u64 = csr_rd_node( + node, CVMX_BGXX_SPUX_BR_PMD_CONTROL(index, interface)); + pmd_control.s.train_en = 1; + csr_wr_node(node, + CVMX_BGXX_SPUX_BR_PMD_CONTROL(index, interface), + pmd_control.u64); + } + + /* 4d. Program all other relevant BGX configuration while + * BGX(0..5)_CMR(0..3)_CONFIG[ENABLE] = 0. This includes all things + * described in this chapter. + */ + /* Always add FCS to PAUSE frames */ + smu_tx_append.u64 = csr_rd_node( + node, CVMX_BGXX_SMUX_TX_APPEND(index, xi.interface)); + smu_tx_append.s.fcs_c = 1; + csr_wr_node(node, CVMX_BGXX_SMUX_TX_APPEND(index, xi.interface), + smu_tx_append.u64); + + /* 4e. If Forward Error Correction is desired for 10GBASE-R or + * 40GBASE-R, enable it by writing + * BGX(0..5)_SPU(0..3)_FEC_CONTROL[FEC_EN] = 1. + */ + /* FEC is optional for 10GBASE-KR, 40GBASE-KR4, and XLAUI. We're going + * to disable it by default + */ + spu_fec_control.u64 = csr_rd_node( + node, CVMX_BGXX_SPUX_FEC_CONTROL(index, xi.interface)); + if (cvmx_helper_bgx_override_fec) + spu_fec_control.s.fec_en = + cvmx_helper_bgx_override_fec(xiface, index); + else + spu_fec_control.s.fec_en = + cvmx_helper_get_port_fec(xiface, index); + csr_wr_node(node, CVMX_BGXX_SPUX_FEC_CONTROL(index, xi.interface), + spu_fec_control.u64); + + /* 4f. If Auto-Negotiation is desired, configure and enable + * Auto-Negotiation as described in Section 33.6.2. + */ + spu_an_control.u64 = csr_rd_node( + node, CVMX_BGXX_SPUX_AN_CONTROL(index, xi.interface)); + /* Disable extended next pages */ + spu_an_control.s.xnp_en = 0; + spu_an_control.s.an_en = use_auto_neg; + csr_wr_node(node, CVMX_BGXX_SPUX_AN_CONTROL(index, xi.interface), + spu_an_control.u64); + + spu_fec_control.u64 = csr_rd_node( + node, CVMX_BGXX_SPUX_FEC_CONTROL(index, xi.interface)); + spu_an_adv.u64 = + csr_rd_node(node, CVMX_BGXX_SPUX_AN_ADV(index, xi.interface)); + spu_an_adv.s.fec_req = spu_fec_control.s.fec_en; + spu_an_adv.s.fec_able = 1; + spu_an_adv.s.a100g_cr10 = 0; + spu_an_adv.s.a40g_cr4 = 0; + spu_an_adv.s.a40g_kr4 = (mode == CVMX_HELPER_INTERFACE_MODE_40G_KR4); + spu_an_adv.s.a10g_kr = (mode == CVMX_HELPER_INTERFACE_MODE_10G_KR); + spu_an_adv.s.a10g_kx4 = 0; + spu_an_adv.s.a1g_kx = 0; + spu_an_adv.s.xnp_able = 0; + spu_an_adv.s.rf = 0; + csr_wr_node(node, CVMX_BGXX_SPUX_AN_ADV(index, xi.interface), + spu_an_adv.u64); + + /* 3. Set BGX(0..5)_SPU_DBG_CONTROL[AN_ARB_LINK_CHK_EN] = 1. */ + spu_dbg_control.u64 = + csr_rd_node(node, CVMX_BGXX_SPU_DBG_CONTROL(xi.interface)); + spu_dbg_control.s.an_nonce_match_dis = 1; /* Needed for loopback */ + spu_dbg_control.s.an_arb_link_chk_en |= kr_mode; + csr_wr_node(node, CVMX_BGXX_SPU_DBG_CONTROL(xi.interface), + spu_dbg_control.u64); + + /* 4. Execute the link bring-up sequence in Section 33.6.3. */ + + /* 5. If the auto-negotiation protocol is successful, + * BGX(0..5)_SPU(0..3)_AN_ADV[AN_COMPLETE] is set along with + * BGX(0..5)_SPU(0..3)_INT[AN_COMPLETE] when the link is up. + */ + + /* 3h. Set BGX(0..5)_CMR(0..3)_CONFIG[ENABLE] = 1 and + * BGX(0..5)_SPU(0..3)_CONTROL1[LO_PWR] = 0 to enable the LMAC. + */ + cmr_config.u64 = + csr_rd_node(node, CVMX_BGXX_CMRX_CONFIG(index, xi.interface)); + cmr_config.s.enable = 1; + csr_wr_node(node, CVMX_BGXX_CMRX_CONFIG(index, xi.interface), + cmr_config.u64); + /* Apply workaround for errata BGX-22429 */ + if (OCTEON_IS_MODEL(OCTEON_CN78XX_PASS1_X) && index) { + cvmx_bgxx_cmrx_config_t cmr0; + + cmr0.u64 = csr_rd_node(node, + CVMX_BGXX_CMRX_CONFIG(0, xi.interface)); + cmr0.s.enable = 1; + csr_wr_node(node, CVMX_BGXX_CMRX_CONFIG(0, xi.interface), + cmr0.u64); + } + + spu_control1.u64 = + csr_rd_node(node, CVMX_BGXX_SPUX_CONTROL1(index, xi.interface)); + spu_control1.s.lo_pwr = 0; + csr_wr_node(node, CVMX_BGXX_SPUX_CONTROL1(index, xi.interface), + spu_control1.u64); + + /* 4g. Set the polarity and lane swapping of the QLM SerDes. Refer to + * Section 33.4.1, BGX(0..5)_SPU(0..3)_MISC_CONTROL[XOR_TXPLRT,XOR_RXPLRT] + * and BGX(0..5)_SPU(0..3)_MISC_CONTROL[TXPLRT,RXPLRT]. + */ + + /* 4c. Write BGX(0..5)_SPU(0..3)_CONTROL1[LO_PWR] = 0. */ + spu_control1.u64 = + csr_rd_node(node, CVMX_BGXX_SPUX_CONTROL1(index, xi.interface)); + spu_control1.s.lo_pwr = 0; + csr_wr_node(node, CVMX_BGXX_SPUX_CONTROL1(index, xi.interface), + spu_control1.u64); + + /* 4d. Select Deficit Idle Count mode and unidirectional enable/disable + * via BGX(0..5)_SMU(0..3)_TX_CTL[DIC_EN,UNI_EN]. + */ + smu_tx_ctl.u64 = + csr_rd_node(node, CVMX_BGXX_SMUX_TX_CTL(index, xi.interface)); + smu_tx_ctl.s.dic_en = 1; + smu_tx_ctl.s.uni_en = 0; + csr_wr_node(node, CVMX_BGXX_SMUX_TX_CTL(index, xi.interface), + smu_tx_ctl.u64); + + { + /* Calculate the number of s-clk cycles per usec. */ + const u64 clock_mhz = 1200; /* todo: fixme */ + cvmx_bgxx_spu_dbg_control_t dbg_control; + + dbg_control.u64 = csr_rd_node( + node, CVMX_BGXX_SPU_DBG_CONTROL(xi.interface)); + dbg_control.s.us_clk_period = clock_mhz - 1; + csr_wr_node(node, CVMX_BGXX_SPU_DBG_CONTROL(xi.interface), + dbg_control.u64); + } + /* The PHY often takes at least 100ms to stabilize */ + __cvmx_helper_bgx_interface_enable_delay(mode); + return 0; +} + +static void __cvmx_bgx_start_training(int node, int unit, int index) +{ + cvmx_bgxx_spux_int_t spu_int; + cvmx_bgxx_spux_br_pmd_control_t pmd_control; + cvmx_bgxx_spux_an_control_t an_control; + + /* Clear the training interrupts (W1C) */ + spu_int.u64 = 0; + spu_int.s.training_failure = 1; + spu_int.s.training_done = 1; + csr_wr_node(node, CVMX_BGXX_SPUX_INT(index, unit), spu_int.u64); + + /* These registers aren't cleared when training is restarted. Manually + * clear them as per Errata BGX-20968. + */ + csr_wr_node(node, CVMX_BGXX_SPUX_BR_PMD_LP_CUP(index, unit), 0); + csr_wr_node(node, CVMX_BGXX_SPUX_BR_PMD_LD_CUP(index, unit), 0); + csr_wr_node(node, CVMX_BGXX_SPUX_BR_PMD_LD_REP(index, unit), 0); + + /* Disable autonegotiation */ + an_control.u64 = + csr_rd_node(node, CVMX_BGXX_SPUX_AN_CONTROL(index, unit)); + an_control.s.an_en = 0; + csr_wr_node(node, CVMX_BGXX_SPUX_AN_CONTROL(index, unit), + an_control.u64); + udelay(1); + + /* Restart training */ + pmd_control.u64 = + csr_rd_node(node, CVMX_BGXX_SPUX_BR_PMD_CONTROL(index, unit)); + pmd_control.s.train_en = 1; + csr_wr_node(node, CVMX_BGXX_SPUX_BR_PMD_CONTROL(index, unit), + pmd_control.u64); + + udelay(1); + pmd_control.u64 = + csr_rd_node(node, CVMX_BGXX_SPUX_BR_PMD_CONTROL(index, unit)); + pmd_control.s.train_restart = 1; + csr_wr_node(node, CVMX_BGXX_SPUX_BR_PMD_CONTROL(index, unit), + pmd_control.u64); +} + +static void __cvmx_bgx_restart_training(int node, int unit, int index) +{ + cvmx_bgxx_spux_int_t spu_int; + cvmx_bgxx_spux_br_pmd_control_t pmd_control; + + /* Clear the training interrupts (W1C) */ + spu_int.u64 = 0; + spu_int.s.training_failure = 1; + spu_int.s.training_done = 1; + csr_wr_node(node, CVMX_BGXX_SPUX_INT(index, unit), spu_int.u64); + + udelay(1700); /* Wait 1.7 msec */ + + /* These registers aren't cleared when training is restarted. Manually + * clear them as per Errata BGX-20968. + */ + csr_wr_node(node, CVMX_BGXX_SPUX_BR_PMD_LP_CUP(index, unit), 0); + csr_wr_node(node, CVMX_BGXX_SPUX_BR_PMD_LD_CUP(index, unit), 0); + csr_wr_node(node, CVMX_BGXX_SPUX_BR_PMD_LD_REP(index, unit), 0); + + /* Restart training */ + pmd_control.u64 = + csr_rd_node(node, CVMX_BGXX_SPUX_BR_PMD_CONTROL(index, unit)); + pmd_control.s.train_restart = 1; + csr_wr_node(node, CVMX_BGXX_SPUX_BR_PMD_CONTROL(index, unit), + pmd_control.u64); +} + +/* + * @INTERNAL + * Wrapper function to configure the BGX, does not enable. + * + * @param xipd_port IPD/PKO port to configure. + * @param phy_pres If set, enable disparity, only applies to RXAUI interface + * + * @return Zero on success, negative on failure. + */ +int __cvmx_helper_bgx_port_init(int xipd_port, int phy_pres) +{ + int xiface = cvmx_helper_get_interface_num(xipd_port); + struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); + struct cvmx_xport xp = cvmx_helper_ipd_port_to_xport(xipd_port); + int index = cvmx_helper_get_interface_index_num(xp.port); + cvmx_helper_interface_mode_t mode; + + if (debug) + debug("%s: interface %u:%d/%d\n", __func__, xi.node, + xi.interface, index); + + mode = cvmx_helper_bgx_get_mode(xiface, index); + + __cvmx_bgx_common_init_pknd(xiface, index); + + if (mode == CVMX_HELPER_INTERFACE_MODE_SGMII || + mode == CVMX_HELPER_INTERFACE_MODE_RGMII) { + cvmx_bgxx_gmp_gmi_txx_thresh_t gmi_tx_thresh; + cvmx_bgxx_gmp_gmi_txx_append_t gmp_txx_append; + cvmx_bgxx_gmp_gmi_txx_sgmii_ctl_t gmp_sgmii_ctl; + + /* Set TX Threshold */ + gmi_tx_thresh.u64 = 0; + gmi_tx_thresh.s.cnt = 0x20; + csr_wr_node(xi.node, + CVMX_BGXX_GMP_GMI_TXX_THRESH(index, xi.interface), + gmi_tx_thresh.u64); + __cvmx_helper_bgx_sgmii_hardware_init_one_time(xiface, index); + gmp_txx_append.u64 = csr_rd_node( + xi.node, + CVMX_BGXX_GMP_GMI_TXX_APPEND(index, xi.interface)); + gmp_sgmii_ctl.u64 = csr_rd_node( + xi.node, + CVMX_BGXX_GMP_GMI_TXX_SGMII_CTL(index, xi.interface)); + gmp_sgmii_ctl.s.align = gmp_txx_append.s.preamble ? 0 : 1; + csr_wr_node(xi.node, + CVMX_BGXX_GMP_GMI_TXX_SGMII_CTL(index, + xi.interface), + gmp_sgmii_ctl.u64); + if (mode == CVMX_HELPER_INTERFACE_MODE_RGMII) { + /* Disable XCV interface when initialized */ + union cvmx_xcv_reset xcv_reset; + + if (debug) + debug("%s: Disabling RGMII XCV interface\n", + __func__); + xcv_reset.u64 = csr_rd(CVMX_XCV_RESET); + xcv_reset.s.enable = 0; + xcv_reset.s.tx_pkt_rst_n = 0; + xcv_reset.s.rx_pkt_rst_n = 0; + csr_wr(CVMX_XCV_RESET, xcv_reset.u64); + } + } else { + int res, cred; + cvmx_bgxx_smux_tx_thresh_t smu_tx_thresh; + + res = __cvmx_helper_bgx_xaui_init(index, xiface); + if (res == -1) { +#ifdef DEBUG_BGX + debug("Failed to enable XAUI for %d:BGX(%d,%d)\n", + xi.node, xi.interface, index); +#endif + return res; + } + /* See BVX_SMU_TX_THRESH register descriptin */ + cred = __cvmx_helper_bgx_fifo_size(xiface, index) >> 4; + smu_tx_thresh.u64 = 0; + smu_tx_thresh.s.cnt = cred - 10; + csr_wr_node(xi.node, + CVMX_BGXX_SMUX_TX_THRESH(index, xi.interface), + smu_tx_thresh.u64); + if (debug) + debug("%s: BGX%d:%d TX-thresh=%d\n", __func__, + xi.interface, index, + (unsigned int)smu_tx_thresh.s.cnt); + + /* Set disparity for RXAUI interface as described in the + * Marvell RXAUI Interface specification. + */ + if (mode == CVMX_HELPER_INTERFACE_MODE_RXAUI && phy_pres) { + cvmx_bgxx_spux_misc_control_t misc_control; + + misc_control.u64 = csr_rd_node( + xi.node, CVMX_BGXX_SPUX_MISC_CONTROL( + index, xi.interface)); + misc_control.s.intlv_rdisp = 1; + csr_wr_node(xi.node, + CVMX_BGXX_SPUX_MISC_CONTROL(index, + xi.interface), + misc_control.u64); + } + } + return 0; +} + +/** + * @INTERNAL + * Configure a port for internal and/or external loopback. Internal loopback + * causes packets sent by the port to be received by Octeon. External loopback + * causes packets received from the wire to sent out again. This is used by + * interfaces using the bgx mac. + * + * @param xipd_port IPD/PKO port to loopback. + * @param enable_internal + * Non zero if you want internal loopback + * @param enable_external + * Non zero if you want external loopback + * + * @return Zero on success, negative on failure. + */ +int __cvmx_helper_bgx_sgmii_configure_loopback(int xipd_port, + int enable_internal, + int enable_external) +{ + int xiface = cvmx_helper_get_interface_num(xipd_port); + struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); + struct cvmx_xport xp = cvmx_helper_ipd_port_to_xport(xipd_port); + int node = xi.node; + int index = cvmx_helper_get_interface_index_num(xp.port); + cvmx_bgxx_gmp_pcs_mrx_control_t gmp_mrx_control; + cvmx_bgxx_gmp_pcs_miscx_ctl_t gmp_misc_ctl; + + if (!cvmx_helper_is_port_valid(xiface, index)) + return 0; + + if (debug) + debug("%s: interface %u:%d/%d\n", __func__, xi.node, + xi.interface, index); + + if (cvmx_helper_bgx_is_rgmii(xi.interface, index)) { + cvmx_xcv_ctl_t xcv_ctl; + cvmx_helper_link_info_t link_info; + + xcv_ctl.u64 = csr_rd(CVMX_XCV_CTL); + xcv_ctl.s.lpbk_int = enable_internal; + xcv_ctl.s.lpbk_ext = enable_external; + csr_wr(CVMX_XCV_CTL, xcv_ctl.u64); + + /* Initialize link and speed */ + __cvmx_helper_bgx_sgmii_hardware_init_link(xiface, index); + link_info = __cvmx_helper_bgx_sgmii_link_get(xipd_port); + __cvmx_helper_bgx_sgmii_hardware_init_link_speed(xiface, index, + link_info); + __cvmx_helper_bgx_rgmii_speed(link_info); + } else { + gmp_mrx_control.u64 = csr_rd_node( + node, + CVMX_BGXX_GMP_PCS_MRX_CONTROL(index, xi.interface)); + gmp_mrx_control.s.loopbck1 = enable_internal; + csr_wr_node(node, + CVMX_BGXX_GMP_PCS_MRX_CONTROL(index, xi.interface), + gmp_mrx_control.u64); + + gmp_misc_ctl.u64 = csr_rd_node( + node, CVMX_BGXX_GMP_PCS_MISCX_CTL(index, xi.interface)); + gmp_misc_ctl.s.loopbck2 = enable_external; + csr_wr_node(node, + CVMX_BGXX_GMP_PCS_MISCX_CTL(index, xi.interface), + gmp_misc_ctl.u64); + __cvmx_helper_bgx_sgmii_hardware_init_link(xiface, index); + } + + return 0; +} + +static int __cvmx_helper_bgx_xaui_link_init(int index, int xiface) +{ + struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); + int node = xi.node; + cvmx_bgxx_spux_status1_t spu_status1; + cvmx_bgxx_spux_status2_t spu_status2; + cvmx_bgxx_spux_br_status2_t br_status2; + cvmx_bgxx_spux_int_t spu_int; + cvmx_bgxx_spux_misc_control_t spu_misc_control; + cvmx_bgxx_spux_an_control_t spu_an_control; + cvmx_bgxx_spux_an_status_t spu_an_status; + cvmx_bgxx_spux_br_pmd_control_t pmd_control; + cvmx_bgxx_cmrx_config_t cmr_config; + cvmx_helper_interface_mode_t mode; + int use_training = 0; + int rgmii_first = 0; + int qlm = cvmx_qlm_lmac(xiface, index); + int use_ber = 0; + u64 err_blks; + u64 ber_cnt; + u64 error_debounce; + + if (debug) + debug("%s: interface %u:%d/%d\n", __func__, xi.node, + xi.interface, index); + + rgmii_first = cvmx_helper_bgx_is_rgmii(xi.interface, index); + + mode = cvmx_helper_bgx_get_mode(xiface, index); + if (mode == CVMX_HELPER_INTERFACE_MODE_10G_KR || + mode == CVMX_HELPER_INTERFACE_MODE_40G_KR4) + use_training = 1; + + if ((mode == CVMX_HELPER_INTERFACE_MODE_XFI || + mode == CVMX_HELPER_INTERFACE_MODE_XLAUI || + mode == CVMX_HELPER_INTERFACE_MODE_10G_KR || + mode == CVMX_HELPER_INTERFACE_MODE_40G_KR4)) + use_ber = 1; + + /* Disable packet reception, CMR as well as SPU block */ + cmr_config.u64 = + csr_rd_node(node, CVMX_BGXX_CMRX_CONFIG(index, xi.interface)); + cmr_config.s.data_pkt_tx_en = 0; + cmr_config.s.data_pkt_rx_en = 0; + csr_wr_node(node, CVMX_BGXX_CMRX_CONFIG(index, xi.interface), + cmr_config.u64); + spu_misc_control.u64 = csr_rd_node( + node, CVMX_BGXX_SPUX_MISC_CONTROL(index, xi.interface)); + spu_misc_control.s.rx_packet_dis = 1; + csr_wr_node(node, CVMX_BGXX_SPUX_MISC_CONTROL(index, xi.interface), + spu_misc_control.u64); + + spu_an_control.u64 = csr_rd_node( + node, CVMX_BGXX_SPUX_AN_CONTROL(index, xi.interface)); + if (spu_an_control.s.an_en) { + if (OCTEON_IS_MODEL(OCTEON_CN78XX_PASS1_X)) { + cvmx_bgxx_spux_int_t spu_int; + + spu_int.u64 = csr_rd_node( + node, CVMX_BGXX_SPUX_INT(index, xi.interface)); + if (!spu_int.s.an_link_good) { + static u64 restart_auto_neg[2][6][4] = { + [0 ... 1][0 ... 5] = { [0 ... 3] = 0 } + }; + u64 now = get_timer(0); + u64 next_restart = + restart_auto_neg[node][xi.interface] + [index] + + 2000; + + if (now >= next_restart) + return -1; + + restart_auto_neg[node][xi.interface][index] = + now; + + /* Clear the auto negotiation (W1C) */ + spu_int.u64 = 0; + spu_int.s.an_complete = 1; + spu_int.s.an_link_good = 1; + spu_int.s.an_page_rx = 1; + csr_wr_node(node, + CVMX_BGXX_SPUX_INT(index, + xi.interface), + spu_int.u64); + /* Restart auto negotiation */ + spu_an_control.u64 = csr_rd_node( + node, CVMX_BGXX_SPUX_AN_CONTROL( + index, xi.interface)); + spu_an_control.s.an_restart = 1; + csr_wr_node(node, + CVMX_BGXX_SPUX_AN_CONTROL( + index, xi.interface), + spu_an_control.u64); + return -1; + } + } else { + spu_an_status.u64 = csr_rd_node( + node, + CVMX_BGXX_SPUX_AN_STATUS(index, xi.interface)); + if (!spu_an_status.s.an_complete) { + static u64 restart_auto_neg[2][6][4] = { + [0 ... 1][0 ... 5] = { [0 ... 3] = 0 } + }; + u64 now = get_timer(0); + u64 next_restart = + restart_auto_neg[node][xi.interface] + [index] + + 2000; + if (now >= next_restart) { +#ifdef DEBUG_BGX + debug("WARNING: BGX%d:%d: Waiting for autoneg to complete\n", + xi.interface, index); +#endif + return -1; + } + + restart_auto_neg[node][xi.interface][index] = + now; + /* Restart auto negotiation */ + spu_an_control.u64 = csr_rd_node( + node, CVMX_BGXX_SPUX_AN_CONTROL( + index, xi.interface)); + spu_an_control.s.an_restart = 1; + csr_wr_node(node, + CVMX_BGXX_SPUX_AN_CONTROL( + index, xi.interface), + spu_an_control.u64); + return -1; + } + } + } + + if (use_training) { + spu_int.u64 = csr_rd_node( + node, CVMX_BGXX_SPUX_INT(index, xi.interface)); + pmd_control.u64 = csr_rd_node( + node, + CVMX_BGXX_SPUX_BR_PMD_CONTROL(index, xi.interface)); + if (OCTEON_IS_MODEL(OCTEON_CN78XX_PASS1_X) && + pmd_control.s.train_en == 0) { + __cvmx_bgx_start_training(node, xi.interface, index); + return -1; + } + cvmx_qlm_gser_errata_27882(node, qlm, index); + spu_int.u64 = csr_rd_node( + node, CVMX_BGXX_SPUX_INT(index, xi.interface)); + + if (spu_int.s.training_failure && + !OCTEON_IS_MODEL(OCTEON_CN78XX_PASS1_X)) { + __cvmx_bgx_restart_training(node, xi.interface, index); + return -1; + } + if (!spu_int.s.training_done) { + debug("Waiting for link training\n"); + return -1; + } + } + + /* (GSER-21957) GSER RX Equalization may make >= 5gbaud non-KR + * channel with DXAUI, RXAUI, XFI and XLAUI, we need to perform + * RX equalization when the link is receiving data the first time + */ + if (use_training == 0) { + int lane = index; + cvmx_bgxx_spux_control1_t control1; + + cmr_config.u64 = csr_rd_node( + node, CVMX_BGXX_CMRX_CONFIG(index, xi.interface)); + control1.u64 = csr_rd_node( + node, CVMX_BGXX_SPUX_CONTROL1(index, xi.interface)); + if (control1.s.loopbck) { + /* Skip RX equalization when in loopback */ + } else if (mode == CVMX_HELPER_INTERFACE_MODE_XLAUI || + mode == CVMX_HELPER_INTERFACE_MODE_XAUI) { + lane = -1; + if (__cvmx_qlm_rx_equalization(node, qlm, lane)) { +#ifdef DEBUG_BGX + debug("%d:%d:%d: Waiting for RX Equalization on QLM%d\n", + node, xi.interface, index, qlm); +#endif + return -1; + } + /* If BGX2 uses both dlms, then configure other dlm also. */ + if (OCTEON_IS_MODEL(OCTEON_CN73XX) && + xi.interface == 2) { + if (__cvmx_qlm_rx_equalization(node, 6, lane)) { +#ifdef DEBUG_BGX + debug("%d:%d:%d: Waiting for RX Equalization on QLM%d\n", + node, xi.interface, index, qlm); +#endif + return -1; + } + } + /* RXAUI */ + } else if (mode == CVMX_HELPER_INTERFACE_MODE_RXAUI) { + lane = index * 2; + if (OCTEON_IS_MODEL(OCTEON_CN73XX) && index >= 2 && + xi.interface == 2) { + lane = 0; + } + if (rgmii_first) + lane--; + if (__cvmx_qlm_rx_equalization(node, qlm, lane) || + __cvmx_qlm_rx_equalization(node, qlm, lane + 1)) { +#ifdef DEBUG_BGX + debug("%d:%d:%d: Waiting for RX Equalization on QLM%d\n", + node, xi.interface, index, qlm); +#endif + return -1; + } + /* XFI */ + } else if (cmr_config.s.lmac_type != 5) { + if (rgmii_first) + lane--; + if (OCTEON_IS_MODEL(OCTEON_CN73XX) && index >= 2 && + xi.interface == 2) { + lane = index - 2; + } else if (OCTEON_IS_MODEL(OCTEON_CNF75XX) && + index >= 2) { + lane = index - 2; + } + if (__cvmx_qlm_rx_equalization(node, qlm, lane)) { +#ifdef DEBUG_BGX + debug("%d:%d:%d: Waiting for RX Equalization on QLM%d\n", + node, xi.interface, index, qlm); +#endif + return -1; + } + } + } + + if (CVMX_WAIT_FOR_FIELD64_NODE( + node, CVMX_BGXX_SPUX_CONTROL1(index, xi.interface), + cvmx_bgxx_spux_control1_t, reset, ==, 0, 10000)) { +#ifdef DEBUG_BGX + debug("ERROR: %d:BGX%d:%d: PCS in reset", node, xi.interface, + index); +#endif + return -1; + } + + if (use_ber) { + if (CVMX_WAIT_FOR_FIELD64_NODE( + node, + CVMX_BGXX_SPUX_BR_STATUS1(index, xi.interface), + cvmx_bgxx_spux_br_status1_t, blk_lock, ==, 1, + 10000)) { +#ifdef DEBUG_BGX + debug("ERROR: %d:BGX%d:%d: BASE-R PCS block not locked\n", + node, xi.interface, index); + + if (mode == CVMX_HELPER_INTERFACE_MODE_XLAUI || + mode == CVMX_HELPER_INTERFACE_MODE_40G_KR4) { + cvmx_bgxx_spux_br_algn_status_t bstatus; + + bstatus.u64 = csr_rd_node( + node, CVMX_BGXX_SPUX_BR_ALGN_STATUS( + index, xi.interface)); + debug("ERROR: %d:BGX%d:%d: LANE BLOCK_LOCK:%x LANE MARKER_LOCK:%x\n", + node, xi.interface, index, + bstatus.s.block_lock, + bstatus.s.marker_lock); + } +#endif + return -1; + } + } else { + /* (5) Check to make sure that the link appears up and stable. + */ + /* Wait for PCS to be aligned */ + if (CVMX_WAIT_FOR_FIELD64_NODE( + node, CVMX_BGXX_SPUX_BX_STATUS(index, xi.interface), + cvmx_bgxx_spux_bx_status_t, alignd, ==, 1, 10000)) { +#ifdef DEBUG_BGX + debug("ERROR: %d:BGX%d:%d: PCS not aligned\n", node, + xi.interface, index); +#endif + return -1; + } + } + + if (use_ber) { + /* Set the BGXX_SPUX_BR_STATUS2.latched_lock bit (latching low). + * This will be checked prior to enabling packet tx and rx, + * ensuring block lock is sustained throughout the BGX link-up + * procedure + */ + br_status2.u64 = csr_rd_node( + node, CVMX_BGXX_SPUX_BR_STATUS2(index, xi.interface)); + br_status2.s.latched_lock = 1; + csr_wr_node(node, + CVMX_BGXX_SPUX_BR_STATUS2(index, xi.interface), + br_status2.u64); + } + + /* Clear rcvflt bit (latching high) and read it back */ + spu_status2.u64 = + csr_rd_node(node, CVMX_BGXX_SPUX_STATUS2(index, xi.interface)); + spu_status2.s.rcvflt = 1; + csr_wr_node(node, CVMX_BGXX_SPUX_STATUS2(index, xi.interface), + spu_status2.u64); + + spu_status2.u64 = + csr_rd_node(node, CVMX_BGXX_SPUX_STATUS2(index, xi.interface)); + if (spu_status2.s.rcvflt) { +#ifdef DEBUG_BGX + debug("ERROR: %d:BGX%d:%d: Receive fault, need to retry\n", + node, xi.interface, index); +#endif + if (OCTEON_IS_MODEL(OCTEON_CN78XX_PASS1_X) && use_training) + __cvmx_bgx_restart_training(node, xi.interface, index); + /* debug("training restarting\n"); */ + return -1; + } + + /* Wait for MAC RX to be ready */ + if (CVMX_WAIT_FOR_FIELD64_NODE( + node, CVMX_BGXX_SMUX_RX_CTL(index, xi.interface), + cvmx_bgxx_smux_rx_ctl_t, status, ==, 0, 10000)) { +#ifdef DEBUG_BGX + debug("ERROR: %d:BGX%d:%d: RX not ready\n", node, xi.interface, + index); +#endif + return -1; + } + + /* Wait for BGX RX to be idle */ + if (CVMX_WAIT_FOR_FIELD64_NODE( + node, CVMX_BGXX_SMUX_CTRL(index, xi.interface), + cvmx_bgxx_smux_ctrl_t, rx_idle, ==, 1, 10000)) { +#ifdef DEBUG_BGX + debug("ERROR: %d:BGX%d:%d: RX not idle\n", node, xi.interface, + index); +#endif + return -1; + } + + /* Wait for GMX TX to be idle */ + if (CVMX_WAIT_FOR_FIELD64_NODE( + node, CVMX_BGXX_SMUX_CTRL(index, xi.interface), + cvmx_bgxx_smux_ctrl_t, tx_idle, ==, 1, 10000)) { +#ifdef DEBUG_BGX + debug("ERROR: %d:BGX%d:%d: TX not idle\n", node, xi.interface, + index); +#endif + return -1; + } + + /* rcvflt should still be 0 */ + spu_status2.u64 = + csr_rd_node(node, CVMX_BGXX_SPUX_STATUS2(index, xi.interface)); + if (spu_status2.s.rcvflt) { +#ifdef DEBUG_BGX + debug("ERROR: %d:BGX%d:%d: Receive fault, need to retry\n", + node, xi.interface, index); +#endif + return -1; + } + + /* Receive link is latching low. Force it high and verify it */ + spu_status1.u64 = + csr_rd_node(node, CVMX_BGXX_SPUX_STATUS1(index, xi.interface)); + spu_status1.s.rcv_lnk = 1; + csr_wr_node(node, CVMX_BGXX_SPUX_STATUS1(index, xi.interface), + spu_status1.u64); + + if (CVMX_WAIT_FOR_FIELD64_NODE( + node, CVMX_BGXX_SPUX_STATUS1(index, xi.interface), + cvmx_bgxx_spux_status1_t, rcv_lnk, ==, 1, 10000)) { +#ifdef DEBUG_BGX + debug("ERROR: %d:BGX%d:%d: Receive link down\n", node, + xi.interface, index); +#endif + return -1; + } + + if (use_ber) { + /* Clearing BER_CNT and ERR_BLKs */ + br_status2.u64 = csr_rd_node( + node, CVMX_BGXX_SPUX_BR_STATUS2(index, xi.interface)); + + /* If set, clear the LATCHED_BER by writing it to a one. */ + if (br_status2.s.latched_ber) + csr_wr_node(node, + CVMX_BGXX_SPUX_BR_STATUS2(index, + xi.interface), + br_status2.u64); + + error_debounce = get_timer(0); + + /* Clear error counts */ + err_blks = 0; + ber_cnt = 0; + + /* Verify that the link is up and error free for 100ms */ + while (get_timer(error_debounce) < 100) { + spu_status1.u64 = csr_rd_node( + node, + CVMX_BGXX_SPUX_STATUS1(index, xi.interface)); + /* Checking that Receive link is still up (rcv_lnk = 1 (up)) */ + if (!spu_status1.s.rcv_lnk) { +#ifdef DEBUG_BGX + debug("ERROR: %d:BGX%d:%d: Receive link down\n", + node, xi.interface, index); +#endif + return -1; + } + + /* Checking if latched_ber = 1 (BER >= 10e^4) */ + br_status2.u64 = csr_rd_node( + node, + CVMX_BGXX_SPUX_BR_STATUS2(index, xi.interface)); + err_blks += br_status2.s.err_blks; + ber_cnt += br_status2.s.ber_cnt; + + if (br_status2.s.latched_ber) { +#ifdef DEBUG_BGX + debug("ERROR: %d:BGX%d:%d: BER test failed, BER >= 10e^4, need to retry\n", + node, xi.interface, index); +#endif + return -1; + } + /* Checking that latched BLOCK_LOCK is still set (Block Lock never lost) */ + if (!br_status2.s.latched_lock) { +#ifdef DEBUG_BGX + debug("ERROR: %d:BGX%d:%d: BASE-R PCS block lock lost, need to retry\n", + node, xi.interface, index); +#endif + return -1; + } + + /* Check error counters. Must be 0 (this error rate# + * is much higher than 1E-12) + */ + if (err_blks > 0) { +#ifdef DEBUG_BGX + debug("ERROR: %d:BGX%d:%d: BASE-R errored-blocks (%llu) detected, need to retry\n", + node, xi.interface, index, + (unsigned long long)err_blks); +#endif + return -1; + } + + if (ber_cnt > 0) { +#ifdef DEBUG_BGX + debug("ERROR: %d:BGX%d:%d: BASE-R bit-errors (%llu) detected, need to retry\n", + node, xi.interface, index, + (unsigned long long)ber_cnt); +#endif + return -1; + } + + udelay(1000); + } + + /* Clear out the BGX error counters/bits. These errors are + * expected as part of the BGX link up procedure + */ + /* BIP_ERR counters clear as part of this read */ + csr_rd_node(node, + CVMX_BGXX_SPUX_BR_BIP_ERR_CNT(index, xi.interface)); + /* BER_CNT and ERR_BLKs clear as part of this read */ + br_status2.u64 = csr_rd_node( + node, CVMX_BGXX_SPUX_BR_STATUS2(index, xi.interface)); + } + + /* (7) Enable packet transmit and receive */ + spu_misc_control.u64 = csr_rd_node( + node, CVMX_BGXX_SPUX_MISC_CONTROL(index, xi.interface)); + spu_misc_control.s.rx_packet_dis = 0; + csr_wr_node(node, CVMX_BGXX_SPUX_MISC_CONTROL(index, xi.interface), + spu_misc_control.u64); + + if (debug) + debug("%s: Enabling tx and rx data packets\n", __func__); + cmr_config.u64 = + csr_rd_node(node, CVMX_BGXX_CMRX_CONFIG(index, xi.interface)); + cmr_config.s.data_pkt_tx_en = 1; + cmr_config.s.data_pkt_rx_en = 1; + csr_wr_node(node, CVMX_BGXX_CMRX_CONFIG(index, xi.interface), + cmr_config.u64); + return 0; +} + +int __cvmx_helper_bgx_xaui_enable(int xiface) +{ + int index; + cvmx_helper_interface_mode_t mode; + int num_ports = cvmx_helper_ports_on_interface(xiface); + + for (index = 0; index < num_ports; index++) { + int res; + int xipd_port = cvmx_helper_get_ipd_port(xiface, index); + int phy_pres; + struct cvmx_xiface xi = + cvmx_helper_xiface_to_node_interface(xiface); + static int count + [CVMX_MAX_NODES][CVMX_HELPER_MAX_IFACE] + [CVMX_HELPER_CFG_MAX_PORT_PER_IFACE] = { + [0 ... CVMX_MAX_NODES - + 1][0 ... CVMX_HELPER_MAX_IFACE - + 1] = { [0 ... CVMX_HELPER_CFG_MAX_PORT_PER_IFACE - + 1] = 0 } + }; + + mode = cvmx_helper_bgx_get_mode(xiface, index); + + /* Set disparity for RXAUI interface as described in the + * Marvell RXAUI Interface specification. + */ + if (mode == CVMX_HELPER_INTERFACE_MODE_RXAUI && + (cvmx_helper_get_port_phy_present(xiface, index))) + phy_pres = 1; + else + phy_pres = 0; + __cvmx_helper_bgx_port_init(xipd_port, phy_pres); + +retry_link: + res = __cvmx_helper_bgx_xaui_link_init(index, xiface); + /* RX Equalization or autonegotiation can take little longer + * retry the link maybe 5 times for now + */ + if (res == -1 && count[xi.node][xi.interface][index] < 5) { + count[xi.node][xi.interface][index]++; +#ifdef DEBUG_BGX + debug("%d:BGX(%d,%d): Failed to get link, retrying\n", + xi.node, xi.interface, index); +#endif + goto retry_link; + } + + if (res == -1) { +#ifdef DEBUG_BGX + debug("%d:BGX(%d,%d): Failed to get link\n", xi.node, + xi.interface, index); +#endif + continue; + } + } + return 0; +} + +cvmx_helper_link_info_t __cvmx_helper_bgx_xaui_link_get(int xipd_port) +{ + int xiface = cvmx_helper_get_interface_num(xipd_port); + struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); + struct cvmx_xport xp = cvmx_helper_ipd_port_to_xport(xipd_port); + int index = cvmx_helper_get_interface_index_num(xp.port); + cvmx_bgxx_spux_status1_t spu_status1; + cvmx_bgxx_smux_tx_ctl_t smu_tx_ctl; + cvmx_bgxx_smux_rx_ctl_t smu_rx_ctl; + cvmx_bgxx_cmrx_config_t cmr_config; + cvmx_helper_link_info_t result; + cvmx_helper_interface_mode_t mode; + cvmx_bgxx_spux_misc_control_t spu_misc_control; + cvmx_bgxx_spux_br_status2_t br_status2; + + result.u64 = 0; + + if (debug) + debug("%s: interface %u:%d/%d\n", __func__, xi.node, + xi.interface, index); + + mode = cvmx_helper_bgx_get_mode(xiface, index); + if (mode == CVMX_HELPER_INTERFACE_MODE_RGMII) + return __cvmx_helper_bgx_sgmii_link_get(xipd_port); + + /* Reading current rx/tx link status */ + spu_status1.u64 = csr_rd_node( + xi.node, CVMX_BGXX_SPUX_STATUS1(index, xi.interface)); + smu_tx_ctl.u64 = csr_rd_node( + xi.node, CVMX_BGXX_SMUX_TX_CTL(index, xi.interface)); + smu_rx_ctl.u64 = csr_rd_node( + xi.node, CVMX_BGXX_SMUX_RX_CTL(index, xi.interface)); + /* Reading tx/rx packet enables */ + cmr_config.u64 = csr_rd_node( + xi.node, CVMX_BGXX_CMRX_CONFIG(index, xi.interface)); + spu_misc_control.u64 = csr_rd_node( + xi.node, CVMX_BGXX_SPUX_MISC_CONTROL(index, xi.interface)); + + if (smu_tx_ctl.s.ls == 0 && smu_rx_ctl.s.status == 0 && + cmr_config.s.data_pkt_tx_en == 1 && + cmr_config.s.data_pkt_rx_en == 1 && + spu_misc_control.s.rx_packet_dis == 0 && + spu_status1.s.rcv_lnk) { + int lanes; + int qlm = cvmx_qlm_lmac(xiface, index); + u64 speed; + + result.s.link_up = 1; + result.s.full_duplex = 1; + if (OCTEON_IS_MODEL(OCTEON_CN78XX)) + speed = cvmx_qlm_get_gbaud_mhz_node(xi.node, qlm); + else + speed = cvmx_qlm_get_gbaud_mhz(qlm); + + cmr_config.u64 = csr_rd_node( + xi.node, CVMX_BGXX_CMRX_CONFIG(index, xi.interface)); + switch (cmr_config.s.lmac_type) { + default: + case 1: // XAUI + speed = (speed * 8 + 5) / 10; + lanes = 4; + break; + case 2: // RXAUI + speed = (speed * 8 + 5) / 10; + lanes = 2; + break; + case 3: // XFI + speed = (speed * 64 + 33) / 66; + lanes = 1; + break; + case 4: // XLAUI + /* Adjust the speed when XLAUI is configured at 6.250Gbps */ + if (speed == 6250) + speed = 6445; + speed = (speed * 64 + 33) / 66; + lanes = 4; + break; + } + + if (debug) + debug("%s: baud: %llu, lanes: %d\n", __func__, + (unsigned long long)speed, lanes); + speed *= lanes; + result.s.speed = speed; + } else { + int res; + u64 err_blks = 0; + u64 ber_cnt = 0; + + /* Check for err_blk and ber errors if 10G or 40G */ + if ((mode == CVMX_HELPER_INTERFACE_MODE_XFI || + mode == CVMX_HELPER_INTERFACE_MODE_XLAUI || + mode == CVMX_HELPER_INTERFACE_MODE_10G_KR || + mode == CVMX_HELPER_INTERFACE_MODE_40G_KR4)) { + br_status2.u64 = csr_rd_node( + xi.node, + CVMX_BGXX_SPUX_BR_STATUS2(index, xi.interface)); + err_blks = br_status2.s.err_blks; + ber_cnt = br_status2.s.ber_cnt; + } + + /* Checking if the link is up and error-free but we are receiving remote-faults */ + if (smu_tx_ctl.s.ls != 1 && smu_rx_ctl.s.status != 1 && + cmr_config.s.data_pkt_tx_en == 1 && + cmr_config.s.data_pkt_rx_en == 1 && + spu_misc_control.s.rx_packet_dis == 0 && + err_blks == 0 && ber_cnt == 0 && + spu_status1.s.rcv_lnk) { + result.s.init_success = 1; +#ifdef DEBUG_BGX + debug("Receiving remote-fault ordered sets %d:BGX(%d,%d)\n", + xi.node, xi.interface, index); +#endif + + } else { + res = __cvmx_helper_bgx_xaui_link_init(index, xiface); + if (res == -1) { +#ifdef DEBUG_BGX + debug("Failed to get %d:BGX(%d,%d) link\n", + xi.node, xi.interface, index); +#endif + } else { +#ifdef DEBUG_BGX + debug("Link initialization successful %d:BGX(%d,%d)\n", + xi.node, xi.interface, index); +#endif + result.s.init_success = 1; + } + } + } + + return result; +} + +int __cvmx_helper_bgx_xaui_link_set(int xipd_port, + cvmx_helper_link_info_t link_info) +{ + int xiface = cvmx_helper_get_interface_num(xipd_port); + struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); + struct cvmx_xport xp = cvmx_helper_ipd_port_to_xport(xipd_port); + int node = xi.node; + int index = cvmx_helper_get_interface_index_num(xp.port); + cvmx_bgxx_smux_tx_ctl_t smu_tx_ctl; + cvmx_bgxx_smux_rx_ctl_t smu_rx_ctl; + cvmx_bgxx_spux_status1_t spu_status1; + cvmx_helper_interface_mode_t mode; + cvmx_bgxx_cmrx_config_t cmr_config; + cvmx_bgxx_spux_misc_control_t spu_misc_control; + + if (debug) + debug("%s: interface %u:%d/%d\n", __func__, xi.node, + xi.interface, index); + + mode = cvmx_helper_bgx_get_mode(xiface, index); + if (mode == CVMX_HELPER_INTERFACE_MODE_RGMII) + return __cvmx_helper_bgx_sgmii_link_set(xipd_port, link_info); + + /* Reading current rx/tx link status */ + smu_tx_ctl.u64 = + csr_rd_node(node, CVMX_BGXX_SMUX_TX_CTL(index, xi.interface)); + smu_rx_ctl.u64 = + csr_rd_node(node, CVMX_BGXX_SMUX_RX_CTL(index, xi.interface)); + spu_status1.u64 = + csr_rd_node(node, CVMX_BGXX_SPUX_STATUS1(index, xi.interface)); + /* Reading tx/rx packet enables */ + cmr_config.u64 = csr_rd_node( + xi.node, CVMX_BGXX_CMRX_CONFIG(index, xi.interface)); + spu_misc_control.u64 = csr_rd_node( + xi.node, CVMX_BGXX_SPUX_MISC_CONTROL(index, xi.interface)); + + /* If the link shouldn't be up, then just return */ + if (!link_info.s.link_up) + return 0; + + /* Do nothing if both RX and TX are happy and packet + * transmission/reception is enabled + */ + if (smu_tx_ctl.s.ls == 0 && smu_rx_ctl.s.status == 0 && + cmr_config.s.data_pkt_tx_en == 1 && + cmr_config.s.data_pkt_rx_en == 1 && + spu_misc_control.s.rx_packet_dis == 0 && spu_status1.s.rcv_lnk) + return 0; + + /* Bring the link up */ + return __cvmx_helper_bgx_xaui_link_init(index, xiface); +} + +int __cvmx_helper_bgx_xaui_configure_loopback(int xipd_port, + int enable_internal, + int enable_external) +{ + int xiface = cvmx_helper_get_interface_num(xipd_port); + struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); + struct cvmx_xport xp = cvmx_helper_ipd_port_to_xport(xipd_port); + int node = xi.node; + int index = cvmx_helper_get_interface_index_num(xp.port); + cvmx_bgxx_spux_control1_t spu_control1; + cvmx_bgxx_smux_ext_loopback_t smu_ext_loopback; + + if (debug) + debug("%s: interface %u:%d/%d\n", __func__, xi.node, + xi.interface, index); + + /* INT_BEAT_GEN must be set for loopback if the QLMs are not clocked. + * Set it whenever we use internal loopback + */ + if (enable_internal) { + cvmx_bgxx_cmrx_config_t cmr_config; + + cmr_config.u64 = csr_rd_node( + node, CVMX_BGXX_CMRX_CONFIG(index, xi.interface)); + cmr_config.s.int_beat_gen = 1; + csr_wr_node(node, CVMX_BGXX_CMRX_CONFIG(index, xi.interface), + cmr_config.u64); + } + /* Set the internal loop */ + spu_control1.u64 = + csr_rd_node(node, CVMX_BGXX_SPUX_CONTROL1(index, xi.interface)); + spu_control1.s.loopbck = enable_internal; + csr_wr_node(node, CVMX_BGXX_SPUX_CONTROL1(index, xi.interface), + spu_control1.u64); + /* Set the external loop */ + smu_ext_loopback.u64 = csr_rd_node( + node, CVMX_BGXX_SMUX_EXT_LOOPBACK(index, xi.interface)); + smu_ext_loopback.s.en = enable_external; + csr_wr_node(node, CVMX_BGXX_SMUX_EXT_LOOPBACK(index, xi.interface), + smu_ext_loopback.u64); + + return __cvmx_helper_bgx_xaui_link_init(index, xiface); +} + +int __cvmx_helper_bgx_mixed_enable(int xiface) +{ + int index; + int num_ports = cvmx_helper_ports_on_interface(xiface); + cvmx_helper_interface_mode_t mode; + + for (index = 0; index < num_ports; index++) { + int xipd_port, phy_pres = 0; + + if (!cvmx_helper_is_port_valid(xiface, index)) + continue; + + mode = cvmx_helper_bgx_get_mode(xiface, index); + + xipd_port = cvmx_helper_get_ipd_port(xiface, index); + + if (mode == CVMX_HELPER_INTERFACE_MODE_RXAUI && + (cvmx_helper_get_port_phy_present(xiface, index))) + phy_pres = 1; + + if (__cvmx_helper_bgx_port_init(xipd_port, phy_pres)) + continue; + + /* For RGMII interface, initialize the link after PKO is setup */ + if (mode == CVMX_HELPER_INTERFACE_MODE_RGMII) + continue; + /* Call SGMII init code for lmac_type = 0|5 */ + else if (mode == CVMX_HELPER_INTERFACE_MODE_SGMII) { + int do_link_set = 1; + + if (do_link_set) + __cvmx_helper_bgx_sgmii_link_set( + xipd_port, + __cvmx_helper_bgx_sgmii_link_get( + xipd_port)); + /* All other lmac type call XAUI init code */ + } else { + int res; + struct cvmx_xiface xi = + cvmx_helper_xiface_to_node_interface(xiface); + static int count + [CVMX_MAX_NODES][CVMX_HELPER_MAX_IFACE] + [CVMX_HELPER_CFG_MAX_PORT_PER_IFACE] = { + [0 ... CVMX_MAX_NODES - + 1][0 ... CVMX_HELPER_MAX_IFACE - + 1] = { [0 ... CVMX_HELPER_CFG_MAX_PORT_PER_IFACE - + 1] = 0 } + }; + +retry_link: + res = __cvmx_helper_bgx_xaui_link_init(index, xiface); + /* RX Equalization or autonegotiation can take little + * longer retry the link maybe 5 times for now + */ + if (res == -1 && + count[xi.node][xi.interface][index] < 5) { + count[xi.node][xi.interface][index]++; + goto retry_link; + } + + if (res == -1) { +#ifdef DEBUG_BGX + debug("Failed to get %d:BGX(%d,%d) link\n", + xi.node, xi.interface, index); +#endif + continue; + } + } + } + return 0; +} + +cvmx_helper_link_info_t __cvmx_helper_bgx_mixed_link_get(int xipd_port) +{ + int xiface = cvmx_helper_get_interface_num(xipd_port); + int index = cvmx_helper_get_interface_index_num(xipd_port); + cvmx_helper_interface_mode_t mode; + + mode = cvmx_helper_bgx_get_mode(xiface, index); + if (mode == CVMX_HELPER_INTERFACE_MODE_SGMII || + mode == CVMX_HELPER_INTERFACE_MODE_RGMII) + return __cvmx_helper_bgx_sgmii_link_get(xipd_port); + else + return __cvmx_helper_bgx_xaui_link_get(xipd_port); +} + +int __cvmx_helper_bgx_mixed_link_set(int xipd_port, + cvmx_helper_link_info_t link_info) +{ + int xiface = cvmx_helper_get_interface_num(xipd_port); + int index = cvmx_helper_get_interface_index_num(xipd_port); + cvmx_helper_interface_mode_t mode; + + mode = cvmx_helper_bgx_get_mode(xiface, index); + if (mode == CVMX_HELPER_INTERFACE_MODE_SGMII || + mode == CVMX_HELPER_INTERFACE_MODE_RGMII) + return __cvmx_helper_bgx_sgmii_link_set(xipd_port, link_info); + else + return __cvmx_helper_bgx_xaui_link_set(xipd_port, link_info); +} + +int __cvmx_helper_bgx_mixed_configure_loopback(int xipd_port, + int enable_internal, + int enable_external) +{ + int xiface = cvmx_helper_get_interface_num(xipd_port); + int index = cvmx_helper_get_interface_index_num(xipd_port); + cvmx_helper_interface_mode_t mode; + + mode = cvmx_helper_bgx_get_mode(xiface, index); + if (mode == CVMX_HELPER_INTERFACE_MODE_SGMII || + mode == CVMX_HELPER_INTERFACE_MODE_RGMII) + return __cvmx_helper_bgx_sgmii_configure_loopback( + xipd_port, enable_internal, enable_external); + else + return __cvmx_helper_bgx_xaui_configure_loopback( + xipd_port, enable_internal, enable_external); +} + +/** + * @INTERNAL + * Configure Priority-Based Flow Control (a.k.a. PFC/CBFC) + * on a specific BGX interface/port. + */ +void __cvmx_helper_bgx_xaui_config_pfc(unsigned int node, + unsigned int interface, + unsigned int index, bool pfc_enable) +{ + int xiface = cvmx_helper_node_interface_to_xiface(node, interface); + struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); + cvmx_bgxx_smux_cbfc_ctl_t cbfc_ctl; + + if (debug) + debug("%s: interface %u:%d/%d\n", __func__, xi.node, + xi.interface, index); + + cbfc_ctl.u64 = + csr_rd_node(node, CVMX_BGXX_SMUX_CBFC_CTL(index, xi.interface)); + + /* Enable all PFC controls if requiested */ + cbfc_ctl.s.rx_en = pfc_enable; + cbfc_ctl.s.tx_en = pfc_enable; + if (debug) + debug("%s: CVMX_BGXX_SMUX_CBFC_CTL(%d,%d)=%#llx\n", __func__, + index, xi.interface, (unsigned long long)cbfc_ctl.u64); + csr_wr_node(node, CVMX_BGXX_SMUX_CBFC_CTL(index, xi.interface), + cbfc_ctl.u64); +} + +/** + * Function to control the generation of FCS, padding by the BGX + * + */ +void cvmx_helper_bgx_tx_options(unsigned int node, unsigned int interface, + unsigned int index, bool fcs_enable, + bool pad_enable) +{ + cvmx_bgxx_cmrx_config_t cmr_config; + cvmx_bgxx_gmp_gmi_txx_append_t gmp_txx_append; + cvmx_bgxx_gmp_gmi_txx_min_pkt_t gmp_min_pkt; + cvmx_bgxx_smux_tx_min_pkt_t smu_min_pkt; + cvmx_bgxx_smux_tx_append_t smu_tx_append; + int xiface = cvmx_helper_node_interface_to_xiface(node, interface); + struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); + + if (!cvmx_helper_is_port_valid(xiface, index)) + return; + + if (debug) + debug("%s: interface %u:%d/%d, fcs: %s, pad: %s\n", __func__, + xi.node, xi.interface, index, + fcs_enable ? "true" : "false", + pad_enable ? "true" : "false"); + + cmr_config.u64 = + csr_rd_node(node, CVMX_BGXX_CMRX_CONFIG(index, xi.interface)); + + (void)cmr_config; /* In case we need LMAC_TYPE later */ + + /* Setting options for both BGX subsystems, regardless of LMAC type */ + + /* Set GMP (SGMII) Tx options */ + gmp_min_pkt.u64 = 0; + /* per HRM Sec 34.3.4.4 */ + gmp_min_pkt.s.min_size = 59; + csr_wr_node(node, CVMX_BGXX_GMP_GMI_TXX_MIN_PKT(index, xi.interface), + gmp_min_pkt.u64); + gmp_txx_append.u64 = csr_rd_node( + node, CVMX_BGXX_GMP_GMI_TXX_APPEND(index, xi.interface)); + gmp_txx_append.s.fcs = fcs_enable; + gmp_txx_append.s.pad = pad_enable; + csr_wr_node(node, CVMX_BGXX_GMP_GMI_TXX_APPEND(index, xi.interface), + gmp_txx_append.u64); + + /* Set SMUX (XAUI/XFI) Tx options */ + /* HRM Sec 33.3.4.3 should read 64 */ + smu_min_pkt.u64 = 0; + smu_min_pkt.s.min_size = 0x40; + csr_wr_node(node, CVMX_BGXX_SMUX_TX_MIN_PKT(index, xi.interface), + smu_min_pkt.u64); + smu_tx_append.u64 = csr_rd_node( + node, CVMX_BGXX_SMUX_TX_APPEND(index, xi.interface)); + smu_tx_append.s.fcs_d = fcs_enable; /* Set data-packet FCS */ + smu_tx_append.s.pad = pad_enable; + csr_wr_node(node, CVMX_BGXX_SMUX_TX_APPEND(index, xi.interface), + smu_tx_append.u64); +} + +/** + * Set mac for the ipd_port + * + * @param xipd_port ipd_port to set the mac + * @param bcst If set, accept all broadcast packets + * @param mcst Multicast mode + * 0 = Force reject all multicast packets + * 1 = Force accept all multicast packets + * 2 = use the address filter CAM. + * @param mac mac address for the ipd_port, or 0 to disable MAC filtering + */ +void cvmx_helper_bgx_set_mac(int xipd_port, int bcst, int mcst, u64 mac) +{ + int xiface = cvmx_helper_get_interface_num(xipd_port); + struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); + int node = xi.node; + int index; + cvmx_bgxx_cmr_rx_adrx_cam_t adr_cam; + cvmx_bgxx_cmrx_rx_adr_ctl_t adr_ctl; + cvmx_bgxx_cmrx_config_t cmr_config; + int saved_state_tx, saved_state_rx; + + index = cvmx_helper_get_interface_index_num(xipd_port); + + if (!cvmx_helper_is_port_valid(xiface, index)) + return; + + if (debug) + debug("%s: interface %u:%d/%d\n", __func__, xi.node, + xi.interface, index); + + cmr_config.u64 = + csr_rd_node(node, CVMX_BGXX_CMRX_CONFIG(index, xi.interface)); + saved_state_tx = cmr_config.s.data_pkt_tx_en; + saved_state_rx = cmr_config.s.data_pkt_rx_en; + cmr_config.s.data_pkt_tx_en = 0; + cmr_config.s.data_pkt_rx_en = 0; + csr_wr_node(node, CVMX_BGXX_CMRX_CONFIG(index, xi.interface), + cmr_config.u64); + + /* Set the mac */ + adr_cam.u64 = 0; + adr_cam.s.id = index; + + if (mac != 0ull) + adr_cam.s.en = 1; + adr_cam.s.adr = mac; + + csr_wr_node(node, CVMX_BGXX_CMR_RX_ADRX_CAM(index * 8, xi.interface), + adr_cam.u64); + + adr_ctl.u64 = csr_rd_node( + node, CVMX_BGXX_CMRX_RX_ADR_CTL(index, xi.interface)); + if (mac != 0ull) + adr_ctl.s.cam_accept = + 1; /* Accept the packet on DMAC CAM address */ + else + adr_ctl.s.cam_accept = 0; /* No filtering, promiscuous */ + + adr_ctl.s.mcst_mode = mcst; /* Use the address filter CAM */ + adr_ctl.s.bcst_accept = bcst; /* Accept all broadcast packets */ + csr_wr_node(node, CVMX_BGXX_CMRX_RX_ADR_CTL(index, xi.interface), + adr_ctl.u64); + /* Set SMAC for PAUSE frames */ + csr_wr_node(node, CVMX_BGXX_GMP_GMI_SMACX(index, xi.interface), mac); + + /* Restore back the interface state */ + cmr_config.s.data_pkt_tx_en = saved_state_tx; + cmr_config.s.data_pkt_rx_en = saved_state_rx; + csr_wr_node(node, CVMX_BGXX_CMRX_CONFIG(index, xi.interface), + cmr_config.u64); + + /* Wait 100ms after bringing up the link to give the PHY some time */ + if (cmr_config.s.enable) { + cvmx_helper_interface_mode_t mode; + + mode = cvmx_helper_bgx_get_mode(xiface, index); + __cvmx_helper_bgx_interface_enable_delay(mode); + } +} + +/** + * Disables the sending of flow control (pause) frames on the specified + * BGX port(s). + * + * @param xiface Which xiface + * @param port_mask Mask (4bits) of which ports on the interface to disable + * backpressure on. + * 1 => disable backpressure + * 0 => enable backpressure + * + * @return 0 on success + * -1 on error + * + * FIXME: Should change the API to handle a single port in every + * invocation, for consistency with other API calls. + */ +int cvmx_bgx_set_backpressure_override(int xiface, unsigned int port_mask) +{ + struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); + cvmx_bgxx_cmr_rx_ovr_bp_t rx_ovr_bp; + int node = xi.node; + + if (xi.interface >= CVMX_HELPER_MAX_GMX) + return 0; + + if (debug) + debug("%s: interface %u:%d port_mask=%#x\n", __func__, xi.node, + xi.interface, port_mask); + + /* Check for valid arguments */ + rx_ovr_bp.u64 = 0; + rx_ovr_bp.s.en = port_mask; /* Per port Enable back pressure override */ + rx_ovr_bp.s.ign_fifo_bp = + port_mask; /* Ignore the RX FIFO full when computing BP */ + + csr_wr_node(node, CVMX_BGXX_CMR_RX_OVR_BP(xi.interface), rx_ovr_bp.u64); + return 0; +} + +int cvmx_bgx_set_flowctl_mode(int xipd_port, cvmx_qos_proto_t qos, + cvmx_qos_pkt_mode_t fc_mode) +{ + int node, xiface, iface, index, mode; + struct cvmx_xiface xi; + const struct { + int bck; + int drp; + } fcmode[4] = { [CVMX_QOS_PKT_MODE_HWONLY] = { 1, 1 }, + [CVMX_QOS_PKT_MODE_SWONLY] = { 0, 0 }, + [CVMX_QOS_PKT_MODE_HWSW] = { 1, 0 }, + [CVMX_QOS_PKT_MODE_DROP] = { 0, 1 } }; + + xiface = cvmx_helper_get_interface_num(xipd_port); + xi = cvmx_helper_xiface_to_node_interface(xiface); + node = xi.node; + iface = xi.interface; + + if (xi.interface >= CVMX_HELPER_MAX_GMX) + return 0; + + index = cvmx_helper_get_interface_index_num(xipd_port); + mode = cvmx_helper_bgx_get_mode(xiface, index); + switch (mode) { + case CVMX_HELPER_INTERFACE_MODE_10G_KR: + case CVMX_HELPER_INTERFACE_MODE_40G_KR4: + case CVMX_HELPER_INTERFACE_MODE_XLAUI: + case CVMX_HELPER_INTERFACE_MODE_XFI: + case CVMX_HELPER_INTERFACE_MODE_RXAUI: + case CVMX_HELPER_INTERFACE_MODE_XAUI: { + cvmx_bgxx_smux_tx_ctl_t txctl; + cvmx_bgxx_smux_cbfc_ctl_t cbfc; + cvmx_bgxx_smux_rx_frm_ctl_t frmctl; + cvmx_bgxx_smux_hg2_control_t hg2ctl; + + txctl.u64 = + csr_rd_node(node, CVMX_BGXX_SMUX_TX_CTL(index, iface)); + cbfc.u64 = csr_rd_node(node, + CVMX_BGXX_SMUX_CBFC_CTL(index, iface)); + frmctl.u64 = csr_rd_node( + node, CVMX_BGXX_SMUX_RX_FRM_CTL(index, iface)); + hg2ctl.u64 = csr_rd_node( + node, CVMX_BGXX_SMUX_HG2_CONTROL(index, iface)); + switch (qos) { + case CVMX_QOS_PROTO_PAUSE: + cbfc.u64 = 0; + hg2ctl.u64 = 0; + frmctl.s.ctl_bck = fcmode[fc_mode].bck; + frmctl.s.ctl_drp = fcmode[fc_mode].drp; + frmctl.s.ctl_mcst = 1; + txctl.s.l2p_bp_conv = 1; + break; + case CVMX_QOS_PROTO_PFC: + hg2ctl.u64 = 0; + hg2ctl.s.logl_en = 0xff; + frmctl.s.ctl_bck = fcmode[fc_mode].bck; + frmctl.s.ctl_drp = fcmode[fc_mode].drp; + frmctl.s.ctl_mcst = 1; + cbfc.s.bck_en = fcmode[fc_mode].bck; + cbfc.s.drp_en = fcmode[fc_mode].drp; + cbfc.s.phys_en = 0; + cbfc.s.logl_en = 0xff; + cbfc.s.tx_en = 1; + cbfc.s.rx_en = 1; + break; + case CVMX_QOS_PROTO_NONE: + cbfc.u64 = 0; + hg2ctl.u64 = 0; + frmctl.s.ctl_bck = fcmode[CVMX_QOS_PKT_MODE_DROP].bck; + frmctl.s.ctl_drp = fcmode[CVMX_QOS_PKT_MODE_DROP].drp; + txctl.s.l2p_bp_conv = 0; + break; + default: + break; + } + csr_wr_node(node, CVMX_BGXX_SMUX_CBFC_CTL(index, iface), + cbfc.u64); + csr_wr_node(node, CVMX_BGXX_SMUX_RX_FRM_CTL(index, iface), + frmctl.u64); + csr_wr_node(node, CVMX_BGXX_SMUX_HG2_CONTROL(index, iface), + hg2ctl.u64); + csr_wr_node(node, CVMX_BGXX_SMUX_TX_CTL(index, iface), + txctl.u64); + break; + } + case CVMX_HELPER_INTERFACE_MODE_SGMII: + case CVMX_HELPER_INTERFACE_MODE_RGMII: { + cvmx_bgxx_gmp_gmi_rxx_frm_ctl_t gmi_frmctl; + + gmi_frmctl.u64 = csr_rd_node( + node, CVMX_BGXX_GMP_GMI_RXX_FRM_CTL(index, iface)); + switch (qos) { + case CVMX_QOS_PROTO_PAUSE: + gmi_frmctl.s.ctl_bck = fcmode[fc_mode].bck; + gmi_frmctl.s.ctl_drp = fcmode[fc_mode].drp; + gmi_frmctl.s.ctl_mcst = 1; + break; + case CVMX_QOS_PROTO_NONE: + gmi_frmctl.s.ctl_bck = + fcmode[CVMX_QOS_PKT_MODE_DROP].bck; + gmi_frmctl.s.ctl_drp = + fcmode[CVMX_QOS_PKT_MODE_DROP].drp; + break; + default: + break; + } + csr_wr_node(node, CVMX_BGXX_GMP_GMI_RXX_FRM_CTL(index, iface), + gmi_frmctl.u64); + } + } /*switch*/ + + return 0; +} diff --git a/arch/mips/mach-octeon/cvmx-helper-board.c b/arch/mips/mach-octeon/cvmx-helper-board.c new file mode 100644 index 0000000..6dcc4e5 --- /dev/null +++ b/arch/mips/mach-octeon/cvmx-helper-board.c @@ -0,0 +1,1824 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018-2022 Marvell International Ltd. + * + * Helper functions to abstract board specific data about + * network ports from the rest of the cvmx-helper files. + */ + +#include <i2c.h> +#include <log.h> +#include <malloc.h> +#include <net.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/octeon_fdt.h> +#include <mach/cvmx-helper.h> +#include <mach/cvmx-helper-board.h> +#include <mach/cvmx-helper-cfg.h> +#include <mach/cvmx-helper-fdt.h> +#include <mach/cvmx-helper-gpio.h> + +#include <mach/cvmx-smix-defs.h> +#include <mach/cvmx-mdio.h> +#include <mach/cvmx-qlm.h> + +DECLARE_GLOBAL_DATA_PTR; + +static bool sfp_parsed; + +static int __cvmx_helper_78xx_parse_phy(struct cvmx_phy_info *phy_info, + int ipd_port); +static int __get_phy_info_from_dt(cvmx_phy_info_t *phy_info, int ipd_port); + +/** + * Writes to a Microsemi VSC7224 16-bit register + * + * @param[in] i2c_bus i2c bus data structure (must be enabled) + * @param addr Address of VSC7224 on the i2c bus + * @param reg 8-bit register number to write to + * @param val 16-bit value to write + * + * @return 0 for success + */ +static int cvmx_write_vsc7224_reg(const struct cvmx_fdt_i2c_bus_info *i2c_bus, + u8 addr, u8 reg, u16 val) +{ + struct udevice *dev; + u8 buffer[2]; + int ret; + + ret = i2c_get_chip(i2c_bus->i2c_bus, addr, 1, &dev); + if (ret) { + debug("Cannot find I2C device: %d\n", ret); + return -1; + } + + ret = dm_i2c_write(dev, reg, buffer, 2); + if (ret) { + debug("Cannot write I2C device: %d\n", ret); + return -1; + } + + return 0; +} + +/** + * Writes to a Microsemi VSC7224 16-bit register + * + * @param[in] i2c_bus i2c bus data structure (must be enabled) + * @param addr Address of VSC7224 on the i2c bus + * @param reg 8-bit register number to write to + * + * @return 16-bit value or error if < 0 + */ +static int cvmx_read_vsc7224_reg(const struct cvmx_fdt_i2c_bus_info *i2c_bus, + u8 addr, u8 reg) +{ + struct udevice *dev; + u8 buffer[2]; + int ret; + + ret = i2c_get_chip(i2c_bus->i2c_bus, addr, 1, &dev); + if (ret) { + debug("Cannot find I2C device: %d\n", ret); + return -1; + } + + ret = dm_i2c_read(dev, reg, buffer, 2); + if (ret) { + debug("Cannot read I2C device: %d\n", ret); + return -1; + } + + return (buffer[0] << 8) | buffer[1]; +} + +/** + * Function called whenever mod_abs/mod_prs has changed for Microsemi VSC7224 + * + * @param sfp pointer to SFP data structure + * @param val 1 if absent, 0 if present, otherwise not set + * @param data user-defined data + * + * @return 0 for success, -1 on error + */ +int cvmx_sfp_vsc7224_mod_abs_changed(struct cvmx_fdt_sfp_info *sfp, int val, + void *data) +{ + int err; + struct cvmx_sfp_mod_info *mod_info; + int length; + struct cvmx_vsc7224 *vsc7224; + struct cvmx_vsc7224_chan *vsc7224_chan; + struct cvmx_vsc7224_tap *taps, *match = NULL; + int i; + + debug("%s(%s, %d, %p): Module %s\n", __func__, sfp->name, val, data, + val ? "absent" : "present"); + if (val) + return 0; + + /* We're here if we detect that the module is now present */ + err = cvmx_sfp_read_i2c_eeprom(sfp); + if (err) { + debug("%s: Error reading the SFP module eeprom for %s\n", + __func__, sfp->name); + return err; + } + mod_info = &sfp->sfp_info; + + if (!mod_info->valid || !sfp->valid) { + debug("%s: Module data is invalid\n", __func__); + return -1; + } + + vsc7224_chan = sfp->vsc7224_chan; + while (vsc7224_chan) { + /* We don't do any rx tuning */ + if (!vsc7224_chan->is_tx) { + vsc7224_chan = vsc7224_chan->next; + continue; + } + + /* Walk through all the channels */ + taps = vsc7224_chan->taps; + if (mod_info->limiting) + length = 0; + else + length = mod_info->max_copper_cable_len; + debug("%s: limiting: %d, length: %d\n", __func__, + mod_info->limiting, length); + + /* Find a matching length in the taps table */ + for (i = 0; i < vsc7224_chan->num_taps; i++) { + if (length >= taps->len) + match = taps; + taps++; + } + if (!match) { + debug("%s(%s, %d, %p): Error: no matching tap for length %d\n", + __func__, sfp->name, val, data, length); + return -1; + } + debug("%s(%s): Applying %cx taps to vsc7224 %s:%d for cable length %d+\n", + __func__, sfp->name, vsc7224_chan->is_tx ? 't' : 'r', + vsc7224_chan->vsc7224->name, vsc7224_chan->lane, + match->len); + /* Program the taps */ + vsc7224 = vsc7224_chan->vsc7224; + cvmx_write_vsc7224_reg(vsc7224->i2c_bus, vsc7224->i2c_addr, + 0x7f, vsc7224_chan->lane); + if (!vsc7224_chan->maintap_disable) + cvmx_write_vsc7224_reg(vsc7224->i2c_bus, + vsc7224->i2c_addr, 0x99, + match->main_tap); + if (!vsc7224_chan->pretap_disable) + cvmx_write_vsc7224_reg(vsc7224->i2c_bus, + vsc7224->i2c_addr, 0x9a, + match->pre_tap); + if (!vsc7224_chan->posttap_disable) + cvmx_write_vsc7224_reg(vsc7224->i2c_bus, + vsc7224->i2c_addr, 0x9b, + match->post_tap); + + /* Re-use val and disable taps if needed */ + if (vsc7224_chan->maintap_disable || + vsc7224_chan->pretap_disable || + vsc7224_chan->posttap_disable) { + val = cvmx_read_vsc7224_reg(vsc7224->i2c_bus, + vsc7224->i2c_addr, 0x97); + if (vsc7224_chan->maintap_disable) + val |= 0x800; + if (vsc7224_chan->pretap_disable) + val |= 0x1000; + if (vsc7224_chan->posttap_disable) + val |= 0x400; + cvmx_write_vsc7224_reg(vsc7224->i2c_bus, + vsc7224->i2c_addr, 0x97, val); + } + vsc7224_chan = vsc7224_chan->next; + } + + return err; +} + +/** + * Update the mod_abs and error LED + * + * @param ipd_port ipd port number + * @param link link information + */ +static void __cvmx_helper_update_sfp(int ipd_port, + struct cvmx_fdt_sfp_info *sfp_info, + cvmx_helper_link_info_t link) +{ + debug("%s(%d): checking mod_abs\n", __func__, ipd_port); + + cvmx_sfp_check_mod_abs(sfp_info, sfp_info->mod_abs_data); +} + +static void cvmx_sfp_update_link(struct cvmx_fdt_sfp_info *sfp, + cvmx_helper_link_info_t link) +{ + while (sfp) { + debug("%s(%s): checking mod_abs\n", __func__, sfp->name); + if (link.s.link_up && sfp->last_mod_abs) + cvmx_sfp_check_mod_abs(sfp, sfp->mod_abs_data); + sfp = sfp->next_iface_sfp; + } +} + +/** + * @INTERNAL + * This function is used ethernet ports link speed. This functions uses the + * device tree information to determine the phy address and type of PHY. + * The only supproted PHYs are Marvell and Broadcom. + * + * @param ipd_port IPD input port associated with the port we want to get link + * status for. + * + * @return The ports link status. If the link isn't fully resolved, this must + * return zero. + */ +cvmx_helper_link_info_t __cvmx_helper_board_link_get_from_dt(int ipd_port) +{ + cvmx_helper_link_info_t result; + cvmx_phy_info_t *phy_info = NULL; + cvmx_phy_info_t local_phy_info; + int xiface = 0, index = 0; + bool use_inband = false; + struct cvmx_fdt_sfp_info *sfp_info; + const void *fdt_addr = CASTPTR(const void *, gd->fdt_blob); + + result.u64 = 0; + + if (ipd_port >= 0) { + int mode; + + xiface = cvmx_helper_get_interface_num(ipd_port); + index = cvmx_helper_get_interface_index_num(ipd_port); + mode = cvmx_helper_interface_get_mode(xiface); + if (!cvmx_helper_get_port_autonegotiation(xiface, index)) { + result.s.link_up = 1; + result.s.full_duplex = 1; + switch (mode) { + case CVMX_HELPER_INTERFACE_MODE_RGMII: + case CVMX_HELPER_INTERFACE_MODE_GMII: + case CVMX_HELPER_INTERFACE_MODE_SGMII: + case CVMX_HELPER_INTERFACE_MODE_QSGMII: + case CVMX_HELPER_INTERFACE_MODE_AGL: + case CVMX_HELPER_INTERFACE_MODE_SPI: + if (OCTEON_IS_MODEL(OCTEON_CN70XX)) { + struct cvmx_xiface xi = + cvmx_helper_xiface_to_node_interface( + xiface); + u64 gbaud = cvmx_qlm_get_gbaud_mhz(0); + + result.s.speed = gbaud * 8 / 10; + if (cvmx_qlm_get_dlm_mode( + 0, xi.interface) == + CVMX_QLM_MODE_SGMII) + result.s.speed >>= 1; + else + result.s.speed >>= 2; + } else { + result.s.speed = 1000; + } + break; + case CVMX_HELPER_INTERFACE_MODE_RXAUI: + case CVMX_HELPER_INTERFACE_MODE_XAUI: + case CVMX_HELPER_INTERFACE_MODE_10G_KR: + case CVMX_HELPER_INTERFACE_MODE_XFI: + result.s.speed = 10000; + break; + case CVMX_HELPER_INTERFACE_MODE_XLAUI: + case CVMX_HELPER_INTERFACE_MODE_40G_KR4: + result.s.speed = 40000; + break; + default: + break; + } + + sfp_info = cvmx_helper_cfg_get_sfp_info(xiface, index); + /* Initialize the SFP info if it hasn't already been + * done. + */ + if (!sfp_info && !sfp_parsed) { + cvmx_sfp_parse_device_tree(fdt_addr); + sfp_parsed = true; + cvmx_sfp_read_all_modules(); + sfp_info = cvmx_helper_cfg_get_sfp_info(xiface, + index); + } + /* If the link is down or the link is up but we still + * register the module as being absent, re-check + * mod_abs. + */ + cvmx_sfp_update_link(sfp_info, result); + + cvmx_helper_update_link_led(xiface, index, result); + + return result; + } + phy_info = cvmx_helper_get_port_phy_info(xiface, index); + if (!phy_info) { + debug("%s: phy info not saved in config, allocating for 0x%x:%d\n", + __func__, xiface, index); + + phy_info = (cvmx_phy_info_t *)cvmx_bootmem_alloc( + sizeof(*phy_info), 0); + if (!phy_info) { + debug("%s: Out of memory\n", __func__); + return result; + } + memset(phy_info, 0, sizeof(*phy_info)); + phy_info->phy_addr = -1; + debug("%s: Setting phy info for 0x%x:%d to %p\n", + __func__, xiface, index, phy_info); + cvmx_helper_set_port_phy_info(xiface, index, phy_info); + } + } else { + /* For management ports we don't store the PHY information + * so we use a local copy instead. + */ + phy_info = &local_phy_info; + memset(phy_info, 0, sizeof(*phy_info)); + phy_info->phy_addr = -1; + } + + if (phy_info->phy_addr == -1) { + if (octeon_has_feature(OCTEON_FEATURE_BGX)) { + if (__cvmx_helper_78xx_parse_phy(phy_info, ipd_port)) { + phy_info->phy_addr = -1; + use_inband = true; + } + } else if (__get_phy_info_from_dt(phy_info, ipd_port) < 0) { + phy_info->phy_addr = -1; + use_inband = true; + } + } + + /* If we can't get the PHY info from the device tree then try + * the inband state. + */ + if (use_inband) { + result.s.full_duplex = 1; + result.s.link_up = 1; + result.s.speed = 1000; + return result; + } + + if (phy_info->phy_addr < 0) + return result; + + if (phy_info->link_function) + result = phy_info->link_function(phy_info); + else + result = cvmx_helper_link_get(ipd_port); + + sfp_info = cvmx_helper_cfg_get_sfp_info(xiface, index); + while (sfp_info) { + /* If the link is down or the link is up but we still register + * the module as being absent, re-check mod_abs. + */ + if (!result.s.link_up || + (result.s.link_up && sfp_info->last_mod_abs)) + __cvmx_helper_update_sfp(ipd_port, sfp_info, result); + sfp_info = sfp_info->next_iface_sfp; + } + + return result; +} + +cvmx_helper_link_info_t __cvmx_helper_board_link_get(int ipd_port) +{ + cvmx_helper_link_info_t result; + + /* Unless we fix it later, all links are defaulted to down */ + result.u64 = 0; + + return __cvmx_helper_board_link_get_from_dt(ipd_port); +} + +void cvmx_helper_update_link_led(int xiface, int index, + cvmx_helper_link_info_t result) +{ +} + +void cvmx_helper_leds_show_error(struct cvmx_phy_gpio_leds *leds, bool error) +{ +} + +int __cvmx_helper_board_interface_probe(int interface, int supported_ports) +{ + return supported_ports; +} + +/** + * Returns the Ethernet node offset in the device tree + * + * @param fdt_addr - pointer to flat device tree in memory + * @param aliases - offset of alias in device tree + * @param ipd_port - ipd port number to look up + * + * @returns offset of Ethernet node if >= 0, error if -1 + */ +int __pip_eth_node(const void *fdt_addr, int aliases, int ipd_port) +{ + char name_buffer[20]; + const char *pip_path; + int pip, iface, eth; + int interface_num = cvmx_helper_get_interface_num(ipd_port); + int interface_index = cvmx_helper_get_interface_index_num(ipd_port); + cvmx_helper_interface_mode_t interface_mode = + cvmx_helper_interface_get_mode(interface_num); + + /* The following are not found in the device tree */ + switch (interface_mode) { + case CVMX_HELPER_INTERFACE_MODE_ILK: + case CVMX_HELPER_INTERFACE_MODE_LOOP: + case CVMX_HELPER_INTERFACE_MODE_SRIO: + debug("ERROR: No node expected for interface: %d, port: %d, mode: %s\n", + interface_index, ipd_port, + cvmx_helper_interface_mode_to_string(interface_mode)); + return -1; + default: + break; + } + pip_path = (const char *)fdt_getprop(fdt_addr, aliases, "pip", NULL); + if (!pip_path) { + debug("ERROR: pip path not found in device tree\n"); + return -1; + } + pip = fdt_path_offset(fdt_addr, pip_path); + debug("ipdd_port=%d pip_path=%s pip=%d ", ipd_port, pip_path, pip); + if (pip < 0) { + debug("ERROR: pip not found in device tree\n"); + return -1; + } + snprintf(name_buffer, sizeof(name_buffer), "interface@%d", + interface_num); + iface = fdt_subnode_offset(fdt_addr, pip, name_buffer); + debug("iface=%d ", iface); + if (iface < 0) { + debug("ERROR : pip intf %d not found in device tree\n", + interface_num); + return -1; + } + snprintf(name_buffer, sizeof(name_buffer), "ethernet@%x", + interface_index); + eth = fdt_subnode_offset(fdt_addr, iface, name_buffer); + debug("eth=%d\n", eth); + if (eth < 0) { + debug("ERROR : pip interface@%d ethernet@%d not found in device tree\n", + interface_num, interface_index); + return -1; + } + return eth; +} + +int __mix_eth_node(const void *fdt_addr, int aliases, int interface_index) +{ + char name_buffer[20]; + const char *mix_path; + int mix; + + snprintf(name_buffer, sizeof(name_buffer), "mix%d", interface_index); + mix_path = + (const char *)fdt_getprop(fdt_addr, aliases, name_buffer, NULL); + if (!mix_path) { + debug("ERROR: mix%d path not found in device tree\n", + interface_index); + } + mix = fdt_path_offset(fdt_addr, mix_path); + if (mix < 0) { + debug("ERROR: %s not found in device tree\n", mix_path); + return -1; + } + return mix; +} + +static int __mdiobus_addr_to_unit(u32 addr) +{ + int unit = (addr >> 7) & 3; + + if (!OCTEON_IS_MODEL(OCTEON_CN68XX) && !OCTEON_IS_MODEL(OCTEON_CN78XX)) + unit >>= 1; + return unit; +} + +/** + * Parse the muxed MDIO interface information from the device tree + * + * @param phy_info - pointer to phy info data structure to update + * @param mdio_offset - offset of MDIO bus + * @param mux_offset - offset of MUX, parent of mdio_offset + * + * @return 0 for success or -1 + */ +static int __get_muxed_mdio_info_from_dt(cvmx_phy_info_t *phy_info, + int mdio_offset, int mux_offset) +{ + const void *fdt_addr = CASTPTR(const void *, gd->fdt_blob); + int phandle; + int smi_offset; + int gpio_offset; + u64 smi_addr = 0; + int len; + u32 *pgpio_handle; + int gpio_count = 0; + u32 *prop_val; + int offset; + const char *prop_name; + + debug("%s(%p, 0x%x, 0x%x)\n", __func__, phy_info, mdio_offset, + mux_offset); + + /* Get register value to put onto the GPIO lines to select */ + phy_info->gpio_value = + cvmx_fdt_get_int(fdt_addr, mdio_offset, "reg", -1); + if (phy_info->gpio_value < 0) { + debug("Could not get register value for muxed MDIO bus from DT\n"); + return -1; + } + + smi_offset = cvmx_fdt_lookup_phandle(fdt_addr, mux_offset, + "mdio-parent-bus"); + if (smi_offset < 0) { + debug("Invalid SMI offset for muxed MDIO interface in device tree\n"); + return -1; + } + smi_addr = cvmx_fdt_get_uint64(fdt_addr, smi_offset, "reg", 0); + + /* Convert SMI address to a MDIO interface */ + switch (smi_addr) { + case 0x1180000001800: + case 0x1180000003800: /* 68XX address */ + phy_info->mdio_unit = 0; + break; + case 0x1180000001900: + case 0x1180000003880: + phy_info->mdio_unit = 1; + break; + case 0x1180000003900: + phy_info->mdio_unit = 2; + break; + case 0x1180000003980: + phy_info->mdio_unit = 3; + break; + default: + phy_info->mdio_unit = 1; + break; + } + /* Find the GPIO MUX controller */ + pgpio_handle = + (u32 *)fdt_getprop(fdt_addr, mux_offset, "gpios", &len); + if (!pgpio_handle || len < 12 || (len % 12) != 0 || + len > CVMX_PHY_MUX_MAX_GPIO * 12) { + debug("Invalid GPIO for muxed MDIO controller in DT\n"); + return -1; + } + + for (gpio_count = 0; gpio_count < len / 12; gpio_count++) { + phandle = fdt32_to_cpu(pgpio_handle[gpio_count * 3]); + phy_info->gpio[gpio_count] = + fdt32_to_cpu(pgpio_handle[gpio_count * 3 + 1]); + gpio_offset = fdt_node_offset_by_phandle(fdt_addr, phandle); + if (gpio_offset < 0) { + debug("Cannot access parent GPIO node in DT\n"); + return -1; + } + if (!fdt_node_check_compatible(fdt_addr, gpio_offset, + "cavium,octeon-3860-gpio")) { + phy_info->gpio_type[gpio_count] = GPIO_OCTEON; + } else if (!fdt_node_check_compatible(fdt_addr, gpio_offset, + "nxp,pca8574")) { + /* GPIO is a TWSI GPIO unit which might sit behind + * another mux. + */ + phy_info->gpio_type[gpio_count] = GPIO_PCA8574; + prop_val = (u32 *)fdt_getprop( + fdt_addr, gpio_offset, "reg", NULL); + if (!prop_val) { + debug("Could not find TWSI address of npx pca8574 GPIO from DT\n"); + return -1; + } + /* Get the TWSI address of the GPIO unit */ + phy_info->cvmx_gpio_twsi[gpio_count] = + fdt32_to_cpu(*prop_val); + /* Get the selector on the GPIO mux if present */ + offset = fdt_parent_offset(fdt_addr, gpio_offset); + prop_val = (u32 *)fdt_getprop(fdt_addr, offset, + "reg", NULL); + if (prop_val) { + phy_info->gpio_parent_mux_select = + fdt32_to_cpu(*prop_val); + /* Go up another level */ + offset = fdt_parent_offset(fdt_addr, offset); + if (!fdt_node_check_compatible(fdt_addr, offset, + "nxp,pca9548")) { + prop_val = (u32 *)fdt_getprop( + fdt_addr, offset, "reg", NULL); + if (!prop_val) { + debug("Could not read MUX TWSI address from DT\n"); + return -1; + } + phy_info->gpio_parent_mux_twsi = + fdt32_to_cpu(*prop_val); + } + } + } else { + prop_name = (char *)fdt_getprop(fdt_addr, gpio_offset, + "compatible", NULL); + debug("Unknown GPIO type %s\n", prop_name); + return -1; + } + } + return 0; +} + +/** + * @INTERNAL + * Converts a BGX address to the node, interface and port number + * + * @param bgx_addr Address of CSR register + * + * @return node, interface and port number, will be -1 for invalid address. + */ +static struct cvmx_xiface __cvmx_bgx_reg_addr_to_xiface(u64 bgx_addr) +{ + struct cvmx_xiface xi = { -1, -1 }; + + xi.node = cvmx_csr_addr_to_node(bgx_addr); + bgx_addr = cvmx_csr_addr_strip_node(bgx_addr); + if ((bgx_addr & 0xFFFFFFFFF0000000) != 0x00011800E0000000) { + debug("%s: Invalid BGX address 0x%llx\n", __func__, + (unsigned long long)bgx_addr); + xi.node = -1; + return xi; + } + xi.interface = (bgx_addr >> 24) & 0x0F; + + return xi; +} + +static cvmx_helper_link_info_t +__get_marvell_phy_link_state(cvmx_phy_info_t *phy_info) +{ + cvmx_helper_link_info_t result; + int phy_status; + u32 phy_addr = phy_info->phy_addr; + + result.u64 = 0; + /* Set to page 0 */ + cvmx_mdio_write(phy_addr >> 8, phy_addr & 0xff, 22, 0); + /* All the speed information can be read from register 17 in one go. */ + phy_status = cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, 17); + + /* If the resolve bit 11 isn't set, see if autoneg is turned off + * (bit 12, reg 0). The resolve bit doesn't get set properly when + * autoneg is off, so force it + */ + if ((phy_status & (1 << 11)) == 0) { + int auto_status = + cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, 0); + if ((auto_status & (1 << 12)) == 0) + phy_status |= 1 << 11; + } + + /* Link is up = Speed/Duplex Resolved + RT-Link Up + G-Link Up. */ + if ((phy_status & 0x0c08) == 0x0c08) { + result.s.link_up = 1; + result.s.full_duplex = ((phy_status >> 13) & 1); + switch ((phy_status >> 14) & 3) { + case 0: /* 10 Mbps */ + result.s.speed = 10; + break; + case 1: /* 100 Mbps */ + result.s.speed = 100; + break; + case 2: /* 1 Gbps */ + result.s.speed = 1000; + break; + case 3: /* Illegal */ + result.u64 = 0; + break; + } + } + return result; +} + +/** + * @INTERNAL + * Get link state of broadcom PHY + * + * @param phy_info PHY information + */ +static cvmx_helper_link_info_t +__get_broadcom_phy_link_state(cvmx_phy_info_t *phy_info) +{ + cvmx_helper_link_info_t result; + u32 phy_addr = phy_info->phy_addr; + int phy_status; + + result.u64 = 0; + /* Below we are going to read SMI/MDIO register 0x19 which works + * on Broadcom parts + */ + phy_status = cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, 0x19); + switch ((phy_status >> 8) & 0x7) { + case 0: + result.u64 = 0; + break; + case 1: + result.s.link_up = 1; + result.s.full_duplex = 0; + result.s.speed = 10; + break; + case 2: + result.s.link_up = 1; + result.s.full_duplex = 1; + result.s.speed = 10; + break; + case 3: + result.s.link_up = 1; + result.s.full_duplex = 0; + result.s.speed = 100; + break; + case 4: + result.s.link_up = 1; + result.s.full_duplex = 1; + result.s.speed = 100; + break; + case 5: + result.s.link_up = 1; + result.s.full_duplex = 1; + result.s.speed = 100; + break; + case 6: + result.s.link_up = 1; + result.s.full_duplex = 0; + result.s.speed = 1000; + break; + case 7: + result.s.link_up = 1; + result.s.full_duplex = 1; + result.s.speed = 1000; + break; + } + return result; +} + +/** + * @INTERNAL + * Get link state of generic gigabit PHY + * + * @param phy_info - information about the PHY + * + * @returns link status of the PHY + */ +static cvmx_helper_link_info_t +__cvmx_get_generic_8023_c22_phy_link_state(cvmx_phy_info_t *phy_info) +{ + cvmx_helper_link_info_t result; + u32 phy_addr = phy_info->phy_addr; + int phy_basic_control; /* Register 0x0 */ + int phy_basic_status; /* Register 0x1 */ + int phy_anog_adv; /* Register 0x4 */ + int phy_link_part_avail; /* Register 0x5 */ + int phy_control; /* Register 0x9 */ + int phy_status; /* Register 0xA */ + + result.u64 = 0; + + phy_basic_status = cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, 1); + if (!(phy_basic_status & 0x4)) /* Check if link is up */ + return result; /* Link is down, return link down */ + + result.s.link_up = 1; + phy_basic_control = cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, 0); + /* Check if autonegotiation is enabled and completed */ + if ((phy_basic_control & (1 << 12)) && (phy_basic_status & (1 << 5))) { + phy_status = + cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, 0xA); + phy_control = + cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, 0x9); + + phy_status &= phy_control << 2; + phy_link_part_avail = + cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, 0x5); + phy_anog_adv = + cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, 0x4); + phy_link_part_avail &= phy_anog_adv; + + if (phy_status & 0xC00) { /* Gigabit full or half */ + result.s.speed = 1000; + result.s.full_duplex = !!(phy_status & 0x800); + } else if (phy_link_part_avail & + 0x0180) { /* 100 full or half */ + result.s.speed = 100; + result.s.full_duplex = !!(phy_link_part_avail & 0x100); + } else if (phy_link_part_avail & 0x0060) { + result.s.speed = 10; + result.s.full_duplex = !!(phy_link_part_avail & 0x0040); + } + } else { + /* Not autonegotiated */ + result.s.full_duplex = !!(phy_basic_control & (1 << 8)); + + if (phy_basic_control & (1 << 6)) + result.s.speed = 1000; + else if (phy_basic_control & (1 << 13)) + result.s.speed = 100; + else + result.s.speed = 10; + } + return result; +} + +static cvmx_helper_link_info_t +__cvmx_get_qualcomm_s17_phy_link_state(cvmx_phy_info_t *phy_info) +{ + cvmx_helper_link_info_t result; + u32 phy_addr = phy_info->phy_addr; + int phy_status; + int auto_status; + + result.u64 = 0; + + phy_status = cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, 17); + + /* If bit 11 isn't set see if autonegotiation is turned off + * (bit 12, reg 0). The resolved bit doesn't get set properly when + * autonegotiation is off, so force it. + */ + if ((phy_status & (1 << 11)) == 0) { + auto_status = cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, 0); + if ((auto_status & (1 << 12)) == 0) + phy_status |= 1 << 11; + } + /* Only return a link if the PHY has finished autonegotiation and set + * the resolved bit (bit 11). + */ + if (phy_status & (1 << 11)) { + result.s.link_up = 1; + result.s.full_duplex = !!(phy_status & (1 << 13)); + switch ((phy_status >> 14) & 3) { + case 0: /* 10Mbps */ + result.s.speed = 10; + break; + case 1: /* 100Mbps */ + result.s.speed = 100; + break; + case 2: /* 1Gbps */ + result.s.speed = 1000; + break; + default: /* Illegal */ + result.u64 = 0; + break; + } + } + debug(" link: %s, duplex: %s, speed: %lu\n", + result.s.link_up ? "up" : "down", + result.s.full_duplex ? "full" : "half", + (unsigned long)result.s.speed); + return result; +} + +static cvmx_helper_link_info_t +__get_generic_8023_c45_phy_link_state(cvmx_phy_info_t *phy_info) +{ + cvmx_helper_link_info_t result; + int phy_status; + int pma_ctrl1; + u32 phy_addr = phy_info->phy_addr; + + result.u64 = 0; + pma_ctrl1 = cvmx_mdio_45_read(phy_addr >> 8, phy_addr & 0xff, 1, 0); + if ((pma_ctrl1 & 0x207c) == 0x2040) + result.s.speed = 10000; + /* PMA Status 1 (1x0001) */ + phy_status = cvmx_mdio_45_read(phy_addr >> 8, phy_addr & 0xff, 1, 0xa); + if (phy_status < 0) + return result; + + result.s.full_duplex = 1; + if ((phy_status & 1) == 0) + return result; + phy_status = cvmx_mdio_45_read(phy_addr >> 8, phy_addr & 0xff, 4, 0x18); + if (phy_status < 0) + return result; + result.s.link_up = (phy_status & 0x1000) ? 1 : 0; + + return result; +} + +static cvmx_helper_link_info_t +__cvmx_get_cortina_phy_link_state(cvmx_phy_info_t *phy_info) +{ + cvmx_helper_link_info_t result; + + result.s.link_up = 1; + result.s.full_duplex = 1; + result.s.speed = 1000; + return result; +} + +static cvmx_helper_link_info_t +__get_vitesse_vsc8490_phy_link_state(cvmx_phy_info_t *phy_info) +{ + cvmx_helper_link_info_t result; + + result.s.link_up = 1; + result.s.full_duplex = 1; + result.s.speed = 1000; + return result; +} + +static cvmx_helper_link_info_t +__get_aquantia_phy_link_state(cvmx_phy_info_t *phy_info) +{ + cvmx_helper_link_info_t result; + + result.s.link_up = 1; + result.s.full_duplex = 1; + result.s.speed = 1000; + return result; +} + +static int __cvmx_helper_78xx_parse_phy(struct cvmx_phy_info *phy_info, + int ipd_port) +{ + const void *fdt_addr = CASTPTR(const void *, gd->fdt_blob); + const char *compat; + int phy; + int parent; + u64 mdio_base; + int node, bus; + int phy_addr; + int index = cvmx_helper_get_interface_index_num(ipd_port); + int xiface = cvmx_helper_get_interface_num(ipd_port); + int compat_len = 0; + + debug("%s(0x%p, %d) ENTER\n", __func__, phy_info, ipd_port); + + phy = cvmx_helper_get_phy_fdt_node_offset(xiface, index); + debug("%s: xiface: 0x%x, index: %d, ipd_port: %d, phy fdt offset: %d\n", + __func__, xiface, index, ipd_port, phy); + if (phy < 0) { + /* If this is the first time through we need to first parse the + * device tree to get the node offsets. + */ + debug("No config present, calling __cvmx_helper_parse_bgx_dt\n"); + if (__cvmx_helper_parse_bgx_dt(fdt_addr)) { + printf("Error: could not parse BGX device tree\n"); + return -1; + } + if (__cvmx_fdt_parse_vsc7224(fdt_addr)) { + debug("Error: could not parse Microsemi VSC7224 in DT\n"); + return -1; + } + if (octeon_has_feature(OCTEON_FEATURE_BGX_XCV) && + __cvmx_helper_parse_bgx_rgmii_dt(fdt_addr)) { + printf("Error: could not parse BGX XCV device tree\n"); + return -1; + } + phy = cvmx_helper_get_phy_fdt_node_offset(xiface, index); + if (phy < 0) { + debug("%s: Could not get PHY node offset for IPD port 0x%x, xiface: 0x%x, index: %d\n", + __func__, ipd_port, xiface, index); + return -1; + } + debug("%s: phy: %d (%s)\n", __func__, phy, + fdt_get_name(fdt_addr, phy, NULL)); + } + + compat = (const char *)fdt_getprop(fdt_addr, phy, "compatible", + &compat_len); + if (!compat) { + printf("ERROR: %d:%d:no compatible prop in phy\n", xiface, + index); + return -1; + } + + debug(" compatible: %s\n", compat); + + phy_info->fdt_offset = phy; + phy_addr = cvmx_fdt_get_int(fdt_addr, phy, "reg", -1); + if (phy_addr == -1) { + printf("Error: %d:%d:could not get PHY address\n", xiface, + index); + return -1; + } + debug(" PHY address: %d, compat: %s\n", phy_addr, compat); + + if (!memcmp("marvell", compat, strlen("marvell"))) { + phy_info->phy_type = MARVELL_GENERIC_PHY; + phy_info->link_function = __get_marvell_phy_link_state; + } else if (!memcmp("broadcom", compat, strlen("broadcom"))) { + phy_info->phy_type = BROADCOM_GENERIC_PHY; + phy_info->link_function = __get_broadcom_phy_link_state; + } else if (!memcmp("cortina", compat, strlen("cortina"))) { + phy_info->phy_type = CORTINA_PHY; + phy_info->link_function = __cvmx_get_cortina_phy_link_state; + } else if (!strcmp("vitesse,vsc8490", compat)) { + phy_info->phy_type = VITESSE_VSC8490_PHY; + phy_info->link_function = __get_vitesse_vsc8490_phy_link_state; + } else if (fdt_stringlist_contains(compat, compat_len, + "ethernet-phy-ieee802.3-c22")) { + phy_info->phy_type = GENERIC_8023_C22_PHY; + phy_info->link_function = + __cvmx_get_generic_8023_c22_phy_link_state; + } else if (fdt_stringlist_contains(compat, compat_len, + "ethernet-phy-ieee802.3-c45")) { + phy_info->phy_type = GENERIC_8023_C22_PHY; + phy_info->link_function = __get_generic_8023_c45_phy_link_state; + } + + phy_info->ipd_port = ipd_port; + phy_info->phy_sub_addr = 0; + phy_info->direct_connect = 1; + + parent = fdt_parent_offset(fdt_addr, phy); + if (!fdt_node_check_compatible(fdt_addr, parent, + "ethernet-phy-nexus")) { + debug(" nexus PHY found\n"); + if (phy_info->phy_type == CORTINA_PHY) { + /* The Cortina CS422X uses the same PHY device for + * multiple ports for XFI. In this case we use a + * nexus and each PHY address is the slice or + * sub-address and the actual PHY address is the + * nexus address. + */ + phy_info->phy_sub_addr = phy_addr; + phy_addr = + cvmx_fdt_get_int(fdt_addr, parent, "reg", -1); + debug(" Cortina PHY real address: 0x%x\n", phy_addr); + } + parent = fdt_parent_offset(fdt_addr, parent); + } + + debug(" Parent: %s\n", fdt_get_name(fdt_addr, parent, NULL)); + if (!fdt_node_check_compatible(fdt_addr, parent, + "cavium,octeon-3860-mdio")) { + debug(" Found Octeon MDIO\n"); + mdio_base = cvmx_fdt_get_uint64(fdt_addr, parent, "reg", + FDT_ADDR_T_NONE); + debug(" MDIO address: 0x%llx\n", + (unsigned long long)mdio_base); + + mdio_base = cvmx_fdt_translate_address(fdt_addr, parent, + (u32 *)&mdio_base); + debug(" Translated: 0x%llx\n", (unsigned long long)mdio_base); + if (mdio_base == FDT_ADDR_T_NONE) { + printf("Could not get MDIO base address from reg field\n"); + return -1; + } + __cvmx_mdio_addr_to_node_bus(mdio_base, &node, &bus); + if (bus < 0) { + printf("Invalid MDIO address 0x%llx, could not detect bus and node\n", + (unsigned long long)mdio_base); + return -1; + } + debug(" MDIO node: %d, bus: %d\n", node, bus); + phy_info->mdio_unit = (node << 2) | (bus & 3); + phy_info->phy_addr = phy_addr | (phy_info->mdio_unit << 8); + } else { + printf("%s: Error: incompatible MDIO bus %s for IPD port %d\n", + __func__, + (const char *)fdt_get_name(fdt_addr, parent, NULL), + ipd_port); + return -1; + } + + debug("%s: EXIT 0\n", __func__); + + return 0; +} + +/** + * Return the MII PHY address associated with the given IPD + * port. The phy address is obtained from the device tree. + * + * @param[out] phy_info - PHY information data structure updated + * @param ipd_port Octeon IPD port to get the MII address for. + * + * @return MII PHY address and bus number, -1 on error, -2 if PHY info missing (OK). + */ +static int __get_phy_info_from_dt(cvmx_phy_info_t *phy_info, int ipd_port) +{ + const void *fdt_addr = CASTPTR(const void *, gd->fdt_blob); + int aliases, eth, phy, phy_parent, ret, i; + int mdio_parent; + const char *phy_compatible_str; + const char *host_mode_str = NULL; + int interface; + int phy_addr_offset = 0; + + debug("%s(%p, %d)\n", __func__, phy_info, ipd_port); + + if (octeon_has_feature(OCTEON_FEATURE_BGX)) + return __cvmx_helper_78xx_parse_phy(phy_info, ipd_port); + + phy_info->phy_addr = -1; + phy_info->phy_sub_addr = 0; + phy_info->ipd_port = ipd_port; + phy_info->direct_connect = -1; + phy_info->phy_type = (cvmx_phy_type_t)-1; + for (i = 0; i < CVMX_PHY_MUX_MAX_GPIO; i++) + phy_info->gpio[i] = -1; + phy_info->mdio_unit = -1; + phy_info->gpio_value = -1; + phy_info->gpio_parent_mux_twsi = -1; + phy_info->gpio_parent_mux_select = -1; + phy_info->link_function = NULL; + phy_info->fdt_offset = -1; + if (!fdt_addr) { + debug("No device tree found.\n"); + return -1; + } + + aliases = fdt_path_offset(fdt_addr, "/aliases"); + if (aliases < 0) { + debug("Error: No /aliases node in device tree.\n"); + return -1; + } + if (ipd_port < 0) { + int interface_index = + ipd_port - CVMX_HELPER_BOARD_MGMT_IPD_PORT; + eth = __mix_eth_node(fdt_addr, aliases, interface_index); + } else { + eth = __pip_eth_node(fdt_addr, aliases, ipd_port); + } + if (eth < 0) { + debug("ERROR : cannot find interface for ipd_port=%d\n", + ipd_port); + return -1; + } + + interface = cvmx_helper_get_interface_num(ipd_port); + /* Get handle to phy */ + phy = cvmx_fdt_lookup_phandle(fdt_addr, eth, "phy-handle"); + if (phy < 0) { + cvmx_helper_interface_mode_t if_mode; + + /* Note that it's OK for RXAUI and ILK to not have a PHY + * connected (i.e. EBB boards in loopback). + */ + debug("Cannot get phy-handle for ipd_port: %d\n", ipd_port); + if_mode = cvmx_helper_interface_get_mode(interface); + if (if_mode != CVMX_HELPER_INTERFACE_MODE_RXAUI && + if_mode != CVMX_HELPER_INTERFACE_MODE_ILK) { + debug("ERROR : phy handle not found in device tree ipd_port=%d\n", + ipd_port); + return -1; + } else { + return -2; + } + } + + phy_compatible_str = + (const char *)fdt_getprop(fdt_addr, phy, "compatible", NULL); + if (!phy_compatible_str) { + debug("ERROR: no compatible prop in phy\n"); + return -1; + } + debug("Checking compatible string \"%s\" for ipd port %d\n", + phy_compatible_str, ipd_port); + phy_info->fdt_offset = phy; + if (!memcmp("marvell", phy_compatible_str, strlen("marvell"))) { + debug("Marvell PHY detected for ipd_port %d\n", ipd_port); + phy_info->phy_type = MARVELL_GENERIC_PHY; + phy_info->link_function = __get_marvell_phy_link_state; + } else if (!memcmp("broadcom", phy_compatible_str, + strlen("broadcom"))) { + phy_info->phy_type = BROADCOM_GENERIC_PHY; + phy_info->link_function = __get_broadcom_phy_link_state; + debug("Broadcom PHY detected for ipd_port %d\n", ipd_port); + } else if (!memcmp("vitesse", phy_compatible_str, strlen("vitesse"))) { + debug("Vitesse PHY detected for ipd_port %d\n", ipd_port); + if (!fdt_node_check_compatible(fdt_addr, phy, + "vitesse,vsc8490")) { + phy_info->phy_type = VITESSE_VSC8490_PHY; + debug("Vitesse VSC8490 detected\n"); + phy_info->link_function = + __get_vitesse_vsc8490_phy_link_state; + } else if (!fdt_node_check_compatible( + fdt_addr, phy, + "ethernet-phy-ieee802.3-c22")) { + phy_info->phy_type = GENERIC_8023_C22_PHY; + phy_info->link_function = + __cvmx_get_generic_8023_c22_phy_link_state; + debug("Vitesse 802.3 c22 detected\n"); + } else { + phy_info->phy_type = GENERIC_8023_C45_PHY; + phy_info->link_function = + __get_generic_8023_c45_phy_link_state; + debug("Vitesse 802.3 c45 detected\n"); + } + } else if (!memcmp("aquantia", phy_compatible_str, + strlen("aquantia"))) { + phy_info->phy_type = AQUANTIA_PHY; + phy_info->link_function = __get_aquantia_phy_link_state; + debug("Aquantia c45 PHY detected\n"); + } else if (!memcmp("cortina", phy_compatible_str, strlen("cortina"))) { + phy_info->phy_type = CORTINA_PHY; + phy_info->link_function = __cvmx_get_cortina_phy_link_state; + host_mode_str = (const char *)fdt_getprop( + fdt_addr, phy, "cortina,host-mode", NULL); + debug("Cortina PHY detected for ipd_port %d\n", ipd_port); + } else if (!memcmp("ti", phy_compatible_str, strlen("ti"))) { + phy_info->phy_type = GENERIC_8023_C45_PHY; + phy_info->link_function = __get_generic_8023_c45_phy_link_state; + debug("TI PHY detected for ipd_port %d\n", ipd_port); + } else if (!fdt_node_check_compatible(fdt_addr, phy, + "atheros,ar8334") || + !fdt_node_check_compatible(fdt_addr, phy, + "qualcomm,qca8334") || + !fdt_node_check_compatible(fdt_addr, phy, + "atheros,ar8337") || + !fdt_node_check_compatible(fdt_addr, phy, + "qualcomm,qca8337")) { + phy_info->phy_type = QUALCOMM_S17; + phy_info->link_function = + __cvmx_get_qualcomm_s17_phy_link_state; + debug("Qualcomm QCA833X switch detected\n"); + } else if (!fdt_node_check_compatible(fdt_addr, phy, + "ethernet-phy-ieee802.3-c22")) { + phy_info->phy_type = GENERIC_8023_C22_PHY; + phy_info->link_function = + __cvmx_get_generic_8023_c22_phy_link_state; + debug("Generic 802.3 c22 PHY detected\n"); + } else if (!fdt_node_check_compatible(fdt_addr, phy, + "ethernet-phy-ieee802.3-c45")) { + phy_info->phy_type = GENERIC_8023_C45_PHY; + phy_info->link_function = __get_generic_8023_c45_phy_link_state; + debug("Generic 802.3 c45 PHY detected\n"); + } else { + debug("Unknown PHY compatibility\n"); + phy_info->phy_type = (cvmx_phy_type_t)-1; + phy_info->link_function = NULL; + } + + phy_info->host_mode = CVMX_PHY_HOST_MODE_UNKNOWN; + if (host_mode_str) { + if (strcmp(host_mode_str, "rxaui") == 0) + phy_info->host_mode = CVMX_PHY_HOST_MODE_RXAUI; + else if (strcmp(host_mode_str, "xaui") == 0) + phy_info->host_mode = CVMX_PHY_HOST_MODE_XAUI; + else if (strcmp(host_mode_str, "sgmii") == 0) + phy_info->host_mode = CVMX_PHY_HOST_MODE_SGMII; + else if (strcmp(host_mode_str, "qsgmii") == 0) + phy_info->host_mode = CVMX_PHY_HOST_MODE_QSGMII; + else + debug("Unknown PHY host mode\n"); + } + + /* Check if PHY parent is the octeon MDIO bus. Some boards are connected + * though a MUX and for them direct_connect_to_phy will be 0 + */ + phy_parent = fdt_parent_offset(fdt_addr, phy); + if (phy_parent < 0) { + debug("ERROR : cannot find phy parent for ipd_port=%d ret=%d\n", + ipd_port, phy_parent); + return -1; + } + /* For multi-phy devices and devices on a MUX, go to the parent */ + ret = fdt_node_check_compatible(fdt_addr, phy_parent, + "ethernet-phy-nexus"); + if (ret == 0) { + /* It's a nexus so check the grandparent. */ + phy_addr_offset = + cvmx_fdt_get_int(fdt_addr, phy_parent, "reg", 0); + phy_parent = fdt_parent_offset(fdt_addr, phy_parent); + } + + /* Check for a muxed MDIO interface */ + mdio_parent = fdt_parent_offset(fdt_addr, phy_parent); + ret = fdt_node_check_compatible(fdt_addr, mdio_parent, + "cavium,mdio-mux"); + if (ret == 0) { + ret = __get_muxed_mdio_info_from_dt(phy_info, phy_parent, + mdio_parent); + if (ret) { + printf("Error reading mdio mux information for ipd port %d\n", + ipd_port); + return -1; + } + } + ret = fdt_node_check_compatible(fdt_addr, phy_parent, + "cavium,octeon-3860-mdio"); + if (ret == 0) { + u32 *mdio_reg_base = + (u32 *)fdt_getprop(fdt_addr, phy_parent, "reg", 0); + phy_info->direct_connect = 1; + if (mdio_reg_base == 0) { + debug("ERROR : unable to get reg property in phy mdio\n"); + return -1; + } + phy_info->mdio_unit = + __mdiobus_addr_to_unit(fdt32_to_cpu(mdio_reg_base[1])); + debug("phy parent=%s reg_base=%08x mdio_unit=%d\n", + fdt_get_name(fdt_addr, phy_parent, NULL), + (int)mdio_reg_base[1], phy_info->mdio_unit); + } else { + phy_info->direct_connect = 0; + /* The PHY is not directly connected to the Octeon MDIO bus. + * SE doesn't have abstractions for MDIO MUX or MDIO MUX + * drivers and hence for the non direct cases code will be + * needed which is board specific. + * For now the MDIO Unit is defaulted to 1. + */ + debug("%s PHY at address: %d is not directly connected\n", + __func__, phy_info->phy_addr); + } + + phy_info->phy_addr = cvmx_fdt_get_int(fdt_addr, phy, "reg", -1); + if (phy_info->phy_addr < 0) { + debug("ERROR: Could not read phy address from reg in DT\n"); + return -1; + } + phy_info->phy_addr += phy_addr_offset; + phy_info->phy_addr |= phy_info->mdio_unit << 8; + debug("%s(%p, %d) => 0x%x\n", __func__, phy_info, ipd_port, + phy_info->phy_addr); + return phy_info->phy_addr; +} + +/** + * @INTERNAL + * Parse the device tree and set whether a port is valid or not. + * + * @param fdt_addr Pointer to device tree + * + * @return 0 for success, -1 on error. + */ +int __cvmx_helper_parse_bgx_dt(const void *fdt_addr) +{ + int port_index; + struct cvmx_xiface xi; + int fdt_port_node = -1; + int fdt_interface_node; + int fdt_phy_node; + u64 reg_addr; + int xiface; + struct cvmx_phy_info *phy_info; + static bool parsed; + int err; + int ipd_port; + + if (parsed) { + debug("%s: Already parsed\n", __func__); + return 0; + } + while ((fdt_port_node = fdt_node_offset_by_compatible( + fdt_addr, fdt_port_node, + "cavium,octeon-7890-bgx-port")) >= 0) { + /* Get the port number */ + port_index = + cvmx_fdt_get_int(fdt_addr, fdt_port_node, "reg", -1); + if (port_index < 0) { + debug("Error: missing reg field for bgx port in device tree\n"); + return -1; + } + debug("%s: Parsing BGX port %d\n", __func__, port_index); + /* Get the interface number */ + fdt_interface_node = fdt_parent_offset(fdt_addr, fdt_port_node); + if (fdt_interface_node < 0) { + debug("Error: device tree corrupt!\n"); + return -1; + } + if (fdt_node_check_compatible(fdt_addr, fdt_interface_node, + "cavium,octeon-7890-bgx")) { + debug("Error: incompatible Ethernet MAC Nexus in device tree!\n"); + return -1; + } + reg_addr = + cvmx_fdt_get_addr(fdt_addr, fdt_interface_node, "reg"); + debug("%s: BGX interface address: 0x%llx\n", __func__, + (unsigned long long)reg_addr); + if (reg_addr == FDT_ADDR_T_NONE) { + debug("Device tree BGX node has invalid address 0x%llx\n", + (unsigned long long)reg_addr); + return -1; + } + reg_addr = cvmx_fdt_translate_address(fdt_addr, + fdt_interface_node, + (u32 *)®_addr); + xi = __cvmx_bgx_reg_addr_to_xiface(reg_addr); + if (xi.node < 0) { + debug("Device tree BGX node has invalid address 0x%llx\n", + (unsigned long long)reg_addr); + return -1; + } + debug("%s: Found BGX node %d, interface %d\n", __func__, + xi.node, xi.interface); + xiface = cvmx_helper_node_interface_to_xiface(xi.node, + xi.interface); + cvmx_helper_set_port_fdt_node_offset(xiface, port_index, + fdt_port_node); + cvmx_helper_set_port_valid(xiface, port_index, true); + + cvmx_helper_set_port_fdt_node_offset(xiface, port_index, + fdt_port_node); + if (fdt_getprop(fdt_addr, fdt_port_node, + "cavium,sgmii-mac-phy-mode", NULL)) + cvmx_helper_set_mac_phy_mode(xiface, port_index, true); + else + cvmx_helper_set_mac_phy_mode(xiface, port_index, false); + + if (fdt_getprop(fdt_addr, fdt_port_node, "cavium,force-link-up", + NULL)) + cvmx_helper_set_port_force_link_up(xiface, port_index, + true); + else + cvmx_helper_set_port_force_link_up(xiface, port_index, + false); + + if (fdt_getprop(fdt_addr, fdt_port_node, + "cavium,sgmii-mac-1000x-mode", NULL)) + cvmx_helper_set_1000x_mode(xiface, port_index, true); + else + cvmx_helper_set_1000x_mode(xiface, port_index, false); + + if (fdt_getprop(fdt_addr, fdt_port_node, + "cavium,disable-autonegotiation", NULL)) + cvmx_helper_set_port_autonegotiation(xiface, port_index, + false); + else + cvmx_helper_set_port_autonegotiation(xiface, port_index, + true); + + fdt_phy_node = cvmx_fdt_lookup_phandle(fdt_addr, fdt_port_node, + "phy-handle"); + if (fdt_phy_node >= 0) { + cvmx_helper_set_phy_fdt_node_offset(xiface, port_index, + fdt_phy_node); + debug("%s: Setting PHY fdt node offset for interface 0x%x, port %d to %d\n", + __func__, xiface, port_index, fdt_phy_node); + debug("%s: PHY node name: %s\n", __func__, + fdt_get_name(fdt_addr, fdt_phy_node, NULL)); + cvmx_helper_set_port_phy_present(xiface, port_index, + true); + ipd_port = cvmx_helper_get_ipd_port(xiface, port_index); + if (ipd_port >= 0) { + debug("%s: Allocating phy info for 0x%x:%d\n", + __func__, xiface, port_index); + phy_info = + (cvmx_phy_info_t *)cvmx_bootmem_alloc( + sizeof(*phy_info), 0); + if (!phy_info) { + debug("%s: Out of memory\n", __func__); + return -1; + } + memset(phy_info, 0, sizeof(*phy_info)); + phy_info->phy_addr = -1; + err = __get_phy_info_from_dt(phy_info, + ipd_port); + if (err) { + debug("%s: Error parsing phy info for ipd port %d\n", + __func__, ipd_port); + return -1; + } + cvmx_helper_set_port_phy_info( + xiface, port_index, phy_info); + debug("%s: Saved phy info\n", __func__); + } + } else { + cvmx_helper_set_phy_fdt_node_offset(xiface, port_index, + -1); + debug("%s: No PHY fdt node offset for interface 0x%x, port %d to %d\n", + __func__, xiface, port_index, fdt_phy_node); + cvmx_helper_set_port_phy_present(xiface, port_index, + false); + } + } + if (!sfp_parsed) + if (cvmx_sfp_parse_device_tree(fdt_addr)) + debug("%s: Error parsing SFP device tree\n", __func__); + parsed = true; + return 0; +} + +int __cvmx_helper_parse_bgx_rgmii_dt(const void *fdt_addr) +{ + u64 reg_addr; + struct cvmx_xiface xi; + int fdt_port_node = -1; + int fdt_interface_node; + int fdt_phy_node; + int port_index; + int xiface; + + /* There's only one xcv (RGMII) interface, so just search for the one + * that's part of a BGX entry. + */ + while ((fdt_port_node = fdt_node_offset_by_compatible( + fdt_addr, fdt_port_node, "cavium,octeon-7360-xcv")) >= + 0) { + fdt_interface_node = fdt_parent_offset(fdt_addr, fdt_port_node); + if (fdt_interface_node < 0) { + printf("Error: device tree corrupt!\n"); + return -1; + } + debug("%s: XCV parent node compatible: %s\n", __func__, + (char *)fdt_getprop(fdt_addr, fdt_interface_node, + "compatible", NULL)); + if (!fdt_node_check_compatible(fdt_addr, fdt_interface_node, + "cavium,octeon-7890-bgx")) + break; + } + if (fdt_port_node == -FDT_ERR_NOTFOUND) { + debug("No XCV/RGMII interface found in device tree\n"); + return 0; + } else if (fdt_port_node < 0) { + debug("%s: Error %d parsing device tree\n", __func__, + fdt_port_node); + return -1; + } + port_index = cvmx_fdt_get_int(fdt_addr, fdt_port_node, "reg", -1); + if (port_index != 0) { + printf("%s: Error: port index (reg) must be 0, not %d.\n", + __func__, port_index); + return -1; + } + reg_addr = cvmx_fdt_get_addr(fdt_addr, fdt_interface_node, "reg"); + if (reg_addr == FDT_ADDR_T_NONE) { + printf("%s: Error: could not get BGX interface address\n", + __func__); + return -1; + } + /* We don't have to bother translating since only 78xx supports OCX and + * doesn't support RGMII. + */ + xi = __cvmx_bgx_reg_addr_to_xiface(reg_addr); + debug("%s: xi.node: %d, xi.interface: 0x%x, addr: 0x%llx\n", __func__, + xi.node, xi.interface, (unsigned long long)reg_addr); + if (xi.node < 0) { + printf("%s: Device tree BGX node has invalid address 0x%llx\n", + __func__, (unsigned long long)reg_addr); + return -1; + } + debug("%s: Found XCV (RGMII) interface on interface %d\n", __func__, + xi.interface); + debug(" phy handle: 0x%x\n", + cvmx_fdt_get_int(fdt_addr, fdt_port_node, "phy-handle", -1)); + fdt_phy_node = + cvmx_fdt_lookup_phandle(fdt_addr, fdt_port_node, "phy-handle"); + debug("%s: phy-handle node: 0x%x\n", __func__, fdt_phy_node); + xiface = cvmx_helper_node_interface_to_xiface(xi.node, xi.interface); + + cvmx_helper_set_port_fdt_node_offset(xiface, port_index, fdt_port_node); + if (fdt_phy_node >= 0) { + debug("%s: Setting PHY fdt node offset for interface 0x%x, port %d to %d\n", + __func__, xiface, port_index, fdt_phy_node); + debug("%s: PHY node name: %s\n", __func__, + fdt_get_name(fdt_addr, fdt_phy_node, NULL)); + cvmx_helper_set_phy_fdt_node_offset(xiface, port_index, + fdt_phy_node); + cvmx_helper_set_port_phy_present(xiface, port_index, true); + } else { + cvmx_helper_set_phy_fdt_node_offset(xiface, port_index, -1); + debug("%s: No PHY fdt node offset for interface 0x%x, port %d to %d\n", + __func__, xiface, port_index, fdt_phy_node); + cvmx_helper_set_port_phy_present(xiface, port_index, false); + } + + return 0; +} + +/** + * Returns if a port is present on an interface + * + * @param fdt_addr - address fo flat device tree + * @param ipd_port - IPD port number + * + * @return 1 if port is present, 0 if not present, -1 if error + */ +int __cvmx_helper_board_get_port_from_dt(void *fdt_addr, int ipd_port) +{ + int port_index; + int aliases; + const char *pip_path; + char name_buffer[24]; + int pip, iface, eth; + cvmx_helper_interface_mode_t mode; + int xiface = cvmx_helper_get_interface_num(ipd_port); + struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); + u32 val; + int phy_node_offset; + int parse_bgx_dt_err; + int parse_vsc7224_err; + + debug("%s(%p, %d)\n", __func__, fdt_addr, ipd_port); + if (octeon_has_feature(OCTEON_FEATURE_BGX)) { + static int fdt_ports_initialized; + + port_index = cvmx_helper_get_interface_index_num(ipd_port); + + if (!fdt_ports_initialized) { + if (octeon_has_feature(OCTEON_FEATURE_BGX_XCV)) { + if (!__cvmx_helper_parse_bgx_rgmii_dt(fdt_addr)) + fdt_ports_initialized = 1; + parse_bgx_dt_err = + __cvmx_helper_parse_bgx_dt(fdt_addr); + parse_vsc7224_err = + __cvmx_fdt_parse_vsc7224(fdt_addr); + if (!parse_bgx_dt_err && !parse_vsc7224_err) + fdt_ports_initialized = 1; + } else { + debug("%s: Error parsing FDT\n", __func__); + return -1; + } + } + + return cvmx_helper_is_port_valid(xiface, port_index); + } + + mode = cvmx_helper_interface_get_mode(xiface); + + switch (mode) { + /* Device tree has information about the following mode types. */ + case CVMX_HELPER_INTERFACE_MODE_RGMII: + case CVMX_HELPER_INTERFACE_MODE_GMII: + case CVMX_HELPER_INTERFACE_MODE_SPI: + case CVMX_HELPER_INTERFACE_MODE_XAUI: + case CVMX_HELPER_INTERFACE_MODE_SGMII: + case CVMX_HELPER_INTERFACE_MODE_QSGMII: + case CVMX_HELPER_INTERFACE_MODE_RXAUI: + case CVMX_HELPER_INTERFACE_MODE_AGL: + case CVMX_HELPER_INTERFACE_MODE_XLAUI: + case CVMX_HELPER_INTERFACE_MODE_XFI: + aliases = 1; + break; + default: + aliases = 0; + break; + } + + /* The device tree information is present on interfaces that have phy */ + if (!aliases) + return 1; + + port_index = cvmx_helper_get_interface_index_num(ipd_port); + + aliases = fdt_path_offset(fdt_addr, "/aliases"); + if (aliases < 0) { + debug("%s: ERROR: /aliases not found in device tree fdt_addr=%p\n", + __func__, fdt_addr); + return -1; + } + + pip_path = (const char *)fdt_getprop(fdt_addr, aliases, "pip", NULL); + if (!pip_path) { + debug("%s: ERROR: interface %x pip path not found in device tree\n", + __func__, xiface); + return -1; + } + pip = fdt_path_offset(fdt_addr, pip_path); + if (pip < 0) { + debug("%s: ERROR: interface %x pip not found in device tree\n", + __func__, xiface); + return -1; + } + snprintf(name_buffer, sizeof(name_buffer), "interface@%d", + xi.interface); + iface = fdt_subnode_offset(fdt_addr, pip, name_buffer); + if (iface < 0) + return 0; + snprintf(name_buffer, sizeof(name_buffer), "ethernet@%x", port_index); + eth = fdt_subnode_offset(fdt_addr, iface, name_buffer); + debug("%s: eth subnode offset %d from %s\n", __func__, eth, + name_buffer); + + if (eth < 0) + return -1; + + cvmx_helper_set_port_fdt_node_offset(xiface, port_index, eth); + + phy_node_offset = cvmx_fdt_get_int(fdt_addr, eth, "phy", -1); + cvmx_helper_set_phy_fdt_node_offset(xiface, port_index, + phy_node_offset); + + if (fdt_getprop(fdt_addr, eth, "cavium,sgmii-mac-phy-mode", NULL)) + cvmx_helper_set_mac_phy_mode(xiface, port_index, true); + else + cvmx_helper_set_mac_phy_mode(xiface, port_index, false); + + if (fdt_getprop(fdt_addr, eth, "cavium,force-link-up", NULL)) + cvmx_helper_set_port_force_link_up(xiface, port_index, true); + else + cvmx_helper_set_port_force_link_up(xiface, port_index, false); + + if (fdt_getprop(fdt_addr, eth, "cavium,sgmii-mac-1000x-mode", NULL)) + cvmx_helper_set_1000x_mode(xiface, port_index, true); + else + cvmx_helper_set_1000x_mode(xiface, port_index, false); + + if (fdt_getprop(fdt_addr, eth, "cavium,disable-autonegotiation", NULL)) + cvmx_helper_set_port_autonegotiation(xiface, port_index, false); + else + cvmx_helper_set_port_autonegotiation(xiface, port_index, true); + + if (mode == CVMX_HELPER_INTERFACE_MODE_AGL) { + bool tx_bypass = false; + + if (fdt_getprop(fdt_addr, eth, "cavium,rx-clk-delay-bypass", + NULL)) + cvmx_helper_set_agl_rx_clock_delay_bypass( + xiface, port_index, true); + else + cvmx_helper_set_agl_rx_clock_delay_bypass( + xiface, port_index, false); + + val = cvmx_fdt_get_int(fdt_addr, eth, "cavium,rx-clk-skew", 0); + cvmx_helper_set_agl_rx_clock_skew(xiface, port_index, val); + + if (fdt_getprop(fdt_addr, eth, "cavium,tx-clk-delay-bypass", + NULL)) + tx_bypass = true; + + val = cvmx_fdt_get_int(fdt_addr, eth, "tx-clk-delay", 0); + cvmx_helper_cfg_set_rgmii_tx_clk_delay(xiface, port_index, + tx_bypass, val); + + val = cvmx_fdt_get_int(fdt_addr, eth, "cavium,refclk-sel", 0); + cvmx_helper_set_agl_refclk_sel(xiface, port_index, val); + } + + return (eth >= 0); +} + +/** + * Given the address of the MDIO registers, output the CPU node and MDIO bus + * + * @param addr 64-bit address of MDIO registers (from device tree) + * @param[out] node CPU node number (78xx) + * @param[out] bus MDIO bus number + */ +void __cvmx_mdio_addr_to_node_bus(u64 addr, int *node, int *bus) +{ + if (OCTEON_IS_MODEL(OCTEON_CN78XX)) { + if (node) + *node = cvmx_csr_addr_to_node(addr); + addr = cvmx_csr_addr_strip_node(addr); + } else { + if (node) + *node = 0; + } + if (OCTEON_IS_MODEL(OCTEON_CN68XX) || OCTEON_IS_MODEL(OCTEON_CN78XX)) { + switch (addr) { + case 0x0001180000003800: + *bus = 0; + break; + case 0x0001180000003880: + *bus = 1; + break; + case 0x0001180000003900: + *bus = 2; + break; + case 0x0001180000003980: + *bus = 3; + break; + default: + *bus = -1; + printf("%s: Invalid SMI bus address 0x%llx\n", __func__, + (unsigned long long)addr); + break; + } + } else if (OCTEON_IS_MODEL(OCTEON_CN73XX) || + OCTEON_IS_MODEL(OCTEON_CNF75XX)) { + switch (addr) { + case 0x0001180000003800: + *bus = 0; + break; + case 0x0001180000003880: + *bus = 1; + break; + default: + *bus = -1; + printf("%s: Invalid SMI bus address 0x%llx\n", __func__, + (unsigned long long)addr); + break; + } + } else { + switch (addr) { + case 0x0001180000001800: + *bus = 0; + break; + case 0x0001180000001900: + *bus = 1; + break; + default: + *bus = -1; + printf("%s: Invalid SMI bus address 0x%llx\n", __func__, + (unsigned long long)addr); + break; + } + } +} diff --git a/arch/mips/mach-octeon/cvmx-helper-cfg.c b/arch/mips/mach-octeon/cvmx-helper-cfg.c index 494108f..a3f4ff0 100644 --- a/arch/mips/mach-octeon/cvmx-helper-cfg.c +++ b/arch/mips/mach-octeon/cvmx-helper-cfg.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (C) 2020 Marvell International Ltd. + * Copyright (C) 2020-2022 Marvell International Ltd. * * Helper Functions for the Configuration Framework */ @@ -100,7 +100,6 @@ static u64 cvmx_cfg_opts[CVMX_HELPER_CFG_OPT_MAX] = { static int cvmx_cfg_max_pko_engines; /* # of PKO DMA engines allocated */ static int cvmx_pko_queue_alloc(u64 port, int count); static void cvmx_init_port_cfg(void); -static const int dbg; int __cvmx_helper_cfg_pknd(int xiface, int index) { @@ -184,16 +183,6 @@ int __cvmx_helper_cfg_pko_max_engine(void) return cvmx_cfg_max_pko_engines; } -int cvmx_helper_cfg_opt_set(cvmx_helper_cfg_option_t opt, uint64_t val) -{ - if (opt >= CVMX_HELPER_CFG_OPT_MAX) - return -1; - - cvmx_cfg_opts[opt] = val; - - return 0; -} - uint64_t cvmx_helper_cfg_opt_get(cvmx_helper_cfg_option_t opt) { if (opt >= CVMX_HELPER_CFG_OPT_MAX) @@ -298,18 +287,6 @@ int cvmx_pko_queue_init_from_cvmx_config_non_pknd(void) return 0; } -int cvmx_helper_pko_queue_config_get(int node, cvmx_user_static_pko_queue_config_t *cfg) -{ - *cfg = __cvmx_pko_queue_static_config[node]; - return 0; -} - -int cvmx_helper_pko_queue_config_set(int node, cvmx_user_static_pko_queue_config_t *cfg) -{ - __cvmx_pko_queue_static_config[node] = *cfg; - return 0; -} - static int queue_range_init; int init_cvmx_pko_que_range(void) @@ -377,91 +354,6 @@ static int cvmx_pko_queue_alloc(u64 port, int count) } /* - * return the queues for "port" - * - * @param port the port for which the queues are returned - * - * Return: 0 on success - * -1 on failure - */ -int cvmx_pko_queue_free(uint64_t port) -{ - int ret_val = -1; - - init_cvmx_pko_que_range(); - if (port >= CVMX_HELPER_CFG_MAX_PKO_QUEUES) { - debug("ERROR: %s port=%d > %d", __func__, (int)port, - CVMX_HELPER_CFG_MAX_PKO_QUEUES); - return -1; - } - - ret_val = cvmx_free_global_resource_range_with_base( - CVMX_GR_TAG_PKO_QUEUES, cvmx_pko_queue_table[port].ccppp_queue_base, - cvmx_pko_queue_table[port].ccppp_num_queues); - if (ret_val != 0) - return ret_val; - - cvmx_pko_queue_table[port].ccppp_num_queues = 0; - cvmx_pko_queue_table[port].ccppp_queue_base = CVMX_HELPER_CFG_INVALID_VALUE; - ret_val = 0; - return ret_val; -} - -void cvmx_pko_queue_free_all(void) -{ - int i; - - for (i = 0; i < CVMX_HELPER_CFG_MAX_PKO_PORT; i++) - if (cvmx_pko_queue_table[i].ccppp_queue_base != - CVMX_HELPER_CFG_INVALID_VALUE) - cvmx_pko_queue_free(i); -} - -void cvmx_pko_queue_show(void) -{ - int i; - - cvmx_show_global_resource_range(CVMX_GR_TAG_PKO_QUEUES); - for (i = 0; i < CVMX_HELPER_CFG_MAX_PKO_PORT; i++) - if (cvmx_pko_queue_table[i].ccppp_queue_base != - CVMX_HELPER_CFG_INVALID_VALUE) - debug("port=%d que_base=%d que_num=%d\n", i, - (int)cvmx_pko_queue_table[i].ccppp_queue_base, - (int)cvmx_pko_queue_table[i].ccppp_num_queues); -} - -void cvmx_helper_cfg_show_cfg(void) -{ - int i, j; - - for (i = 0; i < cvmx_helper_get_number_of_interfaces(); i++) { - debug("%s: interface%d mode %10s nports%4d\n", __func__, i, - cvmx_helper_interface_mode_to_string(cvmx_helper_interface_get_mode(i)), - cvmx_helper_interface_enumerate(i)); - - for (j = 0; j < cvmx_helper_interface_enumerate(i); j++) { - debug("\tpknd[%i][%d]%d", i, j, - __cvmx_helper_cfg_pknd(i, j)); - debug(" pko_port_base[%i][%d]%d", i, j, - __cvmx_helper_cfg_pko_port_base(i, j)); - debug(" pko_port_num[%i][%d]%d\n", i, j, - __cvmx_helper_cfg_pko_port_num(i, j)); - } - } - - for (i = 0; i < CVMX_HELPER_CFG_MAX_PKO_PORT; i++) { - if (__cvmx_helper_cfg_pko_queue_base(i) != - CVMX_HELPER_CFG_INVALID_VALUE) { - debug("%s: pko_port%d qbase%d nqueues%d interface%d index%d\n", - __func__, i, __cvmx_helper_cfg_pko_queue_base(i), - __cvmx_helper_cfg_pko_queue_num(i), - __cvmx_helper_cfg_pko_port_interface(i), - __cvmx_helper_cfg_pko_port_index(i)); - } - } -} - -/* * initialize cvmx_cfg_pko_port_map */ void cvmx_helper_cfg_init_pko_port_map(void) @@ -515,141 +407,6 @@ void cvmx_helper_cfg_init_pko_port_map(void) cvmx_cfg_max_pko_engines = pko_eid; } -void cvmx_helper_cfg_set_jabber_and_frame_max(void) -{ - int interface, port; - /*Set the frame max size and jabber size to 65535. */ - const unsigned int max_frame = 65535; - - // FIXME: should support node argument for remote node init - if (octeon_has_feature(OCTEON_FEATURE_BGX)) { - int ipd_port; - int node = cvmx_get_node_num(); - - for (interface = 0; - interface < cvmx_helper_get_number_of_interfaces(); - interface++) { - int xiface = cvmx_helper_node_interface_to_xiface(node, interface); - cvmx_helper_interface_mode_t imode = cvmx_helper_interface_get_mode(xiface); - int num_ports = cvmx_helper_ports_on_interface(xiface); - - // FIXME: should be an easier way to determine - // that an interface is Ethernet/BGX - switch (imode) { - case CVMX_HELPER_INTERFACE_MODE_SGMII: - case CVMX_HELPER_INTERFACE_MODE_XAUI: - case CVMX_HELPER_INTERFACE_MODE_RXAUI: - case CVMX_HELPER_INTERFACE_MODE_XLAUI: - case CVMX_HELPER_INTERFACE_MODE_XFI: - case CVMX_HELPER_INTERFACE_MODE_10G_KR: - case CVMX_HELPER_INTERFACE_MODE_40G_KR4: - for (port = 0; port < num_ports; port++) { - ipd_port = cvmx_helper_get_ipd_port(xiface, port); - cvmx_pki_set_max_frm_len(ipd_port, max_frame); - cvmx_helper_bgx_set_jabber(xiface, port, max_frame); - } - break; - default: - break; - } - } - } else { - /*Set the frame max size and jabber size to 65535. */ - for (interface = 0; interface < cvmx_helper_get_number_of_interfaces(); - interface++) { - int xiface = cvmx_helper_node_interface_to_xiface(cvmx_get_node_num(), - interface); - /* - * Set the frame max size and jabber size to 65535, as the defaults - * are too small. - */ - cvmx_helper_interface_mode_t imode = cvmx_helper_interface_get_mode(xiface); - int num_ports = cvmx_helper_ports_on_interface(xiface); - - switch (imode) { - case CVMX_HELPER_INTERFACE_MODE_SGMII: - case CVMX_HELPER_INTERFACE_MODE_QSGMII: - case CVMX_HELPER_INTERFACE_MODE_XAUI: - case CVMX_HELPER_INTERFACE_MODE_RXAUI: - for (port = 0; port < num_ports; port++) - csr_wr(CVMX_GMXX_RXX_JABBER(port, interface), 65535); - /* Set max and min value for frame check */ - cvmx_pip_set_frame_check(interface, -1); - break; - - case CVMX_HELPER_INTERFACE_MODE_RGMII: - case CVMX_HELPER_INTERFACE_MODE_GMII: - /* Set max and min value for frame check */ - cvmx_pip_set_frame_check(interface, -1); - for (port = 0; port < num_ports; port++) { - csr_wr(CVMX_GMXX_RXX_FRM_MAX(port, interface), 65535); - csr_wr(CVMX_GMXX_RXX_JABBER(port, interface), 65535); - } - break; - case CVMX_HELPER_INTERFACE_MODE_ILK: - /* Set max and min value for frame check */ - cvmx_pip_set_frame_check(interface, -1); - for (port = 0; port < num_ports; port++) { - int ipd_port = cvmx_helper_get_ipd_port(interface, port); - - cvmx_ilk_enable_la_header(ipd_port, 0); - } - break; - case CVMX_HELPER_INTERFACE_MODE_SRIO: - /* Set max and min value for frame check */ - cvmx_pip_set_frame_check(interface, -1); - break; - case CVMX_HELPER_INTERFACE_MODE_AGL: - /* Set max and min value for frame check */ - cvmx_pip_set_frame_check(interface, -1); - csr_wr(CVMX_AGL_GMX_RXX_FRM_MAX(0), 65535); - csr_wr(CVMX_AGL_GMX_RXX_JABBER(0), 65535); - break; - default: - break; - } - } - } -} - -/** - * Enable storing short packets only in the WQE - * unless NO_WPTR is set, which already has the same effect - */ -void cvmx_helper_cfg_store_short_packets_in_wqe(void) -{ - int interface, port; - cvmx_ipd_ctl_status_t ipd_ctl_status; - unsigned int dyn_rs = 1; - - if (octeon_has_feature(OCTEON_FEATURE_PKI)) - return; - - /* NO_WPTR combines WQE with 1st MBUF, RS is redundant */ - ipd_ctl_status.u64 = csr_rd(CVMX_IPD_CTL_STATUS); - if (ipd_ctl_status.s.no_wptr) { - dyn_rs = 0; - /* Note: consider also setting 'ignrs' wtn NO_WPTR is set */ - } - - for (interface = 0; interface < cvmx_helper_get_number_of_interfaces(); interface++) { - int num_ports = cvmx_helper_ports_on_interface(interface); - - for (port = 0; port < num_ports; port++) { - cvmx_pip_port_cfg_t port_cfg; - int pknd = port; - - if (octeon_has_feature(OCTEON_FEATURE_PKND)) - pknd = cvmx_helper_get_pknd(interface, port); - else - pknd = cvmx_helper_get_ipd_port(interface, port); - port_cfg.u64 = csr_rd(CVMX_PIP_PRT_CFGX(pknd)); - port_cfg.s.dyn_rs = dyn_rs; - csr_wr(CVMX_PIP_PRT_CFGX(pknd), port_cfg.u64); - } - } -} - int __cvmx_helper_cfg_pko_port_interface(int pko_port) { return cvmx_cfg_pko_port_map[pko_port].ccppl_interface; @@ -716,16 +473,6 @@ int cvmx_helper_cfg_ipd2pko_port_base(int ipd_port) return ipd2pko_port_cache[ipd_y][ipd_x].ccppp_base_port; } -int cvmx_helper_cfg_ipd2pko_port_num(int ipd_port) -{ - int ipd_y, ipd_x; - - ipd_y = IPD2PKO_CACHE_Y(ipd_port); - ipd_x = __cvmx_helper_cfg_ipd2pko_cachex(ipd_port); - - return ipd2pko_port_cache[ipd_y][ipd_x].ccppp_nports; -} - /** * Return the number of queues to be assigned to this pko_port * @@ -980,8 +727,6 @@ int __cvmx_helper_init_port_valid(void) rc = __cvmx_helper_parse_bgx_dt(fdt_addr); if (!rc) rc = __cvmx_fdt_parse_vsc7224(fdt_addr); - if (!rc) - rc = __cvmx_fdt_parse_avsp5410(fdt_addr); if (!rc && octeon_has_feature(OCTEON_FEATURE_BGX_XCV)) rc = __cvmx_helper_parse_bgx_rgmii_dt(fdt_addr); @@ -1030,44 +775,6 @@ int __cvmx_helper_init_port_valid(void) return 0; } -typedef int (*cvmx_import_config_t)(void); -cvmx_import_config_t cvmx_import_app_config; - -int __cvmx_helper_init_port_config_data_local(void) -{ - int rv = 0; - int dbg = 0; - - if (!port_cfg_data_initialized) - cvmx_init_port_cfg(); - - if (octeon_has_feature(OCTEON_FEATURE_PKND)) { - if (cvmx_import_app_config) { - rv = (*cvmx_import_app_config)(); - if (rv != 0) { - debug("failed to import config\n"); - return -1; - } - } - - cvmx_helper_cfg_init_pko_port_map(); - __cvmx_helper_cfg_init_ipd2pko_cache(); - } else { - if (cvmx_import_app_config) { - rv = (*cvmx_import_app_config)(); - if (rv != 0) { - debug("failed to import config\n"); - return -1; - } - } - } - if (dbg) { - cvmx_helper_cfg_show_cfg(); - cvmx_pko_queue_show(); - } - return rv; -} - /* * This call is made from Linux octeon_ethernet driver * to setup the PKO with a specific queue count and @@ -1077,9 +784,8 @@ int cvmx_pko_alloc_iport_and_queues(int interface, int port, int port_cnt, int q { int rv, p, port_start, cnt; - if (dbg) - debug("%s: intf %d/%d pcnt %d qcnt %d\n", __func__, interface, port, port_cnt, - queue_cnt); + debug("%s: intf %d/%d pcnt %d qcnt %d\n", __func__, interface, port, port_cnt, + queue_cnt); if (!port_cfg_data_initialized) cvmx_init_port_cfg(); @@ -1122,6 +828,7 @@ static void cvmx_init_port_cfg(void) struct cvmx_srio_port_param *sr; pcfg = &cvmx_cfg_port[node][i][j]; + memset(pcfg, 0, sizeof(*pcfg)); pcfg->port_fdt_node = CVMX_HELPER_CFG_INVALID_VALUE; @@ -1188,8 +895,7 @@ int __cvmx_helper_init_port_config_data(int node) int pknd = 0, bpid = 0; const int use_static_config = 1; - if (dbg) - printf("%s:\n", __func__); + debug("%s:\n", __func__); if (!port_cfg_data_initialized) cvmx_init_port_cfg(); @@ -1295,10 +1001,11 @@ int __cvmx_helper_init_port_config_data(int node) __cvmx_helper_cfg_init_ipd2pko_cache(); } - if (dbg) { - cvmx_helper_cfg_show_cfg(); - cvmx_pko_queue_show(); - } +#ifdef DEBUG + cvmx_helper_cfg_show_cfg(); + cvmx_pko_queue_show(); +#endif + return rv; } @@ -1337,39 +1044,6 @@ int cvmx_helper_get_port_fdt_node_offset(int xiface, int index) } /** - * Search for a port based on its FDT node offset - * - * @param of_offset Node offset of port to search for - * @param[out] xiface xinterface of match - * @param[out] index port index of match - * - * Return: 0 if found, -1 if not found - */ -int cvmx_helper_cfg_get_xiface_index_by_fdt_node_offset(int of_offset, int *xiface, int *index) -{ - int iface; - int i; - int node; - struct cvmx_cfg_port_param *pcfg = NULL; - *xiface = -1; - *index = -1; - - for (node = 0; node < CVMX_MAX_NODES; node++) { - for (iface = 0; iface < CVMX_HELPER_MAX_IFACE; iface++) { - for (i = 0; i < CVMX_HELPER_CFG_MAX_PORT_PER_IFACE; i++) { - pcfg = &cvmx_cfg_port[node][iface][i]; - if (pcfg->valid && pcfg->port_fdt_node == of_offset) { - *xiface = cvmx_helper_node_interface_to_xiface(node, iface); - *index = i; - return 0; - } - } - } - } - return -1; -} - -/** * @INTERNAL * Store the FDT node offset in the device tree of a phy * @@ -1441,23 +1115,6 @@ bool cvmx_helper_get_port_autonegotiation(int xiface, int index) /** * @INTERNAL - * Override default forward error correction for a port - * - * @param xiface node and interface - * @param index port index - * @param enable true to enable fec, false to disable it - */ -void cvmx_helper_set_port_fec(int xiface, int index, bool enable) -{ - struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); - - if (!port_cfg_data_initialized) - cvmx_init_port_cfg(); - cvmx_cfg_port[xi.node][xi.interface][index].enable_fec = enable; -} - -/** - * @INTERNAL * Returns if forward error correction is enabled or not. * * @param xiface node and interface @@ -1476,161 +1133,6 @@ bool cvmx_helper_get_port_fec(int xiface, int index) /** * @INTERNAL - * Configure the SRIO RX interface AGC settings for host mode - * - * @param xiface node and interface - * @param index lane - * @param long_run true for long run, false for short run - * @param agc_override true to put AGC in manual mode - * @param ctle_zero RX equalizer peaking control (default 0x6) - * @param agc_pre_ctle AGC pre-CTLE gain (default 0x5) - * @param agc_post_ctle AGC post-CTLE gain (default 0x4) - * - * NOTE: This must be called before SRIO is initialized to take effect - */ -void cvmx_helper_set_srio_rx(int xiface, int index, bool long_run, bool ctle_zero_override, - u8 ctle_zero, bool agc_override, uint8_t agc_pre_ctle, - uint8_t agc_post_ctle) -{ - struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); - struct cvmx_cfg_port_param *pcfg = &cvmx_cfg_port[xi.node][xi.interface][index]; - struct cvmx_srio_port_param *sr = long_run ? &pcfg->srio_long : &pcfg->srio_short; - - if (!port_cfg_data_initialized) - cvmx_init_port_cfg(); - sr->srio_rx_ctle_zero_override = ctle_zero_override; - sr->srio_rx_ctle_zero = ctle_zero; - sr->srio_rx_ctle_agc_override = agc_override; - sr->srio_rx_agc_pre_ctle = agc_pre_ctle; - sr->srio_rx_agc_post_ctle = agc_post_ctle; -} - -/** - * @INTERNAL - * Get the SRIO RX interface AGC settings for host mode - * - * @param xiface node and interface - * @param index lane - * @param long_run true for long run, false for short run - * @param[out] agc_override true to put AGC in manual mode - * @param[out] ctle_zero RX equalizer peaking control (default 0x6) - * @param[out] agc_pre_ctle AGC pre-CTLE gain (default 0x5) - * @param[out] agc_post_ctle AGC post-CTLE gain (default 0x4) - */ -void cvmx_helper_get_srio_rx(int xiface, int index, bool long_run, bool *ctle_zero_override, - u8 *ctle_zero, bool *agc_override, uint8_t *agc_pre_ctle, - uint8_t *agc_post_ctle) -{ - struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); - struct cvmx_cfg_port_param *pcfg = &cvmx_cfg_port[xi.node][xi.interface][index]; - struct cvmx_srio_port_param *sr = long_run ? &pcfg->srio_long : &pcfg->srio_short; - - if (!port_cfg_data_initialized) - cvmx_init_port_cfg(); - if (ctle_zero_override) - *ctle_zero_override = sr->srio_rx_ctle_zero_override; - if (ctle_zero) - *ctle_zero = sr->srio_rx_ctle_zero; - if (agc_override) - *agc_override = sr->srio_rx_ctle_agc_override; - if (agc_pre_ctle) - *agc_pre_ctle = sr->srio_rx_agc_pre_ctle; - if (agc_post_ctle) - *agc_post_ctle = sr->srio_rx_agc_post_ctle; -} - -/** - * @INTERNAL - * Configure the SRIO TX interface for host mode - * - * @param xiface node and interface - * @param index lane - * @param long_run true for long run, false for short run - * @param tx_swing tx swing value to use (default 0x7), -1 to not - * override. - * @param tx_gain PCS SDS TX gain (default 0x3), -1 to not - * override - * @param tx_premptap_override true to override preemphasis control - * @param tx_premptap_pre preemphasis pre tap value (default 0x0) - * @param tx_premptap_post preemphasis post tap value (default 0xF) - * @param tx_vboost vboost enable (1 = enable, -1 = don't override) - * hardware default is 1. - * - * NOTE: This must be called before SRIO is initialized to take effect - */ -void cvmx_helper_set_srio_tx(int xiface, int index, bool long_run, int tx_swing, int tx_gain, - bool tx_premptap_override, uint8_t tx_premptap_pre, - u8 tx_premptap_post, int tx_vboost) -{ - struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); - struct cvmx_cfg_port_param *pcfg = &cvmx_cfg_port[xi.node][xi.interface][index]; - struct cvmx_srio_port_param *sr = long_run ? &pcfg->srio_long : &pcfg->srio_short; - - if (!port_cfg_data_initialized) - cvmx_init_port_cfg(); - - sr->srio_tx_swing_override = (tx_swing != -1); - sr->srio_tx_swing = tx_swing != -1 ? tx_swing : 0x7; - sr->srio_tx_gain_override = (tx_gain != -1); - sr->srio_tx_gain = tx_gain != -1 ? tx_gain : 0x3; - sr->srio_tx_premptap_override = tx_premptap_override; - sr->srio_tx_premptap_pre = tx_premptap_override ? tx_premptap_pre : 0; - sr->srio_tx_premptap_post = tx_premptap_override ? tx_premptap_post : 0xF; - sr->srio_tx_vboost_override = tx_vboost != -1; - sr->srio_tx_vboost = (tx_vboost != -1) ? tx_vboost : 1; -} - -/** - * @INTERNAL - * Get the SRIO TX interface settings for host mode - * - * @param xiface node and interface - * @param index lane - * @param long_run true for long run, false for short run - * @param[out] tx_swing_override true to override pcs_sds_txX_swing - * @param[out] tx_swing tx swing value to use (default 0x7) - * @param[out] tx_gain_override true to override default gain - * @param[out] tx_gain PCS SDS TX gain (default 0x3) - * @param[out] tx_premptap_override true to override preemphasis control - * @param[out] tx_premptap_pre preemphasis pre tap value (default 0x0) - * @param[out] tx_premptap_post preemphasis post tap value (default 0xF) - * @param[out] tx_vboost_override override vboost setting - * @param[out] tx_vboost vboost enable (default true) - */ -void cvmx_helper_get_srio_tx(int xiface, int index, bool long_run, bool *tx_swing_override, - u8 *tx_swing, bool *tx_gain_override, uint8_t *tx_gain, - bool *tx_premptap_override, uint8_t *tx_premptap_pre, - u8 *tx_premptap_post, bool *tx_vboost_override, bool *tx_vboost) -{ - struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); - struct cvmx_cfg_port_param *pcfg = &cvmx_cfg_port[xi.node][xi.interface][index]; - struct cvmx_srio_port_param *sr = long_run ? &pcfg->srio_long : &pcfg->srio_short; - - if (!port_cfg_data_initialized) - cvmx_init_port_cfg(); - - if (tx_swing_override) - *tx_swing_override = sr->srio_tx_swing_override; - if (tx_swing) - *tx_swing = sr->srio_tx_swing; - if (tx_gain_override) - *tx_gain_override = sr->srio_tx_gain_override; - if (tx_gain) - *tx_gain = sr->srio_tx_gain; - if (tx_premptap_override) - *tx_premptap_override = sr->srio_tx_premptap_override; - if (tx_premptap_pre) - *tx_premptap_pre = sr->srio_tx_premptap_pre; - if (tx_premptap_post) - *tx_premptap_post = sr->srio_tx_premptap_post; - if (tx_vboost_override) - *tx_vboost_override = sr->srio_tx_vboost_override; - if (tx_vboost) - *tx_vboost = sr->srio_tx_vboost; -} - -/** - * @INTERNAL * Sets the PHY info data structure * * @param xiface node and interface @@ -1685,23 +1187,6 @@ struct cvmx_phy_gpio_leds *cvmx_helper_get_port_phy_leds(int xiface, int index) /** * @INTERNAL - * Sets a pointer to the PHY LED configuration (if local GPIOs drive them) - * - * @param xiface node and interface - * @param index portindex - * @param leds pointer to led data structure - */ -void cvmx_helper_set_port_phy_leds(int xiface, int index, struct cvmx_phy_gpio_leds *leds) -{ - struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); - - if (!port_cfg_data_initialized) - cvmx_init_port_cfg(); - cvmx_cfg_port[xi.node][xi.interface][index].gpio_leds = leds; -} - -/** - * @INTERNAL * Disables RGMII TX clock bypass and sets delay value * * @param xiface node and interface @@ -1744,59 +1229,6 @@ void cvmx_helper_cfg_get_rgmii_tx_clk_delay(int xiface, int index, bool *bypass, } /** - * @INTERNAL - * Retrieve the SFP node offset in the device tree - * - * @param xiface node and interface - * @param index port index - * - * Return: offset in device tree or -1 if error or not defined. - */ -int cvmx_helper_cfg_get_sfp_fdt_offset(int xiface, int index) -{ - struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); - - if (!port_cfg_data_initialized) - cvmx_init_port_cfg(); - return cvmx_cfg_port[xi.node][xi.interface][index].sfp_of_offset; -} - -/** - * @INTERNAL - * Sets the SFP node offset - * - * @param xiface node and interface - * @param index port index - * @param sfp_of_offset Offset of SFP node in device tree - */ -void cvmx_helper_cfg_set_sfp_fdt_offset(int xiface, int index, int sfp_of_offset) -{ - struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); - - if (!port_cfg_data_initialized) - cvmx_init_port_cfg(); - cvmx_cfg_port[xi.node][xi.interface][index].sfp_of_offset = sfp_of_offset; -} - -/** - * Get data structure defining the Microsemi VSC7224 channel info - * or NULL if not present - * - * @param xiface node and interface - * @param index port index - * - * Return: pointer to vsc7224 data structure or NULL if not present - */ -struct cvmx_vsc7224_chan *cvmx_helper_cfg_get_vsc7224_chan_info(int xiface, int index) -{ - struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); - - if (!port_cfg_data_initialized) - cvmx_init_port_cfg(); - return cvmx_cfg_port[xi.node][xi.interface][index].vsc7224_chan; -} - -/** * Sets the Microsemi VSC7224 channel info data structure * * @param xiface node and interface @@ -1814,40 +1246,6 @@ void cvmx_helper_cfg_set_vsc7224_chan_info(int xiface, int index, } /** - * Get data structure defining the Avago AVSP5410 phy info - * or NULL if not present - * - * @param xiface node and interface - * @param index port index - * - * Return: pointer to avsp5410 data structure or NULL if not present - */ -struct cvmx_avsp5410 *cvmx_helper_cfg_get_avsp5410_info(int xiface, int index) -{ - struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); - - if (!port_cfg_data_initialized) - cvmx_init_port_cfg(); - return cvmx_cfg_port[xi.node][xi.interface][index].avsp5410; -} - -/** - * Sets the Avago AVSP5410 phy info data structure - * - * @param xiface node and interface - * @param index port index - * @param[in] avsp5410_info Avago AVSP5410 data structure - */ -void cvmx_helper_cfg_set_avsp5410_info(int xiface, int index, struct cvmx_avsp5410 *avsp5410_info) -{ - struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); - - if (!port_cfg_data_initialized) - cvmx_init_port_cfg(); - cvmx_cfg_port[xi.node][xi.interface][index].avsp5410 = avsp5410_info; -} - -/** * Gets the SFP data associated with a port * * @param xiface node and interface @@ -1879,36 +1277,3 @@ void cvmx_helper_cfg_set_sfp_info(int xiface, int index, struct cvmx_fdt_sfp_inf cvmx_init_port_cfg(); cvmx_cfg_port[xi.node][xi.interface][index].sfp_info = sfp_info; } - -/** - * Returns a pointer to the phy device associated with a port - * - * @param xiface node and interface - * @param index port index - * - * return pointer to phy device or NULL if none - */ -struct phy_device *cvmx_helper_cfg_get_phy_device(int xiface, int index) -{ - struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); - - if (!port_cfg_data_initialized) - cvmx_init_port_cfg(); - return cvmx_cfg_port[xi.node][xi.interface][index].phydev; -} - -/** - * Sets the phy device associated with a port - * - * @param xiface node and interface - * @param index port index - * @param[in] phydev phy device to assiciate - */ -void cvmx_helper_cfg_set_phy_device(int xiface, int index, struct phy_device *phydev) -{ - struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); - - if (!port_cfg_data_initialized) - cvmx_init_port_cfg(); - cvmx_cfg_port[xi.node][xi.interface][index].phydev = phydev; -} diff --git a/arch/mips/mach-octeon/cvmx-helper-fdt.c b/arch/mips/mach-octeon/cvmx-helper-fdt.c index 3177dfb..400c73a 100644 --- a/arch/mips/mach-octeon/cvmx-helper-fdt.c +++ b/arch/mips/mach-octeon/cvmx-helper-fdt.c @@ -1,14 +1,17 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (C) 2020 Marvell International Ltd. + * Copyright (C) 2020-2022 Marvell International Ltd. * * FDT Helper functions similar to those provided to U-Boot. */ +#include <dm.h> +#include <i2c.h> #include <log.h> #include <malloc.h> #include <net.h> #include <linux/delay.h> +#include <asm-generic/gpio.h> #include <mach/cvmx-regs.h> #include <mach/cvmx-csr.h> @@ -19,35 +22,6 @@ #include <mach/cvmx-helper-board.h> #include <mach/cvmx-helper-cfg.h> #include <mach/cvmx-helper-fdt.h> -#include <mach/cvmx-helper-gpio.h> - -/** Structure used to get type of GPIO from device tree */ -struct gpio_compat { - char *compatible; /** Compatible string */ - enum cvmx_gpio_type type; /** Type */ - int8_t size; /** (max) Number of pins */ -}; - -#define GPIO_REG_PCA953X_IN 0 -#define GPIO_REG_PCA953X_OUT 1 -#define GPIO_REG_PCA953X_INVERT 2 -#define GPIO_REG_PCA953X_DIR 3 - -#define GPIO_REG_PCA957X_IN 0 -#define GPIO_REG_PCA957X_INVERT 1 -#define GPIO_REG_PCA957X_CFG 4 -#define GPIO_REG_PCA957X_OUT 5 - -enum cvmx_i2c_mux_type { I2C_MUX, I2C_SWITCH }; - -/** Structure used to get type of GPIO from device tree */ -struct mux_compat { - char *compatible; /** Compatible string */ - enum cvmx_i2c_bus_type type; /** Mux chip type */ - enum cvmx_i2c_mux_type mux_type; /** Type of mux */ - u8 enable; /** Enable bit for mux */ - u8 size; /** (max) Number of channels */ -}; /** * Local allocator to handle both SE and U-Boot that also zeroes out memory @@ -57,45 +31,19 @@ struct mux_compat { * Return: pointer to allocated memory or NULL if out of memory. * Alignment is set to 8-bytes. */ -void *__cvmx_fdt_alloc(size_t size) +static void *cvmx_fdt_alloc(size_t size) { return calloc(size, 1); } -/** - * Free allocated memory. - * - * @param ptr pointer to memory to free - * - * NOTE: This only works in U-Boot since SE does not really have a freeing - * mechanism. In SE the memory is zeroed out. - */ -void __cvmx_fdt_free(void *ptr, size_t size) -{ - free(ptr); -} - -/** - * Look up a phandle and follow it to its node then return the offset of that - * node. - * - * @param[in] fdt_addr pointer to FDT blob - * @param node node to read phandle from - * @param[in] prop_name name of property to find - * @param[in,out] lenp Number of phandles, input max number - * @param[out] nodes Array of phandle nodes - * - * Return: -ve error code on error or 0 for success - */ -int cvmx_fdt_lookup_phandles(const void *fdt_addr, int node, - const char *prop_name, int *lenp, - int *nodes) +int cvmx_ofnode_lookup_phandles(ofnode node, const char *prop_name, int *lenp, + ofnode *nodes) { const u32 *phandles; int count; int i; - phandles = fdt_getprop(fdt_addr, node, prop_name, &count); + phandles = ofnode_get_property(node, prop_name, &count); if (!phandles || count < 0) return -FDT_ERR_NOTFOUND; @@ -104,8 +52,8 @@ int cvmx_fdt_lookup_phandles(const void *fdt_addr, int node, count = *lenp; for (i = 0; i < count; i++) - nodes[i] = fdt_node_offset_by_phandle(fdt_addr, - fdt32_to_cpu(phandles[i])); + nodes[i] = ofnode_get_by_phandle(fdt32_to_cpu(phandles[i])); + *lenp = count; return 0; } @@ -143,126 +91,6 @@ int cvmx_fdt_get_cpu_node(const void *fdt_addr, int node) } /** - * Get the total size of the flat device tree - * - * @param[in] fdt_addr Address of FDT - * - * Return: Size of flat device tree in bytes or error if negative. - */ -int cvmx_fdt_get_fdt_size(const void *fdt_addr) -{ - int rc; - - rc = fdt_check_header(fdt_addr); - if (rc) - return rc; - return fdt_totalsize(fdt_addr); -} - -/** - * Returns if a node is compatible with one of the items in the string list - * - * @param[in] fdt_addr Pointer to flat device tree - * @param node Node offset to check - * @param[in] strlist Array of FDT device compatibility strings, - * must end with NULL or empty string. - * - * Return: 0 if at least one item matches, 1 if no matches - */ -int cvmx_fdt_node_check_compatible_list(const void *fdt_addr, int node, const char *const *strlist) -{ - while (*strlist && **strlist) { - if (!fdt_node_check_compatible(fdt_addr, node, *strlist)) - return 0; - strlist++; - } - return 1; -} - -/** - * Given a FDT node, return the next compatible node. - * - * @param[in] fdt_addr Pointer to flat device tree - * @param start_offset Starting node offset or -1 to find the first - * @param strlist Array of FDT device compatibility strings, must - * end with NULL or empty string. - * - * Return: next matching node or -1 if no more matches. - */ -int cvmx_fdt_node_offset_by_compatible_list(const void *fdt_addr, int startoffset, - const char *const *strlist) -{ - int offset; - - for (offset = fdt_next_node(fdt_addr, startoffset, NULL); offset >= 0; - offset = fdt_next_node(fdt_addr, offset, NULL)) { - if (!cvmx_fdt_node_check_compatible_list(fdt_addr, offset, strlist)) - return offset; - } - return -1; -} - -/** - * Attaches a PHY to a SFP or QSFP. - * - * @param sfp sfp to attach PHY to - * @param phy_info phy descriptor to attach or NULL to detach - */ -void cvmx_sfp_attach_phy(struct cvmx_fdt_sfp_info *sfp, struct cvmx_phy_info *phy_info) -{ - sfp->phy_info = phy_info; - if (phy_info) - phy_info->sfp_info = sfp; -} - -/** - * Assigns an IPD port to a SFP slot - * - * @param sfp Handle to SFP data structure - * @param ipd_port Port to assign it to - * - * Return: 0 for success, -1 on error - */ -int cvmx_sfp_set_ipd_port(struct cvmx_fdt_sfp_info *sfp, int ipd_port) -{ - int i; - - if (sfp->is_qsfp) { - int xiface; - cvmx_helper_interface_mode_t mode; - - xiface = cvmx_helper_get_interface_num(ipd_port); - mode = cvmx_helper_interface_get_mode(xiface); - sfp->ipd_port[0] = ipd_port; - - switch (mode) { - case CVMX_HELPER_INTERFACE_MODE_SGMII: - case CVMX_HELPER_INTERFACE_MODE_XFI: - case CVMX_HELPER_INTERFACE_MODE_10G_KR: - for (i = 1; i < 4; i++) - sfp->ipd_port[i] = cvmx_helper_get_ipd_port(xiface, i); - break; - case CVMX_HELPER_INTERFACE_MODE_XLAUI: - case CVMX_HELPER_INTERFACE_MODE_40G_KR4: - sfp->ipd_port[0] = ipd_port; - for (i = 1; i < 4; i++) - sfp->ipd_port[i] = -1; - break; - default: - debug("%s: Interface mode %s for interface 0x%x, ipd_port %d not supported for QSFP\n", - __func__, cvmx_helper_interface_mode_to_string(mode), xiface, - ipd_port); - return -1; - } - } else { - sfp->ipd_port[0] = ipd_port; - for (i = 1; i < 4; i++) - sfp->ipd_port[i] = -1; - } - return 0; -} - -/** * Parses all of the channels assigned to a VSC7224 device * * @param[in] fdt_addr Address of flat device tree @@ -271,10 +99,10 @@ int cvmx_sfp_set_ipd_port(struct cvmx_fdt_sfp_info *sfp, int ipd_port) * * Return: 0 for success, -1 on error */ -static int cvmx_fdt_parse_vsc7224_channels(const void *fdt_addr, int of_offset, +static int cvmx_fdt_parse_vsc7224_channels(ofnode node, struct cvmx_vsc7224 *vsc7224) { - int parent_offset = of_offset; + struct ofnode_phandle_args phandle; int err = 0; int reg; int num_chan = 0; @@ -289,35 +117,33 @@ static int cvmx_fdt_parse_vsc7224_channels(const void *fdt_addr, int of_offset, bool is_tx; bool is_qsfp; const char *mac_str; + ofnode node_chan; - debug("%s(%p, %d, %s)\n", __func__, fdt_addr, of_offset, vsc7224->name); - do { - /* Walk through all channels */ - of_offset = fdt_node_offset_by_compatible(fdt_addr, of_offset, - "vitesse,vsc7224-channel"); - if (of_offset == -FDT_ERR_NOTFOUND) { - break; - } else if (of_offset < 0) { - debug("%s: Failed finding compatible channel\n", - __func__); - err = -1; + debug("%s(%x, %s)\n", __func__, ofnode_to_offset(node), vsc7224->name); + ofnode_for_each_compatible_node(node_chan, "vitesse,vsc7224-channel") { + if (!ofnode_valid(node_chan)) { + debug("%s: Error parsing FDT node %s\n", + __func__, ofnode_get_name(node)); break; } - if (fdt_parent_offset(fdt_addr, of_offset) != parent_offset) + + if (ofnode_to_offset(ofnode_get_parent(node_chan)) != + ofnode_to_offset(node)) break; - reg = cvmx_fdt_get_int(fdt_addr, of_offset, "reg", -1); + + reg = ofnode_get_addr(node_chan); if (reg < 0 || reg > 3) { debug("%s: channel reg is either not present or out of range\n", __func__); err = -1; break; } - is_tx = cvmx_fdt_get_bool(fdt_addr, of_offset, "direction-tx"); + is_tx = ofnode_read_bool(node_chan, "direction-tx"); debug("%s(%s): Adding %cx channel %d\n", __func__, vsc7224->name, is_tx ? 't' : 'r', reg); - tap_values = (const uint32_t *)fdt_getprop(fdt_addr, of_offset, "taps", &len); + tap_values = ofnode_get_property(node_chan, "taps", &len); if (!tap_values) { debug("%s: Error: no taps defined for vsc7224 channel %d\n", __func__, reg); @@ -341,8 +167,8 @@ static int cvmx_fdt_parse_vsc7224_channels(const void *fdt_addr, int of_offset, num_taps = len / 16; debug("%s: Adding %d taps\n", __func__, num_taps); - channel = __cvmx_fdt_alloc(sizeof(*channel) + - num_taps * sizeof(struct cvmx_vsc7224_tap)); + channel = cvmx_fdt_alloc(sizeof(*channel) + + num_taps * sizeof(struct cvmx_vsc7224_tap)); if (!channel) { debug("%s: Out of memory\n", __func__); err = -1; @@ -351,11 +177,12 @@ static int cvmx_fdt_parse_vsc7224_channels(const void *fdt_addr, int of_offset, vsc7224->channel[reg] = channel; channel->num_taps = num_taps; channel->lane = reg; - channel->of_offset = of_offset; + channel->of_offset = ofnode_to_offset(node_chan); channel->is_tx = is_tx; - channel->pretap_disable = cvmx_fdt_get_bool(fdt_addr, of_offset, "pretap-disable"); - channel->posttap_disable = - cvmx_fdt_get_bool(fdt_addr, of_offset, "posttap-disable"); + channel->pretap_disable = ofnode_read_bool(node_chan, + "pretap-disable"); + channel->posttap_disable = ofnode_read_bool(node_chan, + "posttap-disable"); channel->vsc7224 = vsc7224; /* Read all the tap values */ for (i = 0; i < num_taps; i++) { @@ -371,9 +198,9 @@ static int cvmx_fdt_parse_vsc7224_channels(const void *fdt_addr, int of_offset, channel->ipd_port = -1; mac_str = "sfp-mac"; - if (fdt_getprop(fdt_addr, of_offset, mac_str, NULL)) { + if (ofnode_get_property(node_chan, mac_str, NULL)) { is_qsfp = false; - } else if (fdt_getprop(fdt_addr, of_offset, "qsfp-mac", NULL)) { + } else if (ofnode_get_property(node_chan, "qsfp-mac", NULL)) { is_qsfp = true; mac_str = "qsfp-mac"; } else { @@ -381,52 +208,59 @@ static int cvmx_fdt_parse_vsc7224_channels(const void *fdt_addr, int of_offset, vsc7224->name, reg); return -1; } - of_mac = cvmx_fdt_lookup_phandle(fdt_addr, of_offset, mac_str); - if (of_mac < 0) { + + err = ofnode_parse_phandle_with_args(node_chan, mac_str, NULL, + 0, 0, &phandle); + if (err) { debug("%s: Error %d with MAC %s phandle for %s\n", __func__, of_mac, mac_str, vsc7224->name); return -1; } - debug("%s: Found mac at offset %d\n", __func__, of_mac); - err = cvmx_helper_cfg_get_xiface_index_by_fdt_node_offset(of_mac, &xiface, &index); - if (!err) { - channel->xiface = xiface; - channel->index = index; - channel->ipd_port = cvmx_helper_get_ipd_port(xiface, index); - - debug("%s: Found MAC, xiface: 0x%x, index: %d, ipd port: %d\n", __func__, - xiface, index, channel->ipd_port); - if (channel->ipd_port >= 0) { - cvmx_helper_cfg_set_vsc7224_chan_info(xiface, index, channel); - debug("%s: Storing config channel for xiface 0x%x, index %d\n", - __func__, xiface, index); - } - sfp_info = cvmx_helper_cfg_get_sfp_info(xiface, index); - if (!sfp_info) { - debug("%s: Warning: no (Q)SFP+ slot found for xinterface 0x%x, index %d for channel %d\n", - __func__, xiface, index, channel->lane); - continue; - } + debug("%s: Found mac at %s\n", __func__, + ofnode_get_name(phandle.node)); + + xiface = (ofnode_get_addr(ofnode_get_parent(phandle.node)) + >> 24) & 0x0f; + index = ofnode_get_addr(phandle.node); + channel->xiface = xiface; + channel->index = index; + channel->ipd_port = cvmx_helper_get_ipd_port(xiface, index); + + debug("%s: Found MAC, xiface: 0x%x, index: %d, ipd port: %d\n", __func__, + xiface, index, channel->ipd_port); + if (channel->ipd_port >= 0) { + cvmx_helper_cfg_set_vsc7224_chan_info(xiface, index, channel); + debug("%s: Storing config channel for xiface 0x%x, index %d\n", + __func__, xiface, index); + } + sfp_info = cvmx_helper_cfg_get_sfp_info(xiface, index); + if (!sfp_info) { + debug("%s: Warning: no (Q)SFP+ slot found for xinterface 0x%x, index %d for channel %d\n", + __func__, xiface, index, channel->lane); + continue; + } - /* Link it */ - channel->next = sfp_info->vsc7224_chan; - if (sfp_info->vsc7224_chan) - sfp_info->vsc7224_chan->prev = channel; - sfp_info->vsc7224_chan = channel; - sfp_info->is_vsc7224 = true; - debug("%s: Registering VSC7224 %s channel %d with SFP %s\n", __func__, - vsc7224->name, channel->lane, sfp_info->name); - if (!sfp_info->mod_abs_changed) { - debug("%s: Registering cvmx_sfp_vsc7224_mod_abs_changed at %p for xinterface 0x%x, index %d\n", - __func__, &cvmx_sfp_vsc7224_mod_abs_changed, xiface, index); - cvmx_sfp_register_mod_abs_changed( - sfp_info, - &cvmx_sfp_vsc7224_mod_abs_changed, - NULL); - } + /* Link it */ + channel->next = sfp_info->vsc7224_chan; + if (sfp_info->vsc7224_chan) + sfp_info->vsc7224_chan->prev = channel; + sfp_info->vsc7224_chan = channel; + sfp_info->is_vsc7224 = true; + debug("%s: Registering VSC7224 %s channel %d with SFP %s\n", __func__, + vsc7224->name, channel->lane, sfp_info->name); + if (!sfp_info->mod_abs_changed) { + debug("%s: Registering cvmx_sfp_vsc7224_mod_abs_changed at %p for xinterface 0x%x, index %d\n", + __func__, &cvmx_sfp_vsc7224_mod_abs_changed, xiface, index); + cvmx_sfp_register_mod_abs_changed( + sfp_info, + &cvmx_sfp_vsc7224_mod_abs_changed, + NULL); } - } while (!err && num_chan < 4); + + if (num_chan >= 4) + break; + } return err; } @@ -441,12 +275,17 @@ static int cvmx_fdt_parse_vsc7224_channels(const void *fdt_addr, int of_offset, */ int __cvmx_fdt_parse_vsc7224(const void *fdt_addr) { - int of_offset = -1; struct cvmx_vsc7224 *vsc7224 = NULL; - struct cvmx_fdt_gpio_info *gpio_info = NULL; + ofnode node; int err = 0; - int of_parent; static bool parsed; + const int *init_array; + struct udevice *dev; + u16 value; + int reg; + int len; + int ret; + int i; debug("%s(%p)\n", __func__, fdt_addr); @@ -454,30 +293,23 @@ int __cvmx_fdt_parse_vsc7224(const void *fdt_addr) debug("%s: Already parsed\n", __func__); return 0; } - do { - of_offset = fdt_node_offset_by_compatible(fdt_addr, of_offset, - "vitesse,vsc7224"); - debug("%s: of_offset: %d\n", __func__, of_offset); - if (of_offset == -FDT_ERR_NOTFOUND) { - break; - } else if (of_offset < 0) { - err = -1; - debug("%s: Error %d parsing FDT\n", - __func__, of_offset); + + ofnode_for_each_compatible_node(node, "vitesse,vsc7224") { + if (!ofnode_valid(node)) { + debug("%s: Error parsing FDT node %s\n", + __func__, ofnode_get_name(node)); break; } - vsc7224 = __cvmx_fdt_alloc(sizeof(*vsc7224)); - + vsc7224 = cvmx_fdt_alloc(sizeof(*vsc7224)); if (!vsc7224) { debug("%s: Out of memory!\n", __func__); return -1; } - vsc7224->of_offset = of_offset; - vsc7224->i2c_addr = cvmx_fdt_get_int(fdt_addr, of_offset, - "reg", -1); - of_parent = fdt_parent_offset(fdt_addr, of_offset); - vsc7224->i2c_bus = cvmx_fdt_get_i2c_bus(fdt_addr, of_parent); + + vsc7224->of_offset = ofnode_to_offset(node); + vsc7224->i2c_addr = ofnode_get_addr(node); + vsc7224->i2c_bus = cvmx_ofnode_get_i2c_bus(ofnode_get_parent(node)); if (vsc7224->i2c_addr < 0) { debug("%s: Error: reg field missing\n", __func__); err = -1; @@ -488,168 +320,90 @@ int __cvmx_fdt_parse_vsc7224(const void *fdt_addr) err = -1; break; } - vsc7224->name = fdt_get_name(fdt_addr, of_offset, NULL); + vsc7224->name = ofnode_get_name(node); debug("%s: Adding %s\n", __func__, vsc7224->name); - if (fdt_getprop(fdt_addr, of_offset, "reset", NULL)) { - gpio_info = cvmx_fdt_gpio_get_info_phandle(fdt_addr, of_offset, "reset"); - vsc7224->reset_gpio = gpio_info; - } - if (fdt_getprop(fdt_addr, of_offset, "los", NULL)) { - gpio_info = cvmx_fdt_gpio_get_info_phandle(fdt_addr, of_offset, "los"); - vsc7224->los_gpio = gpio_info; - } - debug("%s: Parsing channels\n", __func__); - err = cvmx_fdt_parse_vsc7224_channels(fdt_addr, of_offset, vsc7224); + + err = gpio_request_by_name_nodev(node, "reset", 0, + &vsc7224->reset_gpio, + GPIOD_IS_OUT); if (err) { - debug("%s: Error parsing VSC7224 channels\n", __func__); - break; + printf("%s: reset GPIO not found in DT!\n", __func__); + return -ENODEV; } - } while (of_offset > 0); - if (err) { - debug("%s(): Error\n", __func__); - if (vsc7224) { - if (vsc7224->reset_gpio) - __cvmx_fdt_free(vsc7224->reset_gpio, sizeof(*vsc7224->reset_gpio)); - if (vsc7224->los_gpio) - __cvmx_fdt_free(vsc7224->los_gpio, sizeof(*vsc7224->los_gpio)); - if (vsc7224->i2c_bus) - cvmx_fdt_free_i2c_bus(vsc7224->i2c_bus); - __cvmx_fdt_free(vsc7224, sizeof(*vsc7224)); + err = gpio_request_by_name_nodev(node, "los", 0, + &vsc7224->los_gpio, + GPIOD_IS_IN); + if (err) { + printf("%s: los GPIO not found in DT!\n", __func__); + return -ENODEV; } - } - if (!err) - parsed = true; - - return err; -} - -/** - * @INTERNAL - * Parses all instances of the Avago AVSP5410 gearbox phy - * - * @param[in] fdt_addr Address of flat device tree - * - * Return: 0 for success, error otherwise - */ -int __cvmx_fdt_parse_avsp5410(const void *fdt_addr) -{ - int of_offset = -1; - struct cvmx_avsp5410 *avsp5410 = NULL; - struct cvmx_fdt_sfp_info *sfp_info; - int err = 0; - int of_parent; - static bool parsed; - int of_mac; - int xiface, index; - bool is_qsfp; - const char *mac_str; - - debug("%s(%p)\n", __func__, fdt_addr); - if (parsed) { - debug("%s: Already parsed\n", __func__); - return 0; - } - - do { - of_offset = fdt_node_offset_by_compatible(fdt_addr, of_offset, - "avago,avsp-5410"); - debug("%s: of_offset: %d\n", __func__, of_offset); - if (of_offset == -FDT_ERR_NOTFOUND) { - break; - } else if (of_offset < 0) { - err = -1; - debug("%s: Error %d parsing FDT\n", __func__, of_offset); - break; + /* + * This code was taken from the NIC23 board specific code + * but should be better placed here in the common code + */ + debug("%s: Putting device in reset\n", __func__); + dm_gpio_set_value(&vsc7224->reset_gpio, 1); + mdelay(10); + debug("%s: Taking device out of reset\n", __func__); + dm_gpio_set_value(&vsc7224->reset_gpio, 0); + mdelay(50); + + init_array = ofnode_get_property(node, "vitesse,reg-init", + &len); + if (!init_array) { + debug("%s: No initialization array\n", __func__); + continue; } - - avsp5410 = __cvmx_fdt_alloc(sizeof(*avsp5410)); - - if (!avsp5410) { - debug("%s: Out of memory!\n", __func__); + if ((len % 8) != 0) { + printf("%s: Error: register init string should be an array of reg number followed by value\n", + __func__); return -1; } - avsp5410->of_offset = of_offset; - avsp5410->i2c_addr = cvmx_fdt_get_int(fdt_addr, of_offset, - "reg", -1); - of_parent = fdt_parent_offset(fdt_addr, of_offset); - avsp5410->i2c_bus = cvmx_fdt_get_i2c_bus(fdt_addr, of_parent); - if (avsp5410->i2c_addr < 0) { - debug("%s: Error: reg field missing\n", __func__); - err = -1; - break; - } - if (!avsp5410->i2c_bus) { - debug("%s: Error getting i2c bus\n", __func__); - err = -1; - break; - } - avsp5410->name = fdt_get_name(fdt_addr, of_offset, NULL); - debug("%s: Adding %s\n", __func__, avsp5410->name); - - /* Now find out which interface it's mapped to */ - avsp5410->ipd_port = -1; - mac_str = "sfp-mac"; - if (fdt_getprop(fdt_addr, of_offset, mac_str, NULL)) { - is_qsfp = false; - } else if (fdt_getprop(fdt_addr, of_offset, "qsfp-mac", NULL)) { - is_qsfp = true; - mac_str = "qsfp-mac"; - } else { - debug("%s: Error: MAC not found for %s\n", __func__, avsp5410->name); - return -1; - } - of_mac = cvmx_fdt_lookup_phandle(fdt_addr, of_offset, mac_str); - if (of_mac < 0) { - debug("%s: Error %d with MAC %s phandle for %s\n", __func__, of_mac, - mac_str, avsp5410->name); + ret = i2c_get_chip(vsc7224->i2c_bus->i2c_bus, + vsc7224->i2c_addr, 1, &dev); + if (ret) { + debug("Cannot find I2C device: %d\n", ret); return -1; } - debug("%s: Found mac at offset %d\n", __func__, of_mac); - err = cvmx_helper_cfg_get_xiface_index_by_fdt_node_offset(of_mac, &xiface, &index); - if (!err) { - avsp5410->xiface = xiface; - avsp5410->index = index; - avsp5410->ipd_port = cvmx_helper_get_ipd_port(xiface, index); - - debug("%s: Found MAC, xiface: 0x%x, index: %d, ipd port: %d\n", __func__, - xiface, index, avsp5410->ipd_port); - if (avsp5410->ipd_port >= 0) { - cvmx_helper_cfg_set_avsp5410_info(xiface, index, avsp5410); - debug("%s: Storing config phy for xiface 0x%x, index %d\n", - __func__, xiface, index); - } - sfp_info = cvmx_helper_cfg_get_sfp_info(xiface, index); - if (!sfp_info) { - debug("%s: Warning: no (Q)SFP+ slot found for xinterface 0x%x, index %d\n", - __func__, xiface, index); - continue; + for (i = 0; i < len / sizeof(int); i += 2) { + u8 buffer[2]; + + reg = fdt32_to_cpu(init_array[i]); + value = fdt32_to_cpu(init_array[i + 1]); + buffer[0] = value >> 8; + buffer[1] = value & 0xff; + ret = dm_i2c_write(dev, reg, buffer, 2); + if (ret) { + debug("Cannot write I2C device: %d\n", ret); + return -1; } - sfp_info->is_avsp5410 = true; - sfp_info->avsp5410 = avsp5410; - debug("%s: Registering AVSP5410 %s with SFP %s\n", __func__, avsp5410->name, - sfp_info->name); - if (!sfp_info->mod_abs_changed) { - debug("%s: Registering cvmx_sfp_avsp5410_mod_abs_changed at %p for xinterface 0x%x, index %d\n", - __func__, &cvmx_sfp_avsp5410_mod_abs_changed, xiface, index); - cvmx_sfp_register_mod_abs_changed( - sfp_info, - &cvmx_sfp_avsp5410_mod_abs_changed, - NULL); - } + debug(" Wrote 0x%02x <= 0x%02x%02x\n", reg, + buffer[0], buffer[1]); + } + + debug("%s: Parsing channels\n", __func__); + err = cvmx_fdt_parse_vsc7224_channels(node, vsc7224); + if (err) { + debug("%s: Error parsing VSC7224 channels\n", __func__); + break; } - } while (of_offset > 0); + } if (err) { debug("%s(): Error\n", __func__); - if (avsp5410) { - if (avsp5410->i2c_bus) - cvmx_fdt_free_i2c_bus(avsp5410->i2c_bus); - __cvmx_fdt_free(avsp5410, sizeof(*avsp5410)); + if (vsc7224) { + dm_gpio_free(vsc7224->reset_gpio.dev, + &vsc7224->reset_gpio); + dm_gpio_free(vsc7224->los_gpio.dev, + &vsc7224->los_gpio); + if (vsc7224->i2c_bus) + cvmx_fdt_free_i2c_bus(vsc7224->i2c_bus); + free(vsc7224); } } if (!err) @@ -659,312 +413,76 @@ int __cvmx_fdt_parse_avsp5410(const void *fdt_addr) } /** - * Parse QSFP GPIOs for SFP - * - * @param[in] fdt_addr Pointer to flat device tree - * @param of_offset Offset of QSFP node - * @param[out] sfp_info Pointer to sfp info to fill in + * Given the parent offset of an i2c device build up a list describing the bus + * which can contain i2c muxes and switches. * - * Return: 0 for success - */ -static int cvmx_parse_qsfp(const void *fdt_addr, int of_offset, struct cvmx_fdt_sfp_info *sfp_info) -{ - sfp_info->select = cvmx_fdt_gpio_get_info_phandle(fdt_addr, of_offset, "select"); - sfp_info->mod_abs = cvmx_fdt_gpio_get_info_phandle(fdt_addr, of_offset, "mod_prs"); - sfp_info->reset = cvmx_fdt_gpio_get_info_phandle(fdt_addr, of_offset, "reset"); - sfp_info->interrupt = cvmx_fdt_gpio_get_info_phandle(fdt_addr, of_offset, "interrupt"); - sfp_info->lp_mode = cvmx_fdt_gpio_get_info_phandle(fdt_addr, of_offset, "lp_mode"); - return 0; -} - -/** - * Parse SFP GPIOs for SFP + * @param[in] node ofnode of the parent node of a GPIO device in + * the device tree. * - * @param[in] fdt_addr Pointer to flat device tree - * @param of_offset Offset of SFP node - * @param[out] sfp_info Pointer to sfp info to fill in + * @return pointer to list of i2c devices starting from the root which + * can include i2c muxes and switches or NULL if error. Note that + * all entries are allocated on the heap. * - * Return: 0 for success + * @see cvmx_fdt_free_i2c_bus() */ -static int cvmx_parse_sfp(const void *fdt_addr, int of_offset, struct cvmx_fdt_sfp_info *sfp_info) +struct cvmx_fdt_i2c_bus_info *cvmx_ofnode_get_i2c_bus(ofnode node) { - sfp_info->mod_abs = cvmx_fdt_gpio_get_info_phandle(fdt_addr, of_offset, "mod_abs"); - sfp_info->rx_los = cvmx_fdt_gpio_get_info_phandle(fdt_addr, of_offset, "rx_los"); - sfp_info->tx_disable = cvmx_fdt_gpio_get_info_phandle(fdt_addr, of_offset, "tx_disable"); - sfp_info->tx_error = cvmx_fdt_gpio_get_info_phandle(fdt_addr, of_offset, "tx_error"); - return 0; -} - -/** - * Parse SFP/QSFP EEPROM and diag - * - * @param[in] fdt_addr Pointer to flat device tree - * @param of_offset Offset of SFP node - * @param[out] sfp_info Pointer to sfp info to fill in - * - * Return: 0 for success, -1 on error - */ -static int cvmx_parse_sfp_eeprom(const void *fdt_addr, int of_offset, - struct cvmx_fdt_sfp_info *sfp_info) -{ - int of_eeprom; - int of_diag; - - debug("%s(%p, %d, %s)\n", __func__, fdt_addr, of_offset, sfp_info->name); - of_eeprom = cvmx_fdt_lookup_phandle(fdt_addr, of_offset, "eeprom"); - if (of_eeprom < 0) { - debug("%s: Missing \"eeprom\" from device tree for %s\n", __func__, sfp_info->name); - return -1; - } - - sfp_info->i2c_bus = cvmx_fdt_get_i2c_bus(fdt_addr, fdt_parent_offset(fdt_addr, of_eeprom)); - sfp_info->i2c_eeprom_addr = cvmx_fdt_get_int(fdt_addr, of_eeprom, "reg", 0x50); - - debug("%s(%p, %d, %s, %d)\n", __func__, fdt_addr, of_offset, sfp_info->name, - sfp_info->i2c_eeprom_addr); - - if (!sfp_info->i2c_bus) { - debug("%s: Error: could not determine i2c bus for eeprom for %s\n", __func__, - sfp_info->name); - return -1; + struct cvmx_fdt_i2c_bus_info *businfo = NULL; + struct udevice *bus; + int ret; + + businfo = cvmx_fdt_alloc(sizeof(*businfo)); + if (!businfo) { + debug("Out of memory\n"); + return NULL; } - of_diag = cvmx_fdt_lookup_phandle(fdt_addr, of_offset, "diag"); - if (of_diag >= 0) - sfp_info->i2c_diag_addr = cvmx_fdt_get_int(fdt_addr, of_diag, "reg", 0x51); - else - sfp_info->i2c_diag_addr = 0x51; - return 0; -} - -/** - * Parse SFP information from device tree - * - * @param[in] fdt_addr Address of flat device tree - * - * Return: pointer to sfp info or NULL if error - */ -struct cvmx_fdt_sfp_info *cvmx_helper_fdt_parse_sfp_info(const void *fdt_addr, int of_offset) -{ - struct cvmx_fdt_sfp_info *sfp_info = NULL; - int err = -1; - bool is_qsfp; - if (!fdt_node_check_compatible(fdt_addr, of_offset, "ethernet,sfp-slot")) { - is_qsfp = false; - } else if (!fdt_node_check_compatible(fdt_addr, of_offset, "ethernet,qsfp-slot")) { - is_qsfp = true; - } else { - debug("%s: Error: incompatible sfp/qsfp slot, compatible=%s\n", __func__, - (char *)fdt_getprop(fdt_addr, of_offset, "compatible", NULL)); - goto error_exit; - } + debug("%s: Found node %s\n", __func__, ofnode_get_name(node)); + businfo->of_offset = ofnode_to_offset(node); - debug("%s: %ssfp module found at offset %d\n", __func__, is_qsfp ? "q" : "", of_offset); - sfp_info = __cvmx_fdt_alloc(sizeof(*sfp_info)); - if (!sfp_info) { - debug("%s: Error: out of memory\n", __func__); - goto error_exit; - } - sfp_info->name = fdt_get_name(fdt_addr, of_offset, NULL); - sfp_info->of_offset = of_offset; - sfp_info->is_qsfp = is_qsfp; - sfp_info->last_mod_abs = -1; - sfp_info->last_rx_los = -1; - - if (is_qsfp) - err = cvmx_parse_qsfp(fdt_addr, of_offset, sfp_info); - else - err = cvmx_parse_sfp(fdt_addr, of_offset, sfp_info); - if (err) { - debug("%s: Error in %s parsing %ssfp GPIO info\n", __func__, sfp_info->name, - is_qsfp ? "q" : ""); - goto error_exit; - } - debug("%s: Parsing %ssfp module eeprom\n", __func__, is_qsfp ? "q" : ""); - err = cvmx_parse_sfp_eeprom(fdt_addr, of_offset, sfp_info); - if (err) { - debug("%s: Error parsing eeprom info for %s\n", __func__, sfp_info->name); - goto error_exit; + /* + * Get I2C bus and probe it automatically - needed for later use + */ + ret = device_get_global_by_ofnode(node, &bus); + if (!bus || ret) { + printf("Cannot find a I2C bus\n"); + return NULL; } - /* Register default check for mod_abs changed */ - if (!err) - cvmx_sfp_register_check_mod_abs(sfp_info, cvmx_sfp_check_mod_abs, NULL); + businfo->i2c_bus = bus; -error_exit: - /* Note: we don't free any data structures on error since it gets - * rather complicated with i2c buses and whatnot. - */ - return err ? NULL : sfp_info; + return businfo; } /** - * @INTERNAL - * Parse a slice of the Inphi/Cortina CS4343 in the device tree + * Return the Octeon bus number for a bus descriptor * - * @param[in] fdt_addr Address of flat device tree - * @param of_offset fdt offset of slice - * @param phy_info phy_info data structure + * @param[in] bus bus descriptor * - * Return: slice number if non-negative, otherwise error + * @return Octeon twsi bus number or -1 on error */ -static int cvmx_fdt_parse_cs4343_slice(const void *fdt_addr, int of_offset, - struct cvmx_phy_info *phy_info) +int cvmx_fdt_i2c_get_root_bus(const struct cvmx_fdt_i2c_bus_info *bus) { - struct cvmx_cs4343_slice_info *slice; - int reg; - int reg_offset; - - reg = cvmx_fdt_get_int(fdt_addr, of_offset, "reg", -1); - reg_offset = cvmx_fdt_get_int(fdt_addr, of_offset, "slice_offset", -1); - - if (reg < 0 || reg >= 4) { - debug("%s(%p, %d, %p): Error: reg %d undefined or out of range\n", __func__, - fdt_addr, of_offset, phy_info, reg); - return -1; - } - if (reg_offset % 0x1000 || reg_offset > 0x3000 || reg_offset < 0) { - debug("%s(%p, %d, %p): Error: reg_offset 0x%x undefined or out of range\n", - __func__, fdt_addr, of_offset, phy_info, reg_offset); - return -1; - } - if (!phy_info->cs4343_info) { - debug("%s: Error: phy info cs4343 datastructure is NULL\n", __func__); + if (bus->type != CVMX_I2C_BUS_OCTEON) return -1; - } - debug("%s(%p, %d, %p): %s, reg: %d, slice offset: 0x%x\n", __func__, fdt_addr, of_offset, - phy_info, fdt_get_name(fdt_addr, of_offset, NULL), reg, reg_offset); - slice = &phy_info->cs4343_info->slice[reg]; - slice->name = fdt_get_name(fdt_addr, of_offset, NULL); - slice->mphy = phy_info->cs4343_info; - slice->phy_info = phy_info; - slice->of_offset = of_offset; - slice->slice_no = reg; - slice->reg_offset = reg_offset; - /* SR settings */ - slice->sr_stx_cmode_res = cvmx_fdt_get_int(fdt_addr, of_offset, "sr-stx-cmode-res", 3); - slice->sr_stx_drv_lower_cm = - cvmx_fdt_get_int(fdt_addr, of_offset, "sr-stx-drv-lower-cm", 8); - slice->sr_stx_level = cvmx_fdt_get_int(fdt_addr, of_offset, "sr-stx-level", 0x1c); - slice->sr_stx_pre_peak = cvmx_fdt_get_int(fdt_addr, of_offset, "sr-stx-pre-peak", 1); - slice->sr_stx_muxsubrate_sel = - cvmx_fdt_get_int(fdt_addr, of_offset, "sr-stx-muxsubrate-sel", 0); - slice->sr_stx_post_peak = cvmx_fdt_get_int(fdt_addr, of_offset, "sr-stx-post-peak", 8); - /* CX settings */ - slice->cx_stx_cmode_res = cvmx_fdt_get_int(fdt_addr, of_offset, "cx-stx-cmode-res", 3); - slice->cx_stx_drv_lower_cm = - cvmx_fdt_get_int(fdt_addr, of_offset, "cx-stx-drv-lower-cm", 8); - slice->cx_stx_level = cvmx_fdt_get_int(fdt_addr, of_offset, "cx-stx-level", 0x1c); - slice->cx_stx_pre_peak = cvmx_fdt_get_int(fdt_addr, of_offset, "cx-stx-pre-peak", 1); - slice->cx_stx_muxsubrate_sel = - cvmx_fdt_get_int(fdt_addr, of_offset, "cx-stx-muxsubrate-sel", 0); - slice->cx_stx_post_peak = cvmx_fdt_get_int(fdt_addr, of_offset, "cx-stx-post-peak", 0xC); - /* 1000Base-X settings */ - /* CX settings */ - slice->basex_stx_cmode_res = - cvmx_fdt_get_int(fdt_addr, of_offset, "basex-stx-cmode-res", 3); - slice->basex_stx_drv_lower_cm = - cvmx_fdt_get_int(fdt_addr, of_offset, "basex-stx-drv-lower-cm", 8); - slice->basex_stx_level = cvmx_fdt_get_int(fdt_addr, of_offset, - "basex-stx-level", 0x1c); - slice->basex_stx_pre_peak = cvmx_fdt_get_int(fdt_addr, of_offset, - "basex-stx-pre-peak", 1); - slice->basex_stx_muxsubrate_sel = - cvmx_fdt_get_int(fdt_addr, of_offset, - "basex-stx-muxsubrate-sel", 0); - slice->basex_stx_post_peak = - cvmx_fdt_get_int(fdt_addr, of_offset, "basex-stx-post-peak", 8); - /* Get the link LED gpio pin */ - slice->link_gpio = cvmx_fdt_get_int(fdt_addr, of_offset, - "link-led-gpio", -1); - slice->error_gpio = cvmx_fdt_get_int(fdt_addr, of_offset, - "error-led-gpio", -1); - slice->los_gpio = cvmx_fdt_get_int(fdt_addr, of_offset, - "los-input-gpio", -1); - slice->link_inverted = cvmx_fdt_get_bool(fdt_addr, of_offset, - "link-led-gpio-inverted"); - slice->error_inverted = cvmx_fdt_get_bool(fdt_addr, of_offset, - "error-led-gpio-inverted"); - slice->los_inverted = cvmx_fdt_get_bool(fdt_addr, of_offset, - "los-input-gpio-inverted"); - /* Convert GPIOs to be die based if they're not already */ - if (slice->link_gpio > 4 && slice->link_gpio <= 8) - slice->link_gpio -= 4; - if (slice->error_gpio > 4 && slice->error_gpio <= 8) - slice->error_gpio -= 4; - if (slice->los_gpio > 4 && slice->los_gpio <= 8) - slice->los_gpio -= 4; - - return reg; + return bus->channel; } /** - * @INTERNAL - * Parses either a CS4343 phy or a slice of the phy from the device tree - * @param[in] fdt_addr Address of FDT - * @param of_offset offset of slice or phy in device tree - * @param phy_info phy_info data structure to fill in + * Frees all entries for an i2c bus descriptor * - * Return: 0 for success, -1 on error + * @param bus bus to free + * + * @return 0 */ -int cvmx_fdt_parse_cs4343(const void *fdt_addr, int of_offset, struct cvmx_phy_info *phy_info) +int cvmx_fdt_free_i2c_bus(struct cvmx_fdt_i2c_bus_info *bus) { - int of_slice = -1; - struct cvmx_cs4343_info *cs4343; - int err = -1; - int reg; - - debug("%s(%p, %d, %p): %s (%s)\n", __func__, - fdt_addr, of_offset, phy_info, - fdt_get_name(fdt_addr, of_offset, NULL), - (const char *)fdt_getprop(fdt_addr, of_offset, "compatible", NULL)); + struct cvmx_fdt_i2c_bus_info *last; - if (!phy_info->cs4343_info) - phy_info->cs4343_info = __cvmx_fdt_alloc(sizeof(struct cvmx_cs4343_info)); - if (!phy_info->cs4343_info) { - debug("%s: Error: out of memory!\n", __func__); - return -1; - } - cs4343 = phy_info->cs4343_info; - /* If we're passed to a slice then process only that slice */ - if (!fdt_node_check_compatible(fdt_addr, of_offset, "cortina,cs4343-slice")) { - err = 0; - of_slice = of_offset; - of_offset = fdt_parent_offset(fdt_addr, of_offset); - reg = cvmx_fdt_parse_cs4343_slice(fdt_addr, of_slice, phy_info); - if (reg >= 0) - phy_info->cs4343_slice_info = &cs4343->slice[reg]; - else - err = reg; - } else if (!fdt_node_check_compatible(fdt_addr, of_offset, - "cortina,cs4343")) { - /* Walk through and process all of the slices */ - of_slice = - fdt_node_offset_by_compatible(fdt_addr, of_offset, "cortina,cs4343-slice"); - while (of_slice > 0 && fdt_parent_offset(fdt_addr, of_slice) == - of_offset) { - debug("%s: Parsing slice %s\n", __func__, - fdt_get_name(fdt_addr, of_slice, NULL)); - err = cvmx_fdt_parse_cs4343_slice(fdt_addr, of_slice, - phy_info); - if (err < 0) - break; - of_slice = fdt_node_offset_by_compatible(fdt_addr, - of_slice, - "cortina,cs4343-slice"); - } - } else { - debug("%s: Error: unknown compatible string %s for %s\n", __func__, - (const char *)fdt_getprop(fdt_addr, of_offset, - "compatible", NULL), - fdt_get_name(fdt_addr, of_offset, NULL)); + while (bus) { + last = bus; + bus = bus->child; + free(last); } - - if (err >= 0) { - cs4343->name = fdt_get_name(fdt_addr, of_offset, NULL); - cs4343->phy_info = phy_info; - cs4343->of_offset = of_offset; - } - - return err < 0 ? -1 : 0; + return 0; } diff --git a/arch/mips/mach-octeon/cvmx-helper-fpa.c b/arch/mips/mach-octeon/cvmx-helper-fpa.c new file mode 100644 index 0000000..e870829 --- /dev/null +++ b/arch/mips/mach-octeon/cvmx-helper-fpa.c @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018-2022 Marvell International Ltd. + * + * Helper functions for FPA setup. + */ + +#include <errno.h> +#include <log.h> +#include <time.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-range.h> +#include <mach/cvmx-global-resources.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-ipd.h> +#include <mach/cvmx-pki.h> +#include <mach/cvmx-pko3.h> +#include <mach/cvmx-pko3-queue.h> +#include <mach/cvmx-pko3-resources.h> +#include <mach/cvmx-pip.h> + +#include <mach/cvmx-helper.h> +#include <mach/cvmx-helper-board.h> +#include <mach/cvmx-helper-cfg.h> + +#include <mach/cvmx-helper-bgx.h> +#include <mach/cvmx-helper-cfg.h> +#include <mach/cvmx-helper-util.h> +#include <mach/cvmx-helper-pki.h> +#include <mach/cvmx-helper-pko.h> + +/** + * @INTERNAL + * OBSOLETE + * + * Allocate memory for and initialize a single FPA pool. + * + * @param pool Pool to initialize + * @param buffer_size Size of buffers to allocate in bytes + * @param buffers Number of buffers to put in the pool. Zero is allowed + * @param name String name of the pool for debugging purposes + * @return Zero on success, non-zero on failure + * + * This function is only for transition, will be removed. + */ +int __cvmx_helper_initialize_fpa_pool(int pool, u64 buffer_size, u64 buffers, + const char *name) +{ + return cvmx_fpa_setup_pool(pool, name, NULL, buffer_size, buffers); +} diff --git a/arch/mips/mach-octeon/cvmx-helper-ilk.c b/arch/mips/mach-octeon/cvmx-helper-ilk.c new file mode 100644 index 0000000..9e882f1 --- /dev/null +++ b/arch/mips/mach-octeon/cvmx-helper-ilk.c @@ -0,0 +1,902 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018-2022 Marvell International Ltd. + * + * Functions for ILK initialization, configuration, + * and monitoring. + */ + +#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-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> + +int __cvmx_helper_ilk_enumerate(int xiface) +{ + struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); + + xi.interface -= CVMX_ILK_GBL_BASE(); + return cvmx_ilk_chans[xi.node][xi.interface]; +} + +/** + * @INTERNAL + * Initialize all tx calendar entries to the xoff state. + * Initialize all rx calendar entries to the xon state. The rx calendar entries + * must be in the xon state to allow new pko pipe assignments. If a calendar + * entry is assigned a different pko pipe while in the xoff state, the old pko + * pipe will stay in the xoff state even when no longer used by ilk. + * + * @param intf Interface whose calendar are to be initialized. + */ +static void __cvmx_ilk_clear_cal_cn78xx(int intf) +{ + cvmx_ilk_txx_cal_entryx_t tx_entry; + cvmx_ilk_rxx_cal_entryx_t rx_entry; + int i; + int node = (intf >> 4) & 0xf; + int interface = (intf & 0xf); + + /* Initialize all tx calendar entries to off */ + tx_entry.u64 = 0; + tx_entry.s.ctl = XOFF; + for (i = 0; i < CVMX_ILK_MAX_CAL; i++) { + csr_wr_node(node, CVMX_ILK_TXX_CAL_ENTRYX(i, interface), + tx_entry.u64); + } + + /* Initialize all rx calendar entries to on */ + rx_entry.u64 = 0; + rx_entry.s.ctl = XOFF; + for (i = 0; i < CVMX_ILK_MAX_CAL; i++) { + csr_wr_node(node, CVMX_ILK_RXX_CAL_ENTRYX(i, interface), + rx_entry.u64); + } +} + +/** + * @INTERNAL + * Initialize all tx calendar entries to the xoff state. + * Initialize all rx calendar entries to the xon state. The rx calendar entries + * must be in the xon state to allow new pko pipe assignments. If a calendar + * entry is assigned a different pko pipe while in the xoff state, the old pko + * pipe will stay in the xoff state even when no longer used by ilk. + * + * @param interface whose calendar are to be initialized. + */ +static void __cvmx_ilk_clear_cal_cn68xx(int interface) +{ + cvmx_ilk_txx_idx_cal_t tx_idx; + cvmx_ilk_txx_mem_cal0_t tx_cal0; + cvmx_ilk_txx_mem_cal1_t tx_cal1; + cvmx_ilk_rxx_idx_cal_t rx_idx; + cvmx_ilk_rxx_mem_cal0_t rx_cal0; + cvmx_ilk_rxx_mem_cal1_t rx_cal1; + int i; + + /* + * First we initialize the tx calendar starting from entry 0, + * incrementing the entry with every write. + */ + tx_idx.u64 = 0; + tx_idx.s.inc = 1; + csr_wr(CVMX_ILK_TXX_IDX_CAL(interface), tx_idx.u64); + + /* Set state to xoff for all entries */ + tx_cal0.u64 = 0; + tx_cal0.s.entry_ctl0 = XOFF; + tx_cal0.s.entry_ctl1 = XOFF; + tx_cal0.s.entry_ctl2 = XOFF; + tx_cal0.s.entry_ctl3 = XOFF; + + tx_cal1.u64 = 0; + tx_cal1.s.entry_ctl4 = XOFF; + tx_cal1.s.entry_ctl5 = XOFF; + tx_cal1.s.entry_ctl6 = XOFF; + tx_cal1.s.entry_ctl7 = XOFF; + + /* Write all 288 entries */ + for (i = 0; i < CVMX_ILK_MAX_CAL_IDX; i++) { + csr_wr(CVMX_ILK_TXX_MEM_CAL0(interface), tx_cal0.u64); + csr_wr(CVMX_ILK_TXX_MEM_CAL1(interface), tx_cal1.u64); + } + + /* + * Next we initialize the rx calendar starting from entry 0, + * incrementing the entry with every write. + */ + rx_idx.u64 = 0; + rx_idx.s.inc = 1; + csr_wr(CVMX_ILK_RXX_IDX_CAL(interface), rx_idx.u64); + + /* Set state to xon for all entries */ + rx_cal0.u64 = 0; + rx_cal0.s.entry_ctl0 = XON; + rx_cal0.s.entry_ctl1 = XON; + rx_cal0.s.entry_ctl2 = XON; + rx_cal0.s.entry_ctl3 = XON; + + rx_cal1.u64 = 0; + rx_cal1.s.entry_ctl4 = XON; + rx_cal1.s.entry_ctl5 = XON; + rx_cal1.s.entry_ctl6 = XON; + rx_cal1.s.entry_ctl7 = XON; + + /* Write all 288 entries */ + for (i = 0; i < CVMX_ILK_MAX_CAL_IDX; i++) { + csr_wr(CVMX_ILK_RXX_MEM_CAL0(interface), rx_cal0.u64); + csr_wr(CVMX_ILK_RXX_MEM_CAL1(interface), rx_cal1.u64); + } +} + +/** + * @INTERNAL + * Initialize all calendar entries. + * + * @param interface whose calendar is to be initialized. + */ +void __cvmx_ilk_clear_cal(int interface) +{ + if (OCTEON_IS_MODEL(OCTEON_CN68XX)) + __cvmx_ilk_clear_cal_cn68xx(interface); + else if (OCTEON_IS_MODEL(OCTEON_CN78XX)) + __cvmx_ilk_clear_cal_cn78xx(interface); +} + +void __cvmx_ilk_write_tx_cal_entry_cn68xx(int interface, int channel, + unsigned char bpid) +{ + cvmx_ilk_txx_idx_cal_t tx_idx; + cvmx_ilk_txx_mem_cal0_t tx_cal0; + cvmx_ilk_txx_mem_cal1_t tx_cal1; + int entry; + int window; + int window_entry; + + /* + * The calendar has 288 entries. Each calendar entry represents a + * channel's flow control state or the link flow control state. + * Starting with the first entry, every sixteenth entry is used for the + * link flow control state. The other 15 entries are used for the + * channels flow control state: + * entry 0 ----> link flow control state + * entry 1 ----> channel 0 flow control state + * entry 2 ----> channel 1 flow control state + * ... + * entry 15 ----> channel 14 flow control state + * entry 16 ----> link flow control state + * entry 17 ----> channel 15 flow control state + * + * Also, the calendar is accessed via windows into it. Each window maps + * to 8 entries. + */ + entry = 1 + channel + (channel / 15); + window = entry / 8; + window_entry = entry % 8; + + /* Indicate the window we need to access */ + tx_idx.u64 = 0; + tx_idx.s.index = window; + csr_wr(CVMX_ILK_TXX_IDX_CAL(interface), tx_idx.u64); + + /* Get the window's current value */ + tx_cal0.u64 = csr_rd(CVMX_ILK_TXX_MEM_CAL0(interface)); + tx_cal1.u64 = csr_rd(CVMX_ILK_TXX_MEM_CAL1(interface)); + + /* Force every sixteenth entry as link flow control state */ + if ((window & 1) == 0) + tx_cal0.s.entry_ctl0 = LINK; + + /* Update the entry */ + switch (window_entry) { + case 0: + tx_cal0.s.entry_ctl0 = 0; + tx_cal0.s.bpid0 = bpid; + break; + case 1: + tx_cal0.s.entry_ctl1 = 0; + tx_cal0.s.bpid1 = bpid; + break; + case 2: + tx_cal0.s.entry_ctl2 = 0; + tx_cal0.s.bpid2 = bpid; + break; + case 3: + tx_cal0.s.entry_ctl3 = 0; + tx_cal0.s.bpid3 = bpid; + break; + case 4: + tx_cal1.s.entry_ctl4 = 0; + tx_cal1.s.bpid4 = bpid; + break; + case 5: + tx_cal1.s.entry_ctl5 = 0; + tx_cal1.s.bpid5 = bpid; + break; + case 6: + tx_cal1.s.entry_ctl6 = 0; + tx_cal1.s.bpid6 = bpid; + break; + case 7: + tx_cal1.s.entry_ctl7 = 0; + tx_cal1.s.bpid7 = bpid; + break; + } + + /* Write the window new value */ + csr_wr(CVMX_ILK_TXX_MEM_CAL0(interface), tx_cal0.u64); + csr_wr(CVMX_ILK_TXX_MEM_CAL1(interface), tx_cal1.u64); +} + +void __cvmx_ilk_write_tx_cal_entry_cn78xx(int intf, int channel, + unsigned char bpid) +{ + cvmx_ilk_txx_cal_entryx_t tx_cal; + int calender_16_block = channel / 15; + int calender_16_index = channel % 15 + 1; + int index = calender_16_block * 16 + calender_16_index; + int node = (intf >> 4) & 0xf; + int interface = intf & 0xf; + + /* Program the link status on first channel */ + if (calender_16_index == 1) { + tx_cal.u64 = 0; + tx_cal.s.ctl = 1; + csr_wr_node(node, CVMX_ILK_TXX_CAL_ENTRYX(index - 1, interface), + tx_cal.u64); + } + tx_cal.u64 = 0; + tx_cal.s.ctl = 0; + tx_cal.s.channel = channel; + csr_wr_node(node, CVMX_ILK_TXX_CAL_ENTRYX(index, interface), + tx_cal.u64); +} + +/** + * @INTERNAL + * Setup the channel's tx calendar entry. + * + * @param interface channel belongs to + * @param channel whose calendar entry is to be updated + * @param bpid assigned to the channel + */ +void __cvmx_ilk_write_tx_cal_entry(int interface, int channel, + unsigned char bpid) +{ + if (OCTEON_IS_MODEL(OCTEON_CN68XX)) + __cvmx_ilk_write_tx_cal_entry_cn68xx(interface, channel, bpid); + else + __cvmx_ilk_write_tx_cal_entry_cn78xx(interface, channel, bpid); +} + +void __cvmx_ilk_write_rx_cal_entry_cn78xx(int intf, int channel, + unsigned char bpid) +{ + cvmx_ilk_rxx_cal_entryx_t rx_cal; + int calender_16_block = channel / 15; + int calender_16_index = channel % 15 + 1; + int index = calender_16_block * 16 + calender_16_index; + int node = (intf >> 4) & 0xf; + int interface = intf & 0xf; + + /* Program the link status on first channel */ + if (calender_16_index == 1) { + rx_cal.u64 = 0; + rx_cal.s.ctl = 1; + csr_wr_node(node, CVMX_ILK_RXX_CAL_ENTRYX(index - 1, interface), + rx_cal.u64); + } + rx_cal.u64 = 0; + rx_cal.s.ctl = 0; + rx_cal.s.channel = channel; + csr_wr_node(node, CVMX_ILK_RXX_CAL_ENTRYX(index, interface), + rx_cal.u64); +} + +void __cvmx_ilk_write_rx_cal_entry_cn68xx(int interface, int channel, + unsigned char pipe) +{ + cvmx_ilk_rxx_idx_cal_t rx_idx; + cvmx_ilk_rxx_mem_cal0_t rx_cal0; + cvmx_ilk_rxx_mem_cal1_t rx_cal1; + int entry; + int window; + int window_entry; + + /* + * The calendar has 288 entries. Each calendar entry represents a + * channel's flow control state or the link flow control state. + * Starting with the first entry, every sixteenth entry is used for the + * link flow control state. The other 15 entries are used for the + * channels flow control state: + * entry 0 ----> link flow control state + * entry 1 ----> channel 0 flow control state + * entry 2 ----> channel 1 flow control state + * ... + * entry 15 ----> channel 14 flow control state + * entry 16 ----> link flow control state + * entry 17 ----> channel 15 flow control state + * + * Also, the calendar is accessed via windows into it. Each window maps + * to 8 entries. + */ + entry = 1 + channel + (channel / 15); + window = entry / 8; + window_entry = entry % 8; + + /* Indicate the window we need to access */ + rx_idx.u64 = 0; + rx_idx.s.index = window; + csr_wr(CVMX_ILK_RXX_IDX_CAL(interface), rx_idx.u64); + + /* Get the window's current value */ + rx_cal0.u64 = csr_rd(CVMX_ILK_RXX_MEM_CAL0(interface)); + rx_cal1.u64 = csr_rd(CVMX_ILK_RXX_MEM_CAL1(interface)); + + /* Force every sixteenth entry as link flow control state */ + if ((window & 1) == 0) + rx_cal0.s.entry_ctl0 = LINK; + + /* Update the entry */ + switch (window_entry) { + case 0: + rx_cal0.s.entry_ctl0 = 0; + rx_cal0.s.port_pipe0 = pipe; + break; + case 1: + rx_cal0.s.entry_ctl1 = 0; + rx_cal0.s.port_pipe1 = pipe; + break; + case 2: + rx_cal0.s.entry_ctl2 = 0; + rx_cal0.s.port_pipe2 = pipe; + break; + case 3: + rx_cal0.s.entry_ctl3 = 0; + rx_cal0.s.port_pipe3 = pipe; + break; + case 4: + rx_cal1.s.entry_ctl4 = 0; + rx_cal1.s.port_pipe4 = pipe; + break; + case 5: + rx_cal1.s.entry_ctl5 = 0; + rx_cal1.s.port_pipe5 = pipe; + break; + case 6: + rx_cal1.s.entry_ctl6 = 0; + rx_cal1.s.port_pipe6 = pipe; + break; + case 7: + rx_cal1.s.entry_ctl7 = 0; + rx_cal1.s.port_pipe7 = pipe; + break; + } + + /* Write the window new value */ + csr_wr(CVMX_ILK_RXX_MEM_CAL0(interface), rx_cal0.u64); + csr_wr(CVMX_ILK_RXX_MEM_CAL1(interface), rx_cal1.u64); +} + +/** + * @INTERNAL + * Setup the channel's rx calendar entry. + * + * @param interface channel belongs to + * @param channel whose calendar entry is to be updated + * @param pipe PKO assigned to the channel + */ +void __cvmx_ilk_write_rx_cal_entry(int interface, int channel, + unsigned char pipe) +{ + if (OCTEON_IS_MODEL(OCTEON_CN68XX)) + __cvmx_ilk_write_rx_cal_entry_cn68xx(interface, channel, pipe); + else + __cvmx_ilk_write_rx_cal_entry_cn78xx(interface, channel, pipe); +} + +/** + * @INTERNAL + * Probe a ILK interface and determine the number of ports + * connected to it. The ILK interface should still be down + * after this call. + * + * @param xiface Interface to probe + * + * @return Number of ports on the interface. Zero to disable. + */ +int __cvmx_helper_ilk_probe(int xiface) +{ + int res = 0; + int interface; + struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); + + if (!octeon_has_feature(OCTEON_FEATURE_ILK)) + return res; + + interface = xi.interface - CVMX_ILK_GBL_BASE(); + if (interface >= CVMX_NUM_ILK_INTF) + return 0; + + /* the configuration should be done only once */ + if (cvmx_ilk_get_intf_ena(xiface)) + return cvmx_ilk_chans[xi.node][interface]; + + /* configure lanes and enable the link */ + res = cvmx_ilk_start_interface(((xi.node << 4) | interface), + cvmx_ilk_lane_mask[xi.node][interface]); + if (res < 0) + return 0; + + res = __cvmx_helper_ilk_enumerate(xiface); + + return res; +} + +static int __cvmx_helper_ilk_init_port_cn68xx(int xiface) +{ + int i, j, res = -1; + static int pipe_base = 0, pknd_base; + static cvmx_ilk_pipe_chan_t *pch = NULL, *tmp; + static cvmx_ilk_chan_pknd_t *chpknd = NULL, *tmp1; + static cvmx_ilk_cal_entry_t *calent = NULL, *tmp2; + int enable_rx_cal = 1; + int interface; + struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); + int intf; + int num_chans; + + interface = xi.interface - CVMX_ILK_GBL_BASE(); + intf = (xi.node << 4) | interface; + if (interface >= CVMX_NUM_ILK_INTF) + return 0; + + num_chans = cvmx_ilk_chans[0][interface]; + + /* set up channel to pkind mapping */ + if (pknd_base == 0) + pknd_base = cvmx_helper_get_pknd(xiface, 0); + + /* set up the group of pipes available to ilk */ + if (pipe_base == 0) + pipe_base = + __cvmx_pko_get_pipe(interface + CVMX_ILK_GBL_BASE(), 0); + + if (pipe_base == -1) { + pipe_base = 0; + return 0; + } + + res = cvmx_ilk_set_pipe(xiface, pipe_base, + cvmx_ilk_chans[0][interface]); + if (res < 0) + return 0; + + /* set up pipe to channel mapping */ + i = pipe_base; + if (!pch) { + pch = (cvmx_ilk_pipe_chan_t *)cvmx_bootmem_alloc( + num_chans * sizeof(cvmx_ilk_pipe_chan_t), + sizeof(cvmx_ilk_pipe_chan_t)); + if (!pch) + return 0; + } + + memset(pch, 0, num_chans * sizeof(cvmx_ilk_pipe_chan_t)); + tmp = pch; + for (j = 0; j < num_chans; j++) { + tmp->pipe = i++; + tmp->chan = j; + tmp++; + } + res = cvmx_ilk_tx_set_channel(interface, pch, + cvmx_ilk_chans[0][interface]); + if (res < 0) { + res = 0; + goto err_free_pch; + } + pipe_base += cvmx_ilk_chans[0][interface]; + i = pknd_base; + if (!chpknd) { + chpknd = (cvmx_ilk_chan_pknd_t *)cvmx_bootmem_alloc( + CVMX_ILK_MAX_PKNDS * sizeof(cvmx_ilk_chan_pknd_t), + sizeof(cvmx_ilk_chan_pknd_t)); + if (!chpknd) { + pipe_base -= cvmx_ilk_chans[xi.node][interface]; + res = 0; + goto err_free_pch; + } + } + + memset(chpknd, 0, CVMX_ILK_MAX_PKNDS * sizeof(cvmx_ilk_chan_pknd_t)); + tmp1 = chpknd; + for (j = 0; j < cvmx_ilk_chans[xi.node][interface]; j++) { + tmp1->chan = j; + tmp1->pknd = i++; + tmp1++; + } + + res = cvmx_ilk_rx_set_pknd(xiface, chpknd, + cvmx_ilk_chans[xi.node][interface]); + if (res < 0) { + pipe_base -= cvmx_ilk_chans[xi.node][interface]; + res = 0; + goto err_free_chpknd; + } + pknd_base += cvmx_ilk_chans[xi.node][interface]; + + /* Set up tx calendar */ + if (!calent) { + calent = (cvmx_ilk_cal_entry_t *)cvmx_bootmem_alloc( + CVMX_ILK_MAX_PIPES * sizeof(cvmx_ilk_cal_entry_t), + sizeof(cvmx_ilk_cal_entry_t)); + if (!calent) { + pipe_base -= cvmx_ilk_chans[xi.node][interface]; + pknd_base -= cvmx_ilk_chans[xi.node][interface]; + res = 0; + goto err_free_chpknd; + } + } + + memset(calent, 0, CVMX_ILK_MAX_PIPES * sizeof(cvmx_ilk_cal_entry_t)); + tmp1 = chpknd; + tmp2 = calent; + for (j = 0; j < cvmx_ilk_chans[xi.node][interface]; j++) { + tmp2->pipe_bpid = tmp1->pknd; + tmp2->ent_ctrl = PIPE_BPID; + tmp1++; + tmp2++; + } + res = cvmx_ilk_cal_setup_tx(intf, cvmx_ilk_chans[xi.node][interface], + calent, 1); + if (res < 0) { + pipe_base -= cvmx_ilk_chans[xi.node][interface]; + pknd_base -= cvmx_ilk_chans[xi.node][interface]; + res = 0; + goto err_free_calent; + } + + /* set up rx calendar. allocated memory can be reused. + * this is because max pkind is always less than max pipe + */ + memset(calent, 0, CVMX_ILK_MAX_PIPES * sizeof(cvmx_ilk_cal_entry_t)); + tmp = pch; + tmp2 = calent; + for (j = 0; j < cvmx_ilk_chans[0][interface]; j++) { + tmp2->pipe_bpid = tmp->pipe; + tmp2->ent_ctrl = PIPE_BPID; + tmp++; + tmp2++; + } + if (cvmx_ilk_use_la_mode(interface, 0)) + enable_rx_cal = cvmx_ilk_la_mode_enable_rx_calendar(interface); + else + enable_rx_cal = 1; + + res = cvmx_ilk_cal_setup_rx(intf, cvmx_ilk_chans[xi.node][interface], + calent, CVMX_ILK_RX_FIFO_WM, enable_rx_cal); + if (res < 0) { + pipe_base -= cvmx_ilk_chans[xi.node][interface]; + pknd_base -= cvmx_ilk_chans[xi.node][interface]; + res = 0; + goto err_free_calent; + } + goto out; + +err_free_calent: + /* no free() for cvmx_bootmem_alloc() */ + +err_free_chpknd: + /* no free() for cvmx_bootmem_alloc() */ + +err_free_pch: + /* no free() for cvmx_bootmem_alloc() */ +out: + return res; +} + +static int __cvmx_helper_ilk_init_port_cn78xx(int xiface) +{ + struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); + int interface; + int intf; + + interface = xi.interface - CVMX_ILK_GBL_BASE(); + intf = (xi.node << 4) | interface; + if (interface >= CVMX_NUM_ILK_INTF) + return 0; + + if (OCTEON_IS_MODEL(OCTEON_CN78XX)) { + struct cvmx_pki_style_config style_cfg; + int num_channels = cvmx_ilk_chans[xi.node][interface]; + int index, i; + + for (i = 0; i < num_channels; i++) { + int pknd; + + index = (i % 8); + + /* Set jabber to allow max sized packets */ + if (i == 0) + csr_wr_node(xi.node, + CVMX_ILK_RXX_JABBER(interface), + 0xfff8); + + /* Setup PKND */ + pknd = cvmx_helper_get_pknd(xiface, index); + csr_wr_node(xi.node, CVMX_ILK_RXX_CHAX(i, interface), + pknd); + cvmx_pki_read_style_config( + 0, pknd, CVMX_PKI_CLUSTER_ALL, &style_cfg); + style_cfg.parm_cfg.qpg_port_sh = 0; + /* 256 channels */ + style_cfg.parm_cfg.qpg_port_msb = 8; + cvmx_pki_write_style_config( + 0, pknd, CVMX_PKI_CLUSTER_ALL, &style_cfg); + } + + cvmx_ilk_cal_setup_tx(intf, num_channels, NULL, 1); + cvmx_ilk_cal_setup_rx(intf, num_channels, NULL, + CVMX_ILK_RX_FIFO_WM, 1); + } + return 0; +} + +static int __cvmx_helper_ilk_init_port(int xiface) +{ + if (OCTEON_IS_MODEL(OCTEON_CN68XX)) + return __cvmx_helper_ilk_init_port_cn68xx(xiface); + else + return __cvmx_helper_ilk_init_port_cn78xx(xiface); +} + +/** + * @INTERNAL + * Bringup and enable ILK interface. After this call packet + * I/O should be fully functional. This is called with IPD + * enabled but PKO disabled. + * + * @param xiface Interface to bring up + * + * @return Zero on success, negative on failure + */ +int __cvmx_helper_ilk_enable(int xiface) +{ + if (__cvmx_helper_ilk_init_port(xiface) < 0) + return -1; + + return cvmx_ilk_enable(xiface); +} + +/** + * @INTERNAL + * Return the link state of an IPD/PKO port as returned by ILK link status. + * + * @param ipd_port IPD/PKO port to query + * + * @return Link state + */ +cvmx_helper_link_info_t __cvmx_helper_ilk_link_get(int ipd_port) +{ + cvmx_helper_link_info_t result; + int xiface = cvmx_helper_get_interface_num(ipd_port); + struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); + int interface; + int retry_count = 0; + cvmx_ilk_rxx_cfg1_t ilk_rxx_cfg1; + cvmx_ilk_rxx_int_t ilk_rxx_int; + int lane_mask = 0; + int i; + int node = xi.node; + + result.u64 = 0; + interface = xi.interface - CVMX_ILK_GBL_BASE(); + +retry: + retry_count++; + if (retry_count > 200) + goto fail; + + /* Read RX config and status bits */ + ilk_rxx_cfg1.u64 = csr_rd_node(node, CVMX_ILK_RXX_CFG1(interface)); + ilk_rxx_int.u64 = csr_rd_node(node, CVMX_ILK_RXX_INT(interface)); + + if (ilk_rxx_cfg1.s.rx_bdry_lock_ena == 0) { + /* (GSER-21957) GSER RX Equalization may make >= 5gbaud non-KR + * channel better + */ + if (OCTEON_IS_MODEL(OCTEON_CN78XX)) { + int qlm, lane_mask; + + for (qlm = 4; qlm < 8; qlm++) { + lane_mask = 1 << (qlm - 4) * 4; + if (lane_mask & + cvmx_ilk_lane_mask[node][interface]) { + if (__cvmx_qlm_rx_equalization( + node, qlm, -1)) + goto retry; + } + } + } + + /* Clear the boundary lock status bit */ + ilk_rxx_int.u64 = 0; + ilk_rxx_int.s.word_sync_done = 1; + csr_wr_node(node, CVMX_ILK_RXX_INT(interface), ilk_rxx_int.u64); + + /* We need to start looking for word boundary lock */ + ilk_rxx_cfg1.s.rx_bdry_lock_ena = + cvmx_ilk_lane_mask[node][interface]; + ilk_rxx_cfg1.s.rx_align_ena = 0; + csr_wr_node(node, CVMX_ILK_RXX_CFG1(interface), + ilk_rxx_cfg1.u64); + //debug("ILK%d: Looking for word boundary lock\n", interface); + udelay(50); + goto retry; + } + + if (ilk_rxx_cfg1.s.rx_align_ena == 0) { + if (ilk_rxx_int.s.word_sync_done) { + /* Clear the lane align status bits */ + ilk_rxx_int.u64 = 0; + ilk_rxx_int.s.lane_align_fail = 1; + ilk_rxx_int.s.lane_align_done = 1; + csr_wr_node(node, CVMX_ILK_RXX_INT(interface), + ilk_rxx_int.u64); + + ilk_rxx_cfg1.s.rx_align_ena = 1; + csr_wr_node(node, CVMX_ILK_RXX_CFG1(interface), + ilk_rxx_cfg1.u64); + //printf("ILK%d: Looking for lane alignment\n", interface); + } + udelay(50); + goto retry; + } + + if (ilk_rxx_int.s.lane_align_fail) { + ilk_rxx_cfg1.s.rx_bdry_lock_ena = 0; + ilk_rxx_cfg1.s.rx_align_ena = 0; + csr_wr_node(node, CVMX_ILK_RXX_CFG1(interface), + ilk_rxx_cfg1.u64); + //debug("ILK%d: Lane alignment failed\n", interface); + goto fail; + } + + lane_mask = ilk_rxx_cfg1.s.rx_bdry_lock_ena; + + if (ilk_rxx_cfg1.s.pkt_ena == 0 && ilk_rxx_int.s.lane_align_done) { + cvmx_ilk_txx_cfg1_t ilk_txx_cfg1; + + ilk_txx_cfg1.u64 = + csr_rd_node(node, CVMX_ILK_TXX_CFG1(interface)); + ilk_rxx_cfg1.u64 = + csr_rd_node(node, CVMX_ILK_RXX_CFG1(interface)); + ilk_rxx_cfg1.s.pkt_ena = ilk_txx_cfg1.s.pkt_ena; + csr_wr_node(node, CVMX_ILK_RXX_CFG1(interface), + ilk_rxx_cfg1.u64); + + if (OCTEON_IS_MODEL(OCTEON_CN68XX)) { + /* + * Enable rxf_ctl_perr, rxf_lnk0_perr, rxf_lnk1_perr, + * pop_empty, push_full. + */ + csr_wr(CVMX_ILK_GBL_INT_EN, 0x1f); + /* Enable bad_pipe, bad_seq, txf_err */ + csr_wr(CVMX_ILK_TXX_INT_EN(interface), 0x7); + + /* + * Enable crc24_err, lane_bad_word, + * pkt_drop_{rid,rxf,sop} + */ + csr_wr(CVMX_ILK_RXX_INT_EN(interface), 0x1e2); + } + /* Need to enable ILK interrupts for 78xx */ + + for (i = 0; i < CVMX_ILK_MAX_LANES(); i++) { + if ((1 << i) & lane_mask) { + /* clear pending interrupts, before enabling. */ + csr_wr_node(node, CVMX_ILK_RX_LNEX_INT(i), + 0x1ff); + /* Enable bad_64b67b, bdry_sync_loss, crc32_err, + * dskew_fifo_ovfl, scrm_sync_loss, + * serdes_lock_loss, stat_msg, ukwn_cntl_word + */ + if (OCTEON_IS_MODEL(OCTEON_CN68XX)) + csr_wr(CVMX_ILK_RX_LNEX_INT_EN(i), + 0x1ff); + } + } + + //debug("ILK%d: Lane alignment complete\n", interface); + } + + /* Enable error interrupts, now link is up */ + cvmx_error_enable_group(CVMX_ERROR_GROUP_ILK, + node | (interface << 2) | (lane_mask << 4)); + + result.s.link_up = 1; + result.s.full_duplex = 1; + if (OCTEON_IS_MODEL(OCTEON_CN78XX)) { + int qlm = cvmx_qlm_lmac(xiface, 0); + + result.s.speed = cvmx_qlm_get_gbaud_mhz(qlm) * 64 / 67; + } else { + result.s.speed = + cvmx_qlm_get_gbaud_mhz(1 + interface) * 64 / 67; + } + result.s.speed *= cvmx_pop(lane_mask); + + return result; + +fail: + if (ilk_rxx_cfg1.s.pkt_ena) { + /* Disable the interface */ + ilk_rxx_cfg1.s.pkt_ena = 0; + csr_wr_node(node, CVMX_ILK_RXX_CFG1(interface), + ilk_rxx_cfg1.u64); + + /* Disable error interrupts */ + for (i = 0; i < CVMX_ILK_MAX_LANES(); i++) { + /* Disable bad_64b67b, bdry_sync_loss, crc32_err, + * dskew_fifo_ovfl, scrm_sync_loss, serdes_lock_loss, + * stat_msg, ukwn_cntl_word + */ + if ((1 << i) & lane_mask) { + csr_wr_node(node, CVMX_ILK_RX_LNEX_INT(i), + 0x1ff); + if (OCTEON_IS_MODEL(OCTEON_CN68XX)) + csr_wr(CVMX_ILK_RX_LNEX_INT_EN(i), + ~0x1ff); + } + } + /* Disable error interrupts */ + cvmx_error_enable_group(CVMX_ERROR_GROUP_ILK, 0); + } + + return result; +} + +/** + * @INTERNAL + * Set the link state of an IPD/PKO port. + * + * @param ipd_port IPD/PKO port to configure + * @param link_info The new link state + * + * @return Zero on success, negative on failure + */ +int __cvmx_helper_ilk_link_set(int ipd_port, cvmx_helper_link_info_t link_info) +{ + /* Do nothing */ + return 0; +} diff --git a/arch/mips/mach-octeon/cvmx-helper-ipd.c b/arch/mips/mach-octeon/cvmx-helper-ipd.c new file mode 100644 index 0000000..cb04b63 --- /dev/null +++ b/arch/mips/mach-octeon/cvmx-helper-ipd.c @@ -0,0 +1,286 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018-2022 Marvell International Ltd. + * + * IPD helper functions. + */ + +#include <errno.h> +#include <log.h> +#include <time.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-range.h> +#include <mach/cvmx-global-resources.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-ipd.h> +#include <mach/cvmx-pki.h> +#include <mach/cvmx-pko3.h> +#include <mach/cvmx-pko3-queue.h> +#include <mach/cvmx-pko3-resources.h> +#include <mach/cvmx-pip.h> + +#include <mach/cvmx-helper.h> +#include <mach/cvmx-helper-board.h> +#include <mach/cvmx-helper-cfg.h> + +#include <mach/cvmx-helper-bgx.h> +#include <mach/cvmx-helper-cfg.h> +#include <mach/cvmx-helper-util.h> +#include <mach/cvmx-helper-pki.h> + +/** It allocate pools for packet and wqe pools + * and sets up the FPA hardware + */ +int __cvmx_helper_ipd_setup_fpa_pools(void) +{ + cvmx_fpa_global_initialize(); + if (cvmx_ipd_cfg.packet_pool.buffer_count == 0) + return 0; + __cvmx_helper_initialize_fpa_pool(cvmx_ipd_cfg.packet_pool.pool_num, + cvmx_ipd_cfg.packet_pool.buffer_size, + cvmx_ipd_cfg.packet_pool.buffer_count, + "Packet Buffers"); + if (cvmx_ipd_cfg.wqe_pool.buffer_count == 0) + return 0; + __cvmx_helper_initialize_fpa_pool(cvmx_ipd_cfg.wqe_pool.pool_num, + cvmx_ipd_cfg.wqe_pool.buffer_size, + cvmx_ipd_cfg.wqe_pool.buffer_count, + "WQE Buffers"); + return 0; +} + +/** + * @INTERNAL + * Setup global setting for IPD/PIP not related to a specific + * interface or port. This must be called before IPD is enabled. + * + * @return Zero on success, negative on failure. + */ +int __cvmx_helper_ipd_global_setup(void) +{ + /* Setup the packet and wqe pools*/ + __cvmx_helper_ipd_setup_fpa_pools(); + /* Setup the global packet input options */ + cvmx_ipd_config(cvmx_ipd_cfg.packet_pool.buffer_size / 8, + cvmx_ipd_cfg.first_mbuf_skip / 8, + cvmx_ipd_cfg.not_first_mbuf_skip / 8, + /* The +8 is to account for the next ptr */ + (cvmx_ipd_cfg.first_mbuf_skip + 8) / 128, + /* The +8 is to account for the next ptr */ + (cvmx_ipd_cfg.not_first_mbuf_skip + 8) / 128, + cvmx_ipd_cfg.wqe_pool.pool_num, + (cvmx_ipd_mode_t)(cvmx_ipd_cfg.cache_mode), 1); + return 0; +} + +/** + * Enable or disable FCS stripping for all the ports on an interface. + * + * @param xiface + * @param nports number of ports + * @param has_fcs 0 for disable and !0 for enable + */ +static int cvmx_helper_fcs_op(int xiface, int nports, int has_fcs) +{ + u64 port_bit; + int index; + int pknd; + union cvmx_pip_sub_pkind_fcsx pkind_fcsx; + union cvmx_pip_prt_cfgx port_cfg; + struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); + + if (!octeon_has_feature(OCTEON_FEATURE_PKND)) + return 0; + if (octeon_has_feature(OCTEON_FEATURE_PKI)) { + cvmx_helper_pki_set_fcs_op(xi.node, xi.interface, nports, + has_fcs); + return 0; + } + + port_bit = 0; + for (index = 0; index < nports; index++) + port_bit |= ((u64)1 << cvmx_helper_get_pknd(xiface, index)); + + pkind_fcsx.u64 = csr_rd(CVMX_PIP_SUB_PKIND_FCSX(0)); + if (has_fcs) + pkind_fcsx.s.port_bit |= port_bit; + else + pkind_fcsx.s.port_bit &= ~port_bit; + csr_wr(CVMX_PIP_SUB_PKIND_FCSX(0), pkind_fcsx.u64); + + for (pknd = 0; pknd < 64; pknd++) { + if ((1ull << pknd) & port_bit) { + port_cfg.u64 = csr_rd(CVMX_PIP_PRT_CFGX(pknd)); + port_cfg.s.crc_en = (has_fcs) ? 1 : 0; + csr_wr(CVMX_PIP_PRT_CFGX(pknd), port_cfg.u64); + } + } + + return 0; +} + +/** + * @INTERNAL + * Configure the IPD/PIP tagging and QoS options for a specific + * port. This function determines the POW work queue entry + * contents for a port. The setup performed here is controlled by + * the defines in executive-config.h. + * + * @param ipd_port Port/Port kind to configure. This follows the IPD numbering, + * not the per interface numbering + * + * @return Zero on success, negative on failure + */ +static int __cvmx_helper_ipd_port_setup(int ipd_port) +{ + union cvmx_pip_prt_cfgx port_config; + union cvmx_pip_prt_tagx tag_config; + + if (octeon_has_feature(OCTEON_FEATURE_PKND)) { + int xiface, index, pknd; + union cvmx_pip_prt_cfgbx prt_cfgbx; + + xiface = cvmx_helper_get_interface_num(ipd_port); + index = cvmx_helper_get_interface_index_num(ipd_port); + pknd = cvmx_helper_get_pknd(xiface, index); + + port_config.u64 = csr_rd(CVMX_PIP_PRT_CFGX(pknd)); + tag_config.u64 = csr_rd(CVMX_PIP_PRT_TAGX(pknd)); + + port_config.s.qos = pknd & 0x7; + + /* Default BPID to use for packets on this port-kind */ + prt_cfgbx.u64 = csr_rd(CVMX_PIP_PRT_CFGBX(pknd)); + prt_cfgbx.s.bpid = pknd; + csr_wr(CVMX_PIP_PRT_CFGBX(pknd), prt_cfgbx.u64); + } else { + port_config.u64 = csr_rd(CVMX_PIP_PRT_CFGX(ipd_port)); + tag_config.u64 = csr_rd(CVMX_PIP_PRT_TAGX(ipd_port)); + + /* Have each port go to a different POW queue */ + port_config.s.qos = ipd_port & 0x7; + } + + /* Process the headers and place the IP header in the work queue */ + port_config.s.mode = + (cvmx_pip_port_parse_mode_t)cvmx_ipd_cfg.port_config.parse_mode; + + tag_config.s.ip6_src_flag = + cvmx_ipd_cfg.port_config.tag_fields.ipv6_src_ip; + tag_config.s.ip6_dst_flag = + cvmx_ipd_cfg.port_config.tag_fields.ipv6_dst_ip; + tag_config.s.ip6_sprt_flag = + cvmx_ipd_cfg.port_config.tag_fields.ipv6_src_port; + tag_config.s.ip6_dprt_flag = + cvmx_ipd_cfg.port_config.tag_fields.ipv6_dst_port; + tag_config.s.ip6_nxth_flag = + cvmx_ipd_cfg.port_config.tag_fields.ipv6_next_header; + tag_config.s.ip4_src_flag = + cvmx_ipd_cfg.port_config.tag_fields.ipv4_src_ip; + tag_config.s.ip4_dst_flag = + cvmx_ipd_cfg.port_config.tag_fields.ipv4_dst_ip; + tag_config.s.ip4_sprt_flag = + cvmx_ipd_cfg.port_config.tag_fields.ipv4_src_port; + tag_config.s.ip4_dprt_flag = + cvmx_ipd_cfg.port_config.tag_fields.ipv4_dst_port; + tag_config.s.ip4_pctl_flag = + cvmx_ipd_cfg.port_config.tag_fields.ipv4_protocol; + tag_config.s.inc_prt_flag = + cvmx_ipd_cfg.port_config.tag_fields.input_port; + tag_config.s.tcp6_tag_type = + (cvmx_pow_tag_type_t)cvmx_ipd_cfg.port_config.tag_type; + tag_config.s.tcp4_tag_type = + (cvmx_pow_tag_type_t)cvmx_ipd_cfg.port_config.tag_type; + tag_config.s.ip6_tag_type = + (cvmx_pow_tag_type_t)cvmx_ipd_cfg.port_config.tag_type; + tag_config.s.ip4_tag_type = + (cvmx_pow_tag_type_t)cvmx_ipd_cfg.port_config.tag_type; + tag_config.s.non_tag_type = + (cvmx_pow_tag_type_t)cvmx_ipd_cfg.port_config.tag_type; + + /* Put all packets in group 0. Other groups can be used by the app */ + tag_config.s.grp = 0; + + cvmx_pip_config_port(ipd_port, port_config, tag_config); + + /* Give the user a chance to override our setting for each port */ + if (cvmx_override_ipd_port_setup) + cvmx_override_ipd_port_setup(ipd_port); + + return 0; +} + +/** + * @INTERNAL + * Setup the IPD/PIP for the ports on an interface. Packet + * classification and tagging are set for every port on the + * interface. The number of ports on the interface must already + * have been probed. + * + * @param xiface to setup IPD/PIP for + * + * @return Zero on success, negative on failure + */ +int __cvmx_helper_ipd_setup_interface(int xiface) +{ + cvmx_helper_interface_mode_t mode; + int num_ports = cvmx_helper_ports_on_interface(xiface); + struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); + int ipd_port = cvmx_helper_get_ipd_port(xiface, 0); + int delta; + + if (num_ports == CVMX_HELPER_CFG_INVALID_VALUE) + return 0; + + delta = 1; + if (octeon_has_feature(OCTEON_FEATURE_PKND)) { + if (xi.interface < CVMX_HELPER_MAX_GMX) + delta = 16; + } + + while (num_ports--) { + if (!cvmx_helper_is_port_valid(xiface, num_ports)) + continue; + if (octeon_has_feature(OCTEON_FEATURE_PKI)) + __cvmx_helper_pki_port_setup(xi.node, ipd_port); + else + __cvmx_helper_ipd_port_setup(ipd_port); + ipd_port += delta; + } + /* FCS settings */ + cvmx_helper_fcs_op(xiface, cvmx_helper_ports_on_interface(xiface), + __cvmx_helper_get_has_fcs(xiface)); + + mode = cvmx_helper_interface_get_mode(xiface); + + if (mode == CVMX_HELPER_INTERFACE_MODE_LOOP) + __cvmx_helper_loop_enable(xiface); + + return 0; +} diff --git a/arch/mips/mach-octeon/cvmx-helper-loop.c b/arch/mips/mach-octeon/cvmx-helper-loop.c new file mode 100644 index 0000000..8eaeac3 --- /dev/null +++ b/arch/mips/mach-octeon/cvmx-helper-loop.c @@ -0,0 +1,178 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018-2022 Marvell International Ltd. + * + * Functions for LOOP initialization, configuration, + * and monitoring. + */ + +#include <log.h> +#include <malloc.h> +#include <net.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/octeon_fdt.h> +#include <mach/cvmx-helper.h> +#include <mach/cvmx-helper-board.h> +#include <mach/cvmx-helper-cfg.h> +#include <mach/cvmx-helper-fdt.h> +#include <mach/cvmx-helper-gpio.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-lbk-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> + +int __cvmx_helper_loop_enumerate(int xiface) +{ + return OCTEON_IS_MODEL(OCTEON_CN68XX) ? + 8 : (OCTEON_IS_MODEL(OCTEON_CNF71XX) ? 2 : 4); +} + +/** + * @INTERNAL + * Probe a LOOP interface and determine the number of ports + * connected to it. The LOOP interface should still be down + * after this call. + * + * @param xiface Interface to probe + * + * @return Number of ports on the interface. Zero to disable. + */ +int __cvmx_helper_loop_probe(int xiface) +{ + return __cvmx_helper_loop_enumerate(xiface); +} + +/** + * @INTERNAL + * Bringup and enable a LOOP interface. After this call packet + * I/O should be fully functional. This is called with IPD + * enabled but PKO disabled. + * + * @param interface to bring up + * + * @return Zero on success, negative on failure + */ +int __cvmx_helper_loop_enable(int xiface) +{ + cvmx_pip_prt_cfgx_t port_cfg; + int num_ports, index; + unsigned long offset; + struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); + + num_ports = __cvmx_helper_get_num_ipd_ports(xiface); + /* + * We need to disable length checking so packet < 64 bytes and jumbo + * frames don't get errors + */ + for (index = 0; index < num_ports; index++) { + offset = ((octeon_has_feature(OCTEON_FEATURE_PKND)) ? + cvmx_helper_get_pknd(xiface, index) : + cvmx_helper_get_ipd_port(xiface, index)); + + if (octeon_has_feature(OCTEON_FEATURE_PKI)) { + cvmx_pki_endis_l2_errs(xi.node, offset, 1, 0, 0); + cvmx_pki_endis_fcs_check(xi.node, offset, 0, 0); + } else { + port_cfg.u64 = csr_rd(CVMX_PIP_PRT_CFGX(offset)); + port_cfg.s.maxerr_en = 0; + port_cfg.s.minerr_en = 0; + csr_wr(CVMX_PIP_PRT_CFGX(offset), port_cfg.u64); + } + } + + /* + * Disable FCS stripping for loopback ports + */ + if (!octeon_has_feature(OCTEON_FEATURE_PKND)) { + cvmx_ipd_sub_port_fcs_t ipd_sub_port_fcs; + + ipd_sub_port_fcs.u64 = csr_rd(CVMX_IPD_SUB_PORT_FCS); + ipd_sub_port_fcs.s.port_bit2 = 0; + csr_wr(CVMX_IPD_SUB_PORT_FCS, ipd_sub_port_fcs.u64); + } + /* + * Set PKND and BPID for loopback ports. + */ + if (OCTEON_IS_MODEL(OCTEON_CN68XX)) { + cvmx_pko_reg_loopback_pkind_t lp_pknd; + cvmx_pko_reg_loopback_bpid_t lp_bpid; + + for (index = 0; index < num_ports; index++) { + int pknd = cvmx_helper_get_pknd(xiface, index); + int bpid = cvmx_helper_get_bpid(xiface, index); + + lp_pknd.u64 = csr_rd(CVMX_PKO_REG_LOOPBACK_PKIND); + lp_bpid.u64 = csr_rd(CVMX_PKO_REG_LOOPBACK_BPID); + + if (index == 0) + lp_pknd.s.num_ports = num_ports; + + switch (index) { + case 0: + lp_pknd.s.pkind0 = pknd; + lp_bpid.s.bpid0 = bpid; + break; + case 1: + lp_pknd.s.pkind1 = pknd; + lp_bpid.s.bpid1 = bpid; + break; + case 2: + lp_pknd.s.pkind2 = pknd; + lp_bpid.s.bpid2 = bpid; + break; + case 3: + lp_pknd.s.pkind3 = pknd; + lp_bpid.s.bpid3 = bpid; + break; + case 4: + lp_pknd.s.pkind4 = pknd; + lp_bpid.s.bpid4 = bpid; + break; + case 5: + lp_pknd.s.pkind5 = pknd; + lp_bpid.s.bpid5 = bpid; + break; + case 6: + lp_pknd.s.pkind6 = pknd; + lp_bpid.s.bpid6 = bpid; + break; + case 7: + lp_pknd.s.pkind7 = pknd; + lp_bpid.s.bpid7 = bpid; + break; + } + csr_wr(CVMX_PKO_REG_LOOPBACK_PKIND, lp_pknd.u64); + csr_wr(CVMX_PKO_REG_LOOPBACK_BPID, lp_bpid.u64); + } + } else if (octeon_has_feature(OCTEON_FEATURE_BGX)) { + cvmx_lbk_chx_pkind_t lbk_pkind; + + for (index = 0; index < num_ports; index++) { + lbk_pkind.u64 = 0; + lbk_pkind.s.pkind = cvmx_helper_get_pknd(xiface, index); + csr_wr_node(xi.node, CVMX_LBK_CHX_PKIND(index), + lbk_pkind.u64); + } + } + + return 0; +} diff --git a/arch/mips/mach-octeon/cvmx-helper-npi.c b/arch/mips/mach-octeon/cvmx-helper-npi.c new file mode 100644 index 0000000..92ee1a8 --- /dev/null +++ b/arch/mips/mach-octeon/cvmx-helper-npi.c @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018-2022 Marvell International Ltd. + * + * Functions for NPI initialization, configuration, + * and monitoring. + */ + +#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-pexp-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-sli-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> + +static int cvmx_npi_num_pipes = -1; + +/** + * @INTERNAL + * Probe a NPI interface and determine the number of ports + * connected to it. The NPI interface should still be down + * after this call. + * + * @param interface to probe + * + * @return Number of ports on the interface. Zero to disable. + */ +int __cvmx_helper_npi_probe(int interface) +{ + if (OCTEON_IS_MODEL(OCTEON_CN68XX)) + return 32; + else if (OCTEON_IS_MODEL(OCTEON_CN73XX)) + return 128; + else if (OCTEON_IS_MODEL(OCTEON_CN78XX)) + return 64; + + return 0; +} + +/** + * @INTERNAL + * Bringup and enable a NPI interface. After this call packet + * I/O should be fully functional. This is called with IPD + * enabled but PKO disabled. + * + * @param xiface Interface to bring up + * + * @return Zero on success, negative on failure + */ +int __cvmx_helper_npi_enable(int xiface) +{ + struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); + int interface = xi.interface; + int port; + int num_ports = cvmx_helper_ports_on_interface(interface); + + /* + * On CN50XX, CN52XX, and CN56XX we need to disable length + * checking so packet < 64 bytes and jumbo frames don't get + * errors. + */ + for (port = 0; port < num_ports; port++) { + union cvmx_pip_prt_cfgx port_cfg; + int ipd_port = + (octeon_has_feature(OCTEON_FEATURE_PKND)) ? + cvmx_helper_get_pknd(interface, port) : + cvmx_helper_get_ipd_port(interface, port); + if (octeon_has_feature(OCTEON_FEATURE_PKI)) { + unsigned int node = cvmx_get_node_num(); + + cvmx_pki_endis_l2_errs(node, ipd_port, 0, 0, 0); + + } else { + port_cfg.u64 = csr_rd(CVMX_PIP_PRT_CFGX(ipd_port)); + port_cfg.s.lenerr_en = 0; + port_cfg.s.maxerr_en = 0; + port_cfg.s.minerr_en = 0; + csr_wr(CVMX_PIP_PRT_CFGX(ipd_port), port_cfg.u64); + } + if (OCTEON_IS_MODEL(OCTEON_CN68XX)) { + /* Set up pknd and bpid */ + union cvmx_sli_portx_pkind config; + + config.u64 = csr_rd(CVMX_PEXP_SLI_PORTX_PKIND(port)); + config.s.bpkind = cvmx_helper_get_bpid(interface, port); + config.s.pkind = cvmx_helper_get_pknd(interface, port); + csr_wr(CVMX_PEXP_SLI_PORTX_PKIND(port), config.u64); + } + } + + if (OCTEON_IS_MODEL(OCTEON_CN68XX)) { + /* + * Set up pko pipes. + */ + union cvmx_sli_tx_pipe config; + + config.u64 = csr_rd(CVMX_PEXP_SLI_TX_PIPE); + config.s.base = __cvmx_pko_get_pipe(interface, 0); + config.s.nump = + cvmx_npi_num_pipes < 0 ? num_ports : cvmx_npi_num_pipes; + csr_wr(CVMX_PEXP_SLI_TX_PIPE, config.u64); + } + + /* Enables are controlled by the remote host, so nothing to do here */ + return 0; +} diff --git a/arch/mips/mach-octeon/cvmx-helper-pki.c b/arch/mips/mach-octeon/cvmx-helper-pki.c new file mode 100644 index 0000000..51fa4fb --- /dev/null +++ b/arch/mips/mach-octeon/cvmx-helper-pki.c @@ -0,0 +1,549 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018-2022 Marvell International Ltd. + * + * PKI helper functions. + */ + +#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-pexp-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-sli-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> +#include <mach/cvmx-helper-pki.h> + +#include <mach/cvmx-global-resources.h> +#include <mach/cvmx-pko-internal-ports-range.h> +#include <mach/cvmx-ilk.h> +#include <mach/cvmx-pip.h> + +static int pki_helper_debug; + +bool cvmx_pki_dflt_init[CVMX_MAX_NODES] = { [0 ... CVMX_MAX_NODES - 1] = 1 }; + +static bool cvmx_pki_dflt_bp_en[CVMX_MAX_NODES] = { [0 ... CVMX_MAX_NODES - 1] = + true }; +static struct cvmx_pki_cluster_grp_config pki_dflt_clgrp[CVMX_MAX_NODES] = { + { 0, 0xf }, + { 0, 0xf } +}; + +struct cvmx_pki_pool_config pki_dflt_pool[CVMX_MAX_NODES] = { + [0 ... CVMX_MAX_NODES - + 1] = { .pool_num = -1, .buffer_size = 2048, .buffer_count = 0 } +}; + +struct cvmx_pki_aura_config pki_dflt_aura[CVMX_MAX_NODES] = { + [0 ... CVMX_MAX_NODES - + 1] = { .aura_num = 0, .pool_num = -1, .buffer_count = 0 } +}; + +struct cvmx_pki_style_config pki_dflt_style[CVMX_MAX_NODES] = { + [0 ... CVMX_MAX_NODES - 1] = { .parm_cfg = { .lenerr_en = 1, + .maxerr_en = 1, + .minerr_en = 1, + .fcs_strip = 1, + .fcs_chk = 1, + .first_skip = 40, + .mbuff_size = 2048 } } +}; + +struct cvmx_pki_sso_grp_config pki_dflt_sso_grp[CVMX_MAX_NODES]; +struct cvmx_pki_qpg_config pki_dflt_qpg[CVMX_MAX_NODES]; +struct cvmx_pki_pkind_config pki_dflt_pkind[CVMX_MAX_NODES]; +u64 pkind_style_map[CVMX_MAX_NODES][CVMX_PKI_NUM_PKIND] = { + [0 ... CVMX_MAX_NODES - + 1] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63 } +}; + +/* To store the qos watcher values before they are written to pcam when watcher + * is enabled. There is no cvmx-pip.c file exist so it ended up here + */ +struct cvmx_pki_legacy_qos_watcher qos_watcher[8]; + +/** @INTERNAL + * This function setsup default ltype map + * @param node node number + */ +void __cvmx_helper_pki_set_dflt_ltype_map(int node) +{ + cvmx_pki_write_ltype_map(node, CVMX_PKI_LTYPE_E_NONE, + CVMX_PKI_BELTYPE_NONE); + cvmx_pki_write_ltype_map(node, CVMX_PKI_LTYPE_E_ENET, + CVMX_PKI_BELTYPE_MISC); + cvmx_pki_write_ltype_map(node, CVMX_PKI_LTYPE_E_VLAN, + CVMX_PKI_BELTYPE_MISC); + cvmx_pki_write_ltype_map(node, CVMX_PKI_LTYPE_E_SNAP_PAYLD, + CVMX_PKI_BELTYPE_MISC); + cvmx_pki_write_ltype_map(node, CVMX_PKI_LTYPE_E_ARP, + CVMX_PKI_BELTYPE_MISC); + cvmx_pki_write_ltype_map(node, CVMX_PKI_LTYPE_E_RARP, + CVMX_PKI_BELTYPE_MISC); + cvmx_pki_write_ltype_map(node, CVMX_PKI_LTYPE_E_IP4, + CVMX_PKI_BELTYPE_IP4); + cvmx_pki_write_ltype_map(node, CVMX_PKI_LTYPE_E_IP4_OPT, + CVMX_PKI_BELTYPE_IP4); + cvmx_pki_write_ltype_map(node, CVMX_PKI_LTYPE_E_IP6, + CVMX_PKI_BELTYPE_IP6); + cvmx_pki_write_ltype_map(node, CVMX_PKI_LTYPE_E_IP6_OPT, + CVMX_PKI_BELTYPE_IP6); + cvmx_pki_write_ltype_map(node, CVMX_PKI_LTYPE_E_IPSEC_ESP, + CVMX_PKI_BELTYPE_MISC); + cvmx_pki_write_ltype_map(node, CVMX_PKI_LTYPE_E_IPFRAG, + CVMX_PKI_BELTYPE_MISC); + cvmx_pki_write_ltype_map(node, CVMX_PKI_LTYPE_E_IPCOMP, + CVMX_PKI_BELTYPE_MISC); + cvmx_pki_write_ltype_map(node, CVMX_PKI_LTYPE_E_TCP, + CVMX_PKI_BELTYPE_TCP); + cvmx_pki_write_ltype_map(node, CVMX_PKI_LTYPE_E_UDP, + CVMX_PKI_BELTYPE_UDP); + cvmx_pki_write_ltype_map(node, CVMX_PKI_LTYPE_E_SCTP, + CVMX_PKI_BELTYPE_SCTP); + cvmx_pki_write_ltype_map(node, CVMX_PKI_LTYPE_E_UDP_VXLAN, + CVMX_PKI_BELTYPE_UDP); + cvmx_pki_write_ltype_map(node, CVMX_PKI_LTYPE_E_GRE, + CVMX_PKI_BELTYPE_MISC); + cvmx_pki_write_ltype_map(node, CVMX_PKI_LTYPE_E_NVGRE, + CVMX_PKI_BELTYPE_MISC); + cvmx_pki_write_ltype_map(node, CVMX_PKI_LTYPE_E_GTP, + CVMX_PKI_BELTYPE_MISC); + cvmx_pki_write_ltype_map(node, CVMX_PKI_LTYPE_E_SW28, + CVMX_PKI_BELTYPE_MISC); + cvmx_pki_write_ltype_map(node, CVMX_PKI_LTYPE_E_SW29, + CVMX_PKI_BELTYPE_MISC); + cvmx_pki_write_ltype_map(node, CVMX_PKI_LTYPE_E_SW30, + CVMX_PKI_BELTYPE_MISC); + cvmx_pki_write_ltype_map(node, CVMX_PKI_LTYPE_E_SW31, + CVMX_PKI_BELTYPE_MISC); +} + +/** @INTERNAL + * This function installs the default VLAN entries to identify + * the VLAN and set WQE[vv], WQE[vs] if VLAN is found. In 78XX + * hardware (PKI) is not hardwired to recognize any 802.1Q VLAN + * Ethertypes + * + * @param node node number + */ +int __cvmx_helper_pki_install_dflt_vlan(int node) +{ + struct cvmx_pki_pcam_input pcam_input; + struct cvmx_pki_pcam_action pcam_action; + enum cvmx_pki_term field; + int index; + int bank; + u64 cl_mask = CVMX_PKI_CLUSTER_ALL; + + memset(&pcam_input, 0, sizeof(pcam_input)); + memset(&pcam_action, 0, sizeof(pcam_action)); + + if (OCTEON_IS_MODEL(OCTEON_CN78XX_PASS1_X)) { + /* PKI-20858 */ + int i; + + for (i = 0; i < 4; i++) { + union cvmx_pki_clx_ecc_ctl ecc_ctl; + + ecc_ctl.u64 = + csr_rd_node(node, CVMX_PKI_CLX_ECC_CTL(i)); + ecc_ctl.s.pcam_en = 0; + ecc_ctl.s.pcam0_cdis = 1; + ecc_ctl.s.pcam1_cdis = 1; + csr_wr_node(node, CVMX_PKI_CLX_ECC_CTL(i), ecc_ctl.u64); + } + } + + for (field = CVMX_PKI_PCAM_TERM_ETHTYPE0; + field < CVMX_PKI_PCAM_TERM_ETHTYPE2; field++) { + bank = field & 0x01; + + index = cvmx_pki_pcam_entry_alloc( + node, CVMX_PKI_FIND_AVAL_ENTRY, bank, cl_mask); + if (index < 0) { + debug("ERROR: Allocating pcam entry node=%d bank=%d\n", + node, bank); + return -1; + } + pcam_input.style = 0; + pcam_input.style_mask = 0; + pcam_input.field = field; + pcam_input.field_mask = 0xfd; + pcam_input.data = 0x81000000; + pcam_input.data_mask = 0xffff0000; + pcam_action.parse_mode_chg = CVMX_PKI_PARSE_NO_CHG; + pcam_action.layer_type_set = CVMX_PKI_LTYPE_E_VLAN; + pcam_action.style_add = 0; + pcam_action.pointer_advance = 4; + cvmx_pki_pcam_write_entry( + node, index, cl_mask, pcam_input, + pcam_action); /*cluster_mask in pass2*/ + + index = cvmx_pki_pcam_entry_alloc( + node, CVMX_PKI_FIND_AVAL_ENTRY, bank, cl_mask); + if (index < 0) { + debug("ERROR: Allocating pcam entry node=%d bank=%d\n", + node, bank); + return -1; + } + pcam_input.data = 0x88a80000; + cvmx_pki_pcam_write_entry(node, index, cl_mask, pcam_input, + pcam_action); + + index = cvmx_pki_pcam_entry_alloc( + node, CVMX_PKI_FIND_AVAL_ENTRY, bank, cl_mask); + if (index < 0) { + debug("ERROR: Allocating pcam entry node=%d bank=%d\n", + node, bank); + return -1; + } + pcam_input.data = 0x92000000; + cvmx_pki_pcam_write_entry( + node, index, cl_mask, pcam_input, + pcam_action); /* cluster_mask in pass2*/ + + index = cvmx_pki_pcam_entry_alloc( + node, CVMX_PKI_FIND_AVAL_ENTRY, bank, cl_mask); + if (index < 0) { + debug("ERROR: Allocating pcam entry node=%d bank=%d\n", + node, bank); + return -1; + } + pcam_input.data = 0x91000000; + cvmx_pki_pcam_write_entry(node, index, cl_mask, pcam_input, + pcam_action); + } + return 0; +} + +static int __cvmx_helper_setup_pki_cluster_groups(int node) +{ + u64 cl_mask; + int cl_group; + + cl_group = + cvmx_pki_cluster_grp_alloc(node, pki_dflt_clgrp[node].grp_num); + if (cl_group == CVMX_RESOURCE_ALLOC_FAILED) + return -1; + else if (cl_group == CVMX_RESOURCE_ALREADY_RESERVED) { + if (pki_dflt_clgrp[node].grp_num == -1) + return -1; + else + return 0; /* cluster already configured, share it */ + } + cl_mask = pki_dflt_clgrp[node].cluster_mask; + if (pki_helper_debug) + debug("pki-helper: setup pki cluster grp %d with cl_mask 0x%llx\n", + (int)cl_group, (unsigned long long)cl_mask); + cvmx_pki_attach_cluster_to_group(node, cl_group, cl_mask); + return 0; +} + +/** + * This function sets up pools/auras to be used by PKI + * @param node node number + */ +static int __cvmx_helper_pki_setup_fpa_pools(int node) +{ + u64 buffer_count; + u64 buffer_size; + + if (__cvmx_fpa3_aura_valid(pki_dflt_aura[node].aura)) + return 0; /* aura already configured, share it */ + + buffer_count = pki_dflt_pool[node].buffer_count; + buffer_size = pki_dflt_pool[node].buffer_size; + + if (buffer_count != 0) { + pki_dflt_pool[node].pool = cvmx_fpa3_setup_fill_pool( + node, pki_dflt_pool[node].pool_num, "PKI POOL DFLT", + buffer_size, buffer_count, NULL); + if (!__cvmx_fpa3_pool_valid(pki_dflt_pool[node].pool)) { + cvmx_printf("ERROR: %s: Failed to allocate pool %d\n", + __func__, pki_dflt_pool[node].pool_num); + return -1; + } + pki_dflt_pool[node].pool_num = pki_dflt_pool[node].pool.lpool; + + if (pki_helper_debug) + debug("%s pool %d with buffer size %d cnt %d\n", + __func__, pki_dflt_pool[node].pool_num, + (int)buffer_size, (int)buffer_count); + + pki_dflt_aura[node].pool_num = pki_dflt_pool[node].pool_num; + pki_dflt_aura[node].pool = pki_dflt_pool[node].pool; + } + + buffer_count = pki_dflt_aura[node].buffer_count; + + if (buffer_count != 0) { + pki_dflt_aura[node].aura = cvmx_fpa3_set_aura_for_pool( + pki_dflt_aura[node].pool, pki_dflt_aura[node].aura_num, + "PKI DFLT AURA", buffer_size, buffer_count); + + if (!__cvmx_fpa3_aura_valid(pki_dflt_aura[node].aura)) { + debug("ERROR: %sL Failed to allocate aura %d\n", + __func__, pki_dflt_aura[node].aura_num); + return -1; + } + } + return 0; +} + +static int __cvmx_helper_setup_pki_qpg_table(int node) +{ + int offset; + + offset = cvmx_pki_qpg_entry_alloc(node, pki_dflt_qpg[node].qpg_base, 1); + if (offset == CVMX_RESOURCE_ALLOC_FAILED) + return -1; + else if (offset == CVMX_RESOURCE_ALREADY_RESERVED) + return 0; /* share the qpg table entry */ + if (pki_helper_debug) + debug("pki-helper: set qpg entry at offset %d with port add %d aura %d grp_ok %d grp_bad %d\n", + offset, pki_dflt_qpg[node].port_add, + pki_dflt_qpg[node].aura_num, pki_dflt_qpg[node].grp_ok, + pki_dflt_qpg[node].grp_bad); + cvmx_pki_write_qpg_entry(node, offset, &pki_dflt_qpg[node]); + return 0; +} + +int __cvmx_helper_pki_port_setup(int node, int ipd_port) +{ + int xiface, index; + int pknd, style_num; + int rs; + struct cvmx_pki_pkind_config pkind_cfg; + + if (!cvmx_pki_dflt_init[node]) + return 0; + xiface = cvmx_helper_get_interface_num(ipd_port); + index = cvmx_helper_get_interface_index_num(ipd_port); + + pknd = cvmx_helper_get_pknd(xiface, index); + style_num = pkind_style_map[node][pknd]; + + /* try to reserve the style, if it is not configured already, reserve + and configure it */ + rs = cvmx_pki_style_alloc(node, style_num); + if (rs < 0) { + if (rs == CVMX_RESOURCE_ALLOC_FAILED) + return -1; + } else { + if (pki_helper_debug) + debug("pki-helper: set style %d with default parameters\n", + style_num); + pkind_style_map[node][pknd] = style_num; + /* configure style with default parameters */ + cvmx_pki_write_style_config(node, style_num, + CVMX_PKI_CLUSTER_ALL, + &pki_dflt_style[node]); + } + if (pki_helper_debug) + debug("pki-helper: set pkind %d with initial style %d\n", pknd, + style_num); + /* write pkind configuration */ + pkind_cfg = pki_dflt_pkind[node]; + pkind_cfg.initial_style = style_num; + cvmx_pki_write_pkind_config(node, pknd, &pkind_cfg); + return 0; +} + +int __cvmx_helper_pki_global_setup(int node) +{ + __cvmx_helper_pki_set_dflt_ltype_map(node); + if (!cvmx_pki_dflt_init[node]) + return 0; + /* Setup the packet pools*/ + __cvmx_helper_pki_setup_fpa_pools(node); + /*set up default cluster*/ + __cvmx_helper_setup_pki_cluster_groups(node); + //__cvmx_helper_pki_setup_sso_groups(node); + __cvmx_helper_setup_pki_qpg_table(node); + /* + * errata PKI-19103 backward compat has only 1 aura + * no head line blocking + */ + if (OCTEON_IS_MODEL(OCTEON_CN78XX_PASS1_X)) { + cvmx_pki_buf_ctl_t buf_ctl; + + buf_ctl.u64 = csr_rd_node(node, CVMX_PKI_BUF_CTL); + buf_ctl.s.fpa_wait = 1; + csr_wr_node(node, CVMX_PKI_BUF_CTL, buf_ctl.u64); + } + return 0; +} + +/** + * This function Enabled the PKI hardware to + * start accepting/processing packets. + * + * @param node node number + */ +void cvmx_helper_pki_enable(int node) +{ + if (pki_helper_debug) + debug("enable PKI on node %d\n", node); + __cvmx_helper_pki_install_dflt_vlan(node); + cvmx_pki_setup_clusters(node); + if (cvmx_pki_dflt_bp_en[node]) + cvmx_pki_enable_backpressure(node); + cvmx_pki_parse_enable(node, 0); + cvmx_pki_enable(node); +} + +/** + * This function setups the qos table by allocating qpg entry and writing + * the provided parameters to that entry (offset). + * @param node node number. + * @param qpg_cfg pointer to struct containing qpg configuration + */ +int cvmx_helper_pki_set_qpg_entry(int node, struct cvmx_pki_qpg_config *qpg_cfg) +{ + int offset; + + offset = cvmx_pki_qpg_entry_alloc(node, qpg_cfg->qpg_base, 1); + if (pki_helper_debug) + debug("pki-helper:set qpg entry at offset %d\n", offset); + if (offset == CVMX_RESOURCE_ALREADY_RESERVED) { + debug("INFO:setup_qpg_table: offset %d already reserved\n", + qpg_cfg->qpg_base); + return CVMX_RESOURCE_ALREADY_RESERVED; + } else if (offset == CVMX_RESOURCE_ALLOC_FAILED) { + debug("ERROR:setup_qpg_table: no more entries available\n"); + return CVMX_RESOURCE_ALLOC_FAILED; + } + qpg_cfg->qpg_base = offset; + cvmx_pki_write_qpg_entry(node, offset, qpg_cfg); + return offset; +} + +/** + * This function gets all the PKI parameters related to that + * particular port from hardware. + * @param xipd_port xipd_port port number with node to get parameter of + * @param port_cfg pointer to structure where to store read parameters + */ +void cvmx_pki_get_port_config(int xipd_port, + struct cvmx_pki_port_config *port_cfg) +{ + int xiface, index, pknd; + int style, cl_mask; + cvmx_pki_icgx_cfg_t pki_cl_msk; + struct cvmx_xport xp = cvmx_helper_ipd_port_to_xport(xipd_port); + + /* get the pkind used by this ipd port */ + xiface = cvmx_helper_get_interface_num(xipd_port); + index = cvmx_helper_get_interface_index_num(xipd_port); + pknd = cvmx_helper_get_pknd(xiface, index); + + cvmx_pki_read_pkind_config(xp.node, pknd, &port_cfg->pkind_cfg); + style = port_cfg->pkind_cfg.initial_style; + pki_cl_msk.u64 = csr_rd_node( + xp.node, CVMX_PKI_ICGX_CFG(port_cfg->pkind_cfg.cluster_grp)); + cl_mask = pki_cl_msk.s.clusters; + cvmx_pki_read_style_config(xp.node, style, cl_mask, + &port_cfg->style_cfg); +} + +/** + * This function sets all the PKI parameters related to that + * particular port in hardware. + * @param xipd_port ipd port number with node to get parameter of + * @param port_cfg pointer to structure containing port parameters + */ +void cvmx_pki_set_port_config(int xipd_port, + struct cvmx_pki_port_config *port_cfg) +{ + int xiface, index, pknd; + int style, cl_mask; + cvmx_pki_icgx_cfg_t pki_cl_msk; + struct cvmx_xport xp = cvmx_helper_ipd_port_to_xport(xipd_port); + + /* get the pkind used by this ipd port */ + xiface = cvmx_helper_get_interface_num(xipd_port); + index = cvmx_helper_get_interface_index_num(xipd_port); + pknd = cvmx_helper_get_pknd(xiface, index); + + if (cvmx_pki_write_pkind_config(xp.node, pknd, &port_cfg->pkind_cfg)) + return; + style = port_cfg->pkind_cfg.initial_style; + pki_cl_msk.u64 = csr_rd_node( + xp.node, CVMX_PKI_ICGX_CFG(port_cfg->pkind_cfg.cluster_grp)); + cl_mask = pki_cl_msk.s.clusters; + cvmx_pki_write_style_config(xp.node, style, cl_mask, + &port_cfg->style_cfg); +} + +/** + * This function sets up all th eports of particular interface + * for chosen fcs mode. (only use for backward compatibility). + * New application can control it via init_interface calls. + * @param node node number. + * @param interface interface number. + * @param nports number of ports + * @param has_fcs 1 -- enable fcs check and fcs strip. + * 0 -- disable fcs check. + */ +void cvmx_helper_pki_set_fcs_op(int node, int interface, int nports, + int has_fcs) +{ + int xiface, index; + int pknd; + unsigned int cluster = 0; + cvmx_pki_clx_pkindx_cfg_t pkind_cfg; + + xiface = cvmx_helper_node_interface_to_xiface(node, interface); + for (index = 0; index < nports; index++) { + pknd = cvmx_helper_get_pknd(xiface, index); + while (cluster < CVMX_PKI_NUM_CLUSTER) { + /*find the cluster in use pass2*/ + pkind_cfg.u64 = csr_rd_node( + node, CVMX_PKI_CLX_PKINDX_CFG(pknd, cluster)); + pkind_cfg.s.fcs_pres = has_fcs; + csr_wr_node(node, + CVMX_PKI_CLX_PKINDX_CFG(pknd, cluster), + pkind_cfg.u64); + cluster++; + } + /* make sure fcs_strip and fcs_check is also enable/disable + * for the style used by that port + */ + cvmx_pki_endis_fcs_check(node, pknd, has_fcs, has_fcs); + cluster = 0; + } +} diff --git a/arch/mips/mach-octeon/cvmx-helper-pko.c b/arch/mips/mach-octeon/cvmx-helper-pko.c new file mode 100644 index 0000000..b9ac22c --- /dev/null +++ b/arch/mips/mach-octeon/cvmx-helper-pko.c @@ -0,0 +1,203 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018-2022 Marvell International Ltd. + * + * Helper Functions for the PKO + */ + +#include <errno.h> +#include <log.h> +#include <time.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-range.h> +#include <mach/cvmx-global-resources.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-ipd.h> +#include <mach/cvmx-pki.h> +#include <mach/cvmx-pko3.h> +#include <mach/cvmx-pko3-queue.h> +#include <mach/cvmx-pko3-resources.h> + +#include <mach/cvmx-helper.h> +#include <mach/cvmx-helper-board.h> +#include <mach/cvmx-helper-cfg.h> + +#include <mach/cvmx-helper-bgx.h> +#include <mach/cvmx-helper-cfg.h> +#include <mach/cvmx-helper-util.h> +#include <mach/cvmx-helper-pki.h> + +static s64 pko_fpa_config_pool = -1; +static u64 pko_fpa_config_size = 1024; + +/** + * cvmx_override_pko_queue_priority(int pko_port, u64 + * priorities[16]) is a function pointer. It is meant to allow + * customization of the PKO queue priorities based on the port + * number. Users should set this pointer to a function before + * calling any cvmx-helper operations. + */ +void (*cvmx_override_pko_queue_priority)(int ipd_port, + uint8_t *priorities) = NULL; + +int64_t cvmx_fpa_get_pko_pool(void) +{ + return pko_fpa_config_pool; +} + +/** + * Gets the buffer size of pko pool + */ +u64 cvmx_fpa_get_pko_pool_block_size(void) +{ + return pko_fpa_config_size; +} + +/** + * Initialize PKO command queue buffer pool + */ +static int cvmx_helper_pko_pool_init(void) +{ + u8 pool; + unsigned int buf_count; + unsigned int pkt_buf_count; + int rc; + + /* Reserve pool */ + pool = cvmx_fpa_get_pko_pool(); + + /* Avoid redundant pool creation */ + if (cvmx_fpa_get_block_size(pool) > 0) { +#ifdef DEBUG + debug("WARNING: %s: pool %d already initialized\n", __func__, + pool); +#endif + /* It is up to the app to have sufficient buffer count */ + return pool; + } + + /* Calculate buffer count: one per queue + 3-word-cmds * max_pkts */ + pkt_buf_count = cvmx_fpa_get_packet_pool_buffer_count(); + buf_count = CVMX_PKO_MAX_OUTPUT_QUEUES + (pkt_buf_count * 3) / 8; + + /* Allocate pools for pko command queues */ + rc = __cvmx_helper_initialize_fpa_pool(pool, + cvmx_fpa_get_pko_pool_block_size(), + buf_count, "PKO Cmd-bufs"); + + if (rc < 0) + debug("%s: ERROR: in PKO buffer pool\n", __func__); + + pool = rc; + return pool; +} + +/** + * Initialize the PKO + * + */ +int cvmx_helper_pko_init(void) +{ + int rc; + + rc = cvmx_helper_pko_pool_init(); + if (rc < 0) + return rc; + + __cvmx_helper_init_port_config_data(0); + + cvmx_pko_hw_init(cvmx_fpa_get_pko_pool(), + cvmx_fpa_get_pko_pool_block_size()); + return 0; +} + +/** + * @INTERNAL + * Setup the PKO for the ports on an interface. The number of + * queues per port and the priority of each PKO output queue + * is set here. PKO must be disabled when this function is called. + * + * @param interface to setup PKO for + * + * @return Zero on success, negative on failure + * + * @note This is for PKO1/PKO2, and is not used for PKO3. + */ +int __cvmx_helper_interface_setup_pko(int interface) +{ + /* + * Each packet output queue has an associated priority. The + * higher the priority, the more often it can send a packet. A + * priority of 8 means it can send in all 8 rounds of + * contention. We're going to make each queue one less than + * the last. The vector of priorities has been extended to + * support CN5xxx CPUs, where up to 16 queues can be + * associated to a port. To keep backward compatibility we + * don't change the initial 8 priorities and replicate them in + * the second half. With per-core PKO queues (PKO lockless + * operation) all queues have the same priority. + */ + /* uint8_t priorities[16] = {8,7,6,5,4,3,2,1,8,7,6,5,4,3,2,1}; */ + u8 priorities[16] = { [0 ... 15] = 8 }; + + /* + * Setup the IPD/PIP and PKO for the ports discovered + * above. Here packet classification, tagging and output + * priorities are set. + */ + int num_ports = cvmx_helper_ports_on_interface(interface); + + while (num_ports--) { + int ipd_port; + + if (!cvmx_helper_is_port_valid(interface, num_ports)) + continue; + + ipd_port = cvmx_helper_get_ipd_port(interface, num_ports); + /* + * Give the user a chance to override the per queue + * priorities. + */ + if (cvmx_override_pko_queue_priority) + cvmx_override_pko_queue_priority(ipd_port, priorities); + + cvmx_pko_config_port(ipd_port, + cvmx_pko_get_base_queue(ipd_port), + cvmx_pko_get_num_queues(ipd_port), + priorities); + ipd_port++; + } + return 0; + /* NOTE: + * Now this function is called for all chips including 68xx, + * but on the 68xx it does not enable multiple pko_iports per + * eport, while before it was doing 3 pko_iport per eport + * buf the reason for that is not clear. + */ +} diff --git a/arch/mips/mach-octeon/cvmx-helper-pko3.c b/arch/mips/mach-octeon/cvmx-helper-pko3.c new file mode 100644 index 0000000..37fe45d --- /dev/null +++ b/arch/mips/mach-octeon/cvmx-helper-pko3.c @@ -0,0 +1,1088 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018-2022 Marvell International Ltd. + * + * PKOv3 helper file + */ + +#include <errno.h> +#include <log.h> +#include <time.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-range.h> +#include <mach/cvmx-global-resources.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-ipd.h> +#include <mach/cvmx-pki.h> +#include <mach/cvmx-pko3.h> +#include <mach/cvmx-pko3-queue.h> +#include <mach/cvmx-pko3-resources.h> + +#include <mach/cvmx-helper.h> +#include <mach/cvmx-helper-board.h> +#include <mach/cvmx-helper-cfg.h> + +#include <mach/cvmx-helper-bgx.h> +#include <mach/cvmx-helper-cfg.h> +#include <mach/cvmx-helper-util.h> +#include <mach/cvmx-helper-pki.h> + +/* channels are present at L2 queue level by default */ +static const enum cvmx_pko3_level_e cvmx_pko_default_channel_level = + CVMX_PKO_L2_QUEUES; + +static const int debug; + +static int __pko_pkt_budget, __pko_pkt_quota; + +/* These global variables are relevant for boot CPU only */ +static cvmx_fpa3_gaura_t __cvmx_pko3_aura[CVMX_MAX_NODES]; + +/* This constant can not be modified, defined here for clarity only */ +#define CVMX_PKO3_POOL_BUFFER_SIZE 4096 /* 78XX PKO requires 4KB */ + +/** + * @INTERNAL + * + * Build an owner tag based on interface/port + */ +static int __cvmx_helper_pko3_res_owner(int ipd_port) +{ + int res_owner; + const int res_owner_pfix = 0x19d0 << 14; + + ipd_port &= 0x3fff; /* 12-bit for local CHAN_E value + node */ + + res_owner = res_owner_pfix | ipd_port; + + return res_owner; +} + +/** + * Configure an AURA/POOL designated for PKO internal use. + * + * This pool is used for (a) memory buffers that store PKO descriptor queues, + * (b) buffers for use with PKO_SEND_JUMP_S sub-header. + * + * The buffers of type (a) are never accessed by software, and their number + * should be at least equal to 4 times the number of descriptor queues + * in use. + * + * Type (b) buffers are consumed by PKO3 command-composition code, + * and are released by the hardware upon completion of transmission. + * + * @returns -1 if the pool could not be established or 12-bit AURA + * that includes the node number for use in PKO3 initialization call. + * + * NOTE: Linux kernel should pass its own aura to PKO3 initialization + * function so that the buffers can be mapped into kernel space + * for when software needs to adccess their contents. + * + */ +static int __cvmx_pko3_config_memory(unsigned int node) +{ + cvmx_fpa3_gaura_t aura; + int aura_num; + unsigned int buf_count; + bool small_mem; + int i, num_intf = 0; + const unsigned int pkt_per_buf = + (CVMX_PKO3_POOL_BUFFER_SIZE / sizeof(u64) / 16); + const unsigned int base_buf_count = 1024 * 4; + + /* Simulator has limited memory, but uses one interface at a time */ + // small_mem = cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_SIM; + small_mem = false; + + /* Count the number of live interfaces */ + for (i = 0; i < cvmx_helper_get_number_of_interfaces(); i++) { + int xiface = cvmx_helper_node_interface_to_xiface(node, i); + + if (CVMX_HELPER_INTERFACE_MODE_DISABLED != + cvmx_helper_interface_get_mode(xiface)) + num_intf++; + } + + buf_count = 1024; + __pko_pkt_quota = buf_count * pkt_per_buf; + __pko_pkt_budget = __pko_pkt_quota * num_intf; + (void)small_mem; + (void)base_buf_count; + + if (debug) + debug("%s: Creating AURA with %u buffers for up to %d total packets, %d packets per interface\n", + __func__, buf_count, __pko_pkt_budget, __pko_pkt_quota); + + aura = cvmx_fpa3_setup_aura_and_pool(node, -1, "PKO3 AURA", NULL, + CVMX_PKO3_POOL_BUFFER_SIZE, + buf_count); + + if (!__cvmx_fpa3_aura_valid(aura)) { + printf("ERROR: %s AURA create failed\n", __func__); + return -1; + } + + aura_num = aura.node << 10 | aura.laura; + + /* Store handle for destruction */ + __cvmx_pko3_aura[node] = aura; + + return aura_num; +} + +/** Initialize a channelized port + * This is intended for LOOP, ILK and NPI interfaces which have one MAC + * per interface and need a channel per subinterface (e.g. ring). + * Each channel then may have 'num_queues' descriptor queues + * attached to it, which can also be prioritized or fair. + */ +static int __cvmx_pko3_config_chan_interface(int xiface, unsigned int num_chans, + u8 num_queues, bool prioritized) +{ + int l1_q_num; + int l2_q_base; + enum cvmx_pko3_level_e level; + int res; + int parent_q, child_q; + unsigned int chan, dq; + int pko_mac_num; + u16 ipd_port; + int res_owner, prio; + unsigned int i; + struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); + unsigned int node = xi.node; + char b1[12]; + + if (num_queues == 0) + num_queues = 1; + if ((cvmx_pko3_num_level_queues(CVMX_PKO_DESCR_QUEUES) / num_chans) < 3) + num_queues = 1; + + if (prioritized && num_queues > 1) + prio = num_queues; + else + prio = -1; + + if (debug) + debug("%s: configuring xiface %u:%u with %u chans %u queues each\n", + __func__, xi.node, xi.interface, num_chans, num_queues); + + /* all channels all go to the same mac */ + pko_mac_num = __cvmx_pko3_get_mac_num(xiface, 0); + if (pko_mac_num < 0) { + printf("ERROR: %s: Invalid interface\n", __func__); + return -1; + } + + /* Resources of all channels on this port have common owner */ + ipd_port = cvmx_helper_get_ipd_port(xiface, 0); + + /* Build an identifiable owner */ + res_owner = __cvmx_helper_pko3_res_owner(ipd_port); + + /* Start configuration at L1/PQ */ + level = CVMX_PKO_PORT_QUEUES; + + /* Reserve port queue to make sure the MAC is not already configured */ + l1_q_num = cvmx_pko_alloc_queues(node, level, res_owner, -1, 1); + + if (l1_q_num < 0) { + printf("ERROR: %s: Reserving L1 PQ\n", __func__); + return -1; + } + + res = cvmx_pko3_pq_config(node, pko_mac_num, l1_q_num); + if (res < 0) { + printf("ERROR: %s: Configuring L1 PQ\n", __func__); + return -1; + } + + /* next queue level = L2/SQ */ + level = __cvmx_pko3_sq_lvl_next(level); + + /* allocate level 2 queues, one per channel */ + l2_q_base = + cvmx_pko_alloc_queues(node, level, res_owner, -1, num_chans); + if (l2_q_base < 0) { + printf("ERROR: %s: allocation L2 SQ\n", __func__); + return -1; + } + + /* Configre <num_chans> L2 children for PQ, non-prioritized */ + res = cvmx_pko3_sq_config_children(node, level, l1_q_num, l2_q_base, + num_chans, -1); + + if (res < 0) { + printf("ERROR: %s: Failed channel queues\n", __func__); + return -1; + } + + /* map channels to l2 queues */ + for (chan = 0; chan < num_chans; chan++) { + ipd_port = cvmx_helper_get_ipd_port(xiface, chan); + cvmx_pko3_map_channel(node, l1_q_num, l2_q_base + chan, + ipd_port); + } + + /* next queue level = L3/SQ */ + level = __cvmx_pko3_sq_lvl_next(level); + parent_q = l2_q_base; + + do { + child_q = cvmx_pko_alloc_queues(node, level, res_owner, -1, + num_chans); + + if (child_q < 0) { + printf("ERROR: %s: allocating %s\n", __func__, + __cvmx_pko3_sq_str(b1, level, child_q)); + return -1; + } + + for (i = 0; i < num_chans; i++) { + res = cvmx_pko3_sq_config_children( + node, level, parent_q + i, child_q + i, 1, 1); + + if (res < 0) { + printf("ERROR: %s: configuring %s\n", __func__, + __cvmx_pko3_sq_str(b1, level, child_q)); + return -1; + } + + } /* for i */ + + parent_q = child_q; + level = __cvmx_pko3_sq_lvl_next(level); + + /* Terminate loop on DQ level, it has special handling */ + } while (level != CVMX_PKO_DESCR_QUEUES && + level != CVMX_PKO_LEVEL_INVAL); + + if (level != CVMX_PKO_DESCR_QUEUES) { + printf("ERROR: %s: level sequence error\n", __func__); + return -1; + } + + /* Configure DQs, num_dqs per chan */ + for (chan = 0; chan < num_chans; chan++) { + res = cvmx_pko_alloc_queues(node, level, res_owner, -1, + num_queues); + + if (res < 0) + goto _fail; + dq = res; + + if (OCTEON_IS_MODEL(OCTEON_CN78XX_PASS1_0) && (dq & 7)) + debug("WARNING: %s: DQ# %u not integral of 8\n", + __func__, dq); + + res = cvmx_pko3_sq_config_children(node, level, parent_q + chan, + dq, num_queues, prio); + if (res < 0) + goto _fail; + + /* register DQ range with the translation table */ + res = __cvmx_pko3_ipd_dq_register(xiface, chan, dq, num_queues); + if (res < 0) + goto _fail; + } + + return 0; +_fail: + debug("ERROR: %s: configuring queues for xiface %u:%u chan %u\n", + __func__, xi.node, xi.interface, i); + return -1; +} + +/** Initialize a single Ethernet port with PFC-style channels + * + * One interface can contain multiple ports, this function is per-port + * Here, a physical port is allocated 8 logical channel, one per VLAN + * tag priority, one DQ is assigned to each channel, and all 8 DQs + * are registered for that IPD port. + * Note that the DQs are arrange such that the Ethernet QoS/PCP field + * can be used as an offset to the value returned by cvmx_pko_base_queue_get(). + * + * For HighGig2 mode, 16 channels may be desired, instead of 8, + * but this function does not support that. + */ +static int __cvmx_pko3_config_pfc_interface(int xiface, unsigned int port) +{ + enum cvmx_pko3_level_e level; + int pko_mac_num; + int l1_q_num, l2_q_base; + int child_q, parent_q; + int dq_base; + int res; + const unsigned int num_chans = 8; + cvmx_xiface_t xi = cvmx_helper_xiface_to_node_interface(xiface); + unsigned int node = xi.node; + u16 ipd_port; + int res_owner; + char b1[12]; + unsigned int i; + + if (debug) + debug("%s: configuring xiface %u:%u port %u with %u PFC channels\n", + __func__, node, xi.interface, port, num_chans); + + /* Get MAC number for the iface/port */ + pko_mac_num = __cvmx_pko3_get_mac_num(xiface, port); + if (pko_mac_num < 0) { + printf("ERROR: %s: Invalid interface\n", __func__); + return -1; + } + + ipd_port = cvmx_helper_get_ipd_port(xiface, port); + + /* Build an identifiable owner identifier */ + res_owner = __cvmx_helper_pko3_res_owner(ipd_port); + + level = CVMX_PKO_PORT_QUEUES; + + /* Allocate port queue to make sure the MAC is not already configured */ + l1_q_num = cvmx_pko_alloc_queues(node, level, res_owner, -1, 1); + + if (l1_q_num < 0) { + printf("ERROR: %s: allocation L1 PQ\n", __func__); + return -1; + } + + res = cvmx_pko3_pq_config(xi.node, pko_mac_num, l1_q_num); + if (res < 0) { + printf("ERROR: %s: Configuring %s\n", __func__, + __cvmx_pko3_sq_str(b1, level, l1_q_num)); + return -1; + } + + /* Determine the next queue level */ + level = __cvmx_pko3_sq_lvl_next(level); + + /* Allocate 'num_chans' L2 queues, one per channel */ + l2_q_base = + cvmx_pko_alloc_queues(node, level, res_owner, -1, num_chans); + if (l2_q_base < 0) { + printf("ERROR: %s: allocation L2 SQ\n", __func__); + return -1; + } + + /* Configre <num_chans> L2 children for PQ, with static priority */ + res = cvmx_pko3_sq_config_children(node, level, l1_q_num, l2_q_base, + num_chans, num_chans); + + if (res < 0) { + printf("ERROR: %s: Configuring %s for PFC\n", __func__, + __cvmx_pko3_sq_str(b1, level, l1_q_num)); + return -1; + } + + /* Map each of the allocated channels */ + for (i = 0; i < num_chans; i++) { + u16 chan; + + /* Get CHAN_E value for this PFC channel, PCP in low 3 bits */ + chan = ipd_port | cvmx_helper_prio2qos(i); + + cvmx_pko3_map_channel(node, l1_q_num, l2_q_base + i, chan); + } + + /* Iterate through the levels until DQ and allocate 'num_chans' + * consecutive queues at each level and hook them up + * one-to-one with the parent level queues + */ + + parent_q = l2_q_base; + level = __cvmx_pko3_sq_lvl_next(level); + + do { + child_q = cvmx_pko_alloc_queues(node, level, res_owner, -1, + num_chans); + + if (child_q < 0) { + printf("ERROR: %s: allocating %s\n", __func__, + __cvmx_pko3_sq_str(b1, level, child_q)); + return -1; + } + + for (i = 0; i < num_chans; i++) { + res = cvmx_pko3_sq_config_children( + node, level, parent_q + i, child_q + i, 1, 1); + + if (res < 0) { + printf("ERROR: %s: configuring %s\n", __func__, + __cvmx_pko3_sq_str(b1, level, child_q)); + return -1; + } + + } /* for i */ + + parent_q = child_q; + level = __cvmx_pko3_sq_lvl_next(level); + + /* Terminate loop on DQ level, it has special handling */ + } while (level != CVMX_PKO_DESCR_QUEUES && + level != CVMX_PKO_LEVEL_INVAL); + + if (level != CVMX_PKO_DESCR_QUEUES) { + printf("ERROR: %s: level sequence error\n", __func__); + return -1; + } + + dq_base = cvmx_pko_alloc_queues(node, level, res_owner, -1, num_chans); + if (dq_base < 0) { + printf("ERROR: %s: allocating %s\n", __func__, + __cvmx_pko3_sq_str(b1, level, dq_base)); + return -1; + } + + /* Configure DQs in QoS order, so that QoS/PCP can be index */ + for (i = 0; i < num_chans; i++) { + int dq_num = dq_base + cvmx_helper_prio2qos(i); + + res = cvmx_pko3_sq_config_children(node, level, parent_q + i, + dq_num, 1, 1); + if (res < 0) { + printf("ERROR: %s: configuring %s\n", __func__, + __cvmx_pko3_sq_str(b1, level, dq_num)); + return -1; + } + } + + /* register entire DQ range with the IPD translation table */ + __cvmx_pko3_ipd_dq_register(xiface, port, dq_base, num_chans); + + return 0; +} + +/** + * Initialize a simple interface with a a given number of + * fair or prioritized queues. + * This function will assign one channel per sub-interface. + */ +int __cvmx_pko3_config_gen_interface(int xiface, uint8_t subif, u8 num_queues, + bool prioritized) +{ + cvmx_xiface_t xi = cvmx_helper_xiface_to_node_interface(xiface); + u8 node = xi.node; + int l1_q_num; + int parent_q, child_q; + int dq; + int res, res_owner; + int pko_mac_num; + enum cvmx_pko3_level_e level; + u16 ipd_port; + int static_pri; + char b1[12]; + + num_queues = 1; + + if (num_queues == 0) { + num_queues = 1; + printf("WARNING: %s: xiface %#x misconfigured\n", __func__, + xiface); + } + + /* Configure DQs relative priority (a.k.a. scheduling) */ + if (prioritized) { + /* With 8 queues or fewer, use static priority, else WRR */ + static_pri = (num_queues < 9) ? num_queues : 0; + } else { + /* Set equal-RR scheduling among queues */ + static_pri = -1; + } + + if (debug) + debug("%s: configuring xiface %u:%u/%u nq=%u %s\n", __func__, + xi.node, xi.interface, subif, num_queues, + (prioritized) ? "qos" : "fair"); + + /* Get MAC number for the iface/port */ + pko_mac_num = __cvmx_pko3_get_mac_num(xiface, subif); + if (pko_mac_num < 0) { + printf("ERROR: %s: Invalid interface %u:%u\n", __func__, + xi.node, xi.interface); + return -1; + } + + ipd_port = cvmx_helper_get_ipd_port(xiface, subif); + + if (debug) + debug("%s: xiface %u:%u/%u ipd_port=%#03x\n", __func__, xi.node, + xi.interface, subif, ipd_port); + + /* Build an identifiable owner identifier */ + res_owner = __cvmx_helper_pko3_res_owner(ipd_port); + + level = CVMX_PKO_PORT_QUEUES; + + /* Reserve port queue to make sure the MAC is not already configured */ + l1_q_num = cvmx_pko_alloc_queues(node, level, res_owner, -1, 1); + + if (l1_q_num < 0) { + printf("ERROR %s: xiface %u:%u/%u failed allocation L1 PQ\n", + __func__, xi.node, xi.interface, subif); + return -1; + } + + res = cvmx_pko3_pq_config(node, pko_mac_num, l1_q_num); + if (res < 0) { + printf("ERROR %s: Configuring L1 PQ\n", __func__); + return -1; + } + + parent_q = l1_q_num; + + /* Determine the next queue level */ + level = __cvmx_pko3_sq_lvl_next(level); + + /* Simply chain queues 1-to-1 from L2 to one before DQ level */ + do { + /* allocate next level queue */ + child_q = cvmx_pko_alloc_queues(node, level, res_owner, -1, 1); + + if (child_q < 0) { + printf("ERROR: %s: allocating %s\n", __func__, + __cvmx_pko3_sq_str(b1, level, child_q)); + return -1; + } + + /* Configre newly allocated queue */ + res = cvmx_pko3_sq_config_children(node, level, parent_q, + child_q, 1, 1); + + if (res < 0) { + printf("ERROR: %s: configuring %s\n", __func__, + __cvmx_pko3_sq_str(b1, level, child_q)); + return -1; + } + + /* map IPD/channel to L2/L3 queues */ + if (level == cvmx_pko_default_channel_level) + cvmx_pko3_map_channel(node, l1_q_num, child_q, + ipd_port); + + /* Prepare for next level */ + level = __cvmx_pko3_sq_lvl_next(level); + parent_q = child_q; + + /* Terminate loop on DQ level, it has special handling */ + } while (level != CVMX_PKO_DESCR_QUEUES && + level != CVMX_PKO_LEVEL_INVAL); + + if (level != CVMX_PKO_DESCR_QUEUES) { + printf("ERROR: %s: level sequence error\n", __func__); + return -1; + } + + /* Allocate descriptor queues for the port */ + dq = cvmx_pko_alloc_queues(node, level, res_owner, -1, num_queues); + if (dq < 0) { + printf("ERROR: %s: could not reserve DQs\n", __func__); + return -1; + } + + res = cvmx_pko3_sq_config_children(node, level, parent_q, dq, + num_queues, static_pri); + if (res < 0) { + printf("ERROR: %s: configuring %s\n", __func__, + __cvmx_pko3_sq_str(b1, level, dq)); + return -1; + } + + /* register DQ/IPD translation */ + __cvmx_pko3_ipd_dq_register(xiface, subif, dq, num_queues); + + if (debug) + debug("%s: xiface %u:%u/%u qs %u-%u\n", __func__, xi.node, + xi.interface, subif, dq, dq + num_queues - 1); + return 0; +} + +/** Initialize the NULL interface + * + * A NULL interface is a special case in that it is not + * one of the enumerated interfaces in the system, and does + * not apply to input either. Still, it can be very handy + * for dealing with packets that should be discarded in + * a generic, streamlined way. + * + * The Descriptor Queue 0 will be reserved for the NULL interface + * and the normalized (i.e. IPD) port number has the all-ones value. + */ +static int __cvmx_pko3_config_null_interface(unsigned int node) +{ + int l1_q_num; + int parent_q, child_q; + enum cvmx_pko3_level_e level; + int i, res, res_owner; + int xiface, ipd_port; + int num_dq = 1; /* # of DQs for NULL */ + const int dq = 0; /* Reserve DQ#0 for NULL */ + char pko_mac_num; + char b1[12]; + + if (OCTEON_IS_MODEL(OCTEON_CN78XX)) + pko_mac_num = 0x1C; /* MAC# 28 virtual MAC for NULL */ + else if (OCTEON_IS_MODEL(OCTEON_CN73XX)) + pko_mac_num = 0x0F; /* MAC# 16 virtual MAC for NULL */ + else if (OCTEON_IS_MODEL(OCTEON_CNF75XX)) + pko_mac_num = 0x0A; /* MAC# 10 virtual MAC for NULL */ + else + return -1; + + if (OCTEON_IS_MODEL(OCTEON_CN78XX_PASS1_0)) + num_dq = 8; + + if (debug) + debug("%s: null iface dq=%u-%u\n", __func__, dq, + dq + num_dq - 1); + + ipd_port = cvmx_helper_node_to_ipd_port(node, CVMX_PKO3_IPD_PORT_NULL); + + /* Build an identifiable owner identifier by MAC# for easy release */ + res_owner = __cvmx_helper_pko3_res_owner(ipd_port); + if (res_owner < 0) { + debug("%s: ERROR Invalid interface\n", __func__); + return -1; + } + + level = CVMX_PKO_PORT_QUEUES; + + /* Allocate a port queue */ + l1_q_num = cvmx_pko_alloc_queues(node, level, res_owner, -1, 1); + + if (l1_q_num < 0) { + debug("%s: ERROR reserving L1 SQ\n", __func__); + return -1; + } + + res = cvmx_pko3_pq_config(node, pko_mac_num, l1_q_num); + if (res < 0) { + printf("ERROR: %s: PQ/L1 queue configuration\n", __func__); + return -1; + } + + parent_q = l1_q_num; + + /* Determine the next queue level */ + level = __cvmx_pko3_sq_lvl_next(level); + + /* Simply chain queues 1-to-1 from L2 to one before DQ level */ + do { + /* allocate next level queue */ + child_q = cvmx_pko_alloc_queues(node, level, res_owner, -1, 1); + + if (child_q < 0) { + printf("ERROR: %s: allocating %s\n", __func__, + __cvmx_pko3_sq_str(b1, level, child_q)); + return -1; + } + + /* Configre newly allocated queue */ + res = cvmx_pko3_sq_config_children(node, level, parent_q, + child_q, 1, 1); + + if (res < 0) { + printf("ERROR: %s: configuring %s\n", __func__, + __cvmx_pko3_sq_str(b1, level, child_q)); + return -1; + } + + /* Prepare for next level */ + level = __cvmx_pko3_sq_lvl_next(level); + parent_q = child_q; + + /* Terminate loop on DQ level, it has special handling */ + } while (level != CVMX_PKO_DESCR_QUEUES && + level != CVMX_PKO_LEVEL_INVAL); + + if (level != CVMX_PKO_DESCR_QUEUES) { + printf("ERROR: %s: level sequence error\n", __func__); + return -1; + } + + /* Reserve 'num_dq' DQ's at 0 by convention */ + res = cvmx_pko_alloc_queues(node, level, res_owner, dq, num_dq); + if (dq != res) { + debug("%s: ERROR: could not reserve DQs\n", __func__); + return -1; + } + + res = cvmx_pko3_sq_config_children(node, level, parent_q, dq, num_dq, + num_dq); + if (res < 0) { + printf("ERROR: %s: configuring %s\n", __func__, + __cvmx_pko3_sq_str(b1, level, dq)); + return -1; + } + + /* NULL interface does not need to map to a CHAN_E */ + + /* register DQ/IPD translation */ + xiface = cvmx_helper_node_interface_to_xiface(node, __CVMX_XIFACE_NULL); + __cvmx_pko3_ipd_dq_register(xiface, 0, dq, num_dq); + + /* open the null DQs here */ + for (i = 0; i < num_dq; i++) { + unsigned int limit = 128; /* NULL never really uses much */ + + cvmx_pko_dq_open(node, dq + i); + cvmx_pko3_dq_set_limit(node, dq + i, limit); + } + + return 0; +} + +/** Open all descriptor queues belonging to an interface/port + * @INTERNAL + */ +int __cvmx_pko3_helper_dqs_activate(int xiface, int index, bool min_pad) +{ + int ipd_port, dq_base, dq_count, i; + struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); + unsigned int limit; + + /* Get local IPD port for the interface */ + ipd_port = cvmx_helper_get_ipd_port(xiface, index); + if (ipd_port < 0) { + printf("ERROR: %s: No IPD port for interface %d port %d\n", + __func__, xiface, index); + return -1; + } + + /* Get DQ# range for the IPD port */ + dq_base = cvmx_pko3_get_queue_base(ipd_port); + dq_count = cvmx_pko3_get_queue_num(ipd_port); + if (dq_base < 0 || dq_count <= 0) { + printf("ERROR: %s: No descriptor queues for interface %d port %d\n", + __func__, xiface, index); + return -1; + } + + /* Mask out node from global DQ# */ + dq_base &= (1 << 10) - 1; + + limit = __pko_pkt_quota / dq_count / + cvmx_helper_interface_enumerate(xiface); + + for (i = 0; i < dq_count; i++) { + /* FIXME: 2ms at 1Gbps max packet rate, make speed dependent */ + cvmx_pko_dq_open(xi.node, dq_base + i); + cvmx_pko3_dq_options(xi.node, dq_base + i, min_pad); + + if (debug) + debug("%s: DQ%u limit %d\n", __func__, dq_base + i, + limit); + + cvmx_pko3_dq_set_limit(xi.node, dq_base + i, limit); + __pko_pkt_budget -= limit; + } + + if (__pko_pkt_budget < 0) + printf("WARNING: %s: PKO buffer deficit %d\n", __func__, + __pko_pkt_budget); + else if (debug) + debug("%s: PKO remaining packet budget: %d\n", __func__, + __pko_pkt_budget); + + return i; +} + +/** Configure and initialize PKO3 for an interface + * + * @param xiface is the interface number to configure + * @return 0 on success. + */ +int cvmx_helper_pko3_init_interface(int xiface) +{ + cvmx_helper_interface_mode_t mode; + int node, iface, subif, num_ports; + bool fcs_enable, pad_enable, pad_enable_pko; + u8 fcs_sof_off = 0; + u8 num_queues = 1; + bool qos = false, pfc = false; + int res = -1; + cvmx_xiface_t xi = cvmx_helper_xiface_to_node_interface(xiface); + + node = xi.node; + iface = xi.interface; + mode = cvmx_helper_interface_get_mode(xiface); + num_ports = cvmx_helper_interface_enumerate(xiface); + subif = 0; + + if ((unsigned int)iface < + NUM_ELEMENTS(__cvmx_pko_queue_static_config[node].pknd.pko_cfg_iface)) { + pfc = __cvmx_pko_queue_static_config[node] + .pknd.pko_cfg_iface[iface] + .pfc_enable; + num_queues = __cvmx_pko_queue_static_config[node] + .pknd.pko_cfg_iface[iface] + .queues_per_port; + qos = __cvmx_pko_queue_static_config[node] + .pknd.pko_cfg_iface[iface] + .qos_enable; + } + + /* Force 8 DQs per port for pass 1.0 to circumvent limitations */ + if (OCTEON_IS_MODEL(OCTEON_CN78XX_PASS1_0)) + num_queues = 8; + + /* For ILK there is one IPD port per channel */ + if (mode == CVMX_HELPER_INTERFACE_MODE_ILK) + num_ports = __cvmx_helper_ilk_enumerate(xiface); + + /* Skip non-existent interfaces */ + if (num_ports < 1) { + debug("ERROR: %s: invalid iface %u:%u\n", __func__, node, + iface); + return -1; + } + + if (mode == CVMX_HELPER_INTERFACE_MODE_LOOP) { + num_queues = __cvmx_pko_queue_static_config[node] + .pknd.pko_cfg_loop.queues_per_port; + qos = __cvmx_pko_queue_static_config[node] + .pknd.pko_cfg_loop.qos_enable; + + if (OCTEON_IS_MODEL(OCTEON_CN78XX_PASS1_0)) + num_queues = 8; + + res = __cvmx_pko3_config_chan_interface(xiface, num_ports, + num_queues, qos); + if (res < 0) + goto __cfg_error; + } else if (mode == CVMX_HELPER_INTERFACE_MODE_NPI) { + num_queues = __cvmx_pko_queue_static_config[node] + .pknd.pko_cfg_npi.queues_per_port; + qos = __cvmx_pko_queue_static_config[node] + .pknd.pko_cfg_npi.qos_enable; + + if (OCTEON_IS_MODEL(OCTEON_CN78XX_PASS1_0)) + num_queues = 8; + + res = __cvmx_pko3_config_chan_interface(xiface, num_ports, + num_queues, qos); + if (res < 0) + goto __cfg_error; + } + /* ILK-specific queue configuration */ + else if (mode == CVMX_HELPER_INTERFACE_MODE_ILK) { + unsigned int num_chans = __cvmx_helper_ilk_enumerate(xiface); + + num_queues = 8; + qos = true; + pfc = false; + + if (num_chans >= 128) + num_queues = 1; + else if (num_chans >= 64) + num_queues = 2; + else if (num_chans >= 32) + num_queues = 4; + else + num_queues = 8; + + res = __cvmx_pko3_config_chan_interface(xiface, num_chans, + num_queues, qos); + } + /* Setup all ethernet configured for PFC */ + else if (pfc) { + /* PFC interfaces have 8 prioritized queues */ + for (subif = 0; subif < num_ports; subif++) { + res = __cvmx_pko3_config_pfc_interface(xiface, subif); + if (res < 0) + goto __cfg_error; + + /* Enable PFC/CBFC on BGX */ + __cvmx_helper_bgx_xaui_config_pfc(node, iface, subif, + true); + } + } else { + /* All other interfaces follow static configuration */ + for (subif = 0; subif < num_ports; subif++) { + res = __cvmx_pko3_config_gen_interface(xiface, subif, + num_queues, qos); + if (res < 0) + goto __cfg_error; + } + } + + fcs_enable = __cvmx_helper_get_has_fcs(xiface); + pad_enable = __cvmx_helper_get_pko_padding(xiface); + + /* Do not use PKO PAD/FCS generation on o78p1.x on BGX interfaces */ + if (OCTEON_IS_MODEL(OCTEON_CN78XX_PASS1_X)) + pad_enable_pko = false; + else + pad_enable_pko = pad_enable; + + if (debug) + debug("%s: iface %u:%u FCS=%d pad=%d pko=%d\n", __func__, node, + iface, fcs_enable, pad_enable, pad_enable_pko); + + /* Setup interface options */ + for (subif = 0; subif < num_ports; subif++) { + /* Open interface/port DQs to allow transmission to begin */ + res = __cvmx_pko3_helper_dqs_activate(xiface, subif, + pad_enable_pko); + + if (res < 0) + goto __cfg_error; + + /* ILK has only one MAC, subif == logical-channel */ + if (mode == CVMX_HELPER_INTERFACE_MODE_ILK && subif > 0) + continue; + + /* LOOP has only one MAC, subif == logical-channel */ + if (mode == CVMX_HELPER_INTERFACE_MODE_LOOP && subif > 0) + continue; + + /* NPI has only one MAC, subif == 'ring' */ + if (mode == CVMX_HELPER_INTERFACE_MODE_NPI && subif > 0) + continue; + + /* for sRIO there is 16 byte sRIO header, outside of FCS */ + if (mode == CVMX_HELPER_INTERFACE_MODE_SRIO) + fcs_sof_off = 16; + + if (iface >= CVMX_HELPER_MAX_GMX) { + /* Non-BGX interface, use PKO for FCS/PAD */ + res = cvmx_pko3_interface_options(xiface, subif, + fcs_enable, + pad_enable_pko, + fcs_sof_off); + } else if (pad_enable == pad_enable_pko) { + /* BGX interface: FCS/PAD done by PKO */ + res = cvmx_pko3_interface_options(xiface, subif, + fcs_enable, + pad_enable, + fcs_sof_off); + cvmx_helper_bgx_tx_options(node, iface, subif, false, + false); + } else { + /* BGX interface: FCS/PAD done by BGX */ + res = cvmx_pko3_interface_options(xiface, subif, false, + false, fcs_sof_off); + cvmx_helper_bgx_tx_options(node, iface, subif, + fcs_enable, pad_enable); + } + + if (res < 0) + debug("WARNING: %s: option set failed on iface %u:%u/%u\n", + __func__, node, iface, subif); + if (debug) + debug("%s: face %u:%u/%u fifo size %d\n", __func__, + node, iface, subif, + cvmx_pko3_port_fifo_size(xiface, subif)); + } + return 0; + +__cfg_error: + debug("ERROR: %s: failed on iface %u:%u/%u\n", __func__, node, iface, + subif); + return -1; +} + +/** + * Global initialization for PKO3 + * + * Should only be called once on each node + * + * TBD: Resolve the kernel case. + * When Linux eats up the entire memory, bootmem will be unable to + * satisfy our request, and the memory needs to come from Linux free pages. + */ +int __cvmx_helper_pko3_init_global(unsigned int node, uint16_t gaura) +{ + int res; + + res = cvmx_pko3_hw_init_global(node, gaura); + if (res < 0) { + debug("ERROR: %s:failed block initialization\n", __func__); + return res; + } + + /* configure channel level */ + cvmx_pko3_channel_credit_level(node, cvmx_pko_default_channel_level); + + /* add NULL MAC/DQ setup */ + res = __cvmx_pko3_config_null_interface(node); + if (res < 0) + debug("ERROR: %s: creating NULL interface\n", __func__); + + return res; +} + +/** + * Global initialization for PKO3 + * + * Should only be called once on each node + * + * When Linux eats up the entire memory, bootmem will be unable to + * satisfy our request, and the memory needs to come from Linux free pages. + */ +int cvmx_helper_pko3_init_global(unsigned int node) +{ + void *ptr; + int res = -1; + unsigned int aura_num = ~0; + cvmx_fpa3_gaura_t aura; + + /* Allocate memory required by PKO3 */ + res = __cvmx_pko3_config_memory(node); + if (res < 0) { + debug("ERROR: %s: PKO3 memory allocation error\n", __func__); + return res; + } + + aura_num = res; + aura = __cvmx_pko3_aura[node]; + + /* Exercise the FPA to make sure the AURA is functional */ + ptr = cvmx_fpa3_alloc(aura); + + if (!ptr) { + res = -1; + } else { + cvmx_fpa3_free_nosync(ptr, aura, 0); + res = 0; + } + + if (res < 0) { + debug("ERROR: %s: FPA failure AURA=%u:%d\n", __func__, + aura.node, aura.laura); + return -1; + } + + res = __cvmx_helper_pko3_init_global(node, aura_num); + + if (res < 0) + debug("ERROR: %s: failed to start PPKO\n", __func__); + + return res; +} diff --git a/arch/mips/mach-octeon/cvmx-helper-rgmii.c b/arch/mips/mach-octeon/cvmx-helper-rgmii.c new file mode 100644 index 0000000..dc19c3b --- /dev/null +++ b/arch/mips/mach-octeon/cvmx-helper-rgmii.c @@ -0,0 +1,398 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018-2022 Marvell International Ltd. + * + * Functions for RGMII/GMII/MII initialization, configuration, + * and monitoring. + */ + +#include <log.h> +#include <time.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-helper.h> +#include <mach/cvmx-helper-board.h> + +#include <mach/cvmx-hwpko.h> + +#include <mach/cvmx-asxx-defs.h> +#include <mach/cvmx-dbg-defs.h> +#include <mach/cvmx-gmxx-defs.h> +#include <mach/cvmx-npi-defs.h> +#include <mach/cvmx-pko-defs.h> + +/** + * @INTERNAL + * Probe RGMII ports and determine the number present + * + * @param xiface Interface to probe + * + * @return Number of RGMII/GMII/MII ports (0-4). + */ +int __cvmx_helper_rgmii_probe(int xiface) +{ + struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); + int num_ports = 0; + union cvmx_gmxx_inf_mode mode; + + mode.u64 = csr_rd(CVMX_GMXX_INF_MODE(xi.interface)); + + if (mode.s.type) + debug("ERROR: Unsupported Octeon model in %s\n", __func__); + else + debug("ERROR: Unsupported Octeon model in %s\n", __func__); + return num_ports; +} + +/** + * @INTERNAL + * Configure all of the ASX, GMX, and PKO regsiters required + * to get RGMII to function on the supplied interface. + * + * @param xiface PKO Interface to configure (0 or 1) + * + * @return Zero on success + */ +int __cvmx_helper_rgmii_enable(int xiface) +{ + struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); + int interface = xi.interface; + int num_ports = cvmx_helper_ports_on_interface(interface); + int port; + union cvmx_gmxx_inf_mode mode; + union cvmx_asxx_tx_prt_en asx_tx; + union cvmx_asxx_rx_prt_en asx_rx; + + mode.u64 = csr_rd(CVMX_GMXX_INF_MODE(interface)); + + if (num_ports == -1) + return -1; + if (mode.s.en == 0) + return -1; + + /* Configure the ASX registers needed to use the RGMII ports */ + asx_tx.u64 = 0; + asx_tx.s.prt_en = cvmx_build_mask(num_ports); + csr_wr(CVMX_ASXX_TX_PRT_EN(interface), asx_tx.u64); + + asx_rx.u64 = 0; + asx_rx.s.prt_en = cvmx_build_mask(num_ports); + csr_wr(CVMX_ASXX_RX_PRT_EN(interface), asx_rx.u64); + + /* Configure the GMX registers needed to use the RGMII ports */ + for (port = 0; port < num_ports; port++) { + /* + * Configure more flexible RGMII preamble + * checking. Pass 1 doesn't support this feature. + */ + union cvmx_gmxx_rxx_frm_ctl frm_ctl; + + frm_ctl.u64 = csr_rd(CVMX_GMXX_RXX_FRM_CTL(port, interface)); + /* New field, so must be compile time */ + frm_ctl.s.pre_free = 1; + csr_wr(CVMX_GMXX_RXX_FRM_CTL(port, interface), frm_ctl.u64); + + /* + * Each pause frame transmitted will ask for about 10M + * bit times before resume. If buffer space comes + * available before that time has expired, an XON + * pause frame (0 time) will be transmitted to restart + * the flow. + */ + csr_wr(CVMX_GMXX_TXX_PAUSE_PKT_TIME(port, interface), 20000); + csr_wr(CVMX_GMXX_TXX_PAUSE_PKT_INTERVAL(port, interface), + 19000); + + csr_wr(CVMX_ASXX_TX_CLK_SETX(port, interface), 24); + csr_wr(CVMX_ASXX_RX_CLK_SETX(port, interface), 24); + } + + __cvmx_helper_setup_gmx(interface, num_ports); + + /* enable the ports now */ + for (port = 0; port < num_ports; port++) { + union cvmx_gmxx_prtx_cfg gmx_cfg; + + cvmx_helper_link_autoconf( + cvmx_helper_get_ipd_port(interface, port)); + gmx_cfg.u64 = csr_rd(CVMX_GMXX_PRTX_CFG(port, interface)); + gmx_cfg.s.en = 1; + csr_wr(CVMX_GMXX_PRTX_CFG(port, interface), gmx_cfg.u64); + } + return 0; +} + +/** + * @INTERNAL + * Return the link state of an IPD/PKO port as returned by + * auto negotiation. The result of this function may not match + * Octeon's link config if auto negotiation has changed since + * the last call to cvmx_helper_link_set(). + * + * @param ipd_port IPD/PKO port to query + * + * @return Link state + */ +cvmx_helper_link_info_t __cvmx_helper_rgmii_link_get(int ipd_port) +{ + int interface = cvmx_helper_get_interface_num(ipd_port); + int index = cvmx_helper_get_interface_index_num(ipd_port); + union cvmx_asxx_prt_loop asxx_prt_loop; + + asxx_prt_loop.u64 = csr_rd(CVMX_ASXX_PRT_LOOP(interface)); + if (asxx_prt_loop.s.int_loop & (1 << index)) { + /* Force 1Gbps full duplex on internal loopback */ + cvmx_helper_link_info_t result; + + result.u64 = 0; + result.s.full_duplex = 1; + result.s.link_up = 1; + result.s.speed = 1000; + return result; + } else { + return __cvmx_helper_board_link_get(ipd_port); + } +} + +/** + * @INTERNAL + * Return the link state of an IPD/PKO port as returned by + * auto negotiation. The result of this function may not match + * Octeon's link config if auto negotiation has changed since + * the last call to cvmx_helper_link_set(). + * + * @param ipd_port IPD/PKO port to query + * + * @return Link state + */ +cvmx_helper_link_info_t __cvmx_helper_gmii_link_get(int ipd_port) +{ + cvmx_helper_link_info_t result; + int index = cvmx_helper_get_interface_index_num(ipd_port); + + if (index == 0) { + result = __cvmx_helper_rgmii_link_get(ipd_port); + } else { + result.s.full_duplex = 1; + result.s.link_up = 1; + result.s.speed = 1000; + } + + return result; +} + +/** + * @INTERNAL + * Configure an IPD/PKO port for the specified link state. This + * function does not influence auto negotiation at the PHY level. + * The passed link state must always match the link state returned + * by cvmx_helper_link_get(). It is normally best to use + * cvmx_helper_link_autoconf() instead. + * + * @param ipd_port IPD/PKO port to configure + * @param link_info The new link state + * + * @return Zero on success, negative on failure + */ +int __cvmx_helper_rgmii_link_set(int ipd_port, + cvmx_helper_link_info_t link_info) +{ + int result = 0; + int interface = cvmx_helper_get_interface_num(ipd_port); + int index = cvmx_helper_get_interface_index_num(ipd_port); + union cvmx_gmxx_prtx_cfg original_gmx_cfg; + union cvmx_gmxx_prtx_cfg new_gmx_cfg; + union cvmx_pko_mem_queue_qos pko_mem_queue_qos; + union cvmx_pko_mem_queue_qos pko_mem_queue_qos_save[16]; + union cvmx_gmxx_tx_ovr_bp gmx_tx_ovr_bp; + union cvmx_gmxx_tx_ovr_bp gmx_tx_ovr_bp_save; + int i; + + /* Read the current settings so we know the current enable state */ + original_gmx_cfg.u64 = csr_rd(CVMX_GMXX_PRTX_CFG(index, interface)); + new_gmx_cfg = original_gmx_cfg; + + /* Disable the lowest level RX */ + csr_wr(CVMX_ASXX_RX_PRT_EN(interface), + csr_rd(CVMX_ASXX_RX_PRT_EN(interface)) & ~(1 << index)); + + memset(pko_mem_queue_qos_save, 0, sizeof(pko_mem_queue_qos_save)); + /* Disable all queues so that TX should become idle */ + for (i = 0; i < cvmx_pko_get_num_queues(ipd_port); i++) { + int queue = cvmx_pko_get_base_queue(ipd_port) + i; + + csr_wr(CVMX_PKO_REG_READ_IDX, queue); + pko_mem_queue_qos.u64 = csr_rd(CVMX_PKO_MEM_QUEUE_QOS); + pko_mem_queue_qos.s.pid = ipd_port; + pko_mem_queue_qos.s.qid = queue; + pko_mem_queue_qos_save[i] = pko_mem_queue_qos; + pko_mem_queue_qos.s.qos_mask = 0; + csr_wr(CVMX_PKO_MEM_QUEUE_QOS, pko_mem_queue_qos.u64); + } + + /* Disable backpressure */ + gmx_tx_ovr_bp.u64 = csr_rd(CVMX_GMXX_TX_OVR_BP(interface)); + gmx_tx_ovr_bp_save = gmx_tx_ovr_bp; + gmx_tx_ovr_bp.s.bp &= ~(1 << index); + gmx_tx_ovr_bp.s.en |= 1 << index; + csr_wr(CVMX_GMXX_TX_OVR_BP(interface), gmx_tx_ovr_bp.u64); + csr_rd(CVMX_GMXX_TX_OVR_BP(interface)); + + /* + * Poll the GMX state machine waiting for it to become + * idle. Preferably we should only change speed when it is + * idle. If it doesn't become idle we will still do the speed + * change, but there is a slight chance that GMX will + * lockup. + */ + csr_wr(CVMX_NPI_DBG_SELECT, interface * 0x800 + index * 0x100 + 0x880); + CVMX_WAIT_FOR_FIELD64(CVMX_DBG_DATA, cvmx_dbg_data_t, data & 7, ==, 0, + 10000); + CVMX_WAIT_FOR_FIELD64(CVMX_DBG_DATA, cvmx_dbg_data_t, data & 0xf, ==, 0, + 10000); + + /* Disable the port before we make any changes */ + new_gmx_cfg.s.en = 0; + csr_wr(CVMX_GMXX_PRTX_CFG(index, interface), new_gmx_cfg.u64); + csr_rd(CVMX_GMXX_PRTX_CFG(index, interface)); + + /* Set full/half duplex */ + if (!link_info.s.link_up) + /* Force full duplex on down links */ + new_gmx_cfg.s.duplex = 1; + else + new_gmx_cfg.s.duplex = link_info.s.full_duplex; + + /* Set the link speed. Anything unknown is set to 1Gbps */ + if (link_info.s.speed == 10) { + new_gmx_cfg.s.slottime = 0; + new_gmx_cfg.s.speed = 0; + } else if (link_info.s.speed == 100) { + new_gmx_cfg.s.slottime = 0; + new_gmx_cfg.s.speed = 0; + } else { + new_gmx_cfg.s.slottime = 1; + new_gmx_cfg.s.speed = 1; + } + + /* Adjust the clocks */ + if (link_info.s.speed == 10) { + csr_wr(CVMX_GMXX_TXX_CLK(index, interface), 50); + csr_wr(CVMX_GMXX_TXX_SLOT(index, interface), 0x40); + csr_wr(CVMX_GMXX_TXX_BURST(index, interface), 0); + } else if (link_info.s.speed == 100) { + csr_wr(CVMX_GMXX_TXX_CLK(index, interface), 5); + csr_wr(CVMX_GMXX_TXX_SLOT(index, interface), 0x40); + csr_wr(CVMX_GMXX_TXX_BURST(index, interface), 0); + } else { + csr_wr(CVMX_GMXX_TXX_CLK(index, interface), 1); + csr_wr(CVMX_GMXX_TXX_SLOT(index, interface), 0x200); + csr_wr(CVMX_GMXX_TXX_BURST(index, interface), 0x2000); + } + + /* Do a read to make sure all setup stuff is complete */ + csr_rd(CVMX_GMXX_PRTX_CFG(index, interface)); + + /* Save the new GMX setting without enabling the port */ + csr_wr(CVMX_GMXX_PRTX_CFG(index, interface), new_gmx_cfg.u64); + + /* Enable the lowest level RX */ + if (link_info.s.link_up) + csr_wr(CVMX_ASXX_RX_PRT_EN(interface), + csr_rd(CVMX_ASXX_RX_PRT_EN(interface)) | (1 << index)); + + /* Re-enable the TX path */ + for (i = 0; i < cvmx_pko_get_num_queues(ipd_port); i++) { + int queue = cvmx_pko_get_base_queue(ipd_port) + i; + + csr_wr(CVMX_PKO_REG_READ_IDX, queue); + csr_wr(CVMX_PKO_MEM_QUEUE_QOS, pko_mem_queue_qos_save[i].u64); + } + + /* Restore backpressure */ + csr_wr(CVMX_GMXX_TX_OVR_BP(interface), gmx_tx_ovr_bp_save.u64); + + /* Restore the GMX enable state. Port config is complete */ + new_gmx_cfg.s.en = original_gmx_cfg.s.en; + csr_wr(CVMX_GMXX_PRTX_CFG(index, interface), new_gmx_cfg.u64); + + return result; +} + +/** + * @INTERNAL + * Configure a port for internal and/or external loopback. Internal loopback + * causes packets sent by the port to be received by Octeon. External loopback + * causes packets received from the wire to sent out again. + * + * @param ipd_port IPD/PKO port to loopback. + * @param enable_internal + * Non zero if you want internal loopback + * @param enable_external + * Non zero if you want external loopback + * + * @return Zero on success, negative on failure. + */ +int __cvmx_helper_rgmii_configure_loopback(int ipd_port, int enable_internal, + int enable_external) +{ + int interface = cvmx_helper_get_interface_num(ipd_port); + int index = cvmx_helper_get_interface_index_num(ipd_port); + int original_enable; + union cvmx_gmxx_prtx_cfg gmx_cfg; + union cvmx_asxx_prt_loop asxx_prt_loop; + + /* Read the current enable state and save it */ + gmx_cfg.u64 = csr_rd(CVMX_GMXX_PRTX_CFG(index, interface)); + original_enable = gmx_cfg.s.en; + /* Force port to be disabled */ + gmx_cfg.s.en = 0; + if (enable_internal) { + /* Force speed if we're doing internal loopback */ + gmx_cfg.s.duplex = 1; + gmx_cfg.s.slottime = 1; + gmx_cfg.s.speed = 1; + csr_wr(CVMX_GMXX_TXX_CLK(index, interface), 1); + csr_wr(CVMX_GMXX_TXX_SLOT(index, interface), 0x200); + csr_wr(CVMX_GMXX_TXX_BURST(index, interface), 0x2000); + } + csr_wr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64); + + /* Set the loopback bits */ + asxx_prt_loop.u64 = csr_rd(CVMX_ASXX_PRT_LOOP(interface)); + if (enable_internal) + asxx_prt_loop.s.int_loop |= 1 << index; + else + asxx_prt_loop.s.int_loop &= ~(1 << index); + if (enable_external) + asxx_prt_loop.s.ext_loop |= 1 << index; + else + asxx_prt_loop.s.ext_loop &= ~(1 << index); + csr_wr(CVMX_ASXX_PRT_LOOP(interface), asxx_prt_loop.u64); + + /* Force enables in internal loopback */ + if (enable_internal) { + u64 tmp; + + tmp = csr_rd(CVMX_ASXX_TX_PRT_EN(interface)); + csr_wr(CVMX_ASXX_TX_PRT_EN(interface), (1 << index) | tmp); + tmp = csr_rd(CVMX_ASXX_RX_PRT_EN(interface)); + csr_wr(CVMX_ASXX_RX_PRT_EN(interface), (1 << index) | tmp); + original_enable = 1; + } + + /* Restore the enable state */ + gmx_cfg.s.en = original_enable; + csr_wr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64); + return 0; +} diff --git a/arch/mips/mach-octeon/cvmx-helper-sfp.c b/arch/mips/mach-octeon/cvmx-helper-sfp.c new file mode 100644 index 0000000..a08a6cf --- /dev/null +++ b/arch/mips/mach-octeon/cvmx-helper-sfp.c @@ -0,0 +1,1309 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018-2022 Marvell International Ltd. + */ + +#include <errno.h> +#include <i2c.h> +#include <log.h> +#include <malloc.h> +#include <linux/delay.h> +#include <display_options.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-helper.h> +#include <mach/cvmx-helper-board.h> +#include <mach/cvmx-helper-fdt.h> +#include <mach/cvmx-helper-cfg.h> +#include <mach/cvmx-helper-gpio.h> +#include <mach/cvmx-helper-util.h> + +extern void octeon_i2c_unblock(int bus); + +static struct cvmx_fdt_sfp_info *sfp_list; + +/** + * Local allocator to handle both SE and U-Boot that also zeroes out memory + * + * @param size number of bytes to allocate + * + * @return pointer to allocated memory or NULL if out of memory. + * Alignment is set to 8-bytes. + */ +static void *cvm_sfp_alloc(size_t size) +{ + return calloc(size, 1); +} + +/** + * Free allocated memory. + * + * @param ptr pointer to memory to free + * + * NOTE: This only works in U-Boot since SE does not really have a freeing + * mechanism. In SE the memory is zeroed out and not freed so this + * is a memory leak if errors occur. + */ +static inline void cvm_sfp_free(void *ptr, size_t size) +{ + free(ptr); +} + +/** + * Select a QSFP device before accessing the EEPROM + * + * @param sfp handle for sfp/qsfp connector + * @param enable Set true to select, false to deselect + * + * @return 0 on success or if SFP or no select GPIO, -1 on GPIO error + */ +static int cvmx_qsfp_select(const struct cvmx_fdt_sfp_info *sfp, bool enable) +{ + /* Select is only needed for QSFP modules */ + if (!sfp->is_qsfp) { + debug("%s(%s, %d): not QSFP\n", __func__, sfp->name, enable); + return 0; + } + + if (dm_gpio_is_valid(&sfp->select)) { + /* Note that select is active low */ + return dm_gpio_set_value(&sfp->select, !enable); + } + + debug("%s: select GPIO unknown\n", __func__); + return 0; +} + +static int cvmx_sfp_parse_sfp_buffer(struct cvmx_sfp_mod_info *sfp_info, + const uint8_t *buffer) +{ + u8 csum = 0; + bool csum_good = false; + int i; + + /* Validate the checksum */ + for (i = 0; i < 0x3f; i++) + csum += buffer[i]; + csum_good = csum == buffer[0x3f]; + debug("%s: Lower checksum: 0x%02x, expected: 0x%02x\n", __func__, csum, + buffer[0x3f]); + csum = 0; + for (i = 0x40; i < 0x5f; i++) + csum += buffer[i]; + debug("%s: Upper checksum: 0x%02x, expected: 0x%02x\n", __func__, csum, + buffer[0x5f]); + if (csum != buffer[0x5f] || !csum_good) { + debug("Error: SFP EEPROM checksum information is incorrect\n"); + return -1; + } + + sfp_info->conn_type = buffer[0]; + if (buffer[1] < 1 || buffer[1] > 7) { /* Extended ID */ + debug("Error: Unknown SFP extended identifier 0x%x\n", + buffer[1]); + return -1; + } + if (buffer[1] != 4) { + debug("Module is not SFP/SFP+/SFP28/QSFP+\n"); + return -1; + } + sfp_info->mod_type = buffer[2]; + sfp_info->eth_comp = buffer[3] & 0xf0; + sfp_info->cable_comp = buffer[0x24]; + + /* There are several ways a cable can be marked as active or + * passive. 8.[2-3] specify the SFP+ cable technology. Some + * modules also use 3.[0-1] for Infiniband, though it's + * redundant. + */ + if ((buffer[8] & 0x0C) == 0x08) { + sfp_info->limiting = true; + sfp_info->active_cable = true; + } else if ((buffer[8] & 0xC) == 0x4) { + sfp_info->limiting = false; + sfp_info->active_cable = false; + } + if ((buffer[3] & 3) == 2) { + sfp_info->active_cable = true; + sfp_info->limiting = true; + } + + switch (sfp_info->mod_type) { + case CVMX_SFP_MOD_OPTICAL_LC: + case CVMX_SFP_MOD_OPTICAL_PIGTAIL: + sfp_info->copper_cable = false; + break; + case CVMX_SFP_MOD_COPPER_PIGTAIL: + sfp_info->copper_cable = true; + break; + case CVMX_SFP_MOD_NO_SEP_CONN: + switch (sfp_info->cable_comp) { + case CVMX_SFP_CABLE_100G_25GAUI_C2M_AOC_HIGH_BER: + case CVMX_SFP_CABLE_100G_25GAUI_C2M_AOC_LOW_BER: + case CVMX_SFP_CABLE_100G_25GAUI_C2M_ACC_LOW_BER: + sfp_info->copper_cable = false; + sfp_info->limiting = true; + sfp_info->active_cable = true; + break; + + case CVMX_SFP_CABLE_100G_SR4_25G_SR: + case CVMX_SFP_CABLE_100G_LR4_25G_LR: + case CVMX_SFP_CABLE_100G_ER4_25G_ER: + case CVMX_SFP_CABLE_100G_SR10: + case CVMX_SFP_CABLE_100G_CWDM4_MSA: + case CVMX_SFP_CABLE_100G_PSM4: + case CVMX_SFP_CABLE_100G_CWDM4: + case CVMX_SFP_CABLE_40G_ER4: + case CVMX_SFP_CABLE_4X10G_SR: + case CVMX_SFP_CABLE_G959_1_P1I1_2D1: + case CVMX_SFP_CABLE_G959_1_P1S1_2D2: + case CVMX_SFP_CABLE_G959_1_P1L1_2D2: + case CVMX_SFP_CABLE_100G_CLR4: + case CVMX_SFP_CABLE_100G_2_LAMBDA_DWDM: + case CVMX_SFP_CABLE_40G_SWDM4: + case CVMX_SFP_CABLE_100G_SWDM4: + case CVMX_SFP_CABLE_100G_PAM4_BIDI: + sfp_info->copper_cable = false; + break; + + case CVMX_SFP_CABLE_100G_25GAUI_C2M_ACC_HIGH_BER: + case CVMX_SFP_CABLE_10GBASE_T: + case CVMX_SFP_CABLE_10GBASE_T_SR: + case CVMX_SFP_CABLE_5GBASE_T: + case CVMX_SFP_CABLE_2_5GBASE_T: + sfp_info->copper_cable = true; + sfp_info->limiting = true; + sfp_info->active_cable = true; + break; + + case CVMX_SFP_CABLE_100G_CR4_25G_CR_CA_L: + case CVMX_SFP_CABLE_25G_CR_CA_S: + case CVMX_SFP_CABLE_25G_CR_CA_N: + case CVMX_SFP_CABLE_40G_PSM4: + sfp_info->copper_cable = true; + break; + + default: + switch (sfp_info->eth_comp) { + case CVMX_SFP_CABLE_10GBASE_ER: + case CVMX_SFP_CABLE_10GBASE_LRM: + case CVMX_SFP_CABLE_10GBASE_LR: + case CVMX_SFP_CABLE_10GBASE_SR: + sfp_info->copper_cable = false; + break; + } + break; + } + break; + + case CVMX_SFP_MOD_RJ45: + debug("%s: RJ45 adapter\n", __func__); + sfp_info->copper_cable = true; + sfp_info->active_cable = true; + sfp_info->limiting = true; + break; + case CVMX_SFP_MOD_UNKNOWN: + /* The Avago 1000Base-X to 1000Base-T module reports that it + * is an unknown module type but the Ethernet compliance code + * says it is 1000Base-T. We'll change the reporting to RJ45. + */ + if (buffer[6] & 8) { + debug("RJ45 gigabit module detected\n"); + sfp_info->mod_type = CVMX_SFP_MOD_RJ45; + sfp_info->copper_cable = false; + sfp_info->limiting = true; + sfp_info->active_cable = true; + sfp_info->max_copper_cable_len = buffer[0x12]; + sfp_info->rate = CVMX_SFP_RATE_1G; + } else { + debug("Unknown module type 0x%x\n", sfp_info->mod_type); + } + sfp_info->limiting = true; + break; + case CVMX_SFP_MOD_MXC_2X16: + debug("%s: MXC 2X16\n", __func__); + break; + default: + sfp_info->limiting = true; + break; + } + + if (sfp_info->copper_cable) + sfp_info->max_copper_cable_len = buffer[0x12]; + else + sfp_info->max_50um_om4_cable_length = buffer[0x12] * 10; + + if (buffer[0xe]) + sfp_info->max_single_mode_cable_length = buffer[0xe] * 1000; + else + sfp_info->max_single_mode_cable_length = buffer[0xf] * 100000; + + sfp_info->max_50um_om2_cable_length = buffer[0x10] * 10; + sfp_info->max_62_5um_om1_cable_length = buffer[0x11] * 10; + sfp_info->max_50um_om3_cable_length = buffer[0x13] * 10; + + if (buffer[0xc] == 0xff) { + if (buffer[0x42] >= 255) + sfp_info->rate = CVMX_SFP_RATE_100G; + else if (buffer[0x42] >= 160) + sfp_info->rate = CVMX_SFP_RATE_40G; + else if (buffer[0x42] >= 100) + sfp_info->rate = CVMX_SFP_RATE_25G; + else + sfp_info->rate = CVMX_SFP_RATE_UNKNOWN; + } else if (buffer[0xc] >= 100) { + sfp_info->rate = CVMX_SFP_RATE_10G; + } else if (buffer[0xc] >= 10) { + sfp_info->rate = CVMX_SFP_RATE_1G; + } else { + sfp_info->rate = CVMX_SFP_RATE_UNKNOWN; + } + + if (sfp_info->rate == CVMX_SFP_RATE_UNKNOWN) { + switch (sfp_info->cable_comp) { + case CVMX_SFP_CABLE_100G_SR10: + case CVMX_SFP_CABLE_100G_CWDM4_MSA: + case CVMX_SFP_CABLE_100G_PSM4: + case CVMX_SFP_CABLE_100G_CWDM4: + case CVMX_SFP_CABLE_100G_CLR4: + case CVMX_SFP_CABLE_100G_2_LAMBDA_DWDM: + case CVMX_SFP_CABLE_100G_SWDM4: + case CVMX_SFP_CABLE_100G_PAM4_BIDI: + sfp_info->rate = CVMX_SFP_RATE_100G; + break; + case CVMX_SFP_CABLE_100G_25GAUI_C2M_AOC_HIGH_BER: + case CVMX_SFP_CABLE_100G_SR4_25G_SR: + case CVMX_SFP_CABLE_100G_LR4_25G_LR: + case CVMX_SFP_CABLE_100G_ER4_25G_ER: + case CVMX_SFP_CABLE_100G_25GAUI_C2M_ACC_HIGH_BER: + case CVMX_SFP_CABLE_100G_CR4_25G_CR_CA_L: + case CVMX_SFP_CABLE_25G_CR_CA_S: + case CVMX_SFP_CABLE_25G_CR_CA_N: + case CVMX_SFP_CABLE_100G_25GAUI_C2M_AOC_LOW_BER: + case CVMX_SFP_CABLE_100G_25GAUI_C2M_ACC_LOW_BER: + sfp_info->rate = CVMX_SFP_RATE_25G; + break; + case CVMX_SFP_CABLE_40G_ER4: + case CVMX_SFP_CABLE_4X10G_SR: + case CVMX_SFP_CABLE_40G_PSM4: + case CVMX_SFP_CABLE_40G_SWDM4: + sfp_info->rate = CVMX_SFP_RATE_40G; + break; + case CVMX_SFP_CABLE_G959_1_P1I1_2D1: + case CVMX_SFP_CABLE_G959_1_P1S1_2D2: + case CVMX_SFP_CABLE_G959_1_P1L1_2D2: + case CVMX_SFP_CABLE_10GBASE_T: + case CVMX_SFP_CABLE_10GBASE_T_SR: + case CVMX_SFP_CABLE_5GBASE_T: + case CVMX_SFP_CABLE_2_5GBASE_T: + sfp_info->rate = CVMX_SFP_RATE_10G; + break; + default: + switch (sfp_info->eth_comp) { + case CVMX_SFP_CABLE_10GBASE_ER: + case CVMX_SFP_CABLE_10GBASE_LRM: + case CVMX_SFP_CABLE_10GBASE_LR: + case CVMX_SFP_CABLE_10GBASE_SR: + sfp_info->rate = CVMX_SFP_RATE_10G; + break; + default: + sfp_info->rate = CVMX_SFP_RATE_UNKNOWN; + break; + } + break; + } + } + + if (buffer[0xc] < 0xff) + sfp_info->bitrate_max = buffer[0xc] * 100; + else + sfp_info->bitrate_max = buffer[0x42] * 250; + + if ((buffer[8] & 0xc) == 8) { + if (buffer[0x3c] & 0x4) + sfp_info->limiting = true; + } + + /* Currently we only set this for 25G. FEC is required for CA-S cables + * and for cable lengths >= 5M as of this writing. + */ + if ((sfp_info->rate == CVMX_SFP_RATE_25G && + sfp_info->copper_cable) && + (sfp_info->cable_comp == CVMX_SFP_CABLE_25G_CR_CA_S || + sfp_info->max_copper_cable_len >= 5)) + sfp_info->fec_required = true; + + /* copy strings and vendor info, strings will be automatically NUL + * terminated. + */ + memcpy(sfp_info->vendor_name, &buffer[0x14], 16); + memcpy(sfp_info->vendor_oui, &buffer[0x25], 3); + memcpy(sfp_info->vendor_pn, &buffer[0x28], 16); + memcpy(sfp_info->vendor_rev, &buffer[0x38], 4); + memcpy(sfp_info->vendor_sn, &buffer[0x44], 16); + memcpy(sfp_info->date_code, &buffer[0x54], 8); + + sfp_info->cooled_laser = !!(buffer[0x40] & 4); + sfp_info->internal_cdr = !!(buffer[0x40] & 8); + + if (buffer[0x40] & 0x20) + sfp_info->power_level = 3; + else + sfp_info->power_level = (buffer[0x40] & 2) ? 2 : 1; + + sfp_info->diag_paging = !!(buffer[0x40] & 0x10); + sfp_info->linear_rx_output = !(buffer[0x40] & 1); + sfp_info->los_implemented = !!(buffer[0x41] & 2); + sfp_info->los_inverted = !!(buffer[0x41] & 4); + sfp_info->tx_fault_implemented = !!(buffer[0x41] & 8); + sfp_info->tx_disable_implemented = !!(buffer[0x41] & 0x10); + sfp_info->rate_select_implemented = !!(buffer[0x41] & 0x20); + sfp_info->tuneable_transmitter = !!(buffer[0x41] & 0x40); + sfp_info->rx_decision_threshold_implemented = !!(buffer[0x41] & 0x80); + + sfp_info->diag_monitoring = !!(buffer[0x5c] & 0x40); + sfp_info->diag_rx_power_averaged = !!(buffer[0x5c] & 0x8); + sfp_info->diag_externally_calibrated = !!(buffer[0x5c] & 0x10); + sfp_info->diag_internally_calibrated = !!(buffer[0x5c] & 0x20); + sfp_info->diag_addr_change_required = !!(buffer[0x5c] & 0x4); + sfp_info->diag_soft_rate_select_control = !!(buffer[0x5d] & 2); + sfp_info->diag_app_select_control = !!(buffer[0x5d] & 4); + sfp_info->diag_soft_rate_select_control = !!(buffer[0x5d] & 8); + sfp_info->diag_soft_rx_los_implemented = !!(buffer[0x5d] & 0x10); + sfp_info->diag_soft_tx_fault_implemented = !!(buffer[0x5d] & 0x20); + sfp_info->diag_soft_tx_disable_implemented = !!(buffer[0x5d] & 0x40); + sfp_info->diag_alarm_warning_flags_implemented = + !!(buffer[0x5d] & 0x80); + sfp_info->diag_rev = buffer[0x5e]; + + return 0; +} + +static int cvmx_sfp_parse_qsfp_buffer(struct cvmx_sfp_mod_info *sfp_info, + const uint8_t *buffer) +{ + u8 csum = 0; + bool csum_good = false; + int i; + + /* Validate the checksum */ + for (i = 0x80; i < 0xbf; i++) + csum += buffer[i]; + csum_good = csum == buffer[0xbf]; + debug("%s: Lower checksum: 0x%02x, expected: 0x%02x\n", __func__, csum, + buffer[0xbf]); + csum = 0; + for (i = 0xc0; i < 0xdf; i++) + csum += buffer[i]; + debug("%s: Upper checksum: 0x%02x, expected: 0x%02x\n", __func__, csum, + buffer[0xdf]); + if (csum != buffer[0xdf] || !csum_good) { + debug("Error: SFP EEPROM checksum information is incorrect\n"); + return -1; + } + + sfp_info->conn_type = buffer[0x80]; + sfp_info->mod_type = buffer[0x82]; + sfp_info->eth_comp = buffer[0x83] & 0xf0; + sfp_info->cable_comp = buffer[0xa4]; + + switch (sfp_info->mod_type) { + case CVMX_SFP_MOD_COPPER_PIGTAIL: + case CVMX_SFP_MOD_NO_SEP_CONN: + debug("%s: copper pigtail or no separable cable\n", __func__); + /* There are several ways a cable can be marked as active or + * passive. 8.[2-3] specify the SFP+ cable technology. Some + * modules also use 3.[0-1] for Infiniband, though it's + * redundant. + */ + sfp_info->copper_cable = true; + if ((buffer[0x88] & 0x0C) == 0x08) { + sfp_info->limiting = true; + sfp_info->active_cable = true; + } else if ((buffer[0x88] & 0xC) == 0x4) { + sfp_info->limiting = false; + sfp_info->active_cable = false; + } + if ((buffer[0x83] & 3) == 2) { + sfp_info->active_cable = true; + sfp_info->limiting = true; + } + break; + case CVMX_SFP_MOD_RJ45: + debug("%s: RJ45 adapter\n", __func__); + sfp_info->copper_cable = true; + sfp_info->active_cable = true; + sfp_info->limiting = true; + break; + case CVMX_SFP_MOD_UNKNOWN: + debug("Unknown module type\n"); + /* The Avago 1000Base-X to 1000Base-T module reports that it + * is an unknown module type but the Ethernet compliance code + * says it is 1000Base-T. We'll change the reporting to RJ45. + */ + if (buffer[0x86] & 8) { + sfp_info->mod_type = CVMX_SFP_MOD_RJ45; + sfp_info->copper_cable = false; + sfp_info->limiting = true; + sfp_info->active_cable = true; + sfp_info->max_copper_cable_len = buffer[0x92]; + sfp_info->rate = CVMX_SFP_RATE_1G; + } + fallthrough; + default: + sfp_info->limiting = true; + break; + } + + if (sfp_info->copper_cable) + sfp_info->max_copper_cable_len = buffer[0x92]; + else + sfp_info->max_50um_om4_cable_length = buffer[0x92] * 10; + + debug("%s: copper cable: %d, max copper cable len: %d\n", __func__, + sfp_info->copper_cable, sfp_info->max_copper_cable_len); + if (buffer[0xe]) + sfp_info->max_single_mode_cable_length = buffer[0x8e] * 1000; + else + sfp_info->max_single_mode_cable_length = buffer[0x8f] * 100000; + + sfp_info->max_50um_om2_cable_length = buffer[0x90] * 10; + sfp_info->max_62_5um_om1_cable_length = buffer[0x91] * 10; + sfp_info->max_50um_om3_cable_length = buffer[0x93] * 10; + + if (buffer[0x8c] == 12) { + sfp_info->rate = CVMX_SFP_RATE_1G; + } else if (buffer[0x8c] == 103) { + sfp_info->rate = CVMX_SFP_RATE_10G; + } else if (buffer[0x8c] == 0xff) { + if (buffer[0xc2] == 103) + sfp_info->rate = CVMX_SFP_RATE_100G; + } + + if (buffer[0x8c] < 0xff) + sfp_info->bitrate_max = buffer[0x8c] * 100; + else + sfp_info->bitrate_max = buffer[0xc2] * 250; + + if ((buffer[0x88] & 0xc) == 8) { + if (buffer[0xbc] & 0x4) + sfp_info->limiting = true; + } + + /* Currently we only set this for 25G. FEC is required for CA-S cables + * and for cable lengths >= 5M as of this writing. + */ + /* copy strings and vendor info, strings will be automatically NUL + * terminated. + */ + memcpy(sfp_info->vendor_name, &buffer[0x94], 16); + memcpy(sfp_info->vendor_oui, &buffer[0xa5], 3); + memcpy(sfp_info->vendor_pn, &buffer[0xa8], 16); + memcpy(sfp_info->vendor_rev, &buffer[0xb8], 4); + memcpy(sfp_info->vendor_sn, &buffer[0xc4], 16); + memcpy(sfp_info->date_code, &buffer[0xd4], 8); + + sfp_info->linear_rx_output = !!(buffer[0xc0] & 1); + sfp_info->cooled_laser = !!(buffer[0xc0] & 4); + sfp_info->internal_cdr = !!(buffer[0xc0] & 8); + + if (buffer[0xc0] & 0x20) + sfp_info->power_level = 3; + else + sfp_info->power_level = (buffer[0xc0] & 2) ? 2 : 1; + + sfp_info->diag_paging = !!(buffer[0xc0] & 0x10); + sfp_info->los_implemented = !!(buffer[0xc1] & 2); + sfp_info->los_inverted = !!(buffer[0xc1] & 4); + sfp_info->tx_fault_implemented = !!(buffer[0xc1] & 8); + sfp_info->tx_disable_implemented = !!(buffer[0xc1] & 0x10); + sfp_info->rate_select_implemented = !!(buffer[0xc1] & 0x20); + sfp_info->tuneable_transmitter = !!(buffer[0xc1] & 0x40); + sfp_info->rx_decision_threshold_implemented = !!(buffer[0xc1] & 0x80); + + sfp_info->diag_monitoring = !!(buffer[0xdc] & 0x40); + sfp_info->diag_rx_power_averaged = !!(buffer[0xdc] & 0x8); + sfp_info->diag_externally_calibrated = !!(buffer[0xdc] & 0x10); + sfp_info->diag_internally_calibrated = !!(buffer[0xdc] & 0x20); + sfp_info->diag_addr_change_required = !!(buffer[0xdc] & 0x4); + sfp_info->diag_soft_rate_select_control = !!(buffer[0xdd] & 2); + sfp_info->diag_app_select_control = !!(buffer[0xdd] & 4); + sfp_info->diag_soft_rate_select_control = !!(buffer[0xdd] & 8); + sfp_info->diag_soft_rx_los_implemented = !!(buffer[0xdd] & 0x10); + sfp_info->diag_soft_tx_fault_implemented = !!(buffer[0xdd] & 0x20); + sfp_info->diag_soft_tx_disable_implemented = !!(buffer[0xdd] & 0x40); + sfp_info->diag_alarm_warning_flags_implemented = + !!(buffer[0xdd] & 0x80); + sfp_info->diag_rev = buffer[0xde]; + + return 0; +} + +static bool sfp_verify_checksum(const uint8_t *buffer) +{ + u8 csum = 0; + u8 offset; + bool csum_good = false; + int i; + + switch (buffer[0]) { + case CVMX_SFP_CONN_QSFP: + case CVMX_SFP_CONN_QSFPP: + case CVMX_SFP_CONN_QSFP28: + case CVMX_SFP_CONN_MICRO_QSFP: + case CVMX_SFP_CONN_QSFP_DD: + offset = 0x80; + break; + default: + offset = 0; + break; + } + for (i = offset; i < offset + 0x3f; i++) + csum += buffer[i]; + csum_good = csum == buffer[offset + 0x3f]; + if (!csum_good) { + debug("%s: Lower checksum bad, got 0x%x, expected 0x%x\n", + __func__, csum, buffer[offset + 0x3f]); + return false; + } + csum = 0; + for (i = offset + 0x40; i < offset + 0x5f; i++) + csum += buffer[i]; + if (csum != buffer[offset + 0x5f]) { + debug("%s: Upper checksum bad, got 0x%x, expected 0x%x\n", + __func__, csum, buffer[offset + 0x5f]); + return false; + } + return true; +} + +/** + * Reads and parses SFP/QSFP EEPROM + * + * @param sfp sfp handle to read + * + * @return 0 for success, -1 on error. + */ +int cvmx_sfp_read_i2c_eeprom(struct cvmx_fdt_sfp_info *sfp) +{ + const struct cvmx_fdt_i2c_bus_info *bus = sfp->i2c_bus; + int oct_bus = cvmx_fdt_i2c_get_root_bus(bus); + struct udevice *dev; + u8 buffer[256]; + bool is_qsfp; + int retry; + int err; + + if (!bus) { + debug("%s(%s): Error: i2c bus undefined for eeprom\n", __func__, + sfp->name); + return -1; + } + + is_qsfp = (sfp->sfp_info.conn_type == CVMX_SFP_CONN_QSFP || + sfp->sfp_info.conn_type == CVMX_SFP_CONN_QSFPP || + sfp->sfp_info.conn_type == CVMX_SFP_CONN_QSFP28 || + sfp->sfp_info.conn_type == CVMX_SFP_CONN_MICRO_QSFP) || + sfp->is_qsfp; + + err = cvmx_qsfp_select(sfp, true); + if (err) { + debug("%s: Error selecting SFP/QSFP slot\n", __func__); + return err; + } + + debug("%s: Reading eeprom from i2c address %d:0x%x\n", __func__, + oct_bus, sfp->i2c_eeprom_addr); + for (retry = 0; retry < 3; retry++) { + err = i2c_get_chip(bus->i2c_bus, sfp->i2c_eeprom_addr, 1, &dev); + if (err) { + debug("Cannot find I2C device: %d\n", err); + goto error; + } + + err = dm_i2c_read(dev, 0, buffer, 256); + if (err || !sfp_verify_checksum(buffer)) { + debug("%s: Error %d reading eeprom at 0x%x, bus %d\n", + __func__, err, sfp->i2c_eeprom_addr, oct_bus); + debug("%s: Retry %d\n", __func__, retry + 1); + mdelay(1000); + } else { + break; + } + } + if (err) { + debug("%s: Error reading eeprom from SFP %s\n", __func__, + sfp->name); + return -1; + } +#ifdef DEBUG + print_buffer(0, buffer, 1, 256, 0); +#endif + memset(&sfp->sfp_info, 0, sizeof(struct cvmx_sfp_mod_info)); + + switch (buffer[0]) { + case CVMX_SFP_CONN_SFP: + err = cvmx_sfp_parse_sfp_buffer(&sfp->sfp_info, buffer); + break; + case CVMX_SFP_CONN_QSFP: + case CVMX_SFP_CONN_QSFPP: + case CVMX_SFP_CONN_QSFP28: + case CVMX_SFP_CONN_MICRO_QSFP: + err = cvmx_sfp_parse_qsfp_buffer(&sfp->sfp_info, buffer); + break; + default: + debug("%s: Unknown SFP transceiver type 0x%x\n", __func__, + buffer[0]); + err = -1; + break; + } + +error: + if (is_qsfp) + err |= cvmx_qsfp_select(sfp, false); + + if (!err) { + sfp->valid = true; + sfp->sfp_info.valid = true; + } else { + sfp->valid = false; + sfp->sfp_info.valid = false; + } + + return err; +} + +/** + * Function called to check and return the status of the mod_abs pin or + * mod_pres pin for QSFPs. + * + * @param sfp Handle to SFP information. + * @param data User-defined data passed to the function + * + * @return 0 if absent, 1 if present, -1 on error + */ +int cvmx_sfp_check_mod_abs(struct cvmx_fdt_sfp_info *sfp, void *data) +{ + int val; + int err = 0; + int mode; + + if (!dm_gpio_is_valid(&sfp->mod_abs)) { + debug("%s: Error: mod_abs not set for %s\n", __func__, + sfp->name); + return -1; + } + val = dm_gpio_get_value(&sfp->mod_abs); + debug("%s(%s, %p) mod_abs: %d\n", __func__, sfp->name, data, val); + if (val >= 0 && val != sfp->last_mod_abs && sfp->mod_abs_changed) { + err = 0; + if (!val) { + err = cvmx_sfp_read_i2c_eeprom(sfp); + if (err) + debug("%s: Error reading SFP %s EEPROM\n", + __func__, sfp->name); + } + err = sfp->mod_abs_changed(sfp, val, sfp->mod_abs_changed_data); + } + debug("%s(%s (%p)): Last mod_abs: %d, current: %d, changed: %p, rc: %d, next: %p, caller: %p\n", + __func__, sfp->name, sfp, sfp->last_mod_abs, val, + sfp->mod_abs_changed, err, sfp->next_iface_sfp, + __builtin_return_address(0)); + + if (err >= 0) { + sfp->last_mod_abs = val; + mode = cvmx_helper_interface_get_mode(sfp->xiface); + cvmx_sfp_validate_module(sfp, mode); + } else { + debug("%s: mod_abs_changed for %s returned error\n", __func__, + sfp->name); + } + + return err < 0 ? err : val; +} + +/** + * Reads the EEPROMs of all SFP modules. + * + * @return 0 for success + */ +int cvmx_sfp_read_all_modules(void) +{ + struct cvmx_fdt_sfp_info *sfp; + int val; + bool error = false; + int rc; + + for (sfp = sfp_list; sfp; sfp = sfp->next) { + if (dm_gpio_is_valid(&sfp->mod_abs)) { + /* Check if module absent */ + val = dm_gpio_get_value(&sfp->mod_abs); + sfp->last_mod_abs = val; + if (val) + continue; + } + rc = cvmx_sfp_read_i2c_eeprom(sfp); + if (rc) { + debug("%s: Error reading eeprom from SFP %s\n", + __func__, sfp->name); + error = true; + } + } + + return error ? -1 : 0; +} + +/** + * Registers a function to be called whenever the mod_abs/mod_pres signal + * changes. + * + * @param sfp Handle to SFP data structure + * @param mod_abs_changed Function called whenever mod_abs is changed + * or NULL to remove. + * @param mod_abs_changed_data User-defined data passed to + * mod_abs_changed + * + * @return 0 for success + * + * @NOTE: If multiple SFP slots are linked together, all subsequent slots + * will also be registered for the same handler. + */ +int cvmx_sfp_register_mod_abs_changed(struct cvmx_fdt_sfp_info *sfp, + int (*mod_abs_changed)(struct cvmx_fdt_sfp_info *sfp, + int val, void *data), + void *mod_abs_changed_data) +{ + sfp->mod_abs_changed = mod_abs_changed; + sfp->mod_abs_changed_data = mod_abs_changed_data; + + sfp->last_mod_abs = -2; /* undefined */ + + return 0; +} + +/** + * Parses a SFP slot from the device tree + * + * @param sfp SFP handle to store data in + * @param fdt_addr Address of flat device tree + * @param of_offset Node in device tree for SFP slot + * + * @return 0 on success, -1 on error + */ +static int cvmx_sfp_parse_sfp(struct cvmx_fdt_sfp_info *sfp, ofnode node) +{ + struct ofnode_phandle_args phandle; + int err; + + sfp->name = ofnode_get_name(node); + sfp->of_offset = ofnode_to_offset(node); + + err = gpio_request_by_name_nodev(node, "tx_disable", 0, + &sfp->tx_disable, GPIOD_IS_OUT); + if (err) { + printf("%s: tx_disable not found in DT!\n", __func__); + return -ENODEV; + } + dm_gpio_set_value(&sfp->tx_disable, 0); + + err = gpio_request_by_name_nodev(node, "mod_abs", 0, + &sfp->mod_abs, GPIOD_IS_IN); + if (err) { + printf("%s: mod_abs not found in DT!\n", __func__); + return -ENODEV; + } + + err = gpio_request_by_name_nodev(node, "tx_error", 0, + &sfp->tx_error, GPIOD_IS_IN); + if (err) { + printf("%s: tx_error not found in DT!\n", __func__); + return -ENODEV; + } + + err = gpio_request_by_name_nodev(node, "rx_los", 0, + &sfp->rx_los, GPIOD_IS_IN); + if (err) { + printf("%s: rx_los not found in DT!\n", __func__); + return -ENODEV; + } + + err = ofnode_parse_phandle_with_args(node, "eeprom", NULL, 0, 0, + &phandle); + if (!err) { + sfp->i2c_eeprom_addr = ofnode_get_addr(phandle.node); + debug("%s: eeprom address: 0x%x\n", __func__, + sfp->i2c_eeprom_addr); + + debug("%s: Getting eeprom i2c bus for %s\n", __func__, + sfp->name); + sfp->i2c_bus = cvmx_ofnode_get_i2c_bus(ofnode_get_parent(phandle.node)); + } + + err = ofnode_parse_phandle_with_args(node, "diag", NULL, 0, 0, + &phandle); + if (!err) { + sfp->i2c_diag_addr = ofnode_get_addr(phandle.node); + if (!sfp->i2c_bus) + sfp->i2c_bus = cvmx_ofnode_get_i2c_bus(ofnode_get_parent(phandle.node)); + } + + sfp->last_mod_abs = -2; + sfp->last_rx_los = -2; + + if (!sfp->i2c_bus) { + debug("%s(%s): Error: could not get i2c bus from device tree\n", + __func__, sfp->name); + err = -1; + } + + if (err) { + dm_gpio_free(sfp->tx_disable.dev, &sfp->tx_disable); + dm_gpio_free(sfp->mod_abs.dev, &sfp->mod_abs); + dm_gpio_free(sfp->tx_error.dev, &sfp->tx_error); + dm_gpio_free(sfp->rx_los.dev, &sfp->rx_los); + } else { + sfp->valid = true; + } + + return err; +} + +/** + * Parses a QSFP slot from the device tree + * + * @param sfp SFP handle to store data in + * @param fdt_addr Address of flat device tree + * @param of_offset Node in device tree for SFP slot + * + * @return 0 on success, -1 on error + */ +static int cvmx_sfp_parse_qsfp(struct cvmx_fdt_sfp_info *sfp, ofnode node) +{ + struct ofnode_phandle_args phandle; + int err; + + sfp->is_qsfp = true; + sfp->name = ofnode_get_name(node); + sfp->of_offset = ofnode_to_offset(node); + + err = gpio_request_by_name_nodev(node, "lp_mode", 0, + &sfp->lp_mode, GPIOD_IS_OUT); + if (err) { + printf("%s: lp_mode not found in DT!\n", __func__); + return -ENODEV; + } + + err = gpio_request_by_name_nodev(node, "mod_prs", 0, + &sfp->mod_abs, GPIOD_IS_IN); + if (err) { + printf("%s: mod_prs not found in DT!\n", __func__); + return -ENODEV; + } + + err = gpio_request_by_name_nodev(node, "select", 0, + &sfp->select, GPIOD_IS_IN); + if (err) { + printf("%s: select not found in DT!\n", __func__); + return -ENODEV; + } + + err = gpio_request_by_name_nodev(node, "reset", 0, + &sfp->reset, GPIOD_IS_OUT); + if (err) { + printf("%s: reset not found in DT!\n", __func__); + return -ENODEV; + } + + err = gpio_request_by_name_nodev(node, "interrupt", 0, + &sfp->interrupt, GPIOD_IS_IN); + if (err) { + printf("%s: interrupt not found in DT!\n", __func__); + return -ENODEV; + } + + err = ofnode_parse_phandle_with_args(node, "eeprom", NULL, 0, 0, + &phandle); + if (!err) { + sfp->i2c_eeprom_addr = ofnode_get_addr(phandle.node); + sfp->i2c_bus = cvmx_ofnode_get_i2c_bus(ofnode_get_parent(phandle.node)); + } + + err = ofnode_parse_phandle_with_args(node, "diag", NULL, 0, 0, + &phandle); + if (!err) { + sfp->i2c_diag_addr = ofnode_get_addr(phandle.node); + if (!sfp->i2c_bus) + sfp->i2c_bus = cvmx_ofnode_get_i2c_bus(ofnode_get_parent(phandle.node)); + } + + sfp->last_mod_abs = -2; + sfp->last_rx_los = -2; + + if (!sfp->i2c_bus) { + cvmx_printf("%s(%s): Error: could not get i2c bus from device tree\n", + __func__, sfp->name); + err = -1; + } + + if (err) { + dm_gpio_free(sfp->lp_mode.dev, &sfp->lp_mode); + dm_gpio_free(sfp->mod_abs.dev, &sfp->mod_abs); + dm_gpio_free(sfp->select.dev, &sfp->select); + dm_gpio_free(sfp->reset.dev, &sfp->reset); + dm_gpio_free(sfp->interrupt.dev, &sfp->interrupt); + } else { + sfp->valid = true; + } + + return err; +} + +/** + * Parses the device tree for SFP and QSFP slots + * + * @param fdt_addr Address of flat device-tree + * + * @return 0 for success, -1 on error + */ +int cvmx_sfp_parse_device_tree(const void *fdt_addr) +{ + struct cvmx_fdt_sfp_info *sfp, *first_sfp = NULL, *last_sfp = NULL; + ofnode node; + int err = 0; + int reg; + static bool parsed; + + debug("%s(%p): Parsing...\n", __func__, fdt_addr); + if (parsed) { + debug("%s(%p): Already parsed\n", __func__, fdt_addr); + return 0; + } + + ofnode_for_each_compatible_node(node, "ethernet,sfp-slot") { + if (!ofnode_valid(node)) + continue; + + sfp = cvm_sfp_alloc(sizeof(*sfp)); + if (!sfp) + return -1; + + err = cvmx_sfp_parse_sfp(sfp, node); + if (!err) { + if (!sfp_list) + sfp_list = sfp; + if (last_sfp) + last_sfp->next = sfp; + sfp->prev = last_sfp; + last_sfp = sfp; + debug("%s: parsed %s\n", __func__, sfp->name); + } else { + debug("%s: Error parsing SFP at node %s\n", + __func__, ofnode_get_name(node)); + return err; + } + } + + ofnode_for_each_compatible_node(node, "ethernet,qsfp-slot") { + if (!ofnode_valid(node)) + continue; + + sfp = cvm_sfp_alloc(sizeof(*sfp)); + if (!sfp) + return -1; + + err = cvmx_sfp_parse_qsfp(sfp, node); + if (!err) { + if (!sfp_list) + sfp_list = sfp; + if (last_sfp) + last_sfp->next = sfp; + sfp->prev = last_sfp; + last_sfp = sfp; + debug("%s: parsed %s\n", __func__, sfp->name); + } else { + debug("%s: Error parsing QSFP at node %s\n", + __func__, ofnode_get_name(node)); + return err; + } + } + + if (!octeon_has_feature(OCTEON_FEATURE_BGX)) + return 0; + + err = 0; + ofnode_for_each_compatible_node(node, "cavium,octeon-7890-bgx-port") { + int sfp_nodes[4]; + ofnode sfp_ofnodes[4]; + int num_sfp_nodes; + u64 reg_addr; + struct cvmx_xiface xi; + int xiface, index; + cvmx_helper_interface_mode_t mode; + int i; + int rc; + + if (!ofnode_valid(node)) + break; + + num_sfp_nodes = ARRAY_SIZE(sfp_nodes); + rc = cvmx_ofnode_lookup_phandles(node, "sfp-slot", + &num_sfp_nodes, sfp_ofnodes); + if (rc != 0 || num_sfp_nodes < 1) + rc = cvmx_ofnode_lookup_phandles(node, "qsfp-slot", + &num_sfp_nodes, + sfp_ofnodes); + /* If no SFP or QSFP slot found, go to next port */ + if (rc < 0) + continue; + + last_sfp = NULL; + for (i = 0; i < num_sfp_nodes; i++) { + sfp = cvmx_sfp_find_slot_by_fdt_node(ofnode_to_offset(sfp_ofnodes[i])); + debug("%s: Adding sfp %s (%p) to BGX port\n", + __func__, sfp->name, sfp); + if (last_sfp) + last_sfp->next_iface_sfp = sfp; + else + first_sfp = sfp; + last_sfp = sfp; + } + if (!first_sfp) { + debug("%s: Error: could not find SFP slot for BGX port %s\n", + __func__, + fdt_get_name(fdt_addr, sfp_nodes[0], + NULL)); + err = -1; + break; + } + + /* Get the port index */ + reg = ofnode_get_addr(node); + if (reg < 0) { + debug("%s: Error: could not get BGX port reg value\n", + __func__); + err = -1; + break; + } + index = reg; + + /* Get BGX node and address */ + reg_addr = ofnode_get_addr(ofnode_get_parent(node)); + /* Extrace node */ + xi.node = cvmx_csr_addr_to_node(reg_addr); + /* Extract reg address */ + reg_addr = cvmx_csr_addr_strip_node(reg_addr); + if ((reg_addr & 0xFFFFFFFFF0000000) != + 0x00011800E0000000) { + debug("%s: Invalid BGX address 0x%llx\n", + __func__, (unsigned long long)reg_addr); + xi.node = -1; + err = -1; + break; + } + + /* Extract interface from address */ + xi.interface = (reg_addr >> 24) & 0x0F; + /* Convert to xiface */ + xiface = cvmx_helper_node_interface_to_xiface(xi.node, + xi.interface); + debug("%s: Parsed %d SFP slots for interface 0x%x, index %d\n", + __func__, num_sfp_nodes, xiface, index); + + mode = cvmx_helper_interface_get_mode(xiface); + for (sfp = first_sfp; sfp; sfp = sfp->next_iface_sfp) { + sfp->xiface = xiface; + sfp->index = index; + /* Convert to IPD port */ + sfp->ipd_port[0] = + cvmx_helper_get_ipd_port(xiface, index); + debug("%s: sfp %s (%p) xi: 0x%x, index: 0x%x, node: %d, mode: 0x%x, next: %p\n", + __func__, sfp->name, sfp, sfp->xiface, + sfp->index, xi.node, mode, + sfp->next_iface_sfp); + if (mode == CVMX_HELPER_INTERFACE_MODE_XLAUI || + mode == CVMX_HELPER_INTERFACE_MODE_40G_KR4) + for (i = 1; i < 4; i++) + sfp->ipd_port[i] = -1; + else + for (i = 1; i < 4; i++) + sfp->ipd_port[i] = + cvmx_helper_get_ipd_port( + xiface, i); + } + cvmx_helper_cfg_set_sfp_info(xiface, index, first_sfp); + } + + if (!err) { + parsed = true; + cvmx_sfp_read_all_modules(); + } + + return err; +} + +/** + * Given a fdt node offset find the corresponding SFP or QSFP slot + * + * @param of_offset flat device tree node offset + * + * @return pointer to SFP data structure or NULL if not found + */ +struct cvmx_fdt_sfp_info *cvmx_sfp_find_slot_by_fdt_node(int of_offset) +{ + struct cvmx_fdt_sfp_info *sfp = sfp_list; + + while (sfp) { + if (sfp->of_offset == of_offset) + return sfp; + sfp = sfp->next; + } + return NULL; +} + +static bool cvmx_sfp_validate_quad(struct cvmx_fdt_sfp_info *sfp, + struct cvmx_phy_gpio_leds *leds) +{ + bool multi_led = leds && (leds->next); + bool error = false; + int mod_abs; + + do { + /* Skip missing modules */ + if (dm_gpio_is_valid(&sfp->mod_abs)) + mod_abs = dm_gpio_get_value(&sfp->mod_abs); + else + mod_abs = 0; + if (!mod_abs) { + if (cvmx_sfp_read_i2c_eeprom(sfp)) { + debug("%s: Error reading eeprom for %s\n", + __func__, sfp->name); + } + if (sfp->sfp_info.rate < CVMX_SFP_RATE_10G) { + cvmx_helper_leds_show_error(leds, true); + error = true; + } else if (sfp->sfp_info.rate >= CVMX_SFP_RATE_10G) { + /* We don't support 10GBase-T modules in + * this mode. + */ + switch (sfp->sfp_info.cable_comp) { + case CVMX_SFP_CABLE_10GBASE_T: + case CVMX_SFP_CABLE_10GBASE_T_SR: + case CVMX_SFP_CABLE_5GBASE_T: + case CVMX_SFP_CABLE_2_5GBASE_T: + cvmx_helper_leds_show_error(leds, true); + error = true; + break; + default: + break; + } + } + } else if (multi_led) { + cvmx_helper_leds_show_error(leds, false); + } + + if (multi_led && leds->next) + leds = leds->next; + sfp = sfp->next_iface_sfp; + } while (sfp); + + if (!multi_led) + cvmx_helper_leds_show_error(leds, error); + + return error; +} + +/** + * Validates if the module is correct for the specified port + * + * @param[in] sfp SFP port to check + * @param xiface interface + * @param index port index + * @param speed link speed, -1 if unknown + * @param mode interface mode + * + * @return true if module is valid, false if invalid + * NOTE: This will also toggle the error LED, if present + */ +bool cvmx_sfp_validate_module(struct cvmx_fdt_sfp_info *sfp, int mode) +{ + const struct cvmx_sfp_mod_info *mod_info = &sfp->sfp_info; + int xiface = sfp->xiface; + int index = sfp->index; + struct cvmx_phy_gpio_leds *leds; + bool error = false; + bool quad_mode = false; + + debug("%s(%s, 0x%x, 0x%x, 0x%x)\n", __func__, sfp->name, xiface, index, + mode); + if (!sfp) { + debug("%s: Error: sfp is NULL\n", __func__); + return false; + } + /* No module is valid */ + leds = cvmx_helper_get_port_phy_leds(xiface, index); + if (!leds) + debug("%s: No leds for 0x%x:0x%x\n", __func__, xiface, index); + + if (mode != CVMX_HELPER_INTERFACE_MODE_XLAUI && + mode != CVMX_HELPER_INTERFACE_MODE_40G_KR4 && !sfp->is_qsfp && + sfp->last_mod_abs && leds) { + cvmx_helper_leds_show_error(leds, false); + debug("%s: %s: last_mod_abs: %d, no error\n", __func__, + sfp->name, sfp->last_mod_abs); + return true; + } + + switch (mode) { + case CVMX_HELPER_INTERFACE_MODE_RGMII: + case CVMX_HELPER_INTERFACE_MODE_GMII: + case CVMX_HELPER_INTERFACE_MODE_SGMII: + case CVMX_HELPER_INTERFACE_MODE_QSGMII: + case CVMX_HELPER_INTERFACE_MODE_AGL: + case CVMX_HELPER_INTERFACE_MODE_SPI: + if ((mod_info->active_cable && + mod_info->rate != CVMX_SFP_RATE_1G) || + mod_info->rate < CVMX_SFP_RATE_1G) + error = true; + break; + case CVMX_HELPER_INTERFACE_MODE_RXAUI: + case CVMX_HELPER_INTERFACE_MODE_XAUI: + case CVMX_HELPER_INTERFACE_MODE_10G_KR: + case CVMX_HELPER_INTERFACE_MODE_XFI: + if ((mod_info->active_cable && + mod_info->rate != CVMX_SFP_RATE_10G) || + mod_info->rate < CVMX_SFP_RATE_10G) + error = true; + break; + case CVMX_HELPER_INTERFACE_MODE_XLAUI: + case CVMX_HELPER_INTERFACE_MODE_40G_KR4: + if (!sfp->is_qsfp) { + quad_mode = true; + error = cvmx_sfp_validate_quad(sfp, leds); + } else { + if ((mod_info->active_cable && + mod_info->rate != CVMX_SFP_RATE_40G) || + mod_info->rate < CVMX_SFP_RATE_25G) + error = true; + } + break; + default: + debug("%s: Unsupported interface mode %d on xiface 0x%x\n", + __func__, mode, xiface); + return false; + } + debug("%s: %s: error: %d\n", __func__, sfp->name, error); + if (leds && !quad_mode) + cvmx_helper_leds_show_error(leds, error); + + return !error; +} diff --git a/arch/mips/mach-octeon/cvmx-helper-sgmii.c b/arch/mips/mach-octeon/cvmx-helper-sgmii.c new file mode 100644 index 0000000..b789ad5 --- /dev/null +++ b/arch/mips/mach-octeon/cvmx-helper-sgmii.c @@ -0,0 +1,781 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018-2022 Marvell International Ltd. + * + * Functions for SGMII initialization, configuration, + * and monitoring. + */ + +#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-ipd-defs.h> +#include <mach/cvmx-pcsx-defs.h> +#include <mach/cvmx-pki-defs.h> +#include <mach/cvmx-xcv-defs.h> + +#include <mach/cvmx-helper.h> +#include <mach/cvmx-helper-board.h> +#include <mach/cvmx-helper-cfg.h> + +/** + * @INTERNAL + * Perform initialization required only once for an SGMII port. + * + * @param interface to init + * @param index Index of prot on the interface + * + * @return Zero on success, negative on failure + */ +static int __cvmx_helper_sgmii_hardware_init_one_time(int interface, int index) +{ + const u64 clock_mhz = 1200; /* todo: fixme */ + union cvmx_pcsx_miscx_ctl_reg pcsx_miscx_ctl_reg; + union cvmx_pcsx_linkx_timer_count_reg pcsx_linkx_timer_count_reg; + union cvmx_gmxx_prtx_cfg gmxx_prtx_cfg; + + if (!cvmx_helper_is_port_valid(interface, index)) + return 0; + + /* Disable GMX */ + gmxx_prtx_cfg.u64 = csr_rd(CVMX_GMXX_PRTX_CFG(index, interface)); + gmxx_prtx_cfg.s.en = 0; + csr_wr(CVMX_GMXX_PRTX_CFG(index, interface), gmxx_prtx_cfg.u64); + + /* + * Write PCS*_LINK*_TIMER_COUNT_REG[COUNT] with the + * appropriate value. 1000BASE-X specifies a 10ms + * interval. SGMII specifies a 1.6ms interval. + */ + pcsx_miscx_ctl_reg.u64 = + csr_rd(CVMX_PCSX_MISCX_CTL_REG(index, interface)); + /* Adjust the MAC mode if requested by device tree */ + pcsx_miscx_ctl_reg.s.mac_phy = + cvmx_helper_get_mac_phy_mode(interface, index); + pcsx_miscx_ctl_reg.s.mode = + cvmx_helper_get_1000x_mode(interface, index); + csr_wr(CVMX_PCSX_MISCX_CTL_REG(index, interface), + pcsx_miscx_ctl_reg.u64); + + pcsx_linkx_timer_count_reg.u64 = + csr_rd(CVMX_PCSX_LINKX_TIMER_COUNT_REG(index, interface)); + if (pcsx_miscx_ctl_reg.s.mode) + /* 1000BASE-X */ + pcsx_linkx_timer_count_reg.s.count = + (10000ull * clock_mhz) >> 10; + else + /* SGMII */ + pcsx_linkx_timer_count_reg.s.count = + (1600ull * clock_mhz) >> 10; + + csr_wr(CVMX_PCSX_LINKX_TIMER_COUNT_REG(index, interface), + pcsx_linkx_timer_count_reg.u64); + + /* + * Write the advertisement register to be used as the + * tx_Config_Reg<D15:D0> of the autonegotiation. In + * 1000BASE-X mode, tx_Config_Reg<D15:D0> is PCS*_AN*_ADV_REG. + * In SGMII PHY mode, tx_Config_Reg<D15:D0> is + * PCS*_SGM*_AN_ADV_REG. In SGMII MAC mode, + * tx_Config_Reg<D15:D0> is the fixed value 0x4001, so this + * step can be skipped. + */ + if (pcsx_miscx_ctl_reg.s.mode) { + /* 1000BASE-X */ + union cvmx_pcsx_anx_adv_reg pcsx_anx_adv_reg; + + pcsx_anx_adv_reg.u64 = + csr_rd(CVMX_PCSX_ANX_ADV_REG(index, interface)); + pcsx_anx_adv_reg.s.rem_flt = 0; + pcsx_anx_adv_reg.s.pause = 3; + pcsx_anx_adv_reg.s.hfd = 1; + pcsx_anx_adv_reg.s.fd = 1; + csr_wr(CVMX_PCSX_ANX_ADV_REG(index, interface), + pcsx_anx_adv_reg.u64); + } else { + if (pcsx_miscx_ctl_reg.s.mac_phy) { + /* PHY Mode */ + union cvmx_pcsx_sgmx_an_adv_reg pcsx_sgmx_an_adv_reg; + + pcsx_sgmx_an_adv_reg.u64 = csr_rd( + CVMX_PCSX_SGMX_AN_ADV_REG(index, interface)); + pcsx_sgmx_an_adv_reg.s.dup = 1; + pcsx_sgmx_an_adv_reg.s.speed = 2; + csr_wr(CVMX_PCSX_SGMX_AN_ADV_REG(index, interface), + pcsx_sgmx_an_adv_reg.u64); + } else { + /* MAC Mode - Nothing to do */ + } + } + return 0; +} + +static int __cvmx_helper_need_g15618(void) +{ + if (OCTEON_IS_MODEL(OCTEON_CN63XX) || + OCTEON_IS_MODEL(OCTEON_CN66XX_PASS1_X) || + OCTEON_IS_MODEL(OCTEON_CN68XX)) + return 1; + else + return 0; +} + +/** + * @INTERNAL + * Initialize the SERTES link for the first time or after a loss + * of link. + * + * @param interface to init + * @param index Index of prot on the interface + * + * @return Zero on success, negative on failure + */ +static int __cvmx_helper_sgmii_hardware_init_link(int interface, int index) +{ + union cvmx_pcsx_mrx_control_reg control_reg; + union cvmx_pcsx_miscx_ctl_reg pcsx_miscx_ctl_reg; + bool phy_mode; + bool an_disable; /** Disable autonegotiation */ + bool mode_1000x; /** 1000Base-X mode */ + + if (!cvmx_helper_is_port_valid(interface, index)) + return 0; + + /* + * Take PCS through a reset sequence. + * PCS*_MR*_CONTROL_REG[PWR_DN] should be cleared to zero. + * Write PCS*_MR*_CONTROL_REG[RESET]=1 (while not changing the + * value of the other PCS*_MR*_CONTROL_REG bits). Read + * PCS*_MR*_CONTROL_REG[RESET] until it changes value to + * zero. + */ + control_reg.u64 = csr_rd(CVMX_PCSX_MRX_CONTROL_REG(index, interface)); + + /* + * Errata G-15618 requires disabling PCS soft reset in CN63XX + * pass upto 2.1. + */ + if (!__cvmx_helper_need_g15618()) { + control_reg.s.reset = 1; + csr_wr(CVMX_PCSX_MRX_CONTROL_REG(index, interface), + control_reg.u64); + if (CVMX_WAIT_FOR_FIELD64( + CVMX_PCSX_MRX_CONTROL_REG(index, interface), + cvmx_pcsx_mrx_control_reg_t, reset, ==, 0, 10000)) { + debug("SGMII%x: Timeout waiting for port %d to finish reset\n", + interface, index); + return -1; + } + } + + /* + * Write PCS*_MR*_CONTROL_REG[RST_AN]=1 to ensure a fresh + * sgmii negotiation starts. + */ + phy_mode = cvmx_helper_get_mac_phy_mode(interface, index); + an_disable = (phy_mode || + !cvmx_helper_get_port_autonegotiation(interface, index)); + + control_reg.s.an_en = !an_disable; + + /* Force a PCS reset by powering down the PCS interface + * This is needed to deal with broken Qualcomm/Atheros PHYs and switches + * which never recover if PCS is not power cycled. The alternative + * is to power cycle or hardware reset the Qualcomm devices whenever + * SGMII is initialized. + * + * This is needed for the QCA8033 PHYs as well as the QCA833X switches + * to work. The QCA8337 switch has additional SGMII problems and is + * best avoided if at all possible. Failure to power cycle PCS prevents + * any traffic from flowing between Octeon and Qualcomm devices if there + * is a warm reset. Even a software reset to the Qualcomm device will + * not work. + * + * Note that this problem has been reported between Qualcomm and other + * vendor's processors as well so this problem is not unique to + * Qualcomm and Octeon. + * + * Power cycling PCS doesn't hurt anything with non-Qualcomm devices + * other than adding a 25ms delay during initialization. + */ + control_reg.s.pwr_dn = 1; + csr_wr(CVMX_PCSX_MRX_CONTROL_REG(index, interface), control_reg.u64); + csr_rd(CVMX_PCSX_MRX_CONTROL_REG(index, interface)); + + /* 25ms should be enough, 10ms is too short */ + mdelay(25); + + control_reg.s.pwr_dn = 0; + csr_wr(CVMX_PCSX_MRX_CONTROL_REG(index, interface), control_reg.u64); + + /* The Cortina PHY runs in 1000base-X mode */ + mode_1000x = cvmx_helper_get_1000x_mode(interface, index); + pcsx_miscx_ctl_reg.u64 = + csr_rd(CVMX_PCSX_MISCX_CTL_REG(index, interface)); + pcsx_miscx_ctl_reg.s.mode = mode_1000x; + pcsx_miscx_ctl_reg.s.mac_phy = phy_mode; + csr_wr(CVMX_PCSX_MISCX_CTL_REG(index, interface), + pcsx_miscx_ctl_reg.u64); + if (an_disable) + /* In PHY mode we can't query the link status so we just + * assume that the link is up. + */ + return 0; + + /* + * Wait for PCS*_MR*_STATUS_REG[AN_CPT] to be set, indicating + * that sgmii autonegotiation is complete. In MAC mode this + * isn't an ethernet link, but a link between Octeon and the + * PHY. + */ + if (CVMX_WAIT_FOR_FIELD64(CVMX_PCSX_MRX_STATUS_REG(index, interface), + union cvmx_pcsx_mrx_status_reg, an_cpt, ==, 1, + 10000)) { + debug("SGMII%x: Port %d link timeout\n", interface, index); + return -1; + } + return 0; +} + +/** + * @INTERNAL + * Configure an SGMII link to the specified speed after the SERTES + * link is up. + * + * @param interface to init + * @param index Index of prot on the interface + * @param link_info Link state to configure + * + * @return Zero on success, negative on failure + */ +static int +__cvmx_helper_sgmii_hardware_init_link_speed(int interface, int index, + cvmx_helper_link_info_t link_info) +{ + int is_enabled; + union cvmx_gmxx_prtx_cfg gmxx_prtx_cfg; + union cvmx_pcsx_miscx_ctl_reg pcsx_miscx_ctl_reg; + + if (!cvmx_helper_is_port_valid(interface, index)) + return 0; + + /* Disable GMX before we make any changes. Remember the enable state */ + gmxx_prtx_cfg.u64 = csr_rd(CVMX_GMXX_PRTX_CFG(index, interface)); + is_enabled = gmxx_prtx_cfg.s.en; + gmxx_prtx_cfg.s.en = 0; + csr_wr(CVMX_GMXX_PRTX_CFG(index, interface), gmxx_prtx_cfg.u64); + + /* Wait for GMX to be idle */ + if (CVMX_WAIT_FOR_FIELD64(CVMX_GMXX_PRTX_CFG(index, interface), + cvmx_gmxx_prtx_cfg_t, rx_idle, ==, 1, + 10000) || + CVMX_WAIT_FOR_FIELD64(CVMX_GMXX_PRTX_CFG(index, interface), + cvmx_gmxx_prtx_cfg_t, tx_idle, ==, 1, + 10000)) { + debug("SGMII%d: Timeout waiting for port %d to be idle\n", + interface, index); + return -1; + } + + /* Read GMX CFG again to make sure the disable completed */ + gmxx_prtx_cfg.u64 = csr_rd(CVMX_GMXX_PRTX_CFG(index, interface)); + + /* + * Get the misc control for PCS. We will need to set the + * duplication amount. + */ + pcsx_miscx_ctl_reg.u64 = + csr_rd(CVMX_PCSX_MISCX_CTL_REG(index, interface)); + + /* + * Use GMXENO to force the link down if the status we get says + * it should be down. + */ + pcsx_miscx_ctl_reg.s.gmxeno = !link_info.s.link_up; + + /* Only change the duplex setting if the link is up */ + if (link_info.s.link_up) + gmxx_prtx_cfg.s.duplex = link_info.s.full_duplex; + + /* Do speed based setting for GMX */ + switch (link_info.s.speed) { + case 10: + gmxx_prtx_cfg.s.speed = 0; + gmxx_prtx_cfg.s.speed_msb = 1; + gmxx_prtx_cfg.s.slottime = 0; + /* Setting from GMX-603 */ + pcsx_miscx_ctl_reg.s.samp_pt = 25; + csr_wr(CVMX_GMXX_TXX_SLOT(index, interface), 64); + csr_wr(CVMX_GMXX_TXX_BURST(index, interface), 0); + break; + case 100: + gmxx_prtx_cfg.s.speed = 0; + gmxx_prtx_cfg.s.speed_msb = 0; + gmxx_prtx_cfg.s.slottime = 0; + pcsx_miscx_ctl_reg.s.samp_pt = 0x5; + csr_wr(CVMX_GMXX_TXX_SLOT(index, interface), 64); + csr_wr(CVMX_GMXX_TXX_BURST(index, interface), 0); + break; + case 1000: + gmxx_prtx_cfg.s.speed = 1; + gmxx_prtx_cfg.s.speed_msb = 0; + gmxx_prtx_cfg.s.slottime = 1; + pcsx_miscx_ctl_reg.s.samp_pt = 1; + csr_wr(CVMX_GMXX_TXX_SLOT(index, interface), 512); + if (gmxx_prtx_cfg.s.duplex) + /* full duplex */ + csr_wr(CVMX_GMXX_TXX_BURST(index, interface), 0); + else + /* half duplex */ + csr_wr(CVMX_GMXX_TXX_BURST(index, interface), 8192); + break; + default: + break; + } + + /* Write the new misc control for PCS */ + csr_wr(CVMX_PCSX_MISCX_CTL_REG(index, interface), + pcsx_miscx_ctl_reg.u64); + + /* Write the new GMX settings with the port still disabled */ + csr_wr(CVMX_GMXX_PRTX_CFG(index, interface), gmxx_prtx_cfg.u64); + + /* Read GMX CFG again to make sure the config completed */ + gmxx_prtx_cfg.u64 = csr_rd(CVMX_GMXX_PRTX_CFG(index, interface)); + + /* Restore the enabled / disabled state */ + gmxx_prtx_cfg.s.en = is_enabled; + csr_wr(CVMX_GMXX_PRTX_CFG(index, interface), gmxx_prtx_cfg.u64); + + return 0; +} + +/** + * @INTERNAL + * Bring up the SGMII interface to be ready for packet I/O but + * leave I/O disabled using the GMX override. This function + * follows the bringup documented in 10.6.3 of the manual. + * + * @param interface to bringup + * @param num_ports Number of ports on the interface + * + * @return Zero on success, negative on failure + */ +static int __cvmx_helper_sgmii_hardware_init(int interface, int num_ports) +{ + int index; + int do_link_set = 1; + + /* + * CN63XX Pass 1.0 errata G-14395 requires the QLM De-emphasis + * be programmed. + */ + if (OCTEON_IS_MODEL(OCTEON_CN63XX_PASS1_0)) { + union cvmx_ciu_qlm2 ciu_qlm; + + ciu_qlm.u64 = csr_rd(CVMX_CIU_QLM2); + ciu_qlm.s.txbypass = 1; + ciu_qlm.s.txdeemph = 0xf; + ciu_qlm.s.txmargin = 0xd; + csr_wr(CVMX_CIU_QLM2, ciu_qlm.u64); + } + + /* + * CN63XX Pass 2.x errata G-15273 requires the QLM De-emphasis + * be programmed when using a 156.25Mhz ref clock. + */ + if (OCTEON_IS_MODEL(OCTEON_CN63XX_PASS2_X)) { + /* Read the QLM speed pins */ + union cvmx_mio_rst_boot mio_rst_boot; + + mio_rst_boot.u64 = csr_rd(CVMX_MIO_RST_BOOT); + + if (mio_rst_boot.cn63xx.qlm2_spd == 4) { + union cvmx_ciu_qlm2 ciu_qlm; + + ciu_qlm.u64 = csr_rd(CVMX_CIU_QLM2); + ciu_qlm.s.txbypass = 1; + ciu_qlm.s.txdeemph = 0x0; + ciu_qlm.s.txmargin = 0xf; + csr_wr(CVMX_CIU_QLM2, ciu_qlm.u64); + } + } + + __cvmx_helper_setup_gmx(interface, num_ports); + + for (index = 0; index < num_ports; index++) { + int ipd_port = cvmx_helper_get_ipd_port(interface, index); + + if (!cvmx_helper_is_port_valid(interface, index)) + continue; + __cvmx_helper_sgmii_hardware_init_one_time(interface, index); + if (do_link_set) + __cvmx_helper_sgmii_link_set(ipd_port, + __cvmx_helper_sgmii_link_get(ipd_port)); + } + + return 0; +} + +int __cvmx_helper_sgmii_enumerate(int xiface) +{ + if (OCTEON_IS_MODEL(OCTEON_CNF71XX)) + return 2; + if (OCTEON_IS_MODEL(OCTEON_CN70XX)) { + struct cvmx_xiface xi = + cvmx_helper_xiface_to_node_interface(xiface); + enum cvmx_qlm_mode qlm_mode = + cvmx_qlm_get_dlm_mode(0, xi.interface); + + if (qlm_mode == CVMX_QLM_MODE_SGMII) + return 1; + else if (qlm_mode == CVMX_QLM_MODE_QSGMII) + return 4; + return 0; + } + return 4; +} + +/** + * @INTERNAL + * Probe a SGMII interface and determine the number of ports + * connected to it. The SGMII interface should still be down after + * this call. + * + * @param xiface Interface to probe + * + * @return Number of ports on the interface. Zero to disable. + */ +int __cvmx_helper_sgmii_probe(int xiface) +{ + struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); + int interface = xi.interface; + union cvmx_gmxx_inf_mode mode; + int ports; + + /* + * Check if QLM is configured correct for SGMII, verify the + * speed as well as mode. + */ + if (OCTEON_IS_OCTEON2()) { + int qlm = cvmx_qlm_interface(xiface); + + if (cvmx_qlm_get_mode(qlm) != CVMX_QLM_MODE_SGMII) + return 0; + } + + /* Do not enable the interface if is not in SGMII mode */ + ports = __cvmx_helper_sgmii_enumerate(xiface); + + if (ports <= 0) + return 0; + + /* + * Due to errata GMX-700 on CN56XXp1.x and CN52XXp1.x, the + * interface needs to be enabled before IPD otherwise per port + * backpressure may not work properly. + */ + mode.u64 = csr_rd(CVMX_GMXX_INF_MODE(interface)); + mode.s.en = 1; + csr_wr(CVMX_GMXX_INF_MODE(interface), mode.u64); + + return ports; +} + +/** + * @INTERNAL + * Bringup and enable a SGMII interface. After this call packet + * I/O should be fully functional. This is called with IPD + * enabled but PKO disabled. + * + * @param xiface Interface to bring up + * + * @return Zero on success, negative on failure + */ +int __cvmx_helper_sgmii_enable(int xiface) +{ + int num_ports = cvmx_helper_ports_on_interface(xiface); + struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); + int interface = xi.interface; + int index; + + /* Setup PKND and BPID */ + if (octeon_has_feature(OCTEON_FEATURE_PKND)) { + for (index = 0; index < num_ports; index++) { + union cvmx_gmxx_bpid_msk bpid_msk; + union cvmx_gmxx_bpid_mapx bpid_map; + union cvmx_gmxx_prtx_cfg gmxx_prtx_cfg; + + if (!cvmx_helper_is_port_valid(interface, index)) + continue; + /* Setup PKIND */ + gmxx_prtx_cfg.u64 = + csr_rd(CVMX_GMXX_PRTX_CFG(index, interface)); + gmxx_prtx_cfg.s.pknd = + cvmx_helper_get_pknd(interface, index); + csr_wr(CVMX_GMXX_PRTX_CFG(index, interface), + gmxx_prtx_cfg.u64); + + /* Setup BPID */ + bpid_map.u64 = + csr_rd(CVMX_GMXX_BPID_MAPX(index, interface)); + bpid_map.s.val = 1; + bpid_map.s.bpid = + cvmx_helper_get_bpid(interface, index); + csr_wr(CVMX_GMXX_BPID_MAPX(index, interface), + bpid_map.u64); + + bpid_msk.u64 = csr_rd(CVMX_GMXX_BPID_MSK(interface)); + bpid_msk.s.msk_or |= (1 << index); + bpid_msk.s.msk_and &= ~(1 << index); + csr_wr(CVMX_GMXX_BPID_MSK(interface), bpid_msk.u64); + } + } + + __cvmx_helper_sgmii_hardware_init(interface, num_ports); + + /* CN68XX adds the padding and FCS in PKO, not GMX */ + if (OCTEON_IS_MODEL(OCTEON_CN68XX)) { + union cvmx_gmxx_txx_append gmxx_txx_append_cfg; + + for (index = 0; index < num_ports; index++) { + if (!cvmx_helper_is_port_valid(interface, index)) + continue; + gmxx_txx_append_cfg.u64 = + csr_rd(CVMX_GMXX_TXX_APPEND(index, interface)); + gmxx_txx_append_cfg.s.fcs = 0; + gmxx_txx_append_cfg.s.pad = 0; + csr_wr(CVMX_GMXX_TXX_APPEND(index, interface), + gmxx_txx_append_cfg.u64); + } + } + + /* Enable running disparity check for QSGMII interface */ + if (OCTEON_IS_MODEL(OCTEON_CN70XX) && num_ports > 1) { + union cvmx_gmxx_qsgmii_ctl qsgmii_ctl; + + qsgmii_ctl.u64 = 0; + qsgmii_ctl.s.disparity = 1; + csr_wr(CVMX_GMXX_QSGMII_CTL(interface), qsgmii_ctl.u64); + } + + for (index = 0; index < num_ports; index++) { + union cvmx_gmxx_txx_append append_cfg; + union cvmx_gmxx_txx_sgmii_ctl sgmii_ctl; + union cvmx_gmxx_prtx_cfg gmxx_prtx_cfg; + + if (!cvmx_helper_is_port_valid(interface, index)) + continue; + /* + * Clear the align bit if preamble is set to attain + * maximum tx rate. + */ + append_cfg.u64 = csr_rd(CVMX_GMXX_TXX_APPEND(index, interface)); + sgmii_ctl.u64 = + csr_rd(CVMX_GMXX_TXX_SGMII_CTL(index, interface)); + sgmii_ctl.s.align = append_cfg.s.preamble ? 0 : 1; + csr_wr(CVMX_GMXX_TXX_SGMII_CTL(index, interface), + sgmii_ctl.u64); + + gmxx_prtx_cfg.u64 = + csr_rd(CVMX_GMXX_PRTX_CFG(index, interface)); + gmxx_prtx_cfg.s.en = 1; + csr_wr(CVMX_GMXX_PRTX_CFG(index, interface), gmxx_prtx_cfg.u64); + } + return 0; +} + +/** + * @INTERNAL + * Return the link state of an IPD/PKO port as returned by + * auto negotiation. The result of this function may not match + * Octeon's link config if auto negotiation has changed since + * the last call to cvmx_helper_link_set(). + * + * @param ipd_port IPD/PKO port to query + * + * @return Link state + */ +cvmx_helper_link_info_t __cvmx_helper_sgmii_link_get(int ipd_port) +{ + cvmx_helper_link_info_t result; + union cvmx_pcsx_miscx_ctl_reg pcsx_miscx_ctl_reg; + int interface = cvmx_helper_get_interface_num(ipd_port); + int index = cvmx_helper_get_interface_index_num(ipd_port); + union cvmx_pcsx_mrx_control_reg pcsx_mrx_control_reg; + int speed = 1000; + int qlm; + + result.u64 = 0; + + if (!cvmx_helper_is_port_valid(interface, index)) + return result; + + if (OCTEON_IS_MODEL(OCTEON_CN66XX)) { + union cvmx_gmxx_inf_mode inf_mode; + + inf_mode.u64 = csr_rd(CVMX_GMXX_INF_MODE(interface)); + if (inf_mode.s.rate & (1 << index)) + speed = 2500; + else + speed = 1000; + } else if (OCTEON_IS_MODEL(OCTEON_CN6XXX)) { + qlm = cvmx_qlm_interface(interface); + speed = cvmx_qlm_get_gbaud_mhz(qlm) * 8 / 10; + } else if (OCTEON_IS_MODEL(OCTEON_CNF71XX)) { + speed = cvmx_qlm_get_gbaud_mhz(0) * 8 / 10; + } else if (OCTEON_IS_MODEL(OCTEON_CN70XX)) { + speed = cvmx_qlm_get_gbaud_mhz(0) * 8 / 10; + if (cvmx_qlm_get_dlm_mode(0, interface) == CVMX_QLM_MODE_SGMII) + speed >>= 1; + else + speed >>= 2; + } + + pcsx_mrx_control_reg.u64 = + csr_rd(CVMX_PCSX_MRX_CONTROL_REG(index, interface)); + if (pcsx_mrx_control_reg.s.loopbck1) { + /* Force 1Gbps full duplex link for internal loopback */ + result.s.link_up = 1; + result.s.full_duplex = 1; + result.s.speed = speed; + return result; + } + + pcsx_miscx_ctl_reg.u64 = + csr_rd(CVMX_PCSX_MISCX_CTL_REG(index, interface)); + if (pcsx_miscx_ctl_reg.s.mac_phy || + cvmx_helper_get_port_force_link_up(interface, index)) { + /* PHY Mode */ + /* Note that this also works for 1000base-X mode */ + + result.s.speed = speed; + result.s.full_duplex = 1; + result.s.link_up = 1; + return result; + } + + /* MAC Mode */ + return __cvmx_helper_board_link_get(ipd_port); +} + +/** + * @INTERNAL + * Configure an IPD/PKO port for the specified link state. This + * function does not influence auto negotiation at the PHY level. + * The passed link state must always match the link state returned + * by cvmx_helper_link_get(). It is normally best to use + * cvmx_helper_link_autoconf() instead. + * + * @param ipd_port IPD/PKO port to configure + * @param link_info The new link state + * + * @return Zero on success, negative on failure + */ +int __cvmx_helper_sgmii_link_set(int ipd_port, + cvmx_helper_link_info_t link_info) +{ + union cvmx_pcsx_mrx_control_reg control_reg; + int interface = cvmx_helper_get_interface_num(ipd_port); + int index = cvmx_helper_get_interface_index_num(ipd_port); + + if (!cvmx_helper_is_port_valid(interface, index)) + return 0; + + /* For some devices, i.e. the Qualcomm QCA8337 switch we need to power + * down the PCS interface when the link goes down and power it back + * up when the link returns. + */ + if (link_info.s.link_up || !__cvmx_helper_need_g15618()) { + __cvmx_helper_sgmii_hardware_init_link(interface, index); + } else { + union cvmx_pcsx_miscx_ctl_reg pcsx_miscx_ctl_reg; + + pcsx_miscx_ctl_reg.u64 = + csr_rd(CVMX_PCSX_MISCX_CTL_REG(index, interface)); + + /* Disable autonegotiation when MAC mode is enabled or + * autonegotiation is disabled. + */ + control_reg.u64 = + csr_rd(CVMX_PCSX_MRX_CONTROL_REG(index, interface)); + if (pcsx_miscx_ctl_reg.s.mac_phy == 0 || + !cvmx_helper_get_port_autonegotiation(interface, index)) { + control_reg.s.an_en = 0; + control_reg.s.spdmsb = 1; + control_reg.s.spdlsb = 0; + control_reg.s.dup = 1; + } + csr_wr(CVMX_PCSX_MRX_CONTROL_REG(index, interface), + control_reg.u64); + csr_rd(CVMX_PCSX_MRX_CONTROL_REG(index, interface)); + /* + * Use GMXENO to force the link down it will get + * reenabled later... + */ + pcsx_miscx_ctl_reg.s.gmxeno = 1; + csr_wr(CVMX_PCSX_MISCX_CTL_REG(index, interface), + pcsx_miscx_ctl_reg.u64); + csr_rd(CVMX_PCSX_MISCX_CTL_REG(index, interface)); + return 0; + } + return __cvmx_helper_sgmii_hardware_init_link_speed(interface, index, + link_info); +} + +/** + * @INTERNAL + * Configure a port for internal and/or external loopback. Internal loopback + * causes packets sent by the port to be received by Octeon. External loopback + * causes packets received from the wire to sent out again. + * + * @param ipd_port IPD/PKO port to loopback. + * @param enable_internal + * Non zero if you want internal loopback + * @param enable_external + * Non zero if you want external loopback + * + * @return Zero on success, negative on failure. + */ +int __cvmx_helper_sgmii_configure_loopback(int ipd_port, int enable_internal, + int enable_external) +{ + int interface = cvmx_helper_get_interface_num(ipd_port); + int index = cvmx_helper_get_interface_index_num(ipd_port); + union cvmx_pcsx_mrx_control_reg pcsx_mrx_control_reg; + union cvmx_pcsx_miscx_ctl_reg pcsx_miscx_ctl_reg; + + if (!cvmx_helper_is_port_valid(interface, index)) + return 0; + + pcsx_mrx_control_reg.u64 = + csr_rd(CVMX_PCSX_MRX_CONTROL_REG(index, interface)); + pcsx_mrx_control_reg.s.loopbck1 = enable_internal; + csr_wr(CVMX_PCSX_MRX_CONTROL_REG(index, interface), + pcsx_mrx_control_reg.u64); + + pcsx_miscx_ctl_reg.u64 = + csr_rd(CVMX_PCSX_MISCX_CTL_REG(index, interface)); + pcsx_miscx_ctl_reg.s.loopbck2 = enable_external; + csr_wr(CVMX_PCSX_MISCX_CTL_REG(index, interface), + pcsx_miscx_ctl_reg.u64); + + __cvmx_helper_sgmii_hardware_init_link(interface, index); + return 0; +} diff --git a/arch/mips/mach-octeon/cvmx-helper-util.c b/arch/mips/mach-octeon/cvmx-helper-util.c index 92298fd..6d775ee 100644 --- a/arch/mips/mach-octeon/cvmx-helper-util.c +++ b/arch/mips/mach-octeon/cvmx-helper-util.c @@ -182,127 +182,6 @@ const char *cvmx_helper_interface_mode_to_string(cvmx_helper_interface_mode_t mo } /** - * Debug routine to dump the packet structure to the console - * - * @param work Work queue entry containing the packet to dump - * @return - */ -int cvmx_helper_dump_packet(cvmx_wqe_t *work) -{ - u64 count; - u64 remaining_bytes; - union cvmx_buf_ptr buffer_ptr; - cvmx_buf_ptr_pki_t bptr; - cvmx_wqe_78xx_t *wqe = (void *)work; - u64 start_of_buffer; - u8 *data_address; - u8 *end_of_data; - - if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { - cvmx_pki_dump_wqe(wqe); - cvmx_wqe_pki_errata_20776(work); - } else { - debug("WORD0 = %lx\n", (unsigned long)work->word0.u64); - debug("WORD1 = %lx\n", (unsigned long)work->word1.u64); - debug("WORD2 = %lx\n", (unsigned long)work->word2.u64); - debug("Packet Length: %u\n", cvmx_wqe_get_len(work)); - debug(" Input Port: %u\n", cvmx_wqe_get_port(work)); - debug(" QoS: %u\n", cvmx_wqe_get_qos(work)); - debug(" Buffers: %u\n", cvmx_wqe_get_bufs(work)); - } - - if (cvmx_wqe_get_bufs(work) == 0) { - int wqe_pool; - - if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { - debug("%s: ERROR: Unexpected bufs==0 in WQE\n", __func__); - return -1; - } - wqe_pool = (int)cvmx_fpa_get_wqe_pool(); - buffer_ptr.u64 = 0; - buffer_ptr.s.pool = wqe_pool; - - buffer_ptr.s.size = 128; - buffer_ptr.s.addr = cvmx_ptr_to_phys(work->packet_data); - if (cvmx_likely(!work->word2.s.not_IP)) { - union cvmx_pip_ip_offset pip_ip_offset; - - pip_ip_offset.u64 = csr_rd(CVMX_PIP_IP_OFFSET); - buffer_ptr.s.addr += - (pip_ip_offset.s.offset << 3) - work->word2.s.ip_offset; - buffer_ptr.s.addr += (work->word2.s.is_v6 ^ 1) << 2; - } else { - /* - * WARNING: This code assume that the packet - * is not RAW. If it was, we would use - * PIP_GBL_CFG[RAW_SHF] instead of - * PIP_GBL_CFG[NIP_SHF]. - */ - union cvmx_pip_gbl_cfg pip_gbl_cfg; - - pip_gbl_cfg.u64 = csr_rd(CVMX_PIP_GBL_CFG); - buffer_ptr.s.addr += pip_gbl_cfg.s.nip_shf; - } - } else { - buffer_ptr = work->packet_ptr; - } - - remaining_bytes = cvmx_wqe_get_len(work); - - while (remaining_bytes) { - /* native cn78xx buffer format, unless legacy-translated */ - if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE) && !wqe->pki_wqe_translated) { - bptr.u64 = buffer_ptr.u64; - /* XXX- assumes cache-line aligned buffer */ - start_of_buffer = (bptr.addr >> 7) << 7; - debug(" Buffer Start:%llx\n", (unsigned long long)start_of_buffer); - debug(" Buffer Data: %llx\n", (unsigned long long)bptr.addr); - debug(" Buffer Size: %u\n", bptr.size); - data_address = (uint8_t *)cvmx_phys_to_ptr(bptr.addr); - end_of_data = data_address + bptr.size; - } else { - start_of_buffer = ((buffer_ptr.s.addr >> 7) - buffer_ptr.s.back) << 7; - debug(" Buffer Start:%llx\n", (unsigned long long)start_of_buffer); - debug(" Buffer I : %u\n", buffer_ptr.s.i); - debug(" Buffer Back: %u\n", buffer_ptr.s.back); - debug(" Buffer Pool: %u\n", buffer_ptr.s.pool); - debug(" Buffer Data: %llx\n", (unsigned long long)buffer_ptr.s.addr); - debug(" Buffer Size: %u\n", buffer_ptr.s.size); - data_address = (uint8_t *)cvmx_phys_to_ptr(buffer_ptr.s.addr); - end_of_data = data_address + buffer_ptr.s.size; - } - - debug("\t\t"); - count = 0; - while (data_address < end_of_data) { - if (remaining_bytes == 0) - break; - - remaining_bytes--; - debug("%02x", (unsigned int)*data_address); - data_address++; - if (remaining_bytes && count == 7) { - debug("\n\t\t"); - count = 0; - } else { - count++; - } - } - debug("\n"); - - if (remaining_bytes) { - if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE) && - !wqe->pki_wqe_translated) - buffer_ptr.u64 = *(uint64_t *)cvmx_phys_to_ptr(bptr.addr - 8); - else - buffer_ptr.u64 = - *(uint64_t *)cvmx_phys_to_ptr(buffer_ptr.s.addr - 8); - } - } - return 0; -} - -/** * @INTERNAL * * Extract NO_WPTR mode from PIP/IPD register @@ -678,68 +557,6 @@ void cvmx_helper_free_packet_data(cvmx_wqe_t *work) } } -void cvmx_helper_setup_legacy_red(int pass_thresh, int drop_thresh) -{ - unsigned int node = cvmx_get_node_num(); - int aura, bpid; - int buf_cnt; - bool ena_red = 0, ena_drop = 0, ena_bp = 0; - -#define FPA_RED_AVG_DLY 1 -#define FPA_RED_LVL_DLY 3 -#define FPA_QOS_AVRG 0 - /* Trying to make it backward compatible with older chips */ - - /* Setting up avg_dly and prb_dly, enable bits */ - if (octeon_has_feature(OCTEON_FEATURE_FPA3)) { - cvmx_fpa3_config_red_params(node, FPA_QOS_AVRG, - FPA_RED_LVL_DLY, FPA_RED_AVG_DLY); - } - - /* Disable backpressure on queued buffers which is aura in 78xx*/ - /* - * Assumption is that all packets from all interface and ports goes - * in same poolx/aurax for backward compatibility - */ - aura = cvmx_fpa_get_packet_pool(); - buf_cnt = cvmx_fpa_get_packet_pool_buffer_count(); - pass_thresh = buf_cnt - pass_thresh; - drop_thresh = buf_cnt - drop_thresh; - /* Map aura to bpid 0*/ - bpid = 0; - cvmx_pki_write_aura_bpid(node, aura, bpid); - /* Don't enable back pressure */ - ena_bp = 0; - /* enable RED */ - ena_red = 1; - /* - * This will enable RED on all interfaces since - * they all have packet buffer coming from same aura - */ - cvmx_helper_setup_aura_qos(node, aura, ena_red, ena_drop, pass_thresh, - drop_thresh, ena_bp, 0); -} - -/** - * Setup Random Early Drop to automatically begin dropping packets. - * - * @param pass_thresh - * Packets will begin slowly dropping when there are less than - * this many packet buffers free in FPA 0. - * @param drop_thresh - * All incoming packets will be dropped when there are less - * than this many free packet buffers in FPA 0. - * Return: Zero on success. Negative on failure - */ -int cvmx_helper_setup_red(int pass_thresh, int drop_thresh) -{ - if (octeon_has_feature(OCTEON_FEATURE_PKI)) - cvmx_helper_setup_legacy_red(pass_thresh, drop_thresh); - else - cvmx_ipd_setup_red(pass_thresh, drop_thresh); - return 0; -} - /** * @INTERNAL * Setup the common GMX settings that determine the number of @@ -980,35 +797,6 @@ int cvmx_helper_get_bpid(int interface, int port) } /** - * Display interface statistics. - * - * @param port IPD/PKO port number - * - * Return: none - */ -void cvmx_helper_show_stats(int port) -{ - cvmx_pip_port_status_t status; - cvmx_pko_port_status_t pko_status; - - /* ILK stats */ - if (octeon_has_feature(OCTEON_FEATURE_ILK)) - __cvmx_helper_ilk_show_stats(); - - /* PIP stats */ - cvmx_pip_get_port_stats(port, 0, &status); - debug("port %d: the number of packets - ipd: %d\n", port, - (int)status.packets); - - /* PKO stats */ - cvmx_pko_get_port_status(port, 0, &pko_status); - debug("port %d: the number of packets - pko: %d\n", port, - (int)pko_status.packets); - - /* TODO: other stats */ -} - -/** * Returns the interface number for an IPD/PKO port number. * * @param ipd_port IPD/PKO port number @@ -1187,39 +975,3 @@ int cvmx_helper_get_interface_index_num(int ipd_port) return -1; } - -/** - * Prints out a buffer with the address, hex bytes, and ASCII - * - * @param addr Start address to print on the left - * @param[in] buffer array of bytes to print - * @param count Number of bytes to print - */ -void cvmx_print_buffer_u8(unsigned int addr, const uint8_t *buffer, - size_t count) -{ - uint i; - - while (count) { - unsigned int linelen = count < 16 ? count : 16; - - debug("%08x:", addr); - - for (i = 0; i < linelen; i++) - debug(" %0*x", 2, buffer[i]); - - while (i++ < 17) - debug(" "); - - for (i = 0; i < linelen; i++) { - if (buffer[i] >= 0x20 && buffer[i] < 0x7f) - debug("%c", buffer[i]); - else - debug("."); - } - debug("\n"); - addr += linelen; - buffer += linelen; - count -= linelen; - } -} diff --git a/arch/mips/mach-octeon/cvmx-helper-xaui.c b/arch/mips/mach-octeon/cvmx-helper-xaui.c new file mode 100644 index 0000000..2eb3b7d --- /dev/null +++ b/arch/mips/mach-octeon/cvmx-helper-xaui.c @@ -0,0 +1,518 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018-2022 Marvell International Ltd. + * + * Functions for XAUI initialization, configuration, + * and monitoring. + */ + +#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-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-helper.h> +#include <mach/cvmx-helper-board.h> +#include <mach/cvmx-helper-cfg.h> + +int __cvmx_helper_xaui_enumerate(int xiface) +{ + struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); + int interface = xi.interface; + union cvmx_gmxx_hg2_control gmx_hg2_control; + + if (OCTEON_IS_MODEL(OCTEON_CN70XX)) { + enum cvmx_qlm_mode qlm_mode = + cvmx_qlm_get_dlm_mode(0, interface); + + if (qlm_mode == CVMX_QLM_MODE_RXAUI) + return 1; + return 0; + } + /* If HiGig2 is enabled return 16 ports, otherwise return 1 port */ + gmx_hg2_control.u64 = csr_rd(CVMX_GMXX_HG2_CONTROL(interface)); + if (gmx_hg2_control.s.hg2tx_en) + return 16; + else + return 1; +} + +/** + * @INTERNAL + * Probe a XAUI interface and determine the number of ports + * connected to it. The XAUI interface should still be down + * after this call. + * + * @param xiface Interface to probe + * + * @return Number of ports on the interface. Zero to disable. + */ +int __cvmx_helper_xaui_probe(int xiface) +{ + int i, ports; + struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); + int interface = xi.interface; + union cvmx_gmxx_inf_mode mode; + + /* + * CN63XX Pass 1.0 errata G-14395 requires the QLM De-emphasis + * be programmed. + */ + if (OCTEON_IS_MODEL(OCTEON_CN63XX_PASS1_0)) { + union cvmx_ciu_qlm2 ciu_qlm; + + ciu_qlm.u64 = csr_rd(CVMX_CIU_QLM2); + ciu_qlm.s.txbypass = 1; + ciu_qlm.s.txdeemph = 0x5; + ciu_qlm.s.txmargin = 0x1a; + csr_wr(CVMX_CIU_QLM2, ciu_qlm.u64); + } + + /* + * CN63XX Pass 2.x errata G-15273 requires the QLM De-emphasis + * be programmed when using a 156.25Mhz ref clock. + */ + if (OCTEON_IS_MODEL(OCTEON_CN63XX_PASS2_X)) { + /* Read the QLM speed pins */ + union cvmx_mio_rst_boot mio_rst_boot; + + mio_rst_boot.u64 = csr_rd(CVMX_MIO_RST_BOOT); + + if (mio_rst_boot.cn63xx.qlm2_spd == 0xb) { + union cvmx_ciu_qlm2 ciu_qlm; + + ciu_qlm.u64 = csr_rd(CVMX_CIU_QLM2); + ciu_qlm.s.txbypass = 1; + ciu_qlm.s.txdeemph = 0xa; + ciu_qlm.s.txmargin = 0x1f; + csr_wr(CVMX_CIU_QLM2, ciu_qlm.u64); + } + } + + /* + * Check if QLM is configured correct for XAUI/RXAUI, verify + * the speed as well as mode. + */ + if (OCTEON_IS_MODEL(OCTEON_CN6XXX)) { + int qlm = cvmx_qlm_interface(xiface); + enum cvmx_qlm_mode mode = cvmx_qlm_get_mode(qlm); + + if (mode != CVMX_QLM_MODE_XAUI && mode != CVMX_QLM_MODE_RXAUI) + return 0; + } + + ports = __cvmx_helper_xaui_enumerate(xiface); + + if (ports <= 0) + return 0; + + /* + * Due to errata GMX-700 on CN56XXp1.x and CN52XXp1.x, the + * interface needs to be enabled before IPD otherwise per port + * backpressure may not work properly. + */ + mode.u64 = csr_rd(CVMX_GMXX_INF_MODE(interface)); + mode.s.en = 1; + csr_wr(CVMX_GMXX_INF_MODE(interface), mode.u64); + + if (!OCTEON_IS_MODEL(OCTEON_CN68XX) && + !OCTEON_IS_MODEL(OCTEON_CN70XX)) { + /* + * Setup PKO to support 16 ports for HiGig2 virtual + * ports. We're pointing all of the PKO packet ports + * for this interface to the XAUI. This allows us to + * use HiGig2 backpressure per port. + */ + for (i = 0; i < 16; i++) { + union cvmx_pko_mem_port_ptrs pko_mem_port_ptrs; + + pko_mem_port_ptrs.u64 = 0; + /* + * We set each PKO port to have equal priority + * in a round robin fashion. + */ + pko_mem_port_ptrs.s.static_p = 0; + pko_mem_port_ptrs.s.qos_mask = 0xff; + /* All PKO ports map to the same XAUI hardware port */ + pko_mem_port_ptrs.s.eid = interface * 4; + pko_mem_port_ptrs.s.pid = interface * 16 + i; + pko_mem_port_ptrs.s.bp_port = interface * 16 + i; + csr_wr(CVMX_PKO_MEM_PORT_PTRS, pko_mem_port_ptrs.u64); + } + } + + return ports; +} + +/** + * @INTERNAL + * Bringup XAUI interface. After this call packet I/O should be + * fully functional. + * + * @param interface to bring up + * + * @return Zero on success, negative on failure + */ +int __cvmx_helper_xaui_link_init(int interface) +{ + union cvmx_gmxx_prtx_cfg gmx_cfg; + union cvmx_pcsxx_control1_reg xaui_ctl; + union cvmx_pcsxx_misc_ctl_reg misc_ctl; + union cvmx_gmxx_tx_xaui_ctl tx_ctl; + + /* (1) Interface has already been enabled. */ + + /* (2) Disable GMX. */ + misc_ctl.u64 = csr_rd(CVMX_PCSXX_MISC_CTL_REG(interface)); + misc_ctl.s.gmxeno = 1; + csr_wr(CVMX_PCSXX_MISC_CTL_REG(interface), misc_ctl.u64); + + /* (3) Disable GMX and PCSX interrupts. */ + csr_wr(CVMX_GMXX_RXX_INT_EN(0, interface), 0x0); + csr_wr(CVMX_GMXX_TX_INT_EN(interface), 0x0); + csr_wr(CVMX_PCSXX_INT_EN_REG(interface), 0x0); + + /* (4) Bring up the PCSX and GMX reconciliation layer. */ + /* (4)a Set polarity and lane swapping. */ + /* (4)b */ + tx_ctl.u64 = csr_rd(CVMX_GMXX_TX_XAUI_CTL(interface)); + /* Enable better IFG packing and improves performance */ + tx_ctl.s.dic_en = 1; + tx_ctl.s.uni_en = 0; + csr_wr(CVMX_GMXX_TX_XAUI_CTL(interface), tx_ctl.u64); + + /* (4)c Aply reset sequence */ + xaui_ctl.u64 = csr_rd(CVMX_PCSXX_CONTROL1_REG(interface)); + xaui_ctl.s.lo_pwr = 0; + + /* + * Errata G-15618 requires disabling PCS soft reset in some + * OCTEON II models. + */ + if (!OCTEON_IS_MODEL(OCTEON_CN63XX) && + !OCTEON_IS_MODEL(OCTEON_CN66XX_PASS1_X) && + !OCTEON_IS_MODEL(OCTEON_CN68XX)) + xaui_ctl.s.reset = 1; + csr_wr(CVMX_PCSXX_CONTROL1_REG(interface), xaui_ctl.u64); + + if (OCTEON_IS_MODEL(OCTEON_CN68XX_PASS2_X) && interface != 1) { + /* + * Note that GMX 1 was skipped as GMX0 is on the same + * QLM and will always be done first + * + * Workaround for Errata (G-16467). + */ + int qlm = interface; +#ifdef CVMX_QLM_DUMP_STATE + debug("%s:%d: XAUI%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 XAUI + * at 6.25Ghz + */ + if ((cvmx_qlm_get_gbaud_mhz(qlm) == 6250) && + (cvmx_qlm_jtag_get(qlm, 0, "clkf_byp") != 20)) { + /* Wait 100us for links to stabalize */ + udelay(100); + 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); + /* Wait 100us for links to stabalize */ + udelay(100); + /* Allow TX on QLM */ + cvmx_qlm_jtag_set(qlm, -1, "cfg_tx_idle_set", 0); + } +#ifdef CVMX_QLM_DUMP_STATE + debug("%s:%d: XAUI%d: Done applying workaround for Errata G-16467\n", + __func__, __LINE__, qlm); + cvmx_qlm_display_registers(qlm); + debug("\n\n"); +#endif + } + + /* Wait for PCS to come out of reset */ + if (CVMX_WAIT_FOR_FIELD64(CVMX_PCSXX_CONTROL1_REG(interface), + cvmx_pcsxx_control1_reg_t, reset, ==, 0, + 10000)) + return -1; + /* Wait for PCS to be aligned */ + if (CVMX_WAIT_FOR_FIELD64(CVMX_PCSXX_10GBX_STATUS_REG(interface), + cvmx_pcsxx_10gbx_status_reg_t, alignd, ==, 1, + 10000)) + return -1; + /* Wait for RX to be ready */ + if (CVMX_WAIT_FOR_FIELD64(CVMX_GMXX_RX_XAUI_CTL(interface), + cvmx_gmxx_rx_xaui_ctl_t, status, ==, 0, + 10000)) + return -1; + + /* (6) Configure GMX */ + + /* Wait for GMX RX to be idle */ + if (CVMX_WAIT_FOR_FIELD64(CVMX_GMXX_PRTX_CFG(0, interface), + cvmx_gmxx_prtx_cfg_t, rx_idle, ==, 1, 10000)) + return -1; + /* Wait for GMX TX to be idle */ + if (CVMX_WAIT_FOR_FIELD64(CVMX_GMXX_PRTX_CFG(0, interface), + cvmx_gmxx_prtx_cfg_t, tx_idle, ==, 1, 10000)) + return -1; + + /* GMX configure */ + gmx_cfg.u64 = csr_rd(CVMX_GMXX_PRTX_CFG(0, interface)); + gmx_cfg.s.speed = 1; + gmx_cfg.s.speed_msb = 0; + gmx_cfg.s.slottime = 1; + csr_wr(CVMX_GMXX_TX_PRTS(interface), 1); + csr_wr(CVMX_GMXX_TXX_SLOT(0, interface), 512); + csr_wr(CVMX_GMXX_TXX_BURST(0, interface), 8192); + csr_wr(CVMX_GMXX_PRTX_CFG(0, interface), gmx_cfg.u64); + + /* Wait for receive link */ + if (CVMX_WAIT_FOR_FIELD64(CVMX_PCSXX_STATUS1_REG(interface), + cvmx_pcsxx_status1_reg_t, rcv_lnk, ==, 1, + 10000)) + return -1; + if (CVMX_WAIT_FOR_FIELD64(CVMX_PCSXX_STATUS2_REG(interface), + cvmx_pcsxx_status2_reg_t, xmtflt, ==, 0, + 10000)) + return -1; + if (CVMX_WAIT_FOR_FIELD64(CVMX_PCSXX_STATUS2_REG(interface), + cvmx_pcsxx_status2_reg_t, rcvflt, ==, 0, + 10000)) + return -1; + + /* (8) Enable packet reception */ + misc_ctl.s.gmxeno = 0; + csr_wr(CVMX_PCSXX_MISC_CTL_REG(interface), misc_ctl.u64); + + /* Clear all error interrupts before enabling the interface. */ + csr_wr(CVMX_GMXX_RXX_INT_REG(0, interface), ~0x0ull); + csr_wr(CVMX_GMXX_TX_INT_REG(interface), ~0x0ull); + csr_wr(CVMX_PCSXX_INT_REG(interface), ~0x0ull); + + /* Enable GMX */ + gmx_cfg.u64 = csr_rd(CVMX_GMXX_PRTX_CFG(0, interface)); + gmx_cfg.s.en = 1; + csr_wr(CVMX_GMXX_PRTX_CFG(0, interface), gmx_cfg.u64); + + return 0; +} + +/** + * @INTERNAL + * Bringup and enable a XAUI interface. After this call packet + * I/O should be fully functional. This is called with IPD + * enabled but PKO disabled. + * + * @param xiface Interface to bring up + * + * @return Zero on success, negative on failure + */ +int __cvmx_helper_xaui_enable(int xiface) +{ + struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); + int interface = xi.interface; + + __cvmx_helper_setup_gmx(interface, 1); + + /* Setup PKND and BPID */ + if (octeon_has_feature(OCTEON_FEATURE_PKND)) { + union cvmx_gmxx_bpid_msk bpid_msk; + union cvmx_gmxx_bpid_mapx bpid_map; + union cvmx_gmxx_prtx_cfg gmxx_prtx_cfg; + union cvmx_gmxx_txx_append gmxx_txx_append_cfg; + + /* Setup PKIND */ + gmxx_prtx_cfg.u64 = csr_rd(CVMX_GMXX_PRTX_CFG(0, interface)); + gmxx_prtx_cfg.s.pknd = cvmx_helper_get_pknd(interface, 0); + csr_wr(CVMX_GMXX_PRTX_CFG(0, interface), gmxx_prtx_cfg.u64); + + /* Setup BPID */ + bpid_map.u64 = csr_rd(CVMX_GMXX_BPID_MAPX(0, interface)); + bpid_map.s.val = 1; + bpid_map.s.bpid = cvmx_helper_get_bpid(interface, 0); + csr_wr(CVMX_GMXX_BPID_MAPX(0, interface), bpid_map.u64); + + bpid_msk.u64 = csr_rd(CVMX_GMXX_BPID_MSK(interface)); + bpid_msk.s.msk_or |= 1; + bpid_msk.s.msk_and &= ~1; + csr_wr(CVMX_GMXX_BPID_MSK(interface), bpid_msk.u64); + + /* CN68XX adds the padding and FCS in PKO, not GMX */ + gmxx_txx_append_cfg.u64 = + csr_rd(CVMX_GMXX_TXX_APPEND(0, interface)); + gmxx_txx_append_cfg.s.fcs = 0; + gmxx_txx_append_cfg.s.pad = 0; + csr_wr(CVMX_GMXX_TXX_APPEND(0, interface), + gmxx_txx_append_cfg.u64); + } + + /* 70XX eval boards use Marvel phy, set disparity accordingly. */ + if (OCTEON_IS_MODEL(OCTEON_CN70XX)) { + union cvmx_gmxx_rxaui_ctl rxaui_ctl; + + rxaui_ctl.u64 = csr_rd(CVMX_GMXX_RXAUI_CTL(interface)); + rxaui_ctl.s.disparity = 1; + csr_wr(CVMX_GMXX_RXAUI_CTL(interface), rxaui_ctl.u64); + } + + __cvmx_helper_xaui_link_init(interface); + + return 0; +} + +/** + * @INTERNAL + * Return the link state of an IPD/PKO port as returned by + * auto negotiation. The result of this function may not match + * Octeon's link config if auto negotiation has changed since + * the last call to cvmx_helper_link_set(). + * + * @param ipd_port IPD/PKO port to query + * + * @return Link state + */ +cvmx_helper_link_info_t __cvmx_helper_xaui_link_get(int ipd_port) +{ + int interface = cvmx_helper_get_interface_num(ipd_port); + union cvmx_gmxx_tx_xaui_ctl gmxx_tx_xaui_ctl; + union cvmx_gmxx_rx_xaui_ctl gmxx_rx_xaui_ctl; + union cvmx_pcsxx_status1_reg pcsxx_status1_reg; + cvmx_helper_link_info_t result; + + gmxx_tx_xaui_ctl.u64 = csr_rd(CVMX_GMXX_TX_XAUI_CTL(interface)); + gmxx_rx_xaui_ctl.u64 = csr_rd(CVMX_GMXX_RX_XAUI_CTL(interface)); + pcsxx_status1_reg.u64 = csr_rd(CVMX_PCSXX_STATUS1_REG(interface)); + result.u64 = 0; + + /* Only return a link if both RX and TX are happy */ + if (gmxx_tx_xaui_ctl.s.ls == 0 && gmxx_rx_xaui_ctl.s.status == 0 && + pcsxx_status1_reg.s.rcv_lnk == 1) { + union cvmx_pcsxx_misc_ctl_reg misc_ctl; + + result.s.link_up = 1; + result.s.full_duplex = 1; + if (OCTEON_IS_MODEL(OCTEON_CN68XX)) { + union cvmx_mio_qlmx_cfg qlm_cfg; + int lanes; + int qlm = (interface == 1) ? 0 : interface; + + qlm_cfg.u64 = csr_rd(CVMX_MIO_QLMX_CFG(qlm)); + result.s.speed = cvmx_qlm_get_gbaud_mhz(qlm) * 8 / 10; + lanes = (qlm_cfg.s.qlm_cfg == 7) ? 2 : 4; + result.s.speed *= lanes; + } else if (OCTEON_IS_MODEL(OCTEON_CN6XXX)) { + int qlm = cvmx_qlm_interface(interface); + + result.s.speed = cvmx_qlm_get_gbaud_mhz(qlm) * 8 / 10; + result.s.speed *= 4; + } else { + result.s.speed = 10000; + } + misc_ctl.u64 = csr_rd(CVMX_PCSXX_MISC_CTL_REG(interface)); + if (misc_ctl.s.gmxeno) + __cvmx_helper_xaui_link_init(interface); + } else { + /* Disable GMX and PCSX interrupts. */ + csr_wr(CVMX_GMXX_RXX_INT_EN(0, interface), 0x0); + csr_wr(CVMX_GMXX_TX_INT_EN(interface), 0x0); + csr_wr(CVMX_PCSXX_INT_EN_REG(interface), 0x0); + } + return result; +} + +/** + * @INTERNAL + * Configure an IPD/PKO port for the specified link state. This + * function does not influence auto negotiation at the PHY level. + * The passed link state must always match the link state returned + * by cvmx_helper_link_get(). It is normally best to use + * cvmx_helper_link_autoconf() instead. + * + * @param ipd_port IPD/PKO port to configure + * @param link_info The new link state + * + * @return Zero on success, negative on failure + */ +int __cvmx_helper_xaui_link_set(int ipd_port, cvmx_helper_link_info_t link_info) +{ + int interface = cvmx_helper_get_interface_num(ipd_port); + union cvmx_gmxx_tx_xaui_ctl gmxx_tx_xaui_ctl; + union cvmx_gmxx_rx_xaui_ctl gmxx_rx_xaui_ctl; + + gmxx_tx_xaui_ctl.u64 = csr_rd(CVMX_GMXX_TX_XAUI_CTL(interface)); + gmxx_rx_xaui_ctl.u64 = csr_rd(CVMX_GMXX_RX_XAUI_CTL(interface)); + + /* If the link shouldn't be up, then just return */ + if (!link_info.s.link_up) + return 0; + + /* Do nothing if both RX and TX are happy */ + if (gmxx_tx_xaui_ctl.s.ls == 0 && gmxx_rx_xaui_ctl.s.status == 0) + return 0; + + /* Bring the link up */ + return __cvmx_helper_xaui_link_init(interface); +} + +/** + * @INTERNAL + * Configure a port for internal and/or external loopback. Internal loopback + * causes packets sent by the port to be received by Octeon. External loopback + * causes packets received from the wire to sent out again. + * + * @param ipd_port IPD/PKO port to loopback. + * @param enable_internal + * Non zero if you want internal loopback + * @param enable_external + * Non zero if you want external loopback + * + * @return Zero on success, negative on failure. + */ +extern int __cvmx_helper_xaui_configure_loopback(int ipd_port, + int enable_internal, + int enable_external) +{ + int interface = cvmx_helper_get_interface_num(ipd_port); + union cvmx_pcsxx_control1_reg pcsxx_control1_reg; + union cvmx_gmxx_xaui_ext_loopback gmxx_xaui_ext_loopback; + + /* Set the internal loop */ + pcsxx_control1_reg.u64 = csr_rd(CVMX_PCSXX_CONTROL1_REG(interface)); + pcsxx_control1_reg.s.loopbck1 = enable_internal; + csr_wr(CVMX_PCSXX_CONTROL1_REG(interface), pcsxx_control1_reg.u64); + + /* Set the external loop */ + gmxx_xaui_ext_loopback.u64 = + csr_rd(CVMX_GMXX_XAUI_EXT_LOOPBACK(interface)); + gmxx_xaui_ext_loopback.s.en = enable_external; + csr_wr(CVMX_GMXX_XAUI_EXT_LOOPBACK(interface), + gmxx_xaui_ext_loopback.u64); + + /* Take the link through a reset */ + return __cvmx_helper_xaui_link_init(interface); +} diff --git a/arch/mips/mach-octeon/cvmx-helper.c b/arch/mips/mach-octeon/cvmx-helper.c index d0620d6..ccec57e 100644 --- a/arch/mips/mach-octeon/cvmx-helper.c +++ b/arch/mips/mach-octeon/cvmx-helper.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (C) 2020 Marvell International Ltd. + * Copyright (C) 2020-2022 Marvell International Ltd. * * Helper functions for common, but complicated tasks. */ @@ -305,20 +305,6 @@ static const struct iface_ops iface_ops_npi = { /** * @INTERNAL * This structure specifies the interface methods used by interfaces - * configured as srio. - */ -static const struct iface_ops iface_ops_srio = { - .mode = CVMX_HELPER_INTERFACE_MODE_SRIO, - .enumerate = __cvmx_helper_srio_probe, - .probe = __cvmx_helper_srio_probe, - .enable = __cvmx_helper_srio_enable, - .link_get = __cvmx_helper_srio_link_get, - .link_set = __cvmx_helper_srio_link_set, -}; - -/** - * @INTERNAL - * This structure specifies the interface methods used by interfaces * configured as agl. */ static const struct iface_ops iface_ops_agl = { @@ -442,35 +428,6 @@ int __cvmx_helper_init_interface(int xiface, int num_ipd_ports, int has_fcs, return 0; } -/* - * Shut down the interfaces; free the resources. - * @INTERNAL - */ -void __cvmx_helper_shutdown_interfaces_node(unsigned int node) -{ - int i; - int nifaces; /* number of interfaces */ - struct cvmx_iface *piface; - - nifaces = cvmx_helper_get_number_of_interfaces(); - for (i = 0; i < nifaces; i++) { - piface = &cvmx_interfaces[node][i]; - - /* - * For SE apps, bootmem was meant to be allocated and never - * freed. - */ - piface->cvif_ipd_port_link_info = 0; - } -} - -void __cvmx_helper_shutdown_interfaces(void) -{ - unsigned int node = cvmx_get_node_num(); - - __cvmx_helper_shutdown_interfaces_node(node); -} - int __cvmx_helper_set_link_info(int xiface, int index, cvmx_helper_link_info_t link_info) { struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); @@ -525,11 +482,6 @@ u64 cvmx_rgmii_backpressure_dis = 1; typedef int (*cvmx_export_config_t)(void); cvmx_export_config_t cvmx_export_app_config; -void cvmx_rgmii_set_back_pressure(uint64_t backpressure_dis) -{ - cvmx_rgmii_backpressure_dis = backpressure_dis; -} - /* * internal functions that are not exported in the .h file but must be * declared to make gcc happy. @@ -537,16 +489,6 @@ void cvmx_rgmii_set_back_pressure(uint64_t backpressure_dis) extern cvmx_helper_link_info_t __cvmx_helper_get_link_info(int interface, int port); /** - * cvmx_override_iface_phy_mode(int interface, int index) is a function pointer. - * It is meant to allow customization of interfaces which do not have a PHY. - * - * @returns 0 if MAC decides TX_CONFIG_REG or 1 if PHY decides TX_CONFIG_REG. - * - * If this function pointer is NULL then it defaults to the MAC. - */ -int (*cvmx_override_iface_phy_mode)(int interface, int index); - -/** * cvmx_override_ipd_port_setup(int ipd_port) is a function * pointer. It is meant to allow customization of the IPD * port/port kind setup before packet input/output comes online. @@ -607,7 +549,7 @@ int __cvmx_helper_early_ports_on_interface(int interface) * chip and configuration, this can be 1-16. A value of 0 * specifies that the interface doesn't exist or isn't usable. * - * @param xiface xiface to get the port count for + * @param xiface to get the port count for * * Return: Number of ports on interface. Can be Zero. */ @@ -919,15 +861,9 @@ static cvmx_helper_interface_mode_t __cvmx_get_mode_cnf75xx(int xiface) break; } } else if ((interface < 3) && OCTEON_IS_MODEL(OCTEON_CNF75XX)) { - cvmx_sriox_status_reg_t sriox_status_reg; - int srio_port = interface - 1; - - sriox_status_reg.u64 = csr_rd(CVMX_SRIOX_STATUS_REG(srio_port)); - - if (sriox_status_reg.s.srio) - iface_ops[interface] = &iface_ops_srio; - else - iface_ops[interface] = &iface_ops_dis; + /* SRIO is disabled for now */ + printf("SRIO disabled for now!\n"); + iface_ops[interface] = &iface_ops_dis; } else if (interface == 3) { /* DPI */ iface_ops[interface] = &iface_ops_npi; } else if (interface == 4) { /* LOOP */ @@ -1046,7 +982,6 @@ static cvmx_helper_interface_mode_t __cvmx_get_mode_octeon2(int interface) (OCTEON_IS_MODEL(OCTEON_CN66XX) && interface >= 4 && interface <= 7)) { /* Only present in CN63XX & CN66XX Octeon model */ - union cvmx_sriox_status_reg sriox_status_reg; /* cn66xx pass1.0 has only 2 SRIO interfaces. */ if ((interface == 5 || interface == 7) && @@ -1059,12 +994,9 @@ static cvmx_helper_interface_mode_t __cvmx_get_mode_octeon2(int interface) */ iface_ops[interface] = &iface_ops_dis; } else { - sriox_status_reg.u64 = - csr_rd(CVMX_SRIOX_STATUS_REG(interface - 4)); - if (sriox_status_reg.s.srio) - iface_ops[interface] = &iface_ops_srio; - else - iface_ops[interface] = &iface_ops_dis; + /* SRIO is disabled for now */ + printf("SRIO disabled for now!\n"); + iface_ops[interface] = &iface_ops_dis; } } else if (OCTEON_IS_MODEL(OCTEON_CN66XX)) { union cvmx_mio_qlmx_cfg mio_qlm_cfg; @@ -1440,16 +1372,6 @@ static int __cvmx_helper_global_setup_backpressure(int node) /** * @INTERNAL - * Verify the per port IPD backpressure is aligned properly. - * Return: Zero if working, non zero if misaligned - */ -int __cvmx_helper_backpressure_is_misaligned(void) -{ - return 0; -} - -/** - * @INTERNAL * Enable packet input/output from the hardware. This function is * called after all internal setup is complete and IPD is enabled. * After this function completes, packets will be accepted from the @@ -1467,7 +1389,7 @@ int __cvmx_helper_packet_hardware_enable(int xiface) if (iface_node_ops[xi.node][xi.interface]->enable) result = iface_node_ops[xi.node][xi.interface]->enable(xiface); - result |= __cvmx_helper_board_hardware_enable(xiface); + return result; } @@ -1609,7 +1531,8 @@ int cvmx_helper_initialize_packet_io_node(unsigned int node) /* Skip invalid/disabled interfaces */ if (cvmx_helper_ports_on_interface(xiface) <= 0) continue; - printf("Node %d Interface %d has %d ports (%s)\n", node, interface, + debug("Node %d Interface %d has %d ports (%s)\n", + node, interface, cvmx_helper_ports_on_interface(xiface), cvmx_helper_interface_mode_to_string( cvmx_helper_interface_get_mode(xiface))); @@ -1731,445 +1654,6 @@ int cvmx_agl_set_backpressure_override(u32 interface, uint32_t port_mask) } /** - * Helper function for global packet IO shutdown - */ -int cvmx_helper_shutdown_packet_io_global_cn78xx(int node) -{ - int num_interfaces = cvmx_helper_get_number_of_interfaces(); - cvmx_wqe_t *work; - int interface; - int result = 0; - - /* Shut down all interfaces and disable TX and RX on all ports */ - for (interface = 0; interface < num_interfaces; interface++) { - int xiface = cvmx_helper_node_interface_to_xiface(node, interface); - int index; - int num_ports = cvmx_helper_ports_on_interface(xiface); - - if (num_ports > 4) - num_ports = 4; - - cvmx_bgx_set_backpressure_override(xiface, 0); - for (index = 0; index < num_ports; index++) { - cvmx_helper_link_info_t link_info; - - if (!cvmx_helper_is_port_valid(xiface, index)) - continue; - - cvmx_helper_bgx_shutdown_port(xiface, index); - - /* Turn off link LEDs */ - link_info.u64 = 0; - cvmx_helper_update_link_led(xiface, index, link_info); - } - } - - /* Stop input first */ - cvmx_helper_pki_shutdown(node); - - /* Retrieve all packets from the SSO and free them */ - result = 0; - while ((work = cvmx_pow_work_request_sync(CVMX_POW_WAIT))) { - cvmx_helper_free_pki_pkt_data(work); - cvmx_wqe_pki_free(work); - result++; - } - - if (result > 0) - debug("%s: Purged %d packets from SSO\n", __func__, result); - - /* - * No need to wait for PKO queues to drain, - * dq_close() drains the queues to NULL. - */ - - /* Shutdown PKO interfaces */ - for (interface = 0; interface < num_interfaces; interface++) { - int xiface = cvmx_helper_node_interface_to_xiface(node, interface); - - cvmx_helper_pko3_shut_interface(xiface); - } - - /* Disable MAC address filtering */ - for (interface = 0; interface < num_interfaces; interface++) { - int xiface = cvmx_helper_node_interface_to_xiface(node, interface); - - switch (cvmx_helper_interface_get_mode(xiface)) { - case CVMX_HELPER_INTERFACE_MODE_XAUI: - case CVMX_HELPER_INTERFACE_MODE_RXAUI: - case CVMX_HELPER_INTERFACE_MODE_XLAUI: - case CVMX_HELPER_INTERFACE_MODE_XFI: - case CVMX_HELPER_INTERFACE_MODE_10G_KR: - case CVMX_HELPER_INTERFACE_MODE_40G_KR4: - case CVMX_HELPER_INTERFACE_MODE_SGMII: - case CVMX_HELPER_INTERFACE_MODE_MIXED: { - int index; - int num_ports = cvmx_helper_ports_on_interface(xiface); - - for (index = 0; index < num_ports; index++) { - if (!cvmx_helper_is_port_valid(xiface, index)) - continue; - - /* Reset MAC filtering */ - cvmx_helper_bgx_rx_adr_ctl(node, interface, index, 0, 0, 0); - } - break; - } - default: - break; - } - } - - for (interface = 0; interface < num_interfaces; interface++) { - int index; - int xiface = cvmx_helper_node_interface_to_xiface(node, interface); - int num_ports = cvmx_helper_ports_on_interface(xiface); - - for (index = 0; index < num_ports; index++) { - /* Doing this twice should clear it since no packets - * can be received. - */ - cvmx_update_rx_activity_led(xiface, index, false); - cvmx_update_rx_activity_led(xiface, index, false); - } - } - - /* Shutdown the PKO unit */ - result = cvmx_helper_pko3_shutdown(node); - - /* Release interface structures */ - __cvmx_helper_shutdown_interfaces(); - - return result; -} - -/** - * Undo the initialization performed in - * cvmx_helper_initialize_packet_io_global(). After calling this routine and the - * local version on each core, packet IO for Octeon will be disabled and placed - * in the initial reset state. It will then be safe to call the initialize - * later on. Note that this routine does not empty the FPA pools. It frees all - * buffers used by the packet IO hardware to the FPA so a function emptying the - * FPA after shutdown should find all packet buffers in the FPA. - * - * Return: Zero on success, negative on failure. - */ -int cvmx_helper_shutdown_packet_io_global(void) -{ - const int timeout = 5; /* Wait up to 5 seconds for timeouts */ - int result = 0; - int num_interfaces = cvmx_helper_get_number_of_interfaces(); - int interface; - int num_ports; - int index; - struct cvmx_buffer_list *pool0_buffers; - struct cvmx_buffer_list *pool0_buffers_tail; - cvmx_wqe_t *work; - union cvmx_ipd_ctl_status ipd_ctl_status; - int wqe_pool = (int)cvmx_fpa_get_wqe_pool(); - int node = cvmx_get_node_num(); - cvmx_pcsx_mrx_control_reg_t control_reg; - - if (octeon_has_feature(OCTEON_FEATURE_BGX)) - return cvmx_helper_shutdown_packet_io_global_cn78xx(node); - - /* Step 1: Disable all backpressure */ - for (interface = 0; interface < num_interfaces; interface++) { - cvmx_helper_interface_mode_t mode = - cvmx_helper_interface_get_mode(interface); - - if (mode == CVMX_HELPER_INTERFACE_MODE_AGL) - cvmx_agl_set_backpressure_override(interface, 0x1); - else if (mode != CVMX_HELPER_INTERFACE_MODE_DISABLED) - cvmx_gmx_set_backpressure_override(interface, 0xf); - } - - /* Step 2: Wait for the PKO queues to drain */ - result = __cvmx_helper_pko_drain(); - if (result < 0) { - debug("WARNING: %s: Failed to drain some PKO queues\n", - __func__); - } - - /* Step 3: Disable TX and RX on all ports */ - for (interface = 0; interface < num_interfaces; interface++) { - int xiface = cvmx_helper_node_interface_to_xiface(node, - interface); - - switch (cvmx_helper_interface_get_mode(interface)) { - case CVMX_HELPER_INTERFACE_MODE_DISABLED: - case CVMX_HELPER_INTERFACE_MODE_PCIE: - /* Not a packet interface */ - break; - case CVMX_HELPER_INTERFACE_MODE_NPI: - case CVMX_HELPER_INTERFACE_MODE_SRIO: - case CVMX_HELPER_INTERFACE_MODE_ILK: - /* - * We don't handle the NPI/NPEI/SRIO packet - * engines. The caller must know these are - * idle. - */ - break; - case CVMX_HELPER_INTERFACE_MODE_LOOP: - /* - * Nothing needed. Once PKO is idle, the - * loopback devices must be idle. - */ - break; - case CVMX_HELPER_INTERFACE_MODE_SPI: - /* - * SPI cannot be disabled from Octeon. It is - * the responsibility of the caller to make - * sure SPI is idle before doing shutdown. - * - * Fall through and do the same processing as - * RGMII/GMII. - */ - fallthrough; - case CVMX_HELPER_INTERFACE_MODE_GMII: - case CVMX_HELPER_INTERFACE_MODE_RGMII: - /* Disable outermost RX at the ASX block */ - csr_wr(CVMX_ASXX_RX_PRT_EN(interface), 0); - num_ports = cvmx_helper_ports_on_interface(xiface); - if (num_ports > 4) - num_ports = 4; - for (index = 0; index < num_ports; index++) { - union cvmx_gmxx_prtx_cfg gmx_cfg; - - if (!cvmx_helper_is_port_valid(interface, index)) - continue; - gmx_cfg.u64 = csr_rd(CVMX_GMXX_PRTX_CFG(index, interface)); - gmx_cfg.s.en = 0; - csr_wr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64); - /* Poll the GMX state machine waiting for it to become idle */ - csr_wr(CVMX_NPI_DBG_SELECT, - interface * 0x800 + index * 0x100 + 0x880); - if (CVMX_WAIT_FOR_FIELD64(CVMX_DBG_DATA, union cvmx_dbg_data, - data & 7, ==, 0, timeout * 1000000)) { - debug("GMX RX path timeout waiting for idle\n"); - result = -1; - } - if (CVMX_WAIT_FOR_FIELD64(CVMX_DBG_DATA, union cvmx_dbg_data, - data & 0xf, ==, 0, timeout * 1000000)) { - debug("GMX TX path timeout waiting for idle\n"); - result = -1; - } - } - /* Disable outermost TX at the ASX block */ - csr_wr(CVMX_ASXX_TX_PRT_EN(interface), 0); - /* Disable interrupts for interface */ - csr_wr(CVMX_ASXX_INT_EN(interface), 0); - csr_wr(CVMX_GMXX_TX_INT_EN(interface), 0); - break; - case CVMX_HELPER_INTERFACE_MODE_XAUI: - case CVMX_HELPER_INTERFACE_MODE_RXAUI: - case CVMX_HELPER_INTERFACE_MODE_SGMII: - case CVMX_HELPER_INTERFACE_MODE_QSGMII: - case CVMX_HELPER_INTERFACE_MODE_PICMG: - num_ports = cvmx_helper_ports_on_interface(xiface); - if (num_ports > 4) - num_ports = 4; - for (index = 0; index < num_ports; index++) { - union cvmx_gmxx_prtx_cfg gmx_cfg; - - if (!cvmx_helper_is_port_valid(interface, index)) - continue; - gmx_cfg.u64 = csr_rd(CVMX_GMXX_PRTX_CFG(index, interface)); - gmx_cfg.s.en = 0; - csr_wr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64); - if (CVMX_WAIT_FOR_FIELD64(CVMX_GMXX_PRTX_CFG(index, interface), - union cvmx_gmxx_prtx_cfg, rx_idle, ==, 1, - timeout * 1000000)) { - debug("GMX RX path timeout waiting for idle\n"); - result = -1; - } - if (CVMX_WAIT_FOR_FIELD64(CVMX_GMXX_PRTX_CFG(index, interface), - union cvmx_gmxx_prtx_cfg, tx_idle, ==, 1, - timeout * 1000000)) { - debug("GMX TX path timeout waiting for idle\n"); - result = -1; - } - /* For SGMII some PHYs require that the PCS - * interface be powered down and reset (i.e. - * Atheros/Qualcomm PHYs). - */ - if (cvmx_helper_interface_get_mode(interface) == - CVMX_HELPER_INTERFACE_MODE_SGMII) { - u64 reg; - - reg = CVMX_PCSX_MRX_CONTROL_REG(index, interface); - /* Power down the interface */ - control_reg.u64 = csr_rd(reg); - control_reg.s.pwr_dn = 1; - csr_wr(reg, control_reg.u64); - csr_rd(reg); - } - } - break; - case CVMX_HELPER_INTERFACE_MODE_AGL: { - int port = cvmx_helper_agl_get_port(interface); - union cvmx_agl_gmx_prtx_cfg agl_gmx_cfg; - - agl_gmx_cfg.u64 = csr_rd(CVMX_AGL_GMX_PRTX_CFG(port)); - agl_gmx_cfg.s.en = 0; - csr_wr(CVMX_AGL_GMX_PRTX_CFG(port), agl_gmx_cfg.u64); - if (CVMX_WAIT_FOR_FIELD64(CVMX_AGL_GMX_PRTX_CFG(port), - union cvmx_agl_gmx_prtx_cfg, rx_idle, ==, 1, - timeout * 1000000)) { - debug("AGL RX path timeout waiting for idle\n"); - result = -1; - } - if (CVMX_WAIT_FOR_FIELD64(CVMX_AGL_GMX_PRTX_CFG(port), - union cvmx_agl_gmx_prtx_cfg, tx_idle, ==, 1, - timeout * 1000000)) { - debug("AGL TX path timeout waiting for idle\n"); - result = -1; - } - } break; - default: - break; - } - } - - /* Step 4: Retrieve all packets from the POW and free them */ - while ((work = cvmx_pow_work_request_sync(CVMX_POW_WAIT))) { - cvmx_helper_free_packet_data(work); - cvmx_fpa1_free(work, wqe_pool, 0); - } - - /* Step 5 */ - cvmx_ipd_disable(); - - /* - * Step 6: Drain all prefetched buffers from IPD/PIP. Note that IPD/PIP - * have not been reset yet - */ - __cvmx_ipd_free_ptr(); - - /* Step 7: Free the PKO command buffers and put PKO in reset */ - cvmx_pko_shutdown(); - - /* Step 8: Disable MAC address filtering */ - for (interface = 0; interface < num_interfaces; interface++) { - int xiface = cvmx_helper_node_interface_to_xiface(node, interface); - - switch (cvmx_helper_interface_get_mode(interface)) { - case CVMX_HELPER_INTERFACE_MODE_DISABLED: - case CVMX_HELPER_INTERFACE_MODE_PCIE: - case CVMX_HELPER_INTERFACE_MODE_SRIO: - case CVMX_HELPER_INTERFACE_MODE_ILK: - case CVMX_HELPER_INTERFACE_MODE_NPI: - case CVMX_HELPER_INTERFACE_MODE_LOOP: - break; - case CVMX_HELPER_INTERFACE_MODE_XAUI: - case CVMX_HELPER_INTERFACE_MODE_RXAUI: - case CVMX_HELPER_INTERFACE_MODE_GMII: - case CVMX_HELPER_INTERFACE_MODE_RGMII: - case CVMX_HELPER_INTERFACE_MODE_SPI: - case CVMX_HELPER_INTERFACE_MODE_SGMII: - case CVMX_HELPER_INTERFACE_MODE_QSGMII: - case CVMX_HELPER_INTERFACE_MODE_PICMG: - num_ports = cvmx_helper_ports_on_interface(xiface); - if (num_ports > 4) - num_ports = 4; - for (index = 0; index < num_ports; index++) { - if (!cvmx_helper_is_port_valid(interface, index)) - continue; - csr_wr(CVMX_GMXX_RXX_ADR_CTL(index, interface), 1); - csr_wr(CVMX_GMXX_RXX_ADR_CAM_EN(index, interface), 0); - csr_wr(CVMX_GMXX_RXX_ADR_CAM0(index, interface), 0); - csr_wr(CVMX_GMXX_RXX_ADR_CAM1(index, interface), 0); - csr_wr(CVMX_GMXX_RXX_ADR_CAM2(index, interface), 0); - csr_wr(CVMX_GMXX_RXX_ADR_CAM3(index, interface), 0); - csr_wr(CVMX_GMXX_RXX_ADR_CAM4(index, interface), 0); - csr_wr(CVMX_GMXX_RXX_ADR_CAM5(index, interface), 0); - } - break; - case CVMX_HELPER_INTERFACE_MODE_AGL: { - int port = cvmx_helper_agl_get_port(interface); - - csr_wr(CVMX_AGL_GMX_RXX_ADR_CTL(port), 1); - csr_wr(CVMX_AGL_GMX_RXX_ADR_CAM_EN(port), 0); - csr_wr(CVMX_AGL_GMX_RXX_ADR_CAM0(port), 0); - csr_wr(CVMX_AGL_GMX_RXX_ADR_CAM1(port), 0); - csr_wr(CVMX_AGL_GMX_RXX_ADR_CAM2(port), 0); - csr_wr(CVMX_AGL_GMX_RXX_ADR_CAM3(port), 0); - csr_wr(CVMX_AGL_GMX_RXX_ADR_CAM4(port), 0); - csr_wr(CVMX_AGL_GMX_RXX_ADR_CAM5(port), 0); - } break; - default: - break; - } - } - - /* - * Step 9: Drain all FPA buffers out of pool 0 before we reset - * IPD/PIP. This is needed to keep IPD_QUE0_FREE_PAGE_CNT in - * sync. We temporarily keep the buffers in the pool0_buffers - * list. - */ - pool0_buffers = NULL; - pool0_buffers_tail = NULL; - while (1) { - struct cvmx_buffer_list *buffer = cvmx_fpa1_alloc(0); - - if (buffer) { - buffer->next = NULL; - - if (!pool0_buffers) - pool0_buffers = buffer; - else - pool0_buffers_tail->next = buffer; - - pool0_buffers_tail = buffer; - } else { - break; - } - } - - /* Step 10: Reset IPD and PIP */ - ipd_ctl_status.u64 = csr_rd(CVMX_IPD_CTL_STATUS); - ipd_ctl_status.s.reset = 1; - csr_wr(CVMX_IPD_CTL_STATUS, ipd_ctl_status.u64); - - /* Make sure IPD has finished reset. */ - if (OCTEON_IS_OCTEON2() || OCTEON_IS_MODEL(OCTEON_CN70XX)) { - if (CVMX_WAIT_FOR_FIELD64(CVMX_IPD_CTL_STATUS, union cvmx_ipd_ctl_status, rst_done, - ==, 0, 1000)) { - debug("IPD reset timeout waiting for idle\n"); - result = -1; - } - } - - /* Step 11: Restore the FPA buffers into pool 0 */ - while (pool0_buffers) { - struct cvmx_buffer_list *n = pool0_buffers->next; - - cvmx_fpa1_free(pool0_buffers, 0, 0); - pool0_buffers = n; - } - - /* Step 12: Release interface structures */ - __cvmx_helper_shutdown_interfaces(); - - return result; -} - -/** - * Does core local shutdown of packet io - * - * Return: Zero on success, non-zero on failure - */ -int cvmx_helper_shutdown_packet_io_local(void) -{ - /* - * Currently there is nothing to do per core. This may change - * in the future. - */ - return 0; -} - -/** * Auto configure an IPD/PKO port link state and speed. This * function basically does the equivalent of: * cvmx_helper_link_set(ipd_port, cvmx_helper_link_get(ipd_port)); @@ -2290,50 +1774,6 @@ int cvmx_helper_link_set(int xipd_port, cvmx_helper_link_info_t link_info) return result; } -/** - * Configure a port for internal and/or external loopback. Internal loopback - * causes packets sent by the port to be received by Octeon. External loopback - * causes packets received from the wire to sent out again. - * - * @param xipd_port IPD/PKO port to loopback. - * @param enable_internal - * Non zero if you want internal loopback - * @param enable_external - * Non zero if you want external loopback - * - * Return: Zero on success, negative on failure. - */ -int cvmx_helper_configure_loopback(int xipd_port, int enable_internal, int enable_external) -{ - int result = -1; - int xiface = cvmx_helper_get_interface_num(xipd_port); - struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); - int index = cvmx_helper_get_interface_index_num(xipd_port); - - if (index >= cvmx_helper_ports_on_interface(xiface)) - return -1; - - cvmx_helper_interface_get_mode(xiface); - if (iface_node_ops[xi.node][xi.interface]->loopback) - result = iface_node_ops[xi.node][xi.interface]->loopback(xipd_port, enable_internal, - enable_external); - - return result; -} - -void cvmx_helper_setup_simulator_io_buffer_counts(int node, int num_packet_buffers, int pko_buffers) -{ - if (octeon_has_feature(OCTEON_FEATURE_PKI)) { - cvmx_helper_pki_set_dflt_pool_buffer(node, num_packet_buffers); - cvmx_helper_pki_set_dflt_aura_buffer(node, num_packet_buffers); - - } else { - cvmx_ipd_set_packet_pool_buffer_count(num_packet_buffers); - cvmx_ipd_set_wqe_pool_buffer_count(num_packet_buffers); - cvmx_pko_set_cmd_queue_pool_buffer_count(pko_buffers); - } -} - void *cvmx_helper_mem_alloc(int node, uint64_t alloc_size, uint64_t align) { s64 paddr; @@ -2346,266 +1786,3 @@ void *cvmx_helper_mem_alloc(int node, uint64_t alloc_size, uint64_t align) } return cvmx_phys_to_ptr(paddr); } - -void cvmx_helper_mem_free(void *buffer, uint64_t size) -{ - __cvmx_bootmem_phy_free(cvmx_ptr_to_phys(buffer), size, 0); -} - -int cvmx_helper_qos_config_init(cvmx_qos_proto_t qos_proto, cvmx_qos_config_t *qos_cfg) -{ - int i; - - memset(qos_cfg, 0, sizeof(cvmx_qos_config_t)); - qos_cfg->pkt_mode = CVMX_QOS_PKT_MODE_HWONLY; /* Process PAUSEs in hardware only.*/ - qos_cfg->pool_mode = CVMX_QOS_POOL_PER_PORT; /* One Pool per BGX:LMAC.*/ - qos_cfg->pktbuf_size = 2048; /* Fit WQE + MTU in one buffer.*/ - qos_cfg->aura_size = 1024; /* 1K buffers typically enough for any application.*/ - qos_cfg->pko_pfc_en = 1; /* Enable PKO layout for PFC feature. */ - qos_cfg->vlan_num = 1; /* For Stacked VLAN, use 2nd VLAN in the QPG algorithm.*/ - qos_cfg->qos_proto = qos_proto; /* Use PFC flow-control protocol.*/ - qos_cfg->qpg_base = -1; /* QPG Table index is undefined.*/ - qos_cfg->p_time = 0x60; /* PAUSE packets time window.*/ - qos_cfg->p_interval = 0x10; /* PAUSE packets interval.*/ - for (i = 0; i < CVMX_QOS_NUM; i++) { - qos_cfg->groups[i] = i; /* SSO Groups = 0...7 */ - qos_cfg->group_prio[i] = i; /* SSO Group priority = QOS. */ - qos_cfg->drop_thresh[i] = 99; /* 99% of the Aura size.*/ - qos_cfg->red_thresh[i] = 90; /* 90% of the Aura size.*/ - qos_cfg->bp_thresh[i] = 70; /* 70% of the Aura size.*/ - } - return 0; -} - -int cvmx_helper_qos_port_config_update(int xipdport, cvmx_qos_config_t *qos_cfg) -{ - cvmx_user_static_pko_queue_config_t pkocfg; - cvmx_xport_t xp = cvmx_helper_ipd_port_to_xport(xipdport); - int xiface = cvmx_helper_get_interface_num(xipdport); - cvmx_xiface_t xi = cvmx_helper_xiface_to_node_interface(xiface); - - /* Configure PKO port for PFC SQ layout: */ - cvmx_helper_pko_queue_config_get(xp.node, &pkocfg); - pkocfg.pknd.pko_cfg_iface[xi.interface].pfc_enable = 1; - cvmx_helper_pko_queue_config_set(xp.node, &pkocfg); - return 0; -} - -int cvmx_helper_qos_port_setup(int xipdport, cvmx_qos_config_t *qos_cfg) -{ - const int channles = CVMX_QOS_NUM; - int bufsize = qos_cfg->pktbuf_size; - int aura_size = qos_cfg->aura_size; - cvmx_xport_t xp = cvmx_helper_ipd_port_to_xport(xipdport); - int node = xp.node; - int ipdport = xp.port; - int port = cvmx_helper_get_interface_index_num(xp.port); - int xiface = cvmx_helper_get_interface_num(xipdport); - cvmx_xiface_t xi = cvmx_helper_xiface_to_node_interface(xiface); - cvmx_fpa3_pool_t gpool; - cvmx_fpa3_gaura_t gaura; - cvmx_bgxx_cmr_rx_ovr_bp_t ovrbp; - struct cvmx_pki_qpg_config qpgcfg; - struct cvmx_pki_style_config stcfg, stcfg_dflt; - struct cvmx_pki_pkind_config pkcfg; - int chan, bpid, group, qpg; - int bpen, reden, dropen, passthr, dropthr, bpthr; - int nbufs, pkind, style; - char name[32]; - - if (qos_cfg->pool_mode == CVMX_QOS_POOL_PER_PORT) { - /* Allocate and setup packet Pool: */ - nbufs = aura_size * channles; - sprintf(name, "QOS.P%d", ipdport); - gpool = cvmx_fpa3_setup_fill_pool(node, -1 /*auto*/, name, bufsize, nbufs, NULL); - if (!__cvmx_fpa3_pool_valid(gpool)) { - printf("%s: Failed to setup FPA Pool\n", __func__); - return -1; - } - for (chan = 0; chan < channles; chan++) - qos_cfg->gpools[chan] = gpool; - } else { - printf("%s: Invalid pool_mode %d\n", __func__, qos_cfg->pool_mode); - return -1; - } - /* Allocate QPG entries: */ - qos_cfg->qpg_base = cvmx_pki_qpg_entry_alloc(node, -1 /*auto*/, channles); - if (qos_cfg->qpg_base < 0) { - printf("%s: Failed to allocate QPG entry\n", __func__); - return -1; - } - for (chan = 0; chan < channles; chan++) { - /* Allocate and setup Aura, setup BP threshold: */ - gpool = qos_cfg->gpools[chan]; - sprintf(name, "QOS.A%d", ipdport + chan); - gaura = cvmx_fpa3_set_aura_for_pool(gpool, -1 /*auto*/, name, bufsize, aura_size); - if (!__cvmx_fpa3_aura_valid(gaura)) { - printf("%s: Failed to setup FPA Aura for Channel %d\n", __func__, chan); - return -1; - } - qos_cfg->gauras[chan] = gaura; - bpen = 1; - reden = 1; - dropen = 1; - dropthr = (qos_cfg->drop_thresh[chan] * 10 * aura_size) / 1000; - passthr = (qos_cfg->red_thresh[chan] * 10 * aura_size) / 1000; - bpthr = (qos_cfg->bp_thresh[chan] * 10 * aura_size) / 1000; - cvmx_fpa3_setup_aura_qos(gaura, reden, passthr, dropthr, bpen, bpthr); - cvmx_pki_enable_aura_qos(node, gaura.laura, reden, dropen, bpen); - - /* Allocate BPID, link Aura and Channel using BPID: */ - bpid = cvmx_pki_bpid_alloc(node, -1 /*auto*/); - if (bpid < 0) { - printf("%s: Failed to allocate BPID for channel %d\n", - __func__, chan); - return -1; - } - qos_cfg->bpids[chan] = bpid; - cvmx_pki_write_aura_bpid(node, gaura.laura, bpid); - cvmx_pki_write_channel_bpid(node, ipdport + chan, bpid); - - /* Setup QPG entries: */ - group = qos_cfg->groups[chan]; - qpg = qos_cfg->qpg_base + chan; - cvmx_pki_read_qpg_entry(node, qpg, &qpgcfg); - qpgcfg.port_add = chan; - qpgcfg.aura_num = gaura.laura; - qpgcfg.grp_ok = (node << CVMX_WQE_GRP_NODE_SHIFT) | group; - qpgcfg.grp_bad = (node << CVMX_WQE_GRP_NODE_SHIFT) | group; - qpgcfg.grptag_ok = (node << CVMX_WQE_GRP_NODE_SHIFT) | 0; - qpgcfg.grptag_bad = (node << CVMX_WQE_GRP_NODE_SHIFT) | 0; - cvmx_pki_write_qpg_entry(node, qpg, &qpgcfg); - } - /* Allocate and setup STYLE: */ - cvmx_helper_pki_get_dflt_style(node, &stcfg_dflt); - style = cvmx_pki_style_alloc(node, -1 /*auto*/); - cvmx_pki_read_style_config(node, style, CVMX_PKI_CLUSTER_ALL, &stcfg); - stcfg.tag_cfg = stcfg_dflt.tag_cfg; - stcfg.parm_cfg.tag_type = CVMX_POW_TAG_TYPE_ORDERED; - stcfg.parm_cfg.qpg_qos = CVMX_PKI_QPG_QOS_VLAN; - stcfg.parm_cfg.qpg_base = qos_cfg->qpg_base; - stcfg.parm_cfg.qpg_port_msb = 0; - stcfg.parm_cfg.qpg_port_sh = 0; - stcfg.parm_cfg.qpg_dis_grptag = 1; - stcfg.parm_cfg.fcs_strip = 1; - stcfg.parm_cfg.mbuff_size = bufsize - 64; /* Do not use 100% of the buffer. */ - stcfg.parm_cfg.force_drop = 0; - stcfg.parm_cfg.nodrop = 0; - stcfg.parm_cfg.rawdrp = 0; - stcfg.parm_cfg.cache_mode = 2; /* 1st buffer in L2 */ - stcfg.parm_cfg.wqe_vs = qos_cfg->vlan_num; - cvmx_pki_write_style_config(node, style, CVMX_PKI_CLUSTER_ALL, &stcfg); - - /* Setup PKIND: */ - pkind = cvmx_helper_get_pknd(xiface, port); - cvmx_pki_read_pkind_config(node, pkind, &pkcfg); - pkcfg.cluster_grp = 0; /* OCTEON3 has only one cluster group = 0 */ - pkcfg.initial_style = style; - pkcfg.initial_parse_mode = CVMX_PKI_PARSE_LA_TO_LG; - cvmx_pki_write_pkind_config(node, pkind, &pkcfg); - - /* Setup parameters of the QOS packet and enable QOS flow-control: */ - cvmx_bgx_set_pause_pkt_param(xipdport, 0, 0x0180c2000001, 0x8808, qos_cfg->p_time, - qos_cfg->p_interval); - cvmx_bgx_set_flowctl_mode(xipdport, qos_cfg->qos_proto, qos_cfg->pkt_mode); - - /* Enable PKI channel backpressure in the BGX: */ - ovrbp.u64 = csr_rd_node(node, CVMX_BGXX_CMR_RX_OVR_BP(xi.interface)); - ovrbp.s.en &= ~(1 << port); - ovrbp.s.ign_fifo_bp &= ~(1 << port); - csr_wr_node(node, CVMX_BGXX_CMR_RX_OVR_BP(xi.interface), ovrbp.u64); - return 0; -} - -int cvmx_helper_qos_sso_setup(int xipdport, cvmx_qos_config_t *qos_cfg) -{ - const int channels = CVMX_QOS_NUM; - cvmx_sso_grpx_pri_t grppri; - int chan, qos, group; - cvmx_xport_t xp = cvmx_helper_ipd_port_to_xport(xipdport); - int node = xp.node; - - for (chan = 0; chan < channels; chan++) { - qos = cvmx_helper_qos2prio(chan); - group = qos_cfg->groups[qos]; - grppri.u64 = csr_rd_node(node, CVMX_SSO_GRPX_PRI(group)); - grppri.s.pri = qos_cfg->group_prio[chan]; - csr_wr_node(node, CVMX_SSO_GRPX_PRI(group), grppri.u64); - } - return 0; -} - -int cvmx_helper_get_chan_e_name(int chan, char *namebuf, int buflen) -{ - int n, dpichans; - - if ((unsigned int)chan >= CVMX_PKO3_IPD_NUM_MAX) { - printf("%s: Channel %d is out of range (0..4095)\n", __func__, chan); - return -1; - } - if (OCTEON_IS_MODEL(OCTEON_CN78XX)) - dpichans = 64; - else - dpichans = 128; - - if (chan >= 0 && chan < 64) - n = snprintf(namebuf, buflen, "LBK%d", chan); - else if (chan >= 0x100 && chan < (0x100 + dpichans)) - n = snprintf(namebuf, buflen, "DPI%d", chan - 0x100); - else if (chan == 0x200) - n = snprintf(namebuf, buflen, "NQM"); - else if (chan >= 0x240 && chan < (0x240 + (1 << 1) + 2)) - n = snprintf(namebuf, buflen, "SRIO%d:%d", (chan - 0x240) >> 1, - (chan - 0x240) & 0x1); - else if (chan >= 0x400 && chan < (0x400 + (1 << 8) + 256)) - n = snprintf(namebuf, buflen, "ILK%d:%d", (chan - 0x400) >> 8, - (chan - 0x400) & 0xFF); - else if (chan >= 0x800 && chan < (0x800 + (5 << 8) + (3 << 4) + 16)) - n = snprintf(namebuf, buflen, "BGX%d:%d:%d", (chan - 0x800) >> 8, - ((chan - 0x800) >> 4) & 0x3, (chan - 0x800) & 0xF); - else - n = snprintf(namebuf, buflen, "--"); - return n; -} - -#ifdef CVMX_DUMP_DIAGNOSTICS -void cvmx_helper_dump_for_diagnostics(int node) -{ - if (!(OCTEON_IS_OCTEON3() && !OCTEON_IS_MODEL(OCTEON_CN70XX))) { - printf("Diagnostics are not implemented for this model\n"); - return; - } -#ifdef CVMX_DUMP_GSER - { - int qlm, num_qlms; - - num_qlms = cvmx_qlm_get_num(); - for (qlm = 0; qlm < num_qlms; qlm++) { - cvmx_dump_gser_config_node(node, qlm); - cvmx_dump_gser_status_node(node, qlm); - } - } -#endif -#ifdef CVMX_DUMP_BGX - { - int bgx; - - for (bgx = 0; bgx < CVMX_HELPER_MAX_GMX; bgx++) { - cvmx_dump_bgx_config_node(node, bgx); - cvmx_dump_bgx_status_node(node, bgx); - } - } -#endif -#ifdef CVMX_DUMP_PKI - cvmx_pki_config_dump(node); - cvmx_pki_stats_dump(node); -#endif -#ifdef CVMX_DUMP_PKO - cvmx_helper_pko3_config_dump(node); - cvmx_helper_pko3_stats_dump(node); -#endif -#ifdef CVMX_DUMO_SSO - cvmx_sso_config_dump(node); -#endif -} -#endif diff --git a/arch/mips/mach-octeon/cvmx-ilk.c b/arch/mips/mach-octeon/cvmx-ilk.c new file mode 100644 index 0000000..6223ff2 --- /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; +} diff --git a/arch/mips/mach-octeon/cvmx-ipd.c b/arch/mips/mach-octeon/cvmx-ipd.c new file mode 100644 index 0000000..c462b0f --- /dev/null +++ b/arch/mips/mach-octeon/cvmx-ipd.c @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018-2022 Marvell International Ltd. + * + * IPD Support. + */ + +#include <errno.h> +#include <log.h> +#include <time.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-range.h> +#include <mach/cvmx-global-resources.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-ipd.h> +#include <mach/cvmx-pki.h> +#include <mach/cvmx-pko3.h> +#include <mach/cvmx-pko3-queue.h> +#include <mach/cvmx-pko3-resources.h> + +#include <mach/cvmx-helper.h> +#include <mach/cvmx-helper-board.h> +#include <mach/cvmx-helper-cfg.h> + +#include <mach/cvmx-helper-bgx.h> +#include <mach/cvmx-helper-cfg.h> +#include <mach/cvmx-helper-util.h> +#include <mach/cvmx-helper-pki.h> + +cvmx_ipd_config_t cvmx_ipd_cfg = { + .first_mbuf_skip = 184, + .ipd_enable = 1, + .cache_mode = CVMX_IPD_OPC_MODE_STT, + .packet_pool = { 0, 2048, 0 }, + .wqe_pool = { 1, 128, 0 }, + .port_config = { CVMX_PIP_PORT_CFG_MODE_SKIPL2, + CVMX_POW_TAG_TYPE_ORDERED, CVMX_PIP_TAG_MODE_TUPLE, + .tag_fields = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } } +}; + +#define IPD_RED_AVG_DLY 1000 +#define IPD_RED_PRB_DLY 1000 + +void cvmx_ipd_config(u64 mbuff_size, u64 first_mbuff_skip, + u64 not_first_mbuff_skip, u64 first_back, u64 second_back, + u64 wqe_fpa_pool, cvmx_ipd_mode_t cache_mode, + u64 back_pres_enable_flag) +{ + cvmx_ipd_1st_mbuff_skip_t first_skip; + cvmx_ipd_mbuff_not_first_skip_t not_first_skip; + cvmx_ipd_packet_mbuff_size_t size; + cvmx_ipd_1st_next_ptr_back_t first_back_struct; + cvmx_ipd_second_next_ptr_back_t second_back_struct; + cvmx_ipd_wqe_fpa_queue_t wqe_pool; + cvmx_ipd_ctl_status_t ipd_ctl_reg; + + /* Enforce 1st skip minimum if WQE shares the buffer with packet */ + if (octeon_has_feature(OCTEON_FEATURE_NO_WPTR)) { + union cvmx_ipd_ctl_status ctl_status; + + ctl_status.u64 = csr_rd(CVMX_IPD_CTL_STATUS); + if (ctl_status.s.no_wptr != 0 && first_mbuff_skip < 16) + first_mbuff_skip = 16; + } + + first_skip.u64 = 0; + first_skip.s.skip_sz = first_mbuff_skip; + csr_wr(CVMX_IPD_1ST_MBUFF_SKIP, first_skip.u64); + + not_first_skip.u64 = 0; + not_first_skip.s.skip_sz = not_first_mbuff_skip; + csr_wr(CVMX_IPD_NOT_1ST_MBUFF_SKIP, not_first_skip.u64); + + size.u64 = 0; + size.s.mb_size = mbuff_size; + csr_wr(CVMX_IPD_PACKET_MBUFF_SIZE, size.u64); + + first_back_struct.u64 = 0; + first_back_struct.s.back = first_back; + csr_wr(CVMX_IPD_1st_NEXT_PTR_BACK, first_back_struct.u64); + + second_back_struct.u64 = 0; + second_back_struct.s.back = second_back; + csr_wr(CVMX_IPD_2nd_NEXT_PTR_BACK, second_back_struct.u64); + + wqe_pool.u64 = 0; + wqe_pool.s.wqe_pool = wqe_fpa_pool; + csr_wr(CVMX_IPD_WQE_FPA_QUEUE, wqe_pool.u64); + + ipd_ctl_reg.u64 = csr_rd(CVMX_IPD_CTL_STATUS); + ipd_ctl_reg.s.opc_mode = cache_mode; + ipd_ctl_reg.s.pbp_en = back_pres_enable_flag; + csr_wr(CVMX_IPD_CTL_STATUS, ipd_ctl_reg.u64); + + /* Note: the example RED code is below */ +} + +/** + * Enable IPD + */ +void cvmx_ipd_enable(void) +{ + cvmx_ipd_ctl_status_t ipd_reg; + + ipd_reg.u64 = csr_rd(CVMX_IPD_CTL_STATUS); + + /* + * busy-waiting for rst_done in o68 + */ + if (OCTEON_IS_MODEL(OCTEON_CN68XX)) + while (ipd_reg.s.rst_done != 0) + ipd_reg.u64 = csr_rd(CVMX_IPD_CTL_STATUS); + + if (ipd_reg.s.ipd_en) + debug("Warning: Enabling IPD when IPD already enabled.\n"); + + ipd_reg.s.ipd_en = 1; + + if (cvmx_ipd_cfg.enable_len_M8_fix) + ipd_reg.s.len_m8 = 1; + + csr_wr(CVMX_IPD_CTL_STATUS, ipd_reg.u64); +} diff --git a/arch/mips/mach-octeon/cvmx-pki-resources.c b/arch/mips/mach-octeon/cvmx-pki-resources.c new file mode 100644 index 0000000..ab84172 --- /dev/null +++ b/arch/mips/mach-octeon/cvmx-pki-resources.c @@ -0,0 +1,285 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018-2022 Marvell International Ltd. + * + * PKI Support. + */ + +#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-global-resources.h> + +#include <mach/cvmx-pki-defs.h> +#include <mach/cvmx-pko-defs.h> + +#include <mach/cvmx-pki.h> +#include <mach/cvmx-helper.h> +#include <mach/cvmx-helper-board.h> +#include <mach/cvmx-helper-cfg.h> + +static s32 cvmx_pki_style_refcnt[CVMX_MAX_NODES][CVMX_PKI_NUM_INTERNAL_STYLE]; + +/** + * This function allocates/reserves a style from pool of global styles per node. + * @param node node to allocate style from. + * @param style style to allocate, if -1 it will be allocated + * first available style from style resource. If index is positive + * number and in range, it will try to allocate specified style. + * @return style number on success, + * -1 on alloc failure. + * -2 on resource already reserved. + */ +int cvmx_pki_style_alloc(int node, int style) +{ + int rs; + + if (cvmx_create_global_resource_range(CVMX_GR_TAG_STYLE(node), + CVMX_PKI_NUM_INTERNAL_STYLE)) { + printf("ERROR: Failed to create styles global resource\n"); + return -1; + } + if (style >= 0) { + /* Reserving specific style, use refcnt for sharing */ + rs = cvmx_atomic_fetch_and_add32( + &cvmx_pki_style_refcnt[node][style], 1); + if (rs > 0) + return CVMX_RESOURCE_ALREADY_RESERVED; + + rs = cvmx_reserve_global_resource_range(CVMX_GR_TAG_STYLE(node), + style, style, 1); + if (rs == -1) { + /* This means the style is taken by another app */ + printf("ERROR: style %d is reserved by another app\n", + style); + cvmx_atomic_fetch_and_add32( + &cvmx_pki_style_refcnt[node][style], -1); + return CVMX_RESOURCE_ALLOC_FAILED; + } + } else { + /* Allocate first available style */ + rs = cvmx_allocate_global_resource_range( + CVMX_GR_TAG_STYLE(node), style, 1, 1); + if (rs < 0) { + printf("ERROR: Failed to allocate style, none available\n"); + return CVMX_RESOURCE_ALLOC_FAILED; + } + style = rs; + /* Increment refcnt for newly created style */ + cvmx_atomic_fetch_and_add32(&cvmx_pki_style_refcnt[node][style], + 1); + } + return style; +} + +/** + * This function frees a style from pool of global styles per node. + * @param node node to free style from. + * @param style style to free + * @return 0 on success, -1 on failure or + * if the style is shared a positive count of remaining users for this style. + */ +int cvmx_pki_style_free(int node, int style) +{ + int rs; + + rs = cvmx_atomic_fetch_and_add32(&cvmx_pki_style_refcnt[node][style], + -1); + if (rs > 1) + return rs - 1; + + if (cvmx_free_global_resource_range_with_base(CVMX_GR_TAG_STYLE(node), + style, 1) == -1) { + printf("ERROR Failed to release style %d\n", (int)style); + return -1; + } + return 0; +} + +/** + * This function allocates/reserves a cluster group from per node + cluster group resources. + * @param node node to allocate cluster group from. + @param cl_grp cluster group to allocate/reserve, if -1 , + * allocate any available cluster group. + * @return cluster group number + * -1 on alloc failure. + * -2 on resource already reserved. + */ +int cvmx_pki_cluster_grp_alloc(int node, int cl_grp) +{ + int rs; + + if (node >= CVMX_MAX_NODES) { + printf("ERROR: Invalid node number %d\n", node); + return -1; + } + if (cvmx_create_global_resource_range(CVMX_GR_TAG_CLUSTER_GRP(node), + CVMX_PKI_NUM_CLUSTER_GROUP)) { + printf("ERROR: Failed to create Cluster group global resource\n"); + return -1; + } + if (cl_grp >= 0) { + rs = cvmx_reserve_global_resource_range( + CVMX_GR_TAG_CLUSTER_GRP(node), 0, cl_grp, 1); + if (rs == -1) { + debug("INFO: cl_grp %d is already reserved\n", + (int)cl_grp); + return CVMX_RESOURCE_ALREADY_RESERVED; + } + } else { + rs = cvmx_allocate_global_resource_range( + CVMX_GR_TAG_CLUSTER_GRP(node), 0, 1, 1); + if (rs == -1) { + debug("Warning: Failed to alloc cluster grp\n"); + return CVMX_RESOURCE_ALLOC_FAILED; + } + } + cl_grp = rs; + return cl_grp; +} + +/** + * This function allocates/reserves a pcam entry from node + * @param node node to allocate pcam entry from. + * @param index index of pacm entry (0-191), if -1 , + * allocate any available pcam entry. + * @param bank pcam bank where to allocate/reserve pcan entry from + * @param cluster_mask mask of clusters from which pcam entry is needed. + * @return pcam entry of -1 on failure + */ +int cvmx_pki_pcam_entry_alloc(int node, int index, int bank, u64 cluster_mask) +{ + int rs = 0; + unsigned int cluster; + + for (cluster = 0; cluster < CVMX_PKI_NUM_CLUSTER; cluster++) { + if ((cluster_mask & (1 << cluster)) == 0) + continue; + rs = cvmx_create_global_resource_range( + CVMX_GR_TAG_PCAM(node, cluster, bank), + CVMX_PKI_TOTAL_PCAM_ENTRY); + if (rs != 0) { + printf("ERROR: Failed to create pki pcam global resource\n"); + return -1; + } + if (index >= 0) + rs = cvmx_reserve_global_resource_range( + CVMX_GR_TAG_PCAM(node, cluster, bank), cluster, + index, 1); + else + rs = cvmx_allocate_global_resource_range( + CVMX_GR_TAG_PCAM(node, cluster, bank), cluster, + 1, 1); + if (rs == -1) { + printf("ERROR: PCAM :index %d not available in cluster %d bank %d", + (int)index, (int)cluster, bank); + return -1; + } + } /* for cluster */ + index = rs; + /* implement cluster handle for pass2, for now assume + all clusters will have same base index*/ + return index; +} + +/** + * This function allocates/reserves QPG table entries per node. + * @param node node number. + * @param base_offset base_offset in qpg table. If -1, first available + * qpg base_offset will be allocated. If base_offset is positive + * number and in range, it will try to allocate specified base_offset. + * @param count number of consecutive qpg entries to allocate. They will be consecutive + * from base offset. + * @return qpg table base offset number on success + * -1 on alloc failure. + * -2 on resource already reserved. + */ +int cvmx_pki_qpg_entry_alloc(int node, int base_offset, int count) +{ + int rs; + + if (cvmx_create_global_resource_range(CVMX_GR_TAG_QPG_ENTRY(node), + CVMX_PKI_NUM_QPG_ENTRY)) { + printf("ERROR: Failed to create qpg_entry global resource\n"); + return -1; + } + if (base_offset >= 0) { + rs = cvmx_reserve_global_resource_range( + CVMX_GR_TAG_QPG_ENTRY(node), base_offset, base_offset, + count); + if (rs == -1) { + debug("INFO: qpg entry %d is already reserved\n", + (int)base_offset); + return CVMX_RESOURCE_ALREADY_RESERVED; + } + } else { + rs = cvmx_allocate_global_resource_range( + CVMX_GR_TAG_QPG_ENTRY(node), base_offset, count, 1); + if (rs == -1) { + printf("ERROR: Failed to allocate qpg entry\n"); + return CVMX_RESOURCE_ALLOC_FAILED; + } + } + base_offset = rs; + return base_offset; +} + +/** + * This function frees QPG table entries per node. + * @param node node number. + * @param base_offset base_offset in qpg table. If -1, first available + * qpg base_offset will be allocated. If base_offset is positive + * number and in range, it will try to allocate specified base_offset. + * @param count number of consecutive qpg entries to allocate. They will be consecutive + * from base offset. + * @return qpg table base offset number on success, -1 on failure. + */ +int cvmx_pki_qpg_entry_free(int node, int base_offset, int count) +{ + if (cvmx_free_global_resource_range_with_base( + CVMX_GR_TAG_QPG_ENTRY(node), base_offset, count) == -1) { + printf("ERROR Failed to release qpg offset %d", + (int)base_offset); + return -1; + } + return 0; +} + +int cvmx_pki_mtag_idx_alloc(int node, int idx) +{ + if (cvmx_create_global_resource_range(CVMX_GR_TAG_MTAG_IDX(node), + CVMX_PKI_NUM_MTAG_IDX)) { + printf("ERROR: Failed to create MTAG-IDX global resource\n"); + return -1; + } + if (idx >= 0) { + idx = cvmx_reserve_global_resource_range( + CVMX_GR_TAG_MTAG_IDX(node), idx, idx, 1); + if (idx == -1) { + debug("INFO: MTAG index %d is already reserved\n", + (int)idx); + return CVMX_RESOURCE_ALREADY_RESERVED; + } + } else { + idx = cvmx_allocate_global_resource_range( + CVMX_GR_TAG_MTAG_IDX(node), idx, 1, 1); + if (idx == -1) { + printf("ERROR: Failed to allocate MTAG index\n"); + return CVMX_RESOURCE_ALLOC_FAILED; + } + } + return idx; +} diff --git a/arch/mips/mach-octeon/cvmx-pki.c b/arch/mips/mach-octeon/cvmx-pki.c new file mode 100644 index 0000000..4e2cf08 --- /dev/null +++ b/arch/mips/mach-octeon/cvmx-pki.c @@ -0,0 +1,910 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018-2022 Marvell International Ltd. + * + * PKI Support. + */ + +#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-pki-cluster.h> +#include <mach/cvmx-pki-resources.h> + +#include <mach/cvmx-helper.h> +#include <mach/cvmx-helper-board.h> +#include <mach/cvmx-helper-cfg.h> + +/** + * This function enables PKI + * + * @param node Node to enable PKI. + */ +void cvmx_pki_enable(int node) +{ + cvmx_pki_sft_rst_t sft_rst; + cvmx_pki_buf_ctl_t buf_ctl; + + sft_rst.u64 = csr_rd_node(node, CVMX_PKI_SFT_RST); + while (sft_rst.s.busy != 0) + sft_rst.u64 = csr_rd_node(node, CVMX_PKI_SFT_RST); + + buf_ctl.u64 = csr_rd_node(node, CVMX_PKI_BUF_CTL); + if (buf_ctl.s.pki_en) + debug("Warning: Enabling PKI when PKI already enabled.\n"); + + buf_ctl.s.pki_en = 1; + csr_wr_node(node, CVMX_PKI_BUF_CTL, buf_ctl.u64); +} + +/** + * This function sets the clusters in PKI. + * + * @param node Node to set clusters. + */ +int cvmx_pki_setup_clusters(int node) +{ + int i; + + for (i = 0; i < cvmx_pki_cluster_code_length; i++) + csr_wr_node(node, CVMX_PKI_IMEMX(i), + cvmx_pki_cluster_code_default[i]); + + return 0; +} + +/** + * This function reads global configuration of PKI block. + * + * @param node Node number. + * @param gbl_cfg Pointer to struct to read global configuration. + */ +void cvmx_pki_read_global_config(int node, + struct cvmx_pki_global_config *gbl_cfg) +{ + cvmx_pki_stat_ctl_t stat_ctl; + cvmx_pki_icgx_cfg_t icg_cfg; + cvmx_pki_gbl_pen_t gbl_pen; + cvmx_pki_tag_secret_t tag_secret; + cvmx_pki_frm_len_chkx_t frm_len_chk; + cvmx_pki_buf_ctl_t buf_ctl; + unsigned int cl_grp; + int id; + + stat_ctl.u64 = csr_rd_node(node, CVMX_PKI_STAT_CTL); + gbl_cfg->stat_mode = stat_ctl.s.mode; + + for (cl_grp = 0; cl_grp < CVMX_PKI_NUM_CLUSTER_GROUP; cl_grp++) { + icg_cfg.u64 = csr_rd_node(node, CVMX_PKI_ICGX_CFG(cl_grp)); + gbl_cfg->cluster_mask[cl_grp] = icg_cfg.s.clusters; + } + gbl_pen.u64 = csr_rd_node(node, CVMX_PKI_GBL_PEN); + gbl_cfg->gbl_pen.virt_pen = gbl_pen.s.virt_pen; + gbl_cfg->gbl_pen.clg_pen = gbl_pen.s.clg_pen; + gbl_cfg->gbl_pen.cl2_pen = gbl_pen.s.cl2_pen; + gbl_cfg->gbl_pen.l4_pen = gbl_pen.s.l4_pen; + gbl_cfg->gbl_pen.il3_pen = gbl_pen.s.il3_pen; + gbl_cfg->gbl_pen.l3_pen = gbl_pen.s.l3_pen; + gbl_cfg->gbl_pen.mpls_pen = gbl_pen.s.mpls_pen; + gbl_cfg->gbl_pen.fulc_pen = gbl_pen.s.fulc_pen; + gbl_cfg->gbl_pen.dsa_pen = gbl_pen.s.dsa_pen; + gbl_cfg->gbl_pen.hg_pen = gbl_pen.s.hg_pen; + + tag_secret.u64 = csr_rd_node(node, CVMX_PKI_TAG_SECRET); + gbl_cfg->tag_secret.dst6 = tag_secret.s.dst6; + gbl_cfg->tag_secret.src6 = tag_secret.s.src6; + gbl_cfg->tag_secret.dst = tag_secret.s.dst; + gbl_cfg->tag_secret.src = tag_secret.s.src; + + for (id = 0; id < CVMX_PKI_NUM_FRAME_CHECK; id++) { + frm_len_chk.u64 = csr_rd_node(node, CVMX_PKI_FRM_LEN_CHKX(id)); + gbl_cfg->frm_len[id].maxlen = frm_len_chk.s.maxlen; + gbl_cfg->frm_len[id].minlen = frm_len_chk.s.minlen; + } + buf_ctl.u64 = csr_rd_node(node, CVMX_PKI_BUF_CTL); + gbl_cfg->fpa_wait = buf_ctl.s.fpa_wait; +} + +/** + * This function writes max and min frame lengths to hardware which can be used + * to check the size of frame arrived.There are 2 possible combination which are + * indicated by id field. + * + * @param node Node number. + * @param id Choose which frame len register to write to + * @param len_chk Struct containing byte count for max-sized/min-sized frame check. + */ +static void cvmx_pki_write_frame_len(int node, int id, + struct cvmx_pki_frame_len len_chk) +{ + cvmx_pki_frm_len_chkx_t frm_len_chk; + + frm_len_chk.u64 = csr_rd_node(node, CVMX_PKI_FRM_LEN_CHKX(id)); + frm_len_chk.s.maxlen = len_chk.maxlen; + frm_len_chk.s.minlen = len_chk.minlen; + csr_wr_node(node, CVMX_PKI_FRM_LEN_CHKX(id), frm_len_chk.u64); +} + +/** + * This function writes global configuration of PKI into hw. + * + * @param node Node number. + * @param gbl_cfg Pointer to struct to global configuration. + */ +void cvmx_pki_write_global_config(int node, + struct cvmx_pki_global_config *gbl_cfg) +{ + cvmx_pki_stat_ctl_t stat_ctl; + cvmx_pki_buf_ctl_t buf_ctl; + unsigned int cl_grp; + + for (cl_grp = 0; cl_grp < CVMX_PKI_NUM_CLUSTER_GROUP; cl_grp++) + cvmx_pki_attach_cluster_to_group(node, cl_grp, + gbl_cfg->cluster_mask[cl_grp]); + + stat_ctl.u64 = 0; + stat_ctl.s.mode = gbl_cfg->stat_mode; + csr_wr_node(node, CVMX_PKI_STAT_CTL, stat_ctl.u64); + + buf_ctl.u64 = csr_rd_node(node, CVMX_PKI_BUF_CTL); + buf_ctl.s.fpa_wait = gbl_cfg->fpa_wait; + csr_wr_node(node, CVMX_PKI_BUF_CTL, buf_ctl.u64); + + cvmx_pki_write_global_parse(node, gbl_cfg->gbl_pen); + cvmx_pki_write_tag_secret(node, gbl_cfg->tag_secret); + cvmx_pki_write_frame_len(node, 0, gbl_cfg->frm_len[0]); + cvmx_pki_write_frame_len(node, 1, gbl_cfg->frm_len[1]); +} + +/** + * This function reads per pkind parameters in hardware which defines how + * the incoming packet is processed. + * + * @param node Node number. + * @param pkind PKI supports a large number of incoming interfaces and packets + * arriving on different interfaces or channels may want to be processed + * differently. PKI uses the pkind to determine how the incoming packet + * is processed. + * @param pkind_cfg Pointer to struct conatining pkind configuration read + * from the hardware. + */ +int cvmx_pki_read_pkind_config(int node, int pkind, + struct cvmx_pki_pkind_config *pkind_cfg) +{ + int cluster = 0; + u64 cl_mask; + cvmx_pki_pkindx_icgsel_t icgsel; + cvmx_pki_clx_pkindx_style_t pstyle; + cvmx_pki_icgx_cfg_t icg_cfg; + cvmx_pki_clx_pkindx_cfg_t pcfg; + cvmx_pki_clx_pkindx_skip_t skip; + cvmx_pki_clx_pkindx_l2_custom_t l2cust; + cvmx_pki_clx_pkindx_lg_custom_t lgcust; + + icgsel.u64 = csr_rd_node(node, CVMX_PKI_PKINDX_ICGSEL(pkind)); + icg_cfg.u64 = csr_rd_node(node, CVMX_PKI_ICGX_CFG(icgsel.s.icg)); + pkind_cfg->cluster_grp = (uint8_t)icgsel.s.icg; + cl_mask = (uint64_t)icg_cfg.s.clusters; + cluster = __builtin_ffsll(cl_mask) - 1; + + pstyle.u64 = + csr_rd_node(node, CVMX_PKI_CLX_PKINDX_STYLE(pkind, cluster)); + pkind_cfg->initial_parse_mode = pstyle.s.pm; + pkind_cfg->initial_style = pstyle.s.style; + + pcfg.u64 = csr_rd_node(node, CVMX_PKI_CLX_PKINDX_CFG(pkind, cluster)); + pkind_cfg->fcs_pres = pcfg.s.fcs_pres; + pkind_cfg->parse_en.inst_hdr = pcfg.s.inst_hdr; + pkind_cfg->parse_en.mpls_en = pcfg.s.mpls_en; + pkind_cfg->parse_en.lg_custom = pcfg.s.lg_custom; + pkind_cfg->parse_en.fulc_en = pcfg.s.fulc_en; + pkind_cfg->parse_en.dsa_en = pcfg.s.dsa_en; + pkind_cfg->parse_en.hg2_en = pcfg.s.hg2_en; + pkind_cfg->parse_en.hg_en = pcfg.s.hg_en; + + skip.u64 = csr_rd_node(node, CVMX_PKI_CLX_PKINDX_SKIP(pkind, cluster)); + pkind_cfg->fcs_skip = skip.s.fcs_skip; + pkind_cfg->inst_skip = skip.s.inst_skip; + + l2cust.u64 = csr_rd_node(node, + CVMX_PKI_CLX_PKINDX_L2_CUSTOM(pkind, cluster)); + pkind_cfg->l2_scan_offset = l2cust.s.offset; + + lgcust.u64 = csr_rd_node(node, + CVMX_PKI_CLX_PKINDX_LG_CUSTOM(pkind, cluster)); + pkind_cfg->lg_scan_offset = lgcust.s.offset; + return 0; +} + +/** + * This function writes per pkind parameters in hardware which defines how + * the incoming packet is processed. + * + * @param node Node number. + * @param pkind PKI supports a large number of incoming interfaces and packets + * arriving on different interfaces or channels may want to be processed + * differently. PKI uses the pkind to determine how the incoming + * packet is processed. + * @param pkind_cfg Pointer to struct conatining pkind configuration need + * to be written in the hardware. + */ +int cvmx_pki_write_pkind_config(int node, int pkind, + struct cvmx_pki_pkind_config *pkind_cfg) +{ + unsigned int cluster = 0; + u64 cluster_mask; + cvmx_pki_pkindx_icgsel_t icgsel; + cvmx_pki_clx_pkindx_style_t pstyle; + cvmx_pki_icgx_cfg_t icg_cfg; + cvmx_pki_clx_pkindx_cfg_t pcfg; + cvmx_pki_clx_pkindx_skip_t skip; + cvmx_pki_clx_pkindx_l2_custom_t l2cust; + cvmx_pki_clx_pkindx_lg_custom_t lgcust; + + if (pkind >= CVMX_PKI_NUM_PKIND || + pkind_cfg->cluster_grp >= CVMX_PKI_NUM_CLUSTER_GROUP || + pkind_cfg->initial_style >= CVMX_PKI_NUM_FINAL_STYLE) { + debug("ERROR: Configuring PKIND pkind = %d cluster_group = %d style = %d\n", + pkind, pkind_cfg->cluster_grp, pkind_cfg->initial_style); + return -1; + } + icgsel.u64 = csr_rd_node(node, CVMX_PKI_PKINDX_ICGSEL(pkind)); + icgsel.s.icg = pkind_cfg->cluster_grp; + csr_wr_node(node, CVMX_PKI_PKINDX_ICGSEL(pkind), icgsel.u64); + + icg_cfg.u64 = + csr_rd_node(node, CVMX_PKI_ICGX_CFG(pkind_cfg->cluster_grp)); + cluster_mask = (uint64_t)icg_cfg.s.clusters; + while (cluster < CVMX_PKI_NUM_CLUSTER) { + if (cluster_mask & (0x01L << cluster)) { + pstyle.u64 = csr_rd_node( + node, + CVMX_PKI_CLX_PKINDX_STYLE(pkind, cluster)); + pstyle.s.pm = pkind_cfg->initial_parse_mode; + pstyle.s.style = pkind_cfg->initial_style; + csr_wr_node(node, + CVMX_PKI_CLX_PKINDX_STYLE(pkind, cluster), + pstyle.u64); + + pcfg.u64 = csr_rd_node( + node, CVMX_PKI_CLX_PKINDX_CFG(pkind, cluster)); + pcfg.s.fcs_pres = pkind_cfg->fcs_pres; + pcfg.s.inst_hdr = pkind_cfg->parse_en.inst_hdr; + pcfg.s.mpls_en = pkind_cfg->parse_en.mpls_en; + pcfg.s.lg_custom = pkind_cfg->parse_en.lg_custom; + pcfg.s.fulc_en = pkind_cfg->parse_en.fulc_en; + pcfg.s.dsa_en = pkind_cfg->parse_en.dsa_en; + pcfg.s.hg2_en = pkind_cfg->parse_en.hg2_en; + pcfg.s.hg_en = pkind_cfg->parse_en.hg_en; + csr_wr_node(node, + CVMX_PKI_CLX_PKINDX_CFG(pkind, cluster), + pcfg.u64); + + skip.u64 = csr_rd_node( + node, CVMX_PKI_CLX_PKINDX_SKIP(pkind, cluster)); + skip.s.fcs_skip = pkind_cfg->fcs_skip; + skip.s.inst_skip = pkind_cfg->inst_skip; + csr_wr_node(node, + CVMX_PKI_CLX_PKINDX_SKIP(pkind, cluster), + skip.u64); + + l2cust.u64 = csr_rd_node( + node, + CVMX_PKI_CLX_PKINDX_L2_CUSTOM(pkind, cluster)); + l2cust.s.offset = pkind_cfg->l2_scan_offset; + csr_wr_node(node, + CVMX_PKI_CLX_PKINDX_L2_CUSTOM(pkind, + cluster), + l2cust.u64); + + lgcust.u64 = csr_rd_node( + node, + CVMX_PKI_CLX_PKINDX_LG_CUSTOM(pkind, cluster)); + lgcust.s.offset = pkind_cfg->lg_scan_offset; + csr_wr_node(node, + CVMX_PKI_CLX_PKINDX_LG_CUSTOM(pkind, + cluster), + lgcust.u64); + } + cluster++; + } + return 0; +} + +/** + * This function reads parameters associated with tag configuration in hardware. + * Only first cluster in the group is used. + * + * @param node Node number. + * @param style Style to configure tag for. + * @param cluster_mask Mask of clusters to configure the style for. + * @param tag_cfg Pointer to tag configuration struct. + */ +void cvmx_pki_read_tag_config(int node, int style, uint64_t cluster_mask, + struct cvmx_pki_style_tag_cfg *tag_cfg) +{ + int mask, tag_idx, index; + cvmx_pki_clx_stylex_cfg2_t style_cfg2; + cvmx_pki_clx_stylex_alg_t style_alg; + cvmx_pki_stylex_tag_sel_t tag_sel; + cvmx_pki_tag_incx_ctl_t tag_ctl; + cvmx_pki_tag_incx_mask_t tag_mask; + int cluster = __builtin_ffsll(cluster_mask) - 1; + + style_cfg2.u64 = + csr_rd_node(node, CVMX_PKI_CLX_STYLEX_CFG2(style, cluster)); + style_alg.u64 = + csr_rd_node(node, CVMX_PKI_CLX_STYLEX_ALG(style, cluster)); + + /* 7-Tuple Tag: */ + tag_cfg->tag_fields.layer_g_src = style_cfg2.s.tag_src_lg; + tag_cfg->tag_fields.layer_f_src = style_cfg2.s.tag_src_lf; + tag_cfg->tag_fields.layer_e_src = style_cfg2.s.tag_src_le; + tag_cfg->tag_fields.layer_d_src = style_cfg2.s.tag_src_ld; + tag_cfg->tag_fields.layer_c_src = style_cfg2.s.tag_src_lc; + tag_cfg->tag_fields.layer_b_src = style_cfg2.s.tag_src_lb; + tag_cfg->tag_fields.layer_g_dst = style_cfg2.s.tag_dst_lg; + tag_cfg->tag_fields.layer_f_dst = style_cfg2.s.tag_dst_lf; + tag_cfg->tag_fields.layer_e_dst = style_cfg2.s.tag_dst_le; + tag_cfg->tag_fields.layer_d_dst = style_cfg2.s.tag_dst_ld; + tag_cfg->tag_fields.layer_c_dst = style_cfg2.s.tag_dst_lc; + tag_cfg->tag_fields.layer_b_dst = style_cfg2.s.tag_dst_lb; + tag_cfg->tag_fields.tag_vni = style_alg.s.tag_vni; + tag_cfg->tag_fields.tag_gtp = style_alg.s.tag_gtp; + tag_cfg->tag_fields.tag_spi = style_alg.s.tag_spi; + tag_cfg->tag_fields.tag_sync = style_alg.s.tag_syn; + tag_cfg->tag_fields.ip_prot_nexthdr = style_alg.s.tag_pctl; + tag_cfg->tag_fields.second_vlan = style_alg.s.tag_vs1; + tag_cfg->tag_fields.first_vlan = style_alg.s.tag_vs0; + tag_cfg->tag_fields.mpls_label = style_alg.s.tag_mpls0; + tag_cfg->tag_fields.input_port = style_alg.s.tag_prt; + + /* Custom-Mask Tag: */ + tag_sel.u64 = csr_rd_node(node, CVMX_PKI_STYLEX_TAG_SEL(style)); + for (mask = 0; mask < 4; mask++) { + tag_cfg->mask_tag[mask].enable = + (style_cfg2.s.tag_inc & (1 << mask)) != 0; + switch (mask) { + case 0: + tag_idx = tag_sel.s.tag_idx0; + break; + case 1: + tag_idx = tag_sel.s.tag_idx1; + break; + case 2: + tag_idx = tag_sel.s.tag_idx2; + break; + case 3: + tag_idx = tag_sel.s.tag_idx3; + break; + } + index = tag_idx * 4 + mask; + tag_mask.u64 = csr_rd_node(node, CVMX_PKI_TAG_INCX_MASK(index)); + tag_cfg->mask_tag[mask].val = tag_mask.s.en; + tag_ctl.u64 = csr_rd_node(node, CVMX_PKI_TAG_INCX_CTL(index)); + tag_cfg->mask_tag[mask].base = tag_ctl.s.ptr_sel; + tag_cfg->mask_tag[mask].offset = tag_ctl.s.offset; + } +} + +/** + * This function writes/configures parameters associated with tag configuration in + * hardware. In Custom-Mask Tagging, all four masks use the same base index + * to access Tag Control and Tag Mask registers. + * + * @param node Node number. + * @param style Style to configure tag for. + * @param cluster_mask Mask of clusters to configure the style for. + * @param tag_cfg Pointer to taf configuration struct. + */ +void cvmx_pki_write_tag_config(int node, int style, uint64_t cluster_mask, + struct cvmx_pki_style_tag_cfg *tag_cfg) +{ + int mask, index, tag_idx, mtag_en = 0; + unsigned int cluster = 0; + cvmx_pki_clx_stylex_cfg2_t scfg2; + cvmx_pki_clx_stylex_alg_t style_alg; + cvmx_pki_tag_incx_ctl_t tag_ctl; + cvmx_pki_tag_incx_mask_t tag_mask; + cvmx_pki_stylex_tag_sel_t tag_sel; + + while (cluster < CVMX_PKI_NUM_CLUSTER) { + if (cluster_mask & (0x01L << cluster)) { + /* 7-Tuple Tag: */ + scfg2.u64 = csr_rd_node( + node, CVMX_PKI_CLX_STYLEX_CFG2(style, cluster)); + scfg2.s.tag_src_lg = tag_cfg->tag_fields.layer_g_src; + scfg2.s.tag_src_lf = tag_cfg->tag_fields.layer_f_src; + scfg2.s.tag_src_le = tag_cfg->tag_fields.layer_e_src; + scfg2.s.tag_src_ld = tag_cfg->tag_fields.layer_d_src; + scfg2.s.tag_src_lc = tag_cfg->tag_fields.layer_c_src; + scfg2.s.tag_src_lb = tag_cfg->tag_fields.layer_b_src; + scfg2.s.tag_dst_lg = tag_cfg->tag_fields.layer_g_dst; + scfg2.s.tag_dst_lf = tag_cfg->tag_fields.layer_f_dst; + scfg2.s.tag_dst_le = tag_cfg->tag_fields.layer_e_dst; + scfg2.s.tag_dst_ld = tag_cfg->tag_fields.layer_d_dst; + scfg2.s.tag_dst_lc = tag_cfg->tag_fields.layer_c_dst; + scfg2.s.tag_dst_lb = tag_cfg->tag_fields.layer_b_dst; + csr_wr_node(node, + CVMX_PKI_CLX_STYLEX_CFG2(style, cluster), + scfg2.u64); + + style_alg.u64 = csr_rd_node( + node, CVMX_PKI_CLX_STYLEX_ALG(style, cluster)); + style_alg.s.tag_vni = tag_cfg->tag_fields.tag_vni; + style_alg.s.tag_gtp = tag_cfg->tag_fields.tag_gtp; + style_alg.s.tag_spi = tag_cfg->tag_fields.tag_spi; + style_alg.s.tag_syn = tag_cfg->tag_fields.tag_sync; + style_alg.s.tag_pctl = + tag_cfg->tag_fields.ip_prot_nexthdr; + style_alg.s.tag_vs1 = tag_cfg->tag_fields.second_vlan; + style_alg.s.tag_vs0 = tag_cfg->tag_fields.first_vlan; + style_alg.s.tag_mpls0 = tag_cfg->tag_fields.mpls_label; + style_alg.s.tag_prt = tag_cfg->tag_fields.input_port; + csr_wr_node(node, + CVMX_PKI_CLX_STYLEX_ALG(style, cluster), + style_alg.u64); + + /* Custom-Mask Tag (Part 1): */ + for (mask = 0; mask < 4; mask++) { + if (tag_cfg->mask_tag[mask].enable) + mtag_en++; + } + if (mtag_en) { + scfg2.u64 = csr_rd_node( + node, CVMX_PKI_CLX_STYLEX_CFG2( + style, cluster)); + scfg2.s.tag_inc = 0; + for (mask = 0; mask < 4; mask++) { + if (tag_cfg->mask_tag[mask].enable) + scfg2.s.tag_inc |= 1 << mask; + } + csr_wr_node(node, + CVMX_PKI_CLX_STYLEX_CFG2(style, + cluster), + scfg2.u64); + } + } + cluster++; + } + /* Custom-Mask Tag (Part 2): */ + if (mtag_en) { + tag_idx = cvmx_pki_mtag_idx_alloc(node, -1); + if (tag_idx < 0) + return; + + tag_sel.u64 = csr_rd_node(node, CVMX_PKI_STYLEX_TAG_SEL(style)); + for (mask = 0; mask < 4; mask++) { + if (tag_cfg->mask_tag[mask].enable) { + switch (mask) { + case 0: + tag_sel.s.tag_idx0 = tag_idx; + break; + case 1: + tag_sel.s.tag_idx1 = tag_idx; + break; + case 2: + tag_sel.s.tag_idx2 = tag_idx; + break; + case 3: + tag_sel.s.tag_idx3 = tag_idx; + break; + } + index = tag_idx * 4 + mask; + tag_mask.u64 = csr_rd_node( + node, CVMX_PKI_TAG_INCX_MASK(index)); + tag_mask.s.en = tag_cfg->mask_tag[mask].val; + csr_wr_node(node, CVMX_PKI_TAG_INCX_MASK(index), + tag_mask.u64); + + tag_ctl.u64 = csr_rd_node( + node, CVMX_PKI_TAG_INCX_CTL(index)); + tag_ctl.s.ptr_sel = + tag_cfg->mask_tag[mask].base; + tag_ctl.s.offset = + tag_cfg->mask_tag[mask].offset; + csr_wr_node(node, CVMX_PKI_TAG_INCX_CTL(index), + tag_ctl.u64); + } + } + csr_wr_node(node, CVMX_PKI_STYLEX_TAG_SEL(style), tag_sel.u64); + } +} + +/** + * This function reads parameters associated with style in hardware. + * + * @param node Node number. + * @param style Style to read from. + * @param cluster_mask Mask of clusters style belongs to. + * @param style_cfg Pointer to style config struct. + */ +void cvmx_pki_read_style_config(int node, int style, uint64_t cluster_mask, + struct cvmx_pki_style_config *style_cfg) +{ + cvmx_pki_clx_stylex_cfg_t scfg; + cvmx_pki_clx_stylex_cfg2_t scfg2; + cvmx_pki_clx_stylex_alg_t style_alg; + cvmx_pki_stylex_buf_t style_buf; + int cluster = __builtin_ffsll(cluster_mask) - 1; + + scfg.u64 = csr_rd_node(node, CVMX_PKI_CLX_STYLEX_CFG(style, cluster)); + scfg2.u64 = csr_rd_node(node, CVMX_PKI_CLX_STYLEX_CFG2(style, cluster)); + style_alg.u64 = + csr_rd_node(node, CVMX_PKI_CLX_STYLEX_ALG(style, cluster)); + style_buf.u64 = csr_rd_node(node, CVMX_PKI_STYLEX_BUF(style)); + + style_cfg->parm_cfg.ip6_udp_opt = scfg.s.ip6_udp_opt; + style_cfg->parm_cfg.lenerr_en = scfg.s.lenerr_en; + style_cfg->parm_cfg.lenerr_eqpad = scfg.s.lenerr_eqpad; + style_cfg->parm_cfg.maxerr_en = scfg.s.maxerr_en; + style_cfg->parm_cfg.minerr_en = scfg.s.minerr_en; + style_cfg->parm_cfg.fcs_chk = scfg.s.fcs_chk; + style_cfg->parm_cfg.fcs_strip = scfg.s.fcs_strip; + style_cfg->parm_cfg.minmax_sel = scfg.s.minmax_sel; + style_cfg->parm_cfg.qpg_base = scfg.s.qpg_base; + style_cfg->parm_cfg.qpg_dis_padd = scfg.s.qpg_dis_padd; + style_cfg->parm_cfg.qpg_dis_aura = scfg.s.qpg_dis_aura; + style_cfg->parm_cfg.qpg_dis_grp = scfg.s.qpg_dis_grp; + style_cfg->parm_cfg.qpg_dis_grptag = scfg.s.qpg_dis_grptag; + style_cfg->parm_cfg.rawdrp = scfg.s.rawdrp; + style_cfg->parm_cfg.force_drop = scfg.s.drop; + style_cfg->parm_cfg.nodrop = scfg.s.nodrop; + + style_cfg->parm_cfg.len_lg = scfg2.s.len_lg; + style_cfg->parm_cfg.len_lf = scfg2.s.len_lf; + style_cfg->parm_cfg.len_le = scfg2.s.len_le; + style_cfg->parm_cfg.len_ld = scfg2.s.len_ld; + style_cfg->parm_cfg.len_lc = scfg2.s.len_lc; + style_cfg->parm_cfg.len_lb = scfg2.s.len_lb; + style_cfg->parm_cfg.csum_lg = scfg2.s.csum_lg; + style_cfg->parm_cfg.csum_lf = scfg2.s.csum_lf; + style_cfg->parm_cfg.csum_le = scfg2.s.csum_le; + style_cfg->parm_cfg.csum_ld = scfg2.s.csum_ld; + style_cfg->parm_cfg.csum_lc = scfg2.s.csum_lc; + style_cfg->parm_cfg.csum_lb = scfg2.s.csum_lb; + + style_cfg->parm_cfg.qpg_qos = style_alg.s.qpg_qos; + style_cfg->parm_cfg.tag_type = style_alg.s.tt; + style_cfg->parm_cfg.apad_nip = style_alg.s.apad_nip; + style_cfg->parm_cfg.qpg_port_sh = style_alg.s.qpg_port_sh; + style_cfg->parm_cfg.qpg_port_msb = style_alg.s.qpg_port_msb; + style_cfg->parm_cfg.wqe_vs = style_alg.s.wqe_vs; + + style_cfg->parm_cfg.pkt_lend = style_buf.s.pkt_lend; + style_cfg->parm_cfg.wqe_hsz = style_buf.s.wqe_hsz; + style_cfg->parm_cfg.wqe_skip = style_buf.s.wqe_skip * 128; + style_cfg->parm_cfg.first_skip = style_buf.s.first_skip * 8; + style_cfg->parm_cfg.later_skip = style_buf.s.later_skip * 8; + style_cfg->parm_cfg.cache_mode = style_buf.s.opc_mode; + style_cfg->parm_cfg.mbuff_size = style_buf.s.mb_size * 8; + style_cfg->parm_cfg.dis_wq_dat = style_buf.s.dis_wq_dat; + + cvmx_pki_read_tag_config(node, style, cluster_mask, + &style_cfg->tag_cfg); +} + +/** + * This function writes/configures parameters associated with style in hardware. + * + * @param node Node number. + * @param style Style to configure. + * @param cluster_mask Mask of clusters to configure the style for. + * @param style_cfg Pointer to style config struct. + */ +void cvmx_pki_write_style_config(int node, uint64_t style, u64 cluster_mask, + struct cvmx_pki_style_config *style_cfg) +{ + cvmx_pki_clx_stylex_cfg_t scfg; + cvmx_pki_clx_stylex_cfg2_t scfg2; + cvmx_pki_clx_stylex_alg_t style_alg; + cvmx_pki_stylex_buf_t style_buf; + unsigned int cluster = 0; + + while (cluster < CVMX_PKI_NUM_CLUSTER) { + if (cluster_mask & (0x01L << cluster)) { + scfg.u64 = csr_rd_node( + node, CVMX_PKI_CLX_STYLEX_CFG(style, cluster)); + scfg.s.ip6_udp_opt = style_cfg->parm_cfg.ip6_udp_opt; + scfg.s.lenerr_en = style_cfg->parm_cfg.lenerr_en; + scfg.s.lenerr_eqpad = style_cfg->parm_cfg.lenerr_eqpad; + scfg.s.maxerr_en = style_cfg->parm_cfg.maxerr_en; + scfg.s.minerr_en = style_cfg->parm_cfg.minerr_en; + scfg.s.fcs_chk = style_cfg->parm_cfg.fcs_chk; + scfg.s.fcs_strip = style_cfg->parm_cfg.fcs_strip; + scfg.s.minmax_sel = style_cfg->parm_cfg.minmax_sel; + scfg.s.qpg_base = style_cfg->parm_cfg.qpg_base; + scfg.s.qpg_dis_padd = style_cfg->parm_cfg.qpg_dis_padd; + scfg.s.qpg_dis_aura = style_cfg->parm_cfg.qpg_dis_aura; + scfg.s.qpg_dis_grp = style_cfg->parm_cfg.qpg_dis_grp; + scfg.s.qpg_dis_grptag = + style_cfg->parm_cfg.qpg_dis_grptag; + scfg.s.rawdrp = style_cfg->parm_cfg.rawdrp; + scfg.s.drop = style_cfg->parm_cfg.force_drop; + scfg.s.nodrop = style_cfg->parm_cfg.nodrop; + csr_wr_node(node, + CVMX_PKI_CLX_STYLEX_CFG(style, cluster), + scfg.u64); + + scfg2.u64 = csr_rd_node( + node, CVMX_PKI_CLX_STYLEX_CFG2(style, cluster)); + scfg2.s.len_lg = style_cfg->parm_cfg.len_lg; + scfg2.s.len_lf = style_cfg->parm_cfg.len_lf; + scfg2.s.len_le = style_cfg->parm_cfg.len_le; + scfg2.s.len_ld = style_cfg->parm_cfg.len_ld; + scfg2.s.len_lc = style_cfg->parm_cfg.len_lc; + scfg2.s.len_lb = style_cfg->parm_cfg.len_lb; + scfg2.s.csum_lg = style_cfg->parm_cfg.csum_lg; + scfg2.s.csum_lf = style_cfg->parm_cfg.csum_lf; + scfg2.s.csum_le = style_cfg->parm_cfg.csum_le; + scfg2.s.csum_ld = style_cfg->parm_cfg.csum_ld; + scfg2.s.csum_lc = style_cfg->parm_cfg.csum_lc; + scfg2.s.csum_lb = style_cfg->parm_cfg.csum_lb; + csr_wr_node(node, + CVMX_PKI_CLX_STYLEX_CFG2(style, cluster), + scfg2.u64); + + style_alg.u64 = csr_rd_node( + node, CVMX_PKI_CLX_STYLEX_ALG(style, cluster)); + style_alg.s.qpg_qos = style_cfg->parm_cfg.qpg_qos; + style_alg.s.tt = style_cfg->parm_cfg.tag_type; + style_alg.s.apad_nip = style_cfg->parm_cfg.apad_nip; + style_alg.s.qpg_port_sh = + style_cfg->parm_cfg.qpg_port_sh; + style_alg.s.qpg_port_msb = + style_cfg->parm_cfg.qpg_port_msb; + style_alg.s.wqe_vs = style_cfg->parm_cfg.wqe_vs; + csr_wr_node(node, + CVMX_PKI_CLX_STYLEX_ALG(style, cluster), + style_alg.u64); + } + cluster++; + } + style_buf.u64 = csr_rd_node(node, CVMX_PKI_STYLEX_BUF(style)); + style_buf.s.pkt_lend = style_cfg->parm_cfg.pkt_lend; + style_buf.s.wqe_hsz = style_cfg->parm_cfg.wqe_hsz; + style_buf.s.wqe_skip = (style_cfg->parm_cfg.wqe_skip) / 128; + style_buf.s.first_skip = (style_cfg->parm_cfg.first_skip) / 8; + style_buf.s.later_skip = style_cfg->parm_cfg.later_skip / 8; + style_buf.s.opc_mode = style_cfg->parm_cfg.cache_mode; + style_buf.s.mb_size = (style_cfg->parm_cfg.mbuff_size) / 8; + style_buf.s.dis_wq_dat = style_cfg->parm_cfg.dis_wq_dat; + csr_wr_node(node, CVMX_PKI_STYLEX_BUF(style), style_buf.u64); + + cvmx_pki_write_tag_config(node, style, cluster_mask, + &style_cfg->tag_cfg); +} + +/** + * This function reads qpg entry at specified offset from qpg table. + * + * @param node Node number. + * @param offset Offset in qpg table to read from. + * @param qpg_cfg Pointer to structure containing qpg values. + */ +int cvmx_pki_read_qpg_entry(int node, int offset, + struct cvmx_pki_qpg_config *qpg_cfg) +{ + cvmx_pki_qpg_tblx_t qpg_tbl; + + if (offset >= CVMX_PKI_NUM_QPG_ENTRY) { + debug("ERROR: qpg offset %d is >= 2048\n", offset); + return -1; + } + qpg_tbl.u64 = csr_rd_node(node, CVMX_PKI_QPG_TBLX(offset)); + qpg_cfg->aura_num = qpg_tbl.s.laura; + qpg_cfg->port_add = qpg_tbl.s.padd; + qpg_cfg->grp_ok = qpg_tbl.s.grp_ok; + qpg_cfg->grp_bad = qpg_tbl.s.grp_bad; + qpg_cfg->grptag_ok = qpg_tbl.s.grptag_ok; + qpg_cfg->grptag_bad = qpg_tbl.s.grptag_bad; + return 0; +} + +/** + * This function writes qpg entry at specified offset in qpg table. + * + * @param node Node number. + * @param offset Offset in qpg table to read from. + * @param qpg_cfg Pointer to structure containing qpg values. + */ +void cvmx_pki_write_qpg_entry(int node, int offset, + struct cvmx_pki_qpg_config *qpg_cfg) +{ + cvmx_pki_qpg_tblx_t qpg_tbl; + + qpg_tbl.u64 = csr_rd_node(node, CVMX_PKI_QPG_TBLX(offset)); + qpg_tbl.s.padd = qpg_cfg->port_add; + qpg_tbl.s.laura = qpg_cfg->aura_num; + qpg_tbl.s.grp_ok = qpg_cfg->grp_ok; + qpg_tbl.s.grp_bad = qpg_cfg->grp_bad; + qpg_tbl.s.grptag_ok = qpg_cfg->grptag_ok; + qpg_tbl.s.grptag_bad = qpg_cfg->grptag_bad; + csr_wr_node(node, CVMX_PKI_QPG_TBLX(offset), qpg_tbl.u64); +} + +/** + * This function writes pcam entry at given offset in pcam table in hardware + * + * @param node Node number. + * @param index Offset in pcam table. + * @param cluster_mask Mask of clusters in which to write pcam entry. + * @param input Input keys to pcam match passed as struct. + * @param action PCAM match action passed as struct. + */ +int cvmx_pki_pcam_write_entry(int node, int index, uint64_t cluster_mask, + struct cvmx_pki_pcam_input input, + struct cvmx_pki_pcam_action action) +{ + int bank; + unsigned int cluster = 0; + cvmx_pki_clx_pcamx_termx_t term; + cvmx_pki_clx_pcamx_matchx_t match; + cvmx_pki_clx_pcamx_actionx_t act; + + if (index >= CVMX_PKI_TOTAL_PCAM_ENTRY) { + debug("\nERROR: Invalid pcam entry %d\n", index); + return -1; + } + bank = (int)(input.field & 0x01); + while (cluster < CVMX_PKI_NUM_CLUSTER) { + if (cluster_mask & (0x01L << cluster)) { + term.u64 = csr_rd_node( + node, + CVMX_PKI_CLX_PCAMX_TERMX(cluster, bank, index)); + term.s.valid = 0; + csr_wr_node(node, + CVMX_PKI_CLX_PCAMX_TERMX(cluster, bank, + index), + term.u64); + match.u64 = csr_rd_node( + node, CVMX_PKI_CLX_PCAMX_MATCHX(cluster, bank, + index)); + match.s.data1 = input.data & input.data_mask; + match.s.data0 = (~input.data) & input.data_mask; + csr_wr_node(node, + CVMX_PKI_CLX_PCAMX_MATCHX(cluster, bank, + index), + match.u64); + + act.u64 = csr_rd_node( + node, CVMX_PKI_CLX_PCAMX_ACTIONX(cluster, bank, + index)); + act.s.pmc = action.parse_mode_chg; + act.s.style_add = action.style_add; + act.s.pf = action.parse_flag_set; + act.s.setty = action.layer_type_set; + act.s.advance = action.pointer_advance; + csr_wr_node(node, + CVMX_PKI_CLX_PCAMX_ACTIONX(cluster, bank, + index), + act.u64); + + term.u64 = csr_rd_node( + node, + CVMX_PKI_CLX_PCAMX_TERMX(cluster, bank, index)); + term.s.term1 = input.field & input.field_mask; + term.s.term0 = (~input.field) & input.field_mask; + term.s.style1 = input.style & input.style_mask; + term.s.style0 = (~input.style) & input.style_mask; + term.s.valid = 1; + csr_wr_node(node, + CVMX_PKI_CLX_PCAMX_TERMX(cluster, bank, + index), + term.u64); + } + cluster++; + } + return 0; +} + +/** + * Enables/Disables fcs check and fcs stripping on the pkind. + * + * @param node Node number + * @param pknd PKIND to apply settings on. + * @param fcs_chk Enable/disable fcs check. + * 1 = enable fcs error check. + * 0 = disable fcs error check. + * @param fcs_strip Strip L2 FCS bytes from packet, decrease WQE[LEN] by 4 bytes + * 1 = strip L2 FCS. + * 0 = Do not strip L2 FCS. + */ +void cvmx_pki_endis_fcs_check(int node, int pknd, bool fcs_chk, bool fcs_strip) +{ + int style; + unsigned int cluster; + cvmx_pki_clx_pkindx_style_t pstyle; + cvmx_pki_clx_stylex_cfg_t style_cfg; + + /* Valudate PKIND # */ + if (pknd >= CVMX_PKI_NUM_PKIND) { + printf("%s: PKIND %d out of range\n", __func__, pknd); + return; + } + + for (cluster = 0; cluster < CVMX_PKI_NUM_CLUSTER; cluster++) { + pstyle.u64 = csr_rd_node( + node, CVMX_PKI_CLX_PKINDX_STYLE(pknd, cluster)); + style = pstyle.s.style; + /* Validate STYLE # */ + if (style >= CVMX_PKI_NUM_INTERNAL_STYLE) + continue; + style_cfg.u64 = csr_rd_node( + node, CVMX_PKI_CLX_STYLEX_CFG(style, cluster)); + style_cfg.s.fcs_chk = fcs_chk; + style_cfg.s.fcs_strip = fcs_strip; + csr_wr_node(node, CVMX_PKI_CLX_STYLEX_CFG(style, cluster), + style_cfg.u64); + } +} + +/** + * Enables/Disables l2 length error check and max & min frame length checks + * + * @param node Node number + * @param pknd PKIND to disable error for. + * @param l2len_err L2 length error check enable. + * @param maxframe_err Max frame error check enable. + * @param minframe_err Min frame error check enable. + * 1 = Enabel err checks + * 0 = Disable error checks + */ +void cvmx_pki_endis_l2_errs(int node, int pknd, bool l2len_err, + bool maxframe_err, bool minframe_err) +{ + int style; + unsigned int cluster; + cvmx_pki_clx_pkindx_style_t pstyle; + cvmx_pki_clx_stylex_cfg_t style_cfg; + + /* Valudate PKIND # */ + if (pknd >= CVMX_PKI_NUM_PKIND) { + printf("%s: PKIND %d out of range\n", __func__, pknd); + return; + } + + for (cluster = 0; cluster < CVMX_PKI_NUM_CLUSTER; cluster++) { + pstyle.u64 = csr_rd_node( + node, CVMX_PKI_CLX_PKINDX_STYLE(pknd, cluster)); + style = pstyle.s.style; + /* Validate STYLE # */ + if (style >= CVMX_PKI_NUM_INTERNAL_STYLE) + continue; + style_cfg.u64 = csr_rd_node( + node, CVMX_PKI_CLX_STYLEX_CFG(style, cluster)); + style_cfg.s.lenerr_en = l2len_err; + style_cfg.s.maxerr_en = maxframe_err; + style_cfg.s.minerr_en = minframe_err; + csr_wr_node(node, CVMX_PKI_CLX_STYLEX_CFG(style, cluster), + style_cfg.u64); + } +} diff --git a/arch/mips/mach-octeon/cvmx-pko-internal-ports-range.c b/arch/mips/mach-octeon/cvmx-pko-internal-ports-range.c new file mode 100644 index 0000000..694b4e3 --- /dev/null +++ b/arch/mips/mach-octeon/cvmx-pko-internal-ports-range.c @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018-2022 Marvell International Ltd. + */ + +#include <errno.h> +#include <log.h> +#include <time.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-range.h> +#include <mach/cvmx-global-resources.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-ipd.h> +#include <mach/cvmx-pki.h> +#include <mach/cvmx-pko3.h> +#include <mach/cvmx-pko3-queue.h> +#include <mach/cvmx-pko3-resources.h> + +#include <mach/cvmx-helper.h> +#include <mach/cvmx-helper-board.h> +#include <mach/cvmx-helper-cfg.h> + +#include <mach/cvmx-helper-bgx.h> +#include <mach/cvmx-helper-cfg.h> +#include <mach/cvmx-helper-util.h> +#include <mach/cvmx-helper-pki.h> + +union interface_port { + struct { + int port; + int interface; + } s; + u64 u64; +}; + +static int dbg; + +static int port_range_init; + +int __cvmx_pko_internal_ports_range_init(void) +{ + int rv = 0; + + if (port_range_init) + return 0; + port_range_init = 1; + rv = cvmx_create_global_resource_range(CVMX_GR_TAG_PKO_IPORTS, + CVMX_HELPER_CFG_MAX_PKO_QUEUES); + if (rv != 0) + debug("ERROR : Failed to initialize pko internal port range\n"); + return rv; +} + +int cvmx_pko_internal_ports_alloc(int xiface, int port, u64 count) +{ + int ret_val = -1; + union interface_port inf_port; + struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); + + __cvmx_pko_internal_ports_range_init(); + inf_port.s.interface = xi.interface; + inf_port.s.port = port; + ret_val = cvmx_allocate_global_resource_range(CVMX_GR_TAG_PKO_IPORTS, + inf_port.u64, count, 1); + if (dbg) + debug("internal port alloc : port=%02d base=%02d count=%02d\n", + (int)port, ret_val, (int)count); + if (ret_val == -1) + return ret_val; + cvmx_cfg_port[xi.node][xi.interface][port].ccpp_pko_port_base = ret_val; + cvmx_cfg_port[xi.node][xi.interface][port].ccpp_pko_num_ports = count; + return 0; +} diff --git a/arch/mips/mach-octeon/cvmx-pko.c b/arch/mips/mach-octeon/cvmx-pko.c new file mode 100644 index 0000000..8a91813 --- /dev/null +++ b/arch/mips/mach-octeon/cvmx-pko.c @@ -0,0 +1,788 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018-2022 Marvell International Ltd. + * + * Support library for the hardware Packet Output unit. + */ + +#include <errno.h> +#include <log.h> +#include <time.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-iob-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-pko3.h> +#include <mach/cvmx-pko3-queue.h> +#include <mach/cvmx-pko3-resources.h> + +#include <mach/cvmx-helper.h> +#include <mach/cvmx-helper-board.h> +#include <mach/cvmx-helper-cfg.h> + +#include <mach/cvmx-helper-bgx.h> +#include <mach/cvmx-helper-cfg.h> +#include <mach/cvmx-helper-util.h> +#include <mach/cvmx-helper-pki.h> +#include <mach/cvmx-helper-pko.h> + +DECLARE_GLOBAL_DATA_PTR; + +#define CVMX_PKO_NQ_PER_PORT_MAX 32 + +static cvmx_pko_return_value_t cvmx_pko2_config_port(short ipd_port, + int base_queue, + int num_queues, + const u8 priority[]); + +static const int debug; + +/** + * Internal state of packet output + */ + +/* + * PKO port iterator + * XXX this macro only works for 68XX + */ + +#define pko_for_each_port(__p) \ + for (__p = 0; __p < CVMX_HELPER_CFG_MAX_PKO_PORT; __p++) \ + if (__cvmx_helper_cfg_pko_queue_base(__p) != \ + CVMX_HELPER_CFG_INVALID_VALUE) + +/* + * @INTERNAL + * + * Get INT for a port + * + * @param interface + * @param index + * @return the INT value on success and -1 on error + * + * This function is only for CN68XX. + */ +static int __cvmx_pko_int(int interface, int index) +{ + cvmx_helper_cfg_assert(interface < CVMX_HELPER_MAX_IFACE); + cvmx_helper_cfg_assert(index >= 0); + + switch (interface) { + case 0: + cvmx_helper_cfg_assert(index < 4); + return index; + case 1: + cvmx_helper_cfg_assert(index == 0); + return 4; + case 2: + cvmx_helper_cfg_assert(index < 4); + return index + 8; + case 3: + cvmx_helper_cfg_assert(index < 4); + return index + 0xC; + case 4: + cvmx_helper_cfg_assert(index < 4); + return index + 0x10; + case 5: + cvmx_helper_cfg_assert(index < 256); + return 0x1C; + case 6: + cvmx_helper_cfg_assert(index < 256); + return 0x1D; + case 7: + cvmx_helper_cfg_assert(index < 32); + return 0x1E; + case 8: + cvmx_helper_cfg_assert(index < 8); + return 0x1F; + } + + return -1; +} + +int cvmx_pko_get_base_pko_port(int interface, int index) +{ + if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) + return cvmx_helper_get_ipd_port(interface, index); + else if (octeon_has_feature(OCTEON_FEATURE_PKND)) + return __cvmx_helper_cfg_pko_port_base(interface, index); + else + return cvmx_helper_get_ipd_port(interface, index); +} + +int cvmx_pko_get_base_queue(int port) +{ + if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { + return cvmx_pko3_get_queue_base(port); + } else if (octeon_has_feature(OCTEON_FEATURE_PKND)) { + return __cvmx_helper_cfg_pko_queue_base( + cvmx_helper_cfg_ipd2pko_port_base(port)); + } else { + if (port < 48) + return cvmx_pko_queue_table[port].ccppp_queue_base; + else + return CVMX_PKO_ILLEGAL_QUEUE; + } +} + +int cvmx_pko_get_num_queues(int port) +{ + if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { + return cvmx_pko3_get_queue_num(port); + } else if (octeon_has_feature(OCTEON_FEATURE_PKND)) { + return __cvmx_helper_cfg_pko_queue_num( + cvmx_helper_cfg_ipd2pko_port_base(port)); + } else { + if (port < 48) + return cvmx_pko_queue_table[port].ccppp_num_queues; + } + return 0; +} + +/* + * Allocate memory for PKO engines. + * + * @param engine is the PKO engine ID. + * @return # of 2KB-chunks allocated to this PKO engine. + */ +static int __cvmx_pko_memory_per_engine_o68(int engine) +{ + /* CN68XX has 40KB to devide between the engines in 2KB chunks */ + int max_engine; + int size_per_engine; + int size; + + max_engine = __cvmx_helper_cfg_pko_max_engine(); + size_per_engine = 40 / 2 / max_engine; + + if (engine >= max_engine) + /* Unused engines get no space */ + size = 0; + else if (engine == max_engine - 1) + /* + * The last engine gets all the space lost by rounding. This means + * the ILK gets the most space + */ + size = 40 / 2 - engine * size_per_engine; + else + /* All other engines get the same space */ + size = size_per_engine; + + return size; +} + +/* + * Setup one-to-one mapping between PKO2 iport and eport. + * @INTERNAL + */ +static void __cvmx_pko2_chip_init(void) +{ + int i; + int interface, index, port; + cvmx_helper_interface_mode_t mode; + union cvmx_pko_mem_iport_ptrs config; + + /* + * Initialize every iport with the invalid eid. + */ +#define CVMX_O68_PKO2_INVALID_EID 31 + config.u64 = 0; + config.s.eid = CVMX_O68_PKO2_INVALID_EID; + for (i = 0; i < CVMX_HELPER_CFG_MAX_PKO_PORT; i++) { + config.s.ipid = i; + csr_wr(CVMX_PKO_MEM_IPORT_PTRS, config.u64); + } + + /* + * Set up PKO_MEM_IPORT_PTRS + */ + pko_for_each_port(port) { + interface = __cvmx_helper_cfg_pko_port_interface(port); + index = __cvmx_helper_cfg_pko_port_index(port); + mode = cvmx_helper_interface_get_mode(interface); + + if (mode == CVMX_HELPER_INTERFACE_MODE_DISABLED) + continue; + + config.s.ipid = port; + config.s.qos_mask = 0xff; + config.s.crc = __cvmx_helper_get_has_fcs(interface); + config.s.min_pkt = __cvmx_helper_get_pko_padding(interface); + config.s.intr = __cvmx_pko_int(interface, index); + config.s.eid = __cvmx_helper_cfg_pko_port_eid(port); + config.s.pipe = (mode == CVMX_HELPER_INTERFACE_MODE_LOOP) ? + index : + port; + csr_wr(CVMX_PKO_MEM_IPORT_PTRS, config.u64); + } +} + +int __cvmx_pko_get_pipe(int interface, int index) +{ + /* The loopback ports do not have pipes */ + if (cvmx_helper_interface_get_mode(interface) == + CVMX_HELPER_INTERFACE_MODE_LOOP) + return -1; + /* We use pko_port as the pipe. See __cvmx_pko_port_map_o68(). */ + return cvmx_helper_get_pko_port(interface, index); +} + +static void __cvmx_pko1_chip_init(void) +{ + int queue; + union cvmx_pko_mem_queue_ptrs config; + union cvmx_pko_reg_queue_ptrs1 config1; + const int port = CVMX_PKO_MEM_QUEUE_PTRS_ILLEGAL_PID; + + /* Initialize all queues to connect to port 63 (ILLEGAL_PID) */ + for (queue = 0; queue < CVMX_PKO_MAX_OUTPUT_QUEUES; queue++) { + config1.u64 = 0; + config1.s.idx3 = 0; + config1.s.qid7 = queue >> 7; + + config.u64 = 0; + config.s.tail = 1; + config.s.index = 0; + config.s.port = port; + config.s.queue = queue; + config.s.buf_ptr = 0; + + csr_wr(CVMX_PKO_REG_QUEUE_PTRS1, config1.u64); + csr_wr(CVMX_PKO_MEM_QUEUE_PTRS, config.u64); + } +} + +/** + * Call before any other calls to initialize the packet + * output system. This does chip global config, and should only be + * done by one core. + */ +void cvmx_pko_hw_init(u8 pool, unsigned int bufsize) +{ + union cvmx_pko_reg_cmd_buf config; + union cvmx_iob_fau_timeout fau_to; + int i; + + if (debug) + debug("%s: pool=%u bufsz=%u\n", __func__, pool, bufsize); + + /* chip-specific setup. */ + if (OCTEON_IS_MODEL(OCTEON_CN68XX)) + __cvmx_pko2_chip_init(); + else + __cvmx_pko1_chip_init(); + + /* + * Set the size of the PKO command buffers to an odd number of + * 64bit words. This allows the normal two word send to stay + * aligned and never span a command word buffer. + */ + config.u64 = 0; + config.s.pool = pool; + config.s.size = bufsize / 8 - 1; + csr_wr(CVMX_PKO_REG_CMD_BUF, config.u64); + + /* + * Disable tagwait FAU timeout. This needs to be done before + * anyone might start packet output using tags. + */ + fau_to.u64 = 0; + fau_to.s.tout_val = 0xfff; + fau_to.s.tout_enb = 0; + csr_wr(CVMX_IOB_FAU_TIMEOUT, fau_to.u64); + + if (OCTEON_IS_MODEL(OCTEON_CN68XX)) { + union cvmx_pko_reg_min_pkt min_pkt; + + min_pkt.u64 = 0; + min_pkt.s.size1 = 59; + min_pkt.s.size2 = 59; + min_pkt.s.size3 = 59; + min_pkt.s.size4 = 59; + min_pkt.s.size5 = 59; + min_pkt.s.size6 = 59; + min_pkt.s.size7 = 59; + csr_wr(CVMX_PKO_REG_MIN_PKT, min_pkt.u64); + } + + /* + * If we aren't using all of the queues optimize PKO's + * internal memory. + */ + if (OCTEON_IS_OCTEON2() || OCTEON_IS_MODEL(OCTEON_CN70XX)) { + int max_queues = __cvmx_helper_cfg_pko_max_queue(); + + if (OCTEON_IS_MODEL(OCTEON_CN68XX) && max_queues <= 32) + csr_wr(CVMX_PKO_REG_QUEUE_MODE, 3); + else if (max_queues <= 64) + csr_wr(CVMX_PKO_REG_QUEUE_MODE, 2); + else if (max_queues <= 128) + csr_wr(CVMX_PKO_REG_QUEUE_MODE, 1); + else + csr_wr(CVMX_PKO_REG_QUEUE_MODE, 0); + if (OCTEON_IS_MODEL(OCTEON_CN68XX)) { + for (i = 0; i < 2; i++) { + union cvmx_pko_reg_engine_storagex + engine_storage; + +#define PKO_ASSIGN_ENGINE_STORAGE(index) \ + engine_storage.s.engine##index = \ + __cvmx_pko_memory_per_engine_o68(16 * i + (index)) + + engine_storage.u64 = 0; + PKO_ASSIGN_ENGINE_STORAGE(0); + PKO_ASSIGN_ENGINE_STORAGE(1); + PKO_ASSIGN_ENGINE_STORAGE(2); + PKO_ASSIGN_ENGINE_STORAGE(3); + PKO_ASSIGN_ENGINE_STORAGE(4); + PKO_ASSIGN_ENGINE_STORAGE(5); + PKO_ASSIGN_ENGINE_STORAGE(6); + PKO_ASSIGN_ENGINE_STORAGE(7); + PKO_ASSIGN_ENGINE_STORAGE(8); + PKO_ASSIGN_ENGINE_STORAGE(9); + PKO_ASSIGN_ENGINE_STORAGE(10); + PKO_ASSIGN_ENGINE_STORAGE(11); + PKO_ASSIGN_ENGINE_STORAGE(12); + PKO_ASSIGN_ENGINE_STORAGE(13); + PKO_ASSIGN_ENGINE_STORAGE(14); + PKO_ASSIGN_ENGINE_STORAGE(15); + csr_wr(CVMX_PKO_REG_ENGINE_STORAGEX(i), + engine_storage.u64); + } + } + } +} + +/** + * Enables the packet output hardware. It must already be + * configured. + */ +void cvmx_pko_enable(void) +{ + union cvmx_pko_reg_flags flags; + + flags.u64 = csr_rd(CVMX_PKO_REG_FLAGS); + if (flags.s.ena_pko) + debug("Warning: Enabling PKO when PKO already enabled.\n"); + + flags.s.ena_dwb = cvmx_helper_cfg_opt_get(CVMX_HELPER_CFG_OPT_USE_DWB); + flags.s.ena_pko = 1; + /* + * always enable big endian for 3-word command. Does nothing + * for 2-word. + */ + flags.s.store_be = 1; + csr_wr(CVMX_PKO_REG_FLAGS, flags.u64); +} + +/** + * Configure a output port and the associated queues for use. + * + * @param port Port to configure. + * @param base_queue First queue number to associate with this port. + * @param num_queues Number of queues to associate with this port + * @param priority Array of priority levels for each queue. Values are + * allowed to be 0-8. A value of 8 get 8 times the traffic + * of a value of 1. A value of 0 indicates that no rounds + * will be participated in. These priorities can be changed + * on the fly while the pko is enabled. A priority of 9 + * indicates that static priority should be used. If static + * priority is used all queues with static priority must be + * contiguous starting at the base_queue, and lower numbered + * queues have higher priority than higher numbered queues. + * There must be num_queues elements in the array. + */ +cvmx_pko_return_value_t cvmx_pko_config_port(int port, int base_queue, + int num_queues, + const u8 priority[]) +{ + cvmx_pko_return_value_t result_code; + int queue; + union cvmx_pko_mem_queue_ptrs config; + union cvmx_pko_reg_queue_ptrs1 config1; + int static_priority_base = -1; + int static_priority_end = -1; + int outputbuffer_pool = (int)cvmx_fpa_get_pko_pool(); + u64 outputbuffer_pool_size = cvmx_fpa_get_pko_pool_block_size(); + + /* This function is not used for CN68XX */ + if (OCTEON_IS_MODEL(OCTEON_CN68XX)) + return cvmx_pko2_config_port(port, base_queue, num_queues, + priority); + + if (debug) + debug("%s: port=%d queue=%d-%d pri %#x %#x %#x %#x\n", __func__, + port, base_queue, (base_queue + num_queues - 1), + priority[0], priority[1], priority[2], priority[3]); + + /* The need to handle ILLEGAL_PID port argument + * is obsolete now, the code here can be simplified. + */ + + if (port >= CVMX_PKO_NUM_OUTPUT_PORTS && + port != CVMX_PKO_MEM_QUEUE_PTRS_ILLEGAL_PID) { + debug("ERROR: %s: Invalid port %llu\n", __func__, + (unsigned long long)port); + return CVMX_PKO_INVALID_PORT; + } + + if (base_queue + num_queues > CVMX_PKO_MAX_OUTPUT_QUEUES) { + debug("ERROR: %s: Invalid queue range port = %lld base=%llu numques=%lld\n", + __func__, (unsigned long long)port, + (unsigned long long)base_queue, + (unsigned long long)num_queues); + return CVMX_PKO_INVALID_QUEUE; + } + + if (port != CVMX_PKO_MEM_QUEUE_PTRS_ILLEGAL_PID) { + /* + * Validate the static queue priority setup and set + * static_priority_base and static_priority_end + * accordingly. + */ + for (queue = 0; queue < num_queues; queue++) { + /* Find first queue of static priority */ + int p_queue = queue % 16; + + if (static_priority_base == -1 && + priority[p_queue] == CVMX_PKO_QUEUE_STATIC_PRIORITY) + static_priority_base = queue; + /* Find last queue of static priority */ + if (static_priority_base != -1 && + static_priority_end == -1 && + priority[p_queue] != + CVMX_PKO_QUEUE_STATIC_PRIORITY && + queue) + static_priority_end = queue - 1; + else if (static_priority_base != -1 && + static_priority_end == -1 && + queue == num_queues - 1) + /* all queues're static priority */ + static_priority_end = queue; + + /* + * Check to make sure all static priority + * queues are contiguous. Also catches some + * cases of static priorites not starting at + * queue 0. + */ + if (static_priority_end != -1 && + (int)queue > static_priority_end && + priority[p_queue] == + CVMX_PKO_QUEUE_STATIC_PRIORITY) { + debug("ERROR: %s: Static priority queues aren't contiguous or don't start at base queue. q: %d, eq: %d\n", + __func__, (int)queue, static_priority_end); + return CVMX_PKO_INVALID_PRIORITY; + } + } + if (static_priority_base > 0) { + debug("ERROR: %s: Static priority queues don't start at base queue. sq: %d\n", + __func__, static_priority_base); + return CVMX_PKO_INVALID_PRIORITY; + } + } + + /* + * At this point, static_priority_base and static_priority_end + * are either both -1, or are valid start/end queue numbers + */ + + result_code = CVMX_PKO_SUCCESS; + + for (queue = 0; queue < num_queues; queue++) { + u64 *buf_ptr = NULL; + int p_queue = queue % 16; + + config1.u64 = 0; + config1.s.idx3 = queue >> 3; + config1.s.qid7 = (base_queue + queue) >> 7; + + config.u64 = 0; + config.s.tail = queue == (num_queues - 1); + config.s.index = queue; + config.s.port = port; + config.s.queue = base_queue + queue; + + config.s.static_p = static_priority_base >= 0; + config.s.static_q = (int)queue <= static_priority_end; + config.s.s_tail = (int)queue == static_priority_end; + /* + * Convert the priority into an enable bit field. Try + * to space the bits out evenly so the packet don't + * get grouped up. + */ + switch ((int)priority[p_queue]) { + case 0: + config.s.qos_mask = 0x00; + break; + case 1: + config.s.qos_mask = 0x01; + break; + case 2: + config.s.qos_mask = 0x11; + break; + case 3: + config.s.qos_mask = 0x49; + break; + case 4: + config.s.qos_mask = 0x55; + break; + case 5: + config.s.qos_mask = 0x57; + break; + case 6: + config.s.qos_mask = 0x77; + break; + case 7: + config.s.qos_mask = 0x7f; + break; + case 8: + config.s.qos_mask = 0xff; + break; + case CVMX_PKO_QUEUE_STATIC_PRIORITY: + config.s.qos_mask = 0xff; + break; + default: + debug("ERROR: %s: Invalid priority %llu\n", __func__, + (unsigned long long)priority[p_queue]); + config.s.qos_mask = 0xff; + result_code = CVMX_PKO_INVALID_PRIORITY; + break; + } + + if (port != CVMX_PKO_MEM_QUEUE_PTRS_ILLEGAL_PID) { + cvmx_cmd_queue_result_t cmd_res; + + cmd_res = cvmx_cmd_queue_initialize( + CVMX_CMD_QUEUE_PKO(base_queue + queue), + CVMX_PKO_MAX_QUEUE_DEPTH, outputbuffer_pool, + outputbuffer_pool_size - + CVMX_PKO_COMMAND_BUFFER_SIZE_ADJUST * + 8); + if (cmd_res != CVMX_CMD_QUEUE_SUCCESS) { + switch (cmd_res) { + case CVMX_CMD_QUEUE_NO_MEMORY: + debug("ERROR: %s: Unable to allocate output buffer\n", + __func__); + return CVMX_PKO_NO_MEMORY; + case CVMX_CMD_QUEUE_ALREADY_SETUP: + debug("ERROR: %s: Port already setup. port=%d\n", + __func__, (int)port); + return CVMX_PKO_PORT_ALREADY_SETUP; + case CVMX_CMD_QUEUE_INVALID_PARAM: + default: + debug("ERROR: %s: Command queue initialization failed.\n", + __func__); + return CVMX_PKO_CMD_QUEUE_INIT_ERROR; + } + } + + buf_ptr = (u64 *)cvmx_cmd_queue_buffer( + CVMX_CMD_QUEUE_PKO(base_queue + queue)); + config.s.buf_ptr = cvmx_ptr_to_phys(buf_ptr); + } else { + config.s.buf_ptr = 0; + } + + CVMX_SYNCWS; + + csr_wr(CVMX_PKO_REG_QUEUE_PTRS1, config1.u64); + csr_wr(CVMX_PKO_MEM_QUEUE_PTRS, config.u64); + } + + return result_code; +} + +/* + * Configure queues for an internal port. + * @INTERNAL + * @param pko_port PKO internal port number + * @note this is the PKO2 equivalent to cvmx_pko_config_port() + */ +static cvmx_pko_return_value_t cvmx_pko2_config_port(short ipd_port, + int base_queue, + int num_queues, + const u8 priority[]) +{ + int queue, pko_port; + int static_priority_base; + int static_priority_end; + union cvmx_pko_mem_iqueue_ptrs config; + u64 *buf_ptr = NULL; + int outputbuffer_pool = (int)cvmx_fpa_get_pko_pool(); + u64 outputbuffer_pool_size = cvmx_fpa_get_pko_pool_block_size(); + + pko_port = cvmx_helper_cfg_ipd2pko_port_base(ipd_port); + + if (debug) + debug("%s: ipd_port %d pko_iport %d qbase %d qnum %d\n", + __func__, ipd_port, pko_port, base_queue, num_queues); + + static_priority_base = -1; + static_priority_end = -1; + + /* + * static queue priority validation + */ + for (queue = 0; queue < num_queues; queue++) { + int p_queue = queue % 16; + + if (static_priority_base == -1 && + priority[p_queue] == CVMX_PKO_QUEUE_STATIC_PRIORITY) + static_priority_base = queue; + + if (static_priority_base != -1 && static_priority_end == -1 && + priority[p_queue] != CVMX_PKO_QUEUE_STATIC_PRIORITY && + queue) + static_priority_end = queue - 1; + else if (static_priority_base != -1 && + static_priority_end == -1 && queue == num_queues - 1) + static_priority_end = + queue; /* all queues are static priority */ + + /* + * Check to make sure all static priority queues are contiguous. + * Also catches some cases of static priorites not starting from + * queue 0. + */ + if (static_priority_end != -1 && + (int)queue > static_priority_end && + priority[p_queue] == CVMX_PKO_QUEUE_STATIC_PRIORITY) { + debug("ERROR: %s: Static priority queues aren't contiguous or don't start at base queue. q: %d, eq: %d\n", + __func__, (int)queue, static_priority_end); + } + if (static_priority_base > 0) { + debug("ERROR: %s: Static priority queues don't start at base queue. sq: %d\n", + __func__, static_priority_base); + } + } + + /* + * main loop to set the fields of CVMX_PKO_MEM_IQUEUE_PTRS for + * each queue + */ + for (queue = 0; queue < num_queues; queue++) { + int p_queue = queue % 8; + + config.u64 = 0; + config.s.index = queue; + config.s.qid = base_queue + queue; + config.s.ipid = pko_port; + config.s.tail = (queue == (num_queues - 1)); + config.s.s_tail = (queue == static_priority_end); + config.s.static_p = (static_priority_base >= 0); + config.s.static_q = (queue <= static_priority_end); + + /* + * Convert the priority into an enable bit field. + * Try to space the bits out evenly so the packet + * don't get grouped up. + */ + switch ((int)priority[p_queue]) { + case 0: + config.s.qos_mask = 0x00; + break; + case 1: + config.s.qos_mask = 0x01; + break; + case 2: + config.s.qos_mask = 0x11; + break; + case 3: + config.s.qos_mask = 0x49; + break; + case 4: + config.s.qos_mask = 0x55; + break; + case 5: + config.s.qos_mask = 0x57; + break; + case 6: + config.s.qos_mask = 0x77; + break; + case 7: + config.s.qos_mask = 0x7f; + break; + case 8: + config.s.qos_mask = 0xff; + break; + case CVMX_PKO_QUEUE_STATIC_PRIORITY: + config.s.qos_mask = 0xff; + break; + default: + debug("ERROR: %s: Invalid priority %llu\n", __func__, + (unsigned long long)priority[p_queue]); + config.s.qos_mask = 0xff; + break; + } + + /* + * The command queues + */ + { + cvmx_cmd_queue_result_t cmd_res; + + cmd_res = cvmx_cmd_queue_initialize( + CVMX_CMD_QUEUE_PKO(base_queue + queue), + CVMX_PKO_MAX_QUEUE_DEPTH, outputbuffer_pool, + (outputbuffer_pool_size - + CVMX_PKO_COMMAND_BUFFER_SIZE_ADJUST * 8)); + + if (cmd_res != CVMX_CMD_QUEUE_SUCCESS) { + switch (cmd_res) { + case CVMX_CMD_QUEUE_NO_MEMORY: + debug("ERROR: %s: Unable to allocate output buffer\n", + __func__); + break; + case CVMX_CMD_QUEUE_ALREADY_SETUP: + debug("ERROR: %s: Port already setup\n", + __func__); + break; + case CVMX_CMD_QUEUE_INVALID_PARAM: + default: + debug("ERROR: %s: Command queue initialization failed.", + __func__); + break; + } + debug(" pko_port%d base_queue%d num_queues%d queue%d.\n", + pko_port, base_queue, num_queues, queue); + } + + buf_ptr = (u64 *)cvmx_cmd_queue_buffer( + CVMX_CMD_QUEUE_PKO(base_queue + queue)); + config.s.buf_ptr = cvmx_ptr_to_phys(buf_ptr) >> 7; + } + + CVMX_SYNCWS; + csr_wr(CVMX_PKO_MEM_IQUEUE_PTRS, config.u64); + } + + /* Error detection is resirable here */ + return 0; +} diff --git a/arch/mips/mach-octeon/cvmx-pko3-compat.c b/arch/mips/mach-octeon/cvmx-pko3-compat.c new file mode 100644 index 0000000..3e14232 --- /dev/null +++ b/arch/mips/mach-octeon/cvmx-pko3-compat.c @@ -0,0 +1,656 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018-2022 Marvell International Ltd. + * + */ + +#include <errno.h> +#include <log.h> +#include <time.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-scratch.h> +#include <mach/cvmx-hwfau.h> +#include <mach/cvmx-fau.h> + +#include <mach/cvmx-hwpko.h> +#include <mach/cvmx-ilk.h> +#include <mach/cvmx-pki.h> +#include <mach/cvmx-pko3.h> +#include <mach/cvmx-pko3-queue.h> +#include <mach/cvmx-pko3-resources.h> + +#include <mach/cvmx-helper.h> +#include <mach/cvmx-helper-board.h> +#include <mach/cvmx-helper-cfg.h> + +#include <mach/cvmx-helper-bgx.h> +#include <mach/cvmx-helper-cfg.h> +#include <mach/cvmx-helper-util.h> +#include <mach/cvmx-helper-pki.h> + +/* #undef CVMX_ENABLE_PARAMETER_CHECKING */ +/* #define CVMX_ENABLE_PARAMETER_CHECKING 1 */ +/* #define __PKO3_NATIVE_PTR */ + +static inline u64 cvmx_pko3_legacy_paddr(unsigned int node, u64 addr) +{ + u64 paddr; + + paddr = node; + paddr = (addr & ((1ull << 40) - 1)) | (paddr << 40); + return paddr; +} + +#if CVMX_ENABLE_PARAMETER_CHECKING +/** + * @INTERNAL + * + * Verify the integrity of a legacy buffer link pointer, + * + * Note that the IPD/PIP/PKO hardware would sometimes + * round-up the buf_ptr->size field of the last buffer in a chain to the next + * cache line size, so the sum of buf_ptr->size + * fields for a packet may exceed total_bytes by up to 127 bytes. + * + * @returns 0 on success, a negative number on error. + */ +static int cvmx_pko3_legacy_bufptr_validate(cvmx_buf_ptr_t buf_ptr, + unsigned int gather, + unsigned int buffers, + unsigned int total_bytes) +{ + unsigned int node = cvmx_get_node_num(); + unsigned int segs = 0, bytes = 0; + unsigned int phys_addr; + cvmx_buf_ptr_t ptr; + int delta; + + if (buffers == 0) { + return -1; + } else if (buffers == 1) { + delta = buf_ptr.s.size - total_bytes; + if (delta < 0 || delta > 127) + return -2; + } else if (gather) { + cvmx_buf_ptr_t *vptr; + /* Validate gather list */ + if (buf_ptr.s.size < buffers) + return -3; + phys_addr = cvmx_pko3_legacy_paddr(node, buf_ptr.s.addr); + vptr = cvmx_phys_to_ptr(phys_addr); + for (segs = 0; segs < buffers; segs++) + bytes += vptr[segs].s.size; + delta = bytes - total_bytes; + if (delta < 0 || delta > 127) + return -4; + } else { + void *vptr; + /* Validate linked buffers */ + ptr = buf_ptr; + for (segs = 0; segs < buffers; segs++) { + bytes += ptr.s.size; + phys_addr = cvmx_pko3_legacy_paddr(node, ptr.s.addr); + vptr = cvmx_phys_to_ptr(phys_addr); + memcpy(&ptr, vptr - sizeof(u64), sizeof(u64)); + } + delta = bytes - total_bytes; + if (delta < 0 || delta > 127) + return -5; + } + return 0; +} +#endif /* CVMX_ENABLE_PARAMETER_CHECKING */ + +/* + * @INTERNAL + * + * Implementation note: + * When the packet is sure to not need a jump_buf, + * it will be written directly into cvmseg. + * When the packet might not fit into cvmseg with all + * of its descriptors, a jump_buf is allocated a priori, + * and only header is first placed into cvmseg, all other + * descriptors are placed into jump_buf, and finally + * the PKO_SEND_JUMP_S is written to cvmseg. + * This is because if there are no EXT or TSO descriptors, + * then HDR must be first, and JMP second and that is all + * that should go into cvmseg. + */ +struct __cvmx_pko3_legacy_desc { + u64 *cmd_words; + u64 *jump_buf_base_ptr; + unsigned short word_count; + short last_pool; + u8 port_node; + u8 aura_node; + u8 jump_buf_size; +}; + +/** + * @INTERNAL + * + * Add a subdescriptor into a command buffer, + * and handle command-buffer overflow by allocating a JUMP_s buffer + * from PKO3 internal AURA. + */ +static int __cvmx_pko3_cmd_subdc_add(struct __cvmx_pko3_legacy_desc *desc, + u64 subdc) +{ + /* SEND_JUMP_S missing on Pass1.X */ + if (desc->word_count >= 15) { + printf("%s: ERROR: too many segments\n", __func__); + return -EBADF; + } + + /* Handle small commands simply */ + if (cvmx_likely(!desc->jump_buf_base_ptr)) { + desc->cmd_words[desc->word_count] = subdc; + (desc->word_count)++; + return desc->word_count; + } + + if (cvmx_unlikely(desc->jump_buf_size >= 255)) + return -ENOMEM; + + desc->jump_buf_base_ptr[desc->jump_buf_size++] = subdc; + + return desc->word_count + desc->jump_buf_size; +} + +/** + * @INTERNAL + * + * Finalize command buffer + * + * @returns: number of command words in command buffer and jump buffer + * or negative number on error. + */ + +static int __cvmx_pko3_cmd_done(struct __cvmx_pko3_legacy_desc *desc) +{ + short pko_aura; + cvmx_pko_buf_ptr_t jump_s; + cvmx_pko_send_aura_t aura_s; + + /* no jump buffer, nothing to do */ + if (!desc->jump_buf_base_ptr) + return desc->word_count; + + desc->word_count++; + + /* Verify number of words is 15 */ + if (desc->word_count != 2) { + printf("ERROR: %s: internal error, word_count=%d\n", __func__, + desc->word_count); + return -EINVAL; + } + + /* Add SEND_AURA_S at the end of jump_buf */ + pko_aura = __cvmx_pko3_aura_get(desc->port_node); + + aura_s.u64 = 0; + aura_s.s.aura = pko_aura; + aura_s.s.offset = 0; + aura_s.s.alg = AURAALG_NOP; + aura_s.s.subdc4 = CVMX_PKO_SENDSUBDC_AURA; + + desc->jump_buf_base_ptr[desc->jump_buf_size++] = aura_s.u64; + + /* Add SEND_JUMPS to point to jump_buf */ + jump_s.u64 = 0; + jump_s.s.subdc3 = CVMX_PKO_SENDSUBDC_JUMP; + jump_s.s.addr = cvmx_ptr_to_phys(desc->jump_buf_base_ptr); + jump_s.s.i = 1; /* F=1: Free this buffer when done */ + jump_s.s.size = desc->jump_buf_size; + desc->cmd_words[1] = jump_s.u64; + + return desc->word_count + desc->jump_buf_size; +} + +/** + * @INTERNAL + * + * Handle buffer pools for PKO legacy transmit operation + */ +static inline int cvmx_pko3_legacy_pool(struct __cvmx_pko3_legacy_desc *desc, + int pool) +{ + cvmx_pko_send_aura_t aura_s; + unsigned int aura; + + if (cvmx_unlikely(desc->last_pool == pool)) + return 0; + + aura = desc->aura_node << 10; /* LAURA=AURA[0..9] */ + aura |= pool; + + if (cvmx_likely(desc->last_pool < 0)) { + cvmx_pko_send_hdr_t *hdr_s; + + hdr_s = (void *)&desc->cmd_words[0]; + /* Create AURA from legacy pool (assume LAURA==POOL */ + hdr_s->s.aura = aura; + desc->last_pool = pool; + return 0; + } + + aura_s.u64 = 0; + aura_s.s.subdc4 = CVMX_PKO_SENDSUBDC_AURA; + aura_s.s.offset = 0; + aura_s.s.alg = AURAALG_NOP; + aura |= pool; + aura_s.s.aura = aura; + desc->last_pool = pool; + return __cvmx_pko3_cmd_subdc_add(desc, aura_s.u64); +} + +/** + * @INTERNAL + * + * Backward compatibility for packet transmission using legacy PKO command. + * + * NOTE: Only supports output on node-local ports. + * + * TBD: Could embed destination node in extended DQ number. + */ +cvmx_pko_return_value_t +cvmx_pko3_legacy_xmit(unsigned int dq, cvmx_pko_command_word0_t pko_command, + cvmx_buf_ptr_t packet, u64 addr, bool tag_sw) +{ + cvmx_pko_query_rtn_t pko_status; + cvmx_pko_send_hdr_t *hdr_s; + struct __cvmx_pko3_legacy_desc desc; + u8 *data_ptr; + unsigned int node, seg_cnt; + int res; + cvmx_buf_ptr_pki_t bptr; + + seg_cnt = pko_command.s.segs; + desc.cmd_words = cvmx_pko3_cvmseg_addr(); + + /* Allocate from local aura, assume all old-pools are local */ + node = cvmx_get_node_num(); + desc.aura_node = node; + + /* Derive destination node from dq */ + desc.port_node = dq >> 10; + dq &= (1 << 10) - 1; + + desc.word_count = 1; + desc.last_pool = -1; + + /* For small packets, write descriptors directly to CVMSEG + * but for longer packets use jump_buf + */ + if (seg_cnt < 7 || OCTEON_IS_MODEL(OCTEON_CN78XX_PASS1_X)) { + desc.jump_buf_size = 0; + desc.jump_buf_base_ptr = NULL; + } else { + unsigned int pko_aura = __cvmx_pko3_aura_get(desc.port_node); + + cvmx_fpa3_gaura_t aura = + __cvmx_fpa3_gaura(pko_aura >> 10, pko_aura & 0x3ff); + + /* Allocate from internal AURA, size is 4KiB */ + desc.jump_buf_base_ptr = cvmx_fpa3_alloc(aura); + + if (!desc.jump_buf_base_ptr) + return -ENOMEM; + desc.jump_buf_size = 0; + } + + /* Native buffer-pointer for error checiing */ + bptr.u64 = packet.u64; + +#if CVMX_ENABLE_PARAMETER_CHECKING + if (seg_cnt == 1 && bptr.size == pko_command.s.total_bytes) { + /* + * Special case for native buffer pointer: + * This is the only case where the native pointer-style can be + * automatically identified, that is when an entire packet + * fits into a single buffer by the PKI. + * The use of the native buffers with this function + * should be avoided. + */ + debug("%s: WARNING: Native buffer-pointer\n", __func__); + } else { + /* The buffer ptr is assume to be received in legacy format */ + res = cvmx_pko3_legacy_bufptr_validate( + packet, pko_command.s.gather, pko_command.s.segs, + pko_command.s.total_bytes); + if (res < 0) { + debug("%s: ERROR: Not a valid packet pointer <%d>\n", + __func__, res); + return CVMX_PKO_CMD_QUEUE_INIT_ERROR; + } + } +#endif /* CVMX_ENABLE_PARAMETER_CHECKING */ + + /* Squash warnings */ + (void)bptr; + + /*** Translate legacy PKO fields into PKO3 PKO_SEND_HDR_S ***/ + + /* PKO_SEND_HDR_S is alwasy the first word in the command */ + hdr_s = (void *)&desc.cmd_words[0]; + hdr_s->u64 = 0; + + /* Copy total packet size */ + hdr_s->s.total = pko_command.s.total_bytes; + + /* Endianness */ + hdr_s->s.le = pko_command.s.le; + + /* N2 is the same meaning */ + if (OCTEON_IS_MODEL(OCTEON_CN78XX_PASS1_X)) + hdr_s->s.n2 = 0; /* L2 allocate everything */ + else + hdr_s->s.n2 = pko_command.s.n2; + + /* DF bit has the same meaning */ + hdr_s->s.df = pko_command.s.dontfree; + + /* II bit has the same meaning */ + hdr_s->s.ii = pko_command.s.ignore_i; + + /* non-zero IP header offset requires L3/L4 checksum calculation */ + if (cvmx_unlikely(pko_command.s.ipoffp1 > 0)) { + u8 ipoff, ip0, l4_proto = 0; + + /* Get data pointer for header inspection below */ + if (pko_command.s.gather) { + cvmx_buf_ptr_t *p_ptr; + cvmx_buf_ptr_t blk; + + p_ptr = cvmx_phys_to_ptr( + cvmx_pko3_legacy_paddr(node, packet.s.addr)); + blk = p_ptr[0]; + data_ptr = cvmx_phys_to_ptr( + cvmx_pko3_legacy_paddr(node, blk.s.addr)); + } else { + data_ptr = cvmx_phys_to_ptr( + cvmx_pko3_legacy_paddr(node, packet.s.addr)); + } + + /* Get IP header offset */ + ipoff = pko_command.s.ipoffp1 - 1; + + /* Parse IP header, version, L4 protocol */ + hdr_s->s.l3ptr = ipoff; + ip0 = data_ptr[ipoff]; + + /* IPv4 header length, checksum offload */ + if ((ip0 >> 4) == 4) { + hdr_s->s.l4ptr = hdr_s->s.l3ptr + ((ip0 & 0xf) << 2); + l4_proto = data_ptr[ipoff + 9]; + hdr_s->s.ckl3 = 1; /* Only valid for IPv4 */ + } + /* IPv6 header length is fixed, no checksum */ + if ((ip0 >> 4) == 6) { + hdr_s->s.l4ptr = hdr_s->s.l3ptr + 40; + l4_proto = data_ptr[ipoff + 6]; + } + /* Set L4 checksum algo based on L4 protocol */ + if (l4_proto == 6) + hdr_s->s.ckl4 = /* TCP */ 2; + else if (l4_proto == 17) + hdr_s->s.ckl4 = /* UDP */ 1; + else if (l4_proto == 132) + hdr_s->s.ckl4 = /* SCTP */ 3; + else + hdr_s->s.ckl4 = /* Unknown */ 0; + } + + if (pko_command.s.gather) { + /* Process legacy gather list */ + cvmx_pko_buf_ptr_t gather_s; + cvmx_buf_ptr_t *p_ptr; + cvmx_buf_ptr_t blk; + unsigned int i; + + /* Get gather list pointer */ + p_ptr = cvmx_phys_to_ptr( + cvmx_pko3_legacy_paddr(node, packet.s.addr)); + blk = p_ptr[0]; + /* setup data_ptr */ + data_ptr = cvmx_phys_to_ptr( + cvmx_pko3_legacy_paddr(node, blk.s.addr)); + + for (i = 0; i < seg_cnt; i++) { + if (cvmx_unlikely(cvmx_pko3_legacy_pool( + &desc, blk.s.pool) < 0)) + return CVMX_PKO_NO_MEMORY; + + /* Insert PKO_SEND_GATHER_S for the current buffer */ + gather_s.u64 = 0; + gather_s.s.subdc3 = CVMX_PKO_SENDSUBDC_GATHER; + gather_s.s.size = blk.s.size; + gather_s.s.i = blk.s.i; + gather_s.s.addr = + cvmx_pko3_legacy_paddr(node, blk.s.addr); + + res = __cvmx_pko3_cmd_subdc_add(&desc, gather_s.u64); + if (res < 0) + return CVMX_PKO_NO_MEMORY; + + /* get next bufptr */ + blk = p_ptr[i + 1]; + } /* for i */ + + /* Free original gather-list buffer */ + if ((pko_command.s.ignore_i && !pko_command.s.dontfree) || + packet.s.i == pko_command.s.dontfree) + cvmx_fpa_free_nosync(p_ptr, packet.s.pool, + (i - 1) / 16 + 1); + } else { + /* Process legacy linked buffer list */ + cvmx_pko_buf_ptr_t gather_s; + cvmx_buf_ptr_t blk; + void *vptr; + + data_ptr = cvmx_phys_to_ptr( + cvmx_pko3_legacy_paddr(node, packet.s.addr)); + blk = packet; + + /* + * Legacy linked-buffers converted into flat gather list + * so that the AURA can optionally be changed to reflect + * the POOL number in the legacy pointers + */ + do { + /* Insert PKO_SEND_AURA_S if pool changes */ + if (cvmx_unlikely(cvmx_pko3_legacy_pool( + &desc, blk.s.pool) < 0)) + return CVMX_PKO_NO_MEMORY; + + /* Insert PKO_SEND_GATHER_S for the current buffer */ + gather_s.u64 = 0; + gather_s.s.subdc3 = CVMX_PKO_SENDSUBDC_GATHER; + gather_s.s.size = blk.s.size; + gather_s.s.i = blk.s.i; + gather_s.s.addr = + cvmx_pko3_legacy_paddr(node, blk.s.addr); + + res = __cvmx_pko3_cmd_subdc_add(&desc, gather_s.u64); + if (res < 0) + return CVMX_PKO_NO_MEMORY; + + /* Get the next buffer pointer */ + vptr = cvmx_phys_to_ptr( + cvmx_pko3_legacy_paddr(node, blk.s.addr)); + memcpy(&blk, vptr - sizeof(blk), sizeof(blk)); + + /* Decrement segment count */ + seg_cnt--; + + } while (seg_cnt > 0); + } + + /* This field indicates the presence of 3rd legacy command word */ + /* NOTE: legacy 3rd word may contain CN78XX native phys addr already */ + if (cvmx_unlikely(pko_command.s.rsp)) { + /* PTP bit in word3 is not supported - + * can not be distibguished from larger phys_addr[42..41] + */ + if (pko_command.s.wqp) { + /* <addr> is an SSO WQE */ + cvmx_wqe_word1_t *wqe_p; + cvmx_pko_send_work_t work_s; + + work_s.u64 = 0; + work_s.s.subdc4 = CVMX_PKO_SENDSUBDC_WORK; + work_s.s.addr = addr; + /* Assume WQE is legacy format too */ + wqe_p = cvmx_phys_to_ptr(addr + sizeof(u64)); + work_s.s.grp = wqe_p->cn38xx.grp; + work_s.s.tt = wqe_p->tag_type; + + res = __cvmx_pko3_cmd_subdc_add(&desc, work_s.u64); + } else { + cvmx_pko_send_mem_t mem_s; + /* MEMALG_SET broken on Pass1 */ + if (OCTEON_IS_MODEL(OCTEON_CN78XX_PASS1_0)) { + debug("%s: ERROR: PKO byte-clear not supported\n", + __func__); + } + /* <addr> is a physical address of byte clear */ + mem_s.u64 = 0; + mem_s.s.subdc4 = CVMX_PKO_SENDSUBDC_MEM; + mem_s.s.addr = addr; + mem_s.s.dsz = MEMDSZ_B8; + mem_s.s.alg = MEMALG_SET; + mem_s.s.offset = 0; + + res = __cvmx_pko3_cmd_subdc_add(&desc, mem_s.u64); + } + if (res < 0) + return CVMX_PKO_NO_MEMORY; + } + + /* FAU counter binding reg0 */ + if (pko_command.s.reg0) { + cvmx_pko_send_mem_t mem_s; + + debug("%s: Legacy FAU commands: reg0=%#x sz0=%#x\n", __func__, + pko_command.s.reg0, pko_command.s.size0); + mem_s.u64 = 0; + mem_s.s.subdc4 = CVMX_PKO_SENDSUBDC_MEM; + mem_s.s.addr = cvmx_ptr_to_phys( + CASTPTR(void, __cvmx_fau_sw_addr(pko_command.s.reg0))); + if (cvmx_likely(pko_command.s.size0 == CVMX_FAU_OP_SIZE_64)) + mem_s.s.dsz = MEMDSZ_B64; + else if (pko_command.s.size0 == CVMX_FAU_OP_SIZE_32) + mem_s.s.dsz = MEMDSZ_B32; + else if (pko_command.s.size0 == CVMX_FAU_OP_SIZE_16) + mem_s.s.dsz = MEMDSZ_B16; + else + mem_s.s.dsz = MEMDSZ_B8; + + if (mem_s.s.dsz == MEMDSZ_B16 || mem_s.s.dsz == MEMDSZ_B8) + debug("%s: ERROR: 8/16 bit decrement unsupported", + __func__); + + mem_s.s.offset = pko_command.s.subone0; + if (mem_s.s.offset) + mem_s.s.alg = MEMALG_SUB; + else + mem_s.s.alg = MEMALG_SUBLEN; + + res = __cvmx_pko3_cmd_subdc_add(&desc, mem_s.u64); + if (res < 0) + return CVMX_PKO_NO_MEMORY; + } + + /* FAU counter binding reg1 */ + if (cvmx_unlikely(pko_command.s.reg1)) { + cvmx_pko_send_mem_t mem_s; + + debug("%s: Legacy FAU commands: reg1=%#x sz1=%#x\n", __func__, + pko_command.s.reg1, pko_command.s.size1); + mem_s.u64 = 0; + mem_s.s.subdc4 = CVMX_PKO_SENDSUBDC_MEM; + mem_s.s.addr = cvmx_ptr_to_phys( + CASTPTR(void, __cvmx_fau_sw_addr(pko_command.s.reg1))); + if (cvmx_likely(pko_command.s.size1 == CVMX_FAU_OP_SIZE_64)) + mem_s.s.dsz = MEMDSZ_B64; + else if (pko_command.s.size1 == CVMX_FAU_OP_SIZE_32) + mem_s.s.dsz = MEMDSZ_B32; + else if (pko_command.s.size1 == CVMX_FAU_OP_SIZE_16) + mem_s.s.dsz = MEMDSZ_B16; + else + mem_s.s.dsz = MEMDSZ_B8; + + if (mem_s.s.dsz == MEMDSZ_B16 || mem_s.s.dsz == MEMDSZ_B8) + printf("%s: ERROR: 8/16 bit decrement unsupported", + __func__); + + mem_s.s.offset = pko_command.s.subone1; + if (mem_s.s.offset) + mem_s.s.alg = MEMALG_SUB; + else + mem_s.s.alg = MEMALG_SUBLEN; + + res = __cvmx_pko3_cmd_subdc_add(&desc, mem_s.u64); + if (res < 0) + return CVMX_PKO_NO_MEMORY; + } + + /* These PKO_HDR_S fields are not used: */ + /* hdr_s->s.ds does not have legacy equivalent, remains 0 */ + /* hdr_s->s.format has no legacy equivalent, remains 0 */ + + /*** Finalize command buffer ***/ + res = __cvmx_pko3_cmd_done(&desc); + if (res < 0) + return CVMX_PKO_NO_MEMORY; + + /*** Send the PKO3 command into the Descriptor Queue ***/ + pko_status = + __cvmx_pko3_lmtdma(desc.port_node, dq, desc.word_count, tag_sw); + + /*** Map PKO3 result codes to legacy return values ***/ + if (cvmx_likely(pko_status.s.dqstatus == PKO_DQSTATUS_PASS)) + return CVMX_PKO_SUCCESS; + + debug("%s: ERROR: failed to enqueue: %s\n", __func__, + pko_dqstatus_error(pko_status.s.dqstatus)); + + if (pko_status.s.dqstatus == PKO_DQSTATUS_ALREADY) + return CVMX_PKO_PORT_ALREADY_SETUP; + if (pko_status.s.dqstatus == PKO_DQSTATUS_NOFPABUF || + pko_status.s.dqstatus == PKO_DQSTATUS_NOPKOBUF) + return CVMX_PKO_NO_MEMORY; + if (pko_status.s.dqstatus == PKO_DQSTATUS_NOTCREATED) + return CVMX_PKO_INVALID_QUEUE; + if (pko_status.s.dqstatus == PKO_DQSTATUS_BADSTATE) + return CVMX_PKO_CMD_QUEUE_INIT_ERROR; + if (pko_status.s.dqstatus == PKO_DQSTATUS_SENDPKTDROP) + return CVMX_PKO_INVALID_PORT; + + return CVMX_PKO_INVALID_PORT; +} diff --git a/arch/mips/mach-octeon/cvmx-pko3-queue.c b/arch/mips/mach-octeon/cvmx-pko3-queue.c new file mode 100644 index 0000000..e28afdf --- /dev/null +++ b/arch/mips/mach-octeon/cvmx-pko3-queue.c @@ -0,0 +1,879 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018-2022 Marvell International Ltd. + */ + +#include <errno.h> +#include <log.h> +#include <time.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-pko3.h> +#include <mach/cvmx-pko3-queue.h> +#include <mach/cvmx-pko3-resources.h> + +#include <mach/cvmx-helper.h> +#include <mach/cvmx-helper-board.h> +#include <mach/cvmx-helper-cfg.h> + +#include <mach/cvmx-helper-bgx.h> +#include <mach/cvmx-helper-cfg.h> +#include <mach/cvmx-helper-util.h> +#include <mach/cvmx-helper-pki.h> + +/* Smalles Round-Robin quantum to use +1 */ +#define CVMX_PKO3_RR_QUANTUM_MIN 0x10 + +static int debug; /* 1 for basic, 2 for detailed trace */ + +struct cvmx_pko3_dq { + unsigned dq_count : 6; /* Number of descriptor queues */ + unsigned dq_base : 10; /* Descriptor queue start number */ +#define CVMX_PKO3_SWIZZLE_IPD 0x0 +}; + +/* + * @INTERNAL + * Descriptor Queue to IPD port mapping table. + * + * This pointer is per-core, contains the virtual address + * of a global named block which has 2^12 entries per each + * possible node. + */ +struct cvmx_pko3_dq *__cvmx_pko3_dq_table; + +int cvmx_pko3_get_queue_base(int ipd_port) +{ + struct cvmx_pko3_dq *dq_table; + int ret = -1; + unsigned int i; + struct cvmx_xport xp = cvmx_helper_ipd_port_to_xport(ipd_port); + + /* get per-node table */ + if (cvmx_unlikely(!__cvmx_pko3_dq_table)) + __cvmx_pko3_dq_table_setup(); + + i = CVMX_PKO3_SWIZZLE_IPD ^ xp.port; + + /* get per-node table */ + dq_table = __cvmx_pko3_dq_table + CVMX_PKO3_IPD_NUM_MAX * xp.node; + + if (cvmx_likely(dq_table[i].dq_count > 0)) + ret = xp.node << 10 | dq_table[i].dq_base; + else if (debug) + cvmx_printf("ERROR: %s: no queues for ipd_port=%#x\n", __func__, + ipd_port); + + return ret; +} + +int cvmx_pko3_get_queue_num(int ipd_port) +{ + struct cvmx_pko3_dq *dq_table; + int ret = -1; + unsigned int i; + struct cvmx_xport xp = cvmx_helper_ipd_port_to_xport(ipd_port); + + /* get per-node table */ + if (cvmx_unlikely(!__cvmx_pko3_dq_table)) + __cvmx_pko3_dq_table_setup(); + + i = CVMX_PKO3_SWIZZLE_IPD ^ xp.port; + + /* get per-node table */ + dq_table = __cvmx_pko3_dq_table + CVMX_PKO3_IPD_NUM_MAX * xp.node; + + if (cvmx_likely(dq_table[i].dq_count > 0)) + ret = dq_table[i].dq_count; + else if (debug) + debug("ERROR: %s: no queues for ipd_port=%#x\n", __func__, + ipd_port); + + return ret; +} + +/** + * @INTERNAL + * + * Initialize port/dq table contents + */ +static void __cvmx_pko3_dq_table_init(void *ptr) +{ + unsigned int size = sizeof(struct cvmx_pko3_dq) * + CVMX_PKO3_IPD_NUM_MAX * CVMX_MAX_NODES; + + memset(ptr, 0, size); +} + +/** + * @INTERNAL + * + * Find or allocate global port/dq map table + * which is a named table, contains entries for + * all possible OCI nodes. + * + * The table global pointer is stored in core-local variable + * so that every core will call this function once, on first use. + */ +int __cvmx_pko3_dq_table_setup(void) +{ + void *ptr; + + ptr = cvmx_bootmem_alloc_named_range_once( + /* size */ + sizeof(struct cvmx_pko3_dq) * CVMX_PKO3_IPD_NUM_MAX * + CVMX_MAX_NODES, + /* min_addr, max_addr, align */ + 0ull, 0ull, sizeof(struct cvmx_pko3_dq), + /* name */ + "cvmx_pko3_global_dq_table", __cvmx_pko3_dq_table_init); + + if (debug) + debug("%s: dq_table_ptr=%p\n", __func__, ptr); + + if (!ptr) + return -1; + + __cvmx_pko3_dq_table = ptr; + return 0; +} + +/* + * @INTERNAL + * Register a range of Descriptor Queues with an interface port + * + * This function populates the DQ-to-IPD translation table + * used by the application to retrieve the DQ range (typically ordered + * by priority) for a given IPD-port, which is either a physical port, + * or a channel on a channelized interface (i.e. ILK). + * + * @param xiface is the physical interface number + * @param index is either a physical port on an interface + * or a channel of an ILK interface + * @param dq_base is the first Descriptor Queue number in a consecutive range + * @param dq_count is the number of consecutive Descriptor Queues leading + * the same channel or port. + * + * Only a consecutive range of Descriptor Queues can be associated with any + * given channel/port, and usually they are ordered from most to least + * in terms of scheduling priority. + * + * Note: thus function only populates the node-local translation table. + * NOTE: This function would be cleaner if it had a single ipd_port argument + * + * @returns 0 on success, -1 on failure. + */ +int __cvmx_pko3_ipd_dq_register(int xiface, int index, unsigned int dq_base, + unsigned int dq_count) +{ + struct cvmx_pko3_dq *dq_table; + int ipd_port; + unsigned int i; + struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); + struct cvmx_xport xp; + + if (__cvmx_helper_xiface_is_null(xiface)) { + ipd_port = cvmx_helper_node_to_ipd_port(xi.node, + CVMX_PKO3_IPD_PORT_NULL); + } else { + int p; + + p = cvmx_helper_get_ipd_port(xiface, index); + if (p < 0) { + cvmx_printf("ERROR: %s: xiface %#x has no IPD port\n", + __func__, xiface); + return -1; + } + ipd_port = p; + } + + xp = cvmx_helper_ipd_port_to_xport(ipd_port); + + i = CVMX_PKO3_SWIZZLE_IPD ^ xp.port; + + /* get per-node table */ + if (!__cvmx_pko3_dq_table) + __cvmx_pko3_dq_table_setup(); + + dq_table = __cvmx_pko3_dq_table + CVMX_PKO3_IPD_NUM_MAX * xi.node; + + if (debug) + debug("%s: ipd_port=%#x ix=%#x dq %u cnt %u\n", __func__, + ipd_port, i, dq_base, dq_count); + + /* Check the IPD port has not already been configured */ + if (dq_table[i].dq_count > 0) { + cvmx_printf("%s: ERROR: IPD %#x already registered\n", __func__, + ipd_port); + return -1; + } + + /* Store DQ# range in the queue lookup table */ + dq_table[i].dq_base = dq_base; + dq_table[i].dq_count = dq_count; + + return 0; +} + +/* + * @INTERNAL + * Convert normal CHAN_E (i.e. IPD port) value to compressed channel form + * that is used to populate PKO_LUT. + * + * Note: This code may be model specific. + */ +static int cvmx_pko3_chan_2_xchan(uint16_t ipd_port) +{ + u16 xchan; + u8 off; + static const u8 *xchan_base; + static const u8 xchan_base_cn78xx[16] = { + /* IPD 0x000 */ 0x3c0 >> 4, /* LBK */ + /* IPD 0x100 */ 0x380 >> 4, /* DPI */ + /* IPD 0x200 */ 0xfff >> 4, /* not used */ + /* IPD 0x300 */ 0xfff >> 4, /* not used */ + /* IPD 0x400 */ 0x000 >> 4, /* ILK0 */ + /* IPD 0x500 */ 0x100 >> 4, /* ILK1 */ + /* IPD 0x600 */ 0xfff >> 4, /* not used */ + /* IPD 0x700 */ 0xfff >> 4, /* not used */ + /* IPD 0x800 */ 0x200 >> 4, /* BGX0 */ + /* IPD 0x900 */ 0x240 >> 4, /* BGX1 */ + /* IPD 0xa00 */ 0x280 >> 4, /* BGX2 */ + /* IPD 0xb00 */ 0x2c0 >> 4, /* BGX3 */ + /* IPD 0xc00 */ 0x300 >> 4, /* BGX4 */ + /* IPD 0xd00 */ 0x340 >> 4, /* BGX5 */ + /* IPD 0xe00 */ 0xfff >> 4, /* not used */ + /* IPD 0xf00 */ 0xfff >> 4 /* not used */ + }; + static const u8 xchan_base_cn73xx[16] = { + /* IPD 0x000 */ 0x0c0 >> 4, /* LBK */ + /* IPD 0x100 */ 0x100 >> 4, /* DPI */ + /* IPD 0x200 */ 0xfff >> 4, /* not used */ + /* IPD 0x300 */ 0xfff >> 4, /* not used */ + /* IPD 0x400 */ 0xfff >> 4, /* not used */ + /* IPD 0x500 */ 0xfff >> 4, /* not used */ + /* IPD 0x600 */ 0xfff >> 4, /* not used */ + /* IPD 0x700 */ 0xfff >> 4, /* not used */ + /* IPD 0x800 */ 0x000 >> 4, /* BGX0 */ + /* IPD 0x900 */ 0x040 >> 4, /* BGX1 */ + /* IPD 0xa00 */ 0x080 >> 4, /* BGX2 */ + /* IPD 0xb00 */ 0xfff >> 4, /* not used */ + /* IPD 0xc00 */ 0xfff >> 4, /* not used */ + /* IPD 0xd00 */ 0xfff >> 4, /* not used */ + /* IPD 0xe00 */ 0xfff >> 4, /* not used */ + /* IPD 0xf00 */ 0xfff >> 4 /* not used */ + }; + static const u8 xchan_base_cn75xx[16] = { + /* IPD 0x000 */ 0x040 >> 4, /* LBK */ + /* IPD 0x100 */ 0x080 >> 4, /* DPI */ + /* IPD 0x200 */ 0xeee >> 4, /* SRIO0 noop */ + /* IPD 0x300 */ 0xfff >> 4, /* not used */ + /* IPD 0x400 */ 0xfff >> 4, /* not used */ + /* IPD 0x500 */ 0xfff >> 4, /* not used */ + /* IPD 0x600 */ 0xfff >> 4, /* not used */ + /* IPD 0x700 */ 0xfff >> 4, /* not used */ + /* IPD 0x800 */ 0x000 >> 4, /* BGX0 */ + /* IPD 0x900 */ 0xfff >> 4, /* not used */ + /* IPD 0xa00 */ 0xfff >> 4, /* not used */ + /* IPD 0xb00 */ 0xfff >> 4, /* not used */ + /* IPD 0xc00 */ 0xfff >> 4, /* not used */ + /* IPD 0xd00 */ 0xfff >> 4, /* not used */ + /* IPD 0xe00 */ 0xfff >> 4, /* not used */ + /* IPD 0xf00 */ 0xfff >> 4 /* not used */ + }; + + if (OCTEON_IS_MODEL(OCTEON_CN73XX)) + xchan_base = xchan_base_cn73xx; + if (OCTEON_IS_MODEL(OCTEON_CNF75XX)) + xchan_base = xchan_base_cn75xx; + if (OCTEON_IS_MODEL(OCTEON_CN78XX)) + xchan_base = xchan_base_cn78xx; + + if (!xchan_base) + return -1; + + xchan = ipd_port >> 8; + + /* ILKx, DPI has 8 bits logical channels, others just 6 */ + if (((xchan & 0xfe) == 0x04) || xchan == 0x01) + off = ipd_port & 0xff; + else + off = ipd_port & 0x3f; + + xchan = xchan_base[xchan & 0xf]; + + if (xchan == 0xff) + return -1; /* Invalid IPD_PORT */ + else if (xchan == 0xee) + return -2; /* LUT not used */ + else + return (xchan << 4) | off; +} + +/* + * Map channel number in PKO + * + * @param node is to specify the node to which this configuration is applied. + * @param pq_num specifies the Port Queue (i.e. L1) queue number. + * @param l2_l3_q_num specifies L2/L3 queue number. + * @param channel specifies the channel number to map to the queue. + * + * The channel assignment applies to L2 or L3 Shaper Queues depending + * on the setting of channel credit level. + * + * @return returns none. + */ +void cvmx_pko3_map_channel(unsigned int node, unsigned int pq_num, + unsigned int l2_l3_q_num, uint16_t channel) +{ + union cvmx_pko_l3_l2_sqx_channel sqx_channel; + cvmx_pko_lutx_t lutx; + int xchan; + + sqx_channel.u64 = + csr_rd_node(node, CVMX_PKO_L3_L2_SQX_CHANNEL(l2_l3_q_num)); + + sqx_channel.s.cc_channel = channel; + + csr_wr_node(node, CVMX_PKO_L3_L2_SQX_CHANNEL(l2_l3_q_num), + sqx_channel.u64); + + /* Convert CHAN_E into compressed channel */ + xchan = cvmx_pko3_chan_2_xchan(channel); + + if (debug) + debug("%s: ipd_port=%#x xchan=%#x\n", __func__, channel, xchan); + + if (xchan < 0) { + if (xchan == -1) + cvmx_printf("%s: ERROR: channel %#x not recognized\n", + __func__, channel); + return; + } + + lutx.u64 = 0; + lutx.s.valid = 1; + lutx.s.pq_idx = pq_num; + lutx.s.queue_number = l2_l3_q_num; + + csr_wr_node(node, CVMX_PKO_LUTX(xchan), lutx.u64); + + if (debug) + debug("%s: channel %#x (compressed=%#x) mapped L2/L3 SQ=%u, PQ=%u\n", + __func__, channel, xchan, l2_l3_q_num, pq_num); +} + +/* + * @INTERNAL + * This function configures port queue scheduling and topology parameters + * in hardware. + * + * @param node is to specify the node to which this configuration is applied. + * @param port_queue is the port queue number to be configured. + * @param mac_num is the mac number of the mac that will be tied to this port_queue. + */ +static void cvmx_pko_configure_port_queue(int node, int port_queue, int mac_num) +{ + cvmx_pko_l1_sqx_topology_t pko_l1_topology; + cvmx_pko_l1_sqx_shape_t pko_l1_shape; + cvmx_pko_l1_sqx_link_t pko_l1_link; + + pko_l1_topology.u64 = 0; + pko_l1_topology.s.link = mac_num; + csr_wr_node(node, CVMX_PKO_L1_SQX_TOPOLOGY(port_queue), + pko_l1_topology.u64); + + pko_l1_shape.u64 = 0; + pko_l1_shape.s.link = mac_num; + csr_wr_node(node, CVMX_PKO_L1_SQX_SHAPE(port_queue), pko_l1_shape.u64); + + pko_l1_link.u64 = 0; + pko_l1_link.s.link = mac_num; + csr_wr_node(node, CVMX_PKO_L1_SQX_LINK(port_queue), pko_l1_link.u64); +} + +/* + * @INTERNAL + * This function configures level 2 queues scheduling and topology parameters + * in hardware. + * + * @param node is to specify the node to which this configuration is applied. + * @param queue is the level3 queue number to be configured. + * @param parent_queue is the parent queue at next level for this l3 queue. + * @param prio is this queue's priority in parent's scheduler. + * @param rr_quantum is this queue's round robin quantum value. + * @param child_base is the first child queue number in the static prioriy children. + * @param child_rr_prio is the round robin children priority. + */ +static void cvmx_pko_configure_l2_queue(int node, int queue, int parent_queue, + int prio, int rr_quantum, + int child_base, int child_rr_prio) +{ + cvmx_pko_l2_sqx_schedule_t pko_sq_sched; + cvmx_pko_l2_sqx_topology_t pko_child_topology; + cvmx_pko_l1_sqx_topology_t pko_parent_topology; + + /* parent topology configuration */ + pko_parent_topology.u64 = + csr_rd_node(node, CVMX_PKO_L1_SQX_TOPOLOGY(parent_queue)); + pko_parent_topology.s.prio_anchor = child_base; + pko_parent_topology.s.rr_prio = child_rr_prio; + csr_wr_node(node, CVMX_PKO_L1_SQX_TOPOLOGY(parent_queue), + pko_parent_topology.u64); + + if (debug > 1) + debug("CVMX_PKO_L1_SQX_TOPOLOGY(%u): PRIO_ANCHOR=%u PARENT=%u\n", + parent_queue, pko_parent_topology.s.prio_anchor, + pko_parent_topology.s.link); + + /* scheduler configuration for this sq in the parent queue */ + pko_sq_sched.u64 = 0; + pko_sq_sched.s.prio = prio; + pko_sq_sched.s.rr_quantum = rr_quantum; + csr_wr_node(node, CVMX_PKO_L2_SQX_SCHEDULE(queue), pko_sq_sched.u64); + + /* child topology configuration */ + pko_child_topology.u64 = 0; + pko_child_topology.s.parent = parent_queue; + csr_wr_node(node, CVMX_PKO_L2_SQX_TOPOLOGY(queue), + pko_child_topology.u64); +} + +/* + * @INTERNAL + * This function configures level 3 queues scheduling and topology parameters + * in hardware. + * + * @param node is to specify the node to which this configuration is applied. + * @param queue is the level3 queue number to be configured. + * @param parent_queue is the parent queue at next level for this l3 queue. + * @param prio is this queue's priority in parent's scheduler. + * @param rr_quantum is this queue's round robin quantum value. + * @param child_base is the first child queue number in the static prioriy children. + * @param child_rr_prio is the round robin children priority. + */ +static void cvmx_pko_configure_l3_queue(int node, int queue, int parent_queue, + int prio, int rr_quantum, + int child_base, int child_rr_prio) +{ + cvmx_pko_l3_sqx_schedule_t pko_sq_sched; + cvmx_pko_l3_sqx_topology_t pko_child_topology; + cvmx_pko_l2_sqx_topology_t pko_parent_topology; + + /* parent topology configuration */ + pko_parent_topology.u64 = + csr_rd_node(node, CVMX_PKO_L2_SQX_TOPOLOGY(parent_queue)); + pko_parent_topology.s.prio_anchor = child_base; + pko_parent_topology.s.rr_prio = child_rr_prio; + csr_wr_node(node, CVMX_PKO_L2_SQX_TOPOLOGY(parent_queue), + pko_parent_topology.u64); + + if (debug > 1) + debug("CVMX_PKO_L2_SQX_TOPOLOGY(%u): PRIO_ANCHOR=%u PARENT=%u\n", + parent_queue, pko_parent_topology.s.prio_anchor, + pko_parent_topology.s.parent); + + /* scheduler configuration for this sq in the parent queue */ + pko_sq_sched.u64 = 0; + pko_sq_sched.s.prio = prio; + pko_sq_sched.s.rr_quantum = rr_quantum; + csr_wr_node(node, CVMX_PKO_L3_SQX_SCHEDULE(queue), pko_sq_sched.u64); + + /* child topology configuration */ + pko_child_topology.u64 = 0; + pko_child_topology.s.parent = parent_queue; + csr_wr_node(node, CVMX_PKO_L3_SQX_TOPOLOGY(queue), + pko_child_topology.u64); +} + +/* + * @INTERNAL + * This function configures level 4 queues scheduling and topology parameters + * in hardware. + * + * @param node is to specify the node to which this configuration is applied. + * @param queue is the level4 queue number to be configured. + * @param parent_queue is the parent queue at next level for this l4 queue. + * @param prio is this queue's priority in parent's scheduler. + * @param rr_quantum is this queue's round robin quantum value. + * @param child_base is the first child queue number in the static prioriy children. + * @param child_rr_prio is the round robin children priority. + */ +static void cvmx_pko_configure_l4_queue(int node, int queue, int parent_queue, + int prio, int rr_quantum, + int child_base, int child_rr_prio) +{ + cvmx_pko_l4_sqx_schedule_t pko_sq_sched; + cvmx_pko_l4_sqx_topology_t pko_child_topology; + cvmx_pko_l3_sqx_topology_t pko_parent_topology; + + /* parent topology configuration */ + pko_parent_topology.u64 = + csr_rd_node(node, CVMX_PKO_L3_SQX_TOPOLOGY(parent_queue)); + pko_parent_topology.s.prio_anchor = child_base; + pko_parent_topology.s.rr_prio = child_rr_prio; + csr_wr_node(node, CVMX_PKO_L3_SQX_TOPOLOGY(parent_queue), + pko_parent_topology.u64); + + if (debug > 1) + debug("CVMX_PKO_L3_SQX_TOPOLOGY(%u): PRIO_ANCHOR=%u PARENT=%u\n", + parent_queue, pko_parent_topology.s.prio_anchor, + pko_parent_topology.s.parent); + + /* scheduler configuration for this sq in the parent queue */ + pko_sq_sched.u64 = 0; + pko_sq_sched.s.prio = prio; + pko_sq_sched.s.rr_quantum = rr_quantum; + csr_wr_node(node, CVMX_PKO_L4_SQX_SCHEDULE(queue), pko_sq_sched.u64); + + /* topology configuration */ + pko_child_topology.u64 = 0; + pko_child_topology.s.parent = parent_queue; + csr_wr_node(node, CVMX_PKO_L4_SQX_TOPOLOGY(queue), + pko_child_topology.u64); +} + +/* + * @INTERNAL + * This function configures level 5 queues scheduling and topology parameters + * in hardware. + * + * @param node is to specify the node to which this configuration is applied. + * @param queue is the level5 queue number to be configured. + * @param parent_queue is the parent queue at next level for this l5 queue. + * @param prio is this queue's priority in parent's scheduler. + * @param rr_quantum is this queue's round robin quantum value. + * @param child_base is the first child queue number in the static prioriy children. + * @param child_rr_prio is the round robin children priority. + */ +static void cvmx_pko_configure_l5_queue(int node, int queue, int parent_queue, + int prio, int rr_quantum, + int child_base, int child_rr_prio) +{ + cvmx_pko_l5_sqx_schedule_t pko_sq_sched; + cvmx_pko_l4_sqx_topology_t pko_parent_topology; + cvmx_pko_l5_sqx_topology_t pko_child_topology; + + /* parent topology configuration */ + pko_parent_topology.u64 = + csr_rd_node(node, CVMX_PKO_L4_SQX_TOPOLOGY(parent_queue)); + pko_parent_topology.s.prio_anchor = child_base; + pko_parent_topology.s.rr_prio = child_rr_prio; + csr_wr_node(node, CVMX_PKO_L4_SQX_TOPOLOGY(parent_queue), + pko_parent_topology.u64); + + if (debug > 1) + debug("CVMX_PKO_L4_SQX_TOPOLOGY(%u): PRIO_ANCHOR=%u PARENT=%u\n", + parent_queue, pko_parent_topology.s.prio_anchor, + pko_parent_topology.s.parent); + + /* scheduler configuration for this sq in the parent queue */ + pko_sq_sched.u64 = 0; + pko_sq_sched.s.prio = prio; + pko_sq_sched.s.rr_quantum = rr_quantum; + csr_wr_node(node, CVMX_PKO_L5_SQX_SCHEDULE(queue), pko_sq_sched.u64); + + /* topology configuration */ + pko_child_topology.u64 = 0; + pko_child_topology.s.parent = parent_queue; + csr_wr_node(node, CVMX_PKO_L5_SQX_TOPOLOGY(queue), + pko_child_topology.u64); +} + +/* + * @INTERNAL + * This function configures descriptor queues scheduling and topology parameters + * in hardware. + * + * @param node is to specify the node to which this configuration is applied. + * @param dq is the descriptor queue number to be configured. + * @param parent_queue is the parent queue at next level for this dq. + * @param prio is this queue's priority in parent's scheduler. + * @param rr_quantum is this queue's round robin quantum value. + * @param child_base is the first child queue number in the static prioriy children. + * @param child_rr_prio is the round robin children priority. + */ +static void cvmx_pko_configure_dq(int node, int dq, int parent_queue, int prio, + int rr_quantum, int child_base, + int child_rr_prio) +{ + cvmx_pko_dqx_schedule_t pko_dq_sched; + cvmx_pko_dqx_topology_t pko_dq_topology; + cvmx_pko_l5_sqx_topology_t pko_parent_topology; + cvmx_pko_dqx_wm_ctl_t pko_dq_wm_ctl; + unsigned long long parent_topology_reg; + char lvl; + + if (debug) + debug("%s: dq %u parent %u child_base %u\n", __func__, dq, + parent_queue, child_base); + + if (__cvmx_pko3_sq_lvl_max() == CVMX_PKO_L5_QUEUES) { + parent_topology_reg = CVMX_PKO_L5_SQX_TOPOLOGY(parent_queue); + lvl = 5; + } else if (__cvmx_pko3_sq_lvl_max() == CVMX_PKO_L3_QUEUES) { + parent_topology_reg = CVMX_PKO_L3_SQX_TOPOLOGY(parent_queue); + lvl = 3; + } else { + return; + } + + if (debug) + debug("%s: parent_topology_reg=%#llx\n", __func__, + parent_topology_reg); + + /* parent topology configuration */ + pko_parent_topology.u64 = csr_rd_node(node, parent_topology_reg); + pko_parent_topology.s.prio_anchor = child_base; + pko_parent_topology.s.rr_prio = child_rr_prio; + csr_wr_node(node, parent_topology_reg, pko_parent_topology.u64); + + if (debug > 1) + debug("CVMX_PKO_L%d_SQX_TOPOLOGY(%u): PRIO_ANCHOR=%u PARENT=%u\n", + lvl, parent_queue, pko_parent_topology.s.prio_anchor, + pko_parent_topology.s.parent); + + /* scheduler configuration for this dq in the parent queue */ + pko_dq_sched.u64 = 0; + pko_dq_sched.s.prio = prio; + pko_dq_sched.s.rr_quantum = rr_quantum; + csr_wr_node(node, CVMX_PKO_DQX_SCHEDULE(dq), pko_dq_sched.u64); + + /* topology configuration */ + pko_dq_topology.u64 = 0; + pko_dq_topology.s.parent = parent_queue; + csr_wr_node(node, CVMX_PKO_DQX_TOPOLOGY(dq), pko_dq_topology.u64); + + /* configure for counting packets, not bytes at this level */ + pko_dq_wm_ctl.u64 = 0; + pko_dq_wm_ctl.s.kind = 1; + pko_dq_wm_ctl.s.enable = 0; + csr_wr_node(node, CVMX_PKO_DQX_WM_CTL(dq), pko_dq_wm_ctl.u64); + + if (debug > 1) { + pko_dq_sched.u64 = csr_rd_node(node, CVMX_PKO_DQX_SCHEDULE(dq)); + pko_dq_topology.u64 = + csr_rd_node(node, CVMX_PKO_DQX_TOPOLOGY(dq)); + debug("CVMX_PKO_DQX_TOPOLOGY(%u)PARENT=%u CVMX_PKO_DQX_SCHEDULE(%u) PRIO=%u Q=%u\n", + dq, pko_dq_topology.s.parent, dq, pko_dq_sched.s.prio, + pko_dq_sched.s.rr_quantum); + } +} + +/* + * @INTERNAL + * The following structure selects the Scheduling Queue configuration + * routine for each of the supported levels. + * The initial content of the table will be setup in accordance + * to the specific SoC model and its implemented resources + */ +struct pko3_cfg_tab_s { + /* function pointer for to configure the given level, last=DQ */ + struct { + u8 parent_level; + void (*cfg_sq_func)(int node, int queue, int parent_queue, + int prio, int rr_quantum, int child_base, + int child_rr_prio); + //XXX for debugging exagerated size + } lvl[256]; +}; + +static const struct pko3_cfg_tab_s pko3_cn78xx_cfg = { + { [CVMX_PKO_L2_QUEUES] = { CVMX_PKO_PORT_QUEUES, + cvmx_pko_configure_l2_queue }, + [CVMX_PKO_L3_QUEUES] = { CVMX_PKO_L2_QUEUES, + cvmx_pko_configure_l3_queue }, + [CVMX_PKO_L4_QUEUES] = { CVMX_PKO_L3_QUEUES, + cvmx_pko_configure_l4_queue }, + [CVMX_PKO_L5_QUEUES] = { CVMX_PKO_L4_QUEUES, + cvmx_pko_configure_l5_queue }, + [CVMX_PKO_DESCR_QUEUES] = { CVMX_PKO_L5_QUEUES, + cvmx_pko_configure_dq } } +}; + +static const struct pko3_cfg_tab_s pko3_cn73xx_cfg = { + { [CVMX_PKO_L2_QUEUES] = { CVMX_PKO_PORT_QUEUES, + cvmx_pko_configure_l2_queue }, + [CVMX_PKO_L3_QUEUES] = { CVMX_PKO_L2_QUEUES, + cvmx_pko_configure_l3_queue }, + [CVMX_PKO_DESCR_QUEUES] = { CVMX_PKO_L3_QUEUES, + cvmx_pko_configure_dq } } +}; + +/* + * Configure Port Queue and its children Scheduler Queue + * + * Port Queues (a.k.a L1) are assigned 1-to-1 to MACs. + * L2 Scheduler Queues are used for specifying channels, and thus there + * could be multiple L2 SQs attached to a single L1 PQ, either in a + * fair round-robin scheduling, or with static and/or round-robin priorities. + * + * @param node on which to operate + * @param mac_num is the LMAC number to that is associated with the Port Queue, + * @param pq_num is the number of the L1 PQ attached to the MAC + * + * @returns 0 on success, -1 on failure. + */ +int cvmx_pko3_pq_config(unsigned int node, unsigned int mac_num, + unsigned int pq_num) +{ + char b1[10]; + + if (debug) + debug("%s: MAC%u -> %s\n", __func__, mac_num, + __cvmx_pko3_sq_str(b1, CVMX_PKO_PORT_QUEUES, pq_num)); + + cvmx_pko_configure_port_queue(node, pq_num, mac_num); + + return 0; +} + +/* + * Configure L3 through L5 Scheduler Queues and Descriptor Queues + * + * The Scheduler Queues in Levels 3 to 5 and Descriptor Queues are + * configured one-to-one or many-to-one to a single parent Scheduler + * Queues. The level of the parent SQ is specified in an argument, + * as well as the number of childer to attach to the specific parent. + * The children can have fair round-robin or priority-based scheduling + * when multiple children are assigned a single parent. + * + * @param node on which to operate + * @param child_level is the level of the child queue + * @param parent_queue is the number of the parent Scheduler Queue + * @param child_base is the number of the first child SQ or DQ to assign to + * @param child_count is the number of consecutive children to assign + * @param stat_prio_count is the priority setting for the children L2 SQs + * + * If <stat_prio_count> is -1, the Ln children will have equal Round-Robin + * relationship with eachother. If <stat_prio_count> is 0, all Ln children + * will be arranged in Weighted-Round-Robin, with the first having the most + * precedence. If <stat_prio_count> is between 1 and 8, it indicates how + * many children will have static priority settings (with the first having + * the most precedence), with the remaining Ln children having WRR scheduling. + * + * @returns 0 on success, -1 on failure. + * + * Note: this function supports the configuration of node-local unit. + */ +int cvmx_pko3_sq_config_children(unsigned int node, + enum cvmx_pko3_level_e child_level, + unsigned int parent_queue, + unsigned int child_base, + unsigned int child_count, int stat_prio_count) +{ + enum cvmx_pko3_level_e parent_level; + unsigned int num_elem = 0; + unsigned int rr_quantum, rr_count; + unsigned int child, prio, rr_prio; + const struct pko3_cfg_tab_s *cfg_tbl = NULL; + char b1[10], b2[10]; + + if (OCTEON_IS_MODEL(OCTEON_CN78XX)) { + num_elem = NUM_ELEMENTS(pko3_cn78xx_cfg.lvl); + cfg_tbl = &pko3_cn78xx_cfg; + } + if (OCTEON_IS_MODEL(OCTEON_CN73XX) || OCTEON_IS_MODEL(OCTEON_CNF75XX)) { + num_elem = NUM_ELEMENTS(pko3_cn73xx_cfg.lvl); + cfg_tbl = &pko3_cn73xx_cfg; + } + + if (!cfg_tbl || child_level >= num_elem) { + cvmx_printf("ERROR: %s: model or level %#x invalid\n", __func__, + child_level); + return -1; + } + + parent_level = cfg_tbl->lvl[child_level].parent_level; + + if (!cfg_tbl->lvl[child_level].cfg_sq_func || + cfg_tbl->lvl[child_level].parent_level == 0) { + cvmx_printf("ERROR: %s: queue level %#x invalid\n", __func__, + child_level); + return -1; + } + + /* First static priority is 0 - top precedence */ + prio = 0; + + if (stat_prio_count > (signed int)child_count) + stat_prio_count = child_count; + + /* Valid PRIO field is 0..9, limit maximum static priorities */ + if (stat_prio_count > 9) + stat_prio_count = 9; + + /* Special case of a single child */ + if (child_count == 1) { + rr_count = 0; + rr_prio = 0xF; + /* Special case for Fair-RR */ + } else if (stat_prio_count < 0) { + rr_count = child_count; + rr_prio = 0; + } else { + rr_count = child_count - stat_prio_count; + rr_prio = stat_prio_count; + } + + /* Compute highest RR_QUANTUM */ + if (stat_prio_count > 0) + rr_quantum = CVMX_PKO3_RR_QUANTUM_MIN * rr_count; + else + rr_quantum = CVMX_PKO3_RR_QUANTUM_MIN; + + if (debug) + debug("%s: Parent %s child_base %u rr_pri %u\n", __func__, + __cvmx_pko3_sq_str(b1, parent_level, parent_queue), + child_base, rr_prio); + + /* Parent is configured with child */ + + for (child = child_base; child < (child_base + child_count); child++) { + if (debug) + debug("%s: Child %s of %s prio %u rr_quantum %#x\n", + __func__, + __cvmx_pko3_sq_str(b1, child_level, child), + __cvmx_pko3_sq_str(b2, parent_level, + parent_queue), + prio, rr_quantum); + + cfg_tbl->lvl[child_level].cfg_sq_func(node, child, parent_queue, + prio, rr_quantum, + child_base, rr_prio); + + if (prio < rr_prio) + prio++; + else if (stat_prio_count > 0) + rr_quantum -= CVMX_PKO3_RR_QUANTUM_MIN; + } /* for child */ + + return 0; +} diff --git a/arch/mips/mach-octeon/cvmx-pko3-resources.c b/arch/mips/mach-octeon/cvmx-pko3-resources.c new file mode 100644 index 0000000..ab04e9a --- /dev/null +++ b/arch/mips/mach-octeon/cvmx-pko3-resources.c @@ -0,0 +1,213 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018-2022 Marvell International Ltd. + * + * PKO resources. + */ + +#include <errno.h> +#include <log.h> +#include <time.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-range.h> +#include <mach/cvmx-global-resources.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-pko3.h> +#include <mach/cvmx-pko3-queue.h> +#include <mach/cvmx-pko3-resources.h> + +#include <mach/cvmx-helper.h> +#include <mach/cvmx-helper-board.h> +#include <mach/cvmx-helper-cfg.h> + +#include <mach/cvmx-helper-bgx.h> +#include <mach/cvmx-helper-cfg.h> +#include <mach/cvmx-helper-util.h> +#include <mach/cvmx-helper-pki.h> + +#define CVMX_GR_TAG_PKO_PORT_QUEUES(x) \ + cvmx_get_gr_tag('c', 'v', 'm', '_', 'p', 'k', 'o', 'p', 'o', 'q', '_', \ + ((x) + '0'), '.', '.', '.', '.') +#define CVMX_GR_TAG_PKO_L2_QUEUES(x) \ + cvmx_get_gr_tag('c', 'v', 'm', '_', 'p', 'k', 'o', 'l', '2', 'q', '_', \ + ((x) + '0'), '.', '.', '.', '.') +#define CVMX_GR_TAG_PKO_L3_QUEUES(x) \ + cvmx_get_gr_tag('c', 'v', 'm', '_', 'p', 'k', 'o', 'l', '3', 'q', '_', \ + ((x) + '0'), '.', '.', '.', '.') +#define CVMX_GR_TAG_PKO_L4_QUEUES(x) \ + cvmx_get_gr_tag('c', 'v', 'm', '_', 'p', 'k', 'o', 'l', '4', 'q', '_', \ + ((x) + '0'), '.', '.', '.', '.') +#define CVMX_GR_TAG_PKO_L5_QUEUES(x) \ + cvmx_get_gr_tag('c', 'v', 'm', '_', 'p', 'k', 'o', 'l', '5', 'q', '_', \ + ((x) + '0'), '.', '.', '.', '.') +#define CVMX_GR_TAG_PKO_DESCR_QUEUES(x) \ + cvmx_get_gr_tag('c', 'v', 'm', '_', 'p', 'k', 'o', 'd', 'e', 'q', '_', \ + ((x) + '0'), '.', '.', '.', '.') +#define CVMX_GR_TAG_PKO_PORT_INDEX(x) \ + cvmx_get_gr_tag('c', 'v', 'm', '_', 'p', 'k', 'o', 'p', 'i', 'd', '_', \ + ((x) + '0'), '.', '.', '.', '.') + +/* + * @INRWENAL + * Per-DQ parameters, current and maximum queue depth counters + */ +cvmx_pko3_dq_params_t *__cvmx_pko3_dq_params[CVMX_MAX_NODES]; + +static const short cvmx_pko_num_queues_78XX[256] = { + [CVMX_PKO_PORT_QUEUES] = 32, [CVMX_PKO_L2_QUEUES] = 512, + [CVMX_PKO_L3_QUEUES] = 512, [CVMX_PKO_L4_QUEUES] = 1024, + [CVMX_PKO_L5_QUEUES] = 1024, [CVMX_PKO_DESCR_QUEUES] = 1024 +}; + +static const short cvmx_pko_num_queues_73XX[256] = { + [CVMX_PKO_PORT_QUEUES] = 16, [CVMX_PKO_L2_QUEUES] = 256, + [CVMX_PKO_L3_QUEUES] = 256, [CVMX_PKO_L4_QUEUES] = 0, + [CVMX_PKO_L5_QUEUES] = 0, [CVMX_PKO_DESCR_QUEUES] = 256 +}; + +int cvmx_pko3_num_level_queues(enum cvmx_pko3_level_e level) +{ + unsigned int nq = 0, ne = 0; + + if (OCTEON_IS_MODEL(OCTEON_CN78XX)) { + ne = NUM_ELEMENTS(cvmx_pko_num_queues_78XX); + nq = cvmx_pko_num_queues_78XX[level]; + } + if (OCTEON_IS_MODEL(OCTEON_CN73XX) || OCTEON_IS_MODEL(OCTEON_CNF75XX)) { + ne = NUM_ELEMENTS(cvmx_pko_num_queues_73XX); + nq = cvmx_pko_num_queues_73XX[level]; + } + + if (nq == 0 || level >= ne) { + printf("ERROR: %s: queue level %#x invalid\n", __func__, level); + return -1; + } + + return nq; +} + +static inline struct global_resource_tag +__cvmx_pko_get_queues_resource_tag(int node, enum cvmx_pko3_level_e queue_level) +{ + if (cvmx_pko3_num_level_queues(queue_level) == 0) { + printf("ERROR: %s: queue level %#x invalid\n", __func__, + queue_level); + return CVMX_GR_TAG_INVALID; + } + + switch (queue_level) { + case CVMX_PKO_PORT_QUEUES: + return CVMX_GR_TAG_PKO_PORT_QUEUES(node); + case CVMX_PKO_L2_QUEUES: + return CVMX_GR_TAG_PKO_L2_QUEUES(node); + case CVMX_PKO_L3_QUEUES: + return CVMX_GR_TAG_PKO_L3_QUEUES(node); + case CVMX_PKO_L4_QUEUES: + return CVMX_GR_TAG_PKO_L4_QUEUES(node); + case CVMX_PKO_L5_QUEUES: + return CVMX_GR_TAG_PKO_L5_QUEUES(node); + case CVMX_PKO_DESCR_QUEUES: + return CVMX_GR_TAG_PKO_DESCR_QUEUES(node); + default: + printf("ERROR: %s: queue level %#x invalid\n", __func__, + queue_level); + return CVMX_GR_TAG_INVALID; + } +} + +/** + * Allocate or reserve a pko resource - called by wrapper functions + * @param tag processed global resource tag + * @param base_queue if specified the queue to reserve + * @param owner to be specified for resource + * @param num_queues to allocate + * @param max_num_queues for global resource + */ +int cvmx_pko_alloc_global_resource(struct global_resource_tag tag, + int base_queue, int owner, int num_queues, + int max_num_queues) +{ + int res; + + if (cvmx_create_global_resource_range(tag, max_num_queues)) { + debug("ERROR: Failed to create PKO3 resource: %lx-%lx\n", + (unsigned long)tag.hi, (unsigned long)tag.lo); + return -1; + } + if (base_queue >= 0) { + res = cvmx_reserve_global_resource_range(tag, owner, base_queue, + num_queues); + } else { + res = cvmx_allocate_global_resource_range(tag, owner, + num_queues, 1); + } + if (res < 0) { + debug("ERROR: Failed to %s PKO3 tag %lx:%lx, %i %i %i %i.\n", + ((base_queue < 0) ? "allocate" : "reserve"), + (unsigned long)tag.hi, (unsigned long)tag.lo, base_queue, + owner, num_queues, max_num_queues); + return -1; + } + + return res; +} + +/** + * Allocate or reserve PKO queues - wrapper for cvmx_pko_alloc_global_resource + * + * @param node on which to allocate/reserve PKO queues + * @param level of PKO queue + * @param owner of reserved/allocated resources + * @param base_queue to start reservation/allocatation + * @param num_queues number of queues to be allocated + * @return 0 on success, -1 on failure + */ +int cvmx_pko_alloc_queues(int node, int level, int owner, int base_queue, + int num_queues) +{ + struct global_resource_tag tag = + __cvmx_pko_get_queues_resource_tag(node, level); + int max_num_queues = cvmx_pko3_num_level_queues(level); + + return cvmx_pko_alloc_global_resource(tag, base_queue, owner, + num_queues, max_num_queues); +} + +/** + * @INTERNAL + * + * Initialize the pointer to the descriptor queue parameter table. + * The table is one named block per node, and may be shared between + * applications. + */ +int __cvmx_pko3_dq_param_setup(unsigned int node) +{ + return 0; +} diff --git a/arch/mips/mach-octeon/cvmx-pko3.c b/arch/mips/mach-octeon/cvmx-pko3.c new file mode 100644 index 0000000..d8ce02a --- /dev/null +++ b/arch/mips/mach-octeon/cvmx-pko3.c @@ -0,0 +1,786 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018-2022 Marvell International Ltd. + */ + +#include <errno.h> +#include <log.h> +#include <time.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-pko3.h> +#include <mach/cvmx-pko3-queue.h> +#include <mach/cvmx-pko3-resources.h> + +#include <mach/cvmx-helper.h> +#include <mach/cvmx-helper-board.h> +#include <mach/cvmx-helper-cfg.h> + +#include <mach/cvmx-helper-bgx.h> +#include <mach/cvmx-helper-cfg.h> +#include <mach/cvmx-helper-util.h> +#include <mach/cvmx-helper-pki.h> + +static const int debug; + +#define CVMX_DUMP_REGX(reg) \ + if (debug) \ + debug("%s=%#llx\n", #reg, (long long)csr_rd_node(node, reg)) + +static int cvmx_pko_setup_macs(int node); + +/* + * PKO descriptor queue operation error string + * + * @param dqstatus is the enumeration returned from hardware, + * PKO_QUERY_RTN_S[DQSTATUS]. + * + * @return static constant string error description + */ +const char *pko_dqstatus_error(pko_query_dqstatus_t dqstatus) +{ + char *str = "PKO Undefined error"; + + switch (dqstatus) { + case PKO_DQSTATUS_PASS: + str = "No error"; + break; + case PKO_DQSTATUS_BADSTATE: + str = "PKO queue not ready"; + break; + case PKO_DQSTATUS_NOFPABUF: + str = "PKO failed to allocate buffer from FPA"; + break; + case PKO_DQSTATUS_NOPKOBUF: + str = "PKO out of buffers"; + break; + case PKO_DQSTATUS_FAILRTNPTR: + str = "PKO failed to return buffer to FPA"; + break; + case PKO_DQSTATUS_ALREADY: + str = "PKO queue already opened"; + break; + case PKO_DQSTATUS_NOTCREATED: + str = "PKO queue has not been created"; + break; + case PKO_DQSTATUS_NOTEMPTY: + str = "PKO queue is not empty"; + break; + case PKO_DQSTATUS_SENDPKTDROP: + str = "Illegal PKO command construct"; + break; + } + return str; +} + +/* + * PKO global initialization for 78XX. + * + * @param node is the node on which PKO block is initialized. + * @return none. + */ +int cvmx_pko3_hw_init_global(int node, uint16_t aura) +{ + cvmx_pko_dpfi_flush_t pko_flush; + cvmx_pko_dpfi_fpa_aura_t pko_aura; + cvmx_pko_dpfi_ena_t dpfi_enable; + cvmx_pko_ptf_iobp_cfg_t ptf_iobp_cfg; + cvmx_pko_pdm_cfg_t pko_pdm_cfg; + cvmx_pko_enable_t pko_enable; + cvmx_pko_dpfi_status_t dpfi_status; + cvmx_pko_status_t pko_status; + cvmx_pko_shaper_cfg_t shaper_cfg; + u64 cycles; + const unsigned int timeout = 100; /* 100 milliseconds */ + + if (node != (aura >> 10)) + cvmx_printf("WARNING: AURA vs PKO node mismatch\n"); + + pko_enable.u64 = csr_rd_node(node, CVMX_PKO_ENABLE); + if (pko_enable.s.enable) { + cvmx_printf("WARNING: %s: PKO already enabled on node %u\n", + __func__, node); + return 0; + } + /* Enable color awareness. */ + shaper_cfg.u64 = csr_rd_node(node, CVMX_PKO_SHAPER_CFG); + shaper_cfg.s.color_aware = 1; + csr_wr_node(node, CVMX_PKO_SHAPER_CFG, shaper_cfg.u64); + + /* Clear FLUSH command to be sure */ + pko_flush.u64 = 0; + pko_flush.s.flush_en = 0; + csr_wr_node(node, CVMX_PKO_DPFI_FLUSH, pko_flush.u64); + + /* set the aura number in pko, use aura node from parameter */ + pko_aura.u64 = 0; + pko_aura.s.node = aura >> 10; + pko_aura.s.laura = aura; + csr_wr_node(node, CVMX_PKO_DPFI_FPA_AURA, pko_aura.u64); + + CVMX_DUMP_REGX(CVMX_PKO_DPFI_FPA_AURA); + + dpfi_enable.u64 = 0; + dpfi_enable.s.enable = 1; + csr_wr_node(node, CVMX_PKO_DPFI_ENA, dpfi_enable.u64); + + /* Prepare timeout */ + cycles = get_timer(0); + + /* Wait until all pointers have been returned */ + do { + pko_status.u64 = csr_rd_node(node, CVMX_PKO_STATUS); + if (get_timer(cycles) > timeout) + break; + } while (!pko_status.s.pko_rdy); + + if (!pko_status.s.pko_rdy) { + dpfi_status.u64 = csr_rd_node(node, CVMX_PKO_DPFI_STATUS); + cvmx_printf("ERROR: %s: PKO DFPI failed, PKO_STATUS=%#llx DPFI_STATUS=%#llx\n", + __func__, (unsigned long long)pko_status.u64, + (unsigned long long)dpfi_status.u64); + return -1; + } + + /* Set max outstanding requests in IOBP for any FIFO.*/ + ptf_iobp_cfg.u64 = csr_rd_node(node, CVMX_PKO_PTF_IOBP_CFG); + if (OCTEON_IS_MODEL(OCTEON_CN78XX)) + ptf_iobp_cfg.s.max_read_size = 0x10; /* Recommended by HRM.*/ + else + /* Reduce the value from recommended 0x10 to avoid + * getting "underflow" condition in the BGX TX FIFO. + */ + ptf_iobp_cfg.s.max_read_size = 3; + csr_wr_node(node, CVMX_PKO_PTF_IOBP_CFG, ptf_iobp_cfg.u64); + + /* Set minimum packet size per Ethernet standard */ + pko_pdm_cfg.u64 = 0; + pko_pdm_cfg.s.pko_pad_minlen = 0x3c; /* 60 bytes before FCS */ + csr_wr_node(node, CVMX_PKO_PDM_CFG, pko_pdm_cfg.u64); + + /* Initialize MACs and FIFOs */ + cvmx_pko_setup_macs(node); + + /* enable PKO, although interfaces and queues are not up yet */ + pko_enable.u64 = 0; + pko_enable.s.enable = 1; + csr_wr_node(node, CVMX_PKO_ENABLE, pko_enable.u64); + + /* PKO_RDY set indicates successful initialization */ + pko_status.u64 = csr_rd_node(node, CVMX_PKO_STATUS); + if (pko_status.s.pko_rdy) + return 0; + + cvmx_printf("ERROR: %s: failed, PKO_STATUS=%#llx\n", __func__, + (unsigned long long)pko_status.u64); + return -1; +} + +/* + * Configure Channel credit level in PKO. + * + * @param node is to specify the node to which this configuration is applied. + * @param level specifies the level at which pko channel queues will be configured, + * @return returns 0 if successful and -1 on failure. + */ +int cvmx_pko3_channel_credit_level(int node, enum cvmx_pko3_level_e level) +{ + union cvmx_pko_channel_level channel_level; + + channel_level.u64 = 0; + + if (level == CVMX_PKO_L2_QUEUES) + channel_level.s.cc_level = 0; + else if (level == CVMX_PKO_L3_QUEUES) + channel_level.s.cc_level = 1; + else + return -1; + + csr_wr_node(node, CVMX_PKO_CHANNEL_LEVEL, channel_level.u64); + + return 0; +} + +/** Open configured descriptor queues before queueing packets into them. + * + * @param node is to specify the node to which this configuration is applied. + * @param dq is the descriptor queue number to be opened. + * @return returns 0 on success or -1 on failure. + */ +int cvmx_pko_dq_open(int node, int dq) +{ + cvmx_pko_query_rtn_t pko_status; + pko_query_dqstatus_t dqstatus; + cvmx_pko3_dq_params_t *p_param; + + if (debug) + debug("%s: DEBUG: dq %u\n", __func__, dq); + + __cvmx_pko3_dq_param_setup(node); + + pko_status = __cvmx_pko3_do_dma(node, dq, NULL, 0, CVMX_PKO_DQ_OPEN); + + dqstatus = pko_status.s.dqstatus; + + if (dqstatus == PKO_DQSTATUS_ALREADY) + return 0; + if (dqstatus != PKO_DQSTATUS_PASS) { + cvmx_printf("%s: ERROR: Failed to open dq :%u: %s\n", __func__, + dq, pko_dqstatus_error(dqstatus)); + return -1; + } + + /* Setup the descriptor queue software parameters */ + p_param = cvmx_pko3_dq_parameters(node, dq); + if (p_param) { + p_param->depth = pko_status.s.depth; + if (p_param->limit == 0) + p_param->limit = 1024; /* last-resort default */ + } + + return 0; +} + +/* + * PKO initialization of MACs and FIFOs + * + * All MACs are configured and assigned a specific FIFO, + * and each FIFO is configured with size for a best utilization + * of available FIFO resources. + * + * @param node is to specify which node's pko block for this setup. + * @return returns 0 if successful and -1 on failure. + * + * Note: This function contains model-specific code. + */ +static int cvmx_pko_setup_macs(int node) +{ + unsigned int interface; + unsigned int port, num_ports; + unsigned int mac_num, fifo, pri, cnt; + cvmx_helper_interface_mode_t mode; + const unsigned int num_interfaces = + cvmx_helper_get_number_of_interfaces(); + u8 fifo_group_cfg[8]; + u8 fifo_group_spd[8]; + unsigned int fifo_count = 0; + unsigned int max_fifos = 0, fifo_groups = 0; + struct { + u8 fifo_cnt; + u8 fifo_id; + u8 pri; + u8 spd; + u8 mac_fifo_cnt; + } cvmx_pko3_mac_table[32]; + + if (OCTEON_IS_MODEL(OCTEON_CN78XX)) { + max_fifos = 28; /* exclusive of NULL FIFO */ + fifo_groups = 8; /* inclusive of NULL PTGF */ + } + if (OCTEON_IS_MODEL(OCTEON_CN73XX) || OCTEON_IS_MODEL(OCTEON_CNF75XX)) { + max_fifos = 16; + fifo_groups = 5; + } + + /* Initialize FIFO allocation table */ + memset(&fifo_group_cfg, 0, sizeof(fifo_group_cfg)); + memset(&fifo_group_spd, 0, sizeof(fifo_group_spd)); + memset(cvmx_pko3_mac_table, 0, sizeof(cvmx_pko3_mac_table)); + + /* Initialize all MACs as disabled */ + for (mac_num = 0; mac_num < __cvmx_pko3_num_macs(); mac_num++) { + cvmx_pko3_mac_table[mac_num].pri = 0; + cvmx_pko3_mac_table[mac_num].fifo_cnt = 0; + cvmx_pko3_mac_table[mac_num].fifo_id = 0x1f; + } + + for (interface = 0; interface < num_interfaces; interface++) { + int xiface = + cvmx_helper_node_interface_to_xiface(node, interface); + /* Interface type for ALL interfaces */ + mode = cvmx_helper_interface_get_mode(xiface); + num_ports = cvmx_helper_interface_enumerate(xiface); + + if (mode == CVMX_HELPER_INTERFACE_MODE_DISABLED) + continue; + /* + * Non-BGX interfaces: + * Each of these interfaces has a single MAC really. + */ + if (mode == CVMX_HELPER_INTERFACE_MODE_ILK || + mode == CVMX_HELPER_INTERFACE_MODE_NPI || + mode == CVMX_HELPER_INTERFACE_MODE_LOOP) + num_ports = 1; + + for (port = 0; port < num_ports; port++) { + int i; + + /* Get the per-port mode for BGX-interfaces */ + if (interface < CVMX_HELPER_MAX_GMX) + mode = cvmx_helper_bgx_get_mode(xiface, port); + /* In MIXED mode, LMACs can run different protocols */ + + /* convert interface/port to mac number */ + i = __cvmx_pko3_get_mac_num(xiface, port); + if (i < 0 || i >= (int)__cvmx_pko3_num_macs()) { + cvmx_printf("%s: ERROR: interface %d:%u port %d has no MAC %d/%d\n", + __func__, node, interface, port, i, + __cvmx_pko3_num_macs()); + continue; + } + + if (mode == CVMX_HELPER_INTERFACE_MODE_RXAUI) { + unsigned int bgx_fifo_size = + __cvmx_helper_bgx_fifo_size(xiface, + port); + + cvmx_pko3_mac_table[i].mac_fifo_cnt = + bgx_fifo_size / + (CVMX_BGX_TX_FIFO_SIZE / 4); + cvmx_pko3_mac_table[i].pri = 2; + cvmx_pko3_mac_table[i].spd = 10; + cvmx_pko3_mac_table[i].fifo_cnt = 2; + } else if (mode == CVMX_HELPER_INTERFACE_MODE_XLAUI) { + unsigned int bgx_fifo_size = + __cvmx_helper_bgx_fifo_size(xiface, + port); + + cvmx_pko3_mac_table[i].mac_fifo_cnt = + bgx_fifo_size / + (CVMX_BGX_TX_FIFO_SIZE / 4); + cvmx_pko3_mac_table[i].pri = 4; + cvmx_pko3_mac_table[i].spd = 40; + cvmx_pko3_mac_table[i].fifo_cnt = 4; + } else if (mode == CVMX_HELPER_INTERFACE_MODE_XAUI) { + unsigned int bgx_fifo_size = + __cvmx_helper_bgx_fifo_size(xiface, + port); + + cvmx_pko3_mac_table[i].mac_fifo_cnt = + bgx_fifo_size / + (CVMX_BGX_TX_FIFO_SIZE / 4); + cvmx_pko3_mac_table[i].pri = 3; + cvmx_pko3_mac_table[i].fifo_cnt = 4; + /* DXAUI at 20G, or XAU at 10G */ + cvmx_pko3_mac_table[i].spd = 20; + } else if (mode == CVMX_HELPER_INTERFACE_MODE_XFI) { + unsigned int bgx_fifo_size = + __cvmx_helper_bgx_fifo_size(xiface, + port); + + cvmx_pko3_mac_table[i].mac_fifo_cnt = + bgx_fifo_size / + (CVMX_BGX_TX_FIFO_SIZE / 4); + cvmx_pko3_mac_table[i].pri = 3; + cvmx_pko3_mac_table[i].fifo_cnt = 4; + cvmx_pko3_mac_table[i].spd = 10; + } else if (mode == CVMX_HELPER_INTERFACE_MODE_LOOP) { + cvmx_pko3_mac_table[i].fifo_cnt = 1; + cvmx_pko3_mac_table[i].pri = 1; + cvmx_pko3_mac_table[i].spd = 1; + cvmx_pko3_mac_table[i].mac_fifo_cnt = 1; + } else if (mode == CVMX_HELPER_INTERFACE_MODE_ILK || + mode == CVMX_HELPER_INTERFACE_MODE_SRIO) { + cvmx_pko3_mac_table[i].fifo_cnt = 4; + cvmx_pko3_mac_table[i].pri = 3; + /* ILK/SRIO: speed depends on lane count */ + cvmx_pko3_mac_table[i].spd = 40; + cvmx_pko3_mac_table[i].mac_fifo_cnt = 4; + } else if (mode == CVMX_HELPER_INTERFACE_MODE_NPI) { + cvmx_pko3_mac_table[i].fifo_cnt = 4; + cvmx_pko3_mac_table[i].pri = 2; + /* Actual speed depends on PCIe lanes/mode */ + cvmx_pko3_mac_table[i].spd = 50; + /* SLI Tx FIFO size to be revisitted */ + cvmx_pko3_mac_table[i].mac_fifo_cnt = 1; + } else { + /* Other BGX interface modes: SGMII/RGMII */ + unsigned int bgx_fifo_size = + __cvmx_helper_bgx_fifo_size(xiface, + port); + + cvmx_pko3_mac_table[i].mac_fifo_cnt = + bgx_fifo_size / + (CVMX_BGX_TX_FIFO_SIZE / 4); + cvmx_pko3_mac_table[i].fifo_cnt = 1; + cvmx_pko3_mac_table[i].pri = 1; + cvmx_pko3_mac_table[i].spd = 1; + } + + if (debug) + debug("%s: intf %d:%u port %u %s mac %02u cnt %u macfifo %uk spd %u\n", + __func__, node, interface, port, + cvmx_helper_interface_mode_to_string(mode), + i, cvmx_pko3_mac_table[i].fifo_cnt, + cvmx_pko3_mac_table[i].mac_fifo_cnt * 8, + cvmx_pko3_mac_table[i].spd); + + } /* for port */ + } /* for interface */ + + /* Count the number of requested FIFOs */ + for (fifo_count = mac_num = 0; mac_num < __cvmx_pko3_num_macs(); + mac_num++) + fifo_count += cvmx_pko3_mac_table[mac_num].fifo_cnt; + + if (debug) + debug("%s: initially requested FIFO count %u\n", __func__, + fifo_count); + + /* Heuristically trim FIFO count to fit in available number */ + pri = 1; + cnt = 4; + while (fifo_count > max_fifos) { + for (mac_num = 0; mac_num < __cvmx_pko3_num_macs(); mac_num++) { + if (cvmx_pko3_mac_table[mac_num].fifo_cnt == cnt && + cvmx_pko3_mac_table[mac_num].pri <= pri) { + cvmx_pko3_mac_table[mac_num].fifo_cnt >>= 1; + fifo_count -= + cvmx_pko3_mac_table[mac_num].fifo_cnt; + } + if (fifo_count <= max_fifos) + break; + } + if (pri >= 4) { + pri = 1; + cnt >>= 1; + } else { + pri++; + } + if (cnt == 0) + break; + } + + if (debug) + debug("%s: adjusted FIFO count %u\n", __func__, fifo_count); + + /* Special case for NULL Virtual FIFO */ + fifo_group_cfg[fifo_groups - 1] = 0; + /* there is no MAC connected to NULL FIFO */ + + /* Configure MAC units, and attach a FIFO to each */ + for (fifo = 0, cnt = 4; cnt > 0; cnt >>= 1) { + unsigned int g; + + for (mac_num = 0; mac_num < __cvmx_pko3_num_macs(); mac_num++) { + if (cvmx_pko3_mac_table[mac_num].fifo_cnt < cnt || + cvmx_pko3_mac_table[mac_num].fifo_id != 0x1f) + continue; + + /* Attach FIFO to MAC */ + cvmx_pko3_mac_table[mac_num].fifo_id = fifo; + g = fifo >> 2; + /* Sum speed for FIFO group */ + fifo_group_spd[g] += cvmx_pko3_mac_table[mac_num].spd; + + if (cnt == 4) + fifo_group_cfg[g] = 4; /* 10k,0,0,0 */ + else if (cnt == 2 && (fifo & 0x3) == 0) + fifo_group_cfg[g] = 3; /* 5k,0,5k,0 */ + else if (cnt == 2 && fifo_group_cfg[g] == 3) + /* no change */; + else if (cnt == 1 && (fifo & 0x2) && + fifo_group_cfg[g] == 3) + fifo_group_cfg[g] = 1; /* 5k,0,2.5k 2.5k*/ + else if (cnt == 1 && (fifo & 0x3) == 0x3) + /* no change */; + else if (cnt == 1) + fifo_group_cfg[g] = 0; /* 2.5k x 4 */ + else + cvmx_printf("ERROR: %s: internal error\n", + __func__); + + fifo += cnt; + } + } + + /* Check if there was no error in FIFO allocation */ + if (fifo > max_fifos) { + cvmx_printf("ERROR: %s: Internal error FIFO %u\n", __func__, + fifo); + return -1; + } + + if (debug) + debug("%s: used %u of FIFOs\n", __func__, fifo); + + /* Now configure all FIFO groups */ + for (fifo = 0; fifo < fifo_groups; fifo++) { + cvmx_pko_ptgfx_cfg_t pko_ptgfx_cfg; + + pko_ptgfx_cfg.u64 = csr_rd_node(node, CVMX_PKO_PTGFX_CFG(fifo)); + if (pko_ptgfx_cfg.s.size != fifo_group_cfg[fifo]) + pko_ptgfx_cfg.s.reset = 1; + pko_ptgfx_cfg.s.size = fifo_group_cfg[fifo]; + if (fifo_group_spd[fifo] >= 40) + if (pko_ptgfx_cfg.s.size >= 3) + pko_ptgfx_cfg.s.rate = 3; /* 50 Gbps */ + else + pko_ptgfx_cfg.s.rate = 2; /* 25 Gbps */ + else if (fifo_group_spd[fifo] >= 20) + pko_ptgfx_cfg.s.rate = 2; /* 25 Gbps */ + else if (fifo_group_spd[fifo] >= 10) + pko_ptgfx_cfg.s.rate = 1; /* 12.5 Gbps */ + else + pko_ptgfx_cfg.s.rate = 0; /* 6.25 Gbps */ + + if (debug) + debug("%s: FIFO %#x-%#x size=%u speed=%d rate=%d\n", + __func__, fifo * 4, fifo * 4 + 3, + pko_ptgfx_cfg.s.size, fifo_group_spd[fifo], + pko_ptgfx_cfg.s.rate); + + csr_wr_node(node, CVMX_PKO_PTGFX_CFG(fifo), pko_ptgfx_cfg.u64); + pko_ptgfx_cfg.s.reset = 0; + csr_wr_node(node, CVMX_PKO_PTGFX_CFG(fifo), pko_ptgfx_cfg.u64); + } + + /* Configure all MACs assigned FIFO number */ + for (mac_num = 0; mac_num < __cvmx_pko3_num_macs(); mac_num++) { + cvmx_pko_macx_cfg_t pko_mac_cfg; + + if (debug) + debug("%s: mac#%02u: fifo=%#x cnt=%u speed=%d\n", + __func__, mac_num, + cvmx_pko3_mac_table[mac_num].fifo_id, + cvmx_pko3_mac_table[mac_num].fifo_cnt, + cvmx_pko3_mac_table[mac_num].spd); + + pko_mac_cfg.u64 = csr_rd_node(node, CVMX_PKO_MACX_CFG(mac_num)); + pko_mac_cfg.s.fifo_num = cvmx_pko3_mac_table[mac_num].fifo_id; + csr_wr_node(node, CVMX_PKO_MACX_CFG(mac_num), pko_mac_cfg.u64); + } + + /* Setup PKO MCI0/MCI1/SKID credits */ + for (mac_num = 0; mac_num < __cvmx_pko3_num_macs(); mac_num++) { + cvmx_pko_mci0_max_credx_t pko_mci0_max_cred; + cvmx_pko_mci1_max_credx_t pko_mci1_max_cred; + cvmx_pko_macx_cfg_t pko_mac_cfg; + unsigned int fifo_credit, mac_credit, skid_credit; + unsigned int pko_fifo_cnt, fifo_size; + unsigned int mac_fifo_cnt; + unsigned int tmp; + int saved_fifo_num; + + pko_fifo_cnt = cvmx_pko3_mac_table[mac_num].fifo_cnt; + mac_fifo_cnt = cvmx_pko3_mac_table[mac_num].mac_fifo_cnt; + + /* Skip unused MACs */ + if (pko_fifo_cnt == 0) + continue; + + /* Check for sanity */ + if (pko_fifo_cnt > 4) + pko_fifo_cnt = 1; + + fifo_size = (2 * 1024) + (1024 / 2); /* 2.5KiB */ + fifo_credit = pko_fifo_cnt * fifo_size; + + if (mac_num == 0) { + /* loopback */ + mac_credit = 4096; /* From HRM Sec 13.0 */ + skid_credit = 0; + } else if (mac_num == 1) { + /* DPI */ + mac_credit = 2 * 1024; + skid_credit = 0; + } else if (octeon_has_feature(OCTEON_FEATURE_ILK) && + (mac_num & 0xfe) == 2) { + /* ILK0, ILK1: MAC 2,3 */ + mac_credit = 4 * 1024; /* 4KB fifo */ + skid_credit = 0; + } else if (octeon_has_feature(OCTEON_FEATURE_SRIO) && + (mac_num >= 6) && (mac_num <= 9)) { + /* SRIO0, SRIO1: MAC 6..9 */ + mac_credit = 1024 / 2; + skid_credit = 0; + } else { + /* BGX */ + mac_credit = mac_fifo_cnt * 8 * 1024; + skid_credit = mac_fifo_cnt * 256; + } + + if (debug) + debug("%s: mac %u pko_fifo_credit=%u mac_credit=%u\n", + __func__, mac_num, fifo_credit, mac_credit); + + tmp = (fifo_credit + mac_credit) / 16; + pko_mci0_max_cred.u64 = 0; + pko_mci0_max_cred.s.max_cred_lim = tmp; + + /* Check for overflow */ + if (pko_mci0_max_cred.s.max_cred_lim != tmp) { + cvmx_printf("WARNING: %s: MCI0 credit overflow\n", + __func__); + pko_mci0_max_cred.s.max_cred_lim = 0xfff; + } + + /* Pass 2 PKO hardware does not use the MCI0 credits */ + if (OCTEON_IS_MODEL(OCTEON_CN78XX_PASS1_X)) + csr_wr_node(node, CVMX_PKO_MCI0_MAX_CREDX(mac_num), + pko_mci0_max_cred.u64); + + /* The original CSR formula is the correct one after all */ + tmp = (mac_credit) / 16; + pko_mci1_max_cred.u64 = 0; + pko_mci1_max_cred.s.max_cred_lim = tmp; + + /* Check for overflow */ + if (pko_mci1_max_cred.s.max_cred_lim != tmp) { + cvmx_printf("WARNING: %s: MCI1 credit overflow\n", + __func__); + pko_mci1_max_cred.s.max_cred_lim = 0xfff; + } + + csr_wr_node(node, CVMX_PKO_MCI1_MAX_CREDX(mac_num), + pko_mci1_max_cred.u64); + + tmp = (skid_credit / 256) >> 1; /* valid 0,1,2 */ + pko_mac_cfg.u64 = csr_rd_node(node, CVMX_PKO_MACX_CFG(mac_num)); + + /* The PKO_MACX_CFG bits cannot be changed unless FIFO_MUM=0x1f (unused fifo) */ + saved_fifo_num = pko_mac_cfg.s.fifo_num; + pko_mac_cfg.s.fifo_num = 0x1f; + pko_mac_cfg.s.skid_max_cnt = tmp; + csr_wr_node(node, CVMX_PKO_MACX_CFG(mac_num), pko_mac_cfg.u64); + + pko_mac_cfg.u64 = csr_rd_node(node, CVMX_PKO_MACX_CFG(mac_num)); + pko_mac_cfg.s.fifo_num = saved_fifo_num; + csr_wr_node(node, CVMX_PKO_MACX_CFG(mac_num), pko_mac_cfg.u64); + + if (debug) { + pko_mci0_max_cred.u64 = + csr_rd_node(node, CVMX_PKO_MCI0_MAX_CREDX(mac_num)); + pko_mci1_max_cred.u64 = + csr_rd_node(node, CVMX_PKO_MCI1_MAX_CREDX(mac_num)); + pko_mac_cfg.u64 = + csr_rd_node(node, CVMX_PKO_MACX_CFG(mac_num)); + debug("%s: mac %u PKO_MCI0_MAX_CREDX=%u PKO_MCI1_MAX_CREDX=%u PKO_MACX_CFG[SKID_MAX_CNT]=%u\n", + __func__, mac_num, + pko_mci0_max_cred.s.max_cred_lim, + pko_mci1_max_cred.s.max_cred_lim, + pko_mac_cfg.s.skid_max_cnt); + } + } /* for mac_num */ + + return 0; +} + +/** Set MAC options + * + * The options supported are the parameters below: + * + * @param xiface The physical interface number + * @param index The physical sub-interface port + * @param fcs_enable Enable FCS generation + * @param pad_enable Enable padding to minimum packet size + * @param fcs_sop_off Number of bytes at start of packet to exclude from FCS + * + * The typical use for `fcs_sop_off` is when the interface is configured + * to use a header such as HighGig to precede every Ethernet packet, + * such a header usually does not partake in the CRC32 computation stream, + * and its size must be set with this parameter. + * + * @return Returns 0 on success, -1 if interface/port is invalid. + */ +int cvmx_pko3_interface_options(int xiface, int index, bool fcs_enable, + bool pad_enable, unsigned int fcs_sop_off) +{ + int mac_num; + cvmx_pko_macx_cfg_t pko_mac_cfg; + unsigned int fifo_num; + struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); + + if (debug) + debug("%s: intf %u:%u/%u fcs=%d pad=%d\n", __func__, xi.node, + xi.interface, index, fcs_enable, pad_enable); + + mac_num = __cvmx_pko3_get_mac_num(xiface, index); + if (mac_num < 0) { + cvmx_printf("ERROR: %s: invalid interface %u:%u/%u\n", __func__, + xi.node, xi.interface, index); + return -1; + } + + pko_mac_cfg.u64 = csr_rd_node(xi.node, CVMX_PKO_MACX_CFG(mac_num)); + + /* If MAC is not assigned, return an error */ + if (pko_mac_cfg.s.fifo_num == 0x1f) { + cvmx_printf("ERROR: %s: unused interface %u:%u/%u\n", __func__, + xi.node, xi.interface, index); + return -1; + } + + if (pko_mac_cfg.s.min_pad_ena == pad_enable && + pko_mac_cfg.s.fcs_ena == fcs_enable) { + if (debug) + debug("%s: mac %#x unchanged\n", __func__, mac_num); + return 0; + } + + /* WORKAROUND: Pass1 won't allow change any bits unless FIFO_NUM=0x1f */ + fifo_num = pko_mac_cfg.s.fifo_num; + pko_mac_cfg.s.fifo_num = 0x1f; + + pko_mac_cfg.s.min_pad_ena = pad_enable; + pko_mac_cfg.s.fcs_ena = fcs_enable; + pko_mac_cfg.s.fcs_sop_off = fcs_sop_off; + + csr_wr_node(xi.node, CVMX_PKO_MACX_CFG(mac_num), pko_mac_cfg.u64); + + pko_mac_cfg.s.fifo_num = fifo_num; + csr_wr_node(xi.node, CVMX_PKO_MACX_CFG(mac_num), pko_mac_cfg.u64); + + if (debug) + debug("%s: PKO_MAC[%u]CFG=%#llx\n", __func__, mac_num, + (unsigned long long)csr_rd_node(xi.node, CVMX_PKO_MACX_CFG(mac_num))); + + return 0; +} + +/** Set Descriptor Queue options + * + * The `min_pad` parameter must be in agreement with the interface-level + * padding option for all descriptor queues assigned to that particular + * interface/port. + * + * @param node on which to operate + * @param dq descriptor queue to set + * @param min_pad minimum padding to set for dq + */ +void cvmx_pko3_dq_options(unsigned int node, unsigned int dq, bool min_pad) +{ + cvmx_pko_pdm_dqx_minpad_t reg; + + dq &= (1 << 10) - 1; + reg.u64 = csr_rd_node(node, CVMX_PKO_PDM_DQX_MINPAD(dq)); + reg.s.minpad = min_pad; + csr_wr_node(node, CVMX_PKO_PDM_DQX_MINPAD(dq), reg.u64); +} diff --git a/arch/mips/mach-octeon/cvmx-qlm-tables.c b/arch/mips/mach-octeon/cvmx-qlm-tables.c new file mode 100644 index 0000000..ca22892 --- /dev/null +++ b/arch/mips/mach-octeon/cvmx-qlm-tables.c @@ -0,0 +1,292 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018-2022 Marvell International Ltd. + */ + +#include <mach/cvmx-regs.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> + +const __cvmx_qlm_jtag_field_t __cvmx_qlm_jtag_field_cn63xx[] = { + { "prbs_err_cnt", 299, 252 }, // prbs_err_cnt[47..0] + { "prbs_lock", 251, 251 }, // prbs_lock + { "jtg_prbs_rst_n", 250, 250 }, // jtg_prbs_rst_n + { "jtg_run_prbs31", 249, 249 }, // jtg_run_prbs31 + { "jtg_run_prbs7", 248, 248 }, // jtg_run_prbs7 + { "Unused1", 247, 245 }, // 0 + { "cfg_pwrup_set", 244, 244 }, // cfg_pwrup_set + { "cfg_pwrup_clr", 243, 243 }, // cfg_pwrup_clr + { "cfg_rst_n_set", 242, 242 }, // cfg_rst_n_set + { "cfg_rst_n_clr", 241, 241 }, // cfg_rst_n_clr + { "cfg_tx_idle_set", 240, 240 }, // cfg_tx_idle_set + { "cfg_tx_idle_clr", 239, 239 }, // cfg_tx_idle_clr + { "cfg_tx_byp", 238, 238 }, // cfg_tx_byp + { "cfg_tx_byp_inv", 237, 237 }, // cfg_tx_byp_inv + { "cfg_tx_byp_val", 236, 227 }, // cfg_tx_byp_val[9..0] + { "cfg_loopback", 226, 226 }, // cfg_loopback + { "shlpbck", 225, 224 }, // shlpbck[1..0] + { "sl_enable", 223, 223 }, // sl_enable + { "sl_posedge_sample", 222, 222 }, // sl_posedge_sample + { "trimen", 221, 220 }, // trimen[1..0] + { "serdes_tx_byp", 219, 219 }, // serdes_tx_byp + { "serdes_pll_byp", 218, 218 }, // serdes_pll_byp + { "lowf_byp", 217, 217 }, // lowf_byp + { "spdsel_byp", 216, 216 }, // spdsel_byp + { "div4_byp", 215, 215 }, // div4_byp + { "clkf_byp", 214, 208 }, // clkf_byp[6..0] + { "Unused2", 207, 206 }, // 0 + { "biasdrv_hs_ls_byp", 205, 201 }, // biasdrv_hs_ls_byp[4..0] + { "tcoeff_hf_ls_byp", 200, 197 }, // tcoeff_hf_ls_byp[3..0] + { "biasdrv_hf_byp", 196, 192 }, // biasdrv_hf_byp[4..0] + { "tcoeff_hf_byp", 191, 188 }, // tcoeff_hf_byp[3..0] + { "Unused3", 187, 186 }, // 0 + { "biasdrv_lf_ls_byp", 185, 181 }, // biasdrv_lf_ls_byp[4..0] + { "tcoeff_lf_ls_byp", 180, 177 }, // tcoeff_lf_ls_byp[3..0] + { "biasdrv_lf_byp", 176, 172 }, // biasdrv_lf_byp[4..0] + { "tcoeff_lf_byp", 171, 168 }, // tcoeff_lf_byp[3..0] + { "Unused4", 167, 167 }, // 0 + { "interpbw", 166, 162 }, // interpbw[4..0] + { "pll_cpb", 161, 159 }, // pll_cpb[2..0] + { "pll_cps", 158, 156 }, // pll_cps[2..0] + { "pll_diffamp", 155, 152 }, // pll_diffamp[3..0] + { "Unused5", 151, 150 }, // 0 + { "cfg_rx_idle_set", 149, 149 }, // cfg_rx_idle_set + { "cfg_rx_idle_clr", 148, 148 }, // cfg_rx_idle_clr + { "cfg_rx_idle_thr", 147, 144 }, // cfg_rx_idle_thr[3..0] + { "cfg_com_thr", 143, 140 }, // cfg_com_thr[3..0] + { "cfg_rx_offset", 139, 136 }, // cfg_rx_offset[3..0] + { "cfg_skp_max", 135, 132 }, // cfg_skp_max[3..0] + { "cfg_skp_min", 131, 128 }, // cfg_skp_min[3..0] + { "cfg_fast_pwrup", 127, 127 }, // cfg_fast_pwrup + { "Unused6", 126, 100 }, // 0 + { "detected_n", 99, 99 }, // detected_n + { "detected_p", 98, 98 }, // detected_p + { "dbg_res_rx", 97, 94 }, // dbg_res_rx[3..0] + { "dbg_res_tx", 93, 90 }, // dbg_res_tx[3..0] + { "cfg_tx_pol_set", 89, 89 }, // cfg_tx_pol_set + { "cfg_tx_pol_clr", 88, 88 }, // cfg_tx_pol_clr + { "cfg_rx_pol_set", 87, 87 }, // cfg_rx_pol_set + { "cfg_rx_pol_clr", 86, 86 }, // cfg_rx_pol_clr + { "cfg_rxd_set", 85, 85 }, // cfg_rxd_set + { "cfg_rxd_clr", 84, 84 }, // cfg_rxd_clr + { "cfg_rxd_wait", 83, 80 }, // cfg_rxd_wait[3..0] + { "cfg_cdr_limit", 79, 79 }, // cfg_cdr_limit + { "cfg_cdr_rotate", 78, 78 }, // cfg_cdr_rotate + { "cfg_cdr_bw_ctl", 77, 76 }, // cfg_cdr_bw_ctl[1..0] + { "cfg_cdr_trunc", 75, 74 }, // cfg_cdr_trunc[1..0] + { "cfg_cdr_rqoffs", 73, 64 }, // cfg_cdr_rqoffs[9..0] + { "cfg_cdr_inc2", 63, 58 }, // cfg_cdr_inc2[5..0] + { "cfg_cdr_inc1", 57, 52 }, // cfg_cdr_inc1[5..0] + { "fusopt_voter_sync", 51, 51 }, // fusopt_voter_sync + { "rndt", 50, 50 }, // rndt + { "hcya", 49, 49 }, // hcya + { "hyst", 48, 48 }, // hyst + { "idle_dac", 47, 45 }, // idle_dac[2..0] + { "bg_ref_sel", 44, 44 }, // bg_ref_sel + { "ic50dac", 43, 39 }, // ic50dac[4..0] + { "ir50dac", 38, 34 }, // ir50dac[4..0] + { "tx_rout_comp_bypass", 33, 33 }, // tx_rout_comp_bypass + { "tx_rout_comp_value", 32, 29 }, // tx_rout_comp_value[3..0] + { "tx_res_offset", 28, 25 }, // tx_res_offset[3..0] + { "rx_rout_comp_bypass", 24, 24 }, // rx_rout_comp_bypass + { "rx_rout_comp_value", 23, 20 }, // rx_rout_comp_value[3..0] + { "rx_res_offset", 19, 16 }, // rx_res_offset[3..0] + { "rx_cap_gen2", 15, 12 }, // rx_cap_gen2[3..0] + { "rx_eq_gen2", 11, 8 }, // rx_eq_gen2[3..0] + { "rx_cap_gen1", 7, 4 }, // rx_cap_gen1[3..0] + { "rx_eq_gen1", 3, 0 }, // rx_eq_gen1[3..0] + { NULL, -1, -1 } +}; + +const __cvmx_qlm_jtag_field_t __cvmx_qlm_jtag_field_cn66xx[] = { + { "prbs_err_cnt", 303, 256 }, // prbs_err_cnt[47..0] + { "prbs_lock", 255, 255 }, // prbs_lock + { "jtg_prbs_rx_rst_n", 254, 254 }, // jtg_prbs_rx_rst_n + { "jtg_prbs_tx_rst_n", 253, 253 }, // jtg_prbs_tx_rst_n + { "jtg_prbs_mode", 252, 251 }, // jtg_prbs_mode[252:251] + { "jtg_prbs_rst_n", 250, 250 }, // jtg_prbs_rst_n + { "jtg_run_prbs31", 249, + 249 }, // jtg_run_prbs31 - Use jtg_prbs_mode instead + { "jtg_run_prbs7", 248, + 248 }, // jtg_run_prbs7 - Use jtg_prbs_mode instead + { "Unused1", 247, 246 }, // 0 + { "div5_byp", 245, 245 }, // div5_byp + { "cfg_pwrup_set", 244, 244 }, // cfg_pwrup_set + { "cfg_pwrup_clr", 243, 243 }, // cfg_pwrup_clr + { "cfg_rst_n_set", 242, 242 }, // cfg_rst_n_set + { "cfg_rst_n_clr", 241, 241 }, // cfg_rst_n_clr + { "cfg_tx_idle_set", 240, 240 }, // cfg_tx_idle_set + { "cfg_tx_idle_clr", 239, 239 }, // cfg_tx_idle_clr + { "cfg_tx_byp", 238, 238 }, // cfg_tx_byp + { "cfg_tx_byp_inv", 237, 237 }, // cfg_tx_byp_inv + { "cfg_tx_byp_val", 236, 227 }, // cfg_tx_byp_val[9..0] + { "cfg_loopback", 226, 226 }, // cfg_loopback + { "shlpbck", 225, 224 }, // shlpbck[1..0] + { "sl_enable", 223, 223 }, // sl_enable + { "sl_posedge_sample", 222, 222 }, // sl_posedge_sample + { "trimen", 221, 220 }, // trimen[1..0] + { "serdes_tx_byp", 219, 219 }, // serdes_tx_byp + { "serdes_pll_byp", 218, 218 }, // serdes_pll_byp + { "lowf_byp", 217, 217 }, // lowf_byp + { "spdsel_byp", 216, 216 }, // spdsel_byp + { "div4_byp", 215, 215 }, // div4_byp + { "clkf_byp", 214, 208 }, // clkf_byp[6..0] + { "biasdrv_hs_ls_byp", 207, 203 }, // biasdrv_hs_ls_byp[4..0] + { "tcoeff_hf_ls_byp", 202, 198 }, // tcoeff_hf_ls_byp[4..0] + { "biasdrv_hf_byp", 197, 193 }, // biasdrv_hf_byp[4..0] + { "tcoeff_hf_byp", 192, 188 }, // tcoeff_hf_byp[4..0] + { "biasdrv_lf_ls_byp", 187, 183 }, // biasdrv_lf_ls_byp[4..0] + { "tcoeff_lf_ls_byp", 182, 178 }, // tcoeff_lf_ls_byp[4..0] + { "biasdrv_lf_byp", 177, 173 }, // biasdrv_lf_byp[4..0] + { "tcoeff_lf_byp", 172, 168 }, // tcoeff_lf_byp[4..0] + { "Unused4", 167, 167 }, // 0 + { "interpbw", 166, 162 }, // interpbw[4..0] + { "pll_cpb", 161, 159 }, // pll_cpb[2..0] + { "pll_cps", 158, 156 }, // pll_cps[2..0] + { "pll_diffamp", 155, 152 }, // pll_diffamp[3..0] + { "cfg_err_thr", 151, 150 }, // cfg_err_thr + { "cfg_rx_idle_set", 149, 149 }, // cfg_rx_idle_set + { "cfg_rx_idle_clr", 148, 148 }, // cfg_rx_idle_clr + { "cfg_rx_idle_thr", 147, 144 }, // cfg_rx_idle_thr[3..0] + { "cfg_com_thr", 143, 140 }, // cfg_com_thr[3..0] + { "cfg_rx_offset", 139, 136 }, // cfg_rx_offset[3..0] + { "cfg_skp_max", 135, 132 }, // cfg_skp_max[3..0] + { "cfg_skp_min", 131, 128 }, // cfg_skp_min[3..0] + { "cfg_fast_pwrup", 127, 127 }, // cfg_fast_pwrup + { "Unused6", 126, 101 }, // 0 + { "cfg_indep_dis", 100, 100 }, // cfg_indep_dis + { "detected_n", 99, 99 }, // detected_n + { "detected_p", 98, 98 }, // detected_p + { "dbg_res_rx", 97, 94 }, // dbg_res_rx[3..0] + { "dbg_res_tx", 93, 90 }, // dbg_res_tx[3..0] + { "cfg_tx_pol_set", 89, 89 }, // cfg_tx_pol_set + { "cfg_tx_pol_clr", 88, 88 }, // cfg_tx_pol_clr + { "cfg_rx_pol_set", 87, 87 }, // cfg_rx_pol_set + { "cfg_rx_pol_clr", 86, 86 }, // cfg_rx_pol_clr + { "cfg_rxd_set", 85, 85 }, // cfg_rxd_set + { "cfg_rxd_clr", 84, 84 }, // cfg_rxd_clr + { "cfg_rxd_wait", 83, 80 }, // cfg_rxd_wait[3..0] + { "cfg_cdr_limit", 79, 79 }, // cfg_cdr_limit + { "cfg_cdr_rotate", 78, 78 }, // cfg_cdr_rotate + { "cfg_cdr_bw_ctl", 77, 76 }, // cfg_cdr_bw_ctl[1..0] + { "cfg_cdr_trunc", 75, 74 }, // cfg_cdr_trunc[1..0] + { "cfg_cdr_rqoffs", 73, 64 }, // cfg_cdr_rqoffs[9..0] + { "cfg_cdr_inc2", 63, 58 }, // cfg_cdr_inc2[5..0] + { "cfg_cdr_inc1", 57, 52 }, // cfg_cdr_inc1[5..0] + { "fusopt_voter_sync", 51, 51 }, // fusopt_voter_sync + { "rndt", 50, 50 }, // rndt + { "hcya", 49, 49 }, // hcya + { "hyst", 48, 48 }, // hyst + { "idle_dac", 47, 45 }, // idle_dac[2..0] + { "bg_ref_sel", 44, 44 }, // bg_ref_sel + { "ic50dac", 43, 39 }, // ic50dac[4..0] + { "ir50dac", 38, 34 }, // ir50dac[4..0] + { "tx_rout_comp_bypass", 33, 33 }, // tx_rout_comp_bypass + { "tx_rout_comp_value", 32, 29 }, // tx_rout_comp_value[3..0] + { "tx_res_offset", 28, 25 }, // tx_res_offset[3..0] + { "rx_rout_comp_bypass", 24, 24 }, // rx_rout_comp_bypass + { "rx_rout_comp_value", 23, 20 }, // rx_rout_comp_value[3..0] + { "rx_res_offset", 19, 16 }, // rx_res_offset[3..0] + { "rx_cap_gen2", 15, 12 }, // rx_cap_gen2[3..0] + { "rx_eq_gen2", 11, 8 }, // rx_eq_gen2[3..0] + { "rx_cap_gen1", 7, 4 }, // rx_cap_gen1[3..0] + { "rx_eq_gen1", 3, 0 }, // rx_eq_gen1[3..0] + { NULL, -1, -1 } +}; + +const __cvmx_qlm_jtag_field_t __cvmx_qlm_jtag_field_cn68xx[] = { + { "prbs_err_cnt", 303, 256 }, // prbs_err_cnt[47..0] + { "prbs_lock", 255, 255 }, // prbs_lock + { "jtg_prbs_rx_rst_n", 254, 254 }, // jtg_prbs_rx_rst_n + { "jtg_prbs_tx_rst_n", 253, 253 }, // jtg_prbs_tx_rst_n + { "jtg_prbs_mode", 252, 251 }, // jtg_prbs_mode[252:251] + { "jtg_prbs_rst_n", 250, 250 }, // jtg_prbs_rst_n + { "jtg_run_prbs31", 249, + 249 }, // jtg_run_prbs31 - Use jtg_prbs_mode instead + { "jtg_run_prbs7", 248, + 248 }, // jtg_run_prbs7 - Use jtg_prbs_mode instead + { "Unused1", 247, 245 }, // 0 + { "cfg_pwrup_set", 244, 244 }, // cfg_pwrup_set + { "cfg_pwrup_clr", 243, 243 }, // cfg_pwrup_clr + { "cfg_rst_n_set", 242, 242 }, // cfg_rst_n_set + { "cfg_rst_n_clr", 241, 241 }, // cfg_rst_n_clr + { "cfg_tx_idle_set", 240, 240 }, // cfg_tx_idle_set + { "cfg_tx_idle_clr", 239, 239 }, // cfg_tx_idle_clr + { "cfg_tx_byp", 238, 238 }, // cfg_tx_byp + { "cfg_tx_byp_inv", 237, 237 }, // cfg_tx_byp_inv + { "cfg_tx_byp_val", 236, 227 }, // cfg_tx_byp_val[9..0] + { "cfg_loopback", 226, 226 }, // cfg_loopback + { "shlpbck", 225, 224 }, // shlpbck[1..0] + { "sl_enable", 223, 223 }, // sl_enable + { "sl_posedge_sample", 222, 222 }, // sl_posedge_sample + { "trimen", 221, 220 }, // trimen[1..0] + { "serdes_tx_byp", 219, 219 }, // serdes_tx_byp + { "serdes_pll_byp", 218, 218 }, // serdes_pll_byp + { "lowf_byp", 217, 217 }, // lowf_byp + { "spdsel_byp", 216, 216 }, // spdsel_byp + { "div4_byp", 215, 215 }, // div4_byp + { "clkf_byp", 214, 208 }, // clkf_byp[6..0] + { "biasdrv_hs_ls_byp", 207, 203 }, // biasdrv_hs_ls_byp[4..0] + { "tcoeff_hf_ls_byp", 202, 198 }, // tcoeff_hf_ls_byp[4..0] + { "biasdrv_hf_byp", 197, 193 }, // biasdrv_hf_byp[4..0] + { "tcoeff_hf_byp", 192, 188 }, // tcoeff_hf_byp[4..0] + { "biasdrv_lf_ls_byp", 187, 183 }, // biasdrv_lf_ls_byp[4..0] + { "tcoeff_lf_ls_byp", 182, 178 }, // tcoeff_lf_ls_byp[4..0] + { "biasdrv_lf_byp", 177, 173 }, // biasdrv_lf_byp[4..0] + { "tcoeff_lf_byp", 172, 168 }, // tcoeff_lf_byp[4..0] + { "Unused4", 167, 167 }, // 0 + { "interpbw", 166, 162 }, // interpbw[4..0] + { "pll_cpb", 161, 159 }, // pll_cpb[2..0] + { "pll_cps", 158, 156 }, // pll_cps[2..0] + { "pll_diffamp", 155, 152 }, // pll_diffamp[3..0] + { "cfg_err_thr", 151, 150 }, // cfg_err_thr + { "cfg_rx_idle_set", 149, 149 }, // cfg_rx_idle_set + { "cfg_rx_idle_clr", 148, 148 }, // cfg_rx_idle_clr + { "cfg_rx_idle_thr", 147, 144 }, // cfg_rx_idle_thr[3..0] + { "cfg_com_thr", 143, 140 }, // cfg_com_thr[3..0] + { "cfg_rx_offset", 139, 136 }, // cfg_rx_offset[3..0] + { "cfg_skp_max", 135, 132 }, // cfg_skp_max[3..0] + { "cfg_skp_min", 131, 128 }, // cfg_skp_min[3..0] + { "cfg_fast_pwrup", 127, 127 }, // cfg_fast_pwrup + { "Unused6", 126, 100 }, // 0 + { "detected_n", 99, 99 }, // detected_n + { "detected_p", 98, 98 }, // detected_p + { "dbg_res_rx", 97, 94 }, // dbg_res_rx[3..0] + { "dbg_res_tx", 93, 90 }, // dbg_res_tx[3..0] + { "cfg_tx_pol_set", 89, 89 }, // cfg_tx_pol_set + { "cfg_tx_pol_clr", 88, 88 }, // cfg_tx_pol_clr + { "cfg_rx_pol_set", 87, 87 }, // cfg_rx_pol_set + { "cfg_rx_pol_clr", 86, 86 }, // cfg_rx_pol_clr + { "cfg_rxd_set", 85, 85 }, // cfg_rxd_set + { "cfg_rxd_clr", 84, 84 }, // cfg_rxd_clr + { "cfg_rxd_wait", 83, 80 }, // cfg_rxd_wait[3..0] + { "cfg_cdr_limit", 79, 79 }, // cfg_cdr_limit + { "cfg_cdr_rotate", 78, 78 }, // cfg_cdr_rotate + { "cfg_cdr_bw_ctl", 77, 76 }, // cfg_cdr_bw_ctl[1..0] + { "cfg_cdr_trunc", 75, 74 }, // cfg_cdr_trunc[1..0] + { "cfg_cdr_rqoffs", 73, 64 }, // cfg_cdr_rqoffs[9..0] + { "cfg_cdr_inc2", 63, 58 }, // cfg_cdr_inc2[5..0] + { "cfg_cdr_inc1", 57, 52 }, // cfg_cdr_inc1[5..0] + { "fusopt_voter_sync", 51, 51 }, // fusopt_voter_sync + { "rndt", 50, 50 }, // rndt + { "hcya", 49, 49 }, // hcya + { "hyst", 48, 48 }, // hyst + { "idle_dac", 47, 45 }, // idle_dac[2..0] + { "bg_ref_sel", 44, 44 }, // bg_ref_sel + { "ic50dac", 43, 39 }, // ic50dac[4..0] + { "ir50dac", 38, 34 }, // ir50dac[4..0] + { "tx_rout_comp_bypass", 33, 33 }, // tx_rout_comp_bypass + { "tx_rout_comp_value", 32, 29 }, // tx_rout_comp_value[3..0] + { "tx_res_offset", 28, 25 }, // tx_res_offset[3..0] + { "rx_rout_comp_bypass", 24, 24 }, // rx_rout_comp_bypass + { "rx_rout_comp_value", 23, 20 }, // rx_rout_comp_value[3..0] + { "rx_res_offset", 19, 16 }, // rx_res_offset[3..0] + { "rx_cap_gen2", 15, 12 }, // rx_cap_gen2[3..0] + { "rx_eq_gen2", 11, 8 }, // rx_eq_gen2[3..0] + { "rx_cap_gen1", 7, 4 }, // rx_cap_gen1[3..0] + { "rx_eq_gen1", 3, 0 }, // rx_eq_gen1[3..0] + { NULL, -1, -1 } +}; diff --git a/arch/mips/mach-octeon/cvmx-range.c b/arch/mips/mach-octeon/cvmx-range.c new file mode 100644 index 0000000..2ac20ea --- /dev/null +++ b/arch/mips/mach-octeon/cvmx-range.c @@ -0,0 +1,259 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018-2022 Marvell International Ltd. + */ + +#include <log.h> +#include <time.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-global-resources.h> + +#include <mach/cvmx-pki.h> +#include <mach/cvmx-helper.h> +#include <mach/cvmx-helper-board.h> +#include <mach/cvmx-helper-cfg.h> + +#include <mach/cvmx-range.h> + +#define CVMX_RANGE_AVAILABLE ((u64)-88) +#define addr_of_element(base, index) \ + (1ull << 63 | ((base) + sizeof(u64) + (index) * sizeof(u64))) +#define addr_of_size(base) (1ull << 63 | (base)) + +static const int debug; + +int cvmx_range_memory_size(int nelements) +{ + return sizeof(u64) * (nelements + 1); +} + +int cvmx_range_init(u64 range_addr, int size) +{ + u64 lsize = size; + u64 i; + + cvmx_write64_uint64(addr_of_size(range_addr), lsize); + for (i = 0; i < lsize; i++) { + cvmx_write64_uint64(addr_of_element(range_addr, i), + CVMX_RANGE_AVAILABLE); + } + return 0; +} + +static int64_t cvmx_range_find_next_available(u64 range_addr, u64 index, + int align) +{ + u64 size = cvmx_read64_uint64(addr_of_size(range_addr)); + u64 i; + + while ((index % align) != 0) + index++; + + for (i = index; i < size; i += align) { + u64 r_owner = cvmx_read64_uint64(addr_of_element(range_addr, i)); + + if (debug) + debug("%s: index=%d owner=%llx\n", __func__, (int)i, + (unsigned long long)r_owner); + if (r_owner == CVMX_RANGE_AVAILABLE) + return i; + } + return -1; +} + +static int64_t cvmx_range_find_last_available(u64 range_addr, u64 index, + u64 align) +{ + u64 size = cvmx_read64_uint64(addr_of_size(range_addr)); + u64 i; + + if (index == 0) + index = size - 1; + + while ((index % align) != 0) + index++; + + for (i = index; i > align; i -= align) { + u64 r_owner = cvmx_read64_uint64(addr_of_element(range_addr, i)); + + if (debug) + debug("%s: index=%d owner=%llx\n", __func__, (int)i, + (unsigned long long)r_owner); + if (r_owner == CVMX_RANGE_AVAILABLE) + return i; + } + return -1; +} + +int cvmx_range_alloc_ordered(u64 range_addr, u64 owner, u64 cnt, + int align, int reverse) +{ + u64 i = 0, size; + s64 first_available; + + if (debug) + debug("%s: range_addr=%llx owner=%llx cnt=%d\n", __func__, + (unsigned long long)range_addr, + (unsigned long long)owner, (int)cnt); + + size = cvmx_read64_uint64(addr_of_size(range_addr)); + while (i < size) { + u64 available_cnt = 0; + + if (reverse) + first_available = cvmx_range_find_last_available(range_addr, i, align); + else + first_available = cvmx_range_find_next_available(range_addr, i, align); + if (first_available == -1) + return -1; + i = first_available; + + if (debug) + debug("%s: first_available=%d\n", __func__, (int)first_available); + while ((available_cnt != cnt) && (i < size)) { + u64 r_owner = cvmx_read64_uint64(addr_of_element(range_addr, i)); + + if (r_owner == CVMX_RANGE_AVAILABLE) + available_cnt++; + i++; + } + if (available_cnt == cnt) { + u64 j; + + if (debug) + debug("%s: first_available=%d available=%d\n", + __func__, + (int)first_available, (int)available_cnt); + + for (j = first_available; j < first_available + cnt; + j++) { + u64 a = addr_of_element(range_addr, j); + + cvmx_write64_uint64(a, owner); + } + return first_available; + } + } + + if (debug) { + debug("ERROR: %s: failed to allocate range cnt=%d\n", + __func__, (int)cnt); + cvmx_range_show(range_addr); + } + + return -1; +} + +int cvmx_range_alloc(u64 range_addr, u64 owner, u64 cnt, int align) +{ + return cvmx_range_alloc_ordered(range_addr, owner, cnt, align, 0); +} + +int cvmx_range_reserve(u64 range_addr, u64 owner, u64 base, + u64 cnt) +{ + u64 i, size, r_owner; + u64 up = base + cnt; + + size = cvmx_read64_uint64(addr_of_size(range_addr)); + if (up > size) { + debug("ERROR: %s: invalid base or cnt. range_addr=0x%llx, owner=0x%llx, size=%d base+cnt=%d\n", + __func__, (unsigned long long)range_addr, + (unsigned long long)owner, + (int)size, (int)up); + return -1; + } + for (i = base; i < up; i++) { + r_owner = cvmx_read64_uint64(addr_of_element(range_addr, i)); + if (debug) + debug("%s: %d: %llx\n", + __func__, (int)i, (unsigned long long)r_owner); + if (r_owner != CVMX_RANGE_AVAILABLE) { + if (debug) { + debug("%s: resource already reserved base+cnt=%d %llu %llu %llx %llx %llx\n", + __func__, (int)i, (unsigned long long)cnt, + (unsigned long long)base, + (unsigned long long)r_owner, + (unsigned long long)range_addr, + (unsigned long long)owner); + } + return -1; + } + } + for (i = base; i < up; i++) + cvmx_write64_uint64(addr_of_element(range_addr, i), owner); + return base; +} + +int __cvmx_range_is_allocated(u64 range_addr, int bases[], int count) +{ + u64 i, cnt, size; + u64 r_owner; + + cnt = count; + size = cvmx_read64_uint64(addr_of_size(range_addr)); + for (i = 0; i < cnt; i++) { + u64 base = bases[i]; + + if (base >= size) { + debug("ERROR: %s: invalid base or cnt size=%d base=%d\n", + __func__, (int)size, (int)base); + return 0; + } + r_owner = cvmx_read64_uint64(addr_of_element(range_addr, base)); + if (r_owner == CVMX_RANGE_AVAILABLE) { + if (debug) { + debug("%s: i=%d:base=%d is available\n", + __func__, (int)i, (int)base); + } + return 0; + } + } + return 1; +} + +int cvmx_range_free_mutiple(u64 range_addr, int bases[], int count) +{ + u64 i, cnt; + + cnt = count; + if (__cvmx_range_is_allocated(range_addr, bases, count) != 1) + return -1; + for (i = 0; i < cnt; i++) { + u64 base = bases[i]; + + cvmx_write64_uint64(addr_of_element(range_addr, base), + CVMX_RANGE_AVAILABLE); + } + return 0; +} + +int cvmx_range_free_with_base(u64 range_addr, int base, int cnt) +{ + u64 i, size; + u64 up = base + cnt; + + size = cvmx_read64_uint64(addr_of_size(range_addr)); + if (up > size) { + debug("ERROR: %s: invalid base or cnt size=%d base+cnt=%d\n", + __func__, (int)size, (int)up); + return -1; + } + for (i = base; i < up; i++) { + cvmx_write64_uint64(addr_of_element(range_addr, i), + CVMX_RANGE_AVAILABLE); + } + return 0; +} diff --git a/arch/mips/mach-octeon/include/mach/cvmx-agl.h b/arch/mips/mach-octeon/include/mach/cvmx-agl.h new file mode 100644 index 0000000..4afb3a4 --- /dev/null +++ b/arch/mips/mach-octeon/include/mach/cvmx-agl.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018-2022 Marvell International Ltd. + * + * Functions for AGL (RGMII) commong initialization, configuration. + */ + +#ifndef __CVMX_AGL_H__ +#define __CVMX_AGL_H__ + +/* + * @param port to enable + * + * @return Zero on success, negative on failure + */ +int cvmx_agl_enable(int port); + +cvmx_helper_link_info_t cvmx_agl_link_get(int port); + +/* + * Set MII/RGMII link based on mode. + * + * @param port interface port to set the link. + * @param link_info Link status + * + * @return 0 on success and 1 on failure + */ +int cvmx_agl_link_set(int port, cvmx_helper_link_info_t link_info); + +/** + * Disables the sending of flow control (pause) frames on the specified + * AGL (RGMII) port(s). + * + * @param interface Which interface (0 or 1) + * @param port_mask Mask (4bits) of which ports on the interface to disable + * backpressure on. + * 1 => disable backpressure + * 0 => enable backpressure + * + * @return 0 on success + * -1 on error + */ +int cvmx_agl_set_backpressure_override(u32 interface, uint32_t port_mask); + +#endif /* __CVMX_AGL_H__ */ diff --git a/arch/mips/mach-octeon/include/mach/cvmx-bootmem.h b/arch/mips/mach-octeon/include/mach/cvmx-bootmem.h index 283ac5c..d5c004d 100644 --- a/arch/mips/mach-octeon/include/mach/cvmx-bootmem.h +++ b/arch/mips/mach-octeon/include/mach/cvmx-bootmem.h @@ -26,7 +26,8 @@ /* Real physical addresses of memory regions */ #define OCTEON_DDR0_BASE (0x0ULL) -#define OCTEON_DDR0_SIZE (0x010000000ULL) +/* Use 16MiB here, as 256 leads to overwriting U-Boot reloc space */ +#define OCTEON_DDR0_SIZE (0x001000000ULL) #define OCTEON_DDR1_BASE ((OCTEON_IS_OCTEON2() || OCTEON_IS_OCTEON3()) \ ? 0x20000000ULL : 0x410000000ULL) #define OCTEON_DDR1_SIZE (0x010000000ULL) diff --git a/arch/mips/mach-octeon/include/mach/cvmx-config.h b/arch/mips/mach-octeon/include/mach/cvmx-config.h new file mode 100644 index 0000000..4f66a3c --- /dev/null +++ b/arch/mips/mach-octeon/include/mach/cvmx-config.h @@ -0,0 +1,128 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018-2022 Marvell International Ltd. + */ + +#ifndef __CVMX_CONFIG_H__ +#define __CVMX_CONFIG_H__ + +/************************* Config Specific Defines ************************/ +#define CVMX_LLM_NUM_PORTS 1 + +/**< PKO queues per port for interface 0 (ports 0-15) */ +#define CVMX_PKO_QUEUES_PER_PORT_INTERFACE0 1 + +/**< PKO queues per port for interface 1 (ports 16-31) */ +#define CVMX_PKO_QUEUES_PER_PORT_INTERFACE1 1 + +/**< PKO queues per port for interface 4 (AGL) */ +#define CVMX_PKO_QUEUES_PER_PORT_INTERFACE4 1 + +/**< Limit on the number of PKO ports enabled for interface 0 */ +#define CVMX_PKO_MAX_PORTS_INTERFACE0 CVMX_HELPER_PKO_MAX_PORTS_INTERFACE0 + +/**< Limit on the number of PKO ports enabled for interface 1 */ +#define CVMX_PKO_MAX_PORTS_INTERFACE1 CVMX_HELPER_PKO_MAX_PORTS_INTERFACE1 + +/**< PKO queues per port for PCI (ports 32-35) */ +#define CVMX_PKO_QUEUES_PER_PORT_PCI 1 + +/**< PKO queues per port for Loop devices (ports 36-39) */ +#define CVMX_PKO_QUEUES_PER_PORT_LOOP 1 + +/**< PKO queues per port for SRIO0 devices (ports 40-41) */ +#define CVMX_PKO_QUEUES_PER_PORT_SRIO0 1 + +/**< PKO queues per port for SRIO1 devices (ports 42-43) */ +#define CVMX_PKO_QUEUES_PER_PORT_SRIO1 1 + +/************************* FPA allocation *********************************/ +/* Pool sizes in bytes, must be multiple of a cache line */ +#define CVMX_FPA_POOL_0_SIZE (16 * CVMX_CACHE_LINE_SIZE) +#define CVMX_FPA_POOL_1_SIZE (1 * CVMX_CACHE_LINE_SIZE) +#define CVMX_FPA_POOL_2_SIZE (8 * CVMX_CACHE_LINE_SIZE) +#define CVMX_FPA_POOL_3_SIZE (2 * CVMX_CACHE_LINE_SIZE) +#define CVMX_FPA_POOL_4_SIZE (0 * CVMX_CACHE_LINE_SIZE) +#define CVMX_FPA_POOL_5_SIZE (0 * CVMX_CACHE_LINE_SIZE) +#define CVMX_FPA_POOL_6_SIZE (8 * CVMX_CACHE_LINE_SIZE) +#define CVMX_FPA_POOL_7_SIZE (0 * CVMX_CACHE_LINE_SIZE) + +/* Pools in use */ +/**< Packet buffers */ +#define CVMX_FPA_PACKET_POOL (0) +#ifndef CVMX_FPA_PACKET_POOL_SIZE +#define CVMX_FPA_PACKET_POOL_SIZE CVMX_FPA_POOL_0_SIZE +#endif + +/**< Work queue entries */ +#define CVMX_FPA_WQE_POOL (1) +#define CVMX_FPA_WQE_POOL_SIZE CVMX_FPA_POOL_1_SIZE + +/**< PKO queue command buffers */ +#define CVMX_FPA_OUTPUT_BUFFER_POOL (2) +#define CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE CVMX_FPA_POOL_2_SIZE + +/**< BCH queue command buffers */ +#define CVMX_FPA_BCH_POOL (6) +#define CVMX_FPA_BCH_POOL_SIZE CVMX_FPA_POOL6_SIZE + +/************************* FAU allocation ********************************/ +/* The fetch and add registers are allocated here. They are arranged + * in order of descending size so that all alignment constraints are + * automatically met. + * The enums are linked so that the following enum continues allocating + * where the previous one left off, so the numbering within each + * enum always starts with zero. The macros take care of the address + * increment size, so the values entered always increase by 1. + * FAU registers are accessed with byte addresses. + */ + +#define CVMX_FAU_REG_64_ADDR(x) (((x) << 3) + CVMX_FAU_REG_64_START) +typedef enum { + CVMX_FAU_REG_64_START = 0, + /**< FAU registers for the position in PKO command buffers */ + CVMX_FAU_REG_OQ_ADDR_INDEX = CVMX_FAU_REG_64_ADDR(0), + /* Array of 36 */ + CVMX_FAU_REG_64_END = CVMX_FAU_REG_64_ADDR(36), +} cvmx_fau_reg_64_t; + +#define CVMX_FAU_REG_32_ADDR(x) (((x) << 2) + CVMX_FAU_REG_32_START) +typedef enum { + CVMX_FAU_REG_32_START = CVMX_FAU_REG_64_END, + CVMX_FAU_REG_32_END = CVMX_FAU_REG_32_ADDR(0), +} cvmx_fau_reg_32_t; + +#define CVMX_FAU_REG_16_ADDR(x) (((x) << 1) + CVMX_FAU_REG_16_START) +typedef enum { + CVMX_FAU_REG_16_START = CVMX_FAU_REG_32_END, + CVMX_FAU_REG_16_END = CVMX_FAU_REG_16_ADDR(0), +} cvmx_fau_reg_16_t; + +#define CVMX_FAU_REG_8_ADDR(x) ((x) + CVMX_FAU_REG_8_START) +typedef enum { + CVMX_FAU_REG_8_START = CVMX_FAU_REG_16_END, + CVMX_FAU_REG_8_END = CVMX_FAU_REG_8_ADDR(0), +} cvmx_fau_reg_8_t; + +/* The name CVMX_FAU_REG_AVAIL_BASE is provided to indicate the first available + * FAU address that is not allocated in cvmx-config.h. This is 64 bit aligned. + */ +#define CVMX_FAU_REG_AVAIL_BASE ((CVMX_FAU_REG_8_END + 0x7) & (~0x7ULL)) +#define CVMX_FAU_REG_END (2048) + +/********************** scratch memory allocation *************************/ +/* Scratchpad memory allocation. Note that these are byte memory addresses. + * Some uses of scratchpad (IOBDMA for example) require the use of 8-byte + * aligned addresses, so proper alignment needs to be taken into account. + */ + +/**< Pre allocation for PKO queue command buffers */ +#define CVMX_SCR_OQ_BUF_PRE_ALLOC (0) + +/**< Generic scratch iobdma area */ +#define CVMX_SCR_SCRATCH (8) + +/**< First location available after cvmx-config.h allocated region. */ +#define CVMX_SCR_REG_AVAIL_BASE (16) + +#endif /* __CVMX_CONFIG_H__ */ diff --git a/arch/mips/mach-octeon/include/mach/cvmx-fau.h b/arch/mips/mach-octeon/include/mach/cvmx-fau.h new file mode 100644 index 0000000..d795ff6 --- /dev/null +++ b/arch/mips/mach-octeon/include/mach/cvmx-fau.h @@ -0,0 +1,581 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018-2022 Marvell International Ltd. + * + * Interface to the hardware Fetch and Add Unit. + */ + +#ifndef __CVMX_FAU_H__ +#define __CVMX_FAU_H__ + +extern u8 *cvmx_fau_regs_ptr; + +/** + * Initializes fau, on devices with FAU hw this is a noop. + */ +int cvmx_fau_init(void); + +/** + * Return the location of emulated FAU register + */ +static inline u8 *__cvmx_fau_sw_addr(int reg) +{ + if (cvmx_unlikely(!cvmx_fau_regs_ptr)) + cvmx_fau_init(); + return (cvmx_fau_regs_ptr + reg); +} + +/** + * Perform an atomic 64 bit add + * + * @param reg FAU atomic register to access. 0 <= reg < 2048. + * - Step by 8 for 64 bit access. + * @param value Signed value to add. + * Note: Only the low 22 bits are available. + * @return Value of the register before the update + */ +static inline int64_t cvmx_fau_fetch_and_add64(cvmx_fau_reg64_t reg, + int64_t value) +{ + if (octeon_has_feature(OCTEON_FEATURE_FAU)) + return cvmx_hwfau_fetch_and_add64(reg, value); + + return __atomic_fetch_add(CASTPTR(int64_t, __cvmx_fau_sw_addr(reg)), + value, __ATOMIC_SEQ_CST); +} + +/** + * Perform an atomic 32 bit add + * + * @param reg FAU atomic register to access. 0 <= reg < 2048. + * - Step by 4 for 32 bit access. + * @param value Signed value to add. + * Note: Only the low 22 bits are available. + * @return Value of the register before the update + */ +static inline int32_t cvmx_fau_fetch_and_add32(cvmx_fau_reg32_t reg, + int32_t value) +{ + if (octeon_has_feature(OCTEON_FEATURE_FAU)) + return cvmx_hwfau_fetch_and_add32(reg, value); + + reg ^= SWIZZLE_32; + return __atomic_fetch_add(CASTPTR(int32_t, __cvmx_fau_sw_addr(reg)), + value, __ATOMIC_SEQ_CST); +} + +/** + * Perform an atomic 16 bit add + * + * @param reg FAU atomic register to access. 0 <= reg < 2048. + * - Step by 2 for 16 bit access. + * @param value Signed value to add. + * @return Value of the register before the update + */ +static inline int16_t cvmx_fau_fetch_and_add16(cvmx_fau_reg16_t reg, + int16_t value) +{ + if (octeon_has_feature(OCTEON_FEATURE_FAU)) + return cvmx_hwfau_fetch_and_add16(reg, value); + + reg ^= SWIZZLE_16; + return __atomic_fetch_add(CASTPTR(int16_t, __cvmx_fau_sw_addr(reg)), + value, __ATOMIC_SEQ_CST); +} + +/** + * Perform an atomic 8 bit add + * + * @param reg FAU atomic register to access. 0 <= reg < 2048. + * @param value Signed value to add. + * @return Value of the register before the update + */ +static inline int8_t cvmx_fau_fetch_and_add8(cvmx_fau_reg8_t reg, int8_t value) +{ + if (octeon_has_feature(OCTEON_FEATURE_FAU)) + return cvmx_hwfau_fetch_and_add8(reg, value); + + reg ^= SWIZZLE_8; + return __atomic_fetch_add(CASTPTR(int8_t, __cvmx_fau_sw_addr(reg)), + value, __ATOMIC_SEQ_CST); +} + +/** + * Perform an atomic 64 bit add after the current tag switch + * completes + * + * @param reg FAU atomic register to access. 0 <= reg < 2048. + * - Step by 8 for 64 bit access. + * @param value Signed value to add. + * Note: Only the low 22 bits are available. + * @return If a timeout occurs, the error bit will be set. Otherwise + * the value of the register before the update will be + * returned + */ +static inline cvmx_fau_tagwait64_t +cvmx_fau_tagwait_fetch_and_add64(cvmx_fau_reg64_t reg, int64_t value) +{ + if (octeon_has_feature(OCTEON_FEATURE_FAU)) + return cvmx_hwfau_tagwait_fetch_and_add64(reg, value); + + /* not implemented yet.*/ + return (cvmx_fau_tagwait64_t){ 1, 0 }; +} + +/** + * Perform an atomic 32 bit add after the current tag switch + * completes + * + * @param reg FAU atomic register to access. 0 <= reg < 2048. + * - Step by 4 for 32 bit access. + * @param value Signed value to add. + * Note: Only the low 22 bits are available. + * @return If a timeout occurs, the error bit will be set. Otherwise + * the value of the register before the update will be + * returned + */ +static inline cvmx_fau_tagwait32_t +cvmx_fau_tagwait_fetch_and_add32(cvmx_fau_reg32_t reg, int32_t value) +{ + if (octeon_has_feature(OCTEON_FEATURE_FAU)) + return cvmx_hwfau_tagwait_fetch_and_add32(reg, value); + + /* not implemented yet.*/ + return (cvmx_fau_tagwait32_t){ 1, 0 }; +} + +/** + * Perform an atomic 16 bit add after the current tag switch + * completes + * + * @param reg FAU atomic register to access. 0 <= reg < 2048. + * - Step by 2 for 16 bit access. + * @param value Signed value to add. + * @return If a timeout occurs, the error bit will be set. Otherwise + * the value of the register before the update will be + * returned + */ +static inline cvmx_fau_tagwait16_t +cvmx_fau_tagwait_fetch_and_add16(cvmx_fau_reg16_t reg, int16_t value) +{ + if (octeon_has_feature(OCTEON_FEATURE_FAU)) + return cvmx_hwfau_tagwait_fetch_and_add16(reg, value); + + /* not implemented yet.*/ + return (cvmx_fau_tagwait16_t){ 1, 0 }; +} + +/** + * Perform an atomic 8 bit add after the current tag switch + * completes + * + * @param reg FAU atomic register to access. 0 <= reg < 2048. + * @param value Signed value to add. + * @return If a timeout occurs, the error bit will be set. Otherwise + * the value of the register before the update will be + * returned + */ +static inline cvmx_fau_tagwait8_t +cvmx_fau_tagwait_fetch_and_add8(cvmx_fau_reg8_t reg, int8_t value) +{ + if (octeon_has_feature(OCTEON_FEATURE_FAU)) + return cvmx_hwfau_tagwait_fetch_and_add8(reg, value); + + /* not implemented yet.*/ + return (cvmx_fau_tagwait8_t){ 1, 0 }; +} + +/** + * Perform an async atomic 64 bit add. The old value is + * placed in the scratch memory at byte address scraddr. + * + * @param scraddr Scratch memory byte address to put response in. + * Must be 8 byte aligned. + * @param reg FAU atomic register to access. 0 <= reg < 2048. + * - Step by 8 for 64 bit access. + * @param value Signed value to add. + * Note: Only the low 22 bits are available. + * @return Placed in the scratch pad register + */ +static inline void +cvmx_fau_async_fetch_and_add64(u64 scraddr, cvmx_fau_reg64_t reg, int64_t value) +{ + if (octeon_has_feature(OCTEON_FEATURE_FAU)) { + cvmx_hwfau_async_fetch_and_add64(scraddr, reg, value); + return; + } + cvmx_scratch_write64( + scraddr, + __atomic_fetch_add(CASTPTR(int64_t, __cvmx_fau_sw_addr(reg)), + value, __ATOMIC_SEQ_CST)); +} + +/** + * Perform an async atomic 32 bit add. The old value is + * placed in the scratch memory at byte address scraddr. + * + * @param scraddr Scratch memory byte address to put response in. + * Must be 8 byte aligned. + * @param reg FAU atomic register to access. 0 <= reg < 2048. + * - Step by 4 for 32 bit access. + * @param value Signed value to add. + * Note: Only the low 22 bits are available. + * @return Placed in the scratch pad register + */ +static inline void +cvmx_fau_async_fetch_and_add32(u64 scraddr, cvmx_fau_reg32_t reg, int32_t value) +{ + if (octeon_has_feature(OCTEON_FEATURE_FAU)) { + cvmx_hwfau_async_fetch_and_add32(scraddr, reg, value); + return; + } + cvmx_scratch_write64( + scraddr, + __atomic_fetch_add(CASTPTR(int32_t, __cvmx_fau_sw_addr(reg)), + value, __ATOMIC_SEQ_CST)); +} + +/** + * Perform an async atomic 16 bit add. The old value is + * placed in the scratch memory at byte address scraddr. + * + * @param scraddr Scratch memory byte address to put response in. + * Must be 8 byte aligned. + * @param reg FAU atomic register to access. 0 <= reg < 2048. + * - Step by 2 for 16 bit access. + * @param value Signed value to add. + * @return Placed in the scratch pad register + */ +static inline void +cvmx_fau_async_fetch_and_add16(u64 scraddr, cvmx_fau_reg16_t reg, int16_t value) +{ + if (octeon_has_feature(OCTEON_FEATURE_FAU)) { + cvmx_hwfau_async_fetch_and_add16(scraddr, reg, value); + return; + } + cvmx_scratch_write64( + scraddr, + __atomic_fetch_add(CASTPTR(int16_t, __cvmx_fau_sw_addr(reg)), + value, __ATOMIC_SEQ_CST)); +} + +/** + * Perform an async atomic 8 bit add. The old value is + * placed in the scratch memory at byte address scraddr. + * + * @param scraddr Scratch memory byte address to put response in. + * Must be 8 byte aligned. + * @param reg FAU atomic register to access. 0 <= reg < 2048. + * @param value Signed value to add. + * @return Placed in the scratch pad register + */ +static inline void +cvmx_fau_async_fetch_and_add8(u64 scraddr, cvmx_fau_reg8_t reg, int8_t value) +{ + if (octeon_has_feature(OCTEON_FEATURE_FAU)) { + cvmx_hwfau_async_fetch_and_add8(scraddr, reg, value); + return; + } + cvmx_scratch_write64( + scraddr, + __atomic_fetch_add(CASTPTR(int8_t, __cvmx_fau_sw_addr(reg)), + value, __ATOMIC_SEQ_CST)); +} + +/** + * Perform an async atomic 64 bit add after the current tag + * switch completes. + * + * @param scraddr Scratch memory byte address to put response in. + * Must be 8 byte aligned. + * If a timeout occurs, the error bit (63) will be set. Otherwise + * the value of the register before the update will be + * returned + * @param reg FAU atomic register to access. 0 <= reg < 2048. + * - Step by 8 for 64 bit access. + * @param value Signed value to add. + * Note: Only the low 22 bits are available. + * @return Placed in the scratch pad register + */ +static inline void cvmx_fau_async_tagwait_fetch_and_add64(u64 scraddr, + cvmx_fau_reg64_t reg, + int64_t value) +{ + if (octeon_has_feature(OCTEON_FEATURE_FAU)) { + cvmx_hwfau_async_tagwait_fetch_and_add64(scraddr, reg, value); + return; + } + + /* Broken. Where is the tag wait? */ + cvmx_scratch_write64( + scraddr, + __atomic_fetch_add(CASTPTR(int64_t, __cvmx_fau_sw_addr(reg)), + value, __ATOMIC_SEQ_CST)); +} + +/** + * Perform an async atomic 32 bit add after the current tag + * switch completes. + * + * @param scraddr Scratch memory byte address to put response in. + * Must be 8 byte aligned. + * If a timeout occurs, the error bit (63) will be set. Otherwise + * the value of the register before the update will be + * returned + * @param reg FAU atomic register to access. 0 <= reg < 2048. + * - Step by 4 for 32 bit access. + * @param value Signed value to add. + * Note: Only the low 22 bits are available. + * @return Placed in the scratch pad register + */ +static inline void cvmx_fau_async_tagwait_fetch_and_add32(u64 scraddr, + cvmx_fau_reg32_t reg, + int32_t value) +{ + if (octeon_has_feature(OCTEON_FEATURE_FAU)) { + cvmx_hwfau_async_tagwait_fetch_and_add32(scraddr, reg, value); + return; + } + /* Broken. Where is the tag wait? */ + cvmx_scratch_write64( + scraddr, + __atomic_fetch_add(CASTPTR(int32_t, __cvmx_fau_sw_addr(reg)), + value, __ATOMIC_SEQ_CST)); +} + +/** + * Perform an async atomic 16 bit add after the current tag + * switch completes. + * + * @param scraddr Scratch memory byte address to put response in. + * Must be 8 byte aligned. + * If a timeout occurs, the error bit (63) will be set. Otherwise + * the value of the register before the update will be + * returned + * @param reg FAU atomic register to access. 0 <= reg < 2048. + * - Step by 2 for 16 bit access. + * @param value Signed value to add. + * @return Placed in the scratch pad register + */ +static inline void cvmx_fau_async_tagwait_fetch_and_add16(u64 scraddr, + cvmx_fau_reg16_t reg, + int16_t value) +{ + if (octeon_has_feature(OCTEON_FEATURE_FAU)) { + cvmx_hwfau_async_tagwait_fetch_and_add16(scraddr, reg, value); + return; + } + /* Broken. Where is the tag wait? */ + cvmx_scratch_write64( + scraddr, + __atomic_fetch_add(CASTPTR(int16_t, __cvmx_fau_sw_addr(reg)), + value, __ATOMIC_SEQ_CST)); +} + +/** + * Perform an async atomic 8 bit add after the current tag + * switch completes. + * + * @param scraddr Scratch memory byte address to put response in. + * Must be 8 byte aligned. + * If a timeout occurs, the error bit (63) will be set. Otherwise + * the value of the register before the update will be + * returned + * @param reg FAU atomic register to access. 0 <= reg < 2048. + * @param value Signed value to add. + * @return Placed in the scratch pad register + */ +static inline void cvmx_fau_async_tagwait_fetch_and_add8(u64 scraddr, + cvmx_fau_reg8_t reg, + int8_t value) +{ + if (octeon_has_feature(OCTEON_FEATURE_FAU)) { + cvmx_hwfau_async_tagwait_fetch_and_add8(scraddr, reg, value); + return; + } + /* Broken. Where is the tag wait? */ + cvmx_scratch_write64( + scraddr, + __atomic_fetch_add(CASTPTR(int8_t, __cvmx_fau_sw_addr(reg)), + value, __ATOMIC_SEQ_CST)); +} + +/** + * Perform an atomic 64 bit add + * + * @param reg FAU atomic register to access. 0 <= reg < 2048. + * - Step by 8 for 64 bit access. + * @param value Signed value to add. + */ +static inline void cvmx_fau_atomic_add64(cvmx_fau_reg64_t reg, int64_t value) +{ + if (octeon_has_feature(OCTEON_FEATURE_FAU)) { + cvmx_hwfau_atomic_add64(reg, value); + return; + } + /* Ignored fetch values should be optimized away */ + __atomic_add_fetch(CASTPTR(int64_t, __cvmx_fau_sw_addr(reg)), value, + __ATOMIC_SEQ_CST); +} + +/** + * Perform an atomic 32 bit add + * + * @param reg FAU atomic register to access. 0 <= reg < 2048. + * - Step by 4 for 32 bit access. + * @param value Signed value to add. + */ +static inline void cvmx_fau_atomic_add32(cvmx_fau_reg32_t reg, int32_t value) +{ + if (octeon_has_feature(OCTEON_FEATURE_FAU)) { + cvmx_hwfau_atomic_add32(reg, value); + return; + } + reg ^= SWIZZLE_32; + /* Ignored fetch values should be optimized away */ + __atomic_add_fetch(CASTPTR(int32_t, __cvmx_fau_sw_addr(reg)), value, + __ATOMIC_SEQ_CST); +} + +/** + * Perform an atomic 16 bit add + * + * @param reg FAU atomic register to access. 0 <= reg < 2048. + * - Step by 2 for 16 bit access. + * @param value Signed value to add. + */ +static inline void cvmx_fau_atomic_add16(cvmx_fau_reg16_t reg, int16_t value) +{ + if (octeon_has_feature(OCTEON_FEATURE_FAU)) { + cvmx_hwfau_atomic_add16(reg, value); + return; + } + reg ^= SWIZZLE_16; + /* Ignored fetch values should be optimized away */ + __atomic_add_fetch(CASTPTR(int16_t, __cvmx_fau_sw_addr(reg)), value, + __ATOMIC_SEQ_CST); +} + +/** + * Perform an atomic 8 bit add + * + * @param reg FAU atomic register to access. 0 <= reg < 2048. + * @param value Signed value to add. + */ +static inline void cvmx_fau_atomic_add8(cvmx_fau_reg8_t reg, int8_t value) +{ + if (octeon_has_feature(OCTEON_FEATURE_FAU)) { + cvmx_hwfau_atomic_add8(reg, value); + return; + } + reg ^= SWIZZLE_8; + /* Ignored fetch values should be optimized away */ + __atomic_add_fetch(CASTPTR(int8_t, __cvmx_fau_sw_addr(reg)), value, + __ATOMIC_SEQ_CST); +} + +/** + * Perform an atomic 64 bit write + * + * @param reg FAU atomic register to access. 0 <= reg < 2048. + * - Step by 8 for 64 bit access. + * @param value Signed value to write. + */ +static inline void cvmx_fau_atomic_write64(cvmx_fau_reg64_t reg, int64_t value) +{ + if (octeon_has_feature(OCTEON_FEATURE_FAU)) { + cvmx_hwfau_atomic_write64(reg, value); + return; + } + __atomic_store_n(CASTPTR(int64_t, __cvmx_fau_sw_addr(reg)), value, + __ATOMIC_SEQ_CST); +} + +/** + * Perform an atomic 32 bit write + * + * @param reg FAU atomic register to access. 0 <= reg < 2048. + * - Step by 4 for 32 bit access. + * @param value Signed value to write. + */ +static inline void cvmx_fau_atomic_write32(cvmx_fau_reg32_t reg, int32_t value) +{ + if (octeon_has_feature(OCTEON_FEATURE_FAU)) { + cvmx_hwfau_atomic_write32(reg, value); + return; + } + reg ^= SWIZZLE_32; + __atomic_store_n(CASTPTR(int32_t, __cvmx_fau_sw_addr(reg)), value, + __ATOMIC_SEQ_CST); +} + +/** + * Perform an atomic 16 bit write + * + * @param reg FAU atomic register to access. 0 <= reg < 2048. + * - Step by 2 for 16 bit access. + * @param value Signed value to write. + */ +static inline void cvmx_fau_atomic_write16(cvmx_fau_reg16_t reg, int16_t value) +{ + if (octeon_has_feature(OCTEON_FEATURE_FAU)) { + cvmx_hwfau_atomic_write16(reg, value); + return; + } + reg ^= SWIZZLE_16; + __atomic_store_n(CASTPTR(int16_t, __cvmx_fau_sw_addr(reg)), value, + __ATOMIC_SEQ_CST); +} + +/** + * Perform an atomic 8 bit write + * + * @param reg FAU atomic register to access. 0 <= reg < 2048. + * @param value Signed value to write. + */ +static inline void cvmx_fau_atomic_write8(cvmx_fau_reg8_t reg, int8_t value) +{ + if (octeon_has_feature(OCTEON_FEATURE_FAU)) { + cvmx_hwfau_atomic_write8(reg, value); + return; + } + reg ^= SWIZZLE_8; + __atomic_store_n(CASTPTR(int8_t, __cvmx_fau_sw_addr(reg)), value, + __ATOMIC_SEQ_CST); +} + +/** Allocates 64bit FAU register. + * @param reserve base address to reserve + * @return value is the base address of allocated FAU register + */ +int cvmx_fau64_alloc(int reserve); + +/** Allocates 32bit FAU register. + * @param reserve base address to reserve + * @return value is the base address of allocated FAU register + */ +int cvmx_fau32_alloc(int reserve); + +/** Allocates 16bit FAU register. + * @param reserve base address to reserve + * @return value is the base address of allocated FAU register + */ +int cvmx_fau16_alloc(int reserve); + +/** Allocates 8bit FAU register. + * @param reserve base address to reserve + * @return value is the base address of allocated FAU register + */ +int cvmx_fau8_alloc(int reserve); + +/** Frees the specified FAU register. + * @param address base address of register to release. + * @return 0 on success; -1 on failure + */ +int cvmx_fau_free(int address); + +/** Display the fau registers array + */ +void cvmx_fau_show(void); + +#endif /* __CVMX_FAU_H__ */ diff --git a/arch/mips/mach-octeon/include/mach/cvmx-fpa.h b/arch/mips/mach-octeon/include/mach/cvmx-fpa.h index aa238a8..0660c31 100644 --- a/arch/mips/mach-octeon/include/mach/cvmx-fpa.h +++ b/arch/mips/mach-octeon/include/mach/cvmx-fpa.h @@ -104,8 +104,9 @@ static inline void *cvmx_fpa_alloc(u64 pool) /* FPA3 is handled differently */ if ((octeon_has_feature(OCTEON_FEATURE_FPA3))) { return cvmx_fpa3_alloc(cvmx_fpa1_pool_to_fpa3_aura(pool)); - } else + } else { return cvmx_fpa1_alloc(pool); + } } /** diff --git a/arch/mips/mach-octeon/include/mach/cvmx-fpa3.h b/arch/mips/mach-octeon/include/mach/cvmx-fpa3.h index b3e04d7..9bab03f 100644 --- a/arch/mips/mach-octeon/include/mach/cvmx-fpa3.h +++ b/arch/mips/mach-octeon/include/mach/cvmx-fpa3.h @@ -526,41 +526,4 @@ const char *cvmx_fpa3_get_pool_name(cvmx_fpa3_pool_t pool); int cvmx_fpa3_get_pool_buf_size(cvmx_fpa3_pool_t pool); const char *cvmx_fpa3_get_aura_name(cvmx_fpa3_gaura_t aura); -/* FIXME: Need a different macro for stage2 of u-boot */ - -static inline void cvmx_fpa3_stage2_init(int aura, int pool, u64 stack_paddr, int stacklen, - int buffer_sz, int buf_cnt) -{ - cvmx_fpa_poolx_cfg_t pool_cfg; - - /* Configure pool stack */ - cvmx_write_csr_node(0, CVMX_FPA_POOLX_STACK_BASE(pool), stack_paddr); - cvmx_write_csr_node(0, CVMX_FPA_POOLX_STACK_ADDR(pool), stack_paddr); - cvmx_write_csr_node(0, CVMX_FPA_POOLX_STACK_END(pool), stack_paddr + stacklen); - - /* Configure pool with buffer size */ - pool_cfg.u64 = 0; - pool_cfg.cn78xx.nat_align = 1; - pool_cfg.cn78xx.buf_size = buffer_sz >> 7; - pool_cfg.cn78xx.l_type = 0x2; - pool_cfg.cn78xx.ena = 0; - cvmx_write_csr_node(0, CVMX_FPA_POOLX_CFG(pool), pool_cfg.u64); - /* Reset pool before starting */ - pool_cfg.cn78xx.ena = 1; - cvmx_write_csr_node(0, CVMX_FPA_POOLX_CFG(pool), pool_cfg.u64); - - cvmx_write_csr_node(0, CVMX_FPA_AURAX_CFG(aura), 0); - cvmx_write_csr_node(0, CVMX_FPA_AURAX_CNT_ADD(aura), buf_cnt); - cvmx_write_csr_node(0, CVMX_FPA_AURAX_POOL(aura), (u64)pool); -} - -static inline void cvmx_fpa3_stage2_disable(int aura, int pool) -{ - cvmx_write_csr_node(0, CVMX_FPA_AURAX_POOL(aura), 0); - cvmx_write_csr_node(0, CVMX_FPA_POOLX_CFG(pool), 0); - cvmx_write_csr_node(0, CVMX_FPA_POOLX_STACK_BASE(pool), 0); - cvmx_write_csr_node(0, CVMX_FPA_POOLX_STACK_ADDR(pool), 0); - cvmx_write_csr_node(0, CVMX_FPA_POOLX_STACK_END(pool), 0); -} - #endif /* __CVMX_FPA3_H__ */ diff --git a/arch/mips/mach-octeon/include/mach/cvmx-helper-board.h b/arch/mips/mach-octeon/include/mach/cvmx-helper-board.h index 5837592..9cc61b1 100644 --- a/arch/mips/mach-octeon/include/mach/cvmx-helper-board.h +++ b/arch/mips/mach-octeon/include/mach/cvmx-helper-board.h @@ -9,6 +9,8 @@ #ifndef __CVMX_HELPER_BOARD_H__ #define __CVMX_HELPER_BOARD_H__ +#include <asm-generic/gpio.h> + #define CVMX_VSC7224_NAME_LEN 16 typedef enum { @@ -185,8 +187,8 @@ struct cvmx_vsc7224 { struct cvmx_fdt_i2c_bus_info *i2c_bus; /** Address of VSC7224 on i2c bus */ int i2c_addr; - struct cvmx_fdt_gpio_info *los_gpio; /** LoS GPIO pin */ - struct cvmx_fdt_gpio_info *reset_gpio; /** Reset GPIO pin */ + struct gpio_desc los_gpio; /** LoS GPIO pin */ + struct gpio_desc reset_gpio; /** Reset GPIO pin */ int of_offset; /** Offset in device tree */ }; diff --git a/arch/mips/mach-octeon/include/mach/cvmx-helper-fdt.h b/arch/mips/mach-octeon/include/mach/cvmx-helper-fdt.h index 3328845..c3ce359 100644 --- a/arch/mips/mach-octeon/include/mach/cvmx-helper-fdt.h +++ b/arch/mips/mach-octeon/include/mach/cvmx-helper-fdt.h @@ -14,10 +14,13 @@ #include <fdtdec.h> #include <time.h> #include <asm/global_data.h> +#include <asm-generic/gpio.h> +#include <dm/device.h> #include <linux/libfdt.h> #include <mach/cvmx-helper-sfp.h> +/* todo: this is deprecated and some of it can be removed at some time */ enum cvmx_i2c_bus_type { CVMX_I2C_BUS_OCTEON, CVMX_I2C_MUX_PCA9540, @@ -55,6 +58,8 @@ struct cvmx_fdt_i2c_bus_info { u8 enable_bit; /** True if mux, false if switch */ bool is_mux; + + struct udevice *i2c_bus; }; /** @@ -85,22 +90,24 @@ struct cvmx_fdt_sfp_info { bool is_qsfp; /** True if EEPROM data is valid */ bool valid; + /** SFP tx_disable GPIO descriptor */ - struct cvmx_fdt_gpio_info *tx_disable; + struct gpio_desc tx_disable; /** SFP mod_abs/QSFP mod_prs GPIO descriptor */ - struct cvmx_fdt_gpio_info *mod_abs; + struct gpio_desc mod_abs; /** SFP tx_error GPIO descriptor */ - struct cvmx_fdt_gpio_info *tx_error; + struct gpio_desc tx_error; /** SFP rx_los GPIO discriptor */ - struct cvmx_fdt_gpio_info *rx_los; + struct gpio_desc rx_los; /** QSFP select GPIO descriptor */ - struct cvmx_fdt_gpio_info *select; + struct gpio_desc select; /** QSFP reset GPIO descriptor */ - struct cvmx_fdt_gpio_info *reset; + struct gpio_desc reset; /** QSFP interrupt GPIO descriptor */ - struct cvmx_fdt_gpio_info *interrupt; + struct gpio_desc interrupt; /** QSFP lp_mode GPIO descriptor */ - struct cvmx_fdt_gpio_info *lp_mode; + struct gpio_desc lp_mode; + /** Last mod_abs value */ int last_mod_abs; /** Last rx_los value */ @@ -146,6 +153,9 @@ struct cvmx_fdt_sfp_info { int cvmx_fdt_lookup_phandles(const void *fdt_addr, int node, const char *prop_name, int *lenp, int *nodes); +int cvmx_ofnode_lookup_phandles(ofnode node, const char *prop_name, + int *lenp, ofnode *nodes); + /** * Helper to return the address property * @@ -341,8 +351,7 @@ int cvmx_fdt_node_offset_by_compatible_list(const void *fdt_addr, int startoffse * Given the parent offset of an i2c device build up a list describing the bus * which can contain i2c muxes and switches. * - * @param[in] fdt_addr address of device tree - * @param of_offset Offset of the parent node of a GPIO device in + * @param[in] node ofnode of the parent node of a GPIO device in * the device tree. * * Return: pointer to list of i2c devices starting from the root which @@ -351,7 +360,7 @@ int cvmx_fdt_node_offset_by_compatible_list(const void *fdt_addr, int startoffse * * @see cvmx_fdt_free_i2c_bus() */ -struct cvmx_fdt_i2c_bus_info *cvmx_fdt_get_i2c_bus(const void *fdt_addr, int of_offset); +struct cvmx_fdt_i2c_bus_info *cvmx_ofnode_get_i2c_bus(ofnode node); /** * Return the Octeon bus number for a bus descriptor @@ -497,15 +506,6 @@ int __cvmx_fdt_parse_vsc7224(const void *fdt_addr); int __cvmx_fdt_parse_avsp5410(const void *fdt_addr); /** - * Parse SFP information from device tree - * - * @param[in] fdt_addr Address of flat device tree - * - * Return: pointer to sfp info or NULL if error - */ -struct cvmx_fdt_sfp_info *cvmx_helper_fdt_parse_sfp_info(const void *fdt_addr, int of_offset); - -/** * @INTERNAL * Parses either a CS4343 phy or a slice of the phy from the device tree * @param[in] fdt_addr Address of FDT diff --git a/arch/mips/mach-octeon/include/mach/cvmx-helper-pko.h b/arch/mips/mach-octeon/include/mach/cvmx-helper-pko.h index 806102d..e1eb824 100644 --- a/arch/mips/mach-octeon/include/mach/cvmx-helper-pko.h +++ b/arch/mips/mach-octeon/include/mach/cvmx-helper-pko.h @@ -17,7 +17,7 @@ * number. Users should set this pointer to a function before * calling any cvmx-helper operations. */ -void (*cvmx_override_pko_queue_priority)(int ipd_port, u8 *priorities); +extern void (*cvmx_override_pko_queue_priority)(int ipd_port, u8 *priorities); /** * Gets the fpa pool number of pko pool diff --git a/arch/mips/mach-octeon/include/mach/cvmx-helper.h b/arch/mips/mach-octeon/include/mach/cvmx-helper.h index caa0c69..2a7b133 100644 --- a/arch/mips/mach-octeon/include/mach/cvmx-helper.h +++ b/arch/mips/mach-octeon/include/mach/cvmx-helper.h @@ -128,6 +128,26 @@ enum cvmx_pko_padding { }; /** + * cvmx_override_iface_phy_mode(int interface, int index) is a function pointer. + * It is meant to allow customization of interfaces which do not have a PHY. + * + * @returns 0 if MAC decides TX_CONFIG_REG or 1 if PHY decides TX_CONFIG_REG. + * + * If this function pointer is NULL then it defaults to the MAC. + */ +extern int (*cvmx_override_iface_phy_mode) (int interface, int index); + +/** + * cvmx_override_ipd_port_setup(int ipd_port) is a function + * pointer. It is meant to allow customization of the IPD port/port kind + * setup before packet input/output comes online. It is called + * after cvmx-helper does the default IPD configuration, but + * before IPD is enabled. Users should set this pointer to a + * function before calling any cvmx-helper operations. + */ +extern void (*cvmx_override_ipd_port_setup) (int ipd_port); + +/** * This function enables the IPD and also enables the packet interfaces. * The packet interfaces (RGMII and SPI) must be enabled after the * IPD. This should be called by the user program after any additional diff --git a/arch/mips/mach-octeon/include/mach/cvmx-ilk-defs.h b/arch/mips/mach-octeon/include/mach/cvmx-ilk-defs.h new file mode 100644 index 0000000..d54f9ca --- /dev/null +++ b/arch/mips/mach-octeon/include/mach/cvmx-ilk-defs.h @@ -0,0 +1,2269 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018-2022 Marvell International Ltd. + * + * Configuration and status register (CSR) type definitions for + * Octeon ilk. + */ + +#ifndef __CVMX_ILK_DEFS_H__ +#define __CVMX_ILK_DEFS_H__ + +#define CVMX_ILK_BIST_SUM (0x0001180014000038ull) +#define CVMX_ILK_GBL_CFG (0x0001180014000000ull) +#define CVMX_ILK_GBL_ERR_CFG (0x0001180014000058ull) +#define CVMX_ILK_GBL_INT (0x0001180014000008ull) +#define CVMX_ILK_GBL_INT_EN (0x0001180014000010ull) +#define CVMX_ILK_INT_SUM (0x0001180014000030ull) +#define CVMX_ILK_LNEX_TRN_CTL(offset) \ + (0x00011800140380F0ull + ((offset) & 15) * 1024) +#define CVMX_ILK_LNEX_TRN_LD(offset) \ + (0x00011800140380E0ull + ((offset) & 15) * 1024) +#define CVMX_ILK_LNEX_TRN_LP(offset) \ + (0x00011800140380E8ull + ((offset) & 15) * 1024) +#define CVMX_ILK_LNE_DBG (0x0001180014030008ull) +#define CVMX_ILK_LNE_STS_MSG (0x0001180014030000ull) +#define CVMX_ILK_RID_CFG (0x0001180014000050ull) +#define CVMX_ILK_RXF_IDX_PMAP (0x0001180014000020ull) +#define CVMX_ILK_RXF_MEM_PMAP (0x0001180014000028ull) +#define CVMX_ILK_RXX_BYTE_CNTX(offset, block_id) \ + (0x0001180014023000ull + \ + (((offset) & 255) + ((block_id) & 1) * 0x800ull) * 8) +#define CVMX_ILK_RXX_CAL_ENTRYX(offset, block_id) \ + (0x0001180014021000ull + \ + (((offset) & 511) + ((block_id) & 1) * 0x800ull) * 8) +#define CVMX_ILK_RXX_CFG0(offset) (0x0001180014020000ull + ((offset) & 1) * 16384) +#define CVMX_ILK_RXX_CFG1(offset) (0x0001180014020008ull + ((offset) & 1) * 16384) +#define CVMX_ILK_RXX_CHAX(offset, block_id) \ + (0x0001180014002000ull + \ + (((offset) & 255) + ((block_id) & 1) * 0x200ull) * 8) +#define CVMX_ILK_RXX_CHA_XONX(offset, block_id) \ + (0x0001180014020400ull + (((offset) & 3) + ((block_id) & 1) * 0x800ull) * 8) +#define CVMX_ILK_RXX_ERR_CFG(offset) \ + (0x00011800140200E0ull + ((offset) & 1) * 16384) +#define CVMX_ILK_RXX_FLOW_CTL0(offset) \ + (0x0001180014020090ull + ((offset) & 1) * 16384) +#define CVMX_ILK_RXX_FLOW_CTL1(offset) \ + (0x0001180014020098ull + ((offset) & 1) * 16384) +#define CVMX_ILK_RXX_IDX_CAL(offset) \ + (0x00011800140200A0ull + ((offset) & 1) * 16384) +#define CVMX_ILK_RXX_IDX_STAT0(offset) \ + (0x0001180014020070ull + ((offset) & 1) * 16384) +#define CVMX_ILK_RXX_IDX_STAT1(offset) \ + (0x0001180014020078ull + ((offset) & 1) * 16384) +#define CVMX_ILK_RXX_INT(offset) (0x0001180014020010ull + ((offset) & 1) * 16384) +#define CVMX_ILK_RXX_INT_EN(offset) \ + (0x0001180014020018ull + ((offset) & 1) * 16384) +#define CVMX_ILK_RXX_JABBER(offset) \ + (0x00011800140200B8ull + ((offset) & 1) * 16384) +#define CVMX_ILK_RXX_MEM_CAL0(offset) \ + (0x00011800140200A8ull + ((offset) & 1) * 16384) +#define CVMX_ILK_RXX_MEM_CAL1(offset) \ + (0x00011800140200B0ull + ((offset) & 1) * 16384) +#define CVMX_ILK_RXX_MEM_STAT0(offset) \ + (0x0001180014020080ull + ((offset) & 1) * 16384) +#define CVMX_ILK_RXX_MEM_STAT1(offset) \ + (0x0001180014020088ull + ((offset) & 1) * 16384) +#define CVMX_ILK_RXX_PKT_CNTX(offset, block_id) \ + (0x0001180014022000ull + \ + (((offset) & 255) + ((block_id) & 1) * 0x800ull) * 8) +#define CVMX_ILK_RXX_RID(offset) (0x00011800140200C0ull + ((offset) & 1) * 16384) +#define CVMX_ILK_RXX_STAT0(offset) \ + (0x0001180014020020ull + ((offset) & 1) * 16384) +#define CVMX_ILK_RXX_STAT1(offset) \ + (0x0001180014020028ull + ((offset) & 1) * 16384) +#define CVMX_ILK_RXX_STAT2(offset) \ + (0x0001180014020030ull + ((offset) & 1) * 16384) +#define CVMX_ILK_RXX_STAT3(offset) \ + (0x0001180014020038ull + ((offset) & 1) * 16384) +#define CVMX_ILK_RXX_STAT4(offset) \ + (0x0001180014020040ull + ((offset) & 1) * 16384) +#define CVMX_ILK_RXX_STAT5(offset) \ + (0x0001180014020048ull + ((offset) & 1) * 16384) +#define CVMX_ILK_RXX_STAT6(offset) \ + (0x0001180014020050ull + ((offset) & 1) * 16384) +#define CVMX_ILK_RXX_STAT7(offset) \ + (0x0001180014020058ull + ((offset) & 1) * 16384) +#define CVMX_ILK_RXX_STAT8(offset) \ + (0x0001180014020060ull + ((offset) & 1) * 16384) +#define CVMX_ILK_RXX_STAT9(offset) \ + (0x0001180014020068ull + ((offset) & 1) * 16384) +#define CVMX_ILK_RX_LNEX_CFG(offset) \ + (0x0001180014038000ull + ((offset) & 15) * 1024) +#define CVMX_ILK_RX_LNEX_INT(offset) \ + (0x0001180014038008ull + ((offset) & 15) * 1024) +#define CVMX_ILK_RX_LNEX_INT_EN(offset) \ + (0x0001180014038010ull + ((offset) & 7) * 1024) +#define CVMX_ILK_RX_LNEX_STAT0(offset) \ + (0x0001180014038018ull + ((offset) & 15) * 1024) +#define CVMX_ILK_RX_LNEX_STAT1(offset) \ + (0x0001180014038020ull + ((offset) & 15) * 1024) +#define CVMX_ILK_RX_LNEX_STAT10(offset) \ + (0x0001180014038068ull + ((offset) & 15) * 1024) +#define CVMX_ILK_RX_LNEX_STAT2(offset) \ + (0x0001180014038028ull + ((offset) & 15) * 1024) +#define CVMX_ILK_RX_LNEX_STAT3(offset) \ + (0x0001180014038030ull + ((offset) & 15) * 1024) +#define CVMX_ILK_RX_LNEX_STAT4(offset) \ + (0x0001180014038038ull + ((offset) & 15) * 1024) +#define CVMX_ILK_RX_LNEX_STAT5(offset) \ + (0x0001180014038040ull + ((offset) & 15) * 1024) +#define CVMX_ILK_RX_LNEX_STAT6(offset) \ + (0x0001180014038048ull + ((offset) & 15) * 1024) +#define CVMX_ILK_RX_LNEX_STAT7(offset) \ + (0x0001180014038050ull + ((offset) & 15) * 1024) +#define CVMX_ILK_RX_LNEX_STAT8(offset) \ + (0x0001180014038058ull + ((offset) & 15) * 1024) +#define CVMX_ILK_RX_LNEX_STAT9(offset) \ + (0x0001180014038060ull + ((offset) & 15) * 1024) +#define CVMX_ILK_SER_CFG (0x0001180014000018ull) +#define CVMX_ILK_TXX_BYTE_CNTX(offset, block_id) \ + (0x0001180014013000ull + \ + (((offset) & 255) + ((block_id) & 1) * 0x800ull) * 8) +#define CVMX_ILK_TXX_CAL_ENTRYX(offset, block_id) \ + (0x0001180014011000ull + \ + (((offset) & 511) + ((block_id) & 1) * 0x800ull) * 8) +#define CVMX_ILK_TXX_CFG0(offset) (0x0001180014010000ull + ((offset) & 1) * 16384) +#define CVMX_ILK_TXX_CFG1(offset) (0x0001180014010008ull + ((offset) & 1) * 16384) +#define CVMX_ILK_TXX_CHA_XONX(offset, block_id) \ + (0x0001180014010400ull + (((offset) & 3) + ((block_id) & 1) * 0x800ull) * 8) +#define CVMX_ILK_TXX_DBG(offset) (0x0001180014010070ull + ((offset) & 1) * 16384) +#define CVMX_ILK_TXX_ERR_CFG(offset) \ + (0x00011800140100B0ull + ((offset) & 1) * 16384) +#define CVMX_ILK_TXX_FLOW_CTL0(offset) \ + (0x0001180014010048ull + ((offset) & 1) * 16384) +#define CVMX_ILK_TXX_FLOW_CTL1(offset) \ + (0x0001180014010050ull + ((offset) & 1) * 16384) +#define CVMX_ILK_TXX_IDX_CAL(offset) \ + (0x0001180014010058ull + ((offset) & 1) * 16384) +#define CVMX_ILK_TXX_IDX_PMAP(offset) \ + (0x0001180014010010ull + ((offset) & 1) * 16384) +#define CVMX_ILK_TXX_IDX_STAT0(offset) \ + (0x0001180014010020ull + ((offset) & 1) * 16384) +#define CVMX_ILK_TXX_IDX_STAT1(offset) \ + (0x0001180014010028ull + ((offset) & 1) * 16384) +#define CVMX_ILK_TXX_INT(offset) (0x0001180014010078ull + ((offset) & 1) * 16384) +#define CVMX_ILK_TXX_INT_EN(offset) \ + (0x0001180014010080ull + ((offset) & 1) * 16384) +#define CVMX_ILK_TXX_MEM_CAL0(offset) \ + (0x0001180014010060ull + ((offset) & 1) * 16384) +#define CVMX_ILK_TXX_MEM_CAL1(offset) \ + (0x0001180014010068ull + ((offset) & 1) * 16384) +#define CVMX_ILK_TXX_MEM_PMAP(offset) \ + (0x0001180014010018ull + ((offset) & 1) * 16384) +#define CVMX_ILK_TXX_MEM_STAT0(offset) \ + (0x0001180014010030ull + ((offset) & 1) * 16384) +#define CVMX_ILK_TXX_MEM_STAT1(offset) \ + (0x0001180014010038ull + ((offset) & 1) * 16384) +#define CVMX_ILK_TXX_PIPE(offset) (0x0001180014010088ull + ((offset) & 1) * 16384) +#define CVMX_ILK_TXX_PKT_CNTX(offset, block_id) \ + (0x0001180014012000ull + \ + (((offset) & 255) + ((block_id) & 1) * 0x800ull) * 8) +#define CVMX_ILK_TXX_RMATCH(offset) \ + (0x0001180014010040ull + ((offset) & 1) * 16384) + +/** + * cvmx_ilk_bist_sum + */ +union cvmx_ilk_bist_sum { + u64 u64; + struct cvmx_ilk_bist_sum_s { + u64 rxf_x2p : 1; + u64 rxf_mem19 : 1; + u64 rxf_mem18 : 1; + u64 rxf_mem17 : 1; + u64 rxf_mem16 : 1; + u64 rxf_mem15 : 1; + u64 reserved_52_57 : 6; + u64 rxf_mem8 : 1; + u64 rxf_mem7 : 1; + u64 rxf_mem6 : 1; + u64 rxf_mem5 : 1; + u64 rxf_mem4 : 1; + u64 rxf_mem3 : 1; + u64 reserved_36_45 : 10; + u64 rle7_dsk1 : 1; + u64 rle7_dsk0 : 1; + u64 rle6_dsk1 : 1; + u64 rle6_dsk0 : 1; + u64 rle5_dsk1 : 1; + u64 rle5_dsk0 : 1; + u64 rle4_dsk1 : 1; + u64 rle4_dsk0 : 1; + u64 rle3_dsk1 : 1; + u64 rle3_dsk0 : 1; + u64 rle2_dsk1 : 1; + u64 rle2_dsk0 : 1; + u64 rle1_dsk1 : 1; + u64 rle1_dsk0 : 1; + u64 rle0_dsk1 : 1; + u64 rle0_dsk0 : 1; + u64 rlk1_pmap : 1; + u64 reserved_18_18 : 1; + u64 rlk1_fwc : 1; + u64 reserved_16_16 : 1; + u64 rlk0_pmap : 1; + u64 rlk0_stat1 : 1; + u64 rlk0_fwc : 1; + u64 rlk0_stat : 1; + u64 tlk1_stat1 : 1; + u64 tlk1_fwc : 1; + u64 reserved_9_9 : 1; + u64 tlk1_txf2 : 1; + u64 tlk1_txf1 : 1; + u64 tlk1_txf0 : 1; + u64 tlk0_stat1 : 1; + u64 tlk0_fwc : 1; + u64 reserved_3_3 : 1; + u64 tlk0_txf2 : 1; + u64 tlk0_txf1 : 1; + u64 tlk0_txf0 : 1; + } s; + struct cvmx_ilk_bist_sum_cn68xx { + u64 reserved_58_63 : 6; + u64 rxf_x2p1 : 1; + u64 rxf_x2p0 : 1; + u64 rxf_pmap : 1; + u64 rxf_mem2 : 1; + u64 rxf_mem1 : 1; + u64 rxf_mem0 : 1; + u64 reserved_36_51 : 16; + u64 rle7_dsk1 : 1; + u64 rle7_dsk0 : 1; + u64 rle6_dsk1 : 1; + u64 rle6_dsk0 : 1; + u64 rle5_dsk1 : 1; + u64 rle5_dsk0 : 1; + u64 rle4_dsk1 : 1; + u64 rle4_dsk0 : 1; + u64 rle3_dsk1 : 1; + u64 rle3_dsk0 : 1; + u64 rle2_dsk1 : 1; + u64 rle2_dsk0 : 1; + u64 rle1_dsk1 : 1; + u64 rle1_dsk0 : 1; + u64 rle0_dsk1 : 1; + u64 rle0_dsk0 : 1; + u64 reserved_19_19 : 1; + u64 rlk1_stat1 : 1; + u64 rlk1_fwc : 1; + u64 rlk1_stat : 1; + u64 reserved_15_15 : 1; + u64 rlk0_stat1 : 1; + u64 rlk0_fwc : 1; + u64 rlk0_stat : 1; + u64 tlk1_stat1 : 1; + u64 tlk1_fwc : 1; + u64 tlk1_stat0 : 1; + u64 tlk1_txf2 : 1; + u64 tlk1_txf1 : 1; + u64 tlk1_txf0 : 1; + u64 tlk0_stat1 : 1; + u64 tlk0_fwc : 1; + u64 tlk0_stat0 : 1; + u64 tlk0_txf2 : 1; + u64 tlk0_txf1 : 1; + u64 tlk0_txf0 : 1; + } cn68xx; + struct cvmx_ilk_bist_sum_cn68xxp1 { + u64 reserved_58_63 : 6; + u64 rxf_x2p1 : 1; + u64 rxf_x2p0 : 1; + u64 rxf_pmap : 1; + u64 rxf_mem2 : 1; + u64 rxf_mem1 : 1; + u64 rxf_mem0 : 1; + u64 reserved_36_51 : 16; + u64 rle7_dsk1 : 1; + u64 rle7_dsk0 : 1; + u64 rle6_dsk1 : 1; + u64 rle6_dsk0 : 1; + u64 rle5_dsk1 : 1; + u64 rle5_dsk0 : 1; + u64 rle4_dsk1 : 1; + u64 rle4_dsk0 : 1; + u64 rle3_dsk1 : 1; + u64 rle3_dsk0 : 1; + u64 rle2_dsk1 : 1; + u64 rle2_dsk0 : 1; + u64 rle1_dsk1 : 1; + u64 rle1_dsk0 : 1; + u64 rle0_dsk1 : 1; + u64 rle0_dsk0 : 1; + u64 reserved_18_19 : 2; + u64 rlk1_fwc : 1; + u64 rlk1_stat : 1; + u64 reserved_14_15 : 2; + u64 rlk0_fwc : 1; + u64 rlk0_stat : 1; + u64 reserved_11_11 : 1; + u64 tlk1_fwc : 1; + u64 tlk1_stat : 1; + u64 tlk1_txf2 : 1; + u64 tlk1_txf1 : 1; + u64 tlk1_txf0 : 1; + u64 reserved_5_5 : 1; + u64 tlk0_fwc : 1; + u64 tlk0_stat : 1; + u64 tlk0_txf2 : 1; + u64 tlk0_txf1 : 1; + u64 tlk0_txf0 : 1; + } cn68xxp1; + struct cvmx_ilk_bist_sum_cn78xx { + u64 rxf_x2p : 1; + u64 rxf_mem19 : 1; + u64 rxf_mem18 : 1; + u64 rxf_mem17 : 1; + u64 rxf_mem16 : 1; + u64 rxf_mem15 : 1; + u64 rxf_mem14 : 1; + u64 rxf_mem13 : 1; + u64 rxf_mem12 : 1; + u64 rxf_mem11 : 1; + u64 rxf_mem10 : 1; + u64 rxf_mem9 : 1; + u64 rxf_mem8 : 1; + u64 rxf_mem7 : 1; + u64 rxf_mem6 : 1; + u64 rxf_mem5 : 1; + u64 rxf_mem4 : 1; + u64 rxf_mem3 : 1; + u64 rxf_mem2 : 1; + u64 rxf_mem1 : 1; + u64 rxf_mem0 : 1; + u64 reserved_36_42 : 7; + u64 rle7_dsk1 : 1; + u64 rle7_dsk0 : 1; + u64 rle6_dsk1 : 1; + u64 rle6_dsk0 : 1; + u64 rle5_dsk1 : 1; + u64 rle5_dsk0 : 1; + u64 rle4_dsk1 : 1; + u64 rle4_dsk0 : 1; + u64 rle3_dsk1 : 1; + u64 rle3_dsk0 : 1; + u64 rle2_dsk1 : 1; + u64 rle2_dsk0 : 1; + u64 rle1_dsk1 : 1; + u64 rle1_dsk0 : 1; + u64 rle0_dsk1 : 1; + u64 rle0_dsk0 : 1; + u64 rlk1_pmap : 1; + u64 rlk1_stat : 1; + u64 rlk1_fwc : 1; + u64 rlk1_stat1 : 1; + u64 rlk0_pmap : 1; + u64 rlk0_stat1 : 1; + u64 rlk0_fwc : 1; + u64 rlk0_stat : 1; + u64 tlk1_stat1 : 1; + u64 tlk1_fwc : 1; + u64 tlk1_stat0 : 1; + u64 tlk1_txf2 : 1; + u64 tlk1_txf1 : 1; + u64 tlk1_txf0 : 1; + u64 tlk0_stat1 : 1; + u64 tlk0_fwc : 1; + u64 tlk0_stat0 : 1; + u64 tlk0_txf2 : 1; + u64 tlk0_txf1 : 1; + u64 tlk0_txf0 : 1; + } cn78xx; + struct cvmx_ilk_bist_sum_cn78xx cn78xxp1; +}; + +typedef union cvmx_ilk_bist_sum cvmx_ilk_bist_sum_t; + +/** + * cvmx_ilk_gbl_cfg + */ +union cvmx_ilk_gbl_cfg { + u64 u64; + struct cvmx_ilk_gbl_cfg_s { + u64 reserved_4_63 : 60; + u64 rid_rstdis : 1; + u64 reset : 1; + u64 cclk_dis : 1; + u64 rxf_xlink : 1; + } s; + struct cvmx_ilk_gbl_cfg_s cn68xx; + struct cvmx_ilk_gbl_cfg_cn68xxp1 { + u64 reserved_2_63 : 62; + u64 cclk_dis : 1; + u64 rxf_xlink : 1; + } cn68xxp1; + struct cvmx_ilk_gbl_cfg_s cn78xx; + struct cvmx_ilk_gbl_cfg_s cn78xxp1; +}; + +typedef union cvmx_ilk_gbl_cfg cvmx_ilk_gbl_cfg_t; + +/** + * cvmx_ilk_gbl_err_cfg + */ +union cvmx_ilk_gbl_err_cfg { + u64 u64; + struct cvmx_ilk_gbl_err_cfg_s { + u64 reserved_20_63 : 44; + u64 rxf_flip : 2; + u64 x2p_flip : 2; + u64 reserved_2_15 : 14; + u64 rxf_cor_dis : 1; + u64 x2p_cor_dis : 1; + } s; + struct cvmx_ilk_gbl_err_cfg_s cn78xx; + struct cvmx_ilk_gbl_err_cfg_s cn78xxp1; +}; + +typedef union cvmx_ilk_gbl_err_cfg cvmx_ilk_gbl_err_cfg_t; + +/** + * cvmx_ilk_gbl_int + */ +union cvmx_ilk_gbl_int { + u64 u64; + struct cvmx_ilk_gbl_int_s { + u64 reserved_9_63 : 55; + u64 x2p_dbe : 1; + u64 x2p_sbe : 1; + u64 rxf_dbe : 1; + u64 rxf_sbe : 1; + u64 rxf_push_full : 1; + u64 rxf_pop_empty : 1; + u64 rxf_ctl_perr : 1; + u64 rxf_lnk1_perr : 1; + u64 rxf_lnk0_perr : 1; + } s; + struct cvmx_ilk_gbl_int_cn68xx { + u64 reserved_5_63 : 59; + u64 rxf_push_full : 1; + u64 rxf_pop_empty : 1; + u64 rxf_ctl_perr : 1; + u64 rxf_lnk1_perr : 1; + u64 rxf_lnk0_perr : 1; + } cn68xx; + struct cvmx_ilk_gbl_int_cn68xx cn68xxp1; + struct cvmx_ilk_gbl_int_s cn78xx; + struct cvmx_ilk_gbl_int_s cn78xxp1; +}; + +typedef union cvmx_ilk_gbl_int cvmx_ilk_gbl_int_t; + +/** + * cvmx_ilk_gbl_int_en + */ +union cvmx_ilk_gbl_int_en { + u64 u64; + struct cvmx_ilk_gbl_int_en_s { + u64 reserved_5_63 : 59; + u64 rxf_push_full : 1; + u64 rxf_pop_empty : 1; + u64 rxf_ctl_perr : 1; + u64 rxf_lnk1_perr : 1; + u64 rxf_lnk0_perr : 1; + } s; + struct cvmx_ilk_gbl_int_en_s cn68xx; + struct cvmx_ilk_gbl_int_en_s cn68xxp1; +}; + +typedef union cvmx_ilk_gbl_int_en cvmx_ilk_gbl_int_en_t; + +/** + * cvmx_ilk_int_sum + */ +union cvmx_ilk_int_sum { + u64 u64; + struct cvmx_ilk_int_sum_s { + u64 reserved_13_63 : 51; + u64 rle7_int : 1; + u64 rle6_int : 1; + u64 rle5_int : 1; + u64 rle4_int : 1; + u64 rle3_int : 1; + u64 rle2_int : 1; + u64 rle1_int : 1; + u64 rle0_int : 1; + u64 rlk1_int : 1; + u64 rlk0_int : 1; + u64 tlk1_int : 1; + u64 tlk0_int : 1; + u64 gbl_int : 1; + } s; + struct cvmx_ilk_int_sum_s cn68xx; + struct cvmx_ilk_int_sum_s cn68xxp1; +}; + +typedef union cvmx_ilk_int_sum cvmx_ilk_int_sum_t; + +/** + * cvmx_ilk_lne#_trn_ctl + */ +union cvmx_ilk_lnex_trn_ctl { + u64 u64; + struct cvmx_ilk_lnex_trn_ctl_s { + u64 reserved_4_63 : 60; + u64 trn_lock : 1; + u64 trn_done : 1; + u64 trn_ena : 1; + u64 eie_det : 1; + } s; + struct cvmx_ilk_lnex_trn_ctl_s cn78xx; + struct cvmx_ilk_lnex_trn_ctl_s cn78xxp1; +}; + +typedef union cvmx_ilk_lnex_trn_ctl cvmx_ilk_lnex_trn_ctl_t; + +/** + * cvmx_ilk_lne#_trn_ld + */ +union cvmx_ilk_lnex_trn_ld { + u64 u64; + struct cvmx_ilk_lnex_trn_ld_s { + u64 lp_manual : 1; + u64 reserved_49_62 : 14; + u64 ld_cu_val : 1; + u64 ld_cu_dat : 16; + u64 reserved_17_31 : 15; + u64 ld_sr_val : 1; + u64 ld_sr_dat : 16; + } s; + struct cvmx_ilk_lnex_trn_ld_s cn78xx; + struct cvmx_ilk_lnex_trn_ld_s cn78xxp1; +}; + +typedef union cvmx_ilk_lnex_trn_ld cvmx_ilk_lnex_trn_ld_t; + +/** + * cvmx_ilk_lne#_trn_lp + */ +union cvmx_ilk_lnex_trn_lp { + u64 u64; + struct cvmx_ilk_lnex_trn_lp_s { + u64 reserved_49_63 : 15; + u64 lp_cu_val : 1; + u64 lp_cu_dat : 16; + u64 reserved_17_31 : 15; + u64 lp_sr_val : 1; + u64 lp_sr_dat : 16; + } s; + struct cvmx_ilk_lnex_trn_lp_s cn78xx; + struct cvmx_ilk_lnex_trn_lp_s cn78xxp1; +}; + +typedef union cvmx_ilk_lnex_trn_lp cvmx_ilk_lnex_trn_lp_t; + +/** + * cvmx_ilk_lne_dbg + */ +union cvmx_ilk_lne_dbg { + u64 u64; + struct cvmx_ilk_lne_dbg_s { + u64 reserved_60_63 : 4; + u64 tx_bad_crc32 : 1; + u64 tx_bad_6467_cnt : 5; + u64 tx_bad_sync_cnt : 3; + u64 tx_bad_scram_cnt : 3; + u64 tx_bad_lane_sel : 16; + u64 tx_dis_dispr : 16; + u64 tx_dis_scram : 16; + } s; + struct cvmx_ilk_lne_dbg_cn68xx { + u64 reserved_60_63 : 4; + u64 tx_bad_crc32 : 1; + u64 tx_bad_6467_cnt : 5; + u64 tx_bad_sync_cnt : 3; + u64 tx_bad_scram_cnt : 3; + u64 reserved_40_47 : 8; + u64 tx_bad_lane_sel : 8; + u64 reserved_24_31 : 8; + u64 tx_dis_dispr : 8; + u64 reserved_8_15 : 8; + u64 tx_dis_scram : 8; + } cn68xx; + struct cvmx_ilk_lne_dbg_cn68xx cn68xxp1; + struct cvmx_ilk_lne_dbg_s cn78xx; + struct cvmx_ilk_lne_dbg_s cn78xxp1; +}; + +typedef union cvmx_ilk_lne_dbg cvmx_ilk_lne_dbg_t; + +/** + * cvmx_ilk_lne_sts_msg + */ +union cvmx_ilk_lne_sts_msg { + u64 u64; + struct cvmx_ilk_lne_sts_msg_s { + u64 rx_lnk_stat : 16; + u64 rx_lne_stat : 16; + u64 tx_lnk_stat : 16; + u64 tx_lne_stat : 16; + } s; + struct cvmx_ilk_lne_sts_msg_cn68xx { + u64 reserved_56_63 : 8; + u64 rx_lnk_stat : 8; + u64 reserved_40_47 : 8; + u64 rx_lne_stat : 8; + u64 reserved_24_31 : 8; + u64 tx_lnk_stat : 8; + u64 reserved_8_15 : 8; + u64 tx_lne_stat : 8; + } cn68xx; + struct cvmx_ilk_lne_sts_msg_cn68xx cn68xxp1; + struct cvmx_ilk_lne_sts_msg_s cn78xx; + struct cvmx_ilk_lne_sts_msg_s cn78xxp1; +}; + +typedef union cvmx_ilk_lne_sts_msg cvmx_ilk_lne_sts_msg_t; + +/** + * cvmx_ilk_rid_cfg + */ +union cvmx_ilk_rid_cfg { + u64 u64; + struct cvmx_ilk_rid_cfg_s { + u64 reserved_39_63 : 25; + u64 max_cnt : 7; + u64 reserved_7_31 : 25; + u64 base : 7; + } s; + struct cvmx_ilk_rid_cfg_s cn78xx; + struct cvmx_ilk_rid_cfg_s cn78xxp1; +}; + +typedef union cvmx_ilk_rid_cfg cvmx_ilk_rid_cfg_t; + +/** + * cvmx_ilk_rx#_byte_cnt# + */ +union cvmx_ilk_rxx_byte_cntx { + u64 u64; + struct cvmx_ilk_rxx_byte_cntx_s { + u64 reserved_40_63 : 24; + u64 rx_bytes : 40; + } s; + struct cvmx_ilk_rxx_byte_cntx_s cn78xx; + struct cvmx_ilk_rxx_byte_cntx_s cn78xxp1; +}; + +typedef union cvmx_ilk_rxx_byte_cntx cvmx_ilk_rxx_byte_cntx_t; + +/** + * cvmx_ilk_rx#_cal_entry# + */ +union cvmx_ilk_rxx_cal_entryx { + u64 u64; + struct cvmx_ilk_rxx_cal_entryx_s { + u64 reserved_34_63 : 30; + u64 ctl : 2; + u64 reserved_8_31 : 24; + u64 channel : 8; + } s; + struct cvmx_ilk_rxx_cal_entryx_s cn78xx; + struct cvmx_ilk_rxx_cal_entryx_s cn78xxp1; +}; + +typedef union cvmx_ilk_rxx_cal_entryx cvmx_ilk_rxx_cal_entryx_t; + +/** + * cvmx_ilk_rx#_cfg0 + */ +union cvmx_ilk_rxx_cfg0 { + u64 u64; + struct cvmx_ilk_rxx_cfg0_s { + u64 ext_lpbk_fc : 1; + u64 ext_lpbk : 1; + u64 reserved_60_61 : 2; + u64 lnk_stats_wrap : 1; + u64 bcw_push : 1; + u64 mproto_ign : 1; + u64 ptrn_mode : 1; + u64 lnk_stats_rdclr : 1; + u64 lnk_stats_ena : 1; + u64 mltuse_fc_ena : 1; + u64 cal_ena : 1; + u64 mfrm_len : 13; + u64 brst_shrt : 7; + u64 lane_rev : 1; + u64 brst_max : 5; + u64 reserved_25_25 : 1; + u64 cal_depth : 9; + u64 lane_ena : 16; + } s; + struct cvmx_ilk_rxx_cfg0_cn68xx { + u64 ext_lpbk_fc : 1; + u64 ext_lpbk : 1; + u64 reserved_60_61 : 2; + u64 lnk_stats_wrap : 1; + u64 bcw_push : 1; + u64 mproto_ign : 1; + u64 ptrn_mode : 1; + u64 lnk_stats_rdclr : 1; + u64 lnk_stats_ena : 1; + u64 mltuse_fc_ena : 1; + u64 cal_ena : 1; + u64 mfrm_len : 13; + u64 brst_shrt : 7; + u64 lane_rev : 1; + u64 brst_max : 5; + u64 reserved_25_25 : 1; + u64 cal_depth : 9; + u64 reserved_8_15 : 8; + u64 lane_ena : 8; + } cn68xx; + struct cvmx_ilk_rxx_cfg0_cn68xxp1 { + u64 ext_lpbk_fc : 1; + u64 ext_lpbk : 1; + u64 reserved_57_61 : 5; + u64 ptrn_mode : 1; + u64 lnk_stats_rdclr : 1; + u64 lnk_stats_ena : 1; + u64 mltuse_fc_ena : 1; + u64 cal_ena : 1; + u64 mfrm_len : 13; + u64 brst_shrt : 7; + u64 lane_rev : 1; + u64 brst_max : 5; + u64 reserved_25_25 : 1; + u64 cal_depth : 9; + u64 reserved_8_15 : 8; + u64 lane_ena : 8; + } cn68xxp1; + struct cvmx_ilk_rxx_cfg0_s cn78xx; + struct cvmx_ilk_rxx_cfg0_s cn78xxp1; +}; + +typedef union cvmx_ilk_rxx_cfg0 cvmx_ilk_rxx_cfg0_t; + +/** + * cvmx_ilk_rx#_cfg1 + */ +union cvmx_ilk_rxx_cfg1 { + u64 u64; + struct cvmx_ilk_rxx_cfg1_s { + u64 reserved_62_63 : 2; + u64 rx_fifo_cnt : 12; + u64 reserved_49_49 : 1; + u64 rx_fifo_hwm : 13; + u64 reserved_35_35 : 1; + u64 rx_fifo_max : 13; + u64 pkt_flush : 1; + u64 pkt_ena : 1; + u64 la_mode : 1; + u64 tx_link_fc : 1; + u64 rx_link_fc : 1; + u64 rx_align_ena : 1; + u64 rx_bdry_lock_ena : 16; + } s; + struct cvmx_ilk_rxx_cfg1_cn68xx { + u64 reserved_62_63 : 2; + u64 rx_fifo_cnt : 12; + u64 reserved_48_49 : 2; + u64 rx_fifo_hwm : 12; + u64 reserved_34_35 : 2; + u64 rx_fifo_max : 12; + u64 pkt_flush : 1; + u64 pkt_ena : 1; + u64 la_mode : 1; + u64 tx_link_fc : 1; + u64 rx_link_fc : 1; + u64 rx_align_ena : 1; + u64 reserved_8_15 : 8; + u64 rx_bdry_lock_ena : 8; + } cn68xx; + struct cvmx_ilk_rxx_cfg1_cn68xx cn68xxp1; + struct cvmx_ilk_rxx_cfg1_s cn78xx; + struct cvmx_ilk_rxx_cfg1_s cn78xxp1; +}; + +typedef union cvmx_ilk_rxx_cfg1 cvmx_ilk_rxx_cfg1_t; + +/** + * cvmx_ilk_rx#_cha# + */ +union cvmx_ilk_rxx_chax { + u64 u64; + struct cvmx_ilk_rxx_chax_s { + u64 reserved_6_63 : 58; + u64 port_kind : 6; + } s; + struct cvmx_ilk_rxx_chax_s cn78xx; + struct cvmx_ilk_rxx_chax_s cn78xxp1; +}; + +typedef union cvmx_ilk_rxx_chax cvmx_ilk_rxx_chax_t; + +/** + * cvmx_ilk_rx#_cha_xon# + */ +union cvmx_ilk_rxx_cha_xonx { + u64 u64; + struct cvmx_ilk_rxx_cha_xonx_s { + u64 xon : 64; + } s; + struct cvmx_ilk_rxx_cha_xonx_s cn78xx; + struct cvmx_ilk_rxx_cha_xonx_s cn78xxp1; +}; + +typedef union cvmx_ilk_rxx_cha_xonx cvmx_ilk_rxx_cha_xonx_t; + +/** + * cvmx_ilk_rx#_err_cfg + */ +union cvmx_ilk_rxx_err_cfg { + u64 u64; + struct cvmx_ilk_rxx_err_cfg_s { + u64 reserved_20_63 : 44; + u64 fwc_flip : 2; + u64 pmap_flip : 2; + u64 reserved_2_15 : 14; + u64 fwc_cor_dis : 1; + u64 pmap_cor_dis : 1; + } s; + struct cvmx_ilk_rxx_err_cfg_s cn78xx; + struct cvmx_ilk_rxx_err_cfg_s cn78xxp1; +}; + +typedef union cvmx_ilk_rxx_err_cfg cvmx_ilk_rxx_err_cfg_t; + +/** + * cvmx_ilk_rx#_flow_ctl0 + */ +union cvmx_ilk_rxx_flow_ctl0 { + u64 u64; + struct cvmx_ilk_rxx_flow_ctl0_s { + u64 status : 64; + } s; + struct cvmx_ilk_rxx_flow_ctl0_s cn68xx; + struct cvmx_ilk_rxx_flow_ctl0_s cn68xxp1; +}; + +typedef union cvmx_ilk_rxx_flow_ctl0 cvmx_ilk_rxx_flow_ctl0_t; + +/** + * cvmx_ilk_rx#_flow_ctl1 + */ +union cvmx_ilk_rxx_flow_ctl1 { + u64 u64; + struct cvmx_ilk_rxx_flow_ctl1_s { + u64 status : 64; + } s; + struct cvmx_ilk_rxx_flow_ctl1_s cn68xx; + struct cvmx_ilk_rxx_flow_ctl1_s cn68xxp1; +}; + +typedef union cvmx_ilk_rxx_flow_ctl1 cvmx_ilk_rxx_flow_ctl1_t; + +/** + * cvmx_ilk_rx#_idx_cal + */ +union cvmx_ilk_rxx_idx_cal { + u64 u64; + struct cvmx_ilk_rxx_idx_cal_s { + u64 reserved_14_63 : 50; + u64 inc : 6; + u64 reserved_6_7 : 2; + u64 index : 6; + } s; + struct cvmx_ilk_rxx_idx_cal_s cn68xx; + struct cvmx_ilk_rxx_idx_cal_s cn68xxp1; +}; + +typedef union cvmx_ilk_rxx_idx_cal cvmx_ilk_rxx_idx_cal_t; + +/** + * cvmx_ilk_rx#_idx_stat0 + */ +union cvmx_ilk_rxx_idx_stat0 { + u64 u64; + struct cvmx_ilk_rxx_idx_stat0_s { + u64 reserved_32_63 : 32; + u64 clr : 1; + u64 reserved_24_30 : 7; + u64 inc : 8; + u64 reserved_8_15 : 8; + u64 index : 8; + } s; + struct cvmx_ilk_rxx_idx_stat0_s cn68xx; + struct cvmx_ilk_rxx_idx_stat0_s cn68xxp1; +}; + +typedef union cvmx_ilk_rxx_idx_stat0 cvmx_ilk_rxx_idx_stat0_t; + +/** + * cvmx_ilk_rx#_idx_stat1 + */ +union cvmx_ilk_rxx_idx_stat1 { + u64 u64; + struct cvmx_ilk_rxx_idx_stat1_s { + u64 reserved_32_63 : 32; + u64 clr : 1; + u64 reserved_24_30 : 7; + u64 inc : 8; + u64 reserved_8_15 : 8; + u64 index : 8; + } s; + struct cvmx_ilk_rxx_idx_stat1_s cn68xx; + struct cvmx_ilk_rxx_idx_stat1_s cn68xxp1; +}; + +typedef union cvmx_ilk_rxx_idx_stat1 cvmx_ilk_rxx_idx_stat1_t; + +/** + * cvmx_ilk_rx#_int + */ +union cvmx_ilk_rxx_int { + u64 u64; + struct cvmx_ilk_rxx_int_s { + u64 reserved_13_63 : 51; + u64 pmap_dbe : 1; + u64 pmap_sbe : 1; + u64 fwc_dbe : 1; + u64 fwc_sbe : 1; + u64 pkt_drop_sop : 1; + u64 pkt_drop_rid : 1; + u64 pkt_drop_rxf : 1; + u64 lane_bad_word : 1; + u64 stat_cnt_ovfl : 1; + u64 lane_align_done : 1; + u64 word_sync_done : 1; + u64 crc24_err : 1; + u64 lane_align_fail : 1; + } s; + struct cvmx_ilk_rxx_int_cn68xx { + u64 reserved_9_63 : 55; + u64 pkt_drop_sop : 1; + u64 pkt_drop_rid : 1; + u64 pkt_drop_rxf : 1; + u64 lane_bad_word : 1; + u64 stat_cnt_ovfl : 1; + u64 lane_align_done : 1; + u64 word_sync_done : 1; + u64 crc24_err : 1; + u64 lane_align_fail : 1; + } cn68xx; + struct cvmx_ilk_rxx_int_cn68xxp1 { + u64 reserved_8_63 : 56; + u64 pkt_drop_rid : 1; + u64 pkt_drop_rxf : 1; + u64 lane_bad_word : 1; + u64 stat_cnt_ovfl : 1; + u64 lane_align_done : 1; + u64 word_sync_done : 1; + u64 crc24_err : 1; + u64 lane_align_fail : 1; + } cn68xxp1; + struct cvmx_ilk_rxx_int_s cn78xx; + struct cvmx_ilk_rxx_int_s cn78xxp1; +}; + +typedef union cvmx_ilk_rxx_int cvmx_ilk_rxx_int_t; + +/** + * cvmx_ilk_rx#_int_en + */ +union cvmx_ilk_rxx_int_en { + u64 u64; + struct cvmx_ilk_rxx_int_en_s { + u64 reserved_9_63 : 55; + u64 pkt_drop_sop : 1; + u64 pkt_drop_rid : 1; + u64 pkt_drop_rxf : 1; + u64 lane_bad_word : 1; + u64 stat_cnt_ovfl : 1; + u64 lane_align_done : 1; + u64 word_sync_done : 1; + u64 crc24_err : 1; + u64 lane_align_fail : 1; + } s; + struct cvmx_ilk_rxx_int_en_s cn68xx; + struct cvmx_ilk_rxx_int_en_cn68xxp1 { + u64 reserved_8_63 : 56; + u64 pkt_drop_rid : 1; + u64 pkt_drop_rxf : 1; + u64 lane_bad_word : 1; + u64 stat_cnt_ovfl : 1; + u64 lane_align_done : 1; + u64 word_sync_done : 1; + u64 crc24_err : 1; + u64 lane_align_fail : 1; + } cn68xxp1; +}; + +typedef union cvmx_ilk_rxx_int_en cvmx_ilk_rxx_int_en_t; + +/** + * cvmx_ilk_rx#_jabber + */ +union cvmx_ilk_rxx_jabber { + u64 u64; + struct cvmx_ilk_rxx_jabber_s { + u64 reserved_16_63 : 48; + u64 cnt : 16; + } s; + struct cvmx_ilk_rxx_jabber_s cn68xx; + struct cvmx_ilk_rxx_jabber_s cn68xxp1; + struct cvmx_ilk_rxx_jabber_s cn78xx; + struct cvmx_ilk_rxx_jabber_s cn78xxp1; +}; + +typedef union cvmx_ilk_rxx_jabber cvmx_ilk_rxx_jabber_t; + +/** + * cvmx_ilk_rx#_mem_cal0 + * + * Notes: + * Software must program the calendar table prior to enabling the + * link. + * + * Software must always write ILK_RXx_MEM_CAL0 then ILK_RXx_MEM_CAL1. + * Software must never write them in reverse order or write one without + * writing the other. + * + * A given calendar table entry has no effect on PKO pipe + * backpressure when either: + * - ENTRY_CTLx=Link (1), or + * - ENTRY_CTLx=XON (3) and PORT_PIPEx is outside the range of ILK_TXx_PIPE[BASE/NUMP]. + * + * Within the 8 calendar table entries of one IDX value, if more + * than one affects the same PKO pipe, XOFF always wins over XON, + * regardless of the calendar table order. + * + * Software must always read ILK_RXx_MEM_CAL0 then ILK_RXx_MEM_CAL1. Software + * must never read them in reverse order or read one without reading the + * other. + */ +union cvmx_ilk_rxx_mem_cal0 { + u64 u64; + struct cvmx_ilk_rxx_mem_cal0_s { + u64 reserved_36_63 : 28; + u64 entry_ctl3 : 2; + u64 port_pipe3 : 7; + u64 entry_ctl2 : 2; + u64 port_pipe2 : 7; + u64 entry_ctl1 : 2; + u64 port_pipe1 : 7; + u64 entry_ctl0 : 2; + u64 port_pipe0 : 7; + } s; + struct cvmx_ilk_rxx_mem_cal0_s cn68xx; + struct cvmx_ilk_rxx_mem_cal0_s cn68xxp1; +}; + +typedef union cvmx_ilk_rxx_mem_cal0 cvmx_ilk_rxx_mem_cal0_t; + +/** + * cvmx_ilk_rx#_mem_cal1 + * + * Notes: + * Software must program the calendar table prior to enabling the + * link. + * + * Software must always write ILK_RXx_MEM_CAL0 then ILK_RXx_MEM_CAL1. + * Software must never write them in reverse order or write one without + * writing the other. + * + * A given calendar table entry has no effect on PKO pipe + * backpressure when either: + * - ENTRY_CTLx=Link (1), or + * - ENTRY_CTLx=XON (3) and PORT_PIPEx is outside the range of ILK_TXx_PIPE[BASE/NUMP]. + * + * Within the 8 calendar table entries of one IDX value, if more + * than one affects the same PKO pipe, XOFF always wins over XON, + * regardless of the calendar table order. + * + * Software must always read ILK_RXx_MEM_CAL0 then ILK_Rx_MEM_CAL1. Software + * must never read them in reverse order or read one without reading the + * other. + */ +union cvmx_ilk_rxx_mem_cal1 { + u64 u64; + struct cvmx_ilk_rxx_mem_cal1_s { + u64 reserved_36_63 : 28; + u64 entry_ctl7 : 2; + u64 port_pipe7 : 7; + u64 entry_ctl6 : 2; + u64 port_pipe6 : 7; + u64 entry_ctl5 : 2; + u64 port_pipe5 : 7; + u64 entry_ctl4 : 2; + u64 port_pipe4 : 7; + } s; + struct cvmx_ilk_rxx_mem_cal1_s cn68xx; + struct cvmx_ilk_rxx_mem_cal1_s cn68xxp1; +}; + +typedef union cvmx_ilk_rxx_mem_cal1 cvmx_ilk_rxx_mem_cal1_t; + +/** + * cvmx_ilk_rx#_mem_stat0 + */ +union cvmx_ilk_rxx_mem_stat0 { + u64 u64; + struct cvmx_ilk_rxx_mem_stat0_s { + u64 reserved_28_63 : 36; + u64 rx_pkt : 28; + } s; + struct cvmx_ilk_rxx_mem_stat0_s cn68xx; + struct cvmx_ilk_rxx_mem_stat0_s cn68xxp1; +}; + +typedef union cvmx_ilk_rxx_mem_stat0 cvmx_ilk_rxx_mem_stat0_t; + +/** + * cvmx_ilk_rx#_mem_stat1 + */ +union cvmx_ilk_rxx_mem_stat1 { + u64 u64; + struct cvmx_ilk_rxx_mem_stat1_s { + u64 reserved_36_63 : 28; + u64 rx_bytes : 36; + } s; + struct cvmx_ilk_rxx_mem_stat1_s cn68xx; + struct cvmx_ilk_rxx_mem_stat1_s cn68xxp1; +}; + +typedef union cvmx_ilk_rxx_mem_stat1 cvmx_ilk_rxx_mem_stat1_t; + +/** + * cvmx_ilk_rx#_pkt_cnt# + */ +union cvmx_ilk_rxx_pkt_cntx { + u64 u64; + struct cvmx_ilk_rxx_pkt_cntx_s { + u64 reserved_34_63 : 30; + u64 rx_pkt : 34; + } s; + struct cvmx_ilk_rxx_pkt_cntx_s cn78xx; + struct cvmx_ilk_rxx_pkt_cntx_s cn78xxp1; +}; + +typedef union cvmx_ilk_rxx_pkt_cntx cvmx_ilk_rxx_pkt_cntx_t; + +/** + * cvmx_ilk_rx#_rid + */ +union cvmx_ilk_rxx_rid { + u64 u64; + struct cvmx_ilk_rxx_rid_s { + u64 reserved_7_63 : 57; + u64 max_cnt : 7; + } s; + struct cvmx_ilk_rxx_rid_cn68xx { + u64 reserved_6_63 : 58; + u64 max_cnt : 6; + } cn68xx; + struct cvmx_ilk_rxx_rid_s cn78xx; + struct cvmx_ilk_rxx_rid_s cn78xxp1; +}; + +typedef union cvmx_ilk_rxx_rid cvmx_ilk_rxx_rid_t; + +/** + * cvmx_ilk_rx#_stat0 + */ +union cvmx_ilk_rxx_stat0 { + u64 u64; + struct cvmx_ilk_rxx_stat0_s { + u64 reserved_35_63 : 29; + u64 crc24_match_cnt : 35; + } s; + struct cvmx_ilk_rxx_stat0_cn68xx { + u64 reserved_33_63 : 31; + u64 crc24_match_cnt : 33; + } cn68xx; + struct cvmx_ilk_rxx_stat0_cn68xxp1 { + u64 reserved_27_63 : 37; + u64 crc24_match_cnt : 27; + } cn68xxp1; + struct cvmx_ilk_rxx_stat0_s cn78xx; + struct cvmx_ilk_rxx_stat0_s cn78xxp1; +}; + +typedef union cvmx_ilk_rxx_stat0 cvmx_ilk_rxx_stat0_t; + +/** + * cvmx_ilk_rx#_stat1 + */ +union cvmx_ilk_rxx_stat1 { + u64 u64; + struct cvmx_ilk_rxx_stat1_s { + u64 reserved_20_63 : 44; + u64 crc24_err_cnt : 20; + } s; + struct cvmx_ilk_rxx_stat1_cn68xx { + u64 reserved_18_63 : 46; + u64 crc24_err_cnt : 18; + } cn68xx; + struct cvmx_ilk_rxx_stat1_cn68xx cn68xxp1; + struct cvmx_ilk_rxx_stat1_s cn78xx; + struct cvmx_ilk_rxx_stat1_s cn78xxp1; +}; + +typedef union cvmx_ilk_rxx_stat1 cvmx_ilk_rxx_stat1_t; + +/** + * cvmx_ilk_rx#_stat2 + */ +union cvmx_ilk_rxx_stat2 { + u64 u64; + struct cvmx_ilk_rxx_stat2_s { + u64 reserved_50_63 : 14; + u64 brst_not_full_cnt : 18; + u64 reserved_30_31 : 2; + u64 brst_cnt : 30; + } s; + struct cvmx_ilk_rxx_stat2_cn68xx { + u64 reserved_48_63 : 16; + u64 brst_not_full_cnt : 16; + u64 reserved_28_31 : 4; + u64 brst_cnt : 28; + } cn68xx; + struct cvmx_ilk_rxx_stat2_cn68xxp1 { + u64 reserved_48_63 : 16; + u64 brst_not_full_cnt : 16; + u64 reserved_16_31 : 16; + u64 brst_cnt : 16; + } cn68xxp1; + struct cvmx_ilk_rxx_stat2_s cn78xx; + struct cvmx_ilk_rxx_stat2_s cn78xxp1; +}; + +typedef union cvmx_ilk_rxx_stat2 cvmx_ilk_rxx_stat2_t; + +/** + * cvmx_ilk_rx#_stat3 + */ +union cvmx_ilk_rxx_stat3 { + u64 u64; + struct cvmx_ilk_rxx_stat3_s { + u64 reserved_18_63 : 46; + u64 brst_max_err_cnt : 18; + } s; + struct cvmx_ilk_rxx_stat3_cn68xx { + u64 reserved_16_63 : 48; + u64 brst_max_err_cnt : 16; + } cn68xx; + struct cvmx_ilk_rxx_stat3_cn68xx cn68xxp1; + struct cvmx_ilk_rxx_stat3_s cn78xx; + struct cvmx_ilk_rxx_stat3_s cn78xxp1; +}; + +typedef union cvmx_ilk_rxx_stat3 cvmx_ilk_rxx_stat3_t; + +/** + * cvmx_ilk_rx#_stat4 + */ +union cvmx_ilk_rxx_stat4 { + u64 u64; + struct cvmx_ilk_rxx_stat4_s { + u64 reserved_18_63 : 46; + u64 brst_shrt_err_cnt : 18; + } s; + struct cvmx_ilk_rxx_stat4_cn68xx { + u64 reserved_16_63 : 48; + u64 brst_shrt_err_cnt : 16; + } cn68xx; + struct cvmx_ilk_rxx_stat4_cn68xx cn68xxp1; + struct cvmx_ilk_rxx_stat4_s cn78xx; + struct cvmx_ilk_rxx_stat4_s cn78xxp1; +}; + +typedef union cvmx_ilk_rxx_stat4 cvmx_ilk_rxx_stat4_t; + +/** + * cvmx_ilk_rx#_stat5 + */ +union cvmx_ilk_rxx_stat5 { + u64 u64; + struct cvmx_ilk_rxx_stat5_s { + u64 reserved_25_63 : 39; + u64 align_cnt : 25; + } s; + struct cvmx_ilk_rxx_stat5_cn68xx { + u64 reserved_23_63 : 41; + u64 align_cnt : 23; + } cn68xx; + struct cvmx_ilk_rxx_stat5_cn68xxp1 { + u64 reserved_16_63 : 48; + u64 align_cnt : 16; + } cn68xxp1; + struct cvmx_ilk_rxx_stat5_s cn78xx; + struct cvmx_ilk_rxx_stat5_s cn78xxp1; +}; + +typedef union cvmx_ilk_rxx_stat5 cvmx_ilk_rxx_stat5_t; + +/** + * cvmx_ilk_rx#_stat6 + */ +union cvmx_ilk_rxx_stat6 { + u64 u64; + struct cvmx_ilk_rxx_stat6_s { + u64 reserved_18_63 : 46; + u64 align_err_cnt : 18; + } s; + struct cvmx_ilk_rxx_stat6_cn68xx { + u64 reserved_16_63 : 48; + u64 align_err_cnt : 16; + } cn68xx; + struct cvmx_ilk_rxx_stat6_cn68xx cn68xxp1; + struct cvmx_ilk_rxx_stat6_s cn78xx; + struct cvmx_ilk_rxx_stat6_s cn78xxp1; +}; + +typedef union cvmx_ilk_rxx_stat6 cvmx_ilk_rxx_stat6_t; + +/** + * cvmx_ilk_rx#_stat7 + */ +union cvmx_ilk_rxx_stat7 { + u64 u64; + struct cvmx_ilk_rxx_stat7_s { + u64 reserved_18_63 : 46; + u64 bad_64b67b_cnt : 18; + } s; + struct cvmx_ilk_rxx_stat7_cn68xx { + u64 reserved_16_63 : 48; + u64 bad_64b67b_cnt : 16; + } cn68xx; + struct cvmx_ilk_rxx_stat7_cn68xx cn68xxp1; + struct cvmx_ilk_rxx_stat7_s cn78xx; + struct cvmx_ilk_rxx_stat7_s cn78xxp1; +}; + +typedef union cvmx_ilk_rxx_stat7 cvmx_ilk_rxx_stat7_t; + +/** + * cvmx_ilk_rx#_stat8 + */ +union cvmx_ilk_rxx_stat8 { + u64 u64; + struct cvmx_ilk_rxx_stat8_s { + u64 reserved_32_63 : 32; + u64 pkt_drop_rid_cnt : 16; + u64 pkt_drop_rxf_cnt : 16; + } s; + struct cvmx_ilk_rxx_stat8_s cn68xx; + struct cvmx_ilk_rxx_stat8_s cn68xxp1; + struct cvmx_ilk_rxx_stat8_s cn78xx; + struct cvmx_ilk_rxx_stat8_s cn78xxp1; +}; + +typedef union cvmx_ilk_rxx_stat8 cvmx_ilk_rxx_stat8_t; + +/** + * cvmx_ilk_rx#_stat9 + * + * This register is reserved. + * + */ +union cvmx_ilk_rxx_stat9 { + u64 u64; + struct cvmx_ilk_rxx_stat9_s { + u64 reserved_0_63 : 64; + } s; + struct cvmx_ilk_rxx_stat9_s cn68xx; + struct cvmx_ilk_rxx_stat9_s cn68xxp1; + struct cvmx_ilk_rxx_stat9_s cn78xx; + struct cvmx_ilk_rxx_stat9_s cn78xxp1; +}; + +typedef union cvmx_ilk_rxx_stat9 cvmx_ilk_rxx_stat9_t; + +/** + * cvmx_ilk_rx_lne#_cfg + */ +union cvmx_ilk_rx_lnex_cfg { + u64 u64; + struct cvmx_ilk_rx_lnex_cfg_s { + u64 reserved_9_63 : 55; + u64 rx_dis_psh_skip : 1; + u64 reserved_7_7 : 1; + u64 rx_dis_disp_chk : 1; + u64 rx_scrm_sync : 1; + u64 rx_bdry_sync : 1; + u64 rx_dis_ukwn : 1; + u64 rx_dis_scram : 1; + u64 stat_rdclr : 1; + u64 stat_ena : 1; + } s; + struct cvmx_ilk_rx_lnex_cfg_cn68xx { + u64 reserved_9_63 : 55; + u64 rx_dis_psh_skip : 1; + u64 reserved_6_7 : 2; + u64 rx_scrm_sync : 1; + u64 rx_bdry_sync : 1; + u64 rx_dis_ukwn : 1; + u64 rx_dis_scram : 1; + u64 stat_rdclr : 1; + u64 stat_ena : 1; + } cn68xx; + struct cvmx_ilk_rx_lnex_cfg_cn68xxp1 { + u64 reserved_5_63 : 59; + u64 rx_bdry_sync : 1; + u64 rx_dis_ukwn : 1; + u64 rx_dis_scram : 1; + u64 stat_rdclr : 1; + u64 stat_ena : 1; + } cn68xxp1; + struct cvmx_ilk_rx_lnex_cfg_s cn78xx; + struct cvmx_ilk_rx_lnex_cfg_s cn78xxp1; +}; + +typedef union cvmx_ilk_rx_lnex_cfg cvmx_ilk_rx_lnex_cfg_t; + +/** + * cvmx_ilk_rx_lne#_int + */ +union cvmx_ilk_rx_lnex_int { + u64 u64; + struct cvmx_ilk_rx_lnex_int_s { + u64 reserved_10_63 : 54; + u64 disp_err : 1; + u64 bad_64b67b : 1; + u64 stat_cnt_ovfl : 1; + u64 stat_msg : 1; + u64 dskew_fifo_ovfl : 1; + u64 scrm_sync_loss : 1; + u64 ukwn_cntl_word : 1; + u64 crc32_err : 1; + u64 bdry_sync_loss : 1; + u64 serdes_lock_loss : 1; + } s; + struct cvmx_ilk_rx_lnex_int_cn68xx { + u64 reserved_9_63 : 55; + u64 bad_64b67b : 1; + u64 stat_cnt_ovfl : 1; + u64 stat_msg : 1; + u64 dskew_fifo_ovfl : 1; + u64 scrm_sync_loss : 1; + u64 ukwn_cntl_word : 1; + u64 crc32_err : 1; + u64 bdry_sync_loss : 1; + u64 serdes_lock_loss : 1; + } cn68xx; + struct cvmx_ilk_rx_lnex_int_cn68xx cn68xxp1; + struct cvmx_ilk_rx_lnex_int_s cn78xx; + struct cvmx_ilk_rx_lnex_int_s cn78xxp1; +}; + +typedef union cvmx_ilk_rx_lnex_int cvmx_ilk_rx_lnex_int_t; + +/** + * cvmx_ilk_rx_lne#_int_en + */ +union cvmx_ilk_rx_lnex_int_en { + u64 u64; + struct cvmx_ilk_rx_lnex_int_en_s { + u64 reserved_9_63 : 55; + u64 bad_64b67b : 1; + u64 stat_cnt_ovfl : 1; + u64 stat_msg : 1; + u64 dskew_fifo_ovfl : 1; + u64 scrm_sync_loss : 1; + u64 ukwn_cntl_word : 1; + u64 crc32_err : 1; + u64 bdry_sync_loss : 1; + u64 serdes_lock_loss : 1; + } s; + struct cvmx_ilk_rx_lnex_int_en_s cn68xx; + struct cvmx_ilk_rx_lnex_int_en_s cn68xxp1; +}; + +typedef union cvmx_ilk_rx_lnex_int_en cvmx_ilk_rx_lnex_int_en_t; + +/** + * cvmx_ilk_rx_lne#_stat0 + */ +union cvmx_ilk_rx_lnex_stat0 { + u64 u64; + struct cvmx_ilk_rx_lnex_stat0_s { + u64 reserved_18_63 : 46; + u64 ser_lock_loss_cnt : 18; + } s; + struct cvmx_ilk_rx_lnex_stat0_s cn68xx; + struct cvmx_ilk_rx_lnex_stat0_s cn68xxp1; + struct cvmx_ilk_rx_lnex_stat0_s cn78xx; + struct cvmx_ilk_rx_lnex_stat0_s cn78xxp1; +}; + +typedef union cvmx_ilk_rx_lnex_stat0 cvmx_ilk_rx_lnex_stat0_t; + +/** + * cvmx_ilk_rx_lne#_stat1 + */ +union cvmx_ilk_rx_lnex_stat1 { + u64 u64; + struct cvmx_ilk_rx_lnex_stat1_s { + u64 reserved_18_63 : 46; + u64 bdry_sync_loss_cnt : 18; + } s; + struct cvmx_ilk_rx_lnex_stat1_s cn68xx; + struct cvmx_ilk_rx_lnex_stat1_s cn68xxp1; + struct cvmx_ilk_rx_lnex_stat1_s cn78xx; + struct cvmx_ilk_rx_lnex_stat1_s cn78xxp1; +}; + +typedef union cvmx_ilk_rx_lnex_stat1 cvmx_ilk_rx_lnex_stat1_t; + +/** + * cvmx_ilk_rx_lne#_stat10 + */ +union cvmx_ilk_rx_lnex_stat10 { + u64 u64; + struct cvmx_ilk_rx_lnex_stat10_s { + u64 reserved_43_63 : 21; + u64 prbs_bad : 11; + u64 reserved_11_31 : 21; + u64 prbs_good : 11; + } s; + struct cvmx_ilk_rx_lnex_stat10_s cn78xx; + struct cvmx_ilk_rx_lnex_stat10_s cn78xxp1; +}; + +typedef union cvmx_ilk_rx_lnex_stat10 cvmx_ilk_rx_lnex_stat10_t; + +/** + * cvmx_ilk_rx_lne#_stat2 + */ +union cvmx_ilk_rx_lnex_stat2 { + u64 u64; + struct cvmx_ilk_rx_lnex_stat2_s { + u64 reserved_50_63 : 14; + u64 syncw_good_cnt : 18; + u64 reserved_18_31 : 14; + u64 syncw_bad_cnt : 18; + } s; + struct cvmx_ilk_rx_lnex_stat2_s cn68xx; + struct cvmx_ilk_rx_lnex_stat2_s cn68xxp1; + struct cvmx_ilk_rx_lnex_stat2_s cn78xx; + struct cvmx_ilk_rx_lnex_stat2_s cn78xxp1; +}; + +typedef union cvmx_ilk_rx_lnex_stat2 cvmx_ilk_rx_lnex_stat2_t; + +/** + * cvmx_ilk_rx_lne#_stat3 + */ +union cvmx_ilk_rx_lnex_stat3 { + u64 u64; + struct cvmx_ilk_rx_lnex_stat3_s { + u64 reserved_18_63 : 46; + u64 bad_64b67b_cnt : 18; + } s; + struct cvmx_ilk_rx_lnex_stat3_s cn68xx; + struct cvmx_ilk_rx_lnex_stat3_s cn68xxp1; + struct cvmx_ilk_rx_lnex_stat3_s cn78xx; + struct cvmx_ilk_rx_lnex_stat3_s cn78xxp1; +}; + +typedef union cvmx_ilk_rx_lnex_stat3 cvmx_ilk_rx_lnex_stat3_t; + +/** + * cvmx_ilk_rx_lne#_stat4 + */ +union cvmx_ilk_rx_lnex_stat4 { + u64 u64; + struct cvmx_ilk_rx_lnex_stat4_s { + u64 reserved_59_63 : 5; + u64 cntl_word_cnt : 27; + u64 reserved_27_31 : 5; + u64 data_word_cnt : 27; + } s; + struct cvmx_ilk_rx_lnex_stat4_s cn68xx; + struct cvmx_ilk_rx_lnex_stat4_s cn68xxp1; + struct cvmx_ilk_rx_lnex_stat4_s cn78xx; + struct cvmx_ilk_rx_lnex_stat4_s cn78xxp1; +}; + +typedef union cvmx_ilk_rx_lnex_stat4 cvmx_ilk_rx_lnex_stat4_t; + +/** + * cvmx_ilk_rx_lne#_stat5 + */ +union cvmx_ilk_rx_lnex_stat5 { + u64 u64; + struct cvmx_ilk_rx_lnex_stat5_s { + u64 reserved_18_63 : 46; + u64 unkwn_word_cnt : 18; + } s; + struct cvmx_ilk_rx_lnex_stat5_s cn68xx; + struct cvmx_ilk_rx_lnex_stat5_s cn68xxp1; + struct cvmx_ilk_rx_lnex_stat5_s cn78xx; + struct cvmx_ilk_rx_lnex_stat5_s cn78xxp1; +}; + +typedef union cvmx_ilk_rx_lnex_stat5 cvmx_ilk_rx_lnex_stat5_t; + +/** + * cvmx_ilk_rx_lne#_stat6 + */ +union cvmx_ilk_rx_lnex_stat6 { + u64 u64; + struct cvmx_ilk_rx_lnex_stat6_s { + u64 reserved_18_63 : 46; + u64 scrm_sync_loss_cnt : 18; + } s; + struct cvmx_ilk_rx_lnex_stat6_s cn68xx; + struct cvmx_ilk_rx_lnex_stat6_s cn68xxp1; + struct cvmx_ilk_rx_lnex_stat6_s cn78xx; + struct cvmx_ilk_rx_lnex_stat6_s cn78xxp1; +}; + +typedef union cvmx_ilk_rx_lnex_stat6 cvmx_ilk_rx_lnex_stat6_t; + +/** + * cvmx_ilk_rx_lne#_stat7 + */ +union cvmx_ilk_rx_lnex_stat7 { + u64 u64; + struct cvmx_ilk_rx_lnex_stat7_s { + u64 reserved_18_63 : 46; + u64 scrm_match_cnt : 18; + } s; + struct cvmx_ilk_rx_lnex_stat7_s cn68xx; + struct cvmx_ilk_rx_lnex_stat7_s cn68xxp1; + struct cvmx_ilk_rx_lnex_stat7_s cn78xx; + struct cvmx_ilk_rx_lnex_stat7_s cn78xxp1; +}; + +typedef union cvmx_ilk_rx_lnex_stat7 cvmx_ilk_rx_lnex_stat7_t; + +/** + * cvmx_ilk_rx_lne#_stat8 + */ +union cvmx_ilk_rx_lnex_stat8 { + u64 u64; + struct cvmx_ilk_rx_lnex_stat8_s { + u64 reserved_18_63 : 46; + u64 skipw_good_cnt : 18; + } s; + struct cvmx_ilk_rx_lnex_stat8_s cn68xx; + struct cvmx_ilk_rx_lnex_stat8_s cn68xxp1; + struct cvmx_ilk_rx_lnex_stat8_s cn78xx; + struct cvmx_ilk_rx_lnex_stat8_s cn78xxp1; +}; + +typedef union cvmx_ilk_rx_lnex_stat8 cvmx_ilk_rx_lnex_stat8_t; + +/** + * cvmx_ilk_rx_lne#_stat9 + */ +union cvmx_ilk_rx_lnex_stat9 { + u64 u64; + struct cvmx_ilk_rx_lnex_stat9_s { + u64 reserved_50_63 : 14; + u64 crc32_err_cnt : 18; + u64 reserved_27_31 : 5; + u64 crc32_match_cnt : 27; + } s; + struct cvmx_ilk_rx_lnex_stat9_s cn68xx; + struct cvmx_ilk_rx_lnex_stat9_s cn68xxp1; + struct cvmx_ilk_rx_lnex_stat9_s cn78xx; + struct cvmx_ilk_rx_lnex_stat9_s cn78xxp1; +}; + +typedef union cvmx_ilk_rx_lnex_stat9 cvmx_ilk_rx_lnex_stat9_t; + +/** + * cvmx_ilk_rxf_idx_pmap + */ +union cvmx_ilk_rxf_idx_pmap { + u64 u64; + struct cvmx_ilk_rxf_idx_pmap_s { + u64 reserved_25_63 : 39; + u64 inc : 9; + u64 reserved_9_15 : 7; + u64 index : 9; + } s; + struct cvmx_ilk_rxf_idx_pmap_s cn68xx; + struct cvmx_ilk_rxf_idx_pmap_s cn68xxp1; +}; + +typedef union cvmx_ilk_rxf_idx_pmap cvmx_ilk_rxf_idx_pmap_t; + +/** + * cvmx_ilk_rxf_mem_pmap + */ +union cvmx_ilk_rxf_mem_pmap { + u64 u64; + struct cvmx_ilk_rxf_mem_pmap_s { + u64 reserved_6_63 : 58; + u64 port_kind : 6; + } s; + struct cvmx_ilk_rxf_mem_pmap_s cn68xx; + struct cvmx_ilk_rxf_mem_pmap_s cn68xxp1; +}; + +typedef union cvmx_ilk_rxf_mem_pmap cvmx_ilk_rxf_mem_pmap_t; + +/** + * cvmx_ilk_ser_cfg + */ +union cvmx_ilk_ser_cfg { + u64 u64; + struct cvmx_ilk_ser_cfg_s { + u64 reserved_57_63 : 7; + u64 ser_rxpol_auto : 1; + u64 ser_rxpol : 16; + u64 ser_txpol : 16; + u64 ser_reset_n : 16; + u64 ser_pwrup : 4; + u64 ser_haul : 4; + } s; + struct cvmx_ilk_ser_cfg_cn68xx { + u64 reserved_57_63 : 7; + u64 ser_rxpol_auto : 1; + u64 reserved_48_55 : 8; + u64 ser_rxpol : 8; + u64 reserved_32_39 : 8; + u64 ser_txpol : 8; + u64 reserved_16_23 : 8; + u64 ser_reset_n : 8; + u64 reserved_6_7 : 2; + u64 ser_pwrup : 2; + u64 reserved_2_3 : 2; + u64 ser_haul : 2; + } cn68xx; + struct cvmx_ilk_ser_cfg_cn68xx cn68xxp1; + struct cvmx_ilk_ser_cfg_s cn78xx; + struct cvmx_ilk_ser_cfg_s cn78xxp1; +}; + +typedef union cvmx_ilk_ser_cfg cvmx_ilk_ser_cfg_t; + +/** + * cvmx_ilk_tx#_byte_cnt# + */ +union cvmx_ilk_txx_byte_cntx { + u64 u64; + struct cvmx_ilk_txx_byte_cntx_s { + u64 reserved_40_63 : 24; + u64 tx_bytes : 40; + } s; + struct cvmx_ilk_txx_byte_cntx_s cn78xx; + struct cvmx_ilk_txx_byte_cntx_s cn78xxp1; +}; + +typedef union cvmx_ilk_txx_byte_cntx cvmx_ilk_txx_byte_cntx_t; + +/** + * cvmx_ilk_tx#_cal_entry# + */ +union cvmx_ilk_txx_cal_entryx { + u64 u64; + struct cvmx_ilk_txx_cal_entryx_s { + u64 reserved_34_63 : 30; + u64 ctl : 2; + u64 reserved_8_31 : 24; + u64 channel : 8; + } s; + struct cvmx_ilk_txx_cal_entryx_s cn78xx; + struct cvmx_ilk_txx_cal_entryx_s cn78xxp1; +}; + +typedef union cvmx_ilk_txx_cal_entryx cvmx_ilk_txx_cal_entryx_t; + +/** + * cvmx_ilk_tx#_cfg0 + */ +union cvmx_ilk_txx_cfg0 { + u64 u64; + struct cvmx_ilk_txx_cfg0_s { + u64 ext_lpbk_fc : 1; + u64 ext_lpbk : 1; + u64 int_lpbk : 1; + u64 txf_byp_dis : 1; + u64 reserved_57_59 : 3; + u64 ptrn_mode : 1; + u64 lnk_stats_rdclr : 1; + u64 lnk_stats_ena : 1; + u64 mltuse_fc_ena : 1; + u64 cal_ena : 1; + u64 mfrm_len : 13; + u64 brst_shrt : 7; + u64 lane_rev : 1; + u64 brst_max : 5; + u64 reserved_25_25 : 1; + u64 cal_depth : 9; + u64 lane_ena : 16; + } s; + struct cvmx_ilk_txx_cfg0_cn68xx { + u64 ext_lpbk_fc : 1; + u64 ext_lpbk : 1; + u64 int_lpbk : 1; + u64 reserved_57_60 : 4; + u64 ptrn_mode : 1; + u64 reserved_55_55 : 1; + u64 lnk_stats_ena : 1; + u64 mltuse_fc_ena : 1; + u64 cal_ena : 1; + u64 mfrm_len : 13; + u64 brst_shrt : 7; + u64 lane_rev : 1; + u64 brst_max : 5; + u64 reserved_25_25 : 1; + u64 cal_depth : 9; + u64 reserved_8_15 : 8; + u64 lane_ena : 8; + } cn68xx; + struct cvmx_ilk_txx_cfg0_cn68xx cn68xxp1; + struct cvmx_ilk_txx_cfg0_s cn78xx; + struct cvmx_ilk_txx_cfg0_s cn78xxp1; +}; + +typedef union cvmx_ilk_txx_cfg0 cvmx_ilk_txx_cfg0_t; + +/** + * cvmx_ilk_tx#_cfg1 + */ +union cvmx_ilk_txx_cfg1 { + u64 u64; + struct cvmx_ilk_txx_cfg1_s { + u64 ser_low : 4; + u64 reserved_53_59 : 7; + u64 brst_min : 5; + u64 reserved_43_47 : 5; + u64 ser_limit : 10; + u64 pkt_busy : 1; + u64 pipe_crd_dis : 1; + u64 ptp_delay : 5; + u64 skip_cnt : 4; + u64 pkt_flush : 1; + u64 pkt_ena : 1; + u64 la_mode : 1; + u64 tx_link_fc : 1; + u64 rx_link_fc : 1; + u64 reserved_12_16 : 5; + u64 tx_link_fc_jam : 1; + u64 rx_link_fc_pkt : 1; + u64 rx_link_fc_ign : 1; + u64 rmatch : 1; + u64 tx_mltuse : 8; + } s; + struct cvmx_ilk_txx_cfg1_cn68xx { + u64 reserved_33_63 : 31; + u64 pkt_busy : 1; + u64 pipe_crd_dis : 1; + u64 ptp_delay : 5; + u64 skip_cnt : 4; + u64 pkt_flush : 1; + u64 pkt_ena : 1; + u64 la_mode : 1; + u64 tx_link_fc : 1; + u64 rx_link_fc : 1; + u64 reserved_12_16 : 5; + u64 tx_link_fc_jam : 1; + u64 rx_link_fc_pkt : 1; + u64 rx_link_fc_ign : 1; + u64 rmatch : 1; + u64 tx_mltuse : 8; + } cn68xx; + struct cvmx_ilk_txx_cfg1_cn68xxp1 { + u64 reserved_32_63 : 32; + u64 pipe_crd_dis : 1; + u64 ptp_delay : 5; + u64 skip_cnt : 4; + u64 pkt_flush : 1; + u64 pkt_ena : 1; + u64 la_mode : 1; + u64 tx_link_fc : 1; + u64 rx_link_fc : 1; + u64 reserved_12_16 : 5; + u64 tx_link_fc_jam : 1; + u64 rx_link_fc_pkt : 1; + u64 rx_link_fc_ign : 1; + u64 rmatch : 1; + u64 tx_mltuse : 8; + } cn68xxp1; + struct cvmx_ilk_txx_cfg1_s cn78xx; + struct cvmx_ilk_txx_cfg1_s cn78xxp1; +}; + +typedef union cvmx_ilk_txx_cfg1 cvmx_ilk_txx_cfg1_t; + +/** + * cvmx_ilk_tx#_cha_xon# + */ +union cvmx_ilk_txx_cha_xonx { + u64 u64; + struct cvmx_ilk_txx_cha_xonx_s { + u64 status : 64; + } s; + struct cvmx_ilk_txx_cha_xonx_s cn78xx; + struct cvmx_ilk_txx_cha_xonx_s cn78xxp1; +}; + +typedef union cvmx_ilk_txx_cha_xonx cvmx_ilk_txx_cha_xonx_t; + +/** + * cvmx_ilk_tx#_dbg + */ +union cvmx_ilk_txx_dbg { + u64 u64; + struct cvmx_ilk_txx_dbg_s { + u64 reserved_29_63 : 35; + u64 data_rate : 13; + u64 low_delay : 6; + u64 reserved_3_9 : 7; + u64 tx_bad_crc24 : 1; + u64 tx_bad_ctlw2 : 1; + u64 tx_bad_ctlw1 : 1; + } s; + struct cvmx_ilk_txx_dbg_cn68xx { + u64 reserved_3_63 : 61; + u64 tx_bad_crc24 : 1; + u64 tx_bad_ctlw2 : 1; + u64 tx_bad_ctlw1 : 1; + } cn68xx; + struct cvmx_ilk_txx_dbg_cn68xx cn68xxp1; + struct cvmx_ilk_txx_dbg_s cn78xx; + struct cvmx_ilk_txx_dbg_s cn78xxp1; +}; + +typedef union cvmx_ilk_txx_dbg cvmx_ilk_txx_dbg_t; + +/** + * cvmx_ilk_tx#_err_cfg + */ +union cvmx_ilk_txx_err_cfg { + u64 u64; + struct cvmx_ilk_txx_err_cfg_s { + u64 reserved_20_63 : 44; + u64 fwc_flip : 2; + u64 txf_flip : 2; + u64 reserved_2_15 : 14; + u64 fwc_cor_dis : 1; + u64 txf_cor_dis : 1; + } s; + struct cvmx_ilk_txx_err_cfg_s cn78xx; + struct cvmx_ilk_txx_err_cfg_s cn78xxp1; +}; + +typedef union cvmx_ilk_txx_err_cfg cvmx_ilk_txx_err_cfg_t; + +/** + * cvmx_ilk_tx#_flow_ctl0 + */ +union cvmx_ilk_txx_flow_ctl0 { + u64 u64; + struct cvmx_ilk_txx_flow_ctl0_s { + u64 status : 64; + } s; + struct cvmx_ilk_txx_flow_ctl0_s cn68xx; + struct cvmx_ilk_txx_flow_ctl0_s cn68xxp1; +}; + +typedef union cvmx_ilk_txx_flow_ctl0 cvmx_ilk_txx_flow_ctl0_t; + +/** + * cvmx_ilk_tx#_flow_ctl1 + * + * Notes: + * Do not publish. + * + */ +union cvmx_ilk_txx_flow_ctl1 { + u64 u64; + struct cvmx_ilk_txx_flow_ctl1_s { + u64 reserved_0_63 : 64; + } s; + struct cvmx_ilk_txx_flow_ctl1_s cn68xx; + struct cvmx_ilk_txx_flow_ctl1_s cn68xxp1; +}; + +typedef union cvmx_ilk_txx_flow_ctl1 cvmx_ilk_txx_flow_ctl1_t; + +/** + * cvmx_ilk_tx#_idx_cal + */ +union cvmx_ilk_txx_idx_cal { + u64 u64; + struct cvmx_ilk_txx_idx_cal_s { + u64 reserved_14_63 : 50; + u64 inc : 6; + u64 reserved_6_7 : 2; + u64 index : 6; + } s; + struct cvmx_ilk_txx_idx_cal_s cn68xx; + struct cvmx_ilk_txx_idx_cal_s cn68xxp1; +}; + +typedef union cvmx_ilk_txx_idx_cal cvmx_ilk_txx_idx_cal_t; + +/** + * cvmx_ilk_tx#_idx_pmap + */ +union cvmx_ilk_txx_idx_pmap { + u64 u64; + struct cvmx_ilk_txx_idx_pmap_s { + u64 reserved_23_63 : 41; + u64 inc : 7; + u64 reserved_7_15 : 9; + u64 index : 7; + } s; + struct cvmx_ilk_txx_idx_pmap_s cn68xx; + struct cvmx_ilk_txx_idx_pmap_s cn68xxp1; +}; + +typedef union cvmx_ilk_txx_idx_pmap cvmx_ilk_txx_idx_pmap_t; + +/** + * cvmx_ilk_tx#_idx_stat0 + */ +union cvmx_ilk_txx_idx_stat0 { + u64 u64; + struct cvmx_ilk_txx_idx_stat0_s { + u64 reserved_32_63 : 32; + u64 clr : 1; + u64 reserved_24_30 : 7; + u64 inc : 8; + u64 reserved_8_15 : 8; + u64 index : 8; + } s; + struct cvmx_ilk_txx_idx_stat0_s cn68xx; + struct cvmx_ilk_txx_idx_stat0_s cn68xxp1; +}; + +typedef union cvmx_ilk_txx_idx_stat0 cvmx_ilk_txx_idx_stat0_t; + +/** + * cvmx_ilk_tx#_idx_stat1 + */ +union cvmx_ilk_txx_idx_stat1 { + u64 u64; + struct cvmx_ilk_txx_idx_stat1_s { + u64 reserved_32_63 : 32; + u64 clr : 1; + u64 reserved_24_30 : 7; + u64 inc : 8; + u64 reserved_8_15 : 8; + u64 index : 8; + } s; + struct cvmx_ilk_txx_idx_stat1_s cn68xx; + struct cvmx_ilk_txx_idx_stat1_s cn68xxp1; +}; + +typedef union cvmx_ilk_txx_idx_stat1 cvmx_ilk_txx_idx_stat1_t; + +/** + * cvmx_ilk_tx#_int + */ +union cvmx_ilk_txx_int { + u64 u64; + struct cvmx_ilk_txx_int_s { + u64 reserved_8_63 : 56; + u64 fwc_dbe : 1; + u64 fwc_sbe : 1; + u64 txf_dbe : 1; + u64 txf_sbe : 1; + u64 stat_cnt_ovfl : 1; + u64 bad_pipe : 1; + u64 bad_seq : 1; + u64 txf_err : 1; + } s; + struct cvmx_ilk_txx_int_cn68xx { + u64 reserved_4_63 : 60; + u64 stat_cnt_ovfl : 1; + u64 bad_pipe : 1; + u64 bad_seq : 1; + u64 txf_err : 1; + } cn68xx; + struct cvmx_ilk_txx_int_cn68xx cn68xxp1; + struct cvmx_ilk_txx_int_s cn78xx; + struct cvmx_ilk_txx_int_s cn78xxp1; +}; + +typedef union cvmx_ilk_txx_int cvmx_ilk_txx_int_t; + +/** + * cvmx_ilk_tx#_int_en + */ +union cvmx_ilk_txx_int_en { + u64 u64; + struct cvmx_ilk_txx_int_en_s { + u64 reserved_4_63 : 60; + u64 stat_cnt_ovfl : 1; + u64 bad_pipe : 1; + u64 bad_seq : 1; + u64 txf_err : 1; + } s; + struct cvmx_ilk_txx_int_en_s cn68xx; + struct cvmx_ilk_txx_int_en_s cn68xxp1; +}; + +typedef union cvmx_ilk_txx_int_en cvmx_ilk_txx_int_en_t; + +/** + * cvmx_ilk_tx#_mem_cal0 + * + * Notes: + * Software must always read ILK_TXx_MEM_CAL0 then ILK_TXx_MEM_CAL1. Software + * must never read them in reverse order or read one without reading the + * other. + * + * Software must always write ILK_TXx_MEM_CAL0 then ILK_TXx_MEM_CAL1. + * Software must never write them in reverse order or write one without + * writing the other. + */ +union cvmx_ilk_txx_mem_cal0 { + u64 u64; + struct cvmx_ilk_txx_mem_cal0_s { + u64 reserved_36_63 : 28; + u64 entry_ctl3 : 2; + u64 reserved_33_33 : 1; + u64 bpid3 : 6; + u64 entry_ctl2 : 2; + u64 reserved_24_24 : 1; + u64 bpid2 : 6; + u64 entry_ctl1 : 2; + u64 reserved_15_15 : 1; + u64 bpid1 : 6; + u64 entry_ctl0 : 2; + u64 reserved_6_6 : 1; + u64 bpid0 : 6; + } s; + struct cvmx_ilk_txx_mem_cal0_s cn68xx; + struct cvmx_ilk_txx_mem_cal0_s cn68xxp1; +}; + +typedef union cvmx_ilk_txx_mem_cal0 cvmx_ilk_txx_mem_cal0_t; + +/** + * cvmx_ilk_tx#_mem_cal1 + * + * Notes: + * Software must always read ILK_TXx_MEM_CAL0 then ILK_TXx_MEM_CAL1. Software + * must never read them in reverse order or read one without reading the + * other. + * + * Software must always write ILK_TXx_MEM_CAL0 then ILK_TXx_MEM_CAL1. + * Software must never write them in reverse order or write one without + * writing the other. + */ +union cvmx_ilk_txx_mem_cal1 { + u64 u64; + struct cvmx_ilk_txx_mem_cal1_s { + u64 reserved_36_63 : 28; + u64 entry_ctl7 : 2; + u64 reserved_33_33 : 1; + u64 bpid7 : 6; + u64 entry_ctl6 : 2; + u64 reserved_24_24 : 1; + u64 bpid6 : 6; + u64 entry_ctl5 : 2; + u64 reserved_15_15 : 1; + u64 bpid5 : 6; + u64 entry_ctl4 : 2; + u64 reserved_6_6 : 1; + u64 bpid4 : 6; + } s; + struct cvmx_ilk_txx_mem_cal1_s cn68xx; + struct cvmx_ilk_txx_mem_cal1_s cn68xxp1; +}; + +typedef union cvmx_ilk_txx_mem_cal1 cvmx_ilk_txx_mem_cal1_t; + +/** + * cvmx_ilk_tx#_mem_pmap + */ +union cvmx_ilk_txx_mem_pmap { + u64 u64; + struct cvmx_ilk_txx_mem_pmap_s { + u64 reserved_17_63 : 47; + u64 remap : 1; + u64 reserved_8_15 : 8; + u64 channel : 8; + } s; + struct cvmx_ilk_txx_mem_pmap_s cn68xx; + struct cvmx_ilk_txx_mem_pmap_cn68xxp1 { + u64 reserved_8_63 : 56; + u64 channel : 8; + } cn68xxp1; +}; + +typedef union cvmx_ilk_txx_mem_pmap cvmx_ilk_txx_mem_pmap_t; + +/** + * cvmx_ilk_tx#_mem_stat0 + */ +union cvmx_ilk_txx_mem_stat0 { + u64 u64; + struct cvmx_ilk_txx_mem_stat0_s { + u64 reserved_28_63 : 36; + u64 tx_pkt : 28; + } s; + struct cvmx_ilk_txx_mem_stat0_s cn68xx; + struct cvmx_ilk_txx_mem_stat0_s cn68xxp1; +}; + +typedef union cvmx_ilk_txx_mem_stat0 cvmx_ilk_txx_mem_stat0_t; + +/** + * cvmx_ilk_tx#_mem_stat1 + */ +union cvmx_ilk_txx_mem_stat1 { + u64 u64; + struct cvmx_ilk_txx_mem_stat1_s { + u64 reserved_36_63 : 28; + u64 tx_bytes : 36; + } s; + struct cvmx_ilk_txx_mem_stat1_s cn68xx; + struct cvmx_ilk_txx_mem_stat1_s cn68xxp1; +}; + +typedef union cvmx_ilk_txx_mem_stat1 cvmx_ilk_txx_mem_stat1_t; + +/** + * cvmx_ilk_tx#_pipe + */ +union cvmx_ilk_txx_pipe { + u64 u64; + struct cvmx_ilk_txx_pipe_s { + u64 reserved_24_63 : 40; + u64 nump : 8; + u64 reserved_7_15 : 9; + u64 base : 7; + } s; + struct cvmx_ilk_txx_pipe_s cn68xx; + struct cvmx_ilk_txx_pipe_s cn68xxp1; +}; + +typedef union cvmx_ilk_txx_pipe cvmx_ilk_txx_pipe_t; + +/** + * cvmx_ilk_tx#_pkt_cnt# + */ +union cvmx_ilk_txx_pkt_cntx { + u64 u64; + struct cvmx_ilk_txx_pkt_cntx_s { + u64 reserved_34_63 : 30; + u64 tx_pkt : 34; + } s; + struct cvmx_ilk_txx_pkt_cntx_s cn78xx; + struct cvmx_ilk_txx_pkt_cntx_s cn78xxp1; +}; + +typedef union cvmx_ilk_txx_pkt_cntx cvmx_ilk_txx_pkt_cntx_t; + +/** + * cvmx_ilk_tx#_rmatch + */ +union cvmx_ilk_txx_rmatch { + u64 u64; + struct cvmx_ilk_txx_rmatch_s { + u64 reserved_50_63 : 14; + u64 grnlrty : 2; + u64 brst_limit : 16; + u64 time_limit : 16; + u64 rate_limit : 16; + } s; + struct cvmx_ilk_txx_rmatch_s cn68xx; + struct cvmx_ilk_txx_rmatch_s cn68xxp1; + struct cvmx_ilk_txx_rmatch_s cn78xx; + struct cvmx_ilk_txx_rmatch_s cn78xxp1; +}; + +typedef union cvmx_ilk_txx_rmatch cvmx_ilk_txx_rmatch_t; + +#endif diff --git a/arch/mips/mach-octeon/include/mach/cvmx-iob-defs.h b/arch/mips/mach-octeon/include/mach/cvmx-iob-defs.h new file mode 100644 index 0000000..0af444d --- /dev/null +++ b/arch/mips/mach-octeon/include/mach/cvmx-iob-defs.h @@ -0,0 +1,1328 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018-2022 Marvell International Ltd. + * + * Configuration and status register (CSR) type definitions for + * Octeon iob. + */ + +#ifndef __CVMX_IOB_DEFS_H__ +#define __CVMX_IOB_DEFS_H__ + +#define CVMX_IOB_BIST_STATUS (0x00011800F00007F8ull) +#define CVMX_IOB_CHIP_CUR_PWR (0x00011800F0000828ull) +#define CVMX_IOB_CHIP_GLB_PWR_THROTTLE (0x00011800F0000808ull) +#define CVMX_IOB_CHIP_PWR_OUT (0x00011800F0000818ull) +#define CVMX_IOB_CTL_STATUS (0x00011800F0000050ull) +#define CVMX_IOB_DWB_PRI_CNT (0x00011800F0000028ull) +#define CVMX_IOB_FAU_TIMEOUT (0x00011800F0000000ull) +#define CVMX_IOB_I2C_PRI_CNT (0x00011800F0000010ull) +#define CVMX_IOB_INB_CONTROL_MATCH (0x00011800F0000078ull) +#define CVMX_IOB_INB_CONTROL_MATCH_ENB (0x00011800F0000088ull) +#define CVMX_IOB_INB_DATA_MATCH (0x00011800F0000070ull) +#define CVMX_IOB_INB_DATA_MATCH_ENB (0x00011800F0000080ull) +#define CVMX_IOB_INT_ENB (0x00011800F0000060ull) +#define CVMX_IOB_INT_SUM (0x00011800F0000058ull) +#define CVMX_IOB_N2C_L2C_PRI_CNT (0x00011800F0000020ull) +#define CVMX_IOB_N2C_RSP_PRI_CNT (0x00011800F0000008ull) +#define CVMX_IOB_OUTB_COM_PRI_CNT (0x00011800F0000040ull) +#define CVMX_IOB_OUTB_CONTROL_MATCH (0x00011800F0000098ull) +#define CVMX_IOB_OUTB_CONTROL_MATCH_ENB (0x00011800F00000A8ull) +#define CVMX_IOB_OUTB_DATA_MATCH (0x00011800F0000090ull) +#define CVMX_IOB_OUTB_DATA_MATCH_ENB (0x00011800F00000A0ull) +#define CVMX_IOB_OUTB_FPA_PRI_CNT (0x00011800F0000048ull) +#define CVMX_IOB_OUTB_REQ_PRI_CNT (0x00011800F0000038ull) +#define CVMX_IOB_P2C_REQ_PRI_CNT (0x00011800F0000018ull) +#define CVMX_IOB_PKT_ERR (0x00011800F0000068ull) +#define CVMX_IOB_PP_BIST_STATUS (0x00011800F0000700ull) +#define CVMX_IOB_TO_CMB_CREDITS (0x00011800F00000B0ull) +#define CVMX_IOB_TO_NCB_DID_00_CREDITS (0x00011800F0000800ull) +#define CVMX_IOB_TO_NCB_DID_111_CREDITS (0x00011800F0000B78ull) +#define CVMX_IOB_TO_NCB_DID_223_CREDITS (0x00011800F0000EF8ull) +#define CVMX_IOB_TO_NCB_DID_24_CREDITS (0x00011800F00008C0ull) +#define CVMX_IOB_TO_NCB_DID_32_CREDITS (0x00011800F0000900ull) +#define CVMX_IOB_TO_NCB_DID_40_CREDITS (0x00011800F0000940ull) +#define CVMX_IOB_TO_NCB_DID_55_CREDITS (0x00011800F00009B8ull) +#define CVMX_IOB_TO_NCB_DID_64_CREDITS (0x00011800F0000A00ull) +#define CVMX_IOB_TO_NCB_DID_79_CREDITS (0x00011800F0000A78ull) +#define CVMX_IOB_TO_NCB_DID_96_CREDITS (0x00011800F0000B00ull) +#define CVMX_IOB_TO_NCB_DID_98_CREDITS (0x00011800F0000B10ull) + +/** + * cvmx_iob_bist_status + * + * The result of the BIST run on the IOB memories. + * + */ +union cvmx_iob_bist_status { + u64 u64; + struct cvmx_iob_bist_status_s { + u64 reserved_2_63 : 62; + u64 ibd : 1; + u64 icd : 1; + } s; + struct cvmx_iob_bist_status_cn30xx { + u64 reserved_18_63 : 46; + u64 icnrcb : 1; + u64 icr0 : 1; + u64 icr1 : 1; + u64 icnr1 : 1; + u64 icnr0 : 1; + u64 ibdr0 : 1; + u64 ibdr1 : 1; + u64 ibr0 : 1; + u64 ibr1 : 1; + u64 icnrt : 1; + u64 ibrq0 : 1; + u64 ibrq1 : 1; + u64 icrn0 : 1; + u64 icrn1 : 1; + u64 icrp0 : 1; + u64 icrp1 : 1; + u64 ibd : 1; + u64 icd : 1; + } cn30xx; + struct cvmx_iob_bist_status_cn30xx cn31xx; + struct cvmx_iob_bist_status_cn30xx cn38xx; + struct cvmx_iob_bist_status_cn30xx cn38xxp2; + struct cvmx_iob_bist_status_cn30xx cn50xx; + struct cvmx_iob_bist_status_cn30xx cn52xx; + struct cvmx_iob_bist_status_cn30xx cn52xxp1; + struct cvmx_iob_bist_status_cn30xx cn56xx; + struct cvmx_iob_bist_status_cn30xx cn56xxp1; + struct cvmx_iob_bist_status_cn30xx cn58xx; + struct cvmx_iob_bist_status_cn30xx cn58xxp1; + struct cvmx_iob_bist_status_cn61xx { + u64 reserved_23_63 : 41; + u64 xmdfif : 1; + u64 xmcfif : 1; + u64 iorfif : 1; + u64 rsdfif : 1; + u64 iocfif : 1; + u64 icnrcb : 1; + u64 icr0 : 1; + u64 icr1 : 1; + u64 icnr1 : 1; + u64 icnr0 : 1; + u64 ibdr0 : 1; + u64 ibdr1 : 1; + u64 ibr0 : 1; + u64 ibr1 : 1; + u64 icnrt : 1; + u64 ibrq0 : 1; + u64 ibrq1 : 1; + u64 icrn0 : 1; + u64 icrn1 : 1; + u64 icrp0 : 1; + u64 icrp1 : 1; + u64 ibd : 1; + u64 icd : 1; + } cn61xx; + struct cvmx_iob_bist_status_cn61xx cn63xx; + struct cvmx_iob_bist_status_cn61xx cn63xxp1; + struct cvmx_iob_bist_status_cn61xx cn66xx; + struct cvmx_iob_bist_status_cn68xx { + u64 reserved_18_63 : 46; + u64 xmdfif : 1; + u64 xmcfif : 1; + u64 iorfif : 1; + u64 rsdfif : 1; + u64 iocfif : 1; + u64 icnrcb : 1; + u64 icr0 : 1; + u64 icr1 : 1; + u64 icnr0 : 1; + u64 ibr0 : 1; + u64 ibr1 : 1; + u64 icnrt : 1; + u64 ibrq0 : 1; + u64 ibrq1 : 1; + u64 icrn0 : 1; + u64 icrn1 : 1; + u64 ibd : 1; + u64 icd : 1; + } cn68xx; + struct cvmx_iob_bist_status_cn68xx cn68xxp1; + struct cvmx_iob_bist_status_cn61xx cn70xx; + struct cvmx_iob_bist_status_cn61xx cn70xxp1; + struct cvmx_iob_bist_status_cn61xx cnf71xx; +}; + +typedef union cvmx_iob_bist_status cvmx_iob_bist_status_t; + +/** + * cvmx_iob_chip_cur_pwr + */ +union cvmx_iob_chip_cur_pwr { + u64 u64; + struct cvmx_iob_chip_cur_pwr_s { + u64 reserved_8_63 : 56; + u64 current_power_setting : 8; + } s; + struct cvmx_iob_chip_cur_pwr_s cn70xx; + struct cvmx_iob_chip_cur_pwr_s cn70xxp1; +}; + +typedef union cvmx_iob_chip_cur_pwr cvmx_iob_chip_cur_pwr_t; + +/** + * cvmx_iob_chip_glb_pwr_throttle + * + * Controls the min/max power settings. + * + */ +union cvmx_iob_chip_glb_pwr_throttle { + u64 u64; + struct cvmx_iob_chip_glb_pwr_throttle_s { + u64 reserved_34_63 : 30; + u64 pwr_bw : 2; + u64 pwr_max : 8; + u64 pwr_min : 8; + u64 pwr_setting : 16; + } s; + struct cvmx_iob_chip_glb_pwr_throttle_s cn70xx; + struct cvmx_iob_chip_glb_pwr_throttle_s cn70xxp1; +}; + +typedef union cvmx_iob_chip_glb_pwr_throttle cvmx_iob_chip_glb_pwr_throttle_t; + +/** + * cvmx_iob_chip_pwr_out + * + * Power numbers from the various partitions on the chip. + * + */ +union cvmx_iob_chip_pwr_out { + u64 u64; + struct cvmx_iob_chip_pwr_out_s { + u64 cpu_pwr : 16; + u64 chip_power : 16; + u64 coproc_power : 16; + u64 avg_chip_power : 16; + } s; + struct cvmx_iob_chip_pwr_out_s cn70xx; + struct cvmx_iob_chip_pwr_out_s cn70xxp1; +}; + +typedef union cvmx_iob_chip_pwr_out cvmx_iob_chip_pwr_out_t; + +/** + * cvmx_iob_ctl_status + * + * IOB Control Status = IOB Control and Status Register + * Provides control for IOB functions. + */ +union cvmx_iob_ctl_status { + u64 u64; + struct cvmx_iob_ctl_status_s { + u64 reserved_11_63 : 53; + u64 fif_dly : 1; + u64 xmc_per : 4; + u64 reserved_3_5 : 3; + u64 pko_enb : 1; + u64 dwb_enb : 1; + u64 fau_end : 1; + } s; + struct cvmx_iob_ctl_status_cn30xx { + u64 reserved_5_63 : 59; + u64 outb_mat : 1; + u64 inb_mat : 1; + u64 pko_enb : 1; + u64 dwb_enb : 1; + u64 fau_end : 1; + } cn30xx; + struct cvmx_iob_ctl_status_cn30xx cn31xx; + struct cvmx_iob_ctl_status_cn30xx cn38xx; + struct cvmx_iob_ctl_status_cn30xx cn38xxp2; + struct cvmx_iob_ctl_status_cn30xx cn50xx; + struct cvmx_iob_ctl_status_cn52xx { + u64 reserved_6_63 : 58; + u64 rr_mode : 1; + u64 outb_mat : 1; + u64 inb_mat : 1; + u64 pko_enb : 1; + u64 dwb_enb : 1; + u64 fau_end : 1; + } cn52xx; + struct cvmx_iob_ctl_status_cn30xx cn52xxp1; + struct cvmx_iob_ctl_status_cn30xx cn56xx; + struct cvmx_iob_ctl_status_cn30xx cn56xxp1; + struct cvmx_iob_ctl_status_cn30xx cn58xx; + struct cvmx_iob_ctl_status_cn30xx cn58xxp1; + struct cvmx_iob_ctl_status_cn61xx { + u64 reserved_11_63 : 53; + u64 fif_dly : 1; + u64 xmc_per : 4; + u64 rr_mode : 1; + u64 outb_mat : 1; + u64 inb_mat : 1; + u64 pko_enb : 1; + u64 dwb_enb : 1; + u64 fau_end : 1; + } cn61xx; + struct cvmx_iob_ctl_status_cn63xx { + u64 reserved_10_63 : 54; + u64 xmc_per : 4; + u64 rr_mode : 1; + u64 outb_mat : 1; + u64 inb_mat : 1; + u64 pko_enb : 1; + u64 dwb_enb : 1; + u64 fau_end : 1; + } cn63xx; + struct cvmx_iob_ctl_status_cn63xx cn63xxp1; + struct cvmx_iob_ctl_status_cn61xx cn66xx; + struct cvmx_iob_ctl_status_cn68xx { + u64 reserved_11_63 : 53; + u64 fif_dly : 1; + u64 xmc_per : 4; + u64 rsvr5 : 1; + u64 outb_mat : 1; + u64 inb_mat : 1; + u64 pko_enb : 1; + u64 dwb_enb : 1; + u64 fau_end : 1; + } cn68xx; + struct cvmx_iob_ctl_status_cn68xx cn68xxp1; + struct cvmx_iob_ctl_status_cn70xx { + u64 reserved_10_63 : 54; + u64 xmc_per : 4; + u64 rr_mode : 1; + u64 rsv4 : 1; + u64 rsv3 : 1; + u64 pko_enb : 1; + u64 dwb_enb : 1; + u64 fau_end : 1; + } cn70xx; + struct cvmx_iob_ctl_status_cn70xx cn70xxp1; + struct cvmx_iob_ctl_status_cn61xx cnf71xx; +}; + +typedef union cvmx_iob_ctl_status cvmx_iob_ctl_status_t; + +/** + * cvmx_iob_dwb_pri_cnt + * + * DWB To CMB Priority Counter = Don't Write Back to CMB Priority Counter Enable and Timer Value + * Enables and supplies the timeout count for raising the priority of Don't Write Back request to + * the L2C. + */ +union cvmx_iob_dwb_pri_cnt { + u64 u64; + struct cvmx_iob_dwb_pri_cnt_s { + u64 reserved_16_63 : 48; + u64 cnt_enb : 1; + u64 cnt_val : 15; + } s; + struct cvmx_iob_dwb_pri_cnt_s cn38xx; + struct cvmx_iob_dwb_pri_cnt_s cn38xxp2; + struct cvmx_iob_dwb_pri_cnt_s cn52xx; + struct cvmx_iob_dwb_pri_cnt_s cn52xxp1; + struct cvmx_iob_dwb_pri_cnt_s cn56xx; + struct cvmx_iob_dwb_pri_cnt_s cn56xxp1; + struct cvmx_iob_dwb_pri_cnt_s cn58xx; + struct cvmx_iob_dwb_pri_cnt_s cn58xxp1; + struct cvmx_iob_dwb_pri_cnt_s cn61xx; + struct cvmx_iob_dwb_pri_cnt_s cn63xx; + struct cvmx_iob_dwb_pri_cnt_s cn63xxp1; + struct cvmx_iob_dwb_pri_cnt_s cn66xx; + struct cvmx_iob_dwb_pri_cnt_s cn70xx; + struct cvmx_iob_dwb_pri_cnt_s cn70xxp1; + struct cvmx_iob_dwb_pri_cnt_s cnf71xx; +}; + +typedef union cvmx_iob_dwb_pri_cnt cvmx_iob_dwb_pri_cnt_t; + +/** + * cvmx_iob_fau_timeout + * + * FAU Timeout = Fetch and Add Unit Tag-Switch Timeout + * How many clokc ticks the FAU unit will wait for a tag-switch before timing out. + * for Queue 0. + */ +union cvmx_iob_fau_timeout { + u64 u64; + struct cvmx_iob_fau_timeout_s { + u64 reserved_13_63 : 51; + u64 tout_enb : 1; + u64 tout_val : 12; + } s; + struct cvmx_iob_fau_timeout_s cn30xx; + struct cvmx_iob_fau_timeout_s cn31xx; + struct cvmx_iob_fau_timeout_s cn38xx; + struct cvmx_iob_fau_timeout_s cn38xxp2; + struct cvmx_iob_fau_timeout_s cn50xx; + struct cvmx_iob_fau_timeout_s cn52xx; + struct cvmx_iob_fau_timeout_s cn52xxp1; + struct cvmx_iob_fau_timeout_s cn56xx; + struct cvmx_iob_fau_timeout_s cn56xxp1; + struct cvmx_iob_fau_timeout_s cn58xx; + struct cvmx_iob_fau_timeout_s cn58xxp1; + struct cvmx_iob_fau_timeout_s cn61xx; + struct cvmx_iob_fau_timeout_s cn63xx; + struct cvmx_iob_fau_timeout_s cn63xxp1; + struct cvmx_iob_fau_timeout_s cn66xx; + struct cvmx_iob_fau_timeout_s cn68xx; + struct cvmx_iob_fau_timeout_s cn68xxp1; + struct cvmx_iob_fau_timeout_s cn70xx; + struct cvmx_iob_fau_timeout_s cn70xxp1; + struct cvmx_iob_fau_timeout_s cnf71xx; +}; + +typedef union cvmx_iob_fau_timeout cvmx_iob_fau_timeout_t; + +/** + * cvmx_iob_i2c_pri_cnt + * + * IPD To CMB Store Priority Counter = IPD to CMB Store Priority Counter Enable and Timer Value + * Enables and supplies the timeout count for raising the priority of IPD Store access to the + * CMB. + */ +union cvmx_iob_i2c_pri_cnt { + u64 u64; + struct cvmx_iob_i2c_pri_cnt_s { + u64 reserved_16_63 : 48; + u64 cnt_enb : 1; + u64 cnt_val : 15; + } s; + struct cvmx_iob_i2c_pri_cnt_s cn38xx; + struct cvmx_iob_i2c_pri_cnt_s cn38xxp2; + struct cvmx_iob_i2c_pri_cnt_s cn52xx; + struct cvmx_iob_i2c_pri_cnt_s cn52xxp1; + struct cvmx_iob_i2c_pri_cnt_s cn56xx; + struct cvmx_iob_i2c_pri_cnt_s cn56xxp1; + struct cvmx_iob_i2c_pri_cnt_s cn58xx; + struct cvmx_iob_i2c_pri_cnt_s cn58xxp1; + struct cvmx_iob_i2c_pri_cnt_s cn61xx; + struct cvmx_iob_i2c_pri_cnt_s cn63xx; + struct cvmx_iob_i2c_pri_cnt_s cn63xxp1; + struct cvmx_iob_i2c_pri_cnt_s cn66xx; + struct cvmx_iob_i2c_pri_cnt_s cn70xx; + struct cvmx_iob_i2c_pri_cnt_s cn70xxp1; + struct cvmx_iob_i2c_pri_cnt_s cnf71xx; +}; + +typedef union cvmx_iob_i2c_pri_cnt cvmx_iob_i2c_pri_cnt_t; + +/** + * cvmx_iob_inb_control_match + * + * Match pattern for the inbound control to set the INB_MATCH_BIT. + * + */ +union cvmx_iob_inb_control_match { + u64 u64; + struct cvmx_iob_inb_control_match_s { + u64 reserved_29_63 : 35; + u64 mask : 8; + u64 opc : 4; + u64 dst : 9; + u64 src : 8; + } s; + struct cvmx_iob_inb_control_match_s cn30xx; + struct cvmx_iob_inb_control_match_s cn31xx; + struct cvmx_iob_inb_control_match_s cn38xx; + struct cvmx_iob_inb_control_match_s cn38xxp2; + struct cvmx_iob_inb_control_match_s cn50xx; + struct cvmx_iob_inb_control_match_s cn52xx; + struct cvmx_iob_inb_control_match_s cn52xxp1; + struct cvmx_iob_inb_control_match_s cn56xx; + struct cvmx_iob_inb_control_match_s cn56xxp1; + struct cvmx_iob_inb_control_match_s cn58xx; + struct cvmx_iob_inb_control_match_s cn58xxp1; + struct cvmx_iob_inb_control_match_s cn61xx; + struct cvmx_iob_inb_control_match_s cn63xx; + struct cvmx_iob_inb_control_match_s cn63xxp1; + struct cvmx_iob_inb_control_match_s cn66xx; + struct cvmx_iob_inb_control_match_s cn68xx; + struct cvmx_iob_inb_control_match_s cn68xxp1; + struct cvmx_iob_inb_control_match_s cn70xx; + struct cvmx_iob_inb_control_match_s cn70xxp1; + struct cvmx_iob_inb_control_match_s cnf71xx; +}; + +typedef union cvmx_iob_inb_control_match cvmx_iob_inb_control_match_t; + +/** + * cvmx_iob_inb_control_match_enb + * + * Enables the match of the corresponding bit in the IOB_INB_CONTROL_MATCH reister. + * + */ +union cvmx_iob_inb_control_match_enb { + u64 u64; + struct cvmx_iob_inb_control_match_enb_s { + u64 reserved_29_63 : 35; + u64 mask : 8; + u64 opc : 4; + u64 dst : 9; + u64 src : 8; + } s; + struct cvmx_iob_inb_control_match_enb_s cn30xx; + struct cvmx_iob_inb_control_match_enb_s cn31xx; + struct cvmx_iob_inb_control_match_enb_s cn38xx; + struct cvmx_iob_inb_control_match_enb_s cn38xxp2; + struct cvmx_iob_inb_control_match_enb_s cn50xx; + struct cvmx_iob_inb_control_match_enb_s cn52xx; + struct cvmx_iob_inb_control_match_enb_s cn52xxp1; + struct cvmx_iob_inb_control_match_enb_s cn56xx; + struct cvmx_iob_inb_control_match_enb_s cn56xxp1; + struct cvmx_iob_inb_control_match_enb_s cn58xx; + struct cvmx_iob_inb_control_match_enb_s cn58xxp1; + struct cvmx_iob_inb_control_match_enb_s cn61xx; + struct cvmx_iob_inb_control_match_enb_s cn63xx; + struct cvmx_iob_inb_control_match_enb_s cn63xxp1; + struct cvmx_iob_inb_control_match_enb_s cn66xx; + struct cvmx_iob_inb_control_match_enb_s cn68xx; + struct cvmx_iob_inb_control_match_enb_s cn68xxp1; + struct cvmx_iob_inb_control_match_enb_s cn70xx; + struct cvmx_iob_inb_control_match_enb_s cn70xxp1; + struct cvmx_iob_inb_control_match_enb_s cnf71xx; +}; + +typedef union cvmx_iob_inb_control_match_enb cvmx_iob_inb_control_match_enb_t; + +/** + * cvmx_iob_inb_data_match + * + * Match pattern for the inbound data to set the INB_MATCH_BIT. + * + */ +union cvmx_iob_inb_data_match { + u64 u64; + struct cvmx_iob_inb_data_match_s { + u64 data : 64; + } s; + struct cvmx_iob_inb_data_match_s cn30xx; + struct cvmx_iob_inb_data_match_s cn31xx; + struct cvmx_iob_inb_data_match_s cn38xx; + struct cvmx_iob_inb_data_match_s cn38xxp2; + struct cvmx_iob_inb_data_match_s cn50xx; + struct cvmx_iob_inb_data_match_s cn52xx; + struct cvmx_iob_inb_data_match_s cn52xxp1; + struct cvmx_iob_inb_data_match_s cn56xx; + struct cvmx_iob_inb_data_match_s cn56xxp1; + struct cvmx_iob_inb_data_match_s cn58xx; + struct cvmx_iob_inb_data_match_s cn58xxp1; + struct cvmx_iob_inb_data_match_s cn61xx; + struct cvmx_iob_inb_data_match_s cn63xx; + struct cvmx_iob_inb_data_match_s cn63xxp1; + struct cvmx_iob_inb_data_match_s cn66xx; + struct cvmx_iob_inb_data_match_s cn68xx; + struct cvmx_iob_inb_data_match_s cn68xxp1; + struct cvmx_iob_inb_data_match_s cn70xx; + struct cvmx_iob_inb_data_match_s cn70xxp1; + struct cvmx_iob_inb_data_match_s cnf71xx; +}; + +typedef union cvmx_iob_inb_data_match cvmx_iob_inb_data_match_t; + +/** + * cvmx_iob_inb_data_match_enb + * + * Enables the match of the corresponding bit in the IOB_INB_DATA_MATCH reister. + * + */ +union cvmx_iob_inb_data_match_enb { + u64 u64; + struct cvmx_iob_inb_data_match_enb_s { + u64 data : 64; + } s; + struct cvmx_iob_inb_data_match_enb_s cn30xx; + struct cvmx_iob_inb_data_match_enb_s cn31xx; + struct cvmx_iob_inb_data_match_enb_s cn38xx; + struct cvmx_iob_inb_data_match_enb_s cn38xxp2; + struct cvmx_iob_inb_data_match_enb_s cn50xx; + struct cvmx_iob_inb_data_match_enb_s cn52xx; + struct cvmx_iob_inb_data_match_enb_s cn52xxp1; + struct cvmx_iob_inb_data_match_enb_s cn56xx; + struct cvmx_iob_inb_data_match_enb_s cn56xxp1; + struct cvmx_iob_inb_data_match_enb_s cn58xx; + struct cvmx_iob_inb_data_match_enb_s cn58xxp1; + struct cvmx_iob_inb_data_match_enb_s cn61xx; + struct cvmx_iob_inb_data_match_enb_s cn63xx; + struct cvmx_iob_inb_data_match_enb_s cn63xxp1; + struct cvmx_iob_inb_data_match_enb_s cn66xx; + struct cvmx_iob_inb_data_match_enb_s cn68xx; + struct cvmx_iob_inb_data_match_enb_s cn68xxp1; + struct cvmx_iob_inb_data_match_enb_s cn70xx; + struct cvmx_iob_inb_data_match_enb_s cn70xxp1; + struct cvmx_iob_inb_data_match_enb_s cnf71xx; +}; + +typedef union cvmx_iob_inb_data_match_enb cvmx_iob_inb_data_match_enb_t; + +/** + * cvmx_iob_int_enb + * + * The IOB's interrupt enable register. + * + */ +union cvmx_iob_int_enb { + u64 u64; + struct cvmx_iob_int_enb_s { + u64 reserved_8_63 : 56; + u64 outb_mat : 1; + u64 inb_mat : 1; + u64 p_dat : 1; + u64 np_dat : 1; + u64 p_eop : 1; + u64 p_sop : 1; + u64 np_eop : 1; + u64 np_sop : 1; + } s; + struct cvmx_iob_int_enb_cn30xx { + u64 reserved_4_63 : 60; + u64 p_eop : 1; + u64 p_sop : 1; + u64 np_eop : 1; + u64 np_sop : 1; + } cn30xx; + struct cvmx_iob_int_enb_cn30xx cn31xx; + struct cvmx_iob_int_enb_cn30xx cn38xx; + struct cvmx_iob_int_enb_cn30xx cn38xxp2; + struct cvmx_iob_int_enb_cn50xx { + u64 reserved_6_63 : 58; + u64 p_dat : 1; + u64 np_dat : 1; + u64 p_eop : 1; + u64 p_sop : 1; + u64 np_eop : 1; + u64 np_sop : 1; + } cn50xx; + struct cvmx_iob_int_enb_cn50xx cn52xx; + struct cvmx_iob_int_enb_cn50xx cn52xxp1; + struct cvmx_iob_int_enb_cn50xx cn56xx; + struct cvmx_iob_int_enb_cn50xx cn56xxp1; + struct cvmx_iob_int_enb_cn50xx cn58xx; + struct cvmx_iob_int_enb_cn50xx cn58xxp1; + struct cvmx_iob_int_enb_cn50xx cn61xx; + struct cvmx_iob_int_enb_cn50xx cn63xx; + struct cvmx_iob_int_enb_cn50xx cn63xxp1; + struct cvmx_iob_int_enb_cn50xx cn66xx; + struct cvmx_iob_int_enb_cn68xx { + u64 reserved_0_63 : 64; + } cn68xx; + struct cvmx_iob_int_enb_cn68xx cn68xxp1; + struct cvmx_iob_int_enb_s cn70xx; + struct cvmx_iob_int_enb_s cn70xxp1; + struct cvmx_iob_int_enb_cn50xx cnf71xx; +}; + +typedef union cvmx_iob_int_enb cvmx_iob_int_enb_t; + +/** + * cvmx_iob_int_sum + * + * Contains the different interrupt summary bits of the IOB. + * + */ +union cvmx_iob_int_sum { + u64 u64; + struct cvmx_iob_int_sum_s { + u64 reserved_8_63 : 56; + u64 outb_mat : 1; + u64 inb_mat : 1; + u64 p_dat : 1; + u64 np_dat : 1; + u64 p_eop : 1; + u64 p_sop : 1; + u64 np_eop : 1; + u64 np_sop : 1; + } s; + struct cvmx_iob_int_sum_cn30xx { + u64 reserved_4_63 : 60; + u64 p_eop : 1; + u64 p_sop : 1; + u64 np_eop : 1; + u64 np_sop : 1; + } cn30xx; + struct cvmx_iob_int_sum_cn30xx cn31xx; + struct cvmx_iob_int_sum_cn30xx cn38xx; + struct cvmx_iob_int_sum_cn30xx cn38xxp2; + struct cvmx_iob_int_sum_cn50xx { + u64 reserved_6_63 : 58; + u64 p_dat : 1; + u64 np_dat : 1; + u64 p_eop : 1; + u64 p_sop : 1; + u64 np_eop : 1; + u64 np_sop : 1; + } cn50xx; + struct cvmx_iob_int_sum_cn50xx cn52xx; + struct cvmx_iob_int_sum_cn50xx cn52xxp1; + struct cvmx_iob_int_sum_cn50xx cn56xx; + struct cvmx_iob_int_sum_cn50xx cn56xxp1; + struct cvmx_iob_int_sum_cn50xx cn58xx; + struct cvmx_iob_int_sum_cn50xx cn58xxp1; + struct cvmx_iob_int_sum_cn50xx cn61xx; + struct cvmx_iob_int_sum_cn50xx cn63xx; + struct cvmx_iob_int_sum_cn50xx cn63xxp1; + struct cvmx_iob_int_sum_cn50xx cn66xx; + struct cvmx_iob_int_sum_cn68xx { + u64 reserved_0_63 : 64; + } cn68xx; + struct cvmx_iob_int_sum_cn68xx cn68xxp1; + struct cvmx_iob_int_sum_s cn70xx; + struct cvmx_iob_int_sum_s cn70xxp1; + struct cvmx_iob_int_sum_cn50xx cnf71xx; +}; + +typedef union cvmx_iob_int_sum cvmx_iob_int_sum_t; + +/** + * cvmx_iob_n2c_l2c_pri_cnt + * + * NCB To CMB L2C Priority Counter = NCB to CMB L2C Priority Counter Enable and Timer Value + * Enables and supplies the timeout count for raising the priority of NCB Store/Load access to + * the CMB. + */ +union cvmx_iob_n2c_l2c_pri_cnt { + u64 u64; + struct cvmx_iob_n2c_l2c_pri_cnt_s { + u64 reserved_16_63 : 48; + u64 cnt_enb : 1; + u64 cnt_val : 15; + } s; + struct cvmx_iob_n2c_l2c_pri_cnt_s cn38xx; + struct cvmx_iob_n2c_l2c_pri_cnt_s cn38xxp2; + struct cvmx_iob_n2c_l2c_pri_cnt_s cn52xx; + struct cvmx_iob_n2c_l2c_pri_cnt_s cn52xxp1; + struct cvmx_iob_n2c_l2c_pri_cnt_s cn56xx; + struct cvmx_iob_n2c_l2c_pri_cnt_s cn56xxp1; + struct cvmx_iob_n2c_l2c_pri_cnt_s cn58xx; + struct cvmx_iob_n2c_l2c_pri_cnt_s cn58xxp1; + struct cvmx_iob_n2c_l2c_pri_cnt_s cn61xx; + struct cvmx_iob_n2c_l2c_pri_cnt_s cn63xx; + struct cvmx_iob_n2c_l2c_pri_cnt_s cn63xxp1; + struct cvmx_iob_n2c_l2c_pri_cnt_s cn66xx; + struct cvmx_iob_n2c_l2c_pri_cnt_s cn70xx; + struct cvmx_iob_n2c_l2c_pri_cnt_s cn70xxp1; + struct cvmx_iob_n2c_l2c_pri_cnt_s cnf71xx; +}; + +typedef union cvmx_iob_n2c_l2c_pri_cnt cvmx_iob_n2c_l2c_pri_cnt_t; + +/** + * cvmx_iob_n2c_rsp_pri_cnt + * + * NCB To CMB Response Priority Counter = NCB to CMB Response Priority Counter Enable and Timer + * Value + * Enables and supplies the timeout count for raising the priority of NCB Responses access to the + * CMB. + */ +union cvmx_iob_n2c_rsp_pri_cnt { + u64 u64; + struct cvmx_iob_n2c_rsp_pri_cnt_s { + u64 reserved_16_63 : 48; + u64 cnt_enb : 1; + u64 cnt_val : 15; + } s; + struct cvmx_iob_n2c_rsp_pri_cnt_s cn38xx; + struct cvmx_iob_n2c_rsp_pri_cnt_s cn38xxp2; + struct cvmx_iob_n2c_rsp_pri_cnt_s cn52xx; + struct cvmx_iob_n2c_rsp_pri_cnt_s cn52xxp1; + struct cvmx_iob_n2c_rsp_pri_cnt_s cn56xx; + struct cvmx_iob_n2c_rsp_pri_cnt_s cn56xxp1; + struct cvmx_iob_n2c_rsp_pri_cnt_s cn58xx; + struct cvmx_iob_n2c_rsp_pri_cnt_s cn58xxp1; + struct cvmx_iob_n2c_rsp_pri_cnt_s cn61xx; + struct cvmx_iob_n2c_rsp_pri_cnt_s cn63xx; + struct cvmx_iob_n2c_rsp_pri_cnt_s cn63xxp1; + struct cvmx_iob_n2c_rsp_pri_cnt_s cn66xx; + struct cvmx_iob_n2c_rsp_pri_cnt_s cn70xx; + struct cvmx_iob_n2c_rsp_pri_cnt_s cn70xxp1; + struct cvmx_iob_n2c_rsp_pri_cnt_s cnf71xx; +}; + +typedef union cvmx_iob_n2c_rsp_pri_cnt cvmx_iob_n2c_rsp_pri_cnt_t; + +/** + * cvmx_iob_outb_com_pri_cnt + * + * Commit To NCB Priority Counter = Commit to NCB Priority Counter Enable and Timer Value + * Enables and supplies the timeout count for raising the priority of Commit request to the + * Outbound NCB. + */ +union cvmx_iob_outb_com_pri_cnt { + u64 u64; + struct cvmx_iob_outb_com_pri_cnt_s { + u64 reserved_16_63 : 48; + u64 cnt_enb : 1; + u64 cnt_val : 15; + } s; + struct cvmx_iob_outb_com_pri_cnt_s cn38xx; + struct cvmx_iob_outb_com_pri_cnt_s cn38xxp2; + struct cvmx_iob_outb_com_pri_cnt_s cn52xx; + struct cvmx_iob_outb_com_pri_cnt_s cn52xxp1; + struct cvmx_iob_outb_com_pri_cnt_s cn56xx; + struct cvmx_iob_outb_com_pri_cnt_s cn56xxp1; + struct cvmx_iob_outb_com_pri_cnt_s cn58xx; + struct cvmx_iob_outb_com_pri_cnt_s cn58xxp1; + struct cvmx_iob_outb_com_pri_cnt_s cn61xx; + struct cvmx_iob_outb_com_pri_cnt_s cn63xx; + struct cvmx_iob_outb_com_pri_cnt_s cn63xxp1; + struct cvmx_iob_outb_com_pri_cnt_s cn66xx; + struct cvmx_iob_outb_com_pri_cnt_s cn68xx; + struct cvmx_iob_outb_com_pri_cnt_s cn68xxp1; + struct cvmx_iob_outb_com_pri_cnt_s cn70xx; + struct cvmx_iob_outb_com_pri_cnt_s cn70xxp1; + struct cvmx_iob_outb_com_pri_cnt_s cnf71xx; +}; + +typedef union cvmx_iob_outb_com_pri_cnt cvmx_iob_outb_com_pri_cnt_t; + +/** + * cvmx_iob_outb_control_match + * + * Match pattern for the outbound control to set the OUTB_MATCH_BIT. + * + */ +union cvmx_iob_outb_control_match { + u64 u64; + struct cvmx_iob_outb_control_match_s { + u64 reserved_26_63 : 38; + u64 mask : 8; + u64 eot : 1; + u64 dst : 8; + u64 src : 9; + } s; + struct cvmx_iob_outb_control_match_s cn30xx; + struct cvmx_iob_outb_control_match_s cn31xx; + struct cvmx_iob_outb_control_match_s cn38xx; + struct cvmx_iob_outb_control_match_s cn38xxp2; + struct cvmx_iob_outb_control_match_s cn50xx; + struct cvmx_iob_outb_control_match_s cn52xx; + struct cvmx_iob_outb_control_match_s cn52xxp1; + struct cvmx_iob_outb_control_match_s cn56xx; + struct cvmx_iob_outb_control_match_s cn56xxp1; + struct cvmx_iob_outb_control_match_s cn58xx; + struct cvmx_iob_outb_control_match_s cn58xxp1; + struct cvmx_iob_outb_control_match_s cn61xx; + struct cvmx_iob_outb_control_match_s cn63xx; + struct cvmx_iob_outb_control_match_s cn63xxp1; + struct cvmx_iob_outb_control_match_s cn66xx; + struct cvmx_iob_outb_control_match_s cn68xx; + struct cvmx_iob_outb_control_match_s cn68xxp1; + struct cvmx_iob_outb_control_match_s cn70xx; + struct cvmx_iob_outb_control_match_s cn70xxp1; + struct cvmx_iob_outb_control_match_s cnf71xx; +}; + +typedef union cvmx_iob_outb_control_match cvmx_iob_outb_control_match_t; + +/** + * cvmx_iob_outb_control_match_enb + * + * Enables the match of the corresponding bit in the IOB_OUTB_CONTROL_MATCH reister. + * + */ +union cvmx_iob_outb_control_match_enb { + u64 u64; + struct cvmx_iob_outb_control_match_enb_s { + u64 reserved_26_63 : 38; + u64 mask : 8; + u64 eot : 1; + u64 dst : 8; + u64 src : 9; + } s; + struct cvmx_iob_outb_control_match_enb_s cn30xx; + struct cvmx_iob_outb_control_match_enb_s cn31xx; + struct cvmx_iob_outb_control_match_enb_s cn38xx; + struct cvmx_iob_outb_control_match_enb_s cn38xxp2; + struct cvmx_iob_outb_control_match_enb_s cn50xx; + struct cvmx_iob_outb_control_match_enb_s cn52xx; + struct cvmx_iob_outb_control_match_enb_s cn52xxp1; + struct cvmx_iob_outb_control_match_enb_s cn56xx; + struct cvmx_iob_outb_control_match_enb_s cn56xxp1; + struct cvmx_iob_outb_control_match_enb_s cn58xx; + struct cvmx_iob_outb_control_match_enb_s cn58xxp1; + struct cvmx_iob_outb_control_match_enb_s cn61xx; + struct cvmx_iob_outb_control_match_enb_s cn63xx; + struct cvmx_iob_outb_control_match_enb_s cn63xxp1; + struct cvmx_iob_outb_control_match_enb_s cn66xx; + struct cvmx_iob_outb_control_match_enb_s cn68xx; + struct cvmx_iob_outb_control_match_enb_s cn68xxp1; + struct cvmx_iob_outb_control_match_enb_s cn70xx; + struct cvmx_iob_outb_control_match_enb_s cn70xxp1; + struct cvmx_iob_outb_control_match_enb_s cnf71xx; +}; + +typedef union cvmx_iob_outb_control_match_enb cvmx_iob_outb_control_match_enb_t; + +/** + * cvmx_iob_outb_data_match + * + * Match pattern for the outbound data to set the OUTB_MATCH_BIT. + * + */ +union cvmx_iob_outb_data_match { + u64 u64; + struct cvmx_iob_outb_data_match_s { + u64 data : 64; + } s; + struct cvmx_iob_outb_data_match_s cn30xx; + struct cvmx_iob_outb_data_match_s cn31xx; + struct cvmx_iob_outb_data_match_s cn38xx; + struct cvmx_iob_outb_data_match_s cn38xxp2; + struct cvmx_iob_outb_data_match_s cn50xx; + struct cvmx_iob_outb_data_match_s cn52xx; + struct cvmx_iob_outb_data_match_s cn52xxp1; + struct cvmx_iob_outb_data_match_s cn56xx; + struct cvmx_iob_outb_data_match_s cn56xxp1; + struct cvmx_iob_outb_data_match_s cn58xx; + struct cvmx_iob_outb_data_match_s cn58xxp1; + struct cvmx_iob_outb_data_match_s cn61xx; + struct cvmx_iob_outb_data_match_s cn63xx; + struct cvmx_iob_outb_data_match_s cn63xxp1; + struct cvmx_iob_outb_data_match_s cn66xx; + struct cvmx_iob_outb_data_match_s cn68xx; + struct cvmx_iob_outb_data_match_s cn68xxp1; + struct cvmx_iob_outb_data_match_s cn70xx; + struct cvmx_iob_outb_data_match_s cn70xxp1; + struct cvmx_iob_outb_data_match_s cnf71xx; +}; + +typedef union cvmx_iob_outb_data_match cvmx_iob_outb_data_match_t; + +/** + * cvmx_iob_outb_data_match_enb + * + * Enables the match of the corresponding bit in the IOB_OUTB_DATA_MATCH reister. + * + */ +union cvmx_iob_outb_data_match_enb { + u64 u64; + struct cvmx_iob_outb_data_match_enb_s { + u64 data : 64; + } s; + struct cvmx_iob_outb_data_match_enb_s cn30xx; + struct cvmx_iob_outb_data_match_enb_s cn31xx; + struct cvmx_iob_outb_data_match_enb_s cn38xx; + struct cvmx_iob_outb_data_match_enb_s cn38xxp2; + struct cvmx_iob_outb_data_match_enb_s cn50xx; + struct cvmx_iob_outb_data_match_enb_s cn52xx; + struct cvmx_iob_outb_data_match_enb_s cn52xxp1; + struct cvmx_iob_outb_data_match_enb_s cn56xx; + struct cvmx_iob_outb_data_match_enb_s cn56xxp1; + struct cvmx_iob_outb_data_match_enb_s cn58xx; + struct cvmx_iob_outb_data_match_enb_s cn58xxp1; + struct cvmx_iob_outb_data_match_enb_s cn61xx; + struct cvmx_iob_outb_data_match_enb_s cn63xx; + struct cvmx_iob_outb_data_match_enb_s cn63xxp1; + struct cvmx_iob_outb_data_match_enb_s cn66xx; + struct cvmx_iob_outb_data_match_enb_s cn68xx; + struct cvmx_iob_outb_data_match_enb_s cn68xxp1; + struct cvmx_iob_outb_data_match_enb_s cn70xx; + struct cvmx_iob_outb_data_match_enb_s cn70xxp1; + struct cvmx_iob_outb_data_match_enb_s cnf71xx; +}; + +typedef union cvmx_iob_outb_data_match_enb cvmx_iob_outb_data_match_enb_t; + +/** + * cvmx_iob_outb_fpa_pri_cnt + * + * FPA To NCB Priority Counter = FPA Returns to NCB Priority Counter Enable and Timer Value + * Enables and supplies the timeout count for raising the priority of FPA Rreturn Page request to + * the Outbound NCB. + */ +union cvmx_iob_outb_fpa_pri_cnt { + u64 u64; + struct cvmx_iob_outb_fpa_pri_cnt_s { + u64 reserved_16_63 : 48; + u64 cnt_enb : 1; + u64 cnt_val : 15; + } s; + struct cvmx_iob_outb_fpa_pri_cnt_s cn38xx; + struct cvmx_iob_outb_fpa_pri_cnt_s cn38xxp2; + struct cvmx_iob_outb_fpa_pri_cnt_s cn52xx; + struct cvmx_iob_outb_fpa_pri_cnt_s cn52xxp1; + struct cvmx_iob_outb_fpa_pri_cnt_s cn56xx; + struct cvmx_iob_outb_fpa_pri_cnt_s cn56xxp1; + struct cvmx_iob_outb_fpa_pri_cnt_s cn58xx; + struct cvmx_iob_outb_fpa_pri_cnt_s cn58xxp1; + struct cvmx_iob_outb_fpa_pri_cnt_s cn61xx; + struct cvmx_iob_outb_fpa_pri_cnt_s cn63xx; + struct cvmx_iob_outb_fpa_pri_cnt_s cn63xxp1; + struct cvmx_iob_outb_fpa_pri_cnt_s cn66xx; + struct cvmx_iob_outb_fpa_pri_cnt_s cn68xx; + struct cvmx_iob_outb_fpa_pri_cnt_s cn68xxp1; + struct cvmx_iob_outb_fpa_pri_cnt_s cn70xx; + struct cvmx_iob_outb_fpa_pri_cnt_s cn70xxp1; + struct cvmx_iob_outb_fpa_pri_cnt_s cnf71xx; +}; + +typedef union cvmx_iob_outb_fpa_pri_cnt cvmx_iob_outb_fpa_pri_cnt_t; + +/** + * cvmx_iob_outb_req_pri_cnt + * + * Request To NCB Priority Counter = Request to NCB Priority Counter Enable and Timer Value + * Enables and supplies the timeout count for raising the priority of Request transfers to the + * Outbound NCB. + */ +union cvmx_iob_outb_req_pri_cnt { + u64 u64; + struct cvmx_iob_outb_req_pri_cnt_s { + u64 reserved_16_63 : 48; + u64 cnt_enb : 1; + u64 cnt_val : 15; + } s; + struct cvmx_iob_outb_req_pri_cnt_s cn38xx; + struct cvmx_iob_outb_req_pri_cnt_s cn38xxp2; + struct cvmx_iob_outb_req_pri_cnt_s cn52xx; + struct cvmx_iob_outb_req_pri_cnt_s cn52xxp1; + struct cvmx_iob_outb_req_pri_cnt_s cn56xx; + struct cvmx_iob_outb_req_pri_cnt_s cn56xxp1; + struct cvmx_iob_outb_req_pri_cnt_s cn58xx; + struct cvmx_iob_outb_req_pri_cnt_s cn58xxp1; + struct cvmx_iob_outb_req_pri_cnt_s cn61xx; + struct cvmx_iob_outb_req_pri_cnt_s cn63xx; + struct cvmx_iob_outb_req_pri_cnt_s cn63xxp1; + struct cvmx_iob_outb_req_pri_cnt_s cn66xx; + struct cvmx_iob_outb_req_pri_cnt_s cn68xx; + struct cvmx_iob_outb_req_pri_cnt_s cn68xxp1; + struct cvmx_iob_outb_req_pri_cnt_s cn70xx; + struct cvmx_iob_outb_req_pri_cnt_s cn70xxp1; + struct cvmx_iob_outb_req_pri_cnt_s cnf71xx; +}; + +typedef union cvmx_iob_outb_req_pri_cnt cvmx_iob_outb_req_pri_cnt_t; + +/** + * cvmx_iob_p2c_req_pri_cnt + * + * PKO To CMB Response Priority Counter = PKO to CMB Response Priority Counter Enable and Timer + * Value + * Enables and supplies the timeout count for raising the priority of PKO Load access to the CMB. + */ +union cvmx_iob_p2c_req_pri_cnt { + u64 u64; + struct cvmx_iob_p2c_req_pri_cnt_s { + u64 reserved_16_63 : 48; + u64 cnt_enb : 1; + u64 cnt_val : 15; + } s; + struct cvmx_iob_p2c_req_pri_cnt_s cn38xx; + struct cvmx_iob_p2c_req_pri_cnt_s cn38xxp2; + struct cvmx_iob_p2c_req_pri_cnt_s cn52xx; + struct cvmx_iob_p2c_req_pri_cnt_s cn52xxp1; + struct cvmx_iob_p2c_req_pri_cnt_s cn56xx; + struct cvmx_iob_p2c_req_pri_cnt_s cn56xxp1; + struct cvmx_iob_p2c_req_pri_cnt_s cn58xx; + struct cvmx_iob_p2c_req_pri_cnt_s cn58xxp1; + struct cvmx_iob_p2c_req_pri_cnt_s cn61xx; + struct cvmx_iob_p2c_req_pri_cnt_s cn63xx; + struct cvmx_iob_p2c_req_pri_cnt_s cn63xxp1; + struct cvmx_iob_p2c_req_pri_cnt_s cn66xx; + struct cvmx_iob_p2c_req_pri_cnt_s cn70xx; + struct cvmx_iob_p2c_req_pri_cnt_s cn70xxp1; + struct cvmx_iob_p2c_req_pri_cnt_s cnf71xx; +}; + +typedef union cvmx_iob_p2c_req_pri_cnt cvmx_iob_p2c_req_pri_cnt_t; + +/** + * cvmx_iob_pkt_err + * + * Provides status about the failing packet recevie error. + * + */ +union cvmx_iob_pkt_err { + u64 u64; + struct cvmx_iob_pkt_err_s { + u64 reserved_12_63 : 52; + u64 vport : 6; + u64 port : 6; + } s; + struct cvmx_iob_pkt_err_cn30xx { + u64 reserved_6_63 : 58; + u64 port : 6; + } cn30xx; + struct cvmx_iob_pkt_err_cn30xx cn31xx; + struct cvmx_iob_pkt_err_cn30xx cn38xx; + struct cvmx_iob_pkt_err_cn30xx cn38xxp2; + struct cvmx_iob_pkt_err_cn30xx cn50xx; + struct cvmx_iob_pkt_err_cn30xx cn52xx; + struct cvmx_iob_pkt_err_cn30xx cn52xxp1; + struct cvmx_iob_pkt_err_cn30xx cn56xx; + struct cvmx_iob_pkt_err_cn30xx cn56xxp1; + struct cvmx_iob_pkt_err_cn30xx cn58xx; + struct cvmx_iob_pkt_err_cn30xx cn58xxp1; + struct cvmx_iob_pkt_err_s cn61xx; + struct cvmx_iob_pkt_err_s cn63xx; + struct cvmx_iob_pkt_err_s cn63xxp1; + struct cvmx_iob_pkt_err_s cn66xx; + struct cvmx_iob_pkt_err_s cn70xx; + struct cvmx_iob_pkt_err_s cn70xxp1; + struct cvmx_iob_pkt_err_s cnf71xx; +}; + +typedef union cvmx_iob_pkt_err cvmx_iob_pkt_err_t; + +/** + * cvmx_iob_pp_bist_status + * + * The result of the BIST run on the PPs. + * + */ +union cvmx_iob_pp_bist_status { + u64 u64; + struct cvmx_iob_pp_bist_status_s { + u64 reserved_4_63 : 60; + u64 pp_bstat : 4; + } s; + struct cvmx_iob_pp_bist_status_s cn70xx; + struct cvmx_iob_pp_bist_status_s cn70xxp1; +}; + +typedef union cvmx_iob_pp_bist_status cvmx_iob_pp_bist_status_t; + +/** + * cvmx_iob_to_cmb_credits + * + * Controls the number of reads and writes that may be outstanding to the L2C (via the CMB). + * + */ +union cvmx_iob_to_cmb_credits { + u64 u64; + struct cvmx_iob_to_cmb_credits_s { + u64 reserved_6_63 : 58; + u64 ncb_rd : 3; + u64 ncb_wr : 3; + } s; + struct cvmx_iob_to_cmb_credits_cn52xx { + u64 reserved_9_63 : 55; + u64 pko_rd : 3; + u64 ncb_rd : 3; + u64 ncb_wr : 3; + } cn52xx; + struct cvmx_iob_to_cmb_credits_cn52xx cn61xx; + struct cvmx_iob_to_cmb_credits_cn52xx cn63xx; + struct cvmx_iob_to_cmb_credits_cn52xx cn63xxp1; + struct cvmx_iob_to_cmb_credits_cn52xx cn66xx; + struct cvmx_iob_to_cmb_credits_cn68xx { + u64 reserved_9_63 : 55; + u64 dwb : 3; + u64 ncb_rd : 3; + u64 ncb_wr : 3; + } cn68xx; + struct cvmx_iob_to_cmb_credits_cn68xx cn68xxp1; + struct cvmx_iob_to_cmb_credits_cn52xx cn70xx; + struct cvmx_iob_to_cmb_credits_cn52xx cn70xxp1; + struct cvmx_iob_to_cmb_credits_cn52xx cnf71xx; +}; + +typedef union cvmx_iob_to_cmb_credits cvmx_iob_to_cmb_credits_t; + +/** + * cvmx_iob_to_ncb_did_00_credits + * + * IOB_TO_NCB_DID_00_CREDITS = IOB NCB DID 00 Credits + * + * Number of credits for NCB DID 00. + */ +union cvmx_iob_to_ncb_did_00_credits { + u64 u64; + struct cvmx_iob_to_ncb_did_00_credits_s { + u64 reserved_7_63 : 57; + u64 crd : 7; + } s; + struct cvmx_iob_to_ncb_did_00_credits_s cn68xx; + struct cvmx_iob_to_ncb_did_00_credits_s cn68xxp1; +}; + +typedef union cvmx_iob_to_ncb_did_00_credits cvmx_iob_to_ncb_did_00_credits_t; + +/** + * cvmx_iob_to_ncb_did_111_credits + * + * IOB_TO_NCB_DID_111_CREDITS = IOB NCB DID 111 Credits + * + * Number of credits for NCB DID 111. + */ +union cvmx_iob_to_ncb_did_111_credits { + u64 u64; + struct cvmx_iob_to_ncb_did_111_credits_s { + u64 reserved_7_63 : 57; + u64 crd : 7; + } s; + struct cvmx_iob_to_ncb_did_111_credits_s cn68xx; + struct cvmx_iob_to_ncb_did_111_credits_s cn68xxp1; +}; + +typedef union cvmx_iob_to_ncb_did_111_credits cvmx_iob_to_ncb_did_111_credits_t; + +/** + * cvmx_iob_to_ncb_did_223_credits + * + * IOB_TO_NCB_DID_223_CREDITS = IOB NCB DID 223 Credits + * + * Number of credits for NCB DID 223. + */ +union cvmx_iob_to_ncb_did_223_credits { + u64 u64; + struct cvmx_iob_to_ncb_did_223_credits_s { + u64 reserved_7_63 : 57; + u64 crd : 7; + } s; + struct cvmx_iob_to_ncb_did_223_credits_s cn68xx; + struct cvmx_iob_to_ncb_did_223_credits_s cn68xxp1; +}; + +typedef union cvmx_iob_to_ncb_did_223_credits cvmx_iob_to_ncb_did_223_credits_t; + +/** + * cvmx_iob_to_ncb_did_24_credits + * + * IOB_TO_NCB_DID_24_CREDITS = IOB NCB DID 24 Credits + * + * Number of credits for NCB DID 24. + */ +union cvmx_iob_to_ncb_did_24_credits { + u64 u64; + struct cvmx_iob_to_ncb_did_24_credits_s { + u64 reserved_7_63 : 57; + u64 crd : 7; + } s; + struct cvmx_iob_to_ncb_did_24_credits_s cn68xx; + struct cvmx_iob_to_ncb_did_24_credits_s cn68xxp1; +}; + +typedef union cvmx_iob_to_ncb_did_24_credits cvmx_iob_to_ncb_did_24_credits_t; + +/** + * cvmx_iob_to_ncb_did_32_credits + * + * IOB_TO_NCB_DID_32_CREDITS = IOB NCB DID 32 Credits + * + * Number of credits for NCB DID 32. + */ +union cvmx_iob_to_ncb_did_32_credits { + u64 u64; + struct cvmx_iob_to_ncb_did_32_credits_s { + u64 reserved_7_63 : 57; + u64 crd : 7; + } s; + struct cvmx_iob_to_ncb_did_32_credits_s cn68xx; + struct cvmx_iob_to_ncb_did_32_credits_s cn68xxp1; +}; + +typedef union cvmx_iob_to_ncb_did_32_credits cvmx_iob_to_ncb_did_32_credits_t; + +/** + * cvmx_iob_to_ncb_did_40_credits + * + * IOB_TO_NCB_DID_40_CREDITS = IOB NCB DID 40 Credits + * + * Number of credits for NCB DID 40. + */ +union cvmx_iob_to_ncb_did_40_credits { + u64 u64; + struct cvmx_iob_to_ncb_did_40_credits_s { + u64 reserved_7_63 : 57; + u64 crd : 7; + } s; + struct cvmx_iob_to_ncb_did_40_credits_s cn68xx; + struct cvmx_iob_to_ncb_did_40_credits_s cn68xxp1; +}; + +typedef union cvmx_iob_to_ncb_did_40_credits cvmx_iob_to_ncb_did_40_credits_t; + +/** + * cvmx_iob_to_ncb_did_55_credits + * + * IOB_TO_NCB_DID_55_CREDITS = IOB NCB DID 55 Credits + * + * Number of credits for NCB DID 55. + */ +union cvmx_iob_to_ncb_did_55_credits { + u64 u64; + struct cvmx_iob_to_ncb_did_55_credits_s { + u64 reserved_7_63 : 57; + u64 crd : 7; + } s; + struct cvmx_iob_to_ncb_did_55_credits_s cn68xx; + struct cvmx_iob_to_ncb_did_55_credits_s cn68xxp1; +}; + +typedef union cvmx_iob_to_ncb_did_55_credits cvmx_iob_to_ncb_did_55_credits_t; + +/** + * cvmx_iob_to_ncb_did_64_credits + * + * IOB_TO_NCB_DID_64_CREDITS = IOB NCB DID 64 Credits + * + * Number of credits for NCB DID 64. + */ +union cvmx_iob_to_ncb_did_64_credits { + u64 u64; + struct cvmx_iob_to_ncb_did_64_credits_s { + u64 reserved_7_63 : 57; + u64 crd : 7; + } s; + struct cvmx_iob_to_ncb_did_64_credits_s cn68xx; + struct cvmx_iob_to_ncb_did_64_credits_s cn68xxp1; +}; + +typedef union cvmx_iob_to_ncb_did_64_credits cvmx_iob_to_ncb_did_64_credits_t; + +/** + * cvmx_iob_to_ncb_did_79_credits + * + * IOB_TO_NCB_DID_79_CREDITS = IOB NCB DID 79 Credits + * + * Number of credits for NCB DID 79. + */ +union cvmx_iob_to_ncb_did_79_credits { + u64 u64; + struct cvmx_iob_to_ncb_did_79_credits_s { + u64 reserved_7_63 : 57; + u64 crd : 7; + } s; + struct cvmx_iob_to_ncb_did_79_credits_s cn68xx; + struct cvmx_iob_to_ncb_did_79_credits_s cn68xxp1; +}; + +typedef union cvmx_iob_to_ncb_did_79_credits cvmx_iob_to_ncb_did_79_credits_t; + +/** + * cvmx_iob_to_ncb_did_96_credits + * + * IOB_TO_NCB_DID_96_CREDITS = IOB NCB DID 96 Credits + * + * Number of credits for NCB DID 96. + */ +union cvmx_iob_to_ncb_did_96_credits { + u64 u64; + struct cvmx_iob_to_ncb_did_96_credits_s { + u64 reserved_7_63 : 57; + u64 crd : 7; + } s; + struct cvmx_iob_to_ncb_did_96_credits_s cn68xx; + struct cvmx_iob_to_ncb_did_96_credits_s cn68xxp1; +}; + +typedef union cvmx_iob_to_ncb_did_96_credits cvmx_iob_to_ncb_did_96_credits_t; + +/** + * cvmx_iob_to_ncb_did_98_credits + * + * IOB_TO_NCB_DID_98_CREDITS = IOB NCB DID 96 Credits + * + * Number of credits for NCB DID 98. + */ +union cvmx_iob_to_ncb_did_98_credits { + u64 u64; + struct cvmx_iob_to_ncb_did_98_credits_s { + u64 reserved_7_63 : 57; + u64 crd : 7; + } s; + struct cvmx_iob_to_ncb_did_98_credits_s cn68xx; + struct cvmx_iob_to_ncb_did_98_credits_s cn68xxp1; +}; + +typedef union cvmx_iob_to_ncb_did_98_credits cvmx_iob_to_ncb_did_98_credits_t; + +#endif diff --git a/arch/mips/mach-octeon/include/mach/cvmx-lbk-defs.h b/arch/mips/mach-octeon/include/mach/cvmx-lbk-defs.h new file mode 100644 index 0000000..1068a19 --- /dev/null +++ b/arch/mips/mach-octeon/include/mach/cvmx-lbk-defs.h @@ -0,0 +1,157 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018-2022 Marvell International Ltd. + * + * Configuration and status register (CSR) type definitions for + * Octeon lbk. + */ + +#ifndef __CVMX_LBK_DEFS_H__ +#define __CVMX_LBK_DEFS_H__ + +#define CVMX_LBK_BIST_RESULT (0x0001180012000020ull) +#define CVMX_LBK_CHX_PKIND(offset) (0x0001180012000200ull + ((offset) & 63) * 8) +#define CVMX_LBK_CLK_GATE_CTL (0x0001180012000008ull) +#define CVMX_LBK_DAT_ERR_INFO (0x0001180012000050ull) +#define CVMX_LBK_ECC_CFG (0x0001180012000060ull) +#define CVMX_LBK_INT (0x0001180012000040ull) +#define CVMX_LBK_SFT_RST (0x0001180012000000ull) + +/** + * cvmx_lbk_bist_result + * + * This register provides access to the internal BIST results. Each bit is the + * BIST result of an individual memory (per bit, 0 = pass and 1 = fail). + */ +union cvmx_lbk_bist_result { + u64 u64; + struct cvmx_lbk_bist_result_s { + u64 reserved_1_63 : 63; + u64 dat : 1; + } s; + struct cvmx_lbk_bist_result_s cn73xx; + struct cvmx_lbk_bist_result_s cn78xx; + struct cvmx_lbk_bist_result_s cn78xxp1; + struct cvmx_lbk_bist_result_s cnf75xx; +}; + +typedef union cvmx_lbk_bist_result cvmx_lbk_bist_result_t; + +/** + * cvmx_lbk_ch#_pkind + */ +union cvmx_lbk_chx_pkind { + u64 u64; + struct cvmx_lbk_chx_pkind_s { + u64 reserved_6_63 : 58; + u64 pkind : 6; + } s; + struct cvmx_lbk_chx_pkind_s cn73xx; + struct cvmx_lbk_chx_pkind_s cn78xx; + struct cvmx_lbk_chx_pkind_s cn78xxp1; + struct cvmx_lbk_chx_pkind_s cnf75xx; +}; + +typedef union cvmx_lbk_chx_pkind cvmx_lbk_chx_pkind_t; + +/** + * cvmx_lbk_clk_gate_ctl + * + * This register is for diagnostic use only. + * + */ +union cvmx_lbk_clk_gate_ctl { + u64 u64; + struct cvmx_lbk_clk_gate_ctl_s { + u64 reserved_1_63 : 63; + u64 dis : 1; + } s; + struct cvmx_lbk_clk_gate_ctl_s cn73xx; + struct cvmx_lbk_clk_gate_ctl_s cn78xx; + struct cvmx_lbk_clk_gate_ctl_s cn78xxp1; + struct cvmx_lbk_clk_gate_ctl_s cnf75xx; +}; + +typedef union cvmx_lbk_clk_gate_ctl cvmx_lbk_clk_gate_ctl_t; + +/** + * cvmx_lbk_dat_err_info + */ +union cvmx_lbk_dat_err_info { + u64 u64; + struct cvmx_lbk_dat_err_info_s { + u64 reserved_58_63 : 6; + u64 dbe_ecc_out : 9; + u64 dbe_synd : 9; + u64 dbe_addr : 8; + u64 reserved_26_31 : 6; + u64 sbe_ecc_out : 9; + u64 sbe_synd : 9; + u64 sbe_addr : 8; + } s; + struct cvmx_lbk_dat_err_info_s cn73xx; + struct cvmx_lbk_dat_err_info_s cn78xx; + struct cvmx_lbk_dat_err_info_s cn78xxp1; + struct cvmx_lbk_dat_err_info_s cnf75xx; +}; + +typedef union cvmx_lbk_dat_err_info cvmx_lbk_dat_err_info_t; + +/** + * cvmx_lbk_ecc_cfg + */ +union cvmx_lbk_ecc_cfg { + u64 u64; + struct cvmx_lbk_ecc_cfg_s { + u64 reserved_3_63 : 61; + u64 dat_flip : 2; + u64 dat_cdis : 1; + } s; + struct cvmx_lbk_ecc_cfg_s cn73xx; + struct cvmx_lbk_ecc_cfg_s cn78xx; + struct cvmx_lbk_ecc_cfg_s cn78xxp1; + struct cvmx_lbk_ecc_cfg_s cnf75xx; +}; + +typedef union cvmx_lbk_ecc_cfg cvmx_lbk_ecc_cfg_t; + +/** + * cvmx_lbk_int + */ +union cvmx_lbk_int { + u64 u64; + struct cvmx_lbk_int_s { + u64 reserved_6_63 : 58; + u64 chan_oflow : 1; + u64 chan_uflow : 1; + u64 dat_oflow : 1; + u64 dat_uflow : 1; + u64 dat_dbe : 1; + u64 dat_sbe : 1; + } s; + struct cvmx_lbk_int_s cn73xx; + struct cvmx_lbk_int_s cn78xx; + struct cvmx_lbk_int_s cn78xxp1; + struct cvmx_lbk_int_s cnf75xx; +}; + +typedef union cvmx_lbk_int cvmx_lbk_int_t; + +/** + * cvmx_lbk_sft_rst + */ +union cvmx_lbk_sft_rst { + u64 u64; + struct cvmx_lbk_sft_rst_s { + u64 reserved_1_63 : 63; + u64 reset : 1; + } s; + struct cvmx_lbk_sft_rst_s cn73xx; + struct cvmx_lbk_sft_rst_s cn78xx; + struct cvmx_lbk_sft_rst_s cn78xxp1; + struct cvmx_lbk_sft_rst_s cnf75xx; +}; + +typedef union cvmx_lbk_sft_rst cvmx_lbk_sft_rst_t; + +#endif diff --git a/arch/mips/mach-octeon/include/mach/cvmx-mdio.h b/arch/mips/mach-octeon/include/mach/cvmx-mdio.h new file mode 100644 index 0000000..9bc138f --- /dev/null +++ b/arch/mips/mach-octeon/include/mach/cvmx-mdio.h @@ -0,0 +1,516 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018-2022 Marvell International Ltd. + * + * Interface to the SMI/MDIO hardware, including support for both IEEE 802.3 + * clause 22 and clause 45 operations. + */ + +#ifndef __CVMX_MIO_H__ +#define __CVMX_MIO_H__ + +/** + * PHY register 0 from the 802.3 spec + */ +#define CVMX_MDIO_PHY_REG_CONTROL 0 + +typedef union { + u16 u16; + struct { + u16 reset : 1; + u16 loopback : 1; + u16 speed_lsb : 1; + u16 autoneg_enable : 1; + u16 power_down : 1; + u16 isolate : 1; + u16 restart_autoneg : 1; + u16 duplex : 1; + u16 collision_test : 1; + u16 speed_msb : 1; + u16 unidirectional_enable : 1; + u16 reserved_0_4 : 5; + } s; +} cvmx_mdio_phy_reg_control_t; + +/** + * PHY register 1 from the 802.3 spec + */ +#define CVMX_MDIO_PHY_REG_STATUS 1 +typedef union { + u16 u16; + struct { + u16 capable_100base_t4 : 1; + u16 capable_100base_x_full : 1; + u16 capable_100base_x_half : 1; + u16 capable_10_full : 1; + u16 capable_10_half : 1; + u16 capable_100base_t2_full : 1; + u16 capable_100base_t2_half : 1; + u16 capable_extended_status : 1; + u16 capable_unidirectional : 1; + u16 capable_mf_preamble_suppression : 1; + u16 autoneg_complete : 1; + u16 remote_fault : 1; + u16 capable_autoneg : 1; + u16 link_status : 1; + u16 jabber_detect : 1; + u16 capable_extended_registers : 1; + + } s; +} cvmx_mdio_phy_reg_status_t; + +/** + * PHY register 2 from the 802.3 spec + */ +#define CVMX_MDIO_PHY_REG_ID1 2 +typedef union { + u16 u16; + struct { + u16 oui_bits_3_18; + } s; +} cvmx_mdio_phy_reg_id1_t; + +/** + * PHY register 3 from the 802.3 spec + */ +#define CVMX_MDIO_PHY_REG_ID2 3 +typedef union { + u16 u16; + struct { + u16 oui_bits_19_24 : 6; + u16 model : 6; + u16 revision : 4; + } s; +} cvmx_mdio_phy_reg_id2_t; + +/** + * PHY register 4 from the 802.3 spec + */ +#define CVMX_MDIO_PHY_REG_AUTONEG_ADVER 4 +typedef union { + u16 u16; + struct { + u16 next_page : 1; + u16 reserved_14 : 1; + u16 remote_fault : 1; + u16 reserved_12 : 1; + u16 asymmetric_pause : 1; + u16 pause : 1; + u16 advert_100base_t4 : 1; + u16 advert_100base_tx_full : 1; + u16 advert_100base_tx_half : 1; + u16 advert_10base_tx_full : 1; + u16 advert_10base_tx_half : 1; + u16 selector : 5; + } s; +} cvmx_mdio_phy_reg_autoneg_adver_t; + +/** + * PHY register 5 from the 802.3 spec + */ +#define CVMX_MDIO_PHY_REG_LINK_PARTNER_ABILITY 5 +typedef union { + u16 u16; + struct { + u16 next_page : 1; + u16 ack : 1; + u16 remote_fault : 1; + u16 reserved_12 : 1; + u16 asymmetric_pause : 1; + u16 pause : 1; + u16 advert_100base_t4 : 1; + u16 advert_100base_tx_full : 1; + u16 advert_100base_tx_half : 1; + u16 advert_10base_tx_full : 1; + u16 advert_10base_tx_half : 1; + u16 selector : 5; + } s; +} cvmx_mdio_phy_reg_link_partner_ability_t; + +/** + * PHY register 6 from the 802.3 spec + */ +#define CVMX_MDIO_PHY_REG_AUTONEG_EXPANSION 6 +typedef union { + u16 u16; + struct { + u16 reserved_5_15 : 11; + u16 parallel_detection_fault : 1; + u16 link_partner_next_page_capable : 1; + u16 local_next_page_capable : 1; + u16 page_received : 1; + u16 link_partner_autoneg_capable : 1; + + } s; +} cvmx_mdio_phy_reg_autoneg_expansion_t; + +/** + * PHY register 9 from the 802.3 spec + */ +#define CVMX_MDIO_PHY_REG_CONTROL_1000 9 +typedef union { + u16 u16; + struct { + u16 test_mode : 3; + u16 manual_master_slave : 1; + u16 master : 1; + u16 port_type : 1; + u16 advert_1000base_t_full : 1; + u16 advert_1000base_t_half : 1; + u16 reserved_0_7 : 8; + } s; +} cvmx_mdio_phy_reg_control_1000_t; + +/** + * PHY register 10 from the 802.3 spec + */ +#define CVMX_MDIO_PHY_REG_STATUS_1000 10 +typedef union { + u16 u16; + struct { + u16 master_slave_fault : 1; + u16 is_master : 1; + u16 local_receiver_ok : 1; + u16 remote_receiver_ok : 1; + u16 remote_capable_1000base_t_full : 1; + u16 remote_capable_1000base_t_half : 1; + u16 reserved_8_9 : 2; + u16 idle_error_count : 8; + } s; +} cvmx_mdio_phy_reg_status_1000_t; + +/** + * PHY register 15 from the 802.3 spec + */ +#define CVMX_MDIO_PHY_REG_EXTENDED_STATUS 15 +typedef union { + u16 u16; + struct { + u16 capable_1000base_x_full : 1; + u16 capable_1000base_x_half : 1; + u16 capable_1000base_t_full : 1; + u16 capable_1000base_t_half : 1; + u16 reserved_0_11 : 12; + } s; +} cvmx_mdio_phy_reg_extended_status_t; + +/** + * PHY register 13 from the 802.3 spec + */ +#define CVMX_MDIO_PHY_REG_MMD_CONTROL 13 +typedef union { + u16 u16; + struct { + u16 function : 2; + u16 reserved_5_13 : 9; + u16 devad : 5; + } s; +} cvmx_mdio_phy_reg_mmd_control_t; + +/** + * PHY register 14 from the 802.3 spec + */ +#define CVMX_MDIO_PHY_REG_MMD_ADDRESS_DATA 14 +typedef union { + u16 u16; + struct { + u16 address_data : 16; + } s; +} cvmx_mdio_phy_reg_mmd_address_data_t; + +/* Operating request encodings. */ +#define MDIO_CLAUSE_22_WRITE 0 +#define MDIO_CLAUSE_22_READ 1 + +#define MDIO_CLAUSE_45_ADDRESS 0 +#define MDIO_CLAUSE_45_WRITE 1 +#define MDIO_CLAUSE_45_READ_INC 2 +#define MDIO_CLAUSE_45_READ 3 + +/* MMD identifiers, mostly for accessing devices within XENPAK modules. */ +#define CVMX_MMD_DEVICE_PMA_PMD 1 +#define CVMX_MMD_DEVICE_WIS 2 +#define CVMX_MMD_DEVICE_PCS 3 +#define CVMX_MMD_DEVICE_PHY_XS 4 +#define CVMX_MMD_DEVICE_DTS_XS 5 +#define CVMX_MMD_DEVICE_TC 6 +#define CVMX_MMD_DEVICE_CL22_EXT 29 +#define CVMX_MMD_DEVICE_VENDOR_1 30 +#define CVMX_MMD_DEVICE_VENDOR_2 31 + +#define CVMX_MDIO_TIMEOUT 100000 /* 100 millisec */ + +static inline int cvmx_mdio_bus_id_to_node(int bus_id) +{ + if (OCTEON_IS_MODEL(OCTEON_CN78XX)) + return (bus_id >> 2) & CVMX_NODE_MASK; + else + return 0; +} + +static inline int cvmx_mdio_bus_id_to_bus(int bus_id) +{ + if (OCTEON_IS_MODEL(OCTEON_CN78XX)) + return bus_id & 3; + else + return bus_id; +} + +/* Helper function to put MDIO interface into clause 45 mode */ +static inline void __cvmx_mdio_set_clause45_mode(int bus_id) +{ + cvmx_smix_clk_t smi_clk; + int node = cvmx_mdio_bus_id_to_node(bus_id); + int bus = cvmx_mdio_bus_id_to_bus(bus_id); + + /* Put bus into clause 45 mode */ + smi_clk.u64 = csr_rd_node(node, CVMX_SMIX_CLK(bus)); + smi_clk.s.mode = 1; + smi_clk.s.preamble = 1; + csr_wr_node(node, CVMX_SMIX_CLK(bus), smi_clk.u64); +} + +/* Helper function to put MDIO interface into clause 22 mode */ +static inline void __cvmx_mdio_set_clause22_mode(int bus_id) +{ + cvmx_smix_clk_t smi_clk; + int node = cvmx_mdio_bus_id_to_node(bus_id); + int bus = cvmx_mdio_bus_id_to_bus(bus_id); + + /* Put bus into clause 22 mode */ + smi_clk.u64 = csr_rd_node(node, CVMX_SMIX_CLK(bus)); + smi_clk.s.mode = 0; + csr_wr_node(node, CVMX_SMIX_CLK(bus), smi_clk.u64); +} + +/** + * @INTERNAL + * Function to read SMIX_RD_DAT and check for timeouts. This + * code sequence is done fairly often, so put in one spot. + * + * @param bus_id SMI/MDIO bus to read + * + * @return Value of SMIX_RD_DAT. pending will be set on + * a timeout. + */ +static inline cvmx_smix_rd_dat_t __cvmx_mdio_read_rd_dat(int bus_id) +{ + cvmx_smix_rd_dat_t smi_rd; + int node = cvmx_mdio_bus_id_to_node(bus_id); + int bus = cvmx_mdio_bus_id_to_bus(bus_id); + u64 done; + + done = get_timer(0); + + do { + mdelay(1); + smi_rd.u64 = csr_rd_node(node, CVMX_SMIX_RD_DAT(bus)); + if (get_timer(done) > (CVMX_MDIO_TIMEOUT / 1000)) + break; + } while (smi_rd.s.pending); + + return smi_rd; +} + +/** + * Perform an MII read. This function is used to read PHY + * registers controlling auto negotiation. + * + * @param bus_id MDIO bus number. Zero on most chips, but some chips (ex CN56XX) + * support multiple busses. + * @param phy_id The MII phy id + * @param location Register location to read + * + * @return Result from the read or -1 on failure + */ +static inline int cvmx_mdio_read(int bus_id, int phy_id, int location) +{ + int node = cvmx_mdio_bus_id_to_node(bus_id); + int bus = cvmx_mdio_bus_id_to_bus(bus_id); + cvmx_smix_cmd_t smi_cmd; + cvmx_smix_rd_dat_t smi_rd; + + if (octeon_has_feature(OCTEON_FEATURE_MDIO_CLAUSE_45)) + __cvmx_mdio_set_clause22_mode(bus_id); + + smi_cmd.u64 = 0; + smi_cmd.s.phy_op = MDIO_CLAUSE_22_READ; + smi_cmd.s.phy_adr = phy_id; + smi_cmd.s.reg_adr = location; + csr_wr_node(node, CVMX_SMIX_CMD(bus), smi_cmd.u64); + + smi_rd = __cvmx_mdio_read_rd_dat(bus_id); + if (smi_rd.s.val) + return smi_rd.s.dat; + else + return -1; +} + +/** + * Perform an MII write. This function is used to write PHY + * registers controlling auto negotiation. + * + * @param bus_id MDIO bus number. Zero on most chips, but some chips (ex CN56XX) + * support multiple busses. + * @param phy_id The MII phy id + * @param location Register location to write + * @param val Value to write + * + * @return -1 on error + * 0 on success + */ +static inline int cvmx_mdio_write(int bus_id, int phy_id, int location, int val) +{ + int node = cvmx_mdio_bus_id_to_node(bus_id); + int bus = cvmx_mdio_bus_id_to_bus(bus_id); + cvmx_smix_cmd_t smi_cmd; + cvmx_smix_wr_dat_t smi_wr; + + if (octeon_has_feature(OCTEON_FEATURE_MDIO_CLAUSE_45)) + __cvmx_mdio_set_clause22_mode(bus_id); + + smi_wr.u64 = 0; + smi_wr.s.dat = val; + csr_wr_node(node, CVMX_SMIX_WR_DAT(bus), smi_wr.u64); + + smi_cmd.u64 = 0; + smi_cmd.s.phy_op = MDIO_CLAUSE_22_WRITE; + smi_cmd.s.phy_adr = phy_id; + smi_cmd.s.reg_adr = location; + csr_wr_node(node, CVMX_SMIX_CMD(bus), smi_cmd.u64); + + if (CVMX_WAIT_FOR_FIELD64_NODE(node, CVMX_SMIX_WR_DAT(bus), + cvmx_smix_wr_dat_t, pending, ==, 0, + CVMX_MDIO_TIMEOUT)) + return -1; + + return 0; +} + +/** + * Perform an IEEE 802.3 clause 45 MII read. This function is used to read PHY + * registers controlling auto negotiation. + * + * @param bus_id MDIO bus number. Zero on most chips, but some chips (ex CN56XX) + * support multiple busses. + * @param phy_id The MII phy id + * @param device MDIO Manageable Device (MMD) id + * @param location Register location to read + * + * @return Result from the read or -1 on failure + */ + +static inline int cvmx_mdio_45_read(int bus_id, int phy_id, int device, + int location) +{ + cvmx_smix_cmd_t smi_cmd; + cvmx_smix_rd_dat_t smi_rd; + cvmx_smix_wr_dat_t smi_wr; + int node = cvmx_mdio_bus_id_to_node(bus_id); + int bus = cvmx_mdio_bus_id_to_bus(bus_id); + + if (!octeon_has_feature(OCTEON_FEATURE_MDIO_CLAUSE_45)) + return -1; + + __cvmx_mdio_set_clause45_mode(bus_id); + + smi_wr.u64 = 0; + smi_wr.s.dat = location; + csr_wr_node(node, CVMX_SMIX_WR_DAT(bus), smi_wr.u64); + + smi_cmd.u64 = 0; + smi_cmd.s.phy_op = MDIO_CLAUSE_45_ADDRESS; + smi_cmd.s.phy_adr = phy_id; + smi_cmd.s.reg_adr = device; + csr_wr_node(node, CVMX_SMIX_CMD(bus), smi_cmd.u64); + + if (CVMX_WAIT_FOR_FIELD64_NODE(node, CVMX_SMIX_WR_DAT(bus), + cvmx_smix_wr_dat_t, pending, ==, 0, + CVMX_MDIO_TIMEOUT)) { + debug("cvmx_mdio_45_read: bus_id %d phy_id %2d device %2d register %2d TIME OUT(address)\n", + bus_id, phy_id, device, location); + return -1; + } + + smi_cmd.u64 = 0; + smi_cmd.s.phy_op = MDIO_CLAUSE_45_READ; + smi_cmd.s.phy_adr = phy_id; + smi_cmd.s.reg_adr = device; + csr_wr_node(node, CVMX_SMIX_CMD(bus), smi_cmd.u64); + + smi_rd = __cvmx_mdio_read_rd_dat(bus_id); + if (smi_rd.s.pending) { + debug("cvmx_mdio_45_read: bus_id %d phy_id %2d device %2d register %2d TIME OUT(data)\n", + bus_id, phy_id, device, location); + return -1; + } + + if (smi_rd.s.val) + return smi_rd.s.dat; + + debug("cvmx_mdio_45_read: bus_id %d phy_id %2d device %2d register %2d INVALID READ\n", + bus_id, phy_id, device, location); + return -1; +} + +/** + * Perform an IEEE 802.3 clause 45 MII write. This function is used to write PHY + * registers controlling auto negotiation. + * + * @param bus_id MDIO bus number. Zero on most chips, but some chips (ex CN56XX) + * support multiple busses. + * @param phy_id The MII phy id + * @param device MDIO Manageable Device (MMD) id + * @param location Register location to write + * @param val Value to write + * + * @return -1 on error + * 0 on success + */ +static inline int cvmx_mdio_45_write(int bus_id, int phy_id, int device, + int location, int val) +{ + cvmx_smix_cmd_t smi_cmd; + cvmx_smix_wr_dat_t smi_wr; + int node = cvmx_mdio_bus_id_to_node(bus_id); + int bus = cvmx_mdio_bus_id_to_bus(bus_id); + + if (!octeon_has_feature(OCTEON_FEATURE_MDIO_CLAUSE_45)) + return -1; + + __cvmx_mdio_set_clause45_mode(bus_id); + + smi_wr.u64 = 0; + smi_wr.s.dat = location; + csr_wr_node(node, CVMX_SMIX_WR_DAT(bus), smi_wr.u64); + + smi_cmd.u64 = 0; + smi_cmd.s.phy_op = MDIO_CLAUSE_45_ADDRESS; + smi_cmd.s.phy_adr = phy_id; + smi_cmd.s.reg_adr = device; + csr_wr_node(node, CVMX_SMIX_CMD(bus), smi_cmd.u64); + + if (CVMX_WAIT_FOR_FIELD64_NODE(node, CVMX_SMIX_WR_DAT(bus), + cvmx_smix_wr_dat_t, pending, ==, 0, + CVMX_MDIO_TIMEOUT)) + return -1; + + smi_wr.u64 = 0; + smi_wr.s.dat = val; + csr_wr_node(node, CVMX_SMIX_WR_DAT(bus), smi_wr.u64); + + smi_cmd.u64 = 0; + smi_cmd.s.phy_op = MDIO_CLAUSE_45_WRITE; + smi_cmd.s.phy_adr = phy_id; + smi_cmd.s.reg_adr = device; + csr_wr_node(node, CVMX_SMIX_CMD(bus), smi_cmd.u64); + + if (CVMX_WAIT_FOR_FIELD64_NODE(node, CVMX_SMIX_WR_DAT(bus), + cvmx_smix_wr_dat_t, pending, ==, 0, + CVMX_MDIO_TIMEOUT)) + return -1; + + return 0; +} + +#endif diff --git a/arch/mips/mach-octeon/include/mach/cvmx-npei-defs.h b/arch/mips/mach-octeon/include/mach/cvmx-npei-defs.h new file mode 100644 index 0000000..2e2c248 --- /dev/null +++ b/arch/mips/mach-octeon/include/mach/cvmx-npei-defs.h @@ -0,0 +1,3550 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018-2022 Marvell International Ltd. + * + * Configuration and status register (CSR) type definitions for + * Octeon npei. + */ + +#ifndef __CVMX_NPEI_DEFS_H__ +#define __CVMX_NPEI_DEFS_H__ + +#define CVMX_NPEI_BAR1_INDEXX(offset) \ + (0x0000000000000000ull + ((offset) & 31) * 16) +#define CVMX_NPEI_BIST_STATUS (0x0000000000000580ull) +#define CVMX_NPEI_BIST_STATUS2 (0x0000000000000680ull) +#define CVMX_NPEI_CTL_PORT0 (0x0000000000000250ull) +#define CVMX_NPEI_CTL_PORT1 (0x0000000000000260ull) +#define CVMX_NPEI_CTL_STATUS (0x0000000000000570ull) +#define CVMX_NPEI_CTL_STATUS2 (0x0000000000003C00ull) +#define CVMX_NPEI_DATA_OUT_CNT (0x00000000000005F0ull) +#define CVMX_NPEI_DBG_DATA (0x0000000000000510ull) +#define CVMX_NPEI_DBG_SELECT (0x0000000000000500ull) +#define CVMX_NPEI_DMA0_INT_LEVEL (0x00000000000005C0ull) +#define CVMX_NPEI_DMA1_INT_LEVEL (0x00000000000005D0ull) +#define CVMX_NPEI_DMAX_COUNTS(offset) \ + (0x0000000000000450ull + ((offset) & 7) * 16) +#define CVMX_NPEI_DMAX_DBELL(offset) (0x00000000000003B0ull + ((offset) & 7) * 16) +#define CVMX_NPEI_DMAX_IBUFF_SADDR(offset) \ + (0x0000000000000400ull + ((offset) & 7) * 16) +#define CVMX_NPEI_DMAX_NADDR(offset) (0x00000000000004A0ull + ((offset) & 7) * 16) +#define CVMX_NPEI_DMA_CNTS (0x00000000000005E0ull) +#define CVMX_NPEI_DMA_CONTROL (0x00000000000003A0ull) +#define CVMX_NPEI_DMA_PCIE_REQ_NUM (0x00000000000005B0ull) +#define CVMX_NPEI_DMA_STATE1 (0x00000000000006C0ull) +#define CVMX_NPEI_DMA_STATE1_P1 (0x0000000000000680ull) +#define CVMX_NPEI_DMA_STATE2 (0x00000000000006D0ull) +#define CVMX_NPEI_DMA_STATE2_P1 (0x0000000000000690ull) +#define CVMX_NPEI_DMA_STATE3_P1 (0x00000000000006A0ull) +#define CVMX_NPEI_DMA_STATE4_P1 (0x00000000000006B0ull) +#define CVMX_NPEI_DMA_STATE5_P1 (0x00000000000006C0ull) +#define CVMX_NPEI_INT_A_ENB (0x0000000000000560ull) +#define CVMX_NPEI_INT_A_ENB2 (0x0000000000003CE0ull) +#define CVMX_NPEI_INT_A_SUM (0x0000000000000550ull) +#define CVMX_NPEI_INT_ENB (0x0000000000000540ull) +#define CVMX_NPEI_INT_ENB2 (0x0000000000003CD0ull) +#define CVMX_NPEI_INT_INFO (0x0000000000000590ull) +#define CVMX_NPEI_INT_SUM (0x0000000000000530ull) +#define CVMX_NPEI_INT_SUM2 (0x0000000000003CC0ull) +#define CVMX_NPEI_LAST_WIN_RDATA0 (0x0000000000000600ull) +#define CVMX_NPEI_LAST_WIN_RDATA1 (0x0000000000000610ull) +#define CVMX_NPEI_MEM_ACCESS_CTL (0x00000000000004F0ull) +#define CVMX_NPEI_MEM_ACCESS_SUBIDX(offset) \ + (0x0000000000000280ull + ((offset) & 31) * 16 - 16 * 12) +#define CVMX_NPEI_MSI_ENB0 (0x0000000000003C50ull) +#define CVMX_NPEI_MSI_ENB1 (0x0000000000003C60ull) +#define CVMX_NPEI_MSI_ENB2 (0x0000000000003C70ull) +#define CVMX_NPEI_MSI_ENB3 (0x0000000000003C80ull) +#define CVMX_NPEI_MSI_RCV0 (0x0000000000003C10ull) +#define CVMX_NPEI_MSI_RCV1 (0x0000000000003C20ull) +#define CVMX_NPEI_MSI_RCV2 (0x0000000000003C30ull) +#define CVMX_NPEI_MSI_RCV3 (0x0000000000003C40ull) +#define CVMX_NPEI_MSI_RD_MAP (0x0000000000003CA0ull) +#define CVMX_NPEI_MSI_W1C_ENB0 (0x0000000000003CF0ull) +#define CVMX_NPEI_MSI_W1C_ENB1 (0x0000000000003D00ull) +#define CVMX_NPEI_MSI_W1C_ENB2 (0x0000000000003D10ull) +#define CVMX_NPEI_MSI_W1C_ENB3 (0x0000000000003D20ull) +#define CVMX_NPEI_MSI_W1S_ENB0 (0x0000000000003D30ull) +#define CVMX_NPEI_MSI_W1S_ENB1 (0x0000000000003D40ull) +#define CVMX_NPEI_MSI_W1S_ENB2 (0x0000000000003D50ull) +#define CVMX_NPEI_MSI_W1S_ENB3 (0x0000000000003D60ull) +#define CVMX_NPEI_MSI_WR_MAP (0x0000000000003C90ull) +#define CVMX_NPEI_PCIE_CREDIT_CNT (0x0000000000003D70ull) +#define CVMX_NPEI_PCIE_MSI_RCV (0x0000000000003CB0ull) +#define CVMX_NPEI_PCIE_MSI_RCV_B1 (0x0000000000000650ull) +#define CVMX_NPEI_PCIE_MSI_RCV_B2 (0x0000000000000660ull) +#define CVMX_NPEI_PCIE_MSI_RCV_B3 (0x0000000000000670ull) +#define CVMX_NPEI_PKTX_CNTS(offset) (0x0000000000002400ull + ((offset) & 31) * 16) +#define CVMX_NPEI_PKTX_INSTR_BADDR(offset) \ + (0x0000000000002800ull + ((offset) & 31) * 16) +#define CVMX_NPEI_PKTX_INSTR_BAOFF_DBELL(offset) \ + (0x0000000000002C00ull + ((offset) & 31) * 16) +#define CVMX_NPEI_PKTX_INSTR_FIFO_RSIZE(offset) \ + (0x0000000000003000ull + ((offset) & 31) * 16) +#define CVMX_NPEI_PKTX_INSTR_HEADER(offset) \ + (0x0000000000003400ull + ((offset) & 31) * 16) +#define CVMX_NPEI_PKTX_IN_BP(offset) \ + (0x0000000000003800ull + ((offset) & 31) * 16) +#define CVMX_NPEI_PKTX_SLIST_BADDR(offset) \ + (0x0000000000001400ull + ((offset) & 31) * 16) +#define CVMX_NPEI_PKTX_SLIST_BAOFF_DBELL(offset) \ + (0x0000000000001800ull + ((offset) & 31) * 16) +#define CVMX_NPEI_PKTX_SLIST_FIFO_RSIZE(offset) \ + (0x0000000000001C00ull + ((offset) & 31) * 16) +#define CVMX_NPEI_PKT_CNT_INT (0x0000000000001110ull) +#define CVMX_NPEI_PKT_CNT_INT_ENB (0x0000000000001130ull) +#define CVMX_NPEI_PKT_DATA_OUT_ES (0x00000000000010B0ull) +#define CVMX_NPEI_PKT_DATA_OUT_NS (0x00000000000010A0ull) +#define CVMX_NPEI_PKT_DATA_OUT_ROR (0x0000000000001090ull) +#define CVMX_NPEI_PKT_DPADDR (0x0000000000001080ull) +#define CVMX_NPEI_PKT_INPUT_CONTROL (0x0000000000001150ull) +#define CVMX_NPEI_PKT_INSTR_ENB (0x0000000000001000ull) +#define CVMX_NPEI_PKT_INSTR_RD_SIZE (0x0000000000001190ull) +#define CVMX_NPEI_PKT_INSTR_SIZE (0x0000000000001020ull) +#define CVMX_NPEI_PKT_INT_LEVELS (0x0000000000001100ull) +#define CVMX_NPEI_PKT_IN_BP (0x00000000000006B0ull) +#define CVMX_NPEI_PKT_IN_DONEX_CNTS(offset) \ + (0x0000000000002000ull + ((offset) & 31) * 16) +#define CVMX_NPEI_PKT_IN_INSTR_COUNTS (0x00000000000006A0ull) +#define CVMX_NPEI_PKT_IN_PCIE_PORT (0x00000000000011A0ull) +#define CVMX_NPEI_PKT_IPTR (0x0000000000001070ull) +#define CVMX_NPEI_PKT_OUTPUT_WMARK (0x0000000000001160ull) +#define CVMX_NPEI_PKT_OUT_BMODE (0x00000000000010D0ull) +#define CVMX_NPEI_PKT_OUT_ENB (0x0000000000001010ull) +#define CVMX_NPEI_PKT_PCIE_PORT (0x00000000000010E0ull) +#define CVMX_NPEI_PKT_PORT_IN_RST (0x0000000000000690ull) +#define CVMX_NPEI_PKT_SLIST_ES (0x0000000000001050ull) +#define CVMX_NPEI_PKT_SLIST_ID_SIZE (0x0000000000001180ull) +#define CVMX_NPEI_PKT_SLIST_NS (0x0000000000001040ull) +#define CVMX_NPEI_PKT_SLIST_ROR (0x0000000000001030ull) +#define CVMX_NPEI_PKT_TIME_INT (0x0000000000001120ull) +#define CVMX_NPEI_PKT_TIME_INT_ENB (0x0000000000001140ull) +#define CVMX_NPEI_RSL_INT_BLOCKS (0x0000000000000520ull) +#define CVMX_NPEI_SCRATCH_1 (0x0000000000000270ull) +#define CVMX_NPEI_STATE1 (0x0000000000000620ull) +#define CVMX_NPEI_STATE2 (0x0000000000000630ull) +#define CVMX_NPEI_STATE3 (0x0000000000000640ull) +#define CVMX_NPEI_WINDOW_CTL (0x0000000000000380ull) +#define CVMX_NPEI_WIN_RD_ADDR (0x0000000000000210ull) +#define CVMX_NPEI_WIN_RD_DATA (0x0000000000000240ull) +#define CVMX_NPEI_WIN_WR_ADDR (0x0000000000000200ull) +#define CVMX_NPEI_WIN_WR_DATA (0x0000000000000220ull) +#define CVMX_NPEI_WIN_WR_MASK (0x0000000000000230ull) + +/** + * cvmx_npei_bar1_index# + * + * Total Address is 16Kb; 0x0000 - 0x3fff, 0x000 - 0x7fe(Reg, every other 8B) + * + * General 5kb; 0x0000 - 0x13ff, 0x000 - 0x27e(Reg-General) + * PktMem 10Kb; 0x1400 - 0x3bff, 0x280 - 0x77e(Reg-General-Packet) + * Rsvd 1Kb; 0x3c00 - 0x3fff, 0x780 - 0x7fe(Reg-NCB Only Mode) + * == NPEI_PKT_CNT_INT_ENB[PORT] + * == NPEI_PKT_TIME_INT_ENB[PORT] + * == NPEI_PKT_CNT_INT[PORT] + * == NPEI_PKT_TIME_INT[PORT] + * == NPEI_PKT_PCIE_PORT[PP] + * == NPEI_PKT_SLIST_ROR[ROR] + * == NPEI_PKT_SLIST_ROR[NSR] ? + * == NPEI_PKT_SLIST_ES[ES] + * == NPEI_PKTn_SLIST_BAOFF_DBELL[AOFF] + * == NPEI_PKTn_SLIST_BAOFF_DBELL[DBELL] + * == NPEI_PKTn_CNTS[CNT] + * NPEI_CTL_STATUS[OUTn_ENB] == NPEI_PKT_OUT_ENB[ENB] + * NPEI_BASE_ADDRESS_OUTPUTn[BADDR] == NPEI_PKTn_SLIST_BADDR[ADDR] + * NPEI_DESC_OUTPUTn[SIZE] == NPEI_PKTn_SLIST_FIFO_RSIZE[RSIZE] + * NPEI_Pn_DBPAIR_ADDR[NADDR] == NPEI_PKTn_SLIST_BADDR[ADDR] + + * NPEI_PKTn_SLIST_BAOFF_DBELL[AOFF] + * NPEI_PKT_CREDITSn[PTR_CNT] == NPEI_PKTn_SLIST_BAOFF_DBELL[DBELL] + * NPEI_P0_PAIR_CNTS[AVAIL] == NPEI_PKTn_SLIST_BAOFF_DBELL[DBELL] + * NPEI_P0_PAIR_CNTS[FCNT] == + * NPEI_PKTS_SENTn[PKT_CNT] == NPEI_PKTn_CNTS[CNT] + * NPEI_OUTPUT_CONTROL[Pn_BMODE] == NPEI_PKT_OUT_BMODE[BMODE] + * NPEI_PKT_CREDITSn[PKT_CNT] == NPEI_PKTn_CNTS[CNT] + * NPEI_BUFF_SIZE_OUTPUTn[BSIZE] == NPEI_PKT_SLIST_ID_SIZE[BSIZE] + * NPEI_BUFF_SIZE_OUTPUTn[ISIZE] == NPEI_PKT_SLIST_ID_SIZE[ISIZE] + * NPEI_OUTPUT_CONTROL[On_CSRM] == NPEI_PKT_DPADDR[DPTR] & + * NPEI_PKT_OUT_USE_IPTR[PORT] + * NPEI_OUTPUT_CONTROL[On_ES] == NPEI_PKT_DATA_OUT_ES[ES] + * NPEI_OUTPUT_CONTROL[On_NS] == NPEI_PKT_DATA_OUT_NS[NSR] ? + * NPEI_OUTPUT_CONTROL[On_RO] == NPEI_PKT_DATA_OUT_ROR[ROR] + * NPEI_PKTS_SENT_INT_LEVn[PKT_CNT] == NPEI_PKT_INT_LEVELS[CNT] + * NPEI_PKTS_SENT_TIMEn[PKT_TIME] == NPEI_PKT_INT_LEVELS[TIME] + * NPEI_OUTPUT_CONTROL[IPTR_On] == NPEI_PKT_IPTR[IPTR] + * NPEI_PCIE_PORT_OUTPUT[] == NPEI_PKT_PCIE_PORT[PP] + * + * NPEI_BAR1_INDEXX = NPEI BAR1 IndexX Register + * + * Contains address index and control bits for access to memory ranges of + * BAR-1. Index is build from supplied address [25:22]. + * NPEI_BAR1_INDEX0 through NPEI_BAR1_INDEX15 is used for transactions + * orginating with PCIE-PORT0 and NPEI_BAR1_INDEX16 + * through NPEI_BAR1_INDEX31 is used for transactions originating with + * PCIE-PORT1. + */ +union cvmx_npei_bar1_indexx { + u32 u32; + struct cvmx_npei_bar1_indexx_s { + u32 reserved_18_31 : 14; + u32 addr_idx : 14; + u32 ca : 1; + u32 end_swp : 2; + u32 addr_v : 1; + } s; + struct cvmx_npei_bar1_indexx_s cn52xx; + struct cvmx_npei_bar1_indexx_s cn52xxp1; + struct cvmx_npei_bar1_indexx_s cn56xx; + struct cvmx_npei_bar1_indexx_s cn56xxp1; +}; + +typedef union cvmx_npei_bar1_indexx cvmx_npei_bar1_indexx_t; + +/** + * cvmx_npei_bist_status + * + * NPEI_BIST_STATUS = NPI's BIST Status Register + * + * Results from BIST runs of NPEI's memories. + */ +union cvmx_npei_bist_status { + u64 u64; + struct cvmx_npei_bist_status_s { + u64 pkt_rdf : 1; + u64 reserved_60_62 : 3; + u64 pcr_gim : 1; + u64 pkt_pif : 1; + u64 pcsr_int : 1; + u64 pcsr_im : 1; + u64 pcsr_cnt : 1; + u64 pcsr_id : 1; + u64 pcsr_sl : 1; + u64 reserved_50_52 : 3; + u64 pkt_ind : 1; + u64 pkt_slm : 1; + u64 reserved_36_47 : 12; + u64 d0_pst : 1; + u64 d1_pst : 1; + u64 d2_pst : 1; + u64 d3_pst : 1; + u64 reserved_31_31 : 1; + u64 n2p0_c : 1; + u64 n2p0_o : 1; + u64 n2p1_c : 1; + u64 n2p1_o : 1; + u64 cpl_p0 : 1; + u64 cpl_p1 : 1; + u64 p2n1_po : 1; + u64 p2n1_no : 1; + u64 p2n1_co : 1; + u64 p2n0_po : 1; + u64 p2n0_no : 1; + u64 p2n0_co : 1; + u64 p2n0_c0 : 1; + u64 p2n0_c1 : 1; + u64 p2n0_n : 1; + u64 p2n0_p0 : 1; + u64 p2n0_p1 : 1; + u64 p2n1_c0 : 1; + u64 p2n1_c1 : 1; + u64 p2n1_n : 1; + u64 p2n1_p0 : 1; + u64 p2n1_p1 : 1; + u64 csm0 : 1; + u64 csm1 : 1; + u64 dif0 : 1; + u64 dif1 : 1; + u64 dif2 : 1; + u64 dif3 : 1; + u64 reserved_2_2 : 1; + u64 msi : 1; + u64 ncb_cmd : 1; + } s; + struct cvmx_npei_bist_status_cn52xx { + u64 pkt_rdf : 1; + u64 reserved_60_62 : 3; + u64 pcr_gim : 1; + u64 pkt_pif : 1; + u64 pcsr_int : 1; + u64 pcsr_im : 1; + u64 pcsr_cnt : 1; + u64 pcsr_id : 1; + u64 pcsr_sl : 1; + u64 pkt_imem : 1; + u64 pkt_pfm : 1; + u64 pkt_pof : 1; + u64 reserved_48_49 : 2; + u64 pkt_pop0 : 1; + u64 pkt_pop1 : 1; + u64 d0_mem : 1; + u64 d1_mem : 1; + u64 d2_mem : 1; + u64 d3_mem : 1; + u64 d4_mem : 1; + u64 ds_mem : 1; + u64 reserved_36_39 : 4; + u64 d0_pst : 1; + u64 d1_pst : 1; + u64 d2_pst : 1; + u64 d3_pst : 1; + u64 d4_pst : 1; + u64 n2p0_c : 1; + u64 n2p0_o : 1; + u64 n2p1_c : 1; + u64 n2p1_o : 1; + u64 cpl_p0 : 1; + u64 cpl_p1 : 1; + u64 p2n1_po : 1; + u64 p2n1_no : 1; + u64 p2n1_co : 1; + u64 p2n0_po : 1; + u64 p2n0_no : 1; + u64 p2n0_co : 1; + u64 p2n0_c0 : 1; + u64 p2n0_c1 : 1; + u64 p2n0_n : 1; + u64 p2n0_p0 : 1; + u64 p2n0_p1 : 1; + u64 p2n1_c0 : 1; + u64 p2n1_c1 : 1; + u64 p2n1_n : 1; + u64 p2n1_p0 : 1; + u64 p2n1_p1 : 1; + u64 csm0 : 1; + u64 csm1 : 1; + u64 dif0 : 1; + u64 dif1 : 1; + u64 dif2 : 1; + u64 dif3 : 1; + u64 dif4 : 1; + u64 msi : 1; + u64 ncb_cmd : 1; + } cn52xx; + struct cvmx_npei_bist_status_cn52xxp1 { + u64 reserved_46_63 : 18; + u64 d0_mem0 : 1; + u64 d1_mem1 : 1; + u64 d2_mem2 : 1; + u64 d3_mem3 : 1; + u64 dr0_mem : 1; + u64 d0_mem : 1; + u64 d1_mem : 1; + u64 d2_mem : 1; + u64 d3_mem : 1; + u64 dr1_mem : 1; + u64 d0_pst : 1; + u64 d1_pst : 1; + u64 d2_pst : 1; + u64 d3_pst : 1; + u64 dr2_mem : 1; + u64 n2p0_c : 1; + u64 n2p0_o : 1; + u64 n2p1_c : 1; + u64 n2p1_o : 1; + u64 cpl_p0 : 1; + u64 cpl_p1 : 1; + u64 p2n1_po : 1; + u64 p2n1_no : 1; + u64 p2n1_co : 1; + u64 p2n0_po : 1; + u64 p2n0_no : 1; + u64 p2n0_co : 1; + u64 p2n0_c0 : 1; + u64 p2n0_c1 : 1; + u64 p2n0_n : 1; + u64 p2n0_p0 : 1; + u64 p2n0_p1 : 1; + u64 p2n1_c0 : 1; + u64 p2n1_c1 : 1; + u64 p2n1_n : 1; + u64 p2n1_p0 : 1; + u64 p2n1_p1 : 1; + u64 csm0 : 1; + u64 csm1 : 1; + u64 dif0 : 1; + u64 dif1 : 1; + u64 dif2 : 1; + u64 dif3 : 1; + u64 dr3_mem : 1; + u64 msi : 1; + u64 ncb_cmd : 1; + } cn52xxp1; + struct cvmx_npei_bist_status_cn52xx cn56xx; + struct cvmx_npei_bist_status_cn56xxp1 { + u64 reserved_58_63 : 6; + u64 pcsr_int : 1; + u64 pcsr_im : 1; + u64 pcsr_cnt : 1; + u64 pcsr_id : 1; + u64 pcsr_sl : 1; + u64 pkt_pout : 1; + u64 pkt_imem : 1; + u64 pkt_cntm : 1; + u64 pkt_ind : 1; + u64 pkt_slm : 1; + u64 pkt_odf : 1; + u64 pkt_oif : 1; + u64 pkt_out : 1; + u64 pkt_i0 : 1; + u64 pkt_i1 : 1; + u64 pkt_s0 : 1; + u64 pkt_s1 : 1; + u64 d0_mem : 1; + u64 d1_mem : 1; + u64 d2_mem : 1; + u64 d3_mem : 1; + u64 d4_mem : 1; + u64 d0_pst : 1; + u64 d1_pst : 1; + u64 d2_pst : 1; + u64 d3_pst : 1; + u64 d4_pst : 1; + u64 n2p0_c : 1; + u64 n2p0_o : 1; + u64 n2p1_c : 1; + u64 n2p1_o : 1; + u64 cpl_p0 : 1; + u64 cpl_p1 : 1; + u64 p2n1_po : 1; + u64 p2n1_no : 1; + u64 p2n1_co : 1; + u64 p2n0_po : 1; + u64 p2n0_no : 1; + u64 p2n0_co : 1; + u64 p2n0_c0 : 1; + u64 p2n0_c1 : 1; + u64 p2n0_n : 1; + u64 p2n0_p0 : 1; + u64 p2n0_p1 : 1; + u64 p2n1_c0 : 1; + u64 p2n1_c1 : 1; + u64 p2n1_n : 1; + u64 p2n1_p0 : 1; + u64 p2n1_p1 : 1; + u64 csm0 : 1; + u64 csm1 : 1; + u64 dif0 : 1; + u64 dif1 : 1; + u64 dif2 : 1; + u64 dif3 : 1; + u64 dif4 : 1; + u64 msi : 1; + u64 ncb_cmd : 1; + } cn56xxp1; +}; + +typedef union cvmx_npei_bist_status cvmx_npei_bist_status_t; + +/** + * cvmx_npei_bist_status2 + * + * NPEI_BIST_STATUS2 = NPI's BIST Status Register2 + * + * Results from BIST runs of NPEI's memories. + */ +union cvmx_npei_bist_status2 { + u64 u64; + struct cvmx_npei_bist_status2_s { + u64 reserved_14_63 : 50; + u64 prd_tag : 1; + u64 prd_st0 : 1; + u64 prd_st1 : 1; + u64 prd_err : 1; + u64 nrd_st : 1; + u64 nwe_st : 1; + u64 nwe_wr0 : 1; + u64 nwe_wr1 : 1; + u64 pkt_rd : 1; + u64 psc_p0 : 1; + u64 psc_p1 : 1; + u64 pkt_gd : 1; + u64 pkt_gl : 1; + u64 pkt_blk : 1; + } s; + struct cvmx_npei_bist_status2_s cn52xx; + struct cvmx_npei_bist_status2_s cn56xx; +}; + +typedef union cvmx_npei_bist_status2 cvmx_npei_bist_status2_t; + +/** + * cvmx_npei_ctl_port0 + * + * NPEI_CTL_PORT0 = NPEI's Control Port 0 + * + * Contains control for access for Port0 + */ +union cvmx_npei_ctl_port0 { + u64 u64; + struct cvmx_npei_ctl_port0_s { + u64 reserved_21_63 : 43; + u64 waitl_com : 1; + u64 intd : 1; + u64 intc : 1; + u64 intb : 1; + u64 inta : 1; + u64 intd_map : 2; + u64 intc_map : 2; + u64 intb_map : 2; + u64 inta_map : 2; + u64 ctlp_ro : 1; + u64 reserved_6_6 : 1; + u64 ptlp_ro : 1; + u64 bar2_enb : 1; + u64 bar2_esx : 2; + u64 bar2_cax : 1; + u64 wait_com : 1; + } s; + struct cvmx_npei_ctl_port0_s cn52xx; + struct cvmx_npei_ctl_port0_s cn52xxp1; + struct cvmx_npei_ctl_port0_s cn56xx; + struct cvmx_npei_ctl_port0_s cn56xxp1; +}; + +typedef union cvmx_npei_ctl_port0 cvmx_npei_ctl_port0_t; + +/** + * cvmx_npei_ctl_port1 + * + * NPEI_CTL_PORT1 = NPEI's Control Port1 + * + * Contains control for access for Port1 + */ +union cvmx_npei_ctl_port1 { + u64 u64; + struct cvmx_npei_ctl_port1_s { + u64 reserved_21_63 : 43; + u64 waitl_com : 1; + u64 intd : 1; + u64 intc : 1; + u64 intb : 1; + u64 inta : 1; + u64 intd_map : 2; + u64 intc_map : 2; + u64 intb_map : 2; + u64 inta_map : 2; + u64 ctlp_ro : 1; + u64 reserved_6_6 : 1; + u64 ptlp_ro : 1; + u64 bar2_enb : 1; + u64 bar2_esx : 2; + u64 bar2_cax : 1; + u64 wait_com : 1; + } s; + struct cvmx_npei_ctl_port1_s cn52xx; + struct cvmx_npei_ctl_port1_s cn52xxp1; + struct cvmx_npei_ctl_port1_s cn56xx; + struct cvmx_npei_ctl_port1_s cn56xxp1; +}; + +typedef union cvmx_npei_ctl_port1 cvmx_npei_ctl_port1_t; + +/** + * cvmx_npei_ctl_status + * + * NPEI_CTL_STATUS = NPEI Control Status Register + * + * Contains control and status for NPEI. Writes to this register are not + * oSrdered with writes/reads to the PCIe Memory space. + * To ensure that a write has completed the user must read the register + * before making an access(i.e. PCIe memory space) + * that requires the value of this register to be updated. + */ +union cvmx_npei_ctl_status { + u64 u64; + struct cvmx_npei_ctl_status_s { + u64 reserved_44_63 : 20; + u64 p1_ntags : 6; + u64 p0_ntags : 6; + u64 cfg_rtry : 16; + u64 ring_en : 1; + u64 lnk_rst : 1; + u64 arb : 1; + u64 pkt_bp : 4; + u64 host_mode : 1; + u64 chip_rev : 8; + } s; + struct cvmx_npei_ctl_status_s cn52xx; + struct cvmx_npei_ctl_status_cn52xxp1 { + u64 reserved_44_63 : 20; + u64 p1_ntags : 6; + u64 p0_ntags : 6; + u64 cfg_rtry : 16; + u64 reserved_15_15 : 1; + u64 lnk_rst : 1; + u64 arb : 1; + u64 reserved_9_12 : 4; + u64 host_mode : 1; + u64 chip_rev : 8; + } cn52xxp1; + struct cvmx_npei_ctl_status_s cn56xx; + struct cvmx_npei_ctl_status_cn56xxp1 { + u64 reserved_15_63 : 49; + u64 lnk_rst : 1; + u64 arb : 1; + u64 pkt_bp : 4; + u64 host_mode : 1; + u64 chip_rev : 8; + } cn56xxp1; +}; + +typedef union cvmx_npei_ctl_status cvmx_npei_ctl_status_t; + +/** + * cvmx_npei_ctl_status2 + * + * NPEI_CTL_STATUS2 = NPEI's Control Status2 Register + * + * Contains control and status for NPEI. + * Writes to this register are not ordered with writes/reads to the PCI + * Memory space. + * To ensure that a write has completed the user must read the register before + * making an access(i.e. PCI memory space) that requires the value of this + * register to be updated. + */ +union cvmx_npei_ctl_status2 { + u64 u64; + struct cvmx_npei_ctl_status2_s { + u64 reserved_16_63 : 48; + u64 mps : 1; + u64 mrrs : 3; + u64 c1_w_flt : 1; + u64 c0_w_flt : 1; + u64 c1_b1_s : 3; + u64 c0_b1_s : 3; + u64 c1_wi_d : 1; + u64 c1_b0_d : 1; + u64 c0_wi_d : 1; + u64 c0_b0_d : 1; + } s; + struct cvmx_npei_ctl_status2_s cn52xx; + struct cvmx_npei_ctl_status2_s cn52xxp1; + struct cvmx_npei_ctl_status2_s cn56xx; + struct cvmx_npei_ctl_status2_s cn56xxp1; +}; + +typedef union cvmx_npei_ctl_status2 cvmx_npei_ctl_status2_t; + +/** + * cvmx_npei_data_out_cnt + * + * NPEI_DATA_OUT_CNT = NPEI DATA OUT COUNT + * + * The EXEC data out fifo-count and the data unload counter. + */ +union cvmx_npei_data_out_cnt { + u64 u64; + struct cvmx_npei_data_out_cnt_s { + u64 reserved_44_63 : 20; + u64 p1_ucnt : 16; + u64 p1_fcnt : 6; + u64 p0_ucnt : 16; + u64 p0_fcnt : 6; + } s; + struct cvmx_npei_data_out_cnt_s cn52xx; + struct cvmx_npei_data_out_cnt_s cn52xxp1; + struct cvmx_npei_data_out_cnt_s cn56xx; + struct cvmx_npei_data_out_cnt_s cn56xxp1; +}; + +typedef union cvmx_npei_data_out_cnt cvmx_npei_data_out_cnt_t; + +/** + * cvmx_npei_dbg_data + * + * NPEI_DBG_DATA = NPEI Debug Data Register + * + * Value returned on the debug-data lines from the RSLs + */ +union cvmx_npei_dbg_data { + u64 u64; + struct cvmx_npei_dbg_data_s { + u64 reserved_28_63 : 36; + u64 qlm0_rev_lanes : 1; + u64 reserved_25_26 : 2; + u64 qlm1_spd : 2; + u64 c_mul : 5; + u64 dsel_ext : 1; + u64 data : 17; + } s; + struct cvmx_npei_dbg_data_cn52xx { + u64 reserved_29_63 : 35; + u64 qlm0_link_width : 1; + u64 qlm0_rev_lanes : 1; + u64 qlm1_mode : 2; + u64 qlm1_spd : 2; + u64 c_mul : 5; + u64 dsel_ext : 1; + u64 data : 17; + } cn52xx; + struct cvmx_npei_dbg_data_cn52xx cn52xxp1; + struct cvmx_npei_dbg_data_cn56xx { + u64 reserved_29_63 : 35; + u64 qlm2_rev_lanes : 1; + u64 qlm0_rev_lanes : 1; + u64 qlm3_spd : 2; + u64 qlm1_spd : 2; + u64 c_mul : 5; + u64 dsel_ext : 1; + u64 data : 17; + } cn56xx; + struct cvmx_npei_dbg_data_cn56xx cn56xxp1; +}; + +typedef union cvmx_npei_dbg_data cvmx_npei_dbg_data_t; + +/** + * cvmx_npei_dbg_select + * + * NPEI_DBG_SELECT = Debug Select Register + * + * Contains the debug select value last written to the RSLs. + */ +union cvmx_npei_dbg_select { + u64 u64; + struct cvmx_npei_dbg_select_s { + u64 reserved_16_63 : 48; + u64 dbg_sel : 16; + } s; + struct cvmx_npei_dbg_select_s cn52xx; + struct cvmx_npei_dbg_select_s cn52xxp1; + struct cvmx_npei_dbg_select_s cn56xx; + struct cvmx_npei_dbg_select_s cn56xxp1; +}; + +typedef union cvmx_npei_dbg_select cvmx_npei_dbg_select_t; + +/** + * cvmx_npei_dma#_counts + * + * NPEI_DMA[0..4]_COUNTS = DMA Instruction Counts + * + * Values for determing the number of instructions for DMA[0..4] in the NPEI. + */ +union cvmx_npei_dmax_counts { + u64 u64; + struct cvmx_npei_dmax_counts_s { + u64 reserved_39_63 : 25; + u64 fcnt : 7; + u64 dbell : 32; + } s; + struct cvmx_npei_dmax_counts_s cn52xx; + struct cvmx_npei_dmax_counts_s cn52xxp1; + struct cvmx_npei_dmax_counts_s cn56xx; + struct cvmx_npei_dmax_counts_s cn56xxp1; +}; + +typedef union cvmx_npei_dmax_counts cvmx_npei_dmax_counts_t; + +/** + * cvmx_npei_dma#_dbell + * + * NPEI_DMA_DBELL[0..4] = DMA Door Bell + * + * The door bell register for DMA[0..4] queue. + */ +union cvmx_npei_dmax_dbell { + u32 u32; + struct cvmx_npei_dmax_dbell_s { + u32 reserved_16_31 : 16; + u32 dbell : 16; + } s; + struct cvmx_npei_dmax_dbell_s cn52xx; + struct cvmx_npei_dmax_dbell_s cn52xxp1; + struct cvmx_npei_dmax_dbell_s cn56xx; + struct cvmx_npei_dmax_dbell_s cn56xxp1; +}; + +typedef union cvmx_npei_dmax_dbell cvmx_npei_dmax_dbell_t; + +/** + * cvmx_npei_dma#_ibuff_saddr + * + * NPEI_DMA[0..4]_IBUFF_SADDR = DMA Instruction Buffer Starting Address + * + * The address to start reading Instructions from for DMA[0..4]. + */ +union cvmx_npei_dmax_ibuff_saddr { + u64 u64; + struct cvmx_npei_dmax_ibuff_saddr_s { + u64 reserved_37_63 : 27; + u64 idle : 1; + u64 saddr : 29; + u64 reserved_0_6 : 7; + } s; + struct cvmx_npei_dmax_ibuff_saddr_s cn52xx; + struct cvmx_npei_dmax_ibuff_saddr_cn52xxp1 { + u64 reserved_36_63 : 28; + u64 saddr : 29; + u64 reserved_0_6 : 7; + } cn52xxp1; + struct cvmx_npei_dmax_ibuff_saddr_s cn56xx; + struct cvmx_npei_dmax_ibuff_saddr_cn52xxp1 cn56xxp1; +}; + +typedef union cvmx_npei_dmax_ibuff_saddr cvmx_npei_dmax_ibuff_saddr_t; + +/** + * cvmx_npei_dma#_naddr + * + * NPEI_DMA[0..4]_NADDR = DMA Next Ichunk Address + * + * Place NPEI will read the next Ichunk data from. This is valid when state is 0 + */ +union cvmx_npei_dmax_naddr { + u64 u64; + struct cvmx_npei_dmax_naddr_s { + u64 reserved_36_63 : 28; + u64 addr : 36; + } s; + struct cvmx_npei_dmax_naddr_s cn52xx; + struct cvmx_npei_dmax_naddr_s cn52xxp1; + struct cvmx_npei_dmax_naddr_s cn56xx; + struct cvmx_npei_dmax_naddr_s cn56xxp1; +}; + +typedef union cvmx_npei_dmax_naddr cvmx_npei_dmax_naddr_t; + +/** + * cvmx_npei_dma0_int_level + * + * NPEI_DMA0_INT_LEVEL = NPEI DMA0 Interrupt Level + * + * Thresholds for DMA count and timer interrupts for DMA0. + */ +union cvmx_npei_dma0_int_level { + u64 u64; + struct cvmx_npei_dma0_int_level_s { + u64 time : 32; + u64 cnt : 32; + } s; + struct cvmx_npei_dma0_int_level_s cn52xx; + struct cvmx_npei_dma0_int_level_s cn52xxp1; + struct cvmx_npei_dma0_int_level_s cn56xx; + struct cvmx_npei_dma0_int_level_s cn56xxp1; +}; + +typedef union cvmx_npei_dma0_int_level cvmx_npei_dma0_int_level_t; + +/** + * cvmx_npei_dma1_int_level + * + * NPEI_DMA1_INT_LEVEL = NPEI DMA1 Interrupt Level + * + * Thresholds for DMA count and timer interrupts for DMA1. + */ +union cvmx_npei_dma1_int_level { + u64 u64; + struct cvmx_npei_dma1_int_level_s { + u64 time : 32; + u64 cnt : 32; + } s; + struct cvmx_npei_dma1_int_level_s cn52xx; + struct cvmx_npei_dma1_int_level_s cn52xxp1; + struct cvmx_npei_dma1_int_level_s cn56xx; + struct cvmx_npei_dma1_int_level_s cn56xxp1; +}; + +typedef union cvmx_npei_dma1_int_level cvmx_npei_dma1_int_level_t; + +/** + * cvmx_npei_dma_cnts + * + * NPEI_DMA_CNTS = NPEI DMA Count + * + * The DMA Count values for DMA0 and DMA1. + */ +union cvmx_npei_dma_cnts { + u64 u64; + struct cvmx_npei_dma_cnts_s { + u64 dma1 : 32; + u64 dma0 : 32; + } s; + struct cvmx_npei_dma_cnts_s cn52xx; + struct cvmx_npei_dma_cnts_s cn52xxp1; + struct cvmx_npei_dma_cnts_s cn56xx; + struct cvmx_npei_dma_cnts_s cn56xxp1; +}; + +typedef union cvmx_npei_dma_cnts cvmx_npei_dma_cnts_t; + +/** + * cvmx_npei_dma_control + * + * NPEI_DMA_CONTROL = DMA Control Register + * + * Controls operation of the DMA IN/OUT. + */ +union cvmx_npei_dma_control { + u64 u64; + struct cvmx_npei_dma_control_s { + u64 reserved_40_63 : 24; + u64 p_32b_m : 1; + u64 dma4_enb : 1; + u64 dma3_enb : 1; + u64 dma2_enb : 1; + u64 dma1_enb : 1; + u64 dma0_enb : 1; + u64 b0_lend : 1; + u64 dwb_denb : 1; + u64 dwb_ichk : 9; + u64 fpa_que : 3; + u64 o_add1 : 1; + u64 o_ro : 1; + u64 o_ns : 1; + u64 o_es : 2; + u64 o_mode : 1; + u64 csize : 14; + } s; + struct cvmx_npei_dma_control_s cn52xx; + struct cvmx_npei_dma_control_cn52xxp1 { + u64 reserved_38_63 : 26; + u64 dma3_enb : 1; + u64 dma2_enb : 1; + u64 dma1_enb : 1; + u64 dma0_enb : 1; + u64 b0_lend : 1; + u64 dwb_denb : 1; + u64 dwb_ichk : 9; + u64 fpa_que : 3; + u64 o_add1 : 1; + u64 o_ro : 1; + u64 o_ns : 1; + u64 o_es : 2; + u64 o_mode : 1; + u64 csize : 14; + } cn52xxp1; + struct cvmx_npei_dma_control_s cn56xx; + struct cvmx_npei_dma_control_cn56xxp1 { + u64 reserved_39_63 : 25; + u64 dma4_enb : 1; + u64 dma3_enb : 1; + u64 dma2_enb : 1; + u64 dma1_enb : 1; + u64 dma0_enb : 1; + u64 b0_lend : 1; + u64 dwb_denb : 1; + u64 dwb_ichk : 9; + u64 fpa_que : 3; + u64 o_add1 : 1; + u64 o_ro : 1; + u64 o_ns : 1; + u64 o_es : 2; + u64 o_mode : 1; + u64 csize : 14; + } cn56xxp1; +}; + +typedef union cvmx_npei_dma_control cvmx_npei_dma_control_t; + +/** + * cvmx_npei_dma_pcie_req_num + * + * NPEI_DMA_PCIE_REQ_NUM = NPEI DMA PCIE Outstanding Read Request Number + * + * Outstanding PCIE read request number for DMAs and Packet, maximum number + * is 16 + */ +union cvmx_npei_dma_pcie_req_num { + u64 u64; + struct cvmx_npei_dma_pcie_req_num_s { + u64 dma_arb : 1; + u64 reserved_53_62 : 10; + u64 pkt_cnt : 5; + u64 reserved_45_47 : 3; + u64 dma4_cnt : 5; + u64 reserved_37_39 : 3; + u64 dma3_cnt : 5; + u64 reserved_29_31 : 3; + u64 dma2_cnt : 5; + u64 reserved_21_23 : 3; + u64 dma1_cnt : 5; + u64 reserved_13_15 : 3; + u64 dma0_cnt : 5; + u64 reserved_5_7 : 3; + u64 dma_cnt : 5; + } s; + struct cvmx_npei_dma_pcie_req_num_s cn52xx; + struct cvmx_npei_dma_pcie_req_num_s cn56xx; +}; + +typedef union cvmx_npei_dma_pcie_req_num cvmx_npei_dma_pcie_req_num_t; + +/** + * cvmx_npei_dma_state1 + * + * NPEI_DMA_STATE1 = NPI's DMA State 1 + * + * Results from DMA state register 1 + */ +union cvmx_npei_dma_state1 { + u64 u64; + struct cvmx_npei_dma_state1_s { + u64 reserved_40_63 : 24; + u64 d4_dwe : 8; + u64 d3_dwe : 8; + u64 d2_dwe : 8; + u64 d1_dwe : 8; + u64 d0_dwe : 8; + } s; + struct cvmx_npei_dma_state1_s cn52xx; +}; + +typedef union cvmx_npei_dma_state1 cvmx_npei_dma_state1_t; + +/** + * cvmx_npei_dma_state1_p1 + * + * NPEI_DMA_STATE1_P1 = NPEI DMA Request and Instruction State + * + * DMA engine Debug information. + */ +union cvmx_npei_dma_state1_p1 { + u64 u64; + struct cvmx_npei_dma_state1_p1_s { + u64 reserved_60_63 : 4; + u64 d0_difst : 7; + u64 d1_difst : 7; + u64 d2_difst : 7; + u64 d3_difst : 7; + u64 d4_difst : 7; + u64 d0_reqst : 5; + u64 d1_reqst : 5; + u64 d2_reqst : 5; + u64 d3_reqst : 5; + u64 d4_reqst : 5; + } s; + struct cvmx_npei_dma_state1_p1_cn52xxp1 { + u64 reserved_60_63 : 4; + u64 d0_difst : 7; + u64 d1_difst : 7; + u64 d2_difst : 7; + u64 d3_difst : 7; + u64 reserved_25_31 : 7; + u64 d0_reqst : 5; + u64 d1_reqst : 5; + u64 d2_reqst : 5; + u64 d3_reqst : 5; + u64 reserved_0_4 : 5; + } cn52xxp1; + struct cvmx_npei_dma_state1_p1_s cn56xxp1; +}; + +typedef union cvmx_npei_dma_state1_p1 cvmx_npei_dma_state1_p1_t; + +/** + * cvmx_npei_dma_state2 + * + * NPEI_DMA_STATE2 = NPI's DMA State 2 + * + * Results from DMA state register 2 + */ +union cvmx_npei_dma_state2 { + u64 u64; + struct cvmx_npei_dma_state2_s { + u64 reserved_28_63 : 36; + u64 ndwe : 4; + u64 reserved_21_23 : 3; + u64 ndre : 5; + u64 reserved_10_15 : 6; + u64 prd : 10; + } s; + struct cvmx_npei_dma_state2_s cn52xx; +}; + +typedef union cvmx_npei_dma_state2 cvmx_npei_dma_state2_t; + +/** + * cvmx_npei_dma_state2_p1 + * + * NPEI_DMA_STATE2_P1 = NPEI DMA Instruction Fetch State + * + * DMA engine Debug information. + */ +union cvmx_npei_dma_state2_p1 { + u64 u64; + struct cvmx_npei_dma_state2_p1_s { + u64 reserved_45_63 : 19; + u64 d0_dffst : 9; + u64 d1_dffst : 9; + u64 d2_dffst : 9; + u64 d3_dffst : 9; + u64 d4_dffst : 9; + } s; + struct cvmx_npei_dma_state2_p1_cn52xxp1 { + u64 reserved_45_63 : 19; + u64 d0_dffst : 9; + u64 d1_dffst : 9; + u64 d2_dffst : 9; + u64 d3_dffst : 9; + u64 reserved_0_8 : 9; + } cn52xxp1; + struct cvmx_npei_dma_state2_p1_s cn56xxp1; +}; + +typedef union cvmx_npei_dma_state2_p1 cvmx_npei_dma_state2_p1_t; + +/** + * cvmx_npei_dma_state3_p1 + * + * NPEI_DMA_STATE3_P1 = NPEI DMA DRE State + * + * DMA engine Debug information. + */ +union cvmx_npei_dma_state3_p1 { + u64 u64; + struct cvmx_npei_dma_state3_p1_s { + u64 reserved_60_63 : 4; + u64 d0_drest : 15; + u64 d1_drest : 15; + u64 d2_drest : 15; + u64 d3_drest : 15; + } s; + struct cvmx_npei_dma_state3_p1_s cn52xxp1; + struct cvmx_npei_dma_state3_p1_s cn56xxp1; +}; + +typedef union cvmx_npei_dma_state3_p1 cvmx_npei_dma_state3_p1_t; + +/** + * cvmx_npei_dma_state4_p1 + * + * NPEI_DMA_STATE4_P1 = NPEI DMA DWE State + * + * DMA engine Debug information. + */ +union cvmx_npei_dma_state4_p1 { + u64 u64; + struct cvmx_npei_dma_state4_p1_s { + u64 reserved_52_63 : 12; + u64 d0_dwest : 13; + u64 d1_dwest : 13; + u64 d2_dwest : 13; + u64 d3_dwest : 13; + } s; + struct cvmx_npei_dma_state4_p1_s cn52xxp1; + struct cvmx_npei_dma_state4_p1_s cn56xxp1; +}; + +typedef union cvmx_npei_dma_state4_p1 cvmx_npei_dma_state4_p1_t; + +/** + * cvmx_npei_dma_state5_p1 + * + * NPEI_DMA_STATE5_P1 = NPEI DMA DWE and DRE State + * + * DMA engine Debug information. + */ +union cvmx_npei_dma_state5_p1 { + u64 u64; + struct cvmx_npei_dma_state5_p1_s { + u64 reserved_28_63 : 36; + u64 d4_drest : 15; + u64 d4_dwest : 13; + } s; + struct cvmx_npei_dma_state5_p1_s cn56xxp1; +}; + +typedef union cvmx_npei_dma_state5_p1 cvmx_npei_dma_state5_p1_t; + +/** + * cvmx_npei_int_a_enb + * + * NPEI_INTERRUPT_A_ENB = NPI's Interrupt A Enable Register + * + * Used to allow the generation of interrupts (MSI/INTA) to the PCIe + * CoresUsed to enable the various interrupting conditions of NPEI + */ +union cvmx_npei_int_a_enb { + u64 u64; + struct cvmx_npei_int_a_enb_s { + u64 reserved_10_63 : 54; + u64 pout_err : 1; + u64 pin_bp : 1; + u64 p1_rdlk : 1; + u64 p0_rdlk : 1; + u64 pgl_err : 1; + u64 pdi_err : 1; + u64 pop_err : 1; + u64 pins_err : 1; + u64 dma1_cpl : 1; + u64 dma0_cpl : 1; + } s; + struct cvmx_npei_int_a_enb_s cn52xx; + struct cvmx_npei_int_a_enb_cn52xxp1 { + u64 reserved_2_63 : 62; + u64 dma1_cpl : 1; + u64 dma0_cpl : 1; + } cn52xxp1; + struct cvmx_npei_int_a_enb_s cn56xx; +}; + +typedef union cvmx_npei_int_a_enb cvmx_npei_int_a_enb_t; + +/** + * cvmx_npei_int_a_enb2 + * + * NPEI_INTERRUPT_A_ENB2 = NPEI's Interrupt A Enable2 Register + * + * Used to enable the various interrupting conditions of NPEI + */ +union cvmx_npei_int_a_enb2 { + u64 u64; + struct cvmx_npei_int_a_enb2_s { + u64 reserved_10_63 : 54; + u64 pout_err : 1; + u64 pin_bp : 1; + u64 p1_rdlk : 1; + u64 p0_rdlk : 1; + u64 pgl_err : 1; + u64 pdi_err : 1; + u64 pop_err : 1; + u64 pins_err : 1; + u64 dma1_cpl : 1; + u64 dma0_cpl : 1; + } s; + struct cvmx_npei_int_a_enb2_s cn52xx; + struct cvmx_npei_int_a_enb2_cn52xxp1 { + u64 reserved_2_63 : 62; + u64 dma1_cpl : 1; + u64 dma0_cpl : 1; + } cn52xxp1; + struct cvmx_npei_int_a_enb2_s cn56xx; +}; + +typedef union cvmx_npei_int_a_enb2 cvmx_npei_int_a_enb2_t; + +/** + * cvmx_npei_int_a_sum + * + * NPEI_INTERRUPT_A_SUM = NPI Interrupt A Summary Register + * + * Set when an interrupt condition occurs, write '1' to clear. When an + * interrupt bitin this register is set and + * the cooresponding bit in the NPEI_INT_A_ENB register is set, then + * NPEI_INT_SUM[61] will be set. + */ +union cvmx_npei_int_a_sum { + u64 u64; + struct cvmx_npei_int_a_sum_s { + u64 reserved_10_63 : 54; + u64 pout_err : 1; + u64 pin_bp : 1; + u64 p1_rdlk : 1; + u64 p0_rdlk : 1; + u64 pgl_err : 1; + u64 pdi_err : 1; + u64 pop_err : 1; + u64 pins_err : 1; + u64 dma1_cpl : 1; + u64 dma0_cpl : 1; + } s; + struct cvmx_npei_int_a_sum_s cn52xx; + struct cvmx_npei_int_a_sum_cn52xxp1 { + u64 reserved_2_63 : 62; + u64 dma1_cpl : 1; + u64 dma0_cpl : 1; + } cn52xxp1; + struct cvmx_npei_int_a_sum_s cn56xx; +}; + +typedef union cvmx_npei_int_a_sum cvmx_npei_int_a_sum_t; + +/** + * cvmx_npei_int_enb + * + * NPEI_INTERRUPT_ENB = NPI's Interrupt Enable Register + * + * Used to allow the generation of interrupts (MSI/INTA) to the PCIe + * CoresUsed to enable the various interrupting conditions of NPI + */ +union cvmx_npei_int_enb { + u64 u64; + struct cvmx_npei_int_enb_s { + u64 mio_inta : 1; + u64 reserved_62_62 : 1; + u64 int_a : 1; + u64 c1_ldwn : 1; + u64 c0_ldwn : 1; + u64 c1_exc : 1; + u64 c0_exc : 1; + u64 c1_up_wf : 1; + u64 c0_up_wf : 1; + u64 c1_un_wf : 1; + u64 c0_un_wf : 1; + u64 c1_un_bx : 1; + u64 c1_un_wi : 1; + u64 c1_un_b2 : 1; + u64 c1_un_b1 : 1; + u64 c1_un_b0 : 1; + u64 c1_up_bx : 1; + u64 c1_up_wi : 1; + u64 c1_up_b2 : 1; + u64 c1_up_b1 : 1; + u64 c1_up_b0 : 1; + u64 c0_un_bx : 1; + u64 c0_un_wi : 1; + u64 c0_un_b2 : 1; + u64 c0_un_b1 : 1; + u64 c0_un_b0 : 1; + u64 c0_up_bx : 1; + u64 c0_up_wi : 1; + u64 c0_up_b2 : 1; + u64 c0_up_b1 : 1; + u64 c0_up_b0 : 1; + u64 c1_hpint : 1; + u64 c1_pmei : 1; + u64 c1_wake : 1; + u64 crs1_dr : 1; + u64 c1_se : 1; + u64 crs1_er : 1; + u64 c1_aeri : 1; + u64 c0_hpint : 1; + u64 c0_pmei : 1; + u64 c0_wake : 1; + u64 crs0_dr : 1; + u64 c0_se : 1; + u64 crs0_er : 1; + u64 c0_aeri : 1; + u64 ptime : 1; + u64 pcnt : 1; + u64 pidbof : 1; + u64 psldbof : 1; + u64 dtime1 : 1; + u64 dtime0 : 1; + u64 dcnt1 : 1; + u64 dcnt0 : 1; + u64 dma1fi : 1; + u64 dma0fi : 1; + u64 dma4dbo : 1; + u64 dma3dbo : 1; + u64 dma2dbo : 1; + u64 dma1dbo : 1; + u64 dma0dbo : 1; + u64 iob2big : 1; + u64 bar0_to : 1; + u64 rml_wto : 1; + u64 rml_rto : 1; + } s; + struct cvmx_npei_int_enb_s cn52xx; + struct cvmx_npei_int_enb_cn52xxp1 { + u64 mio_inta : 1; + u64 reserved_62_62 : 1; + u64 int_a : 1; + u64 c1_ldwn : 1; + u64 c0_ldwn : 1; + u64 c1_exc : 1; + u64 c0_exc : 1; + u64 c1_up_wf : 1; + u64 c0_up_wf : 1; + u64 c1_un_wf : 1; + u64 c0_un_wf : 1; + u64 c1_un_bx : 1; + u64 c1_un_wi : 1; + u64 c1_un_b2 : 1; + u64 c1_un_b1 : 1; + u64 c1_un_b0 : 1; + u64 c1_up_bx : 1; + u64 c1_up_wi : 1; + u64 c1_up_b2 : 1; + u64 c1_up_b1 : 1; + u64 c1_up_b0 : 1; + u64 c0_un_bx : 1; + u64 c0_un_wi : 1; + u64 c0_un_b2 : 1; + u64 c0_un_b1 : 1; + u64 c0_un_b0 : 1; + u64 c0_up_bx : 1; + u64 c0_up_wi : 1; + u64 c0_up_b2 : 1; + u64 c0_up_b1 : 1; + u64 c0_up_b0 : 1; + u64 c1_hpint : 1; + u64 c1_pmei : 1; + u64 c1_wake : 1; + u64 crs1_dr : 1; + u64 c1_se : 1; + u64 crs1_er : 1; + u64 c1_aeri : 1; + u64 c0_hpint : 1; + u64 c0_pmei : 1; + u64 c0_wake : 1; + u64 crs0_dr : 1; + u64 c0_se : 1; + u64 crs0_er : 1; + u64 c0_aeri : 1; + u64 ptime : 1; + u64 pcnt : 1; + u64 pidbof : 1; + u64 psldbof : 1; + u64 dtime1 : 1; + u64 dtime0 : 1; + u64 dcnt1 : 1; + u64 dcnt0 : 1; + u64 dma1fi : 1; + u64 dma0fi : 1; + u64 reserved_8_8 : 1; + u64 dma3dbo : 1; + u64 dma2dbo : 1; + u64 dma1dbo : 1; + u64 dma0dbo : 1; + u64 iob2big : 1; + u64 bar0_to : 1; + u64 rml_wto : 1; + u64 rml_rto : 1; + } cn52xxp1; + struct cvmx_npei_int_enb_s cn56xx; + struct cvmx_npei_int_enb_cn56xxp1 { + u64 mio_inta : 1; + u64 reserved_61_62 : 2; + u64 c1_ldwn : 1; + u64 c0_ldwn : 1; + u64 c1_exc : 1; + u64 c0_exc : 1; + u64 c1_up_wf : 1; + u64 c0_up_wf : 1; + u64 c1_un_wf : 1; + u64 c0_un_wf : 1; + u64 c1_un_bx : 1; + u64 c1_un_wi : 1; + u64 c1_un_b2 : 1; + u64 c1_un_b1 : 1; + u64 c1_un_b0 : 1; + u64 c1_up_bx : 1; + u64 c1_up_wi : 1; + u64 c1_up_b2 : 1; + u64 c1_up_b1 : 1; + u64 c1_up_b0 : 1; + u64 c0_un_bx : 1; + u64 c0_un_wi : 1; + u64 c0_un_b2 : 1; + u64 c0_un_b1 : 1; + u64 c0_un_b0 : 1; + u64 c0_up_bx : 1; + u64 c0_up_wi : 1; + u64 c0_up_b2 : 1; + u64 c0_up_b1 : 1; + u64 c0_up_b0 : 1; + u64 c1_hpint : 1; + u64 c1_pmei : 1; + u64 c1_wake : 1; + u64 reserved_29_29 : 1; + u64 c1_se : 1; + u64 reserved_27_27 : 1; + u64 c1_aeri : 1; + u64 c0_hpint : 1; + u64 c0_pmei : 1; + u64 c0_wake : 1; + u64 reserved_22_22 : 1; + u64 c0_se : 1; + u64 reserved_20_20 : 1; + u64 c0_aeri : 1; + u64 ptime : 1; + u64 pcnt : 1; + u64 pidbof : 1; + u64 psldbof : 1; + u64 dtime1 : 1; + u64 dtime0 : 1; + u64 dcnt1 : 1; + u64 dcnt0 : 1; + u64 dma1fi : 1; + u64 dma0fi : 1; + u64 dma4dbo : 1; + u64 dma3dbo : 1; + u64 dma2dbo : 1; + u64 dma1dbo : 1; + u64 dma0dbo : 1; + u64 iob2big : 1; + u64 bar0_to : 1; + u64 rml_wto : 1; + u64 rml_rto : 1; + } cn56xxp1; +}; + +typedef union cvmx_npei_int_enb cvmx_npei_int_enb_t; + +/** + * cvmx_npei_int_enb2 + * + * NPEI_INTERRUPT_ENB2 = NPI's Interrupt Enable2 Register + * + * Used to enable the various interrupting conditions of NPI + */ +union cvmx_npei_int_enb2 { + u64 u64; + struct cvmx_npei_int_enb2_s { + u64 reserved_62_63 : 2; + u64 int_a : 1; + u64 c1_ldwn : 1; + u64 c0_ldwn : 1; + u64 c1_exc : 1; + u64 c0_exc : 1; + u64 c1_up_wf : 1; + u64 c0_up_wf : 1; + u64 c1_un_wf : 1; + u64 c0_un_wf : 1; + u64 c1_un_bx : 1; + u64 c1_un_wi : 1; + u64 c1_un_b2 : 1; + u64 c1_un_b1 : 1; + u64 c1_un_b0 : 1; + u64 c1_up_bx : 1; + u64 c1_up_wi : 1; + u64 c1_up_b2 : 1; + u64 c1_up_b1 : 1; + u64 c1_up_b0 : 1; + u64 c0_un_bx : 1; + u64 c0_un_wi : 1; + u64 c0_un_b2 : 1; + u64 c0_un_b1 : 1; + u64 c0_un_b0 : 1; + u64 c0_up_bx : 1; + u64 c0_up_wi : 1; + u64 c0_up_b2 : 1; + u64 c0_up_b1 : 1; + u64 c0_up_b0 : 1; + u64 c1_hpint : 1; + u64 c1_pmei : 1; + u64 c1_wake : 1; + u64 crs1_dr : 1; + u64 c1_se : 1; + u64 crs1_er : 1; + u64 c1_aeri : 1; + u64 c0_hpint : 1; + u64 c0_pmei : 1; + u64 c0_wake : 1; + u64 crs0_dr : 1; + u64 c0_se : 1; + u64 crs0_er : 1; + u64 c0_aeri : 1; + u64 ptime : 1; + u64 pcnt : 1; + u64 pidbof : 1; + u64 psldbof : 1; + u64 dtime1 : 1; + u64 dtime0 : 1; + u64 dcnt1 : 1; + u64 dcnt0 : 1; + u64 dma1fi : 1; + u64 dma0fi : 1; + u64 dma4dbo : 1; + u64 dma3dbo : 1; + u64 dma2dbo : 1; + u64 dma1dbo : 1; + u64 dma0dbo : 1; + u64 iob2big : 1; + u64 bar0_to : 1; + u64 rml_wto : 1; + u64 rml_rto : 1; + } s; + struct cvmx_npei_int_enb2_s cn52xx; + struct cvmx_npei_int_enb2_cn52xxp1 { + u64 reserved_62_63 : 2; + u64 int_a : 1; + u64 c1_ldwn : 1; + u64 c0_ldwn : 1; + u64 c1_exc : 1; + u64 c0_exc : 1; + u64 c1_up_wf : 1; + u64 c0_up_wf : 1; + u64 c1_un_wf : 1; + u64 c0_un_wf : 1; + u64 c1_un_bx : 1; + u64 c1_un_wi : 1; + u64 c1_un_b2 : 1; + u64 c1_un_b1 : 1; + u64 c1_un_b0 : 1; + u64 c1_up_bx : 1; + u64 c1_up_wi : 1; + u64 c1_up_b2 : 1; + u64 c1_up_b1 : 1; + u64 c1_up_b0 : 1; + u64 c0_un_bx : 1; + u64 c0_un_wi : 1; + u64 c0_un_b2 : 1; + u64 c0_un_b1 : 1; + u64 c0_un_b0 : 1; + u64 c0_up_bx : 1; + u64 c0_up_wi : 1; + u64 c0_up_b2 : 1; + u64 c0_up_b1 : 1; + u64 c0_up_b0 : 1; + u64 c1_hpint : 1; + u64 c1_pmei : 1; + u64 c1_wake : 1; + u64 crs1_dr : 1; + u64 c1_se : 1; + u64 crs1_er : 1; + u64 c1_aeri : 1; + u64 c0_hpint : 1; + u64 c0_pmei : 1; + u64 c0_wake : 1; + u64 crs0_dr : 1; + u64 c0_se : 1; + u64 crs0_er : 1; + u64 c0_aeri : 1; + u64 ptime : 1; + u64 pcnt : 1; + u64 pidbof : 1; + u64 psldbof : 1; + u64 dtime1 : 1; + u64 dtime0 : 1; + u64 dcnt1 : 1; + u64 dcnt0 : 1; + u64 dma1fi : 1; + u64 dma0fi : 1; + u64 reserved_8_8 : 1; + u64 dma3dbo : 1; + u64 dma2dbo : 1; + u64 dma1dbo : 1; + u64 dma0dbo : 1; + u64 iob2big : 1; + u64 bar0_to : 1; + u64 rml_wto : 1; + u64 rml_rto : 1; + } cn52xxp1; + struct cvmx_npei_int_enb2_s cn56xx; + struct cvmx_npei_int_enb2_cn56xxp1 { + u64 reserved_61_63 : 3; + u64 c1_ldwn : 1; + u64 c0_ldwn : 1; + u64 c1_exc : 1; + u64 c0_exc : 1; + u64 c1_up_wf : 1; + u64 c0_up_wf : 1; + u64 c1_un_wf : 1; + u64 c0_un_wf : 1; + u64 c1_un_bx : 1; + u64 c1_un_wi : 1; + u64 c1_un_b2 : 1; + u64 c1_un_b1 : 1; + u64 c1_un_b0 : 1; + u64 c1_up_bx : 1; + u64 c1_up_wi : 1; + u64 c1_up_b2 : 1; + u64 c1_up_b1 : 1; + u64 c1_up_b0 : 1; + u64 c0_un_bx : 1; + u64 c0_un_wi : 1; + u64 c0_un_b2 : 1; + u64 c0_un_b1 : 1; + u64 c0_un_b0 : 1; + u64 c0_up_bx : 1; + u64 c0_up_wi : 1; + u64 c0_up_b2 : 1; + u64 c0_up_b1 : 1; + u64 c0_up_b0 : 1; + u64 c1_hpint : 1; + u64 c1_pmei : 1; + u64 c1_wake : 1; + u64 reserved_29_29 : 1; + u64 c1_se : 1; + u64 reserved_27_27 : 1; + u64 c1_aeri : 1; + u64 c0_hpint : 1; + u64 c0_pmei : 1; + u64 c0_wake : 1; + u64 reserved_22_22 : 1; + u64 c0_se : 1; + u64 reserved_20_20 : 1; + u64 c0_aeri : 1; + u64 ptime : 1; + u64 pcnt : 1; + u64 pidbof : 1; + u64 psldbof : 1; + u64 dtime1 : 1; + u64 dtime0 : 1; + u64 dcnt1 : 1; + u64 dcnt0 : 1; + u64 dma1fi : 1; + u64 dma0fi : 1; + u64 dma4dbo : 1; + u64 dma3dbo : 1; + u64 dma2dbo : 1; + u64 dma1dbo : 1; + u64 dma0dbo : 1; + u64 iob2big : 1; + u64 bar0_to : 1; + u64 rml_wto : 1; + u64 rml_rto : 1; + } cn56xxp1; +}; + +typedef union cvmx_npei_int_enb2 cvmx_npei_int_enb2_t; + +/** + * cvmx_npei_int_info + * + * NPEI_INT_INFO = NPI Interrupt Information + * + * Contains information about some of the interrupt condition that can occur + * in the NPEI_INTERRUPT_SUM register. + */ +union cvmx_npei_int_info { + u64 u64; + struct cvmx_npei_int_info_s { + u64 reserved_12_63 : 52; + u64 pidbof : 6; + u64 psldbof : 6; + } s; + struct cvmx_npei_int_info_s cn52xx; + struct cvmx_npei_int_info_s cn56xx; + struct cvmx_npei_int_info_s cn56xxp1; +}; + +typedef union cvmx_npei_int_info cvmx_npei_int_info_t; + +/** + * cvmx_npei_int_sum + * + * NPEI_INTERRUPT_SUM = NPI Interrupt Summary Register + * + * Set when an interrupt condition occurs, write '1' to clear. + */ +union cvmx_npei_int_sum { + u64 u64; + struct cvmx_npei_int_sum_s { + u64 mio_inta : 1; + u64 reserved_62_62 : 1; + u64 int_a : 1; + u64 c1_ldwn : 1; + u64 c0_ldwn : 1; + u64 c1_exc : 1; + u64 c0_exc : 1; + u64 c1_up_wf : 1; + u64 c0_up_wf : 1; + u64 c1_un_wf : 1; + u64 c0_un_wf : 1; + u64 c1_un_bx : 1; + u64 c1_un_wi : 1; + u64 c1_un_b2 : 1; + u64 c1_un_b1 : 1; + u64 c1_un_b0 : 1; + u64 c1_up_bx : 1; + u64 c1_up_wi : 1; + u64 c1_up_b2 : 1; + u64 c1_up_b1 : 1; + u64 c1_up_b0 : 1; + u64 c0_un_bx : 1; + u64 c0_un_wi : 1; + u64 c0_un_b2 : 1; + u64 c0_un_b1 : 1; + u64 c0_un_b0 : 1; + u64 c0_up_bx : 1; + u64 c0_up_wi : 1; + u64 c0_up_b2 : 1; + u64 c0_up_b1 : 1; + u64 c0_up_b0 : 1; + u64 c1_hpint : 1; + u64 c1_pmei : 1; + u64 c1_wake : 1; + u64 crs1_dr : 1; + u64 c1_se : 1; + u64 crs1_er : 1; + u64 c1_aeri : 1; + u64 c0_hpint : 1; + u64 c0_pmei : 1; + u64 c0_wake : 1; + u64 crs0_dr : 1; + u64 c0_se : 1; + u64 crs0_er : 1; + u64 c0_aeri : 1; + u64 ptime : 1; + u64 pcnt : 1; + u64 pidbof : 1; + u64 psldbof : 1; + u64 dtime1 : 1; + u64 dtime0 : 1; + u64 dcnt1 : 1; + u64 dcnt0 : 1; + u64 dma1fi : 1; + u64 dma0fi : 1; + u64 dma4dbo : 1; + u64 dma3dbo : 1; + u64 dma2dbo : 1; + u64 dma1dbo : 1; + u64 dma0dbo : 1; + u64 iob2big : 1; + u64 bar0_to : 1; + u64 rml_wto : 1; + u64 rml_rto : 1; + } s; + struct cvmx_npei_int_sum_s cn52xx; + struct cvmx_npei_int_sum_cn52xxp1 { + u64 mio_inta : 1; + u64 reserved_62_62 : 1; + u64 int_a : 1; + u64 c1_ldwn : 1; + u64 c0_ldwn : 1; + u64 c1_exc : 1; + u64 c0_exc : 1; + u64 c1_up_wf : 1; + u64 c0_up_wf : 1; + u64 c1_un_wf : 1; + u64 c0_un_wf : 1; + u64 c1_un_bx : 1; + u64 c1_un_wi : 1; + u64 c1_un_b2 : 1; + u64 c1_un_b1 : 1; + u64 c1_un_b0 : 1; + u64 c1_up_bx : 1; + u64 c1_up_wi : 1; + u64 c1_up_b2 : 1; + u64 c1_up_b1 : 1; + u64 c1_up_b0 : 1; + u64 c0_un_bx : 1; + u64 c0_un_wi : 1; + u64 c0_un_b2 : 1; + u64 c0_un_b1 : 1; + u64 c0_un_b0 : 1; + u64 c0_up_bx : 1; + u64 c0_up_wi : 1; + u64 c0_up_b2 : 1; + u64 c0_up_b1 : 1; + u64 c0_up_b0 : 1; + u64 c1_hpint : 1; + u64 c1_pmei : 1; + u64 c1_wake : 1; + u64 crs1_dr : 1; + u64 c1_se : 1; + u64 crs1_er : 1; + u64 c1_aeri : 1; + u64 c0_hpint : 1; + u64 c0_pmei : 1; + u64 c0_wake : 1; + u64 crs0_dr : 1; + u64 c0_se : 1; + u64 crs0_er : 1; + u64 c0_aeri : 1; + u64 reserved_15_18 : 4; + u64 dtime1 : 1; + u64 dtime0 : 1; + u64 dcnt1 : 1; + u64 dcnt0 : 1; + u64 dma1fi : 1; + u64 dma0fi : 1; + u64 reserved_8_8 : 1; + u64 dma3dbo : 1; + u64 dma2dbo : 1; + u64 dma1dbo : 1; + u64 dma0dbo : 1; + u64 iob2big : 1; + u64 bar0_to : 1; + u64 rml_wto : 1; + u64 rml_rto : 1; + } cn52xxp1; + struct cvmx_npei_int_sum_s cn56xx; + struct cvmx_npei_int_sum_cn56xxp1 { + u64 mio_inta : 1; + u64 reserved_61_62 : 2; + u64 c1_ldwn : 1; + u64 c0_ldwn : 1; + u64 c1_exc : 1; + u64 c0_exc : 1; + u64 c1_up_wf : 1; + u64 c0_up_wf : 1; + u64 c1_un_wf : 1; + u64 c0_un_wf : 1; + u64 c1_un_bx : 1; + u64 c1_un_wi : 1; + u64 c1_un_b2 : 1; + u64 c1_un_b1 : 1; + u64 c1_un_b0 : 1; + u64 c1_up_bx : 1; + u64 c1_up_wi : 1; + u64 c1_up_b2 : 1; + u64 c1_up_b1 : 1; + u64 c1_up_b0 : 1; + u64 c0_un_bx : 1; + u64 c0_un_wi : 1; + u64 c0_un_b2 : 1; + u64 c0_un_b1 : 1; + u64 c0_un_b0 : 1; + u64 c0_up_bx : 1; + u64 c0_up_wi : 1; + u64 c0_up_b2 : 1; + u64 c0_up_b1 : 1; + u64 c0_up_b0 : 1; + u64 c1_hpint : 1; + u64 c1_pmei : 1; + u64 c1_wake : 1; + u64 reserved_29_29 : 1; + u64 c1_se : 1; + u64 reserved_27_27 : 1; + u64 c1_aeri : 1; + u64 c0_hpint : 1; + u64 c0_pmei : 1; + u64 c0_wake : 1; + u64 reserved_22_22 : 1; + u64 c0_se : 1; + u64 reserved_20_20 : 1; + u64 c0_aeri : 1; + u64 reserved_15_18 : 4; + u64 dtime1 : 1; + u64 dtime0 : 1; + u64 dcnt1 : 1; + u64 dcnt0 : 1; + u64 dma1fi : 1; + u64 dma0fi : 1; + u64 dma4dbo : 1; + u64 dma3dbo : 1; + u64 dma2dbo : 1; + u64 dma1dbo : 1; + u64 dma0dbo : 1; + u64 iob2big : 1; + u64 bar0_to : 1; + u64 rml_wto : 1; + u64 rml_rto : 1; + } cn56xxp1; +}; + +typedef union cvmx_npei_int_sum cvmx_npei_int_sum_t; + +/** + * cvmx_npei_int_sum2 + * + * NPEI_INTERRUPT_SUM2 = NPI Interrupt Summary2 Register + * + * This is a read only copy of the NPEI_INTERRUPT_SUM register with bit + * variances. + */ +union cvmx_npei_int_sum2 { + u64 u64; + struct cvmx_npei_int_sum2_s { + u64 mio_inta : 1; + u64 reserved_62_62 : 1; + u64 int_a : 1; + u64 c1_ldwn : 1; + u64 c0_ldwn : 1; + u64 c1_exc : 1; + u64 c0_exc : 1; + u64 c1_up_wf : 1; + u64 c0_up_wf : 1; + u64 c1_un_wf : 1; + u64 c0_un_wf : 1; + u64 c1_un_bx : 1; + u64 c1_un_wi : 1; + u64 c1_un_b2 : 1; + u64 c1_un_b1 : 1; + u64 c1_un_b0 : 1; + u64 c1_up_bx : 1; + u64 c1_up_wi : 1; + u64 c1_up_b2 : 1; + u64 c1_up_b1 : 1; + u64 c1_up_b0 : 1; + u64 c0_un_bx : 1; + u64 c0_un_wi : 1; + u64 c0_un_b2 : 1; + u64 c0_un_b1 : 1; + u64 c0_un_b0 : 1; + u64 c0_up_bx : 1; + u64 c0_up_wi : 1; + u64 c0_up_b2 : 1; + u64 c0_up_b1 : 1; + u64 c0_up_b0 : 1; + u64 c1_hpint : 1; + u64 c1_pmei : 1; + u64 c1_wake : 1; + u64 crs1_dr : 1; + u64 c1_se : 1; + u64 crs1_er : 1; + u64 c1_aeri : 1; + u64 c0_hpint : 1; + u64 c0_pmei : 1; + u64 c0_wake : 1; + u64 crs0_dr : 1; + u64 c0_se : 1; + u64 crs0_er : 1; + u64 c0_aeri : 1; + u64 reserved_15_18 : 4; + u64 dtime1 : 1; + u64 dtime0 : 1; + u64 dcnt1 : 1; + u64 dcnt0 : 1; + u64 dma1fi : 1; + u64 dma0fi : 1; + u64 reserved_8_8 : 1; + u64 dma3dbo : 1; + u64 dma2dbo : 1; + u64 dma1dbo : 1; + u64 dma0dbo : 1; + u64 iob2big : 1; + u64 bar0_to : 1; + u64 rml_wto : 1; + u64 rml_rto : 1; + } s; + struct cvmx_npei_int_sum2_s cn52xx; + struct cvmx_npei_int_sum2_s cn52xxp1; + struct cvmx_npei_int_sum2_s cn56xx; +}; + +typedef union cvmx_npei_int_sum2 cvmx_npei_int_sum2_t; + +/** + * cvmx_npei_last_win_rdata0 + * + * NPEI_LAST_WIN_RDATA0 = NPEI Last Window Read Data Port0 + * + * The data from the last initiated window read. + */ +union cvmx_npei_last_win_rdata0 { + u64 u64; + struct cvmx_npei_last_win_rdata0_s { + u64 data : 64; + } s; + struct cvmx_npei_last_win_rdata0_s cn52xx; + struct cvmx_npei_last_win_rdata0_s cn52xxp1; + struct cvmx_npei_last_win_rdata0_s cn56xx; + struct cvmx_npei_last_win_rdata0_s cn56xxp1; +}; + +typedef union cvmx_npei_last_win_rdata0 cvmx_npei_last_win_rdata0_t; + +/** + * cvmx_npei_last_win_rdata1 + * + * NPEI_LAST_WIN_RDATA1 = NPEI Last Window Read Data Port1 + * + * The data from the last initiated window read. + */ +union cvmx_npei_last_win_rdata1 { + u64 u64; + struct cvmx_npei_last_win_rdata1_s { + u64 data : 64; + } s; + struct cvmx_npei_last_win_rdata1_s cn52xx; + struct cvmx_npei_last_win_rdata1_s cn52xxp1; + struct cvmx_npei_last_win_rdata1_s cn56xx; + struct cvmx_npei_last_win_rdata1_s cn56xxp1; +}; + +typedef union cvmx_npei_last_win_rdata1 cvmx_npei_last_win_rdata1_t; + +/** + * cvmx_npei_mem_access_ctl + * + * NPEI_MEM_ACCESS_CTL = NPEI's Memory Access Control + * + * Contains control for access to the PCIe address space. + */ +union cvmx_npei_mem_access_ctl { + u64 u64; + struct cvmx_npei_mem_access_ctl_s { + u64 reserved_14_63 : 50; + u64 max_word : 4; + u64 timer : 10; + } s; + struct cvmx_npei_mem_access_ctl_s cn52xx; + struct cvmx_npei_mem_access_ctl_s cn52xxp1; + struct cvmx_npei_mem_access_ctl_s cn56xx; + struct cvmx_npei_mem_access_ctl_s cn56xxp1; +}; + +typedef union cvmx_npei_mem_access_ctl cvmx_npei_mem_access_ctl_t; + +/** + * cvmx_npei_mem_access_subid# + * + * NPEI_MEM_ACCESS_SUBIDX = NPEI Memory Access SubidX Register + * + * Contains address index and control bits for access to memory from Core PPs. + */ +union cvmx_npei_mem_access_subidx { + u64 u64; + struct cvmx_npei_mem_access_subidx_s { + u64 reserved_42_63 : 22; + u64 zero : 1; + u64 port : 2; + u64 nmerge : 1; + u64 esr : 2; + u64 esw : 2; + u64 nsr : 1; + u64 nsw : 1; + u64 ror : 1; + u64 row : 1; + u64 ba : 30; + } s; + struct cvmx_npei_mem_access_subidx_s cn52xx; + struct cvmx_npei_mem_access_subidx_s cn52xxp1; + struct cvmx_npei_mem_access_subidx_s cn56xx; + struct cvmx_npei_mem_access_subidx_s cn56xxp1; +}; + +typedef union cvmx_npei_mem_access_subidx cvmx_npei_mem_access_subidx_t; + +/** + * cvmx_npei_msi_enb0 + * + * NPEI_MSI_ENB0 = NPEI MSI Enable0 + * + * Used to enable the interrupt generation for the bits in the NPEI_MSI_RCV0. + */ +union cvmx_npei_msi_enb0 { + u64 u64; + struct cvmx_npei_msi_enb0_s { + u64 enb : 64; + } s; + struct cvmx_npei_msi_enb0_s cn52xx; + struct cvmx_npei_msi_enb0_s cn52xxp1; + struct cvmx_npei_msi_enb0_s cn56xx; + struct cvmx_npei_msi_enb0_s cn56xxp1; +}; + +typedef union cvmx_npei_msi_enb0 cvmx_npei_msi_enb0_t; + +/** + * cvmx_npei_msi_enb1 + * + * NPEI_MSI_ENB1 = NPEI MSI Enable1 + * + * Used to enable the interrupt generation for the bits in the NPEI_MSI_RCV1. + */ +union cvmx_npei_msi_enb1 { + u64 u64; + struct cvmx_npei_msi_enb1_s { + u64 enb : 64; + } s; + struct cvmx_npei_msi_enb1_s cn52xx; + struct cvmx_npei_msi_enb1_s cn52xxp1; + struct cvmx_npei_msi_enb1_s cn56xx; + struct cvmx_npei_msi_enb1_s cn56xxp1; +}; + +typedef union cvmx_npei_msi_enb1 cvmx_npei_msi_enb1_t; + +/** + * cvmx_npei_msi_enb2 + * + * NPEI_MSI_ENB2 = NPEI MSI Enable2 + * + * Used to enable the interrupt generation for the bits in the NPEI_MSI_RCV2. + */ +union cvmx_npei_msi_enb2 { + u64 u64; + struct cvmx_npei_msi_enb2_s { + u64 enb : 64; + } s; + struct cvmx_npei_msi_enb2_s cn52xx; + struct cvmx_npei_msi_enb2_s cn52xxp1; + struct cvmx_npei_msi_enb2_s cn56xx; + struct cvmx_npei_msi_enb2_s cn56xxp1; +}; + +typedef union cvmx_npei_msi_enb2 cvmx_npei_msi_enb2_t; + +/** + * cvmx_npei_msi_enb3 + * + * NPEI_MSI_ENB3 = NPEI MSI Enable3 + * + * Used to enable the interrupt generation for the bits in the NPEI_MSI_RCV3. + */ +union cvmx_npei_msi_enb3 { + u64 u64; + struct cvmx_npei_msi_enb3_s { + u64 enb : 64; + } s; + struct cvmx_npei_msi_enb3_s cn52xx; + struct cvmx_npei_msi_enb3_s cn52xxp1; + struct cvmx_npei_msi_enb3_s cn56xx; + struct cvmx_npei_msi_enb3_s cn56xxp1; +}; + +typedef union cvmx_npei_msi_enb3 cvmx_npei_msi_enb3_t; + +/** + * cvmx_npei_msi_rcv0 + * + * NPEI_MSI_RCV0 = NPEI MSI Receive0 + * + * Contains bits [63:0] of the 256 bits oof MSI interrupts. + */ +union cvmx_npei_msi_rcv0 { + u64 u64; + struct cvmx_npei_msi_rcv0_s { + u64 intr : 64; + } s; + struct cvmx_npei_msi_rcv0_s cn52xx; + struct cvmx_npei_msi_rcv0_s cn52xxp1; + struct cvmx_npei_msi_rcv0_s cn56xx; + struct cvmx_npei_msi_rcv0_s cn56xxp1; +}; + +typedef union cvmx_npei_msi_rcv0 cvmx_npei_msi_rcv0_t; + +/** + * cvmx_npei_msi_rcv1 + * + * NPEI_MSI_RCV1 = NPEI MSI Receive1 + * + * Contains bits [127:64] of the 256 bits oof MSI interrupts. + */ +union cvmx_npei_msi_rcv1 { + u64 u64; + struct cvmx_npei_msi_rcv1_s { + u64 intr : 64; + } s; + struct cvmx_npei_msi_rcv1_s cn52xx; + struct cvmx_npei_msi_rcv1_s cn52xxp1; + struct cvmx_npei_msi_rcv1_s cn56xx; + struct cvmx_npei_msi_rcv1_s cn56xxp1; +}; + +typedef union cvmx_npei_msi_rcv1 cvmx_npei_msi_rcv1_t; + +/** + * cvmx_npei_msi_rcv2 + * + * NPEI_MSI_RCV2 = NPEI MSI Receive2 + * + * Contains bits [191:128] of the 256 bits oof MSI interrupts. + */ +union cvmx_npei_msi_rcv2 { + u64 u64; + struct cvmx_npei_msi_rcv2_s { + u64 intr : 64; + } s; + struct cvmx_npei_msi_rcv2_s cn52xx; + struct cvmx_npei_msi_rcv2_s cn52xxp1; + struct cvmx_npei_msi_rcv2_s cn56xx; + struct cvmx_npei_msi_rcv2_s cn56xxp1; +}; + +typedef union cvmx_npei_msi_rcv2 cvmx_npei_msi_rcv2_t; + +/** + * cvmx_npei_msi_rcv3 + * + * NPEI_MSI_RCV3 = NPEI MSI Receive3 + * + * Contains bits [255:192] of the 256 bits oof MSI interrupts. + */ +union cvmx_npei_msi_rcv3 { + u64 u64; + struct cvmx_npei_msi_rcv3_s { + u64 intr : 64; + } s; + struct cvmx_npei_msi_rcv3_s cn52xx; + struct cvmx_npei_msi_rcv3_s cn52xxp1; + struct cvmx_npei_msi_rcv3_s cn56xx; + struct cvmx_npei_msi_rcv3_s cn56xxp1; +}; + +typedef union cvmx_npei_msi_rcv3 cvmx_npei_msi_rcv3_t; + +/** + * cvmx_npei_msi_rd_map + * + * NPEI_MSI_RD_MAP = NPEI MSI Read MAP + * + * Used to read the mapping function of the NPEI_PCIE_MSI_RCV to NPEI_MSI_RCV + * registers. + */ +union cvmx_npei_msi_rd_map { + u64 u64; + struct cvmx_npei_msi_rd_map_s { + u64 reserved_16_63 : 48; + u64 rd_int : 8; + u64 msi_int : 8; + } s; + struct cvmx_npei_msi_rd_map_s cn52xx; + struct cvmx_npei_msi_rd_map_s cn52xxp1; + struct cvmx_npei_msi_rd_map_s cn56xx; + struct cvmx_npei_msi_rd_map_s cn56xxp1; +}; + +typedef union cvmx_npei_msi_rd_map cvmx_npei_msi_rd_map_t; + +/** + * cvmx_npei_msi_w1c_enb0 + * + * NPEI_MSI_W1C_ENB0 = NPEI MSI Write 1 To Clear Enable0 + * + * Used to clear bits in NPEI_MSI_ENB0. This is a PASS2 register. + */ +union cvmx_npei_msi_w1c_enb0 { + u64 u64; + struct cvmx_npei_msi_w1c_enb0_s { + u64 clr : 64; + } s; + struct cvmx_npei_msi_w1c_enb0_s cn52xx; + struct cvmx_npei_msi_w1c_enb0_s cn56xx; +}; + +typedef union cvmx_npei_msi_w1c_enb0 cvmx_npei_msi_w1c_enb0_t; + +/** + * cvmx_npei_msi_w1c_enb1 + * + * NPEI_MSI_W1C_ENB1 = NPEI MSI Write 1 To Clear Enable1 + * + * Used to clear bits in NPEI_MSI_ENB1. This is a PASS2 register. + */ +union cvmx_npei_msi_w1c_enb1 { + u64 u64; + struct cvmx_npei_msi_w1c_enb1_s { + u64 clr : 64; + } s; + struct cvmx_npei_msi_w1c_enb1_s cn52xx; + struct cvmx_npei_msi_w1c_enb1_s cn56xx; +}; + +typedef union cvmx_npei_msi_w1c_enb1 cvmx_npei_msi_w1c_enb1_t; + +/** + * cvmx_npei_msi_w1c_enb2 + * + * NPEI_MSI_W1C_ENB2 = NPEI MSI Write 1 To Clear Enable2 + * + * Used to clear bits in NPEI_MSI_ENB2. This is a PASS2 register. + */ +union cvmx_npei_msi_w1c_enb2 { + u64 u64; + struct cvmx_npei_msi_w1c_enb2_s { + u64 clr : 64; + } s; + struct cvmx_npei_msi_w1c_enb2_s cn52xx; + struct cvmx_npei_msi_w1c_enb2_s cn56xx; +}; + +typedef union cvmx_npei_msi_w1c_enb2 cvmx_npei_msi_w1c_enb2_t; + +/** + * cvmx_npei_msi_w1c_enb3 + * + * NPEI_MSI_W1C_ENB3 = NPEI MSI Write 1 To Clear Enable3 + * + * Used to clear bits in NPEI_MSI_ENB3. This is a PASS2 register. + */ +union cvmx_npei_msi_w1c_enb3 { + u64 u64; + struct cvmx_npei_msi_w1c_enb3_s { + u64 clr : 64; + } s; + struct cvmx_npei_msi_w1c_enb3_s cn52xx; + struct cvmx_npei_msi_w1c_enb3_s cn56xx; +}; + +typedef union cvmx_npei_msi_w1c_enb3 cvmx_npei_msi_w1c_enb3_t; + +/** + * cvmx_npei_msi_w1s_enb0 + * + * NPEI_MSI_W1S_ENB0 = NPEI MSI Write 1 To Set Enable0 + * + * Used to set bits in NPEI_MSI_ENB0. This is a PASS2 register. + */ +union cvmx_npei_msi_w1s_enb0 { + u64 u64; + struct cvmx_npei_msi_w1s_enb0_s { + u64 set : 64; + } s; + struct cvmx_npei_msi_w1s_enb0_s cn52xx; + struct cvmx_npei_msi_w1s_enb0_s cn56xx; +}; + +typedef union cvmx_npei_msi_w1s_enb0 cvmx_npei_msi_w1s_enb0_t; + +/** + * cvmx_npei_msi_w1s_enb1 + * + * NPEI_MSI_W1S_ENB0 = NPEI MSI Write 1 To Set Enable1 + * + * Used to set bits in NPEI_MSI_ENB1. This is a PASS2 register. + */ +union cvmx_npei_msi_w1s_enb1 { + u64 u64; + struct cvmx_npei_msi_w1s_enb1_s { + u64 set : 64; + } s; + struct cvmx_npei_msi_w1s_enb1_s cn52xx; + struct cvmx_npei_msi_w1s_enb1_s cn56xx; +}; + +typedef union cvmx_npei_msi_w1s_enb1 cvmx_npei_msi_w1s_enb1_t; + +/** + * cvmx_npei_msi_w1s_enb2 + * + * NPEI_MSI_W1S_ENB2 = NPEI MSI Write 1 To Set Enable2 + * + * Used to set bits in NPEI_MSI_ENB2. This is a PASS2 register. + */ +union cvmx_npei_msi_w1s_enb2 { + u64 u64; + struct cvmx_npei_msi_w1s_enb2_s { + u64 set : 64; + } s; + struct cvmx_npei_msi_w1s_enb2_s cn52xx; + struct cvmx_npei_msi_w1s_enb2_s cn56xx; +}; + +typedef union cvmx_npei_msi_w1s_enb2 cvmx_npei_msi_w1s_enb2_t; + +/** + * cvmx_npei_msi_w1s_enb3 + * + * NPEI_MSI_W1S_ENB3 = NPEI MSI Write 1 To Set Enable3 + * + * Used to set bits in NPEI_MSI_ENB3. This is a PASS2 register. + */ +union cvmx_npei_msi_w1s_enb3 { + u64 u64; + struct cvmx_npei_msi_w1s_enb3_s { + u64 set : 64; + } s; + struct cvmx_npei_msi_w1s_enb3_s cn52xx; + struct cvmx_npei_msi_w1s_enb3_s cn56xx; +}; + +typedef union cvmx_npei_msi_w1s_enb3 cvmx_npei_msi_w1s_enb3_t; + +/** + * cvmx_npei_msi_wr_map + * + * NPEI_MSI_WR_MAP = NPEI MSI Write MAP + * + * Used to write the mapping function of the NPEI_PCIE_MSI_RCV to NPEI_MSI_RCV + * registers. + */ +union cvmx_npei_msi_wr_map { + u64 u64; + struct cvmx_npei_msi_wr_map_s { + u64 reserved_16_63 : 48; + u64 ciu_int : 8; + u64 msi_int : 8; + } s; + struct cvmx_npei_msi_wr_map_s cn52xx; + struct cvmx_npei_msi_wr_map_s cn52xxp1; + struct cvmx_npei_msi_wr_map_s cn56xx; + struct cvmx_npei_msi_wr_map_s cn56xxp1; +}; + +typedef union cvmx_npei_msi_wr_map cvmx_npei_msi_wr_map_t; + +/** + * cvmx_npei_pcie_credit_cnt + * + * NPEI_PCIE_CREDIT_CNT = NPEI PCIE Credit Count + * + * Contains the number of credits for the pcie port FIFOs used by the NPEI. + * This value needs to be set BEFORE PCIe traffic + * flow from NPEI to PCIE Ports starts. A write to this register will cause + * the credit counts in the NPEI for the two + * PCIE ports to be reset to the value in this register. + */ +union cvmx_npei_pcie_credit_cnt { + u64 u64; + struct cvmx_npei_pcie_credit_cnt_s { + u64 reserved_48_63 : 16; + u64 p1_ccnt : 8; + u64 p1_ncnt : 8; + u64 p1_pcnt : 8; + u64 p0_ccnt : 8; + u64 p0_ncnt : 8; + u64 p0_pcnt : 8; + } s; + struct cvmx_npei_pcie_credit_cnt_s cn52xx; + struct cvmx_npei_pcie_credit_cnt_s cn56xx; +}; + +typedef union cvmx_npei_pcie_credit_cnt cvmx_npei_pcie_credit_cnt_t; + +/** + * cvmx_npei_pcie_msi_rcv + * + * NPEI_PCIE_MSI_RCV = NPEI PCIe MSI Receive + * + * Register where MSI writes are directed from the PCIe. + */ +union cvmx_npei_pcie_msi_rcv { + u64 u64; + struct cvmx_npei_pcie_msi_rcv_s { + u64 reserved_8_63 : 56; + u64 intr : 8; + } s; + struct cvmx_npei_pcie_msi_rcv_s cn52xx; + struct cvmx_npei_pcie_msi_rcv_s cn52xxp1; + struct cvmx_npei_pcie_msi_rcv_s cn56xx; + struct cvmx_npei_pcie_msi_rcv_s cn56xxp1; +}; + +typedef union cvmx_npei_pcie_msi_rcv cvmx_npei_pcie_msi_rcv_t; + +/** + * cvmx_npei_pcie_msi_rcv_b1 + * + * NPEI_PCIE_MSI_RCV_B1 = NPEI PCIe MSI Receive Byte 1 + * + * Register where MSI writes are directed from the PCIe. + */ +union cvmx_npei_pcie_msi_rcv_b1 { + u64 u64; + struct cvmx_npei_pcie_msi_rcv_b1_s { + u64 reserved_16_63 : 48; + u64 intr : 8; + u64 reserved_0_7 : 8; + } s; + struct cvmx_npei_pcie_msi_rcv_b1_s cn52xx; + struct cvmx_npei_pcie_msi_rcv_b1_s cn52xxp1; + struct cvmx_npei_pcie_msi_rcv_b1_s cn56xx; + struct cvmx_npei_pcie_msi_rcv_b1_s cn56xxp1; +}; + +typedef union cvmx_npei_pcie_msi_rcv_b1 cvmx_npei_pcie_msi_rcv_b1_t; + +/** + * cvmx_npei_pcie_msi_rcv_b2 + * + * NPEI_PCIE_MSI_RCV_B2 = NPEI PCIe MSI Receive Byte 2 + * + * Register where MSI writes are directed from the PCIe. + */ +union cvmx_npei_pcie_msi_rcv_b2 { + u64 u64; + struct cvmx_npei_pcie_msi_rcv_b2_s { + u64 reserved_24_63 : 40; + u64 intr : 8; + u64 reserved_0_15 : 16; + } s; + struct cvmx_npei_pcie_msi_rcv_b2_s cn52xx; + struct cvmx_npei_pcie_msi_rcv_b2_s cn52xxp1; + struct cvmx_npei_pcie_msi_rcv_b2_s cn56xx; + struct cvmx_npei_pcie_msi_rcv_b2_s cn56xxp1; +}; + +typedef union cvmx_npei_pcie_msi_rcv_b2 cvmx_npei_pcie_msi_rcv_b2_t; + +/** + * cvmx_npei_pcie_msi_rcv_b3 + * + * NPEI_PCIE_MSI_RCV_B3 = NPEI PCIe MSI Receive Byte 3 + * + * Register where MSI writes are directed from the PCIe. + */ +union cvmx_npei_pcie_msi_rcv_b3 { + u64 u64; + struct cvmx_npei_pcie_msi_rcv_b3_s { + u64 reserved_32_63 : 32; + u64 intr : 8; + u64 reserved_0_23 : 24; + } s; + struct cvmx_npei_pcie_msi_rcv_b3_s cn52xx; + struct cvmx_npei_pcie_msi_rcv_b3_s cn52xxp1; + struct cvmx_npei_pcie_msi_rcv_b3_s cn56xx; + struct cvmx_npei_pcie_msi_rcv_b3_s cn56xxp1; +}; + +typedef union cvmx_npei_pcie_msi_rcv_b3 cvmx_npei_pcie_msi_rcv_b3_t; + +/** + * cvmx_npei_pkt#_cnts + * + * NPEI_PKT[0..31]_CNTS = NPEI Packet ring# Counts + * + * The counters for output rings. + */ +union cvmx_npei_pktx_cnts { + u64 u64; + struct cvmx_npei_pktx_cnts_s { + u64 reserved_54_63 : 10; + u64 timer : 22; + u64 cnt : 32; + } s; + struct cvmx_npei_pktx_cnts_s cn52xx; + struct cvmx_npei_pktx_cnts_s cn56xx; +}; + +typedef union cvmx_npei_pktx_cnts cvmx_npei_pktx_cnts_t; + +/** + * cvmx_npei_pkt#_in_bp + * + * NPEI_PKT[0..31]_IN_BP = NPEI Packet ring# Input Backpressure + * + * The counters and thresholds for input packets to apply backpressure to + * processing of the packets. + */ +union cvmx_npei_pktx_in_bp { + u64 u64; + struct cvmx_npei_pktx_in_bp_s { + u64 wmark : 32; + u64 cnt : 32; + } s; + struct cvmx_npei_pktx_in_bp_s cn52xx; + struct cvmx_npei_pktx_in_bp_s cn56xx; +}; + +typedef union cvmx_npei_pktx_in_bp cvmx_npei_pktx_in_bp_t; + +/** + * cvmx_npei_pkt#_instr_baddr + * + * NPEI_PKT[0..31]_INSTR_BADDR = NPEI Packet ring# Instruction Base Address + * + * Start of Instruction for input packets. + */ +union cvmx_npei_pktx_instr_baddr { + u64 u64; + struct cvmx_npei_pktx_instr_baddr_s { + u64 addr : 61; + u64 reserved_0_2 : 3; + } s; + struct cvmx_npei_pktx_instr_baddr_s cn52xx; + struct cvmx_npei_pktx_instr_baddr_s cn56xx; +}; + +typedef union cvmx_npei_pktx_instr_baddr cvmx_npei_pktx_instr_baddr_t; + +/** + * cvmx_npei_pkt#_instr_baoff_dbell + * + * NPEI_PKT[0..31]_INSTR_BAOFF_DBELL = NPEI Packet ring# Instruction Base + * Address Offset and Doorbell + * + * The doorbell and base address offset for next read. + */ +union cvmx_npei_pktx_instr_baoff_dbell { + u64 u64; + struct cvmx_npei_pktx_instr_baoff_dbell_s { + u64 aoff : 32; + u64 dbell : 32; + } s; + struct cvmx_npei_pktx_instr_baoff_dbell_s cn52xx; + struct cvmx_npei_pktx_instr_baoff_dbell_s cn56xx; +}; + +typedef union cvmx_npei_pktx_instr_baoff_dbell + cvmx_npei_pktx_instr_baoff_dbell_t; + +/** + * cvmx_npei_pkt#_instr_fifo_rsize + * + * NPEI_PKT[0..31]_INSTR_FIFO_RSIZE = NPEI Packet ring# Instruction FIFO and + * Ring Size. + * + * Fifo field and ring size for Instructions. + */ +union cvmx_npei_pktx_instr_fifo_rsize { + u64 u64; + struct cvmx_npei_pktx_instr_fifo_rsize_s { + u64 max : 9; + u64 rrp : 9; + u64 wrp : 9; + u64 fcnt : 5; + u64 rsize : 32; + } s; + struct cvmx_npei_pktx_instr_fifo_rsize_s cn52xx; + struct cvmx_npei_pktx_instr_fifo_rsize_s cn56xx; +}; + +typedef union cvmx_npei_pktx_instr_fifo_rsize cvmx_npei_pktx_instr_fifo_rsize_t; + +/** + * cvmx_npei_pkt#_instr_header + * + * NPEI_PKT[0..31]_INSTR_HEADER = NPEI Packet ring# Instruction Header. + * + * VAlues used to build input packet header. + */ +union cvmx_npei_pktx_instr_header { + u64 u64; + struct cvmx_npei_pktx_instr_header_s { + u64 reserved_44_63 : 20; + u64 pbp : 1; + u64 reserved_38_42 : 5; + u64 rparmode : 2; + u64 reserved_35_35 : 1; + u64 rskp_len : 7; + u64 reserved_22_27 : 6; + u64 use_ihdr : 1; + u64 reserved_16_20 : 5; + u64 par_mode : 2; + u64 reserved_13_13 : 1; + u64 skp_len : 7; + u64 reserved_0_5 : 6; + } s; + struct cvmx_npei_pktx_instr_header_s cn52xx; + struct cvmx_npei_pktx_instr_header_s cn56xx; +}; + +typedef union cvmx_npei_pktx_instr_header cvmx_npei_pktx_instr_header_t; + +/** + * cvmx_npei_pkt#_slist_baddr + * + * NPEI_PKT[0..31]_SLIST_BADDR = NPEI Packet ring# Scatter List Base Address + * + * Start of Scatter List for output packet pointers - MUST be 16 byte aligned + */ +union cvmx_npei_pktx_slist_baddr { + u64 u64; + struct cvmx_npei_pktx_slist_baddr_s { + u64 addr : 60; + u64 reserved_0_3 : 4; + } s; + struct cvmx_npei_pktx_slist_baddr_s cn52xx; + struct cvmx_npei_pktx_slist_baddr_s cn56xx; +}; + +typedef union cvmx_npei_pktx_slist_baddr cvmx_npei_pktx_slist_baddr_t; + +/** + * cvmx_npei_pkt#_slist_baoff_dbell + * + * NPEI_PKT[0..31]_SLIST_BAOFF_DBELL = NPEI Packet ring# Scatter List Base + * Address Offset and Doorbell + * + * The doorbell and base address offset for next read. + */ +union cvmx_npei_pktx_slist_baoff_dbell { + u64 u64; + struct cvmx_npei_pktx_slist_baoff_dbell_s { + u64 aoff : 32; + u64 dbell : 32; + } s; + struct cvmx_npei_pktx_slist_baoff_dbell_s cn52xx; + struct cvmx_npei_pktx_slist_baoff_dbell_s cn56xx; +}; + +typedef union cvmx_npei_pktx_slist_baoff_dbell + cvmx_npei_pktx_slist_baoff_dbell_t; + +/** + * cvmx_npei_pkt#_slist_fifo_rsize + * + * NPEI_PKT[0..31]_SLIST_FIFO_RSIZE = NPEI Packet ring# Scatter List FIFO and + * Ring Size. + * + * The number of scatter pointer pairs in the scatter list. + */ +union cvmx_npei_pktx_slist_fifo_rsize { + u64 u64; + struct cvmx_npei_pktx_slist_fifo_rsize_s { + u64 reserved_32_63 : 32; + u64 rsize : 32; + } s; + struct cvmx_npei_pktx_slist_fifo_rsize_s cn52xx; + struct cvmx_npei_pktx_slist_fifo_rsize_s cn56xx; +}; + +typedef union cvmx_npei_pktx_slist_fifo_rsize cvmx_npei_pktx_slist_fifo_rsize_t; + +/** + * cvmx_npei_pkt_cnt_int + * + * NPEI_PKT_CNT_INT = NPI Packet Counter Interrupt + * + * The packets rings that are interrupting because of Packet Counters. + */ +union cvmx_npei_pkt_cnt_int { + u64 u64; + struct cvmx_npei_pkt_cnt_int_s { + u64 reserved_32_63 : 32; + u64 port : 32; + } s; + struct cvmx_npei_pkt_cnt_int_s cn52xx; + struct cvmx_npei_pkt_cnt_int_s cn56xx; +}; + +typedef union cvmx_npei_pkt_cnt_int cvmx_npei_pkt_cnt_int_t; + +/** + * cvmx_npei_pkt_cnt_int_enb + * + * NPEI_PKT_CNT_INT_ENB = NPI Packet Counter Interrupt Enable + * + * Enable for the packets rings that are interrupting because of Packet Counters. + */ +union cvmx_npei_pkt_cnt_int_enb { + u64 u64; + struct cvmx_npei_pkt_cnt_int_enb_s { + u64 reserved_32_63 : 32; + u64 port : 32; + } s; + struct cvmx_npei_pkt_cnt_int_enb_s cn52xx; + struct cvmx_npei_pkt_cnt_int_enb_s cn56xx; +}; + +typedef union cvmx_npei_pkt_cnt_int_enb cvmx_npei_pkt_cnt_int_enb_t; + +/** + * cvmx_npei_pkt_data_out_es + * + * NPEI_PKT_DATA_OUT_ES = NPEI's Packet Data Out Endian Swap + * + * The Endian Swap for writing Data Out. + */ +union cvmx_npei_pkt_data_out_es { + u64 u64; + struct cvmx_npei_pkt_data_out_es_s { + u64 es : 64; + } s; + struct cvmx_npei_pkt_data_out_es_s cn52xx; + struct cvmx_npei_pkt_data_out_es_s cn56xx; +}; + +typedef union cvmx_npei_pkt_data_out_es cvmx_npei_pkt_data_out_es_t; + +/** + * cvmx_npei_pkt_data_out_ns + * + * NPEI_PKT_DATA_OUT_NS = NPEI's Packet Data Out No Snoop + * + * The NS field for the TLP when writing packet data. + */ +union cvmx_npei_pkt_data_out_ns { + u64 u64; + struct cvmx_npei_pkt_data_out_ns_s { + u64 reserved_32_63 : 32; + u64 nsr : 32; + } s; + struct cvmx_npei_pkt_data_out_ns_s cn52xx; + struct cvmx_npei_pkt_data_out_ns_s cn56xx; +}; + +typedef union cvmx_npei_pkt_data_out_ns cvmx_npei_pkt_data_out_ns_t; + +/** + * cvmx_npei_pkt_data_out_ror + * + * NPEI_PKT_DATA_OUT_ROR = NPEI's Packet Data Out Relaxed Ordering + * + * The ROR field for the TLP when writing Packet Data. + */ +union cvmx_npei_pkt_data_out_ror { + u64 u64; + struct cvmx_npei_pkt_data_out_ror_s { + u64 reserved_32_63 : 32; + u64 ror : 32; + } s; + struct cvmx_npei_pkt_data_out_ror_s cn52xx; + struct cvmx_npei_pkt_data_out_ror_s cn56xx; +}; + +typedef union cvmx_npei_pkt_data_out_ror cvmx_npei_pkt_data_out_ror_t; + +/** + * cvmx_npei_pkt_dpaddr + * + * NPEI_PKT_DPADDR = NPEI's Packet Data Pointer Addr + * + * Used to detemine address and attributes for packet data writes. + */ +union cvmx_npei_pkt_dpaddr { + u64 u64; + struct cvmx_npei_pkt_dpaddr_s { + u64 reserved_32_63 : 32; + u64 dptr : 32; + } s; + struct cvmx_npei_pkt_dpaddr_s cn52xx; + struct cvmx_npei_pkt_dpaddr_s cn56xx; +}; + +typedef union cvmx_npei_pkt_dpaddr cvmx_npei_pkt_dpaddr_t; + +/** + * cvmx_npei_pkt_in_bp + * + * NPEI_PKT_IN_BP = NPEI Packet Input Backpressure + * + * Which input rings have backpressure applied. + */ +union cvmx_npei_pkt_in_bp { + u64 u64; + struct cvmx_npei_pkt_in_bp_s { + u64 reserved_32_63 : 32; + u64 bp : 32; + } s; + struct cvmx_npei_pkt_in_bp_s cn52xx; + struct cvmx_npei_pkt_in_bp_s cn56xx; +}; + +typedef union cvmx_npei_pkt_in_bp cvmx_npei_pkt_in_bp_t; + +/** + * cvmx_npei_pkt_in_done#_cnts + * + * NPEI_PKT_IN_DONE[0..31]_CNTS = NPEI Instruction Done ring# Counts + * + * Counters for instructions completed on Input rings. + */ +union cvmx_npei_pkt_in_donex_cnts { + u64 u64; + struct cvmx_npei_pkt_in_donex_cnts_s { + u64 reserved_32_63 : 32; + u64 cnt : 32; + } s; + struct cvmx_npei_pkt_in_donex_cnts_s cn52xx; + struct cvmx_npei_pkt_in_donex_cnts_s cn56xx; +}; + +typedef union cvmx_npei_pkt_in_donex_cnts cvmx_npei_pkt_in_donex_cnts_t; + +/** + * cvmx_npei_pkt_in_instr_counts + * + * NPEI_PKT_IN_INSTR_COUNTS = NPEI Packet Input Instrutction Counts + * + * Keeps track of the number of instructions read into the FIFO and Packets + * sent to IPD. + */ +union cvmx_npei_pkt_in_instr_counts { + u64 u64; + struct cvmx_npei_pkt_in_instr_counts_s { + u64 wr_cnt : 32; + u64 rd_cnt : 32; + } s; + struct cvmx_npei_pkt_in_instr_counts_s cn52xx; + struct cvmx_npei_pkt_in_instr_counts_s cn56xx; +}; + +typedef union cvmx_npei_pkt_in_instr_counts cvmx_npei_pkt_in_instr_counts_t; + +/** + * cvmx_npei_pkt_in_pcie_port + * + * NPEI_PKT_IN_PCIE_PORT = NPEI's Packet In To PCIe Port Assignment + * + * Assigns Packet Input rings to PCIe ports. + */ +union cvmx_npei_pkt_in_pcie_port { + u64 u64; + struct cvmx_npei_pkt_in_pcie_port_s { + u64 pp : 64; + } s; + struct cvmx_npei_pkt_in_pcie_port_s cn52xx; + struct cvmx_npei_pkt_in_pcie_port_s cn56xx; +}; + +typedef union cvmx_npei_pkt_in_pcie_port cvmx_npei_pkt_in_pcie_port_t; + +/** + * cvmx_npei_pkt_input_control + * + * NPEI_PKT_INPUT_CONTROL = NPEI's Packet Input Control + * + * Control for reads for gather list and instructions. + */ +union cvmx_npei_pkt_input_control { + u64 u64; + struct cvmx_npei_pkt_input_control_s { + u64 reserved_23_63 : 41; + u64 pkt_rr : 1; + u64 pbp_dhi : 13; + u64 d_nsr : 1; + u64 d_esr : 2; + u64 d_ror : 1; + u64 use_csr : 1; + u64 nsr : 1; + u64 esr : 2; + u64 ror : 1; + } s; + struct cvmx_npei_pkt_input_control_s cn52xx; + struct cvmx_npei_pkt_input_control_s cn56xx; +}; + +typedef union cvmx_npei_pkt_input_control cvmx_npei_pkt_input_control_t; + +/** + * cvmx_npei_pkt_instr_enb + * + * NPEI_PKT_INSTR_ENB = NPEI's Packet Instruction Enable + * + * Enables the instruction fetch for a Packet-ring. + */ +union cvmx_npei_pkt_instr_enb { + u64 u64; + struct cvmx_npei_pkt_instr_enb_s { + u64 reserved_32_63 : 32; + u64 enb : 32; + } s; + struct cvmx_npei_pkt_instr_enb_s cn52xx; + struct cvmx_npei_pkt_instr_enb_s cn56xx; +}; + +typedef union cvmx_npei_pkt_instr_enb cvmx_npei_pkt_instr_enb_t; + +/** + * cvmx_npei_pkt_instr_rd_size + * + * NPEI_PKT_INSTR_RD_SIZE = NPEI Instruction Read Size + * + * The number of instruction allowed to be read at one time. + */ +union cvmx_npei_pkt_instr_rd_size { + u64 u64; + struct cvmx_npei_pkt_instr_rd_size_s { + u64 rdsize : 64; + } s; + struct cvmx_npei_pkt_instr_rd_size_s cn52xx; + struct cvmx_npei_pkt_instr_rd_size_s cn56xx; +}; + +typedef union cvmx_npei_pkt_instr_rd_size cvmx_npei_pkt_instr_rd_size_t; + +/** + * cvmx_npei_pkt_instr_size + * + * NPEI_PKT_INSTR_SIZE = NPEI's Packet Instruction Size + * + * Determines if instructions are 64 or 32 byte in size for a Packet-ring. + */ +union cvmx_npei_pkt_instr_size { + u64 u64; + struct cvmx_npei_pkt_instr_size_s { + u64 reserved_32_63 : 32; + u64 is_64b : 32; + } s; + struct cvmx_npei_pkt_instr_size_s cn52xx; + struct cvmx_npei_pkt_instr_size_s cn56xx; +}; + +typedef union cvmx_npei_pkt_instr_size cvmx_npei_pkt_instr_size_t; + +/** + * cvmx_npei_pkt_int_levels + * + * 0x90F0 reserved NPEI_PKT_PCIE_PORT2 + * + * + * NPEI_PKT_INT_LEVELS = NPEI's Packet Interrupt Levels + * + * Output packet interrupt levels. + */ +union cvmx_npei_pkt_int_levels { + u64 u64; + struct cvmx_npei_pkt_int_levels_s { + u64 reserved_54_63 : 10; + u64 time : 22; + u64 cnt : 32; + } s; + struct cvmx_npei_pkt_int_levels_s cn52xx; + struct cvmx_npei_pkt_int_levels_s cn56xx; +}; + +typedef union cvmx_npei_pkt_int_levels cvmx_npei_pkt_int_levels_t; + +/** + * cvmx_npei_pkt_iptr + * + * NPEI_PKT_IPTR = NPEI's Packet Info Poitner + * + * Controls using the Info-Pointer to store length and data. + */ +union cvmx_npei_pkt_iptr { + u64 u64; + struct cvmx_npei_pkt_iptr_s { + u64 reserved_32_63 : 32; + u64 iptr : 32; + } s; + struct cvmx_npei_pkt_iptr_s cn52xx; + struct cvmx_npei_pkt_iptr_s cn56xx; +}; + +typedef union cvmx_npei_pkt_iptr cvmx_npei_pkt_iptr_t; + +/** + * cvmx_npei_pkt_out_bmode + * + * NPEI_PKT_OUT_BMODE = NPEI's Packet Out Byte Mode + * + * Control the updating of the NPEI_PKT#_CNT register. + */ +union cvmx_npei_pkt_out_bmode { + u64 u64; + struct cvmx_npei_pkt_out_bmode_s { + u64 reserved_32_63 : 32; + u64 bmode : 32; + } s; + struct cvmx_npei_pkt_out_bmode_s cn52xx; + struct cvmx_npei_pkt_out_bmode_s cn56xx; +}; + +typedef union cvmx_npei_pkt_out_bmode cvmx_npei_pkt_out_bmode_t; + +/** + * cvmx_npei_pkt_out_enb + * + * NPEI_PKT_OUT_ENB = NPEI's Packet Output Enable + * + * Enables the output packet engines. + */ +union cvmx_npei_pkt_out_enb { + u64 u64; + struct cvmx_npei_pkt_out_enb_s { + u64 reserved_32_63 : 32; + u64 enb : 32; + } s; + struct cvmx_npei_pkt_out_enb_s cn52xx; + struct cvmx_npei_pkt_out_enb_s cn56xx; +}; + +typedef union cvmx_npei_pkt_out_enb cvmx_npei_pkt_out_enb_t; + +/** + * cvmx_npei_pkt_output_wmark + * + * NPEI_PKT_OUTPUT_WMARK = NPEI's Packet Output Water Mark + * + * Value that when the NPEI_PKT#_SLIST_BAOFF_DBELL[DBELL] value is less then + * that backpressure for the rings will be applied. + */ +union cvmx_npei_pkt_output_wmark { + u64 u64; + struct cvmx_npei_pkt_output_wmark_s { + u64 reserved_32_63 : 32; + u64 wmark : 32; + } s; + struct cvmx_npei_pkt_output_wmark_s cn52xx; + struct cvmx_npei_pkt_output_wmark_s cn56xx; +}; + +typedef union cvmx_npei_pkt_output_wmark cvmx_npei_pkt_output_wmark_t; + +/** + * cvmx_npei_pkt_pcie_port + * + * NPEI_PKT_PCIE_PORT = NPEI's Packet To PCIe Port Assignment + * + * Assigns Packet Ports to PCIe ports. + */ +union cvmx_npei_pkt_pcie_port { + u64 u64; + struct cvmx_npei_pkt_pcie_port_s { + u64 pp : 64; + } s; + struct cvmx_npei_pkt_pcie_port_s cn52xx; + struct cvmx_npei_pkt_pcie_port_s cn56xx; +}; + +typedef union cvmx_npei_pkt_pcie_port cvmx_npei_pkt_pcie_port_t; + +/** + * cvmx_npei_pkt_port_in_rst + * + * NPEI_PKT_PORT_IN_RST = NPEI Packet Port In Reset + * + * Vector bits related to ring-port for ones that are reset. + */ +union cvmx_npei_pkt_port_in_rst { + u64 u64; + struct cvmx_npei_pkt_port_in_rst_s { + u64 in_rst : 32; + u64 out_rst : 32; + } s; + struct cvmx_npei_pkt_port_in_rst_s cn52xx; + struct cvmx_npei_pkt_port_in_rst_s cn56xx; +}; + +typedef union cvmx_npei_pkt_port_in_rst cvmx_npei_pkt_port_in_rst_t; + +/** + * cvmx_npei_pkt_slist_es + * + * NPEI_PKT_SLIST_ES = NPEI's Packet Scatter List Endian Swap + * + * The Endian Swap for Scatter List Read. + */ +union cvmx_npei_pkt_slist_es { + u64 u64; + struct cvmx_npei_pkt_slist_es_s { + u64 es : 64; + } s; + struct cvmx_npei_pkt_slist_es_s cn52xx; + struct cvmx_npei_pkt_slist_es_s cn56xx; +}; + +typedef union cvmx_npei_pkt_slist_es cvmx_npei_pkt_slist_es_t; + +/** + * cvmx_npei_pkt_slist_id_size + * + * NPEI_PKT_SLIST_ID_SIZE = NPEI Packet Scatter List Info and Data Size + * + * The Size of the information and data fields pointed to by Scatter List + * pointers. + */ +union cvmx_npei_pkt_slist_id_size { + u64 u64; + struct cvmx_npei_pkt_slist_id_size_s { + u64 reserved_23_63 : 41; + u64 isize : 7; + u64 bsize : 16; + } s; + struct cvmx_npei_pkt_slist_id_size_s cn52xx; + struct cvmx_npei_pkt_slist_id_size_s cn56xx; +}; + +typedef union cvmx_npei_pkt_slist_id_size cvmx_npei_pkt_slist_id_size_t; + +/** + * cvmx_npei_pkt_slist_ns + * + * NPEI_PKT_SLIST_NS = NPEI's Packet Scatter List No Snoop + * + * The NS field for the TLP when fetching Scatter List. + */ +union cvmx_npei_pkt_slist_ns { + u64 u64; + struct cvmx_npei_pkt_slist_ns_s { + u64 reserved_32_63 : 32; + u64 nsr : 32; + } s; + struct cvmx_npei_pkt_slist_ns_s cn52xx; + struct cvmx_npei_pkt_slist_ns_s cn56xx; +}; + +typedef union cvmx_npei_pkt_slist_ns cvmx_npei_pkt_slist_ns_t; + +/** + * cvmx_npei_pkt_slist_ror + * + * NPEI_PKT_SLIST_ROR = NPEI's Packet Scatter List Relaxed Ordering + * + * The ROR field for the TLP when fetching Scatter List. + */ +union cvmx_npei_pkt_slist_ror { + u64 u64; + struct cvmx_npei_pkt_slist_ror_s { + u64 reserved_32_63 : 32; + u64 ror : 32; + } s; + struct cvmx_npei_pkt_slist_ror_s cn52xx; + struct cvmx_npei_pkt_slist_ror_s cn56xx; +}; + +typedef union cvmx_npei_pkt_slist_ror cvmx_npei_pkt_slist_ror_t; + +/** + * cvmx_npei_pkt_time_int + * + * NPEI_PKT_TIME_INT = NPEI Packet Timer Interrupt + * + * The packets rings that are interrupting because of Packet Timers. + */ +union cvmx_npei_pkt_time_int { + u64 u64; + struct cvmx_npei_pkt_time_int_s { + u64 reserved_32_63 : 32; + u64 port : 32; + } s; + struct cvmx_npei_pkt_time_int_s cn52xx; + struct cvmx_npei_pkt_time_int_s cn56xx; +}; + +typedef union cvmx_npei_pkt_time_int cvmx_npei_pkt_time_int_t; + +/** + * cvmx_npei_pkt_time_int_enb + * + * NPEI_PKT_TIME_INT_ENB = NPEI Packet Timer Interrupt Enable + * + * The packets rings that are interrupting because of Packet Timers. + */ +union cvmx_npei_pkt_time_int_enb { + u64 u64; + struct cvmx_npei_pkt_time_int_enb_s { + u64 reserved_32_63 : 32; + u64 port : 32; + } s; + struct cvmx_npei_pkt_time_int_enb_s cn52xx; + struct cvmx_npei_pkt_time_int_enb_s cn56xx; +}; + +typedef union cvmx_npei_pkt_time_int_enb cvmx_npei_pkt_time_int_enb_t; + +/** + * cvmx_npei_rsl_int_blocks + * + * NPEI_RSL_INT_BLOCKS = NPEI RSL Interrupt Blocks Register + * + * Reading this register will return a vector with a bit set '1' for a + * corresponding RSL block + * that presently has an interrupt pending. The Field Description below + * supplies the name of the + * register that software should read to find out why that intterupt bit is set. + */ +union cvmx_npei_rsl_int_blocks { + u64 u64; + struct cvmx_npei_rsl_int_blocks_s { + u64 reserved_31_63 : 33; + u64 iob : 1; + u64 lmc1 : 1; + u64 agl : 1; + u64 reserved_24_27 : 4; + u64 asxpcs1 : 1; + u64 asxpcs0 : 1; + u64 reserved_21_21 : 1; + u64 pip : 1; + u64 spx1 : 1; + u64 spx0 : 1; + u64 lmc0 : 1; + u64 l2c : 1; + u64 usb1 : 1; + u64 rad : 1; + u64 usb : 1; + u64 pow : 1; + u64 tim : 1; + u64 pko : 1; + u64 ipd : 1; + u64 reserved_8_8 : 1; + u64 zip : 1; + u64 dfa : 1; + u64 fpa : 1; + u64 key : 1; + u64 npei : 1; + u64 gmx1 : 1; + u64 gmx0 : 1; + u64 mio : 1; + } s; + struct cvmx_npei_rsl_int_blocks_s cn52xx; + struct cvmx_npei_rsl_int_blocks_s cn52xxp1; + struct cvmx_npei_rsl_int_blocks_s cn56xx; + struct cvmx_npei_rsl_int_blocks_s cn56xxp1; +}; + +typedef union cvmx_npei_rsl_int_blocks cvmx_npei_rsl_int_blocks_t; + +/** + * cvmx_npei_scratch_1 + * + * NPEI_SCRATCH_1 = NPEI's Scratch 1 + * + * A general purpose 64 bit register for SW use. + */ +union cvmx_npei_scratch_1 { + u64 u64; + struct cvmx_npei_scratch_1_s { + u64 data : 64; + } s; + struct cvmx_npei_scratch_1_s cn52xx; + struct cvmx_npei_scratch_1_s cn52xxp1; + struct cvmx_npei_scratch_1_s cn56xx; + struct cvmx_npei_scratch_1_s cn56xxp1; +}; + +typedef union cvmx_npei_scratch_1 cvmx_npei_scratch_1_t; + +/** + * cvmx_npei_state1 + * + * NPEI_STATE1 = NPEI State 1 + * + * State machines in NPEI. For debug. + */ +union cvmx_npei_state1 { + u64 u64; + struct cvmx_npei_state1_s { + u64 cpl1 : 12; + u64 cpl0 : 12; + u64 arb : 1; + u64 csr : 39; + } s; + struct cvmx_npei_state1_s cn52xx; + struct cvmx_npei_state1_s cn52xxp1; + struct cvmx_npei_state1_s cn56xx; + struct cvmx_npei_state1_s cn56xxp1; +}; + +typedef union cvmx_npei_state1 cvmx_npei_state1_t; + +/** + * cvmx_npei_state2 + * + * NPEI_STATE2 = NPEI State 2 + * + * State machines in NPEI. For debug. + */ +union cvmx_npei_state2 { + u64 u64; + struct cvmx_npei_state2_s { + u64 reserved_48_63 : 16; + u64 npei : 1; + u64 rac : 1; + u64 csm1 : 15; + u64 csm0 : 15; + u64 nnp0 : 8; + u64 nnd : 8; + } s; + struct cvmx_npei_state2_s cn52xx; + struct cvmx_npei_state2_s cn52xxp1; + struct cvmx_npei_state2_s cn56xx; + struct cvmx_npei_state2_s cn56xxp1; +}; + +typedef union cvmx_npei_state2 cvmx_npei_state2_t; + +/** + * cvmx_npei_state3 + * + * NPEI_STATE3 = NPEI State 3 + * + * State machines in NPEI. For debug. + */ +union cvmx_npei_state3 { + u64 u64; + struct cvmx_npei_state3_s { + u64 reserved_56_63 : 8; + u64 psm1 : 15; + u64 psm0 : 15; + u64 nsm1 : 13; + u64 nsm0 : 13; + } s; + struct cvmx_npei_state3_s cn52xx; + struct cvmx_npei_state3_s cn52xxp1; + struct cvmx_npei_state3_s cn56xx; + struct cvmx_npei_state3_s cn56xxp1; +}; + +typedef union cvmx_npei_state3 cvmx_npei_state3_t; + +/** + * cvmx_npei_win_rd_addr + * + * NPEI_WIN_RD_ADDR = NPEI Window Read Address Register + * + * The address to be read when the NPEI_WIN_RD_DATA register is read. + */ +union cvmx_npei_win_rd_addr { + u64 u64; + struct cvmx_npei_win_rd_addr_s { + u64 reserved_51_63 : 13; + u64 ld_cmd : 2; + u64 iobit : 1; + u64 rd_addr : 48; + } s; + struct cvmx_npei_win_rd_addr_s cn52xx; + struct cvmx_npei_win_rd_addr_s cn52xxp1; + struct cvmx_npei_win_rd_addr_s cn56xx; + struct cvmx_npei_win_rd_addr_s cn56xxp1; +}; + +typedef union cvmx_npei_win_rd_addr cvmx_npei_win_rd_addr_t; + +/** + * cvmx_npei_win_rd_data + * + * NPEI_WIN_RD_DATA = NPEI Window Read Data Register + * + * Reading this register causes a window read operation to take place. + * Address read is that contained in the NPEI_WIN_RD_ADDR + * register. + */ +union cvmx_npei_win_rd_data { + u64 u64; + struct cvmx_npei_win_rd_data_s { + u64 rd_data : 64; + } s; + struct cvmx_npei_win_rd_data_s cn52xx; + struct cvmx_npei_win_rd_data_s cn52xxp1; + struct cvmx_npei_win_rd_data_s cn56xx; + struct cvmx_npei_win_rd_data_s cn56xxp1; +}; + +typedef union cvmx_npei_win_rd_data cvmx_npei_win_rd_data_t; + +/** + * cvmx_npei_win_wr_addr + * + * NPEI_WIN_WR_ADDR = NPEI Window Write Address Register + * + * Contains the address to be writen to when a write operation is started by + * writing the + * NPEI_WIN_WR_DATA register (see below). + * + * Notes: + * Even though address bit [2] can be set, it should always be kept to '0'. + * + */ +union cvmx_npei_win_wr_addr { + u64 u64; + struct cvmx_npei_win_wr_addr_s { + u64 reserved_49_63 : 15; + u64 iobit : 1; + u64 wr_addr : 46; + u64 reserved_0_1 : 2; + } s; + struct cvmx_npei_win_wr_addr_s cn52xx; + struct cvmx_npei_win_wr_addr_s cn52xxp1; + struct cvmx_npei_win_wr_addr_s cn56xx; + struct cvmx_npei_win_wr_addr_s cn56xxp1; +}; + +typedef union cvmx_npei_win_wr_addr cvmx_npei_win_wr_addr_t; + +/** + * cvmx_npei_win_wr_data + * + * NPEI_WIN_WR_DATA = NPEI Window Write Data Register + * + * Contains the data to write to the address located in the NPEI_WIN_WR_ADDR + * Register. + * Writing the least-significant-byte of this register will cause a write + * operation to take place. + */ +union cvmx_npei_win_wr_data { + u64 u64; + struct cvmx_npei_win_wr_data_s { + u64 wr_data : 64; + } s; + struct cvmx_npei_win_wr_data_s cn52xx; + struct cvmx_npei_win_wr_data_s cn52xxp1; + struct cvmx_npei_win_wr_data_s cn56xx; + struct cvmx_npei_win_wr_data_s cn56xxp1; +}; + +typedef union cvmx_npei_win_wr_data cvmx_npei_win_wr_data_t; + +/** + * cvmx_npei_win_wr_mask + * + * NPEI_WIN_WR_MASK = NPEI Window Write Mask Register + * + * Contains the mask for the data in the NPEI_WIN_WR_DATA Register. + */ +union cvmx_npei_win_wr_mask { + u64 u64; + struct cvmx_npei_win_wr_mask_s { + u64 reserved_8_63 : 56; + u64 wr_mask : 8; + } s; + struct cvmx_npei_win_wr_mask_s cn52xx; + struct cvmx_npei_win_wr_mask_s cn52xxp1; + struct cvmx_npei_win_wr_mask_s cn56xx; + struct cvmx_npei_win_wr_mask_s cn56xxp1; +}; + +typedef union cvmx_npei_win_wr_mask cvmx_npei_win_wr_mask_t; + +/** + * cvmx_npei_window_ctl + * + * NPEI_WINDOW_CTL = NPEI's Window Control + * + * The name of this register is misleading. The timeout value is used for BAR0 + * access from PCIE0 and PCIE1. + * Any access to the regigisters on the RML will timeout as 0xFFFF clock cycle. + * At time of timeout the next + * RML access will start, and interrupt will be set, and in the case of reads + * no data will be returned. + * + * The value of this register should be set to a minimum of 0x200000 to ensure + * that a timeout to an RML register + * occurs on the RML 0xFFFF timer before the timeout for a BAR0 access from + * the PCIE#. + */ +union cvmx_npei_window_ctl { + u64 u64; + struct cvmx_npei_window_ctl_s { + u64 reserved_32_63 : 32; + u64 time : 32; + } s; + struct cvmx_npei_window_ctl_s cn52xx; + struct cvmx_npei_window_ctl_s cn52xxp1; + struct cvmx_npei_window_ctl_s cn56xx; + struct cvmx_npei_window_ctl_s cn56xxp1; +}; + +typedef union cvmx_npei_window_ctl cvmx_npei_window_ctl_t; + +#endif diff --git a/arch/mips/mach-octeon/include/mach/cvmx-pcsxx-defs.h b/arch/mips/mach-octeon/include/mach/cvmx-pcsxx-defs.h new file mode 100644 index 0000000..e16a4c4 --- /dev/null +++ b/arch/mips/mach-octeon/include/mach/cvmx-pcsxx-defs.h @@ -0,0 +1,787 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018-2022 Marvell International Ltd. + * + * Configuration and status register (CSR) type definitions for + * Octeon pcsxx. + */ + +#ifndef __CVMX_PCSXX_DEFS_H__ +#define __CVMX_PCSXX_DEFS_H__ + +static inline u64 CVMX_PCSXX_10GBX_STATUS_REG(unsigned long offset) +{ + switch (cvmx_get_octeon_family()) { + case OCTEON_CN66XX & OCTEON_FAMILY_MASK: + case OCTEON_CN61XX & OCTEON_FAMILY_MASK: + return 0x00011800B0000828ull + (offset) * 0x8000000ull; + case OCTEON_CN70XX & OCTEON_FAMILY_MASK: + case OCTEON_CN63XX & OCTEON_FAMILY_MASK: + return 0x00011800B0000828ull + (offset) * 0x8000000ull; + case OCTEON_CN68XX & OCTEON_FAMILY_MASK: + return 0x00011800B0000828ull + (offset) * 0x1000000ull; + } + return 0x00011800B0000828ull + (offset) * 0x8000000ull; +} + +static inline u64 CVMX_PCSXX_BIST_STATUS_REG(unsigned long offset) +{ + switch (cvmx_get_octeon_family()) { + case OCTEON_CN66XX & OCTEON_FAMILY_MASK: + case OCTEON_CN61XX & OCTEON_FAMILY_MASK: + return 0x00011800B0000870ull + (offset) * 0x8000000ull; + case OCTEON_CN70XX & OCTEON_FAMILY_MASK: + case OCTEON_CN63XX & OCTEON_FAMILY_MASK: + return 0x00011800B0000870ull + (offset) * 0x8000000ull; + case OCTEON_CN68XX & OCTEON_FAMILY_MASK: + return 0x00011800B0000870ull + (offset) * 0x1000000ull; + } + return 0x00011800B0000870ull + (offset) * 0x8000000ull; +} + +static inline u64 CVMX_PCSXX_BIT_LOCK_STATUS_REG(unsigned long offset) +{ + switch (cvmx_get_octeon_family()) { + case OCTEON_CN66XX & OCTEON_FAMILY_MASK: + case OCTEON_CN61XX & OCTEON_FAMILY_MASK: + return 0x00011800B0000850ull + (offset) * 0x8000000ull; + case OCTEON_CN70XX & OCTEON_FAMILY_MASK: + case OCTEON_CN63XX & OCTEON_FAMILY_MASK: + return 0x00011800B0000850ull + (offset) * 0x8000000ull; + case OCTEON_CN68XX & OCTEON_FAMILY_MASK: + return 0x00011800B0000850ull + (offset) * 0x1000000ull; + } + return 0x00011800B0000850ull + (offset) * 0x8000000ull; +} + +static inline u64 CVMX_PCSXX_CONTROL1_REG(unsigned long offset) +{ + switch (cvmx_get_octeon_family()) { + case OCTEON_CN66XX & OCTEON_FAMILY_MASK: + case OCTEON_CN61XX & OCTEON_FAMILY_MASK: + return 0x00011800B0000800ull + (offset) * 0x8000000ull; + case OCTEON_CN70XX & OCTEON_FAMILY_MASK: + case OCTEON_CN63XX & OCTEON_FAMILY_MASK: + return 0x00011800B0000800ull + (offset) * 0x8000000ull; + case OCTEON_CN68XX & OCTEON_FAMILY_MASK: + return 0x00011800B0000800ull + (offset) * 0x1000000ull; + } + return 0x00011800B0000800ull + (offset) * 0x8000000ull; +} + +static inline u64 CVMX_PCSXX_CONTROL2_REG(unsigned long offset) +{ + switch (cvmx_get_octeon_family()) { + case OCTEON_CN66XX & OCTEON_FAMILY_MASK: + case OCTEON_CN61XX & OCTEON_FAMILY_MASK: + return 0x00011800B0000818ull + (offset) * 0x8000000ull; + case OCTEON_CN70XX & OCTEON_FAMILY_MASK: + case OCTEON_CN63XX & OCTEON_FAMILY_MASK: + return 0x00011800B0000818ull + (offset) * 0x8000000ull; + case OCTEON_CN68XX & OCTEON_FAMILY_MASK: + return 0x00011800B0000818ull + (offset) * 0x1000000ull; + } + return 0x00011800B0000818ull + (offset) * 0x8000000ull; +} + +static inline u64 CVMX_PCSXX_INT_EN_REG(unsigned long offset) +{ + switch (cvmx_get_octeon_family()) { + case OCTEON_CN66XX & OCTEON_FAMILY_MASK: + case OCTEON_CN61XX & OCTEON_FAMILY_MASK: + return 0x00011800B0000860ull + (offset) * 0x8000000ull; + case OCTEON_CN70XX & OCTEON_FAMILY_MASK: + case OCTEON_CN63XX & OCTEON_FAMILY_MASK: + return 0x00011800B0000860ull + (offset) * 0x8000000ull; + case OCTEON_CN68XX & OCTEON_FAMILY_MASK: + return 0x00011800B0000860ull + (offset) * 0x1000000ull; + } + return 0x00011800B0000860ull + (offset) * 0x8000000ull; +} + +static inline u64 CVMX_PCSXX_INT_REG(unsigned long offset) +{ + switch (cvmx_get_octeon_family()) { + case OCTEON_CN66XX & OCTEON_FAMILY_MASK: + case OCTEON_CN61XX & OCTEON_FAMILY_MASK: + return 0x00011800B0000858ull + (offset) * 0x8000000ull; + case OCTEON_CN70XX & OCTEON_FAMILY_MASK: + case OCTEON_CN63XX & OCTEON_FAMILY_MASK: + return 0x00011800B0000858ull + (offset) * 0x8000000ull; + case OCTEON_CN68XX & OCTEON_FAMILY_MASK: + return 0x00011800B0000858ull + (offset) * 0x1000000ull; + } + return 0x00011800B0000858ull + (offset) * 0x8000000ull; +} + +static inline u64 CVMX_PCSXX_LOG_ANL_REG(unsigned long offset) +{ + switch (cvmx_get_octeon_family()) { + case OCTEON_CN66XX & OCTEON_FAMILY_MASK: + case OCTEON_CN61XX & OCTEON_FAMILY_MASK: + return 0x00011800B0000868ull + (offset) * 0x8000000ull; + case OCTEON_CN70XX & OCTEON_FAMILY_MASK: + case OCTEON_CN63XX & OCTEON_FAMILY_MASK: + return 0x00011800B0000868ull + (offset) * 0x8000000ull; + case OCTEON_CN68XX & OCTEON_FAMILY_MASK: + return 0x00011800B0000868ull + (offset) * 0x1000000ull; + } + return 0x00011800B0000868ull + (offset) * 0x8000000ull; +} + +static inline u64 CVMX_PCSXX_MISC_CTL_REG(unsigned long offset) +{ + switch (cvmx_get_octeon_family()) { + case OCTEON_CN66XX & OCTEON_FAMILY_MASK: + case OCTEON_CN61XX & OCTEON_FAMILY_MASK: + return 0x00011800B0000848ull + (offset) * 0x8000000ull; + case OCTEON_CN70XX & OCTEON_FAMILY_MASK: + case OCTEON_CN63XX & OCTEON_FAMILY_MASK: + return 0x00011800B0000848ull + (offset) * 0x8000000ull; + case OCTEON_CN68XX & OCTEON_FAMILY_MASK: + return 0x00011800B0000848ull + (offset) * 0x1000000ull; + } + return 0x00011800B0000848ull + (offset) * 0x8000000ull; +} + +static inline u64 CVMX_PCSXX_RX_SYNC_STATES_REG(unsigned long offset) +{ + switch (cvmx_get_octeon_family()) { + case OCTEON_CN66XX & OCTEON_FAMILY_MASK: + case OCTEON_CN61XX & OCTEON_FAMILY_MASK: + return 0x00011800B0000838ull + (offset) * 0x8000000ull; + case OCTEON_CN70XX & OCTEON_FAMILY_MASK: + case OCTEON_CN63XX & OCTEON_FAMILY_MASK: + return 0x00011800B0000838ull + (offset) * 0x8000000ull; + case OCTEON_CN68XX & OCTEON_FAMILY_MASK: + return 0x00011800B0000838ull + (offset) * 0x1000000ull; + } + return 0x00011800B0000838ull + (offset) * 0x8000000ull; +} + +#define CVMX_PCSXX_SERDES_CRDT_CNT_REG(offset) (0x00011800B0000880ull) +static inline u64 CVMX_PCSXX_SPD_ABIL_REG(unsigned long offset) +{ + switch (cvmx_get_octeon_family()) { + case OCTEON_CN66XX & OCTEON_FAMILY_MASK: + case OCTEON_CN61XX & OCTEON_FAMILY_MASK: + return 0x00011800B0000810ull + (offset) * 0x8000000ull; + case OCTEON_CN70XX & OCTEON_FAMILY_MASK: + case OCTEON_CN63XX & OCTEON_FAMILY_MASK: + return 0x00011800B0000810ull + (offset) * 0x8000000ull; + case OCTEON_CN68XX & OCTEON_FAMILY_MASK: + return 0x00011800B0000810ull + (offset) * 0x1000000ull; + } + return 0x00011800B0000810ull + (offset) * 0x8000000ull; +} + +static inline u64 CVMX_PCSXX_STATUS1_REG(unsigned long offset) +{ + switch (cvmx_get_octeon_family()) { + case OCTEON_CN66XX & OCTEON_FAMILY_MASK: + case OCTEON_CN61XX & OCTEON_FAMILY_MASK: + return 0x00011800B0000808ull + (offset) * 0x8000000ull; + case OCTEON_CN70XX & OCTEON_FAMILY_MASK: + case OCTEON_CN63XX & OCTEON_FAMILY_MASK: + return 0x00011800B0000808ull + (offset) * 0x8000000ull; + case OCTEON_CN68XX & OCTEON_FAMILY_MASK: + return 0x00011800B0000808ull + (offset) * 0x1000000ull; + } + return 0x00011800B0000808ull + (offset) * 0x8000000ull; +} + +static inline u64 CVMX_PCSXX_STATUS2_REG(unsigned long offset) +{ + switch (cvmx_get_octeon_family()) { + case OCTEON_CN66XX & OCTEON_FAMILY_MASK: + case OCTEON_CN61XX & OCTEON_FAMILY_MASK: + return 0x00011800B0000820ull + (offset) * 0x8000000ull; + case OCTEON_CN70XX & OCTEON_FAMILY_MASK: + case OCTEON_CN63XX & OCTEON_FAMILY_MASK: + return 0x00011800B0000820ull + (offset) * 0x8000000ull; + case OCTEON_CN68XX & OCTEON_FAMILY_MASK: + return 0x00011800B0000820ull + (offset) * 0x1000000ull; + } + return 0x00011800B0000820ull + (offset) * 0x8000000ull; +} + +static inline u64 CVMX_PCSXX_TX_RX_POLARITY_REG(unsigned long offset) +{ + switch (cvmx_get_octeon_family()) { + case OCTEON_CN66XX & OCTEON_FAMILY_MASK: + case OCTEON_CN61XX & OCTEON_FAMILY_MASK: + return 0x00011800B0000840ull + (offset) * 0x8000000ull; + case OCTEON_CN70XX & OCTEON_FAMILY_MASK: + case OCTEON_CN63XX & OCTEON_FAMILY_MASK: + return 0x00011800B0000840ull + (offset) * 0x8000000ull; + case OCTEON_CN68XX & OCTEON_FAMILY_MASK: + return 0x00011800B0000840ull + (offset) * 0x1000000ull; + } + return 0x00011800B0000840ull + (offset) * 0x8000000ull; +} + +static inline u64 CVMX_PCSXX_TX_RX_STATES_REG(unsigned long offset) +{ + switch (cvmx_get_octeon_family()) { + case OCTEON_CN66XX & OCTEON_FAMILY_MASK: + case OCTEON_CN61XX & OCTEON_FAMILY_MASK: + return 0x00011800B0000830ull + (offset) * 0x8000000ull; + case OCTEON_CN70XX & OCTEON_FAMILY_MASK: + case OCTEON_CN63XX & OCTEON_FAMILY_MASK: + return 0x00011800B0000830ull + (offset) * 0x8000000ull; + case OCTEON_CN68XX & OCTEON_FAMILY_MASK: + return 0x00011800B0000830ull + (offset) * 0x1000000ull; + } + return 0x00011800B0000830ull + (offset) * 0x8000000ull; +} + +/** + * cvmx_pcsx#_10gbx_status_reg + * + * PCSX_10GBX_STATUS_REG = 10gbx_status_reg + * + */ +union cvmx_pcsxx_10gbx_status_reg { + u64 u64; + struct cvmx_pcsxx_10gbx_status_reg_s { + u64 reserved_13_63 : 51; + u64 alignd : 1; + u64 pattst : 1; + u64 reserved_4_10 : 7; + u64 l3sync : 1; + u64 l2sync : 1; + u64 l1sync : 1; + u64 l0sync : 1; + } s; + struct cvmx_pcsxx_10gbx_status_reg_s cn52xx; + struct cvmx_pcsxx_10gbx_status_reg_s cn52xxp1; + struct cvmx_pcsxx_10gbx_status_reg_s cn56xx; + struct cvmx_pcsxx_10gbx_status_reg_s cn56xxp1; + struct cvmx_pcsxx_10gbx_status_reg_s cn61xx; + struct cvmx_pcsxx_10gbx_status_reg_s cn63xx; + struct cvmx_pcsxx_10gbx_status_reg_s cn63xxp1; + struct cvmx_pcsxx_10gbx_status_reg_s cn66xx; + struct cvmx_pcsxx_10gbx_status_reg_s cn68xx; + struct cvmx_pcsxx_10gbx_status_reg_s cn68xxp1; + struct cvmx_pcsxx_10gbx_status_reg_s cn70xx; + struct cvmx_pcsxx_10gbx_status_reg_s cn70xxp1; +}; + +typedef union cvmx_pcsxx_10gbx_status_reg cvmx_pcsxx_10gbx_status_reg_t; + +/** + * cvmx_pcsx#_bist_status_reg + * + * PCSX Bist Status Register + * + */ +union cvmx_pcsxx_bist_status_reg { + u64 u64; + struct cvmx_pcsxx_bist_status_reg_s { + u64 reserved_1_63 : 63; + u64 bist_status : 1; + } s; + struct cvmx_pcsxx_bist_status_reg_s cn52xx; + struct cvmx_pcsxx_bist_status_reg_s cn52xxp1; + struct cvmx_pcsxx_bist_status_reg_s cn56xx; + struct cvmx_pcsxx_bist_status_reg_s cn56xxp1; + struct cvmx_pcsxx_bist_status_reg_s cn61xx; + struct cvmx_pcsxx_bist_status_reg_s cn63xx; + struct cvmx_pcsxx_bist_status_reg_s cn63xxp1; + struct cvmx_pcsxx_bist_status_reg_s cn66xx; + struct cvmx_pcsxx_bist_status_reg_s cn68xx; + struct cvmx_pcsxx_bist_status_reg_s cn68xxp1; + struct cvmx_pcsxx_bist_status_reg_s cn70xx; + struct cvmx_pcsxx_bist_status_reg_s cn70xxp1; +}; + +typedef union cvmx_pcsxx_bist_status_reg cvmx_pcsxx_bist_status_reg_t; + +/** + * cvmx_pcsx#_bit_lock_status_reg + * + * PCSX Bit Lock Status Register + * + */ +union cvmx_pcsxx_bit_lock_status_reg { + u64 u64; + struct cvmx_pcsxx_bit_lock_status_reg_s { + u64 reserved_4_63 : 60; + u64 bitlck3 : 1; + u64 bitlck2 : 1; + u64 bitlck1 : 1; + u64 bitlck0 : 1; + } s; + struct cvmx_pcsxx_bit_lock_status_reg_s cn52xx; + struct cvmx_pcsxx_bit_lock_status_reg_s cn52xxp1; + struct cvmx_pcsxx_bit_lock_status_reg_s cn56xx; + struct cvmx_pcsxx_bit_lock_status_reg_s cn56xxp1; + struct cvmx_pcsxx_bit_lock_status_reg_s cn61xx; + struct cvmx_pcsxx_bit_lock_status_reg_s cn63xx; + struct cvmx_pcsxx_bit_lock_status_reg_s cn63xxp1; + struct cvmx_pcsxx_bit_lock_status_reg_s cn66xx; + struct cvmx_pcsxx_bit_lock_status_reg_s cn68xx; + struct cvmx_pcsxx_bit_lock_status_reg_s cn68xxp1; + struct cvmx_pcsxx_bit_lock_status_reg_s cn70xx; + struct cvmx_pcsxx_bit_lock_status_reg_s cn70xxp1; +}; + +typedef union cvmx_pcsxx_bit_lock_status_reg cvmx_pcsxx_bit_lock_status_reg_t; + +/** + * cvmx_pcsx#_control1_reg + * + * NOTE: Logic Analyzer is enabled with LA_EN for the specified PCS lane only. + * PKT_SZ is effective only when LA_EN=1 + * For normal operation(sgmii or 1000Base-X), this bit must be 0. + * See pcsx.csr for xaui logic analyzer mode. + * For full description see document at .../rtl/pcs/readme_logic_analyzer.txt + * + * + * PCSX regs follow IEEE Std 802.3-2005, Section: 45.2.3 + * + * + * PCSX_CONTROL1_REG = Control Register1 + */ +union cvmx_pcsxx_control1_reg { + u64 u64; + struct cvmx_pcsxx_control1_reg_s { + u64 reserved_16_63 : 48; + u64 reset : 1; + u64 loopbck1 : 1; + u64 spdsel1 : 1; + u64 reserved_12_12 : 1; + u64 lo_pwr : 1; + u64 reserved_7_10 : 4; + u64 spdsel0 : 1; + u64 spd : 4; + u64 reserved_0_1 : 2; + } s; + struct cvmx_pcsxx_control1_reg_s cn52xx; + struct cvmx_pcsxx_control1_reg_s cn52xxp1; + struct cvmx_pcsxx_control1_reg_s cn56xx; + struct cvmx_pcsxx_control1_reg_s cn56xxp1; + struct cvmx_pcsxx_control1_reg_s cn61xx; + struct cvmx_pcsxx_control1_reg_s cn63xx; + struct cvmx_pcsxx_control1_reg_s cn63xxp1; + struct cvmx_pcsxx_control1_reg_s cn66xx; + struct cvmx_pcsxx_control1_reg_s cn68xx; + struct cvmx_pcsxx_control1_reg_s cn68xxp1; + struct cvmx_pcsxx_control1_reg_s cn70xx; + struct cvmx_pcsxx_control1_reg_s cn70xxp1; +}; + +typedef union cvmx_pcsxx_control1_reg cvmx_pcsxx_control1_reg_t; + +/** + * cvmx_pcsx#_control2_reg + * + * PCSX_CONTROL2_REG = Control Register2 + * + */ +union cvmx_pcsxx_control2_reg { + u64 u64; + struct cvmx_pcsxx_control2_reg_s { + u64 reserved_2_63 : 62; + u64 type : 2; + } s; + struct cvmx_pcsxx_control2_reg_s cn52xx; + struct cvmx_pcsxx_control2_reg_s cn52xxp1; + struct cvmx_pcsxx_control2_reg_s cn56xx; + struct cvmx_pcsxx_control2_reg_s cn56xxp1; + struct cvmx_pcsxx_control2_reg_s cn61xx; + struct cvmx_pcsxx_control2_reg_s cn63xx; + struct cvmx_pcsxx_control2_reg_s cn63xxp1; + struct cvmx_pcsxx_control2_reg_s cn66xx; + struct cvmx_pcsxx_control2_reg_s cn68xx; + struct cvmx_pcsxx_control2_reg_s cn68xxp1; + struct cvmx_pcsxx_control2_reg_s cn70xx; + struct cvmx_pcsxx_control2_reg_s cn70xxp1; +}; + +typedef union cvmx_pcsxx_control2_reg cvmx_pcsxx_control2_reg_t; + +/** + * cvmx_pcsx#_int_en_reg + * + * PCSX Interrupt Enable Register + * + */ +union cvmx_pcsxx_int_en_reg { + u64 u64; + struct cvmx_pcsxx_int_en_reg_s { + u64 reserved_7_63 : 57; + u64 dbg_sync_en : 1; + u64 algnlos_en : 1; + u64 synlos_en : 1; + u64 bitlckls_en : 1; + u64 rxsynbad_en : 1; + u64 rxbad_en : 1; + u64 txflt_en : 1; + } s; + struct cvmx_pcsxx_int_en_reg_cn52xx { + u64 reserved_6_63 : 58; + u64 algnlos_en : 1; + u64 synlos_en : 1; + u64 bitlckls_en : 1; + u64 rxsynbad_en : 1; + u64 rxbad_en : 1; + u64 txflt_en : 1; + } cn52xx; + struct cvmx_pcsxx_int_en_reg_cn52xx cn52xxp1; + struct cvmx_pcsxx_int_en_reg_cn52xx cn56xx; + struct cvmx_pcsxx_int_en_reg_cn52xx cn56xxp1; + struct cvmx_pcsxx_int_en_reg_s cn61xx; + struct cvmx_pcsxx_int_en_reg_s cn63xx; + struct cvmx_pcsxx_int_en_reg_s cn63xxp1; + struct cvmx_pcsxx_int_en_reg_s cn66xx; + struct cvmx_pcsxx_int_en_reg_s cn68xx; + struct cvmx_pcsxx_int_en_reg_s cn68xxp1; + struct cvmx_pcsxx_int_en_reg_s cn70xx; + struct cvmx_pcsxx_int_en_reg_s cn70xxp1; +}; + +typedef union cvmx_pcsxx_int_en_reg cvmx_pcsxx_int_en_reg_t; + +/** + * cvmx_pcsx#_int_reg + * + * PCSX Interrupt Register + * Note: DBG_SYNC is a edge triggered interrupt. When set it indicates PCS Synchronization state + * machine in + * Figure 48-7 state diagram in IEEE Std 802.3-2005 changes state SYNC_ACQUIRED_1 to + * SYNC_ACQUIRED_2 + * indicating an invalid code group was received on one of the 4 receive lanes. + * This interrupt should be always disabled and used only for link problem debugging help. + */ +union cvmx_pcsxx_int_reg { + u64 u64; + struct cvmx_pcsxx_int_reg_s { + u64 reserved_7_63 : 57; + u64 dbg_sync : 1; + u64 algnlos : 1; + u64 synlos : 1; + u64 bitlckls : 1; + u64 rxsynbad : 1; + u64 rxbad : 1; + u64 txflt : 1; + } s; + struct cvmx_pcsxx_int_reg_cn52xx { + u64 reserved_6_63 : 58; + u64 algnlos : 1; + u64 synlos : 1; + u64 bitlckls : 1; + u64 rxsynbad : 1; + u64 rxbad : 1; + u64 txflt : 1; + } cn52xx; + struct cvmx_pcsxx_int_reg_cn52xx cn52xxp1; + struct cvmx_pcsxx_int_reg_cn52xx cn56xx; + struct cvmx_pcsxx_int_reg_cn52xx cn56xxp1; + struct cvmx_pcsxx_int_reg_s cn61xx; + struct cvmx_pcsxx_int_reg_s cn63xx; + struct cvmx_pcsxx_int_reg_s cn63xxp1; + struct cvmx_pcsxx_int_reg_s cn66xx; + struct cvmx_pcsxx_int_reg_s cn68xx; + struct cvmx_pcsxx_int_reg_s cn68xxp1; + struct cvmx_pcsxx_int_reg_s cn70xx; + struct cvmx_pcsxx_int_reg_s cn70xxp1; +}; + +typedef union cvmx_pcsxx_int_reg cvmx_pcsxx_int_reg_t; + +/** + * cvmx_pcsx#_log_anl_reg + * + * PCSX Logic Analyzer Register + * NOTE: Logic Analyzer is enabled with LA_EN for xaui only. PKT_SZ is effective only when + * LA_EN=1 + * For normal operation(xaui), this bit must be 0. The dropped lane is used to send rxc[3:0]. + * See pcs.csr for sgmii/1000Base-X logic analyzer mode. + * For full description see document at .../rtl/pcs/readme_logic_analyzer.txt + */ +union cvmx_pcsxx_log_anl_reg { + u64 u64; + struct cvmx_pcsxx_log_anl_reg_s { + u64 reserved_7_63 : 57; + u64 enc_mode : 1; + u64 drop_ln : 2; + u64 lafifovfl : 1; + u64 la_en : 1; + u64 pkt_sz : 2; + } s; + struct cvmx_pcsxx_log_anl_reg_s cn52xx; + struct cvmx_pcsxx_log_anl_reg_s cn52xxp1; + struct cvmx_pcsxx_log_anl_reg_s cn56xx; + struct cvmx_pcsxx_log_anl_reg_s cn56xxp1; + struct cvmx_pcsxx_log_anl_reg_s cn61xx; + struct cvmx_pcsxx_log_anl_reg_s cn63xx; + struct cvmx_pcsxx_log_anl_reg_s cn63xxp1; + struct cvmx_pcsxx_log_anl_reg_s cn66xx; + struct cvmx_pcsxx_log_anl_reg_s cn68xx; + struct cvmx_pcsxx_log_anl_reg_s cn68xxp1; + struct cvmx_pcsxx_log_anl_reg_s cn70xx; + struct cvmx_pcsxx_log_anl_reg_s cn70xxp1; +}; + +typedef union cvmx_pcsxx_log_anl_reg cvmx_pcsxx_log_anl_reg_t; + +/** + * cvmx_pcsx#_misc_ctl_reg + * + * PCSX Misc Control Register + * LN_SWAP for XAUI is to simplify interconnection layout between devices + */ +union cvmx_pcsxx_misc_ctl_reg { + u64 u64; + struct cvmx_pcsxx_misc_ctl_reg_s { + u64 reserved_4_63 : 60; + u64 tx_swap : 1; + u64 rx_swap : 1; + u64 xaui : 1; + u64 gmxeno : 1; + } s; + struct cvmx_pcsxx_misc_ctl_reg_s cn52xx; + struct cvmx_pcsxx_misc_ctl_reg_s cn52xxp1; + struct cvmx_pcsxx_misc_ctl_reg_s cn56xx; + struct cvmx_pcsxx_misc_ctl_reg_s cn56xxp1; + struct cvmx_pcsxx_misc_ctl_reg_s cn61xx; + struct cvmx_pcsxx_misc_ctl_reg_s cn63xx; + struct cvmx_pcsxx_misc_ctl_reg_s cn63xxp1; + struct cvmx_pcsxx_misc_ctl_reg_s cn66xx; + struct cvmx_pcsxx_misc_ctl_reg_s cn68xx; + struct cvmx_pcsxx_misc_ctl_reg_s cn68xxp1; + struct cvmx_pcsxx_misc_ctl_reg_s cn70xx; + struct cvmx_pcsxx_misc_ctl_reg_s cn70xxp1; +}; + +typedef union cvmx_pcsxx_misc_ctl_reg cvmx_pcsxx_misc_ctl_reg_t; + +/** + * cvmx_pcsx#_rx_sync_states_reg + * + * PCSX_RX_SYNC_STATES_REG = Receive Sync States Register + * + */ +union cvmx_pcsxx_rx_sync_states_reg { + u64 u64; + struct cvmx_pcsxx_rx_sync_states_reg_s { + u64 reserved_16_63 : 48; + u64 sync3st : 4; + u64 sync2st : 4; + u64 sync1st : 4; + u64 sync0st : 4; + } s; + struct cvmx_pcsxx_rx_sync_states_reg_s cn52xx; + struct cvmx_pcsxx_rx_sync_states_reg_s cn52xxp1; + struct cvmx_pcsxx_rx_sync_states_reg_s cn56xx; + struct cvmx_pcsxx_rx_sync_states_reg_s cn56xxp1; + struct cvmx_pcsxx_rx_sync_states_reg_s cn61xx; + struct cvmx_pcsxx_rx_sync_states_reg_s cn63xx; + struct cvmx_pcsxx_rx_sync_states_reg_s cn63xxp1; + struct cvmx_pcsxx_rx_sync_states_reg_s cn66xx; + struct cvmx_pcsxx_rx_sync_states_reg_s cn68xx; + struct cvmx_pcsxx_rx_sync_states_reg_s cn68xxp1; + struct cvmx_pcsxx_rx_sync_states_reg_s cn70xx; + struct cvmx_pcsxx_rx_sync_states_reg_s cn70xxp1; +}; + +typedef union cvmx_pcsxx_rx_sync_states_reg cvmx_pcsxx_rx_sync_states_reg_t; + +/** + * cvmx_pcsx#_serdes_crdt_cnt_reg + * + * PCSX SERDES Credit Count + * + */ +union cvmx_pcsxx_serdes_crdt_cnt_reg { + u64 u64; + struct cvmx_pcsxx_serdes_crdt_cnt_reg_s { + u64 reserved_5_63 : 59; + u64 cnt : 5; + } s; + struct cvmx_pcsxx_serdes_crdt_cnt_reg_s cn70xx; + struct cvmx_pcsxx_serdes_crdt_cnt_reg_s cn70xxp1; +}; + +typedef union cvmx_pcsxx_serdes_crdt_cnt_reg cvmx_pcsxx_serdes_crdt_cnt_reg_t; + +/** + * cvmx_pcsx#_spd_abil_reg + * + * PCSX_SPD_ABIL_REG = Speed ability register + * + */ +union cvmx_pcsxx_spd_abil_reg { + u64 u64; + struct cvmx_pcsxx_spd_abil_reg_s { + u64 reserved_2_63 : 62; + u64 tenpasst : 1; + u64 tengb : 1; + } s; + struct cvmx_pcsxx_spd_abil_reg_s cn52xx; + struct cvmx_pcsxx_spd_abil_reg_s cn52xxp1; + struct cvmx_pcsxx_spd_abil_reg_s cn56xx; + struct cvmx_pcsxx_spd_abil_reg_s cn56xxp1; + struct cvmx_pcsxx_spd_abil_reg_s cn61xx; + struct cvmx_pcsxx_spd_abil_reg_s cn63xx; + struct cvmx_pcsxx_spd_abil_reg_s cn63xxp1; + struct cvmx_pcsxx_spd_abil_reg_s cn66xx; + struct cvmx_pcsxx_spd_abil_reg_s cn68xx; + struct cvmx_pcsxx_spd_abil_reg_s cn68xxp1; + struct cvmx_pcsxx_spd_abil_reg_s cn70xx; + struct cvmx_pcsxx_spd_abil_reg_s cn70xxp1; +}; + +typedef union cvmx_pcsxx_spd_abil_reg cvmx_pcsxx_spd_abil_reg_t; + +/** + * cvmx_pcsx#_status1_reg + * + * PCSX_STATUS1_REG = Status Register1 + * + */ +union cvmx_pcsxx_status1_reg { + u64 u64; + struct cvmx_pcsxx_status1_reg_s { + u64 reserved_8_63 : 56; + u64 flt : 1; + u64 reserved_3_6 : 4; + u64 rcv_lnk : 1; + u64 lpable : 1; + u64 reserved_0_0 : 1; + } s; + struct cvmx_pcsxx_status1_reg_s cn52xx; + struct cvmx_pcsxx_status1_reg_s cn52xxp1; + struct cvmx_pcsxx_status1_reg_s cn56xx; + struct cvmx_pcsxx_status1_reg_s cn56xxp1; + struct cvmx_pcsxx_status1_reg_s cn61xx; + struct cvmx_pcsxx_status1_reg_s cn63xx; + struct cvmx_pcsxx_status1_reg_s cn63xxp1; + struct cvmx_pcsxx_status1_reg_s cn66xx; + struct cvmx_pcsxx_status1_reg_s cn68xx; + struct cvmx_pcsxx_status1_reg_s cn68xxp1; + struct cvmx_pcsxx_status1_reg_s cn70xx; + struct cvmx_pcsxx_status1_reg_s cn70xxp1; +}; + +typedef union cvmx_pcsxx_status1_reg cvmx_pcsxx_status1_reg_t; + +/** + * cvmx_pcsx#_status2_reg + * + * PCSX_STATUS2_REG = Status Register2 + * + */ +union cvmx_pcsxx_status2_reg { + u64 u64; + struct cvmx_pcsxx_status2_reg_s { + u64 reserved_16_63 : 48; + u64 dev : 2; + u64 reserved_12_13 : 2; + u64 xmtflt : 1; + u64 rcvflt : 1; + u64 reserved_3_9 : 7; + u64 tengb_w : 1; + u64 tengb_x : 1; + u64 tengb_r : 1; + } s; + struct cvmx_pcsxx_status2_reg_s cn52xx; + struct cvmx_pcsxx_status2_reg_s cn52xxp1; + struct cvmx_pcsxx_status2_reg_s cn56xx; + struct cvmx_pcsxx_status2_reg_s cn56xxp1; + struct cvmx_pcsxx_status2_reg_s cn61xx; + struct cvmx_pcsxx_status2_reg_s cn63xx; + struct cvmx_pcsxx_status2_reg_s cn63xxp1; + struct cvmx_pcsxx_status2_reg_s cn66xx; + struct cvmx_pcsxx_status2_reg_s cn68xx; + struct cvmx_pcsxx_status2_reg_s cn68xxp1; + struct cvmx_pcsxx_status2_reg_s cn70xx; + struct cvmx_pcsxx_status2_reg_s cn70xxp1; +}; + +typedef union cvmx_pcsxx_status2_reg cvmx_pcsxx_status2_reg_t; + +/** + * cvmx_pcsx#_tx_rx_polarity_reg + * + * RX lane polarity vector [3:0] = XOR_RXPLRT<9:6> ^ [4[RXPLRT<1>]]; + * TX lane polarity vector [3:0] = XOR_TXPLRT<5:2> ^ [4[TXPLRT<0>]]; + * In short keep <1:0> to 2'b00, and use <5:2> and <9:6> fields to define per lane polarities + */ +union cvmx_pcsxx_tx_rx_polarity_reg { + u64 u64; + struct cvmx_pcsxx_tx_rx_polarity_reg_s { + u64 reserved_10_63 : 54; + u64 xor_rxplrt : 4; + u64 xor_txplrt : 4; + u64 rxplrt : 1; + u64 txplrt : 1; + } s; + struct cvmx_pcsxx_tx_rx_polarity_reg_s cn52xx; + struct cvmx_pcsxx_tx_rx_polarity_reg_cn52xxp1 { + u64 reserved_2_63 : 62; + u64 rxplrt : 1; + u64 txplrt : 1; + } cn52xxp1; + struct cvmx_pcsxx_tx_rx_polarity_reg_s cn56xx; + struct cvmx_pcsxx_tx_rx_polarity_reg_cn52xxp1 cn56xxp1; + struct cvmx_pcsxx_tx_rx_polarity_reg_s cn61xx; + struct cvmx_pcsxx_tx_rx_polarity_reg_s cn63xx; + struct cvmx_pcsxx_tx_rx_polarity_reg_s cn63xxp1; + struct cvmx_pcsxx_tx_rx_polarity_reg_s cn66xx; + struct cvmx_pcsxx_tx_rx_polarity_reg_s cn68xx; + struct cvmx_pcsxx_tx_rx_polarity_reg_s cn68xxp1; + struct cvmx_pcsxx_tx_rx_polarity_reg_s cn70xx; + struct cvmx_pcsxx_tx_rx_polarity_reg_s cn70xxp1; +}; + +typedef union cvmx_pcsxx_tx_rx_polarity_reg cvmx_pcsxx_tx_rx_polarity_reg_t; + +/** + * cvmx_pcsx#_tx_rx_states_reg + * + * PCSX_TX_RX_STATES_REG = Transmit Receive States Register + * + */ +union cvmx_pcsxx_tx_rx_states_reg { + u64 u64; + struct cvmx_pcsxx_tx_rx_states_reg_s { + u64 reserved_14_63 : 50; + u64 term_err : 1; + u64 syn3bad : 1; + u64 syn2bad : 1; + u64 syn1bad : 1; + u64 syn0bad : 1; + u64 rxbad : 1; + u64 algn_st : 3; + u64 rx_st : 2; + u64 tx_st : 3; + } s; + struct cvmx_pcsxx_tx_rx_states_reg_s cn52xx; + struct cvmx_pcsxx_tx_rx_states_reg_cn52xxp1 { + u64 reserved_13_63 : 51; + u64 syn3bad : 1; + u64 syn2bad : 1; + u64 syn1bad : 1; + u64 syn0bad : 1; + u64 rxbad : 1; + u64 algn_st : 3; + u64 rx_st : 2; + u64 tx_st : 3; + } cn52xxp1; + struct cvmx_pcsxx_tx_rx_states_reg_s cn56xx; + struct cvmx_pcsxx_tx_rx_states_reg_cn52xxp1 cn56xxp1; + struct cvmx_pcsxx_tx_rx_states_reg_s cn61xx; + struct cvmx_pcsxx_tx_rx_states_reg_s cn63xx; + struct cvmx_pcsxx_tx_rx_states_reg_s cn63xxp1; + struct cvmx_pcsxx_tx_rx_states_reg_s cn66xx; + struct cvmx_pcsxx_tx_rx_states_reg_s cn68xx; + struct cvmx_pcsxx_tx_rx_states_reg_s cn68xxp1; + struct cvmx_pcsxx_tx_rx_states_reg_s cn70xx; + struct cvmx_pcsxx_tx_rx_states_reg_s cn70xxp1; +}; + +typedef union cvmx_pcsxx_tx_rx_states_reg cvmx_pcsxx_tx_rx_states_reg_t; + +#endif diff --git a/arch/mips/mach-octeon/include/mach/cvmx-pki-cluster.h b/arch/mips/mach-octeon/include/mach/cvmx-pki-cluster.h new file mode 100644 index 0000000..4d5a9d4 --- /dev/null +++ b/arch/mips/mach-octeon/include/mach/cvmx-pki-cluster.h @@ -0,0 +1,343 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018-2022 Marvell International Ltd. + */ + +/* L4_PORT_CHECK_DISABLE_LF tag */ +/* This file is autogenerated from ipemainc.elf */ +const int cvmx_pki_cluster_code_length = 997; +const u64 cvmx_pki_cluster_code_default[] = { + 0x000000000a000000ull, 0x0000413a68024070ull, 0x0000813800200020ull, + 0x900081b800200020ull, 0x0004da00ffff0001ull, 0x000455ab68010b0eull, + 0x00045fba46010000ull, 0x9046898120002000ull, 0x0004418068010028ull, + 0x90665300680100f0ull, 0x0004413f68004070ull, 0x00065380680100f0ull, + 0x00045a346803a0f0ull, 0x000401b448000001ull, 0x00045cb968030870ull, + 0x0007debd00100010ull, 0x0000813b80008000ull, 0x000441bb68004070ull, + 0xd001c00000000000ull, 0xd021c00000000000ull, 0x00045f80680100f0ull, + 0x0004c639ff000200ull, 0x0004403f72010000ull, 0x0001c00000000000ull, + 0x0001c00000000000ull, 0x000041ba68034078ull, 0x0000512268030870ull, + 0x000041bc68034070ull, 0x00005d3a68030870ull, 0x00045cb942080000ull, + 0x0004552a4e09312dull, 0x00045cb968082868ull, 0x0004410246090000ull, + 0x0000813800800080ull, 0x000401a486000005ull, 0x000615ab74000123ull, + 0x0007122448000004ull, 0x0000813901000000ull, 0x000481b800010001ull, + 0x000685b800020002ull, 0xa006823800010001ull, 0x0006c639ff000400ull, + 0x00085f3e68010a00ull, 0xa0885f3e68010f01ull, 0x00085f3e68010405ull, + 0x00085f3e68010906ull, 0xa0485f3e68010e07ull, 0xa061c00000000000ull, + 0xa4085f3e68010b28ull, 0xa421c00000000000ull, 0x00095f3e68010940ull, + 0xa066403e72010000ull, 0x000941be68034039ull, 0x00085f3e68010305ull, + 0xa4685f3e68010028ull, 0x00095f3e68030030ull, 0x00095f3e68010416ull, + 0x0001c00000000000ull, 0x00065cb942080000ull, 0xa046552a4e09312dull, + 0xa446c639ff000500ull, 0x0006debd00010001ull, 0x0006403e72010001ull, + 0x0001c00000000000ull, 0x0001c00000000000ull, 0x0001c00000000000ull, + 0x0001c00000000000ull, 0x0001c00000000000ull, 0x0001c00000000000ull, + 0x0001c00000000000ull, 0x00065cb942080000ull, 0x0006552a4e09312dull, + 0x00065cb968082868ull, 0x0006410246090000ull, 0x9060813901000000ull, + 0x0004c639ff000800ull, 0x0004400072010000ull, 0x0001c00000000000ull, + 0x0001c00000000000ull, 0x0001c00000000000ull, 0x0001c00000000000ull, + 0x0001c00000000000ull, 0x0001c00000000000ull, 0x00045cb942080000ull, + 0x9084552a4e09312dull, 0x90a4c639ff000900ull, 0x00045f80680100f0ull, + 0x0004403f72010001ull, 0x0001c00000000000ull, 0x0001c00000000000ull, + 0x0001c00000000000ull, 0x0001c00000000000ull, 0x0001c00000000000ull, + 0x0001c00000000000ull, 0x00045cb942080000ull, 0x9004552a4e09312dull, + 0x0004c639ff000a00ull, 0x0004400072010000ull, 0x00048181ff00ff00ull, + 0x0007820101000100ull, 0x0006898100ff00ffull, 0x00048301ffff0180ull, + 0x0008d5ab10001000ull, 0x0004d4a900010001ull, 0x0001c00000000000ull, + 0x00045cb942080000ull, 0x9024552a4e09312dull, 0x0004c639ff000b00ull, + 0x90445f80680100f0ull, 0x000459b368020070ull, 0x000401024000000cull, + 0x0006823fffffffffull, 0x00088281ffffffffull, 0x000ad5ab20002000ull, + 0x0001c00000000000ull, 0x0001c00000000000ull, 0x0001c00000000000ull, + 0x0001c00000000000ull, 0x0001c00000000000ull, 0x0001c00000000000ull, + 0x0004403f72010001ull, 0x0001c00000000000ull, 0x0001c00000000000ull, + 0x0001c00000000000ull, 0x000c8b3fffffc200ull, 0x000c8b01ffff0001ull, + 0x000ddebd00020002ull, 0x00045cb942080000ull, 0x0004552a4e09312dull, + 0x00045cb968082868ull, 0x0004410246090000ull, 0x0000813901000000ull, + 0x000481b800080008ull, 0x9846c639ff001200ull, 0x9861c00000000000ull, + 0x00064180680100f0ull, 0x0006400372010000ull, 0x0001c00000000000ull, + 0x0001c00000000000ull, 0x0001c00000000000ull, 0x0001c00000000000ull, + 0x0001c00000000000ull, 0x0001c00000000000ull, 0x000683891f000200ull, + 0x000ed52a00800080ull, 0x000e5e3c68020070ull, 0x00065cb942080000ull, + 0x0006552a4e09312dull, 0x00065cb968082868ull, 0x0006410246090000ull, + 0x0000813d00020002ull, 0x0004893901000000ull, 0x9004893800040004ull, + 0x9024c639ff001300ull, 0x00044180680100f0ull, 0x9044400372010001ull, + 0x0001c00000000000ull, 0x00045f3e68010044ull, 0x0004debd00040004ull, + 0x0001c00000000000ull, 0x0001c00000000000ull, 0x0001c00000000000ull, + 0x000483891f000200ull, 0x000ed52a00800080ull, 0x000e5e3c68020070ull, + 0x00045cb942080000ull, 0x0004552a4e09312dull, 0x00045cb968082868ull, + 0x0004410246090000ull, 0x000581b902000000ull, 0x9826c639ff001800ull, + 0x9801c00000000000ull, 0x00064180680100f0ull, 0x0006400172030000ull, + 0x0001c00000000000ull, 0x0001c00000000000ull, 0x0001c00000000000ull, + 0x0001c00000000000ull, 0x0001c00000000000ull, 0x0001c00000000000ull, + 0x000682091f000200ull, 0x000883aa00800080ull, 0x000ed52a00400040ull, + 0x000e5e3c68020870ull, 0x000fd52a00800080ull, 0x000f5e3c68020070ull, + 0x000983891f000000ull, 0x000f54a968090148ull, 0x000f59b368020870ull, + 0x00065cb942080000ull, 0x0006552a4e09312dull, 0x00065cb968082868ull, + 0x0006410246090000ull, 0x000081b902000000ull, 0x9826c639ff001900ull, + 0x9801c00000000000ull, 0x00064180680100f0ull, 0x0006400172030001ull, + 0x0001c00000000000ull, 0x0001c00000000000ull, 0x0001c00000000000ull, + 0x0001c00000000000ull, 0x0001c00000000000ull, 0x0001c00000000000ull, + 0x000682091f000200ull, 0x000883aa00800080ull, 0x000ed52a00400040ull, + 0x000e5e3c68020870ull, 0x000fd52a00800080ull, 0x000f5e3c68020070ull, + 0x000983891f000000ull, 0x000f54a968090148ull, 0x000f59b368020870ull, + 0x00065cb942080000ull, 0x0006552a4e09312dull, 0x00065cb968082868ull, + 0x0006410246090000ull, 0x000081b902000000ull, 0x9826c639ff001a00ull, + 0x9801c00000000000ull, 0x00064180680100f0ull, 0x0006400172030000ull, + 0x0001c00000000000ull, 0x0001c00000000000ull, 0x0001c00000000000ull, + 0x0001c00000000000ull, 0x0001c00000000000ull, 0x0001c00000000000ull, + 0x000682091f000200ull, 0x000883aa00800080ull, 0x000ed52a00400040ull, + 0x000e5e3c68020870ull, 0x000fd52a00800080ull, 0x000f5e3c68020070ull, + 0x000983891f000000ull, 0x000f54a968090148ull, 0x000f59b368020870ull, + 0x00065cb942080000ull, 0x0006552a4e09312dull, 0x00065cb968082868ull, + 0x0006410246090000ull, 0x000081b902000000ull, 0x9826c639ff001b00ull, + 0x9801c00000000000ull, 0x00064180680100f0ull, 0x0006400172030001ull, + 0x0001c00000000000ull, 0x0001c00000000000ull, 0x0001c00000000000ull, + 0x0001c00000000000ull, 0x0001c00000000000ull, 0x0001c00000000000ull, + 0x000682091f000200ull, 0x000883aa00800080ull, 0x000ed52a00400040ull, + 0x000e5e3c68020870ull, 0x000fd52a00800080ull, 0x000f5e3c68020070ull, + 0x000983891f000000ull, 0x000f54a968090148ull, 0x000f59b368020870ull, + 0x00065cb942080000ull, 0x0006552a4e09312dull, 0x00065cb968082868ull, + 0x0006410246090000ull, 0x9000813902000000ull, 0x000481b800400040ull, + 0x00068981ffff8847ull, 0x00068581ffff8848ull, 0x0006debd00080008ull, + 0x0006c639ff001e00ull, 0x0006010240000002ull, 0x9801c00000000000ull, + 0x9821c00000000000ull, 0x00065f80680100f0ull, 0x0006403f72010000ull, + 0x0001c00000000000ull, 0x0001c00000000000ull, 0x0001c00000000000ull, + 0x0001c00000000000ull, 0x0001c00000000000ull, 0x0001c00000000000ull, + 0x00065cb942080000ull, 0x0006552a4e09312dull, 0x00065cb968082868ull, + 0x0006010240000004ull, 0x0006823902000000ull, 0x00065f3e68010629ull, + 0xac28828101000100ull, 0x000b010240000004ull, 0xa42b820101000100ull, + 0x0009010240000004ull, 0xac29828101000100ull, 0x000b010240000004ull, + 0xa42b820101000100ull, 0x0009010240000004ull, 0xac29828101000100ull, + 0x000b010240000004ull, 0x0006823904000000ull, 0x0008d4a907c00200ull, + 0x0008593268020070ull, 0x0008dcb902000200ull, 0x9000813902000000ull, + 0x0001c00000000000ull, 0x00040181840005ffull, 0x0006010240000008ull, + 0x9801c00000000000ull, 0x0006debd00200020ull, 0x00048181ffff0806ull, + 0x0006d4a907c00180ull, 0x00048201ffff8035ull, 0x00068581ffff8035ull, + 0x0008d4a907c001c0ull, 0x0006dcb97c007c00ull, 0x00048201ffff0800ull, + 0x00088601ffff86ddull, 0x00068581ffff0800ull, 0x00068581ffff86ddull, + 0x0008d4a907c00200ull, 0x0009dcb97c007c00ull, 0x0007823d00200020ull, + 0x000685bd00200020ull, 0x0008d4a907c00140ull, 0x0004010240000002ull, + 0x0006593268020070ull, 0x000042a486020000ull, 0x000a15ab74000124ull, + 0x9000813904000000ull, 0x0001c00000000000ull, 0x00048181f0004000ull, + 0x9886593268020070ull, 0x0006d4a907c00200ull, 0x00068201ff000000ull, + 0xa40815ab74000345ull, 0x0009debd01000100ull, 0xa429418068010038ull, + 0x00095a3468010870ull, 0x0009028386000005ull, 0x000a068186000014ull, + 0xacca15ab74000343ull, 0xacebc639ff002200ull, 0x000b5f80680100f0ull, + 0xac8b403f72010000ull, 0x000b8203000f0005ull, 0x000b5a3468010070ull, + 0x0009d4a907c00240ull, 0x0001c00000000000ull, 0x0001c00000000000ull, + 0x0001c00000000000ull, 0x000b5cb942080000ull, 0xad0b552a4e09312dull, + 0xad2bc639ff002700ull, 0x000b5f80680100f0ull, 0xac6b403f72010001ull, + 0x0001c00000000000ull, 0x000b82013fff0000ull, 0x0009d52a00010001ull, + 0x0009d4a9f8006800ull, 0x0009593268020870ull, 0x0006418068030230ull, + 0x000b5cb942080000ull, 0x000b552a4e09312dull, 0x0006410240030000ull, + 0x9c01c00000000000ull, 0x0001c00000000000ull, 0x00078201f0006000ull, + 0x0008593268020070ull, 0x0008d4a907c00280ull, 0xa069d4a907c00000ull, + 0x00085a3468010874ull, 0x0008818100ff0000ull, 0x000615ab74000345ull, + 0x00075a3468010078ull, 0x9c8741b9680040f0ull, 0x9ca7c603ff001f00ull, + 0x00075f80680100f0ull, 0x0007403f72010001ull, 0x0001c00000000000ull, + 0x0001c00000000000ull, 0x0001c00000000000ull, 0x0001c00000000000ull, + 0x0001c00000000000ull, 0x0001c00000000000ull, 0x0007418342080000ull, + 0x9cc7552a4e09312dull, 0x9ce7c603ff002000ull, 0x00075f80680100f0ull, + 0x0007403f72010000ull, 0x0001c00000000000ull, 0x0001c00000000000ull, + 0x0001c00000000000ull, 0x0001c00000000000ull, 0x0001c00000000000ull, + 0x0001c00000000000ull, 0x0007418342080000ull, 0x9d07552a4e09312dull, + 0x9d27c603ff002100ull, 0x00075f80680100f0ull, 0x0007403f72010001ull, + 0x0001c00000000000ull, 0x0001c00000000000ull, 0x0001c00000000000ull, + 0x0001c00000000000ull, 0x0001c00000000000ull, 0x0001c00000000000ull, + 0x0007418342080000ull, 0x0007552a4e09312dull, 0x9d475c80680300f0ull, + 0x9d67c639ff002200ull, 0x00075f80680100f0ull, 0x0007403f72010000ull, + 0x0001c00000000000ull, 0x0001c00000000000ull, 0x0001c00000000000ull, + 0x0001c00000000000ull, 0x0001c00000000000ull, 0x0001c00000000000ull, + 0x00075cb942080000ull, 0x0007552a4e09312dull, 0x9d8741b9680040f0ull, + 0x9da7c603ff002400ull, 0x00075f80680100f0ull, 0x0001c00000000000ull, + 0x0001c00000000000ull, 0x0001c00000000000ull, 0x0001c00000000000ull, + 0x0001c00000000000ull, 0x0001c00000000000ull, 0x0001c00000000000ull, + 0x0001c00000000000ull, 0x0001c00000000000ull, 0x0001c00000000000ull, + 0x0007403f72010000ull, 0x0001c00000000000ull, 0x0001c00000000000ull, + 0x0001c00000000000ull, 0x0001c00000000000ull, 0x0001c00000000000ull, + 0x0001c00000000000ull, 0x0007418342080000ull, 0x9dc7552a4e09312dull, + 0x9de7c603ff002500ull, 0x00075f80680100f0ull, 0x0007403f72010001ull, + 0x0001c00000000000ull, 0x0001c00000000000ull, 0x0001c00000000000ull, + 0x0001c00000000000ull, 0x0001c00000000000ull, 0x0001c00000000000ull, + 0x0007418342080000ull, 0x0007552a4e09312dull, 0x0007010240000020ull, + 0x9c01c00000000000ull, 0x9c27c603ff002600ull, 0x00075f80680100f0ull, + 0x0007403f72010000ull, 0x0001c00000000000ull, 0x0001c00000000000ull, + 0x0001c00000000000ull, 0x0001c00000000000ull, 0x0001c00000000000ull, + 0x0001c00000000000ull, 0x0007418342080000ull, 0x0007552a4e09312dull, + 0x9c475c80680300f0ull, 0x9c67c639ff002700ull, 0x00075f80680100f0ull, + 0x0007403f72010001ull, 0x0001c00000000000ull, 0x0001c00000000000ull, + 0x0001c00000000000ull, 0x0001c00000000000ull, 0x0001c00000000000ull, + 0x0001c00000000000ull, 0x00075cb942080000ull, 0x0007552a4e09312dull, + 0x0007010240000008ull, 0xa80782b400ff0000ull, 0x000ad4a907c002c0ull, + 0x000a5a3468010078ull, 0x000a410244010000ull, 0xa80782b400ff003cull, + 0x000ad4a907c002c0ull, 0x000a5a3468010078ull, 0x000a410244010000ull, + 0xa80782b400ff002bull, 0x000ad4a907c002c0ull, 0x000a5a3468010078ull, + 0x000a410244010000ull, 0xa80782b400ff002cull, 0x000ad4a9ffc06ac0ull, + 0x000a593268020870ull, 0x000ad52a00010001ull, 0x000a5a3468010078ull, + 0x000a010240000008ull, 0x0007debd01000100ull, 0x000481bd01000100ull, + 0x0006c639ff002300ull, 0x000641aa68034000ull, 0x000641a968034846ull, + 0x0006403472030001ull, 0x0004822907000200ull, 0x000915ab74000341ull, + 0x000082aa00010001ull, 0x000a86ab00ff0045ull, 0x000adcb978007800ull, + 0x0000822907000200ull, 0x00088a3908000000ull, 0x00065cb942080000ull, + 0x0006552a4e09312dull, 0x00065cb968082868ull, 0x0006410246090000ull, + 0x000042a486020000ull, 0x000a15ab74000343ull, 0x000081b940004000ull, + 0x000685a907c00000ull, 0x000782b807000100ull, 0x000a41b268004070ull, + 0x000a410040030000ull, 0x000a41ba68004078ull, 0x000a410240030000ull, + 0xa801c00000000000ull, 0xa821c00000000000ull, 0x000a4180680100f0ull, + 0x000ac639ff003900ull, 0x000a400372010001ull, 0x0001c00000000000ull, + 0x0001c00000000000ull, 0x0001c00000000000ull, 0x0001c00000000000ull, + 0x0001c00000000000ull, 0x0001c00000000000ull, 0x000a83891f000000ull, + 0x000f542868090a48ull, 0x000f583068020070ull, 0x000a5cb942080000ull, + 0x000a552a4e09312dull, 0x000a5cb968082868ull, 0x000a410246090000ull, + 0x982881b400ff0011ull, 0x9881c00000000000ull, 0x00064180680100f0ull, + 0x00068283ffff12b5ull, 0x000a8a8108000800ull, 0x000ad4a9f8009800ull, + 0x00068303ffff17c1ull, 0x000c8b01c0000000ull, 0xb0ac5bb768010a58ull, + 0x000cd4a9f800b800ull, 0x000c8281ffff6558ull, 0x000adbb701000100ull, + 0x000c8281ffff86ddull, 0x000a8681ffff0800ull, 0x000adbb702000200ull, + 0x000682a9c8009800ull, 0x000adebd02000200ull, 0x000a593268020870ull, + 0x000a010240000008ull, 0x9c21c00000000000ull, 0x0007813400ff002full, + 0x90048201ffff6558ull, 0x00098381ffff0800ull, 0x00088281b0002000ull, + 0x000a593268020870ull, 0x000ad4a9f800a800ull, 0x000adebd02000200ull, + 0x000e593268020870ull, 0x000ed4a9f800a000ull, 0x000e010240000004ull, + 0x000e828180008000ull, 0x000a010240000004ull, 0x000e828120002000ull, + 0x000a010240000004ull, 0x000e828110001000ull, 0x000a010240000004ull, + 0x000082bd02000200ull, 0xa80ac639ff002800ull, 0xa861c00000000000ull, + 0x000a418368010526ull, 0xa84a418368010878ull, 0x000a5bb768030078ull, + 0x000a400172030000ull, 0x000a5b00680100f0ull, 0x000041b468034878ull, + 0x00005fbf68030878ull, 0x00068229c8009800ull, 0x0008010248000008ull, + 0xa001c00000000000ull, 0x000843a486020000ull, 0x00088101ffff0000ull, + 0x000415ab74000464ull, 0x000e15ab74000461ull, 0x0008010240000008ull, + 0x000c41b76800425aull, 0x000c410240030000ull, 0x000a010240000008ull, + 0x000a5cb942080000ull, 0x000a552a4e09312dull, 0x000a5cb968082868ull, + 0x000a410246090000ull, 0x0000422486020000ull, 0x000815ab74000461ull, + 0x000081b940004000ull, 0x000685a9f8000000ull, 0x000782b807000200ull, + 0x000a41b268004078ull, 0x000a410040030000ull, 0x000a41ba68004078ull, + 0x000a410240030000ull, 0xa801c00000000000ull, 0xa821c00000000000ull, + 0x000a4180680100f0ull, 0x000ac639ff003900ull, 0x000a400372010001ull, + 0x0001c00000000000ull, 0x0001c00000000000ull, 0x0001c00000000000ull, + 0x0001c00000000000ull, 0x0001c00000000000ull, 0x0001c00000000000ull, + 0x000a83891f000000ull, 0x000f542868090a48ull, 0x000f583068020070ull, + 0x000a5cb942080000ull, 0x000a552a4e09312dull, 0x000a5cb968082868ull, + 0x000a410246090000ull, 0x000081a9f800b800ull, 0x000689b701000100ull, + 0x000685a9f8009800ull, 0x000685a9f800a800ull, 0x00078229f800b800ull, + 0x000601024000000cull, 0x9801c00000000000ull, 0x00088a3702000200ull, + 0x00088629f800a000ull, 0x00068101ffff8100ull, 0x0004010240000004ull, + 0x9801c00000000000ull, 0x0009dcb910001000ull, 0x00068101ffff86ddull, + 0x00048501ffff0800ull, 0x0005dcb978003800ull, 0x0006010240000002ull, + 0x000081a9f8000000ull, 0x9007813910000000ull, 0x0001c00000000000ull, + 0x00048181f0004000ull, 0x988658b168020070ull, 0x0006d428001f0008ull, + 0x00068201ff000000ull, 0xa40815ab74000545ull, 0x0009debd04000400ull, + 0xa429418068010038ull, 0x00095a3468010870ull, 0x0009028386000005ull, + 0xac8a068186000014ull, 0x000a15ab74000543ull, 0x000b5a3468010070ull, + 0xac6b8303000f0005ull, 0x000dd428001f0009ull, 0x000b83013fff0000ull, + 0x000dd42803e001a0ull, 0x000d58b168020870ull, 0x000ddcb960006000ull, + 0x0006418068030230ull, 0x0006410240030000ull, 0x9c01c00000000000ull, + 0x0001c00000000000ull, 0x00078201f0006000ull, 0x000858b168020070ull, + 0xa068d428001f000aull, 0x00085a3468010874ull, 0x0008818100ff0000ull, + 0x000615ab74000545ull, 0x00075a3468010078ull, 0x0007010240000028ull, + 0xa80782b400ff0000ull, 0x000ad428001f000bull, 0x000a5a3468010078ull, + 0x000a410244010000ull, 0xa80782b400ff003cull, 0x000ad428001f000bull, + 0x000a5a3468010078ull, 0x000a410244010000ull, 0xa80782b400ff002bull, + 0x000ad428001f000bull, 0x000a5a3468010078ull, 0x000a410244010000ull, + 0xa80782b400ff002cull, 0x000ad42803ff01abull, 0x000adcb960006000ull, + 0x000a58b168020870ull, 0x000a5a3468010078ull, 0x000a010240000008ull, + 0x0007debd04000400ull, 0x000481bd04000400ull, 0x0006c639ff002b00ull, + 0x0006832803e001a0ull, 0x000cc18300010001ull, 0x000dc18300010000ull, + 0x000641a868034840ull, 0x0006403472030001ull, 0x00048228001c0008ull, + 0x000915ab74000541ull, 0x000082ab00ff0045ull, 0x000adcb960006000ull, + 0x0001c00000000000ull, 0x0001c00000000000ull, 0x00065cb942080000ull, + 0x0006552a4e09312dull, 0x00065cb968082868ull, 0x0006410246090000ull, + 0x000042a486020000ull, 0x000a15ab74000543ull, 0x000081b940004000ull, + 0x000685a8001f0000ull, 0x000782b807000300ull, 0x000a41b168004070ull, + 0x000a410040030000ull, 0x000a41ba68004078ull, 0x000a410240030000ull, + 0xa801c00000000000ull, 0xa821c00000000000ull, 0x000a4180680100f0ull, + 0x000ac639ff003900ull, 0x000a400372010001ull, 0x0001c00000000000ull, + 0x0001c00000000000ull, 0x0001c00000000000ull, 0x0001c00000000000ull, + 0x0001c00000000000ull, 0x0001c00000000000ull, 0x000a83891f000000ull, + 0x000f542868090a48ull, 0x000f583068020070ull, 0x000a5cb942080000ull, + 0x000a552a4e09312dull, 0x000a5cb968082868ull, 0x000a410246090000ull, + 0x00008329ff000200ull, 0x000c8728001c0008ull, 0x000c813920000000ull, + 0x000481b400ff006cull, 0x0006d42803e001c0ull, 0x000658b168020870ull, + 0xa047823400ff0033ull, 0x0008d42803e00180ull, 0xa0685f80680100f0ull, + 0xa007823400ff0032ull, 0x0008d42803e00180ull, 0xa0285f80680100f0ull, + 0x0007822803e00180ull, 0x0008c639ff002e00ull, 0x0008403f72010000ull, + 0x000858b168020870ull, 0x00085abf680040f0ull, 0x00085d80680100f0ull, + 0x0001c00000000000ull, 0x0001c00000000000ull, 0x0001c00000000000ull, + 0x00085cb942080000ull, 0x0008552a4e09312dull, 0x00085cb968082868ull, + 0x0008410246090000ull, 0x986981b400ff002full, 0x0006d42803e00280ull, + 0x00065a80680100f0ull, 0x000658b168020870ull, 0x000481b400ff0084ull, + 0x0006d42803e00240ull, 0x0004823400ff0011ull, 0x0008d42803e00220ull, + 0x98c481b400ff0006ull, 0x0006d42803e00200ull, 0x00065ebd68010b31ull, + 0x000641806801003cull, 0x0006028386000005ull, 0x000a15ab74000661ull, + 0x0006418068030230ull, 0x0008c180ffff0008ull, 0x0008863400ff0006ull, + 0x0008418240030000ull, 0x000842a486030000ull, 0x000a15ab74000661ull, + 0x9008863400ff0084ull, 0x0004c639ff002f00ull, 0x0004400072010001ull, + 0x000858b168020870ull, 0x0001c00000000000ull, 0x0001c00000000000ull, + 0x0001c00000000000ull, 0x00085cb942080000ull, 0x9028552a4e09312dull, + 0x0004c639ff003000ull, 0x0004403472010000ull, 0x0001c00000000000ull, + 0x0001c00000000000ull, 0x000858b168020870ull, 0x0001c00000000000ull, + 0x000081b940004000ull, 0x000685a803e00000ull, 0x00045cb942080000ull, + 0x0004552a4e09312dull, 0x00045cb968082868ull, 0x0004410246090000ull, + 0x000483891f000000ull, 0x000f542868090a48ull, 0x000f583068020070ull, + 0x000042a486020000ull, 0x000a15ab74000661ull, 0x000782b807000400ull, + 0x000a41b168004078ull, 0x000a410040030000ull, 0x000a41ba68004078ull, + 0x000a410240030000ull, 0xa801c00000000000ull, 0xa821c00000000000ull, + 0x000a4180680100f0ull, 0x000ac639ff003900ull, 0x000a400372010001ull, + 0x0001c00000000000ull, 0x0001c00000000000ull, 0x0001c00000000000ull, + 0x0001c00000000000ull, 0x000041bf68034878ull, 0x00005a3468030878ull, + 0x000a83891f000000ull, 0x000f542868090a48ull, 0x000f583068020070ull, + 0x000a5cb942080000ull, 0x000a552a4e09312dull, 0x000a5cb968082868ull, + 0x000a410246090000ull, 0x00005fb968004250ull, 0x0000003f70000000ull, + 0x000041b968034070ull, 0x0000512268030070ull, 0x0000813800200020ull, + 0x0004413a68024070ull, 0x9001c00000000000ull, 0x000081b800200020ull, + 0x9026898180008000ull, 0x0004890110001000ull, 0x000456ad680100a0ull, + 0x0006898180008000ull, 0x000652a56801001dull, 0x000456ad68090b5bull, + 0x00055680680900f0ull, 0x0005debd00400040ull, 0x00005600680800f0ull, + 0x0000833d00200020ull, 0x000c872907c00000ull, 0x000dd62c20000000ull, + 0x0000822902800280ull, 0x000841b268034070ull, 0x000982a8000a000aull, + 0x000a41b168034070ull, 0x000b822907c00000ull, 0x0000003f70000800ull, + 0x000941b268034070ull, 0x0000418048030000ull, 0x0000018340000008ull, + 0x0009018348000004ull, 0x000050a168030c20ull, 0x000082aa00800080ull, + 0x000850a168080c2bull, 0x0000820800010001ull, 0x000850a168000c20ull, + 0x000752a56808001eull, 0x000a822a00400040ull, 0x00088a0900010001ull, + 0x000841bc68034078ull, 0x000941bc68034070ull, 0x000a583068030870ull, + 0x0000813d00400000ull, 0x0005c180ffff0000ull, 0x00058288001e0000ull, + 0x000b8208001e0008ull, 0x00085d2168004030ull, 0x00098308001e0010ull, + 0x00088608001e0010ull, 0x000c5d2168004070ull, 0x0008418068080025ull, + 0x000841ba6803a0f0ull, 0x000856ad40030000ull, 0x0008c180ffff0000ull, + 0x0005820807000500ull, 0x00088a3d00010001ull, 0x000841be68004050ull, + 0x0005828807000300ull, 0x000a8abd00040004ull, 0x000a41be68004040ull, + 0x0005820807000100ull, 0x00088a2a00800080ull, 0x0008413068004078ull, + 0xa021c00000000000ull, 0x0005828807000200ull, 0x000841806801002dull, + 0x000a8abd00080008ull, 0x000a41be68004026ull, 0x0005820807000400ull, + 0x00088a2907000200ull, 0x000841b46800405aull, 0x000556ad40030000ull, + 0x000081bd00100010ull, 0x0006c180ffff0000ull, 0x0006822a00800080ull, + 0x00088a0900100010ull, 0x0008413c68024070ull, 0xa021c00000000000ull, + 0x0006832907000200ull, 0x0008c181f0008000ull, 0x000841834c00ffffull, + 0x0006822a00400040ull, 0x00088a0900200020ull, 0x0008413c68024078ull, + 0xa021c00000000000ull, 0x000c8b0900400040ull, 0x0008dc01f0008000ull, + 0x000841b84c03ffffull, 0x000c8b2a00010000ull, 0x000c41b44c0300ffull, + 0x000682a9f800a800ull, 0x000a86a9f8009800ull, 0x000a8a8904000400ull, + 0x000a41b64c03ffffull, 0x000a41b74c0300ffull, 0x0000828901000100ull, + 0x000a822803e00180ull, 0x0008413168024078ull, 0x0008833400ff0033ull, + 0x000c010240000004ull, 0xa001c00000000000ull, 0xa021c00000000000ull, + 0x000841814c03ffffull, 0x000841814c03ffffull, 0x000a822803e00280ull, + 0x000841b54c03ffffull, 0x000682287c005800ull, 0x00088a0902000200ull, + 0x0008413068024070ull, 0xa001c00000000000ull, 0x0006830900020002ull, + 0x00088281e0002000ull, 0xa84a868108000800ull, 0xa861c00000000000ull, + 0x000a41814c03ffffull, 0x000a41814c03ffffull, 0x00065380680300f0ull, + 0x000c5321680040b0ull, 0x000dd3260fff0fffull, 0x0006810900800080ull, + 0x0000003f70000400ull, 0x000082a907000200ull, 0x000a413268024070ull, + 0xa50a822902800280ull, 0x0004893d08000800ull, 0x00098301ffffffffull, + 0xa4c98381f000e000ull, 0x00095f00680100f0ull, 0xa5295f3e64010000ull, + 0x0001c00000000000ull, 0xa4ec8b01ffffffffull, 0x00095d00680100f0ull, + 0xa1895d3a64010000ull, 0x000cd5ab80008000ull, 0x00088a01ff00ff00ull, + 0x0008d5ab40004000ull, 0x000ed5ab40004000ull, 0x0004893d40000000ull, + 0x00005700680800f0ull, 0x00005780680900f0ull, 0x00008229f800a000ull, + 0x0008c180ffff0018ull, 0x000857af680320f0ull, 0x0007d72ef1ff0000ull, + 0x0007d7aff0000000ull, 0x0004d72e00fc0000ull, 0x0000812c00020002ull, + 0x0004892907c00200ull, 0x000441a7680040f0ull, 0x000441be4c03ffffull, + 0x000441ba4c03ffffull, 0x000481a803c00200ull, 0x0006413168024078ull, + 0x9801c00000000000ull, 0x9821c00000000000ull, 0x00065f80680100f0ull, + 0x00065fbf64010000ull, 0x000641bf4c03ffffull, 0x000452a568030250ull, + 0x0000000008000000ull, 0x0001c00000000000ull, 0x0001c00000000000ull, + 0x0001c00000000000ull +}; diff --git a/arch/mips/mach-octeon/include/mach/cvmx-pko.h b/arch/mips/mach-octeon/include/mach/cvmx-pko.h new file mode 100644 index 0000000..26e7a9a --- /dev/null +++ b/arch/mips/mach-octeon/include/mach/cvmx-pko.h @@ -0,0 +1,213 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018-2022 Marvell International Ltd. + * + * Backward compatibility for packet transmission using legacy PKO command. + */ + +#ifndef __CVMX_PKO_H__ +#define __CVMX_PKO_H__ + +extern cvmx_pko_return_value_t +cvmx_pko3_legacy_xmit(unsigned int dq, cvmx_pko_command_word0_t pko_command, + cvmx_buf_ptr_t packet, uint64_t addr, bool tag_sw); + +/** + * Complete packet output. cvmx_pko_send_packet_prepare() must be called exactly + * once before this, and the same parameters must be passed to both + * cvmx_pko_send_packet_prepare() and cvmx_pko_send_packet_finish(). + * + * WARNING: This function may have to look up the proper PKO port in + * the IPD port to PKO port map, and is thus slower than calling + * cvmx_pko_send_packet_finish_pkoid() directly if the PKO port + * identifier is known. + * + * @param ipd_port The IPD port corresponding the to pko port the packet is for + * @param queue Queue to use + * @param pko_command + * PKO HW command word + * @param packet to send + * @param use_locking + * CVMX_PKO_LOCK_NONE, CVMX_PKO_LOCK_ATOMIC_TAG, + * or CVMX_PKO_LOCK_CMD_QUEUE + * + * @return returns CVMX_PKO_SUCCESS on success, or error code on failure of output + */ +static inline cvmx_pko_return_value_t +cvmx_pko_send_packet_finish(u64 ipd_port, uint64_t queue, + cvmx_pko_command_word0_t pko_command, + cvmx_buf_ptr_t packet, cvmx_pko_lock_t use_locking) +{ + cvmx_cmd_queue_result_t result; + + if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { + return cvmx_pko3_legacy_xmit(queue, pko_command, packet, 0, + use_locking == + CVMX_PKO_LOCK_ATOMIC_TAG); + } + + if (use_locking == CVMX_PKO_LOCK_ATOMIC_TAG) + cvmx_pow_tag_sw_wait(); + + result = cvmx_cmd_queue_write2(CVMX_CMD_QUEUE_PKO(queue), + (use_locking == CVMX_PKO_LOCK_CMD_QUEUE), + pko_command.u64, packet.u64); + if (cvmx_likely(result == CVMX_CMD_QUEUE_SUCCESS)) { + cvmx_pko_doorbell(ipd_port, queue, 2); + return CVMX_PKO_SUCCESS; + } else if ((result == CVMX_CMD_QUEUE_NO_MEMORY) || + (result == CVMX_CMD_QUEUE_FULL)) { + return CVMX_PKO_NO_MEMORY; + } else { + return CVMX_PKO_INVALID_QUEUE; + } +} + +/** + * Complete packet output. cvmx_pko_send_packet_prepare() must be called exactly + * once before this, and the same parameters must be passed to both + * cvmx_pko_send_packet_prepare() and cvmx_pko_send_packet_finish(). + * + * WARNING: This function may have to look up the proper PKO port in + * the IPD port to PKO port map, and is thus slower than calling + * cvmx_pko_send_packet_finish3_pkoid() directly if the PKO port + * identifier is known. + * + * @param ipd_port The IPD port corresponding the to pko port the packet is for + * @param queue Queue to use + * @param pko_command + * PKO HW command word + * @param packet to send + * @param addr Physical address of a work queue entry or physical address to zero + * on complete. + * @param use_locking + * CVMX_PKO_LOCK_NONE, CVMX_PKO_LOCK_ATOMIC_TAG, + * or CVMX_PKO_LOCK_CMD_QUEUE + * + * @return returns CVMX_PKO_SUCCESS on success, or error code on failure of output + */ +static inline cvmx_pko_return_value_t +cvmx_pko_send_packet_finish3(u64 ipd_port, uint64_t queue, + cvmx_pko_command_word0_t pko_command, + cvmx_buf_ptr_t packet, uint64_t addr, + cvmx_pko_lock_t use_locking) +{ + cvmx_cmd_queue_result_t result; + + if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { + return cvmx_pko3_legacy_xmit(queue, pko_command, packet, addr, + use_locking == + CVMX_PKO_LOCK_ATOMIC_TAG); + } + + if (use_locking == CVMX_PKO_LOCK_ATOMIC_TAG) + cvmx_pow_tag_sw_wait(); + + result = cvmx_cmd_queue_write3(CVMX_CMD_QUEUE_PKO(queue), + (use_locking == CVMX_PKO_LOCK_CMD_QUEUE), + pko_command.u64, packet.u64, addr); + if (cvmx_likely(result == CVMX_CMD_QUEUE_SUCCESS)) { + cvmx_pko_doorbell(ipd_port, queue, 3); + return CVMX_PKO_SUCCESS; + } else if ((result == CVMX_CMD_QUEUE_NO_MEMORY) || + (result == CVMX_CMD_QUEUE_FULL)) { + return CVMX_PKO_NO_MEMORY; + } else { + return CVMX_PKO_INVALID_QUEUE; + } +} + +/** + * Complete packet output. cvmx_pko_send_packet_prepare() must be called exactly + * once before this, and the same parameters must be passed to both + * cvmx_pko_send_packet_prepare() and cvmx_pko_send_packet_finish_pkoid(). + * + * @param pko_port Port to send it on + * @param queue Queue to use + * @param pko_command + * PKO HW command word + * @param packet to send + * @param use_locking + * CVMX_PKO_LOCK_NONE, CVMX_PKO_LOCK_ATOMIC_TAG, + * or CVMX_PKO_LOCK_CMD_QUEUE + * + * @return returns CVMX_PKO_SUCCESS on success, or error code on failure of output + */ +static inline cvmx_pko_return_value_t +cvmx_pko_send_packet_finish_pkoid(int pko_port, uint64_t queue, + cvmx_pko_command_word0_t pko_command, + cvmx_buf_ptr_t packet, cvmx_pko_lock_t use_locking) +{ + cvmx_cmd_queue_result_t result; + + if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { + return cvmx_pko3_legacy_xmit(queue, pko_command, packet, 0, + use_locking == + CVMX_PKO_LOCK_ATOMIC_TAG); + } + + if (use_locking == CVMX_PKO_LOCK_ATOMIC_TAG) + cvmx_pow_tag_sw_wait(); + result = cvmx_cmd_queue_write2(CVMX_CMD_QUEUE_PKO(queue), + (use_locking == CVMX_PKO_LOCK_CMD_QUEUE), + pko_command.u64, packet.u64); + if (cvmx_likely(result == CVMX_CMD_QUEUE_SUCCESS)) { + cvmx_pko_doorbell_pkoid(pko_port, queue, 2); + return CVMX_PKO_SUCCESS; + } else if ((result == CVMX_CMD_QUEUE_NO_MEMORY) || + (result == CVMX_CMD_QUEUE_FULL)) { + return CVMX_PKO_NO_MEMORY; + } else { + return CVMX_PKO_INVALID_QUEUE; + } +} + +/** + * Complete packet output. cvmx_pko_send_packet_prepare() must be called exactly + * once before this, and the same parameters must be passed to both + * cvmx_pko_send_packet_prepare() and cvmx_pko_send_packet_finish_pkoid(). + * + * @param pko_port The PKO port the packet is for + * @param queue Queue to use + * @param pko_command + * PKO HW command word + * @param packet to send + * @param addr Plysical address of a work queue entry or physical address to zero + * on complete. + * @param use_locking + * CVMX_PKO_LOCK_NONE, CVMX_PKO_LOCK_ATOMIC_TAG, + * or CVMX_PKO_LOCK_CMD_QUEUE + * + * @return returns CVMX_PKO_SUCCESS on success, or error code on failure of output + */ +static inline cvmx_pko_return_value_t +cvmx_pko_send_packet_finish3_pkoid(u64 pko_port, uint64_t queue, + cvmx_pko_command_word0_t pko_command, + cvmx_buf_ptr_t packet, uint64_t addr, + cvmx_pko_lock_t use_locking) +{ + cvmx_cmd_queue_result_t result; + + if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { + return cvmx_pko3_legacy_xmit(queue, pko_command, packet, addr, + use_locking == + CVMX_PKO_LOCK_ATOMIC_TAG); + } + + if (use_locking == CVMX_PKO_LOCK_ATOMIC_TAG) + cvmx_pow_tag_sw_wait(); + result = cvmx_cmd_queue_write3(CVMX_CMD_QUEUE_PKO(queue), + (use_locking == CVMX_PKO_LOCK_CMD_QUEUE), + pko_command.u64, packet.u64, addr); + if (cvmx_likely(result == CVMX_CMD_QUEUE_SUCCESS)) { + cvmx_pko_doorbell_pkoid(pko_port, queue, 3); + return CVMX_PKO_SUCCESS; + } else if ((result == CVMX_CMD_QUEUE_NO_MEMORY) || + (result == CVMX_CMD_QUEUE_FULL)) { + return CVMX_PKO_NO_MEMORY; + } else { + return CVMX_PKO_INVALID_QUEUE; + } +} + +#endif /* __CVMX_PKO_H__ */ diff --git a/arch/mips/mach-octeon/include/mach/cvmx-pko3-resources.h b/arch/mips/mach-octeon/include/mach/cvmx-pko3-resources.h new file mode 100644 index 0000000..cc9f375 --- /dev/null +++ b/arch/mips/mach-octeon/include/mach/cvmx-pko3-resources.h @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018-2022 Marvell International Ltd. + */ + +#ifndef __CVMX_PKO3_RESOURCES_H__ +#define __CVMX_PKO3_RESOURCES_H__ + +/* + * Allocate or reserve contiguous list of PKO queues. + * + * @param node is the node number for PKO queues. + * @param level is the PKO queue level. + * @param owner is the owner of PKO queue resources. + * @param base_queue is the PKO queue base number(specify -1 to allocate). + * @param num_queues is the number of PKO queues that have to be reserved or allocated. + * @return returns queue_base if successful or -1 on failure. + */ +int cvmx_pko_alloc_queues(int node, int level, int owner, int base_queue, + int num_queues); + +/** + * Free an allocated/reserved PKO queues for a certain level and owner + * + * @param node on which to allocate/reserve PKO queues + * @param level of PKO queue + * @param owner of reserved/allocated resources + * @return 0 on success, -1 on failure + */ +int cvmx_pko_free_queues(int node, int level, int owner); + +int __cvmx_pko3_dq_param_setup(unsigned node); + +int cvmx_pko3_num_level_queues(enum cvmx_pko3_level_e level); + +#endif /* __CVMX_PKO3_RESOURCES_H__ */ diff --git a/arch/mips/mach-octeon/include/mach/cvmx-pko3.h b/arch/mips/mach-octeon/include/mach/cvmx-pko3.h new file mode 100644 index 0000000..86f89be --- /dev/null +++ b/arch/mips/mach-octeon/include/mach/cvmx-pko3.h @@ -0,0 +1,1052 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018-2022 Marvell International Ltd. + * + */ + +#ifndef __CVMX_PKO3_H__ +#define __CVMX_PKO3_H__ + +DECLARE_GLOBAL_DATA_PTR; + +/* Use full LMTDMA when PARAMETER_CHECKINS is enabled */ +#undef CVMX_ENABLE_PARAMETER_CHECKING +#define CVMX_ENABLE_PARAMETER_CHECKING 0 + +/* + * CVMSEG, scratch line for LMTDMA/LMTST operations: + * 1. It should differ from other CVMSEG uses, e.g. IOBDMA, + * 2. It must agree with the setting of CvmCtl[LMTLINE] control register. + * Contains 16 words, words 1-15 are cleared when word 0 is written to. + */ +#define CVMX_PKO_LMTLINE 2ull + +/* PKO3 queue level identifier */ +enum cvmx_pko3_level_e { + CVMX_PKO_LEVEL_INVAL = 0, + CVMX_PKO_PORT_QUEUES = 0xd1, + CVMX_PKO_L2_QUEUES = 0xc2, + CVMX_PKO_L3_QUEUES = 0xb3, + CVMX_PKO_L4_QUEUES = 0xa4, + CVMX_PKO_L5_QUEUES = 0x95, + CVMX_PKO_DESCR_QUEUES = 0x86, +}; + +enum cvmx_pko_dqop { + CVMX_PKO_DQ_SEND = 0ULL, + CVMX_PKO_DQ_OPEN = 1ULL, + CVMX_PKO_DQ_CLOSE = 2ULL, + CVMX_PKO_DQ_QUERY = 3ULL +}; + +/** + * Returns the PKO DQ..L2 Shaper Time-Wheel clock rate for specified node. + */ +static inline u64 cvmx_pko3_dq_tw_clock_rate_node(int node) +{ + return gd->bus_clk / 768; +} + +/** + * Returns the PKO Port Shaper Time-Wheel clock rate for specified node. + */ +static inline u64 cvmx_pko3_pq_tw_clock_rate_node(int node) +{ + int div; + + if (OCTEON_IS_MODEL(OCTEON_CN78XX)) + div = 96; + else + div = 48; + return gd->bus_clk / div; +} + +/** + * @INTERNAL + * Return the number of MACs in the PKO (exclusing the NULL MAC) + * in a model-dependent manner. + */ +static inline unsigned int __cvmx_pko3_num_macs(void) +{ + if (OCTEON_IS_MODEL(OCTEON_CNF75XX)) + return 10; + if (OCTEON_IS_MODEL(OCTEON_CN73XX)) + return 14; + if (OCTEON_IS_MODEL(OCTEON_CN78XX)) + return 28; + return 0; +} + +/** + * @INTERNAL + * Return the number of queue levels, depending on SoC model + */ +static inline int __cvmx_pko3_sq_lvl_max(void) +{ + if (OCTEON_IS_MODEL(OCTEON_CN73XX)) + return CVMX_PKO_L3_QUEUES; + if (OCTEON_IS_MODEL(OCTEON_CNF75XX)) + return CVMX_PKO_L3_QUEUES; + if (OCTEON_IS_MODEL(OCTEON_CN78XX)) + return CVMX_PKO_L5_QUEUES; + return -1; +} + +/** + * @INTERNAL + * Return the next (lower) queue level for a given level + */ +static inline enum cvmx_pko3_level_e +__cvmx_pko3_sq_lvl_next(enum cvmx_pko3_level_e level) +{ + switch (level) { + default: + return CVMX_PKO_LEVEL_INVAL; + case CVMX_PKO_PORT_QUEUES: + return CVMX_PKO_L2_QUEUES; + case CVMX_PKO_L2_QUEUES: + return CVMX_PKO_L3_QUEUES; + case CVMX_PKO_L3_QUEUES: + if (OCTEON_IS_MODEL(OCTEON_CN73XX) || + OCTEON_IS_MODEL(OCTEON_CNF75XX)) + return CVMX_PKO_DESCR_QUEUES; + return CVMX_PKO_L4_QUEUES; + case CVMX_PKO_L4_QUEUES: + if (OCTEON_IS_MODEL(OCTEON_CN73XX) || + OCTEON_IS_MODEL(OCTEON_CNF75XX)) + return CVMX_PKO_LEVEL_INVAL; + return CVMX_PKO_L5_QUEUES; + case CVMX_PKO_L5_QUEUES: + if (OCTEON_IS_MODEL(OCTEON_CN73XX) || + OCTEON_IS_MODEL(OCTEON_CNF75XX)) + return CVMX_PKO_LEVEL_INVAL; + return CVMX_PKO_DESCR_QUEUES; + } +} + +/** + * @INTERNAL + * Return an SQ identifier string, for debug messages. + */ +static inline char *__cvmx_pko3_sq_str(char *buf, enum cvmx_pko3_level_e level, + unsigned int q) +{ + char *p; + + switch (level) { + default: + strcpy(buf, "ERR-SQ/"); + break; + case CVMX_PKO_PORT_QUEUES: + strcpy(buf, "PQ_L1/"); + break; + case CVMX_PKO_L2_QUEUES: + strcpy(buf, "SQ_L2/"); + break; + case CVMX_PKO_L3_QUEUES: + strcpy(buf, "SQ_L3/"); + break; + case CVMX_PKO_L4_QUEUES: + strcpy(buf, "SQ_L4/"); + break; + case CVMX_PKO_L5_QUEUES: + strcpy(buf, "SQ_L5/"); + break; + case CVMX_PKO_DESCR_QUEUES: + strcpy(buf, "DQ/"); + break; + } + + for (p = buf; *p; p++) + ; + *p++ = '0' + q / 1000; + q -= (q / 1000) * 1000; + *p++ = '0' + q / 100; + q -= (q / 100) * 100; + *p++ = '0' + q / 10; + q -= (q / 10) * 10; + *p++ = '0' + q; + *p++ = ':'; + *p++ = '\0'; + return buf; +} + +union cvmx_pko_query_rtn { + u64 u64; + struct { + u64 dqstatus : 4; + u64 rsvd_50_59 : 10; + u64 dqop : 2; + u64 depth : 48; + } s; +}; + +typedef union cvmx_pko_query_rtn cvmx_pko_query_rtn_t; + +/* PKO_QUERY_RTN_S[DQSTATUS] - cvmx_pko_query_rtn_t->s.dqstatus */ +enum pko_query_dqstatus { + PKO_DQSTATUS_PASS = 0, /* No error */ + PKO_DQSTATUS_BADSTATE = 0x8, /* queue was not ready to enqueue */ + PKO_DQSTATUS_NOFPABUF = 0x9, /* FPA out of buffers */ + PKO_DQSTATUS_NOPKOBUF = 0xA, /* PKO out of buffers */ + PKO_DQSTATUS_FAILRTNPTR = 0xB, /* can't return buffer ptr to FPA */ + PKO_DQSTATUS_ALREADY = 0xC, /* already created */ + PKO_DQSTATUS_NOTCREATED = 0xD, /* not created */ + PKO_DQSTATUS_NOTEMPTY = 0xE, /* queue not empty */ + PKO_DQSTATUS_SENDPKTDROP = 0xF /* packet dropped, illegal construct */ +}; + +typedef enum pko_query_dqstatus pko_query_dqstatus_t; + +/* Sub-command three bit codes (SUBDC3) */ +#define CVMX_PKO_SENDSUBDC_LINK 0x0 +#define CVMX_PKO_SENDSUBDC_GATHER 0x1 +#define CVMX_PKO_SENDSUBDC_JUMP 0x2 +/* Sub-command four bit codes (SUBDC4) */ +#define CVMX_PKO_SENDSUBDC_TSO 0x8 +#define CVMX_PKO_SENDSUBDC_FREE 0x9 +#define CVMX_PKO_SENDSUBDC_WORK 0xA +#define CVMX_PKO_SENDSUBDC_AURA 0xB +#define CVMX_PKO_SENDSUBDC_MEM 0xC +#define CVMX_PKO_SENDSUBDC_EXT 0xD +#define CVMX_PKO_SENDSUBDC_CRC 0xE +#define CVMX_PKO_SENDSUBDC_IMM 0xF + +/** + * pko buf ptr + * This is good for LINK_S, GATHER_S and PKI_BUFLINK_S structure use. + * It can also be used for JUMP_S with F-bit represented by "i" field, + * and the size limited to 8-bit. + */ + +union cvmx_pko_buf_ptr { + u64 u64; + struct { + u64 size : 16; + u64 subdc3 : 3; + u64 i : 1; + u64 rsvd_42_43 : 2; + u64 addr : 42; + } s; +}; + +typedef union cvmx_pko_buf_ptr cvmx_pko_buf_ptr_t; + +/** + * pko_auraalg_e + */ +enum pko_auraalg_e { + AURAALG_NOP = 0x0, /* aura_cnt = No change */ + AURAALG_SUB = 0x3, /* aura_cnt -= pko_send_aura_t.offset */ + AURAALG_SUBLEN = 0x7, /* aura_cnt -= pko_send_aura_t.offset + + * pko_send_hdr_t.total_bytes + */ + AURAALG_SUBMBUF = 0xB /* aura_cnt -= pko_send_aura_t.offset + + * mbufs_freed + */ +}; + +/** + * PKO_CKL4ALG_E + */ +enum pko_clk4alg_e { + CKL4ALG_NONE = 0x0, /* No checksum. */ + CKL4ALG_UDP = 0x1, /* UDP L4 checksum. */ + CKL4ALG_TCP = 0x2, /* TCP L4 checksum. */ + CKL4ALG_SCTP = 0x3, /* SCTP L4 checksum. */ +}; + +/** + * pko_send_aura + */ +union cvmx_pko_send_aura { + u64 u64; + struct { + u64 rsvd_60_63 : 4; + u64 aura : 12; /* NODE+LAURA */ + u64 subdc4 : 4; + u64 alg : 4; /* pko_auraalg_e */ + u64 rsvd_08_39 : 32; + u64 offset : 8; + } s; +}; + +typedef union cvmx_pko_send_aura cvmx_pko_send_aura_t; + +/** + * pko_send_tso + */ +union cvmx_pko_send_tso { + u64 u64; + struct { + u64 l2len : 8; + u64 rsvd_48_55 : 8; + u64 subdc4 : 4; /* 0x8 */ + u64 rsvd_32_43 : 12; + u64 sb : 8; + u64 mss : 16; + u64 eom : 1; + u64 fn : 7; + } s; +}; + +typedef union cvmx_pko_send_tso cvmx_pko_send_tso_t; + +/** + * pko_send_free + */ +union cvmx_pko_send_free { + u64 u64; + struct { + u64 rsvd_48_63 : 16; + u64 subdc4 : 4; /* 0x9 */ + u64 rsvd : 2; + u64 addr : 42; + } s; +}; + +typedef union cvmx_pko_send_free cvmx_pko_send_free_t; + +/* PKO_SEND_HDR_S - PKO header subcommand */ +union cvmx_pko_send_hdr { + u64 u64; + struct { + u64 rsvd_60_63 : 4; + u64 aura : 12; + u64 ckl4 : 2; /* PKO_CKL4ALG_E */ + u64 ckl3 : 1; + u64 ds : 1; + u64 le : 1; + u64 n2 : 1; + u64 ii : 1; + u64 df : 1; + u64 rsvd_39 : 1; + u64 format : 7; + u64 l4ptr : 8; + u64 l3ptr : 8; + u64 total : 16; + } s; +}; + +typedef union cvmx_pko_send_hdr cvmx_pko_send_hdr_t; + +/* PKO_SEND_EXT_S - extended header subcommand */ +union cvmx_pko_send_ext { + u64 u64; + struct { + u64 rsvd_48_63 : 16; + u64 subdc4 : 4; /* _SENDSUBDC_EXT */ + u64 col : 2; /* _COLORALG_E */ + u64 ra : 2; /* _REDALG_E */ + u64 tstmp : 1; + u64 rsvd_24_38 : 15; + u64 markptr : 8; + u64 rsvd_9_15 : 7; + u64 shapechg : 9; + } s; +}; + +typedef union cvmx_pko_send_ext cvmx_pko_send_ext_t; + +/* PKO_MEMDSZ_E */ +enum cvmx_pko_memdsz_e { + MEMDSZ_B64 = 0, + MEMDSZ_B32 = 1, + MEMDSZ_B16 = 2, /* Not in HRM, assumed unsupported */ + MEMDSZ_B8 = 3 +}; + +/* PKO_MEMALG_E */ +enum cvmx_pko_memalg_e { + MEMALG_SET = 0, /* Set mem = PKO_SEND_MEM_S[OFFSET] */ + MEMALG_SETTSTMP = 1, /* Set the memory location to the timestamp + * PKO_SEND_MEM_S[DSZ] must be B64 and a + * PKO_SEND_EXT_S subdescriptor must be in + * the descriptor with PKO_SEND_EXT_S[TSTMP]=1 + */ + MEMALG_SETRSLT = 2, /* [DSZ] = B64; mem = PKO_MEM_RESULT_S. */ + MEMALG_ADD = 8, /* mem = mem + PKO_SEND_MEM_S[OFFSET] */ + MEMALG_SUB = 9, /* mem = mem – PKO_SEND_MEM_S[OFFSET] */ + MEMALG_ADDLEN = 0xA, /* mem += [OFFSET] + PKO_SEND_HDR_S[TOTAL] */ + MEMALG_SUBLEN = 0xB, /* mem -= [OFFSET] + PKO_SEND_HDR_S[TOTAL] */ + MEMALG_ADDMBUF = 0xC, /* mem += [OFFSET] + mbufs_freed */ + MEMALG_SUBMBUF = 0xD /* mem -= [OFFSET] + mbufs_freed */ +}; + +union cvmx_pko_send_mem { + u64 u64; + struct { + u64 rsvd_63 : 1; + u64 wmem : 1; + u64 dsz : 2; + u64 alg : 4; + u64 offset : 8; + u64 subdc4 : 4; + u64 rsvd_42_43 : 2; + u64 addr : 42; + } s; +}; + +typedef union cvmx_pko_send_mem cvmx_pko_send_mem_t; + +union cvmx_pko_send_work { + u64 u64; + struct { + u64 rsvd_62_63 : 2; + u64 grp : 10; + u64 tt : 2; + u64 rsvd_48_49 : 2; + u64 subdc4 : 4; + u64 rsvd_42_43 : 2; + u64 addr : 42; + } s; +}; + +typedef union cvmx_pko_send_work cvmx_pko_send_work_t; + +/*** PKO_SEND_DMA_S - format of IOBDMA/LMTDMA data word ***/ +union cvmx_pko_lmtdma_data { + u64 u64; + struct { + u64 scraddr : 8; + u64 rtnlen : 8; + u64 did : 8; /* 0x51 */ + u64 node : 4; + u64 rsvd_34_35 : 2; + u64 dqop : 2; /* PKO_DQOP_E */ + u64 rsvd_26_31 : 6; + u64 dq : 10; + u64 rsvd_0_15 : 16; + } s; +}; + +typedef union cvmx_pko_lmtdma_data cvmx_pko_lmtdma_data_t; + +typedef struct cvmx_pko3_dq_params_s { + s32 depth; + s32 limit; + u64 pad[15]; +} cvmx_pko3_dq_params_t; + +/* DQ depth cached value */ +extern cvmx_pko3_dq_params_t *__cvmx_pko3_dq_params[CVMX_MAX_NODES]; + +int cvmx_pko3_internal_buffer_count(unsigned int node); + +/** + * @INTERNAL + * PKO3 DQ parameter location + * @param node node + * @param dq dq + */ +static inline cvmx_pko3_dq_params_t *cvmx_pko3_dq_parameters(unsigned int node, + unsigned int dq) +{ + cvmx_pko3_dq_params_t *pparam = NULL; + static cvmx_pko3_dq_params_t dummy; + + dummy.depth = 0; + dummy.limit = (1 << 16); + + if (cvmx_likely(node < CVMX_MAX_NODES)) + pparam = __cvmx_pko3_dq_params[node]; + + if (cvmx_likely(pparam)) + pparam += dq; + else + pparam = &dummy; + + return pparam; +} + +static inline void cvmx_pko3_dq_set_limit(unsigned int node, unsigned int dq, + unsigned int limit) +{ + cvmx_pko3_dq_params_t *pparam; + + pparam = cvmx_pko3_dq_parameters(node, dq); + pparam->limit = limit; +} + +/** + * PKO descriptor queue operation error string + * + * @param dqstatus is the enumeration returned from hardware, + * PKO_QUERY_RTN_S[DQSTATUS]. + * + * @return static constant string error description + */ +const char *pko_dqstatus_error(pko_query_dqstatus_t dqstatus); + +/* + * This function gets PKO mac num for a interface/port. + * + * @param interface is the interface number. + * @param index is the port number. + * @return returns mac number if successful or -1 on failure. + */ +static inline int __cvmx_pko3_get_mac_num(int xiface, int index) +{ + struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); + cvmx_helper_interface_mode_t mode; + int interface_index; + int ilk_mac_base = -1, bgx_mac_base = -1, bgx_ports = 4; + + if (OCTEON_IS_MODEL(OCTEON_CN73XX)) + bgx_mac_base = 2; + + if (OCTEON_IS_MODEL(OCTEON_CNF75XX)) + bgx_mac_base = 2; + + if (OCTEON_IS_MODEL(OCTEON_CN78XX)) { + ilk_mac_base = 2; + bgx_mac_base = 4; + } + + mode = cvmx_helper_interface_get_mode(xiface); + switch (mode) { + case CVMX_HELPER_INTERFACE_MODE_LOOP: + return 0; + case CVMX_HELPER_INTERFACE_MODE_NPI: + return 1; + case CVMX_HELPER_INTERFACE_MODE_ILK: + if (ilk_mac_base < 0) + return -1; + interface_index = (xi.interface - CVMX_ILK_GBL_BASE()); + if (interface_index < 0) + return -1; + return (ilk_mac_base + interface_index); + case CVMX_HELPER_INTERFACE_MODE_SRIO: + return (4 + 2 * xi.interface + index); + default: + if (xi.interface >= CVMX_ILK_GBL_BASE() && ilk_mac_base >= 0) + return -1; + /* All other modes belong to BGX */ + return (bgx_mac_base + bgx_ports * xi.interface + index); + } +} + +/** + * @INTERNAL + * + * Get scratch offset for LMTDMA/LMTST data buffer + * + */ +static inline unsigned int cvmx_pko3_lmtdma_scr_base(void) +{ + return CVMX_PKO_LMTLINE * CVMX_CACHE_LINE_SIZE; +} + +/** + * @INTERNAL + * + * Get address for LMTDMA/LMTST data buffer + * + */ +static inline u64 *cvmx_pko3_cvmseg_addr(void) +{ + const unsigned int scr = cvmx_pko3_lmtdma_scr_base(); + + return (u64 *)(CVMX_SCRATCH_BASE + scr); +} + +/** + * Save scratchpad area + * @param buf storage buffer for saving previous scratchpad contents. + * + * This function should be used whenever the cache line is used + * from a context that might preempt another context that too uses + * the same cache line designated for LMTST/LMTDMA and Wide-Atomic + * operations, such as the hard interrupt context in Linux kernel, + * that could preempt a user-space application on the same processor + * core also using the same scratchpad. + * 'cvmx_lmtline_save()' should be called upon entry into the + * potentially interrupting context, and 'cvmx_lmtline_restore()' should + * be called prior to exitting that context. + */ +static inline void cvmx_lmtline_save(u64 buf[16]) +{ + unsigned int i, scr_off = cvmx_pko3_lmtdma_scr_base(); + unsigned int sz = CVMX_CACHE_LINE_SIZE / sizeof(u64); + + /* wait LMTDMA to finish (if any) */ + CVMX_SYNCIOBDMA; + + /* Copy LMTLINE to user-provided buffer */ + for (i = 0; i < sz; i++) + buf[i] = cvmx_scratch_read64(scr_off + i * sizeof(u64)); +} + +/** + * Restore scratchpad area + * @param buf storage buffer containing the previous content of scratchpad. + */ +static inline void cvmx_lmtline_restore(const u64 buf[16]) +{ + unsigned int i, scr_off = cvmx_pko3_lmtdma_scr_base(); + unsigned int sz = CVMX_CACHE_LINE_SIZE / sizeof(u64); + + /* wait LMTDMA to finsh (if any) */ + CVMX_SYNCIOBDMA; + + /* restore scratchpad area from buf[] */ + for (i = 0; i < sz; i++) + cvmx_scratch_write64(scr_off + i * sizeof(u64), buf[i]); +} + +/* + * @INTERNAL + * Deliver PKO SEND commands via CVMSEG LM and LMTDMA/LMTST. + * The command should be already stored in the CVMSEG address. + * + * @param node is the destination node + * @param dq is the destination descriptor queue. + * @param numwords is the number of outgoing words + * @param tag_wait Wait to finish tag switch just before issueing LMTDMA + * @return the PKO3 native query result structure. + * + * <numwords> must be between 1 and 15 for CVMX_PKO_DQ_SEND command + * + * NOTE: Internal use only. + */ +static inline cvmx_pko_query_rtn_t +__cvmx_pko3_lmtdma(u8 node, uint16_t dq, unsigned int numwords, bool tag_wait) +{ + const enum cvmx_pko_dqop dqop = CVMX_PKO_DQ_SEND; + cvmx_pko_query_rtn_t pko_status; + cvmx_pko_lmtdma_data_t pko_send_dma_data; + u64 dma_addr; + unsigned int scr_base = cvmx_pko3_lmtdma_scr_base(); + unsigned int scr_off; + cvmx_pko3_dq_params_t *pparam; + + if (cvmx_unlikely(numwords < 1 || numwords > 15)) { + debug("%s: ERROR: Internal error\n", __func__); + pko_status.u64 = ~0ull; + return pko_status; + } + + pparam = cvmx_pko3_dq_parameters(node, dq); + + pko_status.u64 = 0; + pko_send_dma_data.u64 = 0; + + /* LMTDMA address offset is (nWords-1) */ + dma_addr = CVMX_LMTDMA_ORDERED_IO_ADDR; + dma_addr += (numwords - 1) << 3; + + scr_off = scr_base + numwords * sizeof(u64); + + /* Write all-ones into the return area */ + cvmx_scratch_write64(scr_off, ~0ull); + + /* Barrier: make sure all prior writes complete before the following */ + CVMX_SYNCWS; + + /* If cached depth exceeds limit, check the real depth */ + if (cvmx_unlikely(pparam->depth > pparam->limit)) { + cvmx_pko_dqx_wm_cnt_t wm_cnt; + + wm_cnt.u64 = csr_rd_node(node, CVMX_PKO_DQX_WM_CNT(dq)); + pko_status.s.depth = wm_cnt.s.count; + pparam->depth = pko_status.s.depth; + + if (pparam->depth > pparam->limit) { + pko_status.s.dqop = dqop; + pko_status.s.dqstatus = PKO_DQSTATUS_NOFPABUF; + return pko_status; + } + } else { + cvmx_atomic_add32_nosync(&pparam->depth, 1); + } + + if (CVMX_ENABLE_PARAMETER_CHECKING) { + /* Request one return word */ + pko_send_dma_data.s.rtnlen = 1; + } else { + /* Do not expect a return word */ + pko_send_dma_data.s.rtnlen = 0; + } + + /* build store data for DMA */ + pko_send_dma_data.s.scraddr = scr_off >> 3; + pko_send_dma_data.s.did = 0x51; + pko_send_dma_data.s.node = node; + pko_send_dma_data.s.dqop = dqop; + pko_send_dma_data.s.dq = dq; + + /* Wait to finish tag switch just before issueing LMTDMA */ + if (tag_wait) + cvmx_pow_tag_sw_wait(); + + /* issue PKO DMA */ + cvmx_write64_uint64(dma_addr, pko_send_dma_data.u64); + + if (cvmx_unlikely(pko_send_dma_data.s.rtnlen)) { + /* Wait for LMTDMA completion */ + CVMX_SYNCIOBDMA; + + /* Retrieve real result */ + pko_status.u64 = cvmx_scratch_read64(scr_off); + pparam->depth = pko_status.s.depth; + } else { + /* Fake positive result */ + pko_status.s.dqop = dqop; + pko_status.s.dqstatus = PKO_DQSTATUS_PASS; + } + + return pko_status; +} + +/* + * @INTERNAL + * Sends PKO descriptor commands via CVMSEG LM and LMTDMA. + * @param node is the destination node + * @param dq is the destination descriptor queue. + * @param cmds[] is an array of 64-bit PKO3 headers/subheaders + * @param numwords is the number of outgoing words + * @param dqop is the operation code + * @return the PKO3 native query result structure. + * + * <numwords> must be between 1 and 15 for CVMX_PKO_DQ_SEND command + * otherwise it must be 0. + * + * NOTE: Internal use only. + */ +static inline cvmx_pko_query_rtn_t __cvmx_pko3_do_dma(u8 node, uint16_t dq, + u64 cmds[], + unsigned int numwords, + enum cvmx_pko_dqop dqop) +{ + const unsigned int scr_base = cvmx_pko3_lmtdma_scr_base(); + cvmx_pko_query_rtn_t pko_status; + cvmx_pko_lmtdma_data_t pko_send_dma_data; + u64 dma_addr; + unsigned int i, scr_off; + cvmx_pko3_dq_params_t *pparam; + + pparam = cvmx_pko3_dq_parameters(node, dq); + CVMX_PREFETCH0(pparam); + /* Push WB */ + CVMX_SYNCWS; + + pko_status.u64 = 0; + pko_send_dma_data.u64 = 0; + + if (cvmx_unlikely(numwords > 15)) { + debug("%s: ERROR: Internal error\n", __func__); + pko_status.u64 = ~0ull; + return pko_status; + } + + /* Store the command words into CVMSEG LM */ + for (i = 0, scr_off = scr_base; i < numwords; i++) { + cvmx_scratch_write64(scr_off, cmds[i]); + scr_off += sizeof(cmds[0]); + } + + /* With 0 data to send, this is an IOBDMA, else LMTDMA operation */ + if (numwords == 0) { + dma_addr = CVMX_IOBDMA_ORDERED_IO_ADDR; + } else { + /* LMTDMA address offset is (nWords-1) */ + dma_addr = CVMX_LMTDMA_ORDERED_IO_ADDR; + dma_addr += (numwords - 1) << 3; + } + + if (cvmx_likely(dqop == CVMX_PKO_DQ_SEND)) { + if (cvmx_unlikely(pparam->depth > pparam->limit)) { + cvmx_pko_dqx_wm_cnt_t wm_cnt; + + wm_cnt.u64 = csr_rd_node(node, CVMX_PKO_DQX_WM_CNT(dq)); + pko_status.s.depth = wm_cnt.s.count; + pparam->depth = pko_status.s.depth; + } + + if (cvmx_unlikely(pparam->depth > pparam->limit)) { + pko_status.s.dqop = dqop; + pko_status.s.dqstatus = PKO_DQSTATUS_NOFPABUF; + return pko_status; + } + + cvmx_atomic_add32_nosync(&pparam->depth, 1); + } + + if (cvmx_unlikely(dqop != CVMX_PKO_DQ_SEND) || + CVMX_ENABLE_PARAMETER_CHECKING) { + /* Request one return word */ + pko_send_dma_data.s.rtnlen = 1; + /* Write all-ones into the return area */ + cvmx_scratch_write64(scr_off, ~0ull); + } else { + /* Do not expext a return word */ + pko_send_dma_data.s.rtnlen = 0; + } + + /* build store data for DMA */ + pko_send_dma_data.s.scraddr = scr_off >> 3; + pko_send_dma_data.s.did = 0x51; + pko_send_dma_data.s.node = node; + pko_send_dma_data.s.dqop = dqop; + pko_send_dma_data.s.dq = dq; + + /* Barrier: make sure all prior writes complete before the following */ + CVMX_SYNCWS; + + /* Wait to finish tag switch just before issueing LMTDMA */ + cvmx_pow_tag_sw_wait(); + + /* issue PKO DMA */ + cvmx_write64_uint64(dma_addr, pko_send_dma_data.u64); + + if (pko_send_dma_data.s.rtnlen) { + /* Wait LMTDMA for completion */ + CVMX_SYNCIOBDMA; + + /* Retrieve real result */ + pko_status.u64 = cvmx_scratch_read64(scr_off); + pparam->depth = pko_status.s.depth; + } else { + /* Fake positive result */ + pko_status.s.dqop = dqop; + pko_status.s.dqstatus = PKO_DQSTATUS_PASS; + } + + return pko_status; +} + +/* + * Transmit packets through PKO, simplified API + * + * @INTERNAL + * + * @param dq is a global destination queue number + * @param pki_ptr specifies packet first linked pointer as returned from + * 'cvmx_wqe_get_pki_pkt_ptr()'. + * @param len is the total number of bytes in the packet. + * @param gaura is the aura to free packet buffers after trasnmit. + * @param pCounter is an address of a 64-bit counter to atomically + * @param ptag is a Flow Tag pointer for packet odering or NULL + * decrement when packet transmission is complete. + * + * @return returns 0 if successful and -1 on failure. + * + * + * NOTE: This is a provisional API, and is subject to change. + */ +static inline int cvmx_pko3_xmit_link_buf(int dq, cvmx_buf_ptr_pki_t pki_ptr, + unsigned int len, int gaura, + u64 *pcounter, u32 *ptag) +{ + cvmx_pko_query_rtn_t pko_status; + cvmx_pko_send_hdr_t hdr_s; + cvmx_pko_buf_ptr_t gtr_s; + unsigned int node, nwords; + unsigned int scr_base = cvmx_pko3_lmtdma_scr_base(); + + /* Separate global DQ# into node and local DQ */ + node = dq >> 10; + dq &= (1 << 10) - 1; + + /* Fill in header */ + hdr_s.u64 = 0; + hdr_s.s.total = len; + hdr_s.s.df = (gaura < 0); + hdr_s.s.ii = 1; + hdr_s.s.aura = (gaura >= 0) ? gaura : 0; + + /* Fill in gather */ + gtr_s.u64 = 0; + gtr_s.s.subdc3 = CVMX_PKO_SENDSUBDC_LINK; + gtr_s.s.addr = pki_ptr.addr; + gtr_s.s.size = pki_ptr.size; + + /* Setup command word pointers */ + cvmx_scratch_write64(scr_base + sizeof(u64) * 0, hdr_s.u64); + cvmx_scratch_write64(scr_base + sizeof(u64) * 1, gtr_s.u64); + nwords = 2; + + /* Conditionally setup an atomic decrement counter */ + if (pcounter) { + cvmx_pko_send_mem_t mem_s; + + mem_s.s.subdc4 = CVMX_PKO_SENDSUBDC_MEM; + mem_s.s.dsz = MEMDSZ_B64; + mem_s.s.alg = MEMALG_SUB; + mem_s.s.offset = 1; + mem_s.s.wmem = 0; + mem_s.s.addr = cvmx_ptr_to_phys(CASTPTR(void, pcounter)); + cvmx_scratch_write64(scr_base + sizeof(u64) * nwords++, + mem_s.u64); + } + + /* To preserve packet order, go atomic with DQ-specific tag */ + if (ptag) + cvmx_pow_tag_sw(*ptag ^ dq, CVMX_POW_TAG_TYPE_ATOMIC); + + /* Do LMTDMA */ + pko_status = __cvmx_pko3_lmtdma(node, dq, nwords, ptag); + + if (cvmx_likely(pko_status.s.dqstatus == PKO_DQSTATUS_PASS)) + return 0; + else + return -1; +} + +/** + * @INTERNAL + * + * Retrieve PKO internal AURA from register. + */ +static inline unsigned int __cvmx_pko3_aura_get(unsigned int node) +{ + static s16 aura = -1; + cvmx_pko_dpfi_fpa_aura_t pko_aura; + + if (aura >= 0) + return aura; + + pko_aura.u64 = csr_rd_node(node, CVMX_PKO_DPFI_FPA_AURA); + + aura = (pko_aura.s.node << 10) | pko_aura.s.laura; + return aura; +} + +/** Open configured descriptor queues before queueing packets into them. + * + * @param node is to specify the node to which this configuration is applied. + * @param dq is the descriptor queue number to be opened. + * @return returns 0 on success or -1 on failure. + */ +int cvmx_pko_dq_open(int node, int dq); + +/** Close a descriptor queue + * + * @param node is to specify the node to which this configuration is applied. + * @param dq is the descriptor queue number to be opened. + * @return returns 0 on success or -1 on failure. + * + * This should be called before changing the DQ parent link, topology, + * or when shutting down the PKO. + */ +int cvmx_pko3_dq_close(int node, int dq); + +/** Query a descriptor queue + * + * @param node is to specify the node to which this configuration is applied. + * @param dq is the descriptor queue number to be opened. + * @return returns the descriptor queue depth on success or -1 on failure. + * + * This should be called before changing the DQ parent link, topology, + * or when shutting down the PKO. + */ +int cvmx_pko3_dq_query(int node, int dq); + +/** Drain a descriptor queue + * + * Before closing a DQ, this call will drain all pending traffic + * on the DQ to the NULL MAC, which will circumvent any traffic + * shaping and flow control to quickly reclaim all packet buffers. + */ +void cvmx_pko3_dq_drain(int node, int dq); + +/* + * PKO global initialization for 78XX. + * + * @param node is the node on which PKO block is initialized. + * @param aura is the 12-bit AURA (including node) for PKO internal use. + * @return none. + */ +int cvmx_pko3_hw_init_global(int node, uint16_t aura); + +/** + * Shutdown the entire PKO + */ +int cvmx_pko3_hw_disable(int node); + +/* Define legacy type here to break circular dependency */ +typedef struct cvmx_pko_port_status cvmx_pko_port_status_t; + +/** + * @INTERNAL + * Backward compatibility for collecting statistics from PKO3 + * + */ +void cvmx_pko3_get_legacy_port_stats(u16 ipd_port, unsigned int clear, + cvmx_pko_port_status_t *status); + +/** Set MAC options + * + * The options supported are the parameters below: + * + * @param xiface The physical interface number + * @param index The physical sub-interface port + * @param fcs_enable Enable FCS generation + * @param pad_enable Enable padding to minimum packet size + * @param fcs_sop_off Number of bytes at start of packet to exclude from FCS + * + * The typical use for `fcs_sop_off` is when the interface is configured + * to use a header such as HighGig to precede every Ethernet packet, + * such a header usually does not partake in the CRC32 computation stream, + * and its size must be set with this parameter. + * + * @return Returns 0 on success, -1 if interface/port is invalid. + */ +int cvmx_pko3_interface_options(int xiface, int index, bool fcs_enable, + bool pad_enable, unsigned int fcs_sop_off); + +/** Set Descriptor Queue options + * + * The `min_pad` parameter must be in agreement with the interface-level + * padding option for all descriptor queues assigned to that particular + * interface/port. + */ +void cvmx_pko3_dq_options(unsigned int node, unsigned int dq, bool min_pad); + +int cvmx_pko3_port_fifo_size(unsigned int xiface, unsigned int index); +int cvmx_pko3_channel_credit_level(int node, enum cvmx_pko3_level_e level); +int cvmx_pko3_port_xoff(unsigned int xiface, unsigned int index); +int cvmx_pko3_port_xon(unsigned int xiface, unsigned int index); + +/* Packet descriptor - PKO3 command buffer + internal state */ +typedef struct cvmx_pko3_pdesc_s { + u64 *jump_buf; /**< jump buffer vaddr */ + s16 last_aura; /**< AURA of the latest LINK_S/GATHER_S */ + unsigned num_words : 5, /**< valid words in word array 2..16 */ + headroom : 10, /**< free bytes at start of 1st buf */ + hdr_offsets : 1, pki_word4_present : 1; + /* PKO3 command buffer: */ + cvmx_pko_send_hdr_t *hdr_s; + u64 word[16]; /**< header and subcommands buffer */ + /* Bookkeeping fields: */ + u64 send_work_s; /**< SEND_WORK_S must be the very last subdc */ + s16 jb_aura; /**< AURA where the jump buffer belongs */ + u16 mem_s_ix; /**< index of first MEM_S subcommand */ + u8 ckl4_alg; /**< L3/L4 alg to use if recalc is needed */ + /* Fields saved from WQE for later inspection */ + cvmx_pki_wqe_word4_t pki_word4; + cvmx_pki_wqe_word2_t pki_word2; +} cvmx_pko3_pdesc_t; + +void cvmx_pko3_pdesc_init(cvmx_pko3_pdesc_t *pdesc); +int cvmx_pko3_pdesc_from_wqe(cvmx_pko3_pdesc_t *pdesc, cvmx_wqe_78xx_t *wqe, + bool free_bufs); +int cvmx_pko3_pdesc_transmit(cvmx_pko3_pdesc_t *pdesc, uint16_t dq, + u32 *flow_tag); +int cvmx_pko3_pdesc_notify_decrement(cvmx_pko3_pdesc_t *pdesc, + volatile u64 *p_counter); +int cvmx_pko3_pdesc_notify_wqe(cvmx_pko3_pdesc_t *pdesc, cvmx_wqe_78xx_t *wqe, + u8 node, uint8_t group, uint8_t tt, u32 tag); +int cvmx_pko3_pdesc_buf_append(cvmx_pko3_pdesc_t *pdesc, void *p_data, + unsigned int data_bytes, unsigned int gaura); +int cvmx_pko3_pdesc_append_free(cvmx_pko3_pdesc_t *pdesc, u64 addr, + unsigned int gaura); +int cvmx_pko3_pdesc_hdr_push(cvmx_pko3_pdesc_t *pdesc, const void *p_data, + u8 data_bytes, uint8_t layer); +int cvmx_pko3_pdesc_hdr_pop(cvmx_pko3_pdesc_t *pdesc, void *hdr_buf, + unsigned int num_bytes); +int cvmx_pko3_pdesc_hdr_peek(cvmx_pko3_pdesc_t *pdesc, void *hdr_buf, + unsigned int num_bytes, unsigned int offset); +void cvmx_pko3_pdesc_set_free(cvmx_pko3_pdesc_t *pdesc, bool free_bufs); + +#endif /* __CVMX_PKO3_H__ */ diff --git a/arch/mips/mach-octeon/include/mach/cvmx-range.h b/arch/mips/mach-octeon/include/mach/cvmx-range.h new file mode 100644 index 0000000..f0c1307 --- /dev/null +++ b/arch/mips/mach-octeon/include/mach/cvmx-range.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018-2022 Marvell International Ltd. + */ + +#ifndef __CVMX_RANGE_H__ +#define __CVMX_RANGE_H__ + +int cvmx_range_init(u64 range_addr, int size); +int cvmx_range_alloc(u64 range_addr, uint64_t owner, uint64_t cnt, int align); +int cvmx_range_alloc_ordered(u64 range_addr, uint64_t owner, u64 cnt, int align, + int reverse); +int cvmx_range_alloc_non_contiguos(u64 range_addr, uint64_t owner, u64 cnt, + int elements[]); +int cvmx_range_reserve(u64 range_addr, uint64_t owner, u64 base, uint64_t cnt); +int cvmx_range_free_with_base(u64 range_addr, int base, int cnt); +int cvmx_range_free_with_owner(u64 range_addr, uint64_t owner); +u64 cvmx_range_get_owner(u64 range_addr, uint64_t base); +void cvmx_range_show(uint64_t range_addr); +int cvmx_range_memory_size(int nelements); +int cvmx_range_free_mutiple(u64 range_addr, int bases[], int count); + +#endif // __CVMX_RANGE_H__ diff --git a/arch/mips/mach-octeon/include/mach/cvmx-regs.h b/arch/mips/mach-octeon/include/mach/cvmx-regs.h index dbb7723..f97c1e9 100644 --- a/arch/mips/mach-octeon/include/mach/cvmx-regs.h +++ b/arch/mips/mach-octeon/include/mach/cvmx-regs.h @@ -6,6 +6,7 @@ #ifndef __CVMX_REGS_H__ #define __CVMX_REGS_H__ +#include <log.h> #include <linux/bitfield.h> #include <linux/bitops.h> #include <linux/io.h> @@ -32,6 +33,7 @@ /* Regs */ #define CVMX_CIU3_NMI 0x0001010000000160ULL +#define CVMX_CIU3_ISCX_W1C(x) (0x0001010090000000ull + ((x) & 1048575) * 8) #define CVMX_MIO_BOOT_LOC_CFGX(x) (0x0001180000000080ULL + ((x) & 1) * 8) #define MIO_BOOT_LOC_CFG_BASE GENMASK_ULL(27, 3) @@ -55,11 +57,19 @@ #define CVMX_RNM_CTL_STATUS 0x0001180040000000ULL #define RNM_CTL_STATUS_EER_VAL BIT_ULL(9) +/* IOBDMA/LMTDMA IO addresses */ +#define CVMX_LMTDMA_ORDERED_IO_ADDR 0xffffffffffffa400ull #define CVMX_IOBDMA_ORDERED_IO_ADDR 0xffffffffffffa200ull /* turn the variable name into a string */ #define CVMX_TMP_STR(x) CVMX_TMP_STR2(x) #define CVMX_TMP_STR2(x) #x +#define VASTR(...) #__VA_ARGS__ + +#define CVMX_PKO_LMTLINE 2ull +#define CVMX_SCRATCH_BASE (-32768l) /* 0xffffffffffff8000 */ + +#define COP0_CVMMEMCTL $11,7 /* Cavium memory control */ #define CVMX_RDHWR(result, regstr) \ asm volatile("rdhwr %[rt],$" CVMX_TMP_STR(regstr) : [rt] "=d"(result)) @@ -67,6 +77,13 @@ asm("rdhwr %[rt],$" CVMX_TMP_STR(regstr) : [rt] "=d"(result)) #define CVMX_POP(result, input) \ asm("pop %[rd],%[rs]" : [rd] "=d"(result) : [rs] "d"(input)) +#define CVMX_MF_COP0(val, cop0) \ + asm("dmfc0 %[rt]," VASTR(cop0) : [rt] "=d" (val)) +#define CVMX_MT_COP0(val, cop0) \ + asm("dmtc0 %[rt]," VASTR(cop0) : : [rt] "d" (val)) + +#define CVMX_MF_CVM_MEM_CTL(val) CVMX_MF_COP0(val, COP0_CVMMEMCTL) +#define CVMX_MT_CVM_MEM_CTL(val) CVMX_MT_COP0(val, COP0_CVMMEMCTL) #define CVMX_SYNC asm volatile("sync\n" : : : "memory") #define CVMX_SYNCW asm volatile("syncw\nsyncw\n" : : : "memory") @@ -81,6 +98,18 @@ #define CVMX_MF_CHORD(dest) CVMX_RDHWR(dest, 30) +#define CVMX_PREFETCH0(address) CVMX_PREFETCH(address, 0) +#define CVMX_PREFETCH128(address) CVMX_PREFETCH(address, 128) + +/** a normal prefetch */ +#define CVMX_PREFETCH(address, offset) CVMX_PREFETCH_PREF0(address, offset) + +/** normal prefetches that use the pref instruction */ +#define CVMX_PREFETCH_PREFX(X, address, offset) \ + asm volatile ("pref %[type], %[off](%[rbase])" : : [rbase] "d" (address), [off] "I" (offset), [type] "n" (X)) +#define CVMX_PREFETCH_PREF0(address, offset) \ + CVMX_PREFETCH_PREFX(0, address, offset) + /* * The macros cvmx_likely and cvmx_unlikely use the * __builtin_expect GCC operation to control branch @@ -406,6 +435,30 @@ static inline unsigned int cvmx_get_local_core_num(void) } /** + * Given a CSR address return the node number of that address + * + * @param addr Address to extract node number from + * + * @return node number + */ +static inline u8 cvmx_csr_addr_to_node(u64 addr) +{ + return (addr >> CVMX_NODE_IO_SHIFT) & CVMX_NODE_MASK; +} + +/** + * Strip the node address bits from a CSR address + * + * @param addr CSR address to strip the node bits from + * + * @return CSR address with the node bits set to zero + */ +static inline u64 cvmx_csr_addr_strip_node(u64 addr) +{ + return addr & ~((u64)CVMX_NODE_MASK << CVMX_NODE_IO_SHIFT); +} + +/** * Returns the number of bits set in the provided value. * Simple wrapper for POP instruction. * @@ -428,14 +481,45 @@ static inline u32 cvmx_pop(u32 val) #define cvmx_printf printf #define cvmx_vprintf vprintf -#if defined(DEBUG) -void cvmx_warn(const char *format, ...) __printf(1, 2); -#else -void cvmx_warn(const char *format, ...); -#endif +/* Use common debug macros */ +#define cvmx_warn debug +#define cvmx_warn_if debug_cond + +/** + * Atomically adds a signed value to a 32 bit (aligned) memory location, + * and returns previous value. + * + * Memory access ordering is enforced before/after the atomic operation, + * so no additional 'sync' instructions are required. + * + * @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 int32_t cvmx_atomic_fetch_and_add32(int32_t * ptr, int32_t incr) +{ + int32_t val; -#define cvmx_warn_if(expression, format, ...) \ - if (expression) \ - cvmx_warn(format, ##__VA_ARGS__) + val = *ptr; + *ptr += incr; + return val; +} + +/** + * Atomically adds a signed value to a 32 bit (aligned) memory location. + * + * 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) + */ +static inline void cvmx_atomic_add32_nosync(int32_t * ptr, int32_t incr) +{ + *ptr += incr; +} #endif /* __CVMX_REGS_H__ */ diff --git a/arch/mips/mach-octeon/include/mach/cvmx-xcv-defs.h b/arch/mips/mach-octeon/include/mach/cvmx-xcv-defs.h new file mode 100644 index 0000000..4fd4d16 --- /dev/null +++ b/arch/mips/mach-octeon/include/mach/cvmx-xcv-defs.h @@ -0,0 +1,226 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018-2022 Marvell International Ltd. + * + * Configuration and status register (CSR) type definitions for + * Octeon xcv. + */ + +#ifndef __CVMX_XCV_DEFS_H__ +#define __CVMX_XCV_DEFS_H__ + +#define CVMX_XCV_BATCH_CRD_RET (0x00011800DB000100ull) +#define CVMX_XCV_COMP_CTL (0x00011800DB000020ull) +#define CVMX_XCV_CTL (0x00011800DB000030ull) +#define CVMX_XCV_DLL_CTL (0x00011800DB000010ull) +#define CVMX_XCV_ECO (0x00011800DB000200ull) +#define CVMX_XCV_INBND_STATUS (0x00011800DB000080ull) +#define CVMX_XCV_INT (0x00011800DB000040ull) +#define CVMX_XCV_RESET (0x00011800DB000000ull) + +/** + * cvmx_xcv_batch_crd_ret + */ +union cvmx_xcv_batch_crd_ret { + u64 u64; + struct cvmx_xcv_batch_crd_ret_s { + u64 reserved_1_63 : 63; + u64 crd_ret : 1; + } s; + struct cvmx_xcv_batch_crd_ret_s cn73xx; +}; + +typedef union cvmx_xcv_batch_crd_ret cvmx_xcv_batch_crd_ret_t; + +/** + * cvmx_xcv_comp_ctl + * + * This register controls programmable compensation. + * + */ +union cvmx_xcv_comp_ctl { + u64 u64; + struct cvmx_xcv_comp_ctl_s { + u64 drv_byp : 1; + u64 reserved_61_62 : 2; + u64 cmp_pctl : 5; + u64 reserved_53_55 : 3; + u64 cmp_nctl : 5; + u64 reserved_45_47 : 3; + u64 drv_pctl : 5; + u64 reserved_37_39 : 3; + u64 drv_nctl : 5; + u64 reserved_31_31 : 1; + u64 pctl_lock : 1; + u64 pctl_sat : 1; + u64 reserved_28_28 : 1; + u64 nctl_lock : 1; + u64 reserved_1_26 : 26; + u64 nctl_sat : 1; + } s; + struct cvmx_xcv_comp_ctl_s cn73xx; +}; + +typedef union cvmx_xcv_comp_ctl cvmx_xcv_comp_ctl_t; + +/** + * cvmx_xcv_ctl + * + * This register contains the status control bits. + * + */ +union cvmx_xcv_ctl { + u64 u64; + struct cvmx_xcv_ctl_s { + u64 reserved_4_63 : 60; + u64 lpbk_ext : 1; + u64 lpbk_int : 1; + u64 speed : 2; + } s; + struct cvmx_xcv_ctl_s cn73xx; +}; + +typedef union cvmx_xcv_ctl cvmx_xcv_ctl_t; + +/** + * cvmx_xcv_dll_ctl + * + * The RGMII timing specification requires that devices transmit clock and + * data synchronously. The specification requires external sources (namely + * the PC board trace routes) to introduce the appropriate 1.5 to 2.0 ns of + * delay. + * + * To eliminate the need for the PC board delays, the RGMII interface has optional + * on-board DLLs for both transmit and receive. For correct operation, at most one + * of the transmitter, board, or receiver involved in an RGMII link should + * introduce delay. By default/reset, the RGMII receivers delay the received clock, + * and the RGMII transmitters do not delay the transmitted clock. Whether this + * default works as-is with a given link partner depends on the behavior of the + * link partner and the PC board. + * + * These are the possible modes of RGMII receive operation: + * + * * XCV_DLL_CTL[CLKRX_BYP] = 0 (reset value) - The RGMII + * receive interface introduces clock delay using its internal DLL. + * This mode is appropriate if neither the remote + * transmitter nor the PC board delays the clock. + * + * * XCV_DLL_CTL[CLKRX_BYP] = 1, [CLKRX_SET] = 0x0 - The + * RGMII receive interface introduces no clock delay. This mode + * is appropriate if either the remote transmitter or the PC board + * delays the clock. + * + * These are the possible modes of RGMII transmit operation: + * + * * XCV_DLL_CTL[CLKTX_BYP] = 1, [CLKTX_SET] = 0x0 (reset value) - + * The RGMII transmit interface introduces no clock + * delay. This mode is appropriate is either the remote receiver + * or the PC board delays the clock. + * + * * XCV_DLL_CTL[CLKTX_BYP] = 0 - The RGMII transmit + * interface introduces clock delay using its internal DLL. + * This mode is appropriate if neither the remote receiver + * nor the PC board delays the clock. + */ +union cvmx_xcv_dll_ctl { + u64 u64; + struct cvmx_xcv_dll_ctl_s { + u64 reserved_32_63 : 32; + u64 lock : 1; + u64 clk_set : 7; + u64 clkrx_byp : 1; + u64 clkrx_set : 7; + u64 clktx_byp : 1; + u64 clktx_set : 7; + u64 reserved_2_7 : 6; + u64 refclk_sel : 2; + } s; + struct cvmx_xcv_dll_ctl_s cn73xx; +}; + +typedef union cvmx_xcv_dll_ctl cvmx_xcv_dll_ctl_t; + +/** + * cvmx_xcv_eco + */ +union cvmx_xcv_eco { + u64 u64; + struct cvmx_xcv_eco_s { + u64 reserved_16_63 : 48; + u64 eco_rw : 16; + } s; + struct cvmx_xcv_eco_s cn73xx; +}; + +typedef union cvmx_xcv_eco cvmx_xcv_eco_t; + +/** + * cvmx_xcv_inbnd_status + * + * This register contains RGMII in-band status. + * + */ +union cvmx_xcv_inbnd_status { + u64 u64; + struct cvmx_xcv_inbnd_status_s { + u64 reserved_4_63 : 60; + u64 duplex : 1; + u64 speed : 2; + u64 link : 1; + } s; + struct cvmx_xcv_inbnd_status_s cn73xx; +}; + +typedef union cvmx_xcv_inbnd_status cvmx_xcv_inbnd_status_t; + +/** + * cvmx_xcv_int + * + * This register controls interrupts. + * + */ +union cvmx_xcv_int { + u64 u64; + struct cvmx_xcv_int_s { + u64 reserved_7_63 : 57; + u64 tx_ovrflw : 1; + u64 tx_undflw : 1; + u64 incomp_byte : 1; + u64 duplex : 1; + u64 reserved_2_2 : 1; + u64 speed : 1; + u64 link : 1; + } s; + struct cvmx_xcv_int_s cn73xx; +}; + +typedef union cvmx_xcv_int cvmx_xcv_int_t; + +/** + * cvmx_xcv_reset + * + * This register controls reset. + * + */ +union cvmx_xcv_reset { + u64 u64; + struct cvmx_xcv_reset_s { + u64 enable : 1; + u64 reserved_16_62 : 47; + u64 clkrst : 1; + u64 reserved_12_14 : 3; + u64 dllrst : 1; + u64 reserved_8_10 : 3; + u64 comp : 1; + u64 reserved_4_6 : 3; + u64 tx_pkt_rst_n : 1; + u64 tx_dat_rst_n : 1; + u64 rx_pkt_rst_n : 1; + u64 rx_dat_rst_n : 1; + } s; + struct cvmx_xcv_reset_s cn73xx; +}; + +typedef union cvmx_xcv_reset cvmx_xcv_reset_t; + +#endif diff --git a/arch/mips/mach-octeon/include/mach/octeon_eth.h b/arch/mips/mach-octeon/include/mach/octeon_eth.h index 096fcfb..83e6207 100644 --- a/arch/mips/mach-octeon/include/mach/octeon_eth.h +++ b/arch/mips/mach-octeon/include/mach/octeon_eth.h @@ -1,17 +1,13 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (C) 2020 Marvell International Ltd. + * Copyright (C) 2020-2022 Marvell International Ltd. */ #ifndef __OCTEON_ETH_H__ #define __OCTEON_ETH_H__ -#include <phy.h> -#include <miiphy.h> - #include <mach/cvmx-helper.h> #include <mach/cvmx-helper-board.h> -#include <mach/octeon_fdt.h> struct eth_device; @@ -27,33 +23,25 @@ struct octeon_eth_info { struct phy_device *phydev; /** PHY device */ struct eth_device *ethdev; /** Eth device this priv is part of */ int mii_addr; - int phy_fdt_offset; /** Offset of PHY info in device tree */ - int fdt_offset; /** Offset of Eth interface in DT */ - int phy_offset; /** Offset of PHY dev in device tree */ + int phy_fdt_offset; /** Offset of PHY info in device tree */ + int fdt_offset; /** Offset of Eth interface in DT */ + int phy_offset; /** Offset of PHY device in device tree */ enum cvmx_phy_type phy_device_type; /** Type of PHY */ /* current link status, use to reconfigure on status changes */ u64 packets_sent; u64 packets_received; - u32 link_speed : 2; - u32 link_duplex : 1; - u32 link_status : 1; - u32 loopback : 1; - u32 enabled : 1; - u32 is_c45 : 1; /** Set if we need to use clause 45 */ - u32 vitesse_sfp_config : 1; /** Need Vitesse SFP config */ - u32 ti_gpio_config : 1; /** Need TI GPIO config */ - u32 bgx_mac_set : 1; /** Has the BGX MAC been set already */ - u64 last_bgx_mac; /** Last BGX MAC address set */ - u64 gmx_base; /** Base address to access GMX CSRs */ - bool mod_abs; /** True if module is absent */ - - /** - * User defined function to check if a SFP+ module is absent or not. - * - * @param dev Ethernet device - * @param data User supplied data - */ - int (*check_mod_abs)(struct eth_device *dev, void *data); + uint32_t link_speed : 2; + uint32_t link_duplex : 1; + uint32_t link_status : 1; + uint32_t loopback : 1; + uint32_t enabled : 1; + uint32_t is_c45 : 1; /** Set if we need to use clause 45 */ + uint32_t vitesse_sfp_config : 1; /** Need Vitesse SFP config */ + uint32_t ti_gpio_config : 1; /** Need TI GPIO configuration */ + uint32_t bgx_mac_set : 1; /** Has the BGX MAC been set already */ + u64 last_bgx_mac; /** Last BGX MAC address set */ + u64 gmx_base; /** Base address to access GMX CSRs */ + bool mod_abs; /** True if module is absent */ /** User supplied data for check_mod_abs */ void *mod_abs_data; @@ -71,12 +59,20 @@ struct octeon_eth_info { * @return 0 for success, otherwise error */ int (*mod_abs_changed)(struct eth_device *dev, bool mod_abs); + /** SDK phy information data structure */ cvmx_phy_info_t phy_info; + + struct udevice *mdio_dev; + struct mii_dev *bus; + struct phy_device *phy_dev; + #ifdef CONFIG_OCTEON_SFP /** Information about connected SFP/SFP+/SFP28/QSFP+/QSFP28 module */ struct octeon_sfp_info sfp; #endif + + cvmx_wqe_t *work; }; /** @@ -136,6 +132,6 @@ void octeon_eth_register_mod_abs_changed(struct eth_device *dev, * * NOTE: If the module state is changed then the module callback is called. */ -void octeon_phy_port_check(struct eth_device *dev); +void octeon_phy_port_check(struct udevice *dev); #endif /* __OCTEON_ETH_H__ */ diff --git a/board/CZ.NIC/turris_mox/turris_mox.c b/board/CZ.NIC/turris_mox/turris_mox.c index 9702d1f..a4738b3 100644 --- a/board/CZ.NIC/turris_mox/turris_mox.c +++ b/board/CZ.NIC/turris_mox/turris_mox.c @@ -13,6 +13,7 @@ #include <button.h> #include <clk.h> #include <dm.h> +#include <dm/of_extra.h> #include <env.h> #include <fdt_support.h> #include <init.h> @@ -216,35 +217,35 @@ static int mox_get_topology(const u8 **ptopology, int *psize, int *pis_sd) #define SW_SMI_CMD_R(d, r) (0x9800 | (((d) & 0x1f) << 5) | ((r) & 0x1f)) #define SW_SMI_CMD_W(d, r) (0x9400 | (((d) & 0x1f) << 5) | ((r) & 0x1f)) -static int sw_multi_read(struct mii_dev *bus, int sw, int dev, int reg) +static int sw_multi_read(struct udevice *bus, int sw, int dev, int reg) { - bus->write(bus, sw, 0, 0, SW_SMI_CMD_R(dev, reg)); + dm_mdio_write(bus, sw, MDIO_DEVAD_NONE, 0, SW_SMI_CMD_R(dev, reg)); mdelay(5); - return bus->read(bus, sw, 0, 1); + return dm_mdio_read(bus, sw, MDIO_DEVAD_NONE, 1); } -static void sw_multi_write(struct mii_dev *bus, int sw, int dev, int reg, +static void sw_multi_write(struct udevice *bus, int sw, int dev, int reg, u16 val) { - bus->write(bus, sw, 0, 1, val); - bus->write(bus, sw, 0, 0, SW_SMI_CMD_W(dev, reg)); + dm_mdio_write(bus, sw, MDIO_DEVAD_NONE, 1, val); + dm_mdio_write(bus, sw, MDIO_DEVAD_NONE, 0, SW_SMI_CMD_W(dev, reg)); mdelay(5); } -static int sw_scratch_read(struct mii_dev *bus, int sw, int reg) +static int sw_scratch_read(struct udevice *bus, int sw, int reg) { sw_multi_write(bus, sw, 0x1c, 0x1a, (reg & 0x7f) << 8); return sw_multi_read(bus, sw, 0x1c, 0x1a) & 0xff; } -static void sw_led_write(struct mii_dev *bus, int sw, int port, int reg, +static void sw_led_write(struct udevice *bus, int sw, int port, int reg, u16 val) { sw_multi_write(bus, sw, port, 0x16, 0x8000 | ((reg & 7) << 12) | (val & 0x7ff)); } -static void sw_blink_leds(struct mii_dev *bus, int peridot, int topaz) +static void sw_blink_leds(struct udevice *bus, int peridot, int topaz) { int i, p; struct { @@ -275,7 +276,7 @@ static void sw_blink_leds(struct mii_dev *bus, int peridot, int topaz) } } -static void check_switch_address(struct mii_dev *bus, int addr) +static void check_switch_address(struct udevice *bus, int addr) { if (sw_scratch_read(bus, addr, 0x70) >> 3 != addr) printf("Check of switch MDIO address failed for 0x%02x\n", @@ -374,36 +375,22 @@ static void mox_phy_modify(struct phy_device *phydev, int page, int reg, static void mox_phy_leds_start_blinking(void) { struct phy_device *phydev; - struct mii_dev *bus; - const char *node_name; - int node; + ofnode phy_node; - node = fdt_path_offset(gd->fdt_blob, "ethernet0"); - if (node < 0) { - printf("Cannot get eth0!\n"); - return; - } + phy_node = ofnode_get_phy_node(ofnode_path("ethernet0")); + if (!ofnode_valid(phy_node)) + goto err; - node_name = fdt_get_name(gd->fdt_blob, node, NULL); - if (!node_name) { - printf("Cannot get eth0 node name!\n"); - return; - } - - bus = miiphy_get_dev_by_name(node_name); - if (!bus) { - printf("Cannot get MDIO bus device!\n"); - return; - } - - phydev = phy_find_by_mask(bus, BIT(1)); - if (!phydev) { - printf("Cannot get ethernet PHY!\n"); - return; - } + phydev = dm_phy_find_by_ofnode(phy_node); + if (!phydev) + goto err; mox_phy_modify(phydev, 3, 0x12, 0x700, 0x400); mox_phy_modify(phydev, 3, 0x10, 0xff, 0xbb); + + return; +err: + printf("Cannot get ethernet PHY!\n"); } static bool read_reset_button(void) @@ -611,6 +598,26 @@ int show_board_info(void) return 0; } +static struct udevice *mox_mdio_bus(void) +{ + struct udevice *bus; + ofnode node; + + node = ofnode_by_compatible(ofnode_null(), "marvell,orion-mdio"); + if (!ofnode_valid(node)) + goto err; + + dm_mdio_probe_devices(); + + if (uclass_get_device_by_ofnode(UCLASS_MDIO, node, &bus)) + goto err; + + return bus; +err: + printf("Cannot get MDIO bus device!\n"); + return NULL; +} + int last_stage_init(void) { struct gpio_desc reset_gpio = {}; @@ -636,16 +643,9 @@ int last_stage_init(void) * 0x70 of Peridot (and potentially Topaz) modules */ if (peridot || topaz) { - struct mii_dev *bus; - const char *node_name; - int node; + struct udevice *bus = mox_mdio_bus(); - node = fdt_path_offset(gd->fdt_blob, "ethernet0"); - node_name = (node >= 0) ? fdt_get_name(gd->fdt_blob, node, NULL) : NULL; - bus = node_name ? miiphy_get_dev_by_name(node_name) : NULL; - if (!bus) { - printf("Cannot get MDIO bus device!\n"); - } else { + if (bus) { int i; for (i = 0; i < peridot; ++i) diff --git a/board/Marvell/mvebu_armada-37xx/board.c b/board/Marvell/mvebu_armada-37xx/board.c index 98e1b36..3e5e0a0 100644 --- a/board/Marvell/mvebu_armada-37xx/board.c +++ b/board/Marvell/mvebu_armada-37xx/board.c @@ -11,6 +11,7 @@ #include <i2c.h> #include <init.h> #include <mmc.h> +#include <miiphy.h> #include <phy.h> #include <asm/global_data.h> #include <asm/io.h> @@ -254,14 +255,15 @@ int board_xhci_enable(fdt_addr_t base) return 0; } +#ifdef CONFIG_LAST_STAGE_INIT /* Helper function for accessing switch devices in multi-chip connection mode */ -static int mii_multi_chip_mode_write(struct mii_dev *bus, int dev_smi_addr, +static int mii_multi_chip_mode_write(struct udevice *bus, int dev_smi_addr, int smi_addr, int reg, u16 value) { u16 smi_cmd = 0; - if (bus->write(bus, dev_smi_addr, 0, - MVEBU_SW_SMI_DATA_REG, value) != 0) { + if (dm_mdio_write(bus, dev_smi_addr, MDIO_DEVAD_NONE, + MVEBU_SW_SMI_DATA_REG, value) != 0) { printf("Error writing to the PHY addr=%02x reg=%02x\n", smi_addr, reg); return -EFAULT; @@ -272,8 +274,8 @@ static int mii_multi_chip_mode_write(struct mii_dev *bus, int dev_smi_addr, (1 << SW_SMI_CMD_SMI_OP_OFF) | (smi_addr << SW_SMI_CMD_DEV_ADDR_OFF) | (reg << SW_SMI_CMD_REG_ADDR_OFF); - if (bus->write(bus, dev_smi_addr, 0, - MVEBU_SW_SMI_CMD_REG, smi_cmd) != 0) { + if (dm_mdio_write(bus, dev_smi_addr, MDIO_DEVAD_NONE, + MVEBU_SW_SMI_CMD_REG, smi_cmd) != 0) { printf("Error writing to the PHY addr=%02x reg=%02x\n", smi_addr, reg); return -EFAULT; @@ -283,11 +285,22 @@ static int mii_multi_chip_mode_write(struct mii_dev *bus, int dev_smi_addr, } /* Bring-up board-specific network stuff */ -int board_network_enable(struct mii_dev *bus) +int last_stage_init(void) { + struct udevice *bus; + ofnode node; + if (!of_machine_is_compatible("globalscale,espressobin")) return 0; + node = ofnode_by_compatible(ofnode_null(), "marvell,orion-mdio"); + if (!ofnode_valid(node) || + uclass_get_device_by_ofnode(UCLASS_MDIO, node, &bus) || + device_probe(bus)) { + printf("Cannot find MDIO bus\n"); + return 0; + } + /* * FIXME: remove this code once Topaz driver gets available * A3720 Community Board Only @@ -327,6 +340,7 @@ int board_network_enable(struct mii_dev *bus) return 0; } +#endif #ifdef CONFIG_OF_BOARD_SETUP int ft_board_setup(void *blob, struct bd_info *bd) diff --git a/board/Marvell/octeon_nic23/board.c b/board/Marvell/octeon_nic23/board.c index 9f5eb2e..3e2c544 100644 --- a/board/Marvell/octeon_nic23/board.c +++ b/board/Marvell/octeon_nic23/board.c @@ -1,10 +1,11 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * Copyright (C) 2021 Stefan Roese <sr@denx.de> + * Copyright (C) 2021-2022 Stefan Roese <sr@denx.de> */ #include <dm.h> #include <ram.h> +#include <asm/gpio.h> #include <mach/octeon_ddr.h> #include <mach/cvmx-qlm.h> @@ -84,6 +85,52 @@ int board_fix_fdt(void *fdt) return rc; } +int board_early_init_f(void) +{ + struct gpio_desc gpio = {}; + ofnode node; + + /* Initial GPIO configuration */ + + /* GPIO 7: Vitesse reset */ + node = ofnode_by_compatible(ofnode_null(), "vitesse,vsc7224"); + if (ofnode_valid(node)) { + gpio_request_by_name_nodev(node, "los", 0, &gpio, GPIOD_IS_IN); + dm_gpio_free(gpio.dev, &gpio); + gpio_request_by_name_nodev(node, "reset", 0, &gpio, + GPIOD_IS_OUT); + if (dm_gpio_is_valid(&gpio)) { + /* Vitesse reset */ + debug("%s: Setting GPIO 7 to 1\n", __func__); + dm_gpio_set_value(&gpio, 1); + } + dm_gpio_free(gpio.dev, &gpio); + } + + /* SFP+ transmitters */ + ofnode_for_each_compatible_node(node, "ethernet,sfp-slot") { + gpio_request_by_name_nodev(node, "tx_disable", 0, + &gpio, GPIOD_IS_OUT); + if (dm_gpio_is_valid(&gpio)) { + debug("%s: Setting GPIO %d to 1\n", __func__, + gpio.offset); + dm_gpio_set_value(&gpio, 1); + } + dm_gpio_free(gpio.dev, &gpio); + gpio_request_by_name_nodev(node, "mod_abs", 0, &gpio, + GPIOD_IS_IN); + dm_gpio_free(gpio.dev, &gpio); + gpio_request_by_name_nodev(node, "tx_error", 0, &gpio, + GPIOD_IS_IN); + dm_gpio_free(gpio.dev, &gpio); + gpio_request_by_name_nodev(node, "rx_los", 0, &gpio, + GPIOD_IS_IN); + dm_gpio_free(gpio.dev, &gpio); + } + + return 0; +} + void board_configure_qlms(void) { octeon_configure_qlm(4, 3000, CVMX_QLM_MODE_SATA_2X1, 0, 0, 0, 0); @@ -100,7 +147,45 @@ void board_configure_qlms(void) int board_late_init(void) { + struct gpio_desc gpio = {}; + ofnode node; + + /* Turn on SFP+ transmitters */ + ofnode_for_each_compatible_node(node, "ethernet,sfp-slot") { + gpio_request_by_name_nodev(node, "tx_disable", 0, + &gpio, GPIOD_IS_OUT); + if (dm_gpio_is_valid(&gpio)) { + debug("%s: Setting GPIO %d to 0\n", __func__, + gpio.offset); + dm_gpio_set_value(&gpio, 0); + } + dm_gpio_free(gpio.dev, &gpio); + } + board_configure_qlms(); return 0; } + +int last_stage_init(void) +{ + struct gpio_desc gpio = {}; + ofnode node; + + node = ofnode_by_compatible(ofnode_null(), "vitesse,vsc7224"); + if (!ofnode_valid(node)) { + printf("Vitesse SPF DT node not found!"); + return 0; + } + + gpio_request_by_name_nodev(node, "reset", 0, &gpio, GPIOD_IS_OUT); + if (dm_gpio_is_valid(&gpio)) { + /* Take Vitesse retimer out of reset */ + debug("%s: Setting GPIO 7 to 0\n", __func__); + dm_gpio_set_value(&gpio, 0); + mdelay(50); + } + dm_gpio_free(gpio.dev, &gpio); + + return 0; +} diff --git a/configs/clearfog_defconfig b/configs/clearfog_defconfig index 1e9c389..a7b6508 100644 --- a/configs/clearfog_defconfig +++ b/configs/clearfog_defconfig @@ -62,6 +62,7 @@ CONFIG_PHY_MARVELL=y CONFIG_PHY_GIGE=y CONFIG_MVNETA=y CONFIG_MII=y +CONFIG_MVMDIO=y CONFIG_PCI=y CONFIG_PCI_MVEBU=y CONFIG_SCSI=y diff --git a/configs/controlcenterdc_defconfig b/configs/controlcenterdc_defconfig index d4b966b..df38b2c 100644 --- a/configs/controlcenterdc_defconfig +++ b/configs/controlcenterdc_defconfig @@ -77,6 +77,7 @@ CONFIG_PHY_MARVELL=y CONFIG_PHY_GIGE=y CONFIG_MVNETA=y CONFIG_MII=y +CONFIG_MVMDIO=y CONFIG_PCI=y CONFIG_DM_PCI_COMPAT=y CONFIG_PCI_MVEBU=y diff --git a/configs/db-88f6820-amc_defconfig b/configs/db-88f6820-amc_defconfig index 46c822f..9b77b4a 100644 --- a/configs/db-88f6820-amc_defconfig +++ b/configs/db-88f6820-amc_defconfig @@ -67,6 +67,7 @@ CONFIG_PHY_MARVELL=y CONFIG_PHY_GIGE=y CONFIG_MVNETA=y CONFIG_MII=y +CONFIG_MVMDIO=y CONFIG_PCI=y CONFIG_PCI_MVEBU=y CONFIG_DEBUG_UART_SHIFT=2 diff --git a/configs/db-88f6820-gp_defconfig b/configs/db-88f6820-gp_defconfig index 2dcbc2f..f56d1fb 100644 --- a/configs/db-88f6820-gp_defconfig +++ b/configs/db-88f6820-gp_defconfig @@ -62,6 +62,7 @@ CONFIG_PHY_MARVELL=y CONFIG_PHY_GIGE=y CONFIG_MVNETA=y CONFIG_MII=y +CONFIG_MVMDIO=y CONFIG_PCI=y CONFIG_PCI_MVEBU=y CONFIG_SCSI=y diff --git a/configs/db-mv784mp-gp_defconfig b/configs/db-mv784mp-gp_defconfig index f19cc54..5683f11 100644 --- a/configs/db-mv784mp-gp_defconfig +++ b/configs/db-mv784mp-gp_defconfig @@ -65,6 +65,7 @@ CONFIG_PHY_MARVELL=y CONFIG_PHY_GIGE=y CONFIG_MVNETA=y CONFIG_MII=y +CONFIG_MVMDIO=y CONFIG_PCI=y CONFIG_PCI_MVEBU=y CONFIG_DEBUG_UART_SHIFT=2 diff --git a/configs/ds414_defconfig b/configs/ds414_defconfig index a3279c1..a83fe07 100644 --- a/configs/ds414_defconfig +++ b/configs/ds414_defconfig @@ -65,6 +65,7 @@ CONFIG_PHY_MARVELL=y CONFIG_PHY_GIGE=y CONFIG_MVNETA=y CONFIG_MII=y +CONFIG_MVMDIO=y CONFIG_PCI=y CONFIG_PCI_MVEBU=y CONFIG_DEBUG_UART_SHIFT=2 diff --git a/configs/helios4_defconfig b/configs/helios4_defconfig index 7d812e8..c2130ba 100644 --- a/configs/helios4_defconfig +++ b/configs/helios4_defconfig @@ -63,6 +63,7 @@ CONFIG_PHY_MARVELL=y CONFIG_PHY_GIGE=y CONFIG_MVNETA=y CONFIG_MII=y +CONFIG_MVMDIO=y CONFIG_PCI=y CONFIG_PCI_MVEBU=y CONFIG_SCSI=y diff --git a/configs/maxbcm_defconfig b/configs/maxbcm_defconfig index 8dd6adf..40f79d4 100644 --- a/configs/maxbcm_defconfig +++ b/configs/maxbcm_defconfig @@ -47,6 +47,7 @@ CONFIG_PHY_MARVELL=y CONFIG_PHY_GIGE=y CONFIG_MVNETA=y CONFIG_MII=y +CONFIG_MVMDIO=y CONFIG_DEBUG_UART_SHIFT=2 CONFIG_SYS_NS16550=y CONFIG_KIRKWOOD_SPI=y diff --git a/configs/mvebu_espressobin-88f3720_defconfig b/configs/mvebu_espressobin-88f3720_defconfig index ff05630..8d7d57f 100644 --- a/configs/mvebu_espressobin-88f3720_defconfig +++ b/configs/mvebu_espressobin-88f3720_defconfig @@ -25,6 +25,7 @@ CONFIG_DISPLAY_BOARDINFO_LATE=y CONFIG_ARCH_EARLY_INIT_R=y CONFIG_BOARD_EARLY_INIT_F=y CONFIG_BOARD_LATE_INIT=y +CONFIG_LAST_STAGE_INIT=y # CONFIG_CMD_FLASH is not set CONFIG_CMD_FUSE=y CONFIG_CMD_GPIO=y @@ -73,9 +74,11 @@ CONFIG_SPI_FLASH_STMICRO=y CONFIG_SPI_FLASH_WINBOND=y CONFIG_SPI_FLASH_MTD=y CONFIG_PHY_MARVELL=y +CONFIG_PHY_FIXED=y CONFIG_PHY_GIGE=y CONFIG_E1000=y CONFIG_MVNETA=y +CONFIG_MVMDIO=y CONFIG_NVME_PCI=y CONFIG_PCI=y CONFIG_PCI_AARDVARK=y diff --git a/configs/octeon_ebb7304_defconfig b/configs/octeon_ebb7304_defconfig index e907144..f70f0d2 100644 --- a/configs/octeon_ebb7304_defconfig +++ b/configs/octeon_ebb7304_defconfig @@ -26,6 +26,7 @@ CONFIG_CMD_PART=y CONFIG_CMD_PCI=y CONFIG_CMD_USB=y CONFIG_CMD_DHCP=y +CONFIG_CMD_MII=y CONFIG_CMD_PING=y CONFIG_CMD_RTC=y CONFIG_CMD_TIME=y @@ -36,6 +37,7 @@ CONFIG_AMIGA_PARTITION=y CONFIG_EFI_PARTITION=y CONFIG_PARTITION_TYPE_GUID=y CONFIG_ENV_IS_IN_FLASH=y +CONFIG_TFTP_TSIZE=y CONFIG_CLK=y # CONFIG_INPUT is not set CONFIG_MISC=y @@ -53,7 +55,12 @@ CONFIG_DM_SPI_FLASH=y CONFIG_SPI_FLASH_ATMEL=y CONFIG_SPI_FLASH_SPANSION=y CONFIG_SPI_FLASH_STMICRO=y +CONFIG_PHYLIB=y +CONFIG_PHY_MARVELL=y +CONFIG_DM_MDIO=y +CONFIG_DM_ETH_PHY=y CONFIG_E1000=y +CONFIG_NET_OCTEON=y CONFIG_PCI=y CONFIG_PCIE_OCTEON=y CONFIG_DM_REGULATOR=y diff --git a/configs/octeon_nic23_defconfig b/configs/octeon_nic23_defconfig index 1a1718a..3ab5838 100644 --- a/configs/octeon_nic23_defconfig +++ b/configs/octeon_nic23_defconfig @@ -14,13 +14,16 @@ CONFIG_ARCH_OCTEON=y CONFIG_TARGET_OCTEON_NIC23=y # CONFIG_MIPS_CACHE_SETUP is not set # CONFIG_MIPS_CACHE_DISABLE is not set +CONFIG_MIPS_RELOCATION_TABLE_SIZE=0xc000 CONFIG_DEBUG_UART=y CONFIG_AHCI=y CONFIG_OF_BOARD_FIXUP=y CONFIG_SYS_CONSOLE_ENV_OVERWRITE=y # CONFIG_SYS_DEVICE_NULLDEV is not set CONFIG_ARCH_MISC_INIT=y +CONFIG_BOARD_EARLY_INIT_F=y CONFIG_BOARD_LATE_INIT=y +CONFIG_LAST_STAGE_INIT=y CONFIG_HUSH_PARSER=y # CONFIG_CMD_FLASH is not set CONFIG_CMD_GPIO=y @@ -37,6 +40,7 @@ CONFIG_CMD_FAT=y CONFIG_CMD_FS_GENERIC=y CONFIG_EFI_PARTITION=y CONFIG_ENV_IS_IN_SPI_FLASH=y +CONFIG_TFTP_TSIZE=y CONFIG_SATA=y CONFIG_AHCI_MVEBU=y CONFIG_CLK=y @@ -50,7 +54,11 @@ CONFIG_DM_SPI_FLASH=y CONFIG_SPI_FLASH_ATMEL=y CONFIG_SPI_FLASH_SPANSION=y CONFIG_SPI_FLASH_STMICRO=y -# CONFIG_NETDEVICES is not set +CONFIG_PHYLIB=y +CONFIG_PHYLIB_10G=y +CONFIG_DM_MDIO=y +CONFIG_DM_ETH_PHY=y +CONFIG_NET_OCTEON=y CONFIG_PCI=y CONFIG_DM_REGULATOR=y CONFIG_DM_REGULATOR_FIXED=y diff --git a/configs/theadorable_debug_defconfig b/configs/theadorable_debug_defconfig index 86129e7..9a03a0a 100644 --- a/configs/theadorable_debug_defconfig +++ b/configs/theadorable_debug_defconfig @@ -70,6 +70,7 @@ CONFIG_PHY_MARVELL=y CONFIG_PHY_GIGE=y CONFIG_MVNETA=y CONFIG_MII=y +CONFIG_MVMDIO=y CONFIG_PCI=y CONFIG_DM_PCI_COMPAT=y CONFIG_PCI_MVEBU=y diff --git a/configs/turris_mox_defconfig b/configs/turris_mox_defconfig index 9a76a11..bcd3699 100644 --- a/configs/turris_mox_defconfig +++ b/configs/turris_mox_defconfig @@ -82,6 +82,7 @@ CONFIG_SPI_FLASH_MTD=y CONFIG_PHY_MARVELL=y CONFIG_PHY_GIGE=y CONFIG_MVNETA=y +CONFIG_MVMDIO=y CONFIG_NVME_PCI=y CONFIG_PCI=y CONFIG_PCI_AARDVARK=y diff --git a/configs/turris_omnia_defconfig b/configs/turris_omnia_defconfig index 5591c97..217e260 100644 --- a/configs/turris_omnia_defconfig +++ b/configs/turris_omnia_defconfig @@ -82,9 +82,11 @@ CONFIG_SPI_FLASH_MACRONIX=y CONFIG_SPI_FLASH_SPANSION=y CONFIG_SPI_FLASH_MTD=y CONFIG_PHY_MARVELL=y +CONFIG_PHY_FIXED=y CONFIG_PHY_GIGE=y CONFIG_MVNETA=y CONFIG_MII=y +CONFIG_MVMDIO=y CONFIG_NVME_PCI=y CONFIG_PCI=y CONFIG_PCI_MVEBU=y diff --git a/configs/uDPU_defconfig b/configs/uDPU_defconfig index c07bad5..f2852ad 100644 --- a/configs/uDPU_defconfig +++ b/configs/uDPU_defconfig @@ -73,6 +73,7 @@ CONFIG_PHY_MARVELL=y CONFIG_PHY_GIGE=y CONFIG_E1000=y CONFIG_MVNETA=y +CONFIG_MVMDIO=y CONFIG_PCI=y CONFIG_PCI_AARDVARK=y CONFIG_PHY=y diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 347fe8a..7fe0e00 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -445,6 +445,7 @@ config MVNETA bool "Marvell Armada XP/385/3700 network interface support" depends on ARMADA_XP || ARMADA_38X || ARMADA_3700 select PHYLIB + select DM_MDIO help This driver supports the network interface units in the Marvell ARMADA XP, ARMADA 38X and ARMADA 3700 SoCs @@ -495,6 +496,13 @@ config MT7628_ETH The MediaTek MT7628 ethernet interface is used on MT7628 and MT7688 based boards. +config NET_OCTEON + bool "MIPS Octeon ethernet support" + depends on ARCH_OCTEON + help + You must select Y to enable network device support for + MIPS Octeon SoCs. If unsure, say n + config NET_OCTEONTX bool "OcteonTX Ethernet support" depends on ARCH_OCTEONTX diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 16733d2..69fb3bb 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -64,6 +64,7 @@ obj-$(CONFIG_MVMDIO) += mvmdio.o obj-$(CONFIG_MVNETA) += mvneta.o obj-$(CONFIG_MVPP2) += mvpp2.o obj-$(CONFIG_NETCONSOLE) += netconsole.o +obj-$(CONFIG_NET_OCTEON) += octeon/ obj-$(CONFIG_NET_OCTEONTX) += octeontx/ obj-$(CONFIG_NET_OCTEONTX2) += octeontx2/ obj-$(CONFIG_OCTEONTX2_CGX_INTF) += octeontx2/cgx_intf.o diff --git a/drivers/net/mvneta.c b/drivers/net/mvneta.c index 15dc714..d2c42c4 100644 --- a/drivers/net/mvneta.c +++ b/drivers/net/mvneta.c @@ -40,11 +40,7 @@ DECLARE_GLOBAL_DATA_PTR; -#if !defined(CONFIG_PHYLIB) -# error Marvell mvneta requires PHYLIB -#endif - -#define CONFIG_NR_CPUS 1 +#define MVNETA_NR_CPUS 1 #define ETH_HLEN 14 /* Total octets in header */ /* 2(HW hdr) 14(MAC hdr) 4(CRC) 32(extra for cache prefetch) */ @@ -192,7 +188,6 @@ DECLARE_GLOBAL_DATA_PTR; #define MVNETA_GMAC_AUTONEG_CONFIG 0x2c0c #define MVNETA_GMAC_FORCE_LINK_DOWN BIT(0) #define MVNETA_GMAC_FORCE_LINK_PASS BIT(1) -#define MVNETA_GMAC_FORCE_LINK_UP (BIT(0) | BIT(1)) #define MVNETA_GMAC_IB_BYPASS_AN_EN BIT(3) #define MVNETA_GMAC_CONFIG_MII_SPEED BIT(5) #define MVNETA_GMAC_CONFIG_GMII_SPEED BIT(6) @@ -282,13 +277,11 @@ struct mvneta_port { unsigned int speed; int init; - int phyaddr; struct phy_device *phydev; #if CONFIG_IS_ENABLED(DM_GPIO) struct gpio_desc phy_reset_gpio; struct gpio_desc sfp_tx_disable_gpio; #endif - struct mii_dev *bus; }; /* The mvneta_tx_desc and mvneta_rx_desc structures describe the @@ -414,15 +407,6 @@ static struct buffer_location buffer_loc; */ #define BD_SPACE (1 << 20) -/* - * Dummy implementation that can be overwritten by a board - * specific function - */ -__weak int board_network_enable(struct mii_dev *bus) -{ - return 0; -} - /* Utility/helper methods */ /* Write helper method */ @@ -576,13 +560,6 @@ static void mvneta_rxq_buf_size_set(struct mvneta_port *pp, mvreg_write(pp, MVNETA_RXQ_SIZE_REG(rxq->id), val); } -static int mvneta_port_is_fixed_link(struct mvneta_port *pp) -{ - /* phy_addr is set to invalid value for fixed link */ - return pp->phyaddr > PHY_MAX_ADDR; -} - - /* Start the Ethernet port RX and TX activity */ static void mvneta_port_up(struct mvneta_port *pp) { @@ -791,7 +768,7 @@ static void mvneta_defaults_set(struct mvneta_port *pp) /* Set CPU queue access map - all CPUs have access to all RX * queues and to all TX queues */ - for (cpu = 0; cpu < CONFIG_NR_CPUS; cpu++) + for (cpu = 0; cpu < MVNETA_NR_CPUS; cpu++) mvreg_write(pp, MVNETA_CPU_MAP(cpu), (MVNETA_CPU_RXQ_ACCESS_ALL_MASK | MVNETA_CPU_TXQ_ACCESS_ALL_MASK)); @@ -834,7 +811,10 @@ static void mvneta_defaults_set(struct mvneta_port *pp) mvreg_write(pp, MVNETA_SDMA_CONFIG, val); /* Enable PHY polling in hardware if not in fixed-link mode */ - if (!mvneta_port_is_fixed_link(pp)) { + if (!CONFIG_IS_ENABLED(PHY_FIXED) || + pp->phydev->phy_id != PHY_FIXED_ID) { + mvreg_write(pp, MVNETA_PHY_ADDR, pp->phydev->addr); + val = mvreg_read(pp, MVNETA_UNIT_CONTROL); val |= MVNETA_PHY_POLLING_ENABLE; mvreg_write(pp, MVNETA_UNIT_CONTROL, val); @@ -1171,38 +1151,46 @@ static void mvneta_adjust_link(struct udevice *dev) { struct mvneta_port *pp = dev_get_priv(dev); struct phy_device *phydev = pp->phydev; - int status_change = 0; - - if (mvneta_port_is_fixed_link(pp)) { - debug("Using fixed link, skip link adjust\n"); - return; - } - - if (phydev->link) { - if ((pp->speed != phydev->speed) || - (pp->duplex != phydev->duplex)) { - u32 val; - - val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); - val &= ~(MVNETA_GMAC_CONFIG_MII_SPEED | - MVNETA_GMAC_CONFIG_GMII_SPEED | - MVNETA_GMAC_CONFIG_FULL_DUPLEX | - MVNETA_GMAC_AN_SPEED_EN | - MVNETA_GMAC_AN_DUPLEX_EN); + bool status_change = false; + + if (phydev->link && + (pp->speed != phydev->speed || pp->duplex != phydev->duplex)) { + u32 val; + + val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); + val &= ~(MVNETA_GMAC_CONFIG_MII_SPEED | + MVNETA_GMAC_CONFIG_GMII_SPEED | + MVNETA_GMAC_CONFIG_FULL_DUPLEX | + MVNETA_GMAC_AN_SPEED_EN | + MVNETA_GMAC_AN_DUPLEX_EN); + + /* FIXME: For fixed-link case, these were the initial settings + * used before the code was converted to use PHY_FIXED. Some of + * these may look nonsensical (for example BYPASS_AN makes sense + * for 1000base-x and 2500base-x modes, AFAIK), and in fact this + * may be changed in the future (when support for inband AN will + * be added). Also, why is ADVERT_FC enabled if we don't enable + * inband AN at all? + */ + if (CONFIG_IS_ENABLED(PHY_FIXED) && + pp->phydev->phy_id == PHY_FIXED_ID) + val = MVNETA_GMAC_IB_BYPASS_AN_EN | + MVNETA_GMAC_SET_FC_EN | + MVNETA_GMAC_ADVERT_FC_EN | + MVNETA_GMAC_SAMPLE_TX_CFG_EN; - if (phydev->duplex) - val |= MVNETA_GMAC_CONFIG_FULL_DUPLEX; + if (phydev->duplex) + val |= MVNETA_GMAC_CONFIG_FULL_DUPLEX; - if (phydev->speed == SPEED_1000) - val |= MVNETA_GMAC_CONFIG_GMII_SPEED; - else - val |= MVNETA_GMAC_CONFIG_MII_SPEED; + if (phydev->speed == SPEED_1000) + val |= MVNETA_GMAC_CONFIG_GMII_SPEED; + else if (pp->speed == SPEED_100) + val |= MVNETA_GMAC_CONFIG_MII_SPEED; - mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); + mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); - pp->duplex = phydev->duplex; - pp->speed = phydev->speed; - } + pp->duplex = phydev->duplex; + pp->speed = phydev->speed; } if (phydev->link != pp->link) { @@ -1212,7 +1200,7 @@ static void mvneta_adjust_link(struct udevice *dev) } pp->link = phydev->link; - status_change = 1; + status_change = true; } if (status_change) { @@ -1428,118 +1416,6 @@ static int mvneta_init(struct udevice *dev) /* U-Boot only functions follow here */ -/* SMI / MDIO functions */ - -static int smi_wait_ready(struct mvneta_port *pp) -{ - u32 timeout = MVNETA_SMI_TIMEOUT; - u32 smi_reg; - - /* wait till the SMI is not busy */ - do { - /* read smi register */ - smi_reg = mvreg_read(pp, MVNETA_SMI); - if (timeout-- == 0) { - printf("Error: SMI busy timeout\n"); - return -EFAULT; - } - } while (smi_reg & MVNETA_SMI_BUSY); - - return 0; -} - -/* - * mvneta_mdio_read - miiphy_read callback function. - * - * Returns 16bit phy register value, or 0xffff on error - */ -static int mvneta_mdio_read(struct mii_dev *bus, int addr, int devad, int reg) -{ - struct mvneta_port *pp = bus->priv; - u32 smi_reg; - u32 timeout; - - /* check parameters */ - if (addr > MVNETA_PHY_ADDR_MASK) { - printf("Error: Invalid PHY address %d\n", addr); - return -EFAULT; - } - - if (reg > MVNETA_PHY_REG_MASK) { - printf("Err: Invalid register offset %d\n", reg); - return -EFAULT; - } - - /* wait till the SMI is not busy */ - if (smi_wait_ready(pp) < 0) - return -EFAULT; - - /* fill the phy address and regiser offset and read opcode */ - smi_reg = (addr << MVNETA_SMI_DEV_ADDR_OFFS) - | (reg << MVNETA_SMI_REG_ADDR_OFFS) - | MVNETA_SMI_OPCODE_READ; - - /* write the smi register */ - mvreg_write(pp, MVNETA_SMI, smi_reg); - - /* wait till read value is ready */ - timeout = MVNETA_SMI_TIMEOUT; - - do { - /* read smi register */ - smi_reg = mvreg_read(pp, MVNETA_SMI); - if (timeout-- == 0) { - printf("Err: SMI read ready timeout\n"); - return -EFAULT; - } - } while (!(smi_reg & MVNETA_SMI_READ_VALID)); - - /* Wait for the data to update in the SMI register */ - for (timeout = 0; timeout < MVNETA_SMI_TIMEOUT; timeout++) - ; - - return mvreg_read(pp, MVNETA_SMI) & MVNETA_SMI_DATA_MASK; -} - -/* - * mvneta_mdio_write - miiphy_write callback function. - * - * Returns 0 if write succeed, -EINVAL on bad parameters - * -ETIME on timeout - */ -static int mvneta_mdio_write(struct mii_dev *bus, int addr, int devad, int reg, - u16 value) -{ - struct mvneta_port *pp = bus->priv; - u32 smi_reg; - - /* check parameters */ - if (addr > MVNETA_PHY_ADDR_MASK) { - printf("Error: Invalid PHY address %d\n", addr); - return -EFAULT; - } - - if (reg > MVNETA_PHY_REG_MASK) { - printf("Err: Invalid register offset %d\n", reg); - return -EFAULT; - } - - /* wait till the SMI is not busy */ - if (smi_wait_ready(pp) < 0) - return -EFAULT; - - /* fill the phy addr and reg offset and write opcode and data */ - smi_reg = value << MVNETA_SMI_DATA_OFFS; - smi_reg |= (addr << MVNETA_SMI_DEV_ADDR_OFFS) - | (reg << MVNETA_SMI_REG_ADDR_OFFS); - smi_reg &= ~MVNETA_SMI_OPCODE_READ; - - /* write the smi register */ - mvreg_write(pp, MVNETA_SMI, smi_reg); - - return 0; -} - static int mvneta_start(struct udevice *dev) { struct mvneta_port *pp = dev_get_priv(dev); @@ -1548,57 +1424,28 @@ static int mvneta_start(struct udevice *dev) mvneta_port_power_up(pp, pp->phy_interface); if (!pp->init || pp->link == 0) { - if (mvneta_port_is_fixed_link(pp)) { - u32 val; - - pp->init = 1; - pp->link = 1; - mvneta_init(dev); - - val = MVNETA_GMAC_FORCE_LINK_UP | - MVNETA_GMAC_IB_BYPASS_AN_EN | - MVNETA_GMAC_SET_FC_EN | - MVNETA_GMAC_ADVERT_FC_EN | - MVNETA_GMAC_SAMPLE_TX_CFG_EN; - - if (pp->duplex) - val |= MVNETA_GMAC_CONFIG_FULL_DUPLEX; - - if (pp->speed == SPEED_1000) - val |= MVNETA_GMAC_CONFIG_GMII_SPEED; - else if (pp->speed == SPEED_100) - val |= MVNETA_GMAC_CONFIG_MII_SPEED; + phydev = dm_eth_phy_connect(dev); + if (!phydev) { + printf("dm_eth_phy_connect failed\n"); + return -ENODEV; + } - mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); - } else { - /* Set phy address of the port */ - mvreg_write(pp, MVNETA_PHY_ADDR, pp->phyaddr); - - phydev = phy_connect(pp->bus, pp->phyaddr, dev, - pp->phy_interface); - if (!phydev) { - printf("phy_connect failed\n"); - return -ENODEV; - } - - pp->phydev = phydev; - phy_config(phydev); - phy_startup(phydev); - if (!phydev->link) { - printf("%s: No link.\n", phydev->dev->name); - return -1; - } - - /* Full init on first call */ - mvneta_init(dev); - pp->init = 1; - return 0; + pp->phydev = phydev; + phy_config(phydev); + phy_startup(phydev); + if (!phydev->link) { + printf("%s: No link.\n", phydev->dev->name); + return -1; } - } - /* Upon all following calls, this is enough */ - mvneta_port_up(pp); - mvneta_port_enable(pp); + /* Full init on first call */ + mvneta_init(dev); + pp->init = 1; + } else { + /* Upon all following calls, this is enough */ + mvneta_port_up(pp); + mvneta_port_enable(pp); + } return 0; } @@ -1692,18 +1539,11 @@ static int mvneta_recv(struct udevice *dev, int flags, uchar **packetp) static int mvneta_probe(struct udevice *dev) { - struct eth_pdata *pdata = dev_get_plat(dev); struct mvneta_port *pp = dev_get_priv(dev); #if CONFIG_IS_ENABLED(DM_GPIO) struct ofnode_phandle_args sfp_args; #endif - void *blob = (void *)gd->fdt_blob; - int node = dev_of_offset(dev); - struct mii_dev *bus; - unsigned long addr; void *bd_space; - int ret; - int fl_node; /* * Allocate buffer area for descs and rx_buffers. This is only @@ -1729,7 +1569,10 @@ static int mvneta_probe(struct udevice *dev) buffer_loc.rx_buffers = (phys_addr_t)(bd_space + size); } - pp->base = (void __iomem *)pdata->iobase; + pp->base = dev_read_addr_ptr(dev); + pp->phy_interface = dev_read_phy_mode(dev); + if (pp->phy_interface == PHY_INTERFACE_MODE_NA) + return -EINVAL; /* Configure MBUS address windows */ if (device_is_compatible(dev, "marvell,armada-3700-neta")) @@ -1737,42 +1580,9 @@ static int mvneta_probe(struct udevice *dev) else mvneta_conf_mbus_windows(pp); - /* PHY interface is already decoded in mvneta_of_to_plat() */ - pp->phy_interface = pdata->phy_interface; - - /* fetch 'fixed-link' property from 'neta' node */ - fl_node = fdt_subnode_offset(blob, node, "fixed-link"); - if (fl_node != -FDT_ERR_NOTFOUND) { - /* set phy_addr to invalid value for fixed link */ - pp->phyaddr = PHY_MAX_ADDR + 1; - pp->duplex = fdtdec_get_bool(blob, fl_node, "full-duplex"); - pp->speed = fdtdec_get_int(blob, fl_node, "speed", 0); - } else { - /* Now read phyaddr from DT */ - addr = fdtdec_get_int(blob, node, "phy", 0); - addr = fdt_node_offset_by_phandle(blob, addr); - pp->phyaddr = fdtdec_get_int(blob, addr, "reg", 0); - } - - bus = mdio_alloc(); - if (!bus) { - printf("Failed to allocate MDIO bus\n"); - return -ENOMEM; - } - - bus->read = mvneta_mdio_read; - bus->write = mvneta_mdio_write; - snprintf(bus->name, sizeof(bus->name), dev->name); - bus->priv = (void *)pp; - pp->bus = bus; - - ret = mdio_register(bus); - if (ret) - return ret; - #if CONFIG_IS_ENABLED(DM_GPIO) - ret = dev_read_phandle_with_args(dev, "sfp", NULL, 0, 0, &sfp_args); - if (!ret && ofnode_is_enabled(sfp_args.node)) + if (!dev_read_phandle_with_args(dev, "sfp", NULL, 0, 0, &sfp_args) && + ofnode_is_enabled(sfp_args.node)) gpio_request_by_name_nodev(sfp_args.node, "tx-disable-gpio", 0, &pp->sfp_tx_disable_gpio, GPIOD_IS_OUT); @@ -1789,7 +1599,7 @@ static int mvneta_probe(struct udevice *dev) dm_gpio_set_value(&pp->sfp_tx_disable_gpio, 0); #endif - return board_network_enable(bus); + return 0; } static void mvneta_stop(struct udevice *dev) @@ -1808,20 +1618,6 @@ static const struct eth_ops mvneta_ops = { .write_hwaddr = mvneta_write_hwaddr, }; -static int mvneta_of_to_plat(struct udevice *dev) -{ - struct eth_pdata *pdata = dev_get_plat(dev); - - pdata->iobase = dev_read_addr(dev); - - /* Get phy-mode / phy_interface from DT */ - pdata->phy_interface = dev_read_phy_mode(dev); - if (pdata->phy_interface == PHY_INTERFACE_MODE_NA) - return -EINVAL; - - return 0; -} - static const struct udevice_id mvneta_ids[] = { { .compatible = "marvell,armada-370-neta" }, { .compatible = "marvell,armada-xp-neta" }, @@ -1833,7 +1629,6 @@ U_BOOT_DRIVER(mvneta) = { .name = "mvneta", .id = UCLASS_ETH, .of_match = mvneta_ids, - .of_to_plat = mvneta_of_to_plat, .probe = mvneta_probe, .ops = &mvneta_ops, .priv_auto = sizeof(struct mvneta_port), diff --git a/drivers/net/octeon/Makefile b/drivers/net/octeon/Makefile new file mode 100644 index 0000000..c573411 --- /dev/null +++ b/drivers/net/octeon/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (C) 2018-2022 Marvell International Ltd. +# + +obj-$(CONFIG_NET_OCTEON) += octeon_eth.o octeon_mdio.o diff --git a/drivers/net/octeon/octeon_eth.c b/drivers/net/octeon/octeon_eth.c new file mode 100644 index 0000000..fbb1afc --- /dev/null +++ b/drivers/net/octeon/octeon_eth.c @@ -0,0 +1,1060 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018-2022 Marvell International Ltd. + */ + +#include <dm.h> +#include <dm/device_compat.h> +#include <env.h> +#include <net.h> +#include <netdev.h> +#include <malloc.h> +#include <miiphy.h> +#include <misc.h> +#include <asm/io.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/octeon_fdt.h> +#include <mach/cvmx-qlm.h> +#include <mach/octeon_eth.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-asxx-defs.h> +#include <mach/cvmx-bgxx-defs.h> +#include <mach/cvmx-dbg-defs.h> +#include <mach/cvmx-gmxx-defs.h> +#include <mach/cvmx-gserx-defs.h> +#include <mach/cvmx-ipd-defs.h> +#include <mach/cvmx-l2c-defs.h> +#include <mach/cvmx-npi-defs.h> +#include <mach/cvmx-pcsx-defs.h> +#include <mach/cvmx-pexp-defs.h> +#include <mach/cvmx-pki-defs.h> +#include <mach/cvmx-pko-defs.h> +#include <mach/cvmx-smix-defs.h> +#include <mach/cvmx-sriox-defs.h> +#include <mach/cvmx-xcv-defs.h> +#include <mach/cvmx-pcsxx-defs.h> + +#include <mach/cvmx-helper.h> +#include <mach/cvmx-helper-board.h> +#include <mach/cvmx-helper-fdt.h> +#include <mach/cvmx-helper-bgx.h> +#include <mach/cvmx-helper-cfg.h> + +#include <mach/cvmx-hwpko.h> +#include <mach/cvmx-pko.h> +#include <mach/cvmx-pki.h> +#include <mach/cvmx-config.h> +#include <mach/cvmx-mdio.h> + +/** Maximum receive packet size (hardware default is 1536) */ +#define CONFIG_OCTEON_NETWORK_MRU 1536 + +#define OCTEON_BOOTLOADER_NAMED_BLOCK_TMP_PREFIX "__tmp" + +/** + * Enables RX packet debugging if octeon_debug_rx_packets is set in the + * environment. + */ +#define DEBUG_RX_PACKET + +/** + * Enables TX packet debugging if octeon_debug_tx_packets is set in the + * environment. + */ +#define DEBUG_TX_PACKET + +/* Global flag indicating common hw has been set up */ +static int octeon_global_hw_inited; + +#if defined(DEBUG_RX_PACKET) || defined(DEBUG_TX_PACKET) +static int packet_rx_debug; +static int packet_tx_debug; +#endif + +/* Make sure that we have enough buffers to keep prefetching blocks happy. + * Absolute minimum is probably about 200. + */ +#define NUM_PACKET_BUFFERS 1000 + +#define PKO_SHUTDOWN_TIMEOUT_VAL 100 + +/* Define the offsets from the base CSR */ +#define GMX_PRT_CFG 0x10 + +#define GMX_RX_FRM_MAX 0x30 +#define GMX_RX_JABBER 0x38 + +#define GMX_RX_ADR_CTL 0x100 +#define GMX_RX_ADR_CAM_EN 0x108 +#define GMX_RX_ADR_CAM0 0x180 +#define GMX_RX_ADR_CAM1 0x188 +#define GMX_RX_ADR_CAM2 0x190 +#define GMX_RX_ADR_CAM3 0x198 +#define GMX_RX_ADR_CAM4 0x1a0 +#define GMX_RX_ADR_CAM5 0x1a8 +#define GMX_TX_OVR_BP 0x4c8 + +/** + * Set the hardware MAC address for a device + * + * @param interface interface of port to set + * @param index index of port to set MAC address for + * @param addr Address structure to change it too. + * @return Zero on success + */ +static int cvm_oct_set_mac_address(struct udevice *dev) +{ + struct octeon_eth_info *priv = dev_get_priv(dev); + struct eth_pdata *pdata = dev_get_plat(dev); + cvmx_gmxx_prtx_cfg_t gmx_cfg; + cvmx_helper_interface_mode_t mode; + cvmx_gmxx_rxx_adr_ctl_t control; + u8 *ptr = (uint8_t *)pdata->enetaddr; + int interface = priv->interface; + int index = priv->index; + u64 mac = 0; + u64 gmx_reg; + int xipd_port; + int i; + + for (i = 0; i < 6; i++) + mac = (mac << 8) | (u64)(ptr[i]); + + debug("%s(%s (%pM))\n", __func__, dev->name, ptr); + mode = cvmx_helper_interface_get_mode(interface); + + /* It's rather expensive to change the MAC address for BGX so we only + * do this if it has changed or not been set previously. + */ + if (octeon_has_feature(OCTEON_FEATURE_BGX)) { + xipd_port = cvmx_helper_get_ipd_port(interface, index); + if (priv->last_bgx_mac != mac || !priv->bgx_mac_set) { + cvmx_helper_bgx_set_mac(xipd_port, 1, 2, mac); + priv->last_bgx_mac = mac; + priv->bgx_mac_set = 1; + } + return 0; + } + + if (mode == CVMX_HELPER_INTERFACE_MODE_AGL) { + gmx_reg = CVMX_AGL_GMX_RXX_INT_REG(0); + } else { + gmx_reg = CVMX_GMXX_RXX_INT_REG(index, interface); + csr_wr(CVMX_GMXX_SMACX(index, interface), mac); + } + + /* Disable interface */ + gmx_cfg.u64 = csr_rd(gmx_reg + GMX_PRT_CFG); + csr_wr(gmx_reg + GMX_PRT_CFG, gmx_cfg.u64 & ~1ull); + debug("%s: gmx reg: 0x%llx\n", __func__, gmx_reg); + + csr_wr(gmx_reg + GMX_RX_ADR_CAM0, ptr[0]); + csr_wr(gmx_reg + GMX_RX_ADR_CAM1, ptr[1]); + csr_wr(gmx_reg + GMX_RX_ADR_CAM2, ptr[2]); + csr_wr(gmx_reg + GMX_RX_ADR_CAM3, ptr[3]); + csr_wr(gmx_reg + GMX_RX_ADR_CAM4, ptr[4]); + csr_wr(gmx_reg + GMX_RX_ADR_CAM5, ptr[5]); + + control.u64 = 0; + control.s.bcst = 1; /* Allow broadcast MAC addresses */ + control.s.mcst = 1; /* Force reject multicast packets */ + control.s.cam_mode = 1; /* Filter packets based on the CAM */ + + csr_wr(gmx_reg + GMX_RX_ADR_CTL, control.u64); + + csr_wr(gmx_reg + GMX_RX_ADR_CAM_EN, 1); + + /* Return interface to previous enable state */ + csr_wr(gmx_reg + GMX_PRT_CFG, gmx_cfg.u64); + + return 0; +} + +static void cvm_oct_fill_hw_memory(u64 pool, u64 size, u64 elements) +{ + static int alloc_count; + char tmp_name[64]; + int ret; + + debug("%s: pool: 0x%llx, size: 0xx%llx, count: 0x%llx\n", + __func__, pool, size, elements); + sprintf(tmp_name, "%s_fpa_alloc_%d", + OCTEON_BOOTLOADER_NAMED_BLOCK_TMP_PREFIX, alloc_count++); + ret = cvmx_fpa_setup_pool(pool, tmp_name, NULL, size, elements); +} + +/** + * Configure common hardware for all interfaces + */ +static void cvm_oct_configure_common_hw(void) +{ + int mru = env_get_ulong("octeon_mru", 0, CONFIG_OCTEON_NETWORK_MRU); + int packet_pool_size = CVMX_FPA_PACKET_POOL_SIZE; + + if (mru > packet_pool_size) + packet_pool_size = (mru + CVMX_CACHE_LINE_SIZE - 1) & + ~(CVMX_CACHE_LINE_SIZE - 1); + + /* Setup the FPA */ + cvmx_fpa_enable(); + + cvm_oct_fill_hw_memory(CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE, + NUM_PACKET_BUFFERS); +#if CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL + if (!octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { + cvm_oct_fill_hw_memory(CVMX_FPA_OUTPUT_BUFFER_POOL, + CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE, 128); + } +#endif + cvm_oct_fill_hw_memory(CVMX_FPA_PACKET_POOL, packet_pool_size, + NUM_PACKET_BUFFERS); + + cvmx_helper_initialize_packet_io_global(); + cvmx_helper_initialize_packet_io_local(); + + /* The MRU defaults to 1536 bytes by the hardware. Setting + * CONFIG_OCTEON_NETWORK_MRU allows this to be overridden. + */ + if (octeon_has_feature(OCTEON_FEATURE_PKI)) { + struct cvmx_pki_global_config gbl_cfg; + int i; + + cvmx_pki_read_global_config(0, &gbl_cfg); + for (i = 0; i < CVMX_PKI_NUM_FRAME_CHECK; i++) + gbl_cfg.frm_len[i].maxlen = mru; + cvmx_pki_write_global_config(0, &gbl_cfg); + } + + /* Set POW get work timeout to maximum value */ + if (octeon_has_feature(OCTEON_FEATURE_CN68XX_WQE) || + octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) + csr_wr(CVMX_SSO_NW_TIM, 0x3ff); + else + csr_wr(CVMX_POW_NW_TIM, 0x3ff); +} + +/** + * Enables Ethernet devices to allow packets to be transmitted and received. + * For example, this is activated when the DHCP command is issued. + * + * @param dev Ethernet device to initialize + * @param bis board data structure, not used. + * + * @return 1 for success + */ +int octeon_eth_init(struct udevice *dev) +{ + struct octeon_eth_info *priv = dev_get_priv(dev); + + debug("%s(), dev_ptr: %p, dev: %s, port: %d\n", __func__, dev, + dev->name, priv->port); + + if (priv->initted_flag) { + debug("%s already initialized\n", dev->name); + return 1; + } + + if (!octeon_global_hw_inited) { + debug("Initializing common hardware\n"); + cvm_oct_configure_common_hw(); + } + + /* Ignore backpressure on RGMII ports */ + if (!octeon_has_feature(OCTEON_FEATURE_BGX)) + csr_wr(priv->gmx_base + GMX_TX_OVR_BP, 0xf << 8 | 0xf); + + debug("%s: Setting MAC address\n", __func__); + cvm_oct_set_mac_address(dev); + + if (!octeon_global_hw_inited) { + debug("Enabling packet input\n"); + cvmx_helper_ipd_and_packet_input_enable(); + octeon_global_hw_inited = 1; + + /* Connect, configure and start the PHY, if the device is + * connected to one. If not, then it's most likely an SPF + * enabled port, which does not have such PHY setup here. + */ + if (priv->mdio_dev) { + priv->phy_dev = dm_eth_phy_connect(dev); + phy_config(priv->phy_dev); + phy_startup(priv->phy_dev); + } + } + priv->enabled = 0; + priv->initted_flag = 1; + + debug("%s exiting successfully\n", __func__); + return 1; +} + +/** + * Initializes the specified interface and port + * + * @param interface interface to initialize + * @param index port index on interface + * @param port ipd port number + * @param if_mode interface mode + * + * @return 0 for success, -1 if out of memory, 1 if port is invalid + */ +static int octeon_eth_initialize(struct udevice *dev, int interface, + int index, int port, + cvmx_helper_interface_mode_t if_mode) +{ + struct octeon_eth_info *oct_eth_info = dev_get_priv(dev); + int eth; + + eth = cvmx_helper_get_port_fdt_node_offset(interface, index); + if (eth <= 0) { + debug("ERROR: No fdt node for interface %d, index %d\n", + interface, index); + return 1; + } + + oct_eth_info->is_c45 = (if_mode == CVMX_HELPER_INTERFACE_MODE_XAUI) || + (if_mode == CVMX_HELPER_INTERFACE_MODE_RXAUI) || + (if_mode == CVMX_HELPER_INTERFACE_MODE_XFI) || + (if_mode == CVMX_HELPER_INTERFACE_MODE_XLAUI) || + (if_mode == CVMX_HELPER_INTERFACE_MODE_10G_KR) || + (if_mode == CVMX_HELPER_INTERFACE_MODE_10G_KR); + oct_eth_info->port = port; + oct_eth_info->index = index; + oct_eth_info->interface = interface; + oct_eth_info->initted_flag = 0; + /* This is guaranteed to force the link state to be printed out */ + oct_eth_info->link_state = 0xffffffffffffffffULL; + debug("Setting up port: %d, int: %d, index: %d, device: octeth%d\n", + oct_eth_info->port, oct_eth_info->interface, oct_eth_info->index, + dev_seq(dev)); + if (if_mode == CVMX_HELPER_INTERFACE_MODE_AGL) { + oct_eth_info->gmx_base = CVMX_AGL_GMX_RXX_INT_REG(0); + } else { + if (!octeon_has_feature(OCTEON_FEATURE_BGX)) + oct_eth_info->gmx_base = + CVMX_GMXX_RXX_INT_REG(index, interface); + } + + return 0; +} + +/** + * @INTERNAL + * Converts a BGX address to the node, interface and port number + * + * @param bgx_addr Address of CSR register + * + * @return node, interface and port number, will be -1 for invalid address. + */ +static struct cvmx_xiface __cvmx_bgx_reg_addr_to_xiface(u64 bgx_addr) +{ + struct cvmx_xiface xi = { -1, -1 }; + + xi.node = cvmx_csr_addr_to_node(bgx_addr); + bgx_addr = cvmx_csr_addr_strip_node(bgx_addr); + if ((bgx_addr & 0xFFFFFFFFF0000000) != 0x00011800E0000000) { + debug("%s: Invalid BGX address 0x%llx\n", __func__, + (unsigned long long)bgx_addr); + xi.node = -1; + return xi; + } + xi.interface = (bgx_addr >> 24) & 0x0F; + + return xi; +} + +static int octeon_nic_probe(struct udevice *dev) +{ + struct octeon_eth_info *info = dev_get_priv(dev); + struct ofnode_phandle_args phandle; + struct cvmx_xiface xi; + ofnode node, mdio_node; + int ipd_port; + int intf; + int ret; + + /* The empty stub is to keep cvmx_user_app_init() happy. */ + cvmx_npi_max_pknds = 1; + __cvmx_helper_init_port_valid(); + + xi = __cvmx_bgx_reg_addr_to_xiface(dev_read_addr(dev)); + intf = xi.interface; + debug("%s: Found BGX node %d, interface %d\n", __func__, xi.node, intf); + + ipd_port = cvmx_helper_get_ipd_port(intf, xi.node); + ret = octeon_eth_initialize(dev, intf, xi.node, ipd_port, + cvmx_helper_interface_get_mode(intf)); + + /* Move to subnode, as this includes the "phy-handle" */ + node = dev_read_first_subnode(dev); + + /* Check if an SPF module is conneted, then no MDIO is probed */ + ret = ofnode_parse_phandle_with_args(node, "sfp-slot", NULL, 0, 0, + &phandle); + if (!ret) { + dev_dbg(dev, "sfp-slot found, not probing for MDIO\n"); + return 0; + } + + /* Continue with MDIO probing */ + ret = ofnode_parse_phandle_with_args(node, "phy-handle", NULL, 0, 0, + &phandle); + if (ret) { + dev_err(dev, "phy-handle not found in subnode\n"); + return -ENODEV; + } + + /* Get MDIO node */ + mdio_node = ofnode_get_parent(phandle.node); + ret = uclass_get_device_by_ofnode(UCLASS_MDIO, mdio_node, + &info->mdio_dev); + if (ret) { + dev_err(dev, "mdio_dev not found\n"); + return -ENODEV; + } + + return 0; +} + +/** + * Sets the hardware MAC address of the Ethernet device + * + * @param dev - Ethernet device + * + * @return 0 for success + */ +int octeon_eth_write_hwaddr(struct udevice *dev) +{ + struct octeon_eth_info *oct_eth_info = dev_get_priv(dev); + struct eth_pdata *pdata = dev_get_plat(dev); + + /* Skip if the interface isn't yet enabled */ + if (!oct_eth_info->enabled) { + debug("%s: Interface not enabled, not setting MAC address\n", + __func__); + return 0; + } + debug("%s: Setting %s address to %02x:%02x:%02x:%02x:%02x:%02x\n", + __func__, dev->name, pdata->enetaddr[0], pdata->enetaddr[1], + pdata->enetaddr[2], pdata->enetaddr[3], pdata->enetaddr[4], + pdata->enetaddr[5]); + return cvm_oct_set_mac_address(dev); +} + +/** + * Enables and disables the XCV RGMII interface + * + * @param interface Interface number + * @param index Port index (should be 0 for RGMII) + * @param enable True to enable it, false to disable it + */ +static void octeon_bgx_xcv_rgmii_enable(int interface, int index, bool enable) +{ + union cvmx_xcv_reset xcv_reset; + + debug("%s(%d, %d, %sable)\n", __func__, interface, index, + enable ? "en" : "dis"); + xcv_reset.u64 = csr_rd(CVMX_XCV_RESET); + xcv_reset.s.rx_pkt_rst_n = enable ? 1 : 0; + csr_wr(CVMX_XCV_RESET, xcv_reset.u64); +} + +/** + * Enables a SGMII interface + * + * @param dev - Ethernet device to initialize + */ +void octeon_eth_sgmii_enable(struct udevice *dev) +{ + struct octeon_eth_info *oct_eth_info; + cvmx_gmxx_prtx_cfg_t gmx_cfg; + int index, interface; + cvmx_helper_interface_mode_t if_mode; + + oct_eth_info = dev_get_priv(dev); + interface = oct_eth_info->interface; + index = oct_eth_info->index; + + debug("%s(%s) (%d.%d)\n", __func__, dev->name, interface, index); + if (octeon_has_feature(OCTEON_FEATURE_BGX)) { + cvmx_bgxx_cmrx_config_t cmr_config; + + cmr_config.u64 = + csr_rd(CVMX_BGXX_CMRX_CONFIG(index, interface)); + cmr_config.s.enable = 1; + cmr_config.s.data_pkt_tx_en = 1; + cmr_config.s.data_pkt_rx_en = 1; + csr_wr(CVMX_BGXX_CMRX_CONFIG(index, interface), cmr_config.u64); + mdelay(100); + if (cvmx_helper_bgx_is_rgmii(interface, index)) + octeon_bgx_xcv_rgmii_enable(interface, index, true); + } else { + if_mode = cvmx_helper_interface_get_mode(interface); + /* Normal operating mode. */ + + if (if_mode == CVMX_HELPER_INTERFACE_MODE_SGMII || + if_mode == CVMX_HELPER_INTERFACE_MODE_QSGMII) { + cvmx_pcsx_miscx_ctl_reg_t pcsx_miscx_ctl_reg; + + debug(" if mode: (Q)SGMII\n"); + pcsx_miscx_ctl_reg.u64 = csr_rd(CVMX_PCSX_MISCX_CTL_REG(index, interface)); + pcsx_miscx_ctl_reg.s.gmxeno = 0; + csr_wr(CVMX_PCSX_MISCX_CTL_REG(index, interface), + pcsx_miscx_ctl_reg.u64); + } else if (if_mode != CVMX_HELPER_INTERFACE_MODE_AGL) { + cvmx_pcsxx_misc_ctl_reg_t pcsxx_misc_ctl_reg; + + debug(" if mode: AGM\n"); + pcsxx_misc_ctl_reg.u64 = + csr_rd(CVMX_PCSXX_MISC_CTL_REG(interface)); + pcsxx_misc_ctl_reg.s.gmxeno = 0; + csr_wr(CVMX_PCSXX_MISC_CTL_REG(interface), + pcsxx_misc_ctl_reg.u64); + } + + gmx_cfg.u64 = csr_rd(oct_eth_info->gmx_base + GMX_PRT_CFG); + gmx_cfg.s.en = 1; + csr_wr(oct_eth_info->gmx_base + GMX_PRT_CFG, gmx_cfg.u64); + gmx_cfg.u64 = csr_rd(oct_eth_info->gmx_base + GMX_PRT_CFG); + } +} + +/** + * Enables an Ethernet interface + * + * @param dev - Ethernet device to enable + */ +void octeon_eth_enable(struct udevice *dev) +{ + struct octeon_eth_info *oct_eth_info; + u64 tmp; + int interface; + cvmx_helper_interface_mode_t if_mode; + + oct_eth_info = dev_get_priv(dev); + interface = oct_eth_info->interface; + if_mode = cvmx_helper_interface_get_mode(interface); + + switch (if_mode) { + case CVMX_HELPER_INTERFACE_MODE_RGMII: + case CVMX_HELPER_INTERFACE_MODE_GMII: + debug(" rgmii/gmii mode\n"); + tmp = csr_rd(CVMX_ASXX_RX_PRT_EN(interface)); + tmp |= (1ull << (oct_eth_info->port & 0x3)); + csr_wr(CVMX_ASXX_RX_PRT_EN(interface), tmp); + tmp = csr_rd(CVMX_ASXX_TX_PRT_EN(interface)); + tmp |= (1ull << (oct_eth_info->port & 0x3)); + csr_wr(CVMX_ASXX_TX_PRT_EN(interface), tmp); + octeon_eth_write_hwaddr(dev); + break; + + case CVMX_HELPER_INTERFACE_MODE_SGMII: + case CVMX_HELPER_INTERFACE_MODE_XAUI: + case CVMX_HELPER_INTERFACE_MODE_RXAUI: + case CVMX_HELPER_INTERFACE_MODE_XLAUI: + case CVMX_HELPER_INTERFACE_MODE_XFI: + case CVMX_HELPER_INTERFACE_MODE_10G_KR: + case CVMX_HELPER_INTERFACE_MODE_40G_KR4: + case CVMX_HELPER_INTERFACE_MODE_MIXED: + case CVMX_HELPER_INTERFACE_MODE_AGL: + debug(" SGMII/XAUI/etc.\n"); + octeon_eth_sgmii_enable(dev); + octeon_eth_write_hwaddr(dev); + break; + + default: + break; + } +} + +void octeon_phy_port_check(struct udevice *dev) +{ + struct octeon_eth_info *oct_eth_info = dev_get_priv(dev); + struct phy_device *phydev = oct_eth_info->phydev; + + if (oct_eth_info->phy_port_check) + oct_eth_info->phy_port_check(phydev); +} + +/** + * Configure the RGMII port for the negotiated speed + * + * @param dev Linux device for the RGMII port + */ +static void cvm_oct_configure_rgmii_speed(struct udevice *dev) +{ + struct octeon_eth_info *priv = dev_get_priv(dev); + int port = priv->port; + cvmx_helper_link_info_t link_state = cvmx_helper_link_get(port); + + /* If the port is down some PHYs we need to check modules, etc. */ + if (!link_state.s.link_up) + octeon_phy_port_check(dev); + + if (link_state.u64 != priv->link_state) { + cvmx_helper_interface_mode_t mode; + + octeon_phy_port_check(dev); + + debug("%s(%s): Link state changed\n", __func__, dev->name); + printf("%s: ", dev->name); + if (!link_state.s.link_up) { + puts("Down "); + } else { + printf("Up %d Mbps ", link_state.s.speed); + if (link_state.s.full_duplex) + puts("Full duplex "); + else + puts("Half duplex "); + } + mode = cvmx_helper_interface_get_mode(priv->interface); + printf("(port %2d) (%s)\n", port, + cvmx_helper_interface_mode_to_string(mode)); + debug("%s: Setting link state\n", __func__); + cvmx_helper_link_set(priv->port, link_state); + priv->link_state = link_state.u64; + } +} + +#if defined(DEBUG_TX_PACKET) || defined(DEBUG_RX_PACKET) +static void print_mac(const char *label, const uint8_t *mac_addr) +{ + printf("%s: %02x:%02x:%02x:%02x:%02x:%02x", label, mac_addr[0], + mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]); +} + +static void print_ip(const void *packet) +{ + u8 *p = (uint8_t *)packet; + u16 length; + u8 hdr_len; + + puts("IP Header:\n"); + if ((p[0] & 0xF0) != 0x40) { + printf("Invalid IP version %d\n", *p >> 4); + return; + } + hdr_len = *p & 0x0F; + if (hdr_len < 5) + printf("Invalid IP header length %d\n", hdr_len); + printf(" Version: 4, Header length: %d\n", hdr_len); + length = (p[2] << 8) | p[3]; + printf(" TOS: 0x%02x, length: %d\n", p[1], length); + printf(" ID: %d, %s%s%s fragment offset: %d\n", (p[4] << 8) | p[5], + p[6] & 0x80 ? "congested, " : "", p[6] & 0x40 ? "DF, " : "", + p[6] & 0x20 ? "MF, " : "", ((p[6] & 0x1F) << 8) | p[7]); + printf(" TTL: %d, Protocol: %d, Header Checksum: 0x%x\n", p[8], p[9], + (p[10] << 8) | p[11]); + printf(" Source IP: %d.%d.%d.%d\n Destination IP: %d.%d.%d.%d\n", + p[12], p[13], p[14], p[15], p[16], p[17], p[18], p[19]); + if (p[9] == 17 || p[9] == 6) + printf(" Source port: %u, Destination port: %u\n", + (p[20] << 8) | p[21], (p[22] << 8) | p[23]); + puts("\n"); +} + +/** + * Prints out a packet for debugging purposes + * + * @param[in] packet - pointer to packet data + * @param length - length of packet in bytes + */ +static void print_packet(const void *packet, int length) +{ + int i, j; + const unsigned char *up = packet; + u16 type = (up[12] << 8 | up[13]); + int start = 14; + + print_mac("DMAC", &up[0]); + puts(" "); + print_mac("SMAC", &up[6]); + printf(" TYPE: %04x\n", type); + + if (type == 0x0800) + print_ip(&up[start]); + + for (i = start; (i + 16) < length; i += 16) { + printf("%04x ", i); + for (j = 0; j < 16; ++j) + printf("%02x ", up[i + j]); + + printf(" "); + for (j = 0; j < 16; ++j) + printf("%c", + ((up[i + j] >= ' ') && (up[i + j] <= '~')) ? + up[i + j] : + '.'); + printf("\n"); + } + printf("%04x ", i); + for (j = 0; i + j < length; ++j) + printf("%02x ", up[i + j]); + + for (; j < 16; ++j) + printf(" "); + + printf(" "); + for (j = 0; i + j < length; ++j) + printf("%c", ((up[i + j] >= ' ') && (up[i + j] <= '~')) ? + up[i + j] : + '.'); + + printf("\n"); +} +#endif + +/** + * String representation of error codes. + */ +static const char * const rx_error_codes[] = { + "OK", + "partial", + "jabber", + "overrun", + "oversize", + "alignment", + "fragment", + "fcs", + "undersize", + "extend", + "length mismatch", + "rgmii rx", + "skip error", + "nibble error (studder)", + "(undefined)", + "(undefined)", + "SPI 4.2 FCS", + "skip", + "L2 malformed", +}; + +/** + * Called to receive a packet + * + * @param dev - device to receive on + * + * @return - length of packet + * + * This function is used to poll packets. In turn it calls NetReceive + * to process the packets. + */ +static int nic_recv(struct udevice *dev, int flags, uchar **packetp) +{ + cvmx_wqe_t *work = cvmx_pow_work_request_sync(CVMX_POW_WAIT); + struct octeon_eth_info *oct_eth_info = dev_get_priv(dev); + cvmx_buf_ptr_t buf_ptr; + void *packet_data; + int length; + int error_code; + + if (!oct_eth_info->enabled) { + oct_eth_info->enabled = 1; + debug("%s: Enabling interface %s\n", __func__, dev->name); + octeon_eth_enable(dev); + } + + if (!work) { + /* + * Somtimes the link is not up yet. Return here in this + * case, this function will be called again later. + */ + return 0; + } + + error_code = cvmx_wqe_get_rcv_err(work); + if (error_code) { + /* Work has error, so drop */ + cvmx_helper_free_packet_data(work); + cvmx_wqe_free(work); + if (error_code < ARRAY_SIZE(rx_error_codes) && + !octeon_has_feature(OCTEON_FEATURE_BGX)) + printf("Receive error (code %d: %s), dropping\n", + error_code, rx_error_codes[error_code]); + else + printf("Receive error (code %d (unknown), dropping\n", + error_code); + return 0; + } + if (cvmx_wqe_get_bufs(work) != 1) { + /* can only support single-buffer packets */ + printf("Abnormal packet received in %u bufs, dropping\n", + cvmx_wqe_get_bufs(work)); + length = cvmx_wqe_get_len(work); + buf_ptr = cvmx_wqe_get_packet_ptr(work); + packet_data = cvmx_phys_to_ptr(buf_ptr.s.addr); + print_packet(packet_data, length); + cvmx_helper_free_packet_data(work); + cvmx_wqe_free(work); + return 0; + } + + buf_ptr = cvmx_wqe_get_packet_ptr(work); + packet_data = cvmx_phys_to_ptr(buf_ptr.s.addr); + length = cvmx_wqe_get_len(work); + + oct_eth_info->packets_received++; + debug("############# got work: %p, len: %d, packet_ptr: %p\n", work, + length, packet_data); +#if defined(DEBUG_RX_PACKET) + if (packet_rx_debug) { + printf("\nRX packet: interface: %d, index: %d\n", + oct_eth_info->interface, oct_eth_info->index); + print_packet(packet_data, length); + } +#endif + *packetp = (uchar *)packet_data; + + /* Save work for free_pkt() */ + oct_eth_info->work = work; + + /* Free WQE and packet data */ + return length; +} + +static int nic_free_pkt(struct udevice *dev, uchar *pkt, int pkt_len) +{ + struct octeon_eth_info *oct_eth_info = dev_get_priv(dev); + cvmx_wqe_t *work = oct_eth_info->work; + + if (!work) + return 0; + + cvmx_helper_free_packet_data(work); + cvmx_wqe_free(work); + oct_eth_info->work = NULL; + + return 0; +} + +/** + * Packet transmit + * + * @param skb Packet to send + * @param dev Device info structure + * @return Always returns zero + */ +static int cvm_oct_xmit(struct udevice *dev, void *packet, int len) +{ + struct octeon_eth_info *priv = dev_get_priv(dev); + int queue = cvmx_pko_get_base_queue(priv->port); + cvmx_pko_command_word0_t pko_command; + cvmx_buf_ptr_t hw_buffer; + int rv; + + debug("%s: addr: %p, len: %d\n", __func__, packet, len); + + hw_buffer.u64 = 0; + hw_buffer.s.addr = cvmx_ptr_to_phys(packet); + hw_buffer.s.pool = CVMX_FPA_PACKET_POOL; + hw_buffer.s.size = len; + hw_buffer.s.back = 0; + + /* Build the PKO command */ + pko_command.u64 = 0; + pko_command.s.subone0 = 1; + pko_command.s.dontfree = 0; + pko_command.s.segs = 1; + pko_command.s.total_bytes = len; + /* Send the packet to the output queue */ + + debug("%s: port: %d, queue: %d\n", __func__, priv->port, queue); + cvmx_pko_send_packet_prepare(priv->port, queue, 0); + rv = cvmx_pko_send_packet_finish(priv->port, queue, pko_command, + hw_buffer, 0); + if (rv) + printf("Failed to send the packet rv=%d\n", rv); + + return 0; +} + +static int nic_xmit(struct udevice *dev, void *pkt, int pkt_len) +{ + struct octeon_eth_info *oct_eth_info = dev_get_priv(dev); + void *fpa_buf = cvmx_fpa_alloc(CVMX_FPA_PACKET_POOL); + + if (!oct_eth_info->enabled) { + oct_eth_info->enabled = 1; + octeon_eth_enable(dev); + } + + /* We need to copy this to a FPA buffer, then give that to TX */ + + if (oct_eth_info->packets_sent == 0 && + !octeon_has_feature(OCTEON_FEATURE_BGX)) + cvm_oct_configure_rgmii_speed(dev); + + if (!fpa_buf) { + printf("ERROR allocating buffer for packet!\n"); + return -1; + } + + memcpy(fpa_buf, pkt, pkt_len); +#ifdef DEBUG_TX_PACKET + if (packet_tx_debug) { + printf("\nTX packet: interface: %d, index: %d\n", + oct_eth_info->interface, oct_eth_info->index); + print_packet(pkt, pkt_len); + } +#endif + cvm_oct_xmit(dev, fpa_buf, pkt_len); + oct_eth_info->packets_sent++; + + return 0; +} + +int nic_open(struct udevice *dev) +{ + octeon_eth_init(dev); + + return 0; +} + +static void octeon_eth_halt_bgx(struct udevice *dev, + cvmx_helper_interface_mode_t mode) +{ + union cvmx_bgxx_cmrx_config cmr_config; + union cvmx_bgxx_cmr_rx_adrx_cam cmr_cam; + struct octeon_eth_info *oct_eth_info = dev_get_priv(dev); + int index = oct_eth_info->index; + int xiface = oct_eth_info->interface; + struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); + + debug("%s(%s(%d.%d), %d)\n", __func__, dev->name, xiface, index, mode); + + /* For RGMII we need to properly shut down the XCV interface */ + if (cvmx_helper_bgx_is_rgmii(xiface, index)) { + debug(" Shut down XCV RGMII\n"); + octeon_bgx_xcv_rgmii_enable(xi.interface, index, false); + } else { + cmr_config.u64 = csr_rd_node(xi.node, + CVMX_BGXX_CMRX_CONFIG(index, xi.interface)); + cmr_config.s.data_pkt_tx_en = 0; + cmr_config.s.data_pkt_rx_en = 0; + csr_wr_node(xi.node, CVMX_BGXX_CMRX_CONFIG(index, xi.interface), + cmr_config.u64); + + cmr_cam.u64 = csr_rd_node(xi.node, + CVMX_BGXX_CMR_RX_ADRX_CAM(index * 8, xi.interface)); + cmr_cam.s.en = 0; + csr_wr_node(xi.node, + CVMX_BGXX_CMR_RX_ADRX_CAM(index * 8, xi.interface), + cmr_cam.u64); + oct_eth_info->last_bgx_mac = 0; + oct_eth_info->bgx_mac_set = 0; + } +} + +/** + * Halts the specified Ethernet interface preventing it from receiving any more + * packets. + * + * @param dev - Ethernet device to shut down. + */ +void octeon_eth_halt(struct udevice *dev) +{ + struct octeon_eth_info *oct_eth_info = dev_get_priv(dev); + int index = oct_eth_info->index; + int interface = oct_eth_info->interface; + cvmx_helper_interface_mode_t mode; + union cvmx_gmxx_rxx_adr_ctl adr_ctl; + cvmx_gmxx_prtx_cfg_t gmx_cfg; + u64 tmp; + + debug("%s(%s): Halting\n", __func__, dev->name); + + oct_eth_info->enabled = 0; + + mode = cvmx_helper_interface_get_mode(oct_eth_info->interface); + if (octeon_has_feature(OCTEON_FEATURE_BGX)) { + octeon_eth_halt_bgx(dev, mode); + return; + } + + /* Stop SCC */ + /* Disable reception on this port at the GMX block */ + switch (mode) { + case CVMX_HELPER_INTERFACE_MODE_RGMII: + case CVMX_HELPER_INTERFACE_MODE_GMII: + debug(" RGMII/GMII\n"); + tmp = csr_rd(CVMX_ASXX_RX_PRT_EN(oct_eth_info->interface)); + tmp &= ~(1ull << index); + /* Disable the RGMII RX ports */ + csr_wr(CVMX_ASXX_RX_PRT_EN(oct_eth_info->interface), tmp); + tmp = csr_rd(CVMX_ASXX_TX_PRT_EN(oct_eth_info->interface)); + tmp &= ~(1ull << index); + /* Disable the RGMII TX ports */ + csr_wr(CVMX_ASXX_TX_PRT_EN(oct_eth_info->interface), tmp); + /* No break! */ + case CVMX_HELPER_INTERFACE_MODE_SGMII: + case CVMX_HELPER_INTERFACE_MODE_QSGMII: + case CVMX_HELPER_INTERFACE_MODE_XAUI: + case CVMX_HELPER_INTERFACE_MODE_RXAUI: + case CVMX_HELPER_INTERFACE_MODE_XLAUI: + case CVMX_HELPER_INTERFACE_MODE_XFI: + case CVMX_HELPER_INTERFACE_MODE_10G_KR: + case CVMX_HELPER_INTERFACE_MODE_40G_KR4: + case CVMX_HELPER_INTERFACE_MODE_MIXED: + case CVMX_HELPER_INTERFACE_MODE_AGL: + /* Disable MAC filtering */ + gmx_cfg.u64 = csr_rd(oct_eth_info->gmx_base + GMX_PRT_CFG); + csr_wr(oct_eth_info->gmx_base + GMX_PRT_CFG, + gmx_cfg.u64 & ~1ull); + adr_ctl.u64 = 0; + adr_ctl.s.bcst = 1; /* Reject broadcast */ + csr_wr(oct_eth_info->gmx_base + GMX_RX_ADR_CTL, adr_ctl.u64); + csr_wr(oct_eth_info->gmx_base + GMX_RX_ADR_CAM_EN, 0); + csr_wr(oct_eth_info->gmx_base + GMX_PRT_CFG, gmx_cfg.u64); + break; + default: + printf("%s: Unknown mode %d for interface 0x%x:%d\n", __func__, + mode, interface, index); + break; + } +} + +void nic_stop(struct udevice *dev) +{ + octeon_eth_halt(dev); +} + +int nic_write_hwaddr(struct udevice *dev) +{ + cvm_oct_set_mac_address(dev); + + return 0; +} + +static const struct eth_ops octeon_nic_ops = { + .start = nic_open, + .stop = nic_stop, + .send = nic_xmit, + .recv = nic_recv, + .free_pkt = nic_free_pkt, + .write_hwaddr = nic_write_hwaddr, +}; + +static const struct udevice_id octeon_nic_ids[] = { + { .compatible = "cavium,octeon-7890-bgx" }, + {} +}; + +U_BOOT_DRIVER(octeon_nic) = { + .name = "octeon_nic", + .id = UCLASS_ETH, + .probe = octeon_nic_probe, + .of_match = octeon_nic_ids, + .ops = &octeon_nic_ops, + .priv_auto = sizeof(struct octeon_eth_info), +}; diff --git a/drivers/net/octeon/octeon_mdio.c b/drivers/net/octeon/octeon_mdio.c new file mode 100644 index 0000000..34ee809 --- /dev/null +++ b/drivers/net/octeon/octeon_mdio.c @@ -0,0 +1,226 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018-2022 Marvell International Ltd. + */ + +#include <dm.h> +#include <fdt_support.h> +#include <log.h> +#include <miiphy.h> +#include <net.h> +#include <linux/delay.h> + +#include <mach/cvmx-regs.h> +#include <mach/cvmx-csr.h> +#include <mach/octeon-model.h> +#include <mach/octeon-feature.h> +#include <mach/cvmx-smix-defs.h> +#include <mach/cvmx-config.h> +#include <mach/cvmx-helper.h> +#include <mach/cvmx-helper-board.h> +#include <mach/cvmx-mdio.h> + +#define CVMX_SMI_DRV_CTL 0x0001180000001828ull +#define DEFAULT_MDIO_SPEED 2500000 /** 2.5 MHz default speed */ + +/** + * cvmx_smi_drv_ctl + * + * Enables the SMI interface. + * + */ +union cvmx_smi_drv_ctl { + u64 u64; + struct cvmx_smi_drv_ctl_s { + u64 reserved_14_63 : 50; + u64 pctl : 6; + u64 reserved_6_7 : 2; + u64 nctl : 6; + } s; +}; + +struct octeon_mdiobus { + struct mii_dev *mii_dev; + /** + * The local bus is in the lower 8 bits, followed by the remote bus in + * the top 8 bits. Bit 16 will be set if the bus is non-local. + */ + u32 bus_id; + + int node; /** Node number */ + int speed; /** Bus speed, normally 2.5 MHz */ + int fdt_node; /** Node in FDT */ + bool local; /** true if local MDIO bus */ +}; + +static int octeon_mdio_read(struct udevice *mdio_dev, int phy_addr, + int dev_addr, int reg_addr) +{ + struct octeon_mdiobus *p = dev_get_priv(mdio_dev); + struct mii_dev *dev = p->mii_dev; + int value; + + debug("%s(0x%p(%s): bus_id=%d phy_addr=%d, 0x%x, 0x%x) - ", __func__, + dev, dev->name, p->bus_id, phy_addr, dev_addr, reg_addr); + if (IS_ENABLED(CONFIG_PHYLIB_10G) && dev_addr != MDIO_DEVAD_NONE) { + debug("clause 45 mode\n"); + value = cvmx_mdio_45_read(p->bus_id & 0xff, phy_addr, dev_addr, + reg_addr); + } else { + value = cvmx_mdio_read(p->bus_id & 0xff, phy_addr, reg_addr); + } + + debug("Return value: 0x%x\n", value); + return value; +} + +static int octeon_mdio_write(struct udevice *mdio_dev, int phy_addr, + int dev_addr, int reg_addr, u16 value) +{ + struct octeon_mdiobus *p = dev_get_priv(mdio_dev); + struct mii_dev *dev = p->mii_dev; + + debug("%s(0x%p(%s): bus_id=%d phy_addr=%d, 0x%x, 0x%x, 0x%x)\n", + __func__, dev, dev->name, p->bus_id, phy_addr, dev_addr, reg_addr, + value); + + if (IS_ENABLED(CONFIG_PHYLIB_10G) && dev_addr != MDIO_DEVAD_NONE) { + debug("clause 45 mode\n"); + return cvmx_mdio_45_write(p->bus_id & 0xff, phy_addr, dev_addr, + reg_addr, value); + } + + return cvmx_mdio_write(p->bus_id & 0xff, phy_addr, reg_addr, value); +} + +/** + * Converts a MDIO register address to a bus number + * + * @param reg_addr MDIO base register address + * + * @return MDIO bus number or -1 if invalid address + */ +int octeon_mdio_reg_addr_to_bus(u64 reg_addr) +{ + int bus_base; + int bus; + + /* Adjust the bus number based on the node number */ + bus_base = cvmx_csr_addr_to_node(reg_addr) * 4; + reg_addr = cvmx_csr_addr_strip_node(reg_addr); + + switch (reg_addr) { + case 0x1180000001800: + case 0x1180000003800: /* 68XX/78XX address */ + bus = 0; + break; + case 0x1180000001900: + case 0x1180000003880: + bus = 1; + break; + case 0x1180000003900: + bus = 2; + break; + case 0x1180000003980: + bus = 3; + break; + default: + printf("%s: Unknown register address 0x%llx\n", __func__, + reg_addr); + return -1; + } + bus += bus_base; + debug("%s: address 0x%llx is bus %d\n", __func__, reg_addr, bus); + return bus; +} + +static int octeon_mdio_probe(struct udevice *dev) +{ + struct octeon_mdiobus *p = dev_get_priv(dev); + union cvmx_smi_drv_ctl drv_ctl; + cvmx_smix_clk_t smi_clk; + u64 mdio_addr; + int bus; + u64 sclock; + u32 sample_dly; + u64 denom; + + mdio_addr = dev_read_addr(dev); + debug("%s: Translated address: 0x%llx\n", __func__, mdio_addr); + bus = octeon_mdio_reg_addr_to_bus(mdio_addr); + p->bus_id = bus; + debug("%s: bus: %d\n", __func__, bus); + + drv_ctl.u64 = csr_rd(CVMX_SMI_DRV_CTL); + drv_ctl.s.pctl = dev_read_u32_default(dev, "cavium,pctl-drive-strength", + drv_ctl.s.pctl); + drv_ctl.s.nctl = dev_read_u32_default(dev, "cavium,nctl-drive-strength", + drv_ctl.s.nctl); + debug("%s: Set MDIO PCTL drive strength to 0x%x and NCTL drive strength to 0x%x\n", + __func__, drv_ctl.s.pctl, drv_ctl.s.nctl); + csr_wr(CVMX_SMI_DRV_CTL, drv_ctl.u64); + + /* Set the bus speed, default is 2.5MHz */ + p->speed = dev_read_u32_default(dev, "cavium,max-speed", + DEFAULT_MDIO_SPEED); + sclock = gd->bus_clk; + smi_clk.u64 = csr_rd(CVMX_SMIX_CLK(bus & 3)); + smi_clk.s.phase = sclock / (p->speed * 2); + + /* Allow sample delay to be specified */ + sample_dly = dev_read_u32_default(dev, "cavium,sample-delay", 0); + /* Only change the sample delay if it is set, otherwise use + * the default value of 2. + */ + if (sample_dly) { + u32 sample; + + denom = (sclock * 1000ULL) / sample_dly; + debug("%s: sclock: %llu, sample_dly: %u ps, denom: %llu\n", + __func__, sclock, sample_dly, denom); + sample = (sclock + denom - 1) / denom; + debug("%s: sample: %u\n", __func__, smi_clk.s.sample); + if (sample < 2) { + printf("%s: warning: cavium,sample-delay %u ps is too small in device tree for %s\n", + __func__, sample_dly, dev->name); + sample = 2; + } + if (sample > (2 * smi_clk.s.phase - 3)) { + printf("%s: warning: cavium,sample-delay %u ps is too large in device tree for %s\n", + __func__, sample_dly, dev->name); + sample = 2 * smi_clk.s.phase - 3; + } + smi_clk.s.sample = sample & 0xf; + smi_clk.s.sample_hi = (sample >> 4) & 0xf; + debug("%s(%s): sample delay: %u ps (%d clocks)\n", __func__, + dev->name, sample_dly, smi_clk.s.sample); + } + csr_wr(CVMX_SMIX_CLK(bus & 3), smi_clk.u64); + + debug("mdio clock phase: %d clocks\n", smi_clk.s.phase); + csr_wr(CVMX_SMIX_CLK(bus & 3), smi_clk.u64); + debug("Enabling SMI interface %s\n", dev->name); + csr_wr(CVMX_SMIX_EN(bus & 3), 1); + + /* Muxed MDIO bus support removed for now! */ + return 0; +} + +static const struct mdio_ops octeon_mdio_ops = { + .read = octeon_mdio_read, + .write = octeon_mdio_write, +}; + +static const struct udevice_id octeon_mdio_ids[] = { + { .compatible = "cavium,octeon-3860-mdio" }, + {} +}; + +U_BOOT_DRIVER(octeon_mdio) = { + .name = "octeon_mdio", + .id = UCLASS_MDIO, + .of_match = octeon_mdio_ids, + .probe = octeon_mdio_probe, + .ops = &octeon_mdio_ops, + .priv_auto = sizeof(struct octeon_mdiobus), +}; diff --git a/include/configs/octeon_common.h b/include/configs/octeon_common.h index 2e4bfd0..7e71c83 100644 --- a/include/configs/octeon_common.h +++ b/include/configs/octeon_common.h @@ -8,7 +8,7 @@ #define __OCTEON_COMMON_H__ #if defined(CONFIG_RAM_OCTEON) -#define CONFIG_SYS_INIT_SP_OFFSET 0x20100000 +#define CONFIG_SYS_INIT_SP_OFFSET 0x20180000 #else /* No DDR init -> run in L2 cache with limited resources */ #define CONFIG_SYS_INIT_SP_OFFSET 0x00180000 diff --git a/include/configs/octeon_ebb7304.h b/include/configs/octeon_ebb7304.h index 358db69..8c6c57b 100644 --- a/include/configs/octeon_ebb7304.h +++ b/include/configs/octeon_ebb7304.h @@ -16,4 +16,6 @@ #define CONFIG_SYS_FLASH_CFI_WIDTH FLASH_CFI_8BIT #define CONFIG_SYS_FLASH_EMPTY_INFO /* flinfo indicates empty blocks */ +#define PHY_ANEG_TIMEOUT 8000 /* PHY needs a longer aneg time */ + #endif /* __CONFIG_H__ */ diff --git a/include/miiphy.h b/include/miiphy.h index 110921f..c66a184 100644 --- a/include/miiphy.h +++ b/include/miiphy.h @@ -189,6 +189,15 @@ int dm_mdio_write(struct udevice *mdio_dev, int addr, int devad, int reg, u16 va int dm_mdio_reset(struct udevice *mdio_dev); /** + * dm_phy_find_by_ofnode - Find PHY device by ofnode + * + * @phynode: PHY's ofnode + * + * Return: pointer to phy_device, or NULL on error + */ +struct phy_device *dm_phy_find_by_ofnode(ofnode phynode); + +/** * dm_mdio_phy_connect - Wrapper over phy_connect for DM MDIO * * @mdiodev: mdio device the PHY is accesible on diff --git a/net/mdio-uclass.c b/net/mdio-uclass.c index 7593618..4401492 100644 --- a/net/mdio-uclass.c +++ b/net/mdio-uclass.c @@ -129,6 +129,28 @@ static int dm_mdio_pre_remove(struct udevice *dev) return 0; } +struct phy_device *dm_phy_find_by_ofnode(ofnode phynode) +{ + struct mdio_perdev_priv *pdata; + struct udevice *mdiodev; + u32 phy_addr; + + if (ofnode_read_u32(phynode, "reg", &phy_addr)) + return NULL; + + if (uclass_get_device_by_ofnode(UCLASS_MDIO, + ofnode_get_parent(phynode), + &mdiodev)) + return NULL; + + if (device_probe(mdiodev)) + return NULL; + + pdata = dev_get_uclass_priv(mdiodev); + + return phy_find_by_mask(pdata->mii_bus, BIT(phy_addr)); +} + struct phy_device *dm_mdio_phy_connect(struct udevice *mdiodev, int phyaddr, struct udevice *ethdev, phy_interface_t interface) |